1 /* 2 * GOTO.C - goto internal batch command. 3 * 4 * History: 5 * 6 * 16 Jul 1998 (Hans B Pufal) 7 * started. 8 * 9 * 16 Jul 1998 (John P Price) 10 * Separated commands into individual files. 11 * 12 * 27-Jul-1998 (John P Price <linux-guru@gcfl.net>) 13 * added config.h include 14 * 15 * 28 Jul 1998 (Hans B Pufal) [HBP_003] 16 * Terminate label on first space character, use only first 8 chars of 17 * label string 18 * 19 * 24-Jan-1999 (Eric Kohl) 20 * Unicode and redirection safe! 21 * 22 * 27-Jan-1999 (Eric Kohl) 23 * Added help text ("/?"). 24 * 25 * 28-Apr-2005 (Magnus Olsen <magnus@greatlord.com>) 26 * Remove all hardcoded strings in En.rc 27 */ 28 29 #include "precomp.h" 30 31 /* 32 * Perform GOTO command. 33 * 34 * Only valid when a batch context is active. 35 */ 36 INT cmd_goto(LPTSTR param) 37 { 38 LPTSTR label, tmp; 39 DWORD dwCurrPos; 40 BOOL bRetry; 41 42 TRACE("cmd_goto(\'%s\')\n", debugstr_aw(param)); 43 44 /* 45 * Keep the help message handling here too. 46 * This allows us to reproduce the Windows' CMD "bug" 47 * (from a batch script): 48 * 49 * SET label=/? 50 * CALL :%%label%% 51 * 52 * calls GOTO help, due to how CALL :label functionality 53 * is internally implemented. 54 * 55 * See https://stackoverflow.com/q/31987023/13530036 56 * for more details. 57 * 58 * Note that the choice of help parsing forbids 59 * any label containing '/?' in it. 60 */ 61 if (_tcsstr(param, _T("/?"))) 62 { 63 ConOutResPaging(TRUE, STRING_GOTO_HELP1); 64 return 0; 65 } 66 67 /* If not in batch, fail */ 68 if (bc == NULL) 69 return 1; 70 71 /* Fail if no label has been provided */ 72 if (*param == _T('\0')) 73 { 74 ConErrResPrintf(STRING_GOTO_ERROR1); 75 ExitBatch(); 76 return 1; 77 } 78 79 /* Strip leading whitespace */ 80 while (_istspace(*param)) 81 ++param; 82 83 /* Support jumping to the end of the file, only if extensions are enabled */ 84 if (bEnableExtensions && 85 (_tcsnicmp(param, _T(":EOF"), 4) == 0) && 86 (!param[4] || _istspace(param[4]))) 87 { 88 /* Position at the end of the batch file */ 89 bc->mempos = bc->memsize; 90 91 /* Do not process any more parts of a compound command */ 92 bc->current = NULL; 93 return 0; 94 } 95 96 /* Skip the first colon or plus sign */ 97 if (*param == _T(':') || *param == _T('+')) 98 ++param; 99 /* Terminate the label at the first delimiter character */ 100 tmp = param; 101 while (!_istcntrl(*tmp) && !_istspace(*tmp) && 102 !_tcschr(_T(":+"), *tmp) && !_tcschr(STANDARD_SEPS, *tmp) /* && 103 !_tcschr(_T("&|<>"), *tmp) */) 104 { 105 ++tmp; 106 } 107 *tmp = _T('\0'); 108 109 /* If we don't have any label, bail out */ 110 if (!*param) 111 goto NotFound; 112 113 /* 114 * Search the next label starting our position, until the end of the file. 115 * If none has been found, restart at the beginning of the file, and continue 116 * until reaching back our old current position. 117 */ 118 bRetry = FALSE; 119 dwCurrPos = bc->mempos; 120 121 retry: 122 while (BatchGetString(textline, ARRAYSIZE(textline))) 123 { 124 if (bRetry && (bc->mempos >= dwCurrPos)) 125 break; 126 127 #if 0 128 /* If this is not a label, continue searching */ 129 if (!_tcschr(textline, _T(':'))) 130 continue; 131 #endif 132 133 label = textline; 134 135 /* A bug in Windows' CMD makes it always ignore the 136 * first character of the line, unless it's a colon. */ 137 if (*label != _T(':')) 138 ++label; 139 140 /* Strip any leading whitespace */ 141 while (_istspace(*label)) 142 ++label; 143 144 /* If this is not a label, continue searching */ 145 if (*label != _T(':')) 146 continue; 147 148 /* Skip the first colon or plus sign */ 149 #if 0 150 if (*label == _T(':') || *label == _T('+')) 151 ++label; 152 #endif 153 ++label; 154 /* Strip any whitespace between the colon and the label */ 155 while (_istspace(*label)) 156 ++label; 157 /* Terminate the label at the first delimiter character */ 158 tmp = label; 159 while (!_istcntrl(*tmp) && !_istspace(*tmp) && 160 !_tcschr(_T(":+"), *tmp) && !_tcschr(STANDARD_SEPS, *tmp) && 161 !_tcschr(_T("&|<>"), *tmp)) 162 { 163 /* Support the escape caret */ 164 if (*tmp == _T('^')) 165 { 166 /* Move the buffer back one character */ 167 memmove(tmp, tmp + 1, (_tcslen(tmp + 1) + 1) * sizeof(TCHAR)); 168 /* We will ignore the new character */ 169 } 170 171 ++tmp; 172 } 173 *tmp = _T('\0'); 174 175 /* Jump if the labels are identical */ 176 if (_tcsicmp(label, param) == 0) 177 { 178 /* Do not process any more parts of a compound command */ 179 bc->current = NULL; 180 return 0; 181 } 182 } 183 if (!bRetry && (bc->mempos >= bc->memsize)) 184 { 185 bRetry = TRUE; 186 bc->mempos = 0; 187 goto retry; 188 } 189 190 NotFound: 191 ConErrResPrintf(STRING_GOTO_ERROR2, param); 192 ExitBatch(); 193 return 1; 194 } 195 196 /* EOF */ 197