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.

1354 lines
46 KiB

  1. /* SCCSID = %W% %E% */
  2. /*
  3. * Copyright Microsoft Corporation, 1983-1987
  4. *
  5. * This Module contains Proprietary Information of Microsoft
  6. * Corporation and should be treated as Confidential.
  7. */
  8. /****************************************************************
  9. * *
  10. * LIBRARY PROCESSING ROUTINES *
  11. * *
  12. ****************************************************************/
  13. #include <minlit.h> /* Types and constants */
  14. #include <bndtrn.h> /* Types and constants */
  15. #include <bndrel.h> /* Types and constants */
  16. #include <lnkio.h> /* Linker I/O definitions */
  17. #include <lnkmsg.h> /* Error messages */
  18. #include <extern.h> /* External declarations */
  19. #include <stdlib.h>
  20. #if OSMSDOS
  21. #include <dos.h> /* DOS interface definitions */
  22. #if CPU286
  23. #define INCL_BASE
  24. #include <os2.h> /* OS/2 interface definitions */
  25. #if defined(M_I86LM)
  26. #undef NEAR
  27. #define NEAR
  28. #endif
  29. #endif
  30. #endif
  31. #define DICHDR 0xF1 /* Dictionary header type (F1H) */
  32. #if OSXENIX
  33. #define ShrOpenRd(f) fopen(f,RDBIN)
  34. #endif
  35. #if NEWIO
  36. #include <errno.h> /* System error codes */
  37. #endif
  38. #define PNSORTMAX 512 /* Maximum # modules can be sorted */
  39. typedef struct _edmt /* Extended Dictionary Module Table */
  40. {
  41. WORD page;
  42. WORD list;
  43. }
  44. edmt;
  45. LOCAL FTYPE fUndefHit; /* found this undef in the library */
  46. LOCAL FTYPE fFileExtracted; /* Took file from library flag */
  47. LOCAL FTYPE fUndefsSeen; /* True if externals seen in library */
  48. LOCAL WORD ipnMac; /* Count of page numbers in sort table */
  49. LOCAL WORD *pnSort; /* Sort table for library page numbers */
  50. /* f(ifh) = pointer to dictionary */
  51. LOCAL WORD mpifhcpnHash[IFHLIBMAX];
  52. /* f(ifh) = # pages in hash table */
  53. LOCAL BYTE mpifhAlign[IFHLIBMAX];
  54. /* f(ifh) = lib alignment factor */
  55. LOCAL RBTYPE vrpNewList; /* List of unprocessed files */
  56. LOCAL FTYPE vfLibOpen; /* Library open flag */
  57. #if M_BYTESWAP OR defined( _WIN32 )
  58. #define getfarword getword /* This assumes no far data */
  59. #else
  60. #define getfarword(x) (((WORD FAR *)(x))[0])
  61. #endif
  62. /*
  63. * INTERFACE WITH ASSEMBLY LANGUAGE FUNCTION
  64. */
  65. WORD libAlign; /* Library alignment factor */
  66. WORD libcpnHash; /* Length of hash table in pages */
  67. BYTE FAR *mpifhDict[IFHLIBMAX];
  68. /*
  69. * FUNCTION PROTOTYPES
  70. */
  71. LOCAL unsigned char NEAR OpenLibrary(unsigned char *sbLib);
  72. LOCAL void NEAR FreeDictionary(void);
  73. #if CPU8086 OR CPU286
  74. LOCAL WORD NEAR readfar(int fh, char FAR *buf,int n);
  75. #endif
  76. LOCAL void NEAR GetDictionary(void);
  77. LOCAL WORD NEAR GetLib(void);
  78. LOCAL void ProcessAnUndef(APROPNAMEPTR papropUndef,
  79. RBTYPE rhte,
  80. RBTYPE rprop,
  81. WORD fNewHte);
  82. LOCAL int cdecl FGtNum(const WORD *pn1, const WORD *pn2);
  83. LOCAL void NEAR LookMod(edmt *modtab,unsigned short iMod);
  84. LOCAL void NEAR LookPage(edmt *modtab,unsigned short cMod,unsigned short page);
  85. LOCAL void NEAR ProcExtDic(char *pExtDic);
  86. LOCAL char * NEAR GetExtDic(void);
  87. #if NEW_LIB_SEARCH
  88. /// undef lookaside list
  89. typedef struct tag_UND
  90. {
  91. struct tag_UND * pNext;
  92. APROPNAMEPTR papropUndef;
  93. DWORD dwLibMask;
  94. RBTYPE rhte;
  95. } UND;
  96. #define C_UNDS_POOL 128
  97. typedef struct tag_UNDPOOL
  98. {
  99. struct tag_UNDPOOL *pNext;
  100. UND und[C_UNDS_POOL];
  101. } UNDPOOL;
  102. // pool storage management variables
  103. UNDPOOL *pundpoolCur;
  104. UNDPOOL *pundpoolHead;
  105. int iundPool = C_UNDS_POOL;
  106. UND * pundFree;
  107. UND * pundListHead;
  108. #define FUndefsLeft() (pundListHead != NULL)
  109. void StoreUndef(APROPNAMEPTR, RBTYPE, RBTYPE, WORD);
  110. #else
  111. #define FUndefsLeft() (fUndefsSeen)
  112. #endif
  113. FTYPE fStoreUndefsInLookaside = FALSE;
  114. /////
  115. #if NOASM
  116. LOCAL WORD NEAR rolw(WORD x, WORD n) /* Rotate word left */
  117. {
  118. return(LO16BITS((x << n) | ((x >> (WORDLN - n)) & ~(~0 << n))));
  119. }
  120. LOCAL WORD NEAR rorw(WORD x, WORD n) /* Rotate word right */
  121. {
  122. return(LO16BITS((x << (WORDLN - n)) | ((x >> n) & ~(~0 << (WORDLN - n)))));
  123. }
  124. #endif
  125. #if OSMSDOS
  126. BSTYPE NEAR ShrOpenRd(pname)
  127. char *pname; /* Name of file (null-terminated) */
  128. {
  129. int fh; /* File handle */
  130. #if NEWIO
  131. if(mpifhfh[ifhLibCur])
  132. {
  133. fh = mpifhfh[ifhLibCur];
  134. /* If dictionary not allocated, seek to beginning since we're
  135. * somewhere else now.
  136. */
  137. if(!mpifhDict[ifhLibCur])
  138. _lseek(fh,0L,0);
  139. }
  140. else
  141. fh = SmartOpen(pname,ifhLibCur);
  142. if(fh > 0)
  143. {
  144. fflush(bsInput);
  145. bsInput->_file = (char) fh;
  146. return(bsInput);
  147. }
  148. else
  149. return(NULL);
  150. #else
  151. if((fh = _sopen(pname,O_RDONLY | O_BINARY,SH_DENYWR)) < 0)
  152. return(NULL);
  153. return(fdopen(fh,RDBIN));
  154. #endif
  155. }
  156. #endif /* OSMSDOS */
  157. #pragma check_stack(on)
  158. /****************************************************************
  159. * *
  160. * OpenLibrary: *
  161. * *
  162. * This function takes as its arguments a pointer to the text *
  163. * of the name of the library to open, a count of the bytes in *
  164. * that name, an index into a global table in which to place *
  165. * the file handle for the opened library. It returns TRUE if *
  166. * it succeeds, FALSE if it fails to open the file; and it *
  167. * dies gracefully if the file is not a valid library. *
  168. * *
  169. ****************************************************************/
  170. LOCAL FTYPE NEAR OpenLibrary(sbLib)
  171. BYTE *sbLib; /* Library name */
  172. {
  173. SBTYPE libnam; /* Library name */
  174. WORD reclen; /* Library header record length */
  175. BSTYPE bsLib; /* File stream pointer for library */
  176. memcpy(libnam,&sbLib[1],B2W(sbLib[0]));
  177. /* Copy library name */
  178. libnam[B2W(sbLib[0])] = '\0'; /* Null-terminate name */
  179. /* WARNING: do not assign bsInput to NULL if open fails, it
  180. * screws up NEWIO.
  181. */
  182. if((bsLib = ShrOpenRd(libnam)) != NULL)
  183. { /* If open successful */
  184. bsInput = bsLib;
  185. /* If dictionary already allocated, no need to do anything */
  186. if(mpifhDict[ifhLibCur])
  187. return((FTYPE) TRUE);
  188. #if OSMSDOS
  189. /* Reduce buffer size. We can avoid calling setvbuf() because
  190. * everything is set up properly at this point.
  191. */
  192. #if OWNSTDIO
  193. bsInput->_bsize = 512;
  194. #else
  195. setvbuf(bsInput, bsInput->_base, _IOFBF, 512);
  196. #endif
  197. #endif
  198. if(getc(bsInput) == LIBHDR) /* If we have a library */
  199. {
  200. reclen = (WORD) (3 + WSGets());
  201. /* Get record length */
  202. for(libAlign = 15; libAlign &&
  203. !(reclen & (1 << libAlign)); --libAlign);
  204. /* Calculate alignment factor */
  205. mpifhAlign[ifhLibCur] = (BYTE) libAlign;
  206. if(libAlign >= 4 && reclen == (WORD) (1 << libAlign))
  207. { /* Check legality of alignment */
  208. libHTAddr = (long) WSGets();
  209. libHTAddr += (long) WSGets() << WORDLN;
  210. /* Get the offset of the hash table */
  211. if (libHTAddr <= 0L)
  212. Fatal(ER_badlib,libnam);
  213. if ((mpifhcpnHash[ifhLibCur] = WSGets()) <= 0)
  214. /* Get size of hash table in pages */
  215. Fatal(ER_badlib,libnam);
  216. #if OSMSDOS
  217. /* Restore big buffer size. Avoid calling setvbuf(). */
  218. #if OWNSTDIO
  219. bsInput->_bsize = LBUFSIZ;
  220. #else
  221. setvbuf(bsInput, bsInput->_base, _IOFBF, LBUFSIZ);
  222. #endif
  223. #endif
  224. return((FTYPE) TRUE); /* Success */
  225. }
  226. }
  227. Fatal(ER_badlib,libnam);
  228. }
  229. return(FALSE); /* Failure */
  230. }
  231. #pragma check_stack(off)
  232. /*
  233. * LookupLibSym: look up a symbol in library dictionary
  234. *
  235. * The minimum page size is 16, so we can return paragraph offsets.
  236. * This is a win because offsets are stored as paragraphs in the
  237. * sorting table anyway. Also, the majority of libraries have page
  238. * size of 16.
  239. *
  240. * Parameters:
  241. * char *psb - pointer to length-prefixed string
  242. * Returns:
  243. * Long paragraph offset to location of module which defines
  244. * the symbol, or 0L if not found.
  245. */
  246. #if NOASM
  247. LOCAL WORD NEAR LookupLibSym(psb)
  248. BYTE *psb; /* Symbol to look up */
  249. {
  250. WORD i1; /* First hash value */
  251. WORD d1; /* First hash delta */
  252. WORD i2; /* Second hash value */
  253. WORD d2; /* Second hash delta */
  254. WORD pn; /* Page number */
  255. WORD dpn; /* Page number delta */
  256. WORD pslot; /* Page slot */
  257. WORD dpslot; /* Page slot delta */
  258. WORD ipn; /* Initial page number */
  259. BYTE FAR *hpg;
  260. #if NOASM
  261. WORD ch1; /* Character */
  262. WORD ch2; /* Character */
  263. char *pc1; /* Character pointer */
  264. char *pc2; /* Character pointer */
  265. WORD length; /* Symbol length */
  266. #endif
  267. #if LIBDEBUG
  268. OutSb(stderr,psb);
  269. fprintf(stderr," is wanted; dictionary is %d pages\r\n",libcpnHash);
  270. #endif
  271. #if NOASM
  272. length = B2W(psb[0]); /* Get symbol length */
  273. pc1 = (char *) psb; /* Initialize */
  274. pc2 = (char *) &psb[B2W(psb[0])]; /* Initialize */
  275. i1 = 0; /* Initialize */
  276. d1 = 0; /* Initialize */
  277. i2 = 0; /* Initialize */
  278. d2 = 0; /* Initialize */
  279. while(length--) /* Hashing loop */
  280. {
  281. ch1 = (WORD) (B2W(*pc1++) | 040);/* Force to lower case */
  282. ch2 = (WORD) (B2W(*pc2--) | 040);/* Force to lower case */
  283. i1 = (WORD) (rolw(i1,2) ^ ch1); /* Hash */
  284. d1 = (WORD) (rolw(d1,2) ^ ch2); /* Hash */
  285. i2 = (WORD) (rorw(i2,2) ^ ch2); /* Hash */
  286. d2 = (WORD) (rorw(d2,2) ^ ch1); /* Hash */
  287. }
  288. #else
  289. i1 = libhash(psb,&d1,&i2,&d2); /* Hash */
  290. #endif
  291. pn = (WORD) (i1 % libcpnHash); /* Calculate page number index */
  292. if(!(dpn = (WORD) (d1 % libcpnHash))) dpn = 1;
  293. /* Calculate page number delta */
  294. pslot = (WORD) (i2 % CSLOTMAX); /* Calculate page slot index */
  295. if(!(dpslot = (WORD) (d2 % CSLOTMAX))) dpslot = 1;
  296. /* Calculate page slot delta */
  297. #if LIBDEBUG
  298. fprintf(stderr,"page index %d, delta %d, bucket index %d, delta %d\r\n",
  299. pn,dpn,pslot,dpslot);
  300. #endif
  301. ipn = pn; /* Remember initial page number */
  302. for(;;) /* Search loop */
  303. {
  304. #if LIBDEBUG
  305. fprintf(stderr,"Page %d:\r\n",pn);
  306. #endif
  307. // Get pointer to the dictionary page
  308. hpg = mpifhDict[ifhLibCur] + (pn << LG2PAG);
  309. for(i2 = 0; i2 < CSLOTMAX; ++i2)/* Loop to check slots */
  310. {
  311. #if LIBDEBUG
  312. fprintf(stderr,"Bucket %d %sempty, page %sfull\r\n",
  313. pslot,hpg[pslot]? "not ": "",
  314. B2W(hpg[CSLOTMAX]) == 0xFF? "": "not ");
  315. #endif
  316. if(!(i1 = (WORD) (B2W(hpg[pslot]) << 1)))
  317. { /* If slot is empty */
  318. if(B2W(hpg[CSLOTMAX]) == 0xFF) break;
  319. /* If page is full, break */
  320. return(0); /* Search failed */
  321. }
  322. #if LIBDEBUG
  323. fprintf(stderr," Comparing ");
  324. OutSb(stderr,psb);
  325. fprintf(stderr," to ");
  326. OutSb(stderr,&hpg[i1]);
  327. fprintf(stderr," %signoring case\r\n",fIgnoreCase? "": "not ");
  328. #endif
  329. if(psb[0] == hpg[i1] && SbNewComp(psb,&hpg[i1],fIgnoreCase))
  330. { /* If symbols match */
  331. #if LIBDEBUG
  332. fprintf(stderr,"Match found in slot %d\r\n",i2 >> 1);
  333. #endif
  334. i1 += (WORD) (B2W(hpg[i1]) + 1); /* Skip over name */
  335. i1 = getfarword(&hpg[i1]);
  336. /* Get page number of module */
  337. return(i1); /* Return page number of module */
  338. }
  339. if((pslot += dpslot) >= CSLOTMAX) pslot -= CSLOTMAX;
  340. /* Try next slot */
  341. }
  342. if((pn += dpn) >= libcpnHash) pn -= libcpnHash;
  343. /* Try next page */
  344. if (ipn == pn) return(0); /* Once around without finding it */
  345. }
  346. }
  347. #endif /*NOASM*/
  348. /*
  349. * FreeDictionary : free space allocated for dictionaries
  350. */
  351. LOCAL void NEAR FreeDictionary ()
  352. {
  353. WORD i;
  354. for (i = 0; i < ifhLibMac; ++i)
  355. if (mpifhDict[i])
  356. FFREE(mpifhDict[i]);
  357. }
  358. #if CPU8086 OR CPU286
  359. /*
  360. * readfar : read() with a far buffer
  361. *
  362. * Emulate read() except use a far buffer. Call the system
  363. * directly.
  364. *
  365. * Returns:
  366. * 0 if error, else number of bytes read.
  367. */
  368. LOCAL WORD NEAR readfar (fh, buf, n)
  369. int fh; /* File handle */
  370. char FAR *buf; /* Buffer to store bytes in */
  371. int n; /* # bytes to read */
  372. {
  373. #if OSMSDOS
  374. unsigned bytesread; /* Number of bytes read */
  375. #if CPU8086
  376. if (_dos_read(fh, buf, n, &bytesread))
  377. return(0);
  378. return(bytesread);
  379. #else
  380. if(DosRead(fh,buf,n,(unsigned FAR *) &bytesread))
  381. return(0);
  382. return(bytesread);
  383. #endif
  384. #endif /* OSMSDOS */
  385. #if OSXENIX
  386. char mybuf[PAGLEN];
  387. int cppage;
  388. char *p;
  389. while(n > 0)
  390. {
  391. cppage = n > PAGLEN ? PAGLEN : n;
  392. if(read(fh,mybuf,cppage) != cppage)
  393. return(0);
  394. n -= cppage;
  395. for(p = mybuf; p < mybuf[cppage]; *buf++ = *p++);
  396. }
  397. #endif
  398. }
  399. #endif
  400. LOCAL void NEAR GetDictionary ()
  401. {
  402. unsigned cb;
  403. #if CPU8086 OR CPU286
  404. // If there is more than 128 pages in dictionary return,
  405. // because the dictionary is bigger than 64k
  406. if (libcpnHash >= 128)
  407. return;
  408. #endif
  409. cb = libcpnHash << LG2PAG;
  410. mpifhDict[ifhLibCur] = GetMem(cb);
  411. // Go to the dictionary and read it in a single call
  412. #if defined(M_I386) || defined( _WIN32 )
  413. fseek(bsInput, libHTAddr, 0);
  414. if (fread(mpifhDict[ifhLibCur], 1, cb, bsInput) != (int) cb)
  415. Fatal(ER_badlib,1 + GetPropName(FetchSym(mpifhrhte[ifhLibCur],FALSE)));
  416. #else
  417. _lseek(fileno(bsInput), libHTAddr, 0);
  418. if (readfar(fileno(bsInput), mpifhDict[ifhLibCur], cb) != cb)
  419. Fatal(ER_badlib,1 + GetPropName(FetchSym(mpifhrhte[ifhLibCur],FALSE)));
  420. #endif
  421. }
  422. #pragma check_stack(on)
  423. LOCAL WORD NEAR GetLib(void) /* Open the next library in list */
  424. {
  425. AHTEPTR pahteLib; /* Pointer to library name */
  426. #if OSMSDOS
  427. SBTYPE sbLib; /* Library name */
  428. SBTYPE sbNew; /* New parts to library name */
  429. #endif
  430. if(mpifhrhte[ifhLibCur] == RHTENIL) /* If this library is to be skipped */
  431. {
  432. return(FALSE); /* No library opened */
  433. }
  434. for(;;) /* Loop to open library */
  435. {
  436. pahteLib = (AHTEPTR ) FetchSym(mpifhrhte[ifhLibCur],FALSE);
  437. /* Get name from hash table */
  438. if(OpenLibrary(GetFarSb(pahteLib->cch))) break;
  439. /* Break if lib opened okay */
  440. if(fNoprompt)
  441. Fatal(ER_libopn,1 + GetFarSb(pahteLib->cch));
  442. else
  443. {
  444. sbLib[0] = '\0'; /* No string yet */
  445. UpdateFileParts(sbLib,GetFarSb(pahteLib->cch));
  446. (*pfPrompt)(sbNew,ER_libopn, /* Prompt for new filespec */
  447. (int) (__int64) (1 + GetFarSb(pahteLib->cch)),
  448. P_EnterNewFileSpec, 0);
  449. }
  450. if(fNoprompt || !sbNew[0])
  451. {
  452. mpifhrhte[ifhLibCur] = RHTENIL;
  453. /* Do not bother next time */
  454. return(FALSE); /* Unsuccessful */
  455. }
  456. #if OSMSDOS
  457. UpdateFileParts(sbLib,sbNew); /* Update file name with new parts */
  458. PropSymLookup(sbLib,ATTRFIL,TRUE);
  459. /* Add library to symbol table */
  460. mpifhrhte[ifhLibCur] = vrhte; /* Save virtual address */
  461. AddLibPath(ifhLibCur); /* Add default path spec, maybe */
  462. #endif
  463. }
  464. vfLibOpen = (FTYPE) TRUE; /* A library is open */
  465. libcpnHash = mpifhcpnHash[ifhLibCur];
  466. libAlign = mpifhAlign[ifhLibCur];
  467. if (mpifhDict[ifhLibCur] == NULL) /* If dictionary not allocated, do it */
  468. GetDictionary();
  469. return(TRUE); /* Success */
  470. }
  471. #pragma check_stack(off)
  472. /****************************************************************
  473. * *
  474. * ProcessAnUndef: *
  475. * *
  476. * This function takes as its arguments two pointers, two *
  477. * RBTYPEs, and a flag. It does not return a meaningful *
  478. * value. Most of the parameters to this function are *
  479. * dummies; this function's address is passed as a parameter, *
  480. * and its parameter list must match those of all the *
  481. * functions whose addresses can be passed as a parameter to *
  482. * the same function to which ProcessAnUndef's address is *
  483. * passed. Called by EnSyms. *
  484. * *
  485. ****************************************************************/
  486. LOCAL void ProcessAnUndef(APROPNAMEPTR papropUndef,
  487. RBTYPE rhte,
  488. RBTYPE rprop__NotUsed__,
  489. WORD fNewHte__NotUsed__)
  490. {
  491. AHTEPTR pahte; /* Pointer to hash table entry */
  492. WORD pn; /* Library page number */
  493. APROPUNDEFPTR pUndef;
  494. ATTRTYPE attr;
  495. #if NOT NEWSYM
  496. SBTYPE sb; /* Undefined symbol */
  497. #endif
  498. fUndefHit = FALSE;
  499. pUndef = (APROPUNDEFPTR ) papropUndef;
  500. attr = pUndef->au_flags;
  501. // don't pull out any "weak" externs or unused aliased externals
  502. if (((attr & WEAKEXT) && !(attr & UNDECIDED)) ||
  503. ((attr & SUBSTITUTE) && !(attr & SEARCH_LIB)))
  504. {
  505. fUndefHit = TRUE; // this item is effectively resolved...
  506. return;
  507. }
  508. fUndefsSeen = (FTYPE) TRUE; /* Set flag */
  509. if(!mpifhDict[ifhLibCur] && !vfLibOpen)
  510. return; /* Return if unable to get library */
  511. pahte = (AHTEPTR ) FetchSym(rhte,FALSE);
  512. /* Fetch name from symbol table */
  513. #if NOT NEWSYM
  514. memcpy(sb,pahte->cch,B2W(pahte->cch[0]) + 1);
  515. /* Copy name */
  516. #endif
  517. #if LIBDEBUG
  518. fprintf(stdout,"Looking for '%s' - ", 1+GetFarSb(pahte->cch));
  519. fflush(stdout);
  520. #endif
  521. #if NEWSYM
  522. if(pn = LookupLibSym(GetFarSb(pahte->cch)))
  523. #else
  524. if(pn = LookupLibSym(sb)) /* If symbol defined in this library */
  525. #endif
  526. {
  527. fUndefHit = TRUE;
  528. #if LIBDEBUG
  529. fprintf(stdout,"Symbol found at page %xH\r\n", pn);
  530. fflush(stdout);
  531. #endif
  532. /* We now try to stuff the page number (pn) into a table that will
  533. * be sorted later.
  534. */
  535. if (ipnMac < PNSORTMAX)
  536. {
  537. pnSort[ipnMac++] = pn;
  538. return;
  539. }
  540. /*
  541. * No room to save the file offset so save file directly.
  542. */
  543. pahte = (AHTEPTR ) FetchSym(mpifhrhte[ifhLibCur],FALSE);
  544. /*
  545. * If SaveInput returns 0, then module was seen before. Means
  546. * that dictionary says symbol is defined in this module but
  547. * for some reason, such as IMPDEF, the definition wasn't
  548. * accepted. In this case, we return.
  549. */
  550. if(!SaveInput(GetFarSb(pahte->cch), (long)pn << libAlign, ifhLibCur, 0))
  551. return;
  552. /*
  553. * If first module extracted, save start of file list.
  554. */
  555. if(!fFileExtracted)
  556. {
  557. vrpNewList = vrpropTailFile;
  558. fFileExtracted = (FTYPE) TRUE;
  559. }
  560. }
  561. #if LIBDEBUG
  562. else
  563. {
  564. fprintf(stdout, "Symbol NOT found\r\n"); /* Debug message */
  565. fflush(stdout);
  566. }
  567. #endif
  568. }
  569. #if NEW_LIB_SEARCH
  570. void StoreUndef(APROPNAMEPTR papropUndef, RBTYPE rhte,
  571. RBTYPE rprop, WORD fNewHte)
  572. {
  573. UND * pund;
  574. APROPUNDEFPTR pUndef;
  575. ATTRTYPE attr;
  576. pUndef = (APROPUNDEFPTR ) papropUndef;
  577. attr = pUndef->au_flags;
  578. // don't pull out any "weak" externs or unused aliased externals
  579. if (((attr & WEAKEXT) && !(attr & UNDECIDED)) ||
  580. ((attr & SUBSTITUTE) && !(attr & SEARCH_LIB)))
  581. return;
  582. #ifdef LIBDEBUG
  583. {
  584. AHTEPTR pahte;
  585. pahte = (AHTEPTR) FetchSym(rhte,FALSE);
  586. fprintf(stdout,"Adding '%s'\r\n", 1+GetFarSb(pahte->cch));
  587. fflush(stdout);
  588. }
  589. #endif
  590. if (pundFree) // check free list
  591. {
  592. pund = pundFree;
  593. pundFree = pundFree->pNext;
  594. }
  595. else if (iundPool < C_UNDS_POOL) // check pool
  596. {
  597. pund = &pundpoolCur->und[iundPool];
  598. iundPool++;
  599. }
  600. else
  601. {
  602. // allocate new pool...
  603. pundpoolCur = (UNDPOOL *)GetMem(sizeof(UNDPOOL));
  604. pundpoolCur->pNext = pundpoolHead;
  605. pundpoolHead = pundpoolCur;
  606. pund = &pundpoolCur->und[0];
  607. iundPool = 1; // entry zero is already used up
  608. }
  609. pund->dwLibMask = 0;
  610. pund->pNext = pundListHead;
  611. pund->papropUndef = papropUndef;
  612. pund->rhte = rhte;
  613. pundListHead = pund;
  614. }
  615. #endif
  616. /*
  617. * Greater-than comparator to be used by Sort routine.
  618. */
  619. LOCAL int cdecl FGtNum(const WORD *pn1, const WORD *pn2)
  620. {
  621. if (*pn1 < *pn2)
  622. return(-1);
  623. if (*pn1 > *pn2)
  624. return(1);
  625. return(0);
  626. }
  627. /************************************************************************
  628. * Extended Dictionary
  629. *
  630. * The extended dictionary occurs at the end of the regular dictionary
  631. * and contains a first-level dependency tree for all the modules
  632. * in the library.
  633. ************************************************************************/
  634. #define LIBEXD 0xf2 /* Library EXtended Dictionary */
  635. /****************************************************************
  636. * *
  637. * Extended Dictionary Format: *
  638. * *
  639. * *
  640. * BYTE =0xF2 Extended Dictionary header *
  641. * WORD length of extended dictionary in bytes *
  642. * excluding 1st 3 bytes *
  643. * *
  644. * Start of ext. dictionary: *
  645. * *
  646. * WORD number of modules in library = N *
  647. * *
  648. * Module table, indexed by module number, with N + 1 fixed- *
  649. * length entries: *
  650. * *
  651. * WORD module page number *
  652. * WORD offset from start of ext. dictionary to list *
  653. * of required modules *
  654. * *
  655. * Last entry is null. *
  656. * *
  657. * Module dependency lists, N variable-length lists: *
  658. * *
  659. * WORD list length (number of required modules) *
  660. * WORD module index, 0-based; this is index to module *
  661. * . . . table at the begin of ext. dictionary. *
  662. * . . . *
  663. * *
  664. * *
  665. ****************************************************************/
  666. #pragma loop_opt(on)
  667. /*
  668. * LookMod : look up a module by index in the extended dictionary
  669. *
  670. * Get the list of modules required by the given module. If not
  671. * already marked, save index in sorting table (which will be
  672. * converted to page number later) and mark the entry in the
  673. * module table as seen by setting the low bit of the list offset.
  674. *
  675. * Parameters:
  676. * modtab: Pointer to module table
  677. * iMod: Index into table, 0-based
  678. */
  679. LOCAL void NEAR LookMod (edmt *modtab, WORD iMod)
  680. {
  681. WORD *pw; /* Pointer to list of indexes */
  682. WORD n; /* List counter */
  683. /*
  684. * Get the pointer to the list. Mask off low bit since it is used
  685. * as a marker.
  686. */
  687. pw = (WORD *) ((char *) modtab + (modtab[iMod].list & ~1));
  688. /*
  689. * For every entry in the list, if the corresponding entry in the
  690. * module table is not marked, save the index in pnSort and mark
  691. * the entry in the module table.
  692. */
  693. for(n = *pw++; n--; pw++)
  694. {
  695. if(!(modtab[*pw].list & 1))
  696. {
  697. /*
  698. * Check for table overflow.
  699. */
  700. if(ipnMac == PNSORTMAX)
  701. return;
  702. pnSort[ipnMac++] = *pw;
  703. modtab[*pw].list |= 1;
  704. }
  705. }
  706. }
  707. /*
  708. * LookPage : Look up a module in the module table by page number
  709. *
  710. * Use binary search. If page is found, call LookMod() on the
  711. * matching entry.
  712. *
  713. * Parameters:
  714. * modtab: Pointer to module table
  715. * cMod: Number of entries in table
  716. * page: Page number
  717. * ASSUMES:
  718. * The highest entry in the table has a page number of 0xffff.
  719. */
  720. LOCAL void NEAR LookPage (edmt *modtab, WORD cMod, WORD page)
  721. {
  722. WORD mid; /* Current mid point */
  723. WORD lo, hi; /* Current low and high points */
  724. lo = 0; /* Table is 0-based. */
  725. hi = (WORD) (cMod - 1);
  726. while(lo <= hi)
  727. {
  728. if(modtab[mid = (WORD) ((lo + hi) >> 1)].page == page)
  729. {
  730. modtab[mid].list |= 1;
  731. LookMod(modtab,mid);
  732. return;
  733. }
  734. else if(modtab[mid].page < page)
  735. lo = (WORD) (mid + 1);
  736. else
  737. hi = (WORD) (mid - 1);
  738. }
  739. }
  740. #pragma loop_opt(off)
  741. /*
  742. * ProcExtDic : Process Extended Dictionary
  743. *
  744. * Store in pnSort all the secondary modules required by
  745. * the modules obtained from the regular dictionary lookup.
  746. *
  747. * Parameters:
  748. * pExtDic: Pointer to extended dictionary
  749. */
  750. LOCAL void NEAR ProcExtDic (pExtDic)
  751. char *pExtDic;
  752. {
  753. WORD *p;
  754. WORD *pEnd;
  755. WORD cMod;
  756. edmt *modtab;
  757. cMod = getword(pExtDic);
  758. modtab = (edmt *) (pExtDic + 2);
  759. /* For the binary search algorithm, we make an artifical last entry
  760. * with a page # at least as high as anything else.
  761. */
  762. modtab[cMod].page = 0xffff;
  763. /* Process by page numbers */
  764. for(p = pnSort, pEnd = &pnSort[ipnMac]; p < pEnd; ++p)
  765. LookPage(modtab, cMod, *p);
  766. /* Now pnSort from pEnd to lfaSort[ipnMac] contains module
  767. * index numbers. Process by index number and convert to page.
  768. */
  769. for( ; p < &pnSort[ipnMac]; ++p)
  770. {
  771. LookMod(modtab,*p);
  772. *p = modtab[*p].page;
  773. }
  774. }
  775. /*
  776. * GetExtDic - Get Extended Dictionary
  777. */
  778. LOCAL char * NEAR GetExtDic ()
  779. {
  780. char *p;
  781. int length;
  782. if(!vfLibOpen)
  783. if(!GetLib())
  784. return(NULL);
  785. /* WARNING: we must just have read dictionary for this to work,
  786. * otherwise an fseek() is required here.
  787. */
  788. if (!mpifhDict[ifhLibCur])
  789. {
  790. fflush(bsInput);
  791. fseek(bsInput, libHTAddr + (libcpnHash << LG2PAG), 0);
  792. }
  793. if(getc(bsInput) != LIBEXD)
  794. return(NULL);
  795. if((p = GetMem(length = WSGets())) != NULL)
  796. if(fread(p,1,length,bsInput) != length)
  797. {
  798. FreeMem(p);
  799. p = NULL;
  800. }
  801. return(p);
  802. }
  803. char *pExtDic = NULL; /* Pointer to extended dictionary */
  804. /****************************************************************
  805. * *
  806. * LibrarySearch: *
  807. * *
  808. * This function takes no arguments. It searches all open *
  809. * libraries to resolve undefined externals. It does not *
  810. * return a meaningful value. *
  811. * *
  812. ****************************************************************/
  813. void NEAR LibrarySearch(void)
  814. {
  815. RBTYPE vrpTmpFileFirst;
  816. WORD ifhLibMacInit; /* Initial number of libs to search */
  817. FTYPE searchMore; /* Search continue flag */
  818. WORD bufpnSort[PNSORTMAX];
  819. /* Actual space for pnSort */
  820. SBTYPE sbLibname; /* Name of current library */
  821. AHTEPTR pahte; /* Pointer to hash table entry */
  822. REGISTER WORD i;
  823. FTYPE fLibPass1 = (FTYPE) TRUE;
  824. /* True if on 1st pass thru libs */
  825. FTYPE *fUsedInPass1; /* True if lib used in 1st pass thru libs */
  826. FTYPE fFirstTime; /* True if lib seen for the first time */
  827. extern FTYPE fNoExtDic; /* True if /NOEXTDICTIONARY */
  828. #if NEW_LIB_SEARCH
  829. UND *pund; /* pointer in undef lookaside list */
  830. UND *pundPrev; /* pointer to previous undef entry */
  831. UND *pundNext; /* pointer to next undef entry */
  832. #endif
  833. fUndefsSeen = (FTYPE) TRUE; /* There are undefined externals */
  834. vfLibOpen = FALSE; /* No libraries open yet */
  835. pnSort = bufpnSort; /* Initialize sort table pointer */
  836. ifhLibMacInit = ifhLibMac;
  837. fUsedInPass1 = (FTYPE *) GetMem(ifhLibMac * sizeof(FTYPE));
  838. if (fUsedInPass1 != NULL)
  839. memset(fUsedInPass1, TRUE, ifhLibMac);
  840. #if NEW_LIB_SEARCH
  841. // build up the the lookaside list
  842. EnSyms(StoreUndef,ATTRUND);
  843. fStoreUndefsInLookaside = TRUE;
  844. #endif
  845. do /* Loop to search libraries */
  846. {
  847. searchMore = FALSE; /* Assume on final pass */
  848. for(ifhLibCur = 0; ifhLibCur < ifhLibMac && FUndefsLeft(); ++ifhLibCur)
  849. { /* While undefs and libraries */
  850. #if NEW_LIB_SEARCH
  851. DWORD libMask = (1<<ifhLibCur);
  852. if (pundListHead->dwLibMask & libMask)
  853. continue; // no need to search this library
  854. // the first item in the list has already
  855. // been searched...
  856. #endif
  857. if(!GetLib())
  858. continue;
  859. /*
  860. * If this is first pass through the libraries and /NOEXT was
  861. * not given, try to get the extended dictionary. We assume that
  862. * if there is one then only one library pass is needed.
  863. */
  864. if(fLibPass1 && !fNoExtDic)
  865. pExtDic = GetExtDic();
  866. else
  867. pExtDic = NULL;
  868. /* If no extended dictionary, reduce buffer size because more
  869. * seeking will be done. This will affect remaining libraries
  870. * in search; we don't care about mixed extended and non-
  871. * extended libraries.
  872. */
  873. if(!pExtDic)
  874. setvbuf(bsInput,bsInput->_base,_IOFBF,1024);
  875. pahte = (AHTEPTR ) FetchSym(mpifhrhte[ifhLibCur],FALSE);
  876. /* Get library name */
  877. memcpy(sbLibname,GetFarSb(pahte->cch),B2W(pahte->cch[0])+1);
  878. #if WIN_3 OR C8_IDE
  879. sbLibname[B2W(*sbLibname)+1] = '\0';
  880. #endif
  881. #if WIN_3
  882. StatMsgWin( "%s\r\n", sbLibname+1);
  883. #endif
  884. #if C8_IDE
  885. if(fC8IDE)
  886. {
  887. sprintf(msgBuf, "@I4%s\r\n", sbLibname+1);
  888. _write(fileno(stderr), msgBuf, strlen(msgBuf));
  889. }
  890. #endif
  891. fFirstTime = (FTYPE) TRUE;
  892. while(FUndefsLeft()) /* While there are undefs seen */
  893. {
  894. fFileExtracted = FALSE; /* Assume we won't take anything */
  895. fUndefsSeen = FALSE; /* Assume no more undefs */
  896. ipnMac = 0; /* Initialize sort table count */
  897. #if NOT NEW_LIB_SEARCH
  898. EnSyms(ProcessAnUndef,ATTRUND);
  899. #else
  900. pund = pundListHead;
  901. pundPrev = NULL;
  902. while (pund)
  903. {
  904. if (pund->dwLibMask & libMask)
  905. {
  906. break; // since items are added to the head,
  907. // as soon as we find one item that has
  908. // already been searched, the rest have
  909. // also already been searched...
  910. // pundPrev = pund;
  911. // pund = pund->pNext;
  912. // continue;
  913. }
  914. pundNext = pund->pNext;
  915. if (pund->papropUndef->an_attr == ATTRUND)
  916. ProcessAnUndef(pund->papropUndef, pund->rhte, 0, 0);
  917. else
  918. fUndefHit = TRUE; // no longer undefined -- remove
  919. if (fUndefHit)
  920. {
  921. // remove this item from the undef list...
  922. if (pundPrev)
  923. pundPrev->pNext = pundNext;
  924. else
  925. pundListHead = pundNext;
  926. pund->pNext = pundFree;
  927. pundFree = pund;
  928. }
  929. else
  930. {
  931. pund->dwLibMask |= libMask;
  932. pundPrev = pund;
  933. }
  934. pund = pundNext;
  935. }
  936. #endif
  937. /* Try to resolve references */
  938. /* If no modules obtained, exit loop. */
  939. if(!ipnMac)
  940. {
  941. #if NEWIO
  942. if (fLibPass1)
  943. {
  944. /*
  945. * If this library is seen for the first time in
  946. * the first pass thru libraries and we don't
  947. * pull out any modules from it, then close this
  948. * library, because there are big chances this
  949. * library is not needed.
  950. */
  951. if (fFirstTime)
  952. {
  953. _close(mpifhfh[ifhLibCur]);
  954. mpifhfh[ifhLibCur] = 0;
  955. /*
  956. * Mark it also as not used in pass 1
  957. * so, we can closed it also in the
  958. * next passes thru libs.
  959. */
  960. if (fUsedInPass1)
  961. fUsedInPass1[ifhLibCur] = FALSE;
  962. }
  963. }
  964. else if (fUsedInPass1 && !fUsedInPass1[ifhLibCur])
  965. {
  966. /*
  967. * In pass "n" thru libs close libraries
  968. * not used in pass 1.
  969. */
  970. _close(mpifhfh[ifhLibCur]);
  971. mpifhfh[ifhLibCur] = 0;
  972. }
  973. #endif
  974. break;
  975. }
  976. fFirstTime = FALSE; /* No longer first time seen */
  977. /* If extended dictionary present, process it. */
  978. if(pExtDic)
  979. ProcExtDic(pExtDic);
  980. /* Sort modules by page offset. */
  981. qsort(pnSort, ipnMac, sizeof(WORD),
  982. (int (__cdecl *)(const void *, const void *)) FGtNum);
  983. /*
  984. * Save each module represented in the table.
  985. */
  986. for (i = 0; i < ipnMac; i++)
  987. {
  988. /*
  989. * If SaveInput returns 0, the module was already seen. See
  990. * above comment in ProcessAnUndef().
  991. */
  992. if(!SaveInput(sbLibname, (long)pnSort[i] << libAlign, ifhLibCur, 0))
  993. continue;
  994. if(!fFileExtracted) /* If no files extracted yet */
  995. {
  996. vrpNewList = vrpropTailFile;
  997. /* Save start of file list */
  998. fFileExtracted = (FTYPE) TRUE;
  999. /* We have extracted a file */
  1000. }
  1001. }
  1002. if(!fFileExtracted)
  1003. break; /* If we didn't take anything, break */
  1004. /* Library might not be open because we may have searched
  1005. * an already-loaded dictionary. If necessary, re-open
  1006. * library.
  1007. */
  1008. if(!vfLibOpen)
  1009. GetLib();
  1010. searchMore = (FTYPE) TRUE; /* Otherwise it's worth another pass */
  1011. vrpTmpFileFirst = rprop1stFile;
  1012. /* Save head of module list */
  1013. rprop1stFile = vrpNewList;
  1014. /* Put new modules at head of list */
  1015. fLibPass = (FTYPE) TRUE; /* Processing object from library */
  1016. DrivePass(ProcP1); /* Do pass 1 on object from library */
  1017. fLibPass = FALSE; /* No longer processing lib. object */
  1018. rprop1stFile = vrpTmpFileFirst;
  1019. /* Restore original head of list */
  1020. if (fUsedInPass1 && ifhLibMacInit < ifhLibMac)
  1021. {
  1022. /* DrivePass added more libraries to search */
  1023. /* Reallocate fUsedInPass1 */
  1024. FTYPE *p; /* Temporary pointer */
  1025. p = (FTYPE *) GetMem(ifhLibMac * sizeof(FTYPE));
  1026. if (p == NULL)
  1027. {
  1028. FFREE(fUsedInPass1);
  1029. fUsedInPass1 = NULL;
  1030. }
  1031. else
  1032. {
  1033. memset(p, TRUE, ifhLibMac);
  1034. memcpy(p, fUsedInPass1, ifhLibMacInit);
  1035. FFREE(fUsedInPass1);
  1036. fUsedInPass1 = p;
  1037. }
  1038. ifhLibMacInit = ifhLibMac;
  1039. }
  1040. }
  1041. /* Free space for extended dictionary if present */
  1042. if(pExtDic)
  1043. FFREE(pExtDic);
  1044. if(vfLibOpen)
  1045. {
  1046. #if NOT NEWIO
  1047. fclose(bsInput); /* Close the library */
  1048. #endif
  1049. vfLibOpen = FALSE; /* No library open */
  1050. }
  1051. }
  1052. /* No longer on 1st pass thru libraries. */
  1053. fLibPass1 = FALSE;
  1054. }
  1055. while(searchMore && FUndefsLeft()); /* Do until search done */
  1056. FreeMem(fUsedInPass1);
  1057. FreeDictionary(); /* Free dictionary space */
  1058. /*
  1059. * Restore large buffer size in case it was reduced.
  1060. */
  1061. setvbuf(bsInput,bsInput->_base,_IOFBF,LBUFSIZ);
  1062. #if NEW_LIB_SEARCH
  1063. fStoreUndefsInLookaside = FALSE;
  1064. while (pundpoolHead)
  1065. {
  1066. pundpoolCur = pundpoolHead->pNext;
  1067. FFREE(pundpoolHead);
  1068. pundpoolHead = pundpoolCur;
  1069. }
  1070. #endif
  1071. }
  1072. #if CMDMSDOS
  1073. /*
  1074. * GetLibAll:
  1075. *
  1076. * Process all the modules in a given library in Pass 1.
  1077. * Create property cells for them and insert into the file list.
  1078. */
  1079. void NEAR GetLibAll(sbLib)
  1080. BYTE *sbLib;
  1081. {
  1082. WORD ifh; /* (fake) library index */
  1083. long lfa; /* Current file offset */
  1084. IOVTYPE iov; /* Overlay number */
  1085. RBTYPE rbFileNext; /* Pointer to next file property */
  1086. RBTYPE rbFileNew; /* Pointer to new file property */
  1087. APROPFILEPTR apropFile, apropFilePrev;
  1088. BYTE *sbInput; /* Asciiz filename */
  1089. int fh; /* File handle */
  1090. fDrivePass = FALSE;
  1091. sbInput = sbLib + 1;
  1092. /* Get the ifh, iov, and pointer to the next file from the current
  1093. * file pointer.
  1094. */
  1095. apropFile = (APROPFILEPTR ) FetchSym(vrpropFile,TRUE);
  1096. ifh = apropFile->af_ifh;
  1097. iov = apropFile->af_iov;
  1098. rbFileNext = apropFile->af_FNxt;
  1099. #if NEWIO
  1100. fh = SmartOpen(sbInput,ifh);
  1101. if (fh <= 0 && lpszLIB != NULL)
  1102. fh = SearchPathLink(lpszLIB, sbInput, ifh, TRUE);
  1103. if (fh > 0)
  1104. {
  1105. fflush(bsInput);
  1106. bsInput->_file = (char) fh;
  1107. }
  1108. else
  1109. Fatal(ER_fileopn,sbInput);
  1110. #else
  1111. if((bsInput = fopen(sbInput,RDBIN)) == NULL)
  1112. Fatal(ER_fileopn,sbInput);
  1113. #endif
  1114. if(getc(bsInput) != LIBHDR) /* Check for valid record type */
  1115. Fatal(ER_badlib,sbInput);
  1116. cbRec = (WORD) (3 + WSGets()); /* Get record length */
  1117. for(libAlign = 15; libAlign && !(cbRec & (1 << libAlign)); --libAlign);
  1118. /* Calculate alignment factor */
  1119. fDrivePass = (FTYPE) TRUE;
  1120. /* Reset current file's lfa from 0 to offset of 1st module */
  1121. apropFile->af_lfa = lfa = 1L << libAlign;
  1122. /* Go to the first module */
  1123. fseek(bsInput,lfa,0);
  1124. /* Process the library as follows: Process the current module.
  1125. * Go to the next module; if it starts with DICHDR then we're
  1126. * done. Else, create a new file property cell for the next
  1127. * module, insert it in the file list, and go to start of loop.
  1128. */
  1129. rect = (WORD) getc(bsInput);
  1130. while (rect != DICHDR)
  1131. {
  1132. ungetc(rect, bsInput);
  1133. lfaLast = apropFile->af_lfa = ftell(bsInput);
  1134. ProcP1();
  1135. while (TYPEOF(rect) != MODEND)
  1136. {
  1137. rect = (WORD) getc(bsInput);
  1138. fseek(bsInput, (cbRec = WSGets()), 1);
  1139. }
  1140. do
  1141. {
  1142. rect = (WORD) getc(bsInput);
  1143. }
  1144. while (rect != THEADR && rect != DICHDR && rect != EOF);
  1145. if (rect == DICHDR)
  1146. {
  1147. if (rbFileNext == RHTENIL)
  1148. vrpropTailFile = vrpropFile;
  1149. #if NOT NEWIO
  1150. fclose(bsInput);
  1151. #else
  1152. rbFilePrev = vrpropFile;
  1153. #endif
  1154. return;
  1155. }
  1156. if (rect == EOF)
  1157. Fatal(ER_libeof);
  1158. // Make a new file property cell
  1159. apropFile = (APROPFILEPTR ) PropAdd(vrhteFile, ATTRFIL);
  1160. rbFileNew = vrprop;
  1161. #if ILINK
  1162. apropFile->af_imod = ++imodCur; // allocate a module number
  1163. apropFile->af_cont = 0;
  1164. apropFile->af_ientOnt = 0;
  1165. #endif
  1166. apropFile->af_rMod = 0;
  1167. apropFile->af_ifh = (char) ifh;
  1168. apropFile->af_iov = (IOVTYPE) iov;
  1169. apropFile->af_FNxt = rbFileNext;
  1170. #if SYMDEB
  1171. apropFile->af_publics = NULL;
  1172. apropFile->af_Src = NULL;
  1173. apropFile->af_SrcLast = NULL;
  1174. apropFile->af_cvInfo = NULL;
  1175. #endif
  1176. apropFile->af_ComDat = 0L;
  1177. apropFile->af_ComDatLast = 0L;
  1178. MARKVP();
  1179. // Get the just-processed property file cell
  1180. apropFilePrev = (APROPFILEPTR ) FetchSym(vrpropFile,TRUE);
  1181. apropFilePrev->af_FNxt = rbFileNew;
  1182. vrpropFile = rbFileNew;
  1183. };
  1184. // Remove an empty Lib from the chain of files
  1185. if (vrpropFile == rprop1stFile)
  1186. {
  1187. // If the empty lib is first on list
  1188. rprop1stFile = rbFileNext;
  1189. }
  1190. else
  1191. {
  1192. #if NEWIO
  1193. apropFilePrev = (APROPFILEPTR)FetchSym(rbFilePrev, TRUE);
  1194. apropFilePrev->af_FNxt = apropFile->af_FNxt;
  1195. #endif
  1196. }
  1197. #if NEWIO
  1198. if (rbFileNext == RHTENIL)
  1199. vrpropTailFile = rbFilePrev; // In case we removed the last file
  1200. _close(fileno(bsInput));
  1201. rbFilePrev = vrpropFile;
  1202. #endif
  1203. }
  1204. #endif /*CMDMSDOS*/