ReactOS Fundraising Campaign 2012
 
€ 4,410 / € 30,000

Information | Donate

Home | Info | Community | Development | myReactOS | Contact Us

  1. Home
  2. Community
  3. Development
  4. myReactOS
  5. Fundraiser 2012

  1. Main Page
  2. Alphabetical List
  3. Data Structures
  4. Directories
  5. File List
  6. Data Fields
  7. Globals
  8. Related Pages

ReactOS Development > Doxygen

batch.c
Go to the documentation of this file.
00001 /*
00002  *  BATCH.C - batch file processor for CMD.EXE.
00003  *
00004  *
00005  *  History:
00006  *
00007  *    ??/??/?? (Evan Jeffrey)
00008  *        started.
00009  *
00010  *    15 Jul 1995 (Tim Norman)
00011  *        modes and bugfixes.
00012  *
00013  *    08 Aug 1995 (Matt Rains)
00014  *        i have cleaned up the source code. changes now bring this
00015  *        source into guidelines for recommended programming practice.
00016  *
00017  *        i have added some constants to help making changes easier.
00018  *
00019  *    29 Jan 1996 (Steffan Kaiser)
00020  *        made a few cosmetic changes
00021  *
00022  *    05 Feb 1996 (Tim Norman)
00023  *        changed to comply with new first/rest calling scheme
00024  *
00025  *    14 Jun 1997 (Steffen Kaiser)
00026  *        bug fixes.  added error level expansion %?.  ctrl-break handling
00027  *
00028  *    16 Jul 1998 (Hans B Pufal)
00029  *        Totally reorganised in conjunction with COMMAND.C (cf) to
00030  *        implement proper BATCH file nesting and other improvements.
00031  *
00032  *    16 Jul 1998 (John P Price <linux-guru@gcfl.net>)
00033  *        Seperated commands into individual files.
00034  *
00035  *    19 Jul 1998 (Hans B Pufal) [HBP_001]
00036  *        Preserve state of echo flag across batch calls.
00037  *
00038  *    19 Jul 1998 (Hans B Pufal) [HBP_002]
00039  *        Implementation of FOR command
00040  *
00041  *    20-Jul-1998 (John P Price <linux-guru@gcfl.net>)
00042  *        added error checking after cmd_alloc calls
00043  *
00044  *    27-Jul-1998 (John P Price <linux-guru@gcfl.net>)
00045  *        added config.h include
00046  *
00047  *    02-Aug-1998 (Hans B Pufal) [HBP_003]
00048  *        Fixed bug in ECHO flag restoration at exit from batch file
00049  *
00050  *    26-Jan-1999 Eric Kohl
00051  *        Replaced CRT io functions by Win32 io functions.
00052  *        Unicode safe!
00053  *
00054  *    23-Feb-2001 (Carl Nettelblad <cnettel@hem.passagen.es>)
00055  *        Fixes made to get "for" working.
00056  *
00057  *    02-Apr-2005 (Magnus Olsen) <magnus@greatlord.com>)
00058  *        Remove all hardcode string to En.rc
00059  */
00060 
00061 #include <precomp.h>
00062 
00063 
00064 /* The stack of current batch contexts.
00065  * NULL when no batch is active
00066  */
00067 LPBATCH_CONTEXT bc = NULL;
00068 
00069 BOOL bEcho = TRUE;          /* The echo flag */
00070 
00071 
00072 
00073 /* Buffer for reading Batch file lines */
00074 TCHAR textline[BATCH_BUFFSIZE];
00075 
00076 
00077 /*
00078  * Returns a pointer to the n'th parameter of the current batch file.
00079  * If no such parameter exists returns pointer to empty string.
00080  * If no batch file is current, returns NULL
00081  *
00082  */
00083 
00084 LPTSTR FindArg(TCHAR Char, BOOL *IsParam0)
00085 {
00086     LPTSTR pp;
00087     INT n = Char - _T('0');
00088 
00089     TRACE ("FindArg: (%d)\n", n);
00090 
00091     if (n < 0 || n > 9)
00092         return NULL;
00093 
00094     n = bc->shiftlevel[n];
00095     *IsParam0 = (n == 0);
00096     pp = bc->params;
00097 
00098     /* Step up the strings till we reach the end */
00099     /* or the one we want */
00100     while (*pp && n--)
00101         pp += _tcslen (pp) + 1;
00102 
00103     return pp;
00104 }
00105 
00106 
00107 /*
00108  * Batch_params builds a parameter list in newlay allocated memory.
00109  * The parameters consist of null terminated strings with a final
00110  * NULL character signalling the end of the parameters.
00111  *
00112 */
00113 
00114 LPTSTR BatchParams (LPTSTR s1, LPTSTR s2)
00115 {
00116     LPTSTR dp = (LPTSTR)cmd_alloc ((_tcslen(s1) + _tcslen(s2) + 3) * sizeof (TCHAR));
00117 
00118     /* JPP 20-Jul-1998 added error checking */
00119     if (dp == NULL)
00120     {
00121         error_out_of_memory();
00122         return NULL;
00123     }
00124 
00125     if (s1 && *s1)
00126     {
00127         s1 = _stpcpy (dp, s1);
00128         *s1++ = _T('\0');
00129     }
00130     else
00131         s1 = dp;
00132 
00133     while (*s2)
00134     {
00135         BOOL inquotes = FALSE;
00136 
00137         /* Find next parameter */
00138         while (_istspace(*s2) || (*s2 && _tcschr(_T(",;="), *s2)))
00139             s2++;
00140         if (!*s2)
00141             break;
00142 
00143         /* Copy it */
00144         do
00145         {
00146             if (!inquotes && (_istspace(*s2) || _tcschr(_T(",;="), *s2)))
00147                 break;
00148             inquotes ^= (*s2 == _T('"'));
00149             *s1++ = *s2++;
00150         } while (*s2);
00151         *s1++ = _T('\0');
00152     }
00153 
00154     *s1 = _T('\0');
00155 
00156     return dp;
00157 }
00158 
00159 /*
00160  * free the allocated memory of a batch file
00161  */
00162 VOID ClearBatch()
00163 {
00164     TRACE ("ClearBatch  mem = %08x    free = %d\n", bc->mem, bc->memfree);
00165 
00166     if (bc->mem && bc->memfree)
00167         cmd_free(bc->mem);
00168 
00169     if (bc->raw_params)
00170         cmd_free(bc->raw_params);
00171 
00172     if (bc->params)
00173         cmd_free(bc->params);
00174 }
00175 
00176 /*
00177  * If a batch file is current, exits it, freeing the context block and
00178  * chaining back to the previous one.
00179  *
00180  * If no new batch context is found, sets ECHO back ON.
00181  *
00182  * If the parameter is non-null or not empty, it is printed as an exit
00183  * message
00184  */
00185 
00186 VOID ExitBatch()
00187 {
00188     ClearBatch();
00189 
00190     TRACE ("ExitBatch\n");
00191 
00192     UndoRedirection(bc->RedirList, NULL);
00193     FreeRedirection(bc->RedirList);
00194 
00195     /* Preserve echo state across batch calls */
00196     bEcho = bc->bEcho;
00197 
00198     while (bc->setlocal)
00199         cmd_endlocal(_T(""));
00200 
00201     bc = bc->prev;
00202 }
00203 
00204 /*
00205  * Load batch file into memory
00206  *
00207  */
00208 void BatchFile2Mem(HANDLE hBatchFile)
00209 {
00210     TRACE ("BatchFile2Mem ()\n");
00211 
00212     bc->memsize = GetFileSize(hBatchFile, NULL);
00213     bc->mem     = (char *)cmd_alloc(bc->memsize+1);     /* 1 extra for '\0' */
00214     
00215     /* if memory is available, read it in and close the file */
00216     if (bc->mem != NULL) 
00217     {
00218         TRACE ("BatchFile2Mem memory %08x - %08x\n",bc->mem,bc->memsize);
00219         SetFilePointer (hBatchFile, 0, NULL, FILE_BEGIN);
00220         ReadFile(hBatchFile, (LPVOID)bc->mem, bc->memsize,  &bc->memsize, NULL);
00221         bc->mem[bc->memsize]='\0';      /* end this, so you can dump it as a string */
00222         bc->memfree=TRUE;               /* this one needs to be freed */
00223     } 
00224     else 
00225     {
00226         bc->memsize=0;                  /* this will prevent mem being accessed */
00227         bc->memfree=FALSE;
00228     }
00229     bc->mempos = 0;                     /* set position to the start */
00230 }
00231 
00232 /*
00233  * Start batch file execution
00234  *
00235  * The firstword parameter is the full filename of the batch file.
00236  *
00237  */
00238 
00239 INT Batch (LPTSTR fullname, LPTSTR firstword, LPTSTR param, PARSED_COMMAND *Cmd)
00240 {
00241     BATCH_CONTEXT new;
00242     LPFOR_CONTEXT saved_fc;
00243     INT i;
00244     INT ret = 0;
00245     BOOL same_fn = FALSE;   
00246 
00247     HANDLE hFile = 0;
00248     SetLastError(0);
00249     if (bc && bc->mem) 
00250     {
00251         TCHAR     fpname[MAX_PATH];
00252         GetFullPathName(fullname, sizeof(fpname) / sizeof(TCHAR), fpname, NULL);
00253         if (_tcsicmp(bc->BatchFilePath,fpname)==0) 
00254             same_fn=TRUE;
00255     }
00256     TRACE ("Batch: (\'%s\', \'%s\', \'%s\')  same_fn = %d\n",
00257         debugstr_aw(fullname), debugstr_aw(firstword), debugstr_aw(param), same_fn);
00258 
00259     if (!same_fn)
00260     {
00261         hFile = CreateFile (fullname, GENERIC_READ, FILE_SHARE_WRITE | FILE_SHARE_READ | FILE_SHARE_DELETE, NULL,
00262                 OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL |
00263                 FILE_FLAG_SEQUENTIAL_SCAN, NULL);
00264 
00265         if (hFile == INVALID_HANDLE_VALUE)
00266         {
00267             ConErrResPuts(STRING_BATCH_ERROR);
00268             return 1;
00269         }
00270     }
00271     
00272     if (bc != NULL && Cmd == bc->current)
00273     {
00274         /* Then we are transferring to another batch */
00275         ClearBatch();
00276         AddBatchRedirection(&Cmd->Redirections);
00277     }
00278     else
00279     {
00280         struct _SETLOCAL *setlocal = NULL;
00281 
00282         if (Cmd == NULL)
00283         {
00284             /* This is a CALL. CALL will set errorlevel to our return value, so
00285              * in order to keep the value of errorlevel unchanged in the case
00286              * of calling an empty batch file, we must return that same value. */
00287             ret = nErrorLevel;
00288         }
00289         else if (bc)
00290         {
00291             /* If a batch file runs another batch file as part of a compound command
00292              * (e.g. "x.bat & somethingelse") then the first file gets terminated. */
00293 
00294             /* Get its SETLOCAL stack so it can be migrated to the new context */
00295             setlocal = bc->setlocal;
00296             bc->setlocal = NULL;
00297             ExitBatch();
00298         }
00299 
00300         /* Create a new context. This function will not
00301          * return until this context has been exited */
00302         new.prev = bc;
00303         /* copy some fields in the new structure if it is the same file */
00304         if (same_fn) {
00305             new.mem     = bc->mem;
00306             new.memsize = bc->memsize;
00307             new.mempos  = 0;
00308             new.memfree = FALSE;            /* don't free this, being used before this */
00309         }
00310         bc = &new;
00311         bc->RedirList = NULL;
00312         bc->setlocal = setlocal;
00313     }
00314 
00315     GetFullPathName(fullname, sizeof(bc->BatchFilePath) / sizeof(TCHAR), bc->BatchFilePath, NULL);
00316     /*  if a new batch file, load it into memory and close the file */
00317     if (!same_fn) 
00318     {
00319         BatchFile2Mem(hFile);
00320         CloseHandle(hFile);
00321     }
00322     
00323     bc->mempos = 0;    /* goto begin of batch file */
00324     bc->bEcho = bEcho; /* Preserve echo across batch calls */
00325     for (i = 0; i < 10; i++)
00326         bc->shiftlevel[i] = i;
00327     
00328     bc->params = BatchParams (firstword, param);
00329     //
00330     // Allocate enough memory to hold the params and copy them over without modifications
00331     //
00332     bc->raw_params = cmd_dup(param);
00333     if (bc->raw_params == NULL)
00334     {
00335         error_out_of_memory();
00336         return 1;
00337     }
00338 
00339     /* Check if this is a "CALL :label" */
00340     if (*firstword == _T(':'))
00341         cmd_goto(firstword);
00342 
00343     /* If we are calling from inside a FOR, hide the FOR variables */
00344     saved_fc = fc;
00345     fc = NULL;
00346 
00347     /* If we have created a new context, don't return
00348      * until this batch file has completed. */
00349     while (bc == &new && !bExit)
00350     {
00351         Cmd = ParseCommand(NULL);
00352         if (!Cmd)
00353             continue;
00354 
00355         /* JPP 19980807 */
00356         /* Echo batch file line */
00357         if (bEcho && !bDisableBatchEcho && Cmd->Type != C_QUIET)
00358         {
00359             if (!bIgnoreEcho)
00360                 ConOutChar(_T('\n'));
00361             PrintPrompt();
00362             EchoCommand(Cmd);
00363             ConOutChar(_T('\n'));
00364         }
00365 
00366         bc->current = Cmd;
00367         ret = ExecuteCommand(Cmd);
00368         FreeCommand(Cmd);
00369     }
00370 
00371     TRACE ("Batch: returns TRUE\n");
00372 
00373     fc = saved_fc;
00374     return ret;
00375 }
00376 
00377 VOID AddBatchRedirection(REDIRECTION **RedirList)
00378 {
00379     REDIRECTION **ListEnd;
00380 
00381     /* Prepend the list to the batch context's list */
00382     ListEnd = RedirList;
00383     while (*ListEnd)
00384         ListEnd = &(*ListEnd)->Next;
00385     *ListEnd = bc->RedirList;
00386     bc->RedirList = *RedirList;
00387 
00388     /* Null out the pointer so that the list will not be cleared prematurely.
00389      * These redirections should persist until the batch file exits. */
00390     *RedirList = NULL;
00391 }
00392 
00393 /*
00394  *   Read a single line from the batch file from the current batch/memory position.
00395  *   Almost a copy of FileGetString with same UNICODE handling
00396  */
00397 BOOL BatchGetString (LPTSTR lpBuffer, INT nBufferLength)
00398 {
00399     LPSTR lpString;
00400     INT len = 0;
00401 #ifdef _UNICODE
00402     lpString = cmd_alloc(nBufferLength);
00403 #else
00404     lpString = lpBuffer;
00405 #endif
00406     /* read all chars from memory until a '\n' is encountered */
00407     if (bc->mem) 
00408     {
00409         for (; (bc->mempos < bc->memsize  &&  len < (nBufferLength-1)); len++) 
00410         {  
00411             lpString[len] = bc->mem[bc->mempos++];
00412             if (lpString[len] == '\n' ) 
00413             {
00414                 len++;
00415                 break;
00416             }
00417         }
00418     }
00419 
00420     if (!len)
00421     {
00422 #ifdef _UNICODE
00423         cmd_free(lpString);
00424 #endif
00425         return FALSE;
00426     }
00427 
00428     lpString[len++] = '\0';
00429 #ifdef _UNICODE
00430     MultiByteToWideChar(OutputCodePage, 0, lpString, -1, lpBuffer, len);
00431     cmd_free(lpString);
00432 #endif
00433     return TRUE;
00434 }
00435 
00436 /*
00437  * Read and return the next executable line form the current batch file
00438  *
00439  * If no batch file is current or no further executable lines are found
00440  * return NULL.
00441  *
00442  * Set eflag to 0 if line is not to be echoed else 1
00443  */
00444 LPTSTR ReadBatchLine ()
00445 {
00446     TRACE ("ReadBatchLine ()\n");
00447 
00448     /* User halt */
00449     if (CheckCtrlBreak (BREAK_BATCHFILE))
00450     {
00451         while (bc)
00452             ExitBatch();
00453         return NULL;
00454     }
00455 
00456     if (!BatchGetString (textline, sizeof (textline) / sizeof (textline[0]) - 1))
00457     {
00458         TRACE ("ReadBatchLine(): Reached EOF!\n");
00459         /* End of file.... */
00460         ExitBatch();
00461         return NULL;
00462     }
00463 
00464     TRACE ("ReadBatchLine(): textline: \'%s\'\n", debugstr_aw(textline));
00465 
00466     if (textline[_tcslen(textline) - 1] != _T('\n'))
00467         _tcscat(textline, _T("\n"));
00468 
00469     return textline;
00470 }
00471 
00472 /* EOF */

Generated on Sun May 27 2012 04:18:10 for ReactOS by doxygen 1.7.6.1

ReactOS is a registered trademark or a trademark of ReactOS Foundation in the United States and other countries.