xref: /reactos/base/shell/cmd/goto.c (revision 37bda06e)
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