home *** CD-ROM | disk | FTP | other *** search
/ Computer Shopper 242 / Issue 242 - April 2008 - DPCS0408DVD.ISO / Open Source / AutoHotKey / Source / AutoHotkey104705_source.exe / source / util.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  2007-08-30  |  111.7 KB  |  2,317 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 <olectl.h> // for OleLoadPicture()
  19. #include <Gdiplus.h> // Used by LoadPicture().
  20. #include "util.h"
  21. #include "globaldata.h"
  22.  
  23.  
  24. int GetYDay(int aMon, int aDay, bool aIsLeapYear)
  25. // Returns a number between 1 and 366.
  26. // Caller must verify that aMon is a number between 1 and 12, and aDay is a number between 1 and 31.
  27. {
  28.     --aMon;  // Convert to zero-based.
  29.     if (aIsLeapYear)
  30.     {
  31.         int leap_offset[12] = {0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335};
  32.         return leap_offset[aMon] + aDay;
  33.     }
  34.     else
  35.     {
  36.         int normal_offset[12] = {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334};
  37.         return normal_offset[aMon] + aDay;
  38.     }
  39. }
  40.  
  41.  
  42.  
  43. int GetISOWeekNumber(char *aBuf, int aYear, int aYDay, int aWDay)
  44. // Caller must ensure that aBuf is of size 7 or greater, that aYear is a valid year (e.g. 2005),
  45. // that aYDay is between 1 and 366, and that aWDay is between 0 and 6 (day of the week).
  46. // Produces the week number in YYYYNN format, e.g. 200501.
  47. // Note that year is also returned because it isn't necessarily the same as aTime's calendar year.
  48. // Based on Linux glibc source code (GPL).
  49. {
  50.     --aYDay;  // Convert to zero based.
  51.     #define ISO_WEEK_START_WDAY 1 // Monday
  52.     #define ISO_WEEK1_WDAY 4      // Thursday
  53.     #define ISO_WEEK_DAYS(yday, wday) (yday - (yday - wday + ISO_WEEK1_WDAY + ((366 / 7 + 2) * 7)) % 7 \
  54.         + ISO_WEEK1_WDAY - ISO_WEEK_START_WDAY);
  55.  
  56.     int year = aYear;
  57.     int days = ISO_WEEK_DAYS(aYDay, aWDay);
  58.  
  59.     if (days < 0) // This ISO week belongs to the previous year.
  60.     {
  61.         --year;
  62.         days = ISO_WEEK_DAYS(aYDay + (365 + IS_LEAP_YEAR(year)), aWDay);
  63.     }
  64.     else
  65.     {
  66.         int d = ISO_WEEK_DAYS(aYDay - (365 + IS_LEAP_YEAR(year)), aWDay);
  67.         if (0 <= d) // This ISO week belongs to the next year.
  68.         {
  69.             ++year;
  70.             days = d;
  71.         }
  72.     }
  73.  
  74.     // Use snprintf() for safety; that is, in case year contains a value longer than 4 digits.
  75.     // This also adds the leading zeros in front of year and week number, if needed.
  76.     return snprintf(aBuf, 7, "%04d%02d", year, (days / 7) + 1); // Return the length of the string produced.
  77. }
  78.  
  79.  
  80.  
  81. ResultType YYYYMMDDToFileTime(char *aYYYYMMDD, FILETIME &aFileTime)
  82. {
  83.     SYSTEMTIME st;
  84.     YYYYMMDDToSystemTime(aYYYYMMDD, st, false);  // "false" because it's validated below.
  85.     // This will return failure if aYYYYMMDD contained any invalid elements, such as an
  86.     // explicit zero for the day of the month.  It also reports failure if st.wYear is
  87.     // less than 1601, which for simplicity is enforced globally throughout the program
  88.     // since none of the Windows API calls seem to support earlier years.
  89.     return SystemTimeToFileTime(&st, &aFileTime) ? OK : FAIL; // The st.wDayOfWeek member is ignored.
  90. }
  91.  
  92.  
  93.  
  94. DWORD YYYYMMDDToSystemTime2(char *aYYYYMMDD, SYSTEMTIME *aSystemTime)
  95. // Calls YYYYMMDDToSystemTime() to fill up to two elements of the aSystemTime array.
  96. // Returns a GDTR bitwise combination to indicate which of the two elements, or both, are valid.
  97. // Caller must ensure that aYYYYMMDD is a modifiable string since it's temporarily altered and restored here.
  98. {
  99.     DWORD gdtr = 0;
  100.     if (!*aYYYYMMDD)
  101.         return gdtr;
  102.     if (*aYYYYMMDD != '-') // Since first char isn't a dash, there is a minimum present.
  103.     {
  104.         char *cp;
  105.         if (cp = strchr(aYYYYMMDD + 1, '-'))
  106.             *cp = '\0'; // Temporarily terminate in case only the leading part of the YYYYMMDD format is present.  Otherwise, the dash and other chars would be considered invalid fields.
  107.         if (YYYYMMDDToSystemTime(aYYYYMMDD, aSystemTime[0], true)) // Date string is valid.
  108.             gdtr |= GDTR_MIN; // Indicate that minimum is present.
  109.         if (cp)
  110.         {
  111.             *cp = '-'; // Undo the temp. termination.
  112.             aYYYYMMDD = cp + 1; // Set it to the maximum's position for use below.
  113.         }
  114.         else // No dash, so there is no maximum.  Indicate this by making aYYYYMMDD empty.
  115.             aYYYYMMDD = "";
  116.     }
  117.     else // *aYYYYMMDD=='-', so only the maximum is present; thus there will be no minimum.
  118.         ++aYYYYMMDD; // Skip over the dash to set it to the maximum's position.
  119.     if (*aYYYYMMDD) // There is a maximum.
  120.     {
  121.         if (YYYYMMDDToSystemTime(aYYYYMMDD, aSystemTime[1], true)) // Date string is valid.
  122.             gdtr |= GDTR_MAX; // Indicate that maximum is present.
  123.     }
  124.     return gdtr;
  125. }
  126.  
  127.  
  128.  
  129. ResultType YYYYMMDDToSystemTime(char *aYYYYMMDD, SYSTEMTIME &aSystemTime, bool aDoValidate)
  130. // Although aYYYYMMDD need not be terminated at the end of the YYYYMMDDHH24MISS string (as long as
  131. // the string's capacity is at least 14), it should be terminated if only the leading part
  132. // of the YYYYMMDDHH24MISS format is present.
  133. // Caller must ensure that aYYYYMMDD is non-NULL.  If aDoValidate is false, OK is always
  134. // returned and aSystemTime might contain invalid elements.  Otherwise, FAIL will be returned
  135. // if the date and time contains any invalid elements, or if the year is less than 1601
  136. // (Windows generally does not support earlier years).
  137. {
  138.     // sscanf() is avoided because it adds 2 KB to the compressed EXE size.
  139.     char temp[16];
  140.     size_t length = strlen(aYYYYMMDD); // Use this rather than incrementing the pointer in case there are ever partial fields such as 20051 vs. 200501.
  141.  
  142.     strlcpy(temp, aYYYYMMDD, 5);
  143.     aSystemTime.wYear = atoi(temp);
  144.  
  145.     if (length > 4) // It has a month component.
  146.     {
  147.         strlcpy(temp, aYYYYMMDD + 4, 3);
  148.         aSystemTime.wMonth = atoi(temp);  // Unlike "struct tm", SYSTEMTIME uses 1 for January, not 0.
  149.         if (aSystemTime.wMonth < 1 || aSystemTime.wMonth > 12) // v1.0.46.07: Must validate month since it's used to access an array further below.
  150.             aSystemTime.wMonth = 1; // For simplicity and due to extreme rarity, just choose an in-range value.
  151.     }
  152.     else
  153.         aSystemTime.wMonth = 1;
  154.  
  155.     if (length > 6) // It has a day-of-month component.
  156.     {
  157.         strlcpy(temp, aYYYYMMDD + 6, 3);
  158.         aSystemTime.wDay = atoi(temp);
  159.     }
  160.     else
  161.         aSystemTime.wDay = 1;
  162.  
  163.     if (length > 8) // It has an hour component.
  164.     {
  165.         strlcpy(temp, aYYYYMMDD + 8, 3);
  166.         aSystemTime.wHour = atoi(temp);
  167.     }
  168.     else
  169.         aSystemTime.wHour = 0;   // Midnight.
  170.  
  171.     if (length > 10) // It has a minutes component.
  172.     {
  173.         strlcpy(temp, aYYYYMMDD + 10, 3);
  174.         aSystemTime.wMinute = atoi(temp);
  175.     }
  176.     else
  177.         aSystemTime.wMinute = 0;
  178.  
  179.     if (length > 12) // It has a seconds component.
  180.     {
  181.         strlcpy(temp, aYYYYMMDD + 12, 3);
  182.         aSystemTime.wSecond = atoi(temp);
  183.     }
  184.     else
  185.         aSystemTime.wSecond = 0;
  186.  
  187.     aSystemTime.wMilliseconds = 0;  // Always set to zero in this case.
  188.  
  189.     // Day-of-week code by Tomohiko Sakamoto:
  190.     static int t[] = {0, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4};
  191.     int y = aSystemTime.wYear;
  192.     y -= aSystemTime.wMonth < 3;
  193.     aSystemTime.wDayOfWeek = (y + y/4 - y/100 + y/400 + t[aSystemTime.wMonth-1] + aSystemTime.wDay) % 7;
  194.  
  195.     if (aDoValidate)
  196.     {
  197.         FILETIME ft;
  198.         // This will return failure if aYYYYMMDD contained any invalid elements, such as an
  199.         // explicit zero for the day of the month.  It also reports failure if st.wYear is
  200.         // less than 1601, which for simplicity is enforced globally throughout the program
  201.         // since none of the Windows API calls seem to support earlier years.
  202.         return SystemTimeToFileTime(&aSystemTime, &ft) ? OK : FAIL;
  203.         // Above: The st.wDayOfWeek member is ignored, but that's okay since only the YYYYMMDDHH24MISS part
  204.         // needs validation.
  205.     }
  206.     return OK;
  207. }
  208.  
  209.  
  210.  
  211. char *FileTimeToYYYYMMDD(char *aBuf, FILETIME &aTime, bool aConvertToLocalTime)
  212. // Returns aBuf.
  213. {
  214.     FILETIME ft;
  215.     if (aConvertToLocalTime)
  216.         FileTimeToLocalFileTime(&aTime, &ft); // MSDN says that target cannot be the same var as source.
  217.     else
  218.         memcpy(&ft, &aTime, sizeof(FILETIME));  // memcpy() might be less code size that a struct assignment, ft = aTime.
  219.     SYSTEMTIME st;
  220.     if (FileTimeToSystemTime(&ft, &st))
  221.         return SystemTimeToYYYYMMDD(aBuf, st);
  222.     *aBuf = '\0';
  223.     return aBuf;
  224. }
  225.  
  226.  
  227.  
  228. char *SystemTimeToYYYYMMDD(char *aBuf, SYSTEMTIME &aTime)
  229. // Returns aBuf.
  230. // Remember not to offer a "aConvertToLocalTime" option, because calling SystemTimeToTzSpecificLocalTime()
  231. // on Win9x apparently results in an invalid time because the function is implemented only as a stub on
  232. // those OSes.
  233. {
  234.     sprintf(aBuf, "%04d%02d%02d" "%02d%02d%02d"
  235.         , aTime.wYear, aTime.wMonth, aTime.wDay
  236.         , aTime.wHour, aTime.wMinute, aTime.wSecond);
  237.     return aBuf;
  238. }
  239.  
  240.  
  241.  
  242. __int64 YYYYMMDDSecondsUntil(char *aYYYYMMDDStart, char *aYYYYMMDDEnd, bool &aFailed)
  243. // Returns the number of seconds from aYYYYMMDDStart until aYYYYMMDDEnd.
  244. // If aYYYYMMDDStart is blank, the current time will be used in its place.
  245. {
  246.     aFailed = true;  // Set default for output parameter, in case of early return.
  247.     if (!aYYYYMMDDStart || !aYYYYMMDDEnd) return 0;
  248.  
  249.     FILETIME ftStart, ftEnd, ftNowUTC;
  250.  
  251.     if (*aYYYYMMDDStart)
  252.     {
  253.         if (!YYYYMMDDToFileTime(aYYYYMMDDStart, ftStart))
  254.             return 0;
  255.     }
  256.     else // Use the current time in its place.
  257.     {
  258.         GetSystemTimeAsFileTime(&ftNowUTC);
  259.         FileTimeToLocalFileTime(&ftNowUTC, &ftStart);  // Convert UTC to local time.
  260.     }
  261.     if (*aYYYYMMDDEnd)
  262.     {
  263.         if (!YYYYMMDDToFileTime(aYYYYMMDDEnd, ftEnd))
  264.             return 0;
  265.     }
  266.     else // Use the current time in its place.
  267.     {
  268.         GetSystemTimeAsFileTime(&ftNowUTC);
  269.         FileTimeToLocalFileTime(&ftNowUTC, &ftEnd);  // Convert UTC to local time.
  270.     }
  271.     aFailed = false;  // Indicate success.
  272.     return FileTimeSecondsUntil(&ftStart, &ftEnd);
  273. }
  274.  
  275.  
  276.  
  277. __int64 FileTimeSecondsUntil(FILETIME *pftStart, FILETIME *pftEnd)
  278. // Returns the number of seconds from pftStart until pftEnd.
  279. {
  280.     if (!pftStart || !pftEnd) return 0;
  281.  
  282.     // The calculation is done this way for compilers that don't support 64-bit math operations (not sure which):
  283.     // Note: This must be LARGE vs. ULARGE because we want the calculation to be signed for cases where
  284.     // pftStart is greater than pftEnd:
  285.     ULARGE_INTEGER uiStart, uiEnd;
  286.     uiStart.LowPart = pftStart->dwLowDateTime;
  287.     uiStart.HighPart = pftStart->dwHighDateTime;
  288.     uiEnd.LowPart = pftEnd->dwLowDateTime;
  289.     uiEnd.HighPart = pftEnd->dwHighDateTime;
  290.     // Must do at least the inner cast to avoid losing negative results:
  291.     return (__int64)((__int64)(uiEnd.QuadPart - uiStart.QuadPart) / 10000000); // Convert from tenths-of-microsecond.
  292. }
  293.  
  294.  
  295.  
  296. SymbolType IsPureNumeric(char *aBuf, BOOL aAllowNegative, BOOL aAllowAllWhitespace
  297.     , BOOL aAllowFloat, BOOL aAllowImpure)  // BOOL vs. bool might squeeze a little more performance out of this frequently-called function.
  298. // String can contain whitespace.
  299. // If aBuf doesn't contain something purely numeric, PURE_NOT_NUMERIC is returned.  The same happens if
  300. // aBuf contains a float but aAllowFloat is false.  Otherwise, PURE_INTEGER or PURE_FLOAT is returned.
  301. // If aAllowAllWhitespace==true and the string is blank or all whitespace, PURE_INTEGER is returned.
  302. // Obsolete comment: Making this non-inline reduces the size of the compressed EXE by only 2K.  Since this
  303. // function is called so often, it seems preferable to keep it inline for performance.
  304. {
  305.     aBuf = omit_leading_whitespace(aBuf); // i.e. caller doesn't have to have ltrimmed, only rtrimmed.
  306.     if (!*aBuf) // The string is empty or consists entirely of whitespace.
  307.         return aAllowAllWhitespace ? PURE_INTEGER : PURE_NOT_NUMERIC;
  308.  
  309.     if (*aBuf == '-')
  310.     {
  311.         if (aAllowNegative)
  312.             ++aBuf;
  313.         else
  314.             return PURE_NOT_NUMERIC;
  315.     }
  316.     else if (*aBuf == '+')
  317.         ++aBuf;
  318.  
  319.     // Relies on short circuit boolean order to prevent reading beyond the end of the string:
  320.     BOOL is_hex = IS_HEX(aBuf); // BOOL vs. bool might squeeze a little more performance out this frequently-called function.
  321.     if (is_hex)
  322.         aBuf += 2;  // Skip over the 0x prefix.
  323.  
  324.     // Set defaults:
  325.     BOOL has_decimal_point = false;
  326.     BOOL has_at_least_one_digit = false; // i.e. a string consisting of only "+", "-" or "." is not considered numeric.
  327.     char c;
  328.  
  329.     for (;; ++aBuf)
  330.     {
  331.         c = *aBuf;
  332.         if (IS_SPACE_OR_TAB(c))
  333.         {
  334.             if (*omit_leading_whitespace(aBuf)) // But that space or tab is followed by something other than whitespace.
  335.                 if (!aAllowImpure) // e.g. "123 456" is not a valid pure number.
  336.                     return PURE_NOT_NUMERIC;
  337.                 // else fall through to the bottom logic.
  338.             // else since just whitespace at the end, the number qualifies as pure, so fall through to the bottom
  339.             // logic (it would already have returned in the loop if it was impure)
  340.             break;
  341.         }
  342.         if (!c) // End of string was encountered.
  343.             break; // The number qualifies as pure, so fall through to the logic at the bottom. (It would already have returned elsewhere in the loop if the number is impure).
  344.         if (c == '.')
  345.         {
  346.             if (!aAllowFloat || has_decimal_point || is_hex)
  347.                 // i.e. if aBuf contains 2 decimal points, it can't be a valid number.
  348.                 // Note that decimal points are allowed in hexadecimal strings, e.g. 0xFF.EE.
  349.                 // But since that format doesn't seem to be supported by VC++'s atof() and probably
  350.                 // related functions, and since it's extremely rare, it seems best not to support it.
  351.                 return PURE_NOT_NUMERIC;
  352.             else
  353.                 has_decimal_point = true;
  354.         }
  355.         else
  356.         {
  357.             if (is_hex ? !isxdigit(c) : (c < '0' || c > '9')) // And since we're here, it's not '.' either.
  358.             {
  359.                 if (aAllowImpure) // Since aStr starts with a number (as verified above), it is considered a number.
  360.                 {
  361.                     if (has_at_least_one_digit)
  362.                         return has_decimal_point ? PURE_FLOAT : PURE_INTEGER;
  363.                     else // i.e. the strings "." and "-" are not considered to be numeric by themselves.
  364.                         return PURE_NOT_NUMERIC;
  365.                 }
  366.                 else
  367.                 {
  368.                     // As written below, this actually tolerates malformed scientific notation such as numbers
  369.                     // containing two or more E's (e.g. 1.0e4e+5e-6,).  But for performance and due to rarity,
  370.                     // it seems best not to check for them.
  371.                     if (toupper(c) != 'E' // v1.0.46.11: Support scientific notation in floating point numbers.
  372.                         || !(has_decimal_point && has_at_least_one_digit)) // But it must have a decimal point and at least one digit to the left of the 'E'. This avoids variable names like "1e4" from being seen as sci-notation literals (for backward compatibility). Some callers rely on this check.
  373.                         return PURE_NOT_NUMERIC;
  374.                     if (aBuf[1] == '-' || aBuf[1] == '+') // The optional sign is present on the exponent.
  375.                         ++aBuf; // Omit it from further consideration so that the outer loop doesn't see it as an extra/illegal sign.
  376.                     if (aBuf[1] < '0' || aBuf[1] > '9')
  377.                         // Even if it is an 'e', ensure what follows it is a valid exponent.  Some callers rely
  378.                         // on this check, such as ones that expect "0.6e" to be non-numeric (for "SetFormat Float") 
  379.                         return PURE_NOT_NUMERIC;
  380.                 }
  381.             }
  382.             else // This character is a valid digit or hex-digit.
  383.                 has_at_least_one_digit = true;
  384.         }
  385.     } // for()
  386.  
  387.     if (has_at_least_one_digit)
  388.         return has_decimal_point ? PURE_FLOAT : PURE_INTEGER;
  389.     else
  390.         return PURE_NOT_NUMERIC; // i.e. the strings "+" "-" and "." are not numeric by themselves.
  391. }
  392.  
  393.  
  394.  
  395. void strlcpy(char *aDst, const char *aSrc, size_t aDstSize) // Non-inline because it benches slightly faster that way.
  396. // Caller must ensure that aDstSize is greater than 0.
  397. // Caller must ensure that the entire capacity of aDst is writable, EVEN WHEN it knows that aSrc is much shorter
  398. // than the aDstSize.  This is because the call to strncpy (which is used for its superior performance) zero-fills
  399. // any unused portion of aDst.
  400. // Description:
  401. // Same as strncpy() but guarantees null-termination of aDst upon return.
  402. // No more than aDstSize - 1 characters will be copied from aSrc into aDst
  403. // (leaving room for the zero terminator, which is always inserted).
  404. // This function is defined in some Unices but is not standard.  But unlike
  405. // other versions, this one uses void for return value for reduced code size
  406. // (since it's called in so many places).
  407. {
  408.     // Disabled for performance and reduced code size:
  409.     //if (!aDst || !aSrc || !aDstSize) return aDstSize;  // aDstSize must not be zero due to the below method.
  410.     // It might be worthwhile to have a custom char-copying-loop here someday so that number of characters
  411.     // actually copied (not including the zero terminator) can be returned to callers who want it.
  412.     --aDstSize; // Convert from size to length (caller has ensured that aDstSize > 0).
  413.     strncpy(aDst, aSrc, aDstSize); // NOTE: In spite of its zero-filling, strncpy() benchmarks considerably faster than a custom loop, probably because it uses 32-bit memory operations vs. 8-bit.
  414.     aDst[aDstSize] = '\0';
  415. }
  416.  
  417.  
  418.  
  419. int snprintf(char *aBuf, int aBufSize, const char *aFormat, ...)
  420. // aBufSize is an int so that any negative values passed in from caller are not lost.
  421. // aBuf will always be terminated here except when aBufSize is <= zero (in which case the caller should
  422. // already have terminated it).  If aBufSize is greater than zero but not large enough to hold the
  423. // entire result, as much of the result as possible is copied and the return value is aBufSize - 1.
  424. // Returns the exact number of characters written, not including the zero terminator.  A negative
  425. // number is never returned, even if aBufSize is <= zero (which means there isn't even enough space left
  426. // to write a zero terminator), under the assumption that the caller has already terminated the string
  427. // and thus prefers to have 0 rather than -1 returned in such cases.
  428. // MSDN says (about _snprintf(), and testing shows that it applies to _vsnprintf() too): "This function
  429. // does not guarantee NULL termination, so ensure it is followed by sz[size - 1] = 0".
  430. {
  431.     // The following should probably never be changed without a full suite of tests to ensure the
  432.     // change doesn't cause the finicky _vsnprintf() to break something.
  433.     if (aBufSize < 1 || !aBuf || !aFormat) return 0; // It's called from so many places that the extra checks seem warranted.
  434.     va_list ap;
  435.     va_start(ap, aFormat);
  436.     // Must use _vsnprintf() not _snprintf() because of the way va_list is handled:
  437.     int result = _vsnprintf(aBuf, aBufSize, aFormat, ap); // "returns the number of characters written, not including the terminating null character, or a negative value if an output error occurs"
  438.     aBuf[aBufSize - 1] = '\0'; // Confirmed through testing: Must terminate at this exact spot because _vsnprintf() doesn't always do it.
  439.     // Fix for v1.0.34: If result==aBufSize, must reduce result by 1 to return an accurate result to the
  440.     // caller.  In other words, if the line above turned the last character into a terminator, one less character
  441.     // is now present in aBuf.
  442.     if (result == aBufSize)
  443.         --result;
  444.     return result > -1 ? result : aBufSize - 1; // Never return a negative value.  See comment under function definition, above.
  445. }
  446.  
  447.  
  448.  
  449. int snprintfcat(char *aBuf, int aBufSize, const char *aFormat, ...)
  450. // aBufSize is an int so that any negative values passed in from caller are not lost.
  451. // aBuf will always be terminated here except when the amount of space left in the buffer is zero or less.
  452. // (in which case the caller should already have terminated it).  If aBufSize is greater than zero but not
  453. // large enough to hold the entire result, as much of the result as possible is copied and the return value
  454. // is space_remaining - 1.
  455. // The caller must have ensured that aBuf and aFormat are non-NULL and that aBuf contains a valid string
  456. // (i.e. that it is null-terminated somewhere within the limits of aBufSize).
  457. // Returns the exact number of characters written, not including the zero terminator.  A negative
  458. // number is never returned, even if aBufSize is <= zero (which means there isn't even enough space left
  459. // to write a zero terminator), under the assumption that the caller has already terminated the string
  460. // and thus prefers to have 0 rather than -1 returned in such cases.
  461. {
  462.     // The following should probably never be changed without a full suite of tests to ensure the
  463.     // change doesn't cause the finicky _vsnprintf() to break something.
  464.     size_t length = strlen(aBuf);
  465.     int space_remaining = (int)(aBufSize - length); // Must cast to int to avoid loss of negative values.
  466.     if (space_remaining < 1) // Can't even terminate it (no room) so just indicate that no characters were copied.
  467.         return 0;
  468.     aBuf += length;  // aBuf is now the spot where the new text will be written.
  469.     va_list ap;
  470.     va_start(ap, aFormat);
  471.     // Must use vsnprintf() not snprintf() because of the way va_list is handled:
  472.     int result = _vsnprintf(aBuf, (size_t)space_remaining, aFormat, ap); // "returns the number of characters written, not including the terminating null character, or a negative value if an output error occurs"
  473.     aBuf[space_remaining - 1] = '\0'; // Confirmed through testing: Must terminate at this exact spot because _vsnprintf() doesn't always do it.
  474.     return result > -1 ? result : space_remaining - 1; // Never return a negative value.  See comment under function definition, above.
  475. }
  476.  
  477.  
  478.  
  479. // Not currently used by anything, so commented out to possibly reduce code size:
  480. //int strlcmp(char *aBuf1, char *aBuf2, UINT aLength1, UINT aLength2)
  481. //// Case sensitive version.  See strlicmp() comments below.
  482. //{
  483. //    if (!aBuf1 || !aBuf2) return 0;
  484. //    if (aLength1 == UINT_MAX) aLength1 = (UINT)strlen(aBuf1);
  485. //    if (aLength2 == UINT_MAX) aLength2 = (UINT)strlen(aBuf2);
  486. //    UINT least_length = aLength1 < aLength2 ? aLength1 : aLength2;
  487. //    int diff;
  488. //    for (UINT i = 0; i < least_length; ++i)
  489. //        if (   diff = (int)((UCHAR)aBuf1[i] - (UCHAR)aBuf2[i])   ) // Use unsigned chars like strcmp().
  490. //            return diff;
  491. //    return (int)(aLength1 - aLength2);
  492. //}
  493.  
  494.  
  495.  
  496. int strlicmp(char *aBuf1, char *aBuf2, UINT aLength1, UINT aLength2)
  497. // Similar to strnicmp but considers each aBuf to be a string of length aLength if aLength was
  498. // specified.  In other words, unlike strnicmp() which would consider strnicmp("ab", "abc", 2)
  499. // [example verified correct] to be a match, this function would consider them to be
  500. // a mismatch.  Another way of looking at it: aBuf1 and aBuf2 will be directly
  501. // compared to one another as though they were actually of length aLength1 and
  502. // aLength2, respectively and then passed to stricmp() (not strnicmp) as those
  503. // shorter strings.  This behavior is useful for cases where you don't want
  504. // to have to bother with temporarily terminating a string so you can compare
  505. // only a substring to something else.  The return value meaning is the
  506. // same as strnicmp().  If either aLength param is UINT_MAX (via the default
  507. // parameters or via explicit call), it will be assumed that the entire
  508. // length of the respective aBuf will be used.
  509. {
  510.     if (!aBuf1 || !aBuf2) return 0;
  511.     if (aLength1 == UINT_MAX) aLength1 = (UINT)strlen(aBuf1);
  512.     if (aLength2 == UINT_MAX) aLength2 = (UINT)strlen(aBuf2);
  513.     UINT least_length = aLength1 < aLength2 ? aLength1 : aLength2;
  514.     int diff;
  515.     for (UINT i = 0; i < least_length; ++i)
  516.         if (   diff = (int)((UCHAR)toupper(aBuf1[i]) - (UCHAR)toupper(aBuf2[i]))   )
  517.             return diff;
  518.     // Since the above didn't return, the strings are equal if they're the same length.
  519.     // Otherwise, the longer one is considered greater than the shorter one since the
  520.     // longer one's next character is by definition something non-zero.  I'm not completely
  521.     // sure that this is the same policy followed by ANSI strcmp():
  522.     return (int)(aLength1 - aLength2);
  523. }
  524.  
  525.  
  526.  
  527. char *strrstr(char *aStr, char *aPattern, StringCaseSenseType aStringCaseSense, int aOccurrence)
  528. // Returns NULL if not found, otherwise the address of the found string.
  529. // This could probably use a faster algorithm someday.  For now it seems adequate because
  530. // scripts rarely use it and when they do, it's usually on short haystack strings (such as
  531. // to find the last period in a filename).
  532. {
  533.     if (aOccurrence < 1)
  534.         return NULL;
  535.     size_t aStr_length = strlen(aStr);
  536.     if (!*aPattern)
  537.         // The empty string is found in every string, and since we're searching from the right, return
  538.         // the position of the zero terminator to indicate the situation:
  539.         return aStr + aStr_length;
  540.  
  541.     size_t aPattern_length = strlen(aPattern);
  542.     char aPattern_last_char = aPattern[aPattern_length - 1];
  543.     char aPattern_last_char_lower = (aStringCaseSense == SCS_INSENSITIVE_LOCALE)
  544.         ? (char)ltolower(aPattern_last_char)
  545.         : tolower(aPattern_last_char);
  546.  
  547.     int occurrence = 0;
  548.     char *match_starting_pos = aStr + aStr_length - 1;
  549.  
  550.     // Keep finding matches from the right until the Nth occurrence (specified by the caller) is found.
  551.     for (;;)
  552.     {
  553.         if (match_starting_pos < aStr)
  554.             return NULL;  // No further matches are possible.
  555.         // Find (from the right) the first occurrence of aPattern's last char:
  556.         char *last_char_match;
  557.         for (last_char_match = match_starting_pos; last_char_match >= aStr; --last_char_match)
  558.         {
  559.             if (aStringCaseSense == SCS_INSENSITIVE) // The most common mode is listed first for performance.
  560.             {
  561.                 if (tolower(*last_char_match) == aPattern_last_char_lower)
  562.                     break;
  563.             }
  564.             else if (aStringCaseSense == SCS_INSENSITIVE_LOCALE)
  565.             {
  566.                 if ((char)ltolower(*last_char_match) == aPattern_last_char_lower)
  567.                     break;
  568.             }
  569.             else // Case sensitive.
  570.             {
  571.                 if (*last_char_match == aPattern_last_char)
  572.                     break;
  573.             }
  574.         }
  575.  
  576.         if (last_char_match < aStr) // No further matches are possible.
  577.             return NULL;
  578.  
  579.         // Now that aPattern's last character has been found in aStr, ensure the rest of aPattern
  580.         // exists in aStr to the left of last_char_match:
  581.         char *full_match, *cp;
  582.         bool found;
  583.         for (found = false, cp = aPattern + aPattern_length - 2, full_match = last_char_match - 1;; --cp, --full_match)
  584.         {
  585.             if (cp < aPattern) // The complete pattern has been found at the position in full_match + 1.
  586.             {
  587.                 ++full_match; // Adjust for the prior iteration's decrement.
  588.                 if (++occurrence == aOccurrence)
  589.                     return full_match;
  590.                 found = true;
  591.                 break;
  592.             }
  593.             if (full_match < aStr) // Only after checking the above is this checked.
  594.                 break;
  595.  
  596.             if (aStringCaseSense == SCS_INSENSITIVE) // The most common mode is listed first for performance.
  597.             {
  598.                 if (tolower(*full_match) != tolower(*cp))
  599.                     break;
  600.             }
  601.             else if (aStringCaseSense == SCS_INSENSITIVE_LOCALE)
  602.             {
  603.                 if (ltolower(*full_match) != ltolower(*cp))
  604.                     break;
  605.             }
  606.             else // Case sensitive.
  607.             {
  608.                 if (*full_match != *cp)
  609.                     break;
  610.             }
  611.         } // for() innermost
  612.         if (found) // Although the above found a match, it wasn't the right one, so resume searching.
  613.             match_starting_pos = full_match - 1;
  614.         else // the pattern broke down, so resume searching at THIS position.
  615.             match_starting_pos = last_char_match - 1;  // Don't go back by more than 1.
  616.     } // while() find next match
  617. }
  618.  
  619.  
  620.  
  621. char *strcasestr(const char *phaystack, const char *pneedle)
  622.     // To make this work with MS Visual C++, this version uses tolower/toupper() in place of
  623.     // _tolower/_toupper(), since apparently in GNU C, the underscore macros are identical
  624.     // to the non-underscore versions; but in MS the underscore ones do an unconditional
  625.     // conversion (mangling non-alphabetic characters such as the zero terminator).  MSDN:
  626.     // tolower: Converts c to lowercase if appropriate
  627.     // _tolower: Converts c to lowercase
  628.  
  629.     // Return the offset of one string within another.
  630.     // Copyright (C) 1994,1996,1997,1998,1999,2000 Free Software Foundation, Inc.
  631.     // This file is part of the GNU C Library.
  632.  
  633.     // The GNU C Library is free software; you can redistribute it and/or
  634.     // modify it under the terms of the GNU Lesser General Public
  635.     // License as published by the Free Software Foundation; either
  636.     // version 2.1 of the License, or (at your option) any later version.
  637.  
  638.     // The GNU C Library is distributed in the hope that it will be useful,
  639.     // but WITHOUT ANY WARRANTY; without even the implied warranty of
  640.     // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  641.     // Lesser General Public License for more details.
  642.  
  643.     // You should have received a copy of the GNU Lesser General Public
  644.     // License along with the GNU C Library; if not, write to the Free
  645.     // Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
  646.     // 02111-1307 USA.
  647.  
  648.     // My personal strstr() implementation that beats most other algorithms.
  649.     // Until someone tells me otherwise, I assume that this is the
  650.     // fastest implementation of strstr() in C.
  651.     // I deliberately chose not to comment it.  You should have at least
  652.     // as much fun trying to understand it, as I had to write it :-).
  653.     // Stephen R. van den Berg, berg@pool.informatik.rwth-aachen.de
  654.  
  655.     // Faster looping by precalculating bl, bu, cl, cu before looping.
  656.     // 2004 Apr 08    Jose Da Silva, digital@joescat@com
  657. {
  658.     register const unsigned char *haystack, *needle;
  659.     register unsigned bl, bu, cl, cu;
  660.     
  661.     haystack = (const unsigned char *) phaystack;
  662.     needle = (const unsigned char *) pneedle;
  663.  
  664.     bl = tolower(*needle);
  665.     if (bl != '\0')
  666.     {
  667.         // Scan haystack until the first character of needle is found:
  668.         bu = toupper(bl);
  669.         haystack--;                /* possible ANSI violation */
  670.         do
  671.         {
  672.             cl = *++haystack;
  673.             if (cl == '\0')
  674.                 goto ret0;
  675.         }
  676.         while ((cl != bl) && (cl != bu));
  677.  
  678.         // See if the rest of needle is a one-for-one match with this part of haystack:
  679.         cl = tolower(*++needle);
  680.         if (cl == '\0')  // Since needle consists of only one character, it is already a match as found above.
  681.             goto foundneedle;
  682.         cu = toupper(cl);
  683.         ++needle;
  684.         goto jin;
  685.         
  686.         for (;;)
  687.         {
  688.             register unsigned a;
  689.             register const unsigned char *rhaystack, *rneedle;
  690.             do
  691.             {
  692.                 a = *++haystack;
  693.                 if (a == '\0')
  694.                     goto ret0;
  695.                 if ((a == bl) || (a == bu))
  696.                     break;
  697.                 a = *++haystack;
  698.                 if (a == '\0')
  699.                     goto ret0;
  700. shloop:
  701.                 ;
  702.             }
  703.             while ((a != bl) && (a != bu));
  704.  
  705. jin:
  706.             a = *++haystack;
  707.             if (a == '\0')  // Remaining part of haystack is shorter than needle.  No match.
  708.                 goto ret0;
  709.  
  710.             if ((a != cl) && (a != cu)) // This promising candidate is not a complete match.
  711.                 goto shloop;            // Start looking for another match on the first char of needle.
  712.             
  713.             rhaystack = haystack-- + 1;
  714.             rneedle = needle;
  715.             a = tolower(*rneedle);
  716.             
  717.             if (tolower(*rhaystack) == (int) a)
  718.             do
  719.             {
  720.                 if (a == '\0')
  721.                     goto foundneedle;
  722.                 ++rhaystack;
  723.                 a = tolower(*++needle);
  724.                 if (tolower(*rhaystack) != (int) a)
  725.                     break;
  726.                 if (a == '\0')
  727.                     goto foundneedle;
  728.                 ++rhaystack;
  729.                 a = tolower(*++needle);
  730.             }
  731.             while (tolower(*rhaystack) == (int) a);
  732.             
  733.             needle = rneedle;        /* took the register-poor approach */
  734.             
  735.             if (a == '\0')
  736.                 break;
  737.         } // for(;;)
  738.     } // if (bl != '\0')
  739. foundneedle:
  740.     return (char*) haystack;
  741. ret0:
  742.     return 0;
  743. }
  744.  
  745.  
  746.  
  747. char *lstrcasestr(const char *phaystack, const char *pneedle)
  748. // This is the locale-obeying variant of strcasestr.  It uses CharUpper/Lower in place of toupper/lower,
  749. // which sees chars like Σ as the same as ─ (depending on code page/locale).  This function is about
  750. // 1 to 8 times slower than strcasestr() depending on factors such as how many partial matches for needle
  751. // are in haystack.
  752. // License: GNU GPL
  753. // Copyright (C) 1994,1996,1997,1998,1999,2000 Free Software Foundation, Inc.
  754. // See strcasestr() for more comments.
  755. {
  756.     register const unsigned char *haystack, *needle;
  757.     register unsigned bl, bu, cl, cu;
  758.     
  759.     haystack = (const unsigned char *) phaystack;
  760.     needle = (const unsigned char *) pneedle;
  761.  
  762.     bl = (UINT)(size_t)ltolower(*needle); // Double cast avoids compiler warning without increasing code size.
  763.     if (bl != 0)
  764.     {
  765.         // Scan haystack until the first character of needle is found:
  766.         bu = (UINT)(size_t)ltoupper(bl);
  767.         haystack--;                /* possible ANSI violation */
  768.         do
  769.         {
  770.             cl = *++haystack;
  771.             if (cl == '\0')
  772.                 goto ret0;
  773.         }
  774.         while ((cl != bl) && (cl != bu));
  775.  
  776.         // See if the rest of needle is a one-for-one match with this part of haystack:
  777.         cl = (UINT)(size_t)ltolower(*++needle);
  778.         if (cl == '\0')  // Since needle consists of only one character, it is already a match as found above.
  779.             goto foundneedle;
  780.         cu = (UINT)(size_t)ltoupper(cl);
  781.         ++needle;
  782.         goto jin;
  783.         
  784.         for (;;)
  785.         {
  786.             register unsigned a;
  787.             register const unsigned char *rhaystack, *rneedle;
  788.             do
  789.             {
  790.                 a = *++haystack;
  791.                 if (a == '\0')
  792.                     goto ret0;
  793.                 if ((a == bl) || (a == bu))
  794.                     break;
  795.                 a = *++haystack;
  796.                 if (a == '\0')
  797.                     goto ret0;
  798. shloop:
  799.                 ;
  800.             }
  801.             while ((a != bl) && (a != bu));
  802.  
  803. jin:
  804.             a = *++haystack;
  805.             if (a == '\0')  // Remaining part of haystack is shorter than needle.  No match.
  806.                 goto ret0;
  807.  
  808.             if ((a != cl) && (a != cu)) // This promising candidate is not a complete match.
  809.                 goto shloop;            // Start looking for another match on the first char of needle.
  810.             
  811.             rhaystack = haystack-- + 1;
  812.             rneedle = needle;
  813.             a = (UINT)(size_t)ltolower(*rneedle);
  814.             
  815.             if ((UINT)(size_t)ltolower(*rhaystack) == (int) a)
  816.             do
  817.             {
  818.                 if (a == '\0')
  819.                     goto foundneedle;
  820.                 ++rhaystack;
  821.                 a = (UINT)(size_t)ltolower(*++needle);
  822.                 if ((UINT)(size_t)ltolower(*rhaystack) != (int) a)
  823.                     break;
  824.                 if (a == '\0')
  825.                     goto foundneedle;
  826.                 ++rhaystack;
  827.                 a = (UINT)(size_t)ltolower(*++needle);
  828.             }
  829.             while ((UINT)(size_t)ltolower(*rhaystack) == (int) a);
  830.             
  831.             needle = rneedle;        /* took the register-poor approach */
  832.             
  833.             if (a == '\0')
  834.                 break;
  835.         } // for(;;)
  836.     } // if (bl != '\0')
  837. foundneedle:
  838.     return (char*) haystack;
  839. ret0:
  840.     return 0;
  841. }
  842.  
  843.  
  844.  
  845. UINT StrReplace(char *aHaystack, char *aOld, char *aNew, StringCaseSenseType aStringCaseSense
  846.     , UINT aLimit, size_t aSizeLimit, char **aDest, size_t *aHaystackLength)
  847. // Replaces all (or aLimit) occurrences of aOld with aNew in aHaystack.
  848. // On success, it returns the number of replacements done (0 if none).  On failure (out of memory), it returns 0
  849. // (and if aDest isn't NULL, it also sets *aDest to NULL on failure).
  850. //
  851. // PARAMETERS:
  852. // - aLimit: Specify UINT_MAX to have no restriction on the number of replacements.  Otherwise, specify a number >=0.
  853. // - aSizeLimit: Specify -1 to assume that aHaystack has enough capacity for any mode #1 replacement. Otherwise,
  854. //   specify the size limit (in either mode 1 or 2), but it must be >= length of aHaystack (simplifies the code).
  855. // - aDest: If NULL, the function will operate in mode #1.  Otherwise, it uses mode #2 (see further below).
  856. // - aHaystackLength: If it isn't NULL, *aHaystackLength must be the length of aHaystack.  HOWEVER, *aHaystackLength
  857. //   is then changed here to be the length of the result string so that caller can use it to improve performance.
  858. //
  859. // MODE 1 (when aDest==NULL): aHaystack is used as both the source and the destination (sometimes temporary memory
  860. // is used for performance, but it's freed afterward and so transparent to the caller).
  861. // When it passes in -1 for aSizeLimit (the deafult), caller must ensure that aHaystack has enough capacity to hold
  862. // the new/replaced result.  When non-NULL, aSizeLimit will be enforced by limiting the number of replacements to
  863. // the available memory (i.e. any remamining replacements are simply not done and that part of haystack is unaltered).
  864. //
  865. // MODE 2 (when aDest!=NULL): If zero replacements are needed, we set *aDest to be aHaystack to indicate that no
  866. // new memory was allocated.  Otherwise, we store in *aDest the address of the new memory that holds the result.
  867. // - The caller is responsible for any new memory in *aDest (freeing it, etc.)
  868. // - The initial value of *aDest doesn't matter.
  869. // - The contents of aHaystack isn't altered, not even if aOld_length==aNew_length (some callers rely on this).
  870. //
  871. // v1.0.45: This function was heavily revised to improve performance and flexibility.  It has also made
  872. // two other/related StrReplace() functions obsolete.  Also, the code has been simplified to avoid doing
  873. // a first pass through haystack to find out exactly how many replacements there are because that step
  874. // nearly doubles the time required for the entire operation (in most cases).  Its benefit was mainly in
  875. // memory savings and avoidance of any reallocs since the initial alloc was always exactly right; however,
  876. // testing shows that one or two reallocs are generally much quicker than doing the size-calculation phase
  877. // because extra alloc'ing & memcpy'ing is much faster than an extra search through haystack for all the matches.
  878. // Furthermore, the new approach minimizes reallocs by using smart prediction.  Furthermore, the caller shrinks
  879. // the result memory via _expand() to avoid having too much extra/overhang.  These optimizations seem to make
  880. // the new approach better than the old one in every way, but especially performance.
  881. {
  882.     #define REPLACEMENT_MODE2 aDest  // For readability.
  883.  
  884.     // THINGS TO SET NOW IN CASE OF EARLY RETURN OR GOTO:
  885.     // Set up the input/output lengths:
  886.     size_t haystack_length = aHaystackLength ? *aHaystackLength : strlen(aHaystack); // For performance, use caller's length if it was provided.
  887.     size_t length_temp; // Just a placeholder/memory location used by the alias below.
  888.     size_t &result_length = aHaystackLength ? *aHaystackLength : length_temp; // Make an alias for convenience and maintainability (if this is an output parameter for our caller, this step takes care that in advance).
  889.     // Set up the output buffer:
  890.     char *result_temp; // In mode #1, holds temporary memory that is freed before we return.
  891.     char *&result = aDest ? *aDest : result_temp; // Make an alias for convenience and maintainability (if aDest is non-NULL, it's an output parameter for our caller, and this step takes care that in advance).
  892.     result = NULL;     // It's allocated only upon first use to avoid a potentially massive allocation that might
  893.     result_length = 0; // be wasted and cause swapping (not to mention that we'll have better ability to estimate the correct total size after the first replacement is discovered).
  894.     size_t result_size = 0;
  895.     // Variables used by both replacement methods.
  896.     char *src, *match_pos;
  897.     // END OF INITIAL SETUP.
  898.  
  899.     // From now on, result_length and result should be kept up-to-date because they may have been set up
  900.     // as output parameters above.
  901.  
  902.     if (!(*aHaystack && *aOld))
  903.     {
  904.         // Nothing to do if aHaystack is blank. If aOld is blank, that is not supported because it would be an
  905.         // infinite loop. This policy is now largely due to backward compatibility because some other policy
  906.         // may have been better.
  907.         result = aHaystack; // Return unaltered string to caller in its output paremeter (result is an alias for *aDest).
  908.         result_length = haystack_length; // This is an alias for an output parameter, so update it for caller.
  909.         return 0; // Report "no replacements".
  910.     }
  911.  
  912.     size_t aOld_length = strlen(aOld);
  913.     size_t aNew_length = strlen(aNew);
  914.     int length_delta = (int)(aNew_length - aOld_length); // Cast to int to avoid loss of unsigned. A negative delta means the replacment substring is smaller than what it's replacing.
  915.  
  916.     if (aSizeLimit != -1) // Caller provided a size *restriction*, so if necessary reduce aLimit to stay within bounds.  Compare directly to -1 due to unsigned.
  917.     {
  918.         int extra_room = (int)(aSizeLimit-1 - haystack_length); // Cast to int to preserve negatives.
  919.         if (extra_room < 0) // Caller isn't supposed to call it this way.  To avoid having to complicate the
  920.             aLimit = 0;     // calculations in the else-if below, allow no replacements in this case.
  921.         else if (length_delta > 0) // New-str is bigger than old-str.  This is checked to avoid going negative or dividing by 0 below. A positive delta means length of new/replacement string is greater than that of what it's replacing.
  922.         {
  923.             UINT upper_limit = (UINT)(extra_room / length_delta);
  924.             if (aLimit > upper_limit)
  925.                 aLimit = upper_limit;
  926.         }
  927.         //else length_delta <= 0, so there no overflow should be possible.  Leave aLimit as-is.
  928.     }
  929.  
  930.     if (!REPLACEMENT_MODE2) // Mode #1
  931.     {
  932.         if (!length_delta // old_len==new_len, so use in-place method because it's just as fast in this case but it avoids the extra memory allocation.
  933.             || haystack_length < 5000) // ...or the in-place method will likely be faster, and an earlier stage has ensured there's no risk of overflow.
  934.             goto in_place_method; // "Goto" to avoid annoying indentation and long IF-blocks.
  935.         //else continue on because the extra-memory method will usually perform better than the in-place method.
  936.         // The extra-memory method is much faster than the in-place method when many replacements are needed because
  937.         // it avoids a memmove() to shift the remainder of the buffer up against the area of memory that
  938.         // will be replaced (which for the in-place method is done for every replacement).  The savings
  939.         // can be enormous if aSource is very large, assuming the system can allocate the memory without swapping.
  940.     }
  941.     // Otherwise:
  942.     // Since above didn't jump to the in place method, either the extra-memory method is preferred or this is mode #2.
  943.     // Never use the in-place method for mode #2 because caller always wants a separate memory area used (for its
  944.     // purposes, the extra-memory method is probably just as fast or faster than in-place method).
  945.  
  946.     // Below uses a temp var. because realloc() returns NULL on failure but leaves original block allocated.
  947.     // Note that if it's given a NULL pointer, realloc() does a malloc() instead.
  948.     char *realloc_temp;
  949.     #define STRREPLACE_REALLOC(size) \
  950.     {\
  951.         result_size = size;\
  952.         if (   !(realloc_temp = (char *)realloc(result, result_size))   )\
  953.             goto out_of_mem;\
  954.         result = realloc_temp;\
  955.     }
  956.  
  957.     // Other variables used by the replacement loop:
  958.     size_t haystack_portion_length, new_result_length;
  959.     UINT replacement_count;
  960.  
  961.     // Perform the replacement:
  962.     for (replacement_count = 0, src = aHaystack
  963.         ; aLimit && (match_pos = strstr2(src, aOld, aStringCaseSense));) // Relies on short-circuit boolean order.
  964.     {
  965.         ++replacement_count;
  966.         --aLimit;
  967.         haystack_portion_length = match_pos - src; // The length of the haystack section between the end of the previous match and the start of the current one.
  968.  
  969.         // Using the required length calculated below, expand/realloc "result" if necessary.
  970.         new_result_length = result_length + haystack_portion_length + aNew_length;
  971.         if (new_result_length >= result_size) // Uses >= to allow room for terminator.
  972.             STRREPLACE_REALLOC(PredictReplacementSize(length_delta, replacement_count, aLimit, (int)haystack_length
  973.                 , (int)new_result_length, (int)(match_pos - aHaystack))); // This will return if an alloc error occurs.
  974.  
  975.         // Now that we know "result" has enough capacity, put the new text into it.  The first step
  976.         // is to copy over the part of haystack that appears before the match.
  977.         if (haystack_portion_length)
  978.         {
  979.             memcpy(result + result_length, src, haystack_portion_length);
  980.             result_length += haystack_portion_length;
  981.         }
  982.         // Now append the replacement string in place of the old string.
  983.         if (aNew_length)
  984.         {
  985.             memcpy(result + result_length, aNew, aNew_length);
  986.             result_length += aNew_length;
  987.         }
  988.         //else omit it altogether; i.e. replace every aOld with the empty string.
  989.  
  990.         // Set up src to be the position where the next iteration will start searching.  For consistency with
  991.         // the in-place method, overlapping matches are not detected.  For example, the replacement
  992.         // of all occurrences of ".." with ". ." in "..." would produce ". ..", not ". . .":
  993.         src = match_pos + aOld_length; // This has two purposes: 1) Since match_pos is about to be altered by strstr, src serves as a placeholder for use by the next iteration; 2) it's also used further below.
  994.     }
  995.  
  996.     if (!replacement_count) // No replacements were done, so optimize by keeping the original (avoids a malloc+memcpy).
  997.     {
  998.         // The following steps are appropriate for both mode #1 and #2 (for simplicity and maintainability,
  999.         // they're all done unconditionally even though mode #1 might not require them all).
  1000.         result = aHaystack; // Return unaltered string to caller in its output paremeter (result is an alias for *aDest).
  1001.         result_length = haystack_length; // This is an alias for an output parameter, so update it for caller.
  1002.         return replacement_count;
  1003.         // Since no memory was allocated, there's never anything to free.
  1004.     }
  1005.     // (Below relies only above having returned when no replacements because it assumes result!=NULL from now on.)
  1006.  
  1007.     // Otherwise, copy the remaining characters after the last replacement (if any) (fixed for v1.0.25.11).
  1008.     if (haystack_portion_length = haystack_length - (src - aHaystack)) // This is the remaining part of haystack that need to be copied over as-is.
  1009.     {
  1010.         new_result_length = result_length + haystack_portion_length;
  1011.         if (new_result_length >= result_size) // Uses >= to allow room for terminator.
  1012.             STRREPLACE_REALLOC(new_result_length + 1); // This will return if an alloc error occurs.
  1013.         memcpy(result + result_length, src, haystack_portion_length); // memcpy() usually benches a little faster than strcpy().
  1014.         result_length = new_result_length; // Remember that result_length is actually an output for our caller, so even if for no other reason, it must be kept accurate for that.
  1015.     }
  1016.     result[result_length] = '\0'; // Must terminate it unconditionally because other sections usually don't do it.
  1017.  
  1018.     if (!REPLACEMENT_MODE2) // Mode #1.
  1019.     {
  1020.         // Since caller didn't provide destination memory, copy the result from our temporary memory (that was used
  1021.         // for performance) back into the caller's original buf (which has already been confirmed to be large enough).
  1022.         memcpy(aHaystack, result, result_length + 1); // Include the zero terminator.
  1023.         free(result); // Free the temp. mem that was used for performance.
  1024.     }
  1025.     return replacement_count;  // The output parameters have already been populated properly above.
  1026.  
  1027. out_of_mem: // This can only happen with the extra-memory method above (which due to its nature can't fall back to the in-place method).
  1028.     if (result)
  1029.     {
  1030.         free(result); // Must be freed in mode #1.  In mode #2, it's probably a non-terminated string (not to mention being an incomplete result), so if it ever isn't freed, it should be terminated.
  1031.         result = NULL; // Indicate failure by setting output param for our caller (this also indicates that the memory was freed).
  1032.     }
  1033.     result_length = 0; // Output parameter for caller, though upon failure it shouldn't matter (just for robustness).
  1034.     return 0;
  1035.  
  1036. in_place_method:
  1037.     // This method is available only to mode #1.  It should help performance for short strings such as those from
  1038.     // ExpandExpression().
  1039.     // This in-place method is used when the extra-memory method wouldn't be faster enough to be worth its cost
  1040.     // for the particular strings involved here.
  1041.     //
  1042.     // Older comment:
  1043.     // The below doesn't quite work when doing a simple replacement such as ".." with ". .".
  1044.     // In the above example, "..." would be changed to ". .." rather than ". . ." as it should be.
  1045.     // Therefore, use a less efficient, but more accurate method instead.  UPDATE: But this method
  1046.     // can cause an infinite loop if the new string is a superset of the old string, so don't use
  1047.     // it after all.
  1048.     //for ( ; ptr = StrReplace(aHaystack, aOld, aNew, aStringCaseSense); ); // Note that this very different from the below.
  1049.  
  1050.     for (replacement_count = 0, src = aHaystack
  1051.         ; aLimit && (match_pos = strstr2(src, aOld, aStringCaseSense)) // Relies on short-circuit boolean order.
  1052.         ; --aLimit, ++replacement_count)
  1053.     {
  1054.         src = match_pos + aNew_length;  // The next search should start at this position when all is adjusted below.
  1055.         if (length_delta) // This check can greatly improve performance if old and new strings happen to be same length.
  1056.         {
  1057.             // Since new string can't fit exactly in place of old string, adjust the target area to
  1058.             // accept exactly the right length so that the rest of the string stays unaltered:
  1059.             memmove(src, match_pos + aOld_length
  1060.                 , haystack_length - (match_pos - aHaystack) - aOld_length + 1); // +1 to include zero terminator.
  1061.             // Above: Calculating length vs. using strlen() makes overall speed of the operation about
  1062.             // twice as fast for some typical test cases in a 2 MB buffer such as replacing \r\n with \n.
  1063.         }
  1064.         memcpy(match_pos, aNew, aNew_length); // Perform the replacement.
  1065.         // Must keep haystack_length updated as we go, for use with memmove() above:
  1066.         haystack_length += length_delta; // Note that length_delta will be negative if aNew is shorter than aOld.
  1067.     }
  1068.  
  1069.     result_length = haystack_length; // Set for caller (it's an alias for an output parameter).
  1070.     result = aHaystack; // Not actually needed in this method, so this is just for maintainability.
  1071.     return replacement_count;
  1072. }
  1073.  
  1074.  
  1075.  
  1076. int PredictReplacementSize(int aLengthDelta, int aReplacementCount, int aLimit, int aHaystackLength
  1077.     , int aCurrentLength, int aEndOffsetOfCurrMatch)
  1078. // Predict how much size the remainder of a replacement operation will consume, including its actual replacements
  1079. // and the parts of haystack that won't need replacement.
  1080. // PARAMETERS:
  1081. // - aLengthDelta: The estimated or actual difference between the length of the replacement and what it's replacing.
  1082. //   A negative number means the replacement is smaller, which will cause a shrinking of the result.
  1083. // - aReplacementCount: The number of replacements so far, including the one the caller is about to do.
  1084. // - aLimit: The *remaining* number of replacements *allowed* (not including the one the caller is about to do).
  1085. // - aHaystackLength: The total length of the original haystack/subject string.
  1086. // - aCurrentLength: The total length of the new/result string including the one the caller is about to do.
  1087. // - aEndOffsetOfCurrMatch: The offset of the char after the last char of the current match.  For example, if
  1088. //   the empty string is the current match and it's found at the beginning of haystack, this value would be 0.
  1089. {
  1090.     // Since realloc() is an expensive operation, especially for huge strings, make an extra
  1091.     // effort to get a good estimate based on how things have been going so far.
  1092.     // While this should definitely improve average-case memory-utilization and usually performance
  1093.     // (by avoiding costly realloc's), this estimate is crude because:
  1094.     // 1) The length of what is being replaced can vary due to wildcards in pattern, etc.
  1095.     // 2) The length of what is replacing it can vary due to backreferences.  Thus, the delta
  1096.     //    of each replacement is only a guess based on that of the current replacement.
  1097.     // 3) For code simplicity, the number of upcoming replacements isn't yet known; thus a guess
  1098.     //    is made based on how many there have been so far compared to percentage complete.
  1099.  
  1100.     int total_delta; // The total increase/decrease in length from the number of predicted additional replacements.
  1101.     int repl_multiplier = aLengthDelta < 0 ? -1 : 1; // Negative is used to keep additional_replacements_expected conservative even when delta is negative.
  1102.  
  1103.     if (aLengthDelta == 0) // Avoid all the calculations because it will wind up being zero anyway.
  1104.         total_delta = 0;
  1105.     else
  1106.     {
  1107.         if (!aHaystackLength // aHaystackLength can be 0 if an empty haystack being replaced by something else. If so, avoid divide-by-zero in the prediction by doing something simpler.
  1108.             || !aEndOffsetOfCurrMatch)  // i.e. don't the prediction if the current match is the empty string and it was found at the very beginning of Haystack because it would be difficult to be accurate (very rare anyway).
  1109.             total_delta = repl_multiplier * aLengthDelta; // Due to rarity, just allow room for one extra after the one we're about to do.
  1110.         else // So the above has ensured that the following won't divide by zero anywhere.
  1111.         {
  1112.             // The following doesn't take into account the original value of aStartingOffset passed in
  1113.             // from the caller because:
  1114.             // 1) It's pretty rare for it to be greater than 0.
  1115.             // 2) Even if it is, the prediction will just be too conservative at first, but that's
  1116.             //    pretty harmless; and anyway each successive realloc followed by a match makes the
  1117.             //    prediction more and more accurate in spite of aStartingOffset>0.
  1118.             // percent_complete must be a double because we need at least 9 digits of precision for cases where
  1119.             // 1 is divided by a big number like 1 GB.
  1120.             double percent_complete = aEndOffsetOfCurrMatch  // Don't subtract 1 (verified correct).
  1121.                 / (double)aHaystackLength; // percent_complete isn't actually a percentage, but a fraction of 1.  e.g. 0.5 rather than 50.
  1122.             int additional_replacements_expected = percent_complete >= 1.0 ? 0  // It's often 100% complete, in which case there's hardly ever another replacement after this one (the only possibility is to replace the final empty-string in haystack with something).
  1123.                 : (int)(
  1124.                 (aReplacementCount / percent_complete) // This is basically "replacements per percentage point, so far".
  1125.                 * (1 - percent_complete) // This is the percentage of haystack remaining to be scanned (e.g. 0.5 for 50%).
  1126.                 + 1 * repl_multiplier // Add 1 or -1 to make it more conservative (i.e. go the opposite direction of ceil when repl_multiplier is negative).
  1127.                 );
  1128.             // additional_replacements_expected is defined as the replacements expected *after* the one the caller
  1129.             // is about to do.
  1130.  
  1131.             if (aLimit >= 0 && aLimit < additional_replacements_expected)
  1132.             {    // A limit is currently in effect and it's less than expected replacements, so cap the expected.
  1133.                 // This helps reduce memory utilization.
  1134.                 additional_replacements_expected = aLimit;
  1135.             }
  1136.             else // No limit or additional_replacements_expected is within the limit.
  1137.             {
  1138.                 // So now things are set up so that there's about a 50/50 chance than no more reallocs
  1139.                 // will be needed.  Since recalloc is costly (due to internal memcpy), try to reduce
  1140.                 // the odds of it happening without going overboard on memory utilization.
  1141.                 // Something a lot more complicated could be used in place of the below to improve things
  1142.                 // a little, but it just doesn't seem worth it given the usage patterns expected and
  1143.                 // the actual benefits.  Besides, there is some limiting logic further below that will
  1144.                 // cap this if it's unreasonably large:
  1145.                 additional_replacements_expected += (int)(0.20*additional_replacements_expected + 1) // +1 so that there's always at least one extra.
  1146.                     * repl_multiplier; // This keeps the estimate conservative if delta < 0.
  1147.             }
  1148.             // The following is the "quality" of the estimate.  For example, if this is the very first replacement
  1149.             // and 1000 more replacements are predicted, there will often be far fewer than 1000 replacements;
  1150.             // in fact, there could well be zero.  So in the below, the quality will range from 1 to 3, where
  1151.             // 1 is the worst quality and 3 is the best.
  1152.             double quality = 1 + 2*(1-(
  1153.                 (double)additional_replacements_expected / (aReplacementCount + additional_replacements_expected)
  1154.                 ));
  1155.             // It seems best to use whichever of the following is greater in the calculation further below:
  1156.             int haystack_or_new_length = (aCurrentLength > aHaystackLength) ? aCurrentLength : aHaystackLength;
  1157.             // The following is a crude sanity limit to avoid going overboard with memory
  1158.             // utilization in extreme cases such as when a big string has many replacements
  1159.             // in its first half, but hardly any in its second.  It does the following:
  1160.             // 1) When Haystack-or-current length is huge, this tries to keep the portion of the memory increase
  1161.             //    that's speculative proportionate to that length, which should reduce the chance of swapping
  1162.             //    (at the expense of some performance in cases where it causes another realloc to be required).
  1163.             // 2) When Haystack-or-current length is relatively small, allow the speculative memory allocation
  1164.             //    to be many times larger than that length because the risk of swapping is low.  HOWEVER, TO
  1165.             //    AVOID WASTING MEMORY, the caller should probably call _expand() to shrink the result
  1166.             //    when it detects that far fewer replacements were needed than predicted (this is currently
  1167.             //    done by Var::AcceptNewMem()).
  1168.             int total_delta_limit = (int)(haystack_or_new_length < 10*1024*1024 ? quality*10*1024*1024
  1169.                 : quality*haystack_or_new_length); // See comment above.
  1170.             total_delta = additional_replacements_expected
  1171.                 * (aLengthDelta < 0 ? -aLengthDelta : aLengthDelta); // So actually, total_delta will be the absolute value.
  1172.             if (total_delta > total_delta_limit)
  1173.                 total_delta = total_delta_limit;
  1174.             total_delta *= repl_multiplier;  // Convert back from absolute value.
  1175.         } // The current match isn't an empty string at the very beginning of haystack.
  1176.     } // aLengthDelta!=0
  1177.  
  1178.     // Above is responsible for having set total_delta properly.
  1179.     int subsequent_length = aHaystackLength - aEndOffsetOfCurrMatch // This is the length of the remaining portion of haystack that might wind up going into the result exactly as-is (adjusted by the below).
  1180.         + total_delta; // This is additional_replacements_expected times the expected delta (the length of each replacement minus what it replaces) [can be negative].
  1181.     if (subsequent_length < 0) // Must not go below zero because that would cause the next line to
  1182.         subsequent_length = 0; // create an increase that's too small to handle the current replacement.
  1183.  
  1184.     // Return the sum of the following:
  1185.     // 1) subsequent_length: The predicted length needed for the remainder of the operation.
  1186.     // 2) aCurrentLength: The amount we need now, which includes room for the replacement the caller is about to do.
  1187.     //    Note that aCurrentLength can be 0 (such as for an empty string replacement).
  1188.     return subsequent_length + aCurrentLength + 1; // Caller relies on +1 for the terminator.
  1189. }
  1190.  
  1191.  
  1192.  
  1193. char *TranslateLFtoCRLF(char *aString)
  1194. // Can't use StrReplace() for this because any CRLFs originally present in aString are not changed (i.e. they
  1195. // don't become CRCRLF) [there may be other reasons].
  1196. // Translates any naked LFs in aString to CRLF.  If there are none, the original string is returned.
  1197. // Otherwise, the translated version is copied into a malloc'd buffer, which the caller must free
  1198. // when it's done with it).  
  1199. {
  1200.     UINT naked_LF_count = 0;
  1201.     size_t length = 0;
  1202.     char *cp;
  1203.  
  1204.     for (cp = aString; *cp; ++cp)
  1205.     {
  1206.         ++length;
  1207.         if (*cp == '\n' && (cp == aString || cp[-1] != '\r')) // Relies on short-circuit boolean order.
  1208.             ++naked_LF_count;
  1209.     }
  1210.  
  1211.     if (!naked_LF_count)
  1212.         return aString;  // The original string is returned, which the caller must check for (vs. new string).
  1213.  
  1214.     // Allocate the new memory that will become the caller's responsibility:
  1215.     char *buf = (char *)malloc(length + naked_LF_count + 1);  // +1 for zero terminator.
  1216.     if (!buf)
  1217.         return NULL;
  1218.  
  1219.     // Now perform the translation.
  1220.     char *dp = buf; // Destination.
  1221.     for (cp = aString; *cp; ++cp)
  1222.     {
  1223.         if (*cp == '\n' && (cp == aString || cp[-1] != '\r')) // Relies on short-circuit boolean order.
  1224.             *dp++ = '\r';  // Insert an extra CR here, then insert the '\n' normally below.
  1225.         *dp++ = *cp;
  1226.     }
  1227.     *dp = '\0';  // Final terminator.
  1228.  
  1229.     return buf;  // Caller must free it when it's done with it.
  1230. }
  1231.  
  1232.  
  1233.  
  1234. bool DoesFilePatternExist(char *aFilePattern, DWORD *aFileAttr)
  1235. // Returns true if the file/folder exists or false otherwise.
  1236. // If non-NULL, aFileAttr's DWORD is set to the attributes of the file/folder if a match is found.
  1237. // If there is no match, its contents are undefined.
  1238. {
  1239.     if (!aFilePattern || !*aFilePattern) return false;
  1240.     // Fix for v1.0.35.12: Don't consider the question mark in "\\?\Volume{GUID}\" to be a wildcard.
  1241.     // Such volume names are obtained from GetVolumeNameForVolumeMountPoint() and perhaps other functions.
  1242.     // However, testing shows that wildcards beyond that first one should be seen as real wildcards
  1243.     // because the wildcard match-method below does work for such volume names.
  1244.     char *cp = strncmp(aFilePattern, "\\\\?\\", 4) ? aFilePattern : aFilePattern + 4;
  1245.     if (StrChrAny(cp, "?*"))
  1246.     {
  1247.         WIN32_FIND_DATA wfd;
  1248.         HANDLE hFile = FindFirstFile(aFilePattern, &wfd);
  1249.         if (hFile == INVALID_HANDLE_VALUE)
  1250.             return false;
  1251.         FindClose(hFile);
  1252.         if (aFileAttr)
  1253.             *aFileAttr = wfd.dwFileAttributes;
  1254.         return true;
  1255.     }
  1256.     else
  1257.     {
  1258.         DWORD attr = GetFileAttributes(aFilePattern);
  1259.         if (aFileAttr)
  1260.             *aFileAttr = attr;
  1261.         return attr != 0xFFFFFFFF;
  1262.     }
  1263. }
  1264.  
  1265.  
  1266.  
  1267. #ifdef _DEBUG
  1268. ResultType FileAppend(char *aFilespec, char *aLine, bool aAppendNewline)
  1269. {
  1270.     if (!aFilespec || !aLine) return FAIL;
  1271.     if (!*aFilespec) return FAIL;
  1272.     FILE *fp = fopen(aFilespec, "a");
  1273.     if (fp == NULL)
  1274.         return FAIL;
  1275.     fputs(aLine, fp);
  1276.     if (aAppendNewline)
  1277.         putc('\n', fp);
  1278.     fclose(fp);
  1279.     return OK;
  1280. }
  1281. #endif
  1282.  
  1283.  
  1284.  
  1285. char *ConvertFilespecToCorrectCase(char *aFullFileSpec)
  1286. // aFullFileSpec must be a modifiable string since it will be converted to proper case.
  1287. // Also, it should be at least MAX_PATH is size because if it contains any short (8.3)
  1288. // components, they will be converted into their long names.
  1289. // Returns aFullFileSpec, the contents of which have been converted to the case used by the
  1290. // file system.  Note: The trick of changing the current directory to be that of
  1291. // aFullFileSpec and then calling GetFullPathName() doesn't always work.  So perhaps the
  1292. // only easy way is to call FindFirstFile() on each directory that composes aFullFileSpec,
  1293. // which is what is done here.  I think there's another way involving some PIDL Explorer
  1294. // function, but that might not support UNCs correctly.
  1295. {
  1296.     if (!aFullFileSpec || !*aFullFileSpec) return aFullFileSpec;
  1297.     size_t length = strlen(aFullFileSpec);
  1298.     if (length < 2 || length >= MAX_PATH) return aFullFileSpec;
  1299.     // Start with something easy, the drive letter:
  1300.     if (aFullFileSpec[1] == ':')
  1301.         aFullFileSpec[0] = toupper(aFullFileSpec[0]);
  1302.     // else it might be a UNC that has no drive letter.
  1303.     char built_filespec[MAX_PATH], *dir_start, *dir_end;
  1304.     if (dir_start = strchr(aFullFileSpec, ':'))
  1305.         // MSDN: "To examine any directory other than a root directory, use an appropriate
  1306.         // path to that directory, with no trailing backslash. For example, an argument of
  1307.         // "C:\windows" will return information about the directory "C:\windows", not about
  1308.         // any directory or file in "C:\windows". An attempt to open a search with a trailing
  1309.         // backslash will always fail."
  1310.         dir_start += 2; // Skip over the first backslash that goes with the drive letter.
  1311.     else // it's probably a UNC
  1312.     {
  1313.         if (strncmp(aFullFileSpec, "\\\\", 2))
  1314.             // It doesn't appear to be a UNC either, so not sure how to deal with it.
  1315.             return aFullFileSpec;
  1316.         // I think MS says you can't use FindFirstFile() directly on a share name, so we
  1317.         // want to omit both that and the server name from consideration (i.e. we don't attempt
  1318.         // to find their proper case).  MSDN: "Similarly, on network shares, you can use an
  1319.         // lpFileName of the form "\\server\service\*" but you cannot use an lpFileName that
  1320.         // points to the share itself, such as "\\server\service".
  1321.         dir_start = aFullFileSpec + 2;
  1322.         char *end_of_server_name = strchr(dir_start, '\\');
  1323.         if (end_of_server_name)
  1324.         {
  1325.             dir_start = end_of_server_name + 1;
  1326.             char *end_of_share_name = strchr(dir_start, '\\');
  1327.             if (end_of_share_name)
  1328.                 dir_start = end_of_share_name + 1;
  1329.         }
  1330.     }
  1331.     // Init the new string (the filespec we're building), e.g. copy just the "c:\\" part.
  1332.     strlcpy(built_filespec, aFullFileSpec, dir_start - aFullFileSpec + 1);
  1333.     WIN32_FIND_DATA found_file;
  1334.     HANDLE file_search;
  1335.     for (dir_end = dir_start; dir_end = strchr(dir_end, '\\'); ++dir_end)
  1336.     {
  1337.         *dir_end = '\0';  // Temporarily terminate.
  1338.         file_search = FindFirstFile(aFullFileSpec, &found_file);
  1339.         *dir_end = '\\'; // Restore it before we do anything else.
  1340.         if (file_search == INVALID_HANDLE_VALUE)
  1341.             return aFullFileSpec;
  1342.         FindClose(file_search);
  1343.         // Append the case-corrected version of this directory name:
  1344.         snprintfcat(built_filespec, sizeof(built_filespec), "%s\\", found_file.cFileName);
  1345.     }
  1346.     // Now do the filename itself:
  1347.     if (   (file_search = FindFirstFile(aFullFileSpec, &found_file)) == INVALID_HANDLE_VALUE   )
  1348.         return aFullFileSpec;
  1349.     FindClose(file_search);
  1350.     snprintfcat(built_filespec, sizeof(built_filespec), "%s", found_file.cFileName);
  1351.     // It might be possible for the new one to be longer than the old, e.g. if some 8.3 short
  1352.     // names were converted to long names by the process.  Thus, the caller should ensure that
  1353.     // aFullFileSpec is large enough:
  1354.     strcpy(aFullFileSpec, built_filespec);
  1355.     return aFullFileSpec;
  1356. }
  1357.  
  1358.  
  1359.  
  1360. char *FileAttribToStr(char *aBuf, DWORD aAttr)
  1361. // Caller must ensure that aAttr is valid (i.e. that it's not 0xFFFFFFFF).
  1362. {
  1363.     if (!aBuf) return aBuf;
  1364.     int length = 0;
  1365.     if (aAttr & FILE_ATTRIBUTE_READONLY)
  1366.         aBuf[length++] = 'R';
  1367.     if (aAttr & FILE_ATTRIBUTE_ARCHIVE)
  1368.         aBuf[length++] = 'A';
  1369.     if (aAttr & FILE_ATTRIBUTE_SYSTEM)
  1370.         aBuf[length++] = 'S';
  1371.     if (aAttr & FILE_ATTRIBUTE_HIDDEN)
  1372.         aBuf[length++] = 'H';
  1373.     if (aAttr & FILE_ATTRIBUTE_NORMAL)
  1374.         aBuf[length++] = 'N';
  1375.     if (aAttr & FILE_ATTRIBUTE_DIRECTORY)
  1376.         aBuf[length++] = 'D';
  1377.     if (aAttr & FILE_ATTRIBUTE_OFFLINE)
  1378.         aBuf[length++] = 'O';
  1379.     if (aAttr & FILE_ATTRIBUTE_COMPRESSED)
  1380.         aBuf[length++] = 'C';
  1381.     if (aAttr & FILE_ATTRIBUTE_TEMPORARY)
  1382.         aBuf[length++] = 'T';
  1383.     aBuf[length] = '\0';  // Perform the final termination.
  1384.     return aBuf;
  1385. }
  1386.  
  1387.  
  1388.  
  1389. unsigned __int64 GetFileSize64(HANDLE aFileHandle)
  1390. // Returns ULLONG_MAX on failure.  Otherwise, it returns the actual file size.
  1391. {
  1392.     ULARGE_INTEGER ui = {0};
  1393.     ui.LowPart = GetFileSize(aFileHandle, &ui.HighPart);
  1394.     if (ui.LowPart == MAXDWORD && GetLastError() != NO_ERROR)
  1395.         return ULLONG_MAX;
  1396.     return (unsigned __int64)ui.QuadPart;
  1397. }
  1398.  
  1399.  
  1400.  
  1401. char *GetLastErrorText(char *aBuf, int aBufSize, bool aUpdateLastError)
  1402. // aBufSize is an int to preserve any negative values the caller might pass in.
  1403. {
  1404.     if (aBufSize < 1)
  1405.         return aBuf;
  1406.     if (aBufSize == 1)
  1407.     {
  1408.         *aBuf = '\0';
  1409.         return aBuf;
  1410.     }
  1411.     DWORD last_error = GetLastError();
  1412.     if (aUpdateLastError)
  1413.         g.LastError = last_error;
  1414.     FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, last_error, 0, aBuf, (DWORD)aBufSize - 1, NULL);
  1415.     return aBuf;
  1416. }
  1417.  
  1418.  
  1419.  
  1420. void AssignColor(char *aColorName, COLORREF &aColor, HBRUSH &aBrush)
  1421. // Assign the color indicated in aColorName (either a name or a hex RGB value) to both
  1422. // aColor and aBrush, deleting any prior handle in aBrush first.  If the color cannot
  1423. // be determined, it will always be set to CLR_DEFAULT (and aBrush set to NULL to match).
  1424. // It will never be set to CLR_NONE.
  1425. {
  1426.     COLORREF color;
  1427.     if (!*aColorName)
  1428.         color = CLR_DEFAULT;
  1429.     else
  1430.     {
  1431.         color = ColorNameToBGR(aColorName);
  1432.         if (color == CLR_NONE) // A matching color name was not found, so assume it's a hex color value.
  1433.             // It seems strtol() automatically handles the optional leading "0x" if present:
  1434.             color = rgb_to_bgr(strtol(aColorName, NULL, 16));
  1435.             // if aColorName does not contain something hex-numeric, black (0x00) will be assumed,
  1436.             // which seems okay given how rare such a problem would be.
  1437.     }
  1438.     if (color != aColor) // It's not already the right color.
  1439.     {
  1440.         aColor = color; // Set default.  v1.0.44.09: Added this line to fix the inability to change to a previously selected color after having changed to the default color.
  1441.         if (aBrush) // Free the resources of the old brush.
  1442.             DeleteObject(aBrush);
  1443.         if (color == CLR_DEFAULT) // Caller doesn't need brush for CLR_DEFAULT, assuming that's even possible.
  1444.             aBrush = NULL;
  1445.         else
  1446.             if (   !(aBrush = CreateSolidBrush(color))   ) // Failure should be very rare.
  1447.                 aColor = CLR_DEFAULT; // A NULL HBRUSH should always corresponds to CLR_DEFAULT.
  1448.     }
  1449. }
  1450.  
  1451.  
  1452.  
  1453. COLORREF ColorNameToBGR(char *aColorName)
  1454. // These are the main HTML color names.  Returns CLR_NONE if a matching HTML color name can't be found.
  1455. // Returns CLR_DEFAULT only if aColorName is the word Default.
  1456. {
  1457.     if (!aColorName || !*aColorName) return CLR_NONE;
  1458.     if (!stricmp(aColorName, "Black"))  return 0x000000;  // These colors are all in BGR format, not RGB.
  1459.     if (!stricmp(aColorName, "Silver")) return 0xC0C0C0;
  1460.     if (!stricmp(aColorName, "Gray"))   return 0x808080;
  1461.     if (!stricmp(aColorName, "White"))  return 0xFFFFFF;
  1462.     if (!stricmp(aColorName, "Maroon")) return 0x000080;
  1463.     if (!stricmp(aColorName, "Red"))    return 0x0000FF;
  1464.     if (!stricmp(aColorName, "Purple")) return 0x800080;
  1465.     if (!stricmp(aColorName, "Fuchsia"))return 0xFF00FF;
  1466.     if (!stricmp(aColorName, "Green"))  return 0x008000;
  1467.     if (!stricmp(aColorName, "Lime"))   return 0x00FF00;
  1468.     if (!stricmp(aColorName, "Olive"))  return 0x008080;
  1469.     if (!stricmp(aColorName, "Yellow")) return 0x00FFFF;
  1470.     if (!stricmp(aColorName, "Navy"))   return 0x800000;
  1471.     if (!stricmp(aColorName, "Blue"))   return 0xFF0000;
  1472.     if (!stricmp(aColorName, "Teal"))   return 0x808000;
  1473.     if (!stricmp(aColorName, "Aqua"))   return 0xFFFF00;
  1474.     if (!stricmp(aColorName, "Default"))return CLR_DEFAULT;
  1475.     return CLR_NONE;
  1476. }
  1477.  
  1478.  
  1479.  
  1480. POINT CenterWindow(int aWidth, int aHeight)
  1481. // Given a the window's width and height, calculates where to position its upper-left corner
  1482. // so that it is centered EVEN IF the task bar is on the left side or top side of the window.
  1483. // This does not currently handle multi-monitor systems explicitly, since those calculations
  1484. // require API functions that don't exist in Win95/NT (and thus would have to be loaded
  1485. // dynamically to allow the program to launch).  Therefore, windows will likely wind up
  1486. // being centered across the total dimensions of all monitors, which usually results in
  1487. // half being on one monitor and half in the other.  This doesn't seem too terrible and
  1488. // might even be what the user wants in some cases (i.e. for really big windows).
  1489. {
  1490.     RECT rect;
  1491.     SystemParametersInfo(SPI_GETWORKAREA, 0, &rect, 0);  // Get desktop rect excluding task bar.
  1492.     // Note that rect.left will NOT be zero if the taskbar is on docked on the left.
  1493.     // Similarly, rect.top will NOT be zero if the taskbar is on docked at the top of the screen.
  1494.     POINT pt;
  1495.     pt.x = rect.left + (((rect.right - rect.left) - aWidth) / 2);
  1496.     pt.y = rect.top + (((rect.bottom - rect.top) - aHeight) / 2);
  1497.     return pt;
  1498. }
  1499.  
  1500.  
  1501.  
  1502. bool FontExist(HDC aHdc, char *aTypeface)
  1503. {
  1504.     LOGFONT lf;
  1505.     lf.lfCharSet = DEFAULT_CHARSET;  // Enumerate all char sets.
  1506.     lf.lfPitchAndFamily = 0;  // Must be zero.
  1507.     strlcpy(lf.lfFaceName, aTypeface, LF_FACESIZE);
  1508.     bool font_exists = false;
  1509.     EnumFontFamiliesEx(aHdc, &lf, (FONTENUMPROC)FontEnumProc, (LPARAM)&font_exists, 0);
  1510.     return font_exists;
  1511. }
  1512.  
  1513.  
  1514.  
  1515. int CALLBACK FontEnumProc(ENUMLOGFONTEX *lpelfe, NEWTEXTMETRICEX *lpntme, DWORD FontType, LPARAM lParam)
  1516. {
  1517.     *(bool *)lParam = true; // Indicate to the caller that the font exists.
  1518.     return 0;  // Stop the enumeration after the first, since even one match means the font exists.
  1519. }
  1520.  
  1521.  
  1522.  
  1523. void ScreenToWindow(POINT &aPoint, HWND aHwnd)
  1524. // Convert screen coordinates to window coordinates (i.e. relative to the window's upper-left corner).
  1525. {
  1526.     RECT rect;
  1527.     GetWindowRect(aHwnd, &rect);
  1528.     aPoint.x -= rect.left;
  1529.     aPoint.y -= rect.top;
  1530. }
  1531.  
  1532.  
  1533.  
  1534. void WindowToScreen(int &aX, int &aY)
  1535. // aX and aY are assumed to be relative to the currently active window.  Here they are converted to
  1536. // screen coordinates based on the position of the active window upper-left corner (not its client area).
  1537. {
  1538.     RECT rect;
  1539.     HWND active_window = GetForegroundWindow();
  1540.     if (active_window && !IsIconic(active_window) && GetWindowRect(active_window, &rect))
  1541.     {
  1542.         aX += rect.left;
  1543.         aY += rect.top;
  1544.     }
  1545.     //else no active window per se, so don't convert the coordinates.  Leave them as-is as desired by the
  1546.     // caller.  More details:
  1547.     // Revert to screen coordinates if the foreground window is minimized.  Although it might be
  1548.     // impossible for a visible window to be both foreground and minmized, it seems that hidden
  1549.     // windows -- such as the script's own main window when activated for the purpose of showing
  1550.     // a popup menu -- can be foreground while simultaneously being minimized.  This fixes an
  1551.     // issue where the mouse will move to the upper-left corner of the screen rather than the
  1552.     // intended coordinates (v1.0.17):
  1553. }
  1554.  
  1555.  
  1556.  
  1557. void GetVirtualDesktopRect(RECT &aRect)
  1558. {
  1559.     aRect.right = GetSystemMetrics(SM_CXVIRTUALSCREEN);
  1560.     if (aRect.right) // A non-zero value indicates the OS supports multiple monitors or at least SM_CXVIRTUALSCREEN.
  1561.     {
  1562.         aRect.left = GetSystemMetrics(SM_XVIRTUALSCREEN);  // Might be negative or greater than zero.
  1563.         aRect.right += aRect.left;
  1564.         aRect.top = GetSystemMetrics(SM_YVIRTUALSCREEN);   // Might be negative or greater than zero.
  1565.         aRect.bottom = aRect.top + GetSystemMetrics(SM_CYVIRTUALSCREEN);
  1566.     }
  1567.     else // Win95/NT do not support SM_CXVIRTUALSCREEN and such, so zero was returned.
  1568.         GetWindowRect(GetDesktopWindow(), &aRect);
  1569. }
  1570.  
  1571.  
  1572.  
  1573. DWORD GetEnvVarReliable(char *aEnvVarName, char *aBuf)
  1574. // Returns the length of what has been copied into aBuf.
  1575. // Caller has ensured that aBuf is large enough (though anything >=32767 is always large enough).
  1576. // This function was added in v1.0.46.08 to fix a long-standing bug that was more fully revealed
  1577. // by 1.0.46.07's reduction of DEREF_BUF_EXPAND_INCREMENT from 32 to 16K (which allowed the Win9x
  1578. // version of GetEnvironmentVariable() to detect that we were lying about the buffer being 32767
  1579. // in size).  The reason for lying is that we don't know exactly how big the buffer is, but we do
  1580. // know it's large enough. So we just pass 32767 in an attempt to make it always succeed
  1581. // (mostly because GetEnvironmentVariable() is such a slow call, so it's best to avoid calling it
  1582. // a second time just to get the length).
  1583. // UPDATE: This is now called for all versions of Windows to avoid any chance of crashing or misbehavior,
  1584. // which tends to happen whenever lying to the API (even if it's for a good cause).  See similar problems
  1585. // at ReadRegString(). See other comments below.
  1586. {
  1587.     // Windows 9x is apparently capable of detecting a lie about the buffer size.
  1588.     // For example, if the memory capacity is only 16K but we pass 32767, it sometimes/always detects that
  1589.     // and returns 0, even if the string being retrieved is short enough (which it is because the caller
  1590.     // already verified it).  To work around this, give it a genuinely large buffer with an accurate size,
  1591.     // then copy the result out into the caller's variable.  This is almost certainly much faster than
  1592.     // doing a second call to GetEnvironmentVariable() to get the length because GetEnv() is known to be a
  1593.     // very slow call.
  1594.     //
  1595.     // Don't use a size greater than 32767 because that will cause it to fail on Win95 (tested by Robert Yalkin).
  1596.     // According to MSDN, 32767 is exactly large enough to handle the largest variable plus its zero terminator.
  1597.     char buf[32767];
  1598.     DWORD length = GetEnvironmentVariable(aEnvVarName, buf, sizeof(buf));
  1599.     // GetEnvironmentVariable() could be called twice, the first time to get the actual size.  But that would
  1600.     // probably perform worse since GetEnvironmentVariable() is a very slow function.  In addition, it would
  1601.     // add code complexity, so it seems best to fetch it into a large buffer then just copy it to dest-var.
  1602.     if (length) // Probably always true under the conditions in effect for our callers.
  1603.         memcpy(aBuf, buf, length + 1); // memcpy() usually benches a little faster than strcpy().
  1604.     else // Failure. The buffer's contents might be undefined in this case.
  1605.         *aBuf = '\0'; // Caller's buf should always have room for an empty string. So make it empty for maintainability, even if not strictly required by caller.
  1606.     return length;
  1607. }
  1608.  
  1609.  
  1610.  
  1611. DWORD ReadRegString(HKEY aRootKey, char *aSubkey, char *aValueName, char *aBuf, size_t aBufSize)
  1612. // Returns the length of the string (0 if empty).
  1613. // Caller must ensure that size of aBuf is REALLY aBufSize (even when it knows aBufSize is more than
  1614. // it needs) because the API apparently reads/writes parts of the buffer beyond the string it writes!
  1615. // Caller must ensure that aBuf isn't NULL because it doesn't seem worth having a "give me the size only" mode.
  1616. // This is because the API might return a size that omits the zero terminator, and the only way to find out for
  1617. // sure is probably to actually fetch the data and check if the terminator is present.
  1618. {
  1619.     HKEY hkey;
  1620.     if (RegOpenKeyEx(aRootKey, aSubkey, 0, KEY_QUERY_VALUE, &hkey) != ERROR_SUCCESS)
  1621.     {
  1622.         *aBuf = '\0';
  1623.         return 0;
  1624.     }
  1625.     DWORD buf_size = (DWORD)aBufSize; // Caller's value might be a constant memory area, so need a modifiable copy.
  1626.     LONG result = RegQueryValueEx(hkey, aValueName, NULL, NULL, (LPBYTE)aBuf, &buf_size);
  1627.     RegCloseKey(hkey);
  1628.     if (result != ERROR_SUCCESS || !buf_size) // Relies on short-circuit boolean order.
  1629.     {
  1630.         *aBuf = '\0'; // MSDN says the contents of the buffer is undefined after the call in some cases, so reset it.
  1631.         return 0;
  1632.     }
  1633.     // Otherwise success and non-empty result.  This also means that buf_size is accurate and <= to what we sent in.
  1634.     // Fix for v1.0.47: ENSURE PROPER STRING TERMINATION. This is suspected to be a source of crashing.
  1635.     // MSDN: "If the data has the REG_SZ, REG_MULTI_SZ or REG_EXPAND_SZ type, the string may not have been
  1636.     // stored with the proper null-terminating characters. Therefore, even if the function returns
  1637.     // ERROR_SUCCESS, the application should ensure that the string is properly terminated before using
  1638.     // it; otherwise, it may overwrite a buffer. (Note that REG_MULTI_SZ strings should have two
  1639.     // null-terminating characters.)"
  1640.     // MSDN: "If the data has the REG_SZ, REG_MULTI_SZ or REG_EXPAND_SZ type, this size includes any
  1641.     // terminating null character or characters."
  1642.     --buf_size; // Convert to the index of the last character written.  This is safe because above already checked that buf_size>0.
  1643.     if (aBuf[buf_size] == '\0') // It wrote at least one zero terminator (it might write more than one for Unicode, or if it's simply stored with more than one in the registry).
  1644.     {
  1645.         while (buf_size && !aBuf[buf_size - 1]) // Scan leftward in case more than one consecutive terminator.
  1646.             --buf_size;
  1647.         return buf_size; // There's a tiny chance that this could the wrong length, namely when the string contains binary zero(s) to the left of non-zero characters.  But that seems too rare to be worth calling strlen() for.
  1648.     }
  1649.     // Otherwise, it didn't write a terminator, so provide one.
  1650.     ++buf_size; // To reflect the fact that we're about to write an extra char.
  1651.     if (buf_size >= aBufSize) // There's no room for a terminator without truncating the data (very rare).  Seems best to indicate failure.
  1652.     {
  1653.         *aBuf = '\0';
  1654.         return 0;
  1655.     }
  1656.     // Otherwise, there's room for the terminator.
  1657.     aBuf[buf_size] = '\0';
  1658.     return buf_size; // There's a tiny chance that this could the wrong length, namely when the string contains binary zero(s) to the left of non-zero characters.  But that seems too rare to be worth calling strlen() for.
  1659. }
  1660.  
  1661.  
  1662.  
  1663. LPVOID AllocInterProcMem(HANDLE &aHandle, DWORD aSize, HWND aHwnd)
  1664. // aHandle is an output parameter that holds the mapping for Win9x and the process handle for NT.
  1665. // Returns NULL on failure (in which case caller should ignore the value of aHandle).
  1666. {
  1667.     // ALLOCATE APPROPRIATE TYPE OF MEMORY (depending on OS type)
  1668.     LPVOID mem;
  1669.     if (g_os.IsWin9x()) // Use file-mapping method.
  1670.     {
  1671.         if (   !(aHandle = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, aSize, NULL))   )
  1672.             return NULL;
  1673.         mem = MapViewOfFile(aHandle, FILE_MAP_ALL_ACCESS, 0, 0, 0);
  1674.     }
  1675.     else // NT/2k/XP/2003 or later.  Use the VirtualAllocEx() so that caller can use Read/WriteProcessMemory().
  1676.     {
  1677.         DWORD pid;
  1678.         GetWindowThreadProcessId(aHwnd, &pid);
  1679.         // Even if the PID is our own, open the process anyway to simplify the code. After all, it would be
  1680.         // pretty silly for a script to access its own ListViews via this method.
  1681.         if (   !(aHandle = OpenProcess(PROCESS_VM_OPERATION | PROCESS_VM_READ | PROCESS_VM_WRITE, FALSE, pid))   )
  1682.             return NULL; // Let ErrorLevel tell the story.
  1683.         // Load function dynamically to allow program to launch on win9x:
  1684.         typedef LPVOID (WINAPI *MyVirtualAllocExType)(HANDLE, LPVOID, SIZE_T, DWORD, DWORD);
  1685.         static MyVirtualAllocExType MyVirtualAllocEx = (MyVirtualAllocExType)GetProcAddress(GetModuleHandle("kernel32")
  1686.             , "VirtualAllocEx");
  1687.         // Reason for using VirtualAllocEx(): When sending LVITEM structures to a control in a remote process, the
  1688.         // structure and its pszText buffer must both be memory inside the remote process rather than in our own.
  1689.         mem = MyVirtualAllocEx(aHandle, NULL, aSize, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
  1690.     }
  1691.     if (!mem)
  1692.         CloseHandle(aHandle); // Closes the mapping for Win9x and the process handle for other OSes. Caller should ignore the value of aHandle when return value is NULL.
  1693.     //else leave the handle open (required for both methods).  It's the caller's responsibility to close it.
  1694.     return mem;
  1695. }
  1696.  
  1697.  
  1698.  
  1699. void FreeInterProcMem(HANDLE aHandle, LPVOID aMem)
  1700. // Caller has ensured that aMem is a file-mapping for Win9x and a VirtualAllocEx block for NT/2k/XP+.
  1701. // Similarly, it has ensured that aHandle is a file-mapping handle for Win9x and a process handle for NT/2k/XP+.
  1702. {
  1703.     if (g_os.IsWin9x())
  1704.         UnmapViewOfFile(aMem);
  1705.     else
  1706.     {
  1707.         // Load function dynamically to allow program to launch on win9x:
  1708.         typedef BOOL (WINAPI *MyVirtualFreeExType)(HANDLE, LPVOID, SIZE_T, DWORD);
  1709.         static MyVirtualFreeExType MyVirtualFreeEx = (MyVirtualFreeExType)GetProcAddress(GetModuleHandle("kernel32")
  1710.             , "VirtualFreeEx");
  1711.         MyVirtualFreeEx(aHandle, aMem, 0, MEM_RELEASE); // Size 0 is used with MEM_RELEASE.
  1712.     }
  1713.     // The following closes either the mapping or the process handle, depending on OS type.
  1714.     // But close it only after the above is done using it.
  1715.     CloseHandle(aHandle);
  1716. }
  1717.  
  1718.  
  1719.  
  1720. HBITMAP LoadPicture(char *aFilespec, int aWidth, int aHeight, int &aImageType, int aIconNumber
  1721.     , bool aUseGDIPlusIfAvailable)
  1722. // Returns NULL on failure.
  1723. // If aIconNumber > 0, an HICON or HCURSOR is returned (both should be interchangeable), never an HBITMAP.
  1724. // However, aIconNumber==1 is treated as a special icon upon which LoadImage is given preference over ExtractIcon
  1725. // for .ico/.cur/.ani files.
  1726. // Otherwise, .ico/.cur/.ani files are normally loaded as HICON (unless aUseGDIPlusIfAvailable is true or
  1727. // something else unusual happened such as file contents not matching file's extension).  This is done to preserve
  1728. // any properties that HICONs have but HBITMAPs lack, namely the ability to be animated and perhaps other things.
  1729. //
  1730. // Loads a JPG/GIF/BMP/ICO/etc. and returns an HBITMAP or HICON to the caller (which it may call
  1731. // DeleteObject()/DestroyIcon() upon, though upon program termination all such handles are freed
  1732. // automatically).  The image is scaled to the specified width and height.  If zero is specified
  1733. // for either, the image's actual size will be used for that dimension.  If -1 is specified for one,
  1734. // that dimension will be kept proportional to the other dimension's size so that the original aspect
  1735. // ratio is retained.
  1736. {
  1737.     HBITMAP hbitmap = NULL;
  1738.     aImageType = -1; // The type of image currently inside hbitmap.  Set default value for output parameter as "unknown".
  1739.  
  1740.     if (!*aFilespec) // Allow blank filename to yield NULL bitmap (and currently, some callers do call it this way).
  1741.         return NULL;
  1742.     if (aIconNumber < 0) // Allowed to be called this way by GUI and others (to avoid need for validation of user input there).
  1743.         aIconNumber = 0; // Use the default behavior, which is "load icon or bitmap, whichever is most appropriate".
  1744.  
  1745.     char *file_ext = strrchr(aFilespec, '.');
  1746.     if (file_ext)
  1747.         ++file_ext;
  1748.  
  1749.     // v1.0.43.07: If aIconNumber is zero, caller didn't specify whether it wanted an icon or bitmap.  Thus,
  1750.     // there must be some kind of detection for whether ExtractIcon is needed instead of GDIPlus/OleLoadPicture.
  1751.     // Although this could be done by attempting ExtractIcon only after GDIPlus/OleLoadPicture fails (or by
  1752.     // somehow checking the internal nature of the file), for performance and code size, it seems best to not
  1753.     // to incur this extra I/O and instead make only one attempt based on the file's extension.
  1754.     // Must use ExtractIcon() if either of the following is true:
  1755.     // 1) Caller gave an icon index of the second or higher icon in the file.  Update for v1.0.43.05: There
  1756.     //    doesn't seem to be any reason to allow a caller to explicitly specify ExtractIcon as the method of
  1757.     //    loading the *first* icon from a .ico file since LoadImage is likely always superior.  This is
  1758.     //    because unlike ExtractIcon/Ex, LoadImage: 1) Doesn't distort icons, especially 16x16 icons; 2) is
  1759.     //    capable of loading icons other than the first by means of width and height parameters.
  1760.     // 2) The target file is of type EXE/DLL/ICL/CPL/etc. (LoadImage() is documented not to work on those file types).
  1761.     //    ICL files (v1.0.43.05): Apparently ICL files are an unofficial file format. Someone on the newsgroups
  1762.     //    said that an ICL is an "ICon Library... a renamed 16-bit Windows .DLL (an NE format executable) which
  1763.     //    typically contains nothing but a resource section. The ICL extension seems to be used by convention."
  1764.     bool ExtractIcon_was_used = aIconNumber > 1 || (file_ext && (
  1765.            !stricmp(file_ext, "exe")
  1766.         || !stricmp(file_ext, "dll")
  1767.         || !stricmp(file_ext, "icl") // Icon library: Unofficial dll container, see notes above.
  1768.         || !stricmp(file_ext, "cpl") // Control panel extension/applet (ExtractIcon is said to work on these).
  1769.         || !stricmp(file_ext, "scr") // Screen saver (ExtractIcon should work since these are really EXEs).
  1770.         // v1.0.44: Below are now omitted to reduce code size and improve performance. They are still supported
  1771.         // indirectly because ExtractIcon is attempted whenever LoadImage() fails further below.
  1772.         //|| !stricmp(file_ext, "drv") // Driver (ExtractIcon is said to work on these).
  1773.         //|| !stricmp(file_ext, "ocx") // OLE/ActiveX Control Extension
  1774.         //|| !stricmp(file_ext, "vbx") // Visual Basic Extension
  1775.         //|| !stricmp(file_ext, "acm") // Audio Compression Manager Driver
  1776.         //|| !stricmp(file_ext, "bpl") // Delphi Library (like a DLL?)
  1777.         // Not supported due to rarity, code size, performance, and uncertainty of whether ExtractIcon works on them.
  1778.         // Update for v1.0.44: The following are now supported indirectly because ExtractIcon is attempted whenever
  1779.         // LoadImage() fails further below.
  1780.         //|| !stricmp(file_ext, "nil") // Norton Icon Library 
  1781.         //|| !stricmp(file_ext, "wlx") // Total/Windows Commander Lister Plug-in
  1782.         //|| !stricmp(file_ext, "wfx") // Total/Windows Commander File System Plug-in
  1783.         //|| !stricmp(file_ext, "wcx") // Total/Windows Commander Plug-in
  1784.         //|| !stricmp(file_ext, "wdx") // Total/Windows Commander Plug-in
  1785.         ));
  1786.     if (ExtractIcon_was_used)
  1787.     {
  1788.         aImageType = IMAGE_ICON;
  1789.         hbitmap = (HBITMAP)ExtractIcon(g_hInstance, aFilespec, aIconNumber > 0 ? aIconNumber - 1 : 0);
  1790.         // Above: Although it isn't well documented at MSDN, apparently both ExtractIcon() and LoadIcon()
  1791.         // scale the icon to the system's large-icon size (usually 32x32) regardless of the actual size of
  1792.         // the icon inside the file.  For this reason, callers should call us in a way that allows us to
  1793.         // give preference to LoadImage() over ExtractIcon() (unless the caller needs to retain backward
  1794.         // compatibility with existing scripts that explicitly specify icon #1 to force the ExtractIcon
  1795.         // method to be used).
  1796.         if (hbitmap < (HBITMAP)2) // i.e. it's NULL or 1. Return value of 1 means "incorrect file type".
  1797.             return NULL; // v1.0.44: Fixed to return NULL vs. hbitmap, since 1 is an invalid handle (perhaps rare since no known bugs caused by it).
  1798.         //else continue on below so that the icon can be resized to the caller's specified dimensions.
  1799.     }
  1800.     else if (aIconNumber > 0) // Caller wanted HICON, never HBITMAP, so set type now to enforce that.
  1801.         aImageType = IMAGE_ICON; // Should be suitable for cursors too, since they're interchangeable for the most part.
  1802.     else if (file_ext) // Make an initial guess of the type of image if the above didn't already determine the type.
  1803.     {
  1804.         if (!stricmp(file_ext, "ico"))
  1805.             aImageType = IMAGE_ICON;
  1806.         else if (!stricmp(file_ext, "cur") || !stricmp(file_ext, "ani"))
  1807.             aImageType = IMAGE_CURSOR;
  1808.         else if (!stricmp(file_ext, "bmp"))
  1809.             aImageType = IMAGE_BITMAP;
  1810.         //else for other extensions, leave set to "unknown" so that the below knows to use IPic or GDI+ to load it.
  1811.     }
  1812.     //else same comment as above.
  1813.  
  1814.     if ((aWidth == -1 || aHeight == -1) && (!aWidth || !aHeight))
  1815.         aWidth = aHeight = 0; // i.e. One dimension is zero and the other is -1, which resolves to the same as "keep original size".
  1816.     bool keep_aspect_ratio = (aWidth == -1 || aHeight == -1);
  1817.  
  1818.     // Caller should ensure that aUseGDIPlusIfAvailable==false when aIconNumber > 0, since it makes no sense otherwise.
  1819.     HINSTANCE hinstGDI = NULL;
  1820.     if (aUseGDIPlusIfAvailable && !(hinstGDI = LoadLibrary("gdiplus"))) // Relies on short-circuit boolean order for performance.
  1821.         aUseGDIPlusIfAvailable = false; // Override any original "true" value as a signal for the section below.
  1822.  
  1823.     if (!hbitmap && aImageType > -1 && !aUseGDIPlusIfAvailable)
  1824.     {
  1825.         // Since image hasn't yet be loaded and since the file type appears to be one supported by
  1826.         // LoadImage() [icon/cursor/bitmap], attempt that first.  If it fails, fall back to the other
  1827.         // methods below in case the file's internal contents differ from what the file extension indicates.
  1828.         int desired_width, desired_height;
  1829.         if (keep_aspect_ratio) // Load image at its actual size.  It will be rescaled to retain aspect ratio later below.
  1830.         {
  1831.             desired_width = 0;
  1832.             desired_height = 0;
  1833.         }
  1834.         else
  1835.         {
  1836.             desired_width = aWidth;
  1837.             desired_height = aHeight;
  1838.         }
  1839.         // For LoadImage() below:
  1840.         // LR_CREATEDIBSECTION applies only when aImageType == IMAGE_BITMAP, but seems appropriate in that case.
  1841.         // Also, if width and height are non-zero, that will determine which icon of a multi-icon .ico file gets
  1842.         // loaded (though I don't know the exact rules of precedence).
  1843.         // KNOWN LIMITATIONS/BUGS:
  1844.         // LoadImage() fails when requesting a size of 1x1 for an image whose orig/actual size is small (e.g. 1x2).
  1845.         // Unlike CopyImage(), perhaps it detects that division by zero would occur and refuses to do the
  1846.         // calculation rather than providing more code to do a correct calculation that doesn't divide by zero.
  1847.         // For example:
  1848.         // LoadImage() Success:
  1849.         //   Gui, Add, Pic, h2 w2, bitmap 1x2.bmp
  1850.         //   Gui, Add, Pic, h1 w1, bitmap 4x6.bmp
  1851.         // LoadImage() Failure:
  1852.         //   Gui, Add, Pic, h1 w1, bitmap 1x2.bmp
  1853.         // LoadImage() also fails on:
  1854.         //   Gui, Add, Pic, h1, bitmap 1x2.bmp
  1855.         // And then it falls back to GDIplus, which in the particular case above appears to traumatize the
  1856.         // parent window (or its picture control), because the GUI window hangs (but not the script) after
  1857.         // doing a FileSelectFolder.  For example:
  1858.         //   Gui, Add, Button,, FileSelectFile
  1859.         //   Gui, Add, Pic, h1, bitmap 1x2.bmp  ; Causes GUI window to hang after FileSelectFolder (due to LoadImage failing then falling back to GDIplus; i.e. GDIplus is somehow triggering the problem).
  1860.         //   Gui, Show
  1861.         //   return
  1862.         //   ButtonFileSelectFile:
  1863.         //   FileSelectFile, outputvar
  1864.         //   return
  1865.         if (hbitmap = (HBITMAP)LoadImage(NULL, aFilespec, aImageType, desired_width, desired_height
  1866.             , LR_LOADFROMFILE | LR_CREATEDIBSECTION))
  1867.         {
  1868.             // The above might have loaded an HICON vs. an HBITMAP (it has been confirmed that LoadImage()
  1869.             // will return an HICON vs. HBITMAP is aImageType is IMAGE_ICON/CURSOR).  Note that HICON and
  1870.             // HCURSOR are identical for most/all Windows API uses.  Also note that LoadImage() will load
  1871.             // an icon as a bitmap if the file contains an icon but IMAGE_BITMAP was passed in (at least
  1872.             // on Windows XP).
  1873.             if (!keep_aspect_ratio) // No further resizing is needed.
  1874.                 return hbitmap;
  1875.             // Otherwise, continue on so that the image can be resized via a second call to LoadImage().
  1876.         }
  1877.         // v1.0.40.10: Abort if file doesn't exist so that GDIPlus isn't even attempted. This is done because
  1878.         // loading GDIPlus apparently disrupts the color palette of certain games, at least old ones that use
  1879.         // DirectDraw in 256-color depth.
  1880.         else if (GetFileAttributes(aFilespec) == 0xFFFFFFFF) // For simplicity, we don't check if it's a directory vs. file, since that should be too rare.
  1881.             return NULL;
  1882.         // v1.0.43.07: Also abort if caller wanted an HICON (not an HBITMAP), since the other methods below
  1883.         // can't yield an HICON.
  1884.         else if (aIconNumber > 0)
  1885.         {
  1886.             // UPDATE for v1.0.44: Attempt ExtractIcon in case its some extension that's
  1887.             // was recognized as an icon container (such as AutoHotkeySC.bin) and thus wasn't handled higher above.
  1888.             hbitmap = (HBITMAP)ExtractIcon(g_hInstance, aFilespec, aIconNumber - 1);
  1889.             if (hbitmap < (HBITMAP)2) // i.e. it's NULL or 1. Return value of 1 means "incorrect file type".
  1890.                 return NULL;
  1891.             ExtractIcon_was_used = true;
  1892.         }
  1893.         //else file exists, so continue on so that the other methods are attempted in case file's contents
  1894.         // differ from what the file extension indicates, or in case the other methods can be successful
  1895.         // even when the above failed.
  1896.     }
  1897.  
  1898.     IPicture *pic = NULL; // Also used to detect whether IPic method was used to load the image.
  1899.  
  1900.     if (!hbitmap) // Above hasn't loaded the image yet, so use the fall-back methods.
  1901.     {
  1902.         // At this point, regardless of the image type being loaded (even an icon), it will
  1903.         // definitely be converted to a Bitmap below.  So set the type:
  1904.         aImageType = IMAGE_BITMAP;
  1905.         // Find out if this file type is supported by the non-GDI+ method.  This check is not foolproof
  1906.         // since all it does is look at the file's extension, not its contents.  However, it doesn't
  1907.         // need to be 100% accurate because its only purpose is to detect whether the higher-overhead
  1908.         // calls to GdiPlus can be avoided.
  1909.         if (aUseGDIPlusIfAvailable || !file_ext || (stricmp(file_ext, "jpg")
  1910.             && stricmp(file_ext, "jpeg") && stricmp(file_ext, "gif"))) // Non-standard file type (BMP is already handled above).
  1911.             if (!hinstGDI) // We don't yet have a handle from an earlier call to LoadLibary().
  1912.                 hinstGDI = LoadLibrary("gdiplus");
  1913.         // If it is suspected that the file type isn't supported, try to use GdiPlus if available.
  1914.         // If it's not available, fall back to the old method in case the filename doesn't properly
  1915.         // reflect its true contents (i.e. in case it really is a JPG/GIF/BMP internally).
  1916.         // If the below LoadLibrary() succeeds, either the OS is XP+ or the GdiPlus extensions have been
  1917.         // installed on an older OS.
  1918.         if (hinstGDI)
  1919.         {
  1920.             // LPVOID and "int" are used to avoid compiler errors caused by... namespace issues?
  1921.             typedef int (WINAPI *GdiplusStartupType)(ULONG_PTR*, LPVOID, LPVOID);
  1922.             typedef VOID (WINAPI *GdiplusShutdownType)(ULONG_PTR);
  1923.             typedef int (WINGDIPAPI *GdipCreateBitmapFromFileType)(LPVOID, LPVOID);
  1924.             typedef int (WINGDIPAPI *GdipCreateHBITMAPFromBitmapType)(LPVOID, LPVOID, DWORD);
  1925.             typedef int (WINGDIPAPI *GdipDisposeImageType)(LPVOID);
  1926.             GdiplusStartupType DynGdiplusStartup = (GdiplusStartupType)GetProcAddress(hinstGDI, "GdiplusStartup");
  1927.               GdiplusShutdownType DynGdiplusShutdown = (GdiplusShutdownType)GetProcAddress(hinstGDI, "GdiplusShutdown");
  1928.               GdipCreateBitmapFromFileType DynGdipCreateBitmapFromFile = (GdipCreateBitmapFromFileType)GetProcAddress(hinstGDI, "GdipCreateBitmapFromFile");
  1929.               GdipCreateHBITMAPFromBitmapType DynGdipCreateHBITMAPFromBitmap = (GdipCreateHBITMAPFromBitmapType)GetProcAddress(hinstGDI, "GdipCreateHBITMAPFromBitmap");
  1930.               GdipDisposeImageType DynGdipDisposeImage = (GdipDisposeImageType)GetProcAddress(hinstGDI, "GdipDisposeImage");
  1931.  
  1932.             ULONG_PTR token;
  1933.             Gdiplus::GdiplusStartupInput gdi_input;
  1934.             Gdiplus::GpBitmap *pgdi_bitmap;
  1935.             if (DynGdiplusStartup && DynGdiplusStartup(&token, &gdi_input, NULL) == Gdiplus::Ok)
  1936.             {
  1937.                 WCHAR filespec_wide[MAX_PATH];
  1938.                 ToWideChar(aFilespec, filespec_wide, MAX_PATH); // Dest. size is in wchars, not bytes.
  1939.                 if (DynGdipCreateBitmapFromFile(filespec_wide, &pgdi_bitmap) == Gdiplus::Ok)
  1940.                 {
  1941.                     if (DynGdipCreateHBITMAPFromBitmap(pgdi_bitmap, &hbitmap, CLR_DEFAULT) != Gdiplus::Ok)
  1942.                         hbitmap = NULL; // Set to NULL to be sure.
  1943.                     DynGdipDisposeImage(pgdi_bitmap); // This was tested once to make sure it really returns Gdiplus::Ok.
  1944.                 }
  1945.                 // The current thought is that shutting it down every time conserves resources.  If so, it
  1946.                 // seems justified since it is probably called infrequently by most scripts:
  1947.                 DynGdiplusShutdown(token);
  1948.             }
  1949.             FreeLibrary(hinstGDI);
  1950.         }
  1951.         else // Using old picture loading method.
  1952.         {
  1953.             // Based on code sample at http://www.codeguru.com/Cpp/G-M/bitmap/article.php/c4935/
  1954.             HANDLE hfile = CreateFile(aFilespec, GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL);
  1955.             if (hfile == INVALID_HANDLE_VALUE)
  1956.                 return NULL;
  1957.             DWORD size = GetFileSize(hfile, NULL);
  1958.             HGLOBAL hglobal = GlobalAlloc(GMEM_MOVEABLE, size);
  1959.             if (!hglobal)
  1960.             {
  1961.                 CloseHandle(hfile);
  1962.                 return NULL;
  1963.             }
  1964.             LPVOID hlocked = GlobalLock(hglobal);
  1965.             if (!hlocked)
  1966.             {
  1967.                 CloseHandle(hfile);
  1968.                 GlobalFree(hglobal);
  1969.                 return NULL;
  1970.             }
  1971.             // Read the file into memory:
  1972.             ReadFile(hfile, hlocked, size, &size, NULL);
  1973.             GlobalUnlock(hglobal);
  1974.             CloseHandle(hfile);
  1975.             LPSTREAM stream;
  1976.             if (FAILED(CreateStreamOnHGlobal(hglobal, FALSE, &stream)) || !stream)  // Relies on short-circuit boolean order.
  1977.             {
  1978.                 GlobalFree(hglobal);
  1979.                 return NULL;
  1980.             }
  1981.             // Specify TRUE to have it do the GlobalFree() for us.  But since the call might fail, it seems best
  1982.             // to free the mem ourselves to avoid uncertainy over what it does on failure:
  1983.             if (FAILED(OleLoadPicture(stream, 0, FALSE, IID_IPicture, (void **)&pic)))
  1984.                 pic = NULL;
  1985.             stream->Release();
  1986.             GlobalFree(hglobal);
  1987.             if (!pic)
  1988.                 return NULL;
  1989.             pic->get_Handle((OLE_HANDLE *)&hbitmap);
  1990.             // Above: MSDN: "The caller is responsible for this handle upon successful return. The variable is set
  1991.             // to NULL on failure."
  1992.             if (!hbitmap)
  1993.             {
  1994.                 pic->Release();
  1995.                 return NULL;
  1996.             }
  1997.             // Don't pic->Release() yet because that will also destroy/invalidate hbitmap handle.
  1998.         } // IPicture method was used.
  1999.     } // IPicture or GDIPlus was used to load the image, not a simple LoadImage() or ExtractIcon().
  2000.  
  2001.     // Above has ensured that hbitmap is now not NULL.
  2002.     // Adjust things if "keep aspect ratio" is in effect:
  2003.     if (keep_aspect_ratio)
  2004.     {
  2005.         HBITMAP hbitmap_to_analyze;
  2006.         ICONINFO ii; // Must be declared at this scope level.
  2007.         if (aImageType == IMAGE_BITMAP)
  2008.             hbitmap_to_analyze = hbitmap;
  2009.         else // icon or cursor
  2010.         {
  2011.             if (GetIconInfo((HICON)hbitmap, &ii)) // Works on cursors too.
  2012.                 hbitmap_to_analyze = ii.hbmMask; // Use Mask because MSDN implies hbmColor can be NULL for monochrome cursors and such.
  2013.             else
  2014.             {
  2015.                 DestroyIcon((HICON)hbitmap);
  2016.                 return NULL; // No need to call pic->Release() because since it's an icon, we know IPicture wasn't used (it only loads bitmaps).
  2017.             }
  2018.         }
  2019.         // Above has ensured that hbitmap_to_analyze is now not NULL.  Find bitmap's dimensions.
  2020.         BITMAP bitmap;
  2021.         GetObject(hbitmap_to_analyze, sizeof(BITMAP), &bitmap); // Realistically shouldn't fail at this stage.
  2022.         if (aHeight == -1)
  2023.         {
  2024.             // Caller wants aHeight calculated based on the specified aWidth (keep aspect ratio).
  2025.             if (bitmap.bmWidth) // Avoid any chance of divide-by-zero.
  2026.                 aHeight = (int)(((double)bitmap.bmHeight / bitmap.bmWidth) * aWidth + .5); // Round.
  2027.         }
  2028.         else
  2029.         {
  2030.             // Caller wants aWidth calculated based on the specified aHeight (keep aspect ratio).
  2031.             if (bitmap.bmHeight) // Avoid any chance of divide-by-zero.
  2032.                 aWidth = (int)(((double)bitmap.bmWidth / bitmap.bmHeight) * aHeight + .5); // Round.
  2033.         }
  2034.         if (aImageType != IMAGE_BITMAP)
  2035.         {
  2036.             // It's our reponsibility to delete these two when they're no longer needed:
  2037.             DeleteObject(ii.hbmColor);
  2038.             DeleteObject(ii.hbmMask);
  2039.             // If LoadImage() vs. ExtractIcon() was used originally, call LoadImage() again because
  2040.             // I haven't found any other way to retain an animated cursor's animation (and perhaps
  2041.             // other icon/cursor attributes) when resizing the icon/cursor (CopyImage() doesn't
  2042.             // retain animation):
  2043.             if (!ExtractIcon_was_used)
  2044.             {
  2045.                 DestroyIcon((HICON)hbitmap); // Destroy the original HICON.
  2046.                 // Load a new one, but at the size newly calculated above.
  2047.                 // Due to an apparent bug in Windows 9x (at least Win98se), the below call will probably
  2048.                 // crash the program with a "divide error" if the specified aWidth and/or aHeight are
  2049.                 // greater than 90.  Since I don't know whether this affects all versions of Windows 9x, and
  2050.                 // all animated cursors, it seems best just to document it here and in the help file rather
  2051.                 // than limiting the dimensions of .ani (and maybe .cur) files for certain operating systems.
  2052.                 return (HBITMAP)LoadImage(NULL, aFilespec, aImageType, aWidth, aHeight, LR_LOADFROMFILE);
  2053.             }
  2054.         }
  2055.     }
  2056.  
  2057.     HBITMAP hbitmap_new; // To hold the scaled image (if scaling is needed).
  2058.     if (pic) // IPicture method was used.
  2059.     {
  2060.         // The below statement is confirmed by having tested that DeleteObject(hbitmap) fails
  2061.         // if called after pic->Release():
  2062.         // "Copy the image. Necessary, because upon pic's release the handle is destroyed."
  2063.         // MSDN: CopyImage(): "[If either width or height] is zero, then the returned image will have the
  2064.         // same width/height as the original."
  2065.         // Note also that CopyImage() seems to provide better scaling quality than using MoveWindow()
  2066.         // (followed by redrawing the parent window) on the static control that contains it:
  2067.         hbitmap_new = (HBITMAP)CopyImage(hbitmap, IMAGE_BITMAP, aWidth, aHeight // We know it's IMAGE_BITMAP in this case.
  2068.             , (aWidth || aHeight) ? 0 : LR_COPYRETURNORG); // Produce original size if no scaling is needed.
  2069.         pic->Release();
  2070.         // No need to call DeleteObject(hbitmap), see above.
  2071.     }
  2072.     else // GDIPlus or a simple method such as LoadImage or ExtractIcon was used.
  2073.     {
  2074.         if (!aWidth && !aHeight) // No resizing needed.
  2075.             return hbitmap;
  2076.         // The following will also handle HICON/HCURSOR correctly if aImageType == IMAGE_ICON/CURSOR.
  2077.         // Also, LR_COPYRETURNORG|LR_COPYDELETEORG is used because it might allow the animation of
  2078.         // a cursor to be retained if the specified size happens to match the actual size of the
  2079.         // cursor.  This is because normally, it seems that CopyImage() omits cursor animation
  2080.         // from the new object.  MSDN: "LR_COPYRETURNORG returns the original hImage if it satisfies
  2081.         // the criteria for the copyùthat is, correct dimensions and color depthùin which case the
  2082.         // LR_COPYDELETEORG flag is ignored. If this flag is not specified, a new object is always created."
  2083.         // KNOWN BUG: Calling CopyImage() when the source image is tiny and the destination width/height
  2084.         // is also small (e.g. 1) causes a divide-by-zero exception.
  2085.         // For example:
  2086.         //   Gui, Add, Pic, h1 w-1, bitmap 1x2.bmp  ; Crash (divide by zero)
  2087.         //   Gui, Add, Pic, h1 w-1, bitmap 2x3.bmp  ; Crash (divide by zero)
  2088.         // However, such sizes seem too rare to document or put in an exception handler for.
  2089.         hbitmap_new = (HBITMAP)CopyImage(hbitmap, aImageType, aWidth, aHeight, LR_COPYRETURNORG | LR_COPYDELETEORG);
  2090.         // Above's LR_COPYDELETEORG deletes the original to avoid cascading resource usage.  MSDN's
  2091.         // LoadImage() docs say:
  2092.         // "When you are finished using a bitmap, cursor, or icon you loaded without specifying the
  2093.         // LR_SHARED flag, you can release its associated memory by calling one of [the three functions]."
  2094.         // Therefore, it seems best to call the right function even though DeleteObject might work on
  2095.         // all of them on some or all current OSes.  UPDATE: Evidence indicates that DestroyIcon()
  2096.         // will also destroy cursors, probably because icons and cursors are literally identical in
  2097.         // every functional way.  One piece of evidence:
  2098.         //> No stack trace, but I know the exact source file and line where the call
  2099.         //> was made. But still, it is annoying when you see 'DestroyCursor' even though
  2100.         //> there is 'DestroyIcon'.
  2101.         // "Can't be helped. Icons and cursors are the same thing" (Tim Robinson (MVP, Windows SDK)).
  2102.         //
  2103.         // Finally, the reason this is important is that it eliminates one handle type
  2104.         // that we would otherwise have to track.  For example, if a gui window is destroyed and
  2105.         // and recreated multiple times, its bitmap and icon handles should all be destroyed each time.
  2106.         // Otherwise, resource usage would cascade upward until the script finally terminated, at
  2107.         // which time all such handles are freed automatically.
  2108.     }
  2109.     return hbitmap_new;
  2110. }
  2111.  
  2112.  
  2113.  
  2114. HBITMAP IconToBitmap(HICON ahIcon, bool aDestroyIcon)
  2115. // Converts HICON to an HBITMAP that has ahIcon's actual dimensions.
  2116. // The incoming ahIcon will be destroyed if the caller passes true for aDestroyIcon.
  2117. // Returns NULL on failure, in which case aDestroyIcon will still have taken effect.
  2118. // If the icon contains any transparent pixels, they will be mapped to CLR_NONE within
  2119. // the bitmap so that the caller can detect them.
  2120. {
  2121.     if (!ahIcon)
  2122.         return NULL;
  2123.  
  2124.     HBITMAP hbitmap = NULL;  // Set default.  This will be the value returned.
  2125.  
  2126.     HDC hdc_desktop = GetDC(HWND_DESKTOP);
  2127.     HDC hdc = CreateCompatibleDC(hdc_desktop); // Don't pass NULL since I think that would result in a monochrome bitmap.
  2128.     if (hdc)
  2129.     {
  2130.         ICONINFO ii;
  2131.         if (GetIconInfo(ahIcon, &ii))
  2132.         {
  2133.             BITMAP icon_bitmap;
  2134.             // Find out how big the icon is and create a bitmap compatible with the desktop DC (not the memory DC,
  2135.             // since its bits per pixel (color depth) is probably 1.
  2136.             if (GetObject(ii.hbmColor, sizeof(BITMAP), &icon_bitmap)
  2137.                 && (hbitmap = CreateCompatibleBitmap(hdc_desktop, icon_bitmap.bmWidth, icon_bitmap.bmHeight))) // Assign
  2138.             {
  2139.                 // To retain maximum quality in case caller needs to resize the bitmap we return, convert the
  2140.                 // icon to a bitmap that matches the icon's actual size:
  2141.                 HGDIOBJ old_object = SelectObject(hdc, hbitmap);
  2142.                 if (old_object) // Above succeeded.
  2143.                 {
  2144.                     // Use DrawIconEx() vs. DrawIcon() because someone said DrawIcon() always draws 32x32
  2145.                     // regardless of the icon's actual size.
  2146.                     // If it's ever needed, this can be extended so that the caller can pass in a background
  2147.                     // color to use in place of any transparent pixels within the icon (apparently, DrawIconEx()
  2148.                     // skips over transparent pixels in the icon when drawing to the DC and its bitmap):
  2149.                     RECT rect = {0, 0, icon_bitmap.bmWidth, icon_bitmap.bmHeight}; // Left, top, right, bottom.
  2150.                     HBRUSH hbrush = CreateSolidBrush(CLR_DEFAULT);
  2151.                     FillRect(hdc, &rect, hbrush);
  2152.                     DeleteObject(hbrush);
  2153.                     // Probably something tried and abandoned: FillRect(hdc, &rect, (HBRUSH)GetStockObject(NULL_BRUSH));
  2154.                     DrawIconEx(hdc, 0, 0, ahIcon, icon_bitmap.bmWidth, icon_bitmap.bmHeight, 0, NULL, DI_NORMAL);
  2155.                     // Debug: Find out properties of new bitmap.
  2156.                     //BITMAP b;
  2157.                     //GetObject(hbitmap, sizeof(BITMAP), &b);
  2158.                     SelectObject(hdc, old_object); // Might be needed (prior to deleting hdc) to prevent memory leak.
  2159.                 }
  2160.             }
  2161.             // It's our reponsibility to delete these two when they're no longer needed:
  2162.             DeleteObject(ii.hbmColor);
  2163.             DeleteObject(ii.hbmMask);
  2164.         }
  2165.         DeleteDC(hdc);
  2166.     }
  2167.     ReleaseDC(HWND_DESKTOP, hdc_desktop);
  2168.     if (aDestroyIcon)
  2169.         DestroyIcon(ahIcon);
  2170.     return hbitmap;
  2171. }
  2172.  
  2173.  
  2174.  
  2175. HRESULT MySetWindowTheme(HWND hwnd, LPCWSTR pszSubAppName, LPCWSTR pszSubIdList)
  2176. {
  2177.     // The library must be loaded dynamically, otherwise the app will not launch on OSes older than XP.
  2178.     // Theme DLL is normally available only on XP+, but an attempt to load it is made unconditionally
  2179.     // in case older OSes can ever have it.
  2180.     HRESULT hresult = !S_OK; // Set default as "failure".
  2181.     HINSTANCE hinstTheme = LoadLibrary("uxtheme");
  2182.     if (hinstTheme)
  2183.     {
  2184.         typedef HRESULT (WINAPI *MySetWindowThemeType)(HWND, LPCWSTR, LPCWSTR);
  2185.           MySetWindowThemeType DynSetWindowTheme = (MySetWindowThemeType)GetProcAddress(hinstTheme, "SetWindowTheme");
  2186.         if (DynSetWindowTheme)
  2187.             hresult = DynSetWindowTheme(hwnd, pszSubAppName, pszSubIdList);
  2188.         FreeLibrary(hinstTheme);
  2189.     }
  2190.     return hresult;
  2191. }
  2192.  
  2193.  
  2194.  
  2195. //HRESULT MyEnableThemeDialogTexture(HWND hwnd, DWORD dwFlags)
  2196. //{
  2197. //    // The library must be loaded dynamically, otherwise the app will not launch on OSes older than XP.
  2198. //    // Theme DLL is normally available only on XP+, but an attempt to load it is made unconditionally
  2199. //    // in case older OSes can ever have it.
  2200. //    HRESULT hresult = !S_OK; // Set default as "failure".
  2201. //    HINSTANCE hinstTheme = LoadLibrary("uxtheme");
  2202. //    if (hinstTheme)
  2203. //    {
  2204. //        typedef HRESULT (WINAPI *MyEnableThemeDialogTextureType)(HWND, DWORD);
  2205. //          MyEnableThemeDialogTextureType DynEnableThemeDialogTexture = (MyEnableThemeDialogTextureType)GetProcAddress(hinstTheme, "EnableThemeDialogTexture");
  2206. //        if (DynEnableThemeDialogTexture)
  2207. //            hresult = DynEnableThemeDialogTexture(hwnd, dwFlags);
  2208. //        FreeLibrary(hinstTheme);
  2209. //    }
  2210. //    return hresult;
  2211. //}
  2212.  
  2213.  
  2214.  
  2215. char *ConvertEscapeSequences(char *aBuf, char aEscapeChar, bool aAllowEscapedSpace)
  2216. // Replaces any escape sequences in aBuf with their reduced equivalent.  For example, if aEscapeChar
  2217. // is accent, Each `n would become a literal linefeed.  aBuf's length should always be the same or
  2218. // lower than when the process started, so there is no chance of overflow.
  2219. {
  2220.     char *cp, *cp1;
  2221.     for (cp = aBuf; ; ++cp)  // Increment to skip over the symbol just found by the inner for().
  2222.     {
  2223.         for (; *cp && *cp != aEscapeChar; ++cp);  // Find the next escape char.
  2224.         if (!*cp) // end of string.
  2225.             break;
  2226.         cp1 = cp + 1;
  2227.         switch (*cp1)
  2228.         {
  2229.             // Only lowercase is recognized for these:
  2230.             case 'a': *cp1 = '\a'; break;  // alert (bell) character
  2231.             case 'b': *cp1 = '\b'; break;  // backspace
  2232.             case 'f': *cp1 = '\f'; break;  // formfeed
  2233.             case 'n': *cp1 = '\n'; break;  // newline
  2234.             case 'r': *cp1 = '\r'; break;  // carriage return
  2235.             case 't': *cp1 = '\t'; break;  // horizontal tab
  2236.             case 'v': *cp1 = '\v'; break;  // vertical tab
  2237.             case 's': // space (not always allowed for backward compatibility reasons).
  2238.                 if (aAllowEscapedSpace)
  2239.                     *cp1 = ' ';
  2240.                 //else do nothing extra, just let the standard action for unrecognized escape sequences.
  2241.                 break;
  2242.             // Otherwise, if it's not one of the above, the escape-char is considered to
  2243.             // mark the next character as literal, regardless of what it is. Examples:
  2244.             // `` -> `
  2245.             // `:: -> :: (effectively)
  2246.             // `; -> ;
  2247.             // `c -> c (i.e. unknown escape sequences resolve to the char after the `)
  2248.         }
  2249.         // Below has a final +1 to include the terminator:
  2250.         MoveMemory(cp, cp1, strlen(cp1) + 1);
  2251.     }
  2252.     return aBuf;
  2253. }
  2254.  
  2255.  
  2256.  
  2257. bool IsStringInList(char *aStr, char *aList, bool aFindExactMatch)
  2258. // Checks if aStr exists in aList (which is a comma-separated list).
  2259. // If aStr is blank, aList must start with a delimiting comma for there to be a match.
  2260. {
  2261.     // Must use a temp. buffer because otherwise there's no easy way to properly match upon strings
  2262.     // such as the following:
  2263.     // if var in string,,with,,literal,,commas
  2264.     char buf[LINE_SIZE];
  2265.     char *this_field = aList, *next_field, *cp;
  2266.  
  2267.     while (*this_field)  // For each field in aList.
  2268.     {
  2269.         // To avoid the need to constantly check for buffer overflow (i.e. to keep it simple),
  2270.         // just copy up to the limit of the buffer:
  2271.         strlcpy(buf, this_field, sizeof(buf));
  2272.         // Find the end of the field inside buf.  In keeping with the tradition set by the Input command,
  2273.         // this always uses comma rather than g_delimiter.
  2274.         for (cp = buf, next_field = this_field; *cp; ++cp, ++next_field)
  2275.         {
  2276.             if (*cp == ',')
  2277.             {
  2278.                 if (cp[1] == ',') // Make this pair into a single literal comma.
  2279.                 {
  2280.                     memmove(cp, cp + 1, strlen(cp + 1) + 1);  // +1 to include the zero terminator.
  2281.                     ++next_field;  // An extra increment since the source string still has both commas of the pair.
  2282.                 }
  2283.                 else // this comma marks the end of the field.
  2284.                 {
  2285.                     *cp = '\0';  // Terminate the buffer to isolate just the current field.
  2286.                     break;
  2287.                 }
  2288.             }
  2289.         }
  2290.  
  2291.         if (*next_field)  // The end of the field occurred prior to the end of aList.
  2292.             ++next_field; // Point it to the character after the delimiter (otherwise, leave it where it is).
  2293.  
  2294.         if (*buf) // It is possible for this to be blank only for the first field.  Example: if var in ,abc
  2295.         {
  2296.             if (aFindExactMatch)
  2297.             {
  2298.                 if (!g_strcmp(aStr, buf)) // Match found
  2299.                     return true;
  2300.             }
  2301.             else // Substring match
  2302.                 if (g_strstr(aStr, buf)) // Match found
  2303.                     return true;
  2304.         }
  2305.         else // First item in the list is the empty string.
  2306.             if (aFindExactMatch) // In this case, this is a match if aStr is also blank.
  2307.             {
  2308.                 if (!*aStr)
  2309.                     return true;
  2310.             }
  2311.             else // Empty string is always found as a substring in any other string.
  2312.                 return true;
  2313.         this_field = next_field;
  2314.     } // while()
  2315.  
  2316.     return false;  // No match found.
  2317. }