Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

470 lines
13 KiB

  1. //+---------------------------------------------------------------------------
  2. //
  3. // Microsoft Windows
  4. // Copyright (C) Microsoft Corporation, 1994 - 1996.
  5. //
  6. // File: atsign.cxx
  7. //
  8. // Contents: Functions to read commands from a script file.
  9. //
  10. // History: 04-20-95 davidmun Created
  11. //
  12. //----------------------------------------------------------------------------
  13. #include <headers.hxx>
  14. #pragma hdrstop
  15. #include "jt.hxx"
  16. //
  17. // Forward references
  18. //
  19. ULONG FindEol(CHAR *pstr, ULONG cchRemaining);
  20. CHAR FindNextNonSpace(CHAR *pstr, ULONG cchRemaining);
  21. //+---------------------------------------------------------------------------
  22. //
  23. // Function: DoAtSign
  24. //
  25. // Synopsis: Get filename from commandline and process it.
  26. //
  27. // Arguments: [ppwsz] - command line
  28. //
  29. // Returns: S_OK - file processed without error
  30. // E_* - error logged
  31. //
  32. // Modifies: *[ppwsz]
  33. //
  34. // History: 04-20-95 davidmun Created
  35. // 01-03-96 DavidMun Support multi-line commands
  36. //
  37. // Notes: This routine may be indirectly recursive.
  38. //
  39. //----------------------------------------------------------------------------
  40. HRESULT DoAtSign(WCHAR **ppwsz)
  41. {
  42. HRESULT hr = S_OK;
  43. ShHANDLE shFile;
  44. ShHANDLE shFileMapping;
  45. VOID *pvBase = NULL;
  46. BOOL fOk;
  47. ULONG cchFile;
  48. TCHAR tszFilename[MAX_PATH + 1] = TEXT("None");
  49. g_Log.Write(LOG_DEBUG, "DoAtSign");
  50. do
  51. {
  52. hr = GetFilename(ppwsz, L"command file name");
  53. BREAK_ON_FAILURE(hr);
  54. #ifdef UNICODE
  55. wcscpy(tszFilename, g_wszLastStringToken);
  56. #else
  57. wcstombs(tszFilename, g_wszLastStringToken, wcslen(g_wszLastStringToken)+1);
  58. #endif
  59. //
  60. // Open the command file, then hand one line at a time to
  61. // ProcessCommandLine (which is our caller, so we're recursing).
  62. //
  63. shFile = CreateFile(
  64. tszFilename,
  65. GENERIC_READ,
  66. FILE_SHARE_READ,
  67. NULL, // default security
  68. OPEN_EXISTING,
  69. FILE_ATTRIBUTE_NORMAL,
  70. NULL); // no template
  71. if (shFile == INVALID_HANDLE_VALUE)
  72. {
  73. hr = E_FAIL;
  74. g_Log.Write(LOG_ERROR, "Can't open file %S", tszFilename);
  75. break;
  76. }
  77. shFileMapping = CreateFileMapping(
  78. shFile,
  79. NULL, // default security
  80. PAGE_READONLY,
  81. 0, 0, // max size is size of file
  82. NULL); // unnamed object
  83. if (shFileMapping == NULL)
  84. {
  85. hr = E_FAIL;
  86. g_Log.Write(LOG_ERROR, "CreateFileMapping (%u)", GetLastError());
  87. break;
  88. }
  89. pvBase = MapViewOfFile(
  90. shFileMapping,
  91. FILE_MAP_READ,
  92. 0, 0, // start mapping at beginning of file
  93. 0); // map entire file
  94. if (!pvBase)
  95. {
  96. hr = E_FAIL;
  97. g_Log.Write(LOG_ERROR, "MapViewOfFile (%u)", GetLastError());
  98. break;
  99. }
  100. //
  101. // pvBase points to start of mapped file. Get the file length.
  102. //
  103. BY_HANDLE_FILE_INFORMATION bhfi;
  104. fOk = GetFileInformationByHandle(shFile, &bhfi);
  105. if (!fOk)
  106. {
  107. hr = E_FAIL;
  108. g_Log.Write(LOG_ERROR, "GetFileInformationByHandle (%u)", GetLastError());
  109. break;
  110. }
  111. cchFile = bhfi.nFileSizeLow;
  112. if (bhfi.nFileSizeHigh)
  113. {
  114. hr = E_FAIL;
  115. g_Log.Write(LOG_ERROR, "File too large");
  116. break;
  117. }
  118. }
  119. while (0);
  120. //
  121. // Finally ready to process file.
  122. //
  123. ULONG cchProcessed = 0;
  124. ULONG ulCurLine = 1;
  125. CHAR *pstrFile = (CHAR *) pvBase;
  126. while (SUCCEEDED(hr) && cchProcessed < cchFile)
  127. {
  128. CHAR szLine[MAX_TOKEN_LEN + 1] = "";
  129. ULONG cchLine = 0;
  130. WCHAR wszLine[MAX_TOKEN_LEN + 1];
  131. WCHAR wszExpandedLine[MAX_TOKEN_LEN + 1];
  132. BOOL fInQuote = FALSE;
  133. BOOL fFoundNextCommand = FALSE;
  134. //
  135. // Copy all chars of the next command from pstrfile into wszline,
  136. // condensing whitespace into single blanks and skipping comment
  137. // lines.
  138. //
  139. for (;
  140. cchProcessed < cchFile &&
  141. !fFoundNextCommand &&
  142. cchLine < MAX_TOKEN_LEN;
  143. cchProcessed++)
  144. {
  145. //
  146. // If we're in a quoted string, copy everything verbatim until
  147. // closing quote
  148. //
  149. if (fInQuote)
  150. {
  151. if (pstrFile[cchProcessed] == '"')
  152. {
  153. fInQuote = FALSE;
  154. }
  155. //
  156. // Check for error case of newline in string
  157. //
  158. if (pstrFile[cchProcessed] == '\n')
  159. {
  160. hr = E_FAIL;
  161. g_Log.Write(LOG_FAIL, "Newline in string constant");
  162. break;
  163. }
  164. szLine[cchLine++] = pstrFile[cchProcessed];
  165. continue;
  166. }
  167. //
  168. // Not already in a quoted string. See if we're entering one.
  169. //
  170. if (pstrFile[cchProcessed] == '"')
  171. {
  172. fInQuote = TRUE;
  173. szLine[cchLine++] = pstrFile[cchProcessed];
  174. continue;
  175. }
  176. //
  177. // Not in or starting a quoted string, so we're free to condense
  178. // whitespace (including newlines) and ignore comment lines
  179. //
  180. if (isspace(pstrFile[cchProcessed]))
  181. {
  182. //
  183. // Only copy this space char if none has been copied yet.
  184. // Bump the line count if the whitespace char we're skipping
  185. // is a newline.
  186. //
  187. if (cchLine && szLine[cchLine - 1] != ' ')
  188. {
  189. szLine[cchLine++] = ' ';
  190. }
  191. if (pstrFile[cchProcessed] == '\n')
  192. {
  193. ulCurLine++;
  194. }
  195. continue;
  196. }
  197. if (pstrFile[cchProcessed] == ';')
  198. {
  199. //
  200. // Skip to end of line
  201. //
  202. cchProcessed += FindEol(
  203. &pstrFile[cchProcessed],
  204. cchFile - cchProcessed);
  205. // subtract 1 because for loop is going to add one
  206. cchProcessed--;
  207. continue;
  208. }
  209. //
  210. // Next char is not quote, semicolon (start of comment), or
  211. // whitespace. If we haven't copied anything yet, copy that char
  212. // as the first of the line.
  213. //
  214. if (!cchLine)
  215. {
  216. szLine[cchLine++] = pstrFile[cchProcessed];
  217. continue;
  218. }
  219. //
  220. // Since we've already started copying stuff, we want to quit when
  221. // we get to the start of the next command, i.e., when we see a
  222. // switch char '/' or '-'.
  223. //
  224. // Unfortunately these two characters also delimit the parts of a
  225. // date, and we don't want to stop copying the line because of a
  226. // date.
  227. //
  228. // Therefore we'll only stop copying if the next non whitespace
  229. // character is not a number. This imposes the constraints that
  230. // no commands can be a number (i.e. /10 cannot be a valid
  231. // command) and that dates must use only digits (i.e. 10-Feb is
  232. // not valid because we'll think -Feb is a command and only copy
  233. // the 10).
  234. //
  235. // Note it isn't safe to check that the *previous* character was a
  236. // digit and assume we're in a date, since a command with a
  237. // numeric argument could be mistaken for a date.
  238. //
  239. // /foo bar=10 /baz
  240. //
  241. if (pstrFile[cchProcessed] == '/' ||
  242. pstrFile[cchProcessed] == '-')
  243. {
  244. CHAR ch;
  245. ch = FindNextNonSpace(
  246. &pstrFile[cchProcessed + 1],
  247. cchFile - (cchProcessed + 1));
  248. if (isdigit(ch))
  249. {
  250. szLine[cchLine++] = pstrFile[cchProcessed];
  251. }
  252. else
  253. {
  254. fFoundNextCommand = TRUE;
  255. cchProcessed--; // because for loop will increment it
  256. }
  257. }
  258. else
  259. {
  260. szLine[cchLine++] = pstrFile[cchProcessed];
  261. }
  262. }
  263. BREAK_ON_FAILURE(hr);
  264. //
  265. // If we stopped copying not because we found the next command or hit
  266. // the end of the file, then it's because we ran out of room in
  267. // szLine, which is an error.
  268. //
  269. if (!fFoundNextCommand && cchProcessed < cchFile)
  270. {
  271. hr = E_FAIL;
  272. g_Log.Write(
  273. LOG_ERROR,
  274. "Line %u is longer than %u chars",
  275. ulCurLine,
  276. MAX_TOKEN_LEN);
  277. break;
  278. }
  279. #ifdef UNICODE
  280. //
  281. // Convert line to wchar and null terminate it.
  282. //
  283. mbstowcs(wszLine, szLine, cchLine);
  284. wszLine[cchLine] = L'\0';
  285. //
  286. // Expand environment variables
  287. //
  288. ULONG cchRequired;
  289. cchRequired = ExpandEnvironmentStrings(
  290. wszLine,
  291. wszExpandedLine,
  292. MAX_TOKEN_LEN+1);
  293. #else
  294. CHAR szExpandedLine[MAX_TOKEN_LEN + 1];
  295. ULONG cchRequired;
  296. szLine[cchLine] = '\0';
  297. cchRequired = ExpandEnvironmentStrings(
  298. szLine,
  299. szExpandedLine,
  300. MAX_TOKEN_LEN+1);
  301. mbstowcs(wszExpandedLine, szExpandedLine, MAX_TOKEN_LEN + 1);
  302. #endif
  303. if (!cchRequired || cchRequired > MAX_TOKEN_LEN + 1)
  304. {
  305. hr = E_FAIL;
  306. g_Log.Write(LOG_FAIL, "ExpandEnvironmentStrings failed");
  307. break;
  308. }
  309. //
  310. // Perform the command in wszExpandedLine, then loop around and
  311. // read the next command.
  312. //
  313. if (SUCCEEDED(hr))
  314. {
  315. g_Log.Write(LOG_DEBUG, "DoAtSign: processing '%S'", wszExpandedLine);
  316. hr = ProcessCommandLine(wszExpandedLine);
  317. }
  318. }
  319. if (FAILED(hr))
  320. {
  321. g_Log.Write(LOG_ERROR, "File: %S Line: %u", tszFilename, ulCurLine);
  322. }
  323. if (pvBase)
  324. {
  325. fOk = UnmapViewOfFile(pvBase);
  326. if (!fOk)
  327. {
  328. hr = E_FAIL;
  329. g_Log.Write(LOG_ERROR, "UnmapViewOfFile (%u)", GetLastError());
  330. }
  331. }
  332. return hr;
  333. }
  334. //+---------------------------------------------------------------------------
  335. //
  336. // Function: FindEol
  337. //
  338. // Synopsis: Return the number of characters between [pstr] and the end
  339. // of the line.
  340. //
  341. // Arguments: [pstr] - non-terminated string
  342. // [cchRemaining] - characters till eof
  343. //
  344. // Returns: Character count
  345. //
  346. // History: 04-20-95 davidmun Created
  347. //
  348. //----------------------------------------------------------------------------
  349. ULONG FindEol(CHAR *pstr, ULONG cchRemaining)
  350. {
  351. CHAR *pstrCur;
  352. ULONG cchCur;
  353. for (pstrCur = pstr, cchCur = 0;
  354. cchCur < cchRemaining;
  355. cchCur++, pstrCur++)
  356. {
  357. if (*pstrCur == '\r' || *pstrCur == '\n')
  358. {
  359. return cchCur;
  360. }
  361. }
  362. return cchCur;
  363. }
  364. //+---------------------------------------------------------------------------
  365. //
  366. // Function: FindNextNonSpace
  367. //
  368. // Synopsis: Return the next non-whitespace character in [pstr], or space
  369. // if there are no non-whitespace characters in the next
  370. // [cchRemaining] characters.
  371. //
  372. // Arguments: [pstr] - non-terminated string
  373. // [cchRemaining] - number of characters in string
  374. //
  375. // Returns: Character as described.
  376. //
  377. // History: 01-10-96 DavidMun Created
  378. //
  379. //----------------------------------------------------------------------------
  380. CHAR FindNextNonSpace(CHAR *pstr, ULONG cchRemaining)
  381. {
  382. CHAR *pstrCur;
  383. ULONG cchCur;
  384. for (pstrCur = pstr, cchCur = 0;
  385. cchCur < cchRemaining;
  386. cchCur++, pstrCur++)
  387. {
  388. if (!isspace(*pstrCur))
  389. {
  390. return *pstrCur;
  391. }
  392. }
  393. return ' ';
  394. }