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 */ 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