850 lines
35 KiB
C
850 lines
35 KiB
C
|
/* SCCSID = %W% %E% */
|
||
|
/*
|
||
|
* Copyright Microsoft Corporation, 1983-1987
|
||
|
*
|
||
|
* This Module contains Proprietary Information of Microsoft
|
||
|
* Corporation and should be treated as Confidential.
|
||
|
*/
|
||
|
/****************************************************************
|
||
|
* *
|
||
|
* NEWCMD.C *
|
||
|
* *
|
||
|
* Support routines for command prompter. *
|
||
|
* *
|
||
|
****************************************************************/
|
||
|
|
||
|
#include <minlit.h> /* Types and constants */
|
||
|
#include <bndtrn.h> /* The same */
|
||
|
#include <bndrel.h> /* Types and constants */
|
||
|
#include <lnkio.h> /* Linker I/O definitions */
|
||
|
#include <lnkmsg.h> /* Error messages */
|
||
|
#include <extern.h> /* External function declarations */
|
||
|
|
||
|
/*
|
||
|
* FUNCTION PROTOTYPES
|
||
|
*/
|
||
|
|
||
|
LOCAL int NEAR GetInputByte(char *prompt);
|
||
|
LOCAL BYTE NEAR GetStreamByte(char *prompt);
|
||
|
LOCAL void NEAR SetUpCommandLine(int argc, char **argv);
|
||
|
LOCAL void NEAR FinishCommandLine(void);
|
||
|
#if AUTOVM
|
||
|
BYTE FAR * NEAR FetchSym1(RBTYPE rb, WORD Dirty);
|
||
|
#define FETCHSYM FetchSym1
|
||
|
#else
|
||
|
#define FETCHSYM FetchSym
|
||
|
#endif
|
||
|
|
||
|
#if OSMSDOS
|
||
|
char *stackbuf;
|
||
|
#endif
|
||
|
LOCAL FTYPE fMoreCommandLine;
|
||
|
/* More command line flag */
|
||
|
LOCAL FTYPE fMoreIndirectFile;
|
||
|
/* More-input-from-file flag */
|
||
|
LOCAL BSTYPE bsIndir; /* File handle for indirect file */
|
||
|
LOCAL FTYPE fEscNext;
|
||
|
LOCAL FTYPE fNewLine = (FTYPE) TRUE;/* New command line */
|
||
|
LOCAL FTYPE fStuffed; /* Put-back-character flag */
|
||
|
LOCAL BYTE bStuffed; /* The character put back */
|
||
|
LOCAL BYTE bSepLast; /* Last separator character */
|
||
|
/* Char to replace spaces with */
|
||
|
LOCAL FTYPE fRedirect; /* True iff stdin not a device */
|
||
|
LOCAL WORD fQuotted; /* TRUE if inside " ... " */
|
||
|
LOCAL char *pszRespFile; /* Pointer to responce file name */
|
||
|
LOCAL char MaskedChar;
|
||
|
|
||
|
#if TRUE
|
||
|
#define SETCASE(c) (c) /* Leave as is */
|
||
|
#else
|
||
|
#define SETCASE(c) UPPER(c) /* Force to upper case */
|
||
|
#endif
|
||
|
|
||
|
#if WIN_3
|
||
|
extern char far *fpszLinkCmdLine;
|
||
|
#endif
|
||
|
|
||
|
#if AUTOVM
|
||
|
|
||
|
/*
|
||
|
* HACK ALERT !!!!!!!!!!!!
|
||
|
*
|
||
|
* This function is repeated here becouse of mixed medium model.
|
||
|
* This the same code as in NEWSYM.c but maked here LOCAL. This
|
||
|
* allows near calls to this function in all segments, otherwise
|
||
|
* function must be called as far.
|
||
|
*/
|
||
|
|
||
|
extern short picur;
|
||
|
|
||
|
/****************************************************************
|
||
|
* *
|
||
|
* FetchSym: *
|
||
|
* *
|
||
|
* This function fetches a symbol from the symbol table given *
|
||
|
* its virtual address. The symbol may either be resident or *
|
||
|
* in virtual memory. *
|
||
|
* *
|
||
|
****************************************************************/
|
||
|
|
||
|
BYTE FAR * NEAR FetchSym1(rb,fDirty)
|
||
|
RBTYPE rb; /* Virtual address */
|
||
|
WORD fDirty; /* Dirty page flag */
|
||
|
{
|
||
|
union {
|
||
|
long vptr; /* Virtual pointer */
|
||
|
BYTE FAR *fptr; /* Far pointer */
|
||
|
struct {
|
||
|
unsigned short offset;
|
||
|
/* Offset value */
|
||
|
unsigned short seg;
|
||
|
} /* Segmnet value */
|
||
|
ptr;
|
||
|
}
|
||
|
pointer; /* Different ways to describe pointer */
|
||
|
|
||
|
pointer.fptr = rb;
|
||
|
|
||
|
if(pointer.ptr.seg) /* If resident - segment value != 0 */
|
||
|
{
|
||
|
picur = 0; /* Picur not valid */
|
||
|
return(pointer.fptr); /* Return pointer */
|
||
|
}
|
||
|
pointer.fptr = (BYTE FAR *) mapva(AREASYMS + (pointer.vptr << SYMSCALE),fDirty);
|
||
|
/* Fetch from virtual memory */
|
||
|
return(pointer.fptr);
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
// Strip path from file spec - leave drive letter and filename
|
||
|
|
||
|
void StripPath(BYTE *sb)
|
||
|
{
|
||
|
char Drive[_MAX_DRIVE];
|
||
|
char Dir[_MAX_DIR];
|
||
|
char Name[_MAX_FNAME];
|
||
|
char Ext[_MAX_EXT];
|
||
|
|
||
|
/* Decompose filename into four components */
|
||
|
|
||
|
sb[sb[0]+1] = '\0';
|
||
|
_splitpath(sb+1, Drive, Dir, Name, Ext);
|
||
|
|
||
|
/* Create modified path name */
|
||
|
|
||
|
_makepath(sb+1, Drive, NULL, Name, Ext);
|
||
|
sb[0] = (BYTE) strlen(sb+1);
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
/****************************************************************
|
||
|
* *
|
||
|
* SetUpCommandLine: *
|
||
|
* *
|
||
|
* This function initializes the command line parser. *
|
||
|
* *
|
||
|
****************************************************************/
|
||
|
|
||
|
LOCAL void NEAR SetUpCommandLine(int argc,char **argv)
|
||
|
{
|
||
|
fMoreCommandLine = (FTYPE) ((argc - 1) != 0 ? TRUE : FALSE);
|
||
|
/* If command line not empty */
|
||
|
if (!_isatty(fileno(stdin))) /* Determine if stdin is a device */
|
||
|
fRedirect = (FTYPE) TRUE;
|
||
|
}
|
||
|
|
||
|
/****************************************************************
|
||
|
* *
|
||
|
* FinishCommandLine: *
|
||
|
* *
|
||
|
* This function takes no arguments. If command input has *
|
||
|
* been coming from a file, then this function closes that *
|
||
|
* file; otherwise, it has no effect. It does not return a *
|
||
|
* meaningful value. *
|
||
|
* *
|
||
|
****************************************************************/
|
||
|
|
||
|
LOCAL void NEAR FinishCommandLine(void) /* Close indirect file */
|
||
|
{
|
||
|
fflush(stdout); /* Force to screen */
|
||
|
if(fMoreIndirectFile) /* If command input from file */
|
||
|
{
|
||
|
fMoreIndirectFile = FALSE; /* No more indirect file */
|
||
|
fclose(bsIndir); /* Close indirect file */
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#if ECS
|
||
|
/*
|
||
|
* GetTxtChr : get the next character from a text file stream
|
||
|
*
|
||
|
* This routine handles mixed DBCS and ASCII characters as
|
||
|
* follows:
|
||
|
*
|
||
|
* 1. The second byte of a DBCS character is returned in a
|
||
|
* word with the high byte set to the lead byte of the character.
|
||
|
* Thus the return value can be used in comparisions with
|
||
|
* ASCII constants without being mistakenly matched.
|
||
|
*
|
||
|
* 2. A DBCS space character (0x8140) is returned as two
|
||
|
* ASCII spaces (0x20). I.e. return a space the 1st and 2nd
|
||
|
* times we're called.
|
||
|
*
|
||
|
* 3. ASCII characters and lead bytes of DBCS characters
|
||
|
* are returned in the low byte of a word with the high byte
|
||
|
* set to 0.
|
||
|
*/
|
||
|
|
||
|
int GetTxtChr (bs)
|
||
|
BSTYPE bs;
|
||
|
{
|
||
|
static int chBuf = -1; /* Character buffer */
|
||
|
int next; /* The next byte */
|
||
|
int next2; /* The one after that */
|
||
|
|
||
|
/* -1 in chBuf means it doesn't contain a valid character */
|
||
|
|
||
|
/* If we're not in the middle of a double-byte character,
|
||
|
* get the next byte and process it.
|
||
|
*/
|
||
|
if(chBuf == -1)
|
||
|
{
|
||
|
next = getc(bs);
|
||
|
/* If this byte is a lead byte, get the following byte
|
||
|
* and store both as a word in chBuf.
|
||
|
*/
|
||
|
if(IsLeadByte(next))
|
||
|
{
|
||
|
next2 = getc(bs);
|
||
|
chBuf = (next << 8) | next2;
|
||
|
/* If the pair matches a DBCS space, set the return value
|
||
|
* to ASCII space.
|
||
|
*/
|
||
|
if(chBuf == 0x8140)
|
||
|
next = 0x20;
|
||
|
}
|
||
|
}
|
||
|
/* Else we're in the middle of a double-byte character. */
|
||
|
else
|
||
|
{
|
||
|
/* If this is the 2nd byte of a DBCS space, set the return
|
||
|
* value to ASCII space.
|
||
|
*/
|
||
|
if(chBuf == 0x8140)
|
||
|
next = 0x20;
|
||
|
/* Else set the return value to the whole DBCS character */
|
||
|
else
|
||
|
next = chBuf;
|
||
|
/* Reset the character buffer */
|
||
|
chBuf = -1;
|
||
|
}
|
||
|
/* Return the next character */
|
||
|
return(next);
|
||
|
}
|
||
|
#endif
|
||
|
/****************************************************************
|
||
|
* *
|
||
|
* GetInputByte: *
|
||
|
* *
|
||
|
* This function takes as its input a pointer to an asciz *
|
||
|
* string with which to prompt the user when more input is *
|
||
|
* necessary. The function returns a byte of input. It *
|
||
|
* checks to make sure the byte is a printable ascii character *
|
||
|
* or a carriage return (^M). *
|
||
|
* *
|
||
|
****************************************************************/
|
||
|
|
||
|
LOCAL int NEAR GetInputByte(prompt)
|
||
|
char *prompt; /* Pointer to prompt text */
|
||
|
{
|
||
|
REGISTER unsigned b; /* Input byte */
|
||
|
#if ECS || defined(_MBCS)
|
||
|
static FTYPE fInDBC; /* True iff in double-byte char */
|
||
|
#endif
|
||
|
|
||
|
if(fMoreIndirectFile) /* If input from file */
|
||
|
{
|
||
|
for(;;) /* Forever */
|
||
|
{
|
||
|
b = GetTxtChr(bsIndir); /* Read a byte */
|
||
|
if(b == EOF || b == 032) break;
|
||
|
/* Break on end of file */
|
||
|
if(fNewLine) /* If at start of line */
|
||
|
{
|
||
|
if (prompt && !fNoEchoLrf)
|
||
|
(*pfCputs)(prompt); /* Prompt the user */
|
||
|
fNewLine = FALSE; /* Not at beginning anymore */
|
||
|
}
|
||
|
if (prompt && !fNoEchoLrf)
|
||
|
{
|
||
|
#if CRLF
|
||
|
/* Allow both ^J and ^M^J to terminate input lines. */
|
||
|
|
||
|
if(b == '\r') continue;
|
||
|
if(b == '\n') (*pfCputc)('\r');
|
||
|
#endif
|
||
|
(*pfCputc)(SETCASE(b)); /* Output byte */
|
||
|
}
|
||
|
if(b == ';' && !fNoEchoLrf) NEWLINE(stdout);
|
||
|
/* Follow escape by newline */
|
||
|
else if(b == '\n') fNewLine = (FTYPE) TRUE;
|
||
|
/* Look for new line */
|
||
|
else if (b == '\t') b = ' ';
|
||
|
/* Translate tabs to spaces */
|
||
|
if(b == '\n' || b >= ' ') return(SETCASE(b));
|
||
|
/* Return if valid char. */
|
||
|
}
|
||
|
FinishCommandLine(); /* Close indirect file */
|
||
|
}
|
||
|
if(fStuffed) /* If a byte saved */
|
||
|
{
|
||
|
fStuffed = FALSE; /* Now we're unstuffed */
|
||
|
return(bStuffed); /* Return the stuffed byte */
|
||
|
}
|
||
|
if(fMoreCommandLine) /* If more command line */
|
||
|
{
|
||
|
for(;;) /* Forever */
|
||
|
{
|
||
|
if (*lpszCmdLine == '\0') /* If at end of command line */
|
||
|
{
|
||
|
fMoreCommandLine = FALSE;
|
||
|
/* No more command line */
|
||
|
fNewLine = (FTYPE) TRUE;/* New command line */
|
||
|
return('\n'); /* Return '\n' */
|
||
|
}
|
||
|
b = (WORD) (*lpszCmdLine++);/* Get next character */
|
||
|
if (b == '\\' && *lpszCmdLine == '"')
|
||
|
{ /* Skip escaped double quotes */
|
||
|
lpszCmdLine++;
|
||
|
if (*lpszCmdLine == '\0')
|
||
|
{
|
||
|
fMoreCommandLine = FALSE;
|
||
|
/* No more command line */
|
||
|
fNewLine = (FTYPE) TRUE;
|
||
|
/* New command line */
|
||
|
fQuotted = FALSE;
|
||
|
return('\n'); /* Return '\n' */
|
||
|
}
|
||
|
else
|
||
|
b = (WORD) (*lpszCmdLine++);
|
||
|
}
|
||
|
#if ECS || defined(_MBCS)
|
||
|
/* If this is a trailing byte of a DBCS char, set the high
|
||
|
* byte of b to nonzero, so b won't be confused with an ASCII
|
||
|
* constant.
|
||
|
*/
|
||
|
if (fInDBC)
|
||
|
{
|
||
|
b |= 0x100;
|
||
|
fInDBC = FALSE;
|
||
|
}
|
||
|
else
|
||
|
fInDBC = (FTYPE) IsLeadByte(b);
|
||
|
#endif
|
||
|
if (b >= ' ') return(SETCASE(b));
|
||
|
/* Return if valid char. */
|
||
|
}
|
||
|
}
|
||
|
for(;;) /* Forever */
|
||
|
{
|
||
|
if(fNewLine) /* If at start of line */
|
||
|
{
|
||
|
if(prompt && ((!fRedirect && !fNoprompt) || (!fEsc && fNoprompt)))
|
||
|
/* If prompt and input from CON */
|
||
|
(*pfCputs)(prompt); /* Prompt the user */
|
||
|
fNewLine = FALSE; /* Not at beginning anymore */
|
||
|
}
|
||
|
b = GetTxtChr(stdin); /* Read a byte from terminal */
|
||
|
if(b == EOF) b = ';'; /* Treat EOF like escape */
|
||
|
else if (b == '\t') b = ' '; /* Treat tab like space */
|
||
|
if(b == '\n') fNewLine = (FTYPE) TRUE; /* New line */
|
||
|
if(b == '\n' || b >= ' ') return(SETCASE(b));
|
||
|
/* Return if character is valid */
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/****************************************************************
|
||
|
* *
|
||
|
* GetStreamByte: *
|
||
|
* *
|
||
|
* This function takes as its input a pointer to a string of *
|
||
|
* text with which to prompt the user, if necessary. It *
|
||
|
* returns a byte of command input, and opens an indirect file *
|
||
|
* to do so if necessary. *
|
||
|
* *
|
||
|
****************************************************************/
|
||
|
|
||
|
LOCAL BYTE NEAR GetStreamByte(prompt)
|
||
|
char *prompt; /* Pointer to text of prompt */
|
||
|
{
|
||
|
REGISTER WORD ich; /* Index variable */
|
||
|
SBTYPE filnam; /* File name buffer */
|
||
|
WORD b; /* A byte */
|
||
|
#if OSMSDOS
|
||
|
extern char *stackbuf;
|
||
|
#endif
|
||
|
|
||
|
if (((b = (WORD)GetInputByte(prompt)) == INDIR) && !fQuotted)
|
||
|
{ /* If user specifies indirect file */
|
||
|
if (fMoreIndirectFile) Fatal(ER_nestrf);
|
||
|
/* Check for nesting */
|
||
|
DEBUGMSG("Getting response file name");
|
||
|
/* Debug message */
|
||
|
ich = 0; /* Initialize index */
|
||
|
while(ich < SBLEN - 1) /* Loop to get file name */
|
||
|
{
|
||
|
b = (WORD)GetInputByte((char *) 0);
|
||
|
/* Read in a byte */
|
||
|
fQuotted = fQuotted ? b != '"' : b == '"';
|
||
|
if ((!fQuotted && (b == ',' || b == '+' || b == ';' || b == ' ')) ||
|
||
|
b == CHSWITCH || b < ' ') break;
|
||
|
/* Exit loop on non-name char. */
|
||
|
if (b != '"')
|
||
|
filnam[ich++] = (char) b;
|
||
|
/* Store in file name */
|
||
|
}
|
||
|
if(b > ' ') /* If legal input character */
|
||
|
{
|
||
|
fStuffed = (FTYPE) TRUE; /* Set flag */
|
||
|
bStuffed = (BYTE) b; /* Save character */
|
||
|
}
|
||
|
filnam[ich] = '\0'; /* Null-terminate file name */
|
||
|
pszRespFile = _strdup(filnam); /* Duplicate file name */
|
||
|
DEBUGMSG(filnam); /* Debug message */
|
||
|
if((bsIndir = fopen(filnam,RDTXT)) == NULL)
|
||
|
Fatal(ER_opnrf,filnam);
|
||
|
#if OSMSDOS
|
||
|
setvbuf(bsIndir,stackbuf,_IOFBF,512);
|
||
|
#endif
|
||
|
fMoreIndirectFile = (FTYPE) TRUE;/* Take input from file now */
|
||
|
b = (WORD)GetInputByte(prompt); /* Read a byte */
|
||
|
DEBUGVALUE(b); /* Debug info */
|
||
|
}
|
||
|
return((BYTE) b); /* Return a byte */
|
||
|
}
|
||
|
|
||
|
/****************************************************************
|
||
|
* *
|
||
|
* GetLine: *
|
||
|
* *
|
||
|
* This function takes as its arguments the address of a *
|
||
|
* buffer in which to return a line of command text and a *
|
||
|
* pointer to a string with which to prompt the user, if *
|
||
|
* necessary. In addition to reading a line, this function *
|
||
|
* will set the global flag fEsc to true if the next *
|
||
|
* character to be read is a semicolon. The function does not *
|
||
|
* return a meaningful value. *
|
||
|
* *
|
||
|
****************************************************************/
|
||
|
|
||
|
void NEAR GetLine(pcmdlin,prompt) /* Get a command line */
|
||
|
BYTE *pcmdlin; /* Pointer to destination string */
|
||
|
char *prompt; /* Pointer to text of prompt string */
|
||
|
{
|
||
|
REGISTER WORD ich; /* Index */
|
||
|
WORD ich1; /* Index */
|
||
|
WORD ich2; /* Index */
|
||
|
BYTE b; /* A byte of input */
|
||
|
WORD fFirstTime; /* Boolean */
|
||
|
|
||
|
fFirstTime = (FTYPE) TRUE; /* Assume it is our first time */
|
||
|
bSepLast = bSep; /* Save last separator */
|
||
|
if(fEscNext) /* If escape character next */
|
||
|
{
|
||
|
pcmdlin[0] = '\0'; /* No command line */
|
||
|
fEsc = (FTYPE) TRUE; /* Set global flag */
|
||
|
return; /* That's all for now */
|
||
|
}
|
||
|
for(;;) /* Forever */
|
||
|
{
|
||
|
fQuotted = FALSE;
|
||
|
ich = 0; /* Initialize index */
|
||
|
while(ich < SBLEN - 1) /* While room in buffer */
|
||
|
{
|
||
|
b = GetStreamByte(prompt); /* Get a byte */
|
||
|
fQuotted = fQuotted ? b != '"' : b == '"';
|
||
|
if (b == '\n' || (!fQuotted && (b == ',' || b == ';')))
|
||
|
{
|
||
|
if (b == ';')
|
||
|
fMoreCommandLine = FALSE;
|
||
|
break; /* Leave loop on end of line */
|
||
|
}
|
||
|
if (!(b == ' ' && ich == 0))/* Store if not a leading space */
|
||
|
{
|
||
|
if (!fQuotted)
|
||
|
{
|
||
|
if (b == '+')
|
||
|
{
|
||
|
if (!MaskedChar)
|
||
|
MaskedChar = b;
|
||
|
b = chMaskSpace;
|
||
|
}
|
||
|
if (b == ' ' && !MaskedChar)
|
||
|
MaskedChar = b;
|
||
|
}
|
||
|
pcmdlin[++ich] = b;
|
||
|
}
|
||
|
}
|
||
|
/*
|
||
|
* If ich==SBLEN-1, last char cannot have been a line terminator
|
||
|
* and buffer is full. If next input char is a line terminator,
|
||
|
* OK, else error.
|
||
|
*/
|
||
|
if(ich == SBLEN - 1 && (b = GetStreamByte(prompt)) != '\n' &&
|
||
|
b != ',' && b != ';')
|
||
|
{
|
||
|
fflush(stdout);
|
||
|
Fatal(ER_linmax);
|
||
|
}
|
||
|
while(ich) /* Loop to trim trailing spaces */
|
||
|
{
|
||
|
if(pcmdlin[ich] != ' ') break;
|
||
|
/* Break on non-space */
|
||
|
--ich; /* Decrement count */
|
||
|
}
|
||
|
ich1 = 0; /* Initialize */
|
||
|
ich2 = 0; /* Initialize */
|
||
|
while(ich2 < ich) /* Loop to remove or replace spaces */
|
||
|
{
|
||
|
++ich2;
|
||
|
if (pcmdlin[ich2] == '"')
|
||
|
{
|
||
|
// Start of quotted file name
|
||
|
|
||
|
while (ich2 < ich && pcmdlin[++ich2] != '"')
|
||
|
pcmdlin[++ich1] = pcmdlin[ich2];
|
||
|
}
|
||
|
else if (pcmdlin[ich2] != ' ' || chMaskSpace != 0 || fQuotted)
|
||
|
{ /* If not space or replacing spaces */
|
||
|
++ich1;
|
||
|
if(!fQuotted && pcmdlin[ich2] == ' ') pcmdlin[ich1] = chMaskSpace;
|
||
|
/* Replace space if replacing */
|
||
|
else pcmdlin[ich1] = pcmdlin[ich2];
|
||
|
/* Else copy the non-space */
|
||
|
}
|
||
|
}
|
||
|
pcmdlin[0] = (BYTE) ich1; /* Set the length */
|
||
|
bSep = b; /* Save the separator */
|
||
|
if (ich ||
|
||
|
!fFirstTime ||
|
||
|
!((bSepLast == ',' && bSep == '\n') ||
|
||
|
(bSepLast == '\n' && bSep == ',')))
|
||
|
break; /* Exit the loop */
|
||
|
fFirstTime = FALSE; /* No the first time */
|
||
|
bSepLast = ','; /* Comma is the field separator */
|
||
|
}
|
||
|
fEscNext = (FTYPE) (b == ';'); /* Set flag */
|
||
|
fEsc = (FTYPE) (!ich && fEscNext); /* Set flag */
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
/****************************************************************
|
||
|
* *
|
||
|
* ParseCmdLine: *
|
||
|
* *
|
||
|
* This function takes no arguments and returns no meaningful *
|
||
|
* value. It parses the command line. *
|
||
|
* *
|
||
|
****************************************************************/
|
||
|
|
||
|
void ParseCmdLine(argc,argv)
|
||
|
/* Parse the command line */
|
||
|
int argc; /* Count of arguments */
|
||
|
char **argv; /* Argument vector */
|
||
|
{
|
||
|
SBTYPE sbFile; /* File name */
|
||
|
SBTYPE sbPrompt; /* Prompt text */
|
||
|
SBTYPE rgb; /* Command line buffer */
|
||
|
FTYPE fMoreInput; /* More input flag */
|
||
|
FTYPE fFirstTime;
|
||
|
AHTEPTR pahte; /* Pointer to hash table entry */
|
||
|
FTYPE fNoList; /* True if no list file */
|
||
|
BYTE *p;
|
||
|
WORD i;
|
||
|
#if OSMSDOS
|
||
|
char buf[512]; /* File buffer */
|
||
|
extern char *stackbuf;
|
||
|
|
||
|
stackbuf = buf;
|
||
|
#endif
|
||
|
#if WIN_3
|
||
|
lpszCmdLine = fpszLinkCmdLine;
|
||
|
#endif
|
||
|
|
||
|
SetUpCommandLine(argc,argv); /* Initialize command line */
|
||
|
chMaskSpace = 0x1f;
|
||
|
bsLst = stdout; /* Assume listing to console */
|
||
|
fLstFileOpen = (FTYPE) TRUE; /* So Fatal will flush it */
|
||
|
fFirstTime = fMoreCommandLine;
|
||
|
do /* Do while more input */
|
||
|
{
|
||
|
fMoreInput = FALSE; /* Assume no more input */
|
||
|
if (fFirstTime)
|
||
|
GetLine(rgb, NULL);
|
||
|
else
|
||
|
GetLine(rgb, strcat(strcpy(sbPrompt,GetMsg(P_objprompt)), " [.obj]: "));
|
||
|
/* Get a line of command text */
|
||
|
if(!rgb[0]) break; /* Break if length 0 */
|
||
|
if(rgb[B2W(rgb[0])] == chMaskSpace) /* If last char is chMaskSpace */
|
||
|
{
|
||
|
fMoreInput = (FTYPE) TRUE; /* More to come */
|
||
|
--rgb[0]; /* Decrement the length */
|
||
|
}
|
||
|
BreakLine(rgb,ProcObject,chMaskSpace); /* Apply ProcObject() to line */
|
||
|
#if CMDMSDOS
|
||
|
if (fFirstTime && !fNoBanner)
|
||
|
DisplayBanner(); /* Display signon banner */
|
||
|
#endif
|
||
|
if (fFirstTime && !fNoEchoLrf)
|
||
|
{
|
||
|
if (fMoreInput || (fMoreIndirectFile && fFirstTime))
|
||
|
{
|
||
|
(*pfCputs)(strcat(strcpy(sbPrompt,GetMsg(P_objprompt)), " [.obj]: "));
|
||
|
/* Prompt the user */
|
||
|
rgb[B2W(rgb[0]) + 2] = '\0';
|
||
|
if (fMoreIndirectFile)
|
||
|
{
|
||
|
if (!fMoreInput && !fNewLine && !fEscNext)
|
||
|
rgb[B2W(rgb[0]) + 1] = ',';
|
||
|
else if (!fMoreInput && fNewLine)
|
||
|
rgb[B2W(rgb[0]) + 1] = ' ';
|
||
|
else if (rgb[B2W(rgb[0]) + 1] == chMaskSpace)
|
||
|
rgb[B2W(rgb[0]) + 1] = '+';
|
||
|
}
|
||
|
else if (rgb[B2W(rgb[0]) + 1] == chMaskSpace)
|
||
|
rgb[B2W(rgb[0]) + 1] = '+';
|
||
|
|
||
|
for (i = 1; i <= rgb[0]; i++)
|
||
|
if (rgb[i] == chMaskSpace)
|
||
|
rgb[i] = MaskedChar;
|
||
|
(*pfCputs)(&rgb[1]); /* And display his response */
|
||
|
if (fMoreInput || fNewLine || fEscNext)
|
||
|
if (!fNoEchoLrf)
|
||
|
NEWLINE(stdout);
|
||
|
}
|
||
|
}
|
||
|
fFirstTime = FALSE;
|
||
|
}
|
||
|
while(fMoreInput); /* End of loop */
|
||
|
#if OVERLAYS
|
||
|
if(fInOverlay) Fatal(ER_unmlpar);
|
||
|
/* Check for parenthesis error
|
||
|
* See ProcObject() to find out
|
||
|
* what is going on here.
|
||
|
*/
|
||
|
#endif
|
||
|
chMaskSpace = 0; /* Remove spaces */
|
||
|
if(rhteFirstObject == RHTENIL) Fatal(ER_noobj);
|
||
|
/* There must be some objects */
|
||
|
#if OEXE
|
||
|
pahte = (AHTEPTR ) FETCHSYM(rhteFirstObject,FALSE);
|
||
|
/* Fetch name of first object */
|
||
|
memcpy(sbFile,GetFarSb(pahte->cch),B2W(pahte->cch[0]) + 1);
|
||
|
/* Copy name of first object */
|
||
|
#if ODOS3EXE
|
||
|
if(fQlib)
|
||
|
UpdateFileParts(sbFile,sbDotQlb);
|
||
|
else if (fBinary)
|
||
|
UpdateFileParts(sbFile,sbDotCom);/* Force extension to .COM */
|
||
|
else
|
||
|
#endif
|
||
|
UpdateFileParts(sbFile,sbDotExe);/* Force extension to .EXE */
|
||
|
#endif
|
||
|
#if OIAPX286
|
||
|
memcpy(sbFile,"\005a.out",6); /* a.out is default for Xenix */
|
||
|
#endif
|
||
|
#if OSMSDOS
|
||
|
if(sbFile[2] == ':') sbFile[1] = (BYTE) (DskCur + 'a');
|
||
|
/* Get drive letter of default drive */
|
||
|
StripPath(sbFile); /* Strip off path specification */
|
||
|
#endif
|
||
|
bufg[0] = 0;
|
||
|
if(!fEsc) /* If command not escaped */
|
||
|
{
|
||
|
strcat(strcpy(sbPrompt, GetMsg(P_runfile)), " [");
|
||
|
sbFile[1 + sbFile[0]] = '\0';
|
||
|
/* Build run file prompt */
|
||
|
/* Prompt for run file */
|
||
|
GetLine(bufg, strcat(strcat(sbPrompt, &sbFile[1]), "]: "));
|
||
|
PeelFlags(bufg); /* Peel flags */
|
||
|
if (B2W(bufg[0]))
|
||
|
memcpy(sbFile,bufg,B2W(bufg[0]) + 1);
|
||
|
/* Store user responce */
|
||
|
else
|
||
|
{
|
||
|
// Store only base name without extension
|
||
|
|
||
|
sbFile[0] -= 4;
|
||
|
}
|
||
|
}
|
||
|
EnterName(sbFile,ATTRNIL,TRUE); /* Create hash tab entry for name */
|
||
|
rhteRunfile = vrhte; /* Save hash table address */
|
||
|
#if OSMSDOS
|
||
|
if (sbFile[0] >= 2 && sbFile[2] == ':')
|
||
|
chRunFile = sbFile[1]; /* If disk specified, use it */
|
||
|
else
|
||
|
chRunFile = (BYTE) (DskCur + 'a');/* Else use current disk */
|
||
|
#endif
|
||
|
fNoList = (FTYPE) (!vfMap && !vfLineNos); /* Set default flag value */
|
||
|
#if OSMSDOS
|
||
|
memcpy(sbFile,"\002a:",3); /* Start with a drive spec */
|
||
|
#else
|
||
|
sbFile[0] = '\0'; /* Null name */
|
||
|
#endif
|
||
|
pahte = (AHTEPTR ) FETCHSYM(vrhte,FALSE);
|
||
|
/* Fetch run file name */
|
||
|
UpdateFileParts(sbFile,GetFarSb(pahte->cch)); /* Use .EXE file name... */
|
||
|
UpdateFileParts(sbFile,sbDotMap); /* ...with .MAP extension */
|
||
|
#if OSMSDOS
|
||
|
sbFile[1] = (BYTE) (DskCur + 'a'); /* Default to default drive */
|
||
|
StripPath(sbFile); /* Strip off path specification */
|
||
|
#endif
|
||
|
fNoList = (FTYPE) (!vfMap && !vfLineNos); /* No list if not /M and not /LI */
|
||
|
if(!fEsc) /* If argument not defaulted */
|
||
|
{
|
||
|
if(bSep == ',') fNoList = FALSE;/* There will be a list file */
|
||
|
if(fNoList) /* If no list file yet */
|
||
|
{
|
||
|
#if OSMSDOS
|
||
|
memcpy(sbFile,"\007nul.map",8);
|
||
|
/* Default null list name */
|
||
|
#endif
|
||
|
#if OSXENIX
|
||
|
memcpy(sbFile,"\007nul.map",8);
|
||
|
/* Default null list name */
|
||
|
#endif
|
||
|
}
|
||
|
strcat(strcpy(sbPrompt, GetMsg(P_listfile)), " [");
|
||
|
sbFile[1 + sbFile[0]] = '\0';
|
||
|
GetLine(rgb, strcat(strcat(sbPrompt, &sbFile[1]), "]: "));
|
||
|
/* Get map file name */
|
||
|
PeelFlags(rgb); /* Process any flags */
|
||
|
if(rgb[0]) /* If name given */
|
||
|
{
|
||
|
fNoList = FALSE; /* There will (maybe) be a list file */
|
||
|
UpdateFileParts(sbFile,rgb);
|
||
|
}
|
||
|
}
|
||
|
chMaskSpace = 0x1f; /* Change spaces to chMaskSpaces */
|
||
|
if(!fEsc) /* If argument not defaulted */
|
||
|
{
|
||
|
strcat(strcpy(sbPrompt,GetMsg(P_libprompt)), " [.lib]: ");
|
||
|
do /* Loop to get library names */
|
||
|
{
|
||
|
fMoreInput = FALSE; /* Assume no more input */
|
||
|
GetLine(rgb, sbPrompt);
|
||
|
if(fEsc || !rgb[0]) break; /* Exit loop if no more input */
|
||
|
if(rgb[B2W(rgb[0])] == chMaskSpace) /* If more to come */
|
||
|
{
|
||
|
fMoreInput = (FTYPE) TRUE; /* Set flag to true */
|
||
|
--rgb[0]; /* Decrement length */
|
||
|
}
|
||
|
BreakLine(rgb,AddLibrary,chMaskSpace);
|
||
|
/* Apply AddLibrary() to lib name(s) */
|
||
|
}
|
||
|
while(fMoreInput); /* End of loop */
|
||
|
}
|
||
|
#if OSEGEXE AND NOT QCLINK
|
||
|
chMaskSpace = 0; /* Remove spaces */
|
||
|
rhteDeffile = RHTENIL; /* Assume no definitions file */
|
||
|
if(!fEsc) /* If argument not defaulted */
|
||
|
{
|
||
|
#if OSMSDOS
|
||
|
GetLine(rgb, strcat(strcpy(sbPrompt,GetMsg(P_defprompt)), " [nul.def]: "));
|
||
|
/* Get def file name */
|
||
|
memcpy(sbPrompt,"\007nul.def",8); /* Default null definition file name */
|
||
|
#endif
|
||
|
#if OSXENIX
|
||
|
GetLine(rgb, strcat(strcpy(sbPrompt,GetMsg(P_defprompt)), " [nul.def]: "));
|
||
|
/* Get def file name */
|
||
|
memcpy(sbPrompt,"\007nul.def",8); /* Default null definition file name */
|
||
|
#endif
|
||
|
PeelFlags(rgb); /* Process any flags */
|
||
|
if(rgb[0]) /* If a name was given */
|
||
|
{
|
||
|
UpdateFileParts(sbPrompt,rgb);
|
||
|
/* Get file name */
|
||
|
memcpy(rgb,sbPrompt,B2W(sbPrompt[0]) + 1);
|
||
|
/* Copy name */
|
||
|
UpdateFileParts(rgb,"\003NUL");
|
||
|
/* Replace name with null name */
|
||
|
if(!SbCompare(sbPrompt,rgb,TRUE))
|
||
|
{ /* If name not null */
|
||
|
EnterName(sbPrompt,ATTRNIL,TRUE);
|
||
|
/* Create hash tab entry for name */
|
||
|
rhteDeffile = vrhte; /* Save hash table address */
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
#endif /* OSEGEXE */
|
||
|
FinishCommandLine(); /* Close indirect file (if any) */
|
||
|
fLstFileOpen = FALSE;
|
||
|
#if OSMSDOS
|
||
|
rhteLstfile = RHTENIL; /* Assume no list file */
|
||
|
#endif
|
||
|
if(!fNoList) /* If a listing wanted */
|
||
|
{
|
||
|
memcpy(sbPrompt,sbFile,B2W(sbFile[0]) + 1);
|
||
|
/* Copy full file name */
|
||
|
UpdateFileParts(sbPrompt,"\003NUL");
|
||
|
/* Change name only */
|
||
|
if(!SbCompare(sbFile,sbPrompt,TRUE))
|
||
|
{ /* If name given not null device */
|
||
|
UpdateFileParts(sbPrompt,"\003CON");
|
||
|
/* Change name only */
|
||
|
if(!SbCompare(sbFile,sbPrompt,TRUE))
|
||
|
{ /* If list file not console */
|
||
|
sbFile[B2W(++sbFile[0])] = '\0';
|
||
|
/* Null-terminate name */
|
||
|
if((bsLst = fopen(&sbFile[1],WRBIN)) == NULL)
|
||
|
Fatal(ER_lstopn); /* Open listing file */
|
||
|
#if OSMSDOS
|
||
|
#ifdef M_I386
|
||
|
if((p = GetMem(512)) != NULL)
|
||
|
#else
|
||
|
if((p = malloc(512)) != NULL)
|
||
|
#endif
|
||
|
setvbuf(bsLst,p,_IOFBF,512);
|
||
|
EnterName(sbFile,ATTRNIL,TRUE);
|
||
|
/* Create hash tab entry for name */
|
||
|
rhteLstfile = vrhte; /* Save hash table address */
|
||
|
#endif
|
||
|
}
|
||
|
else bsLst = stdout; /* Else list to console */
|
||
|
#if OSMSDOS
|
||
|
if(bsLst == stdout) chListFile = (unsigned char) '\377';
|
||
|
/* List file is console */
|
||
|
else if(_isatty(fileno(bsLst))) chListFile = (unsigned char) '\377';
|
||
|
/* List file is some device */
|
||
|
else if(sbFile[2] == ':') /* Else if drive spec given */
|
||
|
chListFile = (BYTE) (sbFile[1] - 'a');
|
||
|
/* Save floppy drive number */
|
||
|
else chListFile = DskCur; /* Else list file on current floppy */
|
||
|
#endif
|
||
|
fLstFileOpen = (FTYPE) TRUE;/* We have a list file */
|
||
|
}
|
||
|
}
|
||
|
#if FALSE AND OSMSDOS AND OWNSTDIO
|
||
|
/* If wer're using our own stdio, set stdout to unbuffered if it
|
||
|
* goes to console.
|
||
|
* CAN'T do this because we now use standard fprintf. Only
|
||
|
* stdio is custom made.
|
||
|
*/
|
||
|
if(_isatty(fileno(stdout)))
|
||
|
{
|
||
|
fflush(stdout);
|
||
|
stdout->_flag |= _IONBF;
|
||
|
}
|
||
|
#endif
|
||
|
#if QCLINK OR Z2_ON
|
||
|
if (fZ2 && pszRespFile != NULL)
|
||
|
_unlink(pszRespFile);
|
||
|
if (pszRespFile != NULL)
|
||
|
FFREE(pszRespFile);
|
||
|
#endif
|
||
|
}
|