windows-nt/Source/XPSP1/NT/sdktools/link16/newflg.c
2020-09-26 16:20:57 +08:00

1634 lines
46 KiB
C

/*
* Copyright Microsoft Corporation, 1983-1989
*
* This Module contains Proprietary Information of Microsoft
* Corporation and should be treated as Confidential.
*/
/****************************************************************
* *
* FLAG PROCESSOR MODULE *
* *
****************************************************************/
#include <minlit.h> /* Types and constants */
#include <bndtrn.h> /* Types and constants */
#include <bndrel.h> /* Types and constants */
#include <lnkio.h> /* Linker I/O definitions */
#include <lnkmsg.h> /* Error messages */
#if OIAPX286
#include <xenfmt.h> /* x.out definitions */
#endif
#include <extern.h> /* External declarations */
#include <newexe.h> /* DOS & 286 .EXE format structure def.s */
#if EXE386
#include <exe386.h> /* 386 .EXE format structure def.s */
#endif
#include <process.h>
extern FTYPE fNoExtDic; /* Not searching extended dictionary */
LOCAL BYTE *osbSwitch; /* Switch pointer */
LOCAL MSGTYPE SwitchErr; /* Switch error number */
/*
* FUNCTION PROTOTYPES
*/
#if TIMINGS
LOCAL void NEAR SwShowTiming(void);
#endif // TIMINGS
#if PCODE
LOCAL void NEAR SwPCode(void);
#endif
LOCAL void NEAR SwAlign(void);
LOCAL void NEAR SwBatch(void);
#if ODOS3EXE
LOCAL void NEAR SwBinary(void);
#endif
LOCAL void NEAR SwCase(void);
LOCAL void NEAR SwCParMax(void);
LOCAL void NEAR SwDelexe(void);
LOCAL void NEAR SwDosExtend(void);
LOCAL void NEAR SwDosseg(void);
LOCAL void NEAR SwDSAlloc(void);
LOCAL void NEAR SwDynamic(void);
LOCAL void NEAR SwIdef(void);
LOCAL void NEAR SwOldOvl(void);
LOCAL void NEAR SwExePack(void);
LOCAL void NEAR SwFarCall(void);
#if EXE386
LOCAL void NEAR SwHeader(void);
#endif
#if NOT WIN_3
LOCAL void NEAR SwHelp(void);
LOCAL void NEAR SwShortHelp(void);
#endif
LOCAL void NEAR SwHigh(void);
#if ILINK
LOCAL void NEAR SwIncremental(void);
#endif
LOCAL void NEAR SwInfo(void);
LOCAL void NEAR SwIntNo(void);
#if (OSEGEXE AND defined(LEGO)) OR EXE386
LOCAL void NEAR SwKeepFixups(void);
#endif
#if EXE386
LOCAL void NEAR SwKeepVSize(void);
#endif
LOCAL void NEAR SwLineNos(void);
LOCAL void NEAR SwMap(void);
#if O68K
LOCAL void NEAR SwMac(void);
#endif /* O68K */
#if WIN_NT
LOCAL void NEAR SwMemAlign(void);
#endif
#if NOT EXE386
LOCAL void NEAR SwNewFiles(void);
#endif
LOCAL void NEAR SwNoDefLib(void);
LOCAL void NEAR SwNoExtDic(void);
LOCAL void NEAR SwNoFarCall(void);
LOCAL void NEAR SwNoGrp(void);
LOCAL void NEAR SwNologo();
LOCAL void NEAR SwNonulls(void);
LOCAL void NEAR SwNoPack(void);
LOCAL void NEAR SwNoUseReal(void);
LOCAL void NEAR SwPack(void);
LOCAL void NEAR SwPackData(void);
LOCAL void NEAR SwPackFunctions(void);
LOCAL void NEAR SwNoPackFunctions(void);
LOCAL void NEAR SwPadCode(void);
LOCAL void NEAR SwPadData(void);
LOCAL void NEAR SwPause(void);
LOCAL void NEAR SwPmType(void);
LOCAL void NEAR SwQuicklib(void);
LOCAL void NEAR SwSegments(void);
LOCAL void NEAR SwStack(void);
LOCAL void NEAR SwSymdeb(void);
LOCAL void NEAR SwWarnFixup(void);
#if DOSEXTENDER
LOCAL void NEAR SwRunReal(void);
#endif
#if QCLINK
LOCAL void NEAR SwZ1(void);
#endif
#if QCLINK OR Z2_ON
LOCAL void NEAR SwZ2 (void);
#endif
#if QCLINK
LOCAL void NEAR SwZincr(void);
#endif
LOCAL int NEAR ParseNo(unsigned long *pResult);
LOCAL int NEAR ParseStr(char *pResult);
LOCAL void NEAR BadFlag(unsigned char *psb, MSGTYPE errnum);
LOCAL int NEAR FPrefix(unsigned char *psb1,unsigned char *psb2);
/*
* ParseNo : Parse switch number
*
* Return value:
* 1 result stored in pointer
* 0 no switch given
* -1 error
*/
LOCAL int NEAR ParseNo(pResult)
unsigned long *pResult;
{
REGISTER char *s; /* String pointer */
REGISTER WORD ch; /* A character */
WORD strlen; /* String length */
WORD base = 10; /* Base to read constant in */
DWORD oldval;
oldval = *pResult = 0L; /* Initialize */
strlen = IFind(osbSwitch,':'); /* Look for a colon in the string */
if(strlen != INIL && strlen < (WORD) (B2W(osbSwitch[0]) - 1))
{ /* If switch form valid */
s = &osbSwitch[strlen + 2];
/* Set pointer past colon */
strlen = B2W(osbSwitch[0]) - strlen - 1;
/* Get length of string left */
if(*s == '0') /* If string starts with 0 */
{
if(strlen > 1 && ((WORD) s[1] & 0137) == 'X')
{ /* If string starts with "0x" */
base = 16; /* Change base to hexadecimal */
++s; /* Skip over "0" */
--strlen; /* Decrement length */
}
else base = 8; /* Else change to octal */
++s; /* Skip "0" (or "x") */
--strlen; /* Decrement length */
}
while(strlen--) /* While not at end of string */
{
ch = B2W(*s++); /* Get character */
if(ch >= '0' && ch <= '9') ch -= (WORD) '0';
/* Remove offset */
else if(ch >= 'A' && ch < 'A' + base - 10) ch -= (WORD) 'A' - 10;
/* Remove offset */
else if(ch >= 'a' && ch < 'a' + base - 10) ch -= (WORD) 'a' - 10;
/* Remove offset */
else /* Invalid character */
{
SwitchErr = ER_swbadnum;
return(-1); /* Error */
}
if((*pResult *= base) < oldval)
{
SwitchErr = ER_swbadnum;
return(-1); /* Error */
}
*pResult += ch;
oldval = *pResult;
}
return(1); /* Number is present */
}
else return(0); /* No number present */
}
/*
* ParseStr : Parse switch string
*
* Return value:
* 1 result stored in string
* 0 no switch string given
*/
LOCAL int NEAR ParseStr(pResult)
char *pResult; /* Length prefixed result */
{
REGISTER char *s; /* String pointer */
WORD strlen; /* String length */
*pResult = '\0'; /* Initialize */
strlen = IFind(osbSwitch,':'); /* Look for a colon in the string */
if(strlen != INIL && strlen < (WORD) (B2W(osbSwitch[0]) - 1))
{ /* If switch form valid */
s = &osbSwitch[strlen + 2];
/* Set pointer past colon */
strlen = B2W(osbSwitch[0]) - strlen - 1;
/* Get length of string left */
*pResult++ = (char) strlen; /* Store length */
while(strlen--) /* While not at end of string */
{
*pResult++ = (char) (*s++); /* Get character */
}
return(1); /* String is present */
}
else return(0); /* No stringr present */
}
#if PCODE
LOCAL void NEAR SwPCode(void)
{
SBTYPE SwString;
fNewExe = (FTYPE) TRUE;
fMPC = (FTYPE) TRUE;
if (ParseStr(SwString))
{
if(SwString[1] == 'n' || SwString[1] == 'N') // /PCODE:NOMPC
{
fIgnoreMpcRun = (FTYPE) TRUE;
fMPC = (FTYPE) FALSE;
}
}
}
#endif
/***************************************************************/
/* Options common to all versions regardless of output format */
LOCAL void NEAR SwCase()
{
fIgnoreCase = (FTYPE) ~IGNORECASE; /* Toggle default case sensitivity */
}
LOCAL void NEAR SwLineNos() /* Line numbers requested */
{
vfLineNos = (FTYPE) TRUE; /* Set flag */
}
#if LOCALSYMS
LOCAL void NEAR SwLocals() /* Local symbols requested */
{
fLocals = (FTYPE) TRUE; /* Set flag */
}
#endif
#pragma check_stack(on)
LOCAL void NEAR SwMap() /* Link map requested */
{
SBTYPE SwString;
int rc;
vfMap = (FTYPE) TRUE; // Set flag
if ((rc = ParseStr(SwString)) <= 0) // Done if no num or error
return;
// The optional parameter following /MAP was originally intended
// to tell the linker how much to space to allocate for sorting
// more public symbols than the stack-based limit. Since we now
// dyamically allocate as much space as possible for sorting,
// the parameter is no longer necessary and its value is ignored.
// However, the side effect of suppressing the "sorted by name"
// list is retained.
if (SwString[1] == 'A' || SwString[1] == 'a')
fListAddrOnly = (FTYPE) TRUE; // /MAP:ADDRESS
else if (SwString[1] == 'F' || SwString[1] == 'f')
fFullMap = (FTYPE) TRUE; // /MAP:FULL or /MAP:full
}
LOCAL void NEAR SwNoDefLib() /* Do not search default library */
{
SBTYPE SwString;
SBTYPE LibName;
if (ParseStr(SwString))
{
vfNoDefaultLibrarySearch = FALSE;
/* Clear flag - selective library search */
strcpy(LibName, sbDotLib);
UpdateFileParts(LibName, SwString);
EnterName(LibName,ATTRSKIPLIB,TRUE);
}
else vfNoDefaultLibrarySearch = (FTYPE) TRUE;
/* Set flag */
}
#pragma check_stack(off)
LOCAL void NEAR SwNologo()
{
// if fNoprompt is already set then either
// a) /BATCH was specified, in which case /NOLOGO is redundant
// b) BATCH was in _MSC_IDE_FLAGS in which case fNoEchoLrf has not been
// set, and we want to suppress echoing of the response file
// (see CAVIAR 2378 [rm])
if (fNoprompt)
fNoEchoLrf = TRUE; /* Do not echo response file */
fNoBanner = TRUE; /* Do not display banner */
}
LOCAL void NEAR SwBatch() /* Do not prompt for files */
{
fNoEchoLrf = (FTYPE) TRUE; /* Do not echo response file */
fNoprompt = (FTYPE) TRUE; /* Do not prompt */
fPauseRun = FALSE; /* Disable /PAUSE */
fNoBanner = (FTYPE) TRUE; /* Do not display banner */
}
#if ODOS3EXE
LOCAL void NEAR SwBinary() /* Produce .COM file */
{
fBinary = (FTYPE) TRUE;
SwNonulls(); /* No nulls */
fFarCallTrans = (FTYPE) TRUE; /* Far call translation */
packLim = LXIVK - 36; /* Default limit is 64K - 36 */
fPackSet = (FTYPE) TRUE; /* Remember packLim was set */
}
#endif
#if SYMDEB
LOCAL void NEAR SwSymdeb() /* Symbolic debugging */
{
SBTYPE SwString;
fSymdeb = (FTYPE) TRUE;
if (ParseStr(SwString))
{
fCVpack = (FTYPE) (SwString[1] == 'c' || SwString[1] == 'C');
}
}
#endif
#if PERFORMANCE
LOCAL void NEAR SwVMPerf() /* Report on VM performance */
{
fPerformance = (FTYPE) TRUE; /* Set flag */
}
#endif
#if OSMSDOS
LOCAL void NEAR SwPause() /* Pause before writing executable */
{
fPauseRun = (FTYPE) TRUE; /* Set flag */
fNoprompt = FALSE; /* Disable /NOPROMPT */
}
#endif
LOCAL void NEAR SwStack() /* Set stack segment size */
{
unsigned long num;
int rc;
if((rc = ParseNo(&num)) < 0) /* Quit if error */
return;
#if EXE386
if(!rc || num > CBMAXSEG32 - 4L)
#else
if(!rc || num > LXIVK - 2L)
#endif
SwitchErr = ER_swstack;
else
#if EXE386
cbStack = num;
#else
cbStack = (WORD) num;
#endif
}
LOCAL void NEAR SwSegments() /* Set maximum number of segments */
{
unsigned long nsegs; /* Number of segments */
int rc;
if((rc = ParseNo(&nsegs)) <= 0) /* Quit if error or no argument */
return;
if(nsegs > (long) GSNMAX)
SwitchErr = ER_swseglim;
else
{
if ((nsegs + 3L) > GSNMAX)
gsnMax = GSNMAX;
else
gsnMax = (SNTYPE) nsegs; /* Else set limit */
}
}
#if EXE386
LOCAL void NEAR SwMemAlign(void)/* Set memory object alignment factor */
{
long align; /* Alignment size in bytes */
int rc;
if ((rc = ParseNo(&align)) < 0) /* Quit if error */
return;
if (rc && align >= 1)
{ /* If value in legal range */
for (objAlign = 32; objAlign != 0; --objAlign)
{ /* Loop to find log of align */
if ((1L << objAlign) & align)
break; /* Break when high bit found */
}
if ((1L << objAlign) == align)
return; /* Align must be power of two */
}
OutWarn(ER_alnbad); /* Output warning message */
objAlign = DFOBJALIGN; /* Use default value */
}
#endif
#if NOT EXE386
LOCAL void NEAR SwNewFiles(void)
{
vFlagsOthers |= NENEWFILES; /* Set flag */
}
#endif
#if FDEBUG
LOCAL void NEAR SwInfo() /* Turn on runtime debugging */
{
fDebug = (FTYPE) TRUE; /* Set flag */
}
#endif
#if LIBMSDOS
LOCAL void NEAR SwNoExtDic() /* Don't search extended dictionary */
{
fNoExtDic = (FTYPE) TRUE;
}
#endif
/***************************************************************/
/* Options for segmented executable format. */
#if OSEGEXE
LOCAL void NEAR SwAlign() /* Set segment alignment factor */
{
long align; /* Alignment size in byutes */
int rc;
if((rc = ParseNo(&align)) < 0) /* Quit if error */
return;
if(rc && align >= 1 && align <= 32768L)
{ /* If value in legal range */
for(fileAlign = 16; fileAlign != 0; --fileAlign)
{ /* Loop to find log of align */
if((1L << fileAlign) & align) break;
/* Break when high bit found */
}
if((1L << fileAlign) == align) return;
/* Align must be power of two */
}
OutWarn(ER_alnbad); /* Output warning message */
fileAlign = DFSAALIGN; /* Use default value */
}
#pragma check_stack(on)
#if OUT_EXP
LOCAL void NEAR SwIdef(void) /* Dump exports to a text file */
{
SBTYPE SwString;
int rc;
if ((rc = ParseStr(SwString)) <= 0) // Done if no string or error
{
bufExportsFileName[0] = '.'; // Use the default file name
return;
}
strcpy(bufExportsFileName, SwString);
}
#endif
#if NOT EXE386
LOCAL void NEAR SwPmType() /* /PMTYPE:<type> */
{
SBTYPE SwString;
if (ParseStr(SwString))
{
if (FPrefix("\002PM", SwString))
vFlags |= NEWINAPI;
else if (FPrefix("\003VIO", SwString))
vFlags |= NEWINCOMPAT;
else if (FPrefix("\005NOVIO", SwString))
vFlags |= NENOTWINCOMPAT;
else
OutWarn(ER_badpmtype, &osbSwitch[1]);
}
else
OutWarn(ER_badpmtype, &osbSwitch[1]);
}
#endif
#pragma check_stack(off)
LOCAL void NEAR SwWarnFixup()
{
fWarnFixup = (FTYPE) TRUE;
}
#if O68K
LOCAL void NEAR SwMac() /* Target is a Macintosh */
{
SBTYPE SwString;
f68k = fTBigEndian = fNewExe = (FTYPE) TRUE;
iMacType = (BYTE) (ParseStr(SwString) && FPrefix("\011SWAPPABLE", SwString)
? MAC_SWAP : MAC_NOSWAP);
/* If we are packing code to the default value, change the default. */
if (fPackSet && packLim == LXIVK - 36)
packLim = LXIVK / 2;
}
#endif /* O68K */
#endif /* OSEGEXE */
/***************************************************************/
/* Options shared by DOS3 and segmented exe formats. */
#if OEXE
/*
* HACK ALERT !!!!!!!!!!!!!!!
* Function SetDosseg is used to hide local call to SwDosseg().
* This function is called from ComRc1 (in NEWTP1.C).
*
*/
void SetDosseg(void)
{
SwDosseg();
}
LOCAL void NEAR SwDosseg() /* DOS Segment ordering switch given */
{
static FTYPE FirstTimeCalled = (FTYPE) TRUE; /* If true create symbols _edata */
/* and _end */
fSegOrder = (FTYPE) TRUE; /* Set switch */
if (FirstTimeCalled && vfPass1)
{
MkPubSym((BYTE *) "\006_edata",0,0,(RATYPE)0);
MkPubSym((BYTE *) "\007__edata",0,0,(RATYPE)0);
MkPubSym((BYTE *) "\004_end",0,0,(RATYPE)0);
MkPubSym((BYTE *) "\005__end",0,0,(RATYPE)0);
FirstTimeCalled = FALSE;
#if ODOS3EXE
if (cparMaxAlloc == 0)
cparMaxAlloc = 0xFFFF; /* Turn off /HIGH */
vfDSAlloc = FALSE; /* Turn off DS Allocation */
#endif
}
}
#if ODOS3EXE
LOCAL void NEAR SwDosExtend(void)
{
long mode; // Extender mode
int rc;
if ((rc = ParseNo(&mode)) < 0) // Quit if error
return;
if (rc)
dosExtMode = (WORD) mode;
fDOSExtended = (FTYPE) TRUE;
}
#endif
#if TIMINGS
LOCAL void NEAR SwShowTiming(void)
{
extern int fShowTiming;
fShowTiming = TRUE;
}
#endif // TIMINGS
#if USE_REAL
LOCAL void NEAR SwNoUseReal(void)
{
fSwNoUseReal = TRUE;
}
#endif
#if FEXEPACK
LOCAL void NEAR SwExePack() /* Set exepack switch */
{
#if QBLIB
/* If /QUICKLIB given, issue fatal error. */
if(fQlib)
Fatal(ER_swqe);
#endif
#if ODOS3EXE
if (cparMaxAlloc == 0)
OutWarn(ER_loadhi);
else
#endif
fExePack = (FTYPE) TRUE;
}
#endif
LOCAL void NEAR SwNonulls ()
{
extern FTYPE fNoNulls;
/*
* /NONULLSDOSSEG: just like /DOSSEG except do not insert
* 16 null bytes in _TEXT.
*/
SwDosseg();
fNoNulls = (FTYPE) TRUE;
}
LOCAL void NEAR SwNoFarCall() /* Disable far call optimization */
{
fFarCallTrans = FALSE;
}
void NEAR SwNoPack() /* Disable code packing */
{
fPackSet = (FTYPE) TRUE; /* Remember packLim was set */
packLim = 0L;
}
LOCAL void NEAR SwPack() /* Pack code segments */
{
int rc;
fPackSet = (FTYPE) TRUE; /* Remember packLim was set */
if((rc = ParseNo(&packLim)) < 0) /* Quit if error */
return;
if(!rc)
#if EXE386
packLim = CBMAXSEG32; /* Default limit is 4Gb */
#else
#if O68K
packLim = iMacType != MAC_NONE ? LXIVK / 2 : LXIVK - 36;
/* Default limit is 32K or 64K - 36 */
#else
packLim = LXIVK - 36; /* Default limit is 64K - 36 */
#endif
else if(packLim > LXIVK) /* If limit set too high */
SwitchErr = ER_swpack;
else if(packLim > LXIVK - 36)
OutWarn(ER_pckval);
#endif
}
LOCAL void NEAR SwPackData() /* Pack data segments */
{
int rc;
fPackData = (FTYPE) TRUE;
if((rc = ParseNo(&DataPackLim)) < 0)/* Quit if error */
return;
if(!rc)
#if EXE386
DataPackLim = CBMAXSEG32; /* Default limit is 4Gb */
#else
DataPackLim = LXIVK; /* Default limit is 64K */
else if(DataPackLim > LXIVK) /* If limit set too high */
SwitchErr = ER_swpack;
#endif
}
LOCAL void NEAR SwNoPackFunctions()// DO NOT eliminate uncalled COMDATs
{
fPackFunctions = (FTYPE) FALSE;
}
LOCAL void NEAR SwPackFunctions()// DO eliminate uncalled COMDATs
{
#if TCE
SBTYPE SwString;
int rc;
#endif
fPackFunctions = (FTYPE) TRUE;
#if TCE
if ((rc = ParseStr(SwString)) <= 0) // Done if no num or error
return;
if (SwString[1] == 'M' || SwString[1] == 'm')
{
fTCE = (FTYPE) TRUE; // /PACKF:MAX = perform TCE
fprintf(stdout, "\r\nTCE is active. ");
}
#endif
}
LOCAL void NEAR SwFarCall() /* Enable far call optimization */
{
fFarCallTrans = (FTYPE) TRUE;
}
#endif /* OEXE */
#if DOSEXTENDER
LOCAL void NEAR SwRunReal(void)
{
OutWarn(ER_rnotfirst);
}
#endif
/***************************************************************/
/* Options for DOS3 exe format. */
#if ODOS3EXE
LOCAL void NEAR SwDSAlloc() /* DS allocation requested */
{
if(!fSegOrder) vfDSAlloc = (FTYPE) TRUE; /* Set flag if not overridden */
}
#if OVERLAYS
LOCAL void NEAR SwDynamic(void)
{
unsigned long cThunks;
int rc;
if ((rc = ParseNo(&cThunks)) < 0)
return; /* Bad argument */
fDynamic = (FTYPE) TRUE;
fFarCallTrans = (FTYPE) TRUE;
fPackSet = (FTYPE) TRUE;
packLim = LXIVK - 36; /* Default limit is 64K - 36 */
if (!rc)
cThunks = 256;
else if (cThunks > LXIVK / OVLTHUNKSIZE)
{
char buf[17];
cThunks = LXIVK / OVLTHUNKSIZE;
OutWarn(ER_arginvalid, "DYNAMIC", _itoa((WORD)cThunks, buf, 10));
}
ovlThunkMax = (WORD) cThunks;
}
LOCAL void NEAR SwOldOvl(void)
{
fOldOverlay = (FTYPE) TRUE;
fDynamic = (FTYPE) FALSE;
}
#endif
LOCAL void NEAR SwHigh() /* Load high */
{
if(!fSegOrder)
{
#if FEXEPACK
if (fExePack == (FTYPE) TRUE)
{
OutWarn(ER_loadhi);
fExePack = FALSE;
}
#endif
cparMaxAlloc = 0; /* Dirty trick! */
}
}
#if OVERLAYS
LOCAL void NEAR SwIntNo()
{
unsigned long intno;
int rc;
if((rc = ParseNo(&intno)) < 0) /* Quit if error */
return;
if(rc == 0 || intno > 255) /* If no number or num exceeds 255 */
SwitchErr = ER_swovl;
else vintno = (BYTE) intno; /* Else store interrupt number */
}
#endif
LOCAL void NEAR SwCParMax()
{
unsigned long cparmax;
int rc;
if((rc = ParseNo(&cparmax)) < 0) /* Quit if error */
return;
if(rc == 0 || cparmax > 0xffffL) /* If no number or num exceeds ffff */
SwitchErr = ER_swcpar;
else cparMaxAlloc = (WORD) cparmax; /* Else store cparMaxAlloc */
}
LOCAL void NEAR SwNoGrp()
{
fNoGrpAssoc = (FTYPE) TRUE; /* Don't associate publics w/ groups */
}
#endif /* ODOS3EXE */
/***************************************************************/
/* Options for ILINK-version */
#if ILINK
LOCAL void NEAR SwIncremental() /* Incremental linking support */
{
//fIncremental = (FTYPE) !fZincr;
fIncremental = (FTYPE) FALSE; //INCR support dropped in 5.30.30
}
#endif
LOCAL void NEAR SwPadCode() /* Code padding */
{
long num;
int rc;
if((rc = ParseNo(&num)) < 0)
return;
/* PADCODE:xxx option specifies code padding size */
if(rc)
{
if(num < 0 || num > 0x8000)
SwitchErr = ER_swbadnum;
else cbPadCode = (WORD) num;
}
}
LOCAL void NEAR SwPadData() /* Data padding */
{
long num;
int rc;
if((rc = ParseNo(&num)) < 0)
return;
/* PADDATA:xxx option specifies data padding size */
if(rc)
{
if(num < 0 || num > 0x8000)
SwitchErr = ER_swbadnum;
else cbPadData = (WORD) num;
}
}
/***************************************************************/
/* Switches for segmented x.out format */
#if OIAPX286
LOCAL void NEAR SwAbsolute ()
{
if(!cbStack)
ParseNo(&absAddr);
}
LOCAL void NEAR SwNoPack() /* Disable code packing */
{
fPack = FALSE;
}
LOCAL void NEAR SwTextbias ()
{
long num;
if(ParseNo(&num) > 0)
stBias = num;
}
LOCAL void NEAR SwDatabias ()
{
long num;
if(ParseNo(&num) > 0)
stDataBias = num;
}
LOCAL void NEAR SwPagesize ()
{
long num;
if(ParseNo(&num) > 0)
cblkPage = num >> 9;
}
LOCAL void NEAR SwTextrbase ()
{
long num;
if(ParseNo(&num) > 0)
rbaseText = num;
}
LOCAL void NEAR SwDatarbase ()
{
long num;
if(ParseNo(&num) > 0)
rbaseData = num;
}
LOCAL void NEAR SwVmod ()
{
long num;
if(ParseNo(&num) <= 0)
return;
switch(num)
{
case 0:
xevmod = XE_VMOD;
break;
case 1:
xevmod = XE_EXEC | XE_VMOD;
break;
default:
SwitchErr = ER_swbadnum;
}
}
#endif /* OIAPX286 */
#if OXOUT OR OIAPX286
LOCAL void NEAR SwNosymbols ()
{
fSymbol = FALSE;
}
LOCAL void NEAR SwMixed ()
{
fMixed = (FTYPE) TRUE;
}
LOCAL void NEAR SwLarge ()
{
fLarge = (FTYPE) TRUE;
SwMedium();
}
LOCAL void NEAR SwMedium()
{
fMedium = (FTYPE) TRUE; /* Medium model */
fIandD = (FTYPE) TRUE; /* Separate code and data */
}
LOCAL void NEAR SwPure()
{
fIandD = (FTYPE) TRUE; /* Separate code and data */
}
#endif /* OXOUT OR OIAPX286 */
/* Options for linker profiling */
#if LNKPROF
char fP1stop = FALSE; /* Stop after pass 1 */
LOCAL void NEAR SwPass1()
{
fP1stop = (FTYPE) TRUE;
}
#endif /* LNKPROF */
/* Special options */
#if QBLIB
LOCAL void NEAR SwQuicklib() /* Create a QB userlibrary */
{
#if FEXEPACK
/* If /EXEPACK given, issue fatal error. */
if(fExePack)
Fatal(ER_swqe);
#endif
fQlib = (FTYPE) TRUE;
SwDosseg(); /* /QUICKLIB forces /DOSSEG */
fNoExtDic = (FTYPE) TRUE; /* /QUICKLIB forces /NOEXTDICTIONARY */
}
#endif
#if (QCLINK) AND NOT EXE386
typedef int (cdecl far * FARFPTYPE)(int, ...);/* Far function pointer type */
extern FARFPTYPE far *pfQTab; /* Table of addresses */
#pragma check_stack(on)
/*
* PromptQC : output a prompt to the QC prompt routine
*
* Call pfQTab[1] with parameters described below.
* Returns:
* always TRUE
*
* QCPrompt : display a message with a prompt
*
* void far QCPrompt (type, msg1, msg2, msg3, pResponse)
* short type; /* type of message, as follows:
* 0 = undefined
* 1 = edit field required (e.g. file name)
* 2 = wait for some action
* all other values undefined
* Any of the following fields may be NULL:
* char far *msg1; /* 1st message (error)
* char far *msg2; /* 2nd message (file name)
* char far *msg3; /* 3rd message (prompt text)
* char far *pResponse; /* Pointer to buffer in which to
* * store response.
*/
int cdecl PromptQC (sbNew,msg,msgparm,pmt,pmtparm)
BYTE *sbNew; /* Buffer for response */
MSGTYPE msg; /* Error message */
int msgparm; /* Message parameter */
MSGTYPE pmt; /* Prompt */
int pmtparm; /* Prompt parameter */
{
int type;
SBTYPE message;
SBTYPE prompt;
if(sbNew != NULL)
type = 1;
else
type = 2;
sprintf(message,GetMsg(msg),msgparm);
sprintf(prompt,GetMsg(pmt),pmtparm);
/* Return value of 1 means interrupt. */
if((*pfQTab[1])(type,(char far *) message,0L,(char far *)prompt,
(char far *) sbNew) == 1)
UserKill();
return(TRUE);
}
#pragma check_stack(off)
/*
* CputcQC : console character output routine for QC
*/
void CputcQC (ch)
int ch;
{
}
/*
* CputsQC : console string output routine for QC
*/
void CputsQC (str)
char *str;
{
}
/*
* SwZ1 : process /Z1:address
*
* /Z1 is a special undocumented switch for QC. It contains
* the address of a table of routines to use for console I/O.
*/
LOCAL void NEAR SwZ1 (void) /* Get address for message I/O */
{
long num;
extern FARFPTYPE far
*pfQTab; /* Table of addresses */
if(ParseNo(&num) <= 0)
return;
pfQTab = (FARFPTYPE far *) num;
pfPrompt = PromptQC;
fNoprompt = FALSE; /* Disable /NOPROMPT */
fNoBanner = (FTYPE) TRUE;
pfCputc = CputcQC;
pfCputs = CputsQC;
fZ1 = (FTYPE) TRUE;
}
/*
* /Zincr is a special undocumented switch for QC. It is required
* for "ILINK-breaking" errors. If ILINK encounters one of these errors,
* it ivokes the linker w /ZINCR which override /INCR.
*/
LOCAL void NEAR SwZincr(void)
{
fZincr = (FTYPE) TRUE;
}
#endif
#if Z2_ON OR (QCLINK AND NOT EXE386)
/*
* SwZ2 : process /Z2
*
* /Z2 is another special undocumented switch for QC.
* It causes deletion of responce file passed to the linker.
*/
LOCAL void NEAR SwZ2 (void)
{
fZ2 = (FTYPE) TRUE;
}
#endif
/* Structure for table of linker options */
struct option
{
char *sbSwitch; /* length-prefixed switch string */
#ifdef M68000
int (*proc)(); /* pointer to switch function */
#else
void (NEAR *proc)(); /* pointer to switch function */
#endif
};
/* Table of linker options */
LOCAL struct option switchTab[] =
{
#if NOT WIN_3
{ "\01?", SwShortHelp },
#endif
#if OIAPX286
{ "\017ABSOLUTEADDRESS", SwAbsolute },
#endif
#if OSEGEXE AND NOT QCLINK
{ "\011ALIGNMENT", SwAlign },
#endif
{ "\005BATCH", SwBatch },
#if LNKPROF
{ "\007BUFSIZE", SwBufsize },
#endif
#if SYMDEB
{ "\010CODEVIEW", SwSymdeb },
#endif
#if ODOS3EXE
{ "\014CPARMAXALLOC", SwCParMax },
#endif
#if OIAPX286
{ "\010DATABIAS", SwDatabias },
{ "\011DATARBASE", SwDatarbase },
#endif
#if ODOS3EXE
{ "\013DOSEXTENDER", SwDosExtend },
#endif
#if OEXE
{ "\006DOSSEG", SwDosseg },
#endif
#if ODOS3EXE
{ "\012DSALLOCATE", SwDSAlloc },
#if OVERLAYS
{ "\007DYNAMIC", SwDynamic },
#endif
#endif
#if FEXEPACK
{ "\007EXEPACK", SwExePack },
#endif
#if OEXE
{ "\022FARCALLTRANSLATION", SwFarCall },
#endif
#if EXE386
{ "\006HEADER", SwHeader },
#endif
#if NOT WIN_3
{ "\004HELP",
#if C8_IDE
SwShortHelp },
#else
SwHelp },
#endif
#endif
#if ODOS3EXE
{ "\004HIGH", SwHigh },
#endif
#if NOT IGNORECASE
{ "\012IGNORECASE", SwCase },
#endif
#if EXE386
{ "\016IMAGEALIGNMENT", SwMemAlign },
#endif
#if ILINK AND NOT IBM_LINK
{ "\013INCREMENTAL", SwIncremental },
#endif
#if FDEBUG
{ "\013INFORMATION", SwInfo },
#endif
#if OSEGEXE AND OUT_EXP
{ "\004IDEF", SwIdef },
#endif
#if (OSEGEXE AND defined(LEGO)) OR EXE386
{ "\012KEEPFIXUPS", SwKeepFixups },
#endif
#if EXE386
{ "\012KEEPVSIZE", SwKeepVSize },
#endif
#if OIAPX286
{ "\005LARGE", SwLarge },
#endif
{ "\013LINENUMBERS", SwLineNos },
#if LOCALSYMS
{ "\014LOCALSYMBOLS", SwLocals },
#endif
#if O68K
{ "\011MACINTOSH", SwMac },
#endif /* O68K */
{ "\003MAP", SwMap },
#if OXOUT OR OIAPX286
{ "\006MEDIUM", SwMedium },
{ "\005MIXED", SwMixed },
#endif
#if NOT EXE386
{ "\010KNOWEAS", SwNewFiles },
#endif
{ "\026NODEFAULTLIBRARYSEARCH",
SwNoDefLib },
#if LIBMSDOS
{ "\017NOEXTDICTIONARY", SwNoExtDic },
#endif
#if OEXE
{ "\024NOFARCALLTRANSLATION",
SwNoFarCall },
#endif
#if ODOS3EXE
{ "\022NOGROUPASSOCIATION", SwNoGrp },
#endif
#if IGNORECASE
{ "\014NOIGNORECASE", SwCase },
#endif
#if TIMINGS
{ "\002BT", SwShowTiming },
#endif // TIMINGS
{ "\006NOLOGO", SwNologo },
{ "\015NONULLSDOSSEG", SwNonulls },
{ "\012NOPACKCODE", SwNoPack },
{ "\017NOPACKFUNCTIONS", SwNoPackFunctions },
#if OXOUT OR OIAPX286
{ "\011NOSYMBOLS", SwNosymbols },
#endif
#if USE_REAL
{ "\011NOFREEMEM", SwNoUseReal },
#endif
#if OVERLAYS
{ "\012OLDOVERLAY", SwOldOvl },
#endif
{ "\007ONERROR", SwDelexe },
#if OVERLAYS
{ "\020OVERLAYINTERRUPT", SwIntNo },
#endif
{ "\010PACKCODE", SwPack },
{ "\010PACKDATA", SwPackData },
{ "\015PACKFUNCTIONS", SwPackFunctions },
#if ILINK AND NOT IBM_LINK
{ "\007PADCODE", SwPadCode },
{ "\007PADDATA", SwPadData },
#endif
#if OIAPX286
{ "\010PAGESIZE", SwPagesize },
#endif
#if OSMSDOS
{ "\005PAUSE", SwPause },
#endif
#if LNKPROF
{ "\005PASS1", SwPass1 },
#endif
#if PCODE
{ "\005PCODE", SwPCode },
#endif
#if OSEGEXE AND NOT (QCLINK OR EXE386)
{ "\006PMTYPE", SwPmType },
#endif
#if OXOUT OR OIAPX286
{ "\004PURE", SwPure },
#endif
#if QBLIB
{ "\014QUICKLIBRARY", SwQuicklib },
#endif
#if DOSEXTENDER AND NOT WIN_NT
{ "\001r", SwRunReal },
#endif
{ "\010SEGMENTS", SwSegments },
{ "\005STACK", SwStack },
#if OIAPX286
{ "010TEXTBIAS", SwTextbias },
{ "\011TEXTRBASE", SwTextrbase },
#endif
#if ODOS3EXE
{ "\004TINY", SwBinary },
#endif
#if PERFORMANCE
{ "\030VIRTUALMEMORYPERFORMANCE",
SwVMPerf },
#endif
#if OIAPX286
{ "\004VMOD", SwVmod },
#endif
#if OSEGEXE AND NOT QCLINK
{ "\011WARNFIXUP", SwWarnFixup },
#endif
#if OSEGEXE AND NOT EXE386 AND QCLINK
{ "\002Z1", SwZ1 },
#endif
#if Z2_ON OR QCLINK
{ "\002Z2", SwZ2 },
#endif
#if QCLINK
{ "\005ZINCR", SwZincr },
#endif
{ NULL, 0}
};
#if QCLINK
#define SWSTOP &switchTab[(sizeof(switchTab)/sizeof(struct option)) - 5]
#else
#if EXE386
#define SWSTOP &switchTab[(sizeof(switchTab)/sizeof(struct option)) - 2]
#else
#define SWSTOP &switchTab[(sizeof(switchTab)/sizeof(struct option)) - 2]
#endif
#endif
#define FIELDLENGTH 28
#if NOT WIN_3
LOCAL void NEAR SwShortHelp() /* Print valid switches */
{
struct option *pTab; /* Option table pointer */
int toggle = 1;
int n;
#if CMDMSDOS
/* Maybe display banner here, in case /NOLOGO seen first. */
DisplayBanner();
#endif
fputs(GetMsg(P_usage1),stdout);
fputs(GetMsg(P_usage2),stdout);
fputs(GetMsg(P_usage3),stdout);
fputs(GetMsg(P_switches),stdout);
NEWLINE(stdout);
for(pTab = switchTab; pTab < SWSTOP; ++pTab)
{
// Don't display undocumented swiches
if (pTab->proc == &SwNewFiles)
{
continue;
}
if (pTab->proc == &SwDosExtend)
{
continue;
}
#ifdef LEGO
#if OSEGEXE
if (pTab->proc == &SwKeepFixups)
continue;
#endif
#endif /* LEGO */
fputs(" /",stdout);
fwrite(&pTab->sbSwitch[1],1,B2W(pTab->sbSwitch[0]),stdout);
/* Output switches in two columns */
if(toggle ^= 1)
NEWLINE(stdout);
else for(n = FIELDLENGTH - B2W(pTab->sbSwitch[0]); n > 0; --n)
fputc(' ',stdout);
}
NEWLINE(stdout);
fflush(stdout);
#if USE_REAL
RealMemExit();
#endif
exit(0);
}
#endif
LOCAL void NEAR SwDelexe() // Supress .EXE generation if errors occur
{
SBTYPE SwString;
int rc;
vfMap = (FTYPE) TRUE; // Set flag
if ((rc = ParseStr(SwString)) == 0) // NOEXE not present
{
OutWarn(ER_opnoarg, "ONERROR");
return;
}
if (SwString[1] == 'N' || SwString[1] == 'n')
{
fDelexe = TRUE; // ONERROR:NOEXE
}
else
{ // ONERROR:????
OutWarn(ER_opnoarg, "ONERROR");
return;
}
}
#if (OSEGEXE AND defined(LEGO)) OR EXE386
LOCAL void NEAR SwKeepFixups(void)
{
fKeepFixups = (FTYPE) TRUE;
}
#endif
#if EXE386
LOCAL void NEAR SwHeader() // Set executable header size
{
int rc;
DWORD newSize;
if ((rc = ParseNo(&newSize)) < 0) // Quit if error
return;
if (rc)
hdrSize = ((newSize << 10) + 0xffffL) & ~0xffffL;
}
LOCAL void NEAR SwKeepVSize(void)
{
fKeepVSize = (FTYPE) TRUE;
}
#endif
#if NOT WIN_3
LOCAL void NEAR SwHelp() /* Print valid switches */
{
intptr_t exitCode;
char *pszPath;
char *pszQH;
char *pszHELPFILES;
char FAR *lpch;
char *pch;
int len;
// Try to use QuickHelp - this is tricky; We have stubbed the
// C run-time environment setup, so spawnlp has no way of
// searching the path. Here we first add the path to C run-time
// environemt table and then invoke spawnlp.
if (lpszPath)
{
// Recreate C run-time PATH variable
len = FSTRLEN(lpszPath);
if ((pszPath = calloc(len + 6, sizeof(char))) != NULL)
{
strcpy(pszPath, "PATH=");
for (lpch = lpszPath, pch = pszPath + 5; len > 0; len--)
*pch++ = *lpch++;
_putenv(pszPath);
}
}
if (lpszQH)
{
// Recreate C run-time QH variable
len = FSTRLEN(lpszQH);
if ((pszQH = calloc(len + 4, sizeof(char))) != NULL)
{
strcpy(pszQH, "QH=");
for (lpch = lpszQH, pch = pszQH + 3; len > 0; len--)
*pch++ = *lpch++;
_putenv(pszQH);
}
}
if (lpszHELPFILES)
{
// Recreate C run-time HELPFILES variable
len = FSTRLEN(lpszHELPFILES);
if ((pszHELPFILES = calloc(len + 12, sizeof(char))) != NULL)
{
strcpy(pszHELPFILES, "HELPFILES=");
for (lpch = lpszHELPFILES, pch = pszHELPFILES + 10; len > 0; len--)
*pch++ = *lpch++;
_putenv(pszHELPFILES);
}
}
#if USE_REAL
RealMemExit();
#endif
exitCode = _spawnlp(P_WAIT, "QH.EXE", "qh", "/u link.exe", NULL);
if (exitCode < 0 || exitCode == 3)
SwShortHelp();
exit(0);
}
#endif
/****************************************************************
* *
* BadFlag: *
* *
* This function takes as its argument a pointer to a length- *
* prefixed string containing an invalid switch. It goes *
* through the customary contortions of dying with grace. *
* *
****************************************************************/
LOCAL void NEAR BadFlag(psb,errnum)
BYTE *psb; /* Pointer to the bad switch */
MSGTYPE errnum; /* Error number */
{
psb[B2W(psb[0]) + 1] = '\0'; /* Null-terminate it */
Fatal(errnum,psb + 1);
}
/****************************************************************
* *
* FPrefix: *
* *
* This function takes as its arguments two pointers to *
* length-prefixed strings. It returns true if the second is *
* a prefix of the first. *
* *
****************************************************************/
LOCAL int NEAR FPrefix(psb1,psb2)
BYTE *psb1; /* Pointer to first string */
BYTE *psb2; /* Pointer to second string */
{
REGISTER WORD len; /* Length of string 2 */
if((len = B2W(psb2[0])) > B2W(psb1[0])) return(FALSE);
/* String 2 cannot be longer */
while(len) /* Compare the strings */
{
if(UPPER(psb2[len]) != UPPER(psb1[len])) return(FALSE);
/* Check for mismatch */
--len; /* Decrement index */
}
return(TRUE); /* 2 is a prefix of 1 */
}
/****************************************************************
* *
* ProcFlag: *
* *
* This function takes as its argument a length-prefixed *
* string containing a single '/-type' flag. It processes it, *
* but does not return a meaningful value. *
* *
****************************************************************/
void ProcFlag(psb) /* Process a flag */
BYTE *psb; /* Pointer to flag string */
{
struct option *pTab; /* Pointer to option table */
struct option *pTabMatch; /* Pointer to matching entry */
WORD ich3; /* Index */
WORD ich4; /* Index */
pTabMatch = NULL; /* Not found */
if((ich3 = IFind(psb,':')) == INIL)
ich3 = B2W(psb[0]); /* Get index to colon */
ich4 = B2W(psb[0]); /* Save the original length */
psb[0] = (BYTE) ich3; /* Set new length */
for(pTab = switchTab; pTab->sbSwitch; pTab++)
{ /* Loop thru switch table */
if(FPrefix(pTab->sbSwitch,psb))
{ /* If we've identified the switch */
if(pTabMatch) /* If there was a previous match */
BadFlag(psb,ER_swambig);/* Ambiguous switch */
pTabMatch = pTab; /* Save the match */
}
}
if(!pTabMatch) /* If no match found */
{
psb[psb[0]+1] = '\0';
OutWarn(ER_swunrecw,&psb[1]); /* Unrecognized switch */
return;
}
psb[0] = (BYTE) ich4; /* Restore original length */
osbSwitch = psb; /* Pass the switch implicitly */
SwitchErr = 0; /* Assume no error */
(*pTabMatch->proc)(); /* Invoke the processing procedure */
if(SwitchErr) BadFlag(psb,SwitchErr); /* Check for errors */
}
#pragma check_stack(on)
/****************************************************************
* *
* PeelFlags: *
* *
* This function takes as its argument a pointer to a length- *
* prefixed string of bytes. It "peels/processes all '/-type' *
* switches." It does not return a meaningful value. *
* *
****************************************************************/
void PeelFlags(psb) /* Peel/process flags */
BYTE *psb; /* Pointer to a byte string */
{
REGISTER WORD ich; /* Index */
SBTYPE sbFlags; /* The flags */
if((ich = IFind(psb,CHSWITCH)) != INIL)
{ /* If a switch found */
memcpy(&sbFlags[1],&psb[ich + 2],B2W(psb[0]) - ich - 1);
/* Move flags to flag buffer */
sbFlags[0] = (BYTE) ((psb[0]) - ich - 1);
/* Set the length of flags */
while(psb[ich] == ' ' && ich) --ich;
/* Delete trailing spaces */
psb[0] = (BYTE) ich; /* Reset length of input line */
ich = sbFlags[0];
while((sbFlags[ich] == ' ' ||
sbFlags[ich] == ';' ||
sbFlags[ich] == ',' ) && ich) --ich;
/* Delete unwanted characters */
sbFlags[0] = (BYTE) ich;
BreakLine(sbFlags,ProcFlag,CHSWITCH);
/* Process the switch */
}
}
#pragma check_stack(off)