/* SCCSID = %W% %E% */ /* * Copyright Microsoft Corporation, 1983-1987 * * This Module contains Proprietary Information of Microsoft * Corporation and should be treated as Confidential. */ /**************************************************************** * * * NEWUTL.C * * * * Linker utilities. * * * ****************************************************************/ #include /* Types, constants */ #include /* More types and constants */ #include /* More types and constants */ #include /* Linker I/O definitions */ #include /* Error messages */ #include /* CodeView support */ #include /* External declarations */ #include /* Near message strings */ #include #include #if EXE386 #include #endif #if NEWIO #include /* System error codes */ #endif #if USE_REAL #if NOT defined( _WIN32 ) #define i386 #include #endif // The memory sizes are in paragraphs. #define TOTAL_CONV_MEM (0xFFFF) #define CONV_MEM_FOR_TNT (0x800) // 32K of memory #define MIN_CONV_MEM (0x1900) // 100 K of memory typedef unsigned short selector_t ; //Define type to hold selectors static selector_t convMemSelector ; // Selector to conv memory. static short noOfParagraphs ; // size of the available blocks in paragraphs static int realModeMemPageable ; // = FALSE #endif #if WIN_NT OR DOSX32 unsigned char FCHGDSK(int drive) { return(FALSE); } #endif #define DISPLAY_ON FALSE #if DISPLAY_ON extern int TurnDisplayOn; #endif APROPCOMDATPTR comdatPrev=NULL; /* Pointer to symbol table entry */ int fSameComdat=FALSE; /* Set if LINSYM to the same COMDAT */ /******************************************************************** * INPUT ROUTINES * ********************************************************************/ /*** GetLineOff - read part of LINNUM record * * Purpose: * This function reads line/offset pair from LINNUM record. It is here * because we want to keep all the I/O functions near and the LINNUM * processing is performed in NEWDEB.C which resides in another segment. * * Input: * - pLine - pointer to line number * - pRa - pointer to offset * * Output: * Returns line/offset pair from OMF record. * * Exceptions: * None. * * Notes: * None. * *************************************************************************/ void GetLineOff(WORD *pLine, RATYPE *pRa) { *pLine = WGets() + QCLinNumDelta; // Get line number // Get code segment offset #if OMF386 if (rect & 1) *pRa = LGets(); else #endif *pRa = (RATYPE) WGets(); } /*** GetGsnInfo - read the segment index of the LINNUM * * Purpose: * This function reads the segemnt index from LINNUM record. It is here * because we want to keep all the I/O functions near and the LINNUM * processing is performed in NEWDEB.C which resides in another segment. * * Input: * - pRa - pointer to offset correction for COMDATs * * Output: * Returns global segment index and for lines in COMDAT record * offset correction. * * Exceptions: * None. * * Notes: * None. * *************************************************************************/ WORD GetGsnInfo(GSNINFO *pInfo) { WORD fSuccess; // TRUE if everything is OK WORD attr; // COMDAT flags WORD comdatIdx; // COMDAT symbol index APROPCOMDATPTR comdat; // Pointer to symbol table entry fSuccess = TRUE; if (TYPEOF(rect) == LINNUM) { // Read regular LINNUM record GetIndex((WORD)0,(WORD)(grMac - 1)); // Skip group index pInfo->gsn = mpsngsn[GetIndex((WORD)1,(WORD)(snMac - 1))]; // Get global SEGDEF number pInfo->comdatRa = 0L; pInfo->comdatSize = 0L; pInfo->fComdat = FALSE; } else { // Read LINSYM record - line numbers for COMDAT attr = (WORD) Gets(); comdatIdx = GetIndex(1, (WORD)(lnameMac - 1)); comdat = (APROPCOMDATPTR ) PropRhteLookup(mplnamerhte[comdatIdx], ATTRCOMDAT, FALSE); fSameComdat = FALSE; if (comdat != NULL) { if(comdat == comdatPrev) fSameComdat = 1; else comdatPrev = comdat; if ((fPackFunctions && !(comdat->ac_flags & REFERENCED_BIT)) || !(comdat->ac_flags & SELECTED_BIT) || comdat->ac_obj != vrpropFile) { SkipBytes((WORD)(cbRec - 1)); fSuccess = FALSE; } else { pInfo->gsn = comdat->ac_gsn; pInfo->comdatRa = comdat->ac_ra; pInfo->comdatSize = comdat->ac_size; pInfo->comdatAlign= comdat->ac_align; pInfo->fComdat = TRUE; } } else { SkipBytes((WORD)(cbRec - 1)); fSuccess = FALSE; } } return(fSuccess); } /**************************************************************** * * * Gets: * * * * Read a byte of input and return it. * * * ****************************************************************/ #if NOASM #if !defined( M_I386 ) && !defined( _WIN32 ) WORD NEAR Gets(void) { REGISTER WORD b; if((b = getc(bsInput)) == EOF) InvalidObject(); /* After reading the byte, decrement the OMF record counter. */ --cbRec; return(b); } #endif #endif #if ALIGN_REC #else /**************************************************************** * * * WGetsHard: * * * * Read a word of input and return it. * * * ****************************************************************/ WORD NEAR WGetsHard() { REGISTER WORD w; // handle hard case... easy case already tested in WGets w = Gets(); /* Get low-order byte */ return(w | (Gets() << BYTELN)); /* Return word */ } #if OMF386 /**************************************************************** * * * LGets: * * * * Read a long word of input and return it. * * * ****************************************************************/ DWORD NEAR LGets() { DWORD lw; FILE * f = bsInput; // NOTE: this code will only work on a BigEndian machine if (f->_cnt >= sizeof(DWORD)) { lw = *(DWORD *)(f->_ptr); f->_ptr += sizeof(DWORD); f->_cnt -= sizeof(DWORD); cbRec -= sizeof(DWORD); return lw; } lw = WGets(); /* Get low-order word */ return(lw | ((DWORD) WGets() << 16));/* Return long word */ } #endif #endif #if 0 /**************************************************************** * * * GetBytes: * * * * Read n bytes from input. * * If n is greater than SBLEN - 1, issue a fatal error. * * * ****************************************************************/ void NEAR GetBytes(pb,n) BYTE *pb; /* Pointer to buffer */ WORD n; /* Number of bytes to read in */ { FILE *f = bsInput; if(n >= SBLEN) InvalidObject(); if (n <= f->_cnt) { memcpy(pb,f->_ptr, n); f->_cnt -= n; f->_ptr += n; } else fread(pb,1,n,f); /* Ask for n bytes */ cbRec -= n; /* Update byte count */ } #endif #if 0 /**************************************************************** * * * SkipBytes: * * * * Skip n bytes of input. * * * ****************************************************************/ void NEAR SkipBytes(n) REGISTER WORD n; /* Number of bytes to skip */ { #if WIN_NT WORD cbRead; SBTYPE skipBuf; cbRec -= n; // Update byte count while (n) // While there are bytes to skip { cbRead = n < sizeof(SBTYPE) ? n : sizeof(SBTYPE); if (fread(skipBuf, 1, cbRead, bsInput) != cbRead) InvalidObject(); n -= cbRead; } #else FILE *f = bsInput; if (f->_cnt >= n) { f->_cnt -= n; f->_ptr += n; } else if(fseek(f,(long) n,1)) InvalidObject(); cbRec -= n; /* Update byte count */ #endif } #endif /**************************************************************** * * * GetIndexHard: (GetIndex -- hard case) * * * * This function reads in a variable-length index field from * * the input file. It takes as its arguments two word values * * which represent the minimum and maximum allowable values * * the index. The function returns the value of the index. * * See p. 12 in "8086 Object Module Formats EPS." * * * ****************************************************************/ WORD NEAR GetIndexHard(imin,imax) WORD imin; /* Minimum permissible value */ WORD imax; /* Maximum permissible value */ { REGISTER WORD index; FILE *f = bsInput; if (f->_cnt >= sizeof(WORD)) { index = *(BYTE *)(f->_ptr); if (index & 0x80) { index <<= BYTELN; index |= *(BYTE *)(f->_ptr+1); index &= 0x7fff; f->_cnt -= sizeof(WORD); f->_ptr += sizeof(WORD); cbRec -= sizeof(WORD); } else { f->_cnt--; f->_ptr++; cbRec--; } } else { if((index = Gets()) & 0x80) index = ((index & 0x7f) << BYTELN) | Gets(); } if(index < imin || index > imax) InvalidObject(); return(index); /* Return a good value */ } /******************************************************************** * STRING ROUTINES * ********************************************************************/ #if OSEGEXE #if NOASM /**************************************************************** * * * zcheck: * * * * Determine length of initial nonzero stream in a buffer, and * * return the length. * * * ****************************************************************/ #if defined(M_I386) #pragma auto_inline(off) #endif WORD zcheck(BYTE *pb, WORD cb) { // Loop down from end until a nonzero byte found. // Return length of remainder of buffer. #if defined(M_I386) _asm { push edi ; Save edi movzx ecx, cb ; Number of bytes to check push ds ; Copy ds into es pop es xor eax, eax ; Looking for zeros mov edi, pb ; Start of buffer add edi, ecx ; Just past the end of buffer dec edi ; Last byte in the buffer std ; Decrement pointer repz scasb ; Scan until non-zero byte found jz AllZeros ; Buffer truly empty inc ecx ; Fix count AllZeros: cld ; Clear flag just to be safe pop edi ; Restore edi mov eax, ecx ; Return count in eax } #endif for(pb = &pb[cb]; cb != 0; --cb) if(*--pb != '\0') break; return(cb); } #endif #endif /* OSEGEXE */ #if defined(M_I386) #pragma auto_inline(on) #endif /*** CheckSegmentsMemory - check is all segments have allocated memory * * Purpose: * Check for not initialized segments. If the segment have a non-zero * size but no initialized data, then we have to allocate for it a * zero filled memory buffer. Normally 'MoveToVm' allocates memory * buffer for segments, but in this case there was no 'moves to VM'. * * Input: * No explicit value is passed. * * Output: * No explicit value is returned. * * Exceptions: * None. * * Notes: * None. * *************************************************************************/ void CheckSegmentsMemory(void) { SEGTYPE seg; SATYPE sa; if (fNewExe) { for (sa = 1; sa < saMac; sa++) if (mpsaMem[sa] == NULL && mpsacb[sa] > 0) mpsaMem[sa] = (BYTE FAR *) GetMem(mpsacb[sa]); } else { for (seg = 1; seg <= segLast; seg++) if (mpsegMem[seg] == NULL && mpsegcb[seg] > 0) mpsegMem[seg] = (BYTE FAR *) GetMem(mpsegcb[seg] + mpsegraFirst[seg]); } } /*** WriteExe - write bytes to the executable file * * Purpose: * Write to the executable file and check for errors. * * Input: * pb - byte buffer to write * cb - buffer size in bytes * * Output: * No explicit value is returned. * * Exceptions: * I/O problems - fatal error and abort * * Notes: * None. * *************************************************************************/ #if !defined( M_I386 ) && !defined( _WIN32 ) #pragma check_stack(on) void WriteExe(void FAR *pb, unsigned cb) { BYTE localBuf[1024]; WORD count; while (cb > 0) { count = (WORD) (cb <= sizeof(localBuf) ? cb : sizeof(localBuf)); FMEMCPY((BYTE FAR *) localBuf, pb, count); if (fwrite((char *) localBuf, sizeof(BYTE), count, bsRunfile) != count) { ExitCode = 4; Fatal(ER_spcrun, strerror(errno)); } cb -= count; ((BYTE FAR *) pb) += count; } } #pragma check_stack(off) #else /*** NoRoomForExe - the exe didn't fit * * Purpose: * emit error message * give fatal error and abort * * Input: * errno must be set * * Output: * No explicit value is returned. * * Notes: * None. * *************************************************************************/ void NoRoomForExe() { ExitCode = 4; Fatal(ER_spcrun, strerror(errno)); } #endif /*** WriteZeros - write zero bytes to the executable file * * Purpose: * Pad executable file with zero bytes. * * Input: * cb - number of bytes to write * * Output: * No explicit value is returned. * * Exceptions: * I/O problems - fatal error and abort * * Notes: * None. * *************************************************************************/ void WriteZeros(unsigned cb) { BYTE buf[512]; unsigned count; memset(buf, 0, sizeof(buf)); while (cb > 0) { count = cb <= sizeof(buf) ? cb : sizeof(buf); WriteExe(buf, count); cb -= count; } } /**************************************************************** * * * MoveToVm: * * * * Move a piece of data into a virtual memory area/va. * * * * Input: cb Count of bytes to be moved. * * obData Address of data to be moved. * * seg Logical segment to which data belongs. * * ra Offset at which data belongs. * * * ****************************************************************/ #pragma intrinsic(memcpy) #if EXE386 void MoveToVm(WORD cb, BYTE *obData, SEGTYPE seg, RATYPE ra) #else void NEAR MoveToVm(WORD cb, BYTE *obData, SEGTYPE seg, RATYPE ra) #endif { long cbtot; /* Count of bytes total */ long cbSeg; /* Segment size */ WORD fError; BYTE FAR *pMemImage; CVINFO FAR *pCVInfo; SATYPE sa; cbtot = (long) cb + ra; if (fDebSeg) { pCVInfo = ((APROPFILEPTR ) FetchSym(vrpropFile, FALSE))->af_cvInfo; if (pCVInfo) { if (seg < (SEGTYPE) (segDebFirst + ObjDebTotal)) { cbSeg = pCVInfo->cv_cbTyp; pMemImage = pCVInfo->cv_typ; } else { cbSeg = pCVInfo->cv_cbSym; pMemImage = pCVInfo->cv_sym; } // Check against segment bounds fError = cbtot > cbSeg; } else { OutError(ER_badcvseg); return; } } else { if (fNewExe) { cbSeg = ((APROPSNPTR) FetchSym(mpgsnrprop[vgsnCur],FALSE))->as_cbMx; sa = mpsegsa[seg]; if (mpsaMem[sa] == NULL) mpsaMem[sa] = (BYTE FAR *) GetMem(mpsacb[sa]); pMemImage = mpsaMem[sa]; // Check against segment bounds fError = (long) ((ra - mpgsndra[vgsnCur]) + cb) > cbSeg; // If data is going up to or past current end of initialized data, // omit any trailing null bytes and reset mpsacbinit. Mpsacbinit // will usually go up but may go down if a common segment over- // writes previous end data with nulls. if ((DWORD) cbtot >= mpsacbinit[sa]) { if ((DWORD) ra < mpsacbinit[sa] || (cb = zcheck(obData,cb)) != 0) mpsacbinit[sa] = (long) ra + cb; } } else { cbSeg = mpsegcb[seg] + mpsegraFirst[seg]; if (mpsegMem[seg] == NULL) mpsegMem[seg] = (BYTE FAR *) GetMem(cbSeg); pMemImage = mpsegMem[seg]; // Check against segment bounds fError = cbtot > cbSeg; } } if (fError) { if (!fDebSeg) OutError(ER_segbnd, 1 + GetFarSb(GetHte(mpgsnrprop[vgsnCur])->cch)); else OutError(ER_badcvseg); } else FMEMCPY(&pMemImage[ra], obData, cb); } #pragma function(memcpy) #if (OSEGEXE AND ODOS3EXE) OR EXE386 /* * Map segment index to memory image address for new-format exes. */ BYTE FAR * NEAR msaNew (SEGTYPE seg) { return(mpsaMem[mpsegsa[seg]]); } #endif #if (OSEGEXE AND ODOS3EXE) OR EXE386 /* * Map segment index to memory image address for DOS3 or 286Xenix exes. */ BYTE FAR * NEAR msaOld (SEGTYPE seg) { return(mpsegMem[seg]); } #endif #if EXE386 /* * Map segment index to VM area address for 386 exes. */ long NEAR msa386 (seg) SEGTYPE seg; { register long *p; /* Pointer to mpsegcb */ register long *pEnd; /* Pointer to end of mpsegcb */ register long va = AREAFSG; /* Current VM address */ /* * Segment number-to-VM area mapping is different for 386 segments * because their size limit is so big that allocating a fixed amount * for each segment is impractical, especially when sdb support is * enabled. So segments are allocated contiguously. Each segment * is padded to a VM page boundary for efficiency. * * Implementation: the fastest way would be to allocate a segment * based table of virtual addresses, but this would take more code * and memory. Counting segment sizes is slower but this is not * time-critical routine, and in most cases there will be very few * segments. */ if (fNewExe) { p = &mpsacb[1]; pEnd = &mpsacb[seg]; } #if ODOS3EXE else { p = &mpsegcb[1]; pEnd = &mpsegcb[seg]; } #endif for( ; p < pEnd; ++p) va += (*p + (PAGLEN - 1)) & ~(PAGLEN - 1); return(va); } #endif /* EXE386 */ /******************************************************************** * (ERROR) MESSAGE ROUTINES * ********************************************************************/ #pragma auto_inline(off) /* * SysFatal : system-level error * * Issue error message and exit with return code 4. */ void cdecl SysFatal (MSGTYPE msg) { ExitCode = 4; Fatal(msg); } void NEAR InvalidObject(void) { Fatal((MSGTYPE)(fDrivePass ? ER_badobj: ER_eofobj)); } #pragma auto_inline(on) /******************************************************************** * MISCELLANEOUS ROUTINES * ********************************************************************/ /* * Output a word integer. */ void OutWord(x) WORD x; /* A word integer */ { WriteExe(&x, CBWORD); } /* * GetLocName : read in a symbol name for L*DEF * * Transform the name by prefixing a space followed by the * module number. Update the length byte. * * Parameters: pointer to a string buffer, 1st byte already * contains length * Returns: nothing */ void NEAR GetLocName (psb) BYTE *psb; /* Name buffer */ { WORD n; BYTE *p; p = &psb[1]; /* Start after length byte */ *p++ = 0x20; /* Prefix begins with space char */ GetBytes(p,B2W(psb[0])); /* Read in text of symbol */ p += B2W(psb[0]); /* Go to end of string */ *p++ = 0x20; n = modkey; /* Initialize */ /* Convert the module key to ASCII and store backwards */ do { *p++ = (BYTE) ((n % 10) + '0'); n /= 10; } while(n); psb[0] = (BYTE) ((p - (psb + 1))); /* Update length byte */ } PROPTYPE EnterName(psym,attr,fCreate) BYTE *psym; /* Pointer to length-prefixed string */ ATTRTYPE attr; /* Attribute to look up */ WORD fCreate; /* Create prop cell if not found */ { return(PropSymLookup(psym, attr, fCreate)); /* Hide call to near function */ } #if CMDMSDOS #pragma check_stack(on) /*** ValidateRunFileName - Check if output file has proper extension * * Purpose: * Check user-specified output file name for valid extension. * Issue warning if extension is invalid and create new file * name with proper extension. * * Input: * ValidExtension - pointer to length prefixed ascii string * representing valid exetension for output * file name. * ForceExtension - TRUE if output file must have new extension, * otherwise user responce takes precedence. * WarnUser - If TRUE than display L4045 if file name changed. * * Output: * rhteRunfile - global virtual pointer to output file * name, changed only if new output name * is created because of invalid original * extension. * warning L4045 - if output file name have to be changed. * *************************************************************************/ void NEAR ValidateRunFileName(BYTE *ValidExtension, WORD ForceExtension, WORD WarnUser) { SBTYPE sb; /* String buffer */ BYTE *psbRunfile; /* Name of runfile */ char oldDrive[_MAX_DRIVE]; char oldDir[_MAX_DIR]; char oldName[_MAX_FNAME]; char oldExt[_MAX_EXT]; /* Get the name of the runfile and check if it has user supplied extension */ psbRunfile = GetFarSb(((AHTEPTR) FetchSym(rhteRunfile,FALSE))->cch); _splitpath(psbRunfile, oldDrive, oldDir, oldName, oldExt); /* Force extension only when no user defined extension */ if (ForceExtension && oldExt[0] == NULL) { memcpy(sb, ValidExtension, strlen(ValidExtension)); memcpy(bufg, psbRunfile, 1 + B2W(*psbRunfile)); } else { memcpy(bufg, ValidExtension, strlen(ValidExtension)); memcpy(sb, psbRunfile, 1 + B2W(*psbRunfile)); } UpdateFileParts(bufg, sb); /* If the name has changed, issue a warning and update rhteRunfile. */ if (!SbCompare(bufg, psbRunfile, (FTYPE) TRUE)) { if (WarnUser && !SbCompare(ValidExtension, sbDotExe, (FTYPE) TRUE)) OutWarn(ER_outputname,bufg + 1); PropSymLookup(bufg, ATTRNIL, TRUE); rhteRunfile = vrhte; } } #pragma check_stack(off) #endif /******************************************************************** * PORTABILITY ROUTINES * ********************************************************************/ #if M_BYTESWAP WORD getword(cp) /* Get a word given a pointer */ REGISTER char *cp; /* Pointer */ { return(B2W(cp[0]) + (B2W(cp[1]) << BYTELN)); /* Return 8086-style word */ } DWORD getdword(cp)/* Get a double word given a pointer */ REGISTER char *cp; /* Pointer */ { return(getword(cp) + (getword(cp+2) << WORDLN)); /* Return 8086-style double word */ } #endif #if NOT M_WORDSWAP OR M_BYTESWAP /* * Portable structure I/O routines */ #define cget(f) fgetc(f) static int bswap; /* Byte-swapped mode (1 on; 0 off) */ static int wswap; /* Word-swapped mode (1 on; 0 off) */ static cput(c,f) char c; FILE *f; { #if FALSE AND OEXE CheckSum(1, &c); #endif fputc(c, f); } static pshort(s,f) REGISTER short s; REGISTER FILE *f; { cput(s & 0xFF,f); /* Low byte */ cput(s >> 8,f); /* High byte */ } static unsigned short gshort(f) REGISTER FILE *f; { REGISTER short s; s = cget(f); /* Get low byte */ return(s + (cget(f) << 8)); /* Get high byte */ } static pbshort(s,f) REGISTER short s; REGISTER FILE *f; { cput(s >> 8,f); /* High byte */ cput(s & 0xFF,f); /* Low byte */ } static unsigned short gbshort(f) REGISTER FILE *f; { REGISTER short s; s = cget(f) << 8; /* Get high byte */ return(s + cget(f)); /* Get low byte */ } static int (*fpstab[2])() = { pshort, pbshort }; static unsigned short (*fgstab[2])() = { gshort, gbshort }; static plong(l,f) long l; REGISTER FILE *f; { (*fpstab[bswap])((short)(l >> 16),f); /* High word */ (*fpstab[bswap])((short) l,f); /* Low word */ } static long glong(f) REGISTER FILE *f; { long l; l = (long) (*fgstab[bswap])(f) << 16; /* Get high word */ return(l + (unsigned) (*fgstab[bswap])(f)); /* Get low word */ } static pwlong(l,f) long l; REGISTER FILE *f; { (*fpstab[bswap])((short) l,f); /* Low word */ (*fpstab[bswap])((short)(l >> 16),f); /* High word */ } static long gwlong(f) REGISTER FILE *f; { long l; l = (unsigned) (*fgstab[bswap])(f); /* Get low word */ return(l + ((long) (*fgstab[bswap])(f) << 16)); /* Get high word */ } static int (*fpltab[2])() = { plong, pwlong }; static long (*fgltab[2])() = { glong, gwlong }; /* * int swrite(cp,dopevec,count,file) * char *cp; * char *dopevec; * int count; * FILE *file; * * Returns number of bytes written. * * Dopevec is a character string with the * following format: * * "[b][w][p]{[]}" * * where [...] denotes an optional part, {...} denotes a part * that may be repeated zero or more times, and <...> denotes * a description of a part. * * b bytes are "swapped" (not in PDP-11 order) * w words are swapped * p struct is "packed" (no padding for alignment) * count of times to repeat following type * one of the following: * c char * s short * l long * * Example: given the struct * * struct * { * short x; * short y; * char z[16]; * long w; * }; * * and assuming it is to be written so as to use VAX byte- and * word-ordering, its dope vector would be: * * "wss16cl" */ int swrite(cp,dopevec,count,file) char *cp; /* Pointer to struct array */ char *dopevec; /* Dope vector for struct */ int count; /* Number of structs in array */ FILE *file; /* File to write to */ { int pack; /* Packed flag */ int rpt; /* Repeat count */ REGISTER int cc = 0; /* Count of characters written */ REGISTER char *dv; /* Dope vector less flags */ short *sp; /* Pointer to short */ long *lp; /* Pointer to long */ bswap = wswap = pack = 0; /* Initialize flags */ while(*dopevec != '\0') /* Loop to set flags */ { if(*dopevec == 'b') bswap = 1; /* Check for byte-swapped flag */ else if(*dopevec == 'p') pack = 1; /* Check for packed flag */ else if(*dopevec == 'w') wswap = 1; /* Check for word-swapped flag */ else break; ++dopevec; } while(count-- > 0) /* Main loop */ { dv = dopevec; /* Initialize */ for(;;) /* Loop to write struct */ { if(*dv >= '0' && *dv <= '9') { /* If there is a repeat count */ rpt = 0; /* Initialize */ do /* Loop to get repeat count */ { rpt = rpt*10 + *dv++ - '0'; /* Take digit */ } while(*dv >= '0' && *dv <= '9'); /* Loop until non-digit found */ } else rpt = 1; /* Else repeat count defaults to one */ if(*dv == '\0') break; /* break if end of dope vector */ switch(*dv++) /* Switch on type character */ { case 'c': /* Character */ #if FALSE AND OEXE CheckSum(rpt, cp); #endif if(fwrite(cp,sizeof(char),rpt,file) != rpt) return(cc); /* Write the characters */ cp += rpt; /* Increment pointer */ cc += rpt; /* Increment count of bytes written */ break; case 's': /* Short */ if(!pack && (cc & 1)) /* If not packed and misaligned */ { cput(*cp++,file); /* Write padding byte */ ++cc; /* Increment byte count */ } sp = (short *) cp; /* Initialize pointer */ while(rpt-- > 0) /* Loop to write shorts */ { (*fpstab[bswap])(*sp++,file); /* Write the short */ if(feof(file) || ferror(file)) return(cc); /* Check for errors */ cc += sizeof(short); /* Increment byte count */ } cp = (char *) sp; /* Update pointer */ break; case 'l': /* Long */ if(!pack && (cc & 3)) /* If not packed and misaligned */ { while(cc & 3) /* While not aligned */ { cput(*cp++,file); /* Write padding byte */ ++cc; /* Increment byte count */ } } lp = (long *) cp; /* Initialize pointer */ while(rpt-- > 0) /* Loop to write longs */ { (*fpltab[wswap])(*lp++,file); /* Write the long */ if(feof(file) || ferror(file)) return(cc); /* Check for errors */ cc += sizeof(long); /* Increment byte count */ } cp = (char *) lp; /* Update pointer */ break; } } } return(cc); /* Return count of bytes written */ } /* * int sread(cp,dopevec,count,file) * char *cp; * char *dopevec; * int count; * FILE *file; * * Returns number of bytes read. * * Dopevec is a character string whose format is described * with swrite() above. */ int sread(cp,dopevec,count,file) char *cp; /* Pointer to struct array */ char *dopevec; /* Dope vector for struct */ int count; /* Number of structs in array */ FILE *file; /* File to read from */ { int pack; /* Packed flag */ int rpt; /* Repeat count */ REGISTER int cc = 0; /* Count of characters written */ REGISTER char *dv; /* Dope vector less flags */ short *sp; /* Pointer to short */ long *lp; /* Pointer to long */ bswap = wswap = pack = 0; /* Initialize flags */ while(*dopevec != '\0') /* Loop to set flags */ { if(*dopevec == 'b') bswap = 1; /* Check for byte-swapped flag */ else if(*dopevec == 'p') pack = 1; /* Check for packed flag */ else if(*dopevec == 'w') wswap = 1; /* Check for word-swapped flag */ else break; ++dopevec; } while(count-- > 0) /* Main loop */ { dv = dopevec; /* Initialize */ for(;;) /* Loop to write struct */ { if(*dv >= '0' && *dv <= '9') { /* If there is a repeat count */ rpt = 0; /* Initialize */ do /* Loop to get repeat count */ { rpt = rpt*10 + *dv++ - '0'; /* Take digit */ } while(*dv >= '0' && *dv <= '9'); /* Loop until non-digit found */ } else rpt = 1; /* Else repeat count defaults to one */ if(*dv == '\0') break; /* break if end of dope vector */ switch(*dv++) /* Switch on type character */ { case 'c': /* Character */ if(fread(cp,sizeof(char),rpt,file) != rpt) return(cc); /* Read the characters */ cp += rpt; /* Increment pointer */ cc += rpt; /* Increment count of bytes written */ break; case 's': /* Short */ if(!pack && (cc & 1)) /* If not packed and misaligned */ { *cp ++ = cget(file); /* Read padding byte */ ++cc; /* Increment byte count */ } sp = (short *) cp; /* Initialize pointer */ while(rpt-- > 0) /* Loop to read shorts */ { *sp++ = (*fgstab[bswap])(file); /* Read the short */ if(feof(file) || ferror(file)) return(cc); /* Check for errors */ cc += sizeof(short); /* Increment byte count */ } cp = (char *) sp; /* Update pointer */ break; case 'l': /* Long */ if(!pack && (cc & 3)) /* If not packed and misaligned */ { while(cc & 3) /* While not aligned */ { *cp++ = cget(file); /* Read padding byte */ ++cc; /* Increment byte count */ } } lp = (long *) cp; /* Initialize pointer */ while(rpt-- > 0) /* Loop to read longs */ { *lp++ = (*fgltab[wswap])(file); /* Read the long */ if(feof(file) || ferror(file)) return(cc); /* Check for errors */ cc += sizeof(long); /* Increment byte count */ } cp = (char *) lp; /* Update pointer */ break; } } } return(cc); /* Return count of bytes written */ } #endif #define CB_POOL 4096 typedef struct _POOLBLK { struct _POOLBLK * pblkNext; // next pool in list int cb; // number of bytes in this pool (free+alloc) char rgb[1]; // data for this pool (variable sized) } POOLBLK; typedef struct _POOL { struct _POOLBLK * pblkHead; // start of poolblk list struct _POOLBLK * pblkCur; // current poolblk we are searching int cb; // # bytes free in current pool char * pch; // pointer to free data in current pool } POOL; void * PInit() { POOL *ppool; // create new pool, set size and allocate CB_POOL bytes ppool = (POOL *)GetMem(sizeof(POOL)); ppool->pblkHead = (POOLBLK *)GetMem(sizeof(POOLBLK) + CB_POOL-1); ppool->pblkHead->cb = CB_POOL; ppool->pblkHead->pblkNext = NULL; ppool->cb = CB_POOL; ppool->pch = &ppool->pblkHead->rgb[0]; ppool->pblkCur = ppool->pblkHead; return (void *)ppool; } void * PAlloc(void *pp, int cb) { POOL *ppool = (POOL *)pp; void *pchRet; POOLBLK *pblkCur, *pblkNext; // if the allocation doesn't fit in the current block if (cb > ppool->cb) { pblkCur = ppool->pblkCur; pblkNext = pblkCur->pblkNext; // then check the next block if (pblkNext && pblkNext->cb >= cb) { // set the master info to reflect the next page... ppool->pblkCur = pblkNext; ppool->cb = pblkNext->cb; ppool->pch = &pblkNext->rgb[0]; memset(ppool->pch, 0, ppool->cb); } else { POOLBLK *pblkNew; // new pool // allocate new memory -- at least enough for this allocation pblkNew = (POOLBLK *)GetMem(sizeof(POOLBLK)+cb+CB_POOL-1); pblkNew->cb = CB_POOL + cb; // link the current page to the new page pblkNew->pblkNext = pblkNext; pblkCur->pblkNext = pblkNew; // set the master info to reflect the new page... ppool->pblkCur = pblkNew; ppool->cb = CB_POOL + cb; ppool->pch = &pblkNew->rgb[0]; } } pchRet = (void *)ppool->pch; ppool->pch += cb; ppool->cb -= cb; return pchRet; } void PFree(void *pp) { POOL *ppool = (POOL *)pp; POOLBLK *pblk = ppool->pblkHead; POOLBLK *pblkNext; while (pblk) { pblkNext = pblk->pblkNext; FFREE(pblk); pblk = pblkNext; } FFREE(ppool); } void PReinit(void *pp) { POOL *ppool = (POOL *)pp; ppool->pblkCur = ppool->pblkHead; ppool->cb = ppool->pblkHead->cb; ppool->pch = &ppool->pblkHead->rgb[0]; memset(ppool->pch, 0, ppool->cb); } #if RGMI_IN_PLACE /**************************************************************** * * * PchSegAddress: * * * * compute the address that will hold this data so we can read * * it in place... we make sure that we can read in place at * * and give errors as in MoveToVm if we cannot * * * * Input: cb Count of bytes to be moved. * * seg Logical segment to which data belongs. * * ra Offset at which data belongs. * * * ****************************************************************/ BYTE FAR * PchSegAddress(WORD cb, SEGTYPE seg, RATYPE ra) { long cbtot; /* Count of bytes total */ long cbSeg; /* Segment size */ WORD fError; BYTE FAR *pMemImage; CVINFO FAR *pCVInfo; SATYPE sa; cbtot = (long) cb + ra; if (fDebSeg) { pCVInfo = ((APROPFILEPTR ) FetchSym(vrpropFile, FALSE))->af_cvInfo; if (pCVInfo) { if (seg < (SEGTYPE) (segDebFirst + ObjDebTotal)) { cbSeg = pCVInfo->cv_cbTyp; pMemImage = pCVInfo->cv_typ; if (!pMemImage) pCVInfo->cv_typ = pMemImage = GetMem(cbSeg); } else { cbSeg = pCVInfo->cv_cbSym; pMemImage = pCVInfo->cv_sym; if (!pMemImage) pCVInfo->cv_sym = pMemImage = GetMem(cbSeg); } // Check against segment bounds fError = cbtot > cbSeg; } else { OutError(ER_badcvseg); return NULL; } } else { if (fNewExe) { cbSeg = ((APROPSNPTR) FetchSym(mpgsnrprop[vgsnCur],FALSE))->as_cbMx; sa = mpsegsa[seg]; if (mpsaMem[sa] == NULL) mpsaMem[sa] = (BYTE FAR *) GetMem(mpsacb[sa]); pMemImage = mpsaMem[sa]; // Check against segment bounds fError = (long) ((ra - mpgsndra[vgsnCur]) + cb) > cbSeg; } else { cbSeg = mpsegcb[seg] + mpsegraFirst[seg]; if (mpsegMem[seg] == NULL) mpsegMem[seg] = (BYTE FAR *) GetMem(cbSeg); pMemImage = mpsegMem[seg]; // Check against segment bounds fError = cbtot > cbSeg; } } if (fError) { if (!fDebSeg) OutError(ER_segbnd, 1 + GetFarSb(GetHte(mpgsnrprop[vgsnCur])->cch)); else OutError(ER_badcvseg); } return (pMemImage + ra); } #endif #if USE_REAL // Indicates if you are running under TNT. // If it returns FALSE today, you are running on NT. int IsDosxnt ( ) { #if defined( _WIN32 ) return FALSE; #else HINSTANCE hLib = GetModuleHandle("kernel32.dll"); if ( hLib != 0 && (GetProcAddress(hLib, "IsTNT") != 0)) { return(TRUE); } else { return(FALSE); } #endif } // Are we running on Win31 or greater. // Note that we know if we are running under Windows we are running in enhanced mode. int IsWin31() { #if defined( _WIN32 ) return FALSE; #else __asm { mov ax,1600h ; Is Win31 or greater running int 2fh cmp al,03h ; Is major version number 3.0 jb NotWin31 ; Major version less than 3.0 ja ItIsWin31 cmp ah,0ah ; Is minor version atleast .10 jb NotWin31 ; Must be Win3.0 } ItIsWin31: return (TRUE); NotWin31: return (FALSE); #endif // NOT _WIN32 } int MakeConvMemPageable ( ) { #if defined( _WIN32 ) return TRUE; #else if ( realModeMemPageable ) { return ( TRUE ); // Somebody already freed the real mode mem. } __asm { mov ax,0100h ; function to get DOS memory. mov bx,TOTAL_CONV_MEM ; Ask for 1 M to get max memory count int 31h jnc errOut ; allocated 1 M - something must be wrong. cmp ax,08h ; Did we fail because of not enough memory jne errOut ; No we failed because of some other reason. cmp bx,MIN_CONV_MEM ; See if we can allocate atleast the min // We could fail for two reasons here . // 1) we really didn't have sufficient memory. // 2) Some TNT app that spawned us already unlocked this memory. For ex: // cl might have already freed up the memory when it calls link.exe. jb errOut ; Too little mem available don't bother. sub bx,CONV_MEM_FOR_TNT ; Leave real mode mem for TNT. mov ax,0100h ; Try again with new amount of memory int 31h ; Ask for the real mode memory from DPMI. jc errOut ; didn't succeed again, give up. mov convMemSelector,dx ; Save the value of the selector for allocated block mov noOfParagraphs,bx ; amount of memory we were able to allocate. mov ax,0006h ; function to get base addr of a selector mov bx,dx ; move the selector to bx int 31h ; Get Segment Base Address jc errOut ; mov bx,cx ; mov lin addr from cx:dx to bx:cx mov cx,dx ; movzx eax,noOfParagraphs shl eax,4 ; Multiply by 16 to get count in bytes. mov di,ax ; transfer size to si:di from eax shr eax,16 ; mov si,ax ; mov ax,602h ; Make real mode memory pageable int 31h jc errOut ; Didn't work. mov ax,703h ; Indicate data in these pages is discardable. int 31h // Even if we fail this call, we will still assume we are succesful, // because it is just a performance enhancement // Also for correctness we should relock the memory once it is free. } realModeMemPageable = TRUE ; errOut: return(realModeMemPageable); #endif // NOT _WIN32 } /* Relock the real mode memory now */ int RelockConvMem ( void ) { #if defined( _WIN32 ) return TRUE; #else if ( !realModeMemPageable ) { return ( TRUE ); // We were never able to free the mem anyway. } __asm { mov bx, convMemSelector mov ax, 0006h int 31h ; Get Segment Base Address. jc errOut ; mov bx,cx ; Mov lin addr from cx:dx to bx:cx mov cx,dx movzx eax,noOfParagraphs shl eax,4 ; Mul paragraphs by 16 to get count in bytes. mov di,ax ;Transfer size to si:di from eax. shr eax,16 mov si,ax mov ax,603h ; Relock real mode region int 31h jc errOut mov dx,convMemSelector mov ax,101h ; Free the real mode memory int 31h jc errOut } realModeMemPageable = FALSE ; return ( TRUE ); errOut: return ( FALSE ); #endif // NOT _WIN32 } void RealMemExit(void) { if(fUseReal) { if(!RelockConvMem()) OutError(ER_membad); fUseReal = FALSE; } } #endif