| 11357 | |
| 11358 | |
| 11359 | ResultType Line::PerformLoopParse(ResultToken *aResultToken, Line *&aJumpToLine, Line *aUntil) |
| 11360 | { |
| 11361 | if (!*ARG1) // Since the input variable's contents are blank, the loop will execute zero times. |
| 11362 | return CONDITION_FALSE; |
| 11363 | |
| 11364 | // The following will be used to hold the parsed items. It needs to have its own storage because |
| 11365 | // even though ARG1 might always be a writable memory area, we can't rely upon it being |
| 11366 | // persistent because it might reside in the deref buffer, in which case the other commands |
| 11367 | // in the loop's body would probably overwrite it. Even if the ARG1's contents aren't in |
| 11368 | // the deref buffer, we still can't modify it (i.e. to temporarily terminate it and thus |
| 11369 | // bypass the need for malloc() below) because that might modify the variable contents, and |
| 11370 | // that variable may be referenced elsewhere in the body of the loop (which would result |
| 11371 | // in unexpected side-effects). So, rather than have a limit of 64K or something (which |
| 11372 | // would limit this feature's usefulness for parsing a large list of filenames, for example), |
| 11373 | // it seems best to dynamically allocate a temporary buffer large enough to hold the |
| 11374 | // contents of ARG1 (the input variable). Update: Since these loops tend to be enclosed |
| 11375 | // by file-read loops, and thus may be called thousands of times in a short period, |
| 11376 | // it should help average performance to use the stack for small vars rather than |
| 11377 | // constantly doing malloc() and free(), which are much higher overhead and probably |
| 11378 | // cause memory fragmentation (especially with thousands of calls): |
| 11379 | size_t space_needed = ArgLength(1) + 1; // +1 for the zero terminator. |
| 11380 | LPTSTR stack_buf, buf; |
| 11381 | #define FREE_PARSE_MEMORY if (buf != stack_buf) free(buf) // Also used by the CSV version of this function. |
| 11382 | #define LOOP_PARSE_BUF_SIZE 40000 // |
| 11383 | if (space_needed <= LOOP_PARSE_BUF_SIZE) |
| 11384 | { |
| 11385 | stack_buf = (LPTSTR)talloca(space_needed); // Helps performance. See comments above. |
| 11386 | buf = stack_buf; |
| 11387 | } |
| 11388 | else |
| 11389 | { |
| 11390 | if ( !(buf = tmalloc(space_needed)) ) |
| 11391 | // Probably best to consider this a critical error, since on the rare times it does happen, the user |
| 11392 | // would probably want to know about it immediately. |
| 11393 | return MemoryError(); |
| 11394 | stack_buf = NULL; // For comparison purposes later below. |
| 11395 | } |
| 11396 | _tcscpy(buf, ARG1); // Make the copy. |
| 11397 | |
| 11398 | // Make a copy of ARG2 and ARG3 in case either one's contents are in the deref buffer, which would |
| 11399 | // probably be overwritten by the commands in the script loop's body: |
| 11400 | TCHAR delimiters[512], omit_list[512]; |
| 11401 | tcslcpy(delimiters, ARG2, _countof(delimiters)); |
| 11402 | tcslcpy(omit_list, ARG3, _countof(omit_list)); |
| 11403 | |
| 11404 | ResultType result = CONDITION_FALSE; |
| 11405 | Line *jump_to_line = nullptr; |
| 11406 | TCHAR *field, *field_end, saved_char; |
| 11407 | size_t field_length; |
| 11408 | global_struct &g = *::g; // Primarily for performance in this case. |
| 11409 | |
| 11410 | for (field = buf;;) |
| 11411 | { |
| 11412 | if (*delimiters) |
| 11413 | { |
| 11414 | if ( !(field_end = StrChrAny(field, delimiters)) ) // No more delimiters found. |
| 11415 | field_end = field + _tcslen(field); // Set it to the position of the zero terminator instead. |
| 11416 | } |
no test coverage detected