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

214 lines
6.3 KiB
C

/*****************************************************************************
*
* string.c
*
* String builtin macros.
*
*****************************************************************************/
#include "m4.h"
/*****************************************************************************
*
* opSubstr
*
* Return the substring of $1 starting from $2 and continuing for
* $3 characters. If $3 is not supplied, then return the entire
* remainder of the string.
*
* If $2 is out of range, then nothing is returned.
*
* If $3 is a negative number, then treat it as zero.
*
* The extra ptokNil covers us in the case where $# is 1.
*
*****************************************************************************/
DeclareOp(opSubstr)
{
if (ctokArgv) {
TOK tok;
ITCH itch = (ITCH)atTraditionalPtok(ptokArgv(2));
if (itch < ctchSPtok(ptokArgv(1))) {
CTCH ctch;
if (ctokArgv >= 3) {
ctch = atTraditionalPtok(ptokArgv(3));
if ((int)ctch < 0) {
ctch = 0;
}
} else {
ctch = ctchMax;
}
ctch = min(ctch, ctchSPtok(ptokArgv(1)) - itch);
Assert(itch + ctch <= ctchSPtok(ptokArgv(1)));
SetStaticPtokPtchCtch(&tok, ptchPtok(ptokArgv(1)) + itch, ctch);
PushPtok(&tok);
}
} else {
#ifdef STRICT_M4
Warn("wrong number of arguments to %P", ptokArgv(0));
#endif
}
}
/*****************************************************************************
*
* opIndex
*
* Return the zero-based location of the first occurrence of $2 in $1,
* or -1 if the substring does not appear. If there are multiple
* matches, the leftmost one is returned.
*
* The extra ptokNil covers us in the case where $# is 1.
*
* QUIRK! AT&T returns -1 if $1 and $2 are both null strings.
* GNU returns 0, which is what I do also.
*
*****************************************************************************/
/* SOMEDAY! -- need minimum and maximum arg count */
DeclareOp(opIndex)
{
if (ctokArgv) {
/*
* Note carefully: itch and itchMac need to be ints
* because itchMac can underflow if you try to search
* for a big string inside a small string.
*/
int itch;
int itchMac = ctchSPtok(ptokArgv(1)) - ctchSPtok(ptokArgv(2));
for (itch = 0; itch <= itchMac; itch++) {
if (fEqPtchPtchCtch(ptchPtok(ptokArgv(1)) + itch,
ptchPtok(ptokArgv(2)),
ctchSPtok(ptokArgv(2)))) {
PushAt(itch);
return;
}
}
PushAt(-1);
} else {
PushQuotedPtok(ptokArgv(0));
}
}
/*****************************************************************************
*
* opTranslit
*
* For each character in the $1, look for a match in $2. If found,
* produce the corresponding character from $3. If there is no
* such character, then produce nothing.
*
* Note that the algorithm must be as specified, in order for
*
* translit(abc,ab,ba)
*
* to result in `bac'.
*
* We actually walk $1 backwards so we can push directly instead
* of having to build a temporary token. But the walking of $2
* must be in the forward direction, so that `translit(a,aa,bc)'
* results in `b' and not `c'.
*
* ptokNil saves us in the case where $# = 1.
*
* QUIRK! If given only one argument, AT&T emits $1 unchanged.
* GNU emits nothing! AT&T is obviously correct, so I side
* with them on this one.
*
*****************************************************************************/
DeclareOp(opTranslit)
{
if (ctokArgv) {
ITCH itch = ctchArgv(1);
while ((int)--itch >= 0) {
TCH tch = ptchArgv(1)[itch];
ITCH itch;
for (itch = 0; itch < ctchArgv(2); itch++) {
if (ptchArgv(2)[itch] == tch) {
if (itch < ctchArgv(3)) {
PushTch(ptchArgv(3)[itch]);
}
break;
}
}
if (itch >= ctchArgv(2)) {
PushTch(tch);
}
}
} else {
PushQuotedPtok(ptokArgv(0));
}
}
/*****************************************************************************
*
* opPatsubst
*
* Scan $1 for any occurrences of $2. If found, replace them with $3.
* If $3 is omitted, then the string is deleted.
*
* As a special case, if $2 is the null string, then $3 is inserted
* at the beginning of the string and between each character of $1.
*
* NOTE! This is a GNU extension.
*
* NOTE! GNU supports regular expressions for $2. We support only
* literal strings.
*
* NOTE! Scanning is required to be forwards, so we temporarily expand
* into the Exp hold, then pop it off when we're done.
*
* QUIRK! If given only one argument, GNU emits nothing!
* This is clearly wrong, so I emit $1.
*
*****************************************************************************/
DeclareOp(opPatsubst)
{
if (ctokArgv) {
CTCH ctchSrc = ctchArgv(1);
PTCH ptchSrc = ptchArgv(1);
CTCH ctchPat = ctchArgv(2); /* ptokNil saves us here */
PTCH ptchPat = ptchArgv(2);
TOK tok;
OpenExpPtok(&tok);
while (ctchSrc >= ctchPat) {
if (fEqPtchPtchCtch(ptchPat, ptchSrc, ctchPat)) {
if (ctokArgv >= 3) {
AddExpPtok(ptokArgv(3));
}
if (ctchSrc == 0) {
AddExpTch(*ptchSrc);
ctchSrc--;
ptchSrc++;
} else {
ctchSrc -= ctchPat;
ptchSrc += ctchPat;
}
} else {
AddExpTch(*ptchSrc);
ctchSrc--;
ptchSrc++;
}
}
/* Flush out what's left of the string */
while (ctchSrc) {
AddExpTch(*ptchSrc);
ctchSrc--;
ptchSrc++;
}
CsopExpDopPdivPtok((DIVOP)PushZPtok, 0, &tok);
} else {
PushQuotedPtok(ptokArgv(0));
}
}