400 lines
10 KiB
C
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);
|
||
|
}
|