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 */
cmd_goto(LPTSTR param)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