1 /*
2 * CALL.C - call internal batch command.
3 *
4 *
5 * History:
6 *
7 * 16 Jul 1998 (Hans B Pufal)
8 * started.
9 *
10 * 16 Jul 1998 (John P Price)
11 * Separated commands into individual files.
12 *
13 * 27-Jul-1998 (John P Price <linux-guru@gcfl.net>)
14 * added config.h include
15 *
16 * 04-Aug-1998 (Hans B Pufal)
17 * added lines to initialize for pointers (HBP004) This fixed the
18 * lock-up that happened sometimes when calling a batch file from
19 * another batch file.
20 *
21 * 07-Jan-1999 (Eric Kohl)
22 * Added help text ("call /?") and cleaned up.
23 *
24 * 20-Jan-1999 (Eric Kohl)
25 * Unicode and redirection safe!
26 *
27 * 02-Apr-2005 (Magnus Olsen <magnus@greatlord.com>)
28 * Remove all hardcoded strings in En.rc
29 */
30
31 #include "precomp.h"
32
33 /* Enable this define for "buggy" Windows' CMD CALL-command compatibility */
34 #define MSCMD_CALL_QUIRKS
35
36
37 /*
38 * Perform CALL command.
39 */
cmd_call(LPTSTR param)40 INT cmd_call(LPTSTR param)
41 {
42 PARSED_COMMAND* Cmd = NULL;
43 BOOL bOldIgnoreParserComments;
44 #ifndef MSCMD_CALL_QUIRKS
45 BOOL bOldHandleContinuations;
46 #else
47 SIZE_T nNumCarets;
48 #endif
49 PTSTR first;
50
51 TRACE("cmd_call(\'%s\')\n", debugstr_aw(param));
52
53 if (!_tcsncmp(param, _T("/?"), 2))
54 {
55 ConOutResPaging(TRUE, STRING_CALL_HELP);
56 return 0;
57 }
58
59 /* Fail if no command or label has been provided */
60 if (*param == _T('\0'))
61 return (nErrorLevel = 1);
62
63 /* Ignore parser comments (starting with a colon) */
64 bOldIgnoreParserComments = bIgnoreParserComments;
65 bIgnoreParserComments = FALSE;
66
67 #ifndef MSCMD_CALL_QUIRKS
68 /* Disable parsing of escape carets */
69 bOldHandleContinuations = bHandleContinuations;
70 bHandleContinuations = FALSE;
71 first = param;
72 #else
73 /*
74 * As the original escape carets have been dealt with during the first
75 * command parsing step, the remaining ones need to be doubled so that
76 * they can again survive the new parsing step done below.
77 * But do it the Windows' CMD "buggy" way: **all** carets are doubled,
78 * even those inside quotes. However, this causes carets inside quotes
79 * to remain doubled after the parsing step...
80 */
81
82 /* Count all the carets */
83 nNumCarets = 0;
84 first = param;
85 while (first)
86 {
87 first = _tcschr(first, _T('^'));
88 if (first)
89 {
90 ++nNumCarets;
91 ++first;
92 }
93 }
94
95 /* Re-allocate a large enough parameter string if needed */
96 if (nNumCarets > 0)
97 {
98 PTCHAR Src, Dest, End;
99
100 // TODO: Improvement: Use the scratch TempBuf if the string is not too long.
101 first = cmd_alloc((_tcslen(param) + nNumCarets + 1) * sizeof(TCHAR));
102 if (!first)
103 {
104 WARN("Cannot allocate memory for new CALL parameter string!\n");
105 error_out_of_memory();
106 return (nErrorLevel = 1);
107 }
108
109 /* Copy the parameter string and double the escape carets */
110 Src = param;
111 Dest = first;
112 while (*Src)
113 {
114 if (*Src != _T('^'))
115 {
116 /* Copy everything before the next caret (or the end of the string) */
117 End = _tcschr(Src, _T('^'));
118 if (!End)
119 End = Src + _tcslen(Src);
120 memcpy(Dest, Src, (End - Src) * sizeof(TCHAR));
121 Dest += End - Src;
122 Src = End;
123 continue;
124 }
125
126 /* Copy the original caret and double it */
127 *Dest++ = *Src;
128 *Dest++ = *Src++;
129 }
130 *Dest = _T('\0');
131 }
132 else
133 {
134 first = param;
135 }
136 #endif
137
138 /*
139 * Reparse the CALL parameter string as a command.
140 * Note that this will trigger a second round of %-variable substitutions.
141 */
142 Cmd = ParseCommand(first);
143
144 /* Restore the global parsing state */
145 #ifndef MSCMD_CALL_QUIRKS
146 bHandleContinuations = bOldHandleContinuations;
147 #endif
148 bIgnoreParserComments = bOldIgnoreParserComments;
149
150 /*
151 * If no command is there, yet no error occurred, this means that
152 * a whitespace label was given. Do not consider this as a failure.
153 */
154 if (!Cmd && !bParseError)
155 {
156 #ifdef MSCMD_CALL_QUIRKS
157 if (first != param)
158 cmd_free(first);
159 #endif
160 return (nErrorLevel = 0);
161 }
162
163 /* Reset bParseError so as to continue running afterwards */
164 bParseError = FALSE;
165
166 /*
167 * Otherwise, if no command is there because a parse error occurred,
168 * or if this an unsupported command: not a standard one, including
169 * FOR and IF, fail and bail out.
170 */
171 if (!Cmd || (Cmd->Type == C_FOR) || (Cmd->Type == C_IF) ||
172 ((Cmd->Type != C_COMMAND) && (Cmd->Type != C_REM)))
173 {
174 ConErrResPrintf(STRING_ERROR_UNEXPECTED, first);
175
176 #ifdef MSCMD_CALL_QUIRKS
177 if (first != param)
178 cmd_free(first);
179 #endif
180 if (Cmd) FreeCommand(Cmd);
181 return (nErrorLevel = 1);
182 }
183
184 #ifdef MSCMD_CALL_QUIRKS
185 if (first != param)
186 cmd_free(first);
187 #endif
188
189 first = Cmd->Command.First;
190 param = Cmd->Command.Rest;
191
192 /* "CALL :label args ..." - Call a subroutine of the current batch file, only if extensions are enabled */
193 if (bEnableExtensions && (*first == _T(':')))
194 {
195 INT ret;
196
197 /* A batch context must be present */
198 if (!bc)
199 {
200 ConErrResPuts(STRING_ERROR_CALL_BAD_LABEL);
201 FreeCommand(Cmd);
202 return (nErrorLevel = 1);
203 }
204
205 ret = Batch(bc->BatchFilePath, first, param, NULL);
206 nErrorLevel = (ret != 0 ? ret : nErrorLevel);
207 }
208 else
209 {
210 nErrorLevel = DoCommand(first, param, NULL);
211 }
212
213 FreeCommand(Cmd);
214 return nErrorLevel;
215 }
216
217 /* EOF */
218