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.

341 lines
12 KiB

  1. // COMMAND.C - NMAKE 'command line' handling routines
  2. //
  3. // Copyright (c) 1988-1990, Microsoft Corporation. All rights reserved.
  4. //
  5. // Purpose:
  6. // Module contains routines to handle NMAKE 'command line' syntax. NMAKE can be
  7. // optionally called by using the syntax 'NMAKE @commandfile'. This allows more
  8. // flexibility and preents a way of getting around DOS's 128-byte limit on the
  9. // length of a command line. Additionally, it saves keystrokes for frequently
  10. // run commands for NMAKE.
  11. //
  12. // Revision History:
  13. // 04-Feb-2000 BTF Ported to Win64
  14. // 15-Nov-1993 JR Major speed improvements
  15. // 15-Oct-1993 HV Use tchar.h instead of mbstring.h directly, change STR*() to _ftcs*()
  16. // 10-May-1993 HV Add include file mbstring.h
  17. // Change the str* functions to STR*
  18. // 14-Aug-1992 SS CAVIAR 2735: handle quoted macro values in command files
  19. // 02-Feb-1990 SB Replace fopen() by FILEOPEN
  20. // 01-Dec-1989 SB Changed realloc() to REALLOC()
  21. // 22-Nov-1989 SB Changed free() to FREE()
  22. // 17-Aug-1989 SB Add error check to closing file
  23. // 05-Apr-1989 SB made func calls NEAR to put all funcs into 1 module
  24. // 20-Oct-1988 SB Notes added to readCommandFile()
  25. // 17-Aug-1988 RB Clean up.
  26. #include "precomp.h"
  27. #pragma hdrstop
  28. void addArgument(char*,unsigned,char***);
  29. void processLine(char*,unsigned*,char***);
  30. void tokenizeLine(char*,unsigned*,char***);
  31. // readCommandFile()
  32. //
  33. // arguments: name pointer to name of command file to read
  34. //
  35. // actions: opens command file
  36. // reads in lines and calls processLine() to
  37. // break them into tokens and to build
  38. // an argument vector (a la argv[])
  39. // calls parseCommandLine() recursively to process
  40. // the accumulated "command line" arguments
  41. // frees space used by the arg vector
  42. //
  43. // modifies: makeFiles in main() by modifying contents of parameter list
  44. // makeTargets in main() by modifying contents of targets parameter
  45. // buf global buffer
  46. //
  47. // notes: function is not ANSI portable because it uses fopen()
  48. // with "rt" type and text mode is a Microsoft extension
  49. //
  50. void
  51. readCommandFile(
  52. char *name
  53. )
  54. {
  55. char *s, // buffer
  56. **vector; // local versions of arg vector
  57. unsigned count = 0; // count
  58. size_t n;
  59. if (!(file = FILEOPEN(name,"rt")))
  60. makeError(0,CANT_OPEN_FILE,name);
  61. vector = NULL; // no args yet
  62. while (fgets(buf,MAXBUF,file)) {
  63. n = _tcslen(buf);
  64. // if we didn't get the whole line, OR the line ended with a backSlash
  65. if ((n == MAXBUF-1 && buf[n-1] != '\n') ||
  66. (buf[n-1] == '\n' && buf[n-2] == '\\')
  67. ) {
  68. if (buf[n-2] == '\\' && buf[n-1] == '\n') {
  69. // Replace \n by \0 and \\ by a space; Also reset length
  70. buf[n-1] = '\0';
  71. buf[n-2] = ' ';
  72. n--;
  73. }
  74. s = makeString(buf);
  75. getRestOfLine(&s,&n);
  76. } else
  77. s = buf;
  78. processLine(s,&count,&vector); // separate into args
  79. if (s != buf)
  80. FREE(s);
  81. }
  82. if (fclose(file) == EOF)
  83. makeError(0, ERROR_CLOSING_FILE, name);
  84. parseCommandLine(count,vector); // evaluate the args
  85. while (count--) // free the arg vector
  86. if(vector[count])
  87. FREE(vector[count]); // NULL entries mean that the space the
  88. } // entry used to pt to is still in use
  89. // getRestOfLine()
  90. //
  91. // arguments: s pointer to readCommandFile()'s buffer
  92. // holding line so far
  93. // n pointer to readCommandFile()'s count of
  94. // the chars in *s
  95. //
  96. // actions: keeps reading in text until it sees a newline
  97. // or the end of file
  98. // reallocs space for the old buffer plus the
  99. // contents of the new buffer each time
  100. // appends new buffer's text to existing text
  101. //
  102. // modifies: s readCommandFile()'s text buffer by realloc'ing
  103. // more space for incoming text
  104. // n readCommandFile()'s count of bytes in s
  105. // buf global buffer
  106. void
  107. getRestOfLine(
  108. char *s[],
  109. size_t *n
  110. )
  111. {
  112. size_t temp;
  113. char *t;
  114. t = buf;
  115. while ((*s)[*n-1] != '\n') { // get rest of line
  116. if (!fgets(t,MAXBUF,file))
  117. break; // we hit EOF
  118. temp = _tcslen(t);
  119. if (t[temp-2] == '\\' && t[temp-1] == '\n') {
  120. //Replace \n by \0 and \\ by a space; Also reset length
  121. t[temp-1] = '\0';
  122. t[temp-2] = ' ';
  123. }
  124. temp = *n;
  125. *n += _tcslen(t);
  126. *s = (char *) REALLOC(*s,*n+1); // + 1 for NULL byte
  127. if (!*s)
  128. makeError(line, MACRO_TOO_LONG);
  129. _tcscpy(*s+temp,t);
  130. }
  131. }
  132. // processLine()
  133. //
  134. // arguments: s pointer to readCommandFile()'s buffer
  135. // holding "command line" to be processed
  136. // count pointer to readCommandFile()'s count of
  137. // "command line" arguments seen so far
  138. // vector pointer to readCommandFile()'s vector of
  139. // pointers to character strings
  140. //
  141. // actions: if the line to be broken into "command line arguments" contains '"'
  142. // breaks all the text before '"' into tokens
  143. // delimited by whitespace (which get put in vector[] by
  144. // tokenizeLine())
  145. // finds the closing '"' and treats the quoted string
  146. // as a single token, adding it to the vector
  147. // recurses on the tail of the line (to check for
  148. // other quoted strings)
  149. // else breaks all text in line into tokens delimited
  150. // by whitespace
  151. //
  152. // modifies: vector readCommandFile()'s vector of pointers to
  153. // "command line argument" strings (by modifying
  154. // the contents of the parameter pointer, vector)
  155. // count readCommandFile()'s count of the arguments in
  156. // the vector (by modifying the contents of the
  157. // parameter pointer, count)
  158. void
  159. processLine(
  160. char *s,
  161. unsigned *count,
  162. char **vector[]
  163. )
  164. {
  165. char *t;
  166. char *u;
  167. size_t m;
  168. size_t n;
  169. BOOL allocFlag = FALSE;
  170. if (!(t = _tcschr(s,'"'))) { // no quoted strings,
  171. tokenizeLine(s,count,vector); // just standard fare
  172. } else {
  173. // There are two kinds of situations in which quotes can occur:
  174. // 1. "FOO = bar baz"
  175. // 2. FOO="bar baz"
  176. if ((t == s) || (*(t-1) != '=')) {
  177. // Case 1 above
  178. *t++ = '\0'; // quoted macrodef
  179. tokenizeLine(s,count,vector); // get tokens before "
  180. } else {
  181. // Case 2 above
  182. *t-- = ' ';
  183. for (u = t; u > s; --u) // find the beginning of the macro name
  184. if (*u == ' ' || *u == '\t' || *u == '\n')
  185. break;
  186. if (u != s) {
  187. *u++ = '\0';
  188. tokenizeLine(s, count, vector);
  189. }
  190. t = u;
  191. }
  192. n = _tcslen(t);
  193. for (u = t; *u; ++u) { // look for closing "
  194. if (*u == '"') { // need " and not ""
  195. if (*(u+1) == '"') {
  196. _tcscpy(u,u+1);
  197. continue;
  198. }
  199. *u++ = '\0'; // terminate macrodef
  200. addArgument(t,*count,vector); // treat as one arg
  201. ++*count;
  202. processLine(u+1,count,vector); // recurse on rest of line
  203. break;
  204. } // TAIL RECURSION -- eliminate later?
  205. if ((*u == '\\')
  206. && WHITESPACE(*(u-1))
  207. && (*(u+1) == '\n')) { // \n always last char
  208. *u = '\0'; // 2 chars go to 1
  209. m = (n = n-2); // adjust length count
  210. if (!allocFlag) {
  211. allocFlag = TRUE;
  212. t = makeString(t);
  213. }
  214. getRestOfLine(&t,&n); // get some more text
  215. u = t + m ; // reset u & continue looping
  216. }
  217. }
  218. if (u == t + n) { // if at end of line
  219. makeError(0,SYNTAX_NO_QUOTE); // and no ", error
  220. }
  221. if (allocFlag) {
  222. FREE(t);
  223. }
  224. }
  225. }
  226. // tokenizeLine()
  227. //
  228. // arguments: s pointer to readCommandFile()'s buffer
  229. // holding "command line" to be tokenized
  230. // count pointer to readCommandFile()'s count of
  231. // "command line" arguments seen so far
  232. // vector pointer to readCommandFile()'s vector of
  233. // pointers to character strings
  234. //
  235. // actions: breaks the line in s into tokens (command line
  236. // arguments) delimited by whitespace
  237. // adds each token to the argument vector
  238. // adjusts the argument counter
  239. //
  240. // modifies: vector readCommandFile()'s vector of pointers to
  241. // "command line argument" strings (by modifying
  242. // the contents of the parameter pointer, vector)
  243. // count readCommandFile()'s count of the arguments in
  244. // the vector (by modifying the contents of the
  245. // parameter pointer, count)
  246. //
  247. // If the user ever wants '@' to be part of an argument in a command file,
  248. // he has to enclose that argument in quotation marks.
  249. void
  250. tokenizeLine( // gets args delimited
  251. char *s, // by whitespace and
  252. unsigned *count, // constructs an arg
  253. char **vector[] // vector
  254. )
  255. {
  256. char *t;
  257. if (t = _tcschr(s,'\\')) {
  258. if (WHITESPACE(*(t-1)) && (*(t+1) == '\n')) {
  259. *t = '\0';
  260. }
  261. }
  262. for (t = _tcstok(s," \t\n"); t; t = _tcstok(NULL," \t\n")) {
  263. if (*t == '@') {
  264. makeError(0,SYNTAX_CMDFILE,t+1);
  265. break; // should we keep on parsing here?
  266. }
  267. addArgument(t,*count,vector);
  268. ++*count;
  269. }
  270. }
  271. // addArgument()
  272. //
  273. // arguments: s pointer to text of argument to be added
  274. // to the "command line argument" vector
  275. // count pointer to readCommandFile()'s count of
  276. // "command line" arguments seen so far
  277. // vector pointer to readCommandFile()'s vector of
  278. // pointers to character strings
  279. //
  280. // actions: allocates space in the vector for the new argument
  281. // allocates space for argument string
  282. // makes vector entry point to argument string
  283. //
  284. // modifies: vector readCommandFile()'s vector of pointers to
  285. // "command line argument" strings (by modifying
  286. // the contents of the parameter pointer, vector)
  287. // (count gets incremented by caller)
  288. //
  289. // To keep from fragmenting memory by doing many realloc() calls for very
  290. // small amounts of space, we get memory in small chunks and use that until
  291. // it is depleted, then we get another chunk . . . .
  292. void
  293. addArgument( // puts s in vector
  294. char *s,
  295. unsigned count,
  296. char **vector[]
  297. )
  298. {
  299. if (!(*vector)) {
  300. *vector = (char**) allocate(CHUNKSIZE*sizeof(char*));
  301. } else if (!(count % CHUNKSIZE)) {
  302. *vector = (char**) REALLOC(*vector,(count+CHUNKSIZE)*sizeof(char*));
  303. if (!*vector) {
  304. makeError(0,OUT_OF_MEMORY);
  305. }
  306. }
  307. (*vector)[count] = makeString(s);
  308. }