home *** CD-ROM | disk | FTP | other *** search
/ Computer Shopper 242 / Issue 242 - April 2008 - DPCS0408DVD.ISO / Open Source / AutoHotKey / Source / AutoHotkey104705_source.exe / source / script.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  2007-11-21  |  624.5 KB  |  12,670 lines

  1. /*
  2. AutoHotkey
  3.  
  4. Copyright 2003-2007 Chris Mallett (support@autohotkey.com)
  5.  
  6. This program is free software; you can redistribute it and/or
  7. modify it under the terms of the GNU General Public License
  8. as published by the Free Software Foundation; either version 2
  9. of the License, or (at your option) any later version.
  10.  
  11. This program is distributed in the hope that it will be useful,
  12. but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14. GNU General Public License for more details.
  15. */
  16.  
  17. #include "stdafx.h" // pre-compiled headers
  18. #include "script.h"
  19. #include "globaldata.h" // for a lot of things
  20. #include "util.h" // for strlcpy() etc.
  21. #include "mt19937ar-cok.h" // for random number generator
  22. #include "window.h" // for a lot of things
  23. #include "application.h" // for MsgSleep()
  24.  
  25. // Globals that are for only this module:
  26. #define MAX_COMMENT_FLAG_LENGTH 15
  27. static char g_CommentFlag[MAX_COMMENT_FLAG_LENGTH + 1] = ";"; // Adjust the below for any changes.
  28. static size_t g_CommentFlagLength = 1; // pre-calculated for performance
  29.  
  30. // General note about the methods in here:
  31. // Want to be able to support multiple simultaneous points of execution
  32. // because more than one subroutine can be executing simultaneously
  33. // (well, more precisely, there can be more than one script subroutine
  34. // that's in a "currently running" state, even though all such subroutines,
  35. // except for the most recent one, are suspended.  So keep this in mind when
  36. // using things such as static data members or static local variables.
  37.  
  38.  
  39. Script::Script()
  40.     : mFirstLine(NULL), mLastLine(NULL), mCurrLine(NULL), mPlaceholderLabel(NULL), mLineCount(0)
  41.     , mThisHotkeyName(""), mPriorHotkeyName(""), mThisHotkeyStartTime(0), mPriorHotkeyStartTime(0)
  42.     , mEndChar(0), mThisHotkeyModifiersLR(0)
  43.     , mNextClipboardViewer(NULL), mOnClipboardChangeIsRunning(false), mOnClipboardChangeLabel(NULL)
  44.     , mOnExitLabel(NULL), mExitReason(EXIT_NONE)
  45.     , mFirstLabel(NULL), mLastLabel(NULL)
  46.     , mFirstFunc(NULL), mLastFunc(NULL)
  47.     , mFirstTimer(NULL), mLastTimer(NULL), mTimerEnabledCount(0), mTimerCount(0)
  48.     , mFirstMenu(NULL), mLastMenu(NULL), mMenuCount(0)
  49.     , mVar(NULL), mVarCount(0), mVarCountMax(0), mLazyVar(NULL), mLazyVarCount(0)
  50.     , mOpenBlockCount(0), mNextLineIsFunctionBody(false)
  51.     , mFuncExceptionVar(NULL), mFuncExceptionVarCount(0)
  52.     , mCurrFileIndex(0), mCombinedLineNumber(0), mNoHotkeyLabels(true), mMenuUseErrorLevel(false)
  53.     , mFileSpec(""), mFileDir(""), mFileName(""), mOurEXE(""), mOurEXEDir(""), mMainWindowTitle("")
  54.     , mIsReadyToExecute(false), AutoExecSectionIsRunning(false)
  55.     , mIsRestart(false), mIsAutoIt2(false), mErrorStdOut(false)
  56. #ifdef AUTOHOTKEYSC
  57.     , mCompiledHasCustomIcon(false)
  58. #else
  59.     , mIncludeLibraryFunctionsThenExit(NULL)
  60. #endif
  61.     , mLinesExecutedThisCycle(0), mUninterruptedLineCountMax(1000), mUninterruptibleTime(15)
  62.     , mRunAsUser(NULL), mRunAsPass(NULL), mRunAsDomain(NULL)
  63.     , mCustomIcon(NULL) // Normally NULL unless there's a custom tray icon loaded dynamically.
  64.     , mCustomIconFile(NULL), mIconFrozen(false), mTrayIconTip(NULL) // Allocated on first use.
  65.     , mCustomIconNumber(0)
  66. {
  67.     // v1.0.25: mLastScriptRest and mLastPeekTime are now initialized right before the auto-exec
  68.     // section of the script is launched, which avoids an initial Sleep(10) in ExecUntil
  69.     // that would otherwise occur.
  70.     *mThisMenuItemName = *mThisMenuName = '\0';
  71.     ZeroMemory(&mNIC, sizeof(mNIC));  // Constructor initializes this, to be safe.
  72.     mNIC.hWnd = NULL;  // Set this as an indicator that it tray icon is not installed.
  73.  
  74.     // Lastly (after the above have been initialized), anything that can fail:
  75.     if (   !(mTrayMenu = AddMenu("Tray"))   ) // realistically never happens
  76.     {
  77.         ScriptError("No tray mem");
  78.         ExitApp(EXIT_CRITICAL);
  79.     }
  80.     else
  81.         mTrayMenu->mIncludeStandardItems = true;
  82.  
  83. #ifdef _DEBUG
  84.     if (ID_FILE_EXIT < ID_MAIN_FIRST) // Not a very thorough check.
  85.         ScriptError("DEBUG: ID_FILE_EXIT is too large (conflicts with IDs reserved via ID_USER_FIRST).");
  86.     if (MAX_CONTROLS_PER_GUI > ID_USER_FIRST - 3)
  87.         ScriptError("DEBUG: MAX_CONTROLS_PER_GUI is too large (conflicts with IDs reserved via ID_USER_FIRST).");
  88.     int LargestMaxParams, i, j;
  89.     ActionTypeType *np;
  90.     // Find the Largest value of MaxParams used by any command and make sure it
  91.     // isn't something larger than expected by the parsing routines:
  92.     for (LargestMaxParams = i = 0; i < g_ActionCount; ++i)
  93.     {
  94.         if (g_act[i].MaxParams > LargestMaxParams)
  95.             LargestMaxParams = g_act[i].MaxParams;
  96.         // This next part has been tested and it does work, but only if one of the arrays
  97.         // contains exactly MAX_NUMERIC_PARAMS number of elements and isn't zero terminated.
  98.         // Relies on short-circuit boolean order:
  99.         for (np = g_act[i].NumericParams, j = 0; j < MAX_NUMERIC_PARAMS && *np; ++j, ++np);
  100.         if (j >= MAX_NUMERIC_PARAMS)
  101.         {
  102.             ScriptError("DEBUG: At least one command has a NumericParams array that isn't zero-terminated."
  103.                 "  This would result in reading beyond the bounds of the array.");
  104.             return;
  105.         }
  106.     }
  107.     if (LargestMaxParams > MAX_ARGS)
  108.         ScriptError("DEBUG: At least one command supports more arguments than allowed.");
  109.     if (sizeof(ActionTypeType) == 1 && g_ActionCount > 256)
  110.         ScriptError("DEBUG: Since there are now more than 256 Action Types, the ActionTypeType"
  111.             " typedef must be changed.");
  112. #endif
  113. }
  114.  
  115.  
  116.  
  117. Script::~Script() // Destructor.
  118. {
  119.     // MSDN: "Before terminating, an application must call the UnhookWindowsHookEx function to free
  120.     // system resources associated with the hook."
  121.     AddRemoveHooks(0); // Remove all hooks.
  122.     if (mNIC.hWnd) // Tray icon is installed.
  123.         Shell_NotifyIcon(NIM_DELETE, &mNIC); // Remove it.
  124.     // Destroy any Progress/SplashImage windows that haven't already been destroyed.  This is necessary
  125.     // because sometimes these windows aren't owned by the main window:
  126.     int i;
  127.     for (i = 0; i < MAX_PROGRESS_WINDOWS; ++i)
  128.     {
  129.         if (g_Progress[i].hwnd && IsWindow(g_Progress[i].hwnd))
  130.             DestroyWindow(g_Progress[i].hwnd);
  131.         if (g_Progress[i].hfont1) // Destroy font only after destroying the window that uses it.
  132.             DeleteObject(g_Progress[i].hfont1);
  133.         if (g_Progress[i].hfont2) // Destroy font only after destroying the window that uses it.
  134.             DeleteObject(g_Progress[i].hfont2);
  135.         if (g_Progress[i].hbrush)
  136.             DeleteObject(g_Progress[i].hbrush);
  137.     }
  138.     for (i = 0; i < MAX_SPLASHIMAGE_WINDOWS; ++i)
  139.     {
  140.         if (g_SplashImage[i].pic)
  141.             g_SplashImage[i].pic->Release();
  142.         if (g_SplashImage[i].hwnd && IsWindow(g_SplashImage[i].hwnd))
  143.             DestroyWindow(g_SplashImage[i].hwnd);
  144.         if (g_SplashImage[i].hfont1) // Destroy font only after destroying the window that uses it.
  145.             DeleteObject(g_SplashImage[i].hfont1);
  146.         if (g_SplashImage[i].hfont2) // Destroy font only after destroying the window that uses it.
  147.             DeleteObject(g_SplashImage[i].hfont2);
  148.         if (g_SplashImage[i].hbrush)
  149.             DeleteObject(g_SplashImage[i].hbrush);
  150.     }
  151.  
  152.     // It is safer/easier to destroy the GUI windows prior to the menus (especially the menu bars).
  153.     // This is because one GUI window might get destroyed and take with it a menu bar that is still
  154.     // in use by an existing GUI window.  GuiType::Destroy() adheres to this philosophy by detaching
  155.     // its menu bar prior to destroying its window:
  156.     for (i = 0; i < MAX_GUI_WINDOWS; ++i)
  157.         GuiType::Destroy(i); // Static method to avoid problems with object destroying itself.
  158.     for (i = 0; i < GuiType::sFontCount; ++i) // Now that GUI windows are gone, delete all GUI fonts.
  159.         if (GuiType::sFont[i].hfont)
  160.             DeleteObject(GuiType::sFont[i].hfont);
  161.     // The above might attempt to delete an HFONT from GetStockObject(DEFAULT_GUI_FONT), etc.
  162.     // But that should be harmless:
  163.     // MSDN: "It is not necessary (but it is not harmful) to delete stock objects by calling DeleteObject."
  164.  
  165.     // Above: Probably best to have removed icon from tray and destroyed any Gui/Splash windows that were
  166.     // using it prior to getting rid of the script's custom icon below:
  167.     if (mCustomIcon)
  168.         DestroyIcon(mCustomIcon);
  169.  
  170.     // Since they're not associated with a window, we must free the resources for all popup menus.
  171.     // Update: Even if a menu is being used as a GUI window's menu bar, see note above for why menu
  172.     // destruction is done AFTER the GUI windows are destroyed:
  173.     UserMenu *menu_to_delete;
  174.     for (UserMenu *m = mFirstMenu; m;)
  175.     {
  176.         menu_to_delete = m;
  177.         m = m->mNextMenu;
  178.         ScriptDeleteMenu(menu_to_delete);
  179.         // Above call should not return FAIL, since the only way FAIL can realistically happen is
  180.         // when a GUI window is still using the menu as its menu bar.  But all GUI windows are gone now.
  181.     }
  182.  
  183.     // Since tooltip windows are unowned, they should be destroyed to avoid resource leak:
  184.     for (i = 0; i < MAX_TOOLTIPS; ++i)
  185.         if (g_hWndToolTip[i] && IsWindow(g_hWndToolTip[i]))
  186.             DestroyWindow(g_hWndToolTip[i]);
  187.  
  188.     if (g_hFontSplash) // The splash window itself should auto-destroyed, since it's owned by main.
  189.         DeleteObject(g_hFontSplash);
  190.  
  191.     if (mOnClipboardChangeLabel) // Remove from viewer chain.
  192.         ChangeClipboardChain(g_hWnd, mNextClipboardViewer);
  193.  
  194.     // Close any open sound item to prevent hang-on-exit in certain operating systems or conditions.
  195.     // If there's any chance that a sound was played and not closed out, or that it is still playing,
  196.     // this check is done.  Otherwise, the check is avoided since it might be a high overhead call,
  197.     // especially if the sound subsystem part of the OS is currently swapped out or something:
  198.     if (g_SoundWasPlayed)
  199.     {
  200.         char buf[MAX_PATH * 2];
  201.         mciSendString("status " SOUNDPLAY_ALIAS " mode", buf, sizeof(buf), NULL);
  202.         if (*buf) // "playing" or "stopped"
  203.             mciSendString("close " SOUNDPLAY_ALIAS, NULL, 0, NULL);
  204.     }
  205.  
  206. #ifdef ENABLE_KEY_HISTORY_FILE
  207.     KeyHistoryToFile();  // Close the KeyHistory file if it's open.
  208. #endif
  209.  
  210.     DeleteCriticalSection(&g_CriticalRegExCache); // g_CriticalRegExCache is used elsewhere for thread-safety.
  211. }
  212.  
  213.  
  214.  
  215. ResultType Script::Init(char *aScriptFilename, bool aIsRestart)
  216. // Returns OK or FAIL.
  217. // Caller has provided an empty string for aScriptFilename if this is a compiled script.
  218. // Otherwise, aScriptFilename can be NULL if caller hasn't determined the filename of the script yet.
  219. {
  220.     mIsRestart = aIsRestart;
  221.     char buf[2048]; // Just to make sure we have plenty of room to do things with.
  222. #ifdef AUTOHOTKEYSC
  223.     // Fix for v1.0.29: Override the caller's use of __argv[0] by using GetModuleFileName(),
  224.     // so that when the script is started from the command line but the user didn't type the
  225.     // extension, the extension will be included.  This necessary because otherwise
  226.     // #SingleInstance wouldn't be able to detect duplicate versions in every case.
  227.     // It also provides more consistency.
  228.     GetModuleFileName(NULL, buf, sizeof(buf));
  229. #else
  230.     if (!aScriptFilename) // v1.0.46.08: Change in policy: store the default script in the My Documents directory rather than in Program Files.  It's more correct and solves issues that occur due to Vista's file-protection scheme.
  231.     {
  232.         // Since no script-file was specified on the command line, use the default name.
  233.         // For backward compatibility, FIRST check if there's an AutoHotkey.ini file in the current
  234.         // directory.  If there is, that needs to be used to retain compatibility.
  235.         aScriptFilename = NAME_P ".ini";
  236.         if (GetFileAttributes(aScriptFilename) == 0xFFFFFFFF) // File doesn't exist, so fall back to new method.
  237.         {
  238.             aScriptFilename = buf;
  239.             VarSizeType filespec_length = BIV_MyDocuments(aScriptFilename, ""); // e.g. C:\Documents and Settings\Home\My Documents
  240.             if (filespec_length    > sizeof(buf)-16) // Need room for 16 characters ('\\' + "AutoHotkey.ahk" + terminator).
  241.                 return FAIL; // Very rare, so for simplicity just abort.
  242.             strcpy(aScriptFilename + filespec_length, "\\AutoHotkey.ahk"); // Append the filename: .ahk vs. .ini seems slightly better in terms of clarity and usefulness (e.g. the ability to double click the default script to launch it).
  243.             // Now everything is set up right because even if aScriptFilename is a nonexistent file, the
  244.             // user will be prompted to create it by a stage further below.
  245.         }
  246.         //else since the legacy .ini file exists, everything is now set up right. (The file might be a directory, but that isn't checked due to rarity.)
  247.     }
  248.     // In case the script is a relative filespec (relative to current working dir):
  249.     char *unused;
  250.     if (!GetFullPathName(aScriptFilename, sizeof(buf), buf, &unused)) // This is also relied upon by mIncludeLibraryFunctionsThenExit.  Succeeds even on nonexistent files.
  251.         return FAIL; // Due to rarity, no error msg, just abort.
  252. #endif
  253.     // Using the correct case not only makes it look better in title bar & tray tool tip,
  254.     // it also helps with the detection of "this script already running" since otherwise
  255.     // it might not find the dupe if the same script name is launched with different
  256.     // lowercase/uppercase letters:
  257.     ConvertFilespecToCorrectCase(buf); // This might change the length, e.g. due to expansion of 8.3 filename.
  258.     char *filename_marker;
  259.     if (   !(filename_marker = strrchr(buf, '\\'))   )
  260.         filename_marker = buf;
  261.     else
  262.         ++filename_marker;
  263.     if (   !(mFileSpec = SimpleHeap::Malloc(buf))   )  // The full spec is stored for convenience, and it's relied upon by mIncludeLibraryFunctionsThenExit.
  264.         return FAIL;  // It already displayed the error for us.
  265.     filename_marker[-1] = '\0'; // Terminate buf in this position to divide the string.
  266.     size_t filename_length = strlen(filename_marker);
  267.     if (   mIsAutoIt2 = (filename_length >= 4 && !stricmp(filename_marker + filename_length - 4, EXT_AUTOIT2))   )
  268.     {
  269.         // Set the old/AutoIt2 defaults for maximum safety and compatibilility.
  270.         // Standalone EXEs (compiled scripts) are always considered to be non-AutoIt2 (otherwise,
  271.         // the user should probably be using the AutoIt2 compiler).
  272.         g_AllowSameLineComments = false;
  273.         g_EscapeChar = '\\';
  274.         g.TitleFindFast = true; // In case the normal default is false.
  275.         g.DetectHiddenText = false;
  276.         // Make the mouse fast like AutoIt2, but not quite insta-move.  2 is expected to be more
  277.         // reliable than 1 since the AutoIt author said that values less than 2 might cause the
  278.         // drag to fail (perhaps just for specific apps, such as games):
  279.         g.DefaultMouseSpeed = 2;
  280.         g.KeyDelay = 20;
  281.         g.WinDelay = 500;
  282.         g.LinesPerCycle = 1;
  283.         g.IntervalBeforeRest = -1;  // i.e. this method is disabled by default for AutoIt2 scripts.
  284.         // Reduce max params so that any non escaped delimiters the user may be using literally
  285.         // in "window text" will still be considered literal, rather than as delimiters for
  286.         // args that are not supported by AutoIt2, such as exclude-title, exclude-text, MsgBox
  287.         // timeout, etc.  Note: Don't need to change IfWinExist and such because those already
  288.         // have special handling to recognize whether exclude-title is really a valid command
  289.         // instead (e.g. IfWinExist, title, text, Gosub, something).
  290.  
  291.         // NOTE: DO NOT ADD the IfWin command series to this section, since there is special handling
  292.         // for parsing those commands to figure out whether they're being used in the old AutoIt2
  293.         // style or the new Exclude Title/Text mode.
  294.  
  295.         // v1.0.40.02: The following is no longer done because a different mechanism is required now
  296.         // that the ARGn macros do not check whether mArgc is too small and substitute an empty string
  297.         // (instead, there is a loop in ExpandArgs that puts an empty string in each sArgDeref entry
  298.         // for which the script omitted a parameter [and that loop relies on MaxParams being absolutely
  299.         // accurate rather than conditional upon whether the script is of type ".aut"]).
  300.         //g_act[ACT_FILESELECTFILE].MaxParams -= 2;
  301.         //g_act[ACT_FILEREMOVEDIR].MaxParams -= 1;
  302.         //g_act[ACT_MSGBOX].MaxParams -= 1;
  303.         //g_act[ACT_INIREAD].MaxParams -= 1;
  304.         //g_act[ACT_STRINGREPLACE].MaxParams -= 1;
  305.         //g_act[ACT_STRINGGETPOS].MaxParams -= 2;
  306.         //g_act[ACT_WINCLOSE].MaxParams -= 3;  // -3 for these two, -2 for the others.
  307.         //g_act[ACT_WINKILL].MaxParams -= 3;
  308.         //g_act[ACT_WINACTIVATE].MaxParams -= 2;
  309.         //g_act[ACT_WINMINIMIZE].MaxParams -= 2;
  310.         //g_act[ACT_WINMAXIMIZE].MaxParams -= 2;
  311.         //g_act[ACT_WINRESTORE].MaxParams -= 2;
  312.         //g_act[ACT_WINHIDE].MaxParams -= 2;
  313.         //g_act[ACT_WINSHOW].MaxParams -= 2;
  314.         //g_act[ACT_WINSETTITLE].MaxParams -= 2;
  315.         //g_act[ACT_WINGETTITLE].MaxParams -= 2;
  316.     }
  317.     if (   !(mFileDir = SimpleHeap::Malloc(buf))   )
  318.         return FAIL;  // It already displayed the error for us.
  319.     if (   !(mFileName = SimpleHeap::Malloc(filename_marker))   )
  320.         return FAIL;  // It already displayed the error for us.
  321. #ifdef AUTOHOTKEYSC
  322.     // Omit AutoHotkey from the window title, like AutoIt3 does for its compiled scripts.
  323.     // One reason for this is to reduce backlash if evil-doers create viruses and such
  324.     // with the program:
  325.     snprintf(buf, sizeof(buf), "%s\\%s", mFileDir, mFileName);
  326. #else
  327.     snprintf(buf, sizeof(buf), "%s\\%s - %s", mFileDir, mFileName, NAME_PV);
  328. #endif
  329.     if (   !(mMainWindowTitle = SimpleHeap::Malloc(buf))   )
  330.         return FAIL;  // It already displayed the error for us.
  331.  
  332.     // It may be better to get the module name this way rather than reading it from the registry
  333.     // (though it might be more proper to parse it out of the command line args or something),
  334.     // in case the user has moved it to a folder other than the install folder, hasn't installed it,
  335.     // or has renamed the EXE file itself.  Also, enclose the full filespec of the module in double
  336.     // quotes since that's how callers usually want it because ActionExec() currently needs it that way:
  337.     *buf = '"';
  338.     if (GetModuleFileName(NULL, buf + 1, sizeof(buf) - 2)) // -2 to leave room for the enclosing double quotes.
  339.     {
  340.         size_t buf_length = strlen(buf);
  341.         buf[buf_length++] = '"';
  342.         buf[buf_length] = '\0';
  343.         if (   !(mOurEXE = SimpleHeap::Malloc(buf))   )
  344.             return FAIL;  // It already displayed the error for us.
  345.         else
  346.         {
  347.             char *last_backslash = strrchr(buf, '\\');
  348.             if (!last_backslash) // probably can't happen due to the nature of GetModuleFileName().
  349.                 mOurEXEDir = "";
  350.             last_backslash[1] = '\0'; // i.e. keep the trailing backslash for convenience.
  351.             if (   !(mOurEXEDir = SimpleHeap::Malloc(buf + 1))   ) // +1 to omit the leading double-quote.
  352.                 return FAIL;  // It already displayed the error for us.
  353.         }
  354.     }
  355.     return OK;
  356. }
  357.  
  358.     
  359.  
  360. ResultType Script::CreateWindows()
  361. // Returns OK or FAIL.
  362. {
  363.     if (!mMainWindowTitle || !*mMainWindowTitle) return FAIL;  // Init() must be called before this function.
  364.     // Register a window class for the main window:
  365.     WNDCLASSEX wc = {0};
  366.     wc.cbSize = sizeof(wc);
  367.     wc.lpszClassName = WINDOW_CLASS_MAIN;
  368.     wc.hInstance = g_hInstance;
  369.     wc.lpfnWndProc = MainWindowProc;
  370.     // The following are left at the default of NULL/0 set higher above:
  371.     //wc.style = 0;  // CS_HREDRAW | CS_VREDRAW
  372.     //wc.cbClsExtra = 0;
  373.     //wc.cbWndExtra = 0;
  374.     wc.hIcon = wc.hIconSm = (HICON)LoadImage(g_hInstance, MAKEINTRESOURCE(IDI_MAIN), IMAGE_ICON, 0, 0, LR_SHARED); // Use LR_SHARED to conserve memory (since the main icon is loaded for so many purposes).
  375.     wc.hCursor = LoadCursor((HINSTANCE) NULL, IDC_ARROW);
  376.     wc.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1);  // Needed for ProgressBar. Old: (HBRUSH)GetStockObject(WHITE_BRUSH);
  377.     wc.lpszMenuName = MAKEINTRESOURCE(IDR_MENU_MAIN); // NULL; // "MainMenu";
  378.     if (!RegisterClassEx(&wc))
  379.     {
  380.         MsgBox("RegClass"); // Short/generic msg since so rare.
  381.         return FAIL;
  382.     }
  383.  
  384.     // Register a second class for the splash window.  The only difference is that
  385.     // it doesn't have the menu bar:
  386.     wc.lpszClassName = WINDOW_CLASS_SPLASH;
  387.     wc.lpszMenuName = NULL; // Override the non-NULL value set higher above.
  388.     if (!RegisterClassEx(&wc))
  389.     {
  390.         MsgBox("RegClass"); // Short/generic msg since so rare.
  391.         return FAIL;
  392.     }
  393.  
  394.     char class_name[64];
  395.     HWND fore_win = GetForegroundWindow();
  396.     bool do_minimize = !fore_win || (GetClassName(fore_win, class_name, sizeof(class_name))
  397.         && !stricmp(class_name, "Shell_TrayWnd")); // Shell_TrayWnd is the taskbar's class on Win98/XP and probably the others too.
  398.  
  399.     // Note: the title below must be constructed the same was as is done by our
  400.     // WinMain() (so that we can detect whether this script is already running)
  401.     // which is why it's standardized in g_script.mMainWindowTitle.
  402.     // Create the main window.  Prevent momentary disruption of Start Menu, which
  403.     // some users understandably don't like, by omitting the taskbar button temporarily.
  404.     // This is done because testing shows that minimizing the window further below, even
  405.     // though the window is hidden, would otherwise briefly show the taskbar button (or
  406.     // at least redraw the taskbar).  Sometimes this isn't noticeable, but other times
  407.     // (such as when the system is under heavy load) a user reported that it is quite
  408.     // noticeable. WS_EX_TOOLWINDOW is used instead of WS_EX_NOACTIVATE because
  409.     // WS_EX_NOACTIVATE is available only on 2000/XP.
  410.     if (   !(g_hWnd = CreateWindowEx(do_minimize ? WS_EX_TOOLWINDOW : 0
  411.         , WINDOW_CLASS_MAIN
  412.         , mMainWindowTitle
  413.         , WS_OVERLAPPEDWINDOW // Style.  Alt: WS_POPUP or maybe 0.
  414.         , CW_USEDEFAULT // xpos
  415.         , CW_USEDEFAULT // ypos
  416.         , CW_USEDEFAULT // width
  417.         , CW_USEDEFAULT // height
  418.         , NULL // parent window
  419.         , NULL // Identifies a menu, or specifies a child-window identifier depending on the window style
  420.         , g_hInstance // passed into WinMain
  421.         , NULL))   ) // lpParam
  422.     {
  423.         MsgBox("CreateWindow"); // Short msg since so rare.
  424.         return FAIL;
  425.     }
  426. #ifdef AUTOHOTKEYSC
  427.     HMENU menu = GetMenu(g_hWnd);
  428.     // Disable the Edit menu item, since it does nothing for a compiled script:
  429.     EnableMenuItem(menu, ID_FILE_EDITSCRIPT, MF_DISABLED | MF_GRAYED);
  430.     if (!g_AllowMainWindow)
  431.     {
  432.         EnableMenuItem(menu, ID_VIEW_KEYHISTORY, MF_DISABLED | MF_GRAYED);
  433.         EnableMenuItem(menu, ID_VIEW_LINES, MF_DISABLED | MF_GRAYED);
  434.         EnableMenuItem(menu, ID_VIEW_VARIABLES, MF_DISABLED | MF_GRAYED);
  435.         EnableMenuItem(menu, ID_VIEW_HOTKEYS, MF_DISABLED | MF_GRAYED);
  436.         // But leave the ID_VIEW_REFRESH menu item enabled because if the script contains a
  437.         // command such as ListLines in it, Refresh can be validly used.
  438.     }
  439. #endif
  440.  
  441.     if (    !(g_hWndEdit = CreateWindow("edit", NULL, WS_CHILD | WS_VISIBLE | WS_BORDER
  442.         | ES_LEFT | ES_MULTILINE | ES_READONLY | WS_VSCROLL // | WS_HSCROLL (saves space)
  443.         , 0, 0, 0, 0, g_hWnd, (HMENU)1, g_hInstance, NULL))   )
  444.     {
  445.         MsgBox("CreateWindow"); // Short msg since so rare.
  446.         return FAIL;
  447.     }
  448.     // FONTS: The font used by default, at least on XP, is GetStockObject(SYSTEM_FONT).
  449.     // It seems preferable to smaller fonts such DEFAULT_GUI_FONT(DEFAULT_GUI_FONT).
  450.     // For more info on pre-loaded fonts (not too many choices), see MSDN's GetStockObject().
  451.     //SendMessage(g_hWndEdit, WM_SETFONT, (WPARAM)GetStockObject(SYSTEM_FONT), 0);
  452.  
  453.     // v1.0.30.05: Specifying a limit of zero opens the control to its maximum text capacity,
  454.     // which removes the 32K size restriction.  Testing shows that this does not increase the actual
  455.     // amount of memory used for controls containing small amounts of text.  All it does is allow
  456.     // the control to allocate more memory as needed.  By specifying zero, a max
  457.     // of 64K becomes available on Windows 9x, and perhaps as much as 4 GB on NT/2k/XP.
  458.     SendMessage(g_hWndEdit, EM_LIMITTEXT, 0, 0);
  459.  
  460.     // Some of the MSDN docs mention that an app's very first call to ShowWindow() makes that
  461.     // function operate in a special mode. Therefore, it seems best to get that first call out
  462.     // of the way to avoid the possibility that the first-call behavior will cause problems with
  463.     // our normal use of ShowWindow() below and other places.  Also, decided to ignore nCmdShow,
  464.     // to avoid any momentary visual effects on startup.
  465.     // Update: It's done a second time because the main window might now be visible if the process
  466.     // that launched ours specified that.  It seems best to override the requested state because
  467.     // some calling processes might specify "maximize" or "shownormal" as generic launch method.
  468.     // The script can display it's own main window with ListLines, etc.
  469.     // MSDN: "the nCmdShow value is ignored in the first call to ShowWindow if the program that
  470.     // launched the application specifies startup information in the structure. In this case,
  471.     // ShowWindow uses the information specified in the STARTUPINFO structure to show the window.
  472.     // On subsequent calls, the application must call ShowWindow with nCmdShow set to SW_SHOWDEFAULT
  473.     // to use the startup information provided by the program that launched the application."
  474.     ShowWindow(g_hWnd, SW_HIDE);
  475.     ShowWindow(g_hWnd, SW_HIDE);
  476.  
  477.     // Now that the first call to ShowWindow() is out of the way, minimize the main window so that
  478.     // if the script is launched from the Start Menu (and perhaps other places such as the
  479.     // Quick-launch toolbar), the window that was active before the Start Menu was displayed will
  480.     // become active again.  But as of v1.0.25.09, this minimize is done more selectively to prevent
  481.     // the launch of a script from knocking the user out of a full-screen game or other application
  482.     // that would be disrupted by an SW_MINIMIZE:
  483.     if (do_minimize)
  484.     {
  485.         ShowWindow(g_hWnd, SW_MINIMIZE);
  486.         SetWindowLong(g_hWnd, GWL_EXSTYLE, 0); // Give the main window back its taskbar button.
  487.     }
  488.     // Note: When the window is not minimized, task manager reports that a simple script (such as
  489.     // one consisting only of the single line "#Persistent") uses 2600 KB of memory vs. ~452 KB if
  490.     // it were immediately minimized.  That is probably just due to the vagaries of how the OS
  491.     // manages windows and memory and probably doesn't actually impact system performance to the
  492.     // degree indicated.  In other words, it's hard to imagine that the failure to do
  493.     // ShowWidnow(g_hWnd, SW_MINIMIZE) unconditionally upon startup (which causes the side effects
  494.     // discussed further above) significantly increases the actual memory load on the system.
  495.  
  496.     g_hAccelTable = LoadAccelerators(g_hInstance, MAKEINTRESOURCE(IDR_ACCELERATOR1));
  497.  
  498.     if (g_NoTrayIcon)
  499.         mNIC.hWnd = NULL;  // Set this as an indicator that tray icon is not installed.
  500.     else
  501.         // Even if the below fails, don't return FAIL in case the user is using a different shell
  502.         // or something.  In other words, it is expected to fail under certain circumstances and
  503.         // we want to tolerate that:
  504.         CreateTrayIcon();
  505.  
  506.     if (mOnClipboardChangeLabel)
  507.         mNextClipboardViewer = SetClipboardViewer(g_hWnd);
  508.  
  509.     return OK;
  510. }
  511.  
  512.  
  513.  
  514. void Script::CreateTrayIcon()
  515. // It is the caller's responsibility to ensure that the previous icon is first freed/destroyed
  516. // before calling us to install a new one.  However, that is probably not needed if the Explorer
  517. // crashed, since the memory used by the tray icon was probably destroyed along with it.
  518. {
  519.     ZeroMemory(&mNIC, sizeof(mNIC));  // To be safe.
  520.     // Using NOTIFYICONDATA_V2_SIZE vs. sizeof(NOTIFYICONDATA) improves compatibility with Win9x maybe.
  521.     // MSDN: "Using [NOTIFYICONDATA_V2_SIZE] for cbSize will allow your application to use NOTIFYICONDATA
  522.     // with earlier Shell32.dll versions, although without the version 6.0 enhancements."
  523.     // Update: Using V2 gives an compile error so trying V1.  Update: Trying sizeof(NOTIFYICONDATA)
  524.     // for compatibility with VC++ 6.x.  This is also what AutoIt3 uses:
  525.     mNIC.cbSize = sizeof(NOTIFYICONDATA);  // NOTIFYICONDATA_V1_SIZE
  526.     mNIC.hWnd = g_hWnd;
  527.     mNIC.uID = AHK_NOTIFYICON; // This is also used for the ID, see TRANSLATE_AHK_MSG for details.
  528.     mNIC.uFlags = NIF_MESSAGE | NIF_TIP | NIF_ICON;
  529.     mNIC.uCallbackMessage = AHK_NOTIFYICON;
  530. #ifdef AUTOHOTKEYSC
  531.     // i.e. don't override the user's custom icon:
  532.     mNIC.hIcon = mCustomIcon ? mCustomIcon : (HICON)LoadImage(g_hInstance, MAKEINTRESOURCE(mCompiledHasCustomIcon ? IDI_MAIN : g_IconTray), IMAGE_ICON, 0, 0, LR_SHARED);
  533. #else
  534.     mNIC.hIcon = mCustomIcon ? mCustomIcon : (HICON)LoadImage(g_hInstance, MAKEINTRESOURCE(g_IconTray), IMAGE_ICON, 0, 0, LR_SHARED); // Use LR_SHARED to conserve memory (since the main icon is loaded for so many purposes).
  535. #endif
  536.     UPDATE_TIP_FIELD
  537.     // If we were called due to an Explorer crash, I don't think it's necessary to call
  538.     // Shell_NotifyIcon() to remove the old tray icon because it was likely destroyed
  539.     // along with Explorer.  So just add it unconditionally:
  540.     if (!Shell_NotifyIcon(NIM_ADD, &mNIC))
  541.         mNIC.hWnd = NULL;  // Set this as an indicator that tray icon is not installed.
  542. }
  543.  
  544.  
  545.  
  546. void Script::UpdateTrayIcon(bool aForceUpdate)
  547. {
  548.     if (!mNIC.hWnd) // tray icon is not installed
  549.         return;
  550.     static bool icon_shows_paused = false;
  551.     static bool icon_shows_suspended = false;
  552.     if (!aForceUpdate && (mIconFrozen || (g.IsPaused == icon_shows_paused && g_IsSuspended == icon_shows_suspended)))
  553.         return; // it's already in the right state
  554.     int icon;
  555.     if (g.IsPaused && g_IsSuspended)
  556.         icon = IDI_PAUSE_SUSPEND;
  557.     else if (g.IsPaused)
  558.         icon = IDI_PAUSE;
  559.     else if (g_IsSuspended)
  560.         icon = g_IconTraySuspend;
  561.     else
  562. #ifdef AUTOHOTKEYSC
  563.         icon = mCompiledHasCustomIcon ? IDI_MAIN : g_IconTray;  // i.e. don't override the user's custom icon.
  564. #else
  565.         icon = g_IconTray;
  566. #endif
  567.     // Use the custom tray icon if the icon is normal (non-paused & non-suspended):
  568.     mNIC.hIcon = (mCustomIcon && (mIconFrozen || (!g.IsPaused && !g_IsSuspended))) ? mCustomIcon
  569.         : (HICON)LoadImage(g_hInstance, MAKEINTRESOURCE(icon), IMAGE_ICON, 0, 0, LR_SHARED); // Use LR_SHARED for simplicity and performance more than to conserve memory in this case.
  570.     if (Shell_NotifyIcon(NIM_MODIFY, &mNIC))
  571.     {
  572.         icon_shows_paused = g.IsPaused;
  573.         icon_shows_suspended = g_IsSuspended;
  574.     }
  575.     // else do nothing, just leave it in the same state.
  576. }
  577.  
  578.  
  579.  
  580. ResultType Script::AutoExecSection()
  581. {
  582.     if (!mIsReadyToExecute)
  583.         return FAIL;
  584.     if (mFirstLine != NULL)
  585.     {
  586.         // Choose a timeout that's a reasonable compromise between the following competing priorities:
  587.         // 1) That we want hotkeys to be responsive as soon as possible after the program launches
  588.         //    in case the user launches by pressing ENTER on a script, for example, and then immediately
  589.         //    tries to use a hotkey.  In addition, we want any timed subroutines to start running ASAP
  590.         //    because in rare cases the user might rely upon that happening.
  591.         // 2) To support the case when the auto-execute section never finishes (such as when it contains
  592.         //    an infinite loop to do background processing), yet we still want to allow the script
  593.         //    to put custom defaults into effect globally (for things such as KeyDelay).
  594.         // Obviously, the above approach has its flaws; there are ways to construct a script that would
  595.         // result in unexpected behavior.  However, the combination of this approach with the fact that
  596.         // the global defaults are updated *again* when/if the auto-execute section finally completes
  597.         // raises the expectation of proper behavior to a very high level.  In any case, I'm not sure there
  598.         // is any better approach that wouldn't break existing scripts or require a redesign of some kind.
  599.         // If this method proves unreliable due to disk activity slowing the program down to a crawl during
  600.         // the critical milliseconds after launch, one thing that might fix that is to have ExecUntil()
  601.         // be forced to run a minimum of, say, 100 lines (if there are that many) before allowing the
  602.         // timer expiration to have its effect.  But that's getting complicated and I'd rather not do it
  603.         // unless someone actually reports that such a thing ever happens.  Still, to reduce the chance
  604.         // of such a thing ever happening, it seems best to boost the timeout from 50 up to 100:
  605.         SET_AUTOEXEC_TIMER(100);
  606.         AutoExecSectionIsRunning = true;
  607.  
  608.         // v1.0.25: This is now done here, closer to the actual execution of the first line in the script,
  609.         // to avoid an unnecessary Sleep(10) that would otherwise occur in ExecUntil:
  610.         mLastScriptRest = mLastPeekTime = GetTickCount();
  611.  
  612.         ++g_nThreads;
  613.         ResultType result = mFirstLine->ExecUntil(UNTIL_RETURN);
  614.         --g_nThreads;
  615.  
  616.         KILL_AUTOEXEC_TIMER  // This also does "g.AllowThreadToBeInterrupted = true"
  617.         AutoExecSectionIsRunning = false;
  618.  
  619.         return result;
  620.     }
  621.     return OK;
  622. }
  623.  
  624.  
  625.  
  626. ResultType Script::Edit()
  627. {
  628. #ifdef AUTOHOTKEYSC
  629.     return OK; // Do nothing.
  630. #else
  631.     // This is here in case a compiled script ever uses the Edit command.  Since the "Edit This
  632.     // Script" menu item is not available for compiled scripts, it can't be called from there.
  633.     TitleMatchModes old_mode = g.TitleMatchMode;
  634.     g.TitleMatchMode = FIND_ANYWHERE;
  635.     HWND hwnd = WinExist(g, mFileName, "", mMainWindowTitle, ""); // Exclude our own main window.
  636.     g.TitleMatchMode = old_mode;
  637.     if (hwnd)
  638.     {
  639.         char class_name[32];
  640.         GetClassName(hwnd, class_name, sizeof(class_name));
  641.         if (!strcmp(class_name, "#32770") || !strnicmp(class_name, "AutoHotkey", 10)) // MessageBox(), InputBox(), FileSelectFile(), or GUI/script-owned window.
  642.             hwnd = NULL;  // Exclude it from consideration.
  643.     }
  644.     if (hwnd)  // File appears to already be open for editing, so use the current window.
  645.         SetForegroundWindowEx(hwnd);
  646.     else
  647.     {
  648.         char buf[MAX_PATH * 2];
  649.         // Enclose in double quotes anything that might contain spaces since the CreateProcess()
  650.         // method, which is attempted first, is more likely to succeed.  This is because it uses
  651.         // the command line method of creating the process, with everything all lumped together:
  652.         snprintf(buf, sizeof(buf), "\"%s\"", mFileSpec);
  653.         if (!ActionExec("edit", buf, mFileDir, false))  // Since this didn't work, try notepad.
  654.         {
  655.             // v1.0.40.06: Try to open .ini files first with their associated editor rather than trying the
  656.             // "edit" verb on them:
  657.             char *file_ext;
  658.             if (   !(file_ext = strrchr(mFileName, '.')) || stricmp(file_ext, ".ini")
  659.                 || !ActionExec("open", buf, mFileDir, false)   ) // Relies on short-circuit boolean order.
  660.             {
  661.                 // Even though notepad properly handles filenames with spaces in them under WinXP,
  662.                 // even without double quotes around them, it seems safer and more correct to always
  663.                 // enclose the filename in double quotes for maximum compatibility with all OSes:
  664.                 if (!ActionExec("notepad.exe", buf, mFileDir, false))
  665.                     MsgBox("Could not open script."); // Short message since so rare.
  666.             }
  667.         }
  668.     }
  669.     return OK;
  670. #endif
  671. }
  672.  
  673.  
  674.  
  675. ResultType Script::Reload(bool aDisplayErrors)
  676. {
  677.     // The new instance we're about to start will tell our process to stop, or it will display
  678.     // a syntax error or some other error, in which case our process will still be running:
  679. #ifdef AUTOHOTKEYSC
  680.     // This is here in case a compiled script ever uses the Reload command.  Since the "Reload This
  681.     // Script" menu item is not available for compiled scripts, it can't be called from there.
  682.     return g_script.ActionExec(mOurEXE, "/restart", g_WorkingDirOrig, aDisplayErrors);
  683. #else
  684.     char arg_string[MAX_PATH + 512];
  685.     snprintf(arg_string, sizeof(arg_string), "/restart \"%s\"", mFileSpec);
  686.     return g_script.ActionExec(mOurEXE, arg_string, g_WorkingDirOrig, aDisplayErrors);
  687. #endif
  688. }
  689.  
  690.  
  691.  
  692. ResultType Script::ExitApp(ExitReasons aExitReason, char *aBuf, int aExitCode)
  693. // Normal exit (if aBuf is NULL), or a way to exit immediately on error (which is mostly
  694. // for times when it would be unsafe to call MsgBox() due to the possibility that it would
  695. // make the situation even worse).
  696. {
  697.     mExitReason = aExitReason;
  698.     bool terminate_afterward = aBuf && !*aBuf;
  699.     if (aBuf && *aBuf)
  700.     {
  701.         char buf[1024];
  702.         // No more than size-1 chars will be written and string will be terminated:
  703.         snprintf(buf, sizeof(buf), "Critical Error: %s\n\n" WILL_EXIT, aBuf);
  704.         // To avoid chance of more errors, don't use MsgBox():
  705.         MessageBox(g_hWnd, buf, g_script.mFileSpec, MB_OK | MB_SETFOREGROUND | MB_APPLMODAL);
  706.         TerminateApp(CRITICAL_ERROR); // Only after the above.
  707.     }
  708.  
  709.     // Otherwise, it's not a critical error.  Note that currently, mOnExitLabel can only be
  710.     // non-NULL if the script is in a runnable state (since registering an OnExit label requires
  711.     // that a script command has executed to do it).  If this ever changes, the !mIsReadyToExecute
  712.     // condition should be added to the below if statement:
  713.     static bool sExitLabelIsRunning = false;
  714.     if (!mOnExitLabel || sExitLabelIsRunning)  // || !mIsReadyToExecute
  715.         // In the case of sExitLabelIsRunning == true:
  716.         // There is another instance of this function beneath us on the stack.  Since we have
  717.         // been called, this is a true exit condition and we exit immediately:
  718.         TerminateApp(aExitCode);
  719.  
  720.     // Otherwise, the script contains the special RunOnExit label that we will run here instead
  721.     // of exiting.  And since it does, we know that the script is in a ready-to-execute state
  722.     // because that is the only way an OnExit label could have been defined in the first place.
  723.     // Usually, the RunOnExit subroutine will contain an Exit or ExitApp statement
  724.     // which results in a recursive call to this function, but this is not required (e.g. the
  725.     // Exit subroutine could display an "Are you sure?" prompt, and if the user chooses "No",
  726.     // the Exit sequence can be aborted by simply not calling ExitApp and letting the thread
  727.     // we create below end normally).
  728.  
  729.     // Next, save the current state of the globals so that they can be restored just prior
  730.     // to returning to our caller:
  731.     char ErrorLevel_saved[ERRORLEVEL_SAVED_SIZE];
  732.     strlcpy(ErrorLevel_saved, g_ErrorLevel->Contents(), sizeof(ErrorLevel_saved)); // Save caller's errorlevel.
  733.     global_struct global_saved;
  734.     CopyMemory(&global_saved, &g, sizeof(global_struct));
  735.     InitNewThread(0, true, true, ACT_INVALID); // Since this special thread should always run, no checking of g_MaxThreadsTotal is done before calling this.
  736.  
  737.     if (g_nFileDialogs) // See MsgSleep() for comments on this.
  738.         SetCurrentDirectory(g_WorkingDir);
  739.  
  740.     // Use g.AllowThreadToBeInterrupted to forbid any hotkeys, timers, or user defined menu items
  741.     // to interrupt.  This is mainly done for peace-of-mind (since possible interactions due to
  742.     // interruptions have not been studied) and the fact that this most users would not want this
  743.     // subroutine to be interruptible (it usually runs quickly anyway).  Another reason to make
  744.     // it non-interruptible is that some OnExit subroutines might destruct things used by the
  745.     // script's hotkeys/timers/menu items, and activating these items during the deconstruction
  746.     // would not be safe.  Finally, if a logoff or shutdown is occurring, it seems best to prevent
  747.     // timed subroutines from running -- which might take too much time and prevent the exit from
  748.     // occurring in a timely fashion.  An option can be added via the FutureUse param to make it
  749.     // interruptible if there is ever a demand for that.  UPDATE: g_AllowInterruption is now used
  750.     // instead of g.AllowThreadToBeInterrupted for two reasons:
  751.     // 1) It avoids the need to do "int mUninterruptedLineCountMax_prev = g_script.mUninterruptedLineCountMax;"
  752.     //    (Disable this item so that ExecUntil() won't automatically make our new thread uninterruptible
  753.     //    after it has executed a certain number of lines).
  754.     // 2) If the thread we're interrupting is uninterruptible, the uinterruptible timer might be
  755.     //    currently pending.  When it fires, it would make the OnExit subroutine interruptible
  756.     //    rather than the underlying subroutine.  The above fixes the first part of that problem.
  757.     //    The 2nd part is fixed by reinstating the timer when the uninterruptible thread is resumed.
  758.     //    This special handling is only necessary here -- not in other places where new threads are
  759.     //    created -- because OnExit is the only type of thread that can interrupt an uninterruptible
  760.     //    thread.
  761.     bool g_AllowInterruption_prev = g_AllowInterruption;  // Save current setting.
  762.     g_AllowInterruption = false; // Mark the thread just created above as permanently uninterruptible (i.e. until it finishes and is destroyed).
  763.  
  764.     // This addresses the 2nd part of the problem described in the above large comment:
  765.     bool uninterruptible_timer_was_pending = g_UninterruptibleTimerExists;
  766.  
  767.     // If the current quasi-thread is paused, the thread we're about to launch
  768.     // will not be, so the icon needs to be checked:
  769.     g_script.UpdateTrayIcon();
  770.  
  771.     sExitLabelIsRunning = true;
  772.     if (mOnExitLabel->Execute() == FAIL)
  773.         // If the subroutine encounters a failure condition such as a runtime error, exit immediately.
  774.         // Otherwise, there will be no way to exit the script if the subroutine fails on each attempt.
  775.         TerminateApp(aExitCode);
  776.     sExitLabelIsRunning = false;  // In case the user wanted the thread to end normally (see above).
  777.  
  778.     if (terminate_afterward)
  779.         TerminateApp(aExitCode);
  780.  
  781.     // Otherwise:
  782.     ResumeUnderlyingThread(&global_saved, ErrorLevel_saved, false);
  783.     g_AllowInterruption = g_AllowInterruption_prev;  // Restore original setting.
  784.     if (uninterruptible_timer_was_pending)
  785.         // Update: An alternative to the below would be to make the current thread interruptible
  786.         // right before the OnExit thread interrupts it, and keep it that way.
  787.         // Below macro recreates the timer if it doesn't already exists (i.e. if it fired during
  788.         // the running of the OnExit subroutine).  Although such a firing would have had
  789.         // no negative impact on the OnExit subroutine (since it's kept always-uninterruptible
  790.         // via g_AllowInterruption), reinstate the timer so that it will make the thread
  791.         // we're resuming interruptible.  The interval might not be exactly right -- since we
  792.         // don't know when the thread started -- but it seems relatively unimportant to
  793.         // worry about such accuracy given how rare and usually-inconsequential this whole
  794.         // scenario is:
  795.         SET_UNINTERRUPTIBLE_TIMER
  796.  
  797.     return OK;  // for caller convenience.
  798. }
  799.  
  800.  
  801.  
  802. void Script::TerminateApp(int aExitCode)
  803. // Note that g_script's destructor takes care of most other cleanup work, such as destroying
  804. // tray icons, menus, and unowned windows such as ToolTip.
  805. {
  806.     // We call DestroyWindow() because MainWindowProc() has left that up to us.
  807.     // DestroyWindow() will cause MainWindowProc() to immediately receive and process the
  808.     // WM_DESTROY msg, which should in turn result in any child windows being destroyed
  809.     // and other cleanup being done:
  810.     if (IsWindow(g_hWnd)) // Adds peace of mind in case WM_DESTROY was already received in some unusual way.
  811.     {
  812.         g_DestroyWindowCalled = true;
  813.         DestroyWindow(g_hWnd);
  814.     }
  815.     Hotkey::AllDestructAndExit(aExitCode);
  816. }
  817.  
  818.  
  819.  
  820. #ifdef AUTOHOTKEYSC
  821. LineNumberType Script::LoadFromFile()
  822. #else
  823. LineNumberType Script::LoadFromFile(bool aScriptWasNotspecified)
  824. #endif
  825. // Returns the number of non-comment lines that were loaded, or LOADING_FAILED on error.
  826. {
  827.     mNoHotkeyLabels = true;  // Indicate that there are no hotkey labels, since we're (re)loading the entire file.
  828.     mIsReadyToExecute = AutoExecSectionIsRunning = false;
  829.     if (!mFileSpec || !*mFileSpec) return LOADING_FAILED;
  830.  
  831. #ifndef AUTOHOTKEYSC  // When not in stand-alone mode, read an external script file.
  832.     DWORD attr = GetFileAttributes(mFileSpec);
  833.     if (attr == MAXDWORD) // File does not exist or lacking the authorization to get its attributes.
  834.     {
  835.         char buf[MAX_PATH + 256];
  836.         if (aScriptWasNotspecified) // v1.0.46.09: Give a more descriptive prompt to help users get started.
  837.         {
  838.             snprintf(buf, sizeof(buf),
  839. "To help you get started, would you like to create a sample script in the My Documents folder?\n"
  840. "\n"
  841. "Press YES to create and display the sample script.\n"
  842. "Press NO to exit.\n");
  843.         }
  844.         else // Mostly for backward compatibility, also prompt to create if an explicitly specified script doesn't exist.
  845.             snprintf(buf, sizeof(buf), "The script file \"%s\" does not exist.  Create it now?", mFileSpec);
  846.         int response = MsgBox(buf, MB_YESNO);
  847.         if (response != IDYES)
  848.             return 0;
  849.         FILE *fp2 = fopen(mFileSpec, "a");
  850.         if (!fp2)
  851.         {
  852.             MsgBox("Could not create file, perhaps because the current directory is read-only"
  853.                 " or has insufficient permissions.");
  854.             return LOADING_FAILED;
  855.         }
  856.         fputs(
  857. "; IMPORTANT INFO ABOUT GETTING STARTED: Lines that start with a\n"
  858. "; semicolon, such as this one, are comments.  They are not executed.\n"
  859. "\n"
  860. "; This script has a special filename and path because it is automatically\n"
  861. "; launched when you run the program directly.  Also, any text file whose\n"
  862. "; name ends in .ahk is associated with the program, which means that it\n"
  863. "; can be launched simply by double-clicking it.  You can have as many .ahk\n"
  864. "; files as you want, located in any folder.  You can also run more than\n"
  865. "; one ahk file simultaneously and each will get its own tray icon.\n"
  866. "\n"
  867. "; SAMPLE HOTKEYS: Below are two sample hotkeys.  The first is Win+Z and it\n"
  868. "; launches a web site in the default browser.  The second is Control+Alt+N\n"
  869. "; and it launches a new Notepad window (or activates an existing one).  To\n"
  870. "; try out these hotkeys, run AutoHotkey again, which will load this file.\n"
  871. "\n"
  872. "#z::Run www.autohotkey.com\n"
  873. "\n"
  874. "^!n::\n"
  875. "IfWinExist Untitled - Notepad\n"
  876. "\tWinActivate\n"
  877. "else\n"
  878. "\tRun Notepad\n"
  879. "return\n"
  880. "\n"
  881. "\n"
  882. "; Note: From now on whenever you run AutoHotkey directly, this script\n"
  883. "; will be loaded.  So feel free to customize it to suit your needs.\n"
  884. "\n"
  885. "; Please read the QUICK-START TUTORIAL near the top of the help file.\n"
  886. "; It explains how to perform common automation tasks such as sending\n"
  887. "; keystrokes and mouse clicks.  It also explains more about hotkeys.\n"
  888. "\n"
  889. , fp2);
  890.         fclose(fp2);
  891.         // One or both of the below would probably fail -- at least on Win95 -- if mFileSpec ever
  892.         // has spaces in it (since it's passed as the entire param string).  So enclose the filename
  893.         // in double quotes.  I don't believe the directory needs to be in double quotes since it's
  894.         // a separate field within the CreateProcess() and ShellExecute() structures:
  895.         snprintf(buf, sizeof(buf), "\"%s\"", mFileSpec);
  896.         if (!ActionExec("edit", buf, mFileDir, false))
  897.             if (!ActionExec("Notepad.exe", buf, mFileDir, false))
  898.             {
  899.                 MsgBox("Can't open script."); // Short msg since so rare.
  900.                 return LOADING_FAILED;
  901.             }
  902.         // future: have it wait for the process to close, then try to open the script again:
  903.         return 0;
  904.     }
  905. #endif
  906.  
  907.     // v1.0.42: Placeholder to use in place of a NULL label to simplify code in some places.
  908.     // This must be created before loading the script because it's relied upon when creating
  909.     // hotkeys to provide an alternative to having a NULL label. It will be given a non-NULL
  910.     // mJumpToLine further down.
  911.     if (   !(mPlaceholderLabel = new Label(""))   ) // Not added to linked list since it's never looked up.
  912.         return LOADING_FAILED;
  913.  
  914.     // Load the main script file.  This will also load any files it includes with #Include.
  915.     if (   LoadIncludedFile(mFileSpec, false, false) != OK
  916.         || !AddLine(ACT_EXIT) // Fix for v1.0.47.04: Add an Exit because otherwise, a script that ends in an IF-statement will crash in PreparseBlocks() because PreparseBlocks() expects every IF-statements mNextLine to be non-NULL (helps loading performance too).
  917.         || !PreparseBlocks(mFirstLine)   ) // Must preparse the blocks before preparsing the If/Else's further below because If/Else may rely on blocks.
  918.         return LOADING_FAILED; // Error was already displayed by the above calls.
  919.     // ABOVE: In v1.0.47, the above may have auto-included additional files from the userlib/stdlib.
  920.     // That's why the above is done prior to adding the EXIT lines and other things below.
  921.  
  922. #ifndef AUTOHOTKEYSC
  923.     if (mIncludeLibraryFunctionsThenExit)
  924.     {
  925.         fclose(mIncludeLibraryFunctionsThenExit);
  926.         return 0; // Tell our caller to do a normal exit.
  927.     }
  928. #endif
  929.  
  930.     // v1.0.35.11: Restore original working directory so that changes made to it by the above (via
  931.     // "#Include C:\Scripts" or "#Include %A_ScriptDir%" or even stdlib/userlib) do not affect the
  932.     // script's runtime working directory.  This preserves the flexibility of having a startup-determined
  933.     // working directory for the script's runtime (i.e. it seems best that the mere presence of
  934.     // "#Include NewDir" should not entirely eliminate this flexibility).
  935.     SetCurrentDirectory(g_WorkingDirOrig); // g_WorkingDirOrig previously set by WinMain().
  936.  
  937.     // Rather than do this, which seems kinda nasty if ever someday support same-line
  938.     // else actions such as "else return", just add two EXITs to the end of every script.
  939.     // That way, if the first EXIT added accidentally "corrects" an actionless ELSE
  940.     // or IF, the second one will serve as the anchoring end-point (mRelatedLine) for that
  941.     // IF or ELSE.  In other words, since we never want mRelatedLine to be NULL, this should
  942.     // make absolutely sure of that:
  943.     //if (mLastLine->mActionType == ACT_ELSE ||
  944.     //    ACT_IS_IF(mLastLine->mActionType)
  945.     //    ...
  946.     // Second ACT_EXIT: even if the last line of the script is already "exit", always add
  947.     // another one in case the script ends in a label.  That way, every label will have
  948.     // a non-NULL target, which simplifies other aspects of script execution.
  949.     // Making sure that all scripts end with an EXIT ensures that if the script
  950.     // file ends with ELSEless IF or an ELSE, that IF's or ELSE's mRelatedLine
  951.     // will be non-NULL, which further simplifies script execution.
  952.     // Not done since it's number doesn't much matter: ++mCombinedLineNumber;
  953.     ++mCombinedLineNumber;  // So that the EXITs will both show up in ListLines as the line # after the last physical one in the script.
  954.     if (!(AddLine(ACT_EXIT) && AddLine(ACT_EXIT))) // Second exit guaranties non-NULL mRelatedLine(s).
  955.         return LOADING_FAILED;
  956.     mPlaceholderLabel->mJumpToLine = mLastLine; // To follow the rule "all labels should have a non-NULL line before the script starts running".
  957.  
  958.     if (!PreparseIfElse(mFirstLine))
  959.         return LOADING_FAILED; // Error was already displayed by the above calls.
  960.  
  961.     // Use FindOrAdd, not Add, because the user may already have added it simply by
  962.     // referring to it in the script:
  963.     if (   !(g_ErrorLevel = FindOrAddVar("ErrorLevel"))   )
  964.         return LOADING_FAILED; // Error.  Above already displayed it for us.
  965.     // Initialize the var state to zero right before running anything in the script:
  966.     g_ErrorLevel->Assign(ERRORLEVEL_NONE);
  967.  
  968.     // Initialize the random number generator:
  969.     // Note: On 32-bit hardware, the generator module uses only 2506 bytes of static
  970.     // data, so it doesn't seem worthwhile to put it in a class (so that the mem is
  971.     // only allocated on first use of the generator).  For v1.0.24, _ftime() is not
  972.     // used since it could be as large as 0.5 KB of non-compressed code.  A simple call to
  973.     // GetSystemTimeAsFileTime() seems just as good or better, since it produces
  974.     // a FILETIME, which is "the number of 100-nanosecond intervals since January 1, 1601."
  975.     // Use the low-order DWORD since the high-order one rarely changes.  If my calculations are correct,
  976.     // the low-order 32-bits traverses its full 32-bit range every 7.2 minutes, which seems to make
  977.     // using it as a seed superior to GetTickCount for most purposes.
  978.     RESEED_RANDOM_GENERATOR;
  979.  
  980.     return mLineCount; // The count of runnable lines that were loaded, which might be zero.
  981. }
  982.  
  983.  
  984.  
  985. bool IsFunction(char *aBuf, bool *aPendingFunctionHasBrace = NULL)
  986. // Helper function for LoadIncludedFile().
  987. // Caller passes in an aBuf containing a candidate line such as "function(x, y)"
  988. // Caller has ensured that aBuf is rtrim'd.
  989. // Caller should pass NULL for aPendingFunctionHasBrace to indicate that function definitions (open-brace
  990. // on same line as function) are not allowed.  When non-NULL *and* aBuf is a function call/def,
  991. // *aPendingFunctionHasBrace is set to true if a brace is present at the end, or false otherwise.
  992. // In addition, any open-brace is removed from aBuf in this mode.
  993. {
  994.     char *action_end = StrChrAny(aBuf, EXPR_ALL_SYMBOLS EXPR_ILLEGAL_CHARS);
  995.     // Can't be a function definition or call without an open-parenthesis as first char found by the above.
  996.     // In addition, if action_end isn't NULL, that confirms that the string in aBuf prior to action_end contains
  997.     // no spaces, tabs, colons, or equal-signs.  As a result, it can't be:
  998.     // 1) a hotstring, since they always start with at least one colon that would be caught immediately as 
  999.     //    first-expr-char-is-not-open-parenthesis by the above.
  1000.     // 2) Any kind of math or assignment, such as var:=(x+y) or var+=(x+y).
  1001.     // The only things it could be other than a function call or function definition are:
  1002.     // Normal label that ends in single colon but contains an open-parenthesis prior to the colon, e.g. Label(x):
  1003.     // Single-line hotkey such as KeyName::MsgBox.  But since '(' isn't valid inside KeyName, this isn't a concern.
  1004.     // In addition, note that it isn't necessary to check for colons that lie outside of quoted strings because
  1005.     // we're only interested in the first "word" of aBuf: If this is indeed a function call or definition, what
  1006.     // lies to the left of its first open-parenthesis can't contain any colons anyway because the above would
  1007.     // have caught it as first-expr-char-is-not-open-parenthesis.  In other words, there's no way for a function's
  1008.     // opening parenthesis to occur after a legtimate/quoted colon or double-colon in its parameters.
  1009.     // v1.0.40.04: Added condition "action_end != aBuf" to allow a hotkey or remap or hotkey such as
  1010.     // such as "(::" to work even if it ends in a close-parenthesis such as "(::)" or "(::MsgBox )"
  1011.     if (   !(action_end && *action_end == '(' && action_end != aBuf
  1012.         && (action_end - aBuf != 2 || strnicmp(aBuf, "IF", 2)))
  1013.         || action_end[1] == ':'   ) // v1.0.44.07: This prevents "$(::fn_call()" from being seen as a function-call vs. hotkey-with-call.  For simplicity and due to rarity, omit_leading_whitespace() isn't called; i.e. assumes that the colon immediate follows the '('.
  1014.         return false;
  1015.     char *aBuf_last_char = action_end + strlen(action_end) - 1; // Above has already ensured that action_end is "(...".
  1016.     if (aPendingFunctionHasBrace) // Caller specified that an optional open-brace may be present at the end of aBuf.
  1017.     {
  1018.         if (*aPendingFunctionHasBrace = (*aBuf_last_char == '{')) // Caller has ensured that aBuf is rtrim'd.
  1019.         {
  1020.             *aBuf_last_char = '\0'; // For the caller, remove it from further consideration.
  1021.             aBuf_last_char = aBuf + rtrim(aBuf, aBuf_last_char - aBuf) - 1; // Omit trailing whitespace too.
  1022.         }
  1023.     }
  1024.     return *aBuf_last_char == ')'; // This last check avoids detecting a label such as "Label(x):" as a function.
  1025.     // Also, it seems best never to allow if(...) to be a function call, even if it's blank inside such as if().
  1026.     // In addition, it seems best not to allow if(...) to ever be a function definition since such a function
  1027.     // could never be called as ACT_EXPRESSION since it would be seen as an IF-stmt instead.
  1028. }
  1029.  
  1030.  
  1031.  
  1032. ResultType Script::LoadIncludedFile(char *aFileSpec, bool aAllowDuplicateInclude, bool aIgnoreLoadFailure)
  1033. // Returns OK or FAIL.
  1034. // Below: Use double-colon as delimiter to set these apart from normal labels.
  1035. // The main reason for this is that otherwise the user would have to worry
  1036. // about a normal label being unintentionally valid as a hotkey, e.g.
  1037. // "Shift:" might be a legitimate label that the user forgot is also
  1038. // a valid hotkey:
  1039. #define HOTKEY_FLAG "::"
  1040. #define HOTKEY_FLAG_LENGTH 2
  1041. {
  1042.     if (!aFileSpec || !*aFileSpec) return FAIL;
  1043.  
  1044. #ifndef AUTOHOTKEYSC
  1045.     if (Line::sSourceFileCount >= Line::sMaxSourceFiles)
  1046.     {
  1047.         if (Line::sSourceFileCount >= ABSOLUTE_MAX_SOURCE_FILES)
  1048.             return ScriptError("Too many includes."); // Short msg since so rare.
  1049.         int new_max;
  1050.         if (Line::sMaxSourceFiles)
  1051.         {
  1052.             new_max = 2*Line::sMaxSourceFiles;
  1053.             if (new_max > ABSOLUTE_MAX_SOURCE_FILES)
  1054.                 new_max = ABSOLUTE_MAX_SOURCE_FILES;
  1055.         }
  1056.         else
  1057.             new_max = 100;
  1058.         // For simplicity and due to rarity of every needing to, expand by reallocating the array.
  1059.         // Use a temp var. because realloc() returns NULL on failure but leaves original block allocated.
  1060.         char **realloc_temp = (char **)realloc(Line::sSourceFile, new_max*sizeof(char *)); // If passed NULL, realloc() will do a malloc().
  1061.         if (!realloc_temp)
  1062.             return ScriptError(ERR_OUTOFMEM); // Short msg since so rare.
  1063.         Line::sSourceFile = realloc_temp;
  1064.         Line::sMaxSourceFiles = new_max;
  1065.     }
  1066.  
  1067.     char full_path[MAX_PATH];
  1068. #endif
  1069.  
  1070.     // Keep this var on the stack due to recursion, which allows newly created lines to be given the
  1071.     // correct file number even when some #include's have been encountered in the middle of the script:
  1072.     int source_file_index = Line::sSourceFileCount;
  1073.  
  1074.     if (!source_file_index)
  1075.         // Since this is the first source file, it must be the main script file.  Just point it to the
  1076.         // location of the filespec already dynamically allocated:
  1077.         Line::sSourceFile[source_file_index] = mFileSpec;
  1078. #ifndef AUTOHOTKEYSC  // The "else" part below should never execute for compiled scripts since they never include anything (other than the main/combined script).
  1079.     else
  1080.     {
  1081.         // Get the full path in case aFileSpec has a relative path.  This is done so that duplicates
  1082.         // can be reliably detected (we only want to avoid including a given file more than once):
  1083.         char *filename_marker;
  1084.         GetFullPathName(aFileSpec, sizeof(full_path), full_path, &filename_marker);
  1085.         // Check if this file was already included.  If so, it's not an error because we want
  1086.         // to support automatic "include once" behavior.  So just ignore repeats:
  1087.         if (!aAllowDuplicateInclude)
  1088.             for (int f = 0; f < source_file_index; ++f) // Here, source_file_index==Line::sSourceFileCount
  1089.                 if (!lstrcmpi(Line::sSourceFile[f], full_path)) // Case insensitive like the file system (testing shows that "โ”€" == "ฮฃ" in the NTFS, which is hopefully how lstrcmpi works regardless of locale).
  1090.                     return OK;
  1091.         // The file is added to the list further below, after the file has been opened, in case the
  1092.         // opening fails and aIgnoreLoadFailure==true.
  1093.     }
  1094. #endif
  1095.  
  1096.     UCHAR *script_buf = NULL;  // Init for the case when the buffer isn't used (non-standalone mode).
  1097.     ULONG nDataSize = 0;
  1098.  
  1099.     // <buf> should be no larger than LINE_SIZE because some later functions rely upon that:
  1100.     char msg_text[MAX_PATH + 256], buf1[LINE_SIZE], buf2[LINE_SIZE], suffix[16], pending_function[LINE_SIZE] = "";
  1101.     char *buf = buf1, *next_buf = buf2; // Oscillate between bufs to improve performance (avoids memcpy from buf2 to buf1).
  1102.     size_t buf_length, next_buf_length, suffix_length;
  1103.     bool pending_function_has_brace;
  1104.  
  1105. #ifndef AUTOHOTKEYSC
  1106.     // Future: might be best to put a stat() or GetFileAttributes() in here for better handling.
  1107.     FILE *fp = fopen(aFileSpec, "r");
  1108.     if (!fp)
  1109.     {
  1110.         if (aIgnoreLoadFailure)
  1111.             return OK;
  1112.         snprintf(msg_text, sizeof(msg_text), "%s file \"%s\" cannot be opened."
  1113.             , Line::sSourceFileCount > 0 ? "#Include" : "Script", aFileSpec);
  1114.         MsgBox(msg_text);
  1115.         return FAIL;
  1116.     }
  1117.     // v1.0.40.11: Otherwise, check if the first three bytes of the file are the UTF-8 BOM marker (and if
  1118.     // so omit them from further consideration).  Apps such as Notepad, WordPad, and Word all insert this
  1119.     // marker if the file is saved in UTF-8 format.  This omits such markers from both the main script and
  1120.     // any files it includes via #Include.
  1121.     // NOTE: To save code size, any UTF-8 BOM bytes at the beginning of a compiled script have already been
  1122.     // stripped out by the script compiler.  Thus, there is no need to check for them in the AUTOHOTKEYSC
  1123.     // section further below.
  1124.     if (fgets(buf, 4, fp)) // Success (the fourth character is the terminator).
  1125.     {
  1126.         if (strcmp(buf, "โˆฉโ•—โ”"))  // UTF-8 BOM marker is NOT present.
  1127.             rewind(fp);  // Go back to the beginning so that the first three bytes aren't omitted during loading.
  1128.             // The code size of rewind() has been checked and it seems very tiny.
  1129.     }
  1130.     //else file read error or EOF, let a later section handle it.
  1131.  
  1132.     // This is done only after the file has been successfully opened in case aIgnoreLoadFailure==true:
  1133.     if (source_file_index > 0)
  1134.         Line::sSourceFile[source_file_index] = SimpleHeap::Malloc(full_path);
  1135.     //else the first file was already taken care of by another means.
  1136.  
  1137. #else // Stand-alone mode (there are no include files in this mode since all of them were merged into the main script at the time of compiling).
  1138.     HS_EXEArc_Read oRead;
  1139.     // AutoIt3: Open the archive in this compiled exe.
  1140.     // Jon gave me some details about why a password isn't needed: "The code in those libararies will
  1141.     // only allow files to be extracted from the exe is is bound to (i.e the script that it was
  1142.     // compiled with).  There are various checks and CRCs to make sure that it can't be used to read
  1143.     // the files from any other exe that is passed."
  1144.     if (oRead.Open(aFileSpec, "") != HS_EXEARC_E_OK)
  1145.     {
  1146.         MsgBox(ERR_EXE_CORRUPTED, 0, aFileSpec); // Usually caused by virus corruption.
  1147.         return FAIL;
  1148.     }
  1149.     // AutoIt3: Read the script (the func allocates the memory for the buffer :) )
  1150.     if (oRead.FileExtractToMem(">AUTOHOTKEY SCRIPT<", &script_buf, &nDataSize) == HS_EXEARC_E_OK)
  1151.         mCompiledHasCustomIcon = false;
  1152.     else if (oRead.FileExtractToMem(">AHK WITH ICON<", &script_buf, &nDataSize) == HS_EXEARC_E_OK)
  1153.         mCompiledHasCustomIcon = true;
  1154.     else
  1155.     {
  1156.         oRead.Close();                            // Close the archive
  1157.         MsgBox("Could not extract script from EXE.", 0, aFileSpec);
  1158.         return FAIL;
  1159.     }
  1160.     UCHAR *script_buf_marker = script_buf;  // "marker" will track where we are in the mem. file as we read from it.
  1161.  
  1162.     // Must cast to int to avoid loss of negative values:
  1163.     #define SCRIPT_BUF_SPACE_REMAINING ((int)(nDataSize - (script_buf_marker - script_buf)))
  1164.     int script_buf_space_remaining, max_chars_to_read; // script_buf_space_remaining must be an int to detect negatives.
  1165.  
  1166.     // AutoIt3: We have the data in RAW BINARY FORM, the script is a text file, so
  1167.     // this means that instead of a newline character, there may also be carridge
  1168.     // returns 0x0d 0x0a (\r\n)
  1169.     HS_EXEArc_Read *fp = &oRead;  // To help consolidate the code below.
  1170. #endif
  1171.  
  1172.     ++Line::sSourceFileCount;
  1173.  
  1174.     // File is now open, read lines from it.
  1175.  
  1176.     char *hotkey_flag, *cp, *cp1, *action_end, *hotstring_start, *hotstring_options;
  1177.     Hotkey *hk;
  1178.     LineNumberType pending_function_line_number, saved_line_number;
  1179.     HookActionType hook_action;
  1180.     bool is_label, suffix_has_tilde, in_comment_section, hotstring_options_all_valid;
  1181.  
  1182.     // For the remap mechanism, e.g. a::b
  1183.     int remap_stage;
  1184.     vk_type remap_source_vk, remap_dest_vk = 0; // Only dest is initialized to enforce the fact that it is the flag/signal to indicate whether remapping is in progress.
  1185.     char remap_source[32], remap_dest[32], remap_dest_modifiers[8]; // Must fit the longest key name (currently Browser_Favorites [17]), but buffer overflow is checked just in case.
  1186.     char *extra_event;
  1187.     bool remap_source_is_mouse, remap_dest_is_mouse, remap_keybd_to_mouse;
  1188.  
  1189.     // For the line continuation mechanism:
  1190.     bool do_ltrim, do_rtrim, literal_escapes, literal_derefs, literal_delimiters
  1191.         , has_continuation_section, is_continuation_line;
  1192.     #define CONTINUATION_SECTION_WITHOUT_COMMENTS 1 // MUST BE 1 because it's the default set by anything that's boolean-true.
  1193.     #define CONTINUATION_SECTION_WITH_COMMENTS    2 // Zero means "not in a continuation section".
  1194.     int in_continuation_section;
  1195.  
  1196.     char *next_option, *option_end, orig_char, one_char_string[2], two_char_string[3]; // Line continuation mechanism's option parsing.
  1197.     one_char_string[1] = '\0';  // Pre-terminate these to simplify code later below.
  1198.     two_char_string[2] = '\0';  //
  1199.     int continuation_line_count;
  1200.  
  1201.     #define MAX_FUNC_VAR_EXCEPTIONS 2000
  1202.     Var *func_exception_var[MAX_FUNC_VAR_EXCEPTIONS];
  1203.  
  1204.     // Init both for main file and any included files loaded by this function:
  1205.     mCurrFileIndex = source_file_index;  // source_file_index is kept on the stack due to recursion (from #include).
  1206.  
  1207. #ifdef AUTOHOTKEYSC
  1208.     // -1 (MAX_UINT in this case) to compensate for the fact that there is a comment containing
  1209.     // the version number added to the top of each compiled script:
  1210.     LineNumberType phys_line_number = -1;
  1211.     // For compiled scripts, limit the number of characters to read to however many remain in the memory
  1212.     // file or the size of the buffer, whichever is less.
  1213.     script_buf_space_remaining = SCRIPT_BUF_SPACE_REMAINING;  // Resolve macro only once, for performance.
  1214.     max_chars_to_read = (LINE_SIZE - 1 < script_buf_space_remaining) ? LINE_SIZE - 1
  1215.         : script_buf_space_remaining;
  1216.     buf_length = GetLine(buf, max_chars_to_read, 0, script_buf_marker);
  1217. #else
  1218.     LineNumberType phys_line_number = 0;
  1219.     buf_length = GetLine(buf, LINE_SIZE - 1, 0, fp);
  1220. #endif
  1221.  
  1222.     if (in_comment_section = !strncmp(buf, "/*", 2))
  1223.     {
  1224.         // Fixed for v1.0.35.08. Must reset buffer to allow a script's first line to be "/*".
  1225.         *buf = '\0';
  1226.         buf_length = 0;
  1227.     }
  1228.  
  1229.     while (buf_length != -1)  // Compare directly to -1 since length is unsigned.
  1230.     {
  1231.         // For each whole line (a line with continuation section is counted as only a single line
  1232.         // for the purpose of this outer loop).
  1233.  
  1234.         // Keep track of this line's *physical* line number within its file for A_LineNumber and
  1235.         // error reporting purposes.  This must be done only in the outer loop so that it tracks
  1236.         // the topmost line of any set of lines merged due to continuation section/line(s)..
  1237.         mCombinedLineNumber = phys_line_number + 1;
  1238.  
  1239.         // This must be reset for each iteration because a prior iteration may have changed it, even
  1240.         // indirectly by calling something that changed it:
  1241.         mCurrLine = NULL;  // To signify that we're in transition, trying to load a new one.
  1242.  
  1243.         // v1.0.44.13: An additional call to IsDirective() is now made up here so that #CommentFlag affects
  1244.         // the line beneath it the same way as other lines (#EscapeChar et. al. didn't have this bug).
  1245.         // It's best not to process ALL directives up here because then they would no longer support a
  1246.         // continuation section beneath them (and possibly other drawbacks because it was never thoroughly
  1247.         // tested).
  1248.         if (!strnicmp(buf, "#CommentFlag", 12)) // Have IsDirective() process this now (it will also process it again later, which is harmless).
  1249.             if (IsDirective(buf) == FAIL) // IsDirective() already displayed the error.
  1250.                 return CloseAndReturn(fp, script_buf, FAIL);
  1251.  
  1252.         // Read in the next line (if that next line is the start of a continuation secttion, append
  1253.         // it to the line currently being processed:
  1254.         for (has_continuation_section = false, in_continuation_section = 0;;)
  1255.         {
  1256.             // This increment relies on the fact that this loop always has at least one iteration:
  1257.             ++phys_line_number; // Tracks phys. line number in *this* file (independent of any recursion caused by #Include).
  1258. #ifdef AUTOHOTKEYSC
  1259.             // See similar section above for comments about the following:
  1260.             script_buf_space_remaining = SCRIPT_BUF_SPACE_REMAINING;  // Resolve macro only once, for performance.
  1261.             max_chars_to_read = (LINE_SIZE - 1 < script_buf_space_remaining) ? LINE_SIZE - 1
  1262.                 : script_buf_space_remaining;
  1263.             next_buf_length = GetLine(next_buf, max_chars_to_read, in_continuation_section, script_buf_marker);
  1264. #else
  1265.             next_buf_length = GetLine(next_buf, LINE_SIZE - 1, in_continuation_section, fp);
  1266. #endif
  1267.             if (next_buf_length && next_buf_length != -1) // Prevents infinite loop when file ends with an unclosed "/*" section.  Compare directly to -1 since length is unsigned.
  1268.             {
  1269.                 if (in_comment_section) // Look for the uncomment-flag.
  1270.                 {
  1271.                     if (!strncmp(next_buf, "*/", 2))
  1272.                     {
  1273.                         in_comment_section = false;
  1274.                         next_buf_length -= 2; // Adjust for removal of /* from the beginning of the string.
  1275.                         memmove(next_buf, next_buf + 2, next_buf_length + 1);  // +1 to include the string terminator.
  1276.                         next_buf_length = ltrim(next_buf, next_buf_length); // Get rid of any whitespace that was between the comment-end and remaining text.
  1277.                         if (!*next_buf) // The rest of the line is empty, so it was just a naked comment-end.
  1278.                             continue;
  1279.                     }
  1280.                     else
  1281.                         continue;
  1282.                 }
  1283.                 else if (!in_continuation_section && !strncmp(next_buf, "/*", 2))
  1284.                 {
  1285.                     in_comment_section = true;
  1286.                     continue; // It's now commented out, so the rest of this line is ignored.
  1287.                 }
  1288.             }
  1289.  
  1290.             if (in_comment_section) // Above has incremented and read the next line, which is everything needed while inside /* .. */
  1291.             {
  1292.                 if (next_buf_length == -1) // Compare directly to -1 since length is unsigned.
  1293.                     break; // By design, it's not an error.  This allows "/*" to be used to comment out the bottommost portion of the script without needing a matching "*/".
  1294.                 // Otherwise, continue reading lines so that they can be merged with the line above them
  1295.                 // if they qualify as continuation lines.
  1296.                 continue;
  1297.             }
  1298.  
  1299.             if (!in_continuation_section) // This is either the first iteration or the line after the end of a previous continuation section.
  1300.             {
  1301.                 // v1.0.38.06: The following has been fixed to exclude "(:" and "(::".  These should be
  1302.                 // labels/hotkeys, not the start of a contination section.  In addition, a line that starts
  1303.                 // with '(' but that ends with ':' should be treated as a label because labels such as
  1304.                 // "(label):" are far more common than something obscure like a continuation section whose
  1305.                 // join character is colon, namely "(Join:".
  1306.                 if (   !(in_continuation_section = (next_buf_length != -1 && *next_buf == '(' // Compare directly to -1 since length is unsigned.
  1307.                     && next_buf[1] != ':' && next_buf[next_buf_length - 1] != ':'))   ) // Relies on short-circuit boolean order.
  1308.                 {
  1309.                     if (next_buf_length == -1)  // Compare directly to -1 since length is unsigned.
  1310.                         break;
  1311.                     if (!next_buf_length)
  1312.                         // It is permitted to have blank lines and comment lines in between the line above
  1313.                         // and any continuation section/line that might come after the end of the
  1314.                         // comment/blank lines:
  1315.                         continue;
  1316.                     // SINCE ABOVE DIDN'T BREAK/CONTINUE, NEXT_BUF IS NON-BLANK.
  1317.                     if (next_buf[next_buf_length - 1] == ':' && *next_buf != ',')
  1318.                         // With the exception of lines starting with a comma, the last character of any
  1319.                         // legitimate continuation line can't be a colon because expressions can't end
  1320.                         // in a colon. The only exception is the ternary operator's colon, but that is
  1321.                         // very rare because it requires the line after it also be a continuation line
  1322.                         // or section, which is unusual to say the least -- so much so that it might be
  1323.                         // too obscure to even document as a known limitation.  Anyway, by excluding lines
  1324.                         // that end with a colon from consideration ambiguity with normal labels
  1325.                         // and non-single-line hotkeys and hotstrings is eliminated.
  1326.                         break;
  1327.  
  1328.                     is_continuation_line = false; // Set default.
  1329.                     switch(toupper(*next_buf)) // Above has ensured *next_buf != '\0' (toupper might have problems with '\0').
  1330.                     {
  1331.                     case 'A': // "AND".
  1332.                         // See comments in the default section further below.
  1333.                         if (!strnicmp(next_buf, "and", 3) && IS_SPACE_OR_TAB_OR_NBSP(next_buf[3])) // Relies on short-circuit boolean order.
  1334.                         {
  1335.                             cp = omit_leading_whitespace(next_buf + 3);
  1336.                             // v1.0.38.06: The following was fixed to use EXPR_CORE vs. EXPR_OPERAND_TERMINATORS
  1337.                             // to properly detect a continuation line whose first char after AND/OR is "!~*&-+()":
  1338.                             if (!strchr(EXPR_CORE, *cp))
  1339.                                 // This check recognizes the following examples as NON-continuation lines by checking
  1340.                                 // that AND/OR aren't followed immediately by something that's obviously an operator:
  1341.                                 //    and := x, and = 2 (but not and += 2 since the an operand can have a unary plus/minus).
  1342.                                 // This is done for backward compatibility.  Also, it's documented that
  1343.                                 // AND/OR/NOT aren't supported as variable names inside expressions.
  1344.                                 is_continuation_line = true; // Override the default set earlier.
  1345.                         }
  1346.                         break;
  1347.                     case 'O': // "OR".
  1348.                         // See comments in the default section further below.
  1349.                         if (toupper(next_buf[1]) == 'R' && IS_SPACE_OR_TAB_OR_NBSP(next_buf[2])) // Relies on short-circuit boolean order.
  1350.                         {
  1351.                             cp = omit_leading_whitespace(next_buf + 2);
  1352.                             // v1.0.38.06: The following was fixed to use EXPR_CORE vs. EXPR_OPERAND_TERMINATORS
  1353.                             // to properly detect a continuation line whose first char after AND/OR is "!~*&-+()":
  1354.                             if (!strchr(EXPR_CORE, *cp)) // See comment in the "AND" case above.
  1355.                                 is_continuation_line = true; // Override the default set earlier.
  1356.                         }
  1357.                         break;
  1358.                     default:
  1359.                         // Desired line continuation operators:
  1360.                         // Pretty much everything, namely:
  1361.                         // +, -, *, /, //, **, <<, >>, &, |, ^, <, >, <=, >=, =, ==, <>, !=, :=, +=, -=, /=, *=, ?, :
  1362.                         // And also the following remaining unaries (i.e. those that aren't also binaries): !, ~
  1363.                         // The first line below checks for ::, ++, and --.  Those can't be continuation lines because:
  1364.                         // "::" isn't a valid operator (this also helps performance if there are many hotstrings).
  1365.                         // ++ and -- are ambiguous with an isolated line containing ++Var or --Var (and besides,
  1366.                         // wanting to use ++ to continue an expression seems extremely rare, though if there's ever
  1367.                         // demand for it, might be able to look at what lies to the right of the operator's operand
  1368.                         // -- though that would produce inconsisent continuation behavior since ++Var itself still
  1369.                         // could never be a continuation line due to ambiguity).
  1370.                         //
  1371.                         // The logic here isn't smart enough to differentiate between a leading ! or - that's
  1372.                         // meant as a continuation character and one that isn't. Even if it were, it would
  1373.                         // still be ambiguous in some cases because the author's intent isn't known; for example,
  1374.                         // the leading minus sign on the second line below is ambiguous, so will probably remain
  1375.                         // a continuation character in both v1 and v2:
  1376.                         //    x := y 
  1377.                         //    -z ? a:=1 : func() 
  1378.                         if ((*next_buf == ':' || *next_buf == '+' || *next_buf == '-') && next_buf[1] == *next_buf // See above.
  1379.                             || (*next_buf == '.' || *next_buf == '?') && !IS_SPACE_OR_TAB_OR_NBSP(next_buf[1]) // The "." and "?" operators require a space or tab after them to be legitimate.  For ".", this is done in case period is ever a legal character in var names, such as struct support.  For "?", it's done for backward compatibility since variable names can contain question marks (though "?" by itself is not considered a variable in v1.0.46).
  1380.                                 && next_buf[1] != '=' // But allow ".=" (and "?=" too for code simplicity), since ".=" is the concat-assign operator.
  1381.                             || !strchr(CONTINUATION_LINE_SYMBOLS, *next_buf)) // Line doesn't start with a continuation char.
  1382.                             break; // Leave is_continuation_line set to its default of false.
  1383.                         // Some of the above checks must be done before the next ones.
  1384.                         if (   !(hotkey_flag = strstr(next_buf, HOTKEY_FLAG))   ) // Without any "::", it can't be a hotkey or hotstring.
  1385.                         {
  1386.                             is_continuation_line = true; // Override the default set earlier.
  1387.                             break;
  1388.                         }
  1389.                         if (*next_buf == ':') // First char is ':', so it's more likely a hotstring than a hotkey.
  1390.                         {
  1391.                             // Remember that hotstrings can contain what *appear* to be quoted literal strings,
  1392.                             // so detecting whether a "::" is in a quoted/literal string in this case would
  1393.                             // be more complicated.  That's one reason this other method is used.
  1394.                             for (hotstring_options_all_valid = true, cp = next_buf + 1; *cp && *cp != ':'; ++cp)
  1395.                                 if (!IS_HOTSTRING_OPTION(*cp)) // Not a perfect test, but eliminates most of what little remaining ambiguity exists between ':' as a continuation character vs. ':' as the start of a hotstring.  It especially eliminates the ":=" operator.
  1396.                                 {
  1397.                                     hotstring_options_all_valid = false;
  1398.                                     break;
  1399.                                 }
  1400.                             if (hotstring_options_all_valid && *cp == ':') // It's almost certainly a hotstring.
  1401.                                 break; // So don't treat it as a continuation line.
  1402.                             //else it's not a hotstring but it might still be a hotkey such as ": & x::".
  1403.                             // So continue checking below.
  1404.                         }
  1405.                         // Since above didn't "break", this line isn't a hotstring but it is probably a hotkey
  1406.                         // because above already discovered that it contains "::" somewhere. So try to find out
  1407.                         // if there's anything that disqualifies this from being a hotkey, such as some
  1408.                         // expression line that contains a quoted/literal "::" (or a line starting with
  1409.                         // a comma that contains an unquoted-but-literal "::" such as for FileAppend).
  1410.                         if (*next_buf == ',')
  1411.                         {
  1412.                             cp = omit_leading_whitespace(next_buf + 1);
  1413.                             // The above has set cp to the position of the non-whitespace item to the right of
  1414.                             // this comma.  Normal (single-colon) labels can't contain commas, so only hotkey
  1415.                             // labels are sources of ambiguity.  In addition, normal labels and hotstrings have
  1416.                             // already been checked for, higher above.
  1417.                             if (   strncmp(cp, HOTKEY_FLAG, HOTKEY_FLAG_LENGTH) // It's not a hotkey such as ",::action".
  1418.                                 && strncmp(cp - 1, COMPOSITE_DELIMITER, COMPOSITE_DELIMITER_LENGTH)   ) // ...and it's not a hotkey such as ", & y::action".
  1419.                                 is_continuation_line = true; // Override the default set earlier.
  1420.                         }
  1421.                         else // First symbol in line isn't a comma but some other operator symbol.
  1422.                         {
  1423.                             // Check if the "::" found earlier appears to be inside a quoted/literal string.
  1424.                             // This check is NOT done for a line beginning with a comma since such lines
  1425.                             // can contain an unquoted-but-literal "::".  In addition, this check is done this
  1426.                             // way to detect hotkeys such as the following:
  1427.                             //   +keyname:: (and other hotkey modifier symbols such as ! and ^)
  1428.                             //   +keyname1 & keyname2::
  1429.                             //   +^:: (i.e. a modifier symbol followed by something that is a hotkey modifer and/or a hotkey suffix and/or an expression operator).
  1430.                             //   <:: and &:: (i.e. hotkeys that are also expression-continuation symbols)
  1431.                             // By contrast, expressions that qualify as continuation lines can look like:
  1432.                             //   . "xxx::yyy"
  1433.                             //   + x . "xxx::yyy"
  1434.                             // In addition, hotkeys like the following should continue to be supported regardless
  1435.                             // of how things are done here:
  1436.                             //   ^"::
  1437.                             //   . & "::
  1438.                             // Finally, keep in mind that an expression-continuation line can start with two
  1439.                             // consecutive unary operators like !! or !*. It can also start with a double-symbol
  1440.                             // operator such as <=, <>, !=, &&, ||, //, **.
  1441.                             for (cp = next_buf; cp < hotkey_flag && *cp != '"'; ++cp);
  1442.                             if (cp == hotkey_flag) // No '"' found to left of "::", so this "::" appears to be a real hotkey flag rather than part of a literal string.
  1443.                                 break; // Treat this line as a normal line vs. continuation line.
  1444.                             for (cp = hotkey_flag + HOTKEY_FLAG_LENGTH; *cp && *cp != '"'; ++cp);
  1445.                             if (*cp)
  1446.                             {
  1447.                                 // Closing quote was found so "::" is probably inside a literal string of an
  1448.                                 // expression (further checking seems unnecessary given the fairly extreme
  1449.                                 // rarity of using '"' as a key in a hotkey definition).
  1450.                                 is_continuation_line = true; // Override the default set earlier.
  1451.                             }
  1452.                             //else no closing '"' found, so this "::" probably belongs to something like +":: or
  1453.                             // . & "::.  Treat this line as a normal line vs. continuation line.
  1454.                         }
  1455.                     } // switch(toupper(*next_buf))
  1456.  
  1457.                     if (is_continuation_line)
  1458.                     {
  1459.                         if (buf_length + next_buf_length >= LINE_SIZE - 1) // -1 to account for the extra space added below.
  1460.                         {
  1461.                             ScriptError(ERR_CONTINUATION_SECTION_TOO_LONG, next_buf);
  1462.                             return CloseAndReturn(fp, script_buf, FAIL);
  1463.                         }
  1464.                         if (*next_buf != ',') // Insert space before expression operators so that built/combined expression works correctly (some operators like 'and', 'or', '.', and '?' currently require spaces on either side) and also for readability of ListLines.
  1465.                             buf[buf_length++] = ' ';
  1466.                         memcpy(buf + buf_length, next_buf, next_buf_length + 1); // Append this line to prev. and include the zero terminator.
  1467.                         buf_length += next_buf_length;
  1468.                         continue; // Check for yet more continuation lines after this one.
  1469.                     }
  1470.                     // Since above didn't continue, there is no continuation line or section.  In addition,
  1471.                     // since this line isn't blank, no further searching is needed.
  1472.                     break;
  1473.                 } // if (!in_continuation_section)
  1474.  
  1475.                 // OTHERWISE in_continuation_section != 0, so the above has found the first line of a new
  1476.                 // continuation section.
  1477.                 // "has_continuation_section" indicates whether the line we're about to construct is partially
  1478.                 // composed of continuation lines beneath it.  It's separate from continuation_line_count
  1479.                 // in case there is another continuation section immediately after/adjacent to the first one,
  1480.                 // but the second one doesn't have any lines in it:
  1481.                 has_continuation_section = true;
  1482.                 continuation_line_count = 0; // Reset for this new section.
  1483.                 // Otherwise, parse options.  First set the defaults, which can be individually overridden
  1484.                 // by any options actually present.  RTrim defaults to ON for two reasons:
  1485.                 // 1) Whitespace often winds up at the end of a lines in a text editor by accident.  In addition,
  1486.                 //    whitespace at the end of any consolidated/merged line will be rtrim'd anyway, since that's
  1487.                 //    how command parsing works.
  1488.                 // 2) Copy & paste from the forum and perhaps other web sites leaves a space at the end of each
  1489.                 //    line.  Although this behavior is probably site/browser-specific, it's a consideration.
  1490.                 do_ltrim = g_ContinuationLTrim; // Start off at global default.
  1491.                 do_rtrim = true; // Seems best to rtrim even if this line is a hotstring, since it is very rare that trailing spaces and tabs would ever be desirable.
  1492.                 // For hotstrings (which could be detected via *buf==':'), it seems best not to default the
  1493.                 // escape character (`) to be literal because the ability to have `t `r and `n inside the
  1494.                 // hotstring continuation section seems more useful/common than the ability to use the
  1495.                 // accent character by itself literally (which seems quite rare in most languages).
  1496.                 literal_escapes = false;
  1497.                 literal_derefs = false;
  1498.                 literal_delimiters = true; // This is the default even for hotstrings because although using (*buf != ':') would improve loading performance, it's not a 100% reliable way to detect hotstrings.
  1499.                 // The default is linefeed because:
  1500.                 // 1) It's the best choice for hotstrings, for which the line continuation mechanism is well suited.
  1501.                 // 2) It's good for FileAppend.
  1502.                 // 3) Minor: Saves memory in large sections by being only one character instead of two.
  1503.                 suffix[0] = '\n';
  1504.                 suffix[1] = '\0';
  1505.                 suffix_length = 1;
  1506.                 for (next_option = omit_leading_whitespace(next_buf + 1); *next_option; next_option = omit_leading_whitespace(option_end))
  1507.                 {
  1508.                     // Find the end of this option item:
  1509.                     if (   !(option_end = StrChrAny(next_option, " \t"))   )  // Space or tab.
  1510.                         option_end = next_option + strlen(next_option); // Set to position of zero terminator instead.
  1511.  
  1512.                     // Temporarily terminate to help eliminate ambiguity for words contained inside other words,
  1513.                     // such as hypothetical "Checked" inside of "CheckedGray":
  1514.                     orig_char = *option_end;
  1515.                     *option_end = '\0';
  1516.  
  1517.                     if (!strnicmp(next_option, "Join", 4))
  1518.                     {
  1519.                         next_option += 4;
  1520.                         strlcpy(suffix, next_option, sizeof(suffix)); // The word "Join" by itself will product an empty string, as documented.
  1521.                         // Passing true for the last parameter supports `s as the special escape character,
  1522.                         // which allows space to be used by itself and also at the beginning or end of a string
  1523.                         // containing other chars.
  1524.                         ConvertEscapeSequences(suffix, g_EscapeChar, true);
  1525.                         suffix_length = strlen(suffix);
  1526.                     }
  1527.                     else if (!strnicmp(next_option, "LTrim", 5))
  1528.                         do_ltrim = (next_option[5] != '0');  // i.e. Only an explicit zero will turn it off.
  1529.                     else if (!strnicmp(next_option, "RTrim", 5))
  1530.                         do_rtrim = (next_option[5] != '0');
  1531.                     else
  1532.                     {
  1533.                         // Fix for v1.0.36.01: Missing "else" above, because otherwise, the option Join`r`n
  1534.                         // would be processed above but also be processed again below, this time seeing the
  1535.                         // accent and thinking it's the signal to treat accents literally for the entire
  1536.                         // continuation section rather than as escape characters.
  1537.                         // Within this terminated option substring, allow the characters to be adjacent to
  1538.                         // improve usability:
  1539.                         for (; *next_option; ++next_option)
  1540.                         {
  1541.                             switch (*next_option)
  1542.                             {
  1543.                             case '`': // Although not using g_EscapeChar (reduces code size/complexity), #EscapeChar is still supported by continuation sections; it's just that enabling the option uses '`' rather than the custom escape-char (one reason is that that custom escape-char might be ambiguous with future/past options if it's somehing weird like an alphabetic character).
  1544.                                 literal_escapes = true;
  1545.                                 break;
  1546.                             case '%': // Same comment as above.
  1547.                                 literal_derefs = true;
  1548.                                 break;
  1549.                             case ',': // Same comment as above.
  1550.                                 literal_delimiters = false;
  1551.                                 break;
  1552.                             case 'C': // v1.0.45.03: For simplicity, anything that begins with "C" is enough to
  1553.                             case 'c': // identify it as the option to allow comments in the section.
  1554.                                 in_continuation_section = CONTINUATION_SECTION_WITH_COMMENTS; // Override the default, which is boolean true (i.e. 1).
  1555.                                 break;
  1556.                             }
  1557.                         }
  1558.                     }
  1559.  
  1560.                     // If the item was not handled by the above, ignore it because it is unknown.
  1561.  
  1562.                     *option_end = orig_char; // Undo the temporary termination.
  1563.  
  1564.                 } // for() each item in option list
  1565.  
  1566.                 continue; // Now that the open-parenthesis of this continuation section has been processed, proceed to the next line.
  1567.             } // if (!in_continuation_section)
  1568.  
  1569.             // Since above didn't "continue", we're in the continuation section and thus next_buf contains
  1570.             // either a line to be appended onto buf or the closing parenthesis of this continuation section.
  1571.             if (next_buf_length == -1) // Compare directly to -1 since length is unsigned.
  1572.             {
  1573.                 ScriptError(ERR_MISSING_CLOSE_PAREN, buf);
  1574.                 return CloseAndReturn(fp, script_buf, FAIL);
  1575.             }
  1576.             if (next_buf_length == -2) // v1.0.45.03: Special flag that means "this is a commented-out line to be
  1577.                 continue;              // entirely omitted from the continuation section." Compare directly to -2 since length is unsigned.
  1578.  
  1579.             if (*next_buf == ')')
  1580.             {
  1581.                 in_continuation_section = 0; // Facilitates back-to-back continuation sections and proper incrementing of phys_line_number.
  1582.                 next_buf_length = rtrim(next_buf); // Done because GetLine() wouldn't have done it due to have told it we're in a continuation section.
  1583.                 // Anything that lies to the right of the close-parenthesis gets appended verbatim, with
  1584.                 // no trimming (for flexibility) and no options-driven translation:
  1585.                 cp = next_buf + 1;  // Use temp var cp to avoid altering next_buf (for maintainability).
  1586.                 --next_buf_length;  // This is now the length of cp, not next_buf.
  1587.             }
  1588.             else
  1589.             {
  1590.                 cp = next_buf;
  1591.                 // The following are done in this block only because anything that comes after the closing
  1592.                 // parenthesis (i.e. the block above) is exempt from translations and custom trimming.
  1593.                 // This means that commas are always delimiters and percent signs are always deref symbols
  1594.                 // in the previous block.
  1595.                 if (do_rtrim)
  1596.                     next_buf_length = rtrim(next_buf, next_buf_length);
  1597.                 if (do_ltrim)
  1598.                     next_buf_length = ltrim(next_buf, next_buf_length);
  1599.                 // Escape each comma and percent sign in the body of the continuation section so that
  1600.                 // the later parsing stages will see them as literals.  Although, it's not always
  1601.                 // necessary to do this (e.g. commas in the last parameter of a command don't need to
  1602.                 // be escaped, nor do percent signs in hotstrings' auto-replace text), the settings
  1603.                 // are applied unconditionally because:
  1604.                 // 1) Determining when its safe to omit the translation would add a lot of code size and complexity.
  1605.                 // 2) The translation doesn't affect the functionality of the script since escaped literals
  1606.                 //    are always de-escaped at a later stage, at least for everything that's likely to matter
  1607.                 //    or that's reasonable to put into a continuation section (e.g. a hotstring's replacement text).
  1608.                 // UPDATE for v1.0.44.11: #EscapeChar, #DerefChar, #Delimiter are now supported by continuation
  1609.                 // sections because there were some requests for that in forum.
  1610.                 int replacement_count = 0;
  1611.                 if (literal_escapes) // literal_escapes must be done FIRST because otherwise it would also replace any accents added for literal_delimiters or literal_derefs.
  1612.                 {
  1613.                     one_char_string[0] = g_EscapeChar; // These strings were terminated earlier, so no need to
  1614.                     two_char_string[0] = g_EscapeChar; // do it here.  In addition, these strings must be set by
  1615.                     two_char_string[1] = g_EscapeChar; // each iteration because the #EscapeChar (and similar directives) can occur multiple times, anywhere in the script.
  1616.                     replacement_count += StrReplace(next_buf, one_char_string, two_char_string, SCS_SENSITIVE, UINT_MAX, LINE_SIZE);
  1617.                 }
  1618.                 if (literal_derefs)
  1619.                 {
  1620.                     one_char_string[0] = g_DerefChar;
  1621.                     two_char_string[0] = g_EscapeChar;
  1622.                     two_char_string[1] = g_DerefChar;
  1623.                     replacement_count += StrReplace(next_buf, one_char_string, two_char_string, SCS_SENSITIVE, UINT_MAX, LINE_SIZE);
  1624.                 }
  1625.                 if (literal_delimiters)
  1626.                 {
  1627.                     one_char_string[0] = g_delimiter;
  1628.                     two_char_string[0] = g_EscapeChar;
  1629.                     two_char_string[1] = g_delimiter;
  1630.                     replacement_count += StrReplace(next_buf, one_char_string, two_char_string, SCS_SENSITIVE, UINT_MAX, LINE_SIZE);
  1631.                 }
  1632.  
  1633.                 if (replacement_count) // Update the length if any actual replacements were done.
  1634.                     next_buf_length = strlen(next_buf);
  1635.             } // Handling of a normal line within a continuation section.
  1636.  
  1637.             // Must check the combined length only after anything that might have expanded the string above.
  1638.             if (buf_length + next_buf_length + suffix_length >= LINE_SIZE)
  1639.             {
  1640.                 ScriptError(ERR_CONTINUATION_SECTION_TOO_LONG, cp);
  1641.                 return CloseAndReturn(fp, script_buf, FAIL);
  1642.             }
  1643.  
  1644.             ++continuation_line_count;
  1645.             // Append this continuation line onto the primary line.
  1646.             // The suffix for the previous line gets written immediately prior writing this next line,
  1647.             // which allows the suffix to be omitted for the final line.  But if this is the first line,
  1648.             // No suffix is written because there is no previous line in the continuation section.
  1649.             // In addition, cp!=next_buf, this is the special line whose text occurs to the right of the
  1650.             // continuation section's closing parenthesis. In this case too, the previous line doesn't
  1651.             // get a suffix.
  1652.             if (continuation_line_count > 1 && suffix_length && cp == next_buf)
  1653.             {
  1654.                 memcpy(buf + buf_length, suffix, suffix_length + 1); // Append and include the zero terminator.
  1655.                 buf_length += suffix_length; // Must be done only after the old value of buf_length was used above.
  1656.             }
  1657.             if (next_buf_length)
  1658.             {
  1659.                 memcpy(buf + buf_length, cp, next_buf_length + 1); // Append this line to prev. and include the zero terminator.
  1660.                 buf_length += next_buf_length; // Must be done only after the old value of buf_length was used above.
  1661.             }
  1662.         } // for() each sub-line (continued line) that composes this line.
  1663.  
  1664.         // buf_length can't be -1 (though next_buf_length can) because outer loop's condition prevents it:
  1665.         if (!buf_length) // Done only after the line number increments above so that the physical line number is properly tracked.
  1666.             goto continue_main_loop; // In lieu of "continue", for performance.
  1667.  
  1668.         // Since neither of the above executed, or they did but didn't "continue",
  1669.         // buf now contains a non-commented line, either by itself or built from
  1670.         // any continuation sections/lines that might have been present.  Also note that
  1671.         // by design, phys_line_number will be greater than mCombinedLineNumber whenever
  1672.         // a continuation section/lines were used to build this combined line.
  1673.  
  1674.         // If there's a previous line waiting to be processed, its fate can now be determined based on the
  1675.         // nature of *this* line:
  1676.         if (*pending_function)
  1677.         {
  1678.             // Somewhat messy to decrement then increment later, but it's probably easier than the
  1679.             // alternatives due to the use of "continue" in some places above.  NOTE: phys_line_number
  1680.             // would not need to be decremented+incremented even if the below resulted in a recursive
  1681.             // call to us (though it doesn't currently) because line_number's only purpose is to
  1682.             // remember where this layer left off when the recursion collapses back to us.
  1683.             // Fix for v1.0.31.05: It's not enough just to decrement mCombinedLineNumber because there
  1684.             // might be some blank lines or commented-out lines between this function call/definition
  1685.             // and the line that follows it, each of which will have previously incremented mCombinedLineNumber.
  1686.             saved_line_number = mCombinedLineNumber;
  1687.             mCombinedLineNumber = pending_function_line_number;  // Done so that any syntax errors that occur during the calls below will report the correct line number.
  1688.             // Open brace means this is a function definition. NOTE: buf was already ltrimmed by GetLine().
  1689.             // Could use *g_act[ACT_BLOCK_BEGIN].Name instead of '{', but it seems too elaborate to be worth it.
  1690.             if (*buf == '{' || pending_function_has_brace) // v1.0.41: Support one-true-brace, e.g. fn(...) {
  1691.             {
  1692.                 // Note that two consecutive function definitions aren't possible:
  1693.                 // fn1()
  1694.                 // fn2()
  1695.                 // {
  1696.                 //  ...
  1697.                 // }
  1698.                 // In the above, the first would automatically be deemed a function call by means of
  1699.                 // the check higher above (by virtue of the fact that the line after it isn't an open-brace).
  1700.                 if (g.CurrentFunc)
  1701.                 {
  1702.                     // Though it might be allowed in the future -- perhaps to have nested functions have
  1703.                     // access to their parent functions' local variables, or perhaps just to improve
  1704.                     // script readability and maintainability -- it's currently not allowed because of
  1705.                     // the practice of maintaining the func_exception_var list on our stack:
  1706.                     ScriptError("Functions cannot contain functions.", pending_function);
  1707.                     return CloseAndReturn(fp, script_buf, FAIL);
  1708.                 }
  1709.                 if (!DefineFunc(pending_function, func_exception_var))
  1710.                     return CloseAndReturn(fp, script_buf, FAIL);
  1711.                 if (pending_function_has_brace) // v1.0.41: Support one-true-brace for function def, e.g. fn() {
  1712.                     if (!AddLine(ACT_BLOCK_BEGIN))
  1713.                         return CloseAndReturn(fp, script_buf, FAIL);
  1714.             }
  1715.             else // It's a function call on a line by itself, such as fn(x). It can't be if(..) because another section checked that.
  1716.             {
  1717.                 if (!ParseAndAddLine(pending_function, ACT_EXPRESSION))
  1718.                     return CloseAndReturn(fp, script_buf, FAIL);
  1719.                 mCurrLine = NULL; // Prevents showing misleading vicinity lines if the line after a function call is a syntax error.
  1720.             }
  1721.             mCombinedLineNumber = saved_line_number;
  1722.             *pending_function = '\0'; // Reset now that it's been fully handled, as an indicator for subsequent iterations.
  1723.             // Now fall through to the below so that *this* line (the one after it) will be processed.
  1724.             // Note that this line might be a pre-processor directive, label, etc. that won't actually
  1725.             // become a runtime line per se.
  1726.         } // if (*pending_function)
  1727.  
  1728.         // By doing the following section prior to checking for hotkey and hotstring labels, double colons do
  1729.         // not need to be escaped inside naked function calls and function definitions such as the following:
  1730.         // fn("::")      ; Function call.
  1731.         // fn(Str="::")  ; Function definition with default value for its param (though technically, strings other than "" aren't yet supported).
  1732.         if (IsFunction(buf, &pending_function_has_brace)) // If true, it's either a function definition or a function call (to be distinguished later).
  1733.         {
  1734.             // Defer this line until the next line comes in, which helps determine whether this line is
  1735.             // a function call vs. definition:
  1736.             strcpy(pending_function, buf);
  1737.             pending_function_line_number = mCombinedLineNumber;
  1738.             goto continue_main_loop; // In lieu of "continue", for performance.
  1739.         }
  1740.  
  1741.         // The following "examine_line" label skips the following parts above:
  1742.         // 1) IsFunction() because that's only for a function call or definition alone on a line
  1743.         //    e.g. not "if fn()" or x := fn().  Those who goto this label don't need that processing.
  1744.         // 2) The "if (*pending_function)" block: Doesn't seem applicable for the callers of this label.
  1745.         // 3) The inner loop that handles continuation sections: Not needed by the callers of this label.
  1746.         // 4) Things like the following should be skipped because callers of this label don't want the
  1747.         //    physical line number changed (which would throw off the count of lines that lie beneath a remap):
  1748.         //    mCombinedLineNumber = phys_line_number + 1;
  1749.         //    ++phys_line_number;
  1750.         // 5) "mCurrLine = NULL": Probably not necessary since it's only for error reporting.  Worst thing
  1751.         //    that could happen is that syntax errors would be thrown off, which testing shows isn't the case.
  1752. examine_line:
  1753.         // "::" alone isn't a hotstring, it's a label whose name is colon.
  1754.         // Below relies on the fact that no valid hotkey can start with a colon, since
  1755.         // ": & somekey" is not valid (since colon is a shifted key) and colon itself
  1756.         // should instead be defined as "+;::".  It also relies on short-circuit boolean:
  1757.         hotstring_start = NULL;
  1758.         hotstring_options = NULL; // Set default as "no options were specified for this hotstring".
  1759.         hotkey_flag = NULL;
  1760.         if (buf[0] == ':' && buf[1])
  1761.         {
  1762.             if (buf[1] != ':')
  1763.             {
  1764.                 hotstring_options = buf + 1; // Point it to the hotstring's option letters.
  1765.                 // The following relies on the fact that options should never contain a literal colon.
  1766.                 // ALSO, the following doesn't use IS_HOTSTRING_OPTION() for backward compatibility,
  1767.                 // performance, and because it seems seldom if ever necessary at this late a stage.
  1768.                 if (   !(hotstring_start = strchr(hotstring_options, ':'))   )
  1769.                     hotstring_start = NULL; // Indicate that this isn't a hotstring after all.
  1770.                 else
  1771.                     ++hotstring_start; // Points to the hotstring itself.
  1772.             }
  1773.             else // Double-colon, so it's a hotstring if there's more after this (but this means no options are present).
  1774.                 if (buf[2])
  1775.                     hotstring_start = buf + 2; // And leave hotstring_options at its default of NULL to indicate no options.
  1776.                 //else it's just a naked "::", which is considered to be an ordinary label whose name is colon.
  1777.         }
  1778.         if (hotstring_start)
  1779.         {
  1780.             // Find the hotstring's final double-colon by considering escape sequences from left to right.
  1781.             // This is necessary for to handles cases such as the following:
  1782.             // ::abc```::::Replacement String
  1783.             // The above hotstring translates literally into "abc`::".
  1784.             char *escaped_double_colon = NULL;
  1785.             for (cp = hotstring_start; ; ++cp)  // Increment to skip over the symbol just found by the inner for().
  1786.             {
  1787.                 for (; *cp && *cp != g_EscapeChar && *cp != ':'; ++cp);  // Find the next escape char or colon.
  1788.                 if (!*cp) // end of string.
  1789.                     break;
  1790.                 cp1 = cp + 1;
  1791.                 if (*cp == ':')
  1792.                 {
  1793.                     if (*cp1 == ':') // Found a non-escaped double-colon, so this is the right one.
  1794.                     {
  1795.                         hotkey_flag = cp++;  // Increment to have loop skip over both colons.
  1796.                         // and the continue with the loop so that escape sequences in the replacement
  1797.                         // text (if there is replacement text) are also translated.
  1798.                     }
  1799.                     // else just a single colon, or the second colon of an escaped pair (`::), so continue.
  1800.                     continue;
  1801.                 }
  1802.                 switch (*cp1)
  1803.                 {
  1804.                     // Only lowercase is recognized for these:
  1805.                     case 'a': *cp1 = '\a'; break;  // alert (bell) character
  1806.                     case 'b': *cp1 = '\b'; break;  // backspace
  1807.                     case 'f': *cp1 = '\f'; break;  // formfeed
  1808.                     case 'n': *cp1 = '\n'; break;  // newline
  1809.                     case 'r': *cp1 = '\r'; break;  // carriage return
  1810.                     case 't': *cp1 = '\t'; break;  // horizontal tab
  1811.                     case 'v': *cp1 = '\v'; break;  // vertical tab
  1812.                     // Otherwise, if it's not one of the above, the escape-char is considered to
  1813.                     // mark the next character as literal, regardless of what it is. Examples:
  1814.                     // `` -> `
  1815.                     // `:: -> :: (effectively)
  1816.                     // `; -> ;
  1817.                     // `c -> c (i.e. unknown escape sequences resolve to the char after the `)
  1818.                 }
  1819.                 // Below has a final +1 to include the terminator:
  1820.                 MoveMemory(cp, cp1, strlen(cp1) + 1);
  1821.                 // Since single colons normally do not need to be escaped, this increments one extra
  1822.                 // for double-colons to skip over the entire pair so that its second colon
  1823.                 // is not seen as part of the hotstring's final double-colon.  Example:
  1824.                 // ::ahc```::::Replacement String
  1825.                 if (*cp == ':' && *cp1 == ':')
  1826.                     ++cp;
  1827.             } // for()
  1828.             if (!hotkey_flag)
  1829.                 hotstring_start = NULL;  // Indicate that this isn't a hotstring after all.
  1830.         }
  1831.         if (!hotstring_start) // Not a hotstring (hotstring_start is checked *again* in case above block changed it; otherwise hotkeys like ": & x" aren't recognized).
  1832.         {
  1833.             // Note that there may be an action following the HOTKEY_FLAG (on the same line).
  1834.             if (hotkey_flag = strstr(buf, HOTKEY_FLAG)) // Find the first one from the left, in case there's more than 1.
  1835.             {
  1836.                 if (hotkey_flag == buf && hotkey_flag[2] == ':') // v1.0.46: Support ":::" to mean "colon is a hotkey".
  1837.                     ++hotkey_flag;
  1838.                 // v1.0.40: It appears to be a hotkey, but validate it as such before committing to processing
  1839.                 // it as a hotkey.  If it fails validation as a hotkey, treat it as a command that just happens
  1840.                 // to contain a double-colon somewhere.  This avoids the need to escape double colons in scripts.
  1841.                 // Note: Hotstrings can't suffer from this type of ambiguity because a leading colon or pair of
  1842.                 // colons makes them easier to detect.
  1843.                 cp = omit_trailing_whitespace(buf, hotkey_flag); // For maintainability.
  1844.                 orig_char = *cp;
  1845.                 *cp = '\0'; // Temporarily terminate.
  1846.                 if (!Hotkey::TextInterpret(omit_leading_whitespace(buf), NULL, false)) // Passing NULL calls it in validate-only mode.
  1847.                     hotkey_flag = NULL; // It's not a valid hotkey, so indicate that it's a command (i.e. one that contains a literal double-colon, which avoids the need to escape the double-colon).
  1848.                 *cp = orig_char; // Undo the temp. termination above.
  1849.             }
  1850.         }
  1851.  
  1852.         // Treat a naked "::" as a normal label whose label name is colon:
  1853.         if (is_label = (hotkey_flag && hotkey_flag > buf)) // It's a hotkey/hotstring label.
  1854.         {
  1855.             if (g.CurrentFunc)
  1856.             {
  1857.                 // Even if it weren't for the reasons below, the first hotkey/hotstring label in a script
  1858.                 // will end the auto-execute section with a "return".  Therefore, if this restriction here
  1859.                 // is ever removed, be sure that that extra return doesn't get put inside the function.
  1860.                 //
  1861.                 // The reason for not allowing hotkeys and hotstrings inside a function's body is that
  1862.                 // when the subroutine is launched, the hotstring/hotkey would be using the function's
  1863.                 // local variables.  But that is not appropriate and it's likely to cause problems even
  1864.                 // if it were.  It doesn't seem useful in any case.  By contrast, normal labels can
  1865.                 // safely exist inside a function body and since the body is a block, other validation
  1866.                 // ensures that a Gosub or Goto can't jump to it from outside the function.
  1867.                 ScriptError("Hotkeys/hotstrings are not allowed inside functions.", buf);
  1868.                 return CloseAndReturn(fp, script_buf, FAIL);
  1869.             }
  1870.             if (mLastLine && mLastLine->mActionType == ACT_IFWINACTIVE)
  1871.             {
  1872.                 mCurrLine = mLastLine; // To show vicinity lines.
  1873.                 ScriptError("IfWin should be #IfWin.", buf);
  1874.                 return CloseAndReturn(fp, script_buf, FAIL);
  1875.             }
  1876.             *hotkey_flag = '\0'; // Terminate so that buf is now the label itself.
  1877.             hotkey_flag += HOTKEY_FLAG_LENGTH;  // Now hotkey_flag is the hotkey's action, if any.
  1878.             if (!hotstring_start)
  1879.             {
  1880.                 ltrim(hotkey_flag); // Has already been rtrimmed by GetLine().
  1881.                 rtrim(buf); // Trim the new substring inside of buf (due to temp termination). It has already been ltrimmed.
  1882.                 cp = hotkey_flag; // Set default, conditionally overridden below (v1.0.44.07).
  1883.                 // v1.0.40: Check if this is a remap rather than hotkey:
  1884.                 if (   *hotkey_flag // This hotkey's action is on the same line as its label.
  1885.                     && (remap_source_vk = TextToVK(cp1 = Hotkey::TextToModifiers(buf, NULL)))
  1886.                     && (remap_dest_vk = hotkey_flag[1] ? TextToVK(cp = Hotkey::TextToModifiers(hotkey_flag, NULL)) : 0xFF)   ) // And the action appears to be a remap destination rather than a command.
  1887.                     // For above:
  1888.                     // Fix for v1.0.44.07: Set remap_dest_vk to 0xFF if hotkey_flag's length is only 1 because:
  1889.                     // 1) It allows a destination key that doesn't exist in the keyboard layout (such as 6::โ‰ก in
  1890.                     //    English).
  1891.                     // 2) It improves performance a little by not calling TextToVK except when the destination key
  1892.                     //    might be a mouse button or some longer key name whose actual/correct VK value is relied
  1893.                     //    upon by other places below.
  1894.                     // Fix for v1.0.40.01: Since remap_dest_vk is also used as the flag to indicate whether
  1895.                     // this line qualifies as a remap, must do it last in the statement above.  Otherwise,
  1896.                     // the statement might short-circuit and leave remap_dest_vk as non-zero even though
  1897.                     // the line shouldn't be a remap.  For example, I think a hotkey such as "x & y::return"
  1898.                     // would trigger such a bug.
  1899.                 {
  1900.                     // These will be ignored in other stages if it turns out not to be a remap later below:
  1901.                     remap_source_is_mouse = IsMouseVK(remap_source_vk);
  1902.                     remap_dest_is_mouse = IsMouseVK(remap_dest_vk);
  1903.                     remap_keybd_to_mouse = !remap_source_is_mouse && remap_dest_is_mouse;
  1904.                     snprintf(remap_source, sizeof(remap_source), "%s%s"
  1905.                         , strlen(cp1) == 1 && IsCharUpper(*cp1) ? "+" : ""  // Allow A::b to be different than a::b.
  1906.                         , buf); // Include any modifiers too, e.g. ^b::c.
  1907.                     strlcpy(remap_dest, cp, sizeof(remap_dest));      // But exclude modifiers here; they're wanted separately.
  1908.                     strlcpy(remap_dest_modifiers, hotkey_flag, sizeof(remap_dest_modifiers));
  1909.                     if (cp - hotkey_flag < sizeof(remap_dest_modifiers)) // Avoid reading beyond the end.
  1910.                         remap_dest_modifiers[cp - hotkey_flag] = '\0';   // Terminate at the proper end of the modifier string.
  1911.                     remap_stage = 0; // Init for use in the next stage.
  1912.                     // In the unlikely event that the dest key has the same name as a command, disqualify it
  1913.                     // from being a remap (as documented).  v1.0.40.05: If the destination key has any modifiers,
  1914.                     // it is unambiguously a key name rather than a command, so the switch() isn't necessary.
  1915.                     if (*remap_dest_modifiers)
  1916.                         goto continue_main_loop; // It will see that remap_dest_vk is non-zero and act accordingly.
  1917.                     switch (remap_dest_vk)
  1918.                     {
  1919.                     case VK_CONTROL: // Checked in case it was specified as "Control" rather than "Ctrl".
  1920.                     case VK_SLEEP:
  1921.                         if (StrChrAny(hotkey_flag, " \t,")) // Not using g_delimiter (reduces code size/complexity).
  1922.                             break; // Any space, tab, or enter means this is a command rather than a remap destination.
  1923.                         goto continue_main_loop; // It will see that remap_dest_vk is non-zero and act accordingly.
  1924.                     // "Return" and "Pause" as destination keys are always considered commands instead.
  1925.                     // This is documented and is done to preserve backward compatibility.
  1926.                     case VK_RETURN:
  1927.                         // v1.0.40.05: Although "Return" can't be a destination, "Enter" can be.  Must compare
  1928.                         // to "Return" not "Enter" so that things like "vk0d" (the VK of "Enter") can also be a
  1929.                         // destination key:
  1930.                         if (!stricmp(remap_dest, "Return"))
  1931.                             break;
  1932.                         goto continue_main_loop; // It will see that remap_dest_vk is non-zero and act accordingly.
  1933.                     case VK_PAUSE:  // Used for both "Pause" and "Break"
  1934.                         break;
  1935.                     default: // All other VKs are valid destinations and thus the remap is valid.
  1936.                         goto continue_main_loop; // It will see that remap_dest_vk is non-zero and act accordingly.
  1937.                     }
  1938.                     // Since above didn't goto, indicate that this is not a remap after all:
  1939.                     remap_dest_vk = 0;
  1940.                 }
  1941.             }
  1942.             // else don't trim hotstrings since literal spaces in both substrings are significant.
  1943.  
  1944.             // If this is the first hotkey label encountered, Add a return before
  1945.             // adding the label, so that the auto-exectute section is terminated.
  1946.             // Only do this if the label is a hotkey because, for example,
  1947.             // the user may want to fully execute a normal script that contains
  1948.             // no hotkeys but does contain normal labels to which the execution
  1949.             // should fall through, if specified, rather than returning.
  1950.             // But this might result in weirdness?  Example:
  1951.             //testlabel:
  1952.             // Sleep, 1
  1953.             // return
  1954.             // ^a::
  1955.             // return
  1956.             // It would put the hard return in between, which is wrong.  But in the case above,
  1957.             // the first sub shouldn't have a return unless there's a part up top that ends in Exit.
  1958.             // So if Exit is encountered before the first hotkey, don't add the return?
  1959.             // Even though wrong, the return is harmless because it's never executed?  Except when
  1960.             // falling through from above into a hotkey (which probably isn't very valid anyway)?
  1961.             // Update: Below must check if there are any true hotkey labels, not just regular labels.
  1962.             // Otherwise, a normal (non-hotkey) label in the autoexecute section would count and
  1963.             // thus the RETURN would never be added here, even though it should be:
  1964.             
  1965.             // Notes about the below macro:
  1966.             // Fix for v1.0.34: Don't point labels to this particular RETURN so that labels
  1967.             // can point to the very first hotkey or hotstring in a script.  For example:
  1968.             // Goto Test
  1969.             // Test:
  1970.             // ^!z::ToolTip Without the fix`, this is never displayed by "Goto Test".
  1971.             // UCHAR_MAX signals it not to point any pending labels to this RETURN.
  1972.             // mCurrLine = NULL -> signifies that we're in transition, trying to load a new one.
  1973.             #define CHECK_mNoHotkeyLabels \
  1974.             if (mNoHotkeyLabels)\
  1975.             {\
  1976.                 mNoHotkeyLabels = false;\
  1977.                 if (!AddLine(ACT_RETURN, NULL, UCHAR_MAX))\
  1978.                     return CloseAndReturn(fp, script_buf, FAIL);\
  1979.                 mCurrLine = NULL;\
  1980.             }
  1981.             CHECK_mNoHotkeyLabels
  1982.             // For hotstrings, the below makes the label include leading colon(s) and the full option
  1983.             // string (if any) so that the uniqueness of labels is preserved.  For example, we want
  1984.             // the following two hotstring labels to be unique rather than considered duplicates:
  1985.             // ::abc::
  1986.             // :c:abc::
  1987.             if (!AddLabel(buf, true)) // Always add a label before adding the first line of its section.
  1988.                 return CloseAndReturn(fp, script_buf, FAIL);
  1989.             hook_action = 0; // Set default.
  1990.             if (*hotkey_flag) // This hotkey's action is on the same line as its label.
  1991.             {
  1992.                 if (!hotstring_start)
  1993.                     // Don't add the alt-tabs as a line, since it has no meaning as a script command.
  1994.                     // But do put in the Return regardless, in case this label is ever jumped to
  1995.                     // via Goto/Gosub:
  1996.                     if (   !(hook_action = Hotkey::ConvertAltTab(hotkey_flag, false))   )
  1997.                         if (!ParseAndAddLine(hotkey_flag, IsFunction(hotkey_flag) ? ACT_EXPRESSION : ACT_INVALID)) // It can't be a function definition vs. call since it's a single-line hotkey.
  1998.                             return CloseAndReturn(fp, script_buf, FAIL);
  1999.                 // Also add a Return that's implicit for a single-line hotkey.  This is also
  2000.                 // done for auto-replace hotstrings in case gosub/goto is ever used to jump
  2001.                 // to their labels:
  2002.                 if (!AddLine(ACT_RETURN))
  2003.                     return CloseAndReturn(fp, script_buf, FAIL);
  2004.             }
  2005.  
  2006.             if (hotstring_start)
  2007.             {
  2008.                 if (!*hotstring_start)
  2009.                 {
  2010.                     // The following error message won't indicate the correct line number because
  2011.                     // the hotstring (as a label) does not actually exist as a line.  But it seems
  2012.                     // best to report it this way in case the hotstring is inside a #Include file,
  2013.                     // so that the correct file name and approximate line number are shown:
  2014.                     ScriptError("This hotstring is missing its abbreviation.", buf); // Display buf vs. hotkey_flag in case the line is simply "::::".
  2015.                     return CloseAndReturn(fp, script_buf, FAIL);
  2016.                 }
  2017.                 // In the case of hotstrings, hotstring_start is the beginning of the hotstring itself,
  2018.                 // i.e. the character after the second colon.  hotstring_options is NULL if no options,
  2019.                 // otherwise it's the first character in the options list (option string is not terminated,
  2020.                 // but instead ends in a colon).  hotkey_flag is blank if it's not an auto-replace
  2021.                 // hotstring, otherwise it contains the auto-replace text.
  2022.                 // v1.0.42: Unlike hotkeys, duplicate hotstrings are not detected.  This is because
  2023.                 // hotstrings are less commonly used and also because it requires more code to find
  2024.                 // hotstring duplicates (and performs a lot worse if a script has thousands of
  2025.                 // hotstrings) because of all the hotstring options.
  2026.                 if (!Hotstring::AddHotstring(mLastLabel, hotstring_options ? hotstring_options : ""
  2027.                     , hotstring_start, hotkey_flag, has_continuation_section))
  2028.                     return CloseAndReturn(fp, script_buf, FAIL);
  2029.             }
  2030.             else // It's a hotkey vs. hotstring.
  2031.             {
  2032.                 if (hk = Hotkey::FindHotkeyByTrueNature(buf, suffix_has_tilde)) // Parent hotkey found.  Add a child/variant hotkey for it.
  2033.                 {
  2034.                     if (hook_action) // suffix_has_tilde has always been ignored for these types (alt-tab hotkeys).
  2035.                     {
  2036.                         // Hotkey::Dynamic() contains logic and comments similar to this, so maintain them together.
  2037.                         // An attempt to add an alt-tab variant to an existing hotkey.  This might have
  2038.                         // merit if the intention is to make it alt-tab now but to later disable that alt-tab
  2039.                         // aspect via the Hotkey cmd to let the context-sensitive variants shine through
  2040.                         // (take effect).
  2041.                         hk->mHookAction = hook_action;
  2042.                     }
  2043.                     else
  2044.                     {
  2045.                         // Detect duplicate hotkey variants to help spot bugs in scripts.
  2046.                         if (hk->FindVariant()) // See if there's already a variant matching the current criteria (suffix_has_tilde does not make variants distinct form each other because it would require firing two hotkey IDs in response to pressing one hotkey, which currently isn't in the design).
  2047.                         {
  2048.                             mCurrLine = NULL;  // Prevents showing unhelpful vicinity lines.
  2049.                             ScriptError("Duplicate hotkey.", buf);
  2050.                             return CloseAndReturn(fp, script_buf, FAIL);
  2051.                         }
  2052.                         if (!hk->AddVariant(mLastLabel, suffix_has_tilde))
  2053.                         {
  2054.                             ScriptError(ERR_OUTOFMEM, buf);
  2055.                             return CloseAndReturn(fp, script_buf, FAIL);
  2056.                         }
  2057.                     }
  2058.                 }
  2059.                 else // No parent hotkey yet, so create it.
  2060.                     if (   !(hk = Hotkey::AddHotkey(mLastLabel, hook_action, NULL, suffix_has_tilde, false))   )
  2061.                         return CloseAndReturn(fp, script_buf, FAIL); // It already displayed the error.
  2062.             }
  2063.             goto continue_main_loop; // In lieu of "continue", for performance.
  2064.         } // if (is_label = ...)
  2065.  
  2066.         // Otherwise, not a hotkey or hotstring.  Check if it's a generic, non-hotkey label:
  2067.         if (buf[buf_length - 1] == ':') // Labels must end in a colon (buf was previously rtrimmed).
  2068.         {
  2069.             if (buf_length == 1) // v1.0.41.01: Properly handle the fact that this line consists of only a colon.
  2070.             {
  2071.                 ScriptError(ERR_UNRECOGNIZED_ACTION, buf);
  2072.                 return CloseAndReturn(fp, script_buf, FAIL);
  2073.             }
  2074.             // Labels (except hotkeys) must contain no whitespace, delimiters, or escape-chars.
  2075.             // This is to avoid problems where a legitimate action-line ends in a colon,
  2076.             // such as "WinActivate SomeTitle" and "#Include c:".
  2077.             // We allow hotkeys to violate this since they may contain commas, and since a normal
  2078.             // script line (i.e. just a plain command) is unlikely to ever end in a double-colon:
  2079.             for (cp = buf, is_label = true; *cp; ++cp)
  2080.                 if (IS_SPACE_OR_TAB(*cp) || *cp == g_delimiter || *cp == g_EscapeChar)
  2081.                 {
  2082.                     is_label = false;
  2083.                     break;
  2084.                 }
  2085.             if (is_label) // It's a generic, non-hotkey/non-hotstring label.
  2086.             {
  2087.                 // v1.0.44.04: Fixed this check by moving it after the above loop.
  2088.                 // Above has ensured buf_length>1, so it's safe to check for double-colon:
  2089.                 // v1.0.44.03: Don't allow anything that ends in "::" (other than a line consisting only
  2090.                 // of "::") to be a normal label.  Assume it's a command instead (if it actually isn't, a
  2091.                 // later stage will report it as "invalid hotkey"). This change avoids the situation in
  2092.                 // which a hotkey like ^!ฮฃ:: is seen as invalid because the current keyboard layout doesn't
  2093.                 // have a "ฮฃ" key. Without this change, if such a hotkey appears at the top of the script,
  2094.                 // its subroutine would execute immediately as a normal label, which would be especially
  2095.                 // bad if the hotkey were something like the "Shutdown" command.
  2096.                 if (buf[buf_length - 2] == ':' && buf_length > 2) // i.e. allow "::" as a normal label, but consider anything else with double-colon to be a failed-hotkey label that terminates the auto-exec section.
  2097.                 {
  2098.                     CHECK_mNoHotkeyLabels // Terminate the auto-execute section since this is a failed hotkey vs. a mere normal label.
  2099.                     snprintf(msg_text, sizeof(msg_text), "Note: The hotkey %s will not be active because it does not exist in the current keyboard layout.", buf);
  2100.                     MsgBox(msg_text);
  2101.                 }
  2102.                 buf[--buf_length] = '\0';  // Remove the trailing colon.
  2103.                 rtrim(buf, buf_length); // Has already been ltrimmed.
  2104.                 if (!AddLabel(buf, false))
  2105.                     return CloseAndReturn(fp, script_buf, FAIL);
  2106.                 goto continue_main_loop; // In lieu of "continue", for performance.
  2107.             }
  2108.         }
  2109.         // Since above didn't "goto", it's not a label.
  2110.         if (*buf == '#')
  2111.         {
  2112.             saved_line_number = mCombinedLineNumber; // Backup in case IsDirective() processes an include file, which would change mCombinedLineNumber's value.
  2113.             switch(IsDirective(buf)) // Note that it may alter the contents of buf, at least in the case of #IfWin.
  2114.             {
  2115.             case CONDITION_TRUE:
  2116.                 // Since the directive may have been a #include which called us recursively,
  2117.                 // restore the class's values for these two, which are maintained separately
  2118.                 // like this to avoid having to specify them in various calls, especially the
  2119.                 // hundreds of calls to ScriptError() and LineError():
  2120.                 mCurrFileIndex = source_file_index;
  2121.                 mCombinedLineNumber = saved_line_number;
  2122.                 goto continue_main_loop; // In lieu of "continue", for performance.
  2123.             case FAIL: // IsDirective() already displayed the error.
  2124.                 return CloseAndReturn(fp, script_buf, FAIL);
  2125.             //case CONDITION_FALSE: Do nothing; let processing below handle it.
  2126.             }
  2127.         }
  2128.         // Otherwise, treat it as a normal script line.
  2129.  
  2130.         // v1.0.41: Support the "} else {" style in one-true-brace (OTB).  As a side-effect,
  2131.         // any command, not just an else, is probably supported to the right of '}', not just "else".
  2132.         // This is undocumented because it would make for less readable scripts, and doesn't seem
  2133.         // to have much value.
  2134.         if (*buf == '}')
  2135.         {
  2136.             if (!AddLine(ACT_BLOCK_END))
  2137.                 return CloseAndReturn(fp, script_buf, FAIL);
  2138.             // The following allows the next stage to see "else" or "else {" if it's present:
  2139.             if (   !*(buf = omit_leading_whitespace(buf + 1))   )
  2140.                 goto continue_main_loop; // It's just a naked "}", so no more processing needed for this line.
  2141.             buf_length = strlen(buf); // Update for possible use below.
  2142.         }
  2143.         // First do a little special handling to support actions on the same line as their
  2144.         // ELSE, e.g.:
  2145.         // else if x = 1
  2146.         // This is done here rather than in ParseAndAddLine() because it's fairly
  2147.         // complicated to do there (already tried it) mostly due to the fact that
  2148.         // literal_map has to be properly passed in a recursive call to itself, as well
  2149.         // as properly detecting special commands that don't have keywords such as
  2150.         // IF comparisons, ACT_ASSIGN, +=, -=, etc.
  2151.         // v1.0.41: '{' was added to the line below to support no spaces inside "}else{".
  2152.         if (!(action_end = StrChrAny(buf, "\t ,{"))) // Position of first tab/space/comma/open-brace.  For simplicitly, a non-standard g_delimiter is not supported.
  2153.             action_end = buf + buf_length; // It's done this way so that ELSE can be fully handled here; i.e. that ELSE does not have to be in the list of commands recognizable by ParseAndAddLine().
  2154.         // The following method ensures that words or variables that start with "Else", e.g. ElseAction, are not
  2155.         // incorrectly detected as an Else command:
  2156.         if (strlicmp(buf, "Else", (UINT)(action_end - buf))) // It's not an ELSE. ("Else" is used vs. g_act[ACT_ELSE].Name for performance).
  2157.         {
  2158.             // It's not an ELSE.  Also, at this stage it can't be ACT_EXPRESSION (such as an isolated function call)
  2159.             // because it would have been already handled higher above.
  2160.             // v1.0.41.01: Check if there is a command/action on the same line as the '{'.  This is apparently
  2161.             // a style that some people use, and it also supports "{}" as a shorthand way of writing an empty block.
  2162.             if (*buf == '{')
  2163.             {
  2164.                 if (!AddLine(ACT_BLOCK_BEGIN))
  2165.                     return CloseAndReturn(fp, script_buf, FAIL);
  2166.                 if (   *(action_end = omit_leading_whitespace(buf + 1))   )  // There is an action to the right of the '{'.
  2167.                 {
  2168.                     mCurrLine = NULL;  // To signify that we're in transition, trying to load a new one.
  2169.                     if (!ParseAndAddLine(action_end, IsFunction(action_end) ? ACT_EXPRESSION : ACT_INVALID)) // If it's a function, it must be a call vs. a definition because a function can't be defined on the same line as an open-brace.
  2170.                         return CloseAndReturn(fp, script_buf, FAIL);
  2171.                 }
  2172.                 // Otherwise, there was either no same-line action or the same-line action was successfully added,
  2173.                 // so do nothing.
  2174.             }
  2175.             else
  2176.                 if (!ParseAndAddLine(buf))
  2177.                     return CloseAndReturn(fp, script_buf, FAIL);
  2178.         }
  2179.         else // This line is an ELSE, possibly with another command immediately after it (on the same line).
  2180.         {
  2181.             // Add the ELSE directly rather than calling ParseAndAddLine() because that function
  2182.             // would resolve escape sequences throughout the entire length of <buf>, which we
  2183.             // don't want because we wouldn't have access to the corresponding literal-map to
  2184.             // figure out the proper use of escaped characters:
  2185.             if (!AddLine(ACT_ELSE))
  2186.                 return CloseAndReturn(fp, script_buf, FAIL);
  2187.             mCurrLine = NULL;  // To signify that we're in transition, trying to load a new one.
  2188.             action_end = omit_leading_whitespace(action_end); // Now action_end is the word after the ELSE.
  2189.             if (*action_end == g_delimiter) // Allow "else, action"
  2190.                 action_end = omit_leading_whitespace(action_end + 1);
  2191.             if (*action_end && !ParseAndAddLine(action_end, IsFunction(action_end) ? ACT_EXPRESSION : ACT_INVALID)) // If it's a function, it must be a call vs. a definition because a function can't be defined on the same line as an Else.
  2192.                 return CloseAndReturn(fp, script_buf, FAIL);
  2193.             // Otherwise, there was either no same-line action or the same-line action was successfully added,
  2194.             // so do nothing.
  2195.         }
  2196.  
  2197. continue_main_loop: // This method is used in lieu of "continue" for performance and code size reduction.
  2198.         if (remap_dest_vk)
  2199.         {
  2200.             // For remapping, decided to use a "macro expansion" approach because I think it's considerably
  2201.             // smaller in code size and complexity than other approaches would be.  I originally wanted to
  2202.             // do it with the hook by changing the incoming event prior to passing it back out again (for
  2203.             // example, a::b would transform an incoming 'a' keystroke into 'b' directly without having
  2204.             // to suppress the original keystroke and simulate a new one).  Unfortunately, the low-level
  2205.             // hooks apparently do not allow this.  Here is the test that confirmed it:
  2206.             // if (event.vkCode == 'A')
  2207.             // {
  2208.             //    event.vkCode = 'B';
  2209.             //    event.scanCode = 0x30; // Or use vk_to_sc(event.vkCode).
  2210.             //    return CallNextHookEx(g_KeybdHook, aCode, wParam, lParam);
  2211.             // }
  2212.             switch (++remap_stage)
  2213.             {
  2214.             case 1: // Stage 1: Add key-down hotkey label, e.g. *LButton::
  2215.                 buf_length = sprintf(buf, "*%s::", remap_source); // Should be no risk of buffer overflow due to prior validation.
  2216.                 goto examine_line; // Have the main loop process the contents of "buf" as though it came in from the script.
  2217.             case 2: // Stage 2.
  2218.                 // Copied into a writable buffer for maintainability: AddLine() might rely on this.
  2219.                 // Also, it seems unnecessary to set press-duration to -1 even though the auto-exec section might
  2220.                 // have set it to something higher than -1 because:
  2221.                 // 1) Press-duration doesn't apply to normal remappings since they use down-only and up-only events.
  2222.                 // 2) Although it does apply to remappings such as a::B and a::^b (due to press-duration being
  2223.                 //    applied after a change to modifier state), those remappings are fairly rare and supporting
  2224.                 //    a non-negative-one press-duration (almost always 0) probably adds a degree of flexibility
  2225.                 //    that may be desirable to keep.
  2226.                 // 3) SendInput may become the predominant SendMode, so press-duration won't often be in effect anyway.
  2227.                 // 4) It has been documented that remappings use the auto-execute section's press-duration.
  2228.                 strcpy(buf, "-1"); // Does NOT need to be "-1, -1" for SetKeyDelay (see above).
  2229.                 // The primary reason for adding Key/MouseDelay -1 is to minimize the chance that a one of
  2230.                 // these hotkey threads will get buried under some other thread such as a timer, which
  2231.                 // would disrupt the remapping if #MaxThreadsPerHotkey is at its default of 1.
  2232.                 AddLine(remap_dest_is_mouse ? ACT_SETMOUSEDELAY : ACT_SETKEYDELAY, &buf, 1, NULL); // PressDuration doesn't need to be specified because it doesn't affect down-only and up-only events.
  2233.                 if (remap_keybd_to_mouse)
  2234.                 {
  2235.                     // Since source is keybd and dest is mouse, prevent keyboard auto-repeat from auto-repeating
  2236.                     // the mouse button (since that would be undesirable 90% of the time).  This is done
  2237.                     // by inserting a single extra IF-statement above the Send that produces the down-event:
  2238.                     buf_length = sprintf(buf, "if not GetKeyState(\"%s\")", remap_dest); // Should be no risk of buffer overflow due to prior validation.
  2239.                     remap_stage = 9; // Have it hit special stage 9+1 next time for code reduction purposes.
  2240.                     goto examine_line; // Have the main loop process the contents of "buf" as though it came in from the script.
  2241.                 }
  2242.                 // Otherwise, remap_keybd_to_mouse==false, so fall through to next case.
  2243.             case 10:
  2244.                 extra_event = ""; // Set default.
  2245.                 switch (remap_dest_vk)
  2246.                 {
  2247.                 case VK_LMENU:
  2248.                 case VK_RMENU:
  2249.                 case VK_MENU:
  2250.                     switch (remap_source_vk)
  2251.                     {
  2252.                     case VK_LCONTROL:
  2253.                     case VK_CONTROL:
  2254.                         extra_event = "{LCtrl up}"; // Somewhat surprisingly, this is enough to make "Ctrl::Alt" properly remap both right and left control.
  2255.                         break;
  2256.                     case VK_RCONTROL:
  2257.                         extra_event = "{RCtrl up}";
  2258.                         break;
  2259.                     // Below is commented out because its only purpose was to allow a shift key remapped to alt
  2260.                     // to be able to alt-tab.  But that wouldn't work correctly due to the need for the following
  2261.                     // hotkey, which does more harm than good by impacting the normal Alt key's ability to alt-tab
  2262.                     // (if the normal Alt key isn't remapped): *Tab::Send {Blind}{Tab}
  2263.                     //case VK_LSHIFT:
  2264.                     //case VK_SHIFT:
  2265.                     //    extra_event = "{LShift up}";
  2266.                     //    break;
  2267.                     //case VK_RSHIFT:
  2268.                     //    extra_event = "{RShift up}";
  2269.                     //    break;
  2270.                     }
  2271.                     break;
  2272.                 }
  2273.                 mCurrLine = NULL; // v1.0.40.04: Prevents showing misleading vicinity lines for a syntax-error such as %::%
  2274.                 sprintf(buf, "{Blind}%s%s{%s DownTemp}", extra_event, remap_dest_modifiers, remap_dest); // v1.0.44.05: DownTemp vs. Down. See Send's DownTemp handler for details.
  2275.                 if (!AddLine(ACT_SEND, &buf, 1, NULL)) // v1.0.40.04: Check for failure due to bad remaps such as %::%.
  2276.                     return CloseAndReturn(fp, script_buf, FAIL);
  2277.                 AddLine(ACT_RETURN);
  2278.                 // Add key-up hotkey label, e.g. *LButton up::
  2279.                 buf_length = sprintf(buf, "*%s up::", remap_source); // Should be no risk of buffer overflow due to prior validation.
  2280.                 remap_stage = 2; // Adjust to hit stage 3 next time (in case this is stage 10).
  2281.                 goto examine_line; // Have the main loop process the contents of "buf" as though it came in from the script.
  2282.             case 3: // Stage 3.
  2283.                 strcpy(buf, "-1");
  2284.                 AddLine(remap_dest_is_mouse ? ACT_SETMOUSEDELAY : ACT_SETKEYDELAY, &buf, 1, NULL);
  2285.                 sprintf(buf, "{Blind}{%s Up}", remap_dest); // Unlike the down-event above, remap_dest_modifiers is not included for the up-event; e.g. ^{b up} is inappropriate.
  2286.                 AddLine(ACT_SEND, &buf, 1, NULL);
  2287.                 AddLine(ACT_RETURN);
  2288.                 remap_dest_vk = 0; // Reset to signal that the remapping expansion is now complete.
  2289.                 break; // Fall through to the next section so that script loading can resume at the next line.
  2290.             }
  2291.         } // if (remap_dest_vk)
  2292.         // Since above didn't "continue", resume loading script line by line:
  2293.         buf = next_buf;
  2294.         buf_length = next_buf_length;
  2295.         next_buf = (buf == buf1) ? buf2 : buf1;
  2296.         // The line above alternates buffers (toggles next_buf to be the unused buffer), which helps
  2297.         // performance because it avoids memcpy from buf2 to buf1.
  2298.     } // for each whole/constructed line.
  2299.  
  2300.     if (*pending_function) // Since this is the last non-comment line, the pending function must be a function call, not a function definition.
  2301.     {
  2302.         // Somewhat messy to decrement then increment later, but it's probably easier than the
  2303.         // alternatives due to the use of "continue" in some places above.
  2304.         saved_line_number = mCombinedLineNumber;
  2305.         mCombinedLineNumber = pending_function_line_number; // Done so that any syntax errors that occur during the calls below will report the correct line number.
  2306.         if (!ParseAndAddLine(pending_function, ACT_EXPRESSION)) // Must be function call vs. definition since otherwise the above would have detected the opening brace beneath it and already cleared pending_function.
  2307.             return CloseAndReturn(fp, script_buf, FAIL);
  2308.         mCombinedLineNumber = saved_line_number;
  2309.     }
  2310.  
  2311. #ifdef AUTOHOTKEYSC
  2312.     free(script_buf); // AutoIt3: Close the archive and free the file in memory.
  2313.     oRead.Close();    //
  2314. #else
  2315.     fclose(fp);
  2316. #endif
  2317.     return OK;
  2318. }
  2319.  
  2320.  
  2321.  
  2322. // Small inline to make LoadIncludedFile() code cleaner.
  2323. #ifdef AUTOHOTKEYSC
  2324. inline ResultType Script::CloseAndReturn(HS_EXEArc_Read *fp, UCHAR *aBuf, ResultType aReturnValue)
  2325. {
  2326.     free(aBuf);
  2327.     fp->Close();
  2328.     return aReturnValue;
  2329. }
  2330. #else
  2331. inline ResultType Script::CloseAndReturn(FILE *fp, UCHAR *aBuf, ResultType aReturnValue)
  2332. {
  2333.     // aBuf is unused in this case.
  2334.     fclose(fp);
  2335.     return aReturnValue;
  2336. }
  2337. #endif
  2338.  
  2339.  
  2340.  
  2341. #ifdef AUTOHOTKEYSC
  2342. size_t Script::GetLine(char *aBuf, int aMaxCharsToRead, int aInContinuationSection, UCHAR *&aMemFile) // last param = reference to pointer
  2343. #else
  2344. size_t Script::GetLine(char *aBuf, int aMaxCharsToRead, int aInContinuationSection, FILE *fp)
  2345. #endif
  2346. {
  2347.     size_t aBuf_length = 0;
  2348. #ifdef AUTOHOTKEYSC
  2349.     if (!aBuf || !aMemFile) return -1;
  2350.     if (aMaxCharsToRead < 1) return -1; // We're signaling to caller that the end of the memory file has been reached.
  2351.     // Otherwise, continue reading characters from the memory file until either a newline is
  2352.     // reached or aMaxCharsToRead have been read:
  2353.     // Track "i" separately from aBuf_length because we want to read beyond the bounds of the memory file.
  2354.     int i;
  2355.     for (i = 0; i < aMaxCharsToRead; ++i)
  2356.     {
  2357.         if (aMemFile[i] == '\n')
  2358.         {
  2359.             // The end of this line has been reached.  Don't copy this char into the target buffer.
  2360.             // In addition, if the previous char was '\r', remove it from the target buffer:
  2361.             if (aBuf_length > 0 && aBuf[aBuf_length - 1] == '\r')
  2362.                 aBuf[--aBuf_length] = '\0';
  2363.             ++i; // i.e. so that aMemFile will be adjusted to omit this newline char.
  2364.             break;
  2365.         }
  2366.         else
  2367.             aBuf[aBuf_length++] = aMemFile[i];
  2368.     }
  2369.     // We either read aMaxCharsToRead or reached the end of the line (as indicated by the newline char).
  2370.     // In the former case, aMemFile might now be changed to be a position outside the bounds of the
  2371.     // memory area, which the caller will reflect back to us during the next call as a 0 value for
  2372.     // aMaxCharsToRead, which we then signal to the caller (above) as the end of the file):
  2373.     aMemFile += i; // Update this value for use by the caller.
  2374.     // Terminate the buffer (the caller has already ensured that there's room for the terminator
  2375.     // via its value of aMaxCharsToRead):
  2376.     aBuf[aBuf_length] = '\0';
  2377. #else
  2378.     if (!aBuf || !fp) return -1;
  2379.     if (aMaxCharsToRead < 1) return 0;
  2380.     if (feof(fp)) return -1; // Previous call to this function probably already read the last line.
  2381.     if (fgets(aBuf, aMaxCharsToRead, fp) == NULL) // end-of-file or error
  2382.     {
  2383.         *aBuf = '\0';  // Reset since on error, contents added by fgets() are indeterminate.
  2384.         return -1;
  2385.     }
  2386.     aBuf_length = strlen(aBuf);
  2387.     if (!aBuf_length)
  2388.         return 0;
  2389.     if (aBuf[aBuf_length-1] == '\n')
  2390.         aBuf[--aBuf_length] = '\0';
  2391.     if (aBuf[aBuf_length-1] == '\r')  // In case there are any, e.g. a Macintosh or Unix file?
  2392.         aBuf[--aBuf_length] = '\0';
  2393. #endif
  2394.  
  2395.     if (aInContinuationSection)
  2396.     {
  2397.         char *cp = omit_leading_whitespace(aBuf);
  2398.         if (aInContinuationSection == CONTINUATION_SECTION_WITHOUT_COMMENTS) // By default, continuation sections don't allow comments (lines beginning with a semicolon are treated as literal text).
  2399.         {
  2400.             // Caller relies on us to detect the end of the continuation section so that trimming
  2401.             // will be done on the final line of the section and so that a comment can immediately
  2402.             // follow the closing parenthesis (on the same line).  Example:
  2403.             // (
  2404.             //    Text
  2405.             // ) ; Same line comment.
  2406.             if (*cp != ')') // This isn't the last line of the continuation section, so leave the line untrimmed (caller will apply the ltrim setting on its own).
  2407.                 return aBuf_length; // Earlier sections are responsible for keeping aBufLength up-to-date with any changes to aBuf.
  2408.             //else this line starts with ')', so continue on to later section that checks for a same-line comment on its right side.
  2409.         }
  2410.         else // aInContinuationSection == CONTINUATION_SECTION_WITH_COMMENTS (i.e. comments are allowed in this continuation section).
  2411.         {
  2412.             // Fix for v1.0.46.09+: The "com" option shouldn't put "ltrim" into effect.
  2413.             if (!strncmp(cp, g_CommentFlag, g_CommentFlagLength)) // Case sensitive.
  2414.             {
  2415.                 *aBuf = '\0'; // Since this line is a comment, have the caller ignore it.
  2416.                 return -2; // Callers tolerate -2 only when in a continuation section.  -2 indicates, "don't include this line at all, not even as a blank line to which the JOIN string (default "\n") will apply.
  2417.             }
  2418.             if (*cp == ')') // This isn't the last line of the continuation section, so leave the line untrimmed (caller will apply the ltrim setting on its own).
  2419.             {
  2420.                 ltrim(aBuf); // Ltrim this line unconditionally so that caller will see that it starts with ')' without having to do extra steps.
  2421.                 aBuf_length = strlen(aBuf); // ltrim() doesn't always return an accurate length, so do it this way.
  2422.             }
  2423.         }
  2424.     }
  2425.     // Since above didn't return, either:
  2426.     // 1) We're not in a continuation section at all, so apply ltrim() to support semicolons after tabs or
  2427.     //    other whitespace.  Seems best to rtrim also.
  2428.     // 2) CONTINUATION_SECTION_WITHOUT_COMMENTS but this line is the final line of the section.  Apply
  2429.     //    trim() and other logic further below because caller might rely on it.
  2430.     // 3) CONTINUATION_SECTION_WITH_COMMENTS (i.e. comments allowed), but this line isn't a comment (though
  2431.     //    it may start with ')' and thus be the final line of this section). In either case, need to check
  2432.     //    for same-line comments further below.
  2433.     if (aInContinuationSection != CONTINUATION_SECTION_WITH_COMMENTS) // Case #1 & #2 above.
  2434.     {
  2435.         aBuf_length = trim(aBuf);
  2436.         if (!strncmp(aBuf, g_CommentFlag, g_CommentFlagLength)) // Case sensitive.
  2437.         {
  2438.             // Due to other checks, aInContinuationSection==false whenever the above condition is true.
  2439.             *aBuf = '\0';
  2440.             return 0;
  2441.         }
  2442.     }
  2443.     //else CONTINUATION_SECTION_WITH_COMMENTS (case #3 above), which due to other checking also means that
  2444.     // this line isn't a comment (though it might have a comment on its right side, which is checked below).
  2445.     // CONTINUATION_SECTION_WITHOUT_COMMENTS would already have returned higher above if this line isn't
  2446.     // the last line of the continuation section.
  2447.     if (g_AllowSameLineComments)
  2448.     {
  2449.         // Handle comment-flags that appear to the right of a valid line.  But don't
  2450.         // allow these types of comments if the script is considers to be the AutoIt2
  2451.         // style, to improve compatibility with old scripts that may use non-escaped
  2452.         // comment-flags as literal characters rather than comments:
  2453.         char *cp, *prevp;
  2454.         for (cp = strstr(aBuf, g_CommentFlag); cp; cp = strstr(cp + g_CommentFlagLength, g_CommentFlag))
  2455.         {
  2456.             // If no whitespace to its left, it's not a valid comment.
  2457.             // We insist on this so that a semi-colon (for example) immediately after
  2458.             // a word (as semi-colons are often used) will not be considered a comment.
  2459.             prevp = cp - 1;
  2460.             if (prevp < aBuf) // should never happen because we already checked above.
  2461.             {
  2462.                 *aBuf = '\0';
  2463.                 return 0;
  2464.             }
  2465.             if (IS_SPACE_OR_TAB_OR_NBSP(*prevp)) // consider it to be a valid comment flag
  2466.             {
  2467.                 *prevp = '\0';
  2468.                 aBuf_length = rtrim_with_nbsp(aBuf, prevp - aBuf); // Since it's our responsibility to return a fully trimmed string.
  2469.                 break; // Once the first valid comment-flag is found, nothing after it can matter.
  2470.             }
  2471.             else // No whitespace to the left.
  2472.                 if (*prevp == g_EscapeChar) // Remove the escape char.
  2473.                 {
  2474.                     // The following isn't exactly correct because it prevents an include filename from ever
  2475.                     // containing the literal string "`;".  This is because attempts to escape the accent via
  2476.                     // "``;" are not supported.  This is documented here as a known limitation because fixing
  2477.                     // it would probably break existing scripts that rely on the fact that accents do not need
  2478.                     // to be escaped inside #Include.  Also, the likelihood of "`;" appearing literally in a
  2479.                     // legitimate #Include file seems vanishingly small.
  2480.                     memmove(prevp, prevp + 1, strlen(prevp + 1) + 1);  // +1 for the terminator.
  2481.                     --aBuf_length;
  2482.                     // Then continue looking for others.
  2483.                 }
  2484.                 // else there wasn't any whitespace to its left, so keep looking in case there's
  2485.                 // another further on in the line.
  2486.         } // for()
  2487.     } // if (g_AllowSameLineComments)
  2488.  
  2489.     return aBuf_length; // The above is responsible for keeping aBufLength up-to-date with any changes to aBuf.
  2490. }
  2491.  
  2492.  
  2493.  
  2494. inline ResultType Script::IsDirective(char *aBuf)
  2495. // aBuf must be a modifiable string since this function modifies it in the case of "#Include %A_ScriptDir%"
  2496. // changes it.  It must also be large enough to accept the replacement of %A_ScriptDir% with a larger string.
  2497. // Returns CONDITION_TRUE, CONDITION_FALSE, or FAIL.
  2498. // Note: Don't assume that every line in the script that starts with '#' is a directive
  2499. // because hotkeys can legitimately start with that as well.  i.e., the following line should
  2500. // not be unconditionally ignored, just because it starts with '#', since it is a valid hotkey:
  2501. // #y::run, notepad
  2502. {
  2503.     char end_flags[] = {' ', '\t', g_delimiter, '\0'}; // '\0' must be last.
  2504.     char *directive_end, *parameter_raw;
  2505.     if (   !(directive_end = StrChrAny(aBuf, end_flags))   )
  2506.     {
  2507.         directive_end = aBuf + strlen(aBuf); // Point it to the zero terminator.
  2508.         parameter_raw = NULL;
  2509.     }
  2510.     else
  2511.         if (!*(parameter_raw = omit_leading_whitespace(directive_end)))
  2512.             parameter_raw = NULL;
  2513.  
  2514.     // The raw parameter retains any leading comma for those directives that need that (none currently).
  2515.     // But the following omits that comma:
  2516.     char *parameter;
  2517.     if (!parameter_raw)
  2518.         parameter = NULL;
  2519.     else // Since parameter_raw is non-NULL, it's also non-blank and non-whitespace due to the above checking.
  2520.         if (*parameter_raw != g_delimiter)
  2521.             parameter = parameter_raw;
  2522.         else // It's a delimiter, so "parameter" will be whatever non-whitespace character follows it, if any.
  2523.             if (!*(parameter = omit_leading_whitespace(parameter_raw + 1)))
  2524.                 parameter = NULL;
  2525.             //else leave it set to the value returned by omit_leading_whitespace().
  2526.  
  2527.     int value; // Helps detect values that are too large, since some of the target globals are UCHAR.
  2528.  
  2529.     // Use strnicmp() so that a match is found as long as aBuf starts with the string in question.
  2530.     // e.g. so that "#SingleInstance, on" will still work too, but
  2531.     // "#a::run, something, "#SingleInstance" (i.e. a hotkey) will not be falsely detected
  2532.     // due to using a more lenient function such as strcasestr().
  2533.     // UPDATE: Using strlicmp() now so that overlapping names, such as #MaxThreads and #MaxThreadsPerHotkey,
  2534.     // won't get mixed up:
  2535.     #define IS_DIRECTIVE_MATCH(directive) (!strlicmp(aBuf, directive, directive_name_length))
  2536.     UINT directive_name_length = (UINT)(directive_end - aBuf); // To avoid calculating it every time in the macro above.
  2537.  
  2538.     bool is_include_again = false; // Set default in case of short-circuit boolean.
  2539.     if (IS_DIRECTIVE_MATCH("#Include") || (is_include_again = IS_DIRECTIVE_MATCH("#IncludeAgain")))
  2540.     {
  2541.         // Standalone EXEs ignore this directive since the included files were already merged in
  2542.         // with the main file when the script was compiled.  These should have been removed
  2543.         // or commented out by Ahk2Exe, but just in case, it's safest to ignore them:
  2544. #ifdef AUTOHOTKEYSC
  2545.         return CONDITION_TRUE;
  2546. #else
  2547.         // If the below decision is ever changed, be sure to update ahk2exe with the same change:
  2548.         // "parameter" is checked rather than parameter_raw for backward compatibility with earlier versions,
  2549.         // in which a leading comma is not considered part of the filename.  Although this behavior is incorrect
  2550.         // because it prevents files whose names start with a comma from being included without the first
  2551.         // delim-comma being there too, it is kept because filesnames that start with a comma seem
  2552.         // exceedingly rare.  As a workaround, the script can do #Include ,,FilenameWithLeadingComma.ahk
  2553.         if (!parameter)
  2554.             return ScriptError(ERR_PARAM1_REQUIRED, aBuf);
  2555.         // v1.0.32:
  2556.         bool ignore_load_failure = (parameter[0] == '*' && toupper(parameter[1]) == 'I'); // Relies on short-circuit boolean order.
  2557.         if (ignore_load_failure)
  2558.         {
  2559.             parameter += 2;
  2560.             if (IS_SPACE_OR_TAB(*parameter)) // Skip over at most one space or tab, since others might be a literal part of the filename.
  2561.                 ++parameter;
  2562.         }
  2563.  
  2564.         size_t space_remaining = LINE_SIZE - (parameter-aBuf);
  2565.         char buf[MAX_PATH];
  2566.         StrReplace(parameter, "%A_ScriptDir%", mFileDir, SCS_INSENSITIVE, 1, space_remaining); // v1.0.35.11.  Caller has ensured string is writable.
  2567.         if (strcasestr(parameter, "%A_AppData%")) // v1.0.45.04: This and the next were requested by Tekl to make it easier to customize scripts on a per-user basis.
  2568.         {
  2569.             BIV_AppData(buf, "A_AppData");
  2570.             StrReplace(parameter, "%A_AppData%", buf, SCS_INSENSITIVE, 1, space_remaining);
  2571.         }
  2572.         if (strcasestr(parameter, "%A_AppDataCommon%")) // v1.0.45.04.
  2573.         {
  2574.             BIV_AppData(buf, "A_AppDataCommon");
  2575.             StrReplace(parameter, "%A_AppDataCommon%", buf, SCS_INSENSITIVE, 1, space_remaining);
  2576.         }
  2577.  
  2578.         DWORD attr = GetFileAttributes(parameter);
  2579.         if (attr != 0xFFFFFFFF && (attr & FILE_ATTRIBUTE_DIRECTORY)) // File exists and its a directory (possibly A_ScriptDir or A_AppData set above).
  2580.         {
  2581.             // v1.0.35.11 allow changing of load-time directory to increase flexibility.  This feature has
  2582.             // been asked for directly or indirectly several times.
  2583.             // If a script ever wants to use a string like "%A_ScriptDir%" literally in an include's filename,
  2584.             // that would not work.  But that seems too rare to worry about.
  2585.             // v1.0.45.01: Call SetWorkingDir() vs. SetCurrentDirectory() so that it succeeds even for a root
  2586.             // drive like C: that lacks a backslash (see SetWorkingDir() for details).
  2587.             SetWorkingDir(parameter);
  2588.             return CONDITION_TRUE;
  2589.         }
  2590.         // Since above didn't return, it's a file (or non-existent file, in which case the below will display
  2591.         // the error).  This will also display any other errors that occur:
  2592.         return LoadIncludedFile(parameter, is_include_again, ignore_load_failure) ? CONDITION_TRUE : FAIL;
  2593. #endif
  2594.     }
  2595.  
  2596.     if (IS_DIRECTIVE_MATCH("#NoEnv"))
  2597.     {
  2598.         g_NoEnv = true;
  2599.         return CONDITION_TRUE;
  2600.     }
  2601.     if (IS_DIRECTIVE_MATCH("#NoTrayIcon"))
  2602.     {
  2603.         g_NoTrayIcon = true;
  2604.         return CONDITION_TRUE;
  2605.     }
  2606.     if (IS_DIRECTIVE_MATCH("#Persistent"))
  2607.     {
  2608.         g_persistent = true;
  2609.         return CONDITION_TRUE;
  2610.     }
  2611.     if (IS_DIRECTIVE_MATCH("#SingleInstance"))
  2612.     {
  2613.         g_AllowOnlyOneInstance = SINGLE_INSTANCE_PROMPT; // Set default.
  2614.         if (parameter)
  2615.         {
  2616.             if (!stricmp(parameter, "Force"))
  2617.                 g_AllowOnlyOneInstance = SINGLE_INSTANCE_REPLACE;
  2618.             else if (!stricmp(parameter, "Ignore"))
  2619.                 g_AllowOnlyOneInstance = SINGLE_INSTANCE_IGNORE;
  2620.             else if (!stricmp(parameter, "Off"))
  2621.                 g_AllowOnlyOneInstance = SINGLE_INSTANCE_OFF;
  2622.         }
  2623.         return CONDITION_TRUE;
  2624.     }
  2625.     if (IS_DIRECTIVE_MATCH("#InstallKeybdHook"))
  2626.     {
  2627.         // It seems best not to report this warning because a user may want to use partial functionality
  2628.         // of a script on Win9x:
  2629.         //MsgBox("#InstallKeybdHook is not supported on Windows 95/98/Me.  This line will be ignored.");
  2630.         if (!g_os.IsWin9x())
  2631.             Hotkey::RequireHook(HOOK_KEYBD);
  2632.         return CONDITION_TRUE;
  2633.     }
  2634.     if (IS_DIRECTIVE_MATCH("#InstallMouseHook"))
  2635.     {
  2636.         // It seems best not to report this warning because a user may want to use partial functionality
  2637.         // of a script on Win9x:
  2638.         //MsgBox("#InstallMouseHook is not supported on Windows 95/98/Me.  This line will be ignored.");
  2639.         if (!g_os.IsWin9x())
  2640.             Hotkey::RequireHook(HOOK_MOUSE);
  2641.         return CONDITION_TRUE;
  2642.     }
  2643.     if (IS_DIRECTIVE_MATCH("#UseHook"))
  2644.     {
  2645.         g_ForceKeybdHook = !parameter || Line::ConvertOnOff(parameter) != TOGGLED_OFF;
  2646.         return CONDITION_TRUE;
  2647.     }
  2648.  
  2649.     if (!strnicmp(aBuf, "#IfWin", 6))
  2650.     {
  2651.         bool invert = !strnicmp(aBuf + 6, "Not", 3);
  2652.         if (!strnicmp(aBuf + (invert ? 9 : 6), "Active", 6)) // It matches #IfWin[Not]Active.
  2653.             g_HotCriterion = invert ? HOT_IF_NOT_ACTIVE : HOT_IF_ACTIVE;
  2654.         else if (!strnicmp(aBuf + (invert ? 9 : 6), "Exist", 5))
  2655.             g_HotCriterion = invert ? HOT_IF_NOT_EXIST : HOT_IF_EXIST;
  2656.         else // It starts with #IfWin but isn't Active or Exist: Don't alter g_HotCriterion.
  2657.             return CONDITION_FALSE; // Indicate unknown directive since there are currently no other possibilities.
  2658.         if (!parameter) // The omission of the parameter indicates that any existing criteria should be turned off.
  2659.         {
  2660.             g_HotCriterion = HOT_NO_CRITERION; // Indicate that no criteria are in effect for subsequent hotkeys.
  2661.             g_HotWinTitle = ""; // Helps maintainability and some things might rely on it.
  2662.             g_HotWinText = "";  //
  2663.             return CONDITION_TRUE;
  2664.         }
  2665.         char *hot_win_title = parameter, *hot_win_text; // Set default for title; text is determined later.
  2666.         // Scan for the first non-escaped comma.  If there is one, it marks the second paramter: WinText.
  2667.         char *cp, *first_non_escaped_comma;
  2668.         for (first_non_escaped_comma = NULL, cp = hot_win_title; ; ++cp)  // Increment to skip over the symbol just found by the inner for().
  2669.         {
  2670.             for (; *cp && !(*cp == g_EscapeChar || *cp == g_delimiter || *cp == g_DerefChar); ++cp);  // Find the next escape char, comma, or %.
  2671.             if (!*cp) // End of string was found.
  2672.                 break;
  2673. #define ERR_ESCAPED_COMMA_PERCENT "Literal commas and percent signs must be escaped (e.g. `%)"
  2674.             if (*cp == g_DerefChar)
  2675.                 return ScriptError(ERR_ESCAPED_COMMA_PERCENT, aBuf);
  2676.             if (*cp == g_delimiter) // non-escaped delimiter was found.
  2677.             {
  2678.                 // Preserve the ability to add future-use parameters such as section of window
  2679.                 // over which the mouse is hovering, e.g. #IfWinActive, Untitled - Notepad,, TitleBar
  2680.                 if (first_non_escaped_comma) // A second non-escaped comma was found.
  2681.                     return ScriptError(ERR_ESCAPED_COMMA_PERCENT, aBuf);
  2682.                 // Otherwise:
  2683.                 first_non_escaped_comma = cp;
  2684.                 continue; // Check if there are any more non-escaped commas.
  2685.             }
  2686.             // Otherwise, an escape character was found, so skip over the next character (if any).
  2687.             if (!*(++cp)) // The string unexpectedly ends in an escape character, so avoid out-of-bounds.
  2688.                 break;
  2689.             // Otherwise, the ++cp above has skipped over the escape-char itself, and the loop's ++cp will now
  2690.             // skip over the char-to-be-escaped, which is not the one we want (even if it is a comma).
  2691.         }
  2692.         if (first_non_escaped_comma) // Above found a non-escaped comma, so there is a second parameter (WinText).
  2693.         {
  2694.             // Omit whitespace to (seems best to conform to convention/expectations rather than give
  2695.             // strange whitespace flexibility that would likely cause unwanted bugs due to inadvertently
  2696.             // have two spaces instead of one).  The user may use `s and `t to put literal leading/trailing
  2697.             // spaces/tabs into these paramters.
  2698.             hot_win_text = omit_leading_whitespace(first_non_escaped_comma + 1);
  2699.             *first_non_escaped_comma = '\0'; // Terminate at the comma to split off hot_win_title on its own.
  2700.             rtrim(hot_win_title, first_non_escaped_comma - hot_win_title);  // Omit whitespace (see similar comment above).
  2701.             // The following must be done only after trimming and omitting whitespace above, so that
  2702.             // `s and `t can be used to insert leading/trailing spaces/tabs.  ConvertEscapeSequences()
  2703.             // also supports insertion of literal commas via escaped sequences.
  2704.             ConvertEscapeSequences(hot_win_text, g_EscapeChar, true);
  2705.         }
  2706.         else
  2707.             hot_win_text = ""; // And leave hot_win_title set to the entire string because there's only one parameter.
  2708.         // The following must be done only after trimming and omitting whitespace above (see similar comment above).
  2709.         ConvertEscapeSequences(hot_win_title, g_EscapeChar, true);
  2710.         // The following also handles the case where both title and text are blank, which could happen
  2711.         // due to something weird but legit like: #IfWinActive, ,
  2712.         if (!SetGlobalHotTitleText(hot_win_title, hot_win_text))
  2713.             return ScriptError(ERR_OUTOFMEM); // So rare that no second param is provided (since its contents may have been temp-terminated or altered above).
  2714.         return CONDITION_TRUE;
  2715.     } // Above completely handles all directives and non-directives that start with "#IfWin".
  2716.  
  2717.     if (IS_DIRECTIVE_MATCH("#Hotstring"))
  2718.     {
  2719.         if (parameter)
  2720.         {
  2721.             char *suboption = strcasestr(parameter, "EndChars");
  2722.             if (suboption)
  2723.             {
  2724.                 // Since it's not realistic to have only a couple, spaces and literal tabs
  2725.                 // must be included in between other chars, e.g. `n `t has a space in between.
  2726.                 // Also, EndChar  \t  will have a space and a tab since there are two spaces
  2727.                 // after the word EndChar.
  2728.                 if (    !(parameter = StrChrAny(suboption, "\t "))   )
  2729.                     return CONDITION_TRUE;
  2730.                 strlcpy(g_EndChars, ++parameter, sizeof(g_EndChars));
  2731.                 ConvertEscapeSequences(g_EndChars, g_EscapeChar, false);
  2732.                 return CONDITION_TRUE;
  2733.             }
  2734.             if (!strnicmp(parameter, "NoMouse", 7)) // v1.0.42.03
  2735.             {
  2736.                 g_HSResetUponMouseClick = false;
  2737.                 return CONDITION_TRUE;
  2738.             }
  2739.             // Otherwise assume it's a list of options.  Note that for compatibility with its
  2740.             // other caller, it will stop at end-of-string or ':', whichever comes first.
  2741.             Hotstring::ParseOptions(parameter, g_HSPriority, g_HSKeyDelay, g_HSSendMode, g_HSCaseSensitive
  2742.                 , g_HSConformToCase, g_HSDoBackspace, g_HSOmitEndChar, g_HSSendRaw, g_HSEndCharRequired
  2743.                 , g_HSDetectWhenInsideWord, g_HSDoReset);
  2744.         }
  2745.         return CONDITION_TRUE;
  2746.     }
  2747.  
  2748.     if (IS_DIRECTIVE_MATCH("#HotkeyModifierTimeout"))
  2749.     {
  2750.         if (parameter)
  2751.             g_HotkeyModifierTimeout = ATOI(parameter);  // parameter was set to the right position by the above macro
  2752.         return CONDITION_TRUE;
  2753.     }
  2754.     if (IS_DIRECTIVE_MATCH("#HotkeyInterval"))
  2755.     {
  2756.         if (parameter)
  2757.         {
  2758.             g_HotkeyThrottleInterval = ATOI(parameter);  // parameter was set to the right position by the above macro
  2759.             if (g_HotkeyThrottleInterval < 10) // values under 10 wouldn't be useful due to timer granularity.
  2760.                 g_HotkeyThrottleInterval = 10;
  2761.         }
  2762.         return CONDITION_TRUE;
  2763.     }
  2764.     if (IS_DIRECTIVE_MATCH("#MaxHotkeysPerInterval"))
  2765.     {
  2766.         if (parameter)
  2767.         {
  2768.             g_MaxHotkeysPerInterval = ATOI(parameter);  // parameter was set to the right position by the above macro
  2769.             if (g_MaxHotkeysPerInterval < 1) // sanity check
  2770.                 g_MaxHotkeysPerInterval = 1;
  2771.         }
  2772.         return CONDITION_TRUE;
  2773.     }
  2774.     if (IS_DIRECTIVE_MATCH("#MaxThreadsPerHotkey"))
  2775.     {
  2776.         if (parameter)
  2777.         {
  2778.             // Use value as a temp holder since it's int vs. UCHAR and can thus detect very large or negative values:
  2779.             value = ATOI(parameter);  // parameter was set to the right position by the above macro
  2780.             if (value > MAX_THREADS_LIMIT) // For now, keep this limited to prevent stack overflow due to too many pseudo-threads.
  2781.                 value = MAX_THREADS_LIMIT;
  2782.             else if (value < 1)
  2783.                 value = 1;
  2784.             g_MaxThreadsPerHotkey = value; // Note: g_MaxThreadsPerHotkey is UCHAR.
  2785.         }
  2786.         return CONDITION_TRUE;
  2787.     }
  2788.     if (IS_DIRECTIVE_MATCH("#MaxThreadsBuffer"))
  2789.     {
  2790.         g_MaxThreadsBuffer = !parameter || Line::ConvertOnOff(parameter) != TOGGLED_OFF;
  2791.         return CONDITION_TRUE;
  2792.     }
  2793.     if (IS_DIRECTIVE_MATCH("#MaxThreads"))
  2794.     {
  2795.         if (parameter)
  2796.         {
  2797.             value = ATOI(parameter);  // parameter was set to the right position by the above macro
  2798.             if (value > MAX_THREADS_LIMIT) // For now, keep this limited to prevent stack overflow due to too many pseudo-threads.
  2799.                 value = MAX_THREADS_LIMIT;
  2800.             else if (value < 1)
  2801.                 value = 1;
  2802.             g_MaxThreadsTotal = value;
  2803.         }
  2804.         return CONDITION_TRUE;
  2805.     }
  2806.  
  2807.     if (IS_DIRECTIVE_MATCH("#ClipboardTimeout"))
  2808.     {
  2809.         if (parameter)
  2810.             g_ClipboardTimeout = ATOI(parameter);  // parameter was set to the right position by the above macro
  2811.         return CONDITION_TRUE;
  2812.     }
  2813.     if (IS_DIRECTIVE_MATCH("#LTrim"))
  2814.     {
  2815.         g_ContinuationLTrim = !parameter || Line::ConvertOnOff(parameter) != TOGGLED_OFF;
  2816.         return CONDITION_TRUE;
  2817.     }
  2818.  
  2819.     if (IS_DIRECTIVE_MATCH("#WinActivateForce"))
  2820.     {
  2821.         g_WinActivateForce = true;
  2822.         return CONDITION_TRUE;
  2823.     }
  2824.     if (IS_DIRECTIVE_MATCH("#ErrorStdOut"))
  2825.     {
  2826.         mErrorStdOut = true;
  2827.         return CONDITION_TRUE;
  2828.     }
  2829.     if (IS_DIRECTIVE_MATCH("#AllowSameLineComments"))  // i.e. There's no way to turn it off, only on.
  2830.     {
  2831.         g_AllowSameLineComments = true;
  2832.         return CONDITION_TRUE;
  2833.     }
  2834.     if (IS_DIRECTIVE_MATCH("#MaxMem"))
  2835.     {
  2836.         if (parameter)
  2837.         {
  2838.             double valuef = ATOF(parameter);  // parameter was set to the right position by the above macro
  2839.             if (valuef > 4095)  // Don't exceed capacity of VarSizeType, which is currently a DWORD (4 gig).
  2840.                 valuef = 4095;  // Don't use 4096 since that might be a special/reserved value for some functions.
  2841.             else if (valuef  < 1)
  2842.                 valuef = 1;
  2843.             g_MaxVarCapacity = (VarSizeType)(valuef * 1024 * 1024);
  2844.         }
  2845.         return CONDITION_TRUE;
  2846.     }
  2847.     if (IS_DIRECTIVE_MATCH("#KeyHistory"))
  2848.     {
  2849.         if (parameter)
  2850.         {
  2851.             g_MaxHistoryKeys = ATOI(parameter);  // parameter was set to the right position by the above macro
  2852.             if (g_MaxHistoryKeys < 0)
  2853.                 g_MaxHistoryKeys = 0;
  2854.             else if (g_MaxHistoryKeys > 500)
  2855.                 g_MaxHistoryKeys = 500;
  2856.             // Above: There are two reasons for limiting the history file to 500 keystrokes:
  2857.             // 1) GetHookStatus() only has a limited size buffer in which to transcribe the keystrokes.
  2858.             //    500 events is about what you would expect to fit in a 32 KB buffer (it the unlikely event
  2859.             //    that the transcribed events create too much text, the text will be truncated, so it's
  2860.             //    not dangerous anyway).
  2861.             // 2) To reduce the impression that AutoHotkey designed for key logging (the key history file
  2862.             //    is in a very unfriendly format that type of key logging anyway).
  2863.         }
  2864.         return CONDITION_TRUE;
  2865.     }
  2866.  
  2867.     // For the below series, it seems okay to allow the comment flag to contain other reserved chars,
  2868.     // such as DerefChar, since comments are evaluated, and then taken out of the game at an earlier
  2869.     // stage than DerefChar and the other special chars.
  2870.     if (IS_DIRECTIVE_MATCH("#CommentFlag"))
  2871.     {
  2872.         if (parameter)
  2873.         {
  2874.             if (!*(parameter + 1))  // i.e. the length is 1
  2875.             {
  2876.                 // Don't allow '#' since it's the preprocessor directive symbol being used here.
  2877.                 // Seems ok to allow "." to be the comment flag, since other constraints mandate
  2878.                 // that at least one space or tab occur to its left for it to be considered a
  2879.                 // comment marker.
  2880.                 if (*parameter == '#' || *parameter == g_DerefChar || *parameter == g_EscapeChar || *parameter == g_delimiter)
  2881.                     return ScriptError(ERR_PARAM1_INVALID, aBuf);
  2882.                 // Exclude hotkey definition chars, such as ^ and !, because otherwise
  2883.                 // the following example wouldn't work:
  2884.                 // User defines ! as the comment flag.
  2885.                 // The following hotkey would never be in effect since it's considered to
  2886.                 // be commented out:
  2887.                 // !^a::run,notepad
  2888.                 if (*parameter == '!' || *parameter == '^' || *parameter == '+' || *parameter == '$' || *parameter == '~' || *parameter == '*'
  2889.                     || *parameter == '<' || *parameter == '>')
  2890.                     // Note that '#' is already covered by the other stmt. above.
  2891.                     return ScriptError(ERR_PARAM1_INVALID, aBuf);
  2892.             }
  2893.             strlcpy(g_CommentFlag, parameter, MAX_COMMENT_FLAG_LENGTH + 1);
  2894.             g_CommentFlagLength = strlen(g_CommentFlag);  // Keep this in sync with above.
  2895.         }
  2896.         return CONDITION_TRUE;
  2897.     }
  2898.     if (IS_DIRECTIVE_MATCH("#EscapeChar"))
  2899.     {
  2900.         if (parameter)
  2901.         {
  2902.             // Don't allow '.' since that can be part of literal floating point numbers:
  2903.             if (   *parameter == '#' || *parameter == g_DerefChar || *parameter == g_delimiter || *parameter == '.'
  2904.                 || (g_CommentFlagLength == 1 && *parameter == *g_CommentFlag)   )
  2905.                 return ScriptError(ERR_PARAM1_INVALID, aBuf);
  2906.             g_EscapeChar = *parameter;
  2907.         }
  2908.         return CONDITION_TRUE;
  2909.     }
  2910.     if (IS_DIRECTIVE_MATCH("#DerefChar"))
  2911.     {
  2912.         if (parameter)
  2913.         {
  2914.             if (   *parameter == g_EscapeChar || *parameter == g_delimiter || *parameter == '.'
  2915.                 || (g_CommentFlagLength == 1 && *parameter == *g_CommentFlag)   ) // Fix for v1.0.47.05: Allow deref char to be # as documented.
  2916.                 return ScriptError(ERR_PARAM1_INVALID, aBuf);
  2917.             g_DerefChar = *parameter;
  2918.         }
  2919.         return CONDITION_TRUE;
  2920.     }
  2921.     if (IS_DIRECTIVE_MATCH("#Delimiter"))
  2922.     {
  2923.         // Attempts to change the delimiter to its starting default (comma) are ignored.
  2924.         // For example, "#Delimiter ," isn't meaningful if the delimiter already is a comma,
  2925.         // which is good because "parameter" has already assumed that the comma is accidental
  2926.         // (not a symbol) and omitted it.
  2927.         if (parameter)
  2928.         {
  2929.             if (   *parameter == '#' || *parameter == g_EscapeChar || *parameter == g_DerefChar || *parameter == '.'
  2930.                 || (g_CommentFlagLength == 1 && *parameter == *g_CommentFlag)   )
  2931.                 return ScriptError(ERR_PARAM1_INVALID, aBuf);
  2932.             g_delimiter = *parameter;
  2933.         }
  2934.         return CONDITION_TRUE;
  2935.     }
  2936.  
  2937.     // Otherwise, report that this line isn't a directive:
  2938.     return CONDITION_FALSE;
  2939. }
  2940.  
  2941.  
  2942.  
  2943. void ScriptTimer::Disable()
  2944. {
  2945.     mEnabled = false;
  2946.     --g_script.mTimerEnabledCount;
  2947.     if (!g_script.mTimerEnabledCount && !g_nLayersNeedingTimer && !Hotkey::sJoyHotkeyCount)
  2948.         KILL_MAIN_TIMER
  2949.     // Above: If there are now no enabled timed subroutines, kill the main timer since there's no other
  2950.     // reason for it to exist if we're here.   This is because or direct or indirect caller is
  2951.     // currently always ExecUntil(), which doesn't need the timer while its running except to
  2952.     // support timed subroutines.  UPDATE: The above is faulty; Must also check g_nLayersNeedingTimer
  2953.     // because our caller can be one that still needs a timer as proven by this script that
  2954.     // hangs otherwise:
  2955.     //SetTimer, Test, on 
  2956.     //Sleep, 1000 
  2957.     //msgbox, done
  2958.     //return
  2959.     //Test: 
  2960.     //SetTimer, Test, off 
  2961.     //return
  2962. }
  2963.  
  2964.  
  2965.  
  2966. ResultType Script::UpdateOrCreateTimer(Label *aLabel, char *aPeriod, char *aPriority, bool aEnable
  2967.     , bool aUpdatePriorityOnly)
  2968. // Caller should specific a blank aPeriod to prevent the timer's period from being changed
  2969. // (i.e. if caller just wants to turn on or off an existing timer).  But if it does this
  2970. // for a non-existent timer, that timer will be created with the default period as specfied in
  2971. // the constructor.
  2972. {
  2973.     ScriptTimer *timer;
  2974.     for (timer = mFirstTimer; timer != NULL; timer = timer->mNextTimer)
  2975.         if (timer->mLabel == aLabel) // Match found.
  2976.             break;
  2977.     bool timer_existed = (timer != NULL);
  2978.     if (!timer_existed)  // Create it.
  2979.     {
  2980.         if (   !(timer = new ScriptTimer(aLabel))   )
  2981.             return ScriptError(ERR_OUTOFMEM);
  2982.         if (!mFirstTimer)
  2983.             mFirstTimer = mLastTimer = timer;
  2984.         else
  2985.         {
  2986.             mLastTimer->mNextTimer = timer;
  2987.             // This must be done after the above:
  2988.             mLastTimer = timer;
  2989.         }
  2990.         ++mTimerCount;
  2991.     }
  2992.     // Update its members:
  2993.     if (aEnable && !timer->mEnabled) // Must check both or the mTimerEnabledCount below will be wrong.
  2994.     {
  2995.         // The exception is if the timer already existed but the caller only wanted its priority changed:
  2996.         if (!(timer_existed && aUpdatePriorityOnly))
  2997.         {
  2998.             timer->mEnabled = true;
  2999.             ++mTimerEnabledCount;
  3000.             SET_MAIN_TIMER  // Ensure the API timer is always running when there is at least one enabled timed subroutine.
  3001.         }
  3002.         //else do nothing, leave it disabled.
  3003.     }
  3004.     else if (!aEnable && timer->mEnabled) // Must check both or the below count will be wrong.
  3005.         timer->Disable();
  3006.  
  3007.     if (*aPeriod) // Caller wanted us to update this member.
  3008.     {
  3009.         __int64 period = ATOI64(aPeriod);
  3010.         if (period < 0) // v1.0.46.16: Support negative periods to mean "run only once".
  3011.         {
  3012.             timer->mRunOnlyOnce = true;
  3013.             timer->mPeriod = (DWORD)-period;
  3014.         }
  3015.         else // Positive number.  v1.0.36.33: Changed from int to DWORD, and ATOI to ATOU, to double its capacity:
  3016.         {
  3017.             timer->mPeriod = (DWORD)period; // Always use this method & check to retain compatibility with existing scripts.
  3018.             timer->mRunOnlyOnce = false;
  3019.         }
  3020.     }
  3021.  
  3022.     if (*aPriority) // Caller wants this member to be changed from its current or default value.
  3023.         timer->mPriority = ATOI(aPriority); // Read any float in a runtime variable reference as an int.
  3024.  
  3025.     if (!(timer_existed && aUpdatePriorityOnly))
  3026.         // Caller relies on us updating mTimeLastRun in this case.  This is done because it's more
  3027.         // flexible, e.g. a user might want to create a timer that is triggered 5 seconds from now.
  3028.         // In such a case, we don't want the timer's first triggering to occur immediately.
  3029.         // Instead, we want it to occur only when the full 5 seconds have elapsed:
  3030.         timer->mTimeLastRun = GetTickCount();
  3031.  
  3032.     // Below is obsolete, see above for why:
  3033.     // We don't have to kill or set the main timer because the only way this function is called
  3034.     // is directly from the execution of a script line inside ExecUntil(), in which case:
  3035.     // 1) KILL_MAIN_TIMER is never needed because the timer shouldn't exist while in ExecUntil().
  3036.     // 2) SET_MAIN_TIMER is never needed because it will be set automatically the next time ExecUntil()
  3037.     //    calls MsgSleep().
  3038.     return OK;
  3039. }
  3040.  
  3041.  
  3042.  
  3043. Label *Script::FindLabel(char *aLabelName)
  3044. // Returns the first label whose name matches aLabelName, or NULL if not found.
  3045. // v1.0.42: Since duplicates labels are now possible (to support #IfWin variants of a particular
  3046. // hotkey or hotstring), callers must be aware that only the first match is returned.
  3047. // This helps performance by requiring on average only half the labels to be searched before
  3048. // a match is found.
  3049. {
  3050.     if (!aLabelName || !*aLabelName) return NULL;
  3051.     for (Label *label = mFirstLabel; label != NULL; label = label->mNextLabel)
  3052.         if (!stricmp(label->mName, aLabelName)) // lstrcmpi() is not used: 1) avoids breaking exisitng scripts; 2) provides consistent behavior across multiple locales; 3) performance.
  3053.             return label; // Match found.
  3054.     return NULL; // No match found.
  3055. }
  3056.  
  3057.  
  3058.  
  3059. ResultType Script::AddLabel(char *aLabelName, bool aAllowDupe)
  3060. // Returns OK or FAIL.
  3061. {
  3062.     if (!*aLabelName)
  3063.         return FAIL; // For now, silent failure because callers should check this beforehand.
  3064.     if (!aAllowDupe && FindLabel(aLabelName)) // Relies on short-circuit boolean order.
  3065.         // Don't attempt to dereference "duplicate_label->mJumpToLine because it might not
  3066.         // exist yet.  Example:
  3067.         // label1:
  3068.         // label1:  <-- This would be a dupe-error but it doesn't yet have an mJumpToLine.
  3069.         // return
  3070.         return ScriptError("Duplicate label.", aLabelName);
  3071.     char *new_name = SimpleHeap::Malloc(aLabelName);
  3072.     if (!new_name)
  3073.         return FAIL;  // It already displayed the error for us.
  3074.     Label *the_new_label = new Label(new_name); // Pass it the dynamic memory area we created.
  3075.     if (the_new_label == NULL)
  3076.         return ScriptError(ERR_OUTOFMEM);
  3077.     the_new_label->mPrevLabel = mLastLabel;  // Whether NULL or not.
  3078.     if (mFirstLabel == NULL)
  3079.         mFirstLabel = the_new_label;
  3080.     else
  3081.         mLastLabel->mNextLabel = the_new_label;
  3082.     // This must be done after the above:
  3083.     mLastLabel = the_new_label;
  3084.     if (!stricmp(new_name, "OnClipboardChange"))
  3085.         mOnClipboardChangeLabel = the_new_label;
  3086.     return OK;
  3087. }
  3088.  
  3089.  
  3090.  
  3091. ResultType Script::ParseAndAddLine(char *aLineText, ActionTypeType aActionType, ActionTypeType aOldActionType
  3092.     , char *aActionName, char *aEndMarker, char *aLiteralMap, size_t aLiteralMapLength)
  3093. // Returns OK or FAIL.
  3094. // aLineText needs to be a string whose contents are modifiable (though the string won't be made any
  3095. // longer than it is now, so it doesn't have to be of size LINE_SIZE). This helps performance by
  3096. // allowing the string to be split into sections without having to make temporary copies.
  3097. {
  3098. #ifdef _DEBUG
  3099.     if (!aLineText || !*aLineText)
  3100.         return ScriptError("DEBUG: ParseAndAddLine() called incorrectly.");
  3101. #endif
  3102.  
  3103.     bool in_quotes;
  3104.     int open_parens;
  3105.  
  3106.     char action_name[MAX_VAR_NAME_LENGTH + 1], *end_marker;
  3107.     if (aActionName) // i.e. this function was called recursively with explicit values for the optional params.
  3108.     {
  3109.         strcpy(action_name, aActionName);
  3110.         end_marker = aEndMarker;
  3111.     }
  3112.     else if (aActionType == ACT_EXPRESSION)
  3113.     {
  3114.         *action_name = '\0';
  3115.         end_marker = NULL; // Indicate that there is no action to mark the end of.
  3116.     }
  3117.     else // We weren't called recursively from self, nor is it ACT_EXPRESSION, so set action_name and end_marker the normal way.
  3118.     {
  3119.         for (;;) // A loop with only one iteration so that "break" can be used instead of a lot of nested if's.
  3120.         {
  3121.             if (!g.CurrentFunc) // Not inside a function body, so "Global"/"Local"/"Static" get no special treatment.
  3122.                 break;
  3123.  
  3124.             #define VAR_DECLARE_NONE   0
  3125.             #define VAR_DECLARE_GLOBAL 1
  3126.             #define VAR_DECLARE_LOCAL  2
  3127.             #define VAR_DECLARE_STATIC 3
  3128.             int declare_type;
  3129.             char *cp;
  3130.             if (!strnicmp(aLineText, "Global", 6)) // Checked first because it's more common than the others.
  3131.             {
  3132.                 cp = aLineText + 6; // The character after the declaration word.
  3133.                 declare_type = VAR_DECLARE_GLOBAL;
  3134.             }
  3135.             else if (!strnicmp(aLineText, "Local", 5))
  3136.             {
  3137.                 cp = aLineText + 5; // The character after the declaration word.
  3138.                 declare_type = VAR_DECLARE_LOCAL;
  3139.             }
  3140.             else if (!strnicmp(aLineText, "Static", 6)) // Static also implies local (for functions that default to global).
  3141.             {
  3142.                 cp = aLineText + 6; // The character after the declaration word.
  3143.                 declare_type = VAR_DECLARE_STATIC;
  3144.             }
  3145.             else // It's not the word "global", "local", or static, so no further checking is done.
  3146.                 break;
  3147.  
  3148.             if (*cp && !IS_SPACE_OR_TAB(*cp)) // There is a character following the word local but it's not a space or tab.
  3149.                 break; // It doesn't qualify as being the global or local keyword because it's something like global2.
  3150.             if (*cp && *(cp = omit_leading_whitespace(cp))) // Probably always a true stmt since caller rtrimmed it, but even if not it's handled correctly.
  3151.             {
  3152.                 // Check whether the first character is an operator by seeing if it alone would be a
  3153.                 // valid variable name.  If it's not valid, this doesn't qualify as the global or local
  3154.                 // keyword because it's something like this instead:
  3155.                 // local := xyz
  3156.                 // local += 3
  3157.                 char orig_char = cp[1];
  3158.                 cp[1] = '\0'; // Temporarily terminate.
  3159.                 ResultType result = Var::ValidateName(cp, false, DISPLAY_NO_ERROR);
  3160.                 cp[1] = orig_char; // Undo the termination.
  3161.                 if (!result) // It's probably operator, e.g. local = %var%
  3162.                     break;
  3163.             }
  3164.             else // It's the word "global", "local", "static" by itself.  But only global is valid that way (when it's the first line in the function body).
  3165.             {
  3166.                 // All of the following must be checked to catch back-to-back conflicting declarations such
  3167.                 // as these:
  3168.                 // global x
  3169.                 // global  ; Should be an error because global vars are implied/automatic.
  3170.                 if (declare_type == VAR_DECLARE_GLOBAL && mNextLineIsFunctionBody && g.CurrentFunc->mDefaultVarType == VAR_ASSUME_NONE)
  3171.                 {
  3172.                     g.CurrentFunc->mDefaultVarType = VAR_ASSUME_GLOBAL;
  3173.                     // No further action is required for the word "global" by itself.
  3174.                     return OK;
  3175.                 }
  3176.                 // Otherwise, it's the word "local"/"static" by itself or "global" by itself but that occurs too far down in the body.
  3177.                 return ScriptError(ERR_UNRECOGNIZED_ACTION, aLineText); // Vague error since so rare.
  3178.             }
  3179.             if (mNextLineIsFunctionBody && g.CurrentFunc->mDefaultVarType == VAR_ASSUME_NONE)
  3180.             {
  3181.                 // Both of the above must be checked to catch back-to-back conflicting declarations such
  3182.                 // as these:
  3183.                 // local x
  3184.                 // global y  ; Should be an error because global vars are implied/automatic.
  3185.                 // This line will become first non-directive, non-label line in the function's body.
  3186.  
  3187.                 // If the first non-directive, non-label line in the function's body contains
  3188.                 // the "local" keyword, everything inside this function will assume that variables
  3189.                 // are global unless they are explicitly declared local (this is the opposite of
  3190.                 // the default).  The converse is also true.  UPDATE: "static" must also force ASSUME_LOCAL
  3191.                 // into effect because otherwise statics wouldn't go into the exception list and thus
  3192.                 // wouldn't be properly looked up when they're referenced throughout the function body.
  3193.                 // Therefore, if the first line of the function body is "static MyVar", VAR_DECLARE_LOCAL
  3194.                 // goes into effect permanently, which can be worked around by using the word "global"
  3195.                 // as the first word of the function instead.
  3196.                 g.CurrentFunc->mDefaultVarType = declare_type == VAR_DECLARE_LOCAL ? VAR_ASSUME_GLOBAL : VAR_ASSUME_LOCAL;
  3197.             }
  3198.             else // Since this isn't the first line of the function's body, mDefaultVarType has aleady been set permanently.
  3199.             {
  3200.                 // Seems best to flag errors since they might be an indication to the user that something
  3201.                 // is being done incorrectly in this function, not to mention being a reminder about what
  3202.                 // mode the function is in:
  3203.                 if (g.CurrentFunc->mDefaultVarType == VAR_ASSUME_GLOBAL)
  3204.                 {
  3205.                     if (declare_type == VAR_DECLARE_GLOBAL)
  3206.                         return ScriptError("Global variables do not need to be declared in this function.", aLineText);
  3207.                 }
  3208.                 else // Must be VAR_ASSUME_LOCAL at this stage.
  3209.                     if (declare_type == VAR_DECLARE_LOCAL)
  3210.                         return ScriptError("Local variables do not need to be declared in this function.", aLineText);
  3211.             }
  3212.             // Since above didn't break or return, a variable is being declared as an exception to the
  3213.             // mode specified by mDefaultVarType (except if it's a static, which would be an exception
  3214.             // only if VAR_ASSUME_GLOBAL is in effect, since statics are implicitly local).
  3215.  
  3216.             // If the declare_type is local or global, inversion must be done (i.e. this will be an exception
  3217.             // variable) because otherwise it would have already displayed an "unnecessary declaration" error
  3218.             // and returned above.  But if the declare_type is static, and given that all static variables are
  3219.             // local, inversion is necessary only if the current mode isn't LOCAL:
  3220.             bool is_already_exception, is_exception = (declare_type != VAR_DECLARE_STATIC
  3221.                 || g.CurrentFunc->mDefaultVarType == VAR_ASSUME_GLOBAL); // Above has ensured that NONE can't be in effect by the time we reach the first static.
  3222.             bool open_brace_was_added, belongs_to_if_or_else_or_loop;
  3223.             VarSizeType var_name_length;
  3224.             char *item;
  3225.  
  3226.             for (belongs_to_if_or_else_or_loop = ACT_IS_IF_OR_ELSE_OR_LOOP(mLastLine->mActionType)
  3227.                 , open_brace_was_added = false, item = cp
  3228.                 ; *item;) // FOR EACH COMMA-SEPARATED ITEM IN THE DECLARATION LIST.
  3229.             {
  3230.                 char *item_end = StrChrAny(item, ", \t=:");  // Comma, space or tab, equal-sign, colon.
  3231.                 if (!item_end) // This is probably the last/only variable in the list; e.g. the "x" in "local x"
  3232.                     item_end = item + strlen(item);
  3233.                 var_name_length = (VarSizeType)(item_end - item);
  3234.  
  3235.                 int always_use;
  3236.                 if (is_exception)
  3237.                     always_use = g.CurrentFunc->mDefaultVarType == VAR_ASSUME_GLOBAL ? ALWAYS_USE_LOCAL : ALWAYS_USE_GLOBAL;
  3238.                 else
  3239.                     always_use = ALWAYS_USE_DEFAULT;
  3240.  
  3241.                 Var *var;
  3242.                 if (   !(var = FindOrAddVar(item, var_name_length, always_use, &is_already_exception))   )
  3243.                     return FAIL; // It already displayed the error.
  3244.                 if (is_already_exception) // It was already in the exception list (previously declared).
  3245.                     return ScriptError("Duplicate declaration.", item);
  3246.                 if (var->Type() != VAR_NORMAL || !strlicmp(item, "ErrorLevel", var_name_length)) // Shouldn't be declared either way (global or local).
  3247.                     return ScriptError("Built-in variables must not be declared.", item);
  3248.                 for (int i = 0; i < g.CurrentFunc->mParamCount; ++i) // Search by name to find both global and local declarations.
  3249.                     if (!strlicmp(item, g.CurrentFunc->mParam[i].var->mName, var_name_length))
  3250.                         return ScriptError("Parameters must not be declared.", item);
  3251.                 if (is_exception)
  3252.                 {
  3253.                     if (mFuncExceptionVarCount >= MAX_FUNC_VAR_EXCEPTIONS)
  3254.                         return ScriptError("Too many declarations.", item); // Short message since it's so unlikely.
  3255.                     mFuncExceptionVar[mFuncExceptionVarCount++] = var;
  3256.                 }
  3257.                 if (declare_type == VAR_DECLARE_STATIC)
  3258.                     var->OverwriteAttrib(VAR_ATTRIB_STATIC);
  3259.  
  3260.                 item_end = omit_leading_whitespace(item_end); // Move up to the next comma, assignment-op, or '\0'.
  3261.  
  3262.                 bool convert_the_operator;
  3263.                 switch(*item_end)
  3264.                 {
  3265.                 case ',':  // No initializer is present for this variable, so move on to the next one.
  3266.                     item = omit_leading_whitespace(item_end + 1); // Set "item" for use by the next iteration.
  3267.                     continue; // No further processing needed below.
  3268.                 case '\0': // No initializer is present for this variable, so move on to the next one.
  3269.                     item = item_end; // Set "item" for use by the next iteration.
  3270.                     continue;
  3271.                 case ':':
  3272.                     if (item_end[1] != '=') // Colon with no following '='.
  3273.                         return ScriptError(ERR_UNRECOGNIZED_ACTION, item); // Vague error since so rare.
  3274.                     item_end += 2; // Point to the character after the ":=".
  3275.                     convert_the_operator = false;
  3276.                     break;
  3277.                 case '=': // Here '=' is clearly an assignment not a comparison, so further below it will be converted to :=
  3278.                     ++item_end; // Point to the character after the "=".
  3279.                     convert_the_operator = true;
  3280.                     break;
  3281.                 }
  3282.                 char *right_side_of_operator = item_end; // Save for use by VAR_DECLARE_STATIC below.
  3283.  
  3284.                 // Since above didn't "continue", this declared variable also has an initializer.
  3285.                 // Add that initializer as a separate line to be executed at runtime. Separate lines
  3286.                 // might actually perform better at runtime because most initializers tend to be simple
  3287.                 // literals or variables that are simplified into non-expressions at runtime. In addition,
  3288.                 // items without an initializer are omitted, further improving runtime performance.
  3289.                 // However, the following must be done ONLY after having done the FindOrAddVar()
  3290.                 // above, since that may have changed this variable to a non-default type (local or global).
  3291.                 // But what about something like "global x, y=x"? Even that should work as long as x
  3292.                 // appears in the list prior to initializers that use it.
  3293.                 // Now, find the comma (or terminator) that marks the end of this sub-statement.
  3294.                 // The search must exclude commas that are inside quoted/literal strings and those that
  3295.                 // are inside parentheses (chiefly those of function-calls, but possibly others).
  3296.  
  3297.                 for (in_quotes = false, open_parens = 0; *item_end; ++item_end) // FIND THE NEXT "REAL" COMMA.
  3298.                 {
  3299.                     if (*item_end == ',') // This is outside the switch() further below so that its "break" can get out of the loop.
  3300.                     {
  3301.                         if (!in_quotes && open_parens < 1) // A delimiting comma other than one in a sub-statement or function. Shouldn't need to worry about unquoted escaped commas since they don't make sense in a declaration list.
  3302.                             break;
  3303.                         // Otherwise, its a quoted/literal comma or one in parentheses (such as function-call).
  3304.                         continue; // Continue past it to look for the correct comma.
  3305.                     }
  3306.                     switch (*item_end)
  3307.                     {
  3308.                     case '"': // There are sections similar this one later below; so see them for comments.
  3309.                         in_quotes = !in_quotes;
  3310.                         break;
  3311.                     case '(':
  3312.                         if (!in_quotes) // Literal parentheses inside a quoted string should not be counted for this purpose.
  3313.                             ++open_parens;
  3314.                         break;
  3315.                     case ')':
  3316.                         if (!in_quotes)
  3317.                         {
  3318.                             if (!open_parens)
  3319.                                 return ScriptError(ERR_MISSING_OPEN_PAREN, item);
  3320.                             --open_parens;
  3321.                         }
  3322.                         break;
  3323.                     //default: some other character; just have the loop skip over it.
  3324.                     }
  3325.                 } // for() to look for the ending comma or terminator of this sub-statement.
  3326.                 if (open_parens) // At least one '(' is never closed.
  3327.                     return ScriptError(ERR_MISSING_CLOSE_PAREN, item); // Use "item" because the problem is probably somewhere after that point in the declaration list.
  3328.                 if (in_quotes)
  3329.                     return ScriptError(ERR_MISSING_CLOSE_QUOTE, item);
  3330.  
  3331.                 // Above has now found the final comma of this sub-statement (or the terminator if there is no comma).
  3332.                 char *terminate_here = omit_trailing_whitespace(item, item_end-1) + 1; // v1.0.47.02: Fix the fact that "x=5 , y=6" would preserve the whitespace at the end of "5".  It also fixes wrongly showing a syntax error for things like: static d="xyz"  , e = 5
  3333.                 char orig_char = *terminate_here;
  3334.                 *terminate_here = '\0'; // Temporarily terminate (it might already be the terminator, but that's harmless).
  3335.  
  3336.                 if (declare_type == VAR_DECLARE_STATIC) // v1.0.46: Support simple initializers for static variables.
  3337.                 {
  3338.                     // The following is similar to the code used to support default values for function parameters.
  3339.                     // So maybe maintain them together.
  3340.                     right_side_of_operator = omit_leading_whitespace(right_side_of_operator);
  3341.                     if (!stricmp(right_side_of_operator, "false"))
  3342.                         var->Assign("0");
  3343.                     else if (!stricmp(right_side_of_operator, "true"))
  3344.                         var->Assign("1");
  3345.                     else // The only other supported initializers are "string", integers, and floats.
  3346.                     {
  3347.                         // Vars could be supported here via FindVar(), but only globals ABOVE this point in
  3348.                         // the script would be supported (since other globals don't exist yet; in fact, even
  3349.                         // those that do exist don't have any contents yet, so it would be pointless). So it
  3350.                         // seems best to wait until full/comprehesive support for expressions is
  3351.                         // studied/designed for both statics and parameter-default-values.
  3352.                         if (*right_side_of_operator == '"' && terminate_here[-1] == '"') // Quoted/literal string.
  3353.                         {
  3354.                             ++right_side_of_operator; // Omit the opening-quote from further consideration.
  3355.                             terminate_here[-1] = '\0'; // Remove the close-quote from further consideration.
  3356.                             ConvertEscapeSequences(right_side_of_operator, g_EscapeChar, false); // Raw escape sequences like `n haven't been converted yet, so do it now.
  3357.                             // Convert all pairs of quotes into single literal quotes:
  3358.                             StrReplace(right_side_of_operator, "\"\"", "\"", SCS_SENSITIVE);
  3359.                         }
  3360.                         else // It's not a quoted string (nor the empty string); or it has a missing ending quote (rare).
  3361.                         {
  3362.                             if (!IsPureNumeric(right_side_of_operator, true, false, true)) // It's not a number, and since we're here it's not a quoted/literal string either.
  3363.                                 return ScriptError("Unsupported static initializer.", right_side_of_operator);
  3364.                             //else it's an int or float, so just assign the numeric string itself (there
  3365.                             // doesn't seem to be any need to convert it to float/int first, though that would
  3366.                             // make things more consistent such as storing .1 as 0.1).
  3367.                         }
  3368.                         if (*right_side_of_operator) // It can be "" in cases such as "" being specified literally in the script, in which case nothing needs to be done because all variables start off as "".
  3369.                             var->Assign(right_side_of_operator);
  3370.                     }
  3371.                 }
  3372.                 else // A non-static initializer, so a line of code must be produced that will executed at runtime every time the function is called.
  3373.                 {
  3374.                     char *line_to_add;
  3375.                     if (convert_the_operator) // Convert first '=' in item to be ":=".
  3376.                     {
  3377.                         // Prevent any chance of overflow by using new_buf (overflow might otherwise occur in cases
  3378.                         // such as this sub-statement being the very last one in the declaration list, and being
  3379.                         // at the limit of the buffer's capacity).
  3380.                         char new_buf[LINE_SIZE]; // Using so much stack space here and in caller seems unlikely to affect performance, so _alloca seems unlikely to help.
  3381.                         StrReplace(strcpy(new_buf, item), "=", ":=", SCS_SENSITIVE, 1); // Can't overflow because there's only one replacement and we know item's length can't be that close to the capacity limit.
  3382.                         line_to_add = new_buf;
  3383.                     }
  3384.                     else
  3385.                         line_to_add = item;
  3386.                     if (belongs_to_if_or_else_or_loop && !open_brace_was_added) // v1.0.46.01: Put braces to allow initializers to work even directly under an IF/ELSE/LOOP.  Note that the braces aren't added or needed for static initializers.
  3387.                     {
  3388.                         if (!AddLine(ACT_BLOCK_BEGIN))
  3389.                             return FAIL;
  3390.                         open_brace_was_added = true;
  3391.                     }
  3392.                     // Call Parse() vs. AddLine() because it detects and optimizes simple assignments into
  3393.                     // non-exprssions for faster runtime execution.
  3394.                     if (!ParseAndAddLine(line_to_add)) // For simplicity and maintainability, call self rather than trying to set things up properly to stay in self.
  3395.                         return FAIL; // Above already displayed the error.
  3396.                 }
  3397.  
  3398.                 *terminate_here = orig_char; // Undo the temporary termination.
  3399.                 // Set "item" for use by the next iteration:
  3400.                 item = (*item_end == ',') // i.e. it's not the terminator and thus not the final item in the list.
  3401.                     ? omit_leading_whitespace(item_end + 1)
  3402.                     : item_end; // It's the terminator, so let the loop detect that to finish.
  3403.             } // for() each item in the declaration list.
  3404.             if (open_brace_was_added)
  3405.                 if (!AddLine(ACT_BLOCK_END))
  3406.                     return FAIL;
  3407.             return OK;
  3408.         } // single-iteration for-loop
  3409.  
  3410.         // Since above didn't return, it's not a declaration such as "global MyVar".
  3411.         if (   !(end_marker = ParseActionType(action_name, aLineText, true))   )
  3412.             return FAIL; // It already displayed the error.
  3413.     }
  3414.     
  3415.     // Above has ensured that end_marker is the address of the last character of the action name,
  3416.     // or NULL if there is no action name.
  3417.     // Find the arguments (not to be confused with exec_params) of this action, if it has any:
  3418.     char *action_args = end_marker ? omit_leading_whitespace(end_marker + 1) : aLineText;
  3419.     // Now action_args is either the first delimiter or the first parameter (if the optional first
  3420.     // delimiter was omitted).
  3421.     bool add_openbrace_afterward = false; // v1.0.41: Set default for use in supporting brace in "if (expr) {" and "Loop {".
  3422.  
  3423.     if (*action_args == g_delimiter)
  3424.     {
  3425.         // Since there's a comma, don't change aActionType because if it's ACT_INVALID, it should stay that way
  3426.         // so that "something, += 4" is not a valid assignment or other operator, but should still be checked
  3427.         // against the list of commands to see if it's something like "MsgBox, += 4" (in this case, a script may
  3428.         // use the comma to avoid ambiguity).
  3429.         // Find the start of the next token (or its ending delimiter if the token is blank such as ", ,"):
  3430.         for (++action_args; IS_SPACE_OR_TAB(*action_args); ++action_args);
  3431.     }
  3432.     else if (!aActionType && !aOldActionType) // i.e. the caller hasn't yet determined this line's action type.
  3433.     {
  3434.         if (!stricmp(action_name, "IF")) // It's an IF-statement.
  3435.         {
  3436.             /////////////////////////////////////
  3437.             // Detect all types of IF-statements.
  3438.             /////////////////////////////////////
  3439.             char *operation, *next_word;
  3440.             if (*action_args == '(') // i.e. if (expression)
  3441.             {
  3442.                 // To support things like the following, the outermost enclosing parentheses are not removed:
  3443.                 // if (x < 3) or (x > 6)
  3444.                 // Also note that although the expression must normally start with an open-parenthesis to be
  3445.                 // recognized as ACT_IFEXPR, it need not end in a close-paren; e.g. if (x = 1) or !done.
  3446.                 // If these or any other parentheses are unbalanced, it will caught further below.
  3447.                 aActionType = ACT_IFEXPR; // Fixed for v1.0.31.01.
  3448.             }
  3449.             else // Generic or indeterminate IF-statement, so find out what type it is.
  3450.             {
  3451.                 DEFINE_END_FLAGS
  3452.                 // Skip over the variable name so that the "is" and "is not" operators are properly supported:
  3453.                 if (   !(operation = StrChrAny(action_args, end_flags))   )
  3454.                     operation = action_args + strlen(action_args); // Point it to the NULL terminator instead.
  3455.                 else
  3456.                     operation = omit_leading_whitespace(operation);
  3457.  
  3458.                 // v1.0.42: Fix "If not Installed" not be seen as "If var-named-'not' in MatchList", being
  3459.                 // careful not to break "If NotInstalled in MatchList".  The following are also fixed in
  3460.                 // a similar way:
  3461.                 // If not BetweenXXX
  3462.                 // If not ContainsXXX
  3463.                 bool first_word_is_not = !strnicmp(action_args, "Not", 3) && strchr(end_flags, action_args[3]);
  3464.  
  3465.                 switch (*operation)
  3466.                 {
  3467.                 case '=': // But don't allow == to be "Equals" since the 2nd '=' might be literal.
  3468.                     aActionType = ACT_IFEQUAL;
  3469.                     break;
  3470.                 case '<':
  3471.                     // Note: User can use whitespace to differentiate a literal symbol from
  3472.                     // part of an operator, e.g. if var1 < =  <--- char is literal
  3473.                     switch(operation[1])
  3474.                     {
  3475.                     case '=': aActionType = ACT_IFLESSOREQUAL; operation[1] = ' '; break;
  3476.                     case '>': aActionType = ACT_IFNOTEQUAL; operation[1] = ' '; break;
  3477.                     default:  aActionType = ACT_IFLESS;  // i.e. some other symbol follows '<'
  3478.                     }
  3479.                     break;
  3480.                 case '>': // Don't allow >< to be NotEqual since the '<' might be intended as a literal part of an arg.
  3481.                     if (operation[1] == '=')
  3482.                     {
  3483.                         aActionType = ACT_IFGREATEROREQUAL;
  3484.                         operation[1] = ' '; // Remove it from so that it won't be considered by later parsing.
  3485.                     }
  3486.                     else
  3487.                         aActionType = ACT_IFGREATER;
  3488.                     break;
  3489.                 case '!':
  3490.                     if (operation[1] == '=')
  3491.                     {
  3492.                         aActionType = ACT_IFNOTEQUAL;
  3493.                         operation[1] = ' '; // Remove it from so that it won't be considered by later parsing.
  3494.                     }
  3495.                     else
  3496.                         // To minimize the times where expressions must have an outer set of parentheses,
  3497.                         // assume all unknown operators are expressions, e.g. "if !var"
  3498.                         aActionType = ACT_IFEXPR;
  3499.                     break;
  3500.                 case 'b': // "Between"
  3501.                 case 'B':
  3502.                     // Must fall back to ACT_IFEXPR, otherwise "if not var_name_beginning_with_b" is a syntax error.
  3503.                     if (first_word_is_not || strnicmp(operation, "between", 7))
  3504.                         aActionType = ACT_IFEXPR;
  3505.                     else
  3506.                     {
  3507.                         aActionType = ACT_IFBETWEEN;
  3508.                         // Set things up to be parsed as args further down.  A delimiter is inserted later below:
  3509.                         memset(operation, ' ', 7);
  3510.                     }
  3511.                     break;
  3512.                 case 'c': // "Contains"
  3513.                 case 'C':
  3514.                     // Must fall back to ACT_IFEXPR, otherwise "if not var_name_beginning_with_c" is a syntax error.
  3515.                     if (first_word_is_not || strnicmp(operation, "contains", 8))
  3516.                         aActionType = ACT_IFEXPR;
  3517.                     else
  3518.                     {
  3519.                         aActionType = ACT_IFCONTAINS;
  3520.                         // Set things up to be parsed as args further down.  A delimiter is inserted later below:
  3521.                         memset(operation, ' ', 8);
  3522.                     }
  3523.                     break;
  3524.                 case 'i':  // "is" or "is not"
  3525.                 case 'I':
  3526.                     switch (toupper(operation[1]))
  3527.                     {
  3528.                     case 's':  // "IS"
  3529.                     case 'S':
  3530.                         if (first_word_is_not)        // v1.0.45: Had forgotten to fix this one with the others,
  3531.                             aActionType = ACT_IFEXPR; // so now "if not is_something" and "if not is_something()" work.
  3532.                         else
  3533.                         {
  3534.                             next_word = omit_leading_whitespace(operation + 2);
  3535.                             if (strnicmp(next_word, "not", 3))
  3536.                                 aActionType = ACT_IFIS;
  3537.                             else
  3538.                             {
  3539.                                 aActionType = ACT_IFISNOT;
  3540.                                 // Remove the word "not" to set things up to be parsed as args further down.
  3541.                                 memset(next_word, ' ', 3);
  3542.                             }
  3543.                             operation[1] = ' '; // Remove the 'S' in "IS".  'I' is replaced with ',' later below.
  3544.                         }
  3545.                         break;
  3546.                     case 'n':  // "IN"
  3547.                     case 'N':
  3548.                         if (first_word_is_not)
  3549.                             aActionType = ACT_IFEXPR;
  3550.                         else
  3551.                         {
  3552.                             aActionType = ACT_IFIN;
  3553.                             operation[1] = ' '; // Remove the 'N' in "IN".  'I' is replaced with ',' later below.
  3554.                         }
  3555.                         break;
  3556.                     default:
  3557.                         // v1.0.35.01 It must fall back to ACT_IFEXPR, otherwise "if not var_name_beginning_with_i"
  3558.                         // is a syntax error.
  3559.                         aActionType = ACT_IFEXPR;
  3560.                     } // switch()
  3561.                     break;
  3562.                 case 'n':  // It's either "not in", "not between", or "not contains"
  3563.                 case 'N':
  3564.                     // Must fall back to ACT_IFEXPR, otherwise "if not var_name_beginning_with_n" is a syntax error.
  3565.                     if (strnicmp(operation, "not", 3))
  3566.                         aActionType = ACT_IFEXPR;
  3567.                     else
  3568.                     {
  3569.                         // Remove the "NOT" separately in case there is more than one space or tab between
  3570.                         // it and the following word, e.g. "not   between":
  3571.                         memset(operation, ' ', 3);
  3572.                         next_word = omit_leading_whitespace(operation + 3);
  3573.                         if (!strnicmp(next_word, "in", 2))
  3574.                         {
  3575.                             aActionType = ACT_IFNOTIN;
  3576.                             memset(next_word, ' ', 2);
  3577.                         }
  3578.                         else if (!strnicmp(next_word, "between", 7))
  3579.                         {
  3580.                             aActionType = ACT_IFNOTBETWEEN;
  3581.                             memset(next_word, ' ', 7);
  3582.                         }
  3583.                         else if (!strnicmp(next_word, "contains", 8))
  3584.                         {
  3585.                             aActionType = ACT_IFNOTCONTAINS;
  3586.                             memset(next_word, ' ', 8);
  3587.                         }
  3588.                     }
  3589.                     break;
  3590.  
  3591.                 default: // To minimize the times where expressions must have an outer set of parentheses, assume all unknown operators are expressions.
  3592.                     aActionType = ACT_IFEXPR;
  3593.                 } // switch()
  3594.             } // Detection of type of IF-statement.
  3595.  
  3596.             if (aActionType == ACT_IFEXPR) // There are various ways above for aActionType to become ACT_IFEXPR.
  3597.             {
  3598.                 // Since this is ACT_IFEXPR, action_args is known not to be an empty string, which is relied on below.
  3599.                 char *action_args_last_char = action_args + strlen(action_args) - 1; // Shouldn't be a whitespace char since those should already have been removed at an earlier stage.
  3600.                 if (*action_args_last_char == '{') // This is an if-expression statement with an open-brace on the same line.
  3601.                 {
  3602.                     *action_args_last_char = '\0';
  3603.                     rtrim(action_args, action_args_last_char - action_args);  // Remove the '{' and all its whitespace from further consideration.
  3604.                     add_openbrace_afterward = true;
  3605.                 }
  3606.             }
  3607.             else // It's a IF-statement, but a traditional/non-expression one.
  3608.             {
  3609.                 // Set things up to be parsed as args later on.
  3610.                 *operation = g_delimiter;
  3611.                 if (aActionType == ACT_IFBETWEEN || aActionType == ACT_IFNOTBETWEEN)
  3612.                 {
  3613.                     // I decided against the syntax "if var between 3,8" because the gain in simplicity
  3614.                     // and the small avoidance of ambiguity didn't seem worth the cost in terms of readability.
  3615.                     for (next_word = operation;;)
  3616.                     {
  3617.                         if (   !(next_word = strcasestr(next_word, "and"))   )
  3618.                             return ScriptError("BETWEEN requires the word AND.", aLineText); // Seems too rare a thing to warrant falling back to ACT_IFEXPR for this.
  3619.                         if (strchr(" \t", *(next_word - 1)) && strchr(" \t", *(next_word + 3)))
  3620.                         {
  3621.                             // Since there's a space or tab on both sides, we know this is the correct "and",
  3622.                             // i.e. not one contained within one of the parameters.  Examples:
  3623.                             // if var between band and cat  ; Don't falsely detect "band"
  3624.                             // if var betwwen Andy and David  ; Don't falsely detect "Andy".
  3625.                             // Replace the word AND with a delimiter so that it will be parsed correctly later:
  3626.                             *next_word = g_delimiter;
  3627.                             *(next_word + 1) = ' ';
  3628.                             *(next_word + 2) = ' ';
  3629.                             break;
  3630.                         }
  3631.                         else
  3632.                             next_word += 3;  // Skip over this false "and".
  3633.                     } // for()
  3634.                 } // ACT_IFBETWEEN
  3635.             } // aActionType != ACT_IFEXPR
  3636.         }
  3637.         else // It isn't an IF-statement, so check for assignments/operators that determine that this line isn't one that starts with a named command.
  3638.         {
  3639.             //////////////////////////////////////////////////////
  3640.             // Detect operators and assignments such as := and +=
  3641.             //////////////////////////////////////////////////////
  3642.             // This section is done before the section that checks whether action_name is a valid command
  3643.             // because it avoids ambiguity in a line such as the following:
  3644.             //    Input = test  ; Would otherwise be confused with the Input command.
  3645.             // But there may be times when a line like this is used:
  3646.             //    MsgBox =  ; i.e. the equals is intended to be the first parameter, not an operator.
  3647.             // In the above case, the user can provide the optional comma to avoid the ambiguity:
  3648.             //    MsgBox, =
  3649.             char action_args_2nd_char = action_args[1];
  3650.             bool convert_pre_inc_or_dec = false; // Set default.
  3651.  
  3652.             switch(*action_args)
  3653.             {
  3654.             case '=': // i.e. var=value (old-style assignment)
  3655.                 aActionType = ACT_ASSIGN;
  3656.                 break;
  3657.             case ':':
  3658.                 // v1.0.40: Allow things like "MsgBox :: test" to be valid by insisting that '=' follows ':'.
  3659.                 if (action_args_2nd_char == '=') // i.e. :=
  3660.                     aActionType = ACT_ASSIGNEXPR;
  3661.                 break;
  3662.             case '+':
  3663.                 // Support for ++i (and in the next case, --i).  In these cases, action_name must be either
  3664.                 // "+" or "-", and the first character of action_args must match it.
  3665.                 if ((convert_pre_inc_or_dec = action_name[0] == '+' && !action_name[1]) // i.e. the pre-increment operator; e.g. ++index.
  3666.                     || action_args_2nd_char == '=') // i.e. x+=y (by contrast, post-increment is recognized only after we check for a command name to cut down on ambiguity).
  3667.                     aActionType = ACT_ADD;
  3668.                 break;
  3669.             case '-':
  3670.                 // Do a complete validation/recognition of the operator to allow a line such as the following,
  3671.                 // which omits the first optional comma, to still be recognized as a command rather than a
  3672.                 // variable-with-operator:
  3673.                 // SetBatchLines -1
  3674.                 if ((convert_pre_inc_or_dec = action_name[0] == '-' && !action_name[1]) // i.e. the pre-decrement operator; e.g. --index.
  3675.                     || action_args_2nd_char == '=') // i.e. x-=y  (by contrast, post-decrement is recognized only after we check for a command name to cut down on ambiguity).
  3676.                     aActionType = ACT_SUB;
  3677.                 break;
  3678.             case '*':
  3679.                 if (action_args_2nd_char == '=') // i.e. *=
  3680.                     aActionType = ACT_MULT;
  3681.                 break;
  3682.             case '/':
  3683.                 if (action_args_2nd_char == '=') // i.e. /=
  3684.                     aActionType = ACT_DIV;
  3685.                 // ACT_DIV is different than //= and // because ACT_DIV supports floating point inputs by yielding
  3686.                 // a floating point result (i.e. it doesn't Floor() the result when the inputs are floats).
  3687.                 else if (action_args_2nd_char == '/' && action_args[2] == '=') // i.e. //=
  3688.                     aActionType = ACT_EXPRESSION; // Mark this line as a stand-alone expression.
  3689.                 break;
  3690.             case '.':
  3691.             case '|':
  3692.             case '&':
  3693.             case '^':
  3694.                 if (action_args_2nd_char == '=') // i.e. .= and |= and &= and ^=
  3695.                     aActionType = ACT_EXPRESSION; // Mark this line as a stand-alone expression.
  3696.                 break;
  3697.             //case '?': Stand-alone ternary such as true ? fn1() : fn2().  These are rare so are
  3698.             // checked later, only after action_name has been checked to see if it's a valid command.
  3699.             case '>':
  3700.             case '<':
  3701.                 if (action_args_2nd_char == *action_args && action_args[2] == '=') // i.e. >>= and <<=
  3702.                     aActionType = ACT_EXPRESSION; // Mark this line as a stand-alone expression.
  3703.                 break;
  3704.             //default: Leave aActionType set to ACT_INVALID. This also covers case '\0' in case that's possible.
  3705.             } // switch()
  3706.  
  3707.             if (aActionType) // An assignment or other type of action was discovered above.
  3708.             {
  3709.                 if (convert_pre_inc_or_dec) // Set up pre-ops like ++index and --index to be parsed properly later.
  3710.                 {
  3711.                     // The following converts:
  3712.                     // ++x -> EnvAdd x,1 (not really "EnvAdd" per se; but ACT_ADD).
  3713.                     // Set action_args to be the word that occurs after the ++ or --:
  3714.                     action_args = omit_leading_whitespace(++action_args); // Though there generally isn't any.
  3715.                     if (StrChrAny(action_args, EXPR_ALL_SYMBOLS ".")) // Support things like ++Var ? f1() : f2() and ++Var /= 5. Don't need strstr(action_args, " ?") because the search already looks for ':'.
  3716.                         aActionType = ACT_EXPRESSION; // Mark this line as a stand-alone expression.
  3717.                     else
  3718.                     {
  3719.                         // Set up aLineText and action_args to be parsed later on as a list of two parameters:
  3720.                         // The variable name followed by the amount to be added or subtracted (e.g. "ScriptVar, 1").
  3721.                         // We're not changing the length of aLineText by doing this, so it should be large enough:
  3722.                         size_t new_length = strlen(action_args);
  3723.                         // Since action_args is just a pointer into the aLineText buffer (which caller has ensured
  3724.                         // is modifiable), use memmove() so that overlapping source & dest are properly handled:
  3725.                         memmove(aLineText, action_args, new_length + 1); // +1 to include the zero terminator.
  3726.                         // Append the second param, which is just "1" since the ++ and -- only inc/dec by 1:
  3727.                         aLineText[new_length++] = g_delimiter;
  3728.                         aLineText[new_length++] = '1';
  3729.                         aLineText[new_length] = '\0';
  3730.                     }
  3731.                 }
  3732.                 else if (aActionType != ACT_EXPRESSION) // i.e. it's ACT_ASSIGN/ASSIGNEXPR/ADD/SUB/MULT/DIV
  3733.                 {
  3734.                     if (aActionType != ACT_ASSIGN) // i.e. it's ACT_ASSIGNEXPR/ADD/SUB/MULT/DIV
  3735.                     {
  3736.                         // Find the first non-function comma, which in the case of ACT_ADD/SUB can be
  3737.                         // either a statement-separator comma (expression) or the time units arg.
  3738.                         // Reasons for this:
  3739.                         // 1) ACT_ADD/SUB: Need to distinguish compound statements from date/time math;
  3740.                         //    e.g. "x+=1, y+=2" should be marked as a stand-alone expression, not date math.
  3741.                         // 2) ACT_ASSIGNEXPR/MULT/DIV (and ACT_ADD/SUB for that matter): Need to make
  3742.                         //    comma-separated sub-expressions into one big ACT_EXPRESSION so that the
  3743.                         //    leftmost sub-expression will get evaluated prior to the others (for consistency
  3744.                         //    and as documented).  However, this has some side-effects, such as making
  3745.                         //    the leftmost /= operator into true division rather than ENV_DIV behavior,
  3746.                         //    and treating blanks as errors in math expressions when otherwise ENV_MULT
  3747.                         //    would treat them as zero.
  3748.                         // ALSO: ACT_ASSIGNEXPR/ADD/SUB/MULT/DIV are made into ACT_EXPRESSION *only* when multi-
  3749.                         // statement commas are present because the following legacy behaviors must be retained:
  3750.                         // 1) Math treatment of blanks as zero in ACT_ADD/SUB/etc.
  3751.                         // 2) EnvDiv's special behavior, which is different than both true divide and floor divide.
  3752.                         // 3) Possibly add/sub's date/time math.
  3753.                         // 4) For performance, don't want trivial assignments to become ACT_EXPRESSION.
  3754.                         char *cp;
  3755.                         for (in_quotes = false, open_parens = 0, cp = action_args + 2; *cp; ++cp)
  3756.                         {
  3757.                             switch (*cp)
  3758.                             {
  3759.                             case '"': // This is whole section similar to another one later below, so see it for comments.
  3760.                                 in_quotes = !in_quotes;
  3761.                                 break;
  3762.                             case '(':
  3763.                                 if (!in_quotes) // Literal parentheses inside a quoted string should not be counted for this purpose.
  3764.                                     ++open_parens;
  3765.                                 break;
  3766.                             case ')':
  3767.                                 if (!in_quotes)
  3768.                                     --open_parens;
  3769.                                 break;
  3770.                             }
  3771.                             if (*cp == g_delimiter && !in_quotes && open_parens < 1) // A delimiting comma other than one in a sub-statement or function. Shouldn't need to worry about unquoted escaped commas since they don't make sense with += and -=.
  3772.                             {
  3773.                                 if (aActionType == ACT_ADD || aActionType == ACT_SUB)
  3774.                                 {
  3775.                                     cp = omit_leading_whitespace(cp + 1);
  3776.                                     if (StrChrAny(cp, EXPR_ALL_SYMBOLS ".")) // Don't need strstr(cp, " ?") because the search already looks for ':'.
  3777.                                         aActionType = ACT_EXPRESSION; // It's clearly an expression not a word like Days or %VarContainingTheWordDays%.
  3778.                                     //else it's probably date/time math, so leave it as-is.
  3779.                                 }
  3780.                                 else // ACT_ASSIGNEXPR/MULT/DIV, for which any non-function comma qualifies it as multi-statement.
  3781.                                     aActionType = ACT_EXPRESSION;
  3782.                                 break;
  3783.                             }
  3784.                         }
  3785.                     }
  3786.                     if (aActionType != ACT_EXPRESSION) // The above didn't make it a stand-alone expression.
  3787.                     {
  3788.                         // The following converts:
  3789.                         // x+=2 -> ACT_ADD x, 2.
  3790.                         // x:=2 -> ACT_ASSIGNEXPR, x, 2
  3791.                         // etc.
  3792.                         // But post-inc/dec are recognized only after we check for a command name to cut down on ambiguity
  3793.                         *action_args = g_delimiter; // Replace the =,+,-,:,*,/ with a delimiter for later parsing.
  3794.                         if (aActionType != ACT_ASSIGN) // i.e. it's not just a plain equal-sign (which has no 2nd char).
  3795.                             action_args[1] = ' '; // Remove the "=" from consideration.
  3796.                     }
  3797.                 }
  3798.                 //else it's already an isolated expression, so no changes are desired.
  3799.                 action_args = aLineText; // Since this is an assignment and/or expression, use the line's full text for later parsing.
  3800.             } // if (aActionType)
  3801.         } // Handling of assignments and other operators.
  3802.     }
  3803.     //else aActionType was already determined by the caller.
  3804.  
  3805.     // Now the above has ensured that action_args is the first parameter itself, or empty-string if none.
  3806.     // If action_args now starts with a delimiter, it means that the first param is blank/empty.
  3807.  
  3808.     if (!aActionType && !aOldActionType) // Caller nor logic above has yet determined the action.
  3809.         if (   !(aActionType = ConvertActionType(action_name))   ) // Is this line a command?
  3810.             aOldActionType = ConvertOldActionType(action_name);    // If not, is it an old-command?
  3811.  
  3812.     if (!aActionType && !aOldActionType) // Didn't find any action or command in this line.
  3813.     {
  3814.         // v1.0.41: Support one-true brace style even if there's no space, but make it strict so that
  3815.         // things like "Loop{ string" are reported as errors (in case user intended a file-pattern loop).
  3816.         if (!stricmp(action_name, "Loop{") && !*action_args)
  3817.         {
  3818.             aActionType = ACT_LOOP;
  3819.             add_openbrace_afterward = true;
  3820.         }
  3821.         else if (*action_args == '?' && IS_SPACE_OR_TAB(action_args[1]) // '?' currently requires a trailing space or tab because variable names can contain '?' (except '?' by itself).  For simplicty, no NBSP check.
  3822.             || strchr(EXPR_ALL_SYMBOLS ".", *action_args))
  3823.         {
  3824.             char *question_mark;
  3825.             if ((*action_args == '+' || *action_args == '-') && action_args[1] == *action_args) // Post-inc/dec. See comments further below.
  3826.             {
  3827.                 if (action_args[2]) // i.e. if the ++ and -- isn't the last thing; e.g. x++ ? fn1() : fn2() ... Var++ //= 2
  3828.                     aActionType = ACT_EXPRESSION; // Mark this line as a stand-alone expression.
  3829.                 else
  3830.                 {
  3831.                     // The logic here allows things like IfWinActive-- to be seen as commands even without
  3832.                     // a space before the -- or ++.  For backward compatibility and code simplicity, it seems
  3833.                     // best to keep that behavior rather than distinguishing between Command-- and Command --.
  3834.                     // In any case, "Command --" should continue to be seen as a command regardless of what
  3835.                     // changes are ever made.  That's why this section occurs below the command-name lookup.
  3836.                     // The following converts x++ to "ACT_ADD x,1".
  3837.                     aActionType = (*action_args == '+') ? ACT_ADD : ACT_SUB;
  3838.                     *action_args = g_delimiter;
  3839.                     action_args[1] = '1';
  3840.                 }
  3841.                 action_args = aLineText; // Since this is an assignment and/or expression, use the line's full text for later parsing.
  3842.             }
  3843.             else if (*action_args == '?' // Don't need a leading space if first char is '?' (though should have a trailing, but for simplicity it isn't checked).
  3844.                 || (question_mark = strstr(action_args, " ? ")) && strchr(question_mark, ':')) // Rough check (see comments below). Relies on short-circuit boolean order.
  3845.             {
  3846.                 // To avoid hindering load-time error detection such as misspelled command names, allow stand-alone
  3847.                 // expressions only for things that can produce a side-effect (currently only ternaries like
  3848.                 // the ones mentioned later below need to be checked since the following other things were
  3849.                 // previously recognized as ACT_EXPRESSION if appropriate: function-calls, post- and
  3850.                 // pre-inc/dec (++/--), and assignment operators like := += *= (though these don't necessarily
  3851.                 // need to be ACT_EXPRESSION to support multi-statement; they can be ACT_ASSIGNEXPR, ACT_ADD, etc.
  3852.                 // and still support comma-separated statements.
  3853.                 // Stand-alone ternaries are checked for here rather than earlier to allow a command name
  3854.                 // (of present) to take precedence (since stand-alone ternaries seem much rarer than 
  3855.                 // "Command ? something" such as "MsgBox ? something".  Could also check for a colon somewhere
  3856.                 // to the right if further ambiguity-resolution is ever needed.  Also, a stand-alone ternary
  3857.                 // should have at least one function-call and/or assignment; otherwise it would serve no purpose.
  3858.                 // A line may contain a stand-alone ternary operator to call functions that have side-effects
  3859.                 // or perform assignments.  For example:
  3860.                 //    IsDone ? fn1() : fn2()
  3861.                 //    3 > 2 ? x:=1 : y:=1
  3862.                 //    (3 > 2) ... not supported due to overlap with continuation sections.
  3863.                 aActionType = ACT_EXPRESSION; // Mark this line as a stand-alone expression.
  3864.                 action_args = aLineText; // Since this is an assignment and/or expression, use the line's full text for later parsing.
  3865.             }
  3866.             //else leave it as an unknown action to avoid hindering load-time error detection.
  3867.             // In other words, don't be too permissive about what gets marked as a stand-alone expression.
  3868.         }
  3869.         if (!aActionType) // Above still didn't find a valid action (i.e. check aActionType again in case the above changed it).
  3870.         {
  3871.             if (*action_args == '(') // v1.0.46.11: Recognize as multi-statements that start with a function, like "fn(), x:=4".  v1.0.47.03: Removed the following check to allow a close-brace to be followed by a comma-less function-call: strchr(action_args, g_delimiter).
  3872.             {
  3873.                 aActionType = ACT_EXPRESSION; // Mark this line as a stand-alone expression.
  3874.                 action_args = aLineText; // Since this is a function-call followed by a comma and some other expression, use the line's full text for later parsing.
  3875.             }
  3876.             else
  3877.                 // v1.0.40: Give a more specific error message now now that hotkeys can make it here due to
  3878.                 // the change that avoids the need to escape double-colons:
  3879.                 return ScriptError(strstr(aLineText, HOTKEY_FLAG) ? "Invalid hotkey." : ERR_UNRECOGNIZED_ACTION, aLineText);
  3880.         }
  3881.     }
  3882.  
  3883.     Action &this_action = aActionType ? g_act[aActionType] : g_old_act[aOldActionType];
  3884.  
  3885.     //////////////////////////////////////////////////////////////////////////////////////////////
  3886.     // Handle escaped-sequences (escaped delimiters and all others except variable deref symbols).
  3887.     // This section must occur after all other changes to the pointer value action_args have
  3888.     // occurred above.
  3889.     //////////////////////////////////////////////////////////////////////////////////////////////
  3890.     // The size of this relies on the fact that caller made sure that aLineText isn't
  3891.     // longer than LINE_SIZE.  Also, it seems safer to use char rather than bool, even
  3892.     // though on most compilers they're the same size.  Char is always of size 1, but bool
  3893.     // can be bigger depending on platform/compiler:
  3894.     char literal_map[LINE_SIZE];
  3895.     ZeroMemory(literal_map, sizeof(literal_map));  // Must be fully zeroed for this purpose.
  3896.     if (aLiteralMap)
  3897.     {
  3898.         // Since literal map is NOT a string, just an array of char values, be sure to
  3899.         // use memcpy() vs. strcpy() on it.  Also, caller's aLiteralMap starts at aEndMarker,
  3900.         // so adjust it so that it starts at the newly found position of action_args instead:
  3901.         int map_offset = (int)(action_args - end_marker);  // end_marker is known not to be NULL when aLiteralMap is non-NULL.
  3902.         int map_length = (int)(aLiteralMapLength - map_offset);
  3903.         if (map_length > 0)
  3904.             memcpy(literal_map, aLiteralMap + map_offset, map_length);
  3905.     }
  3906.     else
  3907.     {
  3908.         // Resolve escaped sequences and make a map of which characters in the string should
  3909.         // be interpreted literally rather than as their native function.  In other words,
  3910.         // convert any escape sequences in order from left to right (this order is important,
  3911.         // e.g. ``% should evaluate to `g_DerefChar not `LITERAL_PERCENT.  This part must be
  3912.         // done *after* checking for comment-flags that appear to the right of a valid line, above.
  3913.         // How literal comment-flags (e.g. semicolons) work:
  3914.         //string1; string2 <-- not a problem since string2 won't be considered a comment by the above.
  3915.         //string1 ; string2  <-- this would be a user mistake if string2 wasn't supposed to be a comment.
  3916.         //string1 `; string 2  <-- since esc seq. is resolved *after* checking for comments, this behaves as intended.
  3917.         // Current limitation: a comment-flag longer than 1 can't be escaped, so if "//" were used,
  3918.         // as a comment flag, it could never have whitespace to the left of it if it were meant to be literal.
  3919.         // Note: This section resolves all escape sequences except those involving g_DerefChar, which
  3920.         // are handled by a later section.
  3921.         char c;
  3922.         int i;
  3923.         for (i = 0; ; ++i)  // Increment to skip over the symbol just found by the inner for().
  3924.         {
  3925.             for (; action_args[i] && action_args[i] != g_EscapeChar; ++i);  // Find the next escape char.
  3926.             if (!action_args[i]) // end of string.
  3927.                 break;
  3928.             c = action_args[i + 1];
  3929.             switch (c)
  3930.             {
  3931.                 // Only lowercase is recognized for these:
  3932.                 case 'a': action_args[i + 1] = '\a'; break;  // alert (bell) character
  3933.                 case 'b': action_args[i + 1] = '\b'; break;  // backspace
  3934.                 case 'f': action_args[i + 1] = '\f'; break;  // formfeed
  3935.                 case 'n': action_args[i + 1] = '\n'; break;  // newline
  3936.                 case 'r': action_args[i + 1] = '\r'; break;  // carriage return
  3937.                 case 't': action_args[i + 1] = '\t'; break;  // horizontal tab
  3938.                 case 'v': action_args[i + 1] = '\v'; break;  // vertical tab
  3939.             }
  3940.             // Replace escape-sequence with its single-char value.  This is done event if the pair isn't
  3941.             // a recognizable escape sequence (e.g. `? becomes ?), which is the Microsoft approach
  3942.             // and might not be a bad way of handing things.  There are some exceptions, however.
  3943.             // The first of these exceptions (g_DerefChar) is mandatory because that char must be
  3944.             // handled at a later stage or escaped g_DerefChars won't work right.  The others are
  3945.             // questionable, and might be worth further consideration.  UPDATE: g_DerefChar is now
  3946.             // done here because otherwise, examples such as this fail:
  3947.             // - The escape char is backslash.
  3948.             // - any instances of \\%, such as c:\\%var% , will not work because the first escape
  3949.             // sequence (\\) is resolved to a single literal backslash.  But then when \% is encountered
  3950.             // by the section that resolves escape sequences for g_DerefChar, the backslash is seen
  3951.             // as an escape char rather than a literal backslash, which is not correct.  Thus, we
  3952.             // resolve all escapes sequences HERE in one go, from left to right.
  3953.  
  3954.             // AutoIt2 definitely treats an escape char that occurs at the very end of
  3955.             // a line as literal.  It seems best to also do it for these other cases too.
  3956.             // UPDATE: I cannot reproduce the above behavior in AutoIt2.  Maybe it only
  3957.             // does it for some commands or maybe I was mistaken.  So for now, this part
  3958.             // is disabled:
  3959.             //if (c == '\0' || c == ' ' || c == '\t')
  3960.             //    literal_map[i] = 1;  // In the map, mark this char as literal.
  3961.             //else
  3962.             {
  3963.                 // So these are also done as well, and don't need an explicit check:
  3964.                 // g_EscapeChar , g_delimiter , (when g_CommentFlagLength > 1 ??): *g_CommentFlag
  3965.                 // Below has a final +1 to include the terminator:
  3966.                 MoveMemory(action_args + i, action_args + i + 1, strlen(action_args + i + 1) + 1);
  3967.                 literal_map[i] = 1;  // In the map, mark this char as literal.
  3968.             }
  3969.             // else: Do nothing, even if the value is zero (the string's terminator).
  3970.         }
  3971.     }
  3972.  
  3973.     ////////////////////////////////////////////////////////////////////////////////////////
  3974.     // Do some special preparsing of the MsgBox command, since it is so frequently used and
  3975.     // it is also the source of problem areas going from AutoIt2 to 3 and also due to the
  3976.     // new numeric parameter at the end.  Whenever possible, we want to avoid the need for
  3977.     // the user to have to escape commas that are intended to be literal.
  3978.     ///////////////////////////////////////////////////////////////////////////////////////
  3979.     int mark, max_params_override = 0; // Set default.
  3980.     if (aActionType == ACT_MSGBOX)
  3981.     {
  3982.         // First find out how many non-literal (non-escaped) delimiters are present.
  3983.         // Use a high maximum so that we can almost always find and analyze the command's
  3984.         // last apparent parameter.  This helps error-checking be more informative in a
  3985.         // case where the command specifies a timeout as its last param but it's next-to-last
  3986.         // param contains delimiters that the user forgot to escape.  In other words, this
  3987.         // helps detect more often when the user is trying to use the timeout feature.
  3988.         // If this weren't done, the command would more often forgive improper syntax
  3989.         // and not report a load-time error, even though it's pretty obvious that a load-time
  3990.         // error should have been reported:
  3991.         #define MAX_MSGBOX_DELIMITERS 20
  3992.         char *delimiter[MAX_MSGBOX_DELIMITERS];
  3993.         int delimiter_count;
  3994.         for (mark = delimiter_count = 0; action_args[mark] && delimiter_count < MAX_MSGBOX_DELIMITERS;)
  3995.         {
  3996.             for (; action_args[mark]; ++mark)
  3997.                 if (action_args[mark] == g_delimiter && !literal_map[mark]) // Match found: a non-literal delimiter.
  3998.                 {
  3999.                     delimiter[delimiter_count++] = action_args + mark;
  4000.                     ++mark; // Skip over this delimiter for the next iteration of the outer loop.
  4001.                     break;
  4002.                 }
  4003.         }
  4004.         // If it has only 1 arg (i.e. 0 delimiters within the arg list) no override is needed.
  4005.         // Otherwise do more checking:
  4006.         if (delimiter_count)
  4007.         {
  4008.             // If the first apparent arg is not a non-blank pure number or there are apparently
  4009.             // only 2 args present (i.e. 1 delimiter in the arg list), assume the command is being
  4010.             // used in its 1-parameter mode:
  4011.             if (delimiter_count <= 1) // 2 parameters or less.
  4012.                 // Force it to be 1-param mode.  In other words, we want to make MsgBox a very forgiving
  4013.                 // command and have it rarely if ever report syntax errors:
  4014.                 max_params_override = 1;
  4015.             else // It has more than 3 apparent params, but is the first param even numeric?
  4016.             {
  4017.                 *delimiter[0] = '\0'; // Temporarily terminate action_args at the first delimiter.
  4018.                 // Note: If it's a number inside a variable reference, it's still considered 1-parameter
  4019.                 // mode to avoid ambiguity (unlike the new deref checking for param #4 mentioned below,
  4020.                 // there seems to be too much ambiguity in this case to justify trying to figure out
  4021.                 // if the first parameter is a pure deref, and thus that the command should use
  4022.                 // 3-param or 4-param mode instead).
  4023.                 if (!IsPureNumeric(action_args)) // No floats allowed.  Allow all-whitespace for aut2 compatibility.
  4024.                     max_params_override = 1;
  4025.                 *delimiter[0] = g_delimiter; // Restore the string.
  4026.                 if (!max_params_override)
  4027.                 {
  4028.                     // IMPORATANT: The MsgBox cmd effectively has 3 parameter modes:
  4029.                     // 1-parameter (where all commas in the 1st parameter are automatically literal)
  4030.                     // 3-parameter (where all commas in the 3rd parameter are automatically literal)
  4031.                     // 4-parameter (whether the 4th parameter is the timeout value)
  4032.                     // Thus, the below must be done in a way that recognizes & supports all 3 modes.
  4033.                     // The above has determined that the cmd isn't in 1-parameter mode.
  4034.                     // If at this point it has exactly 3 apparent params, allow the command to be
  4035.                     // processed normally without an override.  Otherwise, do more checking:
  4036.                     if (delimiter_count == 3) // i.e. 3 delimiters, which means 4 params.
  4037.                     {
  4038.                         // If the 4th parameter isn't blank or pure numeric (i.e. even if it's a pure
  4039.                         // deref, since trying to figure out what's a pure deref is somewhat complicated
  4040.                         // at this early stage of parsing), assume the user didn't intend it to be the
  4041.                         // MsgBox timeout (since that feature is rarely used), instead intending it
  4042.                         // to be part of parameter #3.
  4043.                         if (!IsPureNumeric(delimiter[2] + 1, false, true, true))
  4044.                         {
  4045.                             // Not blank and not a int or float.  Update for v1.0.20: Check if it's a
  4046.                             // single deref.  If so, assume that deref contains the timeout and thus
  4047.                             // 4-param mode is in effect.  This allows the timeout to be contained in
  4048.                             // a variable, which was requested by one user:
  4049.                             char *cp = omit_leading_whitespace(delimiter[2] + 1);
  4050.                             // Relies on short-circuit boolean order:
  4051.                             if (*cp != g_DerefChar || literal_map[cp - action_args]) // not a proper deref char.
  4052.                                 max_params_override = 3;
  4053.                             // else since it does start with a real deref symbol, it must end with one otherwise
  4054.                             // that will be caught later on as a syntax error anyway.  Therefore, don't override
  4055.                             // max_params, just let it be parsed as 4 parameters.
  4056.                         }
  4057.                         // If it has more than 4 params or it has exactly 4 but the 4th isn't blank,
  4058.                         // pure numeric, or a deref: assume it's being used in 3-parameter mode and
  4059.                         // that all the other delimiters were intended to be literal.
  4060.                     }
  4061.                     else if (delimiter_count > 3) // i.e. 4 or more delimiters, which means 5 or more params.
  4062.                         // Since it has too many delimiters to be 4-param mode, Assume it's 3-param mode
  4063.                         // so that non-escaped commas in parameters 4 and beyond will be all treated as
  4064.                         // strings that are part of parameter #3.
  4065.                         max_params_override = 3;
  4066.                     //else if 3 params or less: Don't override via max_params_override, just parse it normally.
  4067.                 }
  4068.             }
  4069.         }
  4070.     } // end of special handling for MsgBox.
  4071.  
  4072.  
  4073.     /////////////////////////////////////////////////////////////
  4074.     // Parse the parameter string into a list of separate params.
  4075.     /////////////////////////////////////////////////////////////
  4076.     // MaxParams has already been verified as being <= MAX_ARGS.
  4077.     // Any g_delimiter-delimited items beyond MaxParams will be included in a lump inside the last param:
  4078.     int nArgs, nArgs_plus_one;
  4079.     char *arg[MAX_ARGS], *arg_map[MAX_ARGS];
  4080.     ActionTypeType subaction_type = ACT_INVALID; // Must init these.
  4081.     ActionTypeType suboldaction_type = OLD_INVALID;
  4082.     char subaction_name[MAX_VAR_NAME_LENGTH + 1], *subaction_end_marker = NULL, *subaction_start = NULL;
  4083.     int max_params = max_params_override ? max_params_override
  4084.         : (mIsAutoIt2 ? (this_action.MaxParamsAu2WithHighBit & 0x7F) // 0x7F removes the high-bit from consideration; that bit is used for an unrelated purpose.
  4085.             : this_action.MaxParams);
  4086.     int max_params_minus_one = max_params - 1;
  4087.     bool is_expression;
  4088.     ActionTypeType *np;
  4089.  
  4090.     for (nArgs = mark = 0; action_args[mark] && nArgs < max_params; ++nArgs)
  4091.     {
  4092.         if (nArgs == 2) // i.e. the 3rd arg is about to be added.
  4093.         {
  4094.             switch (aActionType) // will be ACT_INVALID if this_action is an old-style command.
  4095.             {
  4096.             case ACT_IFWINEXIST:
  4097.             case ACT_IFWINNOTEXIST:
  4098.             case ACT_IFWINACTIVE:
  4099.             case ACT_IFWINNOTACTIVE:
  4100.                 subaction_start = action_args + mark;
  4101.                 if (subaction_end_marker = ParseActionType(subaction_name, subaction_start, false))
  4102.                     if (   !(subaction_type = ConvertActionType(subaction_name))   )
  4103.                         suboldaction_type = ConvertOldActionType(subaction_name);
  4104.                 break;
  4105.             }
  4106.             if (subaction_type || suboldaction_type)
  4107.                 // A valid command was found (i.e. AutoIt2-style) in place of this commands Exclude Title
  4108.                 // parameter, so don't add this item as a param to the command.
  4109.                 break;
  4110.         }
  4111.         arg[nArgs] = action_args + mark;
  4112.         arg_map[nArgs] = literal_map + mark;
  4113.         if (nArgs == max_params_minus_one)
  4114.         {
  4115.             // Don't terminate the last param, just put all the rest of the line
  4116.             // into it.  This avoids the need for the user to escape any commas
  4117.             // that may appear in the last param.  i.e. any commas beyond this
  4118.             // point can't be delimiters because we've already reached MaxArgs
  4119.             // for this command:
  4120.             ++nArgs;
  4121.             break;
  4122.         }
  4123.         // The above does not need the in_quotes and in_parens checks because commas in the last arg
  4124.         // are always literal, so there's no problem even in expressions.
  4125.  
  4126.         // The following implements the "% " prefix as a means of forcing an expression:
  4127.         is_expression = *arg[nArgs] == g_DerefChar && !*arg_map[nArgs] // It's a non-literal deref character.
  4128.             && IS_SPACE_OR_TAB(arg[nArgs][1]); // Followed by a space or tab.
  4129.  
  4130.         // Find the end of the above arg:
  4131.         for (in_quotes = false, open_parens = 0; action_args[mark]; ++mark)
  4132.         {
  4133.             switch (action_args[mark])
  4134.             {
  4135.             case '"':
  4136.                 // The simple method below is sufficient for our purpose even if a quoted string contains
  4137.                 // pairs of double-quotes to represent a single literal quote, e.g. "quoted ""word""".
  4138.                 // In other words, it relies on the fact that there must be an even number of quotes
  4139.                 // inside any mandatory-numeric arg that is an expression such as x=="red,blue"
  4140.                 in_quotes = !in_quotes;
  4141.                 break;
  4142.             case '(':
  4143.                 if (!in_quotes) // Literal parentheses inside a quoted string should not be counted for this purpose.
  4144.                     ++open_parens;
  4145.                 break;
  4146.             case ')':
  4147.                 if (!in_quotes)
  4148.                     --open_parens;
  4149.                 break;
  4150.             }
  4151.  
  4152.             if (action_args[mark] == g_delimiter && !literal_map[mark])  // A non-literal delimiter (unless its within double-quotes of a mandatory-numeric arg) is a match.
  4153.             {
  4154.                 // If we're inside a pair of quotes or parentheses and this arg is known to be an expression, this
  4155.                 // delimiter is part this arg and thus not to be used as a delimiter between command args:
  4156.                 if (in_quotes || open_parens > 0)
  4157.                 {
  4158.                     if (is_expression)
  4159.                         continue;
  4160.                     if (aActionType == ACT_TRANSFORM && (nArgs == 2 || nArgs == 3)) // i.e. the 3rd or 4th arg is about to be added.
  4161.                     {
  4162.                         // Somewhat inefficient in the case where it has to be called for both Arg#2 and Arg#3,
  4163.                         // but that is pretty rare.  Overall, expressions and quoted strings in these args
  4164.                         // is rare too, so the inefficiency of redundant calls to ConvertTransformCmd() is
  4165.                         // very small on average, and seems worth the benefit in terms of code simplification.
  4166.                         // Note that the following might return TRANS_CMD_INVALID just because the sub-command
  4167.                         // is containined in a variable reference.  That is why TRANS_CMD_INVALID does not
  4168.                         // produce an error at this stage, but only later when the line has been constructed
  4169.                         // far enough to call ArgHasDeref():
  4170.                         // i.e. Not the first param, only the third and fourth, which currently are either both numeric or both non-numeric for all cases.
  4171.                         switch(Line::ConvertTransformCmd(arg[1])) // arg[1] is the second arg.
  4172.                         {
  4173.                         // See comment above for why TRANS_CMD_INVALID isn't yet reported as an error:
  4174.                         #define TRANSFORM_NON_EXPRESSION_CASES \
  4175.                         case TRANS_CMD_INVALID:\
  4176.                         case TRANS_CMD_ASC:\
  4177.                         case TRANS_CMD_UNICODE:\
  4178.                         case TRANS_CMD_DEREF:\
  4179.                         case TRANS_CMD_HTML:\
  4180.                             break; // Do nothing.  Leave this_new_arg.is_expression set to its default of false.
  4181.                         TRANSFORM_NON_EXPRESSION_CASES
  4182.                         default:
  4183.                             // For all other sub-commands, Arg #3 and #4 are expression-capable.  It doesn't
  4184.                             // seem necessary to call LegacyArgIsExpression() because the mere fact that
  4185.                             // we're inside a pair of quotes or parentheses seems enough to indicate that this
  4186.                             // really is an expression.
  4187.                             continue;
  4188.                         }
  4189.                     }
  4190.                     // v1.0.43.07: Fixed below to use this_action instead of g_act[aActionType] so that the
  4191.                     // numeric params of legacy commands like EnvAdd/Sub/LeftClick can be detected.  Without
  4192.                     // this fix, the last comma in a line like "EnvSub, var, Add(2, 3)" is seen as a parameter
  4193.                     // delimiter, which causes a loadtime syntax error.
  4194.                     if (np = this_action.NumericParams) // This command has at least one numeric parameter.
  4195.                     {
  4196.                         // As of v1.0.25, pure numeric parameters can optionally be numeric expressions, so check for that:
  4197.                         nArgs_plus_one = nArgs + 1;
  4198.                         for (; *np; ++np)
  4199.                             if (*np == nArgs_plus_one) // This arg is enforced to be purely numeric.
  4200.                                 break;
  4201.                         if (*np) // Match found, so this is a purely numeric arg.
  4202.                             continue; // This delimiter is disqualified, so look for the next one.
  4203.                     }
  4204.                 } // if in quotes or parentheses
  4205.                 // Since above didn't "continue", this is a real delimiter.
  4206.                 action_args[mark] = '\0';  // Terminate the previous arg.
  4207.                 // Trim any whitespace from the previous arg.  This operation
  4208.                 // will not alter the contents of anything beyond action_args[i],
  4209.                 // so it should be safe.  In addition, even though it changes
  4210.                 // the contents of the arg[nArgs] substring, we don't have to
  4211.                 // update literal_map because the map is still accurate due
  4212.                 // to the nature of rtrim).  UPDATE: Note that this version
  4213.                 // of rtrim() specifically avoids trimming newline characters,
  4214.                 // since the user may have included literal newlines at the end
  4215.                 // of the string by using an escape sequence:
  4216.                 rtrim(arg[nArgs]);
  4217.                 // Omit the leading whitespace from the next arg:
  4218.                 for (++mark; IS_SPACE_OR_TAB(action_args[mark]); ++mark);
  4219.                 // Now <mark> marks the end of the string, the start of the next arg,
  4220.                 // or a delimiter-char (if the next arg is blank).
  4221.                 break;  // Arg was found, so let the outer loop handle it.
  4222.             }
  4223.         }
  4224.     }
  4225.  
  4226.     ///////////////////////////////////////////////////////////////////////////////
  4227.     // Ensure there are sufficient parameters for this command.  Note: If MinParams
  4228.     // is greater than 0, the param numbers 1 through MinParams are required to be
  4229.     // non-blank.
  4230.     ///////////////////////////////////////////////////////////////////////////////
  4231.     char error_msg[1024];
  4232.     if (nArgs < this_action.MinParams)
  4233.     {
  4234.         snprintf(error_msg, sizeof(error_msg), "\"%s\" requires at least %d parameter%s."
  4235.             , this_action.Name, this_action.MinParams
  4236.             , this_action.MinParams > 1 ? "s" : "");
  4237.         return ScriptError(error_msg, aLineText);
  4238.     }
  4239.     for (int i = 0; i < this_action.MinParams; ++i) // It's only safe to do this after the above.
  4240.         if (!*arg[i])
  4241.         {
  4242.             snprintf(error_msg, sizeof(error_msg), "\"%s\" requires that parameter #%u be non-blank."
  4243.                 , this_action.Name, i + 1);
  4244.             return ScriptError(error_msg, aLineText);
  4245.         }
  4246.  
  4247.     ////////////////////////////////////////////////////////////////////////
  4248.     // Handle legacy commands that are supported for backward compatibility.
  4249.     ////////////////////////////////////////////////////////////////////////
  4250.     if (aOldActionType)
  4251.     {
  4252.         switch(aOldActionType)
  4253.         {
  4254.         case OLD_LEFTCLICK:
  4255.         case OLD_RIGHTCLICK:
  4256.             // Insert an arg at the beginning of the list to indicate the mouse button.
  4257.             arg[2] = arg[1];  arg_map[2] = arg_map[1];
  4258.             arg[1] = arg[0];  arg_map[1] = arg_map[0];
  4259.             arg[0] = aOldActionType == OLD_LEFTCLICK ? "" : "Right";  arg_map[0] = NULL; // "" is treated the same as "Left"
  4260.             return AddLine(ACT_MOUSECLICK, arg, ++nArgs, arg_map);
  4261.         case OLD_LEFTCLICKDRAG:
  4262.         case OLD_RIGHTCLICKDRAG:
  4263.             // Insert an arg at the beginning of the list to indicate the mouse button.
  4264.             arg[4] = arg[3];  arg_map[4] = arg_map[3]; // Set the 5th arg to be the 4th, etc.
  4265.             arg[3] = arg[2];  arg_map[3] = arg_map[2];
  4266.             arg[2] = arg[1];  arg_map[2] = arg_map[1];
  4267.             arg[1] = arg[0];  arg_map[1] = arg_map[0];
  4268.             arg[0] = (aOldActionType == OLD_LEFTCLICKDRAG) ? "Left" : "Right";  arg_map[0] = NULL;
  4269.             return AddLine(ACT_MOUSECLICKDRAG, arg, ++nArgs, arg_map);
  4270.         case OLD_HIDEAUTOITWIN:
  4271.             // This isn't a perfect mapping because the word "on" or "off" might be contained
  4272.             // in a variable reference, in which case this conversion will be incorrect.
  4273.             // However, variable ref. is exceedingly rare.
  4274.             arg[1] = stricmp(arg[0], "On") ? "Icon" : "NoIcon";
  4275.             arg[0] = "Tray"; // Assign only after we're done using the old arg[0] value above.
  4276.             return AddLine(ACT_MENU, arg, 2, arg_map);
  4277.         case OLD_REPEAT:
  4278.             if (!AddLine(ACT_REPEAT, arg, nArgs, arg_map))
  4279.                 return FAIL;
  4280.             // For simplicity, always enclose repeat-loop's contents in in a block rather
  4281.             // than trying to detect if it has only one line:
  4282.             return AddLine(ACT_BLOCK_BEGIN);
  4283.         case OLD_ENDREPEAT:
  4284.             return AddLine(ACT_BLOCK_END);
  4285.         case OLD_WINGETACTIVETITLE:
  4286.             arg[nArgs] = "A";  arg_map[nArgs] = NULL; // "A" signifies the active window.
  4287.             ++nArgs;
  4288.             return AddLine(ACT_WINGETTITLE, arg, nArgs, arg_map);
  4289.         case OLD_WINGETACTIVESTATS:
  4290.         {
  4291.             // Convert OLD_WINGETACTIVESTATS into *two* new commands:
  4292.             // Command #1: WinGetTitle, OutputVar, A
  4293.             char *width = arg[1];  // Temporary placeholder.
  4294.             arg[1] = "A";  arg_map[1] = NULL;  // Signifies the active window.
  4295.             if (!AddLine(ACT_WINGETTITLE, arg, 2, arg_map))
  4296.                 return FAIL;
  4297.             // Command #2: WinGetPos, XPos, YPos, Width, Height, A
  4298.             // Reassign args in the new command's ordering.  These lines must occur
  4299.             // in this exact order for the copy to work properly:
  4300.             arg[0] = arg[3];  arg_map[0] = arg_map[3];  // xpos
  4301.             arg[3] = arg[2];  arg_map[3] = arg_map[2];  // height
  4302.             arg[2] = width;   arg_map[2] = arg_map[1];  // width
  4303.             arg[1] = arg[4];  arg_map[1] = arg_map[4];  // ypos
  4304.             arg[4] = "A";  arg_map[4] = NULL;  // "A" signifies the active window.
  4305.             return AddLine(ACT_WINGETPOS, arg, 5, arg_map);
  4306.         }
  4307.  
  4308.         case OLD_SETENV:
  4309.             return AddLine(ACT_ASSIGN, arg, nArgs, arg_map);
  4310.         case OLD_ENVADD:
  4311.             return AddLine(ACT_ADD, arg, nArgs, arg_map);
  4312.         case OLD_ENVSUB:
  4313.             return AddLine(ACT_SUB, arg, nArgs, arg_map);
  4314.         case OLD_ENVMULT:
  4315.             return AddLine(ACT_MULT, arg, nArgs, arg_map);
  4316.         case OLD_ENVDIV:
  4317.             return AddLine(ACT_DIV, arg, nArgs, arg_map);
  4318.  
  4319.         // For these, break rather than return so that further processing can be done:
  4320.         case OLD_IFEQUAL:
  4321.             aActionType = ACT_IFEQUAL;
  4322.             break;
  4323.         case OLD_IFNOTEQUAL:
  4324.             aActionType = ACT_IFNOTEQUAL;
  4325.             break;
  4326.         case OLD_IFGREATER:
  4327.             aActionType = ACT_IFGREATER;
  4328.             break;
  4329.         case OLD_IFGREATEROREQUAL:
  4330.             aActionType = ACT_IFGREATEROREQUAL;
  4331.             break;
  4332.         case OLD_IFLESS:
  4333.             aActionType = ACT_IFLESS;
  4334.             break;
  4335.         case OLD_IFLESSOREQUAL:
  4336.             aActionType = ACT_IFLESSOREQUAL;
  4337.             break;
  4338. #ifdef _DEBUG
  4339.         default:
  4340.             return ScriptError("DEBUG: Unhandled Old-Command.", action_name);
  4341. #endif
  4342.         } // switch()
  4343.     }
  4344.  
  4345.     //////////////////////////////////////////////////////////////////////////////////////////////////
  4346.     // Handle AutoIt2-style IF-statements (i.e. the IF's action is on the same line as the condition).
  4347.     //////////////////////////////////////////////////////////////////////////////////////////////////
  4348.     // The check below: Don't bother if this IF (e.g. IfWinActive) has zero params or if the
  4349.     // subaction was already found above:
  4350.     if (nArgs && !subaction_type && !suboldaction_type && ACT_IS_IF_OLD(aActionType, aOldActionType))
  4351.     {
  4352.         char *delimiter;
  4353.         char *last_arg = arg[nArgs - 1];
  4354.         for (mark = (int)(last_arg - action_args); action_args[mark]; ++mark)
  4355.         {
  4356.             if (action_args[mark] == g_delimiter && !literal_map[mark])  // Match found: a non-literal delimiter.
  4357.             {
  4358.                 delimiter = action_args + mark; // save the location of this delimiter
  4359.                 // Omit the leading whitespace from the next arg:
  4360.                 for (++mark; IS_SPACE_OR_TAB(action_args[mark]); ++mark);
  4361.                 // Now <mark> marks the end of the string, the start of the next arg,
  4362.                 // or a delimiter-char (if the next arg is blank).
  4363.                 subaction_start = action_args + mark;
  4364.                 if (subaction_end_marker = ParseActionType(subaction_name, subaction_start, false))
  4365.                 {
  4366.                     if (   !(subaction_type = ConvertActionType(subaction_name))   )
  4367.                         suboldaction_type = ConvertOldActionType(subaction_name);
  4368.                     if (subaction_type || suboldaction_type) // A valid sub-action (command) was found.
  4369.                     {
  4370.                         // Remove this subaction from its parent line; we want it separate:
  4371.                         *delimiter = '\0';
  4372.                         rtrim(last_arg);
  4373.                     }
  4374.                     // else leave it as-is, i.e. as part of the last param, because the delimiter
  4375.                     // found above is probably being used as a literal char even though it isn't
  4376.                     // escaped, e.g. "ifequal, var1, string with embedded, but non-escaped, commas"
  4377.                 }
  4378.                 // else, do nothing; reasoning perhaps similar to above comment.
  4379.                 break;
  4380.             }
  4381.         }
  4382.     }
  4383.  
  4384.     // In v1.0.41, the following one-true-brace styles are also supported:
  4385.     // Loop {   ; Known limitation: Overlaps with file-pattern loop that retrieves single file of name "{".
  4386.     // Loop 5 { ; Also overlaps, this time with file-pattern loop that retrieves numeric filename ending in '{'.
  4387.     // Loop %Var% {  ; Similar, but like the above seems acceptable given extreme rarity of user intending a file pattern.
  4388.     if (aActionType == ACT_LOOP && nArgs == 1 && arg[0][0])  // A loop with exactly one, non-blank arg.
  4389.     {
  4390.         char *arg1 = arg[0]; // For readability and possibly performance.
  4391.         // A loop with the above criteria (exactly one arg) can only validly be a normal/counting loop or
  4392.         // a file-pattern loop if its parameter's last character is '{'.  For the following reasons, any
  4393.         // single-parameter loop that ends in '{' is considered to be one-true brace:
  4394.         // 1) Extremely rare that a file-pattern loop such as "Loop filename {" would ever be used,
  4395.         //    and even if it is, the syntax checker will report an unclosed block, making it apparent
  4396.         //    to the user that a workaround is needed, such as putting the filename into a variable first.
  4397.         // 2) Difficulty and code size of distinguishing all possible valid-one-true-braces from those
  4398.         //    that aren't.  For example, the following are ambiguous, so it seems best for consistency
  4399.         //    and code size reduction just to treat them as one-truce-brace, which will immediately alert
  4400.         //    the user if the brace isn't closed:
  4401.         //    a) Loop % (expression) {   ; Ambiguous because expression could resolve to a string, thus it would be seen as a file-pattern loop.
  4402.         //    b) Loop %Var% {            ; Similar as above, which means all three of these unintentionally support
  4403.         //    c) Loop filename{          ; OTB for some types of file loops because it's not worth the code size to "unsupport" them.
  4404.         //    d) Loop *.txt {            ; Like the above: Unintentionally supported, but not documnented.
  4405.         //
  4406.         // Insist that no characters follow the '{' in case the user intended it to be a file-pattern loop
  4407.         // such as "Loop {literal-filename".
  4408.         char *arg1_last_char = arg1 + strlen(arg1) - 1;
  4409.         if (*arg1_last_char == '{')
  4410.         {
  4411.             add_openbrace_afterward = true;
  4412.             *arg1_last_char = '\0';  // Since it will be fully handled here, remove the brace from further consideration.
  4413.             if (!rtrim(arg1)) // Trimmed down to nothing, so only a brace was present: remove the arg completely.
  4414.                 nArgs = 0;    // This makes later stages recognize it as an infinite loop rather than a zero-iteration loop.
  4415.         }
  4416.     }
  4417.  
  4418.     if (!AddLine(aActionType, arg, nArgs, arg_map))
  4419.         return FAIL;
  4420.     if (add_openbrace_afterward)
  4421.         if (!AddLine(ACT_BLOCK_BEGIN))
  4422.             return FAIL;
  4423.     if (!subaction_type && !suboldaction_type) // There is no subaction in this case.
  4424.         return OK;
  4425.     // Otherwise, recursively add the subaction, and any subactions it might have, beneath
  4426.     // the line just added.  The following example:
  4427.     // IfWinExist, x, y, IfWinNotExist, a, b, Gosub, Sub1
  4428.     // would break down into these lines:
  4429.     // IfWinExist, x, y
  4430.     //    IfWinNotExist, a, b
  4431.     //       Gosub, Sub1
  4432.     return ParseAndAddLine(subaction_start, subaction_type, suboldaction_type, subaction_name, subaction_end_marker
  4433.         , literal_map + (subaction_end_marker - action_args) // Pass only the relevant substring of literal_map.
  4434.         , strlen(subaction_end_marker));
  4435. }
  4436.  
  4437.  
  4438.  
  4439. inline char *Script::ParseActionType(char *aBufTarget, char *aBufSource, bool aDisplayErrors)
  4440. // inline since it's called so often.
  4441. // aBufTarget should be at least MAX_VAR_NAME_LENGTH + 1 in size.
  4442. // Returns NULL if a failure condition occurs; otherwise, the address of the last
  4443. // character of the action name in aBufSource.
  4444. {
  4445.     ////////////////////////////////////////////////////////
  4446.     // Find the action name and the start of the param list.
  4447.     ////////////////////////////////////////////////////////
  4448.     // Allows the delimiter between action-type-name and the first param to be optional by
  4449.     // relying on the fact that action-type-names can't contain spaces. Find first char in
  4450.     // aLineText that is a space, a delimiter, or a tab. Also search for operator symbols
  4451.     // so that assignments and IFs without whitespace are supported, e.g. var1=5,
  4452.     // if var2<%var3%.  Not static in case g_delimiter is allowed to vary:
  4453.     DEFINE_END_FLAGS
  4454.     char *end_marker = StrChrAny(aBufSource, end_flags);
  4455.     if (end_marker) // Found a delimiter.
  4456.     {
  4457.         if (*end_marker == '=' && end_marker > aBufSource && end_marker[-1] == '.') // Relies on short-circuit boolean order.
  4458.             --end_marker; // v1.0.46.01: Support .=, but not any use of '.' because that is reserved as a struct/member operator.
  4459.         if (end_marker > aBufSource) // The delimiter isn't very first char in aBufSource.
  4460.             --end_marker;
  4461.         // else we allow it to be the first char to support "++i" etc.
  4462.     }
  4463.     else // No delimiter found, so set end_marker to the location of the last char in string.
  4464.         end_marker = aBufSource + strlen(aBufSource) - 1;
  4465.     // Now end_marker is the character just prior to the first delimiter or whitespace,
  4466.     // or (in the case of ++ and --) the first delimiter itself.  Find the end of
  4467.     // the action-type name by omitting trailing whitespace:
  4468.     end_marker = omit_trailing_whitespace(aBufSource, end_marker);
  4469.     // If first char in aBufSource is a delimiter, action_name will consist of just that first char:
  4470.     size_t action_name_length = end_marker - aBufSource + 1;
  4471.     if (action_name_length > MAX_VAR_NAME_LENGTH)
  4472.     {
  4473.         if (aDisplayErrors)
  4474.             ScriptError(ERR_UNRECOGNIZED_ACTION, aBufSource); // Short/vague message since so rare.
  4475.         return NULL;
  4476.     }
  4477.     strlcpy(aBufTarget, aBufSource, action_name_length + 1);
  4478.     return end_marker;
  4479. }
  4480.  
  4481.  
  4482.  
  4483. inline ActionTypeType Script::ConvertActionType(char *aActionTypeString)
  4484. // inline since it's called so often, but don't keep it in the .h due to #include issues.
  4485. {
  4486.     // For the loop's index:
  4487.     // Use an int rather than ActionTypeType since it's sure to be large enough to go beyond
  4488.     // 256 if there happen to be exactly 256 actions in the array:
  4489.      for (int action_type = ACT_FIRST_COMMAND; action_type < g_ActionCount; ++action_type)
  4490.         if (!stricmp(aActionTypeString, g_act[action_type].Name)) // Match found.
  4491.             return action_type;
  4492.     return ACT_INVALID;  // On failure to find a match.
  4493. }
  4494.  
  4495.  
  4496.  
  4497. inline ActionTypeType Script::ConvertOldActionType(char *aActionTypeString)
  4498. // inline since it's called so often, but don't keep it in the .h due to #include issues.
  4499. {
  4500.      for (int action_type = OLD_INVALID + 1; action_type < g_OldActionCount; ++action_type)
  4501.         if (!stricmp(aActionTypeString, g_old_act[action_type].Name)) // Match found.
  4502.             return action_type;
  4503.     return OLD_INVALID;  // On failure to find a match.
  4504. }
  4505.  
  4506.  
  4507.  
  4508. bool LegacyArgIsExpression(char *aArgText, char *aArgMap)
  4509. // Helper function for AddLine
  4510. {
  4511.     // The section below is here in light of rare legacy cases such as the below:
  4512.     // -%y%   ; i.e. make it negative.
  4513.     // +%y%   ; might happen with up/down adjustments on SoundSet, GuiControl progress/slider, etc?
  4514.     // Although the above are detected as non-expressions and thus non-double-derefs,
  4515.     // the following are not because they're too rare or would sacrifice too much flexibility:
  4516.     // 1%y%.0 ; i.e. at a tens/hundreds place and make it a floating point.  In addition,
  4517.     //          1%y% could be an array, so best not to tag that as non-expression.
  4518.     //          For that matter, %y%.0 could be an obscure kind of reverse-notation array itself.
  4519.     //          However, as of v1.0.29, things like %y%000 are allowed, e.g. Sleep %Seconds%000
  4520.     // 0x%y%  ; i.e. make it hex (too rare to check for, plus it could be an array).
  4521.     // %y%%z% ; i.e. concatenate two numbers to make a larger number (too rare to check for)
  4522.     char *cp = aArgText + (*aArgText == '-' || *aArgText == '+'); // i.e. +1 if second term evaluates to true.
  4523.     return *cp != g_DerefChar // If no deref, for simplicity assume it's an expression since any such non-numeric item would be extremely rare in pre-expression era.
  4524.         || !aArgMap || *(aArgMap + (cp != aArgText)) // There's no literal-map or this deref char is not really a deref char because it's marked as a literal.
  4525.         || !(cp = strchr(cp + 1, g_DerefChar)) // There is no next deref char.
  4526.         || (cp[1] && !IsPureNumeric(cp + 1, false, true, true)); // But that next deref char is not the last char, which means this is not a single isolated deref. v1.0.29: Allow things like Sleep %Var%000.
  4527.         // Above does not need to check whether last deref char is marked literal in the
  4528.         // arg map because if it is, it would mean the first deref char lacks a matching
  4529.         // close-symbol, which will be caught as a syntax error below regardless of whether
  4530.         // this is an expression.
  4531. }
  4532.  
  4533.  
  4534.  
  4535. ResultType Script::AddLine(ActionTypeType aActionType, char *aArg[], ArgCountType aArgc, char *aArgMap[])
  4536. // aArg must be a collection of pointers to memory areas that are modifiable, and there
  4537. // must be at least aArgc number of pointers in the aArg array.  In v1.0.40, a caller (namely
  4538. // the "macro expansion" for remappings such as "a::b") is allowed to pass a non-NULL value for
  4539. // aArg but a NULL value for aArgMap.
  4540. // Returns OK or FAIL.
  4541. {
  4542. #ifdef _DEBUG
  4543.     if (aActionType == ACT_INVALID)
  4544.         return ScriptError("DEBUG: BAD AddLine", aArgc > 0 ? aArg[0] : "");
  4545. #endif
  4546.  
  4547.     bool do_update_labels;
  4548.     if (!aArg && aArgc == UCHAR_MAX) // Special signal from caller to avoid pointing any pending labels to this particular line.
  4549.     {
  4550.         aArgc = 0;
  4551.         do_update_labels = false;
  4552.     }
  4553.     else
  4554.         do_update_labels = true;
  4555.  
  4556.     Var *target_var;
  4557.     DerefType deref[MAX_DEREFS_PER_ARG];  // Will be used to temporarily store the var-deref locations in each arg.
  4558.     int deref_count;  // How many items are in deref array.
  4559.     ArgStruct *new_arg;  // We will allocate some dynamic memory for this, then hang it onto the new line.
  4560.     size_t operand_length;
  4561.     char *op_begin, *op_end, orig_char;
  4562.     char *this_aArgMap, *this_aArg, *cp;
  4563.     int open_parens;
  4564.     ActionTypeType *np;
  4565.     TransformCmds trans_cmd;
  4566.     bool is_function;
  4567.  
  4568.     //////////////////////////////////////////////////////////
  4569.     // Build the new arg list in dynamic memory.
  4570.     // The allocated structs will be attached to the new line.
  4571.     //////////////////////////////////////////////////////////
  4572.     if (!aArgc)
  4573.         new_arg = NULL;  // Just need an empty array in this case.
  4574.     else
  4575.     {
  4576.         if (   !(new_arg = (ArgStruct *)SimpleHeap::Malloc(aArgc * sizeof(ArgStruct)))   )
  4577.             return ScriptError(ERR_OUTOFMEM);
  4578.  
  4579.         int i, j, i_plus_one;
  4580.         bool in_quotes;
  4581.  
  4582.         for (i = 0; i < aArgc; ++i)
  4583.         {
  4584.             ////////////////
  4585.             // FOR EACH ARG:
  4586.             ////////////////
  4587.             this_aArg = aArg[i];                        // For performance and convenience.
  4588.             this_aArgMap = aArgMap ? aArgMap[i] : NULL; // Same.
  4589.             ArgStruct &this_new_arg = new_arg[i];       // Same.
  4590.             this_new_arg.is_expression = false;         // Set default early, for maintainability.
  4591.  
  4592.             if (aActionType == ACT_TRANSFORM)
  4593.             {
  4594.                 if (i == 1) // The second parameter (since the first is the OutputVar).
  4595.                     // Note that the following might return TRANS_CMD_INVALID just because the sub-command
  4596.                     // is containined in a variable reference.  That is why TRANS_CMD_INVALID does not
  4597.                     // produce an error at this stage, but only later when the line has been constructed
  4598.                     // far enough to call ArgHasDeref():
  4599.                     trans_cmd = Line::ConvertTransformCmd(this_aArg);
  4600.                     // The value of trans_cmd is also used by the syntax checker further below.
  4601.                 else if (i > 1) // i.e. Not the first param, only the third and fourth, which currently are either both numeric or both non-numeric for all cases.
  4602.                 {
  4603.                     switch(trans_cmd)
  4604.                     {
  4605.                     TRANSFORM_NON_EXPRESSION_CASES
  4606.                     default:
  4607.                         // For all other sub-commands, Arg #3 and #4 are expression-capable and will be made so
  4608.                         // if they pass the following check:
  4609.                         this_new_arg.is_expression = LegacyArgIsExpression(this_aArg, this_aArgMap);
  4610.                     }
  4611.                 }
  4612.             }
  4613.  
  4614.             // Before allocating memory for this Arg's text, first check if it's a pure
  4615.             // variable.  If it is, we store it differently (and there's no need to resolve
  4616.             // escape sequences in these cases, since var names can't contain them):
  4617.             if (aActionType == ACT_LOOP && i == 1 && aArg[0] && !stricmp(aArg[0], "Parse")) // Verified.
  4618.                 // i==1 --> 2nd arg's type is based on 1st arg's text.
  4619.                 this_new_arg.type = ARG_TYPE_INPUT_VAR;
  4620.             else
  4621.                 this_new_arg.type = Line::ArgIsVar(aActionType, i);
  4622.             // Since some vars are optional, the below allows them all to be blank or
  4623.             // not present in the arg list.  If a mandatory var is blank at this stage,
  4624.             // it's okay because all mandatory args are validated to be non-blank elsewhere:
  4625.             if (this_new_arg.type != ARG_TYPE_NORMAL)
  4626.             {
  4627.                 if (!*this_aArg)
  4628.                     // An optional input or output variable has been omitted, so indicate
  4629.                     // that this arg is not a variable, just a normal empty arg.  Functions
  4630.                     // such as ListLines() rely on this having been done because they assume,
  4631.                     // for performance reasons, that args marked as variables really are
  4632.                     // variables.  In addition, ExpandArgs() relies on this having been done
  4633.                     // as does the load-time validation for ACT_DRIVEGET:
  4634.                     this_new_arg.type = ARG_TYPE_NORMAL;
  4635.                 else
  4636.                 {
  4637.                     // Does this input or output variable contain a dereference?  If so, it must
  4638.                     // be resolved at runtime (to support arrays, etc.).
  4639.                     // Find the first non-escaped dereference symbol:
  4640.                     for (j = 0; this_aArg[j] && (this_aArg[j] != g_DerefChar || (this_aArgMap && this_aArgMap[j])); ++j);
  4641.                     if (!this_aArg[j])
  4642.                     {
  4643.                         // A non-escaped deref symbol wasn't found, therefore this variable does not
  4644.                         // appear to be something that must be resolved dynamically at runtime.
  4645.                         if (   !(target_var = FindOrAddVar(this_aArg))   )
  4646.                             return FAIL;  // The above already displayed the error.
  4647.                         // If this action type is something that modifies the contents of the var, ensure the var
  4648.                         // isn't a special/reserved one:
  4649.                         if (this_new_arg.type == ARG_TYPE_OUTPUT_VAR && VAR_IS_READONLY(*target_var))
  4650.                             return ScriptError(ERR_VAR_IS_READONLY, this_aArg);
  4651.                         // Rather than removing this arg from the list altogether -- which would distrub
  4652.                         // the ordering and hurt the maintainability of the code -- the next best thing
  4653.                         // in terms of saving memory is to store an empty string in place of the arg's
  4654.                         // text if that arg is a pure variable (i.e. since the name of the variable is already
  4655.                         // stored in the Var object, we don't need to store it twice):
  4656.                         this_new_arg.text = "";
  4657.                         this_new_arg.length = 0;
  4658.                         this_new_arg.deref = (DerefType *)target_var;
  4659.                         continue;
  4660.                     }
  4661.                     // else continue on to the below so that this input or output variable name's dynamic part
  4662.                     // (e.g. array%i%) can be partially resolved.
  4663.                 }
  4664.             }
  4665.             else // this_new_arg.type == ARG_TYPE_NORMAL (excluding those input/output_vars that were converted to normal because they were blank, above).
  4666.             {
  4667.                 // v1.0.29: Allow expressions in any parameter that starts with % followed by a space
  4668.                 // or tab. This should be unambiguous because spaces and tabs are illegal in variable names.
  4669.                 // Since there's little if any benefit to allowing input and output variables to be
  4670.                 // dynamically built via expression, for now it is disallowed.  If ever allow it,
  4671.                 // need to review other sections to ensure they will tolerate it.  Also, the following
  4672.                 // would probably need revision to get it to be detected as an output-variable:
  4673.                 // % Array%i% = value
  4674.                 if (*this_aArg == g_DerefChar && !(this_aArgMap && *this_aArgMap) // It's a non-literal deref character.
  4675.                     && IS_SPACE_OR_TAB(this_aArg[1])) // Followed by a space or tab.
  4676.                 {
  4677.                     this_new_arg.is_expression = true;
  4678.                     // Omit the percent sign and the space after it from further consideration.
  4679.                     this_aArg += 2;
  4680.                     if (this_aArgMap)
  4681.                         this_aArgMap += 2;
  4682.                     // ACT_ASSIGN isn't capable of dealing with expressions because ExecUntil() does not
  4683.                     // call ExpandArgs() automatically for it.  Thus its function, PerformAssign(), would
  4684.                     // not be given the expanded result of the expression.
  4685.                     if (aActionType == ACT_ASSIGN)
  4686.                         aActionType = ACT_ASSIGNEXPR;
  4687.                 }
  4688.             }
  4689.  
  4690.             // Below will set the new var to be the constant empty string if the
  4691.             // source var is NULL or blank.
  4692.             // e.g. If WinTitle is unspecified (blank), but WinText is non-blank.
  4693.             // Using empty string is much safer than NULL because these args
  4694.             // will be frequently accessed by various functions that might
  4695.             // not be equipped to handle NULLs.  Rather than having to remember
  4696.             // to check for NULL in every such case, setting it to a constant
  4697.             // empty string here should make things a lot more maintainable
  4698.             // and less bug-prone.  If there's ever a need for the contents
  4699.             // of this_new_arg to be modifiable (perhaps some obscure API calls require
  4700.             // modifiable strings?) can malloc a single-char to contain the empty string.
  4701.             // 
  4702.             // So that it can be passed to Malloc(), first update the length to match what the text will be
  4703.             // (if the alloc fails, an inaccurate length won't matter because it's an program-abort situation).
  4704.             // The length must fit into a WORD, which it will since each arg is literal text from a script's line,
  4705.             // which is limited to LINE_SIZE. The length member was added in v1.0.44.14 to boost runtime performance.
  4706.             this_new_arg.length = (WORD)strlen(this_aArg);
  4707.             if (   !(this_new_arg.text = SimpleHeap::Malloc(this_aArg, this_new_arg.length))   )
  4708.                 return FAIL;  // It already displayed the error for us.
  4709.  
  4710.             ////////////////////////////////////////////////////
  4711.             // Build the list of dereferenced vars for this arg.
  4712.             ////////////////////////////////////////////////////
  4713.             // Now that any escaped g_DerefChars have been marked, scan new_arg.text to
  4714.             // determine where the variable dereferences are (if any).  In addition to helping
  4715.             // runtime performance, this also serves to validate the script at load-time
  4716.             // so that some errors can be caught early.  Note: this_new_arg.text is scanned rather
  4717.             // than this_aArg because we want to establish pointers to the correct area of
  4718.             // memory:
  4719.             deref_count = 0;  // Init for each arg.
  4720.  
  4721.             if (np = g_act[aActionType].NumericParams) // This command has at least one numeric parameter.
  4722.             {
  4723.                 // As of v1.0.25, pure numeric parameters can optionally be numeric expressions, so check for that:
  4724.                 i_plus_one = i + 1;
  4725.                 for (; *np; ++np)
  4726.                 {
  4727.                     if (*np == i_plus_one) // This arg is enforced to be purely numeric.
  4728.                     {
  4729.                         if (aActionType == ACT_WINMOVE)
  4730.                         {
  4731.                             if (i > 1)
  4732.                             {
  4733.                                 // i indicates this is Arg #3 or beyond, which is one of the args that is
  4734.                                 // either the word "default" or a number/expression.
  4735.                                 if (!stricmp(this_new_arg.text, "default")) // It's not an expression.
  4736.                                     break; // The loop is over because this arg was found in the list.
  4737.                             }
  4738.                             else // This is the first or second arg, which are title/text vs. X/Y when aArgc > 2.
  4739.                                 if (aArgc > 2) // Title/text are not numeric/expressions.
  4740.                                     break; // The loop is over because this arg was found in the list.
  4741.                         }
  4742.                         // Otherwise, it might be an expression so do the final checks.
  4743.                         // Override the original false default of is_expression unless an exception applies.
  4744.                         // Since ACT_ASSIGNEXPR is not a legacy command, none of the legacy exceptions need
  4745.                         // to be applied to it.  For other commands, if any telltale character is present
  4746.                         // it's definitely an expression and the complex check after this one isn't needed:
  4747.                         if (aActionType == ACT_ASSIGNEXPR || StrChrAny(this_new_arg.text, EXPR_TELLTALES))
  4748.                             this_new_arg.is_expression = true;
  4749.                         else
  4750.                             this_new_arg.is_expression = LegacyArgIsExpression(this_new_arg.text, this_aArgMap);
  4751.                         break; // The loop is over if this arg is found in the list of mandatory-numeric args.
  4752.                     } // i is a mandatory-numeric arg
  4753.                 } // for each mandatory-numeric arg of this command, see if this arg matches its number.
  4754.             } // this command has a list of mandatory numeric-args.
  4755.  
  4756.             // To help runtime performance, the below changes an ACT_ASSIGNEXPR, ACT_TRANSFORM, and
  4757.             // perhaps others in the future, to become non-expressions if they contain only a single
  4758.             // numeric literal (or are entirely blank). At runtime, such args are expanded normally
  4759.             // rather than having to run them through the expression evaluator:
  4760.             if (this_new_arg.is_expression && IsPureNumeric(this_new_arg.text, true, true, true))
  4761.                 this_new_arg.is_expression = false;
  4762.  
  4763.             if (this_new_arg.is_expression)
  4764.             {
  4765.                 // Ensure parentheses are balanced:
  4766.                 for (cp = this_new_arg.text, in_quotes = false, open_parens = 0; *cp; ++cp)
  4767.                 {
  4768.                     switch (*cp)
  4769.                     {
  4770.                     // The simple method below is sufficient for our purpose even if a quoted string contains
  4771.                     // pairs of double-quotes to represent a single literal quote, e.g. "quoted ""word""".
  4772.                     // In other words, it relies on the fact that there must be an even number of quotes
  4773.                     // inside any mandatory-numeric arg that is an expression such as x=="red,blue"
  4774.                     case '"':
  4775.                         in_quotes = !in_quotes;
  4776.                         break;
  4777.                     case '(':
  4778.                         if (!in_quotes) // Literal parentheses inside a quoted string should not be counted for this purpose.
  4779.                             ++open_parens;
  4780.                         break;
  4781.                     case ')':
  4782.                         if (!in_quotes)
  4783.                         {
  4784.                             if (!open_parens)
  4785.                                 return ScriptError(ERR_MISSING_OPEN_PAREN, cp); // And indicate cp as the exact spot.
  4786.                             --open_parens;
  4787.                         }
  4788.                         break;
  4789.                     }
  4790.                 }
  4791.                 if (open_parens) // At least one '(' is never closed.
  4792.                     return ScriptError(ERR_MISSING_CLOSE_PAREN, this_new_arg.text);
  4793.  
  4794.                 #define ERR_EXP_ILLEGAL_CHAR "The leftmost character above is illegal in an expression." // "above" refers to the layout of the error dialog.
  4795.                 // ParseDerefs() won't consider escaped percent signs to be illegal, but in this case
  4796.                 // they should be since they have no meaning in expressions.  UPDATE for v1.0.44.11: The following
  4797.                 // is now commented out because it causes false positives (and fixing that probably isn't worth the
  4798.                 // performance & code size).  Specifically, the section below reports an error for escaped delimiters
  4799.                 // inside quotes such as x := "`%".  More importantly, it defeats the continuation section's %
  4800.                 // option; for example:
  4801.                 //   MsgBox %
  4802.                 //   (%  ; <<< This option here is defeated because it causes % to be replaced with `% at an early stage.
  4803.                 //   "%"
  4804.                 //   )
  4805.                 //if (this_aArgMap) // This arg has an arg map indicating which chars are escaped/literal vs. normal.
  4806.                 //    for (j = 0; this_new_arg.text[j]; ++j)
  4807.                 //        if (this_aArgMap[j] && this_new_arg.text[j] == g_DerefChar)
  4808.                 //            return ScriptError(ERR_EXP_ILLEGAL_CHAR, this_new_arg.text + j);
  4809.  
  4810.                 // Resolve all operands that aren't numbers into variable references.  Doing this here at
  4811.                 // load-time greatly improves runtime performance, especially for scripts that have a lot
  4812.                 // of variables.
  4813.                 for (op_begin = this_new_arg.text; *op_begin; op_begin = op_end)
  4814.                 {
  4815.                     if (*op_begin == '.' && op_begin[1] == '=') // v1.0.46.01: Support .=, but not any use of '.' because that is reserved as a struct/member operator.
  4816.                         op_begin += 2;
  4817.                     for (; *op_begin && strchr(EXPR_OPERAND_TERMINATORS, *op_begin); ++op_begin); // Skip over whitespace, operators, and parentheses.
  4818.                     if (!*op_begin) // The above loop reached the end of the string: No operands remaining.
  4819.                         break;
  4820.  
  4821.                     // Now op_begin is the start of an operand, which might be a variable reference, a numeric
  4822.                     // literal, or a string literal.  If it's a string literal, it is left as-is:
  4823.                     if (*op_begin == '"')
  4824.                     {
  4825.                         // Find the end of this string literal, noting that a pair of double quotes is
  4826.                         // a literal double quote inside the string:
  4827.                         for (op_end = op_begin + 1;; ++op_end)
  4828.                         {
  4829.                             if (!*op_end)
  4830.                                 return ScriptError(ERR_MISSING_CLOSE_QUOTE, op_begin);
  4831.                             if (*op_end == '"') // If not followed immediately by another, this is the end of it.
  4832.                             {
  4833.                                 ++op_end;
  4834.                                 if (*op_end != '"') // String terminator or some non-quote character.
  4835.                                     break;  // The previous char is the ending quote.
  4836.                                 //else a pair of quotes, which resolves to a single literal quote.
  4837.                                 // This pair is skipped over and the loop continues until the real end-quote is found.
  4838.                             }
  4839.                         }
  4840.                         // op_end is now set correctly to allow the outer loop to continue.
  4841.                         continue; // Ignore this literal string, letting the runtime expression parser recognize it.
  4842.                     }
  4843.                     
  4844.                     // Find the end of this operand (if *op_end is '\0', strchr() will find that too):
  4845.                     for (op_end = op_begin + 1; !strchr(EXPR_OPERAND_TERMINATORS, *op_end); ++op_end); // Find first whitespace, operator, or paren.
  4846.                     if (*op_end == '=' && op_end[-1] == '.') // v1.0.46.01: Support .=, but not any use of '.' because that is reserved as a struct/member operator.
  4847.                         --op_end;
  4848.                     // Now op_end marks the end of this operand.  The end might be the zero terminator, an operator, etc.
  4849.  
  4850.                     // Must be done only after op_end has been set above (since loop uses op_end):
  4851.                     if (*op_begin == '.' && strchr(" \t=", op_begin[1])) // If true, it can't be something like "5." because the dot inside would never be parsed separately in that case.  Also allows ".=" operator.
  4852.                         continue;
  4853.                     //else any '.' not followed by a space, tab, or '=' is likely a number without a leading zero,
  4854.                     // so continue on below to process it.
  4855.  
  4856.                     operand_length = op_end - op_begin;
  4857.  
  4858.                     // Check if it's AND/OR/NOT:
  4859.                     if (operand_length < 4 && operand_length > 1) // Ordered for short-circuit performance.
  4860.                     {
  4861.                         if (operand_length == 2)
  4862.                         {
  4863.                             if ((*op_begin == 'o' || *op_begin == 'O') && (op_begin[1] == 'r' || op_begin[1] == 'R'))
  4864.                             {    // "OR" was found.
  4865.                                 op_begin[0] = '|'; // v1.0.45: Transform into easier-to-parse symbols for improved
  4866.                                 op_begin[1] = '|'; // runtime performance and reduced code size.
  4867.                                 continue;
  4868.                             }
  4869.                         }
  4870.                         else // operand_length must be 3
  4871.                         {
  4872.                             switch (*op_begin)
  4873.                             {
  4874.                             case 'a':
  4875.                             case 'A':
  4876.                                 if (   (op_begin[1] == 'n' || op_begin[1] == 'N') // Relies on short-circuit boolean order.
  4877.                                     && (op_begin[2] == 'd' || op_begin[2] == 'D')   )
  4878.                                 {    // "AND" was found.
  4879.                                     op_begin[0] = '&'; // v1.0.45: Transform into easier-to-parse symbols for
  4880.                                     op_begin[1] = '&'; // improved runtime performance and reduced code size.
  4881.                                     op_begin[2] = ' '; // A space is used lieu of the complexity of the below.
  4882.                                     // Above seems better than below even though below would make it look a little
  4883.                                     // nicer in ListLines.  BELOW CAN'T WORK because this_new_arg.deref[] can contain
  4884.                                     // offsets that would also need to be adjusted:
  4885.                                     //memmove(op_begin + 2, op_begin + 3, strlen(op_begin+3)+1 ... or some expression involving this_new_arg.length this_new_arg.text);
  4886.                                     //--this_new_arg.length;
  4887.                                     //--op_end; // Ensure op_end is set up properly for the for-loop's post-iteration action.
  4888.                                     continue;
  4889.                                 }
  4890.                                 break;
  4891.  
  4892.                             case 'n': // v1.0.45: Unlike "AND" and "OR" above, this one is not given a substitute
  4893.                             case 'N': // because it's not the same as the "!" operator. See SYM_LOWNOT for comments.
  4894.                                 if (   (op_begin[1] == 'o' || op_begin[1] == 'O') // Relies on short-circuit boolean order.
  4895.                                     && (op_begin[2] == 't' || op_begin[2] == 'T')   )
  4896.                                     continue; // "NOT" was found.
  4897.                                 break;
  4898.                             }
  4899.                         }
  4900.                     } // End of check for AND/OR/NOT.
  4901.  
  4902.                     // Temporarily terminate, which avoids at least the below issue:
  4903.                     // Two or more extremely long var names together could exceed MAX_VAR_NAME_LENGTH
  4904.                     // e.g. LongVar%LongVar2% would be too long to store in a buffer of size MAX_VAR_NAME_LENGTH.
  4905.                     // This seems pretty darn unlikely, but perhaps doubling it would be okay.
  4906.                     // UPDATE: Above is now not an issue since caller's string is temporarily terminated rather
  4907.                     // than making a copy of it.
  4908.                     orig_char = *op_end;
  4909.                     *op_end = '\0';
  4910.  
  4911.                     // Illegal characters are legal when enclosed in double quotes.  So the following is
  4912.                     // done only after the above has ensured this operand is not one enclosed entirely in
  4913.                     // double quotes.
  4914.                     // The following characters are either illegal in expressions or reserved for future use.
  4915.                     // Rather than forbidding g_delimiter and g_DerefChar, it seems best to assume they are at
  4916.                     // their default values for this purpose.  Otherwise, if g_delimiter is an operator, that
  4917.                     // operator would then become impossible inside the expression.
  4918.                     if (cp = StrChrAny(op_begin, EXPR_ILLEGAL_CHARS))
  4919.                         return ScriptError(ERR_EXP_ILLEGAL_CHAR, cp);
  4920.  
  4921.                     // Below takes care of recognizing hexadecimal integers, which avoids the 'x' character
  4922.                     // inside of something like 0xFF from being detected as the name of a variable:
  4923.                     if (   !IsPureNumeric(op_begin, true, false, true) // Not a numeric literal...
  4924.                         && !(*op_begin == '?' && !op_begin[1])   ) // ...and not an isolated '?' operator.  Relies on short-circuit boolean order.
  4925.                     {
  4926.                         is_function = (orig_char == '(');
  4927.                         // This operand must be a variable/function reference or string literal, otherwise it's
  4928.                         // a syntax error.
  4929.                         // Check explicitly for derefs since the vast majority don't have any, and this
  4930.                         // avoids the function call in those cases:
  4931.                         if (strchr(op_begin, g_DerefChar)) // This operand contains at least one double dereference.
  4932.                         {
  4933.                             if (is_function)
  4934.                                 return ScriptError("Dynamic function calls are not supported.", op_begin);
  4935.                             // The derefs are parsed and added to the deref array at this stage (on a
  4936.                             // per-operand basis) rather than all at once for the entire arg because
  4937.                             // the deref array must be ordered according to the physical position of
  4938.                             // derefs inside the arg.  In the following example, the order of derefs
  4939.                             // must be x,i,y: if (x = Array%i% and y = 3)
  4940.                             if (!ParseDerefs(op_begin, this_aArgMap ? this_aArgMap + (op_begin - this_new_arg.text) : NULL
  4941.                                 , deref, deref_count))
  4942.                                 return FAIL; // It already displayed the error.  No need to undo temp. termination.
  4943.                             // And now leave this operand "raw" so that it will later be dereferenced again.
  4944.                             // In the following example, i made into a deref but the result (Array33) must be
  4945.                             // dereferenced during a second stage at runtime: if (x = Array%i%).
  4946.                         }
  4947.                         else // This operand is a variable name or function name (single deref).
  4948.                         {
  4949.                             #define TOO_MANY_REFS "Too many var/func refs." // Short msg since so rare.
  4950.                             if (deref_count >= MAX_DEREFS_PER_ARG)
  4951.                                 return ScriptError(TOO_MANY_REFS, op_begin); // Indicate which operand it ran out of space at.
  4952.                             // Store the deref's starting location, even for functions (leave it set to the start
  4953.                             // of the function's name for use when doing error reporting at other stages -- i.e.
  4954.                             // don't set it to the address of the first param or closing-paren-if-no-params):
  4955.                             deref[deref_count].marker = op_begin;
  4956.                             deref[deref_count].length = (DerefLengthType)operand_length;
  4957.                             if (deref[deref_count].is_function = is_function) // It's a function not a variable.
  4958.                                 // Set to NULL to catch bugs.  It must and will be filled in at a later stage
  4959.                                 // because the setting of each function's mJumpToLine relies upon the fact that
  4960.                                 // functions are added to the linked list only upon being formally defined
  4961.                                 // so that the most recently defined function is always last in the linked
  4962.                                 // list, awaiting its mJumpToLine that will appear beneath it.
  4963.                                 deref[deref_count].func = NULL;
  4964.                             else // It's a variable (or a scientific-notation literal) rather than a function.
  4965.                             {
  4966.                                 if (toupper(op_end[-1]) == 'E' && (orig_char == '+' || orig_char == '-') // Listed first for short-circuit performance with the below.
  4967.                                     && strchr(op_begin, '.')) // v1.0.46.11: This item appears to be a scientific-notation literal with the OPTIONAL +/- sign PRESENT on the exponent (e.g. 1.0e+001), so check that before checking if it's a variable name.
  4968.                                 {
  4969.                                     *op_end = orig_char; // Undo the temporary termination.
  4970.                                     do // Skip over the sign and its exponent; e.g. the "+1" in "1.0e+1".  There must be a sign in this particular sci-notation number or we would never have arrived here.
  4971.                                         ++op_end;
  4972.                                     while (*op_end >= '0' && *op_end <= '9'); // Avoid isdigit() because it sometimes causes a debug assertion failure at: (unsigned)(c + 1) <= 256 (probably only in debug mode), and maybe only when bad data got in it due to some other bug.
  4973.                                     // No need to do the following because a number can't validly be followed by the ".=" operator:
  4974.                                     //if (*op_end == '=' && op_end[-1] == '.') // v1.0.46.01: Support .=, but not any use of '.' because that is reserved as a struct/member operator.
  4975.                                     //    --op_end;
  4976.                                     continue; // Pure number, which doesn't need any processing at this stage.
  4977.                                 }
  4978.                                 // Since above didn't "continue", treat this item as a variable name:
  4979.                                 if (   !(deref[deref_count].var = FindOrAddVar(op_begin, operand_length))   )
  4980.                                     return FAIL; // The called function already displayed the error.
  4981.                             }
  4982.                             ++deref_count; // Since above didn't "continue" or "return".
  4983.                         }
  4984.                     }
  4985.                     //else purely numeric or '?'.  Do nothing since pure numbers and '?' don't need any
  4986.                     // processing at this stage.
  4987.                     *op_end = orig_char; // Undo the temporary termination.
  4988.                 } // expression pre-parsing loop.
  4989.  
  4990.                 // Now that the derefs have all been recognized above, simplify any special cases --
  4991.                 // such as single isolated derefs -- to enhance runtime performance.
  4992.                 // Make args that consist only of a quoted string-literal into non-expressions also:
  4993.                 if (!deref_count && *this_new_arg.text == '"')
  4994.                 {
  4995.                     // It has no derefs (e.g. x:="string" or x:=1024*1024), but since it's a single
  4996.                     // string literal, convert into a non-expression.  This is mainly for use by
  4997.                     // ACT_ASSIGNEXPR, but it seems slightly beneficial for other things in case
  4998.                     // they ever use quoted numeric ARGS such as "13", etc.  It's also simpler
  4999.                     // to do it unconditionally.
  5000.                     // Find the end of this string literal, noting that a pair of double quotes is
  5001.                     // a literal double quote inside the string:
  5002.                     for (cp = this_new_arg.text + 1;; ++cp)
  5003.                     {
  5004.                         if (!*cp) // No matching end-quote. Probably impossible due to validation further above.
  5005.                             return FAIL; // Force a silent failure so that the below can continue with confidence.
  5006.                         if (*cp == '"') // If not followed immediately by another, this is the end of it.
  5007.                         {
  5008.                             ++cp;
  5009.                             if (*cp != '"') // String terminator or some non-quote character.
  5010.                                 break;  // The previous char is the ending quote.
  5011.                             //else a pair of quotes, which resolves to a single literal quote.
  5012.                             // This pair is skipped over and the loop continues until the real end-quote is found.
  5013.                         }
  5014.                     }
  5015.                     // cp is now the character after the first literal string's ending quote.
  5016.                     // If that char is the terminator, that first string is the only string and this
  5017.                     // is a simple assignment of a string literal to be converted here.
  5018.                     // v1.0.40.05: Leave Send/PostMessage args (all of them, but specifically
  5019.                     // wParam and lParam) as expressions so that at runtime, the leading '"' in a
  5020.                     // quoted numeric string such as "123" can be used to differentiate that string
  5021.                     // from a numeric value/expression such as 123 or 122+1.
  5022.                     if (!*cp && aActionType != ACT_SENDMESSAGE && aActionType != ACT_POSTMESSAGE)
  5023.                     {
  5024.                         this_new_arg.is_expression = false;
  5025.                         // Bugfix for 1.0.25.06: The below has been disabled because:
  5026.                         // 1) It yields inconsistent results due to AutoTrim.  For example, the assignment
  5027.                         //    x := "  string" should retain the leading spaces unconditionally, since any
  5028.                         //    more complex expression would.  But if := were converted to = in this case,
  5029.                         //    AutoTrim would be in effect for it, which is undesirable.
  5030.                         // 2) It's not necessary in since ASSIGNEXPR handles both expressions and non-expressions.
  5031.                         //if (aActionType == ACT_ASSIGNEXPR)
  5032.                         //    aActionType = ACT_ASSIGN; // Convert to simple assignment.
  5033.                         *(--cp) = '\0'; // Remove the ending quote.
  5034.                         memmove(this_new_arg.text, this_new_arg.text + 1, cp - this_new_arg.text); // Remove the starting quote.
  5035.                         // Convert all pairs of quotes into single literal quotes:
  5036.                         StrReplace(this_new_arg.text, "\"\"", "\"", SCS_SENSITIVE);
  5037.                         // Above relies on the fact that StrReplace() does not do cascading replacements,
  5038.                         // meaning that a series of characters such as """" would be correctly converted into
  5039.                         // two double quotes rather than collapsing into only one.
  5040.                         this_new_arg.length = (WORD)strlen(this_new_arg.text); // Update length to reflect changes made above.
  5041.                     }
  5042.                 }
  5043.                 // Make things like "Sleep Var" and "Var := X" into non-expressions.  At runtime,
  5044.                 // such args are expanded normally rather than having to run them through the
  5045.                 // expression evaluator.  A simple test script shows that this one change can
  5046.                 // double the runtime performance of certain commands such as EnvAdd:
  5047.                 // Below is somewhat obsolete but kept for reference:
  5048.                 // This policy is basically saying that expressions are allowed to evaluate to strings
  5049.                 // everywhere appropriate, but that at the moment the only appropriate place is x := y
  5050.                 // because all other expressions should resolve to a numeric value by virtue of the fact
  5051.                 // that they *are* numeric parameters.  ValidateName() serves to eliminate cases where
  5052.                 // a single deref is accompanied by literal numbers, strings, or operators, e.g.
  5053.                 // Var := X + 1 ... Var := Var2 "xyz" ... Var := -Var2
  5054.                 else if (deref_count == 1 && Var::ValidateName(this_new_arg.text, false, DISPLAY_NO_ERROR)) // Single isolated deref.
  5055.                 {
  5056.                     // For the future, could consider changing ACT_ASSIGN here to ACT_ASSIGNEXPR because
  5057.                     // the latter probably performs better in this case.  However, the way ValidateName()
  5058.                     // is used above is probably not correct/sufficient to exclude cases to which this
  5059.                     // method should not be applied, such as Var := abc%Var2%.  In any case, some careful
  5060.                     // review of PerformAssign() should be done to gauge side-effects and determine
  5061.                     // whether the performance boost is really that signficant given that PerformAssign()
  5062.                     // is already boosted by the fact that it's exempt from automatic ExpandArgs() in
  5063.                     // ExecUntil().
  5064.                     this_new_arg.is_expression = false;
  5065.                     // But aActionType is left as ACT_ASSIGNEXPR because it probably performs better than
  5066.                     // ACT_ASSIGN in these cases.
  5067.                 }
  5068.                 else if (deref_count && !StrChrAny(this_new_arg.text, EXPR_OPERAND_TERMINATORS)) // No spaces, tabs, etc.
  5069.                 {
  5070.                     // Adjust if any of the following special cases apply:
  5071.                     // x := y  -> Mark as non-expression (after expression-parsing set up parsed derefs above)
  5072.                     //            so that the y deref will be only a single-deref to be directly stored in x.
  5073.                     //            This is done in case y contains a string.  Since an expression normally
  5074.                     //            evaluates to a number, without this workaround, x := y would be useless for
  5075.                     //            a simple assignment of a string.  This case is handled above.
  5076.                     // x := %y% -> Mark the right-side arg as an input variable so that it will be doubly
  5077.                     //             dereferenced, similar to StringTrimRight, Out, %y%, 0.  This seems best
  5078.                     //             because there would be little or no point to having it behave identically
  5079.                     //             to x := y.  It might even be confusing in light of the next case below.
  5080.                     // CASE #3:
  5081.                     // x := Literal%y%Literal%z%Literal -> Same as above.  This is done mostly to support
  5082.                     // retrieving array elements whose contents are *non-numeric* without having to use
  5083.                     // something like StringTrimRight.
  5084.                     
  5085.                     // Now we know it has at least one deref.  But if any operators or other characters disallowed
  5086.                     // in variables are present, it all three cases are disqualified and kept as expressions.
  5087.                     // This check is necessary for all three cases:
  5088.  
  5089.                     // No operators of any kind anywhere.  Not even +/- prefix, since those imply a numeric
  5090.                     // expression.  No chars illegal in var names except the percent signs themselves,
  5091.                     // e.g. *no* whitespace.
  5092.                     // Also, the first deref (indeed, all of them) should point to a percent sign, since
  5093.                     // there should not be any way for non-percent derefs to get mixed in with cases
  5094.                     // 2 or 3.
  5095.                     if (!deref[0].is_function && *deref[0].marker == g_DerefChar) // This appears to be case #2 or #3.
  5096.                     {
  5097.                         // Set it up so that x:=Array%i% behaves the same as StringTrimRight, Out, Array%i%, 0.
  5098.                         this_new_arg.is_expression = false;
  5099.                         this_new_arg.type = ARG_TYPE_INPUT_VAR;
  5100.                     }
  5101.                 }
  5102.             } // if (this_new_arg.is_expression)
  5103.             else // this arg does not contain an expression.
  5104.                 if (!ParseDerefs(this_new_arg.text, this_aArgMap, deref, deref_count))
  5105.                     return FAIL; // It already displayed the error.
  5106.  
  5107.             //////////////////////////////////////////////////////////////
  5108.             // Allocate mem for this arg's list of dereferenced variables.
  5109.             //////////////////////////////////////////////////////////////
  5110.             if (deref_count)
  5111.             {
  5112.                 // +1 for the "NULL-item" terminator:
  5113.                 if (   !(this_new_arg.deref = (DerefType *)SimpleHeap::Malloc((deref_count + 1) * sizeof(DerefType)))   )
  5114.                     return ScriptError(ERR_OUTOFMEM);
  5115.                 memcpy(this_new_arg.deref, deref, deref_count * sizeof(DerefType));
  5116.                 // Terminate the list of derefs with a deref that has a NULL marker:
  5117.                 this_new_arg.deref[deref_count].marker = NULL;
  5118.             }
  5119.             else
  5120.                 this_new_arg.deref = NULL;
  5121.         } // for each arg.
  5122.     } // else there are more than zero args.
  5123.  
  5124.     //////////////////////////////////////////////////////////////////////////////////////
  5125.     // Now the above has allocated some dynamic memory, the pointers to which we turn over
  5126.     // to Line's constructor so that they can be anchored to the new line.
  5127.     //////////////////////////////////////////////////////////////////////////////////////
  5128.     Line *the_new_line = new Line(mCurrFileIndex, mCombinedLineNumber, aActionType, new_arg, aArgc);
  5129.     if (!the_new_line)
  5130.         return ScriptError(ERR_OUTOFMEM);
  5131.  
  5132.     Line &line = *the_new_line;  // For performance and convenience.
  5133.  
  5134.     line.mPrevLine = mLastLine;  // Whether NULL or not.
  5135.     if (mFirstLine == NULL)
  5136.         mFirstLine = the_new_line;
  5137.     else
  5138.         mLastLine->mNextLine = the_new_line;
  5139.     // This must be done after the above:
  5140.     mLastLine = the_new_line;
  5141.     mCurrLine = the_new_line;  // To help error reporting.
  5142.  
  5143.     ///////////////////////////////////////////////////////////////////
  5144.     // Do any post-add validation & handling for specific action types.
  5145.     ///////////////////////////////////////////////////////////////////
  5146. #ifndef AUTOHOTKEYSC // For v1.0.35.01, some syntax checking is removed in compiled scripts to reduce their size.
  5147.     int value;    // For temp use during validation.
  5148.     double value_float;
  5149.     SYSTEMTIME st;  // same.
  5150. #endif
  5151.     // v1.0.38: The following should help reduce code size, and for some commands helps load-time
  5152.     // performance by avoiding multiple resolutions of a given macro:
  5153.     char *new_raw_arg1 = NEW_RAW_ARG1;
  5154.     char *new_raw_arg2 = NEW_RAW_ARG2;
  5155.     char *new_raw_arg3 = NEW_RAW_ARG3;
  5156.     char *new_raw_arg4 = NEW_RAW_ARG4;
  5157.  
  5158.     switch(aActionType)
  5159.     {
  5160.     // Fix for v1.0.35.02:
  5161.     // THESE FIRST FEW CASES MUST EXIT IN BOTH SELF-CONTAINED AND NORMAL VERSION since they alter the
  5162.     // attributes/members of some types of lines:
  5163.     case ACT_LOOP:
  5164.         // If possible, determine the type of loop so that the preparser can better
  5165.         // validate some things:
  5166.         switch (aArgc)
  5167.         {
  5168.         case 0:
  5169.             line.mAttribute = ATTR_LOOP_NORMAL;
  5170.             break;
  5171.         case 1: // With only 1 arg, it must be a normal loop, file-pattern loop, or registry loop.
  5172.             // v1.0.43.07: Added check for new_arg[0].is_expression so that an expression without any variables
  5173.             // it it works (e.g. Loop % 1+1):
  5174.             if (line.ArgHasDeref(1) || new_arg[0].is_expression) // Impossible to know now what type of loop (only at runtime).
  5175.                 line.mAttribute = ATTR_LOOP_UNKNOWN;
  5176.             else
  5177.             {
  5178.                 if (IsPureNumeric(new_raw_arg1, false))
  5179.                     line.mAttribute = ATTR_LOOP_NORMAL;
  5180.                 else
  5181.                     line.mAttribute = line.RegConvertRootKey(new_raw_arg1) ? ATTR_LOOP_REG : ATTR_LOOP_FILEPATTERN;
  5182.             }
  5183.             break;
  5184.         default:  // has 2 or more args.
  5185.             if (line.ArgHasDeref(1)) // Impossible to know now what type of loop (only at runtime).
  5186.                 line.mAttribute = ATTR_LOOP_UNKNOWN;
  5187.             else if (!stricmp(new_raw_arg1, "Read"))
  5188.                 line.mAttribute = ATTR_LOOP_READ_FILE;
  5189.             else if (!stricmp(new_raw_arg1, "Parse"))
  5190.                 line.mAttribute = ATTR_LOOP_PARSE;
  5191.             else // the 1st arg can either be a Root Key or a File Pattern, depending on the type of loop.
  5192.             {
  5193.                 line.mAttribute = line.RegConvertRootKey(new_raw_arg1) ? ATTR_LOOP_REG : ATTR_LOOP_FILEPATTERN;
  5194.                 if (line.mAttribute == ATTR_LOOP_FILEPATTERN)
  5195.                 {
  5196.                     // Validate whatever we can rather than waiting for runtime validation:
  5197.                     if (!line.ArgHasDeref(2) && Line::ConvertLoopMode(new_raw_arg2) == FILE_LOOP_INVALID)
  5198.                         return ScriptError(ERR_PARAM2_INVALID, new_raw_arg2);
  5199.                     if (*new_raw_arg3 && !line.ArgHasDeref(3))
  5200.                         if (strlen(new_raw_arg3) > 1 || (*new_raw_arg3 != '0' && *new_raw_arg3 != '1'))
  5201.                             return ScriptError(ERR_PARAM3_INVALID, new_raw_arg3);
  5202.                 }
  5203.                 else // Registry loop.
  5204.                 {
  5205.                     if (aArgc > 2 && !line.ArgHasDeref(3) && Line::ConvertLoopMode(new_raw_arg3) == FILE_LOOP_INVALID)
  5206.                         return ScriptError(ERR_PARAM3_INVALID, new_raw_arg3);
  5207.                     if (*new_raw_arg4 && !line.ArgHasDeref(4))
  5208.                         if (strlen(new_raw_arg4) > 1 || (*new_raw_arg4 != '0' && *new_raw_arg4 != '1'))
  5209.                             return ScriptError(ERR_PARAM4_INVALID, new_raw_arg4);
  5210.                 }
  5211.             }
  5212.         }
  5213.         break; // Outer switch().
  5214.  
  5215.     case ACT_REPEAT: // These types of loops are always "NORMAL".
  5216.         line.mAttribute = ATTR_LOOP_NORMAL;
  5217.         break;
  5218.  
  5219.     // This one alters g_persistent so is present in its entirety (for simplicity) in both SC an non-SC version.
  5220.     case ACT_GUI:
  5221.         // By design, scripts that use the GUI cmd anywhere are persistent.  Doing this here
  5222.         // also allows WinMain() to later detect whether this script should become #SingleInstance.
  5223.         // Note: Don't directly change g_AllowOnlyOneInstance here in case the remainder of the
  5224.         // script-loading process comes across any explicit uses of #SingleInstance, which would
  5225.         // override the default set here.
  5226.         g_persistent = true;
  5227. #ifndef AUTOHOTKEYSC // For v1.0.35.01, some syntax checking is removed in compiled scripts to reduce their size.
  5228.         if (aArgc > 0 && !line.ArgHasDeref(1))
  5229.         {
  5230.             GuiCommands gui_cmd = line.ConvertGuiCommand(new_raw_arg1);
  5231.  
  5232.             switch (gui_cmd)
  5233.             {
  5234.             case GUI_CMD_INVALID:
  5235.                 return ScriptError(ERR_PARAM1_INVALID, new_raw_arg1);
  5236.             case GUI_CMD_ADD:
  5237.                 if (aArgc > 1 && !line.ArgHasDeref(2))
  5238.                 {
  5239.                     GuiControls control_type;
  5240.                     if (   !(control_type = line.ConvertGuiControl(new_raw_arg2))   )
  5241.                         return ScriptError(ERR_PARAM2_INVALID, new_raw_arg2);
  5242.                     if (control_type == GUI_CONTROL_TREEVIEW && aArgc > 3) // Reserve it for future use such as a tab-indented continuation section that lists the tree hierarchy.
  5243.                         return ScriptError(ERR_PARAM4_OMIT, new_raw_arg4);
  5244.                 }
  5245.                 break;
  5246.             case GUI_CMD_CANCEL:
  5247.             case GUI_CMD_MINIMIZE:
  5248.             case GUI_CMD_MAXIMIZE:
  5249.             case GUI_CMD_RESTORE:
  5250.             case GUI_CMD_DESTROY:
  5251.             case GUI_CMD_DEFAULT:
  5252.             case GUI_CMD_OPTIONS:
  5253.                 if (aArgc > 1)
  5254.                     return ScriptError("Parameter #2 and beyond should be omitted in this case.", new_raw_arg2);
  5255.                 break;
  5256.             case GUI_CMD_SUBMIT:
  5257.             case GUI_CMD_MENU:
  5258.             case GUI_CMD_LISTVIEW:
  5259.             case GUI_CMD_TREEVIEW:
  5260.             case GUI_CMD_FLASH:
  5261.                 if (aArgc > 2)
  5262.                     return ScriptError("Parameter #3 and beyond should be omitted in this case.", new_raw_arg3);
  5263.                 break;
  5264.             // No action for these since they have a varying number of optional params:
  5265.             //case GUI_CMD_SHOW:
  5266.             //case GUI_CMD_FONT:
  5267.             //case GUI_CMD_MARGIN:
  5268.             //case GUI_CMD_TAB:
  5269.             //case GUI_CMD_COLOR: No load-time param validation to avoid larger EXE size.
  5270.             }
  5271.         }
  5272. #endif
  5273.         break;
  5274.  
  5275.     case ACT_GROUPADD:
  5276.     case ACT_GROUPACTIVATE:
  5277.     case ACT_GROUPDEACTIVATE:
  5278.     case ACT_GROUPCLOSE:
  5279.         // For all these, store a pointer to the group to help performance.
  5280.         // We create a non-existent group even for ACT_GROUPACTIVATE, ACT_GROUPDEACTIVATE
  5281.         // and ACT_GROUPCLOSE because we can't rely on the ACT_GROUPADD commands having
  5282.         // been parsed prior to them (e.g. something like "Gosub, DefineGroups" may appear
  5283.         // in the auto-execute portion of the script).
  5284.         if (!line.ArgHasDeref(1))
  5285.             if (   !(line.mAttribute = FindGroup(new_raw_arg1, true))   ) // Create-if-not-found so that performance is enhanced at runtime.
  5286.                 return FAIL;  // The above already displayed the error.
  5287.         if (aActionType == ACT_GROUPACTIVATE || aActionType == ACT_GROUPDEACTIVATE)
  5288.         {
  5289.             if (*new_raw_arg2 && !line.ArgHasDeref(2))
  5290.                 if (strlen(new_raw_arg2) > 1 || toupper(*new_raw_arg2) != 'R')
  5291.                     return ScriptError(ERR_PARAM2_INVALID, new_raw_arg2);
  5292.         }
  5293.         else if (aActionType == ACT_GROUPCLOSE)
  5294.             if (*new_raw_arg2 && !line.ArgHasDeref(2))
  5295.                 if (strlen(new_raw_arg2) > 1 || !strchr("RA", toupper(*new_raw_arg2)))
  5296.                     return ScriptError(ERR_PARAM2_INVALID, new_raw_arg2);
  5297.         break;
  5298.  
  5299. #ifndef AUTOHOTKEYSC // For v1.0.35.01, some syntax checking is removed in compiled scripts to reduce their size.
  5300.     case ACT_RETURN:
  5301.         if (aArgc > 0 && !g.CurrentFunc)
  5302.             return ScriptError("Return's parameter should be blank except inside a function.");
  5303.         break;
  5304.  
  5305.     case ACT_AUTOTRIM:
  5306.     case ACT_DETECTHIDDENWINDOWS:
  5307.     case ACT_DETECTHIDDENTEXT:
  5308.     case ACT_SETSTORECAPSLOCKMODE:
  5309.         if (aArgc > 0 && !line.ArgHasDeref(1) && !line.ConvertOnOff(new_raw_arg1))
  5310.             return ScriptError(ERR_ON_OFF, new_raw_arg1);
  5311.         break;
  5312.  
  5313.     case ACT_STRINGCASESENSE:
  5314.         if (aArgc > 0 && !line.ArgHasDeref(1) && line.ConvertStringCaseSense(new_raw_arg1) == SCS_INVALID)
  5315.             return ScriptError(ERR_ON_OFF_LOCALE, new_raw_arg1);
  5316.         break;
  5317.  
  5318.     case ACT_SETBATCHLINES:
  5319.         if (aArgc > 0 && !line.ArgHasDeref(1))
  5320.         {
  5321.             if (!strcasestr(new_raw_arg1, "ms") && !IsPureNumeric(new_raw_arg1, true, false)) // For simplicity and due to rarity, new_arg[0].is_expression isn't checked, so a line with no variables or function-calls like "SetBatchLines % 1+1" will be wrongly seen as a syntax error.
  5322.                 return ScriptError(ERR_PARAM1_INVALID, new_raw_arg1);
  5323.         }
  5324.         break;
  5325.  
  5326.     case ACT_SUSPEND:
  5327.         if (aArgc > 0 && !line.ArgHasDeref(1) && !line.ConvertOnOffTogglePermit(new_raw_arg1))
  5328.             return ScriptError(ERR_ON_OFF_TOGGLE_PERMIT, new_raw_arg1);
  5329.         break;
  5330.  
  5331.     case ACT_BLOCKINPUT:
  5332.         if (aArgc > 0 && !line.ArgHasDeref(1) && !line.ConvertBlockInput(new_raw_arg1))
  5333.             return ScriptError(ERR_PARAM1_INVALID, new_raw_arg1);
  5334.         break;
  5335.  
  5336.     case ACT_SENDMODE:
  5337.         if (aArgc > 0 && !line.ArgHasDeref(1) && line.ConvertSendMode(new_raw_arg1, SM_INVALID) == SM_INVALID)
  5338.             return ScriptError(ERR_PARAM1_INVALID, new_raw_arg1);
  5339.         break;
  5340.  
  5341.     case ACT_PAUSE:
  5342.     case ACT_KEYHISTORY:
  5343.         if (aArgc > 0 && !line.ArgHasDeref(1) && !line.ConvertOnOffToggle(new_raw_arg1))
  5344.             return ScriptError(ERR_ON_OFF_TOGGLE, new_raw_arg1);
  5345.         break;
  5346.  
  5347.     case ACT_SETNUMLOCKSTATE:
  5348.     case ACT_SETSCROLLLOCKSTATE:
  5349.     case ACT_SETCAPSLOCKSTATE:
  5350.         if (aArgc > 0 && !line.ArgHasDeref(1) && !line.ConvertOnOffAlways(new_raw_arg1))
  5351.             return ScriptError(ERR_PARAM1_INVALID, new_raw_arg1);
  5352.         break;
  5353.  
  5354.     case ACT_STRINGMID:
  5355.         if (aArgc > 4 && !line.ArgHasDeref(5) && stricmp(NEW_RAW_ARG5, "L"))
  5356.             return ScriptError(ERR_PARAM5_INVALID, NEW_RAW_ARG5);
  5357.         break;
  5358.  
  5359.     case ACT_STRINGGETPOS:
  5360.         if (*new_raw_arg4 && !line.ArgHasDeref(4) && !strchr("LR1", toupper(*new_raw_arg4)))
  5361.             return ScriptError(ERR_PARAM4_INVALID, new_raw_arg4);
  5362.         break;
  5363.  
  5364.     case ACT_STRINGSPLIT:
  5365.         if (*new_raw_arg1 && !line.ArgHasDeref(1)) // The output array must be a legal name.
  5366.         {
  5367.             // 1.0.46.10: Fixed to look up ArrayName0 in advance (here at loadtime) so that runtime can
  5368.             // know whether it's local or global.  This is necessary because only here at loadtime
  5369.             // is there any awareness of the current function's list of declared variables (to conserve
  5370.             // memory, that list is longer available at runtime).
  5371.             char temp_var_name[MAX_VAR_NAME_LENGTH + 10]; // Provide extra room for trailing "0", and to detect names that are too long.
  5372.             snprintf(temp_var_name, sizeof(temp_var_name), "%s0", new_raw_arg1);
  5373.             if (   !(the_new_line->mAttribute = FindOrAddVar(temp_var_name))   )
  5374.                 return FAIL;  // The above already displayed the error.
  5375.         }
  5376.         //else it's a dynamic array name.  Since that's very rare, just use the old runtime behavior for
  5377.         // backward compatibility.
  5378.         break;
  5379.  
  5380.     case ACT_REGREAD:
  5381.         // The below has two checks in case the user is using the 5-param method with the 5th parameter
  5382.         // being blank to indicate that the key's "default" value should be read.  For example:
  5383.         // RegRead, OutVar, REG_SZ, HKEY_CURRENT_USER, Software\Winamp,
  5384.         if (aArgc > 4 || line.RegConvertValueType(new_raw_arg2))
  5385.         {
  5386.             // The obsolete 5-param method is being used, wherein ValueType is the 2nd param.
  5387.             if (*new_raw_arg3 && !line.ArgHasDeref(3) && !line.RegConvertRootKey(new_raw_arg3))
  5388.                 return ScriptError(ERR_REG_KEY, new_raw_arg3);
  5389.         }
  5390.         else // 4-param method.
  5391.             if (*new_raw_arg2 && !line.ArgHasDeref(2) && !line.RegConvertRootKey(new_raw_arg2))
  5392.                 return ScriptError(ERR_REG_KEY, new_raw_arg2);
  5393.         break;
  5394.  
  5395.     case ACT_REGWRITE:
  5396.         // Both of these checks require that at least two parameters be present.  Otherwise, the command
  5397.         // is being used in its registry-loop mode and is validated elsewhere:
  5398.         if (aArgc > 1)
  5399.         {
  5400.             if (*new_raw_arg1 && !line.ArgHasDeref(1) && !line.RegConvertValueType(new_raw_arg1))
  5401.                 return ScriptError(ERR_REG_VALUE_TYPE, new_raw_arg1);
  5402.             if (*new_raw_arg2 && !line.ArgHasDeref(2) && !line.RegConvertRootKey(new_raw_arg2))
  5403.                 return ScriptError(ERR_REG_KEY, new_raw_arg2);
  5404.         }
  5405.         break;
  5406.  
  5407.     case ACT_REGDELETE:
  5408.         if (*new_raw_arg1 && !line.ArgHasDeref(1) && !line.RegConvertRootKey(new_raw_arg1))
  5409.             return ScriptError(ERR_REG_KEY, new_raw_arg1);
  5410.         break;
  5411.  
  5412.     case ACT_SOUNDGET:
  5413.     case ACT_SOUNDSET:
  5414.         if (aActionType == ACT_SOUNDSET && aArgc > 0 && !line.ArgHasDeref(1))
  5415.         {
  5416.             // The value of catching syntax errors at load-time seems to outweigh the fact that this check
  5417.             // sees a valid no-deref expression such as 300-250 as invalid.
  5418.             value_float = ATOF(new_raw_arg1);
  5419.             if (value_float < -100 || value_float > 100)
  5420.                 return ScriptError(ERR_PERCENT, new_raw_arg1);
  5421.         }
  5422.         if (*new_raw_arg2 && !line.ArgHasDeref(2) && !line.SoundConvertComponentType(new_raw_arg2))
  5423.             return ScriptError(ERR_PARAM2_INVALID, new_raw_arg2);
  5424.         if (*new_raw_arg3 && !line.ArgHasDeref(3) && line.SoundConvertControlType(new_raw_arg3) == MIXERCONTROL_CONTROLTYPE_INVALID)
  5425.             return ScriptError(ERR_PARAM3_INVALID, new_raw_arg3);
  5426.         break;
  5427.  
  5428.     case ACT_SOUNDSETWAVEVOLUME:
  5429.         if (aArgc > 0 && !line.ArgHasDeref(1))
  5430.         {
  5431.             // The value of catching syntax errors at load-time seems to outweigh the fact that this check
  5432.             // sees a valid no-deref expression such as 300-250 as invalid.
  5433.             value_float = ATOF(new_raw_arg1);
  5434.             if (value_float < -100 || value_float > 100)
  5435.                 return ScriptError(ERR_PERCENT, new_raw_arg1);
  5436.         }
  5437.         break;
  5438.  
  5439.     case ACT_SOUNDPLAY:
  5440.         if (*new_raw_arg2 && !line.ArgHasDeref(2) && stricmp(new_raw_arg2, "wait") && stricmp(new_raw_arg2, "1"))
  5441.             return ScriptError(ERR_PARAM2_INVALID, new_raw_arg2);
  5442.         break;
  5443.  
  5444.     case ACT_PIXELSEARCH:
  5445.     case ACT_IMAGESEARCH:
  5446.         if (!*new_raw_arg3 || !*new_raw_arg4 || !*NEW_RAW_ARG5 || !*NEW_RAW_ARG6 || !*NEW_RAW_ARG7)
  5447.             return ScriptError("Parameters 3 through 7 must not be blank.");
  5448.         if (aActionType != ACT_IMAGESEARCH)
  5449.         {
  5450.             if (*NEW_RAW_ARG8 && !line.ArgHasDeref(8))
  5451.             {
  5452.                 // The value of catching syntax errors at load-time seems to outweigh the fact that this check
  5453.                 // sees a valid no-deref expression such as 300-200 as invalid.
  5454.                 value = ATOI(NEW_RAW_ARG8);
  5455.                 if (value < 0 || value > 255)
  5456.                     return ScriptError(ERR_PARAM8_INVALID, NEW_RAW_ARG8);
  5457.             }
  5458.         }
  5459.         break;
  5460.  
  5461.     case ACT_COORDMODE:
  5462.         if (*new_raw_arg1 && !line.ArgHasDeref(1) && !line.ConvertCoordModeAttrib(new_raw_arg1))
  5463.             return ScriptError(ERR_PARAM1_INVALID, new_raw_arg1);
  5464.         break;
  5465.  
  5466.     case ACT_SETDEFAULTMOUSESPEED:
  5467.         if (*new_raw_arg1 && !line.ArgHasDeref(1))
  5468.  
  5469.         {
  5470.             // The value of catching syntax errors at load-time seems to outweigh the fact that this check
  5471.             // sees a valid no-deref expression such as 1+2 as invalid.
  5472.             value = ATOI(new_raw_arg1);
  5473.             if (value < 0 || value > MAX_MOUSE_SPEED)
  5474.                 return ScriptError(ERR_MOUSE_SPEED, new_raw_arg1);
  5475.         }
  5476.         break;
  5477.  
  5478.     case ACT_MOUSEMOVE:
  5479.         if (*new_raw_arg3 && !line.ArgHasDeref(3))
  5480.         {
  5481.             // The value of catching syntax errors at load-time seems to outweigh the fact that this check
  5482.             // sees a valid no-deref expression such as 200-150 as invalid.
  5483.             value = ATOI(new_raw_arg3);
  5484.             if (value < 0 || value > MAX_MOUSE_SPEED)
  5485.                 return ScriptError(ERR_MOUSE_SPEED, new_raw_arg3);
  5486.         }
  5487.         if (*new_raw_arg4 && !line.ArgHasDeref(4) && toupper(*new_raw_arg4) != 'R')
  5488.             return ScriptError(ERR_PARAM4_INVALID, new_raw_arg4);
  5489.         if (!line.ValidateMouseCoords(new_raw_arg1, new_raw_arg2))
  5490.             return ScriptError(ERR_MOUSE_COORD, new_raw_arg1);
  5491.         break;
  5492.  
  5493.     case ACT_MOUSECLICK:
  5494.         if (*NEW_RAW_ARG5 && !line.ArgHasDeref(5))
  5495.         {
  5496.             // The value of catching syntax errors at load-time seems to outweigh the fact that this check
  5497.             // sees a valid no-deref expression such as 200-150 as invalid.
  5498.             value = ATOI(NEW_RAW_ARG5);
  5499.             if (value < 0 || value > MAX_MOUSE_SPEED)
  5500.                 return ScriptError(ERR_MOUSE_SPEED, NEW_RAW_ARG5);
  5501.         }
  5502.         if (*NEW_RAW_ARG6 && !line.ArgHasDeref(6))
  5503.             if (strlen(NEW_RAW_ARG6) > 1 || !strchr("UD", toupper(*NEW_RAW_ARG6)))  // Up / Down
  5504.                 return ScriptError(ERR_PARAM6_INVALID, NEW_RAW_ARG6);
  5505.         if (*NEW_RAW_ARG7 && !line.ArgHasDeref(7) && toupper(*NEW_RAW_ARG7) != 'R')
  5506.             return ScriptError(ERR_PARAM7_INVALID, NEW_RAW_ARG7);
  5507.         // Check that the button is valid (e.g. left/right/middle):
  5508.         if (*new_raw_arg1 && !line.ArgHasDeref(1) && !line.ConvertMouseButton(new_raw_arg1)) // Treats blank as "Left".
  5509.             return ScriptError(ERR_MOUSE_BUTTON, new_raw_arg1);
  5510.         if (!line.ValidateMouseCoords(new_raw_arg2, new_raw_arg3))
  5511.             return ScriptError(ERR_MOUSE_COORD, new_raw_arg2);
  5512.         break;
  5513.  
  5514.     case ACT_MOUSECLICKDRAG:
  5515.         // Even though we check for blanks here at load-time, we don't bother to do so at runtime
  5516.         // (i.e. if a dereferenced var resolved to blank, it will be treated as a zero):
  5517.         if (!*new_raw_arg4 || !*NEW_RAW_ARG5)
  5518.             return ScriptError("Parameter #4 and 5 required.");
  5519.         if (*NEW_RAW_ARG6 && !line.ArgHasDeref(6))
  5520.         {
  5521.             // The value of catching syntax errors at load-time seems to outweigh the fact that this check
  5522.             // sees a valid no-deref expression such as 200-150 as invalid.
  5523.             value = ATOI(NEW_RAW_ARG6);
  5524.             if (value < 0 || value > MAX_MOUSE_SPEED)
  5525.                 return ScriptError(ERR_MOUSE_SPEED, NEW_RAW_ARG6);
  5526.         }
  5527.         if (*NEW_RAW_ARG7 && !line.ArgHasDeref(7) && toupper(*NEW_RAW_ARG7) != 'R')
  5528.             return ScriptError(ERR_PARAM7_INVALID, NEW_RAW_ARG7);
  5529.         if (!line.ArgHasDeref(1))
  5530.             if (!line.ConvertMouseButton(new_raw_arg1, false))
  5531.                 return ScriptError(ERR_MOUSE_BUTTON, new_raw_arg1);
  5532.         if (!line.ValidateMouseCoords(new_raw_arg2, new_raw_arg3))
  5533.             return ScriptError(ERR_MOUSE_COORD, new_raw_arg2);
  5534.         if (!line.ValidateMouseCoords(new_raw_arg4, NEW_RAW_ARG5))
  5535.             return ScriptError(ERR_MOUSE_COORD, new_raw_arg4);
  5536.         break;
  5537.  
  5538.     case ACT_CONTROLSEND:
  5539.     case ACT_CONTROLSENDRAW:
  5540.         // Window params can all be blank in this case, but characters to send should
  5541.         // be non-blank (but it's ok if its a dereferenced var that resolves to blank
  5542.         // at runtime):
  5543.         if (!*new_raw_arg2)
  5544.             return ScriptError(ERR_PARAM2_REQUIRED);
  5545.         break;
  5546.  
  5547.     case ACT_CONTROLCLICK:
  5548.         // Check that the button is valid (e.g. left/right/middle):
  5549.         if (*new_raw_arg4 && !line.ArgHasDeref(4)) // i.e. it's allowed to be blank (defaults to left).
  5550.             if (!line.ConvertMouseButton(new_raw_arg4)) // Treats blank as "Left".
  5551.                 return ScriptError(ERR_MOUSE_BUTTON, new_raw_arg4);
  5552.         break;
  5553.  
  5554.     case ACT_ADD:
  5555.     case ACT_SUB:
  5556.         if (aArgc > 2)
  5557.         {
  5558.             if (*new_raw_arg3 && !line.ArgHasDeref(3))
  5559.                 if (!strchr("SMHD", toupper(*new_raw_arg3)))  // (S)econds, (M)inutes, (H)ours, or (D)ays
  5560.                     return ScriptError(ERR_PARAM3_INVALID, new_raw_arg3);
  5561.             if (aActionType == ACT_SUB && *new_raw_arg2 && !line.ArgHasDeref(2))
  5562.                 if (!YYYYMMDDToSystemTime(new_raw_arg2, st, true))
  5563.                     return ScriptError(ERR_INVALID_DATETIME, new_raw_arg2);
  5564.         }
  5565.         break;
  5566.  
  5567.     case ACT_FILEINSTALL:
  5568.     case ACT_FILECOPY:
  5569.     case ACT_FILEMOVE:
  5570.     case ACT_FILECOPYDIR:
  5571.     case ACT_FILEMOVEDIR:
  5572.         if (*new_raw_arg3 && !line.ArgHasDeref(3))
  5573.         {
  5574.             // The value of catching syntax errors at load-time seems to outweigh the fact that this check
  5575.             // sees a valid no-deref expression such as 2-1 as invalid.
  5576.             value = ATOI(new_raw_arg3);
  5577.             bool is_pure_numeric = IsPureNumeric(new_raw_arg3, false, true); // Consider negatives to be non-numeric.
  5578.             if (aActionType == ACT_FILEMOVEDIR)
  5579.             {
  5580.                 if (!is_pure_numeric && toupper(*new_raw_arg3) != 'R'
  5581.                     || is_pure_numeric && value > 2) // IsPureNumeric() already checked if value < 0. 
  5582.                     return ScriptError(ERR_PARAM3_INVALID, new_raw_arg3);
  5583.             }
  5584.             else
  5585.             {
  5586.                 if (!is_pure_numeric || value > 1) // IsPureNumeric() already checked if value < 0.
  5587.                     return ScriptError(ERR_PARAM3_INVALID, new_raw_arg3);
  5588.             }
  5589.         }
  5590.         if (aActionType == ACT_FILEINSTALL)
  5591.         {
  5592.             if (aArgc > 0 && line.ArgHasDeref(1))
  5593.                 return ScriptError("Must not contain variables.", new_raw_arg1);
  5594.         }
  5595.         break;
  5596.  
  5597.     case ACT_FILEREMOVEDIR:
  5598.         if (*new_raw_arg2 && !line.ArgHasDeref(2))
  5599.         {
  5600.             // The value of catching syntax errors at load-time seems to outweigh the fact that this check
  5601.             // sees a valid no-deref expression such as 3-2 as invalid.
  5602.             value = ATOI(new_raw_arg2);
  5603.             if (!IsPureNumeric(new_raw_arg2, false, true) || value > 1) // IsPureNumeric() prevents negatives.
  5604.                 return ScriptError(ERR_PARAM2_INVALID, new_raw_arg2);
  5605.         }
  5606.         break;
  5607.  
  5608.     case ACT_FILESETATTRIB:
  5609.         if (*new_raw_arg1 && !line.ArgHasDeref(1))
  5610.         {
  5611.             for (char *cp = new_raw_arg1; *cp; ++cp)
  5612.                 if (!strchr("+-^RASHNOT", toupper(*cp)))
  5613.                     return ScriptError(ERR_PARAM1_INVALID, new_raw_arg1);
  5614.         }
  5615.         // For the next two checks:
  5616.         // The value of catching syntax errors at load-time seems to outweigh the fact that this check
  5617.         // sees a valid no-deref expression such as 300-200 as invalid.
  5618.         if (aArgc > 2 && !line.ArgHasDeref(3) && line.ConvertLoopMode(new_raw_arg3) == FILE_LOOP_INVALID)
  5619.             return ScriptError(ERR_PARAM3_INVALID, new_raw_arg3);
  5620.         if (*new_raw_arg4 && !line.ArgHasDeref(4))
  5621.             if (strlen(new_raw_arg4) > 1 || (*new_raw_arg4 != '0' && *new_raw_arg4 != '1'))
  5622.                 return ScriptError(ERR_PARAM4_INVALID, new_raw_arg4);
  5623.         break;
  5624.  
  5625.     case ACT_FILEGETTIME:
  5626.         if (*new_raw_arg3 && !line.ArgHasDeref(3))
  5627.             if (strlen(new_raw_arg3) > 1 || !strchr("MCA", toupper(*new_raw_arg3)))
  5628.                 return ScriptError(ERR_PARAM3_INVALID, new_raw_arg3);
  5629.         break;
  5630.  
  5631.     case ACT_FILESETTIME:
  5632.         if (*new_raw_arg1 && !line.ArgHasDeref(1))
  5633.             if (!YYYYMMDDToSystemTime(new_raw_arg1, st, true))
  5634.                 return ScriptError(ERR_INVALID_DATETIME, new_raw_arg1);
  5635.         if (*new_raw_arg3 && !line.ArgHasDeref(3))
  5636.             if (strlen(new_raw_arg3) > 1 || !strchr("MCA", toupper(*new_raw_arg3)))
  5637.                 return ScriptError(ERR_PARAM3_INVALID, new_raw_arg3);
  5638.         // For the next two checks:
  5639.         // The value of catching syntax errors at load-time seems to outweigh the fact that this check
  5640.         // sees a valid no-deref expression such as 300-200 as invalid.
  5641.         if (aArgc > 3 && !line.ArgHasDeref(4) && line.ConvertLoopMode(new_raw_arg4) == FILE_LOOP_INVALID)
  5642.             return ScriptError(ERR_PARAM4_INVALID, new_raw_arg4);
  5643.         if (*NEW_RAW_ARG5 && !line.ArgHasDeref(5))
  5644.             if (strlen(NEW_RAW_ARG5) > 1 || (*NEW_RAW_ARG5 != '0' && *NEW_RAW_ARG5 != '1'))
  5645.                 return ScriptError(ERR_PARAM5_INVALID, NEW_RAW_ARG5);
  5646.         break;
  5647.  
  5648.     case ACT_FILEGETSIZE:
  5649.         if (*new_raw_arg3 && !line.ArgHasDeref(3))
  5650.             if (strlen(new_raw_arg3) > 1 || !strchr("BKM", toupper(*new_raw_arg3))) // Allow B=Bytes as undocumented.
  5651.                 return ScriptError(ERR_PARAM3_INVALID, new_raw_arg3);
  5652.         break;
  5653.  
  5654.     case ACT_SETTITLEMATCHMODE:
  5655.         if (aArgc > 0 && !line.ArgHasDeref(1) && !line.ConvertTitleMatchMode(new_raw_arg1))
  5656.             return ScriptError(ERR_TITLEMATCHMODE, new_raw_arg1);
  5657.         break;
  5658.  
  5659.     case ACT_SETFORMAT:
  5660.         if (aArgc > 0 && !line.ArgHasDeref(1))
  5661.         {
  5662.             if (!stricmp(new_raw_arg1, "Float"))
  5663.             {
  5664.                 if (aArgc > 1 && !line.ArgHasDeref(2))
  5665.                 {
  5666.                     if (!IsPureNumeric(new_raw_arg2, true, false, true, true) // v1.0.46.11: Allow impure numbers to support scientific notation; e.g. 0.6e or 0.6E.
  5667.                         || strlen(new_raw_arg2) >= sizeof(g.FormatFloat) - 2)
  5668.                         return ScriptError(ERR_PARAM2_INVALID, new_raw_arg2);
  5669.                 }
  5670.             }
  5671.             else if (!stricmp(new_raw_arg1, "Integer"))
  5672.             {
  5673.                 if (aArgc > 1 && !line.ArgHasDeref(2) && toupper(*new_raw_arg2) != 'H' && toupper(*new_raw_arg2) != 'D')
  5674.                     return ScriptError(ERR_PARAM2_INVALID, new_raw_arg2);
  5675.             }
  5676.             else
  5677.                 return ScriptError(ERR_PARAM1_INVALID, new_raw_arg1);
  5678.         }
  5679.         // Size must be less than sizeof() minus 2 because need room to prepend the '%' and append
  5680.         // the 'f' to make it a valid format specifier string:
  5681.         break;
  5682.  
  5683.     case ACT_TRANSFORM:
  5684.         if (aArgc > 1 && !line.ArgHasDeref(2))
  5685.         {
  5686.             // The value of trans_cmd was already set at an earlier stage, but only here can the error
  5687.             // for new_raw_arg3 be displayed because only here was it finally possible to call
  5688.             // ArgHasDeref() [above].
  5689.             if (trans_cmd == TRANS_CMD_INVALID)
  5690.                 return ScriptError(ERR_PARAM2_INVALID, new_raw_arg2);
  5691.             if (trans_cmd == TRANS_CMD_UNICODE && !*line.mArg[0].text) // blank text means output-var is not a dynamically built one.
  5692.             {
  5693.                 // If the output var isn't the clipboard, the mode is "retrieve clipboard text as UTF-8".
  5694.                 // Therefore, Param#3 should be blank in that case to avoid unnecessary fetching of the
  5695.                 // entire clipboard contents as plain text when in fact the command itself will be
  5696.                 // directly accessing the clipboard rather than relying on the automatic parameter and
  5697.                 // deref handling.
  5698.                 if (VAR(line.mArg[0])->Type() == VAR_CLIPBOARD)
  5699.                 {
  5700.                     if (aArgc < 3)
  5701.                         return ScriptError("Parameter #3 must not be blank in this case.");
  5702.                 }
  5703.                 else
  5704.                     if (aArgc > 2)
  5705.                         return ScriptError(ERR_PARAM3_MUST_BE_BLANK, new_raw_arg3);
  5706.                 break; // This type has been fully checked above.
  5707.             }
  5708.  
  5709.             // The value of catching syntax errors at load-time seems to outweigh the fact that this check
  5710.             // sees a valid no-deref expression such as 1+2 as invalid.
  5711.             if (!line.ArgHasDeref(3)) // "true" since it might have been made into an InputVar due to being a simple expression.
  5712.             {
  5713.                 switch(trans_cmd)
  5714.                 {
  5715.                 case TRANS_CMD_CHR:
  5716.                 case TRANS_CMD_BITNOT:
  5717.                 case TRANS_CMD_BITSHIFTLEFT:
  5718.                 case TRANS_CMD_BITSHIFTRIGHT:
  5719.                 case TRANS_CMD_BITAND:
  5720.                 case TRANS_CMD_BITOR:
  5721.                 case TRANS_CMD_BITXOR:
  5722.                     if (!IsPureNumeric(new_raw_arg3, true, false))
  5723.                         return ScriptError("Parameter #3 must be an integer in this case.", new_raw_arg3);
  5724.                     break;
  5725.  
  5726.                 case TRANS_CMD_MOD:
  5727.                 case TRANS_CMD_EXP:
  5728.                 case TRANS_CMD_ROUND:
  5729.                 case TRANS_CMD_CEIL:
  5730.                 case TRANS_CMD_FLOOR:
  5731.                 case TRANS_CMD_ABS:
  5732.                 case TRANS_CMD_SIN:
  5733.                 case TRANS_CMD_COS:
  5734.                 case TRANS_CMD_TAN:
  5735.                 case TRANS_CMD_ASIN:
  5736.                 case TRANS_CMD_ACOS:
  5737.                 case TRANS_CMD_ATAN:
  5738.                     if (!IsPureNumeric(new_raw_arg3, true, false, true))
  5739.                         return ScriptError("Parameter #3 must be a number in this case.", new_raw_arg3);
  5740.                     break;
  5741.  
  5742.                 case TRANS_CMD_POW:
  5743.                 case TRANS_CMD_SQRT:
  5744.                 case TRANS_CMD_LOG:
  5745.                 case TRANS_CMD_LN:
  5746.                     if (!IsPureNumeric(new_raw_arg3, false, false, true))
  5747.                         return ScriptError("Parameter #3 must be a positive integer in this case.", new_raw_arg3);
  5748.                     break;
  5749.  
  5750.                 // The following are not listed above because no validation of Parameter #3 is needed at this stage:
  5751.                 // TRANS_CMD_ASC
  5752.                 // TRANS_CMD_UNICODE
  5753.                 // TRANS_CMD_HTML
  5754.                 // TRANS_CMD_DEREF
  5755.                 }
  5756.             }
  5757.  
  5758.             switch(trans_cmd)
  5759.             {
  5760.             case TRANS_CMD_ASC:
  5761.             case TRANS_CMD_CHR:
  5762.             case TRANS_CMD_DEREF:
  5763.             case TRANS_CMD_UNICODE:
  5764.             case TRANS_CMD_HTML:
  5765.             case TRANS_CMD_EXP:
  5766.             case TRANS_CMD_SQRT:
  5767.             case TRANS_CMD_LOG:
  5768.             case TRANS_CMD_LN:
  5769.             case TRANS_CMD_CEIL:
  5770.             case TRANS_CMD_FLOOR:
  5771.             case TRANS_CMD_ABS:
  5772.             case TRANS_CMD_SIN:
  5773.             case TRANS_CMD_COS:
  5774.             case TRANS_CMD_TAN:
  5775.             case TRANS_CMD_ASIN:
  5776.             case TRANS_CMD_ACOS:
  5777.             case TRANS_CMD_ATAN:
  5778.             case TRANS_CMD_BITNOT:
  5779.                 if (*new_raw_arg4)
  5780.                     return ScriptError(ERR_PARAM4_OMIT, new_raw_arg4);
  5781.                 break;
  5782.  
  5783.             case TRANS_CMD_BITAND:
  5784.             case TRANS_CMD_BITOR:
  5785.             case TRANS_CMD_BITXOR:
  5786.                 if (!line.ArgHasDeref(4) && !IsPureNumeric(new_raw_arg4, true, false))
  5787.                     return ScriptError("Parameter #4 must be an integer in this case.", new_raw_arg4);
  5788.                 break;
  5789.  
  5790.             case TRANS_CMD_BITSHIFTLEFT:
  5791.             case TRANS_CMD_BITSHIFTRIGHT:
  5792.                 if (!line.ArgHasDeref(4) && !IsPureNumeric(new_raw_arg4, false, false))
  5793.                     return ScriptError("Parameter #4 must be a positive integer in this case.", new_raw_arg4);
  5794.                 break;
  5795.  
  5796.             case TRANS_CMD_ROUND:
  5797.                 if (*new_raw_arg4 && !line.ArgHasDeref(4) && !IsPureNumeric(new_raw_arg4, true, false))
  5798.                     return ScriptError("Parameter #4 must be blank or an integer in this case.", new_raw_arg4);
  5799.                 break;
  5800.  
  5801.             case TRANS_CMD_MOD:
  5802.             case TRANS_CMD_POW:
  5803.                 if (!line.ArgHasDeref(4) && !IsPureNumeric(new_raw_arg4, true, false, true))
  5804.                     return ScriptError("Parameter #4 must be a number in this case.", new_raw_arg4);
  5805.                 break;
  5806. #ifdef _DEBUG
  5807.             default:
  5808.                 return ScriptError("DEBUG: Unhandled", new_raw_arg2);  // To improve maintainability.
  5809. #endif
  5810.             }
  5811.  
  5812.             switch(trans_cmd)
  5813.             {
  5814.             case TRANS_CMD_CHR:
  5815.                 if (!line.ArgHasDeref(3))
  5816.                 {
  5817.                     value = ATOI(new_raw_arg3);
  5818.                     if (!IsPureNumeric(new_raw_arg3, false, false) || value > 255) // IsPureNumeric() checks for value < 0 too.
  5819.                         return ScriptError(ERR_PARAM3_INVALID, new_raw_arg3);
  5820.                 }
  5821.                 break;
  5822.             case TRANS_CMD_MOD:
  5823.                 if (!line.ArgHasDeref(4) && !ATOF(new_raw_arg4)) // Parameter is omitted or something that resolves to zero.
  5824.                     return ScriptError(ERR_DIVIDEBYZERO, new_raw_arg4);
  5825.                 break;
  5826.             }
  5827.         }
  5828.         break;
  5829.  
  5830.     case ACT_MENU:
  5831.         if (aArgc > 1 && !line.ArgHasDeref(2))
  5832.         {
  5833.             MenuCommands menu_cmd = line.ConvertMenuCommand(new_raw_arg2);
  5834.  
  5835.             switch(menu_cmd)
  5836.             {
  5837.             case MENU_CMD_TIP:
  5838.             case MENU_CMD_ICON:
  5839.             case MENU_CMD_NOICON:
  5840.             case MENU_CMD_MAINWINDOW:
  5841.             case MENU_CMD_NOMAINWINDOW:
  5842.             case MENU_CMD_CLICK:
  5843.             {
  5844.                 bool is_tray = true;  // Assume true if unknown.
  5845.                 if (aArgc > 0 && !line.ArgHasDeref(1))
  5846.                     if (stricmp(new_raw_arg1, "tray"))
  5847.                         is_tray = false;
  5848.                 if (!is_tray)
  5849.                     return ScriptError(ERR_MENUTRAY, new_raw_arg1);
  5850.                 break;
  5851.             }
  5852.             }
  5853.  
  5854.             switch (menu_cmd)
  5855.             {
  5856.             case MENU_CMD_INVALID:
  5857.                 return ScriptError(ERR_PARAM2_INVALID, new_raw_arg2);
  5858.  
  5859.             case MENU_CMD_NODEFAULT:
  5860.             case MENU_CMD_STANDARD:
  5861.             case MENU_CMD_NOSTANDARD:
  5862.             case MENU_CMD_DELETEALL:
  5863.             case MENU_CMD_NOICON:
  5864.             case MENU_CMD_MAINWINDOW:
  5865.             case MENU_CMD_NOMAINWINDOW:
  5866.                 if (*new_raw_arg3 || *new_raw_arg4 || *NEW_RAW_ARG5 || *NEW_RAW_ARG6)
  5867.                     return ScriptError("Parameter #3 and beyond should be omitted in this case.", new_raw_arg3);
  5868.                 break;
  5869.  
  5870.             case MENU_CMD_RENAME:
  5871.             case MENU_CMD_USEERRORLEVEL:
  5872.             case MENU_CMD_CHECK:
  5873.             case MENU_CMD_UNCHECK:
  5874.             case MENU_CMD_TOGGLECHECK:
  5875.             case MENU_CMD_ENABLE:
  5876.             case MENU_CMD_DISABLE:
  5877.             case MENU_CMD_TOGGLEENABLE:
  5878.             case MENU_CMD_DEFAULT:
  5879.             case MENU_CMD_DELETE:
  5880.             case MENU_CMD_TIP:
  5881.             case MENU_CMD_CLICK:
  5882.                 if (   menu_cmd != MENU_CMD_RENAME && (*new_raw_arg4 || *NEW_RAW_ARG5 || *NEW_RAW_ARG6)   )
  5883.                     return ScriptError("Parameter #4 and beyond should be omitted in this case.", new_raw_arg4);
  5884.                 switch(menu_cmd)
  5885.                 {
  5886.                 case MENU_CMD_USEERRORLEVEL:
  5887.                 case MENU_CMD_TIP:
  5888.                 case MENU_CMD_DEFAULT:
  5889.                 case MENU_CMD_DELETE:
  5890.                     break;  // i.e. for commands other than the above, do the default below.
  5891.                 default:
  5892.                     if (!*new_raw_arg3)
  5893.                         return ScriptError("Parameter #3 must not be blank in this case.");
  5894.                 }
  5895.                 break;
  5896.  
  5897.             // These have a highly variable number of parameters, or are too rarely used
  5898.             // to warrant detailed load-time checking, so are not validated here:
  5899.             //case MENU_CMD_SHOW:
  5900.             //case MENU_CMD_ADD:
  5901.             //case MENU_CMD_COLOR:
  5902.             //case MENU_CMD_ICON:
  5903.             }
  5904.         }
  5905.         break;
  5906.  
  5907.     case ACT_THREAD:
  5908.         if (aArgc > 0 && !line.ArgHasDeref(1) && !line.ConvertThreadCommand(new_raw_arg1))
  5909.             return ScriptError(ERR_PARAM1_INVALID, new_raw_arg1);
  5910.         break;
  5911.  
  5912.     case ACT_CONTROL:
  5913.         if (aArgc > 0 && !line.ArgHasDeref(1))
  5914.         {
  5915.             ControlCmds control_cmd = line.ConvertControlCmd(new_raw_arg1);
  5916.             switch (control_cmd)
  5917.             {
  5918.             case CONTROL_CMD_INVALID:
  5919.                 return ScriptError(ERR_PARAM1_INVALID, new_raw_arg1);
  5920.             case CONTROL_CMD_STYLE:
  5921.             case CONTROL_CMD_EXSTYLE:
  5922.             case CONTROL_CMD_TABLEFT:
  5923.             case CONTROL_CMD_TABRIGHT:
  5924.             case CONTROL_CMD_ADD:
  5925.             case CONTROL_CMD_DELETE:
  5926.             case CONTROL_CMD_CHOOSE:
  5927.             case CONTROL_CMD_CHOOSESTRING:
  5928.             case CONTROL_CMD_EDITPASTE:
  5929.                 if (control_cmd != CONTROL_CMD_TABLEFT && control_cmd != CONTROL_CMD_TABRIGHT && !*new_raw_arg2)
  5930.                     return ScriptError("Parameter #2 must not be blank in this case.");
  5931.                 break;
  5932.             default: // All commands except the above should have a blank Value parameter.
  5933.                 if (*new_raw_arg2)
  5934.                     return ScriptError(ERR_PARAM2_MUST_BE_BLANK, new_raw_arg2);
  5935.             }
  5936.         }
  5937.         break;
  5938.  
  5939.     case ACT_CONTROLGET:
  5940.         if (aArgc > 1 && !line.ArgHasDeref(2))
  5941.         {
  5942.             ControlGetCmds control_get_cmd = line.ConvertControlGetCmd(new_raw_arg2);
  5943.             switch (control_get_cmd)
  5944.             {
  5945.             case CONTROLGET_CMD_INVALID:
  5946.                 return ScriptError(ERR_PARAM2_INVALID, new_raw_arg2);
  5947.             case CONTROLGET_CMD_FINDSTRING:
  5948.             case CONTROLGET_CMD_LINE:
  5949.                 if (!*new_raw_arg3)
  5950.                     return ScriptError("Parameter #3 must not be blank in this case.");
  5951.                 break;
  5952.             case CONTROLGET_CMD_LIST:
  5953.                 break; // Simply break for any sub-commands that have an optional parameter 3.
  5954.             default: // All commands except the above should have a blank Value parameter.
  5955.                 if (*new_raw_arg3)
  5956.                     return ScriptError(ERR_PARAM3_MUST_BE_BLANK, new_raw_arg3);
  5957.             }
  5958.         }
  5959.         break;
  5960.  
  5961.     case ACT_GUICONTROL:
  5962.         if (!*new_raw_arg2) // ControlID
  5963.             return ScriptError(ERR_PARAM2_REQUIRED);
  5964.         if (aArgc > 0 && !line.ArgHasDeref(1))
  5965.         {
  5966.             GuiControlCmds guicontrol_cmd = line.ConvertGuiControlCmd(new_raw_arg1);
  5967.             switch (guicontrol_cmd)
  5968.             {
  5969.             case GUICONTROL_CMD_INVALID:
  5970.                 return ScriptError(ERR_PARAM1_INVALID, new_raw_arg1);
  5971.             case GUICONTROL_CMD_CONTENTS:
  5972.             case GUICONTROL_CMD_TEXT:
  5973.                 break; // Do nothing for the above commands since Param3 is optional.
  5974.             case GUICONTROL_CMD_MOVE:
  5975.             case GUICONTROL_CMD_MOVEDRAW:
  5976.             case GUICONTROL_CMD_CHOOSE:
  5977.             case GUICONTROL_CMD_CHOOSESTRING:
  5978.                 if (!*new_raw_arg3)
  5979.                     return ScriptError("Parameter #3 must not be blank in this case.");
  5980.                 break;
  5981.             default: // All commands except the above should have a blank Text parameter.
  5982.                 if (*new_raw_arg3)
  5983.                     return ScriptError(ERR_PARAM3_MUST_BE_BLANK, new_raw_arg3);
  5984.             }
  5985.         }
  5986.         break;
  5987.  
  5988.     case ACT_GUICONTROLGET:
  5989.         if (aArgc > 1 && !line.ArgHasDeref(2))
  5990.         {
  5991.             GuiControlGetCmds guicontrolget_cmd = line.ConvertGuiControlGetCmd(new_raw_arg2);
  5992.             // This first check's error messages take precedence over the next check's:
  5993.             switch (guicontrolget_cmd)
  5994.             {
  5995.             case GUICONTROLGET_CMD_INVALID:
  5996.                 return ScriptError(ERR_PARAM2_INVALID, new_raw_arg2);
  5997.             case GUICONTROLGET_CMD_CONTENTS:
  5998.                 break; // Do nothing, since Param4 is optional in this case.
  5999.             default: // All commands except the above should have a blank parameter here.
  6000.                 if (*new_raw_arg4) // Currently true for all, since it's a FutureUse param.
  6001.                     return ScriptError(ERR_PARAM4_MUST_BE_BLANK, new_raw_arg4);
  6002.             }
  6003.             if (guicontrolget_cmd == GUICONTROLGET_CMD_FOCUS || guicontrolget_cmd == GUICONTROLGET_CMD_FOCUSV)
  6004.             {
  6005.                 if (*new_raw_arg3)
  6006.                     return ScriptError(ERR_PARAM3_MUST_BE_BLANK, new_raw_arg3);
  6007.             }
  6008.             // else it can be optionally blank, in which case the output variable is used as the
  6009.             // ControlID also.
  6010.         }
  6011.         break;
  6012.  
  6013.     case ACT_DRIVE:
  6014.         if (aArgc > 0 && !line.ArgHasDeref(1))
  6015.         {
  6016.             DriveCmds drive_cmd = line.ConvertDriveCmd(new_raw_arg1);
  6017.             if (!drive_cmd)
  6018.                 return ScriptError(ERR_PARAM1_INVALID, new_raw_arg1);
  6019.             if (drive_cmd != DRIVE_CMD_EJECT && !*new_raw_arg2)
  6020.                 return ScriptError("Parameter #2 must not be blank in this case.");
  6021.             // For DRIVE_CMD_LABEL: Note that is is possible and allowed for the new label to be blank.
  6022.             // Not currently done since all sub-commands take a mandatory or optional ARG3:
  6023.             //if (drive_cmd != ... && *new_raw_arg3)
  6024.             //    return ScriptError(ERR_PARAM3_MUST_BE_BLANK, new_raw_arg3);
  6025.         }
  6026.         break;
  6027.  
  6028.     case ACT_DRIVEGET:
  6029.         if (!line.ArgHasDeref(2))  // Don't check "aArgc > 1" because of DRIVEGET_CMD_SETLABEL's param format.
  6030.         {
  6031.             DriveGetCmds drive_get_cmd = line.ConvertDriveGetCmd(new_raw_arg2);
  6032.             if (!drive_get_cmd)
  6033.                 return ScriptError(ERR_PARAM2_INVALID, new_raw_arg2);
  6034.             if (drive_get_cmd != DRIVEGET_CMD_LIST && drive_get_cmd != DRIVEGET_CMD_STATUSCD && !*new_raw_arg3)
  6035.                 return ScriptError("Parameter #3 must not be blank in this case.");
  6036.             if (drive_get_cmd != DRIVEGET_CMD_SETLABEL && (aArgc < 1 || line.mArg[0].type == ARG_TYPE_NORMAL))
  6037.                 // The output variable has been omitted.
  6038.                 return ScriptError("Parameter #1 must not be blank in this case.");
  6039.         }
  6040.         break;
  6041.  
  6042.     case ACT_PROCESS:
  6043.         if (aArgc > 0 && !line.ArgHasDeref(1))
  6044.         {
  6045.             ProcessCmds process_cmd = line.ConvertProcessCmd(new_raw_arg1);
  6046.             if (process_cmd != PROCESS_CMD_PRIORITY && process_cmd != PROCESS_CMD_EXIST && !*new_raw_arg2)
  6047.                 return ScriptError("Parameter #2 must not be blank in this case.");
  6048.             switch (process_cmd)
  6049.             {
  6050.             case PROCESS_CMD_INVALID:
  6051.                 return ScriptError(ERR_PARAM1_INVALID, new_raw_arg1);
  6052.             case PROCESS_CMD_EXIST:
  6053.             case PROCESS_CMD_CLOSE:
  6054.                 if (*new_raw_arg3)
  6055.                     return ScriptError(ERR_PARAM3_MUST_BE_BLANK, new_raw_arg3);
  6056.                 break;
  6057.             case PROCESS_CMD_PRIORITY:
  6058.                 if (!*new_raw_arg3 || (!line.ArgHasDeref(3) && !strchr(PROCESS_PRIORITY_LETTERS, toupper(*new_raw_arg3))))
  6059.                     return ScriptError(ERR_PARAM3_INVALID, new_raw_arg3);
  6060.                 break;
  6061.             case PROCESS_CMD_WAIT:
  6062.             case PROCESS_CMD_WAITCLOSE:
  6063.                 if (*new_raw_arg3 && !line.ArgHasDeref(3) && !IsPureNumeric(new_raw_arg3, false, true, true))
  6064.                     return ScriptError("If present, parameter #3 must be a positive number in this case.", new_raw_arg3);
  6065.                 break;
  6066.             }
  6067.         }
  6068.         break;
  6069.  
  6070.     // For ACT_WINMOVE, don't validate anything for mandatory args so that its two modes of
  6071.     // operation can be supported: 2-param mode and normal-param mode.
  6072.     // For these, although we validate that at least one is non-blank here, it's okay at
  6073.     // runtime for them all to resolve to be blank, without an error being reported.
  6074.     // It's probably more flexible that way since the commands are equipped to handle
  6075.     // all-blank params.
  6076.     // Not these because they can be used with the "last-used window" mode:
  6077.     //case ACT_IFWINEXIST:
  6078.     //case ACT_IFWINNOTEXIST:
  6079.     // Not these because they can have their window params all-blank to work in "last-used window" mode:
  6080.     //case ACT_IFWINACTIVE:
  6081.     //case ACT_IFWINNOTACTIVE:
  6082.     //case ACT_WINACTIVATE:
  6083.     //case ACT_WINWAITCLOSE:
  6084.     //case ACT_WINWAITACTIVE:
  6085.     //case ACT_WINWAITNOTACTIVE:
  6086.     case ACT_WINACTIVATEBOTTOM:
  6087.         if (!*new_raw_arg1 && !*new_raw_arg2 && !*new_raw_arg3 && !*new_raw_arg4)
  6088.             return ScriptError(ERR_WINDOW_PARAM);
  6089.         break;
  6090.  
  6091.     case ACT_WINWAIT:
  6092.         if (!*new_raw_arg1 && !*new_raw_arg2 && !*new_raw_arg4 && !*NEW_RAW_ARG5) // ARG3 is omitted because it's the timeout.
  6093.             return ScriptError(ERR_WINDOW_PARAM);
  6094.         break;
  6095.  
  6096.     case ACT_WINMENUSELECTITEM:
  6097.         // Window params can all be blank in this case, but the first menu param should
  6098.         // be non-blank (but it's ok if its a dereferenced var that resolves to blank
  6099.         // at runtime):
  6100.         if (!*new_raw_arg3)
  6101.             return ScriptError(ERR_PARAM3_REQUIRED);
  6102.         break;
  6103.  
  6104.     case ACT_WINSET:
  6105.         if (aArgc > 0 && !line.ArgHasDeref(1))
  6106.         {
  6107.             switch(line.ConvertWinSetAttribute(new_raw_arg1))
  6108.             {
  6109.             case WINSET_TRANSPARENT:
  6110.                 if (aArgc > 1 && !line.ArgHasDeref(2))
  6111.                 {
  6112.                     value = ATOI(new_raw_arg2);
  6113.                     if (value < 0 || value > 255)
  6114.                         return ScriptError(ERR_PARAM2_INVALID, new_raw_arg2);
  6115.                 }
  6116.                 break;
  6117.             case WINSET_TRANSCOLOR:
  6118.                 if (!*new_raw_arg2)
  6119.                     return ScriptError("Parameter #2 must not be blank in this case.");
  6120.                 break;
  6121.             case WINSET_ALWAYSONTOP:
  6122.                 if (aArgc > 1 && !line.ArgHasDeref(2) && !line.ConvertOnOffToggle(new_raw_arg2))
  6123.                     return ScriptError(ERR_ON_OFF_TOGGLE, new_raw_arg2);
  6124.                 break;
  6125.             case WINSET_BOTTOM:
  6126.             case WINSET_TOP:
  6127.             case WINSET_REDRAW:
  6128.             case WINSET_ENABLE:
  6129.             case WINSET_DISABLE:
  6130.                 if (*new_raw_arg2)
  6131.                     return ScriptError(ERR_PARAM2_MUST_BE_BLANK);
  6132.                 break;
  6133.             case WINSET_INVALID:
  6134.                 return ScriptError(ERR_PARAM1_INVALID, new_raw_arg1);
  6135.             }
  6136.         }
  6137.         break;
  6138.  
  6139.     case ACT_WINGET:
  6140.         if (!line.ArgHasDeref(2) && !line.ConvertWinGetCmd(new_raw_arg2)) // It's okay if ARG2 is blank.
  6141.             return ScriptError(ERR_PARAM2_INVALID, new_raw_arg2);
  6142.         break;
  6143.  
  6144.     case ACT_SYSGET:
  6145.         if (!line.ArgHasDeref(2) && !line.ConvertSysGetCmd(new_raw_arg2))
  6146.             return ScriptError(ERR_PARAM2_INVALID, new_raw_arg2);
  6147.         break;
  6148.  
  6149.     case ACT_INPUTBOX:
  6150.         if (*NEW_RAW_ARG9)  // && !line.ArgHasDeref(9)
  6151.             return ScriptError("Parameter #9 must be blank.", NEW_RAW_ARG9);
  6152.         break;
  6153.  
  6154.     case ACT_MSGBOX:
  6155.         if (aArgc > 1) // i.e. this MsgBox is using the 3-param or 4-param style.
  6156.             if (!line.ArgHasDeref(1)) // i.e. if it's a deref, we won't try to validate it now.
  6157.                 if (!IsPureNumeric(new_raw_arg1)) // Allow it to be entirely whitespace to indicate 0, like Aut2.
  6158.                     return ScriptError(ERR_PARAM1_INVALID, new_raw_arg1);
  6159.         if (aArgc > 3) // EVEN THOUGH IT'S NUMERIC, due to MsgBox's smart-comma handling, this cannot be an expression because it would never have been detected as the fourth parameter to begin with.
  6160.             if (!line.ArgHasDeref(4)) // i.e. if it's a deref, we won't try to validate it now.
  6161.                 if (!IsPureNumeric(new_raw_arg4, false, true, true))
  6162.                     return ScriptError(ERR_PARAM4_INVALID, new_raw_arg4);
  6163.         break;
  6164.  
  6165.     case ACT_IFMSGBOX:
  6166.         if (aArgc > 0 && !line.ArgHasDeref(1) && !line.ConvertMsgBoxResult(new_raw_arg1))
  6167.             return ScriptError(ERR_PARAM1_INVALID, new_raw_arg1);
  6168.         break;
  6169.  
  6170.     case ACT_IFIS:
  6171.     case ACT_IFISNOT:
  6172.         if (aArgc > 1 && !line.ArgHasDeref(2) && !line.ConvertVariableTypeName(new_raw_arg2))
  6173.             // Don't refer to it as "Parameter #2" because this command isn't formatted/displayed that way.
  6174.             // Update: Param2 is more descriptive than the other (short) alternatives:
  6175.             return ScriptError(ERR_PARAM2_INVALID, new_raw_arg2);
  6176.         break;
  6177.  
  6178.     case ACT_GETKEYSTATE:
  6179.         // v1.0.44.03: Don't validate single-character key names because although a character like โฟ might have no
  6180.         // matching VK in system's default layout, that layout could change to something which does have a VK for it.
  6181.         if (aArgc > 1 && !line.ArgHasDeref(2) && strlen(new_raw_arg2) > 1 && !TextToVK(new_raw_arg2) && !ConvertJoy(new_raw_arg2))
  6182.             return ScriptError(ERR_INVALID_KEY_OR_BUTTON, new_raw_arg2);
  6183.         break;
  6184.  
  6185.     case ACT_KEYWAIT: // v1.0.44.03: See comment above.
  6186.         if (aArgc > 0 && !line.ArgHasDeref(1) && strlen(new_raw_arg1) > 1 && !TextToVK(new_raw_arg1) && !ConvertJoy(new_raw_arg1))
  6187.             return ScriptError(ERR_INVALID_KEY_OR_BUTTON, new_raw_arg1);
  6188.         break;
  6189.  
  6190.     case ACT_DIV:
  6191.         if (!line.ArgHasDeref(2) && !new_arg[1].is_expression) // i.e. don't validate the following until runtime:
  6192.             if (!ATOF(new_raw_arg2))                           // x/=y ... x/=(4/4)/4 (v1.0.46.01: added is_expression check for expressions with no variables or function-calls).
  6193.                 return ScriptError(ERR_DIVIDEBYZERO, new_raw_arg2);
  6194.         break;
  6195. #endif  // The above section is in place only if when not AUTOHOTKEYSC.
  6196.     }
  6197.  
  6198.     if (mNextLineIsFunctionBody)
  6199.     {
  6200.         mLastFunc->mJumpToLine = the_new_line;
  6201.         mNextLineIsFunctionBody = false;
  6202.         if (g.CurrentFunc->mDefaultVarType == VAR_ASSUME_NONE)
  6203.             g.CurrentFunc->mDefaultVarType = VAR_ASSUME_LOCAL;  // Set default since no override was discovered at the top of the body.
  6204.     }
  6205.  
  6206.     // No checking for unbalanced blocks is done here.  That is done by PreparseBlocks() because
  6207.     // it displays more informative error messages:
  6208.     if (aActionType == ACT_BLOCK_BEGIN)
  6209.     {
  6210.         ++mOpenBlockCount;
  6211.         // It's only necessary to check mLastFunc, not the one(s) that come before it, to see if its
  6212.         // mJumpToLine is NULL.  This is because our caller has made it impossible for a function
  6213.         // to ever have been defined in the first place if it lacked its opening brace.  Search on
  6214.         // "consecutive function" for more comments.  In addition, the following does not check
  6215.         // that mOpenBlockCount is exactly 1, because: 1) Want to be able to support function
  6216.         // definitions inside of other function definitions (to help script maintainability); 2) If
  6217.         // mOpenBlockCount is 0 or negative, that will be caught as a syntax error by PreparseBlocks(),
  6218.         // which yields a more informative error message that we could here.
  6219.         if (mLastFunc && !mLastFunc->mJumpToLine) // If this stmt is true, caller has ensured that g.CurrentFunc isn't NULL.
  6220.         {
  6221.             // The above check relies upon the fact that mLastFunc->mIsBuiltIn must be false at this stage,
  6222.             // which is the case because any non-overridden built-in function won't get added until after all
  6223.             // lines have been added, namely PreparseBlocks().
  6224.             line.mAttribute = ATTR_TRUE;  // Flag this ACT_BLOCK_BEGIN as the opening brace of the function's body.
  6225.             // For efficiency, and to prevent ExecUntil from starting a new recursion layer for the function's
  6226.             // body, the function's execution should begin at the first line after its open-brace (even if that
  6227.             // first line is another open-brace or the function's close-brace (i.e. an empty function):
  6228.             mNextLineIsFunctionBody = true;
  6229.         }
  6230.     }
  6231.     else if (aActionType == ACT_BLOCK_END)
  6232.     {
  6233.         --mOpenBlockCount;
  6234.         if (g.CurrentFunc && !mOpenBlockCount) // Any negative mOpenBlockCount is caught by a different stage.
  6235.         {
  6236.             line.mAttribute = ATTR_TRUE;  // Flag this ACT_BLOCK_END as the ending brace of a function's body.
  6237.             g.CurrentFunc = NULL;
  6238.             mFuncExceptionVar = NULL;  // Notify FindVar() that there is no exception list to search.
  6239.         }
  6240.     }
  6241.  
  6242.     // Above must be done prior to the below, since it sometimes sets mAttribute for use below.
  6243.  
  6244.     ///////////////////////////////////////////////////////////////
  6245.     // Update any labels that should refer to the newly added line.
  6246.     ///////////////////////////////////////////////////////////////
  6247.     // If the label most recently added doesn't yet have an anchor in the script, provide it.
  6248.     // UPDATE: In addition, keep searching backward through the labels until a non-NULL
  6249.     // mJumpToLine is found.  All the ones with a NULL should point to this new line to
  6250.     // support cases where one label immediately follows another in the script.
  6251.     // Example:
  6252.     // #a::  <-- don't leave this label with a NULL jumppoint.
  6253.     // LaunchA:
  6254.     // ...
  6255.     // return
  6256.     if (do_update_labels)
  6257.     {
  6258.         for (Label *label = mLastLabel; label != NULL && label->mJumpToLine == NULL; label = label->mPrevLabel)
  6259.         {
  6260.             if (line.mActionType == ACT_BLOCK_BEGIN && line.mAttribute) // Non-zero mAttribute signfies the open-brace of a function body.
  6261.                 return ScriptError("A label must not point to a function.");
  6262.             if (line.mActionType == ACT_ELSE)
  6263.                 return ScriptError("A label must not point to an ELSE.");
  6264.             // Don't allow this because it may cause problems in a case such as this because
  6265.             // label1 points to the end-block which is at the same level (and thus normally
  6266.             // an allowable jumppoint) as the goto.  But we don't want to allow jumping into
  6267.             // a block that belongs to a control structure.  In this case, it would probably
  6268.             // result in a runtime error when the execution unexpectedly encounters the ELSE
  6269.             // after performing the goto:
  6270.             // goto, label1
  6271.             // if x
  6272.             // {
  6273.             //    ...
  6274.             //    label1:
  6275.             // }
  6276.             // else
  6277.             //    ...
  6278.             //
  6279.             // An alternate way to deal with the above would be to make each block-end be owned
  6280.             // by its block-begin rather than the block that encloses them both.
  6281.             if (line.mActionType == ACT_BLOCK_END)
  6282.                 return ScriptError("A label must not point to the end of a block. For loops, use Continue vs. Goto.");
  6283.             label->mJumpToLine = the_new_line;
  6284.         }
  6285.     }
  6286.  
  6287.     ++mLineCount;  // Right before returning "success", increment our count.
  6288.     return OK;
  6289. }
  6290.  
  6291.  
  6292.  
  6293. ResultType Script::ParseDerefs(char *aArgText, char *aArgMap, DerefType *aDeref, int &aDerefCount)
  6294. // Caller provides modifiable aDerefCount, which might be non-zero to indicate that there are already
  6295. // some items in the aDeref array.
  6296. // Returns FAIL or OK.
  6297. {
  6298.     size_t deref_string_length; // So that overflow can be detected, this is not of type DerefLengthType.
  6299.  
  6300.     // For each dereference found in aArgText:
  6301.     for (int j = 0;; ++j)  // Increment to skip over the symbol just found by the inner for().
  6302.     {
  6303.         // Find next non-literal g_DerefChar:
  6304.         for (; aArgText[j] && (aArgText[j] != g_DerefChar || (aArgMap && aArgMap[j])); ++j);
  6305.         if (!aArgText[j])
  6306.             break;
  6307.         // else: Match was found; this is the deref's open-symbol.
  6308.         if (aDerefCount >= MAX_DEREFS_PER_ARG)
  6309.             return ScriptError(TOO_MANY_REFS, aArgText); // Short msg since so rare.
  6310.         DerefType &this_deref = aDeref[aDerefCount];  // For performance.
  6311.         this_deref.marker = aArgText + j;  // Store the deref's starting location.
  6312.         // Find next g_DerefChar, even if it's a literal.
  6313.         for (++j; aArgText[j] && aArgText[j] != g_DerefChar; ++j);
  6314.         if (!aArgText[j])
  6315.             return ScriptError("This parameter contains a variable name missing its ending percent sign.", aArgText);
  6316.         // Otherwise: Match was found; this should be the deref's close-symbol.
  6317.         if (aArgMap && aArgMap[j])  // But it's mapped as literal g_DerefChar.
  6318.             return ScriptError("Invalid `%.", aArgText); // Short msg. since so rare.
  6319.         deref_string_length = aArgText + j - this_deref.marker + 1;
  6320.         if (deref_string_length == 2) // The percent signs were empty, e.g. %%
  6321.             return ScriptError("Empty variable reference (%%).", aArgText); // Short msg. since so rare.
  6322.         if (deref_string_length - 2 > MAX_VAR_NAME_LENGTH) // -2 for the opening & closing g_DerefChars
  6323.             return ScriptError("Variable name too long.", aArgText); // Short msg. since so rare.
  6324.         this_deref.is_function = false;
  6325.         this_deref.length = (DerefLengthType)deref_string_length;
  6326.         if (   !(this_deref.var = FindOrAddVar(this_deref.marker + 1, this_deref.length - 2))   )
  6327.             return FAIL;  // The called function already displayed the error.
  6328.         ++aDerefCount;
  6329.     } // for each dereference.
  6330.  
  6331.     return OK;
  6332. }
  6333.  
  6334.  
  6335.  
  6336. ResultType Script::DefineFunc(char *aBuf, Var *aFuncExceptionVar[])
  6337. // Returns OK or FAIL.
  6338. // Caller has already called ValidateName() on the function, and it is known that this valid name
  6339. // is followed immediately by an open-paren.  aFuncExceptionVar is the address of an array on
  6340. // the caller's stack that will hold the list of exception variables (those that must be explicitly
  6341. // declared as either local or global) within the body of the function.
  6342. {
  6343.     char *param_end, *param_start = strchr(aBuf, '('); // Caller has ensured that this will return non-NULL.
  6344.  
  6345.     Func *found_func = FindFunc(aBuf, param_start - aBuf);
  6346.     if (found_func)
  6347.     {
  6348.         if (!found_func->mIsBuiltIn)
  6349.             return ScriptError("Duplicate function definition.", aBuf); // Seems more descriptive than "Function already defined."
  6350.         else // It's a built-in function that the user wants to override with a custom definition.
  6351.         {
  6352.             found_func->mIsBuiltIn = false;  // Override built-in with custom.
  6353.             found_func->mParamCount = 0; // Revert to the default appropriate for non-built-in functions.
  6354.             found_func->mMinParams = 0;  //
  6355.             found_func->mJumpToLine = NULL; // Fixed for v1.0.35.12: Must reset for detection elsewhere.
  6356.             g.CurrentFunc = found_func;
  6357.         }
  6358.     }
  6359.     else
  6360.         // The value of g.CurrentFunc must be set here rather than by our caller since AddVar(), which we call,
  6361.         // relies upon it having been done.
  6362.         if (   !(g.CurrentFunc = AddFunc(aBuf, param_start - aBuf, false))   )
  6363.             return FAIL; // It already displayed the error.
  6364.  
  6365.     Func &func = *g.CurrentFunc; // For performance and convenience.
  6366.     int insert_pos;
  6367.     size_t param_length, value_length;
  6368.     FuncParam param[MAX_FUNCTION_PARAMS];
  6369.     int param_count = 0;
  6370.     char buf[LINE_SIZE], *target;
  6371.     bool param_must_have_default = false;
  6372.  
  6373.     for (param_start = omit_leading_whitespace(param_start + 1);;)
  6374.     {
  6375.         if (*param_start == ')') // No more params.
  6376.             break;
  6377.  
  6378.         // Must start the search at param_start, not param_start+1, so that something like fn(, x) will be properly handled:
  6379.         if (   !*param_start || !(param_end = StrChrAny(param_start, ", \t=)"))   ) // Look for first comma, space, tab, =, or close-paren.
  6380.             return ScriptError(ERR_MISSING_CLOSE_PAREN, aBuf);
  6381.  
  6382.         if (param_count >= MAX_FUNCTION_PARAMS)
  6383.             return ScriptError("Too many params.", param_start); // Short msg since so rare.
  6384.         FuncParam &this_param = param[param_count]; // For performance and convenience.
  6385.  
  6386.         // To enhance syntax error catching, consider ByRef to be a keyword; i.e. that can never be the name
  6387.         // of a formal parameter:
  6388.         if (this_param.is_byref = !strlicmp(param_start, "ByRef", (UINT)(param_end - param_start))) // ByRef.
  6389.         {
  6390.             // Omit the ByRef keyword from further consideration:
  6391.             param_start = omit_leading_whitespace(param_end);
  6392.             if (   !*param_start || !(param_end = StrChrAny(param_start, ", \t=)"))   ) // Look for first comma, space, tab, =, or close-paren.
  6393.                 return ScriptError(ERR_MISSING_CLOSE_PAREN, aBuf);
  6394.         }
  6395.  
  6396.         if (   !(param_length = param_end - param_start)   )
  6397.             return ScriptError(ERR_BLANK_PARAM, aBuf); // Reporting aBuf vs. param_start seems more informative since Vicinity isn't shown.
  6398.  
  6399.         // This will search for local variables, never globals, by virtue of the fact that this
  6400.         // new function's mDefaultVarType is always VAR_ASSUME_NONE at this early stage of its creation:
  6401.         if (this_param.var = FindVar(param_start, param_length, &insert_pos))  // Assign.
  6402.             return ScriptError("Duplicate parameter.", param_start);
  6403.         if (   !(this_param.var = AddVar(param_start, param_length, insert_pos, 2))   ) // Pass 2 as last parameter to mean "it's a local but more specifically a function's parameter".
  6404.             return FAIL; // It already displayed the error, including attempts to have reserved names as parameter names.
  6405.  
  6406.         // v1.0.35: Check if a default value is specified for this parameter and set up for the next iteration.
  6407.         // The following section is similar to that used to support initializers for static variables.
  6408.         // So maybe maintain them together.
  6409.         this_param.default_type = PARAM_DEFAULT_NONE;  // Set default.
  6410.         param_start = omit_leading_whitespace(param_end);
  6411.         if (*param_start == '=') // This is the default value of the param just added.
  6412.         {
  6413.             param_start = omit_leading_whitespace(param_start + 1); // Start of the default value.
  6414.             if (*param_start == '"') // Quoted literal string, or the empty string.
  6415.             {
  6416.                 // v1.0.46.13: Adde support for quoted/literal strings beyond simply "".
  6417.                 // The following section is nearly identical to one in ExpandExpression().
  6418.                 // Find the end of this string literal, noting that a pair of double quotes is
  6419.                 // a literal double quote inside the string.
  6420.                 for (target = buf, param_end = param_start + 1;;) // Omit the starting-quote from consideration, and from the resulting/built string.
  6421.                 {
  6422.                     if (!*param_end) // No matching end-quote. Probably impossible due to load-time validation.
  6423.                         return ScriptError(ERR_MISSING_CLOSE_QUOTE, param_start); // Reporting param_start vs. aBuf seems more informative in the case of quoted/literal strings.
  6424.                     if (*param_end == '"') // And if it's not followed immediately by another, this is the end of it.
  6425.                     {
  6426.                         ++param_end;
  6427.                         if (*param_end != '"') // String terminator or some non-quote character.
  6428.                             break;  // The previous char is the ending quote.
  6429.                         //else a pair of quotes, which resolves to a single literal quote. So fall through
  6430.                         // to the below, which will copy of quote character to the buffer. Then this pair
  6431.                         // is skipped over and the loop continues until the real end-quote is found.
  6432.                     }
  6433.                     //else some character other than '\0' or '"'.
  6434.                     *target++ = *param_end++;
  6435.                 }
  6436.                 *target = '\0'; // Terminate it in the buffer.
  6437.                 // The above has also set param_end for use near the bottom of the loop.
  6438.                 ConvertEscapeSequences(buf, g_EscapeChar, false); // Raw escape sequences like `n haven't been converted yet, so do it now.
  6439.                 this_param.default_type = PARAM_DEFAULT_STR;
  6440.                 this_param.default_str = *buf ? SimpleHeap::Malloc(buf, target-buf) : "";
  6441.             }
  6442.             else // A default value other than a quoted/literal string.
  6443.             {
  6444.                 if (!(param_end = StrChrAny(param_start, ", \t=)"))) // Somewhat debatable but stricter seems better.
  6445.                     return ScriptError(ERR_MISSING_COMMA, aBuf); // Reporting aBuf vs. param_start seems more informative since Vicinity isn't shown.
  6446.                 value_length = param_end - param_start;
  6447.                 if (value_length > MAX_FORMATTED_NUMBER_LENGTH) // Too rare to justify elaborate handling or error reporting.
  6448.                     value_length = MAX_FORMATTED_NUMBER_LENGTH;
  6449.                 strlcpy(buf, param_start, value_length + 1);  // Make a temp copy to simplify the below (especially IsPureNumeric).
  6450.                 if (!stricmp(buf, "false"))
  6451.                 {
  6452.                     this_param.default_type = PARAM_DEFAULT_INT;
  6453.                     this_param.default_int64 = 0;
  6454.                 }
  6455.                 else if (!stricmp(buf, "true"))
  6456.                 {
  6457.                     this_param.default_type = PARAM_DEFAULT_INT;
  6458.                     this_param.default_int64 = 1;
  6459.                 }
  6460.                 else // The only things supported other than the above are integers and floats.
  6461.                 {
  6462.                     // Vars could be supported here via FindVar(), but only globals ABOVE this point in
  6463.                     // the script would be supported (since other globals don't exist yet). So it seems
  6464.                     // best to wait until full/comprehesive support for expressions is studied/designed
  6465.                     // for both static initializers and parameter-default-values.
  6466.                     switch(IsPureNumeric(buf, true, false, true))
  6467.                     {
  6468.                     case PURE_INTEGER:
  6469.                         this_param.default_type = PARAM_DEFAULT_INT;
  6470.                         this_param.default_int64 = ATOI64(buf);
  6471.                         break;
  6472.                     case PURE_FLOAT:
  6473.                         this_param.default_type = PARAM_DEFAULT_FLOAT;
  6474.                         this_param.default_double = ATOF(buf);
  6475.                         break;
  6476.                     default: // Not numeric (and also not a quoted string because that was handled earlier).
  6477.                         return ScriptError("Unsupported parameter default.", aBuf);
  6478.                     }
  6479.                 }
  6480.             }
  6481.             param_must_have_default = true;  // For now, all other params after this one must also have default values.
  6482.             // Set up for the next iteration:
  6483.             param_start = omit_leading_whitespace(param_end);
  6484.         }
  6485.         else // This parameter does not have a default value specified.
  6486.         {
  6487.             if (param_must_have_default)
  6488.                 return ScriptError("Parameter default required.", this_param.var->mName);
  6489.             ++func.mMinParams;
  6490.         }
  6491.         ++param_count;
  6492.  
  6493.         if (*param_start != ',' && *param_start != ')') // Something like "fn(a, b c)" (missing comma) would cause this.
  6494.             return ScriptError(ERR_MISSING_COMMA, aBuf); // Reporting aBuf vs. param_start seems more informative since Vicinity isn't shown.
  6495.         if (*param_start == ',')
  6496.         {
  6497.             param_start = omit_leading_whitespace(param_start + 1);
  6498.             if (*param_start == ')') // If *param_start is ',' it will be caught as an error by the next iteration.
  6499.                 return ScriptError(ERR_BLANK_PARAM, aBuf); // Reporting aBuf vs. param_start seems more informative since Vicinity isn't shown.
  6500.         }
  6501.         //else it's ')', in which case the next iteration will handle it.
  6502.         // Above has ensured that param_start now points to the next parameter, or ')' if none.
  6503.     } // for() each formal parameter.
  6504.  
  6505.     if (param_count)
  6506.     {
  6507.         // Allocate memory only for the actual number of parameters actually present.
  6508.         size_t size = param_count * sizeof(param[0]);
  6509.         if (   !(func.mParam = (FuncParam *)SimpleHeap::Malloc(size))   )
  6510.             return ScriptError(ERR_OUTOFMEM);
  6511.         func.mParamCount = param_count;
  6512.         memcpy(func.mParam, param, size);
  6513.     }
  6514.     //else leave func.mParam/mParamCount set to their NULL/0 defaults.
  6515.  
  6516.     // Indicate success:
  6517.     mFuncExceptionVar = aFuncExceptionVar; // Give mFuncExceptionVar its address, to be used for any var declarations inside this function's body.
  6518.     mFuncExceptionVarCount = 0;  // Reset in preparation of declarations that appear beneath this function's definition.
  6519.     return OK;
  6520. }
  6521.  
  6522.  
  6523.  
  6524. #ifndef AUTOHOTKEYSC
  6525. struct FuncLibrary
  6526. {
  6527.     char *path;
  6528.     DWORD length;
  6529. };
  6530.  
  6531. Func *Script::FindFuncInLibrary(char *aFuncName, size_t aFuncNameLength, bool &aErrorWasShown)
  6532. // Caller must ensure that aFuncName doesn't already exist as a defined function.
  6533. // If aFuncNameLength is 0, the entire length of aFuncName is used.
  6534. {
  6535.     aErrorWasShown = false; // Set default for this output parameter.
  6536.  
  6537.     int i;
  6538.     char *char_after_last_backslash, *terminate_here;
  6539.     DWORD attr;
  6540.  
  6541.     #define FUNC_LIB_EXT ".ahk"
  6542.     #define FUNC_LIB_EXT_LENGTH 4
  6543.     #define FUNC_USER_LIB "\\AutoHotkey\\Lib\\" // Needs leading and trailing backslash.
  6544.     #define FUNC_USER_LIB_LENGTH 16
  6545.     #define FUNC_STD_LIB "Lib\\" // Needs trailing but not leading backslash.
  6546.     #define FUNC_STD_LIB_LENGTH 4
  6547.  
  6548.     #define FUNC_LIB_COUNT 2
  6549.     static FuncLibrary sLib[FUNC_LIB_COUNT] = {0};
  6550.  
  6551.     if (!sLib[0].path) // Allocate & discover paths only upon first use because many scripts won't use anything from the library. This saves a bit of memory and performance.
  6552.     {
  6553.         for (i = 0; i < FUNC_LIB_COUNT; ++i)
  6554.             if (   !(sLib[i].path = SimpleHeap::Malloc(MAX_PATH))   ) // Need MAX_PATH for to allow room for appending each candidate file/function name.
  6555.                 return NULL; // Due to rarity, simply pass the failure back to caller.
  6556.  
  6557.         // DETERMINE PATH TO "USER" LIBRARY:
  6558.         FuncLibrary *this_lib = sLib; // For convenience and maintainability.
  6559.         this_lib->length = BIV_MyDocuments(this_lib->path, "");
  6560.         if (this_lib->length < MAX_PATH-FUNC_USER_LIB_LENGTH)
  6561.         {
  6562.             strcpy(this_lib->path + this_lib->length, FUNC_USER_LIB);
  6563.             this_lib->length += FUNC_USER_LIB_LENGTH;
  6564.         }
  6565.         else // Insufficient room to build the path name.
  6566.         {
  6567.             *this_lib->path = '\0'; // Mark this library as disabled.
  6568.             this_lib->length = 0;   //
  6569.         }
  6570.  
  6571.         // DETERMINE PATH TO "STANDARD" LIBRARY:
  6572.         this_lib = sLib + 1; // For convenience and maintainability.
  6573.         GetModuleFileName(NULL, this_lib->path, MAX_PATH); // The full path to the currently-running AutoHotkey.exe.
  6574.         char_after_last_backslash = 1 + strrchr(this_lib->path, '\\'); // Should always be found, so failure isn't checked.
  6575.         this_lib->length = (DWORD)(char_after_last_backslash - this_lib->path); // The length up to and including the last backslash.
  6576.         if (this_lib->length < MAX_PATH-FUNC_STD_LIB_LENGTH)
  6577.         {
  6578.             strcpy(this_lib->path + this_lib->length, FUNC_STD_LIB);
  6579.             this_lib->length += FUNC_STD_LIB_LENGTH;
  6580.         }
  6581.         else // Insufficient room to build the path name.
  6582.         {
  6583.             *this_lib->path = '\0'; // Mark this library as disabled.
  6584.             this_lib->length = 0;   //
  6585.         }
  6586.  
  6587.         for (i = 0; i < FUNC_LIB_COUNT; ++i)
  6588.         {
  6589.             attr = GetFileAttributes(sLib[i].path); // Seems to accept directories that have a trailing backslash, which is good because it simplifies the code.
  6590.             if (attr == 0xFFFFFFFF || !(attr & FILE_ATTRIBUTE_DIRECTORY)) // Directory doesn't exist or it's a file vs. directory. Relies on short-circuit boolean order.
  6591.             {
  6592.                 *sLib[i].path = '\0'; // Mark this library as disabled.
  6593.                 sLib[i].length = 0;   //
  6594.             }
  6595.         }
  6596.     }
  6597.     // Above must ensure that all sLib[].path elements are non-NULL (but they can be "" to indicate "no library").
  6598.  
  6599.     if (!aFuncNameLength) // Caller didn't specify, so use the entire string.
  6600.         aFuncNameLength = strlen(aFuncName);
  6601.  
  6602.     char *dest, *first_underscore, class_name_buf[MAX_VAR_NAME_LENGTH + 1];
  6603.     char *naked_filename = aFuncName;               // Set up for the first iteration.
  6604.     size_t naked_filename_length = aFuncNameLength; //
  6605.  
  6606.     for (int second_iteration = 0; second_iteration < 2; ++second_iteration)
  6607.     {
  6608.         for (i = 0; i < FUNC_LIB_COUNT; ++i)
  6609.         {
  6610.             if (!*sLib[i].path) // Library is marked disabled, so skip it.
  6611.                 continue;
  6612.  
  6613.             if (sLib[i].length + naked_filename_length >= MAX_PATH-FUNC_LIB_EXT_LENGTH)
  6614.                 continue; // Path too long to match in this library, but try others.
  6615.             dest = (char *)memcpy(sLib[i].path + sLib[i].length, naked_filename, naked_filename_length); // Append the filename to the library path.
  6616.             strcpy(dest + naked_filename_length, FUNC_LIB_EXT); // Append the file extension.
  6617.  
  6618.             attr = GetFileAttributes(sLib[i].path); // Testing confirms that GetFileAttributes() doesn't support wildcards; which is good because we want filenames containing question marks to be "not found" rather than being treated as a match-pattern.
  6619.             if (attr == 0xFFFFFFFF || (attr & FILE_ATTRIBUTE_DIRECTORY)) // File doesn't exist or it's a directory. Relies on short-circuit boolean order.
  6620.                 continue;
  6621.  
  6622.             // Since above didn't "continue", a file exists whose name matches that of the requested function.
  6623.             // Before loading/including that file, set the working directory to its folder so that if it uses
  6624.             // #Include, it will be able to use more convenient/intuitive relative paths.  This is similar to
  6625.             // the "#Include DirName" feature.
  6626.             // Call SetWorkingDir() vs. SetCurrentDirectory() so that it succeeds even for a root drive like
  6627.             // C: that lacks a backslash (see SetWorkingDir() for details).
  6628.             terminate_here = sLib[i].path + sLib[i].length - 1; // The trailing backslash in the full-path-name to this library.
  6629.             *terminate_here = '\0'; // Temporarily terminate it for use with SetWorkingDir().
  6630.             SetWorkingDir(sLib[i].path); // See similar section in the #Include directive.
  6631.             *terminate_here = '\\'; // Undo the termination.
  6632.  
  6633.             if (!LoadIncludedFile(sLib[i].path, false, false)) // Fix for v1.0.47.05: Pass false for allow-dupe because otherwise, it's possible for a stdlib file to attempt to include itself (especially via the LibNamePrefix_ method) and thus give a misleading "duplicate function" vs. "func does not exist" error message.  Obsolete: For performance, pass true for allow-dupe so that it doesn't have to check for a duplicate file (seems too rare to worry about duplicates since by definition, the function doesn't yet exist so it's file shouldn't yet be included).
  6634.             {
  6635.                 aErrorWasShown = true; // Above has just displayed its error (e.g. syntax error in a line, failed to open the include file, etc).  So override the default set earlier.
  6636.                 return NULL;
  6637.             }
  6638.  
  6639.             if (mIncludeLibraryFunctionsThenExit)
  6640.             {
  6641.                 // For each included library-file, write out two #Include lines:
  6642.                 // 1) Use #Include in its "change working directory" mode so that any explicit #include directives
  6643.                 //    or FileInstalls inside the library file itself will work consistently and properly.
  6644.                 // 2) Use #IncludeAgain (to improve performance since no dupe-checking is needed) to include
  6645.                 //    the library file itself.
  6646.                 // We don't directly append library files onto the main script here because:
  6647.                 // 1) ahk2exe needs to be able to see and act upon FileInstall and #Include lines (i.e. library files
  6648.                 //    might contain #Include even though it's rare).
  6649.                 // 2) #IncludeAgain and #Include directives that bring in fragments rather than entire functions or
  6650.                 //    subroutines wouldn't work properly if we resolved such includes in AutoHotkey.exe because they
  6651.                 //    wouldn't be properly interleaved/asynchronous, but instead brought out of their library file
  6652.                 //    and deposited separately/synchronously into the temp-include file by some new logic at the
  6653.                 //    AutoHotkey.exe's code for the #Include directive.
  6654.                 // 3) ahk2exe prefers to omit comments from included files to minimize size of compiled scripts.
  6655.                 fprintf(mIncludeLibraryFunctionsThenExit, "#Include %-0.*s\n#IncludeAgain %s\n"
  6656.                     , sLib[i].length, sLib[i].path, sLib[i].path);
  6657.                 // Now continue on normally so that our caller can continue looking for syntax errors.
  6658.             }
  6659.  
  6660.             // Now that a matching filename has been found, it seems best to stop searching here even if that
  6661.             // file doesn't actually contain the requested function.  This helps library authors catch bugs/typos.
  6662.             return FindFunc(aFuncName, aFuncNameLength);
  6663.         } // for() each library directory.
  6664.  
  6665.         // Now that the first iteration is done, set up for the second one that searches by class/prefix.
  6666.         // Notes about ambiguity and naming collisions:
  6667.         // By the time it gets to the prefix/class search, it's almost given up.  Even if it wrongly finds a
  6668.         // match in a filename that isn't really a class, it seems inconsequential because at worst it will
  6669.         // still not find the function and will then say "call to nonexistent function".  In addition, the
  6670.         // ability to customize which libraries are searched is planned.  This would allow a publicly
  6671.         // distributed script to turn off all libraries except stdlib.
  6672.         if (   !(first_underscore = strchr(aFuncName, '_'))   ) // No second iteration needed.
  6673.             break; // All loops are done because second iteration is the last possible attempt.
  6674.         naked_filename_length = first_underscore - aFuncName;
  6675.         if (naked_filename_length >= sizeof(class_name_buf)) // Class name too long (probably impossible currently).
  6676.             break; // All loops are done because second iteration is the last possible attempt.
  6677.         naked_filename = class_name_buf; // Point it to a buffer for use below.
  6678.         memcpy(naked_filename, aFuncName, naked_filename_length);
  6679.         naked_filename[naked_filename_length] = '\0';
  6680.     } // 2-iteration for().
  6681.  
  6682.     // Since above didn't return, no match found in any library.
  6683.     return NULL;
  6684. }
  6685. #endif
  6686.  
  6687.  
  6688.  
  6689. Func *Script::FindFunc(char *aFuncName, size_t aFuncNameLength)
  6690. // Returns the Function whose name matches aFuncName (which caller has ensured isn't NULL).
  6691. // If it doesn't exist, NULL is returned.
  6692. {
  6693.     if (!aFuncNameLength) // Caller didn't specify, so use the entire string.
  6694.         aFuncNameLength = strlen(aFuncName);
  6695.  
  6696.     // For the below, no error is reported because callers don't want that.  Instead, simply return
  6697.     // NULL to indicate that names that are illegal or too long are not found.  If the caller later
  6698.     // tries to add the function, it will get an error then:
  6699.     if (aFuncNameLength > MAX_VAR_NAME_LENGTH)
  6700.         return NULL;
  6701.  
  6702.     // The following copy is made because it allows the name searching to use stricmp() instead of
  6703.     // strlicmp(), which close to doubles the performance.  The copy includes only the first aVarNameLength
  6704.     // characters from aVarName:
  6705.     char func_name[MAX_VAR_NAME_LENGTH + 1];
  6706.     strlcpy(func_name, aFuncName, aFuncNameLength + 1);  // +1 to convert length to size.
  6707.  
  6708.     Func *pfunc;
  6709.     for (pfunc = mFirstFunc; pfunc; pfunc = pfunc->mNextFunc)
  6710.         if (!stricmp(func_name, pfunc->mName)) // lstrcmpi() is not used: 1) avoids breaking exisitng scripts; 2) provides consistent behavior across multiple locales; 3) performance.
  6711.             return pfunc; // Match found.
  6712.  
  6713.     // Since above didn't return, there is no match.  See if it's a built-in function that hasn't yet
  6714.     // been added to the function list.
  6715.  
  6716.     // Set defaults to be possibly overridden below:
  6717.     int min_params = 1;
  6718.     int max_params = 1;
  6719.     BuiltInFunctionType bif;
  6720.     char *suffix = func_name + 3;
  6721.  
  6722.     if (!strnicmp(func_name, "LV_", 3)) // It's a ListView function.
  6723.     {
  6724.         suffix = func_name + 3;
  6725.         if (!stricmp(suffix, "GetNext"))
  6726.         {
  6727.             bif = BIF_LV_GetNextOrCount;
  6728.             min_params = 0;
  6729.             max_params = 2;
  6730.         }
  6731.         else if (!stricmp(suffix, "GetCount"))
  6732.         {
  6733.             bif = BIF_LV_GetNextOrCount;
  6734.             min_params = 0; // But leave max at its default of 1.
  6735.         }
  6736.         else if (!stricmp(suffix, "GetText"))
  6737.         {
  6738.             bif = BIF_LV_GetText;
  6739.             min_params = 2;
  6740.             max_params = 3;
  6741.         }
  6742.         else if (!stricmp(suffix, "Add"))
  6743.         {
  6744.             bif = BIF_LV_AddInsertModify;
  6745.             min_params = 0; // 0 params means append a blank row.
  6746.             max_params = 10000; // An arbitrarily high limit that will never realistically be reached.
  6747.         }
  6748.         else if (!stricmp(suffix, "Insert"))
  6749.         {
  6750.             bif = BIF_LV_AddInsertModify;
  6751.             // Leave min_params at 1.  Passing only 1 param to it means "insert a blank row".
  6752.             max_params = 10000; // An arbitrarily high limit that will never realistically be reached.
  6753.         }
  6754.         else if (!stricmp(suffix, "Modify"))
  6755.         {
  6756.             bif = BIF_LV_AddInsertModify; // Although it shares the same function with "Insert", it can still have its own min/max params.
  6757.             min_params = 2;
  6758.             max_params = 10000; // An arbitrarily high limit that will never realistically be reached.
  6759.         }
  6760.         else if (!stricmp(suffix, "Delete"))
  6761.         {
  6762.             bif = BIF_LV_Delete;
  6763.             min_params = 0; // Leave max at its default of 1.
  6764.         }
  6765.         else if (!stricmp(suffix, "InsertCol"))
  6766.         {
  6767.             bif = BIF_LV_InsertModifyDeleteCol;
  6768.             // Leave min_params at 1 because inserting a blank column ahead of the first column
  6769.             // does not seem useful enough to sacrifice the no-parameter mode, which might have
  6770.             // potential future uses.
  6771.             max_params = 3;
  6772.         }
  6773.         else if (!stricmp(suffix, "ModifyCol"))
  6774.         {
  6775.             bif = BIF_LV_InsertModifyDeleteCol;
  6776.             min_params = 0;
  6777.             max_params = 3;
  6778.         }
  6779.         else if (!stricmp(suffix, "DeleteCol"))
  6780.             bif = BIF_LV_InsertModifyDeleteCol; // Leave min/max set to 1.
  6781.         else if (!stricmp(suffix, "SetImageList"))
  6782.         {
  6783.             bif = BIF_LV_SetImageList;
  6784.             max_params = 2; // Leave min at 1.
  6785.         }
  6786.         else
  6787.             return NULL;
  6788.     }
  6789.     else if (!strnicmp(func_name, "TV_", 3)) // It's a TreeView function.
  6790.     {
  6791.         suffix = func_name + 3;
  6792.         if (!stricmp(suffix, "Add"))
  6793.         {
  6794.             bif = BIF_TV_AddModifyDelete;
  6795.             max_params = 3; // Leave min at its default of 1.
  6796.         }
  6797.         else if (!stricmp(suffix, "Modify"))
  6798.         {
  6799.             bif = BIF_TV_AddModifyDelete;
  6800.             max_params = 3; // One-parameter mode is "select specified item".
  6801.         }
  6802.         else if (!stricmp(suffix, "Delete"))
  6803.         {
  6804.             bif = BIF_TV_AddModifyDelete;
  6805.             min_params = 0;
  6806.         }
  6807.         else if (!stricmp(suffix, "GetParent") || !stricmp(suffix, "GetChild") || !stricmp(suffix, "GetPrev"))
  6808.             bif = BIF_TV_GetRelatedItem;
  6809.         else if (!stricmp(suffix, "GetCount") || !stricmp(suffix, "GetSelection"))
  6810.         {
  6811.             bif = BIF_TV_GetRelatedItem;
  6812.             min_params = 0;
  6813.             max_params = 0;
  6814.         }
  6815.         else if (!stricmp(suffix, "GetNext")) // Unlike "Prev", Next also supports 0 or 2 parameters.
  6816.         {
  6817.             bif = BIF_TV_GetRelatedItem;
  6818.             min_params = 0;
  6819.             max_params = 2;
  6820.         }
  6821.         else if (!stricmp(suffix, "Get") || !stricmp(suffix, "GetText"))
  6822.         {
  6823.             bif = BIF_TV_Get;
  6824.             min_params = 2;
  6825.             max_params = 2;
  6826.         }
  6827.         else
  6828.             return NULL;
  6829.     }
  6830.     else if (!strnicmp(func_name, "IL_", 3)) // It's an ImageList function.
  6831.     {
  6832.         suffix = func_name + 3;
  6833.         if (!stricmp(suffix, "Create"))
  6834.         {
  6835.             bif = BIF_IL_Create;
  6836.             min_params = 0;
  6837.             max_params = 3;
  6838.         }
  6839.         else if (!stricmp(suffix, "Destroy"))
  6840.         {
  6841.             bif = BIF_IL_Destroy; // Leave Min/Max set to 1.
  6842.         }
  6843.         else if (!stricmp(suffix, "Add"))
  6844.         {
  6845.             bif = BIF_IL_Add;
  6846.             min_params = 2;
  6847.             max_params = 4;
  6848.         }
  6849.         else
  6850.             return NULL;
  6851.     }
  6852.     else if (!stricmp(func_name, "SB_SetText"))
  6853.     {
  6854.         bif = BIF_StatusBar;
  6855.         max_params = 3; // Leave min_params at its default of 1.
  6856.     }
  6857.     else if (!stricmp(func_name, "SB_SetParts"))
  6858.     {
  6859.         bif = BIF_StatusBar;
  6860.         min_params = 0;
  6861.         max_params = 255; // 255 params alllows for up to 256 parts, which is SB's max.
  6862.     }
  6863.     else if (!stricmp(func_name, "SB_SetIcon"))
  6864.     {
  6865.         bif = BIF_StatusBar;
  6866.         max_params = 3; // Leave min_params at its default of 1.
  6867.     }
  6868.     else if (!stricmp(func_name, "StrLen"))
  6869.         bif = BIF_StrLen;
  6870.     else if (!stricmp(func_name, "SubStr"))
  6871.     {
  6872.         bif = BIF_SubStr;
  6873.         min_params = 2;
  6874.         max_params = 3;
  6875.     }
  6876.     else if (!stricmp(func_name, "InStr"))
  6877.     {
  6878.         bif = BIF_InStr;
  6879.         min_params = 2;
  6880.         max_params = 4;
  6881.     }
  6882.     else if (!stricmp(func_name, "RegExMatch"))
  6883.     {
  6884.         bif = BIF_RegEx;
  6885.         min_params = 2;
  6886.         max_params = 4;
  6887.     }
  6888.     else if (!stricmp(func_name, "RegExReplace"))
  6889.     {
  6890.         bif = BIF_RegEx;
  6891.         min_params = 2;
  6892.         max_params = 6;
  6893.     }
  6894.     else if (!stricmp(func_name, "GetKeyState"))
  6895.     {
  6896.         bif = BIF_GetKeyState;
  6897.         max_params = 2;
  6898.     }
  6899.     else if (!stricmp(func_name, "Asc"))
  6900.         bif = BIF_Asc;
  6901.     else if (!stricmp(func_name, "Chr"))
  6902.         bif = BIF_Chr;
  6903.     else if (!stricmp(func_name, "NumGet"))
  6904.     {
  6905.         bif = BIF_NumGet;
  6906.         max_params = 3;
  6907.     }
  6908.     else if (!stricmp(func_name, "NumPut"))
  6909.     {
  6910.         bif = BIF_NumPut;
  6911.         min_params = 2;
  6912.         max_params = 4;
  6913.     }
  6914.     else if (!stricmp(func_name, "IsLabel"))
  6915.         bif = BIF_IsLabel;
  6916.     else if (!stricmp(func_name, "DllCall"))
  6917.     {
  6918.         bif = BIF_DllCall;
  6919.         max_params = 10000; // An arbitrarily high limit that will never realistically be reached.
  6920.     }
  6921.     else if (!stricmp(func_name, "VarSetCapacity"))
  6922.     {
  6923.         bif = BIF_VarSetCapacity;
  6924.         max_params = 3;
  6925.     }
  6926.     else if (!stricmp(func_name, "FileExist"))
  6927.         bif = BIF_FileExist;
  6928.     else if (!stricmp(func_name, "WinExist") || !stricmp(func_name, "WinActive"))
  6929.     {
  6930.         bif = BIF_WinExistActive;
  6931.         min_params = 0;
  6932.         max_params = 4;
  6933.     }
  6934.     else if (!stricmp(func_name, "Round"))
  6935.     {
  6936.         bif = BIF_Round;
  6937.         max_params = 2;
  6938.     }
  6939.     else if (!stricmp(func_name, "Floor") || !stricmp(func_name, "Ceil"))
  6940.         bif = BIF_FloorCeil;
  6941.     else if (!stricmp(func_name, "Mod"))
  6942.     {
  6943.         bif = BIF_Mod;
  6944.         min_params = 2;
  6945.         max_params = 2;
  6946.     }
  6947.     else if (!stricmp(func_name, "Abs"))
  6948.         bif = BIF_Abs;
  6949.     else if (!stricmp(func_name, "Sin"))
  6950.         bif = BIF_Sin;
  6951.     else if (!stricmp(func_name, "Cos"))
  6952.         bif = BIF_Cos;
  6953.     else if (!stricmp(func_name, "Tan"))
  6954.         bif = BIF_Tan;
  6955.     else if (!stricmp(func_name, "ASin") || !stricmp(func_name, "ACos"))
  6956.         bif = BIF_ASinACos;
  6957.     else if (!stricmp(func_name, "ATan"))
  6958.         bif = BIF_ATan;
  6959.     else if (!stricmp(func_name, "Exp"))
  6960.         bif = BIF_Exp;
  6961.     else if (!stricmp(func_name, "Sqrt") || !stricmp(func_name, "Log") || !stricmp(func_name, "Ln"))
  6962.         bif = BIF_SqrtLogLn;
  6963.     else if (!stricmp(func_name, "OnMessage"))
  6964.     {
  6965.         bif = BIF_OnMessage;
  6966.         max_params = 3;  // Leave min at 1.
  6967.         // By design, scripts that use OnMessage are persistent by default.  Doing this here
  6968.         // also allows WinMain() to later detect whether this script should become #SingleInstance.
  6969.         // Note: Don't directly change g_AllowOnlyOneInstance here in case the remainder of the
  6970.         // script-loading process comes across any explicit uses of #SingleInstance, which would
  6971.         // override the default set here.
  6972.         g_persistent = true;
  6973.     }
  6974.     else if (!stricmp(func_name, "RegisterCallback"))
  6975.     {
  6976.         bif = BIF_RegisterCallback;
  6977.         max_params = 4; // Leave min_params at 1.
  6978.     }
  6979.     else
  6980.         return NULL; // Maint: There may be other lines above that also return NULL.
  6981.  
  6982.     // Since above didn't return, this is a built-in function that hasn't yet been added to the list.
  6983.     // Add it now:
  6984.     if (   !(pfunc = AddFunc(func_name, aFuncNameLength, true))   )
  6985.         return NULL;
  6986.  
  6987.     pfunc->mBIF = bif;
  6988.     pfunc->mMinParams = min_params;
  6989.     pfunc->mParamCount = max_params;
  6990.  
  6991.     return pfunc;
  6992. }
  6993.  
  6994.  
  6995.  
  6996. Func *Script::AddFunc(char *aFuncName, size_t aFuncNameLength, bool aIsBuiltIn)
  6997. // This function should probably not be called by anyone except FindOrAddFunc, which has already done
  6998. // the dupe-checking.
  6999. // Returns the address of the new function or NULL on failure.
  7000. // The caller must already have verified that this isn't a duplicate function.
  7001. {
  7002.     if (!aFuncNameLength) // Caller didn't specify, so use the entire string.
  7003.         aFuncNameLength = strlen(aFuncName);
  7004.  
  7005.     if (aFuncNameLength > MAX_VAR_NAME_LENGTH)
  7006.     {
  7007.         // Dynamic function-calls such as MyFuncArray%i%() aren't currently supported, so the first
  7008.         // item below is commented out:
  7009.         // Load-time callers should check for this.  But at runtime, it's possible for a dynamically
  7010.         // resolved function name to be too long.  Note that aFuncName should be the exact variable
  7011.         // name and does not need to be truncated to aFuncNameLength whenever this error occurs
  7012.         // (i.e. at runtime):
  7013.         //if (mIsReadyToExecute) // Runtime error.
  7014.         //    ScriptError("Function name too long." ERR_ABORT, aFuncName);
  7015.         //else
  7016.             ScriptError("Function name too long.", aFuncName);
  7017.         return NULL;
  7018.     }
  7019.  
  7020.     // Make a temporary copy that includes only the first aFuncNameLength characters from aFuncName:
  7021.     char func_name[MAX_VAR_NAME_LENGTH + 1];
  7022.     strlcpy(func_name, aFuncName, aFuncNameLength + 1);  // See explanation above.  +1 to convert length to size.
  7023.  
  7024.     // In the future, it might be best to add another check here to disallow function names that consist
  7025.     // entirely of numbers.  However, this hasn't been done yet because:
  7026.     // 1) Not sure if there will ever be a good enough reason.
  7027.     // 2) Even if it's done in the far future, it won't break many scripts (pure-numeric functions should be very rare).
  7028.     // 3) Those scripts that are broken are not broken in a bad way because the pre-parser will generate a
  7029.     //    load-time error, which is easy to fix (unlike runtime errors, which require that part of the script
  7030.     //    to actually execute).
  7031.     if (!Var::ValidateName(func_name, mIsReadyToExecute, DISPLAY_FUNC_ERROR))  // Variable and function names are both validated the same way.
  7032.         // Above already displayed error for us.  This can happen at loadtime or runtime (e.g. StringSplit).
  7033.         return NULL;
  7034.  
  7035.     // Allocate some dynamic memory to pass to the constructor:
  7036.     char *new_name = SimpleHeap::Malloc(func_name, aFuncNameLength);
  7037.     if (!new_name)
  7038.         // It already displayed the error for us.  These mem errors are so unusual that we're not going
  7039.         // to bother varying the error message to include ERR_ABORT if this occurs during runtime.
  7040.         return NULL;
  7041.  
  7042.     Func *the_new_func = new Func(new_name, aIsBuiltIn);
  7043.     if (!the_new_func)
  7044.     {
  7045.         ScriptError(ERR_OUTOFMEM);
  7046.         return NULL;
  7047.     }
  7048.  
  7049.     // v1.0.47: The following ISN'T done because it would slow down commonly used functions. This is because
  7050.     // commonly-called functions like InStr() tend to be added first (since they appear so often throughout
  7051.     // the script); thus subsequent lookups are fast if they are kept at the beginning of the list rather
  7052.     // than being displaced to the end by all other functions).
  7053.     // NOT DONE for the reason above:
  7054.     // Unlike most of the other link lists, attach new items at the beginning of the list because
  7055.     // that allows the standard/user library feature to perform much better for scripts that have hundreds
  7056.     // of functions.  This is because functions brought in dynamically from a library will then be at the
  7057.     // beginning of the list, which allows the function lookup that immediately follows library-loading to
  7058.     // find a match almost immediately.
  7059.     if (!mFirstFunc) // The list is empty, so this will be the first and last item.
  7060.         mFirstFunc = the_new_func;
  7061.     else
  7062.         mLastFunc->mNextFunc = the_new_func;
  7063.     // This must be done after the above:
  7064.     mLastFunc = the_new_func; // There's at least one spot in the code that relies on mLastFunc being the most recently added function.
  7065.  
  7066.     return the_new_func;
  7067. }
  7068.  
  7069.  
  7070.  
  7071. size_t Line::ArgLength(int aArgNum)
  7072. // "ArgLength" is the arg's fully resolved, dereferenced length during runtime.
  7073. // Callers must call this only at times when sArgDeref and sArgVar are defined/meaningful.
  7074. // Caller must ensure that aArgNum should be 1 or greater.
  7075. // ArgLength() was added in v1.0.44.14 to help its callers improve performance by avoiding
  7076. // costly calls to strlen() (which is especially beneficial for huge strings).
  7077. {
  7078. #ifdef _DEBUG
  7079.     if (aArgNum < 1)
  7080.     {
  7081.         LineError("DEBUG: BAD", WARN);
  7082.         aArgNum = 1;  // But let it continue.
  7083.     }
  7084. #endif
  7085.     if (aArgNum > mArgc) // Arg doesn't exist, so don't try accessing sArgVar (unlike sArgDeref, it wouldn't be valid to do so).
  7086.         return 0; // i.e. treat it as the empty string.
  7087.     // The length is not known and must be calculcated in the following situations:
  7088.     // - The arg consists of more than just a single isolated variable name (not possible if the arg is
  7089.     //   ARG_TYPE_INPUT_VAR).
  7090.     // - The arg is a built-in variable, in which case the length isn't known, so it must be derived from
  7091.     //   the string copied into sArgDeref[] by an earlier stage.
  7092.     // - The arg is a normal variable but it's VAR_ATTRIB_BINARY_CLIP. In such cases, our callers do not
  7093.     //   recognize/support binary-clipboard as binary and want the apparent length of the string returned
  7094.     //   (i.e. strlen(), which takes into account the position of the first binary zero wherever it may be).
  7095.     --aArgNum; // Convert to zero-based index (squeeze a little more performance out of it by avoiding a new variable).
  7096.     if (sArgVar[aArgNum])
  7097.     {
  7098.         Var &var = *sArgVar[aArgNum]; // For performance and convenience.
  7099.         if (var.Type() == VAR_NORMAL && (g_NoEnv || var.Length())) // v1.0.46.02: Recognize environment variables (when g_NoEnv==false) by falling through to strlen() for them.
  7100.             return var.LengthIgnoreBinaryClip(); // Do it the fast way (unless it's binary clipboard, in which case this call will internally call strlen()).
  7101.     }
  7102.     // Otherwise, length isn't known due to no variable, a built-in variable, or an environment variable.
  7103.     // So do it the slow way.
  7104.     return strlen(sArgDeref[aArgNum]);
  7105. }
  7106.  
  7107.  
  7108.  
  7109. Var *Line::ResolveVarOfArg(int aArgIndex, bool aCreateIfNecessary)
  7110. // Returns NULL on failure.  Caller has ensured that none of this arg's derefs are function-calls.
  7111. // Args that are input or output variables are normally resolved at load-time, so that
  7112. // they contain a pointer to their Var object.  This is done for performance.  However,
  7113. // in order to support dynamically resolved variables names like AutoIt2 (e.g. arrays),
  7114. // we have to do some extra work here at runtime.
  7115. // Callers specify false for aCreateIfNecessary whenever the contents of the variable
  7116. // they're trying to find is unimportant.  For example, dynamically built input variables,
  7117. // such as "StringLen, length, array%i%", do not need to be created if they weren't
  7118. // previously assigned to (i.e. they weren't previously used as an output variable).
  7119. // In the above example, the array element would never be created here.  But if the output
  7120. // variable were dynamic, our call would have told us to create it.
  7121. {
  7122.     // The requested ARG isn't even present, so it can't have a variable.  Currently, this should
  7123.     // never happen because the loading procedure ensures that input/output args are not marked
  7124.     // as variables if they are blank (and our caller should check for this and not call in that case):
  7125.     if (aArgIndex >= mArgc)
  7126.         return NULL;
  7127.     ArgStruct &this_arg = mArg[aArgIndex]; // For performance and convenience.
  7128.  
  7129.     // Since this function isn't inline (since it's called so frequently), there isn't that much more
  7130.     // overhead to doing this check, even though it shouldn't be needed since it's the caller's
  7131.     // responsibility:
  7132.     if (this_arg.type == ARG_TYPE_NORMAL) // Arg isn't an input or output variable.
  7133.         return NULL;
  7134.     if (!*this_arg.text) // The arg's variable is not one that needs to be dynamically resolved.
  7135.         return VAR(this_arg); // Return the var's address that was already determined at load-time.
  7136.     // The above might return NULL in the case where the arg is optional (i.e. the command allows
  7137.     // the var name to be omitted).  But in that case, the caller should either never have called this
  7138.     // function or should check for NULL upon return.  UPDATE: This actually never happens, see
  7139.     // comment above the "if (aArgIndex >= mArgc)" line.
  7140.  
  7141.     // Static to correspond to the static empty_var further below.  It needs the memory area
  7142.     // to support resolving dynamic environment variables.  In the following example,
  7143.     // the result will be blank unless the first line is present (without this fix here):
  7144.     //null = %SystemRoot%  ; bogus line as a required workaround in versions prior to v1.0.16
  7145.     //thing = SystemRoot
  7146.     //StringTrimLeft, output, %thing%, 0
  7147.     //msgbox %output%
  7148.  
  7149.     static char sVarName[MAX_VAR_NAME_LENGTH + 1];  // Will hold the dynamically built name.
  7150.  
  7151.     // At this point, we know the requested arg is a variable that must be dynamically resolved.
  7152.     // This section is similar to that in ExpandArg(), so they should be maintained together:
  7153.     char *pText = this_arg.text; // Start at the begining of this arg's text.
  7154.     int var_name_length = 0;
  7155.  
  7156.     if (this_arg.deref) // There's at least one deref.
  7157.     {
  7158.         // Caller has ensured that none of these derefs are function calls (i.e. deref->is_function is alway false).
  7159.         for (DerefType *deref = this_arg.deref  // Start off by looking for the first deref.
  7160.             ; deref->marker; ++deref)  // A deref with a NULL marker terminates the list.
  7161.         {
  7162.             // FOR EACH DEREF IN AN ARG (if we're here, there's at least one):
  7163.             // Copy the chars that occur prior to deref->marker into the buffer:
  7164.             for (; pText < deref->marker && var_name_length < MAX_VAR_NAME_LENGTH; sVarName[var_name_length++] = *pText++);
  7165.             if (var_name_length >= MAX_VAR_NAME_LENGTH && pText < deref->marker) // The variable name would be too long!
  7166.             {
  7167.                 // This type of error is just a warning because this function isn't set up to cause a true
  7168.                 // failure.  This is because the use of dynamically named variables is rare, and only for
  7169.                 // people who should know what they're doing.  In any case, when the caller of this
  7170.                 // function called it to resolve an output variable, it will see tha the result is
  7171.                 // NULL and terminate the current subroutine.
  7172.                 #define DYNAMIC_TOO_LONG "This dynamically built variable name is too long." \
  7173.                     "  If this variable was not intended to be dynamic, remove the % symbols from it."
  7174.                 LineError(DYNAMIC_TOO_LONG, FAIL, this_arg.text);
  7175.                 return NULL;
  7176.             }
  7177.             // Now copy the contents of the dereferenced var.  For all cases, aBuf has already
  7178.             // been verified to be large enough, assuming the value hasn't changed between the
  7179.             // time we were called and the time the caller calculated the space needed.
  7180.             if (deref->var->Get() > (VarSizeType)(MAX_VAR_NAME_LENGTH - var_name_length)) // The variable name would be too long!
  7181.             {
  7182.                 LineError(DYNAMIC_TOO_LONG, FAIL, this_arg.text);
  7183.                 return NULL;
  7184.             }
  7185.             var_name_length += deref->var->Get(sVarName + var_name_length);
  7186.             // Finally, jump over the dereference text. Note that in the case of an expression, there might not
  7187.             // be any percent signs within the text of the dereference, e.g. x + y, not %x% + %y%.
  7188.             pText += deref->length;
  7189.         }
  7190.     }
  7191.  
  7192.     // Copy any chars that occur after the final deref into the buffer:
  7193.     for (; *pText && var_name_length < MAX_VAR_NAME_LENGTH; sVarName[var_name_length++] = *pText++);
  7194.     if (var_name_length >= MAX_VAR_NAME_LENGTH && *pText) // The variable name would be too long!
  7195.     {
  7196.         LineError(DYNAMIC_TOO_LONG, FAIL, this_arg.text);
  7197.         return NULL;
  7198.     }
  7199.     
  7200.     if (!var_name_length)
  7201.     {
  7202.         LineError("This dynamic variable is blank. If this variable was not intended to be dynamic,"
  7203.             " remove the % symbols from it.", FAIL, this_arg.text);
  7204.         return NULL;
  7205.     }
  7206.  
  7207.     // Terminate the buffer, even if nothing was written into it:
  7208.     sVarName[var_name_length] = '\0';
  7209.  
  7210.     static Var empty_var(sVarName, (void *)VAR_NORMAL, false); // Must use sVarName here.  See comment above for why.
  7211.  
  7212.     Var *found_var;
  7213.     if (!aCreateIfNecessary)
  7214.     {
  7215.         // Now we've dynamically build the variable name.  It's possible that the name is illegal,
  7216.         // so check that (the name is automatically checked by FindOrAddVar(), so we only need to
  7217.         // check it if we're not calling that):
  7218.         if (!Var::ValidateName(sVarName, g_script.mIsReadyToExecute))
  7219.             return NULL; // Above already displayed error for us.
  7220.         // The use of ALWAYS_PREFER_LOCAL below improves flexibility of assume-global functions
  7221.         // by allowing this command to resolve to a local first if such a local exists:
  7222.         if (found_var = g_script.FindVar(sVarName, var_name_length, NULL, ALWAYS_PREFER_LOCAL)) // Assign.
  7223.             return found_var;
  7224.         // At this point, this is either a non-existent variable or a reserved/built-in variable
  7225.         // that was never statically referenced in the script (only dynamically), e.g. A_IPAddress%A_Index%
  7226.         if (Script::GetVarType(sVarName) == (void *)VAR_NORMAL)
  7227.             // If not found: for performance reasons, don't create it because caller just wants an empty variable.
  7228.             return &empty_var;
  7229.         //else it's the clipboard or some other built-in variable, so continue onward so that the
  7230.         // variable gets created in the variable list, which is necessary to allow it to be properly
  7231.         // dereferenced, e.g. in a script consisting of only the following:
  7232.         // Loop, 4
  7233.         //     StringTrimRight, IP, A_IPAddress%A_Index%, 0
  7234.     }
  7235.     // Otherwise, aCreateIfNecessary is true or we want to create this variable unconditionally for the
  7236.     // reason described above.  ALWAYS_PREFER_LOCAL is used so that any existing local variable will
  7237.     // take precedence over a global of the same name when assume-global is in effect.  If neither type
  7238.     // of variable exists, a global variable will be created if assume-global is in effect.
  7239.     if (   !(found_var = g_script.FindOrAddVar(sVarName, var_name_length, ALWAYS_PREFER_LOCAL))   )
  7240.         return NULL;  // Above will already have displayed the error.
  7241.     if (this_arg.type == ARG_TYPE_OUTPUT_VAR && VAR_IS_READONLY(*found_var))
  7242.     {
  7243.         LineError(ERR_VAR_IS_READONLY, FAIL, sVarName);
  7244.         return NULL;  // Don't return the var, preventing the caller from assigning to it.
  7245.     }
  7246.     else
  7247.         return found_var;
  7248. }
  7249.  
  7250.  
  7251.  
  7252. Var *Script::FindOrAddVar(char *aVarName, size_t aVarNameLength, int aAlwaysUse, bool *apIsException)
  7253. // Caller has ensured that aVarName isn't NULL.
  7254. // Returns the Var whose name matches aVarName.  If it doesn't exist, it is created.
  7255. {
  7256.     if (!*aVarName)
  7257.         return NULL;
  7258.     int insert_pos;
  7259.     bool is_local; // Used to detect which type of var should be added in case the result of the below is NULL.
  7260.     Var *var;
  7261.     if (var = FindVar(aVarName, aVarNameLength, &insert_pos, aAlwaysUse, apIsException, &is_local))
  7262.         return var;
  7263.     // Otherwise, no match found, so create a new var.  This will return NULL if there was a problem,
  7264.     // in which case AddVar() will already have displayed the error:
  7265.     return AddVar(aVarName, aVarNameLength, insert_pos, is_local);
  7266. }
  7267.  
  7268.  
  7269.  
  7270. Var *Script::FindVar(char *aVarName, size_t aVarNameLength, int *apInsertPos, int aAlwaysUse
  7271.     , bool *apIsException, bool *apIsLocal)
  7272. // Caller has ensured that aVarName isn't NULL.  It must also ignore the contents of apInsertPos when
  7273. // a match (non-NULL value) is returned.
  7274. // Returns the Var whose name matches aVarName.  If it doesn't exist, NULL is returned.
  7275. // If caller provided a non-NULL apInsertPos, it will be given a the array index that a newly
  7276. // inserted item should have to keep the list in sorted order (which also allows the ListVars command
  7277. // to display the variables in alphabetical order).
  7278. {
  7279.     if (!*aVarName)
  7280.         return NULL;
  7281.     if (!aVarNameLength) // Caller didn't specify, so use the entire string.
  7282.         aVarNameLength = strlen(aVarName);
  7283.  
  7284.     // For the below, no error is reported because callers don't want that.  Instead, simply return
  7285.     // NULL to indicate that names that are illegal or too long are not found.  When the caller later
  7286.     // tries to add the variable, it will get an error then:
  7287.     if (aVarNameLength > MAX_VAR_NAME_LENGTH)
  7288.         return NULL;
  7289.  
  7290.     // The following copy is made because it allows the various searches below to use stricmp() instead of
  7291.     // strlicmp(), which close to doubles their performance.  The copy includes only the first aVarNameLength
  7292.     // characters from aVarName:
  7293.     char var_name[MAX_VAR_NAME_LENGTH + 1];
  7294.     strlcpy(var_name, aVarName, aVarNameLength + 1);  // +1 to convert length to size.
  7295.  
  7296.     Var *found_var = NULL; // Set default.
  7297.     bool is_local;
  7298.     if (aAlwaysUse == ALWAYS_USE_GLOBAL)
  7299.         is_local = false;
  7300.     else if (aAlwaysUse == ALWAYS_USE_LOCAL)
  7301.         // v1.0.44.10: The following was changed from it's former value of "true" so that places further below
  7302.         // (including passing is_local is call to AddVar()) don't have to ensure that g.CurrentFunc!=NULL.
  7303.         // This fixes a crash that occured when a caller specified ALWAYS_USE_LOCAL even though the current
  7304.         // thread isn't actually inside a *called* function (perhaps meaning things like a timed subroutine
  7305.         // that lies inside a "container function").
  7306.         // Some callers like SYSGET_CMD_MONITORAREA might try to find/add a local array if they see that their
  7307.         // base variable is classified as local (such classification occurs at loadtime, but only for non-dynamic
  7308.         // variable references).  But the current thread entered a "container function" by means other than a
  7309.         // function-call (such as SetTimer), not only is g.CurrentFunc NULL, but there's no easy way to discover
  7310.         // which function owns the currently executing line (a means could be added to the class "Var" or "Line"
  7311.         // but doesn't seem worth it yet due to performance and memory reduction).
  7312.         is_local = (g.CurrentFunc != NULL);
  7313.     else if (aAlwaysUse == ALWAYS_PREFER_LOCAL)
  7314.     {
  7315.         if (g.CurrentFunc) // Caller relies on us to do this final check.
  7316.             is_local = true;
  7317.         else
  7318.         {
  7319.             is_local = false;
  7320.             aAlwaysUse = ALWAYS_USE_GLOBAL;  // Override aAlwaysUse for maintainability, in case there are more references to it below.
  7321.         }
  7322.     }
  7323.     else // aAlwaysUse == ALWAYS_USE_DEFAULT
  7324.     {
  7325.         is_local = g.CurrentFunc && g.CurrentFunc->mDefaultVarType != VAR_ASSUME_GLOBAL; // i.e. ASSUME_LOCAL or ASSUME_NONE
  7326.         if (mFuncExceptionVar) // Caller has ensured that this non-NULL if and only if g.CurrentFunc is non-NULL.
  7327.         {
  7328.             int i;
  7329.             for (i = 0; i < mFuncExceptionVarCount; ++i)
  7330.             {
  7331.                 if (!stricmp(var_name, mFuncExceptionVar[i]->mName)) // lstrcmpi() is not used: 1) avoids breaking exisitng scripts; 2) provides consistent behavior across multiple locales; 3) performance.
  7332.                 {
  7333.                     is_local = !is_local;  // Since it's an exception, it's always the opposite of what it would have been.
  7334.                     found_var = mFuncExceptionVar[i];
  7335.                     break;
  7336.                 }
  7337.             }
  7338.             // The following section is necessary because a function's parameters are not put into the
  7339.             // exception list during load-time.  Thus, for an VAR_ASSUME_GLOBAL function, these are basically
  7340.             // treated as exceptions too.
  7341.             // If this function is one that assumes variables are global, the function's parameters are
  7342.             // implicitly declared local because parameters are always local:
  7343.             // Since the following is inside this block, it is checked only at loadtime.  It doesn't need
  7344.             // to be checked at runtime because most things that resolve input variables or variables whose
  7345.             // contents will be read (as compared to something that tries to create a dynamic variable, such
  7346.             // as ResolveVarOfArg() for an output variable) at runtime use the ALWAYS_PREFER_LOCAL flag to
  7347.             // indicate that a local of the same name as a global should take precedence.  This adds more
  7348.             // flexibility/benefit than its costs in terms of confusion because otherwise there would be
  7349.             // no way to dynamically reference the local variables of an assume-global function.
  7350.             if (g.CurrentFunc->mDefaultVarType == VAR_ASSUME_GLOBAL && !is_local) // g.CurrentFunc is also known to be non-NULL in this case.
  7351.             {
  7352.                 for (i = 0; i < g.CurrentFunc->mParamCount; ++i)
  7353.                     if (!stricmp(var_name, g.CurrentFunc->mParam[i].var->mName)) // lstrcmpi() is not used: 1) avoids breaking exisitng scripts; 2) provides consistent behavior across multiple locales; 3) performance.
  7354.                     {
  7355.                         is_local = true;
  7356.                         found_var = g.CurrentFunc->mParam[i].var;
  7357.                         break;
  7358.                     }
  7359.             }
  7360.         } // if (there is an exception list)
  7361.     } // aAlwaysUse == ALWAYS_USE_DEFAULT
  7362.  
  7363.     // Above has ensured that g.CurrentFunc!=NULL whenever is_local==true.
  7364.  
  7365.     if (apIsLocal) // Its purpose is to inform caller of type it would have been in case we don't find a match.
  7366.         *apIsLocal = is_local; // And it stays this way even if globals will be searched because caller wants that.  In other words, a local var is created by default when there is not existing global or local.
  7367.     if (apInsertPos) // Set default.  Caller should ignore the value when match is found.
  7368.         *apInsertPos = -1;
  7369.     if (apIsException)
  7370.         *apIsException = (found_var != NULL);
  7371.  
  7372.     if (found_var) // Match found (as an exception or load-time "is parameter" exception).
  7373.         return found_var; // apInsertPos does not need to be set because caller doesn't need it when match is found.
  7374.  
  7375.     // Init for binary search loop:
  7376.     int left, right, mid, result;  // left/right must be ints to allow them to go negative and detect underflow.
  7377.     Var **var;  // An array of pointers-to-var.
  7378.     if (is_local)
  7379.     {
  7380.         var = g.CurrentFunc->mVar;
  7381.         right = g.CurrentFunc->mVarCount - 1;
  7382.     }
  7383.     else
  7384.     {
  7385.         var = mVar;
  7386.         right = mVarCount - 1;
  7387.     }
  7388.  
  7389.     // Binary search:
  7390.     for (left = 0; left <= right;) // "right" was already initialized above.
  7391.     {
  7392.         mid = (left + right) / 2;
  7393.         result = stricmp(var_name, var[mid]->mName); // lstrcmpi() is not used: 1) avoids breaking exisitng scripts; 2) provides consistent behavior across multiple locales; 3) performance.
  7394.         if (result > 0)
  7395.             left = mid + 1;
  7396.         else if (result < 0)
  7397.             right = mid - 1;
  7398.         else // Match found.
  7399.             return var[mid];
  7400.     }
  7401.  
  7402.     // Since above didn't return, no match was found in the main list, so search the lazy list if there
  7403.     // is one.  If there's no lazy list, the value of "left" established above will be used as the
  7404.     // insertion point further below:
  7405.     if (is_local)
  7406.     {
  7407.         var = g.CurrentFunc->mLazyVar;
  7408.         right = g.CurrentFunc->mLazyVarCount - 1;
  7409.     }
  7410.     else
  7411.     {
  7412.         var = mLazyVar;
  7413.         right = mLazyVarCount - 1;
  7414.     }
  7415.  
  7416.     if (var) // There is a lazy list to search (and even if the list is empty, left must be reset to 0 below).
  7417.     {
  7418.         // Binary search:
  7419.         for (left = 0; left <= right;)  // "right" was already initialized above.
  7420.         {
  7421.             mid = (left + right) / 2;
  7422.             result = stricmp(var_name, var[mid]->mName); // lstrcmpi() is not used: 1) avoids breaking exisitng scripts; 2) provides consistent behavior across multiple locales; 3) performance.
  7423.             if (result > 0)
  7424.                 left = mid + 1;
  7425.             else if (result < 0)
  7426.                 right = mid - 1;
  7427.             else // Match found.
  7428.                 return var[mid];
  7429.         }
  7430.     }
  7431.  
  7432.     // Since above didn't return, no match was found and "left" always contains the position where aVarName
  7433.     // should be inserted to keep the list sorted.  The item is always inserted into the lazy list unless
  7434.     // there is no lazy list.
  7435.     // Set the output parameter, if present:
  7436.     if (apInsertPos) // Caller wants this value even if we'll be resorting to searching the global list below.
  7437.         *apInsertPos = left; // This is the index a newly inserted item should have to keep alphabetical order.
  7438.  
  7439.     // Since no match was found, if this is a local fall back to searching the list of globals at runtime
  7440.     // if the caller didn't insist on a particular type:
  7441.     if (is_local)
  7442.     {
  7443.         if (aAlwaysUse == ALWAYS_PREFER_LOCAL)
  7444.         {
  7445.             // In this case, callers want to fall back to globals when a local wasn't found.  However,
  7446.             // they want the insertion (if our caller will be doing one) to insert according to the
  7447.             // current assume-mode.  Therefore, if the mode is VAR_ASSUME_GLOBAL, pass the apIsLocal
  7448.             // and apInsertPos variables to FindVar() so that it will update them to be global.
  7449.             // Otherwise, do not pass them since they were already set correctly by us above.
  7450.             if (g.CurrentFunc->mDefaultVarType == VAR_ASSUME_GLOBAL)
  7451.                 return FindVar(aVarName, aVarNameLength, apInsertPos, ALWAYS_USE_GLOBAL, NULL, apIsLocal);
  7452.             else
  7453.                 return FindVar(aVarName, aVarNameLength, NULL, ALWAYS_USE_GLOBAL);
  7454.         }
  7455.         if (aAlwaysUse == ALWAYS_USE_DEFAULT && mIsReadyToExecute) // In this case, fall back to globals only at runtime.
  7456.             return FindVar(aVarName, aVarNameLength, NULL, ALWAYS_USE_GLOBAL);
  7457.     }
  7458.     // Otherwise, since above didn't return:
  7459.     return NULL; // No match.
  7460. }
  7461.  
  7462.  
  7463.  
  7464. Var *Script::AddVar(char *aVarName, size_t aVarNameLength, int aInsertPos, int aIsLocal)
  7465. // Returns the address of the new variable or NULL on failure.
  7466. // Caller must ensure that g.CurrentFunc!=NULL whenever aIsLocal==true.
  7467. // Caller must ensure that aVarName isn't NULL and that this isn't a duplicate variable name.
  7468. // In addition, it has provided aInsertPos, which is the insertion point so that the list stays sorted.
  7469. // Finally, aIsLocal has been provided to indicate which list, global or local, should receive this
  7470. // new variable.  aIsLocal is normally 0 or 1 (boolean), but it may be 2 to indicate "it's a local AND a
  7471. // function's parameter".
  7472. {
  7473.     if (!*aVarName) // Should never happen, so just silently indicate failure.
  7474.         return NULL;
  7475.     if (!aVarNameLength) // Caller didn't specify, so use the entire string.
  7476.         aVarNameLength = strlen(aVarName);
  7477.  
  7478.     if (aVarNameLength > MAX_VAR_NAME_LENGTH)
  7479.     {
  7480.         // Load-time callers should check for this.  But at runtime, it's possible for a dynamically
  7481.         // resolved variable name to be too long.  Note that aVarName should be the exact variable
  7482.         // name and does not need to be truncated to aVarNameLength whenever this error occurs
  7483.         // (i.e. at runtime):
  7484.         if (mIsReadyToExecute) // Runtime error.
  7485.             ScriptError("Variable name too long." ERR_ABORT, aVarName);
  7486.         else
  7487.             ScriptError("Variable name too long.", aVarName);
  7488.         return NULL;
  7489.     }
  7490.  
  7491.     // Make a temporary copy that includes only the first aVarNameLength characters from aVarName:
  7492.     char var_name[MAX_VAR_NAME_LENGTH + 1];
  7493.     strlcpy(var_name, aVarName, aVarNameLength + 1);  // See explanation above.  +1 to convert length to size.
  7494.  
  7495.     if (!Var::ValidateName(var_name, mIsReadyToExecute))
  7496.         // Above already displayed error for us.  This can happen at loadtime or runtime (e.g. StringSplit).
  7497.         return NULL;
  7498.  
  7499.     // Not necessary or desirable to add built-in variables to a function's list of locals.  Always keep
  7500.     // built-in vars in the global list for efficiency and to keep them out of ListVars.  Note that another
  7501.     // section at loadtime displays an error for any attempt to explicitly declare built-in variables as
  7502.     // either global or local.
  7503.     void *var_type = GetVarType(var_name);
  7504.     if (aIsLocal && (var_type != (void *)VAR_NORMAL || !stricmp(var_name, "ErrorLevel"))) // Attempt to create built-in variable as local.
  7505.     {
  7506.         if (aIsLocal == 1) // It's not a UDF's parameter, so fall back to the global built-in variable of this name rather than displaying an error.
  7507.             return FindOrAddVar(var_name, aVarNameLength, ALWAYS_USE_GLOBAL); // Force find-or-create of global.
  7508.         else // aIsLocal == 2, which means "this is a local variable and a function's parameter".
  7509.         {
  7510.             ScriptError("Illegal parameter name.", aVarName); // Short message since so rare.
  7511.             return NULL;
  7512.         }
  7513.     }
  7514.  
  7515.     // Allocate some dynamic memory to pass to the constructor:
  7516.     char *new_name = SimpleHeap::Malloc(var_name, aVarNameLength);
  7517.     if (!new_name)
  7518.         // It already displayed the error for us.  These mem errors are so unusual that we're not going
  7519.         // to bother varying the error message to include ERR_ABORT if this occurs during runtime.
  7520.         return NULL;
  7521.  
  7522.     Var *the_new_var = new Var(new_name, var_type, aIsLocal != 0); // , aAttrib);
  7523.     if (the_new_var == NULL)
  7524.     {
  7525.         ScriptError(ERR_OUTOFMEM);
  7526.         return NULL;
  7527.     }
  7528.  
  7529.     // If there's a lazy var list, aInsertPos provided by the caller is for it, so this new variable
  7530.     // always gets inserted into that list because there's always room for one more (because the
  7531.     // previously added variable would have purged it if it had reached capacity).
  7532.     Var **lazy_var = aIsLocal ? g.CurrentFunc->mLazyVar : mLazyVar;
  7533.     int &lazy_var_count = aIsLocal ? g.CurrentFunc->mLazyVarCount : mLazyVarCount; // Used further below too.
  7534.     if (lazy_var)
  7535.     {
  7536.         if (aInsertPos != lazy_var_count) // Need to make room at the indicated position for this variable.
  7537.             memmove(lazy_var + aInsertPos + 1, lazy_var + aInsertPos, (lazy_var_count - aInsertPos) * sizeof(Var *));
  7538.         //else both are zero or the item is being inserted at the end of the list, so it's easy.
  7539.         lazy_var[aInsertPos] = the_new_var;
  7540.         ++lazy_var_count;
  7541.         // In a testing creating between 200,000 and 400,000 variables, using a size of 1000 vs. 500 improves
  7542.         // the speed by 17%, but when you substract out the binary search time (leaving only the insert time),
  7543.         // the increase is more like 34%.  But there is a diminishing return after that: Going to 2000 only
  7544.         // gains 20%, and to 4000 only gains an addition 10%.  Therefore, to conserve memory in functions that
  7545.         // have so many variables that the lazy list is used, a good trade-off seems to be 2000 (8 KB of memory)
  7546.         // per function that needs it.
  7547.         #define MAX_LAZY_VARS 2000 // Don't make this larger than 90000 without altering the incremental increase of alloc_count further below.
  7548.         if (lazy_var_count < MAX_LAZY_VARS) // The lazy list hasn't yet reached capacity, so no need to merge it into the main list.
  7549.             return the_new_var;
  7550.     }
  7551.  
  7552.     // Since above didn't return, either there is no lazy list or the lazy list is full and needs to be
  7553.     // merged into the main list.
  7554.  
  7555.     // Create references to whichever variable list (local or global) is being acted upon.  These
  7556.     // references simplify the code:
  7557.     Var **&var = aIsLocal ? g.CurrentFunc->mVar : mVar; // This needs to be a ref. too in case it needs to be realloc'd.
  7558.     int &var_count = aIsLocal ? g.CurrentFunc->mVarCount : mVarCount;
  7559.     int &var_count_max = aIsLocal ? g.CurrentFunc->mVarCountMax : mVarCountMax;
  7560.     int alloc_count;
  7561.  
  7562.     // Since the above would have returned if the lazy list is present but not yet full, if the left side
  7563.     // of the OR below is false, it also means that lazy_var is NULL.  Thus lazy_var==NULL is implicit for the
  7564.     // right side of the OR:
  7565.     if ((lazy_var && var_count + MAX_LAZY_VARS > var_count_max) || var_count == var_count_max)
  7566.     {
  7567.         // Increase by orders of magnitude each time because realloc() is probably an expensive operation
  7568.         // in terms of hurting performance.  So here, a little bit of memory is sacrificed to improve
  7569.         // the expected level of performance for scripts that use hundreds of thousands of variables.
  7570.         if (!var_count_max)
  7571.             alloc_count = aIsLocal ? 100 : 1000;  // 100 conserves memory since every function needs such a block, and most functions have much fewer than 100 local variables.
  7572.         else if (var_count_max < 1000)
  7573.             alloc_count = 1000;
  7574.         else if (var_count_max < 9999) // Making this 9999 vs. 10000 allows an exact/whole number of lazy_var blocks to fit into main indices between 10000 and 99999
  7575.             alloc_count = 9999;
  7576.         else if (var_count_max < 100000)
  7577.         {
  7578.             alloc_count = 100000;
  7579.             // This is also the threshold beyond which the lazy list is used to accelerate performance.
  7580.             // Create the permanently lazy list:
  7581.             Var **&lazy_var = aIsLocal ? g.CurrentFunc->mLazyVar : mLazyVar;
  7582.             if (   !(lazy_var = (Var **)malloc(MAX_LAZY_VARS * sizeof(Var *)))   )
  7583.             {
  7584.                 ScriptError(ERR_OUTOFMEM);
  7585.                 return NULL;
  7586.             }
  7587.         }
  7588.         else if (var_count_max < 1000000)
  7589.             alloc_count = 1000000;
  7590.         else
  7591.             alloc_count = var_count_max + 1000000;  // i.e. continue to increase by 4MB (1M*4) each time.
  7592.  
  7593.         Var **temp = (Var **)realloc(var, alloc_count * sizeof(Var *)); // If passed NULL, realloc() will do a malloc().
  7594.         if (!temp)
  7595.         {
  7596.             ScriptError(ERR_OUTOFMEM);
  7597.             return NULL;
  7598.         }
  7599.         var = temp;
  7600.         var_count_max = alloc_count;
  7601.     }
  7602.  
  7603.     if (!lazy_var)
  7604.     {
  7605.         if (aInsertPos != var_count) // Need to make room at the indicated position for this variable.
  7606.             memmove(var + aInsertPos + 1, var + aInsertPos, (var_count - aInsertPos) * sizeof(Var *));
  7607.         //else both are zero or the item is being inserted at the end of the list, so it's easy.
  7608.         var[aInsertPos] = the_new_var;
  7609.         ++var_count;
  7610.         return the_new_var;
  7611.     }
  7612.     //else the variable was already inserted into the lazy list, so the above is not done.
  7613.  
  7614.     // Since above didn't return, the lazy list is not only present, but full because otherwise it
  7615.     // would have returned higher above.
  7616.  
  7617.     // Since the lazy list is now at its max capacity, merge it into the main list (if the
  7618.     // main list was at capacity, this section relies upon the fact that the above already
  7619.     // increased its capacity by an amount far larger than the number of items containined
  7620.     // in the lazy list).
  7621.  
  7622.     // LAZY LIST: Although it's not nearly as good as hashing (which might be implemented in the future,
  7623.     // though it would be no small undertaking since it affects so many design aspects, both load-time
  7624.     // and runtime for scripts), this method of accelerating inserts into a binary search array is
  7625.     // enormously beneficial because it improves the scalability of binary-search by two orders
  7626.     // of magnitude (from about 100,000 variables to at least 5M).  Credit for the idea goes to Lazlo.
  7627.     // DETAILS:
  7628.     // The fact that this merge operation is so much faster than total work required
  7629.     // to insert each one into the main list is the whole reason for having the lazy
  7630.     // list.  In other words, the large memmove() that would otherwise be required
  7631.     // to insert each new variable into the main list is completely avoided.  Large memmove()s
  7632.     // are far more costly than small ones because apparently they can't fit into the CPU
  7633.     // cache, so the operation would take hundreds or even thousands of times longer
  7634.     // depending on the speed difference between main memory and CPU cache.  But above and
  7635.     // beyond the CPU cache issue, the lazy sorting method results in vastly less memory
  7636.     // being moved than would have been required without it, so even if the CPU doesn't have
  7637.     // a cache, the lazy list method vastly increases performance for scripts that have more
  7638.     // than 100,000 variables, allowing at least 5 million variables to be created without a
  7639.     // dramatic reduction in performance.
  7640.  
  7641.     char *target_name;
  7642.     Var **insert_pos, **insert_pos_prev;
  7643.     int i, left, right, mid;
  7644.  
  7645.     // Append any items from the lazy list to the main list that are alphabetically greater than
  7646.     // the last item in the main list.  Above has already ensured that the main list is large enough
  7647.     // to accept all items in the lazy list.
  7648.     for (i = lazy_var_count - 1, target_name = var[var_count - 1]->mName
  7649.         ; i > -1 && stricmp(target_name, lazy_var[i]->mName) < 0
  7650.         ; --i);
  7651.     // Above is a self-contained loop.
  7652.     // Now do a separate loop to append (in the *correct* order) anything found above.
  7653.     for (int j = i + 1; j < lazy_var_count; ++j) // Might have zero iterations.
  7654.         var[var_count++] = lazy_var[j];
  7655.     lazy_var_count = i + 1; // The number of items that remain after moving out those that qualified.
  7656.  
  7657.     // This will have zero iterations if the above already moved them all:
  7658.     for (insert_pos = var + var_count, i = lazy_var_count - 1; i > -1; --i)
  7659.     {
  7660.         // Modified binary search that relies on the fact that caller has ensured a match will never
  7661.         // be found in the main list for each item in the lazy list:
  7662.         for (target_name = lazy_var[i]->mName, left = 0, right = (int)(insert_pos - var - 1); left <= right;)
  7663.         {
  7664.             mid = (left + right) / 2;
  7665.             if (stricmp(target_name, var[mid]->mName) > 0) // lstrcmpi() is not used: 1) avoids breaking exisitng scripts; 2) provides consistent behavior across multiple locales; 3) performance.
  7666.                 left = mid + 1;
  7667.             else // it must be < 0 because caller has ensured it can't be equal (i.e. that there will be no match)
  7668.                 right = mid - 1;
  7669.         }
  7670.         // Now "left" contains the insertion point is is known to be less than var_count due to a previous
  7671.         // set of loops above.  Make a gap there large enough to hold all items because that allows a
  7672.         // smaller total amount of memory to be moved by shifting the gap to the left in the main list,
  7673.         // gradually filling it as we go:
  7674.         insert_pos_prev = insert_pos;  // "prev" is the now the position of the beginning of the gap, but the gap is about to be shifted left by moving memory right.
  7675.         insert_pos = var + left; // This is where it *would* be inserted if we weren't doing the accelerated merge.
  7676.         memmove(insert_pos + i + 1, insert_pos, (insert_pos_prev - insert_pos) * sizeof(Var *));
  7677.         var[left + i] = lazy_var[i]; // Now insert this item at the far right side of the gap just created.
  7678.     }
  7679.     var_count += lazy_var_count;
  7680.     lazy_var_count = 0;  // Indicate that the lazy var list is now empty.
  7681.  
  7682.     return the_new_var;
  7683. }
  7684.  
  7685.  
  7686.  
  7687. void *Script::GetVarType(char *aVarName)
  7688. {
  7689.     // Convert to lowercase to help performance a little (it typically only helps loadtime performance because
  7690.     // this function is rarely called during script-runtime).
  7691.     char lowercase[MAX_VAR_NAME_LENGTH + 1];
  7692.     strlcpy(lowercase, aVarName, sizeof(lowercase)); // Caller should have ensured it fits, but call strlcpy() for maintainability.
  7693.     CharLower(lowercase);
  7694.     // Above: CharLower() is smaller in code size than strlwr(), but CharLower uses the OS locale and strlwr uses
  7695.     // the setlocal() locale (which is always the same if setlocal() is never called).  However, locale
  7696.     // differences shouldn't affect the cases checked below; some evidence of this is at MSDN:
  7697.     // "CharLower always maps uppercase I to lowercase I, even when the current language is Turkish or Azeri."
  7698.  
  7699.     if (lowercase[0] != 'a' || lowercase[1] != '_')  // This check helps average-case performance.
  7700.     {
  7701.         if (   !strcmp(lowercase, "true")
  7702.             || !strcmp(lowercase, "false")) return BIV_True_False;
  7703.         if (!strcmp(lowercase, "clipboard")) return (void *)VAR_CLIPBOARD;
  7704.         if (!strcmp(lowercase, "clipboardall")) return (void *)VAR_CLIPBOARDALL;
  7705.         if (!strcmp(lowercase, "comspec")) return BIV_ComSpec; // Lacks an "A_" prefix for backward compatibility with pre-NoEnv scripts and also it's easier to type & remember.
  7706.         if (!strcmp(lowercase, "programfiles")) return BIV_ProgramFiles; // v1.0.43.08: Added to ease the transition to #NoEnv.
  7707.         // Otherwise:
  7708.         return (void *)VAR_NORMAL;
  7709.     }
  7710.  
  7711.     // Otherwise, lowercase begins with "a_", so it's probably one of the built-in variables.
  7712.     char *lower = lowercase + 2;
  7713.  
  7714.     // Keeping the most common ones near the top helps performance a little.
  7715.     if (!strcmp(lower, "index")) return BIV_LoopIndex;  // A short name since it's typed so often.
  7716.  
  7717.     if (   !strcmp(lower, "mmmm")    // Long name of month.
  7718.         || !strcmp(lower, "mmm")     // 3-char abbrev. month name.
  7719.         || !strcmp(lower, "dddd")    // Name of weekday, e.g. Sunday
  7720.         || !strcmp(lower, "ddd")   ) // Abbrev., e.g. Sun
  7721.         return BIV_MMM_DDD;
  7722.  
  7723.     if (   !strcmp(lower, "yyyy")
  7724.         || !strcmp(lower, "year") // Same as above.
  7725.         || !strcmp(lower, "mm")   // 01 thru 12
  7726.         || !strcmp(lower, "mon")  // Same
  7727.         || !strcmp(lower, "dd")   // 01 thru 31
  7728.         || !strcmp(lower, "mday") // Same
  7729.         || !strcmp(lower, "wday")
  7730.         || !strcmp(lower, "yday")
  7731.         || !strcmp(lower, "yweek")
  7732.         || !strcmp(lower, "hour")
  7733.         || !strcmp(lower, "min")
  7734.         || !strcmp(lower, "sec")
  7735.         || !strcmp(lower, "msec")   )
  7736.         return BIV_DateTime;
  7737.  
  7738.     if (!strcmp(lower, "tickcount")) return BIV_TickCount;
  7739.     if (   !strcmp(lower, "now")
  7740.         || !strcmp(lower, "nowutc")) return BIV_Now;
  7741.  
  7742.     if (!strcmp(lower, "workingdir")) return BIV_WorkingDir;
  7743.     if (!strcmp(lower, "scriptname")) return BIV_ScriptName;
  7744.     if (!strcmp(lower, "scriptdir")) return BIV_ScriptDir;
  7745.     if (!strcmp(lower, "scriptfullpath")) return BIV_ScriptFullPath;
  7746.     if (!strcmp(lower, "linenumber")) return BIV_LineNumber;
  7747.     if (!strcmp(lower, "linefile")) return BIV_LineFile;
  7748.  
  7749. // A_IsCompiled is left blank/undefined in uncompiled scripts.
  7750. #ifdef AUTOHOTKEYSC
  7751.     if (!strcmp(lower, "iscompiled")) return BIV_IsCompiled;
  7752. #endif
  7753.  
  7754.     if (   !strcmp(lower, "batchlines")
  7755.         || !strcmp(lower, "numbatchlines")) return BIV_BatchLines;
  7756.     if (!strcmp(lower, "titlematchmode")) return BIV_TitleMatchMode;
  7757.     if (!strcmp(lower, "titlematchmodespeed")) return BIV_TitleMatchModeSpeed;
  7758.     if (!strcmp(lower, "detecthiddenwindows")) return BIV_DetectHiddenWindows;
  7759.     if (!strcmp(lower, "detecthiddentext")) return BIV_DetectHiddenText;
  7760.     if (!strcmp(lower, "autotrim")) return BIV_AutoTrim;
  7761.     if (!strcmp(lower, "stringcasesense")) return BIV_StringCaseSense;
  7762.     if (!strcmp(lower, "formatinteger")) return BIV_FormatInteger;
  7763.     if (!strcmp(lower, "formatfloat")) return BIV_FormatFloat;
  7764.     if (!strcmp(lower, "keydelay")) return BIV_KeyDelay;
  7765.     if (!strcmp(lower, "windelay")) return BIV_WinDelay;
  7766.     if (!strcmp(lower, "controldelay")) return BIV_ControlDelay;
  7767.     if (!strcmp(lower, "mousedelay")) return BIV_MouseDelay;
  7768.     if (!strcmp(lower, "defaultmousespeed")) return BIV_DefaultMouseSpeed;
  7769.     if (!strcmp(lower, "issuspended")) return BIV_IsSuspended;
  7770.  
  7771.     if (!strcmp(lower, "iconhidden")) return BIV_IconHidden;
  7772.     if (!strcmp(lower, "icontip")) return BIV_IconTip;
  7773.     if (!strcmp(lower, "iconfile")) return BIV_IconFile;
  7774.     if (!strcmp(lower, "iconnumber")) return BIV_IconNumber;
  7775.  
  7776.     if (!strcmp(lower, "exitreason")) return BIV_ExitReason;
  7777.  
  7778.     if (!strcmp(lower, "ostype")) return BIV_OSType;
  7779.     if (!strcmp(lower, "osversion")) return BIV_OSVersion;
  7780.     if (!strcmp(lower, "language")) return BIV_Language;
  7781.     if (   !strcmp(lower, "computername")
  7782.         || !strcmp(lower, "username")) return BIV_UserName_ComputerName;
  7783.  
  7784.     if (!strcmp(lower, "windir")) return BIV_WinDir;
  7785.     if (!strcmp(lower, "temp")) return BIV_Temp; // Debatably should be A_TempDir, but brevity seemed more popular with users, perhaps for heavy uses of the temp folder.
  7786.     if (!strcmp(lower, "programfiles")) return BIV_ProgramFiles;
  7787.     if (!strcmp(lower, "mydocuments")) return BIV_MyDocuments;
  7788.  
  7789.     if (   !strcmp(lower, "appdata")
  7790.         || !strcmp(lower, "appdatacommon")) return BIV_AppData;
  7791.     if (   !strcmp(lower, "desktop")
  7792.         || !strcmp(lower, "desktopcommon")) return BIV_Desktop;
  7793.     if (   !strcmp(lower, "startmenu")
  7794.         || !strcmp(lower, "startmenucommon")) return BIV_StartMenu;
  7795.     if (   !strcmp(lower, "programs")
  7796.         || !strcmp(lower, "programscommon")) return BIV_Programs;
  7797.     if (   !strcmp(lower, "startup")
  7798.         || !strcmp(lower, "startupcommon")) return BIV_Startup;
  7799.  
  7800.     if (!strcmp(lower, "isadmin")) return BIV_IsAdmin;
  7801.     if (!strcmp(lower, "cursor")) return BIV_Cursor;
  7802.     if (   !strcmp(lower, "caretx")
  7803.         || !strcmp(lower, "carety")) return BIV_Caret;
  7804.     if (   !strcmp(lower, "screenwidth")
  7805.         || !strcmp(lower, "screenheight")) return BIV_ScreenWidth_Height;
  7806.  
  7807.     if (!strncmp(lower, "ipaddress", 9))
  7808.     {
  7809.         lower += 9;
  7810.         return (*lower >= '1' && *lower <= '4'
  7811.             && !lower[1]) // Make sure has only one more character rather than none or several (e.g. A_IPAddress1abc should not be match).
  7812.             ? BIV_IPAddress
  7813.             : (void *)VAR_NORMAL; // Otherwise it can't be a match for any built-in variable.
  7814.     }
  7815.  
  7816.     if (!strncmp(lower, "loop", 4))
  7817.     {
  7818.         lower += 4;
  7819.         if (!strcmp(lower, "readline")) return BIV_LoopReadLine;
  7820.         if (!strcmp(lower, "field")) return BIV_LoopField;
  7821.  
  7822.         if (!strncmp(lower, "file", 4))
  7823.         {
  7824.             lower += 4;
  7825.             if (!strcmp(lower, "name")) return BIV_LoopFileName;
  7826.             if (!strcmp(lower, "shortname")) return BIV_LoopFileShortName;
  7827.             if (!strcmp(lower, "ext")) return BIV_LoopFileExt;
  7828.             if (!strcmp(lower, "dir")) return BIV_LoopFileDir;
  7829.             if (!strcmp(lower, "fullpath")) return BIV_LoopFileFullPath;
  7830.             if (!strcmp(lower, "longpath")) return BIV_LoopFileLongPath;
  7831.             if (!strcmp(lower, "shortpath")) return BIV_LoopFileShortPath;
  7832.             if (!strcmp(lower, "attrib")) return BIV_LoopFileAttrib;
  7833.  
  7834.             if (   !strcmp(lower, "timemodified")
  7835.                 || !strcmp(lower, "timecreated")
  7836.                 || !strcmp(lower, "timeaccessed")) return BIV_LoopFileTime;
  7837.             if (   !strcmp(lower, "size")
  7838.                 || !strcmp(lower, "sizekb")
  7839.                 || !strcmp(lower, "sizemb")) return BIV_LoopFileSize;
  7840.             // Otherwise, it can't be a match for any built-in variable:
  7841.             return (void *)VAR_NORMAL;
  7842.         }
  7843.  
  7844.         if (!strncmp(lower, "reg", 3))
  7845.         {
  7846.             lower += 3;
  7847.             if (!strcmp(lower, "type")) return BIV_LoopRegType;
  7848.             if (!strcmp(lower, "key")) return BIV_LoopRegKey;
  7849.             if (!strcmp(lower, "subkey")) return BIV_LoopRegSubKey;
  7850.             if (!strcmp(lower, "name")) return BIV_LoopRegName;
  7851.             if (!strcmp(lower, "timemodified")) return BIV_LoopRegTimeModified;
  7852.             // Otherwise, it can't be a match for any built-in variable:
  7853.             return (void *)VAR_NORMAL;
  7854.         }
  7855.     }
  7856.  
  7857.     if (!strcmp(lower, "thisfunc")) return BIV_ThisFunc;
  7858.     if (!strcmp(lower, "thislabel")) return BIV_ThisLabel;
  7859.     if (!strcmp(lower, "thismenuitem")) return BIV_ThisMenuItem;
  7860.     if (!strcmp(lower, "thismenuitempos")) return BIV_ThisMenuItemPos;
  7861.     if (!strcmp(lower, "thismenu")) return BIV_ThisMenu;
  7862.     if (!strcmp(lower, "thishotkey")) return BIV_ThisHotkey;
  7863.     if (!strcmp(lower, "priorhotkey")) return BIV_PriorHotkey;
  7864.     if (!strcmp(lower, "timesincethishotkey")) return BIV_TimeSinceThisHotkey;
  7865.     if (!strcmp(lower, "timesincepriorhotkey")) return BIV_TimeSincePriorHotkey;
  7866.     if (!strcmp(lower, "endchar")) return BIV_EndChar;
  7867.     if (!strcmp(lower, "lasterror")) return BIV_LastError;
  7868.  
  7869.     if (!strcmp(lower, "eventinfo")) return BIV_EventInfo; // It's called "EventInfo" vs. "GuiEventInfo" because it applies to non-Gui events such as OnClipboardChange.
  7870.     if (!strcmp(lower, "guicontrol")) return BIV_GuiControl;
  7871.  
  7872.     if (   !strcmp(lower, "guicontrolevent") // v1.0.36: A_GuiEvent was added as a synonym for A_GuiControlEvent because it seems unlikely that A_GuiEvent will ever be needed for anything:
  7873.         || !strcmp(lower, "guievent")) return BIV_GuiEvent;
  7874.  
  7875.     if (   !strcmp(lower, "gui")
  7876.         || !strcmp(lower, "guiwidth")
  7877.         || !strcmp(lower, "guiheight")
  7878.         || !strcmp(lower, "guix") // Naming: Brevity seems more a benefit than would A_GuiEventX's improved clarity.
  7879.         || !strcmp(lower, "guiy")) return BIV_Gui; // These can be overloaded if a GuiMove label or similar is ever needed.
  7880.  
  7881.     if (!strcmp(lower, "timeidle")) return BIV_TimeIdle;
  7882.     if (!strcmp(lower, "timeidlephysical")) return BIV_TimeIdlePhysical;
  7883.     if (   !strcmp(lower, "space")
  7884.         || !strcmp(lower, "tab")) return BIV_Space_Tab;
  7885.     if (!strcmp(lower, "ahkversion")) return BIV_AhkVersion;
  7886.     if (!strcmp(lower, "ahkpath")) return BIV_AhkPath;
  7887.  
  7888.     // Since above didn't return:
  7889.     return (void *)VAR_NORMAL;
  7890. }
  7891.  
  7892.  
  7893.  
  7894. WinGroup *Script::FindGroup(char *aGroupName, bool aCreateIfNotFound)
  7895. // Caller must ensure that aGroupName isn't NULL.  But if it's the empty string, NULL is returned.
  7896. // Returns the Group whose name matches aGroupName.  If it doesn't exist, it is created if aCreateIfNotFound==true.
  7897. // Thread-safety: This function is thread-safe (except when when called with aCreateIfNotFound==true) even when
  7898. // the main thread happens to be calling AddGroup() and changing the linked list while it's being traversed here
  7899. // by the hook thread.  However, any subsequent changes to this function or AddGroup() must be carefully reviewed.
  7900. {
  7901.     if (!*aGroupName)
  7902.         return NULL;
  7903.     for (WinGroup *group = mFirstGroup; group != NULL; group = group->mNextGroup)
  7904.         if (!stricmp(group->mName, aGroupName)) // lstrcmpi() is not used: 1) avoids breaking exisitng scripts; 2) provides consistent behavior across multiple locales; 3) performance.
  7905.             return group; // Match found.
  7906.     // Otherwise, no match found, so create a new group.
  7907.     if (!aCreateIfNotFound || AddGroup(aGroupName) != OK)
  7908.         return NULL;
  7909.     return mLastGroup;
  7910. }
  7911.  
  7912.  
  7913.  
  7914. ResultType Script::AddGroup(char *aGroupName)
  7915. // Returns OK or FAIL.
  7916. // The caller must already have verfied that this isn't a duplicate group.
  7917. // This function is not thread-safe because it adds an entry to the quasi-global list of window groups.
  7918. // In addition, if this function is being called by one thread while another thread is calling FindGroup(),
  7919. // the thread-safety notes in FindGroup() apply.
  7920. {
  7921.     size_t aGroupName_length = strlen(aGroupName);
  7922.     if (aGroupName_length > MAX_VAR_NAME_LENGTH)
  7923.         return ScriptError("Group name too long.", aGroupName);
  7924.     if (!Var::ValidateName(aGroupName, false, DISPLAY_NO_ERROR)) // Seems best to use same validation as var names.
  7925.         return ScriptError("Illegal group name.", aGroupName);
  7926.  
  7927.     char *new_name = SimpleHeap::Malloc(aGroupName, aGroupName_length);
  7928.     if (!new_name)
  7929.         return FAIL;  // It already displayed the error for us.
  7930.  
  7931.     // The precise method by which the follows steps are done should be thread-safe even if
  7932.     // some other thread calls FindGroup() in the middle of the operation.  But any changes
  7933.     // must be carefully reviewed:
  7934.     WinGroup *the_new_group = new WinGroup(new_name);
  7935.     if (the_new_group == NULL)
  7936.         return ScriptError(ERR_OUTOFMEM);
  7937.     if (mFirstGroup == NULL)
  7938.         mFirstGroup = the_new_group;
  7939.     else
  7940.         mLastGroup->mNextGroup = the_new_group;
  7941.     // This must be done after the above:
  7942.     mLastGroup = the_new_group;
  7943.     return OK;
  7944. }
  7945.  
  7946.  
  7947.  
  7948. Line *Script::PreparseBlocks(Line *aStartingLine, bool aFindBlockEnd, Line *aParentLine)
  7949. // aFindBlockEnd should be true, only when this function is called
  7950. // by itself.  The end of this function relies upon this definition.
  7951. // Will return NULL to the top-level caller if there's an error, or if
  7952. // mLastLine is NULL (i.e. the script is empty).
  7953. {
  7954.     // Not thread-safe, so this can only parse one script at a time.
  7955.     // Not a problem for the foreseeable future:
  7956.     static int nest_level; // Level zero is the outermost one: outside all blocks.
  7957.     static bool abort;
  7958.     if (!aParentLine)
  7959.     {
  7960.         // We were called from outside, not recursively, so init these.  This is
  7961.         // very important if this function is ever to be called from outside
  7962.         // more than once, even though it isn't currently:
  7963.         nest_level = 0;
  7964.         abort = false;
  7965.     }
  7966.  
  7967.     int i, open_parens;
  7968.     bool in_quotes;
  7969.     DerefType *deref, *deref2;
  7970.     char *param_start, *param_end, *param_last_char, *cp, c;
  7971.     bool found;
  7972.  
  7973.     // Don't check aStartingLine here at top: only do it at the bottom
  7974.     // for its differing return values.
  7975.     for (Line *line = aStartingLine; line;)
  7976.     {
  7977.         // Check if any of each arg's derefs are function calls.  If so, do some validation and
  7978.         // preprocessing to set things up for better runtime performance:
  7979.         for (i = 0; i < line->mArgc; ++i) // For each arg.
  7980.         {
  7981.             ArgStruct &this_arg = line->mArg[i]; // For performance and convenience.
  7982.             // Exclude the derefs of output and input vars from consideration, since they can't
  7983.             // be function calls:
  7984.             if (!this_arg.is_expression // For now, only expressions are capable of calling functions. If ever change this, might want to add a check here for this_arg.type != ARG_TYPE_NORMAL (for performance).
  7985.                 || !this_arg.deref) // No function-calls present.
  7986.                 continue;
  7987.             for (deref = this_arg.deref; deref->marker; ++deref) // For each deref.
  7988.             {
  7989.                 if (!deref->is_function)
  7990.                     continue;
  7991.                 if (   !(deref->func = FindFunc(deref->marker, deref->length))   )
  7992.                 {
  7993. #ifndef AUTOHOTKEYSC
  7994.                     bool error_was_shown;
  7995.                     if (   !(deref->func = FindFuncInLibrary(deref->marker, deref->length, error_was_shown))   )
  7996.                     {
  7997.                         abort = true; // So that the caller doesn't also report an error.
  7998.                         // When above already displayed the proximate cause of the error, it's usually
  7999.                         // undesirable to show the cascade effects of that error in a second dialog:
  8000.                         return error_was_shown ? NULL : line->PreparseError(ERR_NONEXISTENT_FUNCTION, deref->marker);
  8001.                     }
  8002. #else
  8003.                     abort = true;
  8004.                     return line->PreparseError(ERR_NONEXISTENT_FUNCTION, deref->marker);
  8005. #endif
  8006.                 }
  8007.                 // An earlier stage has ensured that if the function exists, it's mJumpToLine is non-NULL.
  8008.                 Func &func = *deref->func; // For performance and convenience.
  8009.                 // Ealier stage has ensured that strchr() will always find an open-parenthesis:
  8010.                 for (deref->param_count = 0, param_start = omit_leading_whitespace(strchr(deref->marker, '(') + 1);;)
  8011.                 {
  8012.                     // For each parameter of this function-call.
  8013.                     if (*param_start == ')') // No more params.
  8014.                         break;
  8015.                     if (*param_start == ',')
  8016.                     {
  8017.                         abort = true; // So that the caller doesn't also report an error.
  8018.                         return line->PreparseError(ERR_BLANK_PARAM, deref->marker);
  8019.                     }
  8020.                     // Although problems such as blank/empty parameters and missing close-paren were already
  8021.                     // checked by DefineFunc(), that was done only for the function's formal definition, not
  8022.                     // the calls to it.  And although parentheses were balanced in all expressions at an earlier
  8023.                     // stage, it's done again here in case function calls are ever allowed to be occur in
  8024.                     // a non-expression (or dynamic functions calls such as FnArray%i%() are ever supported):
  8025.                     if (!*param_start)
  8026.                     {
  8027.                         abort = true; // So that the caller doesn't also report an error.
  8028.                         return line->PreparseError(ERR_MISSING_CLOSE_PAREN, deref->marker);
  8029.                     }
  8030.  
  8031.                     // Find the end of this function-param by taking into account nested parentheses, omitting
  8032.                     // from consideration any parentheses inside of quoted/literal strings.  When this loop is done,
  8033.                     // param_end this param's final comma or this function-call's close-paren when this param
  8034.                     // is the last one.
  8035.                     for (in_quotes = false, open_parens = 0, param_end = param_start;; ++param_end)
  8036.                     {
  8037.                         // If nested function calls are encountered within the function call being examined
  8038.                         // now, they are skipped over because they will be processed here only when the outer
  8039.                         // loop gets to them.
  8040.                         c = *param_end; // switch() is not used so that "break" can be used to exit the loop.
  8041.                         if (c == ',')
  8042.                         {
  8043.                             if (!(in_quotes || open_parens)) // This comma belongs to our function, so it marks the end of this param.
  8044.                                 break;
  8045.                             //else it's not a real comma since it's inside the parentheses of a subexpression or
  8046.                             // sub-function, or inside a quoted/literal string.  Ignore it.
  8047.                         }
  8048.                         else if (c == ')')
  8049.                         {
  8050.                             if (!in_quotes)
  8051.                             {
  8052.                                 if (!open_parens) // This is our function's close-paren, and thus the end of this param.
  8053.                                     break;
  8054.                                 else
  8055.                                     --open_parens;
  8056.                             }
  8057.                             //else it's not a real paren since it's inside a quoted/literal string.  Ignore it.
  8058.                         }
  8059.                         else if (c == '(')
  8060.                         {
  8061.                             if (!in_quotes) // Literal parentheses inside a quoted string should not be counted for this purpose.
  8062.                                 ++open_parens;
  8063.                         }
  8064.                         else if (c == '"')
  8065.                             // The simple method below is sufficient for our purpose even if a quoted string contains
  8066.                             // pairs of double-quotes to represent a single literal quote, e.g. "quoted ""word""".
  8067.                             // In other words, it relies on the fact that there must be an even number of quotes
  8068.                             // inside any mandatory-numeric arg that is an expression such as x=="red,blue"
  8069.                             in_quotes = !in_quotes;
  8070.                         else if (!c) // This function lacks a closing paren.
  8071.                         {
  8072.                             // Might happen if this is a syntax error not catchable by the earlier stage of syntax
  8073.                             // checking (paren balancing, quote balancing, etc.)
  8074.                             abort = true; // So that the caller doesn't also report an error.
  8075.                             return line->PreparseError(ERR_MISSING_CLOSE_PAREN, deref->marker);
  8076.                         }
  8077.                         //else it's some other, non-special character, so ignore it.
  8078.                     } // for() that finds the end of this param of this function.
  8079.  
  8080.                     // Above would have returned unless *param_end is either a comma or close-paren (namely the
  8081.                     // one that terminates this parameter of this function).
  8082.  
  8083.                     if (deref->param_count >= func.mParamCount) // Check this every iteration to avoid going beyond MAX_FUNCTION_PARAMS.
  8084.                     {
  8085.                         abort = true; // So that the caller doesn't also report an error.
  8086.                         return line->PreparseError("Too many parameters passed to function.", deref->marker);
  8087.                     }
  8088.                     // Below relies on the above check having been done first to avoid reading beyond the
  8089.                     // end of the mParam array.
  8090.                     // If this parameter is formally declared as ByRef, report a load-time error if
  8091.                     // the actual-parameter is obviously not a variable (can't catch everything, such
  8092.                     // as invalid double derefs, e.g. Array%VarContainingSpaces%):
  8093.                     if (!func.mIsBuiltIn && func.mParam[deref->param_count].is_byref)
  8094.                     {
  8095.                         // First check if there are any EXPR_TELLTALES characters in this param, since the
  8096.                         // presence of an expression for this parameter means it can't resolve to a variable
  8097.                         // as required by ByRef:
  8098.                         for (cp = param_start, param_last_char = omit_trailing_whitespace(param_start, param_end - 1)
  8099.                             ; cp <= param_last_char; ++cp)
  8100.                         {
  8101.                             if (*cp == ':' && cp[1] == '=')
  8102.                                 // v1.0.46.05: This section fixes the inability to pass ByRef certain non-trivial
  8103.                                 // assignments like X := " ". Although this doesn't give 100% detection, something
  8104.                                 // more elaborate seems unjustified (in both code size and performance) given that
  8105.                                 // this is only a syntax check.
  8106.                                 break;
  8107.                             if (strchr(EXPR_FORBIDDEN_BYREF, *cp)) // This character isn't allowed in something passed ByRef unless it's an assignment (which is checked below).
  8108.                             {
  8109.                                 if (Line::StartsWithAssignmentOp(cp) || strstr(cp, " ? ")) // v1.0.46.09: Also allow a ternary unconditionally, because it can be an arbitrarily complex expression followed by two branches that yield variables.
  8110.                                 {
  8111.                                     // Skip over :=, +=, -=, *=, /=, ++, -- ... because they can be passed ByRef.
  8112.                                     // In fact, don't even continue the loop because any assignment can be followed
  8113.                                     // by an arbitrarily complex sub-expression that shouldn't disqualify ByRef.
  8114.                                     break;
  8115.                                 }
  8116.                                 abort = true; // So that the caller doesn't also report an error.
  8117.                                 return line->PreparseError(ERR_BYREF, param_start);   // param_start seems more informative than func.mParam[deref->param_count].var->mName
  8118.                             }
  8119.                         }
  8120.                         // Below relies on the above having been done because the above should prevent
  8121.                         // any is_function derefs from being possible since their parentheses would have been caught
  8122.                         // as an error:
  8123.                         // For each deref after the function name itself, ensure that there is at least
  8124.                         // one deref in between this param's param_start and param_end.  This finds many
  8125.                         // common syntax errors such as passing a literal number or string to a ByRef
  8126.                         // parameter.  Note that there can be more than one for something like Array%i%_%j%
  8127.                         // or a ternary like true ? x : y.
  8128.                         for (found = false, deref2 = deref + 1; deref2->marker; ++deref2)
  8129.                             if (deref2->marker >= param_start && deref2->marker < param_end)
  8130.                             {
  8131.                                 found = true;
  8132.                                 break;
  8133.                             }
  8134.                         if (!found)
  8135.                         {
  8136.                             abort = true; // So that the caller doesn't also report an error.
  8137.                             return line->PreparseError(ERR_BYREF, param_start); // param_start seems more informative than func.mParam[deref->param_count].var->mName
  8138.                         }
  8139.                     }
  8140.  
  8141.                     ++deref->param_count;
  8142.  
  8143.                     // Set up for the next iteration:
  8144.                     param_start = param_end; // Must already be a comma or close-paren due to checking higher above.
  8145.                     if (*param_start == ',')
  8146.                     {
  8147.                         param_start = omit_leading_whitespace(param_start + 1);
  8148.                         if (*param_start == ')')
  8149.                         {
  8150.                             abort = true; // So that the caller doesn't also report an error.
  8151.                             return line->PreparseError(ERR_BLANK_PARAM, param_start); // Report param_start vs. aBuf to give an idea of where the blank parameter is in a possibly long list of params.
  8152.                         }
  8153.                     }
  8154.                     //else it might be ')', in which case the next iteration will handle it.
  8155.                     // Above has ensured that param_start now points to the next parameter, or ')' if none.
  8156.                 } // for each parameter of this function call.
  8157.                 if (deref->param_count < func.mMinParams)
  8158.                 {
  8159.                     abort = true; // So that the caller doesn't also report an error.
  8160.                     return line->PreparseError("Too few parameters passed to function.", deref->marker);
  8161.                 }
  8162.             } // for each deref of this arg
  8163.         } // for each arg of this line
  8164.  
  8165.         // All lines in our recursion layer are assigned to the block that the caller specified:
  8166.         if (line->mParentLine == NULL) // i.e. don't do it if it's already "owned" by an IF or ELSE.
  8167.             line->mParentLine = aParentLine; // Can be NULL.
  8168.  
  8169.         if (ACT_IS_IF_OR_ELSE_OR_LOOP(line->mActionType) || line->mActionType == ACT_REPEAT)
  8170.         {
  8171.             // Make the line immediately following each ELSE, IF or LOOP be enclosed by that stmt.
  8172.             // This is done to make it illegal for a Goto or Gosub to jump into a deeper layer,
  8173.             // such as in this example:
  8174.             // #y::
  8175.             // ifwinexist, pad
  8176.             // {
  8177.             //    goto, label1
  8178.             //    ifwinexist, pad
  8179.             //    label1:
  8180.             //    ; With or without the enclosing block, the goto would still go to an illegal place
  8181.             //    ; in the below, resulting in an "unexpected else" error:
  8182.             //    {
  8183.             //         msgbox, ifaction
  8184.             //    } ; not necessary to make this line enclosed by the if because labels can't point to it?
  8185.             // else
  8186.             //    msgbox, elseaction
  8187.             // }
  8188.             // return
  8189.  
  8190.             // In this case, the loader should have already ensured that line->mNextLine is not NULL:
  8191.             line->mNextLine->mParentLine = line;
  8192.             // Go onto the IF's or ELSE's action in case it too is an IF, rather than skipping over it:
  8193.             line = line->mNextLine;
  8194.             continue;
  8195.         }
  8196.  
  8197.         switch (line->mActionType)
  8198.         {
  8199.         case ACT_BLOCK_BEGIN:
  8200.             // Some insane limit too large to ever likely be exceeded, yet small enough not
  8201.             // to be a risk of stack overflow when recursing in ExecUntil().  Mostly, this is
  8202.             // here to reduce the chance of a program crash if a binary file, a corrupted file,
  8203.             // or something unexpected has been loaded as a script when it shouldn't have been.
  8204.             // Update: Increased the limit from 100 to 1000 so that large "else if" ladders
  8205.             // can be constructed.  Going much larger than 1000 seems unwise since ExecUntil()
  8206.             // will have to recurse for each nest-level, possibly resulting in stack overflow
  8207.             // if things get too deep:
  8208.             if (nest_level > 1000)
  8209.             {
  8210.                 abort = true; // So that the caller doesn't also report an error.
  8211.                 return line->PreparseError("Nesting too deep."); // Short msg since so rare.
  8212.             }
  8213.             // Since the current convention is to store the line *after* the
  8214.             // BLOCK_END as the BLOCK_BEGIN's related line, that line can
  8215.             // be legitimately NULL if this block's BLOCK_END is the last
  8216.             // line in the script.  So it's up to the called function
  8217.             // to report an error if it never finds a BLOCK_END for us.
  8218.             // UPDATE: The design requires that we do it here instead:
  8219.             ++nest_level;
  8220.             if (NULL == (line->mRelatedLine = PreparseBlocks(line->mNextLine, 1, line)))
  8221.                 if (abort) // the above call already reported the error.
  8222.                     return NULL;
  8223.                 else
  8224.                 {
  8225.                     abort = true; // So that the caller doesn't also report an error.
  8226.                     return line->PreparseError(ERR_MISSING_CLOSE_BRACE);
  8227.                 }
  8228.             --nest_level;
  8229.             // The convention is to have the BLOCK_BEGIN's related_line
  8230.             // point to the line *after* the BLOCK_END.
  8231.             line->mRelatedLine = line->mRelatedLine->mNextLine;  // Might be NULL now.
  8232.             // Otherwise, since any blocks contained inside this one would already
  8233.             // have been handled by the recursion in the above call, continue searching
  8234.             // from the end of this block:
  8235.             line = line->mRelatedLine; // If NULL, the loop-condition will catch it.
  8236.             break;
  8237.         case ACT_BLOCK_END:
  8238.             // Return NULL (failure) if the end was found but we weren't looking for one
  8239.             // (i.e. it's an orphan).  Otherwise return the line after the block_end line,
  8240.             // which will become the caller's mRelatedLine.  UPDATE: Return the
  8241.             // END_BLOCK line itself so that the caller can differentiate between
  8242.             // a NULL due to end-of-script and a NULL caused by an error:
  8243.             return aFindBlockEnd ? line  // Doesn't seem necessary to set abort to true.
  8244.                 : line->PreparseError(ERR_MISSING_OPEN_BRACE);
  8245.         default: // Continue line-by-line.
  8246.             line = line->mNextLine;
  8247.         } // switch()
  8248.     } // for each line
  8249.  
  8250.     // End of script has been reached.  <line> is now NULL so don't attempt to dereference it.
  8251.     // If we were still looking for an EndBlock to match up with a begin, that's an error.
  8252.     // Don't report the error here because we don't know which begin-block is waiting
  8253.     // for an end (the caller knows and must report the error).  UPDATE: Must report
  8254.     // the error here (see comments further above for explanation).   UPDATE #2: Changed
  8255.     // it again: Now we let the caller handle it again:
  8256.     if (aFindBlockEnd)
  8257.         //return mLastLine->PreparseError("The script ends while a block is still open (missing }).");
  8258.         return NULL;
  8259.     // If no error, return something non-NULL to indicate success to the top-level caller.
  8260.     // We know we're returning to the top-level caller because aFindBlockEnd is only true
  8261.     // when we're recursed, and in that case the above would have returned.  Thus,
  8262.     // we're not recursed upon reaching this line:
  8263.     return mLastLine;
  8264. }
  8265.  
  8266.  
  8267.  
  8268. Line *Script::PreparseIfElse(Line *aStartingLine, ExecUntilMode aMode, AttributeType aLoopTypeFile
  8269.     , AttributeType aLoopTypeReg, AttributeType aLoopTypeRead, AttributeType aLoopTypeParse)
  8270. // Zero is the default for aMode, otherwise:
  8271. // Will return NULL to the top-level caller if there's an error, or if
  8272. // mLastLine is NULL (i.e. the script is empty).
  8273. // Note: This function should be called with aMode == ONLY_ONE_LINE
  8274. // only when aStartingLine's ActionType is something recursable such
  8275. // as IF and BEGIN_BLOCK.  Otherwise, it won't return after only one line.
  8276. {
  8277.     // Don't check aStartingLine here at top: only do it at the bottom
  8278.     // for it's differing return values.
  8279.     Line *line_temp;
  8280.     // Although rare, a statement can be enclosed in more than one type of special loop,
  8281.     // e.g. both a file-loop and a reg-loop:
  8282.     AttributeType loop_type_file, loop_type_reg, loop_type_read, loop_type_parse;
  8283.     for (Line *line = aStartingLine; line != NULL;)
  8284.     {
  8285.         if (ACT_IS_IF(line->mActionType) || line->mActionType == ACT_LOOP || line->mActionType == ACT_REPEAT)
  8286.         {
  8287.             // ActionType is an IF or a LOOP.
  8288.             line_temp = line->mNextLine;  // line_temp is now this IF's or LOOP's action-line.
  8289.             // Update: Below is commented out because it's now impossible (since all scripts end in ACT_EXIT):
  8290.             //if (line_temp == NULL) // This is an orphan IF/LOOP (has no action-line) at the end of the script.
  8291.             //    return line->PreparseError("Q"); // Placeholder. Formerly "This if-statement or loop has no action."
  8292.  
  8293.             // Other things rely on this check having been done, such as "if (line->mRelatedLine != NULL)":
  8294.             if (line_temp->mActionType == ACT_ELSE || line_temp->mActionType == ACT_BLOCK_END)
  8295.                 return line->PreparseError("Inappropriate line beneath IF or LOOP.");
  8296.  
  8297.             // We're checking for ATTR_LOOP_FILEPATTERN here to detect whether qualified commands enclosed
  8298.             // in a true file loop are allowed to omit their filename parameter:
  8299.             loop_type_file = ATTR_NONE;
  8300.             if (aLoopTypeFile == ATTR_LOOP_FILEPATTERN || line->mAttribute == ATTR_LOOP_FILEPATTERN)
  8301.                 // i.e. if either one is a file-loop, that's enough to establish
  8302.                 // the fact that we're in a file loop.
  8303.                 loop_type_file = ATTR_LOOP_FILEPATTERN;
  8304.             else if (aLoopTypeFile == ATTR_LOOP_UNKNOWN || line->mAttribute == ATTR_LOOP_UNKNOWN)
  8305.                 // ATTR_LOOP_UNKNOWN takes precedence over ATTR_LOOP_NORMAL because
  8306.                 // we can't be sure if we're in a file loop, but it's correct to
  8307.                 // assume that we are (otherwise, unwarranted syntax errors may be reported
  8308.                 // later on in here).
  8309.                 loop_type_file = ATTR_LOOP_UNKNOWN;
  8310.             else if (aLoopTypeFile == ATTR_LOOP_NORMAL || line->mAttribute == ATTR_LOOP_NORMAL)
  8311.                 loop_type_file = ATTR_LOOP_NORMAL;
  8312.  
  8313.             // The section is the same as above except for registry vs. file loops:
  8314.             loop_type_reg = ATTR_NONE;
  8315.             if (aLoopTypeReg == ATTR_LOOP_REG || line->mAttribute == ATTR_LOOP_REG)
  8316.                 loop_type_reg = ATTR_LOOP_REG;
  8317.             else if (aLoopTypeReg == ATTR_LOOP_UNKNOWN || line->mAttribute == ATTR_LOOP_UNKNOWN)
  8318.                 loop_type_reg = ATTR_LOOP_UNKNOWN;
  8319.             else if (aLoopTypeReg == ATTR_LOOP_NORMAL || line->mAttribute == ATTR_LOOP_NORMAL)
  8320.                 loop_type_reg = ATTR_LOOP_NORMAL;
  8321.  
  8322.             // Same as above except for READ-FILE loops:
  8323.             loop_type_read = ATTR_NONE;
  8324.             if (aLoopTypeRead == ATTR_LOOP_READ_FILE || line->mAttribute == ATTR_LOOP_READ_FILE)
  8325.                 loop_type_read = ATTR_LOOP_READ_FILE;
  8326.             else if (aLoopTypeRead == ATTR_LOOP_UNKNOWN || line->mAttribute == ATTR_LOOP_UNKNOWN)
  8327.                 loop_type_read = ATTR_LOOP_UNKNOWN;
  8328.             else if (aLoopTypeRead == ATTR_LOOP_NORMAL || line->mAttribute == ATTR_LOOP_NORMAL)
  8329.                 loop_type_read = ATTR_LOOP_NORMAL;
  8330.  
  8331.             // Same as above except for PARSING loops:
  8332.             loop_type_parse = ATTR_NONE;
  8333.             if (aLoopTypeParse == ATTR_LOOP_PARSE || line->mAttribute == ATTR_LOOP_PARSE)
  8334.                 loop_type_parse = ATTR_LOOP_PARSE;
  8335.             else if (aLoopTypeParse == ATTR_LOOP_UNKNOWN || line->mAttribute == ATTR_LOOP_UNKNOWN)
  8336.                 loop_type_parse = ATTR_LOOP_UNKNOWN;
  8337.             else if (aLoopTypeParse == ATTR_LOOP_NORMAL || line->mAttribute == ATTR_LOOP_NORMAL)
  8338.                 loop_type_parse = ATTR_LOOP_NORMAL;
  8339.  
  8340.             // Check if the IF's action-line is something we want to recurse.  UPDATE: Always
  8341.             // recurse because other line types, such as Goto and Gosub, need to be preparsed
  8342.             // by this function even if they are the single-line actions of an IF or an ELSE:
  8343.             // Recurse this line rather than the next because we want
  8344.             // the called function to recurse again if this line is a ACT_BLOCK_BEGIN
  8345.             // or is itself an IF:
  8346.             line_temp = PreparseIfElse(line_temp, ONLY_ONE_LINE, loop_type_file, loop_type_reg, loop_type_read
  8347.                 , loop_type_parse);
  8348.             // If not end-of-script or error, line_temp is now either:
  8349.             // 1) If this if's/loop's action was a BEGIN_BLOCK: The line after the end of the block.
  8350.             // 2) If this if's/loop's action was another IF or LOOP:
  8351.             //    a) the line after that if's else's action; or (if it doesn't have one):
  8352.             //    b) the line after that if's/loop's action
  8353.             // 3) If this if's/loop's action was some single-line action: the line after that action.
  8354.             // In all of the above cases, line_temp is now the line where we
  8355.             // would expect to find an ELSE for this IF, if it has one.
  8356.  
  8357.             // Now the above has ensured that line_temp is this line's else, if it has one.
  8358.             // Note: line_temp will be NULL if the end of the script has been reached.
  8359.             // UPDATE: That can't happen now because all scripts end in ACT_EXIT:
  8360.             if (line_temp == NULL) // Error or end-of-script was reached.
  8361.                 return NULL;
  8362.  
  8363.             // Seems best to keep this check for mainability because changes to other checks can impact
  8364.             // whether this check will ever be "true":
  8365.             if (line->mRelatedLine != NULL)
  8366.                 return line->PreparseError("Q"); // Placeholder since it shouldn't happen.  Formerly "This if-statement or LOOP unexpectedly already had an ELSE or end-point."
  8367.             // Set it to the else's action, rather than the else itself, since the else itself
  8368.             // is never needed during execution.  UPDATE: No, instead set it to the ELSE itself
  8369.             // (if it has one) since we jump here at runtime when the IF is finished (whether
  8370.             // it's condition was true or false), thus skipping over any nested IF's that
  8371.             // aren't in blocks beneath it.  If there's no ELSE, the below value serves as
  8372.             // the jumppoint we go to when the if-statement is finished.  Example:
  8373.             // if x
  8374.             //   if y
  8375.             //     if z
  8376.             //       action1
  8377.             //     else
  8378.             //       action2
  8379.             // action3
  8380.             // x's jumppoint should be action3 so that all the nested if's
  8381.             // under the first one can be skipped after the "if x" line is recursively
  8382.             // evaluated.  Because of this behavior, all IFs will have a related line
  8383.             // with the possibly exception of the very last if-statement in the script
  8384.             // (which is possible only if the script doesn't end in a Return or Exit).
  8385.             line->mRelatedLine = line_temp;  // Even if <line> is a LOOP and line_temp and else?
  8386.  
  8387.             // Even if aMode == ONLY_ONE_LINE, an IF and its ELSE count as a single
  8388.             // statement (one line) due to its very nature (at least for this purpose),
  8389.             // so always continue on to evaluate the IF's ELSE, if present:
  8390.             if (line_temp->mActionType == ACT_ELSE)
  8391.             {
  8392.                 if (line->mActionType == ACT_LOOP || line->mActionType == ACT_REPEAT)
  8393.                 {
  8394.                      // this can't be our else, so let the caller handle it.
  8395.                     if (aMode != ONLY_ONE_LINE)
  8396.                         // This ELSE was encountered while sequentially scanning the contents
  8397.                         // of a block or at the otuermost nesting layer.  More thought is required
  8398.                         // to verify this is correct.  UPDATE: This check is very old and I haven't
  8399.                         // found a case that can produce it yet, but until proven otherwise its safer
  8400.                         // to assume it's possible.
  8401.                         return line_temp->PreparseError(ERR_ELSE_WITH_NO_IF);
  8402.                     // Let the caller handle this else, since it can't be ours:
  8403.                     return line_temp;
  8404.                 }
  8405.                 // Now use line vs. line_temp to hold the new values, so that line_temp
  8406.                 // stays as a marker to the ELSE line itself:
  8407.                 line = line_temp->mNextLine;  // Set it to the else's action line.
  8408.                 // Update: The following is now impossible because all scripts end in ACT_EXIT.
  8409.                 // Thus, it's commented out:
  8410.                 //if (line == NULL) // An else with no action.
  8411.                 //    return line_temp->PreparseError("Q"); // Placeholder since impossible. Formerly "This ELSE has no action."
  8412.                 if (line->mActionType == ACT_ELSE || line->mActionType == ACT_BLOCK_END)
  8413.                     return line_temp->PreparseError("Inappropriate line beneath ELSE.");
  8414.                 // Assign to line rather than line_temp:
  8415.                 line = PreparseIfElse(line, ONLY_ONE_LINE, aLoopTypeFile, aLoopTypeReg, aLoopTypeRead
  8416.                     , aLoopTypeParse);
  8417.                 if (line == NULL)
  8418.                     return NULL; // Error or end-of-script.
  8419.                 // Set this ELSE's jumppoint.  This is similar to the jumppoint set for
  8420.                 // an ELSEless IF, so see related comments above:
  8421.                 line_temp->mRelatedLine = line;
  8422.             }
  8423.             else // line doesn't have an else, so just continue processing from line_temp's position
  8424.                 line = line_temp;
  8425.  
  8426.             // Both cases above have ensured that line is now the first line beyond the
  8427.             // scope of the if-statement and that of any ELSE it may have.
  8428.  
  8429.             if (aMode == ONLY_ONE_LINE) // Return the next unprocessed line to the caller.
  8430.                 return line;
  8431.             // Otherwise, continue processing at line's new location:
  8432.             continue;
  8433.         } // ActionType is "IF".
  8434.  
  8435.         // Since above didn't continue, do the switch:
  8436.         char *line_raw_arg1 = LINE_RAW_ARG1; // Resolve only once to help reduce code size.
  8437.         char *line_raw_arg2 = LINE_RAW_ARG2; //
  8438.  
  8439.         switch (line->mActionType)
  8440.         {
  8441.         case ACT_BLOCK_BEGIN:
  8442.             line = PreparseIfElse(line->mNextLine, UNTIL_BLOCK_END, aLoopTypeFile, aLoopTypeReg, aLoopTypeRead
  8443.                 , aLoopTypeParse);
  8444.             // "line" is now either NULL due to an error, or the location of the END_BLOCK itself.
  8445.             if (line == NULL)
  8446.                 return NULL; // Error.
  8447.             break;
  8448.         case ACT_BLOCK_END:
  8449.             if (aMode == ONLY_ONE_LINE)
  8450.                  // Syntax error.  The caller would never expect this single-line to be an
  8451.                  // end-block.  UPDATE: I think this is impossible because callers only use
  8452.                  // aMode == ONLY_ONE_LINE when aStartingLine's ActionType is already
  8453.                  // known to be an IF or a BLOCK_BEGIN:
  8454.                 return line->PreparseError("Q"); // Placeholder (see above). Formerly "Unexpected end-of-block (single)."
  8455.             if (UNTIL_BLOCK_END)
  8456.                 // Return line rather than line->mNextLine because, if we're at the end of
  8457.                 // the script, it's up to the caller to differentiate between that condition
  8458.                 // and the condition where NULL is an error indicator.
  8459.                 return line;
  8460.             // Otherwise, we found an end-block we weren't looking for.  This should be
  8461.             // impossible since the block pre-parsing already balanced all the blocks?
  8462.             return line->PreparseError("Q"); // Placeholder (see above). Formerly "Unexpected end-of-block (multi)."
  8463.         case ACT_BREAK:
  8464.         case ACT_CONTINUE:
  8465.             if (!aLoopTypeFile && !aLoopTypeReg && !aLoopTypeRead && !aLoopTypeParse)
  8466.                 return line->PreparseError("Break/Continue must be enclosed by a Loop.");
  8467.             break;
  8468.  
  8469.         case ACT_GOTO:  // These two must be done here (i.e. *after* all the script lines have been added),
  8470.         case ACT_GOSUB: // so that labels both above and below each Gosub/Goto can be resolved.
  8471.             if (line->ArgHasDeref(1))
  8472.                 // Since the jump-point contains a deref, it must be resolved at runtime:
  8473.                 line->mRelatedLine = NULL;
  8474.             else
  8475.                 if (!line->GetJumpTarget(false))
  8476.                     return NULL; // Error was already displayed by called function.
  8477.             break;
  8478.  
  8479.         // These next 4 must also be done here (i.e. *after* all the script lines have been added),
  8480.         // so that labels both above and below this line can be resolved:
  8481.         case ACT_ONEXIT:
  8482.             if (*line_raw_arg1 && !line->ArgHasDeref(1))
  8483.                 if (   !(line->mAttribute = FindLabel(line_raw_arg1))   )
  8484.                     return line->PreparseError(ERR_NO_LABEL);
  8485.             break;
  8486.  
  8487.         case ACT_HOTKEY:
  8488.             if (   *line_raw_arg2 && !line->ArgHasDeref(2)
  8489.                 && !line->ArgHasDeref(1) && strnicmp(line_raw_arg1, "IfWin", 5)   ) // v1.0.42: Omit IfWinXX from validation.
  8490.                 if (   !(line->mAttribute = FindLabel(line_raw_arg2))   )
  8491.                     if (!Hotkey::ConvertAltTab(line_raw_arg2, true))
  8492.                         return line->PreparseError(ERR_NO_LABEL);
  8493.             break;
  8494.  
  8495.         case ACT_SETTIMER:
  8496.             if (!line->ArgHasDeref(1))
  8497.                 if (   !(line->mAttribute = FindLabel(line_raw_arg1))   )
  8498.                     return line->PreparseError(ERR_NO_LABEL);
  8499.             if (*line_raw_arg2 && !line->ArgHasDeref(2))
  8500.                 if (!Line::ConvertOnOff(line_raw_arg2) && !IsPureNumeric(line_raw_arg2, true) // v1.0.46.16: Allow negatives to support the new run-only-once mode.
  8501.                     && !line->mArg[1].is_expression) // v1.0.46.10: Don't consider expressions THAT CONTAIN NO VARIABLES OR FUNCTION-CALLS like "% 2*500" to be a syntax error.
  8502.                     return line->PreparseError(ERR_PARAM2_INVALID);
  8503.             break;
  8504.  
  8505.         case ACT_GROUPADD: // This must be done here because it relies on all other lines already having been added.
  8506.             if (*LINE_RAW_ARG4 && !line->ArgHasDeref(4))
  8507.             {
  8508.                 // If the label name was contained in a variable, that label is now resolved and cannot
  8509.                 // be changed.  This is in contrast to something like "Gosub, %MyLabel%" where a change in
  8510.                 // the value of MyLabel will change the behavior of the Gosub at runtime:
  8511.                 Label *label = FindLabel(LINE_RAW_ARG4);
  8512.                 if (!label)
  8513.                     return line->PreparseError(ERR_NO_LABEL);
  8514.                 line->mRelatedLine = (Line *)label; // The script loader has ensured that this can't be NULL.
  8515.                 // Can't do this because the current line won't be the launching point for the
  8516.                 // Gosub.  Instead, the launching point will be the GroupActivate rather than the
  8517.                 // GroupAdd, so it will be checked by the GroupActivate or not at all (since it's
  8518.                 // not that important in the case of a Gosub -- it's mostly for Goto's):
  8519.                 //return IsJumpValid(label->mJumpToLine);
  8520.             }
  8521.             break;
  8522.  
  8523.         case ACT_ELSE:
  8524.             // Should never happen because the part that handles the if's, above, should find
  8525.             // all the elses and handle them.  UPDATE: This happens if there's
  8526.             // an extra ELSE in this scope level that has no IF:
  8527.             return line->PreparseError(ERR_ELSE_WITH_NO_IF);
  8528.         } // switch()
  8529.  
  8530.         line = line->mNextLine; // If NULL due to physical end-of-script, the for-loop's condition will catch it.
  8531.         if (aMode == ONLY_ONE_LINE) // Return the next unprocessed line to the caller.
  8532.             // In this case, line shouldn't be (and probably can't be?) NULL because the line after
  8533.             // a single-line action shouldn't be the physical end of the script.  That's because
  8534.             // the loader has ensured that all scripts now end in ACT_EXIT.  And that final
  8535.             // ACT_EXIT should never be parsed here in ONLY_ONE_LINE mode because the only time
  8536.             // that mode is used is for the action of an IF, an ELSE, or possibly a LOOP.
  8537.             // In all of those cases, the final ACT_EXIT line in the script (which is explicitly
  8538.             // insertted by the loader) cannot be the line that was just processed by the
  8539.             // switch().  Therefore, the above assignment should not have set line to NULL
  8540.             // (which is good because NULL would probably be construed as "failure" by our
  8541.             // caller in this case):
  8542.             return line;
  8543.         // else just continue the for-loop at the new value of line.
  8544.     } // for()
  8545.  
  8546.     // End of script has been reached.  line is now NULL so don't dereference it.
  8547.  
  8548.     // If we were still looking for an EndBlock to match up with a begin, that's an error.
  8549.     // This indicates that the at least one BLOCK_BEGIN is missing a BLOCK_END.
  8550.     // However, since the blocks were already balanced by the block pre-parsing function,
  8551.     // this should be impossible unless the design of this function is flawed.
  8552.     if (aMode == UNTIL_BLOCK_END)
  8553. #ifdef _DEBUG
  8554.         return mLastLine->PreparseError("DEBUG: The script ended while a block was still open."); // This is a bug because the preparser already verified all blocks are balanced.
  8555. #else
  8556.         return NULL; // Shouldn't happen, so just return failure.
  8557. #endif
  8558.  
  8559.     // If we were told to process a single line, we were recursed and it should have returned above,
  8560.     // so it's an error here (can happen if we were called with aStartingLine == NULL?):
  8561.     if (aMode == ONLY_ONE_LINE)
  8562.         return mLastLine->PreparseError("Q"); // Placeholder since probably impossible.  Formerly "The script ended while an action was still expected."
  8563.  
  8564.     // Otherwise, return something non-NULL to indicate success to the top-level caller:
  8565.     return mLastLine;
  8566. }
  8567.  
  8568.  
  8569. //-------------------------------------------------------------------------------------
  8570.  
  8571. // Init static vars:
  8572. Line *Line::sLog[] = {NULL};  // Initialize all the array elements.
  8573. DWORD Line::sLogTick[]; // No initialization needed.
  8574. int Line::sLogNext = 0;  // Start at the first element.
  8575.  
  8576. #ifdef AUTOHOTKEYSC  // Reduces code size to omit things that are unused, and helps catch bugs at compile-time.
  8577.     char *Line::sSourceFile[1]; // No init needed.
  8578. #else
  8579.     char **Line::sSourceFile = NULL; // Init to NULL for use with realloc() and for maintainability.
  8580.     int Line::sMaxSourceFiles = 0;
  8581. #endif
  8582.     int Line::sSourceFileCount = 0; // Zero source files initially.  The main script will be the first.
  8583.  
  8584. char *Line::sDerefBuf = NULL;  // Buffer to hold the values of any args that need to be dereferenced.
  8585. size_t Line::sDerefBufSize = 0;
  8586. int Line::sLargeDerefBufs = 0; // Keeps track of how many large bufs exist on the call-stack, for the purpose of determining when to stop the buffer-freeing timer.
  8587. char *Line::sArgDeref[MAX_ARGS]; // No init needed.
  8588. Var *Line::sArgVar[MAX_ARGS]; // Same.
  8589.  
  8590.  
  8591. void Line::FreeDerefBufIfLarge()
  8592. {
  8593.     if (sDerefBufSize > LARGE_DEREF_BUF_SIZE)
  8594.     {
  8595.         // Freeing the buffer should be safe even if the script's current quasi-thread is in the middle
  8596.         // of executing a command, since commands are all designed to make only temporary use of the
  8597.         // deref buffer (they make copies of anything they need prior to calling MsgSleep() or anything
  8598.         // else that might pump messages and thus result in a call to us here).
  8599.         free(sDerefBuf); // The above size-check has ensured this is non-NULL.
  8600.         SET_S_DEREF_BUF(NULL, 0);
  8601.         --sLargeDerefBufs;
  8602.         if (!sLargeDerefBufs)
  8603.             KILL_DEREF_TIMER
  8604.     }
  8605.     //else leave the timer running because some other deref buffer in a recursed ExpandArgs() layer
  8606.     // is still waiting to be freed (even if it isn't, it should be harmless to keep the timer running
  8607.     // just in case, since each call to ExpandArgs() will reset/postpone the timer due to the script
  8608.     // having demonstrated that it isn't idle).
  8609. }
  8610.  
  8611.  
  8612.  
  8613. ResultType Line::ExecUntil(ExecUntilMode aMode, char **apReturnValue, Line **apJumpToLine)
  8614. // Start executing at "this" line, stop when aMode indicates.
  8615. // RECURSIVE: Handles all lines that involve flow-control.
  8616. // aMode can be UNTIL_RETURN, UNTIL_BLOCK_END, ONLY_ONE_LINE.
  8617. // Returns FAIL, OK, EARLY_RETURN, or EARLY_EXIT.
  8618. // apJumpToLine is a pointer to Line-ptr (handle), which is an output parameter.  If NULL,
  8619. // the caller is indicating it doesn't need this value, so it won't (and can't) be set by
  8620. // the called recursion layer.
  8621. {
  8622.     Line *unused_jump_to_line;
  8623.     Line *&caller_jump_to_line = apJumpToLine ? *apJumpToLine : unused_jump_to_line; // Simplifies code in other places.
  8624.     // Important to init, since most of the time it will keep this value.
  8625.     // Tells caller that no jump is required (default):
  8626.     caller_jump_to_line = NULL;
  8627.  
  8628.     // The benchmark improvement of having the following variables declared outside the loop rather than inside
  8629.     // is about 0.25%.  Since that is probably not even statistically significant, the only reason for declaring
  8630.     // them here is in case compilers other than MSVC++ 7.1 benefit more -- and because it's an old silly habit.
  8631.     __int64 loop_iteration;
  8632.     WIN32_FIND_DATA *loop_file;
  8633.     RegItemStruct *loop_reg_item;
  8634.     LoopReadFileStruct *loop_read_file;
  8635.     char *loop_field;
  8636.  
  8637.     Line *jump_to_line; // Don't use *apJumpToLine because it might not exist.
  8638.     Label *jump_to_label;  // For use with Gosub & Goto & GroupActivate.
  8639.     ResultType if_condition, result;
  8640.     LONG_OPERATION_INIT
  8641.  
  8642.     for (Line *line = this; line != NULL;)
  8643.     {
  8644.         // If a previous command (line) had the clipboard open, perhaps because it directly accessed
  8645.         // the clipboard via Var::Contents(), we close it here for performance reasons (see notes
  8646.         // in Clipboard::Open() for details):
  8647.         CLOSE_CLIPBOARD_IF_OPEN;
  8648.  
  8649.         // The below must be done at least when the keybd or mouse hook is active, but is currently
  8650.         // always done since it's a very low overhead call, and has the side-benefit of making
  8651.         // the app maximally responsive when the script is busy during high BatchLines.
  8652.         // This low-overhead call achieves at least two purposes optimally:
  8653.         // 1) Keyboard and mouse lag is minimized when the hook(s) are installed, since this single
  8654.         //    Peek() is apparently enough to route all pending input to the hooks (though it's inexplicable
  8655.         //    why calling MsgSleep(-1) does not achieve this goal, since it too does a Peek().
  8656.         //    Nevertheless, that is the testing result that was obtained: the mouse cursor lagged
  8657.         //    in tight script loops even when MsgSleep(-1) or (0) was called every 10ms or so.
  8658.         // 2) The app is maximally responsive while executing with a high or infinite BatchLines.
  8659.         // 3) Hotkeys are maximally responsive.  For example, if a user has game hotkeys, using
  8660.         //    a GetTickCount() method (which very slightly improves performance by cutting back on
  8661.         //    the number of Peek() calls) would introduce up to 10ms of delay before the hotkey
  8662.         //    finally takes effect.  10ms can be significant in games, where ping (latency) itself
  8663.         //    can sometimes be only 10 or 20ms. UPDATE: It looks like PeekMessage() yields CPU time
  8664.         //    automatically, similar to a Sleep(0), when our queue has no messages.  Since this would
  8665.         //    make scripts slow to a crawl, only do the Peek() every 5ms or so (though the timer
  8666.         //    granularity is 10ms on mosts OSes, so that's the true interval).
  8667.         // 4) Timed subroutines are run as consistently as possible (to help with this, a check
  8668.         //    similar to the below is also done for single commmands that take a long time, such
  8669.         //    as URLDownloadToFile, FileSetAttrib, etc.
  8670.         LONG_OPERATION_UPDATE
  8671.  
  8672.         // If interruptions are currently forbidden, it's our responsibility to check if the number
  8673.         // of lines that have been run since this quasi-thread started now indicate that
  8674.         // interruptibility should be reenabled.  But if UninterruptedLineCountMax is negative, don't
  8675.         // bother checking because this quasi-thread will stay non-interruptible until it finishes.
  8676.         // v1.0.38.04: If g.ThreadIsCritical==true, no need to check or accumulate g.UninterruptedLineCount
  8677.         // because the script is now in charge of this thread's interruptibility.
  8678.         if (!g.AllowThreadToBeInterrupted && !g.ThreadIsCritical && g_script.mUninterruptedLineCountMax > -1) // Ordered for short-circuit performance.
  8679.         {
  8680.             // Note that there is a timer that handles the UninterruptibleTime setting, so we don't
  8681.             // have handle that setting here.  But that timer is killed by the DISABLE_UNINTERRUPTIBLE
  8682.             // macro we call below.  This is because we don't want the timer to "fire" after we've
  8683.             // already met the conditions which allow interruptibility to be restored, because if
  8684.             // it did, it might interfere with the fact that some other code might already be using
  8685.             // g.AllowThreadToBeInterrupted again for its own purpose:
  8686.             if (g.UninterruptedLineCount > g_script.mUninterruptedLineCountMax)
  8687.                 MAKE_THREAD_INTERRUPTIBLE
  8688.             else
  8689.                 // Incrementing this unconditionally makes it a cruder measure than g.LinesPerCycle,
  8690.                 // but it seems okay to be less accurate for this purpose:
  8691.                 ++g.UninterruptedLineCount;
  8692.         }
  8693.  
  8694.         // The below handles the message-loop checking regardless of whether
  8695.         // aMode is ONLY_ONE_LINE (i.e. recursed) or not (i.e. we're using
  8696.         // the for-loop to execute the script linearly):
  8697.         if ((g.LinesPerCycle > -1 && g_script.mLinesExecutedThisCycle >= g.LinesPerCycle)
  8698.             || (g.IntervalBeforeRest > -1 && tick_now - g_script.mLastScriptRest >= (DWORD)g.IntervalBeforeRest))
  8699.             // Sleep in between batches of lines, like AutoIt, to reduce the chance that
  8700.             // a maxed CPU will interfere with time-critical apps such as games,
  8701.             // video capture, or video playback.  Note: MsgSleep() will reset
  8702.             // mLinesExecutedThisCycle for us:
  8703.             MsgSleep(10);  // Don't use INTERVAL_UNSPECIFIED, which wouldn't sleep at all if there's a msg waiting.
  8704.  
  8705.         // At this point, a pause may have been triggered either by the above MsgSleep()
  8706.         // or due to the action of a command (e.g. Pause, or perhaps tray menu "pause" was selected during Sleep):
  8707.         while (g.IsPaused) // Benches slightly faster than while() for some reason. Also, an initial "if (g.IsPaused)" prior to the loop doesn't make it any faster.
  8708.             MsgSleep(INTERVAL_UNSPECIFIED);  // Must check often to periodically run timed subroutines.
  8709.  
  8710.         // Do these only after the above has had its opportunity to spend a significant amount
  8711.         // of time doing what it needed to do.  i.e. do these immediately before the line will actually
  8712.         // be run so that the time it takes to run will be reflected in the ListLines log.
  8713.         g_script.mCurrLine = line;  // Simplifies error reporting when we get deep into function calls.
  8714.  
  8715.         // Maintain a circular queue of the lines most recently executed:
  8716.         sLog[sLogNext] = line; // The code actually runs faster this way than if this were combined with the above.
  8717.         // Get a fresh tick in case tick_now is out of date.  Strangely, it takes benchmarks 3% faster
  8718.         // on my system with this line than without it, but that's probably just a quirk of the build
  8719.         // or the CPU's caching.  It was already shown previously that the released version of 1.0.09
  8720.         // was almost 2% faster than an early version of this version (yet even now, that prior version
  8721.         // benchmarks slower than this one, which I can't explain).
  8722.         sLogTick[sLogNext++] = GetTickCount();  // Incrementing here vs. separately benches a little faster.
  8723.         if (sLogNext >= LINE_LOG_SIZE)
  8724.             sLogNext = 0;
  8725.  
  8726.         // Do this only after the opportunity to Sleep (above) has passed, because during
  8727.         // that sleep, a new subroutine might be launched which would likely overwrite the
  8728.         // deref buffer used for arg expansion, below:
  8729.         // Expand any dereferences contained in this line's args.
  8730.         // Note: Only one line at a time be expanded via the above function.  So be sure
  8731.         // to store any parts of a line that are needed prior to moving on to the next
  8732.         // line (e.g. control stmts such as IF and LOOP).  Also, don't expand
  8733.         // ACT_ASSIGN because a more efficient way of dereferencing may be possible
  8734.         // in that case:
  8735.         if (line->mActionType != ACT_ASSIGN)
  8736.         {
  8737.             result = line->ExpandArgs();
  8738.             // As of v1.0.31, ExpandArgs() will also return EARLY_EXIT if a function call inside one of this
  8739.             // line's expressions did an EXIT.
  8740.             if (result != OK)
  8741.                 return result; // In the case of FAIL: Abort the current subroutine, but don't terminate the app.
  8742.         }
  8743.  
  8744.         if (ACT_IS_IF(line->mActionType))
  8745.         {
  8746.             ++g_script.mLinesExecutedThisCycle;  // If and its else count as one line for this purpose.
  8747.             if_condition = line->EvaluateCondition();
  8748.             if (if_condition == FAIL)
  8749.                 return FAIL;
  8750.             if (if_condition == CONDITION_TRUE)
  8751.             {
  8752.                 // line->mNextLine has already been verified non-NULL by the pre-parser, so
  8753.                 // this dereference is safe:
  8754.                 result = line->mNextLine->ExecUntil(ONLY_ONE_LINE, apReturnValue, &jump_to_line);
  8755.                 if (jump_to_line == line)
  8756.                     // Since this IF's ExecUntil() encountered a Goto whose target is the IF
  8757.                     // itself, continue with the for-loop without moving to a different
  8758.                     // line.  Also: stay in this recursion layer even if aMode == ONLY_ONE_LINE
  8759.                     // because we don't want the caller handling it because then it's cleanup
  8760.                     // to jump to its end-point (beyond its own and any unowned elses) won't work.
  8761.                     // Example:
  8762.                     // if x  <-- If this layer were to do it, its own else would be unexpectedly encountered.
  8763.                     //    label1:
  8764.                     //    if y  <-- We want this statement's layer to handle the goto.
  8765.                     //       goto, label1
  8766.                     //    else
  8767.                     //       ...
  8768.                     // else
  8769.                     //   ...
  8770.                     continue;
  8771.                 if (aMode == ONLY_ONE_LINE)
  8772.                 {
  8773.                     // When jump_to_line!=NULL, the above call to ExecUntil() told us to jump somewhere.
  8774.                     // But since we're in ONLY_ONE_LINE mode, our caller must handle it because only it knows how
  8775.                     // to extricate itself from whatever it's doing:
  8776.                     caller_jump_to_line = jump_to_line; // Tell the caller to handle this jump (if applicable). jump_to_line==NULL is ok.
  8777.                     return result;
  8778.                 }
  8779.                 if (result == FAIL || result == EARLY_RETURN || result == EARLY_EXIT
  8780.                     || result == LOOP_BREAK || result == LOOP_CONTINUE)
  8781.                     // EARLY_RETURN can occur if this if's action was a block, and that block
  8782.                     // contained a RETURN, or if this if's only action is RETURN.  It can't
  8783.                     // occur if we just executed a Gosub, because that Gosub would have been
  8784.                     // done from a deeper recursion layer (and executing a Gosub in
  8785.                     // ONLY_ONE_LINE mode can never return EARLY_RETURN).
  8786.                     return result;
  8787.                 // Now this if-statement, including any nested if's and their else's,
  8788.                 // has been fully evaluated by the recusion above.  We must jump to
  8789.                 // the end of this if-statement to get to the right place for
  8790.                 // execution to resume.  UPDATE: Or jump to the goto target if the
  8791.                 // call to ExecUntil told us to do that instead:
  8792.                 if (jump_to_line != NULL && jump_to_line->mParentLine != line->mParentLine)
  8793.                 {
  8794.                     caller_jump_to_line = jump_to_line; // Tell the caller to handle this jump.
  8795.                     return OK;
  8796.                 }
  8797.                 if (jump_to_line != NULL) // jump to where the caller told us to go, rather than the end of IF.
  8798.                     line = jump_to_line;
  8799.                 else // Do the normal clean-up for an IF statement:
  8800.                 {
  8801.                     // Set line to be either the IF's else or the end of the if-stmt:
  8802.                     if (   !(line = line->mRelatedLine)   )
  8803.                         // The preparser has ensured that the only time this can happen is when
  8804.                         // the end of the script has been reached (i.e. this if-statement
  8805.                         // has no else and it's the last statement in the script):
  8806.                         return OK;
  8807.                     if (line->mActionType == ACT_ELSE)
  8808.                         line = line->mRelatedLine;
  8809.                         // Now line is the ELSE's "I'm finished" jump-point, which is where
  8810.                         // we want to be.  If line is now NULL, it will be caught when this
  8811.                         // loop iteration is ended by the "continue" stmt below.  UPDATE:
  8812.                         // it can't be NULL since all scripts now end in ACT_EXIT.
  8813.                     // else the IF had NO else, so we're already at the IF's "I'm finished" jump-point.
  8814.                 }
  8815.             }
  8816.             else // if_condition == CONDITION_FALSE
  8817.             {
  8818.                 if (   !(line = line->mRelatedLine)   )  // Set to IF's related line.
  8819.                     // The preparser has ensured that this can only happen if the end of the script
  8820.                     // has been reached.  UPDATE: Probably can't happen anymore since all scripts
  8821.                     // are now provided with a terminating ACT_EXIT:
  8822.                     return OK;
  8823.                 if (line->mActionType != ACT_ELSE && aMode == ONLY_ONE_LINE)
  8824.                     // Since this IF statement has no ELSE, and since it was executed
  8825.                     // in ONLY_ONE_LINE mode, the IF-ELSE statement, which counts as
  8826.                     // one line for the purpose of ONLY_ONE_LINE mode, has finished:
  8827.                     return OK;
  8828.                 if (line->mActionType == ACT_ELSE) // This IF has an else.
  8829.                 {
  8830.                     // Preparser has ensured that every ELSE has a non-NULL next line:
  8831.                     result = line->mNextLine->ExecUntil(ONLY_ONE_LINE, apReturnValue, &jump_to_line);
  8832.                     if (aMode == ONLY_ONE_LINE)
  8833.                     {
  8834.                         // When jump_to_line!=NULL, the above call to ExecUntil() told us to jump somewhere.
  8835.                         // But since we're in ONLY_ONE_LINE mode, our caller must handle it because only it knows how
  8836.                         // to extricate itself from whatever it's doing:
  8837.                         caller_jump_to_line = jump_to_line; // Tell the caller to handle this jump (if applicable). jump_to_line==NULL is ok.
  8838.                         return result;
  8839.                     }
  8840.                     if (result == FAIL || result == EARLY_RETURN || result == EARLY_EXIT
  8841.                         || result == LOOP_BREAK || result == LOOP_CONTINUE)
  8842.                         return result;
  8843.                     if (jump_to_line != NULL && jump_to_line->mParentLine != line->mParentLine)
  8844.                     {
  8845.                         caller_jump_to_line = jump_to_line; // Tell the caller to handle this jump.
  8846.                         return OK;
  8847.                     }
  8848.                     if (jump_to_line != NULL)
  8849.                         // jump to where the called function told us to go, rather than the end of our ELSE.
  8850.                         line = jump_to_line;
  8851.                     else // Do the normal clean-up for an ELSE statement.
  8852.                         line = line->mRelatedLine;
  8853.                         // Now line is the ELSE's "I'm finished" jump-point, which is where
  8854.                         // we want to be.  If line is now NULL, it will be caught when this
  8855.                         // loop iteration is ended by the "continue" stmt below.  UPDATE:
  8856.                         // it can't be NULL since all scripts now end in ACT_EXIT.
  8857.                     // else the IF had NO else, so we're already at the IF's "I'm finished" jump-point.
  8858.                 }
  8859.                 // else the IF had NO else, so we're already at the IF's "I'm finished" jump-point.
  8860.             } // if_condition == CONDITION_FALSE
  8861.             continue; // Let the for-loop process the new location specified by <line>.
  8862.         } // if (ACT_IS_IF)
  8863.  
  8864.         // If above didn't continue, it's not an IF, so handle the other
  8865.         // flow-control types:
  8866.         switch (line->mActionType)
  8867.         {
  8868.         case ACT_GOSUB:
  8869.             // A single gosub can cause an infinite loop if misused (i.e. recusive gosubs),
  8870.             // so be sure to do this to prevent the program from hanging:
  8871.             ++g_script.mLinesExecutedThisCycle;
  8872.             if (   !(jump_to_label = (Label *)line->mRelatedLine)   )
  8873.                 // The label is a dereference, otherwise it would have been resolved at load-time.
  8874.                 // So send true because we don't want to update its mRelatedLine.  This is because
  8875.                 // we want to resolve the label every time through the loop in case the variable
  8876.                 // that contains the label changes, e.g. Gosub, %MyLabel%
  8877.                 if (   !(jump_to_label = line->GetJumpTarget(true))   )
  8878.                     return FAIL; // Error was already displayed by called function.
  8879.             // I'm pretty sure it's not valid for this call to ExecUntil() to tell us to jump
  8880.             // somewhere, because the called function, or a layer even deeper, should handle
  8881.             // the goto prior to returning to us?  So the last parameter is omitted:
  8882.             result = jump_to_label->Execute();
  8883.             // Must do these return conditions in this specific order:
  8884.             if (result == FAIL || result == EARLY_EXIT)
  8885.                 return result;
  8886.             if (aMode == ONLY_ONE_LINE)
  8887.                 // This Gosub doesn't want its caller to know that the gosub's
  8888.                 // subroutine returned early:
  8889.                 return (result == EARLY_RETURN) ? OK : result;
  8890.             // If the above didn't return, the subroutine finished successfully and
  8891.             // we should now continue on with the line after the Gosub:
  8892.             line = line->mNextLine;
  8893.             continue;  // Resume looping starting at the above line.  "continue" is actually slight faster than "break" in these cases.
  8894.  
  8895.         case ACT_GOTO:
  8896.             // A single goto can cause an infinite loop if misused, so be sure to do this to
  8897.             // prevent the program from hanging:
  8898.             ++g_script.mLinesExecutedThisCycle;
  8899.             if (   !(jump_to_label = (Label *)line->mRelatedLine)   )
  8900.                 // The label is a dereference, otherwise it would have been resolved at load-time.
  8901.                 // So send true because we don't want to update its mRelatedLine.  This is because
  8902.                 // we want to resolve the label every time through the loop in case the variable
  8903.                 // that contains the label changes, e.g. Gosub, %MyLabel%
  8904.                 if (   !(jump_to_label = line->GetJumpTarget(true))   )
  8905.                     return FAIL; // Error was already displayed by called function.
  8906.             // Now that the Goto is certain to occur:
  8907.             g.CurrentLabel = jump_to_label; // v1.0.46.16: Support A_ThisLabel.
  8908.             // One or both of these lines can be NULL.  But the preparser should have
  8909.             // ensured that all we need to do is a simple compare to determine
  8910.             // whether this Goto should be handled by this layer or its caller
  8911.             // (i.e. if this Goto's target is not in our nesting level, it MUST be the
  8912.             // caller's responsibility to either handle it or pass it on to its
  8913.             // caller).
  8914.             if (aMode == ONLY_ONE_LINE || line->mParentLine != jump_to_label->mJumpToLine->mParentLine)
  8915.             {
  8916.                 caller_jump_to_line = jump_to_label->mJumpToLine; // Tell the caller to handle this jump.
  8917.                 return OK;
  8918.             }
  8919.             // Otherwise, we will handle this Goto since it's in our nesting layer:
  8920.             line = jump_to_label->mJumpToLine;
  8921.             continue;  // Resume looping starting at the above line.  "continue" is actually slight faster than "break" in these cases.
  8922.  
  8923.         case ACT_GROUPACTIVATE: // Similar to ACT_GOSUB, which is why this section is here rather than in Perform().
  8924.         {
  8925.             ++g_script.mLinesExecutedThisCycle; // Always increment for GroupActivate.
  8926.             WinGroup *group;
  8927.             if (   !(group = (WinGroup *)mAttribute)   )
  8928.                 group = g_script.FindGroup(ARG1);
  8929.             result = OK; // Set default.
  8930.             if (group)
  8931.             {
  8932.                 // Note: This will take care of DoWinDelay if needed:
  8933.                 group->Activate(*ARG2 && !stricmp(ARG2, "R"), NULL, &jump_to_label);
  8934.                 if (jump_to_label)
  8935.                 {
  8936.                     if (!line->IsJumpValid(*jump_to_label))
  8937.                         // This check probably isn't necessary since IsJumpValid() is mostly
  8938.                         // for Goto's.  But just in case the gosub's target label is some
  8939.                         // crazy place:
  8940.                         return FAIL;
  8941.                     // This section is just like the Gosub code above, so maintain them together.
  8942.                     result = jump_to_label->Execute();
  8943.                     if (result == FAIL || result == EARLY_EXIT)
  8944.                         return result;
  8945.                 }
  8946.             }
  8947.             //else no such group, so just proceed.
  8948.             if (aMode == ONLY_ONE_LINE)  // v1.0.45: These two lines were moved here from above to provide proper handling for GroupActivate that lacks a jump/gosub and that lies directly beneath an IF or ELSE.
  8949.                 return (result == EARLY_RETURN) ? OK : result;
  8950.             line = line->mNextLine;
  8951.             continue;  // Resume looping starting at the above line.  "continue" is actually slight faster than "break" in these cases.
  8952.         }
  8953.  
  8954.         case ACT_RETURN:
  8955.             // Although a return is really just a kind of block-end, keep it separate
  8956.             // because when a return is encountered inside a block, it has a double function:
  8957.             // to first break out of all enclosing blocks and then return from the gosub.
  8958.             // NOTE: The return's ARG1 expression has been evaluated by ExpandArgs() above,
  8959.             // which is desirable *even* if apReturnValue is NULL (i.e. the caller will be
  8960.             // ignoring the return value) in case the return's expression calls a function
  8961.             // which has side-effects.  For example, "return LogThisEvent()".
  8962.             if (apReturnValue) // Caller wants the return value.
  8963.                 *apReturnValue = ARG1; // This sets it to blank if this return lacks an arg.
  8964.             //else the return value, if any, is discarded.
  8965.             // Don't count returns against the total since they should be nearly instantaneous. UPDATE: even if
  8966.             // the return called a function (e.g. return fn()), that function's lines would have been added
  8967.             // to the total, so there doesn't seem much problem with not doing it here.
  8968.             //++g_script.mLinesExecutedThisCycle;
  8969.             if (aMode != UNTIL_RETURN)
  8970.                 // Tells the caller to return early if it's not the Gosub that directly
  8971.                 // brought us into this subroutine.  i.e. it allows us to escape from
  8972.                 // any number of nested blocks in order to get back out of their
  8973.                 // recursive layers and back to the place this RETURN has meaning
  8974.                 // to someone (at the right recursion layer):
  8975.                 return EARLY_RETURN;
  8976.             return OK;
  8977.  
  8978.         case ACT_BREAK:
  8979.             return LOOP_BREAK;
  8980.  
  8981.         case ACT_CONTINUE:
  8982.             return LOOP_CONTINUE;
  8983.  
  8984.         case ACT_LOOP:
  8985.         case ACT_REPEAT:
  8986.         {
  8987.             HKEY root_key_type; // For registry loops, this holds the type of root key, independent of whether it is local or remote.
  8988.             AttributeType attr = line->mAttribute;
  8989.             if (attr == ATTR_LOOP_REG)
  8990.                 root_key_type = RegConvertRootKey(ARG1);
  8991.             else if (ATTR_LOOP_IS_UNKNOWN_OR_NONE(attr))
  8992.             {
  8993.                 // Since it couldn't be determined at load-time (probably due to derefs),
  8994.                 // determine whether it's a file-loop, registry-loop or a normal/counter loop.
  8995.                 // But don't change the value of line->mAttribute because that's our
  8996.                 // indicator of whether this needs to be evaluated every time for
  8997.                 // this particular loop (since the nature of the loop can change if the
  8998.                 // contents of the variables dereferenced for this line change during runtime):
  8999.                 switch (line->mArgc)
  9000.                 {
  9001.                 case 0:
  9002.                     attr = ATTR_LOOP_NORMAL;
  9003.                     break;
  9004.                 case 1:
  9005.                     // Unlike at loadtime, allow it to be negative at runtime in case it was a variable
  9006.                     // reference that resolved to a negative number, to indicate that 0 iterations
  9007.                     // should be performed.  UPDATE: Also allow floating point numbers at runtime
  9008.                     // but not at load-time (since it doesn't make sense to have a literal floating
  9009.                     // point number as the iteration count, but a variable containing a pure float
  9010.                     // should be allowed):
  9011.                     if (IsPureNumeric(ARG1, true, true, true))
  9012.                         attr = ATTR_LOOP_NORMAL;
  9013.                     else
  9014.                     {
  9015.                         root_key_type = RegConvertRootKey(ARG1);
  9016.                         attr = root_key_type ? ATTR_LOOP_REG : ATTR_LOOP_FILEPATTERN;
  9017.                     }
  9018.                     break;
  9019.                 default: // 2 or more args.
  9020.                     if (!stricmp(ARG1, "Read"))
  9021.                         attr = ATTR_LOOP_READ_FILE;
  9022.                     // Note that a "Parse" loop is not allowed to have it's first param be a variable reference
  9023.                     // that resolves to the word "Parse" at runtime.  This is because the input variable would not
  9024.                     // have been resolved in this case (since the type of loop was unknown at load-time),
  9025.                     // and it would be complicated to have to add code for that, especially since there's
  9026.                     // virtually no conceivable use for allowing it be a variable reference.
  9027.                     else
  9028.                     {
  9029.                         root_key_type = RegConvertRootKey(ARG1);
  9030.                         attr = root_key_type ? ATTR_LOOP_REG : ATTR_LOOP_FILEPATTERN;
  9031.                     }
  9032.                 }
  9033.             }
  9034.  
  9035.             // HANDLE ANY ERROR CONDITIONS THAT CAN ABORT THE LOOP:
  9036.             FileLoopModeType file_loop_mode;
  9037.             bool recurse_subfolders;
  9038.             if (attr == ATTR_LOOP_FILEPATTERN)
  9039.             {
  9040.                 file_loop_mode = (line->mArgc <= 1) ? FILE_LOOP_FILES_ONLY : ConvertLoopMode(ARG2);
  9041.                 if (file_loop_mode == FILE_LOOP_INVALID)
  9042.                     return line->LineError(ERR_PARAM2_INVALID ERR_ABORT, FAIL, ARG2);
  9043.                 recurse_subfolders = (*ARG3 == '1' && !*(ARG3 + 1));
  9044.             }
  9045.             else if (attr == ATTR_LOOP_REG)
  9046.             {
  9047.                 file_loop_mode = (line->mArgc <= 2) ? FILE_LOOP_FILES_ONLY : ConvertLoopMode(ARG3);
  9048.                 if (file_loop_mode == FILE_LOOP_INVALID)
  9049.                     return line->LineError(ERR_PARAM3_INVALID ERR_ABORT, FAIL, ARG3);
  9050.                 recurse_subfolders = (*ARG4 == '1' && !*(ARG4 + 1));
  9051.             }
  9052.  
  9053.             // ONLY AFTER THE ABOVE IS IT CERTAIN THE LOOP WILL LAUNCH (i.e. there was no error or early return).
  9054.             // So only now is it safe to make changes to global things like g.mLoopIteration.
  9055.             bool continue_main_loop = false; // Init these output parameters prior to starting each type of loop.
  9056.             jump_to_line = NULL;             //
  9057.  
  9058.             // IN CASE THERE'S AN OUTER LOOP ENCLOSING THIS ONE, BACK UP THE A_LOOPXXX VARIABLES:
  9059.             loop_iteration = g.mLoopIteration;
  9060.             loop_file = g.mLoopFile;
  9061.             loop_reg_item = g.mLoopRegItem;
  9062.             loop_read_file = g.mLoopReadFile;
  9063.             loop_field = g.mLoopField;
  9064.  
  9065.             // INIT "A_INDEX" (one-based not zero-based). This is done here rather than in each PerformLoop()
  9066.             // function because it reduces code size and also because registry loops and file-pattern loops
  9067.             // can be intrinsically recursive (this is also related to the loop-recursion bugfix documented
  9068.             // for v1.0.20: fixes A_Index so that it doesn't wrongly reset to 0 inside recursive file-loops
  9069.             // and registry loops).
  9070.             g.mLoopIteration = 1;
  9071.  
  9072.             // PERFORM THE LOOP:
  9073.             switch ((size_t)attr)
  9074.             {
  9075.             case ATTR_LOOP_NORMAL: // Listed first for performance.
  9076.                 bool is_infinite; // "is_infinite" is more maintainable and future-proof than using LLONG_MAX to simulate an infinite loop. Plus it gives peace-of-mind and the LLONG_MAX method doesn't measurably improve benchmarks (nor does BOOL vs. bool).
  9077.                 __int64 iteration_limit;
  9078.                 if (line->mArgc > 0) // At least one parameter is present.
  9079.                 {
  9080.                     // Note that a 0 means infinite in AutoIt2 for the REPEAT command; so the following handles
  9081.                     // that too.
  9082.                     iteration_limit = ATOI64(ARG1); // If it's negative, zero iterations will be performed automatically.
  9083.                     is_infinite = (line->mActionType == ACT_REPEAT && !iteration_limit);
  9084.                 }
  9085.                 else // It's either ACT_REPEAT or an ACT_LOOP without parameters.
  9086.                 {
  9087.                     iteration_limit = 0; // Avoids debug-mode's "used without having been defined" (though it's merely passed as a parameter, not ever used in this case).
  9088.                     is_infinite = true;  // Override the default set earlier.
  9089.                 }
  9090.                 result = line->PerformLoop(apReturnValue, continue_main_loop, jump_to_line
  9091.                     , iteration_limit, is_infinite);
  9092.                 break;
  9093.             case ATTR_LOOP_PARSE:
  9094.                 // The phrase "csv" is unique enough since user can always rearrange the letters
  9095.                 // to do a literal parse using C, S, and V as delimiters:
  9096.                 if (stricmp(ARG3, "CSV"))
  9097.                     result = line->PerformLoopParse(apReturnValue, continue_main_loop, jump_to_line);
  9098.                 else
  9099.                     result = line->PerformLoopParseCSV(apReturnValue, continue_main_loop, jump_to_line);
  9100.                 break;
  9101.             case ATTR_LOOP_READ_FILE:
  9102.                 FILE *read_file;
  9103.                 if (*ARG2 && (read_file = fopen(ARG2, "r"))) // v1.0.47: Added check for "" to avoid debug-assertion failure while in debug mode (maybe it's bad to to open file "" in release mode too).
  9104.                 {
  9105.                     result = line->PerformLoopReadFile(apReturnValue, continue_main_loop, jump_to_line, read_file, ARG3);
  9106.                     fclose(read_file);
  9107.                 }
  9108.                 else
  9109.                     // The open of a the input file failed.  So just set result to OK since setting the
  9110.                     // ErrorLevel isn't supported with loops (since that seems like it would be an overuse
  9111.                     // of ErrorLevel, perhaps changing its value too often when the user would want
  9112.                     // it saved -- in any case, changing that now might break existing scripts).
  9113.                     result = OK;
  9114.                 break;
  9115.             case ATTR_LOOP_FILEPATTERN:
  9116.                 result = line->PerformLoopFilePattern(apReturnValue, continue_main_loop, jump_to_line, file_loop_mode
  9117.                     , recurse_subfolders, ARG1);
  9118.                 break;
  9119.             case ATTR_LOOP_REG:
  9120.                 // This isn't the most efficient way to do things (e.g. the repeated calls to
  9121.                 // RegConvertRootKey()), but it the simplest way for now.  Optimization can
  9122.                 // be done at a later time:
  9123.                 bool is_remote_registry;
  9124.                 HKEY root_key;
  9125.                 if (root_key = RegConvertRootKey(ARG1, &is_remote_registry)) // This will open the key if it's remote.
  9126.                 {
  9127.                     // root_key_type needs to be passed in order to support GetLoopRegKey():
  9128.                     result = line->PerformLoopReg(apReturnValue, continue_main_loop, jump_to_line, file_loop_mode
  9129.                         , recurse_subfolders, root_key_type, root_key, ARG2);
  9130.                     if (is_remote_registry)
  9131.                         RegCloseKey(root_key);
  9132.                 }
  9133.                 else
  9134.                     // The open of a remote key failed (we know it's remote otherwise it should have
  9135.                     // failed earlier rather than here).  So just set result to OK since no ErrorLevel
  9136.                     // setting is supported with loops (since that seems like it would be an overuse
  9137.                     // of ErrorLevel, perhaps changing its value too often when the user would want
  9138.                     // it saved.  But in any case, changing that now might break existing scripts).
  9139.                     result = OK;
  9140.                 break;
  9141.             }
  9142.  
  9143.             // RESTORE THE PREVIOUS A_LOOPXXX VARIABLES.  If there isn't an outer loop, this will set them
  9144.             // all to NULL/0, which is the most proper and also in keeping with historical behavior.
  9145.             // This backup/restore approach was adopted in v1.0.44.14 to simplify things and improve maintainability.
  9146.             // This change improved performance by only 1%, which isn't statistically significant.  More importantly,
  9147.             // it indirectly fixed the following bug:
  9148.             // When a "return" is executed inside a loop's body (and possibly an Exit or Fail too, but those are
  9149.             // covered more for simplicity and maintainability), the situations below require superglobals like
  9150.             // A_Index and A_LoopField to be restored for the outermost caller of ExecUntil():
  9151.             // 1) Var%A_Index% := func_that_returns_directly_from_inside_a_loop_body().
  9152.             //    The above happened because the return in the function's loop failed to restore A_Index for its
  9153.             //    caller because it had been designed to restore inter-line, not for intra-line activities like
  9154.             //    calling functions.
  9155.             // 2) A command that has expressions in two separate parameters and one of those parameters calls
  9156.             //    a function that returns directly from inside one of its loop bodies.
  9157.             //
  9158.             // This change was made feasible by making the A_LoopXXX attributes thread-specific, which prevents
  9159.             // interrupting threads from affecting the values our thread sees here.  So that change protects
  9160.             // against thread interruptions, and this backup/restore change here keeps the Loop variables in
  9161.             // sync with the current nesting level (braces, gosub, etc.)
  9162.             // 
  9163.             // The memory for structs like g.mLoopFile resides in the stack of an instance of PerformLoop(),
  9164.             // which is our caller or our caller's caller, etc.  In other words, it shouldn't be possible for
  9165.             // variables like g.mLoopFile to be non-NULL if there isn't a PerformLoop() beneath us in the stack.
  9166.             g.mLoopIteration = loop_iteration;
  9167.             g.mLoopFile = loop_file;
  9168.             g.mLoopRegItem = loop_reg_item;
  9169.             g.mLoopReadFile = loop_read_file;
  9170.             g.mLoopField = loop_field;
  9171.             // Above is done unconditionally (regardless of the value of "result") for simplicity and maintainability.
  9172.  
  9173.             if (result == FAIL || result == EARLY_RETURN || result == EARLY_EXIT)
  9174.                 return result;
  9175.             // else result can be LOOP_BREAK or OK, but not LOOP_CONTINUE.
  9176.             if (continue_main_loop) // It signaled us to do this:
  9177.                 continue;
  9178.  
  9179.             if (aMode == ONLY_ONE_LINE)
  9180.             {
  9181.                 // When jump_to_line!=NULL, the above call to ExecUntil() told us to jump somewhere.
  9182.                 // But since we're in ONLY_ONE_LINE mode, our caller must handle it because only it knows how
  9183.                 // to extricate itself from whatever it's doing:
  9184.                 caller_jump_to_line = jump_to_line; // Tell the caller to handle this jump (if any).  jump_to_line==NULL is ok.
  9185.                 // Return OK even if our result was LOOP_CONTINUE because we already handled the continue:
  9186.                 return OK;
  9187.             }
  9188.             if (jump_to_line)
  9189.             {
  9190.                 if (jump_to_line->mParentLine != line->mParentLine)
  9191.                 {
  9192.                     // Our caller must handle the jump if it doesn't share the same parent as the
  9193.                     // current line (i.e. it's not at the same nesting level) because that means
  9194.                     // the jump target is at a more shallow nesting level than where we are now:
  9195.                     caller_jump_to_line = jump_to_line; // Tell the caller to handle this jump (if applicable).
  9196.                     return OK;
  9197.                 }
  9198.                 // Since above didn't return, we're supposed to handle this jump.  So jump and then
  9199.                 // continue execution from there:
  9200.                 line = jump_to_line;
  9201.                 continue; // end this case of the switch().
  9202.             }
  9203.             // Since the above didn't return or break, either the loop has completed the specified
  9204.             // number of iterations or it was broken via the break command.  In either case, we jump
  9205.             // to the line after our loop's structure and continue there:
  9206.             line = line->mRelatedLine;
  9207.             continue;  // Resume looping starting at the above line.  "continue" is actually slight faster than "break" in these cases.
  9208.         } // case ACT_LOOP.
  9209.  
  9210.         case ACT_EXIT:
  9211.             // If this script has no hotkeys and hasn't activated one of the hooks, EXIT will cause the
  9212.             // the program itself to terminate.  Otherwise, it causes us to return from all blocks
  9213.             // and Gosubs (i.e. all the way out of the current subroutine, which was usually triggered
  9214.             // by a hotkey):
  9215.             if (IS_PERSISTENT)
  9216.                 return EARLY_EXIT;  // It's "early" because only the very end of the script is the "normal" exit.
  9217.                 // EARLY_EXIT needs to be distinct from FAIL for ExitApp() and AutoExecSection().
  9218.             else
  9219.                 // This has been tested and it does yield to the OS the error code indicated in ARG1,
  9220.                 // if present (otherwise it returns 0, naturally) as expected:
  9221.                 return g_script.ExitApp(EXIT_EXIT, NULL, ATOI(ARG1));
  9222.  
  9223.         case ACT_EXITAPP: // Unconditional exit.
  9224.             return g_script.ExitApp(EXIT_EXIT, NULL, ATOI(ARG1));
  9225.  
  9226.         case ACT_BLOCK_BEGIN:
  9227.             if (line->mAttribute) // This is the ACT_BLOCK_BEGIN that starts a function's body.
  9228.             {
  9229.                 // Any time this happens at runtime it means a function has been defined inside the
  9230.                 // auto-execute section, a block, or other place the flow of execution can reach
  9231.                 // on its own.  This is not considered a call to the function.  Instead, the entire
  9232.                 // body is just skipped over using this high performance method.  However, the function's
  9233.                 // opening brace will show up in ListLines, but that seems preferable to the performance
  9234.                 // overhead of explicitly removing it here.
  9235.                 line = line->mRelatedLine; // Resume execution at the line following this functions end-block.
  9236.                 continue;  // Resume looping starting at the above line.  "continue" is actually slight faster than "break" in these cases.
  9237.             }
  9238.             // Don't count block-begin/end against the total since they should be nearly instantaneous:
  9239.             //++g_script.mLinesExecutedThisCycle;
  9240.             // In this case, line->mNextLine is already verified non-NULL by the pre-parser:
  9241.             result = line->mNextLine->ExecUntil(UNTIL_BLOCK_END, apReturnValue, &jump_to_line);
  9242.             if (jump_to_line == line)
  9243.                 // Since this Block-begin's ExecUntil() encountered a Goto whose target is the
  9244.                 // block-begin itself, continue with the for-loop without moving to a different
  9245.                 // line.  Also: stay in this recursion layer even if aMode == ONLY_ONE_LINE
  9246.                 // because we don't want the caller handling it because then it's cleanup
  9247.                 // to jump to its end-point (beyond its own and any unowned elses) won't work.
  9248.                 // Example:
  9249.                 // if x  <-- If this layer were to do it, its own else would be unexpectedly encountered.
  9250.                 // label1:
  9251.                 // { <-- We want this statement's layer to handle the goto.
  9252.                 //    if y
  9253.                 //       goto, label1
  9254.                 //    else
  9255.                 //       ...
  9256.                 // }
  9257.                 // else
  9258.                 //   ...
  9259.                 continue;
  9260.             if (aMode == ONLY_ONE_LINE)
  9261.             {
  9262.                 // When jump_to_line!=NULL, the above call to ExecUntil() told us to jump somewhere.
  9263.                 // But since we're in ONLY_ONE_LINE mode, our caller must handle it because only it knows how
  9264.                 // to extricate itself from whatever it's doing:
  9265.                 caller_jump_to_line = jump_to_line; // Tell the caller to handle this jump (if applicable).  jump_to_line==NULL is ok.
  9266.                 return result;
  9267.             }
  9268.             if (result == FAIL || result == EARLY_RETURN || result == EARLY_EXIT
  9269.                 || result == LOOP_BREAK || result == LOOP_CONTINUE)
  9270.                 return result;
  9271.             // Currently, all blocks are normally executed in ONLY_ONE_LINE mode because
  9272.             // they are the direct actions of an IF, an ELSE, or a LOOP.  So the
  9273.             // above will already have returned except when the user has created a
  9274.             // generic, standalone block with no assciated control statement.
  9275.             // Check to see if we need to jump somewhere:
  9276.             if (jump_to_line != NULL && line->mParentLine != jump_to_line->mParentLine)
  9277.             {
  9278.                 caller_jump_to_line = jump_to_line; // Tell the caller to handle this jump (if applicable).
  9279.                 return OK;
  9280.             }
  9281.             if (jump_to_line != NULL) // jump to where the caller told us to go, rather than the end of our block.
  9282.                 line = jump_to_line;
  9283.             else // Just go to the end of our block and continue from there.
  9284.                 line = line->mRelatedLine;
  9285.                 // Now line is the line after the end of this block.  Can be NULL (end of script).
  9286.                 // UPDATE: It can't be NULL (not that it matters in this case) since the loader
  9287.                 // has ensured that all scripts now end in an ACT_EXIT.
  9288.             continue;  // Resume looping starting at the above line.  "continue" is actually slight faster than "break" in these cases.
  9289.  
  9290.         case ACT_BLOCK_END:
  9291.             // Don't count block-begin/end against the total since they should be nearly instantaneous:
  9292.             //++g_script.mLinesExecutedThisCycle;
  9293.             if (aMode != UNTIL_BLOCK_END)
  9294.                 // Rajat found a way for this to happen that basically amounts to this:
  9295.                 // If within a loop you gosub a label that is also inside of the block, and
  9296.                 // that label sometimes doesn't return (i.e. due to a missing "return" somewhere
  9297.                 // in its flow of control), the loop(s)'s block-end symbols will be encountered
  9298.                 // by the subroutine, and these symbols don't have meaning to it.  In other words,
  9299.                 // the subroutine has put us into a waiting-for-return state rather than a
  9300.                 // waiting-for-block-end state, so when block-end's are encountered, that is
  9301.                 // considered a runtime error:
  9302.                 return line->LineError("A \"return\" must be encountered prior to this \"}\"." ERR_ABORT);  // Former error msg was "Unexpected end-of-block (Gosub without Return?)."
  9303.             return OK; // It's the caller's responsibility to resume execution at the next line, if appropriate.
  9304.  
  9305.         // ACT_ELSE can happen when one of the cases in this switch failed to properly handle
  9306.         // aMode == ONLY_ONE_LINE.  But even if ever happens, it will just drop into the default
  9307.         // case, which will result in a FAIL (silent exit of thread) as an indicator of the problem.
  9308.         // So it's commented out:
  9309.         //case ACT_ELSE:
  9310.         //    // Shouldn't happen if the pre-parser and this function are designed properly?
  9311.         //    return line->LineError("Unexpected ELSE." ERR_ABORT);
  9312.  
  9313.         default:
  9314.             ++g_script.mLinesExecutedThisCycle;
  9315.             result = line->Perform();
  9316.             if (!result || aMode == ONLY_ONE_LINE)
  9317.                 // Thus, Perform() should be designed to only return FAIL if it's an error that would make
  9318.                 // it unsafe to proceed in the subroutine we're executing now:
  9319.                 return result; // Can be either OK or FAIL.
  9320.             line = line->mNextLine;
  9321.         } // switch()
  9322.     } // for each line
  9323.  
  9324.     // Above loop ended because the end of the script was reached.
  9325.     // At this point, it should be impossible for aMode to be
  9326.     // UNTIL_BLOCK_END because that would mean that the blocks
  9327.     // aren't all balanced (or there is a design flaw in this
  9328.     // function), but they are balanced because the preparser
  9329.     // verified that.  It should also be impossible for the
  9330.     // aMode to be ONLY_ONE_LINE because the function is only
  9331.     // called in that mode to execute the first action-line
  9332.     // beneath an IF or an ELSE, and the preparser has already
  9333.     // verified that every such IF and ELSE has a non-NULL
  9334.     // line after it.  Finally, aMode can be UNTIL_RETURN, but
  9335.     // that is normal mode of operation at the top level,
  9336.     // so probably shouldn't be considered an error.  For example,
  9337.     // if the script has no hotkeys, it is executed from its
  9338.     // first line all the way to the end.  For it not to have
  9339.     // a RETURN or EXIT is not an error.  UPDATE: The loader
  9340.     // now ensures that all scripts end in ACT_EXIT, so
  9341.     // this line should never be reached:
  9342.     return OK;
  9343. }
  9344.  
  9345.  
  9346.  
  9347. ResultType Line::EvaluateCondition() // __forceinline on this reduces benchmarks, probably because it reduces caching effectiveness by having code in the case that doesn't execute much in the benchmarks.
  9348. // Returns FAIL, CONDITION_TRUE, or CONDITION_FALSE.
  9349. {
  9350. #ifdef _DEBUG
  9351.     if (!ACT_IS_IF(mActionType))
  9352.         return LineError("DEBUG: EvaluateCondition() was called with a line that isn't a condition."
  9353.             ERR_ABORT);
  9354. #endif
  9355.  
  9356.     SymbolType var_is_pure_numeric, value_is_pure_numeric, value2_is_pure_numeric;
  9357.     int if_condition;
  9358.     char *cp;
  9359.  
  9360.     switch (mActionType)
  9361.     {
  9362.     case ACT_IFEXPR:
  9363.         // Use ATOF to support hex, float, and integer formats.  Also, explicitly compare to 0.0
  9364.         // to avoid truncation of double, which would result in a value such as 0.1 being seen
  9365.         // as false rather than true.  Fixed in v1.0.25.12 so that only the following are false:
  9366.         // 0
  9367.         // 0.0
  9368.         // 0x0
  9369.         // (variants of the above)
  9370.         // blank string
  9371.         // ... in other words, "if var" should be true if it contains a non-numeric string.
  9372.         cp = ARG1;  // It should help performance to resolve the ARG1 macro only once.
  9373.         if (!*cp)
  9374.             if_condition = false;
  9375.         else if (!IsPureNumeric(cp, true, false, true)) // i.e. a var containing all whitespace would be considered "true", since it's a non-blank string that isn't equal to 0.0.
  9376.             if_condition = true;
  9377.         else // It's purely numeric, not blank, and not all whitespace.
  9378.             if_condition = (ATOF(cp) != 0.0);
  9379.         break;
  9380.  
  9381.     // For ACT_IFWINEXIST and ACT_IFWINNOTEXIST, although we validate that at least one
  9382.     // of their window params is non-blank during load, it's okay at runtime for them
  9383.     // all to resolve to be blank (due to derefs), without an error being reported.
  9384.     // It's probably more flexible that way, and in any event WinExist() is equipped to
  9385.     // handle all-blank params:
  9386.     case ACT_IFWINEXIST:
  9387.         // NULL-check this way avoids compiler warnings:
  9388.         if_condition = (WinExist(g, FOUR_ARGS, false, true) != NULL);
  9389.         break;
  9390.     case ACT_IFWINNOTEXIST:
  9391.         if_condition = !WinExist(g, FOUR_ARGS, false, true); // Seems best to update last-used even here.
  9392.         break;
  9393.     case ACT_IFWINACTIVE:
  9394.         if_condition = (WinActive(g, FOUR_ARGS, true) != NULL);
  9395.         break;
  9396.     case ACT_IFWINNOTACTIVE:
  9397.         if_condition = !WinActive(g, FOUR_ARGS, true);
  9398.         break;
  9399.  
  9400.     case ACT_IFEXIST:
  9401.         if_condition = DoesFilePatternExist(ARG1);
  9402.         break;
  9403.     case ACT_IFNOTEXIST:
  9404.         if_condition = !DoesFilePatternExist(ARG1);
  9405.         break;
  9406.  
  9407.     case ACT_IFINSTRING:
  9408.     case ACT_IFNOTINSTRING:
  9409.         // The most common mode is listed first for performance:
  9410.         if_condition = g_strstr(ARG1, ARG2) != NULL; // To reduce code size, resolve large macro only once for both these commands.
  9411.         if (mActionType == ACT_IFNOTINSTRING)
  9412.             if_condition = !if_condition;
  9413.         break;
  9414.  
  9415.     case ACT_IFEQUAL:
  9416.     case ACT_IFNOTEQUAL:
  9417.         // For now, these seem to be the best rules to follow:
  9418.         // 1) If either one is non-empty and non-numeric, they're compared as strings.
  9419.         // 2) Otherwise, they're compared as numbers (with empty vars treated as zero).
  9420.         // In light of the above, two empty values compared to each other is the same as
  9421.         // "0 compared to 0".  e.g. if the clipboard is blank, the line "if clipboard ="
  9422.         // would be true.  However, the following are side-effects (are there any more?):
  9423.         // if var1 =    ; statement is true if var1 contains a literal zero (possibly harmful)
  9424.         // if var1 = 0  ; statement is true if var1 is blank (mostly harmless?)
  9425.         // if var1 !=   ; statement is false if var1 contains a literal zero (possibly harmful)
  9426.         // if var1 != 0 ; statement is false if var1 is blank (mostly harmless?)
  9427.         // In light of the above, the BOTH_ARE_NUMERIC macro has been altered to return
  9428.         // false if one of the items is a literal zero and the other is blank, so that
  9429.         // the two items will be compared as strings.  UPDATE: Altered it again because it
  9430.         // seems best to consider blanks to always be non-numeric (i.e. if either var is blank,
  9431.         // they will be compared as strings rather than as numbers):
  9432.  
  9433.         #undef DETERMINE_NUMERIC_TYPES
  9434.         #define DETERMINE_NUMERIC_TYPES \
  9435.             value_is_pure_numeric = IsPureNumeric(ARG2, true, false, true);\
  9436.             var_is_pure_numeric = IsPureNumeric(ARG1, true, false, true);
  9437.         #define DETERMINE_NUMERIC_TYPES2 \
  9438.             DETERMINE_NUMERIC_TYPES \
  9439.             value2_is_pure_numeric = IsPureNumeric(ARG3, true, false, true);
  9440.         #define IF_EITHER_IS_NON_NUMERIC if (!value_is_pure_numeric || !var_is_pure_numeric)
  9441.         #define IF_EITHER_IS_NON_NUMERIC2 if (!value_is_pure_numeric || !value2_is_pure_numeric || !var_is_pure_numeric)
  9442.         #undef IF_EITHER_IS_FLOAT
  9443.         #define IF_EITHER_IS_FLOAT if (value_is_pure_numeric == PURE_FLOAT || var_is_pure_numeric == PURE_FLOAT)
  9444.  
  9445.         if (mArgc > 1 && ARGVARRAW1 && ARGVARRAW1->IsBinaryClip() && ARGVARRAW2 && ARGVARRAW2->IsBinaryClip())
  9446.             if_condition = (ARGVARRAW1->Length() == ARGVARRAW2->Length()) // Accessing ARGVARRAW in all these places is safe due to the check mArgc > 1.
  9447.                 && !memcmp(ARGVARRAW1->Contents(), ARGVARRAW2->Contents(), ARGVARRAW1->Length());
  9448.         else
  9449.         {
  9450.             DETERMINE_NUMERIC_TYPES
  9451.             IF_EITHER_IS_NON_NUMERIC
  9452.                 if_condition = !g_strcmp(ARG1, ARG2);
  9453.             else IF_EITHER_IS_FLOAT  // It might perform better to only do float conversions & math when necessary.
  9454.                 if_condition = ATOF(ARG1) == ATOF(ARG2);
  9455.             else
  9456.                 if_condition = ATOI64(ARG1) == ATOI64(ARG2);
  9457.         }
  9458.         if (mActionType == ACT_IFNOTEQUAL)
  9459.             if_condition = !if_condition;
  9460.         break;
  9461.  
  9462.     case ACT_IFLESS:
  9463.         DETERMINE_NUMERIC_TYPES
  9464.         IF_EITHER_IS_NON_NUMERIC
  9465.             if_condition = g_strcmp(ARG1, ARG2) < 0;
  9466.         else IF_EITHER_IS_FLOAT  // It might perform better to only do float conversions & math when necessary.
  9467.             if_condition = ATOF(ARG1) < ATOF(ARG2);
  9468.         else
  9469.             if_condition = ATOI64(ARG1) < ATOI64(ARG2);
  9470.         break;
  9471.     case ACT_IFLESSOREQUAL:
  9472.         DETERMINE_NUMERIC_TYPES
  9473.         IF_EITHER_IS_NON_NUMERIC
  9474.             if_condition = g_strcmp(ARG1, ARG2) < 1;
  9475.         else IF_EITHER_IS_FLOAT  // It might perform better to only do float conversions & math when necessary.
  9476.             if_condition = ATOF(ARG1) <= ATOF(ARG2);
  9477.         else
  9478.             if_condition = ATOI64(ARG1) <= ATOI64(ARG2);
  9479.         break;
  9480.     case ACT_IFGREATER:
  9481.         DETERMINE_NUMERIC_TYPES
  9482.         IF_EITHER_IS_NON_NUMERIC
  9483.             if_condition = g_strcmp(ARG1, ARG2) > 0;
  9484.         else IF_EITHER_IS_FLOAT  // It might perform better to only do float conversions & math when necessary.
  9485.             if_condition = ATOF(ARG1) > ATOF(ARG2);
  9486.         else
  9487.             if_condition = ATOI64(ARG1) > ATOI64(ARG2);
  9488.         break;
  9489.     case ACT_IFGREATEROREQUAL:
  9490.         DETERMINE_NUMERIC_TYPES
  9491.         IF_EITHER_IS_NON_NUMERIC
  9492.             if_condition = g_strcmp(ARG1, ARG2) > -1;
  9493.         else IF_EITHER_IS_FLOAT  // It might perform better to only do float conversions & math when necessary.
  9494.             if_condition = ATOF(ARG1) >= ATOF(ARG2);
  9495.         else
  9496.             if_condition = ATOI64(ARG1) >= ATOI64(ARG2);
  9497.         break;
  9498.  
  9499.     case ACT_IFBETWEEN:
  9500.     case ACT_IFNOTBETWEEN:
  9501.         DETERMINE_NUMERIC_TYPES2
  9502.         IF_EITHER_IS_NON_NUMERIC2
  9503.         {
  9504.             if (g.StringCaseSense == SCS_INSENSITIVE) // The most common mode is listed first for performance.
  9505.                 if_condition = !(stricmp(ARG1, ARG2) < 0 || stricmp(ARG1, ARG3) > 0);
  9506.             else if (g.StringCaseSense == SCS_INSENSITIVE_LOCALE)
  9507.                 if_condition = lstrcmpi(ARG1, ARG2) > -1 && lstrcmpi(ARG1, ARG3) < 1;
  9508.             else  // case sensitive
  9509.                 if_condition = !(strcmp(ARG1, ARG2) < 0 || strcmp(ARG1, ARG3) > 0);
  9510.         }
  9511.         else IF_EITHER_IS_FLOAT
  9512.         {
  9513.             double arg1_as_float = ATOF(ARG1);
  9514.             if_condition = arg1_as_float >= ATOF(ARG2) && arg1_as_float <= ATOF(ARG3);
  9515.         }
  9516.         else
  9517.         {
  9518.             __int64 arg1_as_int64 = ATOI64(ARG1);
  9519.             if_condition = arg1_as_int64 >= ATOI64(ARG2) && arg1_as_int64 <= ATOI64(ARG3);
  9520.         }
  9521.         if (mActionType == ACT_IFNOTBETWEEN)
  9522.             if_condition = !if_condition;
  9523.         break;
  9524.  
  9525.     case ACT_IFIN:
  9526.     case ACT_IFNOTIN:
  9527.         if_condition = IsStringInList(ARG1, ARG2, true);
  9528.         if (mActionType == ACT_IFNOTIN)
  9529.             if_condition = !if_condition;
  9530.         break;
  9531.  
  9532.     case ACT_IFCONTAINS:
  9533.     case ACT_IFNOTCONTAINS:
  9534.         if_condition = IsStringInList(ARG1, ARG2, false);
  9535.         if (mActionType == ACT_IFNOTCONTAINS)
  9536.             if_condition = !if_condition;
  9537.         break;
  9538.  
  9539.     case ACT_IFIS:
  9540.     case ACT_IFISNOT:
  9541.     {
  9542.         char *cp;
  9543.         VariableTypeType variable_type = ConvertVariableTypeName(ARG2);
  9544.         if (variable_type == VAR_TYPE_INVALID)
  9545.         {
  9546.             // Type is probably a dereferenced variable that resolves to an invalid type name.
  9547.             // It seems best to make the condition false in these cases, rather than pop up
  9548.             // a runtime error dialog:
  9549.             if_condition = false;
  9550.             break;
  9551.         }
  9552.         switch(variable_type)
  9553.         {
  9554.         case VAR_TYPE_NUMBER:
  9555.             if_condition = IsPureNumeric(ARG1, true, false, true);  // Floats are defined as being numeric.
  9556.             break;
  9557.         case VAR_TYPE_INTEGER:
  9558.             if_condition = IsPureNumeric(ARG1, true, false, false);
  9559.             break;
  9560.         case VAR_TYPE_FLOAT:
  9561.             if_condition = IsPureNumeric(ARG1, true, false, true) == PURE_FLOAT;
  9562.             break;
  9563.         case VAR_TYPE_TIME:
  9564.         {
  9565.             SYSTEMTIME st;
  9566.             // Also insist on numeric, because even though YYYYMMDDToFileTime() will properly convert a
  9567.             // non-conformant string such as "2004.4", for future compatibility, we don't want to
  9568.             // report that such strings are valid times:
  9569.             if_condition = IsPureNumeric(ARG1, false, false, false) && YYYYMMDDToSystemTime(ARG1, st, true);
  9570.             break;
  9571.         }
  9572.         case VAR_TYPE_DIGIT:
  9573.             if_condition = true;
  9574.             for (cp = ARG1; *cp; ++cp)
  9575.                 if (!isdigit((UCHAR)*cp))
  9576.                 {
  9577.                     if_condition = false;
  9578.                     break;
  9579.                 }
  9580.             break;
  9581.         case VAR_TYPE_XDIGIT:
  9582.             cp = ARG1;
  9583.             if (!strnicmp(cp, "0x", 2)) // v1.0.44.09: Allow 0x prefix, which seems to do more good than harm (unlikely to break existing scripts).
  9584.                 cp += 2;
  9585.             if_condition = true;
  9586.             for (; *cp; ++cp)
  9587.                 if (!isxdigit((UCHAR)*cp))
  9588.                 {
  9589.                     if_condition = false;
  9590.                     break;
  9591.                 }
  9592.             break;
  9593.         case VAR_TYPE_ALNUM:
  9594.             // Like AutoIt3, the empty string is considered to be alphabetic, which is only slightly debatable.
  9595.             if_condition = true;
  9596.             for (cp = ARG1; *cp; ++cp)
  9597.                 if (!IsCharAlphaNumeric(*cp)) // Use this to better support chars from non-English languages.
  9598.                 {
  9599.                     if_condition = false;
  9600.                     break;
  9601.                 }
  9602.             break;
  9603.         case VAR_TYPE_ALPHA:
  9604.             // Like AutoIt3, the empty string is considered to be alphabetic, which is only slightly debatable.
  9605.             if_condition = true;
  9606.             for (cp = ARG1; *cp; ++cp)
  9607.                 if (!IsCharAlpha(*cp)) // Use this to better support chars from non-English languages.
  9608.                 {
  9609.                     if_condition = false;
  9610.                     break;
  9611.                 }
  9612.             break;
  9613.         case VAR_TYPE_UPPER:
  9614.             if_condition = true;
  9615.             for (cp = ARG1; *cp; ++cp)
  9616.                 if (!IsCharUpper(*cp)) // Use this to better support chars from non-English languages.
  9617.                 {
  9618.                     if_condition = false;
  9619.                     break;
  9620.                 }
  9621.             break;
  9622.         case VAR_TYPE_LOWER:
  9623.             if_condition = true;
  9624.             for (cp = ARG1; *cp; ++cp)
  9625.                 if (!IsCharLower(*cp)) // Use this to better support chars from non-English languages.
  9626.                 {
  9627.                     if_condition = false;
  9628.                     break;
  9629.                 }
  9630.             break;
  9631.         case VAR_TYPE_SPACE:
  9632.             if_condition = true;
  9633.             for (cp = ARG1; *cp; ++cp)
  9634.                 if (!isspace(*cp))
  9635.                 {
  9636.                     if_condition = false;
  9637.                     break;
  9638.                 }
  9639.             break;
  9640.         }
  9641.         if (mActionType == ACT_IFISNOT)
  9642.             if_condition = !if_condition;
  9643.         break;
  9644.     }
  9645.  
  9646.     case ACT_IFMSGBOX:
  9647.     {
  9648.         int mb_result = ConvertMsgBoxResult(ARG1);
  9649.         if (!mb_result)
  9650.             return LineError(ERR_PARAM1_INVALID ERR_ABORT, FAIL, ARG1);
  9651.         if_condition = (g.MsgBoxResult == mb_result);
  9652.         break;
  9653.     }
  9654.     default: // Should never happen, but return an error if it does.
  9655. #ifdef _DEBUG
  9656.         return LineError("DEBUG: EvaluateCondition(): Unhandled windowing action type." ERR_ABORT);
  9657. #else
  9658.         return FAIL;
  9659. #endif
  9660.     }
  9661.     return if_condition ? CONDITION_TRUE : CONDITION_FALSE;
  9662. }
  9663.  
  9664.  
  9665.  
  9666. ResultType Line::PerformLoop(char **apReturnValue, bool &aContinueMainLoop, Line *&aJumpToLine
  9667.     , __int64 aIterationLimit, bool aIsInfinite) // bool performs better than BOOL in current benchmarks for this.
  9668. // This performs much better (by at least 7%) as a function than as inline code, probably because
  9669. // it's only called to set up the loop, not each time through the loop.
  9670. {
  9671.     ResultType result;
  9672.     Line *jump_to_line;
  9673.     for (; aIsInfinite || g.mLoopIteration <= aIterationLimit; ++g.mLoopIteration)
  9674.     {
  9675.         // Execute once the body of the loop (either just one statement or a block of statements).
  9676.         // Preparser has ensured that every LOOP has a non-NULL next line.
  9677.         result = mNextLine->ExecUntil(ONLY_ONE_LINE, apReturnValue, &jump_to_line);
  9678.         if (result == LOOP_BREAK || result == EARLY_RETURN || result == EARLY_EXIT || result == FAIL)
  9679.         {
  9680.             // Although ExecUntil() will treat the LOOP_BREAK result identically to OK, we
  9681.             // need to return LOOP_BREAK in case our caller is another instance of this
  9682.             // same function (i.e. due to recursing into subfolders):
  9683.             return result;
  9684.         }
  9685.         if (jump_to_line)
  9686.         {
  9687.             if (jump_to_line == this)
  9688.                 // Since this LOOP's ExecUntil() encountered a Goto whose target is the LOOP
  9689.                 // itself, continue with the for-loop without moving to a different
  9690.                 // line.  Also: stay in this recursion layer even if aMode == ONLY_ONE_LINE
  9691.                 // because we don't want the caller handling it because then it's cleanup
  9692.                 // to jump to its end-point (beyond its own and any unowned elses) won't work.
  9693.                 // Example:
  9694.                 // if x  <-- If this layer were to do it, its own else would be unexpectedly encountered.
  9695.                 //    label1:
  9696.                 //    loop  <-- We want this statement's layer to handle the goto.
  9697.                 //       goto, label1
  9698.                 // else
  9699.                 //   ...
  9700.                 // Also, signal all our callers to return until they get back to the original
  9701.                 // ExecUntil() instance that started the loop:
  9702.                 aContinueMainLoop = true;
  9703.             else // jump_to_line must be a line that's at the same level or higher as our Exec_Until's LOOP statement itself.
  9704.                 aJumpToLine = jump_to_line; // Signal the caller to handle this jump.
  9705.             break;
  9706.         }
  9707.         // Otherwise, the result of executing the body of the loop, above, was either OK
  9708.         // (the current iteration completed normally) or LOOP_CONTINUE (the current loop
  9709.         // iteration was cut short).  In both cases, just continue on through the loop.
  9710.     } // for()
  9711.  
  9712.     // The script's loop is now over.
  9713.     return OK;
  9714. }
  9715.  
  9716.  
  9717.  
  9718. ResultType Line::PerformLoopFilePattern(char **apReturnValue, bool &aContinueMainLoop, Line *&aJumpToLine
  9719.     , FileLoopModeType aFileLoopMode, bool aRecurseSubfolders, char *aFilePattern)
  9720. // Note: Even if aFilePattern is just a directory (i.e. with not wildcard pattern), it seems best
  9721. // not to append "\\*.*" to it because the pattern might be a script variable that the user wants
  9722. // to conditionally resolve to various things at runtime.  In other words, it's valid to have
  9723. // only a single directory be the target of the loop.
  9724. {
  9725.     // Make a local copy of the path given in aFilePattern because as the lines of
  9726.     // the loop are executed, the deref buffer (which is what aFilePattern might
  9727.     // point to if we were called from ExecUntil()) may be overwritten --
  9728.     // and we will need the path string for every loop iteration.  We also need
  9729.     // to determine naked_filename_or_pattern:
  9730.     char file_path[MAX_PATH], naked_filename_or_pattern[MAX_PATH]; // Giving +3 extra for "*.*" seems fairly pointless because any files that actually need that extra room would fail to be retrieved by FindFirst/Next due to their inability to support paths much over 256.
  9731.     size_t file_path_length;
  9732.     strlcpy(file_path, aFilePattern, sizeof(file_path));
  9733.     char *last_backslash = strrchr(file_path, '\\');
  9734.     if (last_backslash)
  9735.     {
  9736.         strcpy(naked_filename_or_pattern, last_backslash + 1); // Naked filename.  No danger of overflow due size of src vs. dest.
  9737.         *(last_backslash + 1) = '\0';  // Convert file_path to be the file's path, but use +1 to retain the final backslash on the string.
  9738.         file_path_length = strlen(file_path);
  9739.     }
  9740.     else
  9741.     {
  9742.         strcpy(naked_filename_or_pattern, file_path); // No danger of overflow due size of src vs. dest.
  9743.         *file_path = '\0'; // There is no path, so make it empty to use current working directory.
  9744.         file_path_length = 0;
  9745.     }
  9746.  
  9747.     // g.mLoopFile is the current file of the file-loop that encloses this file-loop, if any.
  9748.     // The below is our own current_file, which will take precedence over g.mLoopFile if this
  9749.     // loop is a file-loop:
  9750.     BOOL file_found;
  9751.     WIN32_FIND_DATA new_current_file;
  9752.     HANDLE file_search = FindFirstFile(aFilePattern, &new_current_file);
  9753.     for ( file_found = (file_search != INVALID_HANDLE_VALUE) // Convert FindFirst's return value into a boolean so that it's compatible with with FindNext's.
  9754.         ; file_found && FileIsFilteredOut(new_current_file, aFileLoopMode, file_path, file_path_length)
  9755.         ; file_found = FindNextFile(file_search, &new_current_file));
  9756.     // file_found and new_current_file have now been set for use below.
  9757.     // Above is responsible for having properly set file_found and file_search.
  9758.  
  9759.     ResultType result;
  9760.     Line *jump_to_line;
  9761.     for (; file_found; ++g.mLoopIteration)
  9762.     {
  9763.         g.mLoopFile = &new_current_file; // inner file-loop's file takes precedence over any outer file-loop's.
  9764.         // Other types of loops leave g.mLoopFile unchanged so that a file-loop can enclose some other type of
  9765.         // inner loop, and that inner loop will still have access to the outer loop's current file.
  9766.  
  9767.         // Execute once the body of the loop (either just one statement or a block of statements).
  9768.         // Preparser has ensured that every LOOP has a non-NULL next line.
  9769.         result = mNextLine->ExecUntil(ONLY_ONE_LINE, apReturnValue, &jump_to_line);
  9770.         if (result == LOOP_BREAK || result == EARLY_RETURN || result == EARLY_EXIT || result == FAIL)
  9771.         {
  9772.             FindClose(file_search);
  9773.             // Although ExecUntil() will treat the LOOP_BREAK result identically to OK, we
  9774.             // need to return LOOP_BREAK in case our caller is another instance of this
  9775.             // same function (i.e. due to recursing into subfolders):
  9776.             return result;
  9777.         }
  9778.         if (jump_to_line) // See comments in PerformLoop() about this section.
  9779.         {
  9780.             if (jump_to_line == this)
  9781.                 aContinueMainLoop = true;
  9782.             else
  9783.                 aJumpToLine = jump_to_line; // Signal our caller to handle this jump.
  9784.             break;
  9785.         }
  9786.         // Otherwise, the result of executing the body of the loop, above, was either OK
  9787.         // (the current iteration completed normally) or LOOP_CONTINUE (the current loop
  9788.         // iteration was cut short).  In both cases, just continue on through the loop.
  9789.         // But first do end-of-iteration steps:
  9790.         while ((file_found = FindNextFile(file_search, &new_current_file))
  9791.             && FileIsFilteredOut(new_current_file, aFileLoopMode, file_path, file_path_length)); // Relies on short-circuit boolean order.
  9792.             // Above is a self-contained loop that keeps fetching files until there's no more files, or a file
  9793.             // is found that isn't filtered out.  It also sets file_found and new_current_file for use by the
  9794.             // outer loop.
  9795.     } // for()
  9796.  
  9797.     // The script's loop is now over.
  9798.     if (file_search != INVALID_HANDLE_VALUE)
  9799.         FindClose(file_search);
  9800.  
  9801.     // If aRecurseSubfolders is true, we now need to perform the loop's body for every subfolder to
  9802.     // search for more files and folders inside that match aFilePattern.  We can't do this in the
  9803.     // first loop, above, because it may have a restricted file-pattern such as *.txt and we want to
  9804.     // find and recurse into ALL folders:
  9805.     if (!aRecurseSubfolders) // No need to continue into the "recurse" section.
  9806.         return OK;
  9807.  
  9808.     // Since above didn't return, this is a file-loop and recursion into sub-folders has been requested.
  9809.     // Append *.* to file_path so that we can retrieve all files and folders in the aFilePattern
  9810.     // main folder.  We're only interested in the folders, but we have to use *.* to ensure
  9811.     // that the search will find all folder names:
  9812.     if (file_path_length > sizeof(file_path) - 4) // v1.0.45.03: No room to append "*.*", so for simplicity, skip this folder (don't recurse into it).
  9813.         return OK; // This situation might be impossible except for 32000-capable paths because the OS seems to reserve room inside every directory for at least the maximum length of a short filename.
  9814.     char *append_pos = file_path + file_path_length;
  9815.     strcpy(append_pos, "*.*"); // Above has already verified that no overflow is possible.
  9816.  
  9817.     file_search = FindFirstFile(file_path, &new_current_file);
  9818.     if (file_search == INVALID_HANDLE_VALUE)
  9819.         return OK; // Nothing more to do.
  9820.     // Otherwise, recurse into any subdirectories found inside this parent directory.
  9821.  
  9822.     size_t path_and_pattern_length = file_path_length + strlen(naked_filename_or_pattern); // Calculated only once for performance.
  9823.     do
  9824.     {
  9825.         if (!(new_current_file.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) // We only want directories (except "." and "..").
  9826.             || new_current_file.cFileName[0] == '.' && (!new_current_file.cFileName[1]      // Relies on short-circuit boolean order.
  9827.                 || new_current_file.cFileName[1] == '.' && !new_current_file.cFileName[2])  //
  9828.             // v1.0.45.03: Skip over folders whose full-path-names are too long to be supported by the ANSI
  9829.             // versions of FindFirst/FindNext.  Without this fix, the section below formerly called PerformLoop()
  9830.             // with a truncated full-path-name, which caused the last_backslash-finding logic to find the wrong
  9831.             // backslash, which in turn caused infinite recursion and a stack overflow (i.e. caused by the
  9832.             // full-path-name getting truncated in the same spot every time, endlessly).
  9833.             || path_and_pattern_length + strlen(new_current_file.cFileName) > sizeof(file_path) - 2) // -2 to reflect: 1) the backslash to be added between cFileName and naked_filename_or_pattern; 2) the zero terminator.
  9834.             continue;
  9835.         // Build the new search pattern, which consists of the original file_path + the subfolder name
  9836.         // we just discovered + the original pattern:
  9837.         sprintf(append_pos, "%s\\%s", new_current_file.cFileName, naked_filename_or_pattern); // Indirectly set file_path to the new search pattern.  This won't overflow due to the check above.
  9838.         // Pass NULL for the 2nd param because it will determine its own current-file when it does
  9839.         // its first loop iteration.  This is because this directory is being recursed into, not
  9840.         // processed itself as a file-loop item (since this was already done in the first loop,
  9841.         // above, if its name matches the original search pattern):
  9842.         result = PerformLoopFilePattern(apReturnValue, aContinueMainLoop, aJumpToLine, aFileLoopMode, aRecurseSubfolders, file_path);
  9843.         // result should never be LOOP_CONTINUE because the above call to PerformLoop() should have
  9844.         // handled that case.  However, it can be LOOP_BREAK if it encoutered the break command.
  9845.         if (result == LOOP_BREAK || result == EARLY_RETURN || result == EARLY_EXIT || result == FAIL)
  9846.         {
  9847.             FindClose(file_search);
  9848.             return result;  // Return even LOOP_BREAK, since our caller can be either ExecUntil() or ourself.
  9849.         }
  9850.         if (aContinueMainLoop // The call to PerformLoop() above signaled us to break & return.
  9851.             || aJumpToLine)
  9852.             // Above: There's no need to check "aJumpToLine == this" because PerformLoop() would already have
  9853.             // handled it.  But if it set aJumpToLine to be non-NULL, it means we have to return and let our caller
  9854.             // handle the jump.
  9855.             break;
  9856.     } while (FindNextFile(file_search, &new_current_file));
  9857.     FindClose(file_search);
  9858.  
  9859.     return OK;
  9860. }
  9861.  
  9862.  
  9863.  
  9864. ResultType Line::PerformLoopReg(char **apReturnValue, bool &aContinueMainLoop, Line *&aJumpToLine, FileLoopModeType aFileLoopMode
  9865.     , bool aRecurseSubfolders, HKEY aRootKeyType, HKEY aRootKey, char *aRegSubkey)
  9866. // aRootKeyType is the type of root key, independent of whether it's local or remote.
  9867. // This is used because there's no easy way to determine which root key a remote HKEY
  9868. // refers to.
  9869. {
  9870.     RegItemStruct reg_item(aRootKeyType, aRootKey, aRegSubkey);
  9871.     HKEY hRegKey;
  9872.  
  9873.     // Open the specified subkey.  Be sure to only open with the minimum permission level so that
  9874.     // the keys & values can be deleted or written to (though I'm not sure this would be an issue
  9875.     // in most cases):
  9876.     if (RegOpenKeyEx(reg_item.root_key, reg_item.subkey, 0, KEY_QUERY_VALUE | KEY_ENUMERATE_SUB_KEYS, &hRegKey) != ERROR_SUCCESS)
  9877.         return OK;
  9878.  
  9879.     // Get the count of how many values and subkeys are contained in this parent key:
  9880.     DWORD count_subkeys;
  9881.     DWORD count_values;
  9882.     if (RegQueryInfoKey(hRegKey, NULL, NULL, NULL, &count_subkeys, NULL, NULL
  9883.         , &count_values, NULL, NULL, NULL, NULL) != ERROR_SUCCESS)
  9884.     {
  9885.         RegCloseKey(hRegKey);
  9886.         return OK;
  9887.     }
  9888.  
  9889.     ResultType result;
  9890.     Line *jump_to_line;
  9891.     DWORD i;
  9892.  
  9893.     // See comments in PerformLoop() for details about this section.
  9894.     // Note that ®_item is passed to ExecUntil() rather than... (comment was never finished).
  9895.     #define MAKE_SCRIPT_LOOP_PROCESS_THIS_ITEM \
  9896.     {\
  9897.         g.mLoopRegItem = ®_item;\
  9898.         result = mNextLine->ExecUntil(ONLY_ONE_LINE, apReturnValue, &jump_to_line);\
  9899.         ++g.mLoopIteration;\
  9900.         if (result == LOOP_BREAK || result == EARLY_RETURN || result == EARLY_EXIT || result == FAIL)\
  9901.         {\
  9902.             RegCloseKey(hRegKey);\
  9903.             return result;\
  9904.         }\
  9905.         if (jump_to_line)\
  9906.         {\
  9907.             if (jump_to_line == this)\
  9908.                 aContinueMainLoop = true;\
  9909.             else\
  9910.                 aJumpToLine = jump_to_line;\
  9911.             break;\
  9912.         }\
  9913.     }
  9914.  
  9915.     DWORD name_size;
  9916.  
  9917.     // First enumerate the values, which are analogous to files in the file system.
  9918.     // Later, the subkeys ("subfolders") will be done:
  9919.     if (count_values > 0 && aFileLoopMode != FILE_LOOP_FOLDERS_ONLY) // The caller doesn't want "files" (values) excluded.
  9920.     {
  9921.         reg_item.InitForValues();
  9922.         // Going in reverse order allows values to be deleted without disrupting the enumeration,
  9923.         // at least in some cases:
  9924.         for (i = count_values - 1;; --i) 
  9925.         { 
  9926.             // Don't use CONTINUE in loops such as this due to the loop-ending condition being explicitly
  9927.             // checked at the bottom.
  9928.             name_size = sizeof(reg_item.name);  // Must reset this every time through the loop.
  9929.             *reg_item.name = '\0';
  9930.             if (RegEnumValue(hRegKey, i, reg_item.name, &name_size, NULL, ®_item.type, NULL, NULL) == ERROR_SUCCESS)
  9931.                 MAKE_SCRIPT_LOOP_PROCESS_THIS_ITEM
  9932.             // else continue the loop in case some of the lower indexes can still be retrieved successfully.
  9933.             if (i == 0)  // Check this here due to it being an unsigned value that we don't want to go negative.
  9934.                 break;
  9935.         }
  9936.     }
  9937.  
  9938.     // If the loop is neither processing subfolders nor recursing into them, don't waste the performance
  9939.     // doing the next loop:
  9940.     if (!count_subkeys || (aFileLoopMode == FILE_LOOP_FILES_ONLY && !aRecurseSubfolders))
  9941.     {
  9942.         RegCloseKey(hRegKey);
  9943.         return OK;
  9944.     }
  9945.  
  9946.     // Enumerate the subkeys, which are analogous to subfolders in the files system:
  9947.     // Going in reverse order allows keys to be deleted without disrupting the enumeration,
  9948.     // at least in some cases:
  9949.     reg_item.InitForSubkeys();
  9950.     char subkey_full_path[MAX_REG_ITEM_LENGTH + 1]; // But doesn't include the root key name, which is not only by design but testing shows that if it did, the length could go over 260.
  9951.     for (i = count_subkeys - 1;; --i) // Will have zero iterations if there are no subkeys.
  9952.     {
  9953.         // Don't use CONTINUE in loops such as this due to the loop-ending condition being explicitly
  9954.         // checked at the bottom.
  9955.         name_size = sizeof(reg_item.name); // Must be reset for every iteration.
  9956.         if (RegEnumKeyEx(hRegKey, i, reg_item.name, &name_size, NULL, NULL, NULL, ®_item.ftLastWriteTime) == ERROR_SUCCESS)
  9957.         {
  9958.             if (aFileLoopMode != FILE_LOOP_FILES_ONLY) // have the script's loop process this subkey.
  9959.                 MAKE_SCRIPT_LOOP_PROCESS_THIS_ITEM
  9960.             if (aRecurseSubfolders) // Now recurse into the subkey, regardless of whether it was processed above.
  9961.             {
  9962.                 // Build the new subkey name using the an area of memory on the stack that we won't need
  9963.                 // after the recusive call returns to us.  Omit the leading backslash if subkey is blank,
  9964.                 // which supports recursively searching the contents of keys contained within a root key
  9965.                 // (fixed for v1.0.17):
  9966.                 snprintf(subkey_full_path, sizeof(subkey_full_path), "%s%s%s", reg_item.subkey
  9967.                     , *reg_item.subkey ? "\\" : "", reg_item.name);
  9968.                 // This section is very similar to the one in PerformLoop(), so see it for comments:
  9969.                 result = PerformLoopReg(apReturnValue, aContinueMainLoop, aJumpToLine, aFileLoopMode
  9970.                     , aRecurseSubfolders, aRootKeyType, aRootKey, subkey_full_path);
  9971.                 if (result == LOOP_BREAK || result == EARLY_RETURN || result == EARLY_EXIT || result == FAIL)
  9972.                 {
  9973.                     RegCloseKey(hRegKey);
  9974.                     return result;
  9975.                 }
  9976.                 if (aContinueMainLoop || aJumpToLine)
  9977.                     break;
  9978.             }
  9979.         }
  9980.         // else continue the loop in case some of the lower indexes can still be retrieved successfully.
  9981.         if (i == 0)  // Check this here due to it being an unsigned value that we don't want to go negative.
  9982.             break;
  9983.     }
  9984.     RegCloseKey(hRegKey);
  9985.     return OK;
  9986. }
  9987.  
  9988.  
  9989.  
  9990. ResultType Line::PerformLoopParse(char **apReturnValue, bool &aContinueMainLoop, Line *&aJumpToLine)
  9991. {
  9992.     if (!*ARG2) // Since the input variable's contents are blank, the loop will execute zero times.
  9993.         return OK;
  9994.  
  9995.     // The following will be used to hold the parsed items.  It needs to have its own storage because
  9996.     // even though ARG2 might always be a writable memory area, we can't rely upon it being
  9997.     // persistent because it might reside in the deref buffer, in which case the other commands
  9998.     // in the loop's body would probably overwrite it.  Even if the ARG2's contents aren't in
  9999.     // the deref buffer, we still can't modify it (i.e. to temporarily terminate it and thus
  10000.     // bypass the need for malloc() below) because that might modify the variable contents, and
  10001.     // that variable may be referenced elsewhere in the body of the loop (which would result
  10002.     // in unexpected side-effects).  So, rather than have a limit of 64K or something (which
  10003.     // would limit this feature's usefulness for parsing a large list of filenames, for example),
  10004.     // it seems best to dynamically allocate a temporary buffer large enough to hold the
  10005.     // contents of ARG2 (the input variable).  Update: Since these loops tend to be enclosed
  10006.     // by file-read loops, and thus may be called thousands of times in a short period,
  10007.     // it should help average performance to use the stack for small vars rather than
  10008.     // constantly doing malloc() and free(), which are much higher overhead and probably
  10009.     // cause memory fragmentation (especially with thousands of calls):
  10010.     size_t space_needed = ArgLength(2) + 1;  // +1 for the zero terminator.
  10011.     char *stack_buf, *buf;
  10012.     #define FREE_PARSE_MEMORY if (buf != stack_buf) free(buf)  // Also used by the CSV version of this function.
  10013.     #define LOOP_PARSE_BUF_SIZE 40000                          //
  10014.     if (space_needed <= LOOP_PARSE_BUF_SIZE)
  10015.     {
  10016.         stack_buf = (char *)_alloca(LOOP_PARSE_BUF_SIZE); // Helps performance.  See comments above.
  10017.         buf = stack_buf;
  10018.     }
  10019.     else
  10020.     {
  10021.         if (   !(buf = (char *)malloc(space_needed))   )
  10022.             // Probably best to consider this a critical error, since on the rare times it does happen, the user
  10023.             // would probably want to know about it immediately.
  10024.             return LineError(ERR_OUTOFMEM, FAIL, ARG2);
  10025.         stack_buf = NULL; // For comparison purposes later below.
  10026.     }
  10027.     strcpy(buf, ARG2); // Make the copy.
  10028.  
  10029.     // Make a copy of ARG3 and ARG4 in case either one's contents are in the deref buffer, which would
  10030.     // probably be overwritten by the commands in the script loop's body:
  10031.     char delimiters[512], omit_list[512];
  10032.     strlcpy(delimiters, ARG3, sizeof(delimiters));
  10033.     strlcpy(omit_list, ARG4, sizeof(omit_list));
  10034.  
  10035.     ResultType result;
  10036.     Line *jump_to_line;
  10037.     char *field, *field_end, saved_char;
  10038.     size_t field_length;
  10039.  
  10040.     for (field = buf;;)
  10041.     { 
  10042.         if (*delimiters)
  10043.         {
  10044.             if (   !(field_end = StrChrAny(field, delimiters))   ) // No more delimiters found.
  10045.                 field_end = field + strlen(field);  // Set it to the position of the zero terminator instead.
  10046.         }
  10047.         else // Since no delimiters, every char in the input string is treated as a separate field.
  10048.         {
  10049.             // But exclude this char if it's in the omit_list:
  10050.             if (*omit_list && strchr(omit_list, *field))
  10051.             {
  10052.                 ++field; // Move on to the next char.
  10053.                 if (!*field) // The end of the string has been reached.
  10054.                     break;
  10055.                 continue;
  10056.             }
  10057.             field_end = field + 1;
  10058.         }
  10059.  
  10060.         saved_char = *field_end;  // In case it's a non-delimited list of single chars.
  10061.         *field_end = '\0';  // Temporarily terminate so that GetLoopField() will see the correct substring.
  10062.  
  10063.         if (*omit_list && *field && *delimiters)  // If no delimiters, the omit_list has already been handled above.
  10064.         {
  10065.             // Process the omit list.
  10066.             field = omit_leading_any(field, omit_list, field_end - field);
  10067.             if (*field) // i.e. the above didn't remove all the chars due to them all being in the omit-list.
  10068.             {
  10069.                 field_length = omit_trailing_any(field, omit_list, field_end - 1);
  10070.                 field[field_length] = '\0';  // Terminate here, but don't update field_end, since saved_char needs it.
  10071.             }
  10072.         }
  10073.  
  10074.         // See comments in PerformLoop() for details about this section.
  10075.         g.mLoopField = field;
  10076.         result = mNextLine->ExecUntil(ONLY_ONE_LINE, apReturnValue, &jump_to_line);
  10077.         ++g.mLoopIteration;
  10078.  
  10079.         if (result == LOOP_BREAK || result == EARLY_RETURN || result == EARLY_EXIT || result == FAIL)
  10080.         {
  10081.             FREE_PARSE_MEMORY;
  10082.             return result;
  10083.         }
  10084.         if (jump_to_line) // See comments in PerformLoop() about this section.
  10085.         {
  10086.             if (jump_to_line == this)
  10087.                 aContinueMainLoop = true;
  10088.             else
  10089.                 aJumpToLine = jump_to_line; // Signal our caller to handle this jump.
  10090.             break;
  10091.         }
  10092.         if (!saved_char) // The last item in the list has just been processed, so the loop is done.
  10093.             break;
  10094.         *field_end = saved_char;  // Undo the temporary termination, in case the list of delimiters is blank.
  10095.         field = *delimiters ? field_end + 1 : field_end;  // Move on to the next field.
  10096.     }
  10097.     FREE_PARSE_MEMORY;
  10098.     return OK;
  10099. }
  10100.  
  10101.  
  10102.  
  10103. ResultType Line::PerformLoopParseCSV(char **apReturnValue, bool &aContinueMainLoop, Line *&aJumpToLine)
  10104. // This function is similar to PerformLoopParse() so the two should be maintained together.
  10105. // See PerformLoopParse() for comments about the below (comments have been mostly stripped
  10106. // from this function).
  10107. {
  10108.     if (!*ARG2) // Since the input variable's contents are blank, the loop will execute zero times.
  10109.         return OK;
  10110.  
  10111.     // See comments in PerformLoopParse() for details.
  10112.     size_t space_needed = ArgLength(2) + 1;  // +1 for the zero terminator.
  10113.     char *stack_buf, *buf;
  10114.     if (space_needed <= LOOP_PARSE_BUF_SIZE)
  10115.     {
  10116.         stack_buf = (char *)_alloca(LOOP_PARSE_BUF_SIZE); // Helps performance.  See comments above.
  10117.         buf = stack_buf;
  10118.     }
  10119.     else
  10120.     {
  10121.         if (   !(buf = (char *)malloc(space_needed))   )
  10122.             return LineError(ERR_OUTOFMEM, FAIL, ARG2);
  10123.         stack_buf = NULL; // For comparison purposes later below.
  10124.     }
  10125.     strcpy(buf, ARG2); // Make the copy.
  10126.  
  10127.     char omit_list[512];
  10128.     strlcpy(omit_list, ARG4, sizeof(omit_list));
  10129.  
  10130.     ResultType result;
  10131.     Line *jump_to_line;
  10132.     char *field, *field_end, saved_char;
  10133.     size_t field_length;
  10134.     bool field_is_enclosed_in_quotes;
  10135.  
  10136.     for (field = buf;;)
  10137.     {
  10138.         if (*field == '"')
  10139.         {
  10140.             // For each field, check if the optional leading double-quote is present.  If it is,
  10141.             // skip over it since we always know it's the one that marks the beginning of
  10142.             // the that field.  This assumes that a field containing escaped double-quote is
  10143.             // always contained in double quotes, which is how Excel does it.  For example:
  10144.             // """string with escaped quotes""" resolves to a literal quoted string:
  10145.             field_is_enclosed_in_quotes = true;
  10146.             ++field;
  10147.         }
  10148.         else
  10149.             field_is_enclosed_in_quotes = false;
  10150.  
  10151.         for (field_end = field;;)
  10152.         {
  10153.             if (   !(field_end = strchr(field_end, field_is_enclosed_in_quotes ? '"' : ','))   )
  10154.             {
  10155.                 // This is the last field in the string, so set field_end to the position of
  10156.                 // the zero terminator instead:
  10157.                 field_end = field + strlen(field);
  10158.                 break;
  10159.             }
  10160.             if (field_is_enclosed_in_quotes)
  10161.             {
  10162.                 // The quote discovered above marks the end of the string if it isn't followed
  10163.                 // by another quote.  But if it is a pair of quotes, replace it with a single
  10164.                 // literal double-quote and then keep searching for the real ending quote:
  10165.                 if (field_end[1] == '"')  // A pair of quotes was encountered.
  10166.                 {
  10167.                     memmove(field_end, field_end + 1, strlen(field_end + 1) + 1); // +1 to include terminator.
  10168.                     ++field_end; // Skip over the literal double quote that we just produced.
  10169.                     continue; // Keep looking for the "real" ending quote.
  10170.                 }
  10171.                 // Otherwise, this quote marks the end of the field, so just fall through and break.
  10172.             }
  10173.             // else field is not enclosed in quotes, so the comma discovered above must be a delimiter.
  10174.             break;
  10175.         }
  10176.  
  10177.         saved_char = *field_end; // This can be the terminator, a comma, or a double-quote.
  10178.         *field_end = '\0';  // Terminate here so that GetLoopField() will see the correct substring.
  10179.  
  10180.         if (*omit_list && *field)
  10181.         {
  10182.             // Process the omit list.
  10183.             field = omit_leading_any(field, omit_list, field_end - field);
  10184.             if (*field) // i.e. the above didn't remove all the chars due to them all being in the omit-list.
  10185.             {
  10186.                 field_length = omit_trailing_any(field, omit_list, field_end - 1);
  10187.                 field[field_length] = '\0';  // Terminate here, but don't update field_end, since we need its pos.
  10188.             }
  10189.         }
  10190.  
  10191.         // See comments in PerformLoop() for details about this section.
  10192.         g.mLoopField = field;
  10193.         result = mNextLine->ExecUntil(ONLY_ONE_LINE, apReturnValue, &jump_to_line);
  10194.         ++g.mLoopIteration;
  10195.  
  10196.         if (result == LOOP_BREAK || result == EARLY_RETURN || result == EARLY_EXIT || result == FAIL)
  10197.         {
  10198.             FREE_PARSE_MEMORY;
  10199.             return result;
  10200.         }
  10201.         if (jump_to_line) // See comments in PerformLoop() about this section.
  10202.         {
  10203.             if (jump_to_line == this)
  10204.                 aContinueMainLoop = true;
  10205.             else
  10206.                 aJumpToLine = jump_to_line; // Signal our caller to handle this jump.
  10207.             break;
  10208.         }
  10209.  
  10210.         if (!saved_char) // The last item in the list has just been processed, so the loop is done.
  10211.             break;
  10212.         if (saved_char == ',') // Set "field" to be the position of the next field.
  10213.             field = field_end + 1;
  10214.         else // saved_char must be a double-quote char.
  10215.         {
  10216.             field = field_end + 1;
  10217.             if (!*field) // No more fields occur after this one.
  10218.                 break;
  10219.             // Find the next comma, which must be a real delimiter since we're in between fields:
  10220.             if (   !(field = strchr(field, ','))   ) // No more fields.
  10221.                 break;
  10222.             // Set it to be the first character of the next field, which might be a double-quote
  10223.             // or another comma (if the field is empty).
  10224.             ++field;
  10225.         }
  10226.     }
  10227.     FREE_PARSE_MEMORY;
  10228.     return OK;
  10229. }
  10230.  
  10231.  
  10232.  
  10233. ResultType Line::PerformLoopReadFile(char **apReturnValue, bool &aContinueMainLoop, Line *&aJumpToLine, FILE *aReadFile, char *aWriteFileName)
  10234. {
  10235.     LoopReadFileStruct loop_info(aReadFile, aWriteFileName);
  10236.     size_t line_length;
  10237.     ResultType result;
  10238.     Line *jump_to_line;
  10239.  
  10240.     for (; fgets(loop_info.mCurrentLine, sizeof(loop_info.mCurrentLine), loop_info.mReadFile);)
  10241.     { 
  10242.         line_length = strlen(loop_info.mCurrentLine);
  10243.         if (line_length && loop_info.mCurrentLine[line_length - 1] == '\n') // Remove newlines like FileReadLine does.
  10244.             loop_info.mCurrentLine[--line_length] = '\0';
  10245.         // See comments in PerformLoop() for details about this section.
  10246.         g.mLoopReadFile = &loop_info;
  10247.         result = mNextLine->ExecUntil(ONLY_ONE_LINE, apReturnValue, &jump_to_line);
  10248.         ++g.mLoopIteration;
  10249.         if (result == LOOP_BREAK || result == EARLY_RETURN || result == EARLY_EXIT || result == FAIL)
  10250.         {
  10251.             if (loop_info.mWriteFile)
  10252.                 fclose(loop_info.mWriteFile);
  10253.             return result;
  10254.         }
  10255.         if (jump_to_line) // See comments in PerformLoop() about this section.
  10256.         {
  10257.             if (jump_to_line == this)
  10258.                 aContinueMainLoop = true;
  10259.             else
  10260.                 aJumpToLine = jump_to_line; // Signal our caller to handle this jump.
  10261.             break;
  10262.         }
  10263.     }
  10264.  
  10265.     if (loop_info.mWriteFile)
  10266.         fclose(loop_info.mWriteFile);
  10267.  
  10268.     // Don't return result because we want to always return OK unless it was one of the values
  10269.     // already explicitly checked and returned above.  In other words, there might be values other
  10270.     // than OK that aren't explicitly checked for, above.
  10271.     return OK;
  10272. }
  10273.  
  10274.  
  10275.  
  10276. __forceinline ResultType Line::Perform() // __forceinline() currently boosts performance a bit, though it's probably more due to the butterly effect and cache hits/misses.
  10277. // Performs only this line's action.
  10278. // Returns OK or FAIL.
  10279. // The function should not be called to perform any flow-control actions such as
  10280. // Goto, Gosub, Return, Block-Begin, Block-End, If, Else, etc.
  10281. {
  10282.     char buf_temp[MAX_REG_ITEM_LENGTH + 1], *contents; // For registry and other things.
  10283.     WinGroup *group; // For the group commands.
  10284.     Var *output_var = OUTPUT_VAR; // Okay if NULL. Users of it should only consider it valid if their first arg is actually an output_variable.
  10285.     ToggleValueType toggle;  // For commands that use on/off/neutral.
  10286.     // Use signed values for these in case they're really given an explicit negative value:
  10287.     int start_char_num, chars_to_extract; // For String commands.
  10288.     size_t source_length; // For String commands.
  10289.     SymbolType var_is_pure_numeric, value_is_pure_numeric; // For math operations.
  10290.     vk_type vk; // For GetKeyState.
  10291.     Label *target_label;  // For ACT_SETTIMER and ACT_HOTKEY
  10292.     int instance_number;  // For sound commands.
  10293.     DWORD component_type; // For sound commands.
  10294.     __int64 device_id;  // For sound commands.  __int64 helps avoid compiler warning for some conversions.
  10295.     bool is_remote_registry; // For Registry commands.
  10296.     HKEY root_key; // For Registry commands.
  10297.     ResultType result;  // General purpose.
  10298.  
  10299.     // Even though the loading-parser already checked, check again, for now,
  10300.     // at least until testing raises confidence.  UPDATE: Don't this because
  10301.     // sometimes (e.g. ACT_ASSIGN/ADD/SUB/MULT/DIV) the number of parameters
  10302.     // required at load-time is different from that at runtime, because params
  10303.     // are taken out or added to the param list:
  10304.     //if (nArgs < g_act[mActionType].MinParams) ...
  10305.  
  10306.     switch (mActionType)
  10307.     {
  10308.     case ACT_ASSIGN:
  10309.         // Note: This line's args have not yet been dereferenced in this case (i.e. ExpandArgs() hasn't been
  10310.         // called).  The below function will handle that if it is needed.
  10311.         return PerformAssign();  // It will report any errors for us.
  10312.  
  10313.     case ACT_ASSIGNEXPR:
  10314.         // Currently, ACT_ASSIGNEXPR can occur even when mArg[1].is_expression==false, such as things like var:=5
  10315.         // and var:=Array%i%.  Search on "is_expression = " to find such cases in the script-loading/parsing
  10316.         // routines.
  10317.         if (mArgc > 1)
  10318.         {
  10319.             if (mArg[1].is_expression) // v1.0.45: ExpandExpression() already took care of it for us (for performance reasons).
  10320.                 return OK;
  10321.             // sArgVar is used to enhance performance, which would otherwise be poor for dynamic variables
  10322.             // such as Var:=Array%i% (which is an expression and handled by ACT_ASSIGNEXPR rather than
  10323.             // ACT_ASSIGN) because Array%i% would have to be resolved twice (once here and once
  10324.             // previously by ExpandArgs()) just to find out if it's IsBinaryClip()).
  10325.             if (ARGVARRAW2) // RAW is safe due to the above check of mArgc > 1.
  10326.             {
  10327.                 if (ARGVARRAW2->IsBinaryClip()) // This can be reached via things like: x := binary_clip
  10328.                     // Performance should be good in this case since IsBinaryClip() implies a single isolated deref,
  10329.                     // which would never have been copied into the deref buffer.
  10330.                     return output_var->AssignBinaryClip(*ARGVARRAW2); // ARG2 must be VAR_NORMAL due to IsBinaryClip() check above (it can't even be VAR_CLIPBOARDALL).
  10331.                 // v1.0.46.01: The following can be reached because loadtime no longer translates such statements
  10332.                 // into ACT_ASSIGN vs. ACT_ASSIGNEXPR.  Even without that change, it can also be reached by
  10333.                 // something like:
  10334.                 //    DynClipboardAll = ClipboardAll
  10335.                 //    ClipSaved := %DynClipboardAll%
  10336.                 if (ARGVARRAW2->Type() == VAR_CLIPBOARDALL)
  10337.                     return output_var->AssignClipboardAll();
  10338.             }
  10339.         }
  10340.         // Note that simple assignments such as Var:="xyz" or Var:=Var2 are resolved to be
  10341.         // non-expressions at load-time.  In these cases, ARG2 would have been expanded
  10342.         // normally rather than evaluated as an expression.
  10343.         return output_var->Assign(ARG2); // ARG2 now contains the evaluated result of the expression.
  10344.  
  10345.     case ACT_EXPRESSION:
  10346.         // Nothing needs to be done because the expression in ARG1 (which is the only arg) has already
  10347.         // been evaluated and its functions and subfunctions called.  Examples:
  10348.         //    fn(123, "string", var, fn2(y))
  10349.         //    x&=3
  10350.         //    var ? func() : x:=y
  10351.         return OK;
  10352.  
  10353.     // Like AutoIt2, if either output_var or ARG1 aren't purely numeric, they
  10354.     // will be considered to be zero for all of the below math functions:
  10355.     case ACT_ADD:
  10356.         #undef DETERMINE_NUMERIC_TYPES
  10357.         #define DETERMINE_NUMERIC_TYPES \
  10358.             value_is_pure_numeric = IsPureNumeric(ARG2, true, false, true, true);\
  10359.             var_is_pure_numeric = IsPureNumeric(output_var->Contents(), true, false, true, true);
  10360.  
  10361.         // Some performance can be gained by relying on the fact that short-circuit boolean
  10362.         // can skip the "var_is_pure_numeric" check whenever value_is_pure_numeric == PURE_FLOAT.
  10363.         // This is because var_is_pure_numeric is never directly needed here (unlike EvaluateCondition()).
  10364.         // However, benchmarks show that this makes such a small difference that it's not worth the
  10365.         // loss of maintainability and the slightly larger code size due to macro expansion:
  10366.         //#undef IF_EITHER_IS_FLOAT
  10367.         //#define IF_EITHER_IS_FLOAT if (value_is_pure_numeric == PURE_FLOAT \
  10368.         //    || IsPureNumeric(output_var->Contents(), true, false, true, true) == PURE_FLOAT)
  10369.  
  10370.         DETERMINE_NUMERIC_TYPES
  10371.  
  10372.         if (*ARG3 && strchr("SMHD", toupper(*ARG3))) // the command is being used to add a value to a date-time.
  10373.         {
  10374.             if (!value_is_pure_numeric) // It's considered to be zero, so the output_var is left unchanged:
  10375.                 return OK;
  10376.             else
  10377.             {
  10378.                 // Use double to support a floating point value for days, hours, minutes, etc:
  10379.                 double nUnits = ATOF(ARG2);  // ATOF() returns a double, at least on MSVC++ 7.x
  10380.                 FILETIME ft, ftNowUTC;
  10381.                 if (*output_var->Contents())
  10382.                 {
  10383.                     if (!YYYYMMDDToFileTime(output_var->Contents(), ft))
  10384.                         return output_var->Assign(""); // Set to blank to indicate the problem.
  10385.                 }
  10386.                 else // The output variable is currently blank, so substitute the current time for it.
  10387.                 {
  10388.                     GetSystemTimeAsFileTime(&ftNowUTC);
  10389.                     FileTimeToLocalFileTime(&ftNowUTC, &ft);  // Convert UTC to local time.
  10390.                 }
  10391.                 // Convert to 10ths of a microsecond (the units of the FILETIME struct):
  10392.                 switch (toupper(*ARG3))
  10393.                 {
  10394.                 case 'S': // Seconds
  10395.                     nUnits *= (double)10000000;
  10396.                     break;
  10397.                 case 'M': // Minutes
  10398.                     nUnits *= ((double)10000000 * 60);
  10399.                     break;
  10400.                 case 'H': // Hours
  10401.                     nUnits *= ((double)10000000 * 60 * 60);
  10402.                     break;
  10403.                 case 'D': // Days
  10404.                     nUnits *= ((double)10000000 * 60 * 60 * 24);
  10405.                     break;
  10406.                 }
  10407.                 // Convert ft struct to a 64-bit variable (maybe there's some way to avoid these conversions):
  10408.                 ULARGE_INTEGER ul;
  10409.                 ul.LowPart = ft.dwLowDateTime;
  10410.                 ul.HighPart = ft.dwHighDateTime;
  10411.                 // Add the specified amount of time to the result value:
  10412.                 ul.QuadPart += (__int64)nUnits;  // Seems ok to cast/truncate in light of the *=10000000 above.
  10413.                 // Convert back into ft struct:
  10414.                 ft.dwLowDateTime = ul.LowPart;
  10415.                 ft.dwHighDateTime = ul.HighPart;
  10416.                 return output_var->Assign(FileTimeToYYYYMMDD(buf_temp, ft, false));
  10417.             }
  10418.         }
  10419.         else // ARG3 is absent or invalid, so do normal math (not date-time).
  10420.         {
  10421.             IF_EITHER_IS_FLOAT
  10422.                 return output_var->Assign(ATOF(output_var->Contents()) + ATOF(ARG2));  // Overload: Assigns a double.
  10423.             else // Non-numeric variables or values are considered to be zero for the purpose of the calculation.
  10424.                 return output_var->Assign(ATOI64(output_var->Contents()) + ATOI64(ARG2));  // Overload: Assigns an int.
  10425.         }
  10426.         return OK;  // Never executed.
  10427.  
  10428.     case ACT_SUB:
  10429.         if (*ARG3 && strchr("SMHD", toupper(*ARG3))) // the command is being used to subtract date-time values.
  10430.         {
  10431.             bool failed;
  10432.             // If either ARG2 or output_var->Contents() is blank, it will default to the current time:
  10433.             __int64 time_until = YYYYMMDDSecondsUntil(ARG2, output_var->Contents(), failed);
  10434.             if (failed) // Usually caused by an invalid component in the date-time string.
  10435.                 return output_var->Assign("");
  10436.             switch (toupper(*ARG3))
  10437.             {
  10438.             // Do nothing in the case of 'S' (seconds).  Otherwise:
  10439.             case 'M': time_until /= 60; break; // Minutes
  10440.             case 'H': time_until /= 60 * 60; break; // Hours
  10441.             case 'D': time_until /= 60 * 60 * 24; break; // Days
  10442.             }
  10443.             // Only now that any division has been performed (to reduce the magnitude of
  10444.             // time_until) do we cast down into an int, which is the standard size
  10445.             // used for non-float results (the result is always non-float for subtraction
  10446.             // of two date-times):
  10447.             return output_var->Assign(time_until); // Assign as signed 64-bit.
  10448.         }
  10449.         else // ARG3 is absent or invalid, so do normal math (not date-time).
  10450.         {
  10451.             DETERMINE_NUMERIC_TYPES
  10452.             IF_EITHER_IS_FLOAT
  10453.                 return output_var->Assign(ATOF(output_var->Contents()) - ATOF(ARG2));  // Overload: Assigns a double.
  10454.             else // Non-numeric variables or values are considered to be zero for the purpose of the calculation.
  10455.                 return output_var->Assign(ATOI64(output_var->Contents()) - ATOI64(ARG2));  // Overload: Assigns an INT.
  10456.         }
  10457.         // All paths above return.
  10458.  
  10459.     case ACT_MULT:
  10460.         DETERMINE_NUMERIC_TYPES
  10461.         IF_EITHER_IS_FLOAT
  10462.             return output_var->Assign(ATOF(output_var->Contents()) * ATOF(ARG2));  // Overload: Assigns a double.
  10463.         else // Non-numeric variables or values are considered to be zero for the purpose of the calculation.
  10464.             return output_var->Assign(ATOI64(output_var->Contents()) * ATOI64(ARG2));  // Overload: Assigns an INT.
  10465.  
  10466.     case ACT_DIV:
  10467.         DETERMINE_NUMERIC_TYPES
  10468.         IF_EITHER_IS_FLOAT
  10469.         {
  10470.             double ARG2_as_float = ATOF(ARG2);  // Since ATOF() returns double, at least on MSVC++ 7.x
  10471.             if (!ARG2_as_float)              // v1.0.46: Make behavior more consistent with expressions by
  10472.                 return output_var->Assign(); // avoiding a runtime error dialog; just make the output variable blank.
  10473.             return output_var->Assign(ATOF(output_var->Contents()) / ARG2_as_float);  // Overload: Assigns a double.
  10474.         }
  10475.         else // Non-numeric variables or values are considered to be zero for the purpose of the calculation.
  10476.         {
  10477.             __int64 ARG2_as_int = ATOI64(ARG2);
  10478.             if (!ARG2_as_int)                // v1.0.46: Make behavior more consistent with expressions by
  10479.                 return output_var->Assign(); // avoiding a runtime error dialog; just make the output variable blank.
  10480.             return output_var->Assign(ATOI64(output_var->Contents()) / ARG2_as_int);  // Overload: Assigns an INT.
  10481.         }
  10482.  
  10483.     case ACT_STRINGLEFT:
  10484.         chars_to_extract = ATOI(ARG3); // Use 32-bit signed to detect negatives and fit it VarSizeType.
  10485.         if (chars_to_extract < 0)
  10486.             // For these we don't report an error, since it might be intentional for
  10487.             // it to be called this way, in which case it will do nothing other than
  10488.             // set the output var to be blank.
  10489.             chars_to_extract = 0;
  10490.         else
  10491.         {
  10492.             source_length = ArgLength(2); // Should be quick because Arg2 is an InputVar (except when it's a built-in var perhaps).
  10493.             if (chars_to_extract > (int)source_length)
  10494.                 chars_to_extract = (int)source_length; // Assign() requires a length that's <= the actual length of the string.
  10495.         }
  10496.         // It will display any error that occurs.
  10497.         return output_var->Assign(ARG2, chars_to_extract);
  10498.  
  10499.     case ACT_STRINGRIGHT:
  10500.         chars_to_extract = ATOI(ARG3); // Use 32-bit signed to detect negatives and fit it VarSizeType.
  10501.         if (chars_to_extract < 0)
  10502.             chars_to_extract = 0;
  10503.         source_length = ArgLength(2);
  10504.         if ((UINT)chars_to_extract > source_length)
  10505.             chars_to_extract = (int)source_length;
  10506.         // It will display any error that occurs:
  10507.         return output_var->Assign(ARG2 + source_length - chars_to_extract, chars_to_extract);
  10508.  
  10509.     case ACT_STRINGMID:
  10510.         // v1.0.43.10: Allow chars-to-extract to be blank, which means "get all characters".
  10511.         // However, for backward compatibility, examine the raw arg, not ARG4.  That way, any existing
  10512.         // scripts that use a variable reference or expression that resolves to an empty string will
  10513.         // have the parameter treated as zero (as in previous versions) rather than "all characters".
  10514.         if (mArgc < 4 || !*mArg[3].text)
  10515.             chars_to_extract = INT_MAX;
  10516.         else
  10517.         {
  10518.             chars_to_extract = ATOI(ARG4); // Use 32-bit signed to detect negatives and fit it VarSizeType.
  10519.             if (chars_to_extract < 1)
  10520.                 return output_var->Assign();  // Set it to be blank in this case.
  10521.         }
  10522.         start_char_num = ATOI(ARG3);
  10523.         if (toupper(*ARG5) == 'L')  // Chars to the left of start_char_num will be extracted.
  10524.         {
  10525.             // TRANSLATE "L" MODE INTO THE EQUIVALENT NORMAL MODE:
  10526.             if (start_char_num < 1) // Starting at a character number that is invalid for L mode.
  10527.                 return output_var->Assign();  // Blank seems most appropriate for the L option in this case.
  10528.             start_char_num -= (chars_to_extract - 1);
  10529.             if (start_char_num < 1)
  10530.                 // Reduce chars_to_extract to reflect the fact that there aren't enough chars
  10531.                 // to the left of start_char_num, so we'll extract only them:
  10532.                 chars_to_extract -= (1 - start_char_num);
  10533.         }
  10534.         // ABOVE HAS CONVERTED "L" MODE INTO NORMAL MODE, so "L" no longer needs to be considered below.
  10535.         // UPDATE: The below is also needed for the L option to work correctly.  Older:
  10536.         // It's somewhat debatable, but it seems best not to report an error in this and
  10537.         // other cases.  The result here is probably enough to speak for itself, for script
  10538.         // debugging purposes:
  10539.         if (start_char_num < 1)
  10540.             start_char_num = 1; // 1 is the position of the first char, unlike StringGetPos.
  10541.         source_length = ArgLength(2); // This call seems unavoidable in both "L" mode and normal mode.
  10542.         if (source_length < (UINT)start_char_num) // Source is empty or start_char_num lies to the right of the entire string.
  10543.             return output_var->Assign(); // No chars exist there, so set it to be blank.
  10544.         source_length -= (start_char_num - 1); // Fix for v1.0.44.14: Adjust source_length to be the length starting at start_char_num.  Otherwise, the length passed to Assign() could be too long, and it now expects an accurate length.
  10545.         if ((UINT)chars_to_extract > source_length)
  10546.             chars_to_extract = (int)source_length;
  10547.         return output_var->Assign(ARG2 + start_char_num - 1, chars_to_extract);
  10548.  
  10549.     case ACT_STRINGTRIMLEFT:
  10550.         chars_to_extract = ATOI(ARG3); // Use 32-bit signed to detect negatives and fit it VarSizeType.
  10551.         if (chars_to_extract < 0)
  10552.             chars_to_extract = 0;
  10553.         source_length = ArgLength(2);
  10554.         if ((UINT)chars_to_extract > source_length) // This could be intentional, so don't display an error.
  10555.             chars_to_extract = (int)source_length;
  10556.         return output_var->Assign(ARG2 + chars_to_extract, (VarSizeType)(source_length - chars_to_extract));
  10557.  
  10558.     case ACT_STRINGTRIMRIGHT:
  10559.         chars_to_extract = ATOI(ARG3); // Use 32-bit signed to detect negatives and fit it VarSizeType.
  10560.         if (chars_to_extract < 0)
  10561.             chars_to_extract = 0;
  10562.         source_length = ArgLength(2);
  10563.         if ((UINT)chars_to_extract > source_length) // This could be intentional, so don't display an error.
  10564.             chars_to_extract = (int)source_length;
  10565.         return output_var->Assign(ARG2, (VarSizeType)(source_length - chars_to_extract)); // It already displayed any error.
  10566.  
  10567.     case ACT_STRINGLOWER:
  10568.     case ACT_STRINGUPPER:
  10569.         contents = output_var->Contents(); // Set default.
  10570.         if (contents != ARG2 || output_var->Type() != VAR_NORMAL) // It's compared this way in case ByRef/aliases are involved.  This will detect even them.
  10571.         {
  10572.             // Clipboard is involved and/or source != dest.  Do it the more comprehensive way.
  10573.             // Set up the var, enlarging it if necessary.  If the output_var is of type VAR_CLIPBOARD,
  10574.             // this call will set up the clipboard for writing.
  10575.             // Fix for v1.0.45.02: The v1.0.45 change where the value is assigned directly without sizing the
  10576.             // variable first doesn't work in cases when the variable is the clipboard.  This is because the
  10577.             // clipboard's buffer is changeable (for the case conversion later below) only when using the following
  10578.             // approach, not a simple "assign then modify its Contents()".
  10579.             if (output_var->Assign(NULL, (VarSizeType)ArgLength(2)) != OK)
  10580.                 return FAIL;
  10581.             contents = output_var->Contents(); // Do this only after the above might have changed the contents mem address.
  10582.             // Copy the input variable's text directly into the output variable:
  10583.             strcpy(contents, ARG2);
  10584.         }
  10585.         //else input and output are the same, normal variable; so nothing needs to be copied over.  Just leave
  10586.         // contents at the default set earlier, then convert its case.
  10587.         if (*ARG3 && toupper(*ARG3) == 'T' && !*(ARG3 + 1)) // Convert to title case.
  10588.             StrToTitleCase(contents);
  10589.         else if (mActionType == ACT_STRINGLOWER)
  10590.             CharLower(contents);
  10591.         else
  10592.             CharUpper(contents);
  10593.         return output_var->Close();  // In case it's the clipboard.
  10594.  
  10595.     case ACT_STRINGLEN:
  10596.         return output_var->Assign((__int64)(ARGVARRAW2 && ARGVARRAW2->IsBinaryClip() // Load-time validation has ensured mArgc > 1.
  10597.             ? ARGVARRAW2->Length() + 1 // +1 to include the entire 4-byte terminator, which seems best in this case.
  10598.             : ArgLength(2)));
  10599.         // The above must be kept in sync with the StringLen() function elsewhere.
  10600.  
  10601.     case ACT_STRINGGETPOS:
  10602.     {
  10603.         char *arg4 = ARG4;
  10604.         int pos = -1; // Set default.
  10605.         int occurrence_number;
  10606.         if (*arg4 && strchr("LR", toupper(*arg4)))
  10607.             occurrence_number = *(arg4 + 1) ? ATOI(arg4 + 1) : 1;
  10608.         else
  10609.             occurrence_number = 1;
  10610.         // Intentionally allow occurrence_number to resolve to a negative, for scripting flexibililty:
  10611.         if (occurrence_number > 0)
  10612.         {
  10613.             if (!*ARG3) // It might be intentional, in obscure cases, to search for the empty string.
  10614.                 pos = 0;
  10615.                 // Above: empty string is always found immediately (first char from left) regardless
  10616.                 // of whether the search will be conducted from the right.  This is because it's too
  10617.                 // rare to worry about giving it any more explicit handling based on search direction.
  10618.             else
  10619.             {
  10620.                 char *found, *haystack = ARG2, *needle = ARG3;
  10621.                 int offset = ATOI(ARG5); // v1.0.30.03
  10622.                 if (offset < 0)
  10623.                     offset = 0;
  10624.                 size_t haystack_length = offset ? ArgLength(2) : 1; // Avoids calling ArgLength() if no offset, in which case length isn't needed here.
  10625.                 if (offset < (int)haystack_length)
  10626.                 {
  10627.                     if (*arg4 == '1' || toupper(*arg4) == 'R') // Conduct the search starting at the right side, moving leftward.
  10628.                     {
  10629.                         char prev_char, *terminate_here;
  10630.                         if (offset)
  10631.                         {
  10632.                             terminate_here = haystack + haystack_length - offset;
  10633.                             prev_char = *terminate_here;
  10634.                             *terminate_here = '\0';  // Temporarily terminate for the duration of the search.
  10635.                         }
  10636.                         // Want it to behave like in this example: If searching for the 2nd occurrence of
  10637.                         // FF in the string FFFF, it should find the first two F's, not the middle two:
  10638.                         found = strrstr(haystack, needle, (StringCaseSenseType)g.StringCaseSense, occurrence_number);
  10639.                         if (offset)
  10640.                             *terminate_here = prev_char;
  10641.                     }
  10642.                     else
  10643.                     {
  10644.                         // Want it to behave like in this example: If searching for the 2nd occurrence of
  10645.                         // FF in the string FFFF, it should find position 3 (the 2nd pair), not position 2:
  10646.                         size_t needle_length = ArgLength(3);
  10647.                         int i;
  10648.                         for (i = 1, found = haystack + offset; ; ++i, found += needle_length)
  10649.                             if (!(found = g_strstr(found, needle)) || i == occurrence_number)
  10650.                                 break;
  10651.                     }
  10652.                     if (found)
  10653.                         pos = (int)(found - haystack);
  10654.                     // else leave pos set to its default value, -1.
  10655.                 }
  10656.                 //else offset >= haystack_length, so no match is possible in either left or right mode.
  10657.             }
  10658.         }
  10659.         g_ErrorLevel->Assign(pos < 0 ? ERRORLEVEL_ERROR : ERRORLEVEL_NONE);
  10660.         return output_var->Assign(pos); // Assign() already displayed any error that may have occurred.
  10661.     }
  10662.  
  10663.     case ACT_STRINGREPLACE:
  10664.         return StringReplace();
  10665.  
  10666.     case ACT_TRANSFORM:
  10667.         return Transform(ARG2, ARG3, ARG4);
  10668.  
  10669.     case ACT_STRINGSPLIT:
  10670.         return StringSplit(ARG1, ARG2, ARG3, ARG4);
  10671.  
  10672.     case ACT_SPLITPATH:
  10673.         return SplitPath(ARG1);
  10674.  
  10675.     case ACT_SORT:
  10676.         return PerformSort(ARG1, ARG2);
  10677.  
  10678.     case ACT_PIXELSEARCH:
  10679.         // ATOI() works on ARG7 (the color) because any valid BGR or RGB color has 0x00 in the high order byte:
  10680.         return PixelSearch(ATOI(ARG3), ATOI(ARG4), ATOI(ARG5), ATOI(ARG6), ATOI(ARG7), ATOI(ARG8), ARG9, false);
  10681.     case ACT_IMAGESEARCH:
  10682.         return ImageSearch(ATOI(ARG3), ATOI(ARG4), ATOI(ARG5), ATOI(ARG6), ARG7);
  10683.     case ACT_PIXELGETCOLOR:
  10684.         return PixelGetColor(ATOI(ARG2), ATOI(ARG3), ARG4);
  10685.  
  10686.     case ACT_SEND:
  10687.     case ACT_SENDRAW:
  10688.         SendKeys(ARG1, mActionType == ACT_SENDRAW, g.SendMode);
  10689.         return OK;
  10690.     case ACT_SENDINPUT: // Raw mode is supported via {Raw} in ARG1.
  10691.         SendKeys(ARG1, false, g.SendMode == SM_INPUT_FALLBACK_TO_PLAY ? SM_INPUT_FALLBACK_TO_PLAY : SM_INPUT);
  10692.         return OK;
  10693.     case ACT_SENDPLAY: // Raw mode is supported via {Raw} in ARG1.
  10694.         SendKeys(ARG1, false, SM_PLAY);
  10695.         return OK;
  10696.     case ACT_SENDEVENT:
  10697.         SendKeys(ARG1, false, SM_EVENT);
  10698.         return OK;
  10699.  
  10700.     case ACT_CLICK:
  10701.         return PerformClick(ARG1);
  10702.     case ACT_MOUSECLICKDRAG:
  10703.         return PerformMouse(mActionType, SEVEN_ARGS);
  10704.     case ACT_MOUSECLICK:
  10705.         return PerformMouse(mActionType, THREE_ARGS, "", "", ARG5, ARG7, ARG4, ARG6);
  10706.     case ACT_MOUSEMOVE:
  10707.         return PerformMouse(mActionType, "", ARG1, ARG2, "", "", ARG3, ARG4);
  10708.  
  10709.     case ACT_MOUSEGETPOS:
  10710.         return MouseGetPos(ATOU(ARG5));
  10711.  
  10712.     case ACT_WINACTIVATE:
  10713.     case ACT_WINACTIVATEBOTTOM:
  10714.         if (WinActivate(g, FOUR_ARGS, mActionType == ACT_WINACTIVATEBOTTOM))
  10715.             // It seems best to do these sleeps here rather than in the windowing
  10716.             // functions themselves because that way, the program can use the
  10717.             // windowing functions without being subject to the script's delay
  10718.             // setting (i.e. there are probably cases when we don't need
  10719.             // to wait, such as bringing a message box to the foreground,
  10720.             // since no other actions will be dependent on it actually
  10721.             // having happened:
  10722.             DoWinDelay;
  10723.         return OK;
  10724.  
  10725.     case ACT_WINMINIMIZE:
  10726.     case ACT_WINMAXIMIZE:
  10727.     case ACT_WINRESTORE:
  10728.     case ACT_WINHIDE:
  10729.     case ACT_WINSHOW:
  10730.     case ACT_WINCLOSE:
  10731.     case ACT_WINKILL:
  10732.     {
  10733.         // Set initial guess for is_ahk_group (further refined later).  For ahk_group, WinText,
  10734.         // ExcludeTitle, and ExcludeText must be blank so that they are reserved for future use
  10735.         // (i.e. they're currently not supported since the group's own criteria take precedence):
  10736.         bool is_ahk_group = !(strnicmp(ARG1, "ahk_group", 9) || *ARG2 || *ARG4);
  10737.         // The following is not quite accurate since is_ahk_group is only a guess at this stage, but
  10738.         // given the extreme rarity of the guess being wrong, this shortcut seems justified to reduce
  10739.         // the code size/complexity.  A wait_time of zero seems best for group closing because it's
  10740.         // currently implemented to do the wait after every window in the group.  In addition,
  10741.         // this makes "WinClose ahk_group GroupName" behave identically to "GroupClose GroupName",
  10742.         // which seems best, for consistency:
  10743.         int wait_time = is_ahk_group ? 0 : DEFAULT_WINCLOSE_WAIT;
  10744.         if (mActionType == ACT_WINCLOSE || mActionType == ACT_WINKILL) // ARG3 is contains the wait time.
  10745.         {
  10746.             if (*ARG3 && !(wait_time = (int)(1000 * ATOF(ARG3)))   )
  10747.                 wait_time = 500; // Legacy (prior to supporting floating point): 0 is defined as 500ms, which seems more useful than a true zero.
  10748.             if (*ARG5)
  10749.                 is_ahk_group = false;  // Override the default.
  10750.         }
  10751.         else
  10752.             if (*ARG3)
  10753.                 is_ahk_group = false;  // Override the default.
  10754.         // Act upon all members of this group (WinText/ExcludeTitle/ExcludeText are ignored in this mode).
  10755.         if (is_ahk_group && (group = g_script.FindGroup(omit_leading_whitespace(ARG1 + 9)))) // Assign.
  10756.             return group->ActUponAll(mActionType, wait_time); // It will do DoWinDelay if appropriate.
  10757.         //else try to act upon it as though "ahk_group something" is a literal window title.
  10758.     
  10759.         // Since above didn't return, it's not "ahk_group", so do the normal single-window behavior.
  10760.         if (mActionType == ACT_WINCLOSE || mActionType == ACT_WINKILL)
  10761.         {
  10762.             if (WinClose(g, ARG1, ARG2, wait_time, ARG4, ARG5, mActionType == ACT_WINKILL)) // It closed something.
  10763.                 DoWinDelay;
  10764.             return OK;
  10765.         }
  10766.         else
  10767.             return PerformShowWindow(mActionType, FOUR_ARGS);
  10768.     }
  10769.  
  10770.     case ACT_ENVGET:
  10771.         return EnvGet(ARG2);
  10772.  
  10773.     case ACT_ENVSET:
  10774.         // MSDN: "If [the 2nd] parameter is NULL, the variable is deleted from the current processร†s environment."
  10775.         // My: Though it seems okay, for now, just to set it to be blank if the user omitted the 2nd param or
  10776.         // left it blank (AutoIt3 does this too).  Also, no checking is currently done to ensure that ARG2
  10777.         // isn't longer than 32K, since future OSes may support longer env. vars.  SetEnvironmentVariable()
  10778.         // might return 0(fail) in that case anyway.  Also, ARG1 may be a dereferenced variable that resolves
  10779.         // to the name of an Env. Variable.  In any case, this name need not correspond to any existing
  10780.         // variable name within the script (i.e. script variables and env. variables aren't tied to each other
  10781.         // in any way).  This seems to be the most flexible approach, but are there any shortcomings?
  10782.         // The only one I can think of is that if the script tries to fetch the value of an env. var (perhaps
  10783.         // one that some other spawned program changed), and that var's name corresponds to the name of a
  10784.         // script var, the script var's value (if non-blank) will be fetched instead.
  10785.         // Note: It seems, at least under WinXP, that env variable names can contain spaces.  So it's best
  10786.         // not to validate ARG1 the same way we validate script variables (i.e. just let\
  10787.         // SetEnvironmentVariable()'s return value determine whether there's an error).  However, I just
  10788.         // realized that it's impossible to "retrieve" the value of an env var that has spaces (until now,
  10789.         // since the EnvGet command is available).
  10790.         return g_ErrorLevel->Assign(SetEnvironmentVariable(ARG1, ARG2) ? ERRORLEVEL_NONE : ERRORLEVEL_ERROR);
  10791.  
  10792.     case ACT_ENVUPDATE:
  10793.     {
  10794.         // From the AutoIt3 source:
  10795.         // AutoIt3 uses SMTO_BLOCK (which prevents our thread from doing anything during the call)
  10796.         // vs. SMTO_NORMAL.  Since I'm not sure why, I'm leaving it that way for now:
  10797.         ULONG nResult;
  10798.         if (SendMessageTimeout(HWND_BROADCAST, WM_SETTINGCHANGE, 0, (LPARAM)"Environment", SMTO_BLOCK, 15000, &nResult))
  10799.             return g_ErrorLevel->Assign(ERRORLEVEL_NONE);
  10800.         else
  10801.             return g_ErrorLevel->Assign(ERRORLEVEL_ERROR);
  10802.     }
  10803.  
  10804.     case ACT_URLDOWNLOADTOFILE:
  10805.         return URLDownloadToFile(TWO_ARGS);
  10806.  
  10807.     case ACT_RUNAS:
  10808.         if (!g_os.IsWin2000orLater()) // Do nothing if the OS doesn't support it.
  10809.             return OK;
  10810.         if (mArgc < 1)
  10811.         {
  10812.             if (!g_script.mRunAsUser) // memory not yet allocated so nothing needs to be done.
  10813.                 return OK;
  10814.             *g_script.mRunAsUser = *g_script.mRunAsPass = *g_script.mRunAsDomain = 0; // wide-character terminator.
  10815.             return OK;
  10816.         }
  10817.         // Otherwise, the credentials are being set or updated:
  10818.         if (!g_script.mRunAsUser) // allocate memory (only needed the first time this is done).
  10819.         {
  10820.             // It's more memory efficient to allocate a single block and divide it up.
  10821.             // This memory is freed automatically by the OS upon program termination.
  10822.             if (   !(g_script.mRunAsUser = (WCHAR *)malloc(3 * RUNAS_SIZE_IN_BYTES))   )
  10823.                 return LineError(ERR_OUTOFMEM ERR_ABORT);
  10824.             g_script.mRunAsPass = g_script.mRunAsUser + RUNAS_SIZE_IN_WCHARS;   // Fixed for v1.0.47.01 to use RUNAS_SIZE_IN_WCHARS vs. RUNAS_SIZE_IN_BYTES (since pointer math adds 2 bytes not 1 due to the type of pointer).
  10825.             g_script.mRunAsDomain = g_script.mRunAsPass + RUNAS_SIZE_IN_WCHARS; // 
  10826.         }
  10827.         ToWideChar(ARG1, g_script.mRunAsUser, RUNAS_SIZE_IN_WCHARS);    // Dest. size is in wchars, not bytes.
  10828.         ToWideChar(ARG2, g_script.mRunAsPass, RUNAS_SIZE_IN_WCHARS);    //
  10829.         ToWideChar(ARG3, g_script.mRunAsDomain, RUNAS_SIZE_IN_WCHARS);  //
  10830.         return OK;
  10831.  
  10832.     case ACT_RUN: // Be sure to pass NULL for 2nd param.
  10833.         if (strcasestr(ARG3, "UseErrorLevel"))
  10834.             return g_ErrorLevel->Assign(g_script.ActionExec(ARG1, NULL, ARG2, false, ARG3, NULL, true
  10835.                 , true, ARGVAR4) ? ERRORLEVEL_NONE : "ERROR");
  10836.             // The special string ERROR is used, rather than a number like 1, because currently
  10837.             // RunWait might in the future be able to return any value, including 259 (STATUS_PENDING).
  10838.         else // If launch fails, display warning dialog and terminate current thread.
  10839.             return g_script.ActionExec(ARG1, NULL, ARG2, true, ARG3, NULL, false, true, ARGVAR4);
  10840.  
  10841.     case ACT_RUNWAIT:
  10842.     case ACT_CLIPWAIT:
  10843.     case ACT_KEYWAIT:
  10844.     case ACT_WINWAIT:
  10845.     case ACT_WINWAITCLOSE:
  10846.     case ACT_WINWAITACTIVE:
  10847.     case ACT_WINWAITNOTACTIVE:
  10848.         return PerformWait();
  10849.  
  10850.     case ACT_WINMOVE:
  10851.         return mArgc > 2 ? WinMove(EIGHT_ARGS) : WinMove("", "", ARG1, ARG2);
  10852.  
  10853.     case ACT_WINMENUSELECTITEM:
  10854.         return WinMenuSelectItem(ELEVEN_ARGS);
  10855.  
  10856.     case ACT_CONTROLSEND:
  10857.     case ACT_CONTROLSENDRAW:
  10858.         return ControlSend(SIX_ARGS, mActionType == ACT_CONTROLSENDRAW);
  10859.  
  10860.     case ACT_CONTROLCLICK:
  10861.         if (   !(vk = ConvertMouseButton(ARG4))   ) // Treats blank as "Left".
  10862.             return LineError(ERR_MOUSE_BUTTON ERR_ABORT, FAIL, ARG4);
  10863.         return ControlClick(vk, *ARG5 ? ATOI(ARG5) : 1, ARG6, ARG1, ARG2, ARG3, ARG7, ARG8);
  10864.  
  10865.     case ACT_CONTROLMOVE:
  10866.         return ControlMove(NINE_ARGS);
  10867.     case ACT_CONTROLGETPOS:
  10868.         return ControlGetPos(ARG5, ARG6, ARG7, ARG8, ARG9);
  10869.     case ACT_CONTROLGETFOCUS:
  10870.         return ControlGetFocus(ARG2, ARG3, ARG4, ARG5);
  10871.     case ACT_CONTROLFOCUS:
  10872.         return ControlFocus(FIVE_ARGS);
  10873.     case ACT_CONTROLSETTEXT:
  10874.         return ControlSetText(SIX_ARGS);
  10875.     case ACT_CONTROLGETTEXT:
  10876.         return ControlGetText(ARG2, ARG3, ARG4, ARG5, ARG6);
  10877.     case ACT_CONTROL:
  10878.         return Control(SEVEN_ARGS);
  10879.     case ACT_CONTROLGET:
  10880.         return ControlGet(ARG2, ARG3, ARG4, ARG5, ARG6, ARG7, ARG8);
  10881.     case ACT_STATUSBARGETTEXT:
  10882.         return StatusBarGetText(ARG2, ARG3, ARG4, ARG5, ARG6);
  10883.     case ACT_STATUSBARWAIT:
  10884.         return StatusBarWait(EIGHT_ARGS);
  10885.     case ACT_POSTMESSAGE:
  10886.     case ACT_SENDMESSAGE:
  10887.         return ScriptPostSendMessage(mActionType == ACT_SENDMESSAGE);
  10888.     case ACT_PROCESS:
  10889.         return ScriptProcess(THREE_ARGS);
  10890.     case ACT_WINSET:
  10891.         return WinSet(SIX_ARGS);
  10892.     case ACT_WINSETTITLE:
  10893.         return mArgc > 1 ? WinSetTitle(FIVE_ARGS) : WinSetTitle("", "", ARG1);
  10894.     case ACT_WINGETTITLE:
  10895.         return WinGetTitle(ARG2, ARG3, ARG4, ARG5);
  10896.     case ACT_WINGETCLASS:
  10897.         return WinGetClass(ARG2, ARG3, ARG4, ARG5);
  10898.     case ACT_WINGET:
  10899.         return WinGet(ARG2, ARG3, ARG4, ARG5, ARG6);
  10900.     case ACT_WINGETTEXT:
  10901.         return WinGetText(ARG2, ARG3, ARG4, ARG5);
  10902.     case ACT_WINGETPOS:
  10903.         return WinGetPos(ARG5, ARG6, ARG7, ARG8);
  10904.  
  10905.     case ACT_SYSGET:
  10906.         return SysGet(ARG2, ARG3);
  10907.  
  10908.     case ACT_WINMINIMIZEALL:
  10909.         PostMessage(FindWindow("Shell_TrayWnd", NULL), WM_COMMAND, 419, 0);
  10910.         DoWinDelay;
  10911.         return OK;
  10912.     case ACT_WINMINIMIZEALLUNDO:
  10913.         PostMessage(FindWindow("Shell_TrayWnd", NULL), WM_COMMAND, 416, 0);
  10914.         DoWinDelay;
  10915.         return OK;
  10916.  
  10917.     case ACT_ONEXIT:
  10918.         if (!*ARG1) // Reset to normal Exit behavior.
  10919.         {
  10920.             g_script.mOnExitLabel = NULL;
  10921.             return OK;
  10922.         }
  10923.         // If it wasn't resolved at load-time, it must be a variable reference:
  10924.         if (   !(target_label = (Label *)mAttribute)   )
  10925.             if (   !(target_label = g_script.FindLabel(ARG1))   )
  10926.                 return LineError(ERR_NO_LABEL ERR_ABORT, FAIL, ARG1);
  10927.         g_script.mOnExitLabel = target_label;
  10928.         return OK;
  10929.  
  10930.     case ACT_HOTKEY:
  10931.         // mAttribute is the label resolved at loadtime, if available (for performance).
  10932.         return Hotkey::Dynamic(THREE_ARGS, (Label *)mAttribute);
  10933.  
  10934.     case ACT_SETTIMER: // A timer is being created, changed, or enabled/disabled.
  10935.         // Note that only one timer per label is allowed because the label is the unique identifier
  10936.         // that allows us to figure out whether to "update or create" when searching the list of timers.
  10937.         if (   !(target_label = (Label *)mAttribute)   ) // Since it wasn't resolved at load-time, it must be a variable reference.
  10938.             if (   !(target_label = g_script.FindLabel(ARG1))   )
  10939.                 return LineError(ERR_NO_LABEL ERR_ABORT, FAIL, ARG1);
  10940.         // And don't update mAttribute (leave it NULL) because we want ARG1 to be dynamically resolved
  10941.         // every time the command is executed (in case the contents of the referenced variable change).
  10942.         // In the data structure that holds the timers, we store the target label rather than the target
  10943.         // line so that a label can be registered independently as a timers even if there another label
  10944.         // that points to the same line such as in this example:
  10945.         // Label1::
  10946.         // Label2::
  10947.         // ...
  10948.         // return
  10949.         if (*ARG2)
  10950.         {
  10951.             toggle = Line::ConvertOnOff(ARG2);
  10952.             if (!toggle && !IsPureNumeric(ARG2, true, true, true)) // Allow it to be neg. or floating point at runtime.
  10953.                 return LineError(ERR_PARAM2_INVALID, FAIL, ARG2);
  10954.         }
  10955.         else
  10956.             toggle = TOGGLE_INVALID;
  10957.         // Below relies on distinguishing a true empty string from one that is sent to the function
  10958.         // as empty as a signal.  Don't change it without a full understanding because it's likely
  10959.         // to break compatibility or something else:
  10960.         switch(toggle)
  10961.         {
  10962.         case TOGGLED_ON:  
  10963.         case TOGGLED_OFF: g_script.UpdateOrCreateTimer(target_label, "", ARG3, toggle == TOGGLED_ON, false); break;
  10964.         // Timer is always (re)enabled when ARG2 specifies a numeric period or is blank + there's no ARG3.
  10965.         // If ARG2 is blank but ARG3 (priority) isn't, tell it to update only the priority and nothing else:
  10966.         default: g_script.UpdateOrCreateTimer(target_label, ARG2, ARG3, true, !*ARG2 && *ARG3);
  10967.         }
  10968.         return OK;
  10969.  
  10970.     case ACT_CRITICAL:
  10971.         // For code size reduction, no runtime validation is done (only load-time).  Thus, anything other
  10972.         // than "Off" (especially NEUTRAL) is considered to be "On":
  10973.         toggle = ConvertOnOff(ARG1, NEUTRAL);
  10974.         if (g.ThreadIsCritical = (toggle != TOGGLED_OFF)) // Assign.
  10975.         {
  10976.             // v1.0.46: When the current thread is critical, have the script check messages less often to
  10977.             // reduce situations where an OnMesage or GUI message must be discarded due to "thread already
  10978.             // running".  Using 16 rather than the default of 5 solves reliability problems in a custom-menu-draw
  10979.             // script and probably many similar scripts -- even when the system is under load (though 16 might not
  10980.             // be enough during an extreme load depending on the exact preemption/timeslice dynamics involved).
  10981.             // DON'T GO TOO HIGH because this setting reduces response time for ALL messages, even those that
  10982.             // don't launch script threads (especially painting/drawing and other screen-update events).
  10983.             // Future enhancement: Could allow the value of 16 to be customized via something like "Critical 25".
  10984.             // However, it seems best not to allow it to go too high (say, no more than 2000) because that would
  10985.             // cause the script to completely hang if the critical thread never finishes, or takes a long time
  10986.             // to finish.  A configurable limit might also allow things to work better on Win9x because it has
  10987.             // a bigger tickcount granularity.
  10988.             if (!*ARG1 || toggle == TOGGLED_ON) // i.e. an omitted first arg is the same as "ON".
  10989.                 g.PeekFrequency = 16; // Some hardware has a tickcount granularity of 15 instead of 10, so this covers more variations.
  10990.             else // ARG1 is present but it's not "On" or "Off"; so treat it as a number.
  10991.                 g.PeekFrequency = ATOU(ARG1); // For flexibility (and due to rarity), don't bother checking if too large/small (even if it is it's probably inconsequential).
  10992.             g.AllowThreadToBeInterrupted = false;
  10993.             g.LinesPerCycle = -1;      // v1.0.47: It seems best to ensure SetBatchLines -1 is in effect because
  10994.             g.IntervalBeforeRest = -1; // otherwise it may check messages during the interval that it isn't supposed to.
  10995.         }
  10996.         else
  10997.         {
  10998.             g.PeekFrequency = DEFAULT_PEEK_FREQUENCY;
  10999.             g.AllowThreadToBeInterrupted = true;
  11000.         }
  11001.         // If it's being turned off, allow thread to be immediately interrupted regardless of any
  11002.         // "Thread Interrupt" settings.
  11003.         // Now that the thread's interruptibility has been explicitly set, the script is in charge
  11004.         // of managing this thread's interruptibility, thus kill the timer unconditionally:
  11005.         KILL_UNINTERRUPTIBLE_TIMER  // Done here for maintainability and performance, even though UninterruptibleTimeout() will also kill it.
  11006.         // Although the above kills the timer, it does not remove any WM_TIMER message that it might already
  11007.         // have placed into the queue.  And since we have other types of timers, purging the queue of all
  11008.         // WM_TIMERS would be too great a loss of maintainability and reliability.  To solve this,
  11009.         // UninterruptibleTimeout() checks the value of g.ThreadIsCritical.
  11010.         return OK;
  11011.  
  11012.     case ACT_THREAD:
  11013.         switch (ConvertThreadCommand(ARG1))
  11014.         {
  11015.         case THREAD_CMD_PRIORITY:
  11016.             g.Priority = ATOI(ARG2);
  11017.             break;
  11018.         case THREAD_CMD_INTERRUPT:
  11019.             // If either one is blank, leave that setting as it was before.
  11020.             if (*ARG1)
  11021.                 g_script.mUninterruptibleTime = ATOI(ARG2);  // 32-bit (for compatibility with DWORDs returned by GetTickCount).
  11022.             if (*ARG2)
  11023.                 g_script.mUninterruptedLineCountMax = ATOI(ARG3);  // 32-bit also, to help performance (since huge values seem unnecessary).
  11024.             break;
  11025.         case THREAD_CMD_NOTIMERS:
  11026.             g.AllowTimers = (*ARG2 && ATOI64(ARG2) == 0);
  11027.             break;
  11028.         // If invalid command, do nothing since that is always caught at load-time unless the command
  11029.         // is in a variable reference (very rare in this case).
  11030.         }
  11031.         return OK;
  11032.  
  11033.     case ACT_GROUPADD: // Adding a WindowSpec *to* a group, not adding a group.
  11034.     {
  11035.         if (   !(group = (WinGroup *)mAttribute)   )
  11036.             if (   !(group = g_script.FindGroup(ARG1, true))   )  // Last parameter -> create-if-not-found.
  11037.                 return FAIL;  // It already displayed the error for us.
  11038.         target_label = NULL;
  11039.         if (*ARG4)
  11040.         {
  11041.             if (   !(target_label = (Label *)mRelatedLine)   ) // Jump target hasn't been resolved yet, probably due to it being a deref.
  11042.                 if (   !(target_label = g_script.FindLabel(ARG4))   )
  11043.                     return LineError(ERR_NO_LABEL ERR_ABORT, FAIL, ARG4);
  11044.             // Can't do this because the current line won't be the launching point for the
  11045.             // Gosub.  Instead, the launching point will be the GroupActivate rather than the
  11046.             // GroupAdd, so it will be checked by the GroupActivate or not at all (since it's
  11047.             // not that important in the case of a Gosub -- it's mostly for Goto's):
  11048.             //return IsJumpValid(label->mJumpToLine);
  11049.         }
  11050.         return group->AddWindow(ARG2, ARG3, target_label, ARG5, ARG6);
  11051.     }
  11052.  
  11053.     // Note ACT_GROUPACTIVATE is handled by ExecUntil(), since it's better suited to do the Gosub.
  11054.     case ACT_GROUPDEACTIVATE:
  11055.         if (   !(group = (WinGroup *)mAttribute)   )
  11056.             group = g_script.FindGroup(ARG1);
  11057.         if (group)
  11058.             group->Deactivate(*ARG2 && !stricmp(ARG2, "R"));  // Note: It will take care of DoWinDelay if needed.
  11059.         //else nonexistent group: By design, do nothing.
  11060.         return OK;
  11061.  
  11062.     case ACT_GROUPCLOSE:
  11063.         if (   !(group = (WinGroup *)mAttribute)   )
  11064.             group = g_script.FindGroup(ARG1);
  11065.         if (group)
  11066.             if (*ARG2 && !stricmp(ARG2, "A"))
  11067.                 group->ActUponAll(ACT_WINCLOSE, 0);  // Note: It will take care of DoWinDelay if needed.
  11068.             else
  11069.                 group->CloseAndGoToNext(*ARG2 && !stricmp(ARG2, "R"));  // Note: It will take care of DoWinDelay if needed.
  11070.         //else nonexistent group: By design, do nothing.
  11071.         return OK;
  11072.  
  11073.     case ACT_GETKEYSTATE:
  11074.         return GetKeyJoyState(ARG2, ARG3);
  11075.  
  11076.     case ACT_RANDOM:
  11077.     {
  11078.         if (!output_var) // v1.0.42.03: Special mode to change the seed.
  11079.         {
  11080.             init_genrand(ATOU(ARG2)); // It's documented that an unsigned 32-bit number is required.
  11081.             return OK;
  11082.         }
  11083.         bool use_float = IsPureNumeric(ARG2, true, false, true) == PURE_FLOAT
  11084.             || IsPureNumeric(ARG3, true, false, true) == PURE_FLOAT;
  11085.         if (use_float)
  11086.         {
  11087.             double rand_min = *ARG2 ? ATOF(ARG2) : 0;
  11088.             double rand_max = *ARG3 ? ATOF(ARG3) : INT_MAX;
  11089.             // Seems best not to use ErrorLevel for this command at all, since silly cases
  11090.             // such as Max > Min are too rare.  Swap the two values instead.
  11091.             if (rand_min > rand_max)
  11092.             {
  11093.                 double rand_swap = rand_min;
  11094.                 rand_min = rand_max;
  11095.                 rand_max = rand_swap;
  11096.             }
  11097.             return output_var->Assign((genrand_real1() * (rand_max - rand_min)) + rand_min);
  11098.         }
  11099.         else // Avoid using floating point, where possible, which may improve speed a lot more than expected.
  11100.         {
  11101.             int rand_min = *ARG2 ? ATOI(ARG2) : 0;
  11102.             int rand_max = *ARG3 ? ATOI(ARG3) : INT_MAX;
  11103.             // Seems best not to use ErrorLevel for this command at all, since silly cases
  11104.             // such as Max > Min are too rare.  Swap the two values instead.
  11105.             if (rand_min > rand_max)
  11106.             {
  11107.                 int rand_swap = rand_min;
  11108.                 rand_min = rand_max;
  11109.                 rand_max = rand_swap;
  11110.             }
  11111.             // Do NOT use genrand_real1() to generate random integers because of cases like
  11112.             // min=0 and max=1: we want an even distribution of 1's and 0's in that case, not
  11113.             // something skewed that might result due to rounding/truncation issues caused by
  11114.             // the float method used above:
  11115.             // AutoIt3: __int64 is needed here to do the proper conversion from unsigned long to signed long:
  11116.             return output_var->Assign(   (int)(__int64(genrand_int32()
  11117.                 % ((__int64)rand_max - rand_min + 1)) + rand_min)   );
  11118.         }
  11119.     }
  11120.  
  11121.     case ACT_DRIVESPACEFREE:
  11122.         return DriveSpace(ARG2, true);
  11123.  
  11124.     case ACT_DRIVE:
  11125.         return Drive(THREE_ARGS);
  11126.  
  11127.     case ACT_DRIVEGET:
  11128.         return DriveGet(ARG2, ARG3);
  11129.  
  11130.     case ACT_SOUNDGET:
  11131.     case ACT_SOUNDSET:
  11132.         device_id = *ARG4 ? ATOI(ARG4) - 1 : 0;
  11133.         if (device_id < 0)
  11134.             device_id = 0;
  11135.         instance_number = 1;  // Set default.
  11136.         component_type = *ARG2 ? SoundConvertComponentType(ARG2, &instance_number) : MIXERLINE_COMPONENTTYPE_DST_SPEAKERS;
  11137.         return SoundSetGet(mActionType == ACT_SOUNDGET ? NULL : ARG1
  11138.             , component_type, instance_number  // Which instance of this component, 1 = first
  11139.             , *ARG3 ? SoundConvertControlType(ARG3) : MIXERCONTROL_CONTROLTYPE_VOLUME  // Default
  11140.             , (UINT)device_id);
  11141.  
  11142.     case ACT_SOUNDGETWAVEVOLUME:
  11143.     case ACT_SOUNDSETWAVEVOLUME:
  11144.         device_id = *ARG2 ? ATOI(ARG2) - 1 : 0;
  11145.         if (device_id < 0)
  11146.             device_id = 0;
  11147.         return (mActionType == ACT_SOUNDGETWAVEVOLUME) ? SoundGetWaveVolume((HWAVEOUT)device_id)
  11148.             : SoundSetWaveVolume(ARG1, (HWAVEOUT)device_id);
  11149.  
  11150.     case ACT_SOUNDBEEP:
  11151.         // For simplicity and support for future/greater capabilities, no range checking is done.
  11152.         // It simply calls the function with the two DWORD values provided. It avoids setting
  11153.         // ErrorLevel because failure is rare and also because a script might want play a beep
  11154.         // right before displaying an error dialog that uses the previous value of ErrorLevel.
  11155.         Beep(*ARG1 ? ATOU(ARG1) : 523, *ARG2 ? ATOU(ARG2) : 150);
  11156.         return OK;
  11157.  
  11158.     case ACT_SOUNDPLAY:
  11159.         return SoundPlay(ARG1, *ARG2 && !stricmp(ARG2, "wait") || !stricmp(ARG2, "1"));
  11160.  
  11161.     case ACT_FILEAPPEND:
  11162.         // Uses the read-file loop's current item filename was explicitly leave blank (i.e. not just
  11163.         // a reference to a variable that's blank):
  11164.         return FileAppend(ARG2, ARG1, (mArgc < 2) ? g.mLoopReadFile : NULL);
  11165.  
  11166.     case ACT_FILEREAD:
  11167.         return FileRead(ARG2);
  11168.  
  11169.     case ACT_FILEREADLINE:
  11170.         return FileReadLine(ARG2, ARG3);
  11171.  
  11172.     case ACT_FILEDELETE:
  11173.         return FileDelete();
  11174.  
  11175.     case ACT_FILERECYCLE:
  11176.         return FileRecycle(ARG1);
  11177.  
  11178.     case ACT_FILERECYCLEEMPTY:
  11179.         return FileRecycleEmpty(ARG1);
  11180.  
  11181.     case ACT_FILEINSTALL:
  11182.         return FileInstall(THREE_ARGS);
  11183.  
  11184.     case ACT_FILECOPY:
  11185.     {
  11186.         int error_count = Util_CopyFile(ARG1, ARG2, ATOI(ARG3) == 1, false);
  11187.         if (!error_count)
  11188.             return g_ErrorLevel->Assign(ERRORLEVEL_NONE);
  11189.         if (g_script.mIsAutoIt2)
  11190.             return g_ErrorLevel->Assign(ERRORLEVEL_ERROR); // For backward compatibility with v2.
  11191.         return g_ErrorLevel->Assign(error_count);
  11192.     }
  11193.     case ACT_FILEMOVE:
  11194.         return g_ErrorLevel->Assign(Util_CopyFile(ARG1, ARG2, ATOI(ARG3) == 1, true));
  11195.     case ACT_FILECOPYDIR:
  11196.         return g_ErrorLevel->Assign(Util_CopyDir(ARG1, ARG2, ATOI(ARG3) == 1) ? ERRORLEVEL_NONE : ERRORLEVEL_ERROR);
  11197.     case ACT_FILEMOVEDIR:
  11198.         if (toupper(*ARG3) == 'R')
  11199.         {
  11200.             // Perform a simple rename instead, which prevents the operation from being only partially
  11201.             // complete if the source directory is in use (due to being a working dir for a currently
  11202.             // running process, or containing a file that is being written to).  In other words,
  11203.             // the operation will be "all or none":
  11204.             g_ErrorLevel->Assign(MoveFile(ARG1, ARG2) ? ERRORLEVEL_NONE : ERRORLEVEL_ERROR);
  11205.             return OK;
  11206.         }
  11207.         // Otherwise:
  11208.         return g_ErrorLevel->Assign(Util_MoveDir(ARG1, ARG2, ATOI(ARG3)) ? ERRORLEVEL_NONE : ERRORLEVEL_ERROR);
  11209.  
  11210.     case ACT_FILECREATEDIR:
  11211.         return FileCreateDir(ARG1);
  11212.     case ACT_FILEREMOVEDIR:
  11213.         if (!*ARG1) // Consider an attempt to create or remove a blank dir to be an error.
  11214.             return g_ErrorLevel->Assign(ERRORLEVEL_ERROR);
  11215.         return g_ErrorLevel->Assign(Util_RemoveDir(ARG1, ATOI(ARG2) == 1) ? ERRORLEVEL_NONE : ERRORLEVEL_ERROR);
  11216.  
  11217.     case ACT_FILEGETATTRIB:
  11218.         // The specified ARG, if non-blank, takes precedence over the file-loop's file (if any):
  11219.         #define USE_FILE_LOOP_FILE_IF_ARG_BLANK(arg) (*arg ? arg : (g.mLoopFile ? g.mLoopFile->cFileName : ""))
  11220.         return FileGetAttrib(USE_FILE_LOOP_FILE_IF_ARG_BLANK(ARG2));
  11221.     case ACT_FILESETATTRIB:
  11222.         FileSetAttrib(ARG1, USE_FILE_LOOP_FILE_IF_ARG_BLANK(ARG2), ConvertLoopMode(ARG3), ATOI(ARG4) == 1);
  11223.         return OK; // Always return OK since ErrorLevel will indicate if there was a problem.
  11224.     case ACT_FILEGETTIME:
  11225.         return FileGetTime(USE_FILE_LOOP_FILE_IF_ARG_BLANK(ARG2), *ARG3);
  11226.     case ACT_FILESETTIME:
  11227.         FileSetTime(ARG1, USE_FILE_LOOP_FILE_IF_ARG_BLANK(ARG2), *ARG3, ConvertLoopMode(ARG4), ATOI(ARG5) == 1);
  11228.         return OK; // Always return OK since ErrorLevel will indicate if there was a problem.
  11229.     case ACT_FILEGETSIZE:
  11230.         return FileGetSize(USE_FILE_LOOP_FILE_IF_ARG_BLANK(ARG2), ARG3);
  11231.     case ACT_FILEGETVERSION:
  11232.         return FileGetVersion(USE_FILE_LOOP_FILE_IF_ARG_BLANK(ARG2));
  11233.  
  11234.     case ACT_SETWORKINGDIR:
  11235.         SetWorkingDir(ARG1);
  11236.         return OK;
  11237.  
  11238.     case ACT_FILESELECTFILE:
  11239.         return FileSelectFile(ARG2, ARG3, ARG4, ARG5);
  11240.  
  11241.     case ACT_FILESELECTFOLDER:
  11242.         return FileSelectFolder(ARG2, ARG3, ARG4);
  11243.  
  11244.     case ACT_FILEGETSHORTCUT:
  11245.         return FileGetShortcut(ARG1);
  11246.     case ACT_FILECREATESHORTCUT:
  11247.         return FileCreateShortcut(NINE_ARGS);
  11248.  
  11249.     case ACT_KEYHISTORY:
  11250. #ifdef ENABLE_KEY_HISTORY_FILE
  11251.         if (*ARG1 || *ARG2)
  11252.         {
  11253.             switch (ConvertOnOffToggle(ARG1))
  11254.             {
  11255.             case NEUTRAL:
  11256.             case TOGGLE:
  11257.                 g_KeyHistoryToFile = !g_KeyHistoryToFile;
  11258.                 if (!g_KeyHistoryToFile)
  11259.                     KeyHistoryToFile();  // Signal it to close the file, if it's open.
  11260.                 break;
  11261.             case TOGGLED_ON:
  11262.                 g_KeyHistoryToFile = true;
  11263.                 break;
  11264.             case TOGGLED_OFF:
  11265.                 g_KeyHistoryToFile = false;
  11266.                 KeyHistoryToFile();  // Signal it to close the file, if it's open.
  11267.                 break;
  11268.             // We know it's a variable because otherwise the loading validation would have caught it earlier:
  11269.             case TOGGLE_INVALID:
  11270.                 return LineError(ERR_PARAM1_INVALID, FAIL, ARG1);
  11271.             }
  11272.             if (*ARG2) // The user also specified a filename, so update the target filename.
  11273.                 KeyHistoryToFile(ARG2);
  11274.             return OK;
  11275.         }
  11276. #endif
  11277.         // Otherwise:
  11278.         return ShowMainWindow(MAIN_MODE_KEYHISTORY, false); // Pass "unrestricted" when the command is explicitly used in the script.
  11279.     case ACT_LISTLINES:
  11280.         return ShowMainWindow(MAIN_MODE_LINES, false); // Pass "unrestricted" when the command is explicitly used in the script.
  11281.     case ACT_LISTVARS:
  11282.         return ShowMainWindow(MAIN_MODE_VARS, false); // Pass "unrestricted" when the command is explicitly used in the script.
  11283.     case ACT_LISTHOTKEYS:
  11284.         return ShowMainWindow(MAIN_MODE_HOTKEYS, false); // Pass "unrestricted" when the command is explicitly used in the script.
  11285.  
  11286.     case ACT_MSGBOX:
  11287.     {
  11288.         int result;
  11289.         HWND dialog_owner = THREAD_DIALOG_OWNER; // Resolve macro only once to reduce code size.
  11290.         // If the MsgBox window can't be displayed for any reason, always return FAIL to
  11291.         // the caller because it would be unsafe to proceed with the execution of the
  11292.         // current script subroutine.  For example, if the script contains an IfMsgBox after,
  11293.         // this line, it's result would be unpredictable and might cause the subroutine to perform
  11294.         // the opposite action from what was intended (e.g. Delete vs. don't delete a file).
  11295.         if (!mArgc) // When called explicitly with zero params, it displays this default msg.
  11296.             result = MsgBox("Press OK to continue.", MSGBOX_NORMAL, NULL, 0, dialog_owner);
  11297.         else if (mArgc == 1) // In the special 1-parameter mode, the first param is the prompt.
  11298.             result = MsgBox(ARG1, MSGBOX_NORMAL, NULL, 0, dialog_owner);
  11299.         else
  11300.             result = MsgBox(ARG3, ATOI(ARG1), ARG2, ATOF(ARG4), dialog_owner); // dialog_owner passed via parameter to avoid internally-displayed MsgBoxes from being affected by script-thread's owner setting.
  11301.         // Above allows backward compatibility with AutoIt2's param ordering while still
  11302.         // permitting the new method of allowing only a single param.
  11303.         // v1.0.40.01: Rather than displaying another MsgBox in response to a failed attempt to display
  11304.         // a MsgBox, it seems better (less likely to cause trouble) just to abort the thread.  This also
  11305.         // solves a double-msgbox issue when the maximum number of MsgBoxes is reached.  In addition, the
  11306.         // max-msgbox limit is the most common reason for failure, in which case a warning dialog has
  11307.         // already been displayed, so there is no need to display another:
  11308.         //if (!result)
  11309.         //    // It will fail if the text is too large (say, over 150K or so on XP), but that
  11310.         //    // has since been fixed by limiting how much it tries to display.
  11311.         //    // If there were too many message boxes displayed, it will already have notified
  11312.         //    // the user of this via a final MessageBox dialog, so our call here will
  11313.         //    // not have any effect.  The below only takes effect if MsgBox()'s call to
  11314.         //    // MessageBox() failed in some unexpected way:
  11315.         //    LineError("The MsgBox could not be displayed." ERR_ABORT);
  11316.         return result ? OK : FAIL;
  11317.     }
  11318.  
  11319.     case ACT_INPUTBOX:
  11320.         return InputBox(output_var, ARG2, ARG3, toupper(*ARG4) == 'H' // 4th is whether to hide input.
  11321.             , *ARG5 ? ATOI(ARG5) : INPUTBOX_DEFAULT  // Width
  11322.             , *ARG6 ? ATOI(ARG6) : INPUTBOX_DEFAULT  // Height
  11323.             , *ARG7 ? ATOI(ARG7) : INPUTBOX_DEFAULT  // Xpos
  11324.             , *ARG8 ? ATOI(ARG8) : INPUTBOX_DEFAULT  // Ypos
  11325.             // ARG9: future use for Font name & size, e.g. "Courier:8"
  11326.             , ATOF(ARG10)  // Timeout
  11327.             , ARG11  // Initial default string for the edit field.
  11328.             );
  11329.  
  11330.     case ACT_SPLASHTEXTON:
  11331.         return SplashTextOn(*ARG1 ? ATOI(ARG1) : 200, *ARG2 ? ATOI(ARG2) : 0, ARG3, ARG4);
  11332.     case ACT_SPLASHTEXTOFF:
  11333.         DESTROY_SPLASH
  11334.         return OK;
  11335.  
  11336.     case ACT_PROGRESS:
  11337.         return Splash(FIVE_ARGS, "", false);  // ARG6 is for future use and currently not passed.
  11338.  
  11339.     case ACT_SPLASHIMAGE:
  11340.         return Splash(ARG2, ARG3, ARG4, ARG5, ARG6, ARG1, true);  // ARG7 is for future use and currently not passed.
  11341.  
  11342.     case ACT_TOOLTIP:
  11343.         return ToolTip(FOUR_ARGS);
  11344.  
  11345.     case ACT_TRAYTIP:
  11346.         return TrayTip(FOUR_ARGS);
  11347.  
  11348.     case ACT_INPUT:
  11349.         return Input();
  11350.  
  11351.  
  11352. //////////////////////////////////////////////////////////////////////////
  11353.  
  11354.     case ACT_COORDMODE:
  11355.     {
  11356.         bool screen_mode;
  11357.         if (!*ARG2 || !stricmp(ARG2, "Screen"))
  11358.             screen_mode = true;
  11359.         else if (!stricmp(ARG2, "Relative"))
  11360.             screen_mode = false;
  11361.         else  // Since validated at load-time, too rare to return FAIL for.
  11362.             return OK;
  11363.         CoordModeAttribType attrib = ConvertCoordModeAttrib(ARG1);
  11364.         if (attrib)
  11365.         {
  11366.             if (screen_mode)
  11367.                 g.CoordMode |= attrib;
  11368.             else
  11369.                 g.CoordMode &= ~attrib;
  11370.         }
  11371.         //else too rare to report an error, since load-time validation normally catches it.
  11372.         return OK;
  11373.     }
  11374.  
  11375.     case ACT_SETDEFAULTMOUSESPEED:
  11376.         g.DefaultMouseSpeed = (UCHAR)ATOI(ARG1);
  11377.         // In case it was a deref, force it to be some default value if it's out of range:
  11378.         if (g.DefaultMouseSpeed < 0 || g.DefaultMouseSpeed > MAX_MOUSE_SPEED)
  11379.             g.DefaultMouseSpeed = DEFAULT_MOUSE_SPEED;
  11380.         return OK;
  11381.  
  11382.     case ACT_SENDMODE:
  11383.         g.SendMode = ConvertSendMode(ARG1, g.SendMode); // Leave value unchanged if ARG1 is invalid.
  11384.         return OK;
  11385.  
  11386.     case ACT_SETKEYDELAY:
  11387.         if (!stricmp(ARG3, "Play"))
  11388.         {
  11389.             if (*ARG1)
  11390.                 g.KeyDelayPlay = ATOI(ARG1);
  11391.             if (*ARG2)
  11392.                 g.PressDurationPlay = ATOI(ARG2);
  11393.         }
  11394.         else
  11395.         {
  11396.             if (*ARG1)
  11397.                 g.KeyDelay = ATOI(ARG1);
  11398.             if (*ARG2)
  11399.                 g.PressDuration = ATOI(ARG2);
  11400.         }
  11401.         return OK;
  11402.     case ACT_SETMOUSEDELAY:
  11403.         if (!stricmp(ARG2, "Play"))
  11404.             g.MouseDelayPlay = ATOI(ARG1);
  11405.         else
  11406.             g.MouseDelay = ATOI(ARG1);
  11407.         return OK;
  11408.     case ACT_SETWINDELAY:
  11409.         g.WinDelay = ATOI(ARG1);
  11410.         return OK;
  11411.     case ACT_SETCONTROLDELAY:
  11412.         g.ControlDelay = ATOI(ARG1);
  11413.         return OK;
  11414.  
  11415.     case ACT_SETBATCHLINES:
  11416.         // This below ensures that IntervalBeforeRest and LinesPerCycle aren't both in effect simultaneously
  11417.         // (i.e. that both aren't greater than -1), even though ExecUntil() has code to prevent a double-sleep
  11418.         // even if that were to happen.
  11419.         if (strcasestr(ARG1, "ms")) // This detection isn't perfect, but it doesn't seem necessary to be too demanding.
  11420.         {
  11421.             g.LinesPerCycle = -1;  // Disable the old BatchLines method in favor of the new one below.
  11422.             g.IntervalBeforeRest = ATOI(ARG1);  // If negative, script never rests.  If 0, it rests after every line.
  11423.         }
  11424.         else
  11425.         {
  11426.             g.IntervalBeforeRest = -1;  // Disable the new method in favor of the old one below:
  11427.             // This value is signed 64-bits to support variable reference (i.e. containing a large int)
  11428.             // the user might throw at it:
  11429.             if (   !(g.LinesPerCycle = ATOI64(ARG1))   )
  11430.                 // Don't interpret zero as "infinite" because zero can accidentally
  11431.                 // occur if the dereferenced var was blank:
  11432.                 g.LinesPerCycle = 10;  // The old default, which is retained for compatbility with existing scripts.
  11433.         }
  11434.         return OK;
  11435.  
  11436.     case ACT_SETSTORECAPSLOCKMODE:
  11437.         if (   (toggle = ConvertOnOff(ARG1, NEUTRAL)) != NEUTRAL   )
  11438.             g.StoreCapslockMode = (toggle == TOGGLED_ON);
  11439.         return OK;
  11440.  
  11441.     case ACT_SETTITLEMATCHMODE:
  11442.         switch (ConvertTitleMatchMode(ARG1))
  11443.         {
  11444.         case FIND_IN_LEADING_PART: g.TitleMatchMode = FIND_IN_LEADING_PART; return OK;
  11445.         case FIND_ANYWHERE: g.TitleMatchMode = FIND_ANYWHERE; return OK;
  11446.         case FIND_REGEX: g.TitleMatchMode = FIND_REGEX; return OK;
  11447.         case FIND_EXACT: g.TitleMatchMode = FIND_EXACT; return OK;
  11448.         case FIND_FAST: g.TitleFindFast = true; return OK;
  11449.         case FIND_SLOW: g.TitleFindFast = false; return OK;
  11450.         }
  11451.         return LineError(ERR_TITLEMATCHMODE ERR_ABORT, FAIL, ARG1);
  11452.  
  11453.     case ACT_SETFORMAT:
  11454.         // For now, it doesn't seem necessary to have runtime validation of the first parameter.
  11455.         // Just ignore the command if it's not valid:
  11456.         if (!stricmp(ARG1, "Float"))
  11457.         {
  11458.             // -2 to allow room for the letter 'f' and the '%' that will be added:
  11459.             if (ArgLength(2) >= sizeof(g.FormatFloat) - 2) // A variable that resolved to something too long.
  11460.                 return OK; // Seems best not to bother with a runtime error for something so rare.
  11461.             // Make sure the formatted string wouldn't exceed the buffer size:
  11462.             __int64 width = ATOI64(ARG2);
  11463.             char *dot_pos = strchr(ARG2, '.');
  11464.             __int64 precision = dot_pos ? ATOI64(dot_pos + 1) : 0;
  11465.             if (width + precision + 2 > MAX_FORMATTED_NUMBER_LENGTH) // +2 to allow room for decimal point itself and leading minus sign.
  11466.                 return OK; // Don't change it.
  11467.             // Create as "%ARG2f".  Note that %f can handle doubles in MSVC++:
  11468.             sprintf(g.FormatFloat, "%%%s%s%s", ARG2
  11469.                 , dot_pos ? "" : "." // Add a dot if none was specified so that "0" is the same as "0.", which seems like the most user-friendly approach; it's also easier to document in the help file.
  11470.                 , IsPureNumeric(ARG2, true, true, true) ? "f" : ""); // If it's not pure numeric, assume the user already included the desired letter (e.g. SetFormat, Float, 0.6e).
  11471.         }
  11472.         else if (!stricmp(ARG1, "Integer"))
  11473.         {
  11474.             switch(*ARG2)
  11475.             {
  11476.             case 'd':
  11477.             case 'D':
  11478.                 g.FormatIntAsHex = false;
  11479.                 break;
  11480.             case 'h':
  11481.             case 'H':
  11482.                 g.FormatIntAsHex = true;
  11483.                 break;
  11484.             // Otherwise, since the first letter isn't recongized, do nothing since 99% of the time such a
  11485.             // probably would be caught at load-time.
  11486.             }
  11487.         }
  11488.         // Otherwise, ignore invalid type at runtime since 99% of the time it would be caught at load-time:
  11489.         return OK;
  11490.  
  11491.     case ACT_FORMATTIME:
  11492.         return FormatTime(ARG2, ARG3);
  11493.  
  11494.     case ACT_MENU:
  11495.         return g_script.PerformMenu(FIVE_ARGS);
  11496.  
  11497.     case ACT_GUI:
  11498.         return g_script.PerformGui(FOUR_ARGS);
  11499.  
  11500.     case ACT_GUICONTROL:
  11501.         return GuiControl(THREE_ARGS);
  11502.  
  11503.     case ACT_GUICONTROLGET:
  11504.         return GuiControlGet(ARG2, ARG3, ARG4);
  11505.  
  11506.     ////////////////////////////////////////////////////////////////////////////////////////
  11507.     // For these, it seems best not to report an error during runtime if there's
  11508.     // an invalid value (e.g. something other than On/Off/Blank) in a param containing
  11509.     // a dereferenced variable, since settings are global and affect all subroutines,
  11510.     // not just the one that we would otherwise report failure for:
  11511.     case ACT_SUSPEND:
  11512.         switch (ConvertOnOffTogglePermit(ARG1))
  11513.         {
  11514.         case NEUTRAL:
  11515.         case TOGGLE:
  11516.             ToggleSuspendState();
  11517.             break;
  11518.         case TOGGLED_ON:
  11519.             if (!g_IsSuspended)
  11520.                 ToggleSuspendState();
  11521.             break;
  11522.         case TOGGLED_OFF:
  11523.             if (g_IsSuspended)
  11524.                 ToggleSuspendState();
  11525.             break;
  11526.         case TOGGLE_PERMIT:
  11527.             // In this case do nothing.  The user is just using this command as a flag to indicate that
  11528.             // this subroutine should not be suspended.
  11529.             break;
  11530.         // We know it's a variable because otherwise the loading validation would have caught it earlier:
  11531.         case TOGGLE_INVALID:
  11532.             return LineError(ERR_PARAM1_INVALID, FAIL, ARG1);
  11533.         }
  11534.         return OK;
  11535.     case ACT_PAUSE:
  11536.         return ChangePauseState(ConvertOnOffToggle(ARG1), (bool)ATOI(ARG2));
  11537.     case ACT_AUTOTRIM:
  11538.         if (   (toggle = ConvertOnOff(ARG1, NEUTRAL)) != NEUTRAL   )
  11539.             g.AutoTrim = (toggle == TOGGLED_ON);
  11540.         return OK;
  11541.     case ACT_STRINGCASESENSE:
  11542.         if ((g.StringCaseSense = ConvertStringCaseSense(ARG1)) == SCS_INVALID)
  11543.             g.StringCaseSense = SCS_INSENSITIVE; // For simplicity, just fall back to default if value is invalid (normally its caught at load-time; only rarely here).
  11544.         return OK;
  11545.     case ACT_DETECTHIDDENWINDOWS:
  11546.         if (   (toggle = ConvertOnOff(ARG1, NEUTRAL)) != NEUTRAL   )
  11547.             g.DetectHiddenWindows = (toggle == TOGGLED_ON);
  11548.         return OK;
  11549.     case ACT_DETECTHIDDENTEXT:
  11550.         if (   (toggle = ConvertOnOff(ARG1, NEUTRAL)) != NEUTRAL   )
  11551.             g.DetectHiddenText = (toggle == TOGGLED_ON);
  11552.         return OK;
  11553.     case ACT_BLOCKINPUT:
  11554.         switch (toggle = ConvertBlockInput(ARG1))
  11555.         {
  11556.         case TOGGLED_ON:
  11557.             ScriptBlockInput(true);
  11558.             break;
  11559.         case TOGGLED_OFF:
  11560.             ScriptBlockInput(false);
  11561.             break;
  11562.         case TOGGLE_SEND:
  11563.         case TOGGLE_MOUSE:
  11564.         case TOGGLE_SENDANDMOUSE:
  11565.         case TOGGLE_DEFAULT:
  11566.             g_BlockInputMode = toggle;
  11567.             break;
  11568.         case TOGGLE_MOUSEMOVE:
  11569.             g_BlockMouseMove = true;
  11570.             Hotkey::InstallMouseHook();
  11571.             break;
  11572.         case TOGGLE_MOUSEMOVEOFF:
  11573.             g_BlockMouseMove = false; // But the mouse hook is left installed because it might be needed by other things. This approach is similar to that used by the Input command.
  11574.             break;
  11575.         // default (NEUTRAL or TOGGLE_INVALID): do nothing.
  11576.         }
  11577.         return OK;
  11578.  
  11579.     ////////////////////////////////////////////////////////////////////////////////////////
  11580.     // For these, it seems best not to report an error during runtime if there's
  11581.     // an invalid value (e.g. something other than On/Off/Blank) in a param containing
  11582.     // a dereferenced variable, since settings are global and affect all subroutines,
  11583.     // not just the one that we would otherwise report failure for:
  11584.     case ACT_SETNUMLOCKSTATE:
  11585.         return SetToggleState(VK_NUMLOCK, g_ForceNumLock, ARG1);
  11586.     case ACT_SETCAPSLOCKSTATE:
  11587.         return SetToggleState(VK_CAPITAL, g_ForceCapsLock, ARG1);
  11588.     case ACT_SETSCROLLLOCKSTATE:
  11589.         return SetToggleState(VK_SCROLL, g_ForceScrollLock, ARG1);
  11590.  
  11591.     case ACT_EDIT:
  11592.         g_script.Edit();
  11593.         return OK;
  11594.     case ACT_RELOAD:
  11595.         g_script.Reload(true);
  11596.         // Even if the reload failed, it seems best to return OK anyway.  That way,
  11597.         // the script can take some follow-on action, e.g. it can sleep for 1000
  11598.         // after issuing the reload command and then take action under the assumption
  11599.         // that the reload didn't work (since obviously if the process and thread
  11600.         // in which the Sleep is running still exist, it didn't work):
  11601.         return OK;
  11602.  
  11603.     case ACT_SLEEP:
  11604.     {
  11605.         // Only support 32-bit values for this command, since it seems unlikely anyone would to have
  11606.         // it sleep more than 24.8 days or so.  It also helps performance on 32-bit hardware because
  11607.         // MsgSleep() is so heavily called and checks the value of the first parameter frequently:
  11608.         int sleep_time = ATOI(ARG1); // Keep it signed vs. unsigned for backward compatibility (e.g. scripts that do Sleep -1).
  11609.  
  11610.         // Do a true sleep on Win9x because the MsgSleep() method is very inaccurate on Win9x
  11611.         // for some reason (a MsgSleep(1) causes a sleep between 10 and 55ms, for example).
  11612.         // But only do so for short sleeps, for which the user has a greater expectation of
  11613.         // accuracy.  UPDATE: Do not change the 25 below without also changing it in Critical's
  11614.         // documentation.
  11615.         if (sleep_time < 25 && sleep_time > 0 && g_os.IsWin9x()) // Ordered for short-circuit performance. v1.0.38.05: Added "sleep_time > 0" so that Sleep -1/0 will work the same on Win9x as it does on other OSes.
  11616.             Sleep(sleep_time);
  11617.         else
  11618.             MsgSleep(sleep_time);
  11619.         return OK;
  11620.     }
  11621.  
  11622.     case ACT_INIREAD:
  11623.         return IniRead(ARG2, ARG3, ARG4, ARG5);
  11624.     case ACT_INIWRITE:
  11625.         return IniWrite(FOUR_ARGS);
  11626.     case ACT_INIDELETE:
  11627.         // To preserve maximum compatibility with existing scripts, only send NULL if ARG3
  11628.         // was explicitly omitted.  This is because some older scripts might rely on the
  11629.         // fact that a blank ARG3 does not delete the entire section, but rather does
  11630.         // nothing (that fact is untested):
  11631.         return IniDelete(ARG1, ARG2, mArgc < 3 ? NULL : ARG3);
  11632.  
  11633.     case ACT_REGREAD:
  11634.         if (mArgc < 2 && g.mLoopRegItem) // Uses the registry loop's current item.
  11635.             // If g.mLoopRegItem->name specifies a subkey rather than a value name, do this anyway
  11636.             // so that it will set ErrorLevel to ERROR and set the output variable to be blank.
  11637.             // Also, do not use RegCloseKey() on this, even if it's a remote key, since our caller handles that:
  11638.             return RegRead(g.mLoopRegItem->root_key, g.mLoopRegItem->subkey, g.mLoopRegItem->name);
  11639.         // Otherwise:
  11640.         if (mArgc > 4 || RegConvertValueType(ARG2)) // The obsolete 5-param method (ARG2 is unused).
  11641.             result = RegRead(root_key = RegConvertRootKey(ARG3, &is_remote_registry), ARG4, ARG5);
  11642.         else
  11643.             result = RegRead(root_key = RegConvertRootKey(ARG2, &is_remote_registry), ARG3, ARG4);
  11644.         if (is_remote_registry && root_key) // Never try to close local root keys, which the OS keeps always-open.
  11645.             RegCloseKey(root_key);
  11646.         return result;
  11647.     case ACT_REGWRITE:
  11648.         if (mArgc < 2 && g.mLoopRegItem) // Uses the registry loop's current item.
  11649.             // If g.mLoopRegItem->name specifies a subkey rather than a value name, do this anyway
  11650.             // so that it will set ErrorLevel to ERROR.  An error will also be indicated if
  11651.             // g.mLoopRegItem->type is an unsupported type:
  11652.             return RegWrite(g.mLoopRegItem->type, g.mLoopRegItem->root_key, g.mLoopRegItem->subkey, g.mLoopRegItem->name, ARG1);
  11653.         // Otherwise:
  11654.         result = RegWrite(RegConvertValueType(ARG1), root_key = RegConvertRootKey(ARG2, &is_remote_registry)
  11655.             , ARG3, ARG4, ARG5); // If RegConvertValueType(ARG1) yields REG_NONE, RegWrite() will set ErrorLevel rather than displaying a runtime error.
  11656.         if (is_remote_registry && root_key) // Never try to close local root keys, which the OS keeps always-open.
  11657.             RegCloseKey(root_key);
  11658.         return result;
  11659.     case ACT_REGDELETE:
  11660.         if (mArgc < 1 && g.mLoopRegItem) // Uses the registry loop's current item.
  11661.         {
  11662.             // In this case, if the current reg item is a value, just delete it normally.
  11663.             // But if it's a subkey, append it to the dir name so that the proper subkey
  11664.             // will be deleted as the user intended:
  11665.             if (g.mLoopRegItem->type == REG_SUBKEY)
  11666.             {
  11667.                 snprintf(buf_temp, sizeof(buf_temp), "%s\\%s", g.mLoopRegItem->subkey, g.mLoopRegItem->name);
  11668.                 return RegDelete(g.mLoopRegItem->root_key, buf_temp, "");
  11669.             }
  11670.             else
  11671.                 return RegDelete(g.mLoopRegItem->root_key, g.mLoopRegItem->subkey, g.mLoopRegItem->name);
  11672.         }
  11673.         // Otherwise:
  11674.         result = RegDelete(root_key = RegConvertRootKey(ARG1, &is_remote_registry), ARG2, ARG3);
  11675.         if (is_remote_registry && root_key) // Never try to close local root keys, which the OS always keeps open.
  11676.             RegCloseKey(root_key);
  11677.         return result;
  11678.  
  11679.     case ACT_OUTPUTDEBUG:
  11680.         OutputDebugString(ARG1); // It does not return a value for the purpose of setting ErrorLevel.
  11681.         return OK;
  11682.  
  11683.     case ACT_SHUTDOWN:
  11684.         return Util_Shutdown(ATOI(ARG1)) ? OK : FAIL; // Range of ARG1 is not validated in case other values are supported in the future.
  11685.     } // switch()
  11686.  
  11687.     // Since above didn't return, this line's mActionType isn't handled here,
  11688.     // so caller called it wrong.  ACT_INVALID should be impossible because
  11689.     // Script::AddLine() forbids it.
  11690.  
  11691. #ifdef _DEBUG
  11692.     return LineError("DEBUG: Perform(): Unhandled action type." ERR_ABORT);
  11693. #else
  11694.     return FAIL;
  11695. #endif
  11696. }
  11697.  
  11698.  
  11699.  
  11700. ResultType Line::Deref(Var *aOutputVar, char *aBuf)
  11701. // Similar to ExpandArg(), except it parses and expands all variable references contained in aBuf.
  11702. {
  11703.     aOutputVar = aOutputVar->ResolveAlias(); // Necessary for proper detection below of whether it's invalidly used as a source for itself.
  11704.  
  11705.     // This transient variable is used resolving environment variables that don't already exist
  11706.     // in the script's variable list (due to the fact that they aren't directly referenced elsewhere
  11707.     // in the script):
  11708.     char var_name[MAX_VAR_NAME_LENGTH + 1] = "";
  11709.     Var temp_var(var_name, (void *)VAR_NORMAL, false);
  11710.  
  11711.     Var *var;
  11712.     VarSizeType expanded_length;
  11713.     size_t var_name_length;
  11714.     char *cp, *cp1, *dest;
  11715.  
  11716.     // Do two passes:
  11717.     // #1: Calculate the space needed so that aOutputVar can be given more capacity if necessary.
  11718.     // #2: Expand the contents of aBuf into aOutputVar.
  11719.  
  11720.     for (int which_pass = 0; which_pass < 2; ++which_pass)
  11721.     {
  11722.         if (which_pass) // Starting second pass.
  11723.         {
  11724.             // Set up aOutputVar, enlarging it if necessary.  If it is of type VAR_CLIPBOARD,
  11725.             // this call will set up the clipboard for writing:
  11726.             if (aOutputVar->Assign(NULL, expanded_length) != OK)
  11727.                 return FAIL;
  11728.             dest = aOutputVar->Contents();  // Init, and for performance.
  11729.         }
  11730.         else // First pass.
  11731.             expanded_length = 0; // Init prior to accumulation.
  11732.  
  11733.         for (cp = aBuf; ; ++cp)  // Increment to skip over the deref/escape just found by the inner for().
  11734.         {
  11735.             // Find the next escape char or deref symbol:
  11736.             for (; *cp && *cp != g_EscapeChar && *cp != g_DerefChar; ++cp)
  11737.             {
  11738.                 if (which_pass) // 2nd pass
  11739.                     *dest++ = *cp;  // Copy all non-variable-ref characters literally.
  11740.                 else // just accumulate the length
  11741.                     ++expanded_length;
  11742.             }
  11743.             if (!*cp) // End of string while scanning/copying.  The current pass is now complete.
  11744.                 break;
  11745.             if (*cp == g_EscapeChar)
  11746.             {
  11747.                 if (which_pass) // 2nd pass
  11748.                 {
  11749.                     cp1 = cp + 1;
  11750.                     switch (*cp1) // See ConvertEscapeSequences() for more details.
  11751.                     {
  11752.                         // Only lowercase is recognized for these:
  11753.                         case 'a': *dest = '\a'; break;  // alert (bell) character
  11754.                         case 'b': *dest = '\b'; break;  // backspace
  11755.                         case 'f': *dest = '\f'; break;  // formfeed
  11756.                         case 'n': *dest = '\n'; break;  // newline
  11757.                         case 'r': *dest = '\r'; break;  // carriage return
  11758.                         case 't': *dest = '\t'; break;  // horizontal tab
  11759.                         case 'v': *dest = '\v'; break;  // vertical tab
  11760.                         default:  *dest = *cp1; // These other characters are resolved just as they are, including '\0'.
  11761.                     }
  11762.                     ++dest;
  11763.                 }
  11764.                 else
  11765.                     ++expanded_length;
  11766.                 // Increment cp here and it will be incremented again by the outer loop, i.e. +2.
  11767.                 // In other words, skip over the escape character, treating it and its target character
  11768.                 // as a single character.
  11769.                 ++cp;
  11770.                 continue;
  11771.             }
  11772.             // Otherwise, it's a dereference symbol, so calculate the size of that variable's contents
  11773.             // and add that to expanded_length (or copy the contents into aOutputVar if this is the
  11774.             // second pass).
  11775.             // Find the reference's ending symbol (don't bother with catching escaped deref chars here
  11776.             // -- e.g. %MyVar`% --  since it seems too troublesome to justify given how extremely rarely
  11777.             // it would be an issue):
  11778.             for (cp1 = cp + 1; *cp1 && *cp1 != g_DerefChar; ++cp1);
  11779.             if (!*cp1)    // Since end of string was found, this deref is not correctly terminated.
  11780.                 continue; // For consistency, omit it entirely.
  11781.             var_name_length = cp1 - cp - 1;
  11782.             if (var_name_length && var_name_length <= MAX_VAR_NAME_LENGTH)
  11783.             {
  11784.                 strlcpy(var_name, cp + 1, var_name_length + 1);  // +1 to convert var_name_length to size.
  11785.                 // The use of ALWAYS_PREFER_LOCAL below improves flexibility of assume-global functions
  11786.                 // by allowing this command to resolve to a local first if such a local exists.
  11787.                 // Fixed for v1.0.34: Use FindOrAddVar() vs. FindVar() so that environment or built-in
  11788.                 // variables that aren't directly referenced elsewhere in the script will still work:
  11789.                 if (   !(var = g_script.FindOrAddVar(var_name, var_name_length, ALWAYS_PREFER_LOCAL))   )
  11790.                     // Variable doesn't exist, but since it might be an environment variable never referenced
  11791.                     // directly elsewhere in the script, do special handling:
  11792.                     var = &temp_var;  // Relies on the fact that var_temp.mName *is* the var_name pointer.
  11793.                 else
  11794.                     var = var->ResolveAlias(); // This was already done (above) for aOutputVar.
  11795.                 // Don't allow the output variable to be read into itself this way because its contents
  11796.                 if (var != aOutputVar) // Both of these have had ResolveAlias() called, if required, to make the comparison accurate.
  11797.                 {
  11798.                     if (which_pass) // 2nd pass
  11799.                         dest += var->Get(dest);
  11800.                     else // just accumulate the length
  11801.                         expanded_length += var->Get(); // Add in the length of the variable's contents.
  11802.                 }
  11803.             }
  11804.             // else since the variable name between the deref symbols is blank or too long: for consistency in behavior,
  11805.             // it seems best to omit the dereference entirely (don't put it into aOutputVar).
  11806.             cp = cp1; // For the next loop iteration, continue at the char after this reference's final deref symbol.
  11807.         } // for()
  11808.     } // for() (first and second passes)
  11809.  
  11810.     *dest = '\0';  // Terminate the output variable.
  11811.     aOutputVar->Length() = (VarSizeType)strlen(aOutputVar->Contents()); // Update to actual in case estimate was too large.
  11812.     return aOutputVar->Close();  // In case it's the clipboard.
  11813. }
  11814.  
  11815.  
  11816.  
  11817. char *Line::LogToText(char *aBuf, int aBufSize) // aBufSize should be an int to preserve negatives from caller (caller relies on this).
  11818. // aBufSize is an int so that any negative values passed in from caller are not lost.
  11819. // Translates sLog into its text equivalent, putting the result into aBuf and
  11820. // returning the position in aBuf of its new string terminator.
  11821. // Caller has ensured that aBuf is non-NULL and that aBufSize is reasonable (at least 256).
  11822. {
  11823.     char *aBuf_orig = aBuf;
  11824.  
  11825.     // Store the position of where each retry done by the outer loop will start writing:
  11826.     char *aBuf_log_start = aBuf + snprintf(aBuf, aBufSize, "Script lines most recently executed (oldest first)."
  11827.         "  Press [F5] to refresh.  The seconds elapsed between a line and the one after it is in parentheses to"
  11828.         " the right (if not 0).  The bottommost line's elapsed time is the number of seconds since it executed.\r\n\r\n");
  11829.  
  11830.     int i, lines_to_show, line_index, line_index2, space_remaining; // space_remaining must be an int to detect negatives.
  11831.     DWORD elapsed;
  11832.     bool this_item_is_special, next_item_is_special;
  11833.  
  11834.     // In the below, sLogNext causes it to start at the oldest logged line and continue up through the newest:
  11835.     for (lines_to_show = LINE_LOG_SIZE, line_index = sLogNext;;) // Retry with fewer lines in case the first attempt doesn't fit in the buffer.
  11836.     {
  11837.         aBuf = aBuf_log_start; // Reset target position in buffer to the place where log should begin.
  11838.         for (next_item_is_special = false, i = 0; i < lines_to_show; ++i, ++line_index)
  11839.         {
  11840.             if (line_index >= LINE_LOG_SIZE) // wrap around, because sLog is a circular queue
  11841.                 line_index -= LINE_LOG_SIZE; // Don't just reset it to zero because an offset larger than one may have been added to it.
  11842.             if (!sLog[line_index]) // No line has yet been logged in this slot.
  11843.                 continue;
  11844.             this_item_is_special = next_item_is_special;
  11845.             next_item_is_special = false;  // Set default.
  11846.             if (i + 1 < lines_to_show)  // There are still more lines to be processed
  11847.             {
  11848.                 if (this_item_is_special) // And we know from the above that this special line is not the last line.
  11849.                     // Due to the fact that these special lines are usually only useful when they appear at the
  11850.                     // very end of the log, omit them from the log-display when they're not the last line.
  11851.                     // In the case of a high-frequency SetTimer, this greatly reduces the log clutter that
  11852.                     // would otherwise occur:
  11853.                     continue;
  11854.  
  11855.                 // Since above didn't continue, this item isn't special, so display it normally.
  11856.                 elapsed = sLogTick[line_index + 1 >= LINE_LOG_SIZE ? 0 : line_index + 1] - sLogTick[line_index];
  11857.                 if (elapsed > INT_MAX) // INT_MAX is about one-half of DWORD's capacity.
  11858.                 {
  11859.                     // v1.0.30.02: Assume that huge values (greater than 24 days or so) were caused by
  11860.                     // the new policy of storing WinWait/RunWait/etc.'s line in the buffer whenever
  11861.                     // it was interrupted and later resumed by a thread.  In other words, there are now
  11862.                     // extra lines in the buffer which are considered "special" because they don't indicate
  11863.                     // a line that actually executed, but rather one that is still executing (waiting).
  11864.                     // See ACT_WINWAIT for details.
  11865.                     next_item_is_special = true; // Override the default.
  11866.                     if (i + 2 == lines_to_show) // The line after this one is not only special, but the last one that will be shown, so recalculate this one correctly.
  11867.                         elapsed = GetTickCount() - sLogTick[line_index];
  11868.                     else // Neither this line nor the special one that follows it is the last.
  11869.                     {
  11870.                         // Refer to the line after the next (special) line to get this line's correct elapsed time.
  11871.                         line_index2 = line_index + 2;
  11872.                         if (line_index2 >= LINE_LOG_SIZE)
  11873.                             line_index2 -= LINE_LOG_SIZE;
  11874.                         elapsed = sLogTick[line_index2] - sLogTick[line_index];
  11875.                     }
  11876.                 }
  11877.             }
  11878.             else // This is the last line (whether special or not), so compare it's time against the current time instead.
  11879.                 elapsed = GetTickCount() - sLogTick[line_index];
  11880.             space_remaining = BUF_SPACE_REMAINING;  // Resolve macro only once for performance.
  11881.             // Truncate really huge lines so that the Edit control's size is less likely to be exhausted.
  11882.             // In v1.0.30.02, this is even more likely due to having increased the line-buf's capacity from
  11883.             // 200 to 400, therefore the truncation point was reduced from 500 to 200 to make it more likely
  11884.             // that the first attempt to fit the lines_to_show number of lines into the buffer will succeed.
  11885.             aBuf = sLog[line_index]->ToText(aBuf, space_remaining < 200 ? space_remaining : 200, true, elapsed, this_item_is_special);
  11886.             // If the line above can't fit everything it needs into the remaining space, it will fill all
  11887.             // of the remaining space, and thus the check against LINE_LOG_FINAL_MESSAGE_LENGTH below
  11888.             // should never fail to catch that, and then do a retry.
  11889.         } // Inner for()
  11890.  
  11891.         #define LINE_LOG_FINAL_MESSAGE "\r\nPress [F5] to refresh." // Keep the next line in sync with this.
  11892.         #define LINE_LOG_FINAL_MESSAGE_LENGTH 24
  11893.         if (BUF_SPACE_REMAINING > LINE_LOG_FINAL_MESSAGE_LENGTH || lines_to_show < 120) // Either success or can't succeed.
  11894.             break;
  11895.  
  11896.         // Otherwise, there is insufficient room to put everything in, but there's still room to retry
  11897.         // with a smaller value of lines_to_show:
  11898.         lines_to_show -= 100;
  11899.         line_index = sLogNext + (LINE_LOG_SIZE - lines_to_show); // Move the starting point forward in time so that the oldest log entries are omitted.
  11900.  
  11901.     } // outer for() that retries the log-to-buffer routine.
  11902.  
  11903.     // Must add the return value, not LINE_LOG_FINAL_MESSAGE_LENGTH, in case insufficient room (i.e. in case
  11904.     // outer loop terminated due to lines_to_show being too small).
  11905.     return aBuf + snprintf(aBuf, BUF_SPACE_REMAINING, LINE_LOG_FINAL_MESSAGE);
  11906. }
  11907.  
  11908.  
  11909.  
  11910. char *Line::VicinityToText(char *aBuf, int aBufSize) // aBufSize should be an int to preserve negatives from caller (caller relies on this).
  11911. // aBufSize is an int so that any negative values passed in from caller are not lost.
  11912. // Caller has ensured that aBuf isn't NULL.
  11913. // Translates the current line and the lines above and below it into their text equivalent
  11914. // putting the result into aBuf and returning the position in aBuf of its new string terminator.
  11915. {
  11916.     char *aBuf_orig = aBuf;
  11917.  
  11918.     #define LINES_ABOVE_AND_BELOW 7
  11919.  
  11920.     // Determine the correct value for line_start and line_end:
  11921.     int i;
  11922.     Line *line_start, *line_end;
  11923.     for (i = 0, line_start = this
  11924.         ; i < LINES_ABOVE_AND_BELOW && line_start->mPrevLine != NULL
  11925.         ; ++i, line_start = line_start->mPrevLine);
  11926.  
  11927.     for (i = 0, line_end = this
  11928.         ; i < LINES_ABOVE_AND_BELOW && line_end->mNextLine != NULL
  11929.         ; ++i, line_end = line_end->mNextLine);
  11930.  
  11931. #ifdef AUTOHOTKEYSC
  11932.     if (!g_AllowMainWindow) // Override the above to show only a single line, to conceal the script's source code.
  11933.     {
  11934.         line_start = this;
  11935.         line_end = this;
  11936.     }
  11937. #endif
  11938.  
  11939.     // Now line_start and line_end are the first and last lines of the range
  11940.     // we want to convert to text, and they're non-NULL.
  11941.     aBuf += snprintf(aBuf, BUF_SPACE_REMAINING, "\tLine#\n");
  11942.  
  11943.     int space_remaining; // Must be an int to preserve any negative results.
  11944.  
  11945.     // Start at the oldest and continue up through the newest:
  11946.     for (Line *line = line_start;;)
  11947.     {
  11948.         if (line == this)
  11949.             strlcpy(aBuf, "--->\t", BUF_SPACE_REMAINING);
  11950.         else
  11951.             strlcpy(aBuf, "\t", BUF_SPACE_REMAINING);
  11952.         aBuf += strlen(aBuf);
  11953.         space_remaining = BUF_SPACE_REMAINING;  // Resolve macro only once for performance.
  11954.         // Truncate large lines so that the dialog is more readable:
  11955.         aBuf = line->ToText(aBuf, space_remaining < 500 ? space_remaining : 500, false);
  11956.         if (line == line_end)
  11957.             break;
  11958.         line = line->mNextLine;
  11959.     }
  11960.     return aBuf;
  11961. }
  11962.  
  11963.  
  11964.  
  11965. char *Line::ToText(char *aBuf, int aBufSize, bool aCRLF, DWORD aElapsed, bool aLineWasResumed) // aBufSize should be an int to preserve negatives from caller (caller relies on this).
  11966. // aBufSize is an int so that any negative values passed in from caller are not lost.
  11967. // Caller has ensured that aBuf isn't NULL.
  11968. // Translates this line into its text equivalent, putting the result into aBuf and
  11969. // returning the position in aBuf of its new string terminator.
  11970. {
  11971.     if (aBufSize < 3)
  11972.         return aBuf;
  11973.     else
  11974.         aBufSize -= (1 + aCRLF);  // Reserve one char for LF/CRLF after each line (so that it always get added).
  11975.  
  11976.     char *aBuf_orig = aBuf;
  11977.  
  11978.     aBuf += snprintf(aBuf, aBufSize, "%03u: ", mLineNumber);
  11979.     if (aLineWasResumed)
  11980.         aBuf += snprintf(aBuf, BUF_SPACE_REMAINING, "STILL WAITING (%0.2f): ", (float)aElapsed / 1000.0);
  11981.  
  11982.     if (mActionType == ACT_IFBETWEEN || mActionType == ACT_IFNOTBETWEEN)
  11983.         aBuf += snprintf(aBuf, BUF_SPACE_REMAINING, "if %s %s %s and %s"
  11984.             , *mArg[0].text ? mArg[0].text : VAR(mArg[0])->mName  // i.e. don't resolve dynamic variable names.
  11985.             , g_act[mActionType].Name, RAW_ARG2, RAW_ARG3);
  11986.     else if (ACT_IS_ASSIGN(mActionType) || (ACT_IS_IF(mActionType) && mActionType < ACT_FIRST_COMMAND))
  11987.         // Only these other commands need custom conversion.
  11988.         aBuf += snprintf(aBuf, BUF_SPACE_REMAINING, "%s%s %s %s"
  11989.             , ACT_IS_IF(mActionType) ? "if " : ""
  11990.             , *mArg[0].text ? mArg[0].text : VAR(mArg[0])->mName  // i.e. don't resolve dynamic variable names.
  11991.             , g_act[mActionType].Name, RAW_ARG2);
  11992.     else
  11993.     {
  11994.         aBuf += snprintf(aBuf, BUF_SPACE_REMAINING, "%s", g_act[mActionType].Name);
  11995.         for (int i = 0; i < mArgc; ++i)
  11996.             // This method a little more efficient than using snprintfcat().
  11997.             // Also, always use the arg's text for input and output args whose variables haven't
  11998.             // been been resolved at load-time, since the text has everything in it we want to display
  11999.             // and thus there's no need to "resolve" dynamic variables here (e.g. array%i%).
  12000.             aBuf += snprintf(aBuf, BUF_SPACE_REMAINING, ",%s", (mArg[i].type != ARG_TYPE_NORMAL && !*mArg[i].text)
  12001.                 ? VAR(mArg[i])->mName : mArg[i].text);
  12002.     }
  12003.     if (aElapsed && !aLineWasResumed)
  12004.         aBuf += snprintf(aBuf, BUF_SPACE_REMAINING, " (%0.2f)", (float)aElapsed / 1000.0);
  12005.     // UPDATE for v1.0.25: It seems that MessageBox(), which is the only way these lines are currently
  12006.     // displayed, prefers \n over \r\n because otherwise, Ctrl-C on the MsgBox copies the lines all
  12007.     // onto one line rather than formatted nicely as separate lines.
  12008.     // Room for LF or CRLF was reserved at the top of this function:
  12009.     if (aCRLF)
  12010.         *aBuf++ = '\r';
  12011.     *aBuf++ = '\n';
  12012.     *aBuf = '\0';
  12013.     return aBuf;
  12014. }
  12015.  
  12016.  
  12017.  
  12018. void Line::ToggleSuspendState()
  12019. {
  12020.     // If suspension is being turned on:
  12021.     // It seems unnecessary, and possibly undesirable, to purge any pending hotkey msgs from the msg queue.
  12022.     // Even if there are some, it's possible that they are exempt from suspension so we wouldn't want to
  12023.     // globally purge all messages anyway.
  12024.     g_IsSuspended = !g_IsSuspended;
  12025.     Hotstring::SuspendAll(g_IsSuspended);  // Must do this prior to ManifestAllHotkeysHotstringsHooks() to avoid incorrect removal of hook.
  12026.     Hotkey::ManifestAllHotkeysHotstringsHooks(); // Update the state of all hotkeys based on the complex interdependencies hotkeys have with each another.
  12027.     g_script.UpdateTrayIcon();
  12028.     CheckMenuItem(GetMenu(g_hWnd), ID_FILE_SUSPEND, g_IsSuspended ? MF_CHECKED : MF_UNCHECKED);
  12029. }
  12030.  
  12031.  
  12032.  
  12033. ResultType Line::ChangePauseState(ToggleValueType aChangeTo, bool aAlwaysOperateOnUnderlyingThread)
  12034. // Returns OK or FAIL.
  12035. // Note: g_Idle must be false since we're always called from a script subroutine, not from
  12036. // the tray menu.  Therefore, the value of g_Idle need never be checked here.
  12037. {
  12038.     switch (aChangeTo)
  12039.     {
  12040.     case TOGGLED_ON:
  12041.         break; // By breaking insteading of returning, pause will be put into effect further below.
  12042.     case TOGGLED_OFF:
  12043.         // v1.0.37.06: The old method was to unpause the the nearest paused thread on the call stack;
  12044.         // but it was flawed because if the thread that made the flag true is interrupted, and the new
  12045.         // thread is paused via the pause command, and that thread is then interrupted, when the paused
  12046.         // thread resumes it would automatically and wrongly be unpaused (i.e. the unpause ticket would
  12047.         // be used at a level higher in the call stack than intended).
  12048.         // Flag this thread so that when it ends, the thread beneath it will be unpaused.  If that thread
  12049.         // (which can be the idle thread) isn't paused the following flag-change will be ignored at a later
  12050.         // stage. This method also relies on the fact that the current thread cannot itself be paused right
  12051.         // now because it is what got us here.
  12052.         g.UnderlyingThreadIsPaused = false; // Necessary even for the "idle thread" (otherwise, the Pause command wouldn't be able to unpause it).
  12053.         return OK;
  12054.     case NEUTRAL: // the user omitted the parameter entirely, which is considered the same as "toggle"
  12055.     case TOGGLE:
  12056.         // Update for v1.0.37.06: "Pause" and "Pause Toggle" are more useful if they always apply to the
  12057.         // thread immediately beneath the current thread rather than "any underlying thread that's paused".
  12058.         if (g.UnderlyingThreadIsPaused)
  12059.         {
  12060.             g.UnderlyingThreadIsPaused = false; // Flag it to be unpaused when it gets resumed.
  12061.             return OK;
  12062.         }
  12063.         // Otherwise, since the underlying thread is not paused, continue onward to do the "pause enabled"
  12064.         // logic below:
  12065.         break;
  12066.     default: // TOGGLE_INVALID or some other disallowed value.
  12067.         // We know it's a variable because otherwise the loading validation would have caught it earlier:
  12068.         return LineError(ERR_PARAM1_INVALID, FAIL, ARG1);
  12069.     }
  12070.  
  12071.     // Since above didn't return, pause should be turned on.
  12072.     if (aAlwaysOperateOnUnderlyingThread) // v1.0.37.06: Allow underlying thread to be directly paused rather than pausing the current thread.
  12073.     {
  12074.         g.UnderlyingThreadIsPaused = true; // If the underlying thread is already paused, this flag change will be ignored at a later stage.
  12075.         return OK;
  12076.     }
  12077.     // Otherwise, pause the current subroutine (which by definition isn't paused since it had to be 
  12078.     // active to call us).  It seems best not to attempt to change the Hotkey mRunAgainAfterFinished
  12079.     // attribute for the current hotkey (assuming it's even a hotkey that got us here) or
  12080.     // for them all.  This is because it's conceivable that this Pause command occurred
  12081.     // in a background thread, such as a timed subroutine, in which case we wouldn't want the
  12082.     // pausing of that thread to affect anything else the user might be doing with hotkeys.
  12083.     // UPDATE: The above is flawed because by definition the script's quasi-thread that got
  12084.     // us here is now active.  Since it is active, the script will immediately become dormant
  12085.     // when this is executed, waiting for the user to press a hotkey to launch a new
  12086.     // quasi-thread.  Thus, it seems best to reset all the mRunAgainAfterFinished flags
  12087.     // in case we are in a hotkey subroutine and in case this hotkey has a buffered repeat-again
  12088.     // action pending, which the user probably wouldn't want to happen after the script is unpaused:
  12089.     Hotkey::ResetRunAgainAfterFinished();
  12090.     g.IsPaused = true;
  12091.     ++g_nPausedThreads; // Always incremented because we're never called to pause the "idle thread", only real threads.
  12092.     g_script.UpdateTrayIcon();
  12093.     CheckMenuItem(GetMenu(g_hWnd), ID_FILE_PAUSE, MF_CHECKED);
  12094.     return OK;
  12095. }
  12096.  
  12097.  
  12098.  
  12099. ResultType Line::ScriptBlockInput(bool aEnable)
  12100. // Always returns OK for caller convenience.
  12101. {
  12102.     // Must be running Win98/2000+ for this function to be successful.
  12103.     // We must dynamically load the function to retain compatibility with Win95 (program won't launch
  12104.     // at all otherwise).
  12105.     typedef void (CALLBACK *BlockInput)(BOOL);
  12106.     static BlockInput lpfnDLLProc = (BlockInput)GetProcAddress(GetModuleHandle("user32"), "BlockInput");
  12107.     // Always turn input ON/OFF even if g_BlockInput says its already in the right state.  This is because
  12108.     // BlockInput can be externally and undetectibly disabled, e.g. if the user presses Ctrl-Alt-Del:
  12109.     if (lpfnDLLProc)
  12110.         (*lpfnDLLProc)(aEnable ? TRUE : FALSE);
  12111.     g_BlockInput = aEnable;
  12112.     return OK;  // By design, it never returns FAIL.
  12113. }
  12114.  
  12115.  
  12116.  
  12117. Line *Line::PreparseError(char *aErrorText, char *aExtraInfo)
  12118. // Returns a different type of result for use with the Pre-parsing methods.
  12119. {
  12120.     // Make all preparsing errors critical because the runtime reliability
  12121.     // of the program relies upon the fact that the aren't any kind of
  12122.     // problems in the script (otherwise, unexpected behavior may result).
  12123.     // Update: It's okay to return FAIL in this case.  CRITICAL_ERROR should
  12124.     // be avoided whenever OK and FAIL are sufficient by themselves, because
  12125.     // otherwise, callers can't use the NOT operator to detect if a function
  12126.     // failed (since FAIL is value zero, but CRITICAL_ERROR is non-zero):
  12127.     LineError(aErrorText, FAIL, aExtraInfo);
  12128.     return NULL; // Always return NULL because the callers use it as their return value.
  12129. }
  12130.  
  12131.  
  12132.  
  12133. ResultType Line::LineError(char *aErrorText, ResultType aErrorType, char *aExtraInfo)
  12134. {
  12135.     if (!aErrorText)
  12136.         aErrorText = "";
  12137.     if (!aExtraInfo)
  12138.         aExtraInfo = "";
  12139.  
  12140.     if (g_script.mErrorStdOut && !g_script.mIsReadyToExecute) // i.e. runtime errors are always displayed via dialog.
  12141.     {
  12142.         // JdeB said:
  12143.         // Just tested it in Textpad, Crimson and Scite. they all recognise the output and jump
  12144.         // to the Line containing the error when you double click the error line in the output
  12145.         // window (like it works in C++).  Had to change the format of the line to: 
  12146.         // printf("%s (%d) : ==> %s: \n%s \n%s\n",szInclude, nAutScriptLine, szText, szScriptLine, szOutput2 );
  12147.         // MY: Full filename is required, even if it's the main file, because some editors (EditPlus)
  12148.         // seem to rely on that to determine which file and line number to jump to when the user double-clicks
  12149.         // the error message in the output window.
  12150.         // v1.0.47: Added a space before the colon as originally intended.  Toralf said, "With this minor
  12151.         // change the error lexer of Scite recognizes this line as a Microsoft error message and it can be
  12152.         // used to jump to that line."
  12153.         #define STD_ERROR_FORMAT "%s (%d) : ==> %s\n"
  12154.         printf(STD_ERROR_FORMAT, sSourceFile[mFileIndex], mLineNumber, aErrorText); // printf() does not signifantly increase the size of the EXE, probably because it shares most of the same code with sprintf(), etc.
  12155.         if (*aExtraInfo)
  12156.             printf("     Specifically: %s\n", aExtraInfo);
  12157.     }
  12158.     else
  12159.     {
  12160.         char source_file[MAX_PATH * 2];
  12161.         if (mFileIndex)
  12162.             snprintf(source_file, sizeof(source_file), " in #include file \"%s\"", sSourceFile[mFileIndex]);
  12163.         else
  12164.             *source_file = '\0'; // Don't bother cluttering the display if it's the main script file.
  12165.  
  12166.         char buf[MSGBOX_TEXT_SIZE];
  12167.         char *buf_marker = buf + snprintf(buf, sizeof(buf), "%s%s: %-1.500s\n\n"  // Keep it to a sane size in case it's huge.
  12168.             , aErrorType == WARN ? "Warning" : (aErrorType == CRITICAL_ERROR ? "Critical Error" : "Error")
  12169.             , source_file, aErrorText);
  12170.         if (*aExtraInfo)
  12171.             // Use format specifier to make sure really huge strings that get passed our
  12172.             // way, such as a var containing clipboard text, are kept to a reasonable size:
  12173.             buf_marker += snprintfcat(buf, sizeof(buf), "Specifically: %-1.100s%s\n\n"
  12174.             , aExtraInfo, strlen(aExtraInfo) > 100 ? "..." : "");
  12175.         buf_marker = VicinityToText(buf_marker, (int)(sizeof(buf) - (buf_marker - buf))); // Cast to int to avoid loss of negative values.
  12176.         if (aErrorType == CRITICAL_ERROR || (aErrorType == FAIL && !g_script.mIsReadyToExecute))
  12177.             strlcpy(buf_marker, g_script.mIsRestart ? ("\n" OLD_STILL_IN_EFFECT) : ("\n" WILL_EXIT)
  12178.                 , (int)(sizeof(buf) - (buf_marker - buf))); // Cast to int to avoid loss of negative values.
  12179.         g_script.mCurrLine = this;  // This needs to be set in some cases where the caller didn't.
  12180.         //g_script.ShowInEditor();
  12181.         MsgBox(buf);
  12182.     }
  12183.  
  12184.     if (aErrorType == CRITICAL_ERROR && g_script.mIsReadyToExecute)
  12185.         // Also ask the main message loop function to quit and announce to the system that
  12186.         // we expect it to quit.  In most cases, this is unnecessary because all functions
  12187.         // called to get to this point will take note of the CRITICAL_ERROR and thus keep
  12188.         // return immediately, all the way back to main.  However, there may cases
  12189.         // when this isn't true:
  12190.         // Note: Must do this only after MsgBox, since it appears that new dialogs can't
  12191.         // be created once it's done.  Update: Using ExitApp() now, since it's known to be
  12192.         // more reliable:
  12193.         //PostQuitMessage(CRITICAL_ERROR);
  12194.         // This will attempt to run the OnExit subroutine, which should be okay since that subroutine
  12195.         // will terminate the script if it encounters another runtime error:
  12196.         g_script.ExitApp(EXIT_ERROR);
  12197.  
  12198.     return aErrorType; // The caller told us whether it should be a critical error or not.
  12199. }
  12200.  
  12201.  
  12202.  
  12203. ResultType Script::ScriptError(char *aErrorText, char *aExtraInfo) //, ResultType aErrorType)
  12204. // Even though this is a Script method, including it here since it shares
  12205. // a common theme with the other error-displaying functions:
  12206. {
  12207.     if (mCurrLine)
  12208.         // If a line is available, do LineError instead since it's more specific.
  12209.         // If an error occurs before the script is ready to run, assume it's always critical
  12210.         // in the sense that the program will exit rather than run the script.
  12211.         // Update: It's okay to return FAIL in this case.  CRITICAL_ERROR should
  12212.         // be avoided whenever OK and FAIL are sufficient by themselves, because
  12213.         // otherwise, callers can't use the NOT operator to detect if a function
  12214.         // failed (since FAIL is value zero, but CRITICAL_ERROR is non-zero):
  12215.         return mCurrLine->LineError(aErrorText, FAIL, aExtraInfo);
  12216.     // Otherwise: The fact that mCurrLine is NULL means that the line currently being loaded
  12217.     // has not yet been successfully added to the linked list.  Such errors will always result
  12218.     // in the program exiting.
  12219.     if (!aErrorText)
  12220.         aErrorText = "Unk"; // Placeholder since it shouldn't be NULL.
  12221.     if (!aExtraInfo) // In case the caller explicitly called it with NULL.
  12222.         aExtraInfo = "";
  12223.  
  12224.     if (g_script.mErrorStdOut && !g_script.mIsReadyToExecute) // i.e. runtime errors are always displayed via dialog.
  12225.     {
  12226.         // See LineError() for details.
  12227.         printf(STD_ERROR_FORMAT, Line::sSourceFile[mCurrFileIndex], mCombinedLineNumber, aErrorText);
  12228.         if (*aExtraInfo)
  12229.             printf("     Specifically: %s\n", aExtraInfo);
  12230.     }
  12231.     else
  12232.     {
  12233.         char buf[MSGBOX_TEXT_SIZE], *cp = buf;
  12234.         int buf_space_remaining = (int)sizeof(buf);
  12235.  
  12236.         cp += snprintf(cp, buf_space_remaining, "Error at line %u", mCombinedLineNumber); // Don't call it "critical" because it's usually a syntax error.
  12237.         buf_space_remaining = (int)(sizeof(buf) - (cp - buf));
  12238.  
  12239.         if (mCurrFileIndex)
  12240.         {
  12241.             cp += snprintf(cp, buf_space_remaining, " in #include file \"%s\"", Line::sSourceFile[mCurrFileIndex]);
  12242.             buf_space_remaining = (int)(sizeof(buf) - (cp - buf));
  12243.         }
  12244.         //else don't bother cluttering the display if it's the main script file.
  12245.  
  12246.         cp += snprintf(cp, buf_space_remaining, ".\n\n");
  12247.         buf_space_remaining = (int)(sizeof(buf) - (cp - buf));
  12248.  
  12249.         if (*aExtraInfo)
  12250.         {
  12251.             cp += snprintf(cp, buf_space_remaining, "Line Text: %-1.100s%s\nError: "  // i.e. the word "Error" is omitted as being too noisy when there's no ExtraInfo to put into the dialog.
  12252.                 , aExtraInfo // aExtraInfo defaults to "" so this is safe.
  12253.                 , strlen(aExtraInfo) > 100 ? "..." : "");
  12254.             buf_space_remaining = (int)(sizeof(buf) - (cp - buf));
  12255.         }
  12256.         snprintf(cp, buf_space_remaining, "%s\n\n%s", aErrorText, mIsRestart ? OLD_STILL_IN_EFFECT : WILL_EXIT);
  12257.  
  12258.         //ShowInEditor();
  12259.         MsgBox(buf);
  12260.     }
  12261.     return FAIL; // See above for why it's better to return FAIL than CRITICAL_ERROR.
  12262. }
  12263.  
  12264.  
  12265.  
  12266. char *Script::ListVars(char *aBuf, int aBufSize) // aBufSize should be an int to preserve negatives from caller (caller relies on this).
  12267. // aBufSize is an int so that any negative values passed in from caller are not lost.
  12268. // Translates this script's list of variables into text equivalent, putting the result
  12269. // into aBuf and returning the position in aBuf of its new string terminator.
  12270. {
  12271.     char *aBuf_orig = aBuf;
  12272.     if (g.CurrentFunc)
  12273.     {
  12274.         // This definition might help compiler string pooling by ensuring it stays the same for both usages:
  12275.         #define LIST_VARS_UNDERLINE "\r\n--------------------------------------------------\r\n"
  12276.         // Start at the oldest and continue up through the newest:
  12277.         aBuf += snprintf(aBuf, BUF_SPACE_REMAINING, "Local Variables for %s()%s", g.CurrentFunc->mName, LIST_VARS_UNDERLINE);
  12278.         Func &func = *g.CurrentFunc; // For performance.
  12279.         for (int i = 0; i < func.mVarCount; ++i)
  12280.             if (func.mVar[i]->Type() == VAR_NORMAL) // Don't bother showing clipboard and other built-in vars.
  12281.                 aBuf = func.mVar[i]->ToText(aBuf, BUF_SPACE_REMAINING, true);
  12282.     }
  12283.     // v1.0.31: The description "alphabetical" is kept even though it isn't quite true
  12284.     // when the lazy variable list exists, since those haven't yet been sorted into the main list.
  12285.     // However, 99.9% of scripts do not use the lazy list, so it seems too rare to worry about other
  12286.     // than document it in the ListVars command in the help file:
  12287.     aBuf += snprintf(aBuf, BUF_SPACE_REMAINING, "%sGlobal Variables (alphabetical)%s"
  12288.         , g.CurrentFunc ? "\r\n\r\n" : "", LIST_VARS_UNDERLINE);
  12289.     // Start at the oldest and continue up through the newest:
  12290.     for (int i = 0; i < mVarCount; ++i)
  12291.         if (mVar[i]->Type() == VAR_NORMAL) // Don't bother showing clipboard and other built-in vars.
  12292.             aBuf = mVar[i]->ToText(aBuf, BUF_SPACE_REMAINING, true);
  12293.     return aBuf;
  12294. }
  12295.  
  12296.  
  12297.  
  12298. char *Script::ListKeyHistory(char *aBuf, int aBufSize) // aBufSize should be an int to preserve negatives from caller (caller relies on this).
  12299. // aBufSize is an int so that any negative values passed in from caller are not lost.
  12300. // Translates this key history into text equivalent, putting the result
  12301. // into aBuf and returning the position in aBuf of its new string terminator.
  12302. {
  12303.     char *aBuf_orig = aBuf; // Needed for the BUF_SPACE_REMAINING macro.
  12304.     // I was initially concerned that GetWindowText() can hang if the target window is
  12305.     // hung.  But at least on newer OS's, this doesn't seem to be a problem: MSDN says
  12306.     // "If the window does not have a caption, the return value is a null string. This
  12307.     // behavior is by design. It allows applications to call GetWindowText without hanging
  12308.     // if the process that owns the target window is hung. However, if the target window
  12309.     // is hung and it belongs to the calling application, GetWindowText will hang the
  12310.     // calling application."
  12311.     HWND target_window = GetForegroundWindow();
  12312.     char win_title[100];
  12313.     if (target_window)
  12314.         GetWindowText(target_window, win_title, sizeof(win_title));
  12315.     else
  12316.         *win_title = '\0';
  12317.  
  12318.     char timer_list[128] = "";
  12319.     for (ScriptTimer *timer = mFirstTimer; timer != NULL; timer = timer->mNextTimer)
  12320.         if (timer->mEnabled)
  12321.             snprintfcat(timer_list, sizeof(timer_list) - 3, "%s ", timer->mLabel->mName); // Allow room for "..."
  12322.     if (*timer_list)
  12323.     {
  12324.         size_t length = strlen(timer_list);
  12325.         if (length > (sizeof(timer_list) - 5))
  12326.             strlcpy(timer_list + length, "...", sizeof(timer_list) - length);
  12327.         else if (timer_list[length - 1] == ' ')
  12328.             timer_list[--length] = '\0';  // Remove the last space if there was room enough for it to have been added.
  12329.     }
  12330.  
  12331.     char LRtext[256];
  12332.     aBuf += snprintf(aBuf, aBufSize,
  12333.         "Window: %s"
  12334.         //"\r\nBlocks: %u"
  12335.         "\r\nKeybd hook: %s"
  12336.         "\r\nMouse hook: %s"
  12337.         "\r\nEnabled Timers: %u of %u (%s)"
  12338.         //"\r\nInterruptible?: %s"
  12339.         "\r\nInterrupted threads: %d%s"
  12340.         "\r\nPaused threads: %d of %d (%d layers)"
  12341.         "\r\nModifiers (GetKeyState() now) = %s"
  12342.         "\r\n"
  12343.         , win_title
  12344.         //, SimpleHeap::GetBlockCount()
  12345.         , g_KeybdHook == NULL ? "no" : "yes"
  12346.         , g_MouseHook == NULL ? "no" : "yes"
  12347.         , mTimerEnabledCount, mTimerCount, timer_list
  12348.         //, INTERRUPTIBLE ? "yes" : "no"
  12349.         , g_nThreads > 1 ? g_nThreads - 1 : 0
  12350.         , g_nThreads > 1 ? " (preempted: they will resume when the current thread finishes)" : ""
  12351.         , g_nPausedThreads, g_nThreads, g_nLayersNeedingTimer
  12352.         , ModifiersLRToText(GetModifierLRState(true), LRtext));
  12353.     GetHookStatus(aBuf, BUF_SPACE_REMAINING);
  12354.     aBuf += strlen(aBuf); // Adjust for what GetHookStatus() wrote to the buffer.
  12355.     return aBuf + snprintf(aBuf, BUF_SPACE_REMAINING, g_KeyHistory ? "\r\nPress [F5] to refresh."
  12356.         : "\r\nKey History has been disabled via #KeyHistory 0.");
  12357. }
  12358.  
  12359.  
  12360.  
  12361. ResultType Script::ActionExec(char *aAction, char *aParams, char *aWorkingDir, bool aDisplayErrors
  12362.     , char *aRunShowMode, HANDLE *aProcess, bool aUpdateLastError, bool aUseRunAs, Var *aOutputVar)
  12363. // Caller should specify NULL for aParams if it wants us to attempt to parse out params from
  12364. // within aAction.  Caller may specify empty string ("") instead to specify no params at all.
  12365. // Remember that aAction and aParams can both be NULL, so don't dereference without checking first.
  12366. // Note: For the Run & RunWait commands, aParams should always be NULL.  Params are parsed out of
  12367. // the aActionString at runtime, here, rather than at load-time because Run & RunWait might contain
  12368. // deferenced variable(s), which can only be resolved at runtime.
  12369. {
  12370.     HANDLE hprocess_local;
  12371.     HANDLE &hprocess = aProcess ? *aProcess : hprocess_local; // To simplify other things.
  12372.     hprocess = NULL; // Init output param if the caller gave us memory to store it.  Even if caller didn't, other things below may rely on this being initialized.
  12373.     if (aOutputVar) // Same
  12374.         aOutputVar->Assign();
  12375.  
  12376.     // Launching nothing is always a success:
  12377.     if (!aAction || !*aAction) return OK;
  12378.  
  12379.     size_t aAction_length = strlen(aAction);
  12380.     if (aAction_length >= LINE_SIZE) // Max length supported by CreateProcess() is 32 KB. But there hasn't been any demand to go above 16 KB, so seems little need to support it (plus it reduces risk of stack overflow).
  12381.     {
  12382.         if (aDisplayErrors)
  12383.             ScriptError("String too long." ERR_ABORT); // Short msg since so rare.
  12384.         return FAIL;
  12385.     }
  12386.     // Declare this buf here to ensure it's in scope for the entire function, since its
  12387.     // contents may be referred to indirectly:
  12388.     char *parse_buf = (char *)_alloca(aAction_length + 1); // v1.0.44.14: _alloca() helps conserve stack space.
  12389.  
  12390.     // Make sure this is set to NULL because CreateProcess() won't work if it's the empty string:
  12391.     if (aWorkingDir && !*aWorkingDir)
  12392.         aWorkingDir = NULL;
  12393.  
  12394.     #define IS_VERB(str) (   !stricmp(str, "find") || !stricmp(str, "explore") || !stricmp(str, "open")\
  12395.         || !stricmp(str, "edit") || !stricmp(str, "print") || !stricmp(str, "properties")   )
  12396.  
  12397.     // Set default items to be run by ShellExecute().  These are also used by the error
  12398.     // reporting at the end, which is why they're initialized even if CreateProcess() works
  12399.     // and there's no need to use ShellExecute():
  12400.     char *shell_action = aAction;
  12401.     char *shell_params = aParams ? aParams : "";
  12402.     bool shell_action_is_system_verb = false;
  12403.  
  12404.     ///////////////////////////////////////////////////////////////////////////////////
  12405.     // This next section is done prior to CreateProcess() because when aParams is NULL,
  12406.     // we need to find out whether aAction contains a system verb.
  12407.     ///////////////////////////////////////////////////////////////////////////////////
  12408.     if (aParams) // Caller specified the params (even an empty string counts, for this purpose).
  12409.         shell_action_is_system_verb = IS_VERB(shell_action);
  12410.     else // Caller wants us to try to parse params out of aAction.
  12411.     {
  12412.         // Make a copy so that we can modify it (i.e. split it into action & params):
  12413.         strcpy(parse_buf, aAction); // parse_buf is already known to be large enough.
  12414.  
  12415.         // Find out the "first phrase" in the string.  This is done to support the special "find" and "explore"
  12416.         // operations as well as minmize the chance that executable names intended by the user to be parameters
  12417.         // will not be considered to be the program to run (e.g. for use with a compiler, perhaps).
  12418.         char *first_phrase, *first_phrase_end, *second_phrase;
  12419.         if (*parse_buf == '"')
  12420.         {
  12421.             first_phrase = parse_buf + 1;  // Omit the double-quotes, for use with CreateProcess() and such.
  12422.             first_phrase_end = strchr(first_phrase, '"');
  12423.         }
  12424.         else
  12425.         {
  12426.             first_phrase = parse_buf;
  12427.             // Set first_phrase_end to be the location of the first whitespace char, if
  12428.             // one exists:
  12429.             first_phrase_end = StrChrAny(first_phrase, " \t"); // Find space or tab.
  12430.         }
  12431.         // Now first_phrase_end is either NULL, the position of the last double-quote in first-phrase,
  12432.         // or the position of the first whitespace char to the right of first_phrase.
  12433.         if (first_phrase_end)
  12434.         {
  12435.             // Split into two phrases:
  12436.             *first_phrase_end = '\0';
  12437.             second_phrase = first_phrase_end + 1;
  12438.         }
  12439.         else // the entire string is considered to be the first_phrase, and there's no second:
  12440.             second_phrase = NULL;
  12441.         if (shell_action_is_system_verb = IS_VERB(first_phrase))
  12442.         {
  12443.             shell_action = first_phrase;
  12444.             shell_params = second_phrase ? second_phrase : "";
  12445.         }
  12446.         else
  12447.         {
  12448. // Rather than just consider the first phrase to be the executable and the rest to be the param, we check it
  12449. // for a proper extension so that the user can launch a document name containing spaces, without having to
  12450. // enclose it in double quotes.  UPDATE: Want to be able to support executable filespecs without requiring them
  12451. // to be enclosed in double quotes.  Therefore, search the entire string, rather than just first_phrase, for
  12452. // the left-most occurrence of a valid executable extension.  This should be fine since the user can still
  12453. // pass in EXEs and such as params as long as the first executable is fully qualified with its real extension
  12454. // so that we can tell that it's the action and not one of the params.
  12455.  
  12456. // This method is rather crude because is doesn't handle an extensionless executable such as "notepad test.txt"
  12457. // It's important that it finds the first occurrence of an executable extension in case there are other
  12458. // occurrences in the parameters.  Also, .pif and .lnk are currently not considered executables for this purpose
  12459. // since they probably don't accept parameters:
  12460.             strcpy(parse_buf, aAction);  // Restore the original value in case it was changed. parse_buf is already known to be large enough.
  12461.             char *action_extension;
  12462.             if (   !(action_extension = strcasestr(parse_buf, ".exe "))   )
  12463.                 if (   !(action_extension = strcasestr(parse_buf, ".exe\""))   )
  12464.                     if (   !(action_extension = strcasestr(parse_buf, ".bat "))   )
  12465.                         if (   !(action_extension = strcasestr(parse_buf, ".bat\""))   )
  12466.                             if (   !(action_extension = strcasestr(parse_buf, ".com "))   )
  12467.                                 if (   !(action_extension = strcasestr(parse_buf, ".com\""))   )
  12468.                                     // Not 100% sure that .cmd and .hta are genuine executables in every sense:
  12469.                                     if (   !(action_extension = strcasestr(parse_buf, ".cmd "))   )
  12470.                                         if (   !(action_extension = strcasestr(parse_buf, ".cmd\""))   )
  12471.                                             if (   !(action_extension = strcasestr(parse_buf, ".hta "))   )
  12472.                                                 action_extension = strcasestr(parse_buf, ".hta\"");
  12473.  
  12474.             if (action_extension)
  12475.             {
  12476.                 shell_action = parse_buf;
  12477.                 // +4 for the 3-char extension with the period:
  12478.                 shell_params = action_extension + 4;  // exec_params is now the start of params, or empty-string.
  12479.                 if (*shell_params == '"')
  12480.                     // Exclude from shell_params since it's probably belongs to the action, not the params
  12481.                     // (i.e. it's paired with another double-quote at the start):
  12482.                     ++shell_params;
  12483.                 if (*shell_params)
  12484.                 {
  12485.                     // Terminate the <aAction> string in the right place.  For this to work correctly,
  12486.                     // at least one space must exist between action & params (shortcoming?):
  12487.                     *shell_params = '\0';
  12488.                     ++shell_params;
  12489.                     ltrim(shell_params); // Might be empty string after this, which is ok.
  12490.                 }
  12491.                 // else there doesn't appear to be any params, so just leave shell_params set to empty string.
  12492.             }
  12493.             // else there's no extension: so assume the whole <aAction> is a document name to be opened by
  12494.             // the shell.  So leave shell_action and shell_params set their original defaults.
  12495.         }
  12496.     }
  12497.  
  12498.     // This is distinct from hprocess being non-NULL because the two aren't always the
  12499.     // same.  For example, if the user does "Run, find D:\" or "RunWait, www.yahoo.com",
  12500.     // no new process handle will be available even though the launch was successful:
  12501.     bool success = false;
  12502.     char system_error_text[512] = "";
  12503.  
  12504.     bool use_runas = aUseRunAs && mRunAsUser && (*mRunAsUser || *mRunAsPass || *mRunAsDomain);
  12505.     if (use_runas && shell_action_is_system_verb)
  12506.     {
  12507.         if (aDisplayErrors)
  12508.             ScriptError("System verbs unsupported with RunAs." ERR_ABORT);
  12509.         return FAIL;
  12510.     }
  12511.  
  12512.     // If the caller originally gave us NULL for aParams, always try CreateProcess() before
  12513.     // trying ShellExecute().  This is because ShellExecute() is usually a lot slower.
  12514.     // The only exception is if the action appears to be a verb such as open, edit, or find.
  12515.     // In that case, we'll also skip the CreateProcess() attempt and do only the ShellExecute().
  12516.     // If the user really meant to launch find.bat or find.exe, for example, he should add
  12517.     // the extension (e.g. .exe) to differentiate "find" from "find.exe":
  12518.     if (!shell_action_is_system_verb)
  12519.     {
  12520.         STARTUPINFO si = {0}; // Zero fill to be safer.
  12521.         si.cb = sizeof(si);
  12522.         // The following are left at the default of NULL/0 set higher above:
  12523.         //si.lpReserved = si.lpDesktop = si.lpTitle = NULL;
  12524.         //si.lpReserved2 = NULL;
  12525.         si.dwFlags = STARTF_USESHOWWINDOW;  // This tells it to use the value of wShowWindow below.
  12526.         si.wShowWindow = (aRunShowMode && *aRunShowMode) ? Line::ConvertRunMode(aRunShowMode) : SW_SHOWNORMAL;
  12527.         PROCESS_INFORMATION pi = {0};
  12528.  
  12529.         // Since CreateProcess() requires that the 2nd param be modifiable, ensure that it is
  12530.         // (even if this is ANSI and not Unicode; it's just safer):
  12531.         char *command_line; // Need a new buffer other than parse_buf because parse_buf's contents may still be pointed to directly or indirectly for use further below.
  12532.         if (aParams && *aParams)
  12533.         {
  12534.             command_line = (char *)_alloca(aAction_length + strlen(aParams) + 10); // +10 to allow room for space, terminator, and any extra chars that might get added in the future.
  12535.             sprintf(command_line, "%s %s", aAction, aParams);
  12536.         }
  12537.         else // We're running the original action from caller.
  12538.         {
  12539.             command_line = (char *)_alloca(aAction_length + 1);
  12540.             strcpy(command_line, aAction); // CreateProcessW() requires modifiable string.  Although non-W version is used now, it feels safer to make it modifiable anyway.
  12541.         }
  12542.  
  12543.         if (use_runas)
  12544.         {
  12545.             if (!DoRunAs(command_line, aWorkingDir, aDisplayErrors, aUpdateLastError, si.wShowWindow  // wShowWindow (min/max/hide).
  12546.                 , aOutputVar, pi, success, hprocess, system_error_text)) // These are output parameters it will set for us.
  12547.                 return FAIL; // It already displayed the error, if appropriate.
  12548.         }
  12549.         else
  12550.         {
  12551.             // MSDN: "If [lpCurrentDirectory] is NULL, the new process is created with the same
  12552.             // current drive and directory as the calling process." (i.e. since caller may have
  12553.             // specified a NULL aWorkingDir).  Also, we pass NULL in for the first param so that
  12554.             // it will behave the following way (hopefully under all OSes): "the first white-space รป delimited
  12555.             // token of the command line specifies the module name. If you are using a long file name that
  12556.             // contains a space, use quoted strings to indicate where the file name ends and the arguments
  12557.             // begin (see the explanation for the lpApplicationName parameter). If the file name does not
  12558.             // contain an extension, .exe is appended. Therefore, if the file name extension is .com,
  12559.             // this parameter must include the .com extension. If the file name ends in a period (.) with
  12560.             // no extension, or if the file name contains a path, .exe is not appended. If the file name does
  12561.             // not contain a directory path, the system searches for the executable file in the following
  12562.             // sequence...".
  12563.             // Provide the app name (first param) if possible, for greater expected reliability.
  12564.             // UPDATE: Don't provide the module name because if it's enclosed in double quotes,
  12565.             // CreateProcess() will fail, at least under XP:
  12566.             //if (CreateProcess(aParams && *aParams ? aAction : NULL
  12567.             if (CreateProcess(NULL, command_line, NULL, NULL, FALSE, 0, NULL, aWorkingDir, &si, &pi))
  12568.             {
  12569.                 success = true;
  12570.                 if (pi.hThread)
  12571.                     CloseHandle(pi.hThread); // Required to avoid memory leak.
  12572.                 hprocess = pi.hProcess;
  12573.                 if (aOutputVar)
  12574.                     aOutputVar->Assign(pi.dwProcessId);
  12575.             }
  12576.             else
  12577.                 GetLastErrorText(system_error_text, sizeof(system_error_text), aUpdateLastError);
  12578.         }
  12579.     }
  12580.  
  12581.     if (!success) // Either the above wasn't attempted, or the attempt failed.  So try ShellExecute().
  12582.     {
  12583.         if (use_runas)
  12584.         {
  12585.             // Since CreateProcessWithLogonW() was either not attempted or did not work, it's probably
  12586.             // best to display an error rather than trying to run it without the RunAs settings.
  12587.             // This policy encourages users to have RunAs in effect only when necessary:
  12588.             if (aDisplayErrors)
  12589.                 ScriptError("Launch Error (possibly related to RunAs)." ERR_ABORT, system_error_text);
  12590.             return FAIL;
  12591.         }
  12592.         SHELLEXECUTEINFO sei = {0};
  12593.         // sei.hwnd is left NULL to avoid potential side-effects with having a hidden window be the parent.
  12594.         // However, doing so may result in the launched app appearing on a different monitor than the
  12595.         // script's main window appears on (for multimonitor systems).  This seems fairly inconsequential
  12596.         // since scripted workarounds are possible.
  12597.         sei.cbSize = sizeof(sei);
  12598.         // Below: "indicate that the hProcess member receives the process handle" and not to display error dialog:
  12599.         sei.fMask = SEE_MASK_NOCLOSEPROCESS | SEE_MASK_FLAG_NO_UI;
  12600.         sei.lpDirectory = aWorkingDir; // OK if NULL or blank; that will cause current dir to be used.
  12601.         sei.nShow = (aRunShowMode && *aRunShowMode) ? Line::ConvertRunMode(aRunShowMode) : SW_SHOWNORMAL;
  12602.         if (shell_action_is_system_verb)
  12603.         {
  12604.             sei.lpVerb = shell_action;
  12605.             if (!stricmp(shell_action, "properties"))
  12606.                 sei.fMask |= SEE_MASK_INVOKEIDLIST;  // Need to use this for the "properties" verb to work reliably.
  12607.             sei.lpFile = shell_params;
  12608.             sei.lpParameters = NULL;
  12609.         }
  12610.         else
  12611.         {
  12612.             sei.lpVerb = NULL;  // A better choice than "open" because NULL causes default verb to be used.
  12613.             sei.lpFile = shell_action;
  12614.             sei.lpParameters = *shell_params ? shell_params : NULL; // Above has ensured that shell_params isn't NULL.
  12615.             // Above was fixed v1.0.42.06 to be NULL rather than the empty string to prevent passing an
  12616.             // extra space at the end of a parameter list (this might happen only when launching a shortcut
  12617.             // [.lnk file]).  MSDN states: "If the lpFile member specifies a document file, lpParameters should
  12618.             // be NULL."  This implies that NULL is a suitable value for lpParameters in cases where you don't
  12619.             // want to pass any parameters at all.
  12620.         }
  12621.         if (ShellExecuteEx(&sei)) // Relies on short-circuit boolean order.
  12622.         {
  12623.             hprocess = sei.hProcess;
  12624.             // aOutputVar is left blank because:
  12625.             // ProcessID is not available when launched this way, and since GetProcessID() is only
  12626.             // available in WinXP SP1, no effort is currently made to dynamically load it from
  12627.             // kernel32.dll (to retain compatibility with older OSes).
  12628.             success = true;
  12629.         }
  12630.         else
  12631.             GetLastErrorText(system_error_text, sizeof(system_error_text), aUpdateLastError);
  12632.     }
  12633.  
  12634.     if (!success) // The above attempt(s) to launch failed.
  12635.     {
  12636.         if (aDisplayErrors)
  12637.         {
  12638.             char error_text[2048], verb_text[128];
  12639.             if (shell_action_is_system_verb)
  12640.                 snprintf(verb_text, sizeof(verb_text), "\nVerb: <%s>", shell_action);
  12641.             else // Don't bother showing it if it's just "open".
  12642.                 *verb_text = '\0';
  12643.             // Use format specifier to make sure it doesn't get too big for the error
  12644.             // function to display:
  12645.             snprintf(error_text, sizeof(error_text)
  12646.                 , "Failed attempt to launch program or document:"
  12647.                 "\nAction: <%-0.400s%s>"
  12648.                 "%s"
  12649.                 "\nParams: <%-0.400s%s>\n\n" ERR_ABORT_NO_SPACES
  12650.                 , shell_action, strlen(shell_action) > 400 ? "..." : ""
  12651.                 , verb_text
  12652.                 , shell_params, strlen(shell_params) > 400 ? "..." : ""
  12653.                 );
  12654.             ScriptError(error_text, system_error_text);
  12655.         }
  12656.         return FAIL;
  12657.     }
  12658.  
  12659.     // Otherwise, success:
  12660.     if (aUpdateLastError)
  12661.         g.LastError = 0; // Force zero to indicate success, which seems more maintainable and reliable than calling GetLastError() right here.
  12662.  
  12663.     // If aProcess isn't NULL, the caller wanted the process handle left open and so it must eventually call
  12664.     // CloseHandle().  Otherwise, we should close the process if it's non-NULL (it can be NULL in the case of
  12665.     // launching things like "find D:\" or "www.yahoo.com").
  12666.     if (!aProcess && hprocess)
  12667.         CloseHandle(hprocess); // Required to avoid memory leak.
  12668.     return OK;
  12669. }
  12670.