home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / MAKEDE.ZIP / OBJUTILS.C < prev    next >
C/C++ Source or Header  |  1992-09-09  |  19KB  |  537 lines

  1. //***** objutils.c  --  Object Library Engine ******
  2.  
  3. #include <stdio.h>
  4. #include <stdlib.h>
  5. #include <string.h>
  6. #include <stdarg.h>
  7. #include <io.h>
  8. #include "objutils.h"
  9.  
  10. typedef struct
  11. {
  12.     unsigned char SymbolDictBlock[DICTBLOCKSIZE]; // symbol dictionary block
  13.     int FreeSpaceIdx;                // cursor to next free symbol space slot
  14.     bool IsFull;                     // is this sym. dict. block full?
  15.     int BlockNumber;                 // current block number
  16. } DICTBLOCK;
  17.  
  18. // The number of pages in the Symbol Dictionary has to be a prime <= 251.
  19. // NOTE: The smallest page number in MS LIB is 2, in Borland TLIB it's 1.
  20.  
  21. static int Primes[] =
  22. {
  23.     2,   3,   5,   7,  11,  13,  17,  19,  23,  29,  31,  37,  41,  43,  47,
  24.    53,  59,  61,  67,  71,  73,  79,  83,  89,  97, 101, 103, 107, 109, 113,
  25.   127, 131, 137, 139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, 197,
  26.   199, 211, 223, 227, 229, 233, 239, 241, 251
  27. };
  28.  
  29. // Symbol Dictionary Block
  30. static DICTBLOCK DictBlock;
  31.  
  32. //  GetLibHeader -- Get header of an object module library. The library 
  33. //  header's ( record type F0) main purpose is to identify this data file as a
  34. //  library, give page size, and size and location of Symbol Dictionary.
  35.  
  36. void GetLibHeader(LIBHDR *LibHeader, FILE *InLibFH)
  37. {
  38.     if (fgetc(InLibFH) != LIBHEADER) 
  39.        Output(Error, NOFILE, "Bogus Library Header\n");
  40.  
  41.     // NOTE: The LIBHDR data structure has been enlarged to include more
  42.     // info than the actual LIB header contains.  As a result, a few more bytes
  43.     // are read in past the actual header when we take sizeof(LIBHDR).  This
  44.     // is no problem since there's plenty to read after the header, anyway!
  45.  
  46.     if (fread(LibHeader, sizeof(LIBHDR), 1, InLibFH) != 1) 
  47.        Output(Error, NOFILE, "Couldn't Read Library Header\n");
  48.  
  49.     // Add in Header length word & checksum byte
  50.     LibHeader->PageSize += 3;
  51.  
  52.     // Determine if LIB includes Microsoft's LIBMOD extension
  53.     // Find the first OBJ module in the LIB file
  54.     if (fseek(InLibFH, (long) LibHeader->PageSize, SEEK_SET) != 0)
  55.        Output(Error, NOFILE, "Seek for first object module failed\n");
  56.  
  57.     LibHeader->IsLIBMODFormat = FindLIBMOD(InLibFH);
  58.  
  59.     LibHeader->IsCaseSensitive = LibHeader->Flags == 0x01 ? true : false;
  60.  
  61.     // Make it clear that we haven't read Symbol Dictionary yet
  62.     DictBlock.BlockNumber = UNDEFINED;
  63. }
  64.  
  65. //  FindModule -- Find a module in Symbol Dictionary and return its file
  66. //  position.  If not found, return -1L.
  67.  
  68. long FindModule(char *ModuleName, LIBHDR *LibHeader, FILE *InLibFH)
  69. {
  70.     char *ObjName;
  71.     DICTENTRY DictEntry;
  72.     char *ExtP;
  73.  
  74.     // Allow extra space for terminating "!\0"
  75.     if ((ObjName = malloc(strlen(ModuleName) + 2)) == NULL) 
  76.        Output(Error, NOFILE, "OBJ Name Memory Allocation Failed\n");
  77.  
  78.     strcpy(ObjName, ModuleName);
  79.  
  80.     // Allow search for module name xxx.obj
  81.     if ((ExtP = strrchr(ObjName, '.')) != NULL)
  82.        *ExtP = '\0';
  83.  
  84.     // NOTE: Module names are stored in LIB's with terminating '!'
  85.     strcat(ObjName, "!");
  86.  
  87.     DictEntry = FindSymbol(ObjName, LibHeader, InLibFH);
  88.  
  89.     free(ObjName);
  90.  
  91.     return (DictEntry.IsFound == true ? DictEntry.ModuleFilePos : -1L);
  92. }
  93.  
  94. //  FindSymbol  --  Find a symbol in Symbol Dictionary by (repeatedly, if 
  95. //  necessary) hashing the symbol and doing dictionary lookup.
  96.  
  97. DICTENTRY FindSymbol(char *SymbolZ, LIBHDR *LibHeader, FILE *InLibFH)
  98. {
  99.     DICTENTRY DictEntry;
  100.     char *SymbolP;
  101.     HashT HashVal;
  102.     int MaxTries;
  103.     int Block, Bucket;
  104.  
  105.     HashVal = Hash(SymbolZ, LibHeader->NumDictBlocks);
  106.     Block = HashVal.BlockHash;
  107.     Bucket = HashVal.BucketHash;
  108.     MaxTries = LibHeader->NumDictBlocks * NUMBUCKETS;
  109.     DictEntry.IsFound = false;
  110.  
  111.     while (MaxTries--)
  112.        {
  113.        DictEntry = GetSymDictEntry(Block, Bucket, LibHeader, InLibFH);
  114.  
  115.        // Three alternatives to check after Symbol Dictionary lookup:
  116.        // 1. If the entry is zero, but the dictionary block is NOT full,
  117.        //    the symbol is not present:
  118.        if (DictEntry.IsFound == false && DictBlock.IsFull == false)
  119.           return (DictEntry);
  120.  
  121.        // 2. If the entry is zero, and the dictionary block is full, the
  122.        //    symbol may have been rehashed to another block; keep looking:
  123.        // 3. If the entry is non-zero, we still have to verify the symbol.
  124.        //    If it's the wrong one (hash clash), keep looking:
  125.        if (DictEntry.IsFound == true)
  126.           {
  127.           // Get the symbol name
  128.           SymbolP = MakeASCIIZ(DictEntry.SymbolP);
  129.  
  130.           // Choose case-sensitive or insensitive comparison as appropriate
  131.           if ((LibHeader->IsCaseSensitive == true ? strcmp(SymbolZ, SymbolP) :
  132.                stricmp(SymbolZ, SymbolP)) == STR_EQUAL)
  133.              {
  134.              free(SymbolP);
  135.              return (DictEntry);
  136.              }
  137.  
  138.           free(SymbolP);
  139.           }
  140.  
  141.        // Cases 2 and 3 (w/o a symbol match) require re-hash:
  142.        Block += HashVal.BlockOvfl;
  143.        Bucket += HashVal.BucketOvfl;
  144.        Block %= LibHeader->NumDictBlocks;
  145.        Bucket %= NUMBUCKETS;
  146.        }
  147.  
  148.     // We never found the entry!
  149.     DictEntry.IsFound = false;
  150.  
  151.     return (DictEntry);
  152. }
  153.  
  154. //  Hash  --  Hash a symbol for Symbol Dictionary entry
  155. //  Inputs: SymbolZ - Symbol in ASCIIZ form; NumHashBlocks - current number of 
  156. //    Symbol Dictionary blocks (MS LIB max. 251 blocks)
  157. //  Outputs: Hash data structure, containing: BlockHash, index of block 
  158. //    containing symbol; BlockOvfl, block index's rehash delta; BucketHash, 
  159. //    index of symbol's bucket (position) on page; BucketOvfl, bucket index's 
  160. //    rehash delta
  161. //  Algorithm: Determine block index, i.e. page number in Symbol Dictionary 
  162. //    where the symbol is to reside, and the bucket index, i.e. the position 
  163. //    within that page (0-36). If this leads to collision, retry with bucket 
  164. //    delta until entire block has turned out to be full. Then, apply block 
  165. //    delta, and start over with original bucket index.
  166.  
  167. HashT Hash(char SymbolZ[], int NumHashBlocks)
  168. {
  169.     HashT SymHash;                     // the resulting aggregate hash values
  170.     unsigned char *SymbolC;            // symbol with prepended count
  171.     int  SymLength;                    // length of symbol to be hashed
  172.     unsigned char *FwdP, *BwdP;        // temp. pts's to string: forward/back.
  173.     unsigned int FwdC, BwdC;           // current char's at fwd/backw. pointers
  174.     unsigned int BlockH, BlockD, BucketH, BucketD;   // temporary values 
  175.     int i;
  176.  
  177.     SymLength = strlen(SymbolZ);
  178.  
  179.     // Make symbol string in Length byte/ASCII string format
  180.     if ((SymbolC = malloc(SymLength + 2)) == NULL)
  181.        Output(Error, NOFILE, "Memory Allocation Failed\n");
  182.  
  183.     SymbolC[0] = (unsigned char) SymLength;
  184.  
  185.     // copy w/o EOS
  186.     strncpy((signed char *) &SymbolC[1], SymbolZ, SymLength + 1);
  187.  
  188.     FwdP = &SymbolC[0];
  189.     BwdP = &SymbolC[SymLength];
  190.     BlockH = BlockD = BucketH = BucketD = 0;
  191.     for (i = 0; i < SymLength; i++)
  192.        {
  193.        // Hashing is done case-insensitive, incl. length byte
  194.        FwdC = (unsigned int) *FwdP++ | 0x20;
  195.        BwdC = (unsigned int) *BwdP-- | 0x20;
  196.  
  197.        // XOR the current character (moving forward or reverse, depending
  198.        // on variable calculated) with the intermediate result rotated
  199.        // by 2 bits (again, left or right, depending on variable).
  200.        // Block Hash: traverse forward, rotate left
  201.        BlockH = FwdC ^ ROL(BlockH, 2);
  202.  
  203.        // Block Overflow delta: traverse reverse, rotate left
  204.        BlockD = BwdC ^ ROL(BlockD, 2);
  205.  
  206.        // Bucket Hash: traverse reverse, rotate right
  207.        BucketH = BwdC ^ ROR(BucketH, 2);
  208.  
  209.        // Bucket Overflow delta: traverse forward, rotate right
  210.        BucketD = FwdC ^ ROR(BucketD, 2);
  211.        }
  212.  
  213.     // NOTE: Results are zero-based
  214.     SymHash.BlockHash = BlockH % NumHashBlocks;
  215.     SymHash.BucketHash = BucketH % NUMBUCKETS;
  216.  
  217.     // Obviously, hash deltas of 0 would be nonsense!
  218.     SymHash.BlockOvfl = max(BlockD % NumHashBlocks, 1);
  219.     SymHash.BucketOvfl = max(BucketD % NUMBUCKETS, 1);
  220.  
  221.     free(SymbolC);
  222.     return (SymHash);
  223. }
  224.  
  225. //  GetSymDictBlock  --  Read and pre-process a Symbol Dictionary block
  226.  
  227. void GetSymDictBlock(int BlockNumber, LIBHDR *LibHeader, FILE *InLibFH)
  228. {
  229.     // Find and read the whole Symbol Dictionary block
  230.     if (fseek(InLibFH, LibHeader->DictionaryOffset + (long) BlockNumber * 
  231.             (long) DICTBLOCKSIZE, SEEK_SET) != 0)
  232.        Output(Error, NOFILE, "Could Not Find Symbol Dictionary\n");
  233.  
  234.     if (fread(DictBlock.SymbolDictBlock, DICTBLOCKSIZE, 1, InLibFH) != 1) 
  235.        Output(Error, NOFILE, "Couldn't Read Library Header\n");
  236.  
  237.     // Is this block all used up?
  238.     DictBlock.FreeSpaceIdx = DictBlock.SymbolDictBlock[NUMBUCKETS];
  239.     DictBlock.IsFull = (DictBlock.FreeSpaceIdx == DICTBLKFULL) ? true : false;
  240.  
  241.     // For future reference, remember block number
  242.     DictBlock.BlockNumber = BlockNumber;
  243. }
  244.  
  245. //  GetSymDictEntry
  246. //  Look up and process a Symbol Dictionary block entry
  247.  
  248. DICTENTRY GetSymDictEntry(int BlockNumber, int BucketNumber, LIBHDR *LibHeader, FILE *InLibFH)
  249. {
  250.     DICTENTRY DictEntry;
  251.     unsigned char SymbolOffset;
  252.     unsigned char SymbolLength;
  253.     int PageNumber;
  254.     // Remember entry's block/bucket and init. to no (NULL) entry
  255.     DictEntry.BlockNumber = BlockNumber;
  256.     DictEntry.BucketNumber = BucketNumber;
  257.     DictEntry.SymbolP = NULL;   
  258.     DictEntry.IsFound = false;
  259.  
  260.     // Make sure the appropriate block was already read from obj. mod. library
  261.     if (DictBlock.BlockNumber != BlockNumber)
  262.         GetSymDictBlock(BlockNumber, LibHeader, InLibFH);
  263.  
  264.     // WORD offset of symbol in dictionary block: 0 means no entry
  265.     SymbolOffset = DictBlock.SymbolDictBlock[BucketNumber];
  266.  
  267.     if (SymbolOffset != 0)
  268.        {
  269.        // Since it's word offset, need to multiply by two
  270.        DictEntry.SymbolP = &DictBlock.SymbolDictBlock[SymbolOffset * 2];
  271.  
  272.        // Get the symbol's object module offset in LIB
  273.        SymbolLength = *DictEntry.SymbolP;
  274.  
  275.        // Object module's LIB page number is right after symbol string
  276.        PageNumber = *(int *) (DictEntry.SymbolP + SymbolLength + 1);
  277.  
  278.        DictEntry.ModuleFilePos = (long) PageNumber * (long)LibHeader->PageSize;
  279.        DictEntry.IsFound = true;
  280.        }
  281.     return (DictEntry);
  282. }
  283.  
  284. //  GetModuleName -- Read the OMF module header record (THEADR - 80h) or, if
  285. //    present, MS's LIBMOD extension record type. NOTE: For Microsoft C, 
  286. //    THEADR reflects the source code name file at compilation time. OBJ name 
  287. //    may differ from this; the LIBMOD record will contain its name. For 
  288. //    Borland C++, THEADR is the only pertinent record and will contain OBJ 
  289. //    module's name rather than the source's.
  290.  
  291. char *GetModuleName(long ModuleFilePos, LIBHDR *LibHeader, FILE *InLibFH)
  292. {
  293.     int SymbolLength;
  294.     char *ModuleName;
  295.     OMFHEADER OmfHeader;
  296.  
  297.     // Position at beginning of pertinent object module
  298.     if (fseek(InLibFH, ModuleFilePos, SEEK_SET) != 0)
  299.         Output(Error, NOFILE, "Seek for object module at %lx failed\n", ModuleFilePos);
  300.  
  301.     if (LibHeader->IsLIBMODFormat == false)
  302.        {
  303.        if (fread(&OmfHeader, sizeof(OmfHeader), 1, InLibFH) != 1)
  304.           Output(Error, NOFILE, "Couldn't Read THEADR at %lx\n", ModuleFilePos);
  305.  
  306.        if (OmfHeader.RecType != THEADR)
  307.           Output(Error, NOFILE, "Bogus THEADR OMF record at %lx\n", ModuleFilePos);
  308.        }
  309.     else
  310.        if (FindLIBMOD(InLibFH) == false)
  311.           {
  312.           Output(Warning, NOFILE, "No LIBMOD record found at %lx\n", ModuleFilePos);
  313.           return (NULL);
  314.           }
  315.  
  316.     SymbolLength = fgetc(InLibFH);
  317.  
  318.     if ((ModuleName = malloc(SymbolLength + 1)) == NULL)
  319.        Output(Error, NOFILE, "Malloc failure Reading module name\n");
  320.  
  321.     if (fread(ModuleName, SymbolLength, 1, InLibFH) != 1) 
  322.        Output(Error, NOFILE, "Couldn't Read THEADR\n");
  323.  
  324.     ModuleName[SymbolLength] = '\0';
  325.  
  326.     return(ModuleName);
  327. }
  328.  
  329. //  FindLIBMOD  --  Get a LIBMOD (A3) comment record, if present.
  330. //  NOTE: This is a special OMF COMENT (88h) record comment class used by
  331. //  Microsoft only.  It provides the name of the object modules which may 
  332. //  differ from the source (contained in THEADR). This record is added when an
  333. //  object module is put into library, and stripped out when it's extracted. 
  334. //  This routine will leave file pointer at the LIBMOD name field.
  335.  
  336. bool FindLIBMOD(FILE *InLibFH)
  337. {
  338.     COMENTHEADER CommentHdr;
  339.  
  340.     // Search (up to) all COMENT records in OBJ module
  341.     while (FindObjRecord(InLibFH, COMENT) == true)
  342.        {
  343.        if (fread(&CommentHdr, sizeof(CommentHdr), 1, InLibFH) != 1)
  344.           Output(Error, NOFILE, "Couldn't Read OBJ\n");
  345.  
  346.        if (CommentHdr.CommentClass == LIBMOD)
  347.            return (true);
  348.        else
  349.           // if not found: forward to next record, and retry
  350.           if (fseek(InLibFH, (long) CommentHdr.RecLength
  351.                                     - sizeof(CommentHdr)
  352.                                     + sizeof(OMFHEADER), SEEK_CUR) != 0)
  353.              Output(Error, NOFILE, "Seek retry for LIBMOD failed\n");
  354.        }
  355.  
  356.     // We got here only if COMENT of class LIBMOD was never found
  357.     return (false);
  358. }
  359.  
  360. //  FindObjRecord  --  Find an object module record in one given module.
  361. //  On call, file pointer must be set to an objec record.  Search will
  362. //  quit at the end of current module (or when record found).
  363.  
  364. bool FindObjRecord(FILE *ObjFH, unsigned char RecType)
  365. {
  366.     OMFHEADER ObjHeader;
  367.  
  368.     while (fread(&ObjHeader, sizeof(ObjHeader), 1, ObjFH) == 1)
  369.        {
  370.        // If it's the record type we're looking for, we're done
  371.        if (ObjHeader.RecType == RecType)
  372.           {
  373.           // Return with obj module set to record requested
  374.           if (fseek(ObjFH, -(long) sizeof(ObjHeader), SEEK_CUR) != 0)
  375.              Output(Error, NOFILE, "Seek for Record Type %02x failed\n", RecType & 0xFF);
  376.           return (true);
  377.           }
  378.  
  379.        // End of object module, record type NEVER found
  380.        if (ObjHeader.RecType == MODEND)
  381.           return (false);
  382.  
  383.        // Forward file pointer to next object module record
  384.        if (fseek(ObjFH, (long) ObjHeader.RecLength, SEEK_CUR) != 0)
  385.           Output(Error, NOFILE, "Seek retry for Record Type %02x failed\n", RecType & 0xFF);
  386.        }
  387.  
  388.     // If this quit due to I/O condition, it's either EOF or I/O error
  389.     if (feof(ObjFH) == 0) 
  390.        Output(Error, NOFILE, "Couldn't Read OBJ\n");
  391.  
  392.     // we completed w/o error and w/o finding the record (should NEVER happen)
  393.     return (false);
  394. }
  395.  
  396. //  ExtractModule -- Find an object module in a library and extract it into
  397. //  "stand-alone" object file.  Return true if ok, else false. 
  398. //  Optional: Can specify a new name for the module.
  399.  
  400. bool ExtractModule(char *ModuleName, char *NewModuleName, LIBHDR *LibHeader, FILE *InLibFH)
  401. {
  402.     long FilePos;
  403.     char *NewObjP;
  404.     char *NewObjName;
  405.     FILE *NewObjFH;
  406.  
  407.     // Find the object module's position in the library file
  408.     FilePos = FindModule(ModuleName, LibHeader, InLibFH);
  409.  
  410.     if (FilePos == -1L)
  411.         return (false);
  412.  
  413.     // Determine name for new .obj, and set it up
  414.     NewObjP = NewModuleName != NULL ? NewModuleName : ModuleName;
  415.  
  416.     if ((NewObjName = malloc(strlen(NewObjP) + 5)) == NULL)
  417.        Output(Error, NOFILE, "Malloc failure Making module name %s\n", NewObjP);
  418.  
  419.     strcpy(NewObjName, NewObjP);
  420.  
  421.     // Open the new .obj file, and pass everything off to low-level routine
  422.     if ((NewObjFH = fopen(NewObjName, "wb")) == NULL)
  423.        Output(Error, NOFILE, "Open failure new module %s\n", NewObjName);
  424.  
  425.     CopyObjModule(NewObjFH, FilePos, InLibFH);
  426.  
  427.     fclose(NewObjFH);
  428.  
  429.     free(NewObjName);
  430.  
  431.     return (true);
  432. }
  433.  
  434. //  CopyObjModule  --  Low-level copy of LIB member to OBJ file.
  435.  
  436. void CopyObjModule(FILE *NewObjFH, long FilePos, FILE *InLibFH)
  437. {
  438.     OMFHEADER RecHdr;
  439.  
  440.     // Get to the object module in LIB 
  441.     if (fseek(InLibFH, FilePos, SEEK_SET) != 0)
  442.        Output(Error, NOFILE, "Seek failure to file position %ld\n", FilePos);
  443.  
  444.     // Write module from LIB to separate obj file
  445.     do {
  446.        // Read OMF header record, this will give record type and length
  447.        if (fread(&RecHdr, sizeof(RecHdr), 1, InLibFH) != 1)
  448.           Output(Error, NOFILE, "Couldn't Read OBJ\n");
  449.  
  450.        // Need to check every COMENT record to make sure to strip LIBMOD out
  451.        if (RecHdr.RecType == COMENT)
  452.           {
  453.           // Throw away next byte (Attrib COMENT byte) for now
  454.           fgetc(InLibFH);
  455.  
  456.           // Check COMENT's Comment Class
  457.           // If it's a LIBMOD, set file pointer ro next record and continue
  458.           if (fgetc(InLibFH) == LIBMOD)
  459.              {
  460.              if (fseek(InLibFH, (long) RecHdr.RecLength - 2L, SEEK_CUR) != 0)
  461.                  Output(Error, NOFILE, "Seek error on COMENT\n");
  462.              continue;
  463.              }
  464.           else
  465.              // Wasn't a LIBMOD: reset file pointer to continue normally
  466.              if (fseek(InLibFH, -2L, SEEK_CUR) != 0)
  467.                 Output(Error, NOFILE, "Seek error on COMENT\n");
  468.           }
  469.  
  470.        if (fwrite(&RecHdr, sizeof(RecHdr), 1, NewObjFH) != 1)
  471.           Output(Error, NOFILE, "Couldn't Write new OBJ\n");
  472.  
  473.        while (RecHdr.RecLength--)
  474.           fputc(fgetc(InLibFH), NewObjFH);
  475.  
  476.        } while (RecHdr.RecType != MODEND);
  477. }
  478.  
  479.  
  480. //****** --  Service functions *******
  481.  
  482. // MakeASCIIZ - Take a string of 1-byte length/data format, and make it ASCIIZ.
  483.  
  484. char *MakeASCIIZ(unsigned char *LString)
  485. {
  486.     char *ASCIIZString;
  487.     unsigned char StringLength;
  488.  
  489.     StringLength = *LString++;
  490.  
  491.     if ((ASCIIZString = malloc((int) StringLength + 1)) == NULL) 
  492.         return (NULL);
  493.  
  494.     strncpy(ASCIIZString, (signed char *) LString, StringLength);
  495.  
  496.     ASCIIZString[StringLength] = '\0';
  497.  
  498.     return (ASCIIZString);
  499. }
  500.  
  501. // Output -- Write to the output stream. This function adds an exception-
  502. // handling layer to disk IO. It handles abnormal program termination, and 
  503. // warnings to both stderr and output. Three types of message can be handled: 
  504. // Message, simply printed to a file; Warning, print to file AND stderr; 
  505. // Error, same as warning, but terminate with abnormal exit code.
  506.  
  507. void Output(MESSAGETYPE MsgType, FILE *Stream, char *OutputFormat, ...)
  508. {
  509.     char OutputBuffer[133];
  510.     va_list VarArgP;
  511.  
  512.     va_start(VarArgP, OutputFormat);
  513.     vsprintf(OutputBuffer, OutputFormat, VarArgP);
  514.  
  515.     // If this is (non-fatal) warning or (fatal) error, also send it to stderr 
  516.     if (MsgType != Message)
  517.        fprintf(stderr, "\a%s", OutputBuffer);
  518.  
  519.     // In any case: attempt to print message to output file.  Exception check. 
  520.     if (Stream != NOFILE)
  521.        if ((size_t) fprintf(Stream, OutputBuffer) != strlen(OutputBuffer))
  522.           {
  523.           fprintf(stderr, "\aDisk Write Failure!\n");
  524.           abort();
  525.           }
  526.  
  527.     /* If this was (fatal) error message, abort on the spot */
  528.     if (MsgType == Error)
  529.        {
  530.        flushall();
  531.        fcloseall();
  532.        abort();
  533.        }
  534.  
  535.     va_end(VarArgP);
  536. }
  537.