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

400 lines
10 KiB
C

/*****************************************************************************
*
* builtin.c
*
* Builtin macros.
*
*****************************************************************************/
#include "m4.h"
extern TOK tokColonTab;
extern TOK tokEol;
/*****************************************************************************
*
* opIfdef
*
* If $1 is defined, then return $2, else $3.
*
* If $# < 2, then there's no point in returning anything at all.
*
* The extra ptokNil covers us in the case where $# is 2.
*
* QUIRK! GNU m4 emits a warning if $# < 2. AT&T remains silent.
* I side with AT&T on this one.
*
* QUIRK! GNU m4 emits `$0' if $# = 0. AT&T silently ignores
* the entire macro call. I side with GNU on this one.
*
*****************************************************************************/
DeclareOp(opIfdef)
{
if (ctokArgv >= 2) {
if (pmacFindPtok(ptokArgv(1))) {
PushPtok(ptokArgv(2));
} else {
PushPtok(ptokArgv(3));
}
} else if (ctokArgv == 0) {
PushQuotedPtok(ptokArgv(0));
} else {
#ifdef STRICT_M4
Warn("wrong number of arguments to %P", ptokArgv(0));
#endif
}
}
/*****************************************************************************
*
* opIfelse
*
* If $1 and $2 are identical, then return $3.
* If there are only four arguments, then return $4.
* Else, shift three and restart.
*
* If there are fewer than three arguments, then return nothing.
*
* The extra ptokNil saves us in the cases where $# = 2 + 3n.
*
* QUIRK! GNU m4 emits a warning if $# = 2 + 3n. AT&T remains silent.
* I side with AT&T on this one.
*
*****************************************************************************/
DeclareOp(opIfelse)
{
if (ctokArgv >= 3) { /* Need at least three for starters */
ITOK itok = 1;
do {
if (fEqPtokPtok(ptokArgv(itok), ptokArgv(itok+1))) {
PushPtok(ptokArgv(itok+2)); /* ptokNil saves us here */
return;
}
itok += 3;
} while (itok <= ctokArgv - 1); /* While at least two args left */
if (itok == ctokArgv) { /* If only one left... */
PushPtok(ptokArgv(itok));
} else {
Assert(itok == ctokArgv + 1); /* Else must be zero left */
}
return;
}
}
/*****************************************************************************
*
* opShift
*
* Return all but the first argument, quoted and pushed back with
* commas in between. We push them in reverse order so that they
* show up properly.
*
*****************************************************************************/
DeclareOpc(opcShift)
{
if (itok > 1) {
PushQuotedPtok(ptok);
if (itok > 2) {
PushTch(tchComma);
}
}
}
DeclareOp(opShift)
{
EachReverseOpcArgvDw(opcShift, argv, 0);
}
/*****************************************************************************
*
* opLen
*
* Returns the length of its argument.
* The extra ptokNil covers us in the case where $# is zero.
*
* QUIRK! AT&T m4 silently ignores the case where $# is zero, but
* GNU m4 will emit `$0' so as to reduce potential conflict with an
* identically-spelled language keyword. I side with GNU on this one.
*
* SOMEDAY! -- this quirk should be an op attribute.
*
*
*****************************************************************************/
DeclareOp(opLen)
{
if (ctokArgv) {
#ifdef STRICT_M4
if (ctokArgv != 1) {
Warn("wrong number of arguments to %P", ptokArgv(0));
}
#endif
PushAt(ctchArgv(1));
} else {
PushQuotedPtok(ptokArgv(0));
}
}
/*****************************************************************************
*
* opTraceon
*
* With no arguments, turns on global tracing.
* Otherwise, turns on local tracing on the specified macros.
*
* opTraceoff
*
* Turns off global tracing, and also turns off local tracing on the
* specified macros (if any).
*
*****************************************************************************/
DeclareOpc(opcTraceonoff)
{
PMAC pmac = pmacFindPtok(ptok);
if (pmac) {
pmac->pval->fTrace = dw;
}
}
DeclareOp(opTraceon)
{
if (ctokArgv == 0) {
g_fTrace = 1;
} else {
EachOpcArgvDw(opcTraceonoff, argv, 1);
}
}
DeclareOp(opTraceoff)
{
g_fTrace = 0;
EachOpcArgvDw(opcTraceonoff, argv, 0);
}
/*****************************************************************************
*
* opDnl
*
* Gobbles all characters up to and including the next newline.
*
* If EOF is reached, push the EOF back and stop.
*
* QUIRK! AT&T m4 silently ignores the case where $# > 0. GNU m4
* issues a warning. I side with AT&T on this one.
*
*****************************************************************************/
DeclareOp(opDnl)
{
TCH tch;
#ifdef STRICT_M4
if (ctokArgv != 0) {
Warn("wrong number of arguments to %P", ptokArgv(0));
}
#endif
while ((tch = tchGet()) != '\n') {
if (tch == tchMagic) {
TCH tch = tchGet();
if (tch == tchEof) {
PushPtok(&tokEof); /* Eek! Does this actually work? */
break;
}
}
}
}
/*****************************************************************************
*
* opChangequote - not implemented
* opChangecom - not implemented
* opUndivert - not implemented
* opSyscmd - not implemented
* opSysval - not implemented
* opMaketemp - not implemented
* opM4exit - not implemented
* opM4wrap - not implemented
*
*****************************************************************************/
/*****************************************************************************
*
* opDivert
*
* We currently support only two diversions:
*
* 0 = stdout
* 1-9 = unsupported
* <anything else> = /dev/null
*
* This is just barely enough to get DirectX building.
*
*****************************************************************************/
DeclareOp(opDivert)
{
#ifdef STRICT_M4
if (ctokArgv != 1) {
Warn("wrong number of arguments to divert");
}
#endif
if (ctokArgv > 0) {
PTOK ptok = ptokArgv(1);
if (ptok->ctch == 1 && ptok->u.ptch[0] == TEXT('0')) {
g_pdivCur = g_pdivOut;
} else {
g_pdivCur = g_pdivNul;
}
}
}
/*****************************************************************************
*
* opDivnum
*
* We currently support only two diversions:
*
* 0 = stdout
* 1-9 = unsupported
* <anything else> = /dev/null
*
* This is just barely enough to get DirectX building.
*
*****************************************************************************/
DeclareOp(opDivnum)
{
#ifdef STRICT_M4
if (ctokArgv != 0) {
Warn("wrong number of arguments to %P", ptokArgv(0));
}
#endif
PushAt(g_pdivCur == g_pdivOut ? 0 : -1);
}
/*****************************************************************************
*
* opErrprint
*
* Prints its argument on the dianostic output file.
* The extra ptokNil covers us in the case where $# is zero.
*
* QUIRK! AT&T m4 silently ignores excess arguments. GNU m4 emits
* all arguments, separated by spaces. I side with AT&T on this one.
*
*****************************************************************************/
DeclareOp(opErrprint)
{
#ifdef STRICT_M4
if (ctokArgv != 1) {
Warn("wrong number of arguments to errprint");
}
#endif
AddPdivPtok(g_pdivErr, ptokArgv(1));
FlushPdiv(g_pdivErr);
}
/*****************************************************************************
*
* opDumpdef
*
* With no arguments, dumps all definitions.
* Otherwise, dumps only the specified macros.
*
* QUIRK! When given multiple arguments, AT&T m4 dumps the macros in
* the order listed. GNU m4 dumps them in reverse order. (!)
* I side with AT&T on this one.
*
*****************************************************************************/
void STDCALL
DumpdefPmac(PMAC pmac)
{
PTCH ptch, ptchMax;
AddPdivPtok(g_pdivErr, &pmac->tokName);
AddPdivPtok(g_pdivErr, &tokColonTab);
ptch = ptchPtok(&pmac->pval->tok);
ptchMax = ptchMaxPtok(&pmac->pval->tok);
for ( ; ptch < ptchMax; ptch++) {
AddPdivTch(g_pdivErr, *ptch); /* SOMEDAY -- internals! - do they show up okay? */
}
AddPdivPtok(g_pdivErr, &tokEol);
}
DeclareOpc(opcDumpdef)
{
PMAC pmac = pmacFindPtok(ptok);
if (pmac) {
DumpdefPmac(pmac);
}
}
DeclareOp(opDumpdef)
{
if (ctokArgv == 0) {
EachMacroOp(DumpdefPmac);
} else {
EachOpcArgvDw(opcDumpdef, argv, 0);
}
FlushPdiv(g_pdivErr);
}
/*****************************************************************************
*
* opInclude
* opSinclude
*
* Pushes the contents of the file named in the argument.
* Sinclude says nothing if the file is inaccessible.
*
* QUIRK! AT&T m4 silently ignores the case where $1 is null, but
* GNU m4 issues an error (no such file or directory). I side with
* GNU on this one.
*
* QUIRK! AT&T m4 silently ignores the case where $# is zero, but
* GNU m4 will emit `$0' so as to reduce potential conflict with an
* identically-spelled language keyword. I side with GNU on this one.
*
* QUIRK! AT&T m4 silently ignores arguments $2 onward. GNU emits
* a warning but continues. I side with AT&T on this one.
*
*****************************************************************************/
void STDCALL
opIncludeF(ARGV argv, BOOL fFatal)
{
if (ctokArgv) {
PTCH ptch = ptchDupPtok(ptokArgv(1));
if (ptch) {
if (hfInputPtchF(ptch, fFatal) == hfNil) {
FreePv(ptch);
}
#ifdef STRICT_M4
if (ctokArgv != 1) {
Warn("excess arguments to built-in %P ignored", ptokArgv(0));
}
#endif
}
} else {
PushQuotedPtok(ptokArgv(0));
}
}
DeclareOp(opInclude)
{
opIncludeF(argv, 1);
}
DeclareOp(opSinclude)
{
opIncludeF(argv, 0);
}