Windows NT 4.0 source code leak
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.

2130 lines
57 KiB

4 years ago
  1. /*** UTIL.C -- Data structure manipulation functions ***************************
  2. *
  3. * Copyright (c) 1988-1990, Microsoft Corporation. All rights reserved.
  4. *
  5. * Purpose:
  6. * This module contains routines manipulating the Data Structures of NMAKE. The
  7. * routines are independent of the Mode of execution (Real/Bound).
  8. *
  9. * Revision History:
  10. * 01-Feb-1994 HV Turn off extra info display.
  11. * 17-Jan-1994 HV Fixed bug #3548: possible bug in findMacroValues because we
  12. * are scanning 'string' byte-by-byte instead of char-by-char
  13. * 15-Nov-1993 JdR Major speed improvements
  14. * 15-Oct-1993 HV Use tchar.h instead of mbstring.h directly, change STR*() to _ftcs*()
  15. * 03-Jun-1993 HV Add helper local function TruncateString for findFirst.
  16. * 10-May-1993 HV Add include file mbstring.h
  17. * Change the str* functions to STR*
  18. * 08-Apr-1993 HV Rewrite prependPath() to use _splitpath() and _makepath()
  19. * 31-Mar-1993 HV Rewrite drive(), path(), filename(), and extension() to use
  20. * _splitpath() instead of parsing the pathname by itself.
  21. * 08-Jun-1992 SS Port to DOSX32
  22. * 27-May-1992 RG Changed open_file to use _fsopen with _SH_DENYWR
  23. * 29-May-1990 SB ^$ was not becoming same as $$ for command lines ...
  24. * 01-May-1990 SB Nasty Preprocessor quirk bug in modifySpecialvalue() fixed
  25. * 27-Feb-1990 SB GP fault for '!if "$(debug"=="y"' fixed (bug t119)
  26. * 08-Feb-1990 SB GP fault for 'echo $(FOO:" =" ^) fixed
  27. * 06-Feb-1990 SB Handle $* etc in presence of Quoted names.
  28. * 02-Feb-1990 SB Add file_open() function
  29. * 31-Jan-1990 SB Debug changes; testAddr used to track problems at that addr
  30. * 08-Dec-1989 SB Changed SPRINTF() to avoid C6 warnings with -Oes
  31. * 04-Dec-1989 SB ZFormat() proto was misspelled as Zformat()
  32. * 01-Dec-1989 SB realloc_memory() added; allocate() uses _msize() now
  33. * 22-Nov-1989 SB Add strcmpiquote() and unQuote()
  34. * 22-Nov-1989 SB add #ifdef DEBUG_MEMORY funcs free_memory() and mem_status()
  35. * 13-Nov-1989 SB restoreEnv() function unreferenced
  36. * 08-Oct-1989 SB Added searchBucket(); find() now checks equivalence of quoted
  37. * and unquoted versions of a target.
  38. * 06-Sep-1989 SB $* in in-line files was clobbering Global variable 'buf'
  39. * 18-Aug-1989 SB heapdump() gets two parameters
  40. * 03-Jul-1989 SB moved curTime() to utilb.c and utilr.c to handle DOSONLY ver
  41. * 30-Jun-1989 SB added curTime() to get current Time.
  42. * 28-Jun-1989 SB changed copyEnviron()
  43. * 05-Jun-1989 SB makeString("") instead of using "" in DGROUP for null macro
  44. * 21-May-1989 SB modified find() to understand that targets 'foo.c', '.\foo.c'
  45. * and './foo.c' are the same.
  46. * 13-May-1989 SB Added functions path(), drive(), filename(), extension(),
  47. * strbskip(), strbscan() instead of ZTOOLS library
  48. * 12-May-1989 SB Fixed bug in substitute strings
  49. * 10-May-1989 SB Added stuff for ESCH handling changes in Quotes;
  50. * 01-May-1989 SB changed return value of allocate().
  51. * 14-Apr-1989 SB restoreEnv() created for macroBackInheritance
  52. * 05-Apr-1989 SB made all funcs NEAR; Reqd to make all function calls NEAR
  53. * 17-Mar-1989 SB substituteStrings() now has 3 new error checks & avoids GPs
  54. * 13-Mar-1989 SB ZFormat() was missing the legal case of '%%'
  55. * 22-Feb-1989 SB ZFormat() has buffer overflow check with extra parameter
  56. * and SPRINTF() gives a new error
  57. * 15-Feb-1989 SB Changed modifySpecialValue(), was not performing correctly
  58. * for $(@D) and $(@B) for some cases.
  59. * 13-Feb-1989 SB Made Prototypes for ZTools Library functions as extern
  60. * 5-Dec-1988 SB Made SPRINTF() cdecl as NMAKE now uses Pascal calling
  61. * 25-Nov-1988 SB Added SPRINTF() and ZFormat() and also added prototypes for
  62. * functions used from ZTools Library (6 of them)
  63. * 10-Nov-1988 SB Removed mixed mode functions (bound & real) to utilr.c
  64. * & utilb.c; corr globals/debug data also moved
  65. * 10-Oct-1988 SB Add Comments for hash().
  66. * 18-Sep-1988 RB Fix bad flag checking for targets in find().
  67. * 15-Sep-1988 RB Move some def's out to GLOBALS.
  68. * 22-Aug-1988 RB Don't find undefined macros.
  69. * 17-Aug-1988 RB Clean up. Clear memory in allocate().
  70. * 8-Jul-1988 rj Minor speedup (?) to find().
  71. * 7-Jul-1988 rj Modified find and hash; less efficient, but case-indep.
  72. * 1-Jul-1988 rj Fixed line truncation after null special macro.
  73. * 30-Jun-1988 rj Fixed bug with checkDynamicDependency not handling $$.
  74. * 29-Jun-1988 rj Fixed bug with extra * after expanding $**.
  75. * Fixed abysmal screwup with $$(@?).
  76. * Fixed handling of F, B, R modifiers.
  77. * 22-Jun-1988 rj Added friendly filename truncation (findFirst).
  78. * 22-Jun-1988 rj Fixed bug #3 (.abc.def.ghi not detected).
  79. * 16-Jun-1988 rj Modified several routines to look for escape
  80. * character when walking through strings.
  81. * 27-May-1988 rb Fixed space-appending on list-expansion macros.
  82. * Don't include trailing path separator in $(@D).
  83. *
  84. *******************************************************************************/
  85. #include "nmake.h"
  86. #include "nmmsg.h"
  87. #include "proto.h"
  88. #include "globals.h"
  89. #include "grammar.h"
  90. /* Prototypes of functions local to this module */
  91. LOCAL void NEAR putValue(char**, char**, char**, char**, char*, unsigned*, char *);
  92. LOCAL void NEAR substituteStrings(char**, char**, char**, char**, char*,
  93. unsigned*, char *);
  94. LOCAL char * NEAR isolateMacroName(char*, char*);
  95. LOCAL char * NEAR checkDynamicDependency(char*);
  96. LOCAL void NEAR increaseBuffer(char**, char**, char**, unsigned*, char *);
  97. LOCAL void NEAR putSpecial(char**, char**, char**, char**, unsigned*,
  98. unsigned, char *);
  99. char * NEAR modifySpecialValue(char, char*, char*);
  100. LOCAL BOOL NEAR ZFormat(char *, unsigned, char *, char *);
  101. LOCAL STRINGLIST * NEAR searchBucket(char *, STRINGLIST **, unsigned);
  102. LOCAL int NEAR envVars(char **environ);
  103. /* Prototypes of functions used by ZFormat from ZTools Library */
  104. char * NEAR strbscan(char *, char *);
  105. LOCAL char * NEAR strbskip(char *, char *);
  106. LOCAL int NEAR drive(const char *, char *);
  107. LOCAL int NEAR path(const char *, char *);
  108. LOCAL int NEAR filename(const char *, char *);
  109. LOCAL int NEAR extension(const char *, char *);
  110. #ifdef HEAP
  111. extern void NEAR heapdump(char *file, int line);
  112. #endif
  113. #ifdef DEBUG_MEMORY
  114. unsigned long blocksize = 500L;
  115. BOOL fDebugMem = FALSE;
  116. FILE *memory;
  117. unsigned long freecount = 0L;
  118. unsigned testAddr = 0xdf78;
  119. #endif
  120. char special1[] = "*@<?";
  121. char special2[] = "DFBR";
  122. #if !defined(NDEBUG) && !defined(NT_BUILD)
  123. unsigned long TotalFree = 0;
  124. unsigned long TotalReallyFreed = 0;
  125. unsigned long TotalAlloc = 0;
  126. unsigned long TotalBlkAlloc = 0;
  127. unsigned long TotalLost = 0;
  128. unsigned long CntAlloc = 0;
  129. unsigned long FreePtrCnt = 0;
  130. void NEAR printStats(void) {
  131. #if defined(STATISTICS)
  132. fprintf(stderr,"\n\tMemory Allocation:\n");
  133. fprintf(stderr,"\t\ttotal allocation:\t%12.lu\n", TotalAlloc);
  134. fprintf(stderr,"\t\ttotal freed:\t\t%12.lu\n", TotalFree);
  135. fprintf(stderr,"\t\tblocks allocated:\t\t%12.lu\n", TotalBlkAlloc);
  136. fprintf(stderr,"\t\tbytes lost:\t\t\t%12.lu\n", TotalLost);
  137. fprintf(stderr,"\t\tindividual allocations:\t%12.lu\n", CntAlloc);
  138. fprintf(stderr,"\t\tpointers freed:\t\t%12.lu\n\n", FreePtrCnt);
  139. #endif
  140. #if defined(STATISTICS)
  141. fprintf(stderr,"\tMacros:\n");
  142. fprintf(stderr,"\t\tsearches:\t\t%12.lu\n", CntfindMacro);
  143. fprintf(stderr,"\t\tchain walks:\t\t%12.lu\n", CntmacroChains);
  144. fprintf(stderr,"\t\tinsertions:\t\t%12.lu\n", CntinsertMacro);
  145. fprintf(stderr,"\n\tTargets:\n");
  146. fprintf(stderr,"\t\tsearches:\t\t%12.lu\n", CntfindTarget);
  147. fprintf(stderr,"\t\tchain walks:\t\t%12.lu\n", CnttargetChains);
  148. fprintf(stderr,"\n\tOthers:\n");
  149. fprintf(stderr,"\t\tstricmp compares:\t%12.lu\n", CntStriCmp);
  150. fprintf(stderr,"\t\tString List frees:\t%12.lu\n", CntFreeStrList);
  151. fprintf(stderr,"\t\tString List allocs:\t%12.lu\n", CntAllocStrList);
  152. #endif
  153. }
  154. #endif
  155. #define ALLOCBLKSIZE 32768
  156. unsigned long AllocFreeCnt = 0;
  157. char * PtrAlloc = 0;
  158. STRINGLIST *PtrFreeStrList;
  159. #ifndef HEAP
  160. /*
  161. * rallocate - allocate raw memory (not cleared)
  162. *
  163. * Tries to allocate a chunk of memory, prints error message and exits if
  164. * the requested amount is not available.
  165. */
  166. void * NEAR rallocate(unsigned size)
  167. {
  168. char * chunk;
  169. chunk = (char *)malloc(size);
  170. if (chunk == NULL) {
  171. #ifdef DEBUG_MEMORY
  172. if (fDebug)
  173. fprintf(memory, "out of memory after allocating %lu bytes\n", TotalAlloc);
  174. #endif
  175. makeError(currentLine, OUT_OF_MEMORY);
  176. }
  177. return (void *)chunk;
  178. }
  179. #endif
  180. /*
  181. * allocate - allocate memory and clear it
  182. *
  183. * Tries to allocate a chunk of memory, prints error message and exits if
  184. * the requested amount is not available.
  185. * IMPORTANT: we must clear the memory here. Code elsewhere relies on
  186. * this.
  187. */
  188. void * NEAR
  189. allocate(size)
  190. unsigned size; /* Number of bytes requested */
  191. {
  192. char *chunk;
  193. #ifdef DEBUG_MEMORY
  194. if (!fDebugMem) {
  195. memory = fopen("c:\\tmp\\memory.out", "a");
  196. fDebugMem = TRUE;
  197. }
  198. #endif
  199. #ifdef HEAP
  200. if (fHeapChk)
  201. size += 4;
  202. #endif
  203. #if !defined(NDEBUG) && !defined(NT_BUILD)
  204. TotalAlloc += size;
  205. CntAlloc++;
  206. #endif
  207. #ifdef HEAP
  208. chunk = (char *)malloc(size);
  209. if (chunk == NULL) {
  210. #ifdef DEBUG_MEMORY
  211. if (fDebug)
  212. fprintf(memory, "out of memory after allocating %lu bytes\n", TotalAlloc);
  213. #endif
  214. makeError(currentLine, OUT_OF_MEMORY);
  215. }
  216. #else
  217. chunk = (char *)rallocate(size);
  218. #endif
  219. memset((void *)chunk, 0, size);
  220. #ifdef DEBUG_MEMORY
  221. if (fDebug) {
  222. TotalAlloc += size;
  223. if (TotalAlloc >= blocksize) {
  224. mem_status();
  225. blocksize += 500L ;
  226. }
  227. if (_heapchk() != _HEAPOK)
  228. fprintf(stderr, "Error: heap is not Ok");
  229. }
  230. #endif
  231. #ifdef HEAP
  232. // if (_heapchk() != _HEAPOK)
  233. // fprintf(stderr, "Error: heap is not Ok");
  234. size = _msize(chunk);
  235. if (fHeapChk) {
  236. chunk[0] = 'N';
  237. chunk[1] = 'M';
  238. chunk[size - 2] = 'K';
  239. chunk[size - 1] = 'E';
  240. chunk += 2;
  241. }
  242. #endif
  243. #ifdef DEBUG_MEMORY
  244. if (FP_OFF(chunk) == testAddr)
  245. fprintf(stderr, "Changed ...allocated\n");
  246. #endif
  247. return((void*)chunk);
  248. }
  249. void * NEAR alloc_stringlist(void) /* Number of bytes requested */
  250. {
  251. STRINGLIST *chunk;
  252. #if defined(STATISTICS)
  253. CntAllocStrList++;
  254. #endif
  255. if (PtrFreeStrList != NULL) {
  256. chunk = PtrFreeStrList;
  257. PtrFreeStrList = chunk->next;
  258. }
  259. else {
  260. if (AllocFreeCnt < sizeof(STRINGLIST)) {
  261. PtrAlloc = (char *)malloc(ALLOCBLKSIZE);
  262. if (PtrAlloc == NULL) {
  263. #ifdef DEBUG_MEMORY
  264. if (fDebug)
  265. fprintf(memory, "out of memory after allocating %lu bytes\n", TotalAlloc);
  266. #endif
  267. makeError(currentLine, OUT_OF_MEMORY);
  268. }
  269. #if !defined(NDEBUG) && !defined(NT_BUILD)
  270. TotalAlloc += ALLOCBLKSIZE;
  271. CntAlloc++;
  272. #endif
  273. AllocFreeCnt = ALLOCBLKSIZE;
  274. }
  275. chunk = (STRINGLIST *)PtrAlloc;
  276. PtrAlloc += sizeof(STRINGLIST);
  277. AllocFreeCnt -= sizeof(STRINGLIST);
  278. }
  279. chunk->next = NULL;
  280. chunk->text = NULL;
  281. return (void *)chunk;
  282. }
  283. void NEAR free_stringlist(STRINGLIST *pMem)
  284. {
  285. #if defined(STATISTICS)
  286. CntFreeStrList++;
  287. #endif
  288. if (PtrFreeStrList == NULL) {
  289. pMem->next = NULL;
  290. PtrFreeStrList = pMem;
  291. }
  292. else {
  293. #ifdef HEAP
  294. STRINGLIST *tmp = PtrFreeStrList;
  295. do {
  296. if (tmp == pMem) {
  297. fprintf(stderr, "free same pointer twice: %p\n", pMem);
  298. return;
  299. }
  300. tmp = tmp->next;
  301. }
  302. while (tmp);
  303. pMem->text = NULL;
  304. #endif
  305. pMem->next = PtrFreeStrList;
  306. PtrFreeStrList = pMem;
  307. }
  308. }
  309. #ifdef HEAP
  310. void NEAR
  311. free_memory(pMem)
  312. void *pMem;
  313. {
  314. char *p = pMem;
  315. unsigned uFree;
  316. #ifdef DEBUG_MEMORY
  317. if (FP_OFF(pMem) == testAddr)
  318. fprintf(stderr, "Changed ... freed\n");
  319. #endif
  320. if (fHeapChk) {
  321. p -= 2;
  322. if (p[0] != 'N' && p[1] != 'M')
  323. fprintf(stderr, "Alloc start signature error at %p\n", pMem);
  324. #if defined(FLAT)
  325. __try {
  326. uFree = _msize(p);
  327. }
  328. __except(1) {
  329. fprintf(stderr, "non malloc ptr freed at %p\n", pMem);
  330. return;
  331. }
  332. #else
  333. // if (_heapchk() != _HEAPOK)
  334. // fprintf(stderr, "Error: heap is not Ok");
  335. uFree = _msize(p);
  336. #endif
  337. if (p[uFree - 2] != 'K' && p[uFree - 1] != 'E')
  338. fprintf(stderr, "Alloc end signature error at %p\n", pMem);
  339. #ifdef FLAT
  340. memset(p, 0xFF, uFree);
  341. #else
  342. _fmemset(p, 0xFF, uFree);
  343. #endif
  344. }
  345. #ifdef DEBUG_MEMORY
  346. freecount += uFree;
  347. #endif
  348. free(p);
  349. }
  350. void * NEAR
  351. realloc_memory(pMem, uSize)
  352. void *pMem;
  353. unsigned uSize;
  354. {
  355. unsigned old;
  356. unsigned new;
  357. void *pNew;
  358. char *p = pMem;
  359. #ifdef DEBUG_MEMORY
  360. if (FP_OFF(pMem) == testAddr)
  361. fprintf(stderr, "Changed ... reallocated\n");
  362. #endif
  363. if (fHeapChk) {
  364. p -= 2;
  365. if (p[0] != 'N' && p[1] != 'M')
  366. fprintf(stderr, "Alloc start signature error at %p\n", pMem);
  367. old = _msize(p);
  368. if (p[old - 2] != 'K' && p[old - 1] != 'E')
  369. fprintf(stderr, "Alloc end signature error at %p\n", pMem);
  370. uSize += 4;
  371. }
  372. pNew = realloc(p, uSize);
  373. if (fHeapChk) {
  374. if (pNew == NULL)
  375. return(pNew);
  376. new = _msize(pNew);
  377. }
  378. #ifdef DEBUG_MEMORY
  379. if (new > old)
  380. TotalAlloc += new - old;
  381. else
  382. freecount += old - new;
  383. #endif
  384. if (fHeapChk) {
  385. if (p != pNew)
  386. #ifdef FLAT
  387. memset(p, 0xFF, old);
  388. #else
  389. _fmemset(p, 0xFF, old);
  390. #endif
  391. }
  392. p = (char *)pNew;
  393. if (fHeapChk) {
  394. p[new - 2] = 'K';
  395. p[new - 1] = 'E';
  396. p += 2;
  397. }
  398. return(p);
  399. }
  400. #endif
  401. #if 0
  402. void NEAR free_memory(void *pMem)
  403. {
  404. #if defined(FLAT)
  405. __try {
  406. (void)_msize(pMem);
  407. }
  408. __except(1) {
  409. fprintf(stderr, "non malloc ptr freed at %p\n", pMem);
  410. return;
  411. }
  412. #endif
  413. free(pMem);
  414. }
  415. #endif
  416. #ifdef DEBUG_MEMORY
  417. void NEAR
  418. mem_status(void)
  419. {
  420. if (fDebug) {
  421. fprintf(memory, "allocated : %lu ", TotalAlloc);
  422. fprintf(memory, "Freed : %lu ", freecount);
  423. fprintf(memory, "Still allocated : %lu\n", TotalAlloc - freecount);
  424. fprintf(memory, "Mem Avail in Near Heap : %u\n", _memavl());
  425. }
  426. }
  427. #endif
  428. char * NEAR
  429. makeString(s) /* allocates space, copies*/
  430. char *s; /* the given string into */
  431. { /* the newly allocated */
  432. char *t; /* space, and returns ptr*/
  433. unsigned l = _ftcslen(s) + 1;
  434. t = (char *)rallocate(l);
  435. memcpy(t, s, l);
  436. return(t);
  437. }
  438. void NEAR
  439. prependItem(list, element) /* makes element the head */
  440. STRINGLIST **list; /* of list */
  441. STRINGLIST *element;
  442. {
  443. element->next = *list;
  444. *list = element;
  445. }
  446. void NEAR
  447. appendItem(list, element) /* makes element the tail */
  448. STRINGLIST **list; /* of list */
  449. STRINGLIST *element;
  450. {
  451. for (; *list; list = &(*list)->next);
  452. *list = element;
  453. }
  454. /* hash - returns hash value corresponding to a string
  455. *
  456. * Purpose:
  457. * This is a hash function. The hash function uses the following Algorithm --
  458. * Add the characters making up the string (s) to get N (ASCII values)
  459. * N mod total ,gives the hash value,
  460. * where, total is MAXMACRO for macros
  461. * MAXTARGET targets
  462. * Additionally, for targets it takes Uppercase Values alone, since, targets
  463. * are generally filenames and DOS/OS2 filenames are case independent.
  464. *
  465. * Input:
  466. * s = name for which a hash is required
  467. * total = Constant used in the hash function
  468. * targetFlag = boolean flag; true for targets, false for macros
  469. *
  470. * Output:
  471. * Returns hash value between 0 and (total-1)
  472. *
  473. */
  474. unsigned NEAR
  475. hash(s, total, targetFlag)
  476. char *s;
  477. unsigned total;
  478. BOOL targetFlag;
  479. {
  480. unsigned n;
  481. unsigned c;
  482. if (targetFlag) {
  483. for (n = 0; c = *s; (n += c), s++)
  484. if (c == '/') c = '\\'; /* slash == backslash in targets */
  485. else c = _totupper(c); /* lower-case == upper-case in targets */
  486. }
  487. else for (n = 0; *s; n += *s++);
  488. return(n % total);
  489. }
  490. /*
  491. * find - look up a string in a hash table
  492. *
  493. * Look up a macro or target name in a hash table and return the entry
  494. * or NULL.
  495. * If a macro and undefined, return NULL.
  496. * Targets get matched in a special way because of filename equivalence.
  497. */
  498. STRINGLIST * NEAR
  499. find(str, limit, table, targetFlag)
  500. char *str;
  501. unsigned limit;
  502. STRINGLIST *table[];
  503. BOOL targetFlag;
  504. {
  505. unsigned n;
  506. char *string = str;
  507. char *quote;
  508. STRINGLIST *found;
  509. BOOL fAllocStr = FALSE;
  510. if (*string) {
  511. n = hash(string, limit, targetFlag);
  512. if (targetFlag) {
  513. #if defined(STATISTICS)
  514. CntfindTarget++;
  515. #endif
  516. found = searchBucket(string, table, n);
  517. if (found)
  518. return(found);
  519. //Look for .\string
  520. if (!_ftcsncmp(string, ".\\", 2)
  521. || !_ftcsncmp(string, "./", 2))
  522. string += 2;
  523. else {
  524. string = (char *)rallocate(2 + _ftcslen(str) + 1);
  525. _ftcscat(_ftcscpy(string, ".\\"), str);
  526. fAllocStr = (BOOL)TRUE;
  527. }
  528. n = hash(string, limit, targetFlag);
  529. found = searchBucket(string, table, n);
  530. if (found) {
  531. if (fAllocStr)
  532. FREE(string);
  533. return(found);
  534. }
  535. //Look for ./string
  536. if (string != (str + 2))
  537. string[1] = '/';
  538. n = hash(string, limit, targetFlag);
  539. found = searchBucket(string, table, n);
  540. if (fAllocStr)
  541. FREE(string);
  542. if (found)
  543. return(found);
  544. //Look for "foo" or foo
  545. if (*str == '"') {
  546. quote = unQuote(str);
  547. }
  548. else {
  549. unsigned len = _ftcslen(str) + 2;
  550. quote = allocate(len + 1);
  551. _ftcscat(_ftcscat(_ftcscpy(quote, "\""), str), "\"");
  552. }
  553. n = hash(quote, limit, targetFlag);
  554. found = searchBucket(quote, table, n);
  555. FREE(quote);
  556. return found;
  557. }
  558. else {
  559. for (found = table[n]; found; found = found->next)
  560. if (!_ftcscmp(found->text, string))
  561. return((((MACRODEF *)found)->flags & M_UNDEFINED) ? NULL : found);
  562. }
  563. }
  564. return(NULL);
  565. }
  566. /*
  567. * FINDMACROVALUES --
  568. * looks up a macro's value in hash table, prepends to list a STRINGLIST
  569. * element holding pointer to macro's text, then recurses on any macro
  570. * invocations in the value
  571. *
  572. * The lexer checks for overrun in names (they must be < 128 chars).
  573. * If a longer, undefined macro is only referred to in the value of
  574. * another macro which is never invoked, the error will not be flagged.
  575. * I think this is reasonable behavior.
  576. *
  577. * MACRO NAMES CAN ONLY CONSIST OF ALPHANUMERIC CHARS AND UNDERSCORE
  578. *
  579. * we pass a null list pointer-pointer if we just want to check for cyclical
  580. * definitions w/o building the list.
  581. *
  582. * the name parameter is what's on the left side of an = when we're just
  583. * checking cyclical definitions. When we "find" the macros in a target
  584. * block, we have to pass the name of the macro whose text we're recursing
  585. * on in our recursive call to findMacroValues().
  586. *
  587. * Might want to check into how to do this w/o recursion (is it possible?)
  588. *
  589. * This function is RECURSIVE.
  590. */
  591. /* Added a fix to make this function handle expand macros which refer
  592. * to other recursive macros.
  593. *
  594. * levelSeen is the recLevel at which a macroname was first seen so that
  595. * the appropriate expansion can be calculated (even when recursing ...)
  596. */
  597. #pragma check_stack(on)
  598. BOOL NEAR
  599. findMacroValues(char *string, /* string to check*/
  600. STRINGLIST **list, /* list to build */
  601. STRINGLIST **newtail, /* tail of list to update */
  602. char *name, /* name = string */
  603. unsigned recLevel, /* recursion level*/
  604. unsigned levelSeen,
  605. UCHAR flags) {
  606. char macroName[MAXNAME];
  607. char *s;
  608. MACRODEF *p;
  609. STRINGLIST *q,
  610. *r,
  611. dummy,
  612. *tail;
  613. unsigned i;
  614. BOOL inQuotes = (BOOL) FALSE; /* flag when inside quote marks */
  615. if (list) {
  616. if (newtail) {
  617. tail = *newtail;
  618. }
  619. else {
  620. tail = *list;
  621. if (tail) {
  622. while (tail->next) {
  623. tail = tail->next;
  624. }
  625. }
  626. }
  627. }
  628. else {
  629. tail = NULL;
  630. }
  631. for (s = string; *s; ++s) { /* walk the string*/
  632. for (; *s && *s != '$'; s = _ftcsinc(s)) { /* find next macro*/
  633. if (*s == '\"')
  634. inQuotes = (BOOL) !inQuotes;
  635. if (!inQuotes && *s == ESCH) {
  636. ++s; /* skip past ESCH */
  637. if (*s == '\"')
  638. inQuotes = (BOOL) !inQuotes;
  639. }
  640. }
  641. if (!*s) break; /* move past '$' */
  642. if (!s[1])
  643. if (ON(flags, M_ENVIRONMENT_DEF)) {
  644. if (newtail) *newtail = tail;
  645. return(FALSE);
  646. }
  647. else
  648. makeError(currentLine, SYNTAX_ONE_DOLLAR);
  649. s = _ftcsinc(s);
  650. if (!inQuotes && *s == ESCH) {
  651. s = _ftcsinc(s);
  652. if (!MACRO_CHAR(*s))
  653. if (ON(flags, M_ENVIRONMENT_DEF)) {
  654. if (newtail) *newtail = tail;
  655. return(FALSE);
  656. }
  657. else
  658. makeError(currentLine, SYNTAX_BAD_CHAR, *s);
  659. }
  660. if (*s == '$') { /* $$ = dynamic */
  661. s = checkDynamicDependency(s); /* dependency */
  662. continue; /* or just $$->$ */
  663. }
  664. else if (*s == '(') { /* name is longer */
  665. s = isolateMacroName(s+1, macroName); /* than 1 char */
  666. if (_ftcschr(special1, *macroName))
  667. continue;
  668. }
  669. else {
  670. if (_ftcschr(special1, *s))
  671. continue; /* 1-letter macro */
  672. if (!MACRO_CHAR(*s))
  673. if (ON(flags, M_ENVIRONMENT_DEF)) {
  674. if (newtail) *newtail = tail;
  675. return(FALSE);
  676. }
  677. else
  678. makeError(currentLine, SYNTAX_ONE_DOLLAR);
  679. macroName[0] = *s;
  680. macroName[1] = '\0';
  681. }
  682. // If list isn't NULL, allocate storage for a new node. Otherwise
  683. // this function was called purely to verify the macro name was
  684. // valid and we can just use the dummy node as a place holder.
  685. //
  686. // 2/28/92 BryanT dummy.text wasn't being initialized each
  687. // time. As a result, if we were to recurse
  688. // this function, whatever value was in text
  689. // on the last iteration is still there.
  690. // In the case where the macroName doesn't exist
  691. // in the the call to findMacro(), and the old
  692. // dummy->text field contained a '$', the
  693. // function would recurse infinitely.
  694. // Set to an empty string now
  695. //
  696. // q = (list) ? makeNewStrListElement() : &dummy;
  697. if (list != NULL)
  698. {
  699. q = makeNewStrListElement();
  700. }
  701. else
  702. {
  703. dummy.next = NULL;
  704. dummy.text = makeString(" ");
  705. q = &dummy;
  706. }
  707. if (p = findMacro(macroName)) {
  708. // macro names are case sensitive
  709. if (name && !_ftcscmp(name, macroName)) { /* self-refer- */
  710. r = p->values; /* ential macro */
  711. for (i = recLevel; i != levelSeen && r; --i)
  712. r = r->next; /* (a = $a;b) */
  713. q->text = (r) ? r->text : makeString("");
  714. }
  715. else if (ON(p->flags, M_EXPANDING_THIS_ONE)) { /* recursive def */
  716. if (ON(flags, M_ENVIRONMENT_DEF)) {
  717. if (newtail) *newtail = tail;
  718. return(FALSE);
  719. }
  720. else
  721. makeError(currentLine, CYCLE_IN_MACRODEF, macroName);
  722. }
  723. else q->text = p->values->text;
  724. }
  725. if (list) { /* if blding list */
  726. if (!p || ON(p->flags, M_UNDEFINED))
  727. q->text = makeString(""); /* if macro undefd*/
  728. q->next = NULL; /* use NULL as its value */
  729. if (tail) {
  730. tail->next = q;
  731. }
  732. else {
  733. *list = q;
  734. }
  735. tail = q;
  736. } /* if found text, */
  737. if (!p || !_ftcschr(q->text, '$')) continue; /* and found $ in*/
  738. SET(p->flags, M_EXPANDING_THIS_ONE); /* text, recurse */
  739. findMacroValues(q->text, list, &tail, macroName, recLevel+1,
  740. (name && _ftcscmp(name, macroName)? recLevel : levelSeen), flags);
  741. CLEAR(p->flags, M_EXPANDING_THIS_ONE);
  742. }
  743. if (newtail) *newtail = tail;
  744. return(TRUE);
  745. }
  746. #pragma check_stack()
  747. /*
  748. * isolateMacroName -- returns pointer to name of macro in extended invocation
  749. *
  750. * arguments: s pointer to macro invocation
  751. * macro pointer to location to store macro's name
  752. *
  753. * returns: pointer to end of macro's name
  754. */
  755. LOCAL char * NEAR
  756. isolateMacroName(s, macro) /* isolates name and moves s */
  757. char *s; /* past closing paren */
  758. char *macro; /* lexer already ckd for bad */
  759. { /* syntax */
  760. char *t;
  761. for (t = macro; *s && *s != ')' && *s != ':'; *t++ = *s++) {
  762. if (*s == ESCH) {
  763. s++;
  764. if (!MACRO_CHAR(*s))
  765. makeError(currentLine, SYNTAX_BAD_CHAR, *s);
  766. }
  767. }
  768. while (*s != ')') {
  769. if (*s == ESCH)
  770. s++;
  771. if (!*s)
  772. break;
  773. s++;
  774. }
  775. if (*s != ')')
  776. makeError(currentLine, SYNTAX_NO_PAREN);
  777. *t = '\0';
  778. if (t - macro > MAXNAME)
  779. makeError(currentLine, NAME_TOO_LONG);
  780. return(s);
  781. }
  782. /* figures out length of the special macro in question, and returns a ptr to
  783. * thee char after the last char in the invocation
  784. */
  785. LOCAL char * NEAR
  786. checkDynamicDependency(s)
  787. char *s;
  788. {
  789. char *t;
  790. t = s + 1;
  791. if (*t == ESCH) return(t); /* If $^, leave us at the ^ */
  792. if (*t == '(')
  793. if (*++t == ESCH) return(t);
  794. else if (*t == '@') {
  795. if (*++t == ESCH) makeError(currentLine, SYNTAX_BAD_CHAR, *++t);
  796. else if (*t == ')') return(t);
  797. else if (_ftcschr(special2, *t)) {
  798. if (*++t == ESCH) makeError(currentLine, SYNTAX_BAD_CHAR, *++t);
  799. else if (*t == ')') return(t);
  800. }
  801. }
  802. else {
  803. t = s + 1; /* invalid spec. mac. */
  804. if (*t == ESCH) return(t); /* evals. to $( */
  805. return(++t);
  806. }
  807. return(s);
  808. } /* char matched */
  809. /*
  810. * removes and expands any macros that exist in the string macroStr.
  811. * could return a different string (in case expandMacros needs more
  812. * buffer size for macro expansion. it is the caller's responsibility
  813. * to free the string soon as it is not required....
  814. */
  815. char * NEAR
  816. removeMacros(macroStr)
  817. char *macroStr;
  818. {
  819. STRINGLIST *eMacros = NULL;
  820. STRINGLIST *m;
  821. if (_ftcschr(macroStr, '$')) {
  822. findMacroValues(macroStr, &eMacros, NULL, NULL, 0, 0, 0);
  823. #ifdef DEBUG_MACRO_EXPANSION
  824. #ifdef HEAP
  825. heapdump(__FILE__, __LINE__);
  826. #endif
  827. #endif
  828. m = eMacros;
  829. macroStr = expandMacros(macroStr, &eMacros);
  830. #ifdef DEBUG_MACRO_EXPANSION
  831. #ifdef HEAP
  832. heapdump(__FILE__, __LINE__);
  833. #endif
  834. #endif
  835. while (eMacros = m) {
  836. #if 0
  837. /* free macros' text */
  838. FREE(m->text); //NOTE: This is unsafe !!!!
  839. #endif
  840. m = m->next;
  841. FREE_STRINGLIST(eMacros);
  842. }
  843. }
  844. return(macroStr);
  845. }
  846. /*
  847. * expandMacros -- expand all macros in a string s
  848. *
  849. * arguments: s string to expand
  850. * macros list of macros being expanded (for recursive calls)
  851. *
  852. * actions: allocate room for expanded string
  853. * look for macros in string (handling ESCH properly (v1.5))
  854. * parse macro--determine its type
  855. * use putSpecial to handle special macros
  856. * recurse on list of macros
  857. * use putValue to put value of just-found macro in string
  858. * return expanded string
  859. *
  860. * returns: string with all macros expanded
  861. *
  862. * CALLER CHECKS TO SEE IF _ftcschr(STRING, '$') IN ORER TO CALL THIS.
  863. * this doesn't work for HUGE macros yet. need to make data far.
  864. *
  865. * we save the original string and the list of ptrs to macro values
  866. * to be substituted.
  867. * the caller has to free the expansion buffer
  868. */
  869. /*
  870. * expandMacros updates the macros pointer and frees the skipped elements
  871. */
  872. char * NEAR
  873. expandMacros(s, macros)
  874. char *s; /* text to expand */
  875. STRINGLIST **macros;
  876. {
  877. STRINGLIST *p;
  878. char *t,
  879. *end;
  880. char *text, *xresult;
  881. BOOL inQuotes = (BOOL) FALSE; /* flag when inside quote marks */
  882. char *w;
  883. BOOL freeFlag = FALSE;
  884. char resultbuffer[MAXBUF];
  885. unsigned len = MAXBUF;
  886. char *result = resultbuffer;
  887. end = result + MAXBUF;
  888. for (t = result; *s;) { /* look for macros*/
  889. for (; *s && *s != '$'; *t++ = *s++) { /* as we copy the*/
  890. if (t == end) { /* string */
  891. increaseBuffer(&result, &t, &end, &len, &resultbuffer[0]);
  892. }
  893. if (*s == '\"')
  894. inQuotes = (BOOL) !inQuotes;
  895. if (!inQuotes && *s == ESCH) {
  896. *t++ = ESCH;
  897. s++;
  898. if (*s == '\"')
  899. inQuotes = (BOOL) !inQuotes;
  900. }
  901. }
  902. if (t == end) { /* string */
  903. increaseBuffer(&result, &t, &end, &len, &resultbuffer[0]);
  904. }
  905. if (!*s) break; /* s exhausted */
  906. w = (s+1); /* don't check for ^ here; already did in findMacroValues*/
  907. if (*w == '(' /* found a macro */
  908. && _ftcschr(special1, *(w+1))) {
  909. putSpecial(&result, &s, &t, &end, &len, X_SPECIAL_MACRO, &resultbuffer[0]);
  910. continue;
  911. }
  912. else if (*w++ == '$') { /* double ($$) */
  913. if (*w == ESCH) /* $$^... */
  914. putSpecial(&result, &s, &t, &end, &len, DOLLAR_MACRO, &resultbuffer[0]);
  915. else if (*w == '@') /* $$@ */
  916. putSpecial(&result, &s, &t, &end, &len, DYNAMIC_MACRO, &resultbuffer[0]);
  917. else if ((*w == '(')
  918. && (*++w == '@')
  919. && (*w == ')'))
  920. putSpecial(&result, &s, &t, &end, &len, DYNAMIC_MACRO, &resultbuffer[0]);
  921. else if (((*++w=='F') || (*w=='D') || (*w=='B') || (*w=='R'))
  922. && (*++w == ')'))
  923. putSpecial(&result, &s, &t, &end, &len, X_DYNAMIC_MACRO, &resultbuffer[0]);
  924. else putSpecial(&result, &s, &t, &end, &len, DOLLAR_MACRO, &resultbuffer[0]); /* $$ */
  925. continue;
  926. }
  927. else if (_ftcschr(special1, s[1])) { /* $?*< */
  928. putSpecial(&result, &s, &t, &end, &len, SPECIAL_MACRO, &resultbuffer[0]);
  929. continue;
  930. }
  931. if (!*macros)
  932. makeError(currentLine, MACRO_INTERNAL);
  933. //
  934. // skip this element in the macros list
  935. //
  936. if (_ftcschr((*macros)->text, '$')) { /* recurse */
  937. p = *macros;
  938. *macros = (*macros)->next;
  939. text = expandMacros(p->text, macros);
  940. freeFlag = TRUE;
  941. }
  942. else {
  943. text = (*macros)->text;
  944. *macros = (*macros)->next;
  945. }
  946. putValue(&result, &s, &t, &end, text, &len, &resultbuffer[0]);
  947. if (freeFlag) {
  948. FREE(text);
  949. freeFlag = FALSE;
  950. }
  951. }
  952. if (t == end) {
  953. increaseBuffer(&result, &t, &end, &len, &resultbuffer[0]);
  954. }
  955. *t++ = '\0';
  956. //Allocate result buffer
  957. if (!(xresult = rallocate(t-result)))
  958. makeError(currentLine, MACRO_TOO_LONG);
  959. memcpy(xresult, result, t-result);
  960. return(xresult);
  961. }
  962. /*
  963. * increaseBuffer -- increase the size of a string buffer, with error check
  964. *
  965. * arguments: result pointer to pointer to start of buffer
  966. * t pointer to pointer to end of buffer (before expansion)
  967. * end pointer to pointer to end of buffer (after expansion)
  968. * len pointer to amount by which to expand buffer
  969. * first address of initial stack buffer
  970. *
  971. * actions: check for out of memory
  972. * allocate new buffer
  973. * reset pointers properly
  974. *
  975. * modifies: t, end to point to previous end and new end of buffer
  976. *
  977. * uses 0 as line number because by the time we hit an error in this routine,
  978. * the line number will be set at the last line of the makefile (because we'll
  979. * have already read and parsed the file)
  980. */
  981. LOCAL void NEAR
  982. increaseBuffer(result, t, end, len, first)
  983. char **result;
  984. char **t;
  985. char **end;
  986. unsigned *len;
  987. char *first;
  988. {
  989. unsigned newSize;
  990. #ifndef FLAT
  991. if (*len == MAXSEGMENT) /* already at limit */
  992. makeError(currentLine, MACRO_TOO_LONG); /* for memory usage */
  993. #endif
  994. //
  995. // determine if result points to the firstbuffer and make a dynamic copy first.
  996. //
  997. if (*result == first) {
  998. char *p = rallocate(*len);
  999. memcpy(p, *result, *len);
  1000. *result = p;
  1001. }
  1002. newSize = *len + MAXBUF;
  1003. #ifdef DEBUG
  1004. if (fDebug) {
  1005. #ifdef HEAP
  1006. heapdump(__FILE__, __LINE__);
  1007. #endif
  1008. fprintf(stderr,"\t\tAttempting to reallocate %d bytes to %d\n", *len, newSize);
  1009. }
  1010. #endif
  1011. if (!(*result = REALLOC(*result, newSize)))
  1012. makeError(currentLine, MACRO_TOO_LONG);
  1013. *t = *result + *len; /* reset pointers, len*/
  1014. *len = newSize;
  1015. *end = *result + *len;
  1016. }
  1017. /*
  1018. * putSpecial -- expand value of special macro
  1019. *
  1020. * arguments: result ppointer to start of string being expanded
  1021. * name ppointer to macro name being expanded
  1022. * dest ppointer to place to store expanded value
  1023. * end ppointer to end of dest's buffer
  1024. * length pointer to amount by which to increase dest's buffer
  1025. * which type of special macro
  1026. * first address of initial stack buffer
  1027. *
  1028. * actions: depending on type of macro, set "value" equal to macro's value
  1029. * if macro expands to a list, store whole list in "value" ($?, $*)
  1030. * otherwise, modify value according to F, B, D, R flag
  1031. * use putValue to insert the value in dest
  1032. *
  1033. * has to detect error if user tries $* etc. when they aren't defined
  1034. * fix to handle string substitutions, whitespace around names, etc
  1035. * right now list macros are limited to 1024 bytes total
  1036. */
  1037. LOCAL void NEAR
  1038. putSpecial(result, name, dest, end, length, which, first)
  1039. char **result;
  1040. char **name;
  1041. char **dest;
  1042. char **end;
  1043. unsigned *length;
  1044. unsigned which; /* find close paren */
  1045. char *first;
  1046. { /* and move past it */
  1047. char *value = 0;
  1048. STRINGLIST *p;
  1049. BOOL listMacro = FALSE,
  1050. modifier = FALSE,
  1051. star = FALSE;
  1052. unsigned i = 1;
  1053. char c,
  1054. nameBuf[MAXNAME],
  1055. *temp;
  1056. switch (which) {
  1057. case X_SPECIAL_MACRO: i = 2;
  1058. modifier = TRUE;
  1059. case SPECIAL_MACRO: switch ((*name)[i]) {
  1060. case '<': value = dollarLessThan;
  1061. break;
  1062. case '@': value = dollarAt;
  1063. break;
  1064. case '?': value = (char*) dollarQuestion;
  1065. listMacro = TRUE;
  1066. break;
  1067. case '*': if ((*name)[i+1] != '*') {
  1068. value = dollarStar;
  1069. star = TRUE;
  1070. break;
  1071. }
  1072. value = (char*) dollarStarStar;
  1073. listMacro = TRUE;
  1074. ++i;
  1075. break;
  1076. default: break;
  1077. }
  1078. ++i;
  1079. break;
  1080. case X_DYNAMIC_MACRO: i = 4;
  1081. modifier = TRUE;
  1082. case DYNAMIC_MACRO: value = dollarDollarAt;
  1083. break;
  1084. case DOLLAR_MACRO: if (*dest == *end)
  1085. increaseBuffer(result, dest, end, length, first);
  1086. *(*dest)++ = '$';
  1087. *name += 2;
  1088. return;
  1089. default: return; /* can't happen */
  1090. }
  1091. if (!value) {
  1092. for (temp = *name; *temp && *temp != ' ' && *temp != '\t'; temp++);
  1093. c = *temp; *temp = '\0';
  1094. makeError(currentLine, ILLEGAL_SPECIAL_MACRO, *name);
  1095. *temp = c;
  1096. listMacro = FALSE;
  1097. value = makeString(""); // value is freed below, must be on heap [rm]
  1098. }
  1099. if (listMacro) {
  1100. char *pVal, *endVal;
  1101. unsigned lenVal = MAXBUF;
  1102. p = (STRINGLIST*) value;
  1103. pVal = (char *)allocate(MAXBUF);
  1104. endVal = pVal + MAXBUF;
  1105. for (value = pVal; p; p = p->next) {
  1106. temp = p->text;
  1107. if (modifier) temp = modifySpecialValue((*name)[i], nameBuf, temp);
  1108. while(*temp) {
  1109. if (value == endVal)
  1110. increaseBuffer(&pVal, &value, &endVal, &lenVal, NULL);
  1111. *value++ = *temp++;
  1112. }
  1113. if (value == endVal)
  1114. increaseBuffer(&pVal, &value, &endVal, &lenVal, NULL);
  1115. *value = '\0';
  1116. /*
  1117. * Append a space if there are more elements in the list. [RB]
  1118. */
  1119. if (p->next) {
  1120. *value++ = ' ';
  1121. if (value == endVal)
  1122. increaseBuffer(&pVal, &value, &endVal, &lenVal, NULL);
  1123. *value = '\0';
  1124. }
  1125. }
  1126. value = pVal;
  1127. }
  1128. else {
  1129. //For some reason 'buf' was being used here clobbering global 'buf
  1130. // instead of nameBuf
  1131. if (star) value = modifySpecialValue('R', nameBuf, value);
  1132. if (modifier) value = modifySpecialValue((*name)[i], nameBuf, value);
  1133. }
  1134. putValue(result, name, dest, end, value, length, first);
  1135. if (value != dollarAt && value != dollarDollarAt && value != dollarLessThan &&
  1136. (value < nameBuf || value >= nameBuf + MAXNAME))
  1137. FREE(value);
  1138. }
  1139. /*** modifySpecialValue -- alter path name according to modifier ***************
  1140. *
  1141. * Scope:
  1142. * Local.
  1143. *
  1144. * Purpose:
  1145. * The dynamic macros of NMAKE have modifiers F,B,D & R. This routine does the
  1146. * job of producing a modified special value for a given filename.
  1147. *
  1148. * Input:
  1149. * c -- determines the type of modification (modifier is one of F,B,D & R
  1150. * buf -- location for storing modified value
  1151. * value -- The path specification to be modified
  1152. *
  1153. * Output:
  1154. * Returns a pointer to the modified value
  1155. *
  1156. * Errors/Warnings:
  1157. *
  1158. * Assumes:
  1159. * That initially buf pointed to previously allocated memory of size MAXNAME.
  1160. *
  1161. * Modifies Globals:
  1162. *
  1163. * Uses Globals:
  1164. *
  1165. * Notes:
  1166. * Given a path specification of the type "<drive:><path><filename><.ext>", the
  1167. * modifiers F,B,D and R stand for following --
  1168. * F - <filename><.ext> - actual Filename
  1169. * B - <filename> - Base filename
  1170. * D - <drive:><path> - Directory
  1171. * R - <drive:><path><filename> - Real filename (filename without extension)
  1172. * This routine handles OS/2 1.20 filenames as well. The last period in the
  1173. * path specification is the start of the extension. When directory part is null
  1174. * the function returns '.' for current directory.
  1175. *
  1176. * This function now handles quoted filenames too
  1177. *
  1178. *******************************************************************************/
  1179. char * NEAR
  1180. modifySpecialValue(c, buf, value)
  1181. char c;
  1182. char *buf;
  1183. char *value;
  1184. {
  1185. char *lastSlash, //last path separator from "\\/"
  1186. *extension; //points to the extension
  1187. BOOL fQuoted;
  1188. lastSlash = extension = NULL;
  1189. _ftcscpy(buf, value);
  1190. fQuoted = (BOOL) (buf[0] == '"');
  1191. value = buf + _ftcslen(buf) - 1; //start from the end of pathname
  1192. for (;value >= buf; value--)
  1193. if (PATH_SEPARATOR(*value)) { //scan upto first path separator
  1194. lastSlash = value;
  1195. break;
  1196. }
  1197. else if (*value == '.' && !extension) //last '.' is extension
  1198. extension = value;
  1199. switch(c) {
  1200. case 'D': if (lastSlash) {
  1201. if (buf[1] == ':' && lastSlash == buf + 2)
  1202. ++lastSlash; //'d:\foo.obj' --> 'd:\'
  1203. *lastSlash = '\0';
  1204. }
  1205. else if (buf[1] == ':')
  1206. buf[2] = '\0'; //'d:foo.obj' --> 'd:'
  1207. else
  1208. _ftcscpy(buf, "."); //'foo.obj' --> '.'
  1209. break;
  1210. case 'B': if (extension) //for 'B' extension is clobbered
  1211. *extension = '\0';
  1212. case 'F': if (lastSlash)
  1213. buf = lastSlash + 1;
  1214. else if (buf[1] == ':') //'d:foo.obj' --> foo for B
  1215. buf+=2; //'d:foo.obj' --> foo.obj for F
  1216. break;
  1217. case 'R': if (extension)
  1218. *extension = '\0'; //extension clobbered
  1219. }
  1220. if (fQuoted) {
  1221. char *pEnd = _ftcschr(buf, '\0');
  1222. *pEnd++ = '"';
  1223. *pEnd = '\0';
  1224. }
  1225. return(buf);
  1226. }
  1227. /*
  1228. * putValue -- store expanded macro's value in dest and advance past it
  1229. *
  1230. * arguments: result ppointer to start of string being expanded
  1231. * name ppointer to macro name being expanded
  1232. * dest ppointer to place to store expanded value
  1233. * end ppointer to end of dest's buffer
  1234. * source pointer to text of expanded macro
  1235. * length pointer to amount by which to increase dest's buffer
  1236. * first address of initial stack buffer
  1237. *
  1238. * actions: if there is a substitution, call substituteStrings to do it
  1239. * else copy source text into dest
  1240. * advance *name past end of macro's invocation
  1241. *
  1242. * already did error checking in lexer
  1243. */
  1244. LOCAL void NEAR
  1245. putValue(result, name, dest, end, source, length, first)
  1246. char **result;
  1247. char **name;
  1248. char **dest;
  1249. char **end;
  1250. char *source;
  1251. unsigned *length;
  1252. char *first;
  1253. {
  1254. char *s;
  1255. char *t; /* temporary pointer */
  1256. if (*++*name == ESCH) ++*name; /* go past $ & ESCH if any*/
  1257. s = _ftcschr(*name, ':');
  1258. for (t = *name; *t && *t != ')'; t++) /* go find first non-escaped ) */
  1259. if (*t == ESCH) t++;
  1260. if ((**name == '(') /* substitute only if there is */
  1261. && s /* a : before a non-escaped ) */
  1262. && (s < t)) {
  1263. substituteStrings(result, &s, dest, end, source, length, first);
  1264. *name = s;
  1265. }
  1266. else {
  1267. for (; *source; *(*dest)++ = *source++) /* copy source into dest */
  1268. if (*dest == *end) increaseBuffer(result, dest, end, length, first);
  1269. if (**name == '$') ++*name; /* go past $$ */
  1270. if (**name == '(') /* advance from ( to ) */
  1271. while (*++*name != ')');
  1272. else if (**name == '*' && *(*name + 1) == '*') ++*name; /* skip $** */
  1273. ++*name; /* move all the way past */
  1274. }
  1275. }
  1276. /*
  1277. * substituteStrings -- perform macro substitution
  1278. *
  1279. * arguments: result ppointer to start of string being expanded
  1280. * name ppointer to macro name being expanded
  1281. * dest ppointer to place to store substituted value
  1282. * end ppointer to end of dest's buffer
  1283. * source pointer to text of expanded macro (before sub.)
  1284. * length pointer to amount by which to increase dest's buffer
  1285. * first address of initial stack buffer
  1286. *
  1287. * changes: [SB]
  1288. * old, new now dynamically allocated; saves memory; 3 errors detected
  1289. * for macro syntax in script files.
  1290. *
  1291. * note: [SB]
  1292. * we could use lexer routines recursively if we get rid of the globals
  1293. * and then these errors needn't be flagged. [?]
  1294. *
  1295. * actions: store text to convert from in old
  1296. * store text to convert to in new
  1297. * scan source text
  1298. * when a match is found, copy new text into dest &
  1299. * skip over old text
  1300. * else copy one character from source text into dest
  1301. *
  1302. * returns: nothing
  1303. */
  1304. LOCAL void NEAR
  1305. substituteStrings(result, name, dest, end, source, length, first)
  1306. char **result;
  1307. char **name;
  1308. char **dest;
  1309. char **end;
  1310. char *source;
  1311. unsigned *length;
  1312. char *first;
  1313. {
  1314. char *old,
  1315. *new;
  1316. char *pEq, *pPar, *t;
  1317. char *s;
  1318. unsigned i;
  1319. ++*name;
  1320. for (pEq = *name; *pEq && *pEq != '='; pEq++)
  1321. if (*pEq == ESCH)
  1322. pEq++;
  1323. if (*pEq != '=') makeError(line, SYNTAX_NO_EQUALS);
  1324. if (pEq == *name) makeError(line, SYNTAX_NO_SEQUENCE);
  1325. for (pPar = pEq; *pPar && *pPar != ')'; pPar++)
  1326. if (*pPar == ESCH)
  1327. pPar++;
  1328. if (*pPar != ')') makeError(line, SYNTAX_NO_PAREN);
  1329. old = (char *)allocate((pEq - *name) + 1);
  1330. for (s = old, t = *name; *t != '='; *s++ = *t++)
  1331. if (*t == ESCH)
  1332. ++t;
  1333. *s = '\0';
  1334. i = _ftcslen(old);
  1335. new = (char *)allocate(pPar - pEq);
  1336. for (s = new, t++; *t != ')'; *s++ = *t++)
  1337. if (*t == ESCH)
  1338. ++t;
  1339. *s = '\0';
  1340. *name = pPar + 1;
  1341. while (*source) {
  1342. if ((*source == *old) /* check for match*/
  1343. && !_ftcsncmp(source, old, i)) { /* copy new in for*/
  1344. for (s = new; *s; *(*dest)++ = *s++) /* old string */
  1345. if (*dest == *end)
  1346. increaseBuffer(result, dest, end, length, first);
  1347. source += i;
  1348. continue;
  1349. }
  1350. if (*dest == *end)
  1351. increaseBuffer(result, dest, end, length, first);
  1352. *(*dest)++ = *source++; /* else copy 1 char */
  1353. }
  1354. FREE(old);
  1355. FREE(new);
  1356. }
  1357. /*** prependPath -- prepend the path from pszWildcard to pszFilename ***********
  1358. *
  1359. * Scope:
  1360. * Global.
  1361. *
  1362. * Purpose:
  1363. * This function is called to first extract the path (drive & dir parts) from
  1364. * pszWildcard, the prepend that path to pszFilename. The result string is
  1365. * a reconstruction of the full pathname. Normally, the pszWildcard parameter
  1366. * is the same as the first parameter supplied to findFirst(), and pszFilename
  1367. * is what returned by findFirst/findNext.
  1368. *
  1369. * Input:
  1370. * pszWildcard -- Same as the first parameter supplied to findFirst()
  1371. * pszFilename -- Same as the return value of findFirst/FindNext()
  1372. *
  1373. * Output:
  1374. * Return the reconstructed full pathname. The user must be responsible to
  1375. * free up the memory allocated by this string.
  1376. *
  1377. * Errors/Warnings:
  1378. *
  1379. * Assumes:
  1380. * Since pszWildcard, the first parameter to findFirst() must include a filename
  1381. * part; this is what I assume. If the filename part is missing, then
  1382. * _splitpath will mistaken the directory part of pszWildcard as the filename
  1383. * part and things will be very ugly.
  1384. *
  1385. * Modifies Globals:
  1386. * None.
  1387. *
  1388. * Uses Globals:
  1389. * None.
  1390. *
  1391. * Notes:
  1392. *
  1393. * History:
  1394. * 08-Apr-1993 HV Rewrite prependPath() to use _splitpath() and _makepath()
  1395. *
  1396. *******************************************************************************/
  1397. char * NEAR
  1398. prependPath(const char *pszWildcard, const char *pszFilename)
  1399. {
  1400. // The following are the components when breaking up pszWildcard
  1401. char szDrive[_MAX_DRIVE];
  1402. char szDir[_MAX_DIR];
  1403. // The following are the resutling full pathname.
  1404. char szPath[_MAX_PATH];
  1405. char * pszResultPath;
  1406. // First break up the pszWildcard, throwing away the filename and the
  1407. // extension parts.
  1408. _splitpath(pszWildcard, szDrive, szDir, NULL, NULL);
  1409. // Then, glue the drive & dir components of pszWildcard to pszFilename
  1410. _makepath(szPath, szDrive, szDir, pszFilename, NULL);
  1411. // Make a copy of the resulting string and return it.
  1412. pszResultPath = makeString(szPath);
  1413. return (pszResultPath);
  1414. } // prependPath()
  1415. /* isRule -- examines a string to determine whether it's a rule definition
  1416. *
  1417. * arguments: s string to examine for rule-ness
  1418. *
  1419. * actions: assume it's not a rule
  1420. * skip past first brace pair (if any)
  1421. * if next character is a period,
  1422. * look for next brace
  1423. * if there are no path separators between second brace pair,
  1424. * and there's just a suffix after them, it's a rule
  1425. * else if there's another period later on, and no path seps
  1426. * after it, then it's a rule.
  1427. *
  1428. * returns: TRUE if it's a rule, FALSE otherwise.
  1429. */
  1430. BOOL NEAR
  1431. isRule(s)
  1432. char *s;
  1433. {
  1434. char *t = s,
  1435. *u;
  1436. BOOL result = FALSE;
  1437. if (*t == '{') { /* 1st char is {, so */
  1438. while (*++t && *t != '}') /* we skip over rest */
  1439. if (*t == ESCH) ++t;
  1440. if (*t) ++t; /* of path (no error */
  1441. } /* checking) */
  1442. if (*t == '.') {
  1443. for (u = t; *u && *u != '{'; ++u) /* find first non-escaped { */
  1444. if (*u == ESCH) ++u;
  1445. s = t;
  1446. while (t < u) { /* look for path seps. */
  1447. if (PATH_SEPARATOR(*t)) break; /* if we find any, it's*/
  1448. ++t; /* not a rule (they */
  1449. } /* can't be in suffix) */
  1450. if (*u && (t == u)) { /* if not at end & no path sep */
  1451. while (*++u && *u != '}') /* find first non-esc } */
  1452. if (*u == ESCH) ++u;
  1453. if (*u) {
  1454. ++u;
  1455. if (*u == '.' /* if you find it, with . just*/
  1456. && !_ftcschr(u+1, '/' ) /* next to it & no path seps.,*/
  1457. && !_ftcschr(u+1, '\\')) /* it's a rule */
  1458. if (_ftcschr(u+1, '.')) /* too many suffixes */
  1459. makeError(currentLine, TOO_MANY_RULE_NAMES);
  1460. else result = TRUE;
  1461. }
  1462. }
  1463. else if (((u = _ftcspbrk(s+1, "./\\")) && (*u == '.'))
  1464. && !_ftcschr(u+1, '/')
  1465. && !_ftcschr(u+1, '\\'))
  1466. if (_ftcschr(u+1, '.')) /* too many suffixes */
  1467. makeError(currentLine, TOO_MANY_RULE_NAMES);
  1468. else result = TRUE;
  1469. }
  1470. return(result);
  1471. }
  1472. /* ZFormat - extmake syntax worker routine.
  1473. *
  1474. * pStr destination string where formatted result is placed.
  1475. * fmt formatting string. The valid extmake syntax is ...
  1476. * %% is always %
  1477. * %s is the first dependent filename
  1478. * %|<dpfe>F is the appropriate portion out of %s
  1479. * d drive
  1480. * p path
  1481. * f filename
  1482. * e extension
  1483. * %|F same as %s
  1484. * One needn't escape a %, unless it is a valid extmake syntax
  1485. * pFirstDep is the dependent filename used for expansion
  1486. */
  1487. LOCAL BOOL NEAR
  1488. ZFormat(
  1489. char *pStr,
  1490. unsigned limit,
  1491. char *fmt,
  1492. char *pFirstDep
  1493. ) {
  1494. char c;
  1495. char *pEnd = pStr + limit;
  1496. char *s;
  1497. BOOL fError, fDrive, fPath, fFilename, fExtension;
  1498. char buf[CCHMAXPATHCOMP];
  1499. for (; (c = *fmt) && (pStr < pEnd); fmt++) {
  1500. if (c != '%')
  1501. *pStr++ = c;
  1502. else {
  1503. switch (*++fmt) {
  1504. case '%': // '%%' -> '%'
  1505. *pStr++ = '%';
  1506. break;
  1507. case 's':
  1508. for (s = pFirstDep; *s && pStr < pEnd; *pStr++ = *s++)
  1509. ;
  1510. break;
  1511. case '|':
  1512. s = fmt-1;
  1513. fError = fDrive = fPath = fFilename = fExtension = FALSE;
  1514. *buf = '\0';
  1515. do {
  1516. switch (*++fmt) {
  1517. case 'd':
  1518. fDrive = TRUE;
  1519. break;
  1520. case 'p':
  1521. fPath = TRUE;
  1522. break;
  1523. case 'f':
  1524. fFilename = TRUE;
  1525. break;
  1526. case 'e':
  1527. fExtension = TRUE;
  1528. break;
  1529. case 'F':
  1530. if (fmt[-1] == '|') {
  1531. fDrive = TRUE;
  1532. fPath = TRUE;
  1533. fFilename = TRUE;
  1534. fExtension = TRUE;
  1535. }
  1536. break;
  1537. default :
  1538. fError = TRUE;
  1539. break;
  1540. }
  1541. if (fError)
  1542. break;
  1543. } while (*fmt != 'F');
  1544. if (fError) {
  1545. for (; s <= fmt && pStr < pEnd; *pStr++ = *s++)
  1546. ;
  1547. break;
  1548. }
  1549. if (!pFirstDep)
  1550. makeError(0, EXTMAKE_NO_FILENAME);
  1551. if (fDrive)
  1552. drive(pFirstDep, buf);
  1553. if (fPath)
  1554. path(pFirstDep, strend(buf));
  1555. if (fFilename)
  1556. filename(pFirstDep, strend(buf));
  1557. if (fExtension)
  1558. extension(pFirstDep, strend(buf));
  1559. for (s = buf; *s && pStr < pEnd; *pStr++ = *s++)
  1560. ;
  1561. break;
  1562. default:
  1563. *pStr++ = '%';
  1564. if (pStr == pEnd)
  1565. return(TRUE);
  1566. *pStr++ = *fmt;
  1567. break;
  1568. }
  1569. }
  1570. }
  1571. if (pStr < pEnd) {
  1572. *pStr = '\0';
  1573. return(FALSE);
  1574. }
  1575. return(TRUE);
  1576. }
  1577. void NEAR
  1578. expandExtmake(
  1579. char *buf,
  1580. char *fmt,
  1581. char *pFirstDep
  1582. ) {
  1583. if (ZFormat(buf, MAXCMDLINELENGTH, fmt, pFirstDep))
  1584. makeError(0, COMMAND_TOO_LONG, fmt);
  1585. }
  1586. /*** drive -- copy a drive from source to dest if present **********************
  1587. *
  1588. * Scope:
  1589. * Local.
  1590. *
  1591. * Purpose:
  1592. * copy a drive from source to dest if present, return TRUE if we found one
  1593. *
  1594. * Input:
  1595. * const char *src -- The full path to extract the drive from.
  1596. * char *dst -- The buffer to copy the drive to, must be alloc'd before.
  1597. *
  1598. * Output:
  1599. * Return TRUE if a drive part is found, else return FALSE.
  1600. *
  1601. * Errors/Warnings:
  1602. *
  1603. * Assumes:
  1604. * 1. src is a legal pathname.
  1605. * 2. src does not contain network path (i.e. \\foo\bar)
  1606. * 3. The buffer dst is large enough to contain the result.
  1607. * 4. src does not contain quote since _splitpath() treat quotes a normal char.
  1608. *
  1609. * Modifies Globals:
  1610. * None.
  1611. *
  1612. * Uses Globals:
  1613. * None.
  1614. *
  1615. * Notes:
  1616. *
  1617. * History:
  1618. * 31-Mar-1993 HV Rewrite drive(), path(), filename(), and extension() to use
  1619. * _splitpath() instead of parsing the pathname by itself.
  1620. *
  1621. *******************************************************************************/
  1622. LOCAL int NEAR
  1623. drive(const char *src, char *dst)
  1624. {
  1625. _splitpath(src,
  1626. dst, // Drive
  1627. NULL, // Dir
  1628. NULL, // Filename
  1629. NULL); // Extension
  1630. return (0 != _ftcslen(dst));
  1631. }
  1632. /*** extension -- copy a extension from source to dest if present **************
  1633. *
  1634. * Scope:
  1635. * Local.
  1636. *
  1637. * Purpose:
  1638. * copy a drive from source to dest if present, return TRUE if we found one
  1639. *
  1640. * Input:
  1641. * const char *src -- The full path to extract the extension from.
  1642. * char *dst -- The buffer to copy the extension to.
  1643. *
  1644. * Output:
  1645. * Return TRUE if a extension part is found, else return FALSE.
  1646. *
  1647. * Errors/Warnings:
  1648. *
  1649. * Assumes:
  1650. * 1. src is a legal pathname.
  1651. * 2. src does not contain network path (i.e. \\foo\bar)
  1652. * 3. The buffer dst is large enough to contain the result.
  1653. * 4. src does not contain quote since _splitpath() treat quotes a normal char.
  1654. *
  1655. * Modifies Globals:
  1656. * None.
  1657. *
  1658. * Uses Globals:
  1659. * None.
  1660. *
  1661. * Notes:
  1662. *
  1663. * History:
  1664. * 31-Mar-1993 HV Rewrite drive(), path(), filename(), and extension() to use
  1665. * _splitpath() instead of parsing the pathname by itself.
  1666. *
  1667. *******************************************************************************/
  1668. LOCAL int NEAR
  1669. extension(const char *src, char *dst)
  1670. {
  1671. _splitpath(src,
  1672. NULL, // Drive
  1673. NULL, // Dir
  1674. NULL, // Filename
  1675. dst); // Extension
  1676. return (0 != _ftcslen(dst));
  1677. }
  1678. /*** filename -- copy a filename from source to dest if present ****************
  1679. *
  1680. * Scope:
  1681. * Local.
  1682. *
  1683. * Purpose:
  1684. * copy a filename from source to dest if present, return TRUE if we found one
  1685. *
  1686. * Input:
  1687. * const char *src -- The full path to extract the filename from.
  1688. * char *dst -- The buffer to copy the filename to.
  1689. *
  1690. * Output:
  1691. * Return TRUE if a filename part is found, else return FALSE.
  1692. *
  1693. * Errors/Warnings:
  1694. *
  1695. * Assumes:
  1696. * 1. src is a legal pathname.
  1697. * 2. src does not contain network path (i.e. \\foo\bar)
  1698. * 3. The buffer dst is large enough to contain the result.
  1699. * 4. src does not contain quote since _splitpath() treat quotes a normal char.
  1700. *
  1701. * Modifies Globals:
  1702. * None.
  1703. *
  1704. * Uses Globals:
  1705. * None.
  1706. *
  1707. * Notes:
  1708. * BUGBUG: (posible) when src == '..' --> dst = '.', src == '.', dst = ''
  1709. * This is the way _splitpath works.
  1710. *
  1711. * History:
  1712. * 31-Mar-1993 HV Rewrite drive(), path(), filename(), and extension() to use
  1713. * _splitpath() instead of parsing the pathname by itself.
  1714. *
  1715. *******************************************************************************/
  1716. LOCAL int NEAR
  1717. filename(const char *src, char *dst)
  1718. {
  1719. _splitpath(src,
  1720. NULL, // Drive
  1721. NULL, // Directory
  1722. dst, // Filename
  1723. NULL); // Extension
  1724. return (0 != _ftcslen(dst));
  1725. }
  1726. /*** path -- copy a path from source to dest if present ************************
  1727. *
  1728. * Scope:
  1729. * Local.
  1730. *
  1731. * Purpose:
  1732. * copy a path from source to dest if present, return TRUE if we found one
  1733. *
  1734. * Input:
  1735. * const char *src -- The full path to extract the path from.
  1736. * char *dst -- The buffer to copy the path to.
  1737. *
  1738. * Output:
  1739. * Return TRUE if a path part is found, else return FALSE.
  1740. *
  1741. * Errors/Warnings:
  1742. *
  1743. * Assumes:
  1744. * 1. src is a legal pathname.
  1745. * 2. src does not contain network path (i.e. \\foo\bar)
  1746. * 3. The buffer dst is large enough to contain the result.
  1747. * 4. src does not contain quote since _splitpath() treat quotes a normal char.
  1748. *
  1749. * Modifies Globals:
  1750. * None.
  1751. *
  1752. * Uses Globals:
  1753. * None.
  1754. *
  1755. * Notes:
  1756. *
  1757. * History:
  1758. * 31-Mar-1993 HV Rewrite drive(), path(), filename(), and extension() to use
  1759. * _splitpath() instead of parsing the pathname by itself.
  1760. *
  1761. *******************************************************************************/
  1762. LOCAL int NEAR
  1763. path(const char *src, char *dst)
  1764. {
  1765. _splitpath(src,
  1766. NULL, // Drive
  1767. dst, // Directory
  1768. NULL, // Filename
  1769. NULL); // Extension
  1770. return (0 != _ftcslen(dst));
  1771. }
  1772. #if 0 //UNUSED
  1773. /* returns pointer to 1st char not in set
  1774. */
  1775. LOCAL char * NEAR
  1776. strbskip(string, set)
  1777. char *string, *set;
  1778. {
  1779. char *q = set;
  1780. for (;;) {
  1781. if (!*string)
  1782. break;
  1783. if (*string == *q) {
  1784. ++string;
  1785. q = set;
  1786. }
  1787. else if (!*++q)
  1788. break;
  1789. }
  1790. return(string);
  1791. }
  1792. /* returns pointer to 1st char in set or end
  1793. */
  1794. char * NEAR
  1795. strbscan(string, set)
  1796. char *string, *set;
  1797. {
  1798. char *q = set;
  1799. for (;;) {
  1800. if (!*string)
  1801. break;
  1802. if (*string == *q)
  1803. break;
  1804. else if (!*++q) {
  1805. string++;
  1806. q = set;
  1807. }
  1808. }
  1809. return(string);
  1810. }
  1811. #endif
  1812. //
  1813. // On some systems, the environment strings may be allocated as one large block,
  1814. // making it expensive to manipulate them. In that case, we copy each environment
  1815. // strings to its own allocation and release the large block
  1816. //
  1817. #if !defined(FLAT)
  1818. char ** NEAR
  1819. copyEnviron(environ)
  1820. char **environ;
  1821. {
  1822. int envc = envVars(environ);
  1823. char **newEnv;
  1824. int i;
  1825. newEnv = (char **)allocate(envc * sizeof(char *));
  1826. for (i = 0; *environ; ++environ) {
  1827. newEnv[i++] = makeString(*environ);
  1828. #ifdef TEST_ENVIRON
  1829. fprintf(stderr,"%s\n", *environ);
  1830. #endif
  1831. }
  1832. return(newEnv);
  1833. }
  1834. LOCAL int NEAR
  1835. envVars(environ)
  1836. char **environ;
  1837. {
  1838. int i;
  1839. for (i = 1; *environ; environ++, i++)
  1840. ;
  1841. return(i);
  1842. }
  1843. //
  1844. // This function assumes an environment consisting of individually allocated
  1845. // environment variables.
  1846. //
  1847. void NEAR
  1848. freeEnviron(environ)
  1849. char **environ;
  1850. {
  1851. char **prgch = environ;
  1852. for (; *prgch; prgch++)
  1853. FREE(*prgch);
  1854. FREE(environ);
  1855. }
  1856. #endif
  1857. LOCAL STRINGLIST * NEAR
  1858. searchBucket(string, table, hash)
  1859. char *string;
  1860. STRINGLIST *table[];
  1861. unsigned hash;
  1862. {
  1863. char *s, *t;
  1864. STRINGLIST *p;
  1865. for (p = table[hash]; p; p = p->next) {
  1866. #if defined(STATISTICS)
  1867. CnttargetChains++;
  1868. #endif
  1869. for (s = string, t = p->text; *s && *t; s++, t++)
  1870. {
  1871. if (*s == '\\' || *s == '/') /* / == \ in targets */
  1872. if (*t == '\\' || *t == '/')
  1873. continue;
  1874. else
  1875. break;
  1876. else if (_totupper(*s) == _totupper(*t)) /* lc == UC */
  1877. continue;
  1878. else
  1879. break;
  1880. }
  1881. if (!*s && !*t)
  1882. return(p);
  1883. }
  1884. return(NULL);
  1885. }
  1886. int NEAR
  1887. strcmpiquote(str1, str2)
  1888. char *str1;
  1889. char *str2;
  1890. {
  1891. int rc;
  1892. char *s1, *s2;
  1893. char *t;
  1894. #ifdef DEBUG_HEAP
  1895. volatile int fDebug = FALSE;
  1896. if (fDebug) {
  1897. int rc = _heapchk();
  1898. if (rc != _HEAPOK)
  1899. fprintf(stderr,"Heap is screwed up\n");
  1900. #ifdef HEAP
  1901. heapdump(__FILE__, __LINE__);
  1902. #endif
  1903. }
  1904. #endif
  1905. #if defined(STATISTICS)
  1906. CntStriCmp++;
  1907. #endif
  1908. s1 = _alloca(_ftcslen(str1) + 1);
  1909. s2 = _alloca(_ftcslen(str2) + 1);
  1910. if (*str1 == '"')
  1911. str1++;
  1912. for (t = s1;*str1;*t++=*str1++)
  1913. ;
  1914. if (t[-1] == '"')
  1915. t--;
  1916. *t = '\0';
  1917. if (*str2 == '"')
  1918. str2++;
  1919. for (t = s2;*str2;*t++=*str2++)
  1920. ;
  1921. if (t[-1] == '"')
  1922. t--;
  1923. *t = '\0';
  1924. rc = _ftcsicmp(s1, s2);
  1925. return(rc);
  1926. }
  1927. //
  1928. // Remove quotes from a string, if any
  1929. // Returns a copy of the string
  1930. // Note that there may be quotes at the start, the end or either side.
  1931. //
  1932. char * NEAR
  1933. unQuote(str)
  1934. char *str;
  1935. {
  1936. char *s = (char *)rallocate(_ftcslen(str) + 1), *t;
  1937. #if defined(STATISTICS)
  1938. CntunQuotes++;
  1939. #endif
  1940. if (*str == '"')
  1941. str++;
  1942. for (t = s;*str;*t++=*str++)
  1943. ;
  1944. if (t[-1] == '"')
  1945. t--;
  1946. *t = '\0';
  1947. return(s);
  1948. }
  1949. FILE * NEAR
  1950. open_file(name, mode)
  1951. char *name;
  1952. char *mode;
  1953. {
  1954. //If name contains Quotes, remove these before opening the file
  1955. if (*name == '"') {
  1956. *(_ftcsrchr(name, '"')) = '\0';
  1957. _ftcscpy(name, name+1);
  1958. }
  1959. //allow sharing between makes running in different dos boxes
  1960. return(_fsopen(name, mode, _SH_DENYWR));
  1961. }
  1962. /*** TruncateString -- Truncate a string to certain size, take care of MBCS ****
  1963. *
  1964. * Scope:
  1965. * GLOBAL.
  1966. *
  1967. * Purpose:
  1968. * Since an MBCS string can mix double-byte & single-byte characters, simply
  1969. * truncating the string by terminate it with a NULL byte won't work.
  1970. * TruncateString will make sure that the string is cut off at the character
  1971. * boundary.
  1972. *
  1973. * Input:
  1974. * pszString -- The string to be truncated.
  1975. * uLen -- The length to truncate. The final string's length might be
  1976. * less than this be cause of double-byte character.
  1977. *
  1978. * Output:
  1979. * pszString -- The truncated string.
  1980. *
  1981. * Errors/Warnings:
  1982. *
  1983. * Assumes:
  1984. *
  1985. * Modifies Globals:
  1986. * None.
  1987. *
  1988. * Uses Globals:
  1989. * None.
  1990. *
  1991. * Notes:
  1992. *
  1993. * History:
  1994. * 03-Jun-1993 HV Add helper local function TruncateString for findFirst.
  1995. *
  1996. *******************************************************************************/
  1997. void NEAR
  1998. TruncateString(char *pszString, unsigned uLen)
  1999. {
  2000. char *pEnd = pszString; // Points to the end of the string
  2001. unsigned cByte; // Number of bytes to advance depend on lead
  2002. // byte or not
  2003. // Loop to get to the end of the string, exit only when we have exhausted
  2004. // the string, or when the length limit is reached.
  2005. while(*pEnd)
  2006. {
  2007. // If the the character is a lead byte, advance 2 bytes,
  2008. // else, just advance 1 byte.
  2009. #ifdef _MBCS
  2010. cByte = _ismbblead(*pEnd) ? 2 : 1;
  2011. #else
  2012. cByte = 1;
  2013. #endif
  2014. // If we hit the limit by advancing, stop now.
  2015. if (pEnd - pszString + cByte > uLen)
  2016. {
  2017. *pEnd = '\0'; // Truncate it.
  2018. break;
  2019. }
  2020. // Otherwise, advance the pointer to the next character (not byte)
  2021. pEnd += cByte;
  2022. } // while
  2023. } // TruncateString