root/usr/src/cmd/sgs/m4/common/m4macs.c
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */

/*
 * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

/*
 * Copyright (c) 2011 Gary Mills
 */

/*      Copyright (c) 1988 AT&T */
/*        All Rights Reserved   */

#include        <limits.h>
#include        <unistd.h>
#include        <sys/types.h>
#include        "m4.h"

#define arg(n)  (c < (n) ? nullstr: ap[n])
static void mkpid(char *);
static void def(wchar_t **, int, int);
static void dump(wchar_t *, wchar_t *);
static void incl(wchar_t **, int, int);
static int leftmatch(wchar_t *, wchar_t *);

static void
dochcom(wchar_t **ap, int c)
{
        wchar_t *l = arg(1);
        wchar_t *r = arg(2);

        if (wcslen(l) > MAXSYM || wcslen(r) > MAXSYM)
                error2(gettext(
                "comment marker longer than %d chars"), MAXSYM);
        (void) wcscpy(lcom, l);
        (void) wcscpy(rcom, *r ? r : L"\n");
}

static void
docq(wchar_t **ap, int c)
{
        wchar_t *l = arg(1);
        wchar_t *r = arg(2);

        if (wcslen(l) > MAXSYM || wcslen(r) > MAXSYM)
                error2(gettext(
                "quote marker longer than %d chars"), MAXSYM);

        if (c <= 1 && !*l) {
                l = L"`";
                r = L"'";
        } else if (c == 1) {
                r = l;
        }

        (void) wcscpy(lquote, l);
        (void) wcscpy(rquote, r);
}

static void
dodecr(wchar_t **ap, int c)
{
        pbnum(ctol(arg(1))-1);
}

void
dodef(wchar_t **ap, int c)
{
        def(ap, c, NOPUSH);
}

static void
def(wchar_t **ap, int c, int mode)
{
        wchar_t *s;

        if (c < 1)
                return;

        s = ap[1];
        if (is_alpha(*s) || *s == '_') {
                s++;
                while (is_alnum(*s) || *s == '_')
                        s++;
        }
        if (*s || s == ap[1])
                error(gettext("bad macro name"));

        if ((ap[2] != NULL) && (wcscmp(ap[1], ap[2]) == 0))
                error(gettext("macro defined as itself"));

        install(ap[1], arg(2), mode);
}

static void
dodefn(wchar_t **ap, int c)
{
        wchar_t *d;

        while (c > 0)
                if ((d = lookup(ap[c--])->def) != NULL) {
                        putbak(*rquote);
                        while (*d)
                                putbak(*d++);
                        putbak(*lquote);
                }
}

static void
dodiv(wchar_t **ap, int c)
{
        int f;

        f = wstoi(arg(1));
        if (f >= 10 || f < 0) {
                cf = NULL;
                ofx = f;
                return;
        }
        tempfile[7] = 'a'+f;
        if (ofile[f] || (ofile[f] = xfopen(tempfile, "w"))) {
                ofx = f;
                cf = ofile[f];
        }
}

/* ARGSUSED */
static void
dodivnum(wchar_t **ap, int c)
{
        pbnum((long)ofx);
}

/* ARGSUSED */
static void
dodnl(wchar_t **ap, int c)
{
        wchar_t t;

        while ((t = getchr()) != '\n' && t != WEOF)
                ;
}

static void
dodump(wchar_t **ap, int c)
{
        struct nlist *np;
        int     i;

        if (c > 0)
                while (c--) {
                        if ((np = lookup(*++ap))->name != NULL)
                                dump(np->name, np->def);
                }
        else
                for (i = 0; i < hshsize; i++)
                        for (np = hshtab[i]; np != NULL; np = np->next)
                                dump(np->name, np->def);
}

/*ARGSUSED*/
static void
dump(wchar_t *name, wchar_t *defnn)
{
        wchar_t *s = defnn;

#if !defined(__lint)    /* lint doesn't grok "%ws" */
        (void) fprintf(stderr, "%ws:\t", name);
#endif

        while (*s++)
                ;
        --s;

        while (s > defnn) {
                --s;
                if (is_builtin(*s)) {
#if !defined(__lint)    /* lint doesn't grok "%ws" */
                        (void) fprintf(stderr, "<%ws>",
                            barray[builtin_idx(*s)].bname);
                } else {
#endif
                        (void) fputwc(*s, stderr);
                }
        }
        (void) fputc('\n', stderr);
}

/*ARGSUSED*/
static void
doerrp(wchar_t **ap, int c)
{
#if !defined(__lint)    /* lint doesn't grok "%ws" */
        if (c > 0)
                (void) fprintf(stderr, "%ws", ap[1]);
#endif
}

long    evalval;        /* return value from yacc stuff */
wchar_t *pe;    /* used by grammar */

static void
doeval(wchar_t **ap, int c)
{
        int base = wstoi(arg(2));
        int pad = wstoi(arg(3));
        extern  int yyparse(void);

        evalval = 0;
        if (c > 0) {
                pe = ap[1];
                if (yyparse() != 0)
                        error(gettext(
                        "invalid expression"));
        }
        pbnbr(evalval, base > 0 ? base:10, pad > 0 ? pad : 1);
}

/*
 * doexit
 *
 * Process m4exit macro.
 */
static void
doexit(wchar_t **ap, int c)
{
        delexit(wstoi(arg(1)), 1);
}

static void
doif(wchar_t **ap, int c)
{
        if (c < 3)
                return;
        while (c >= 3) {
                if (wcscmp(ap[1], ap[2]) == 0) {
                        pbstr(ap[3]);
                        return;
                }
                c -= 3;
                ap += 3;
        }
        if (c > 0)
                pbstr(ap[1]);
}

static void
doifdef(wchar_t **ap, int c)
{
        if (c < 2)
                return;

        while (c >= 2) {
                if (lookup(ap[1])->name != NULL) {
                        pbstr(ap[2]);
                        return;
                }
                c -= 2;
                ap += 2;
        }

        if (c > 0)
                pbstr(ap[1]);
}

static void
doincl(wchar_t **ap, int c)
{
        incl(ap, c, 1);
}

static void
incl(wchar_t **ap, int c, int noisy)
{
        if (c > 0 && wcslen(ap[1]) > 0) {
                if (ifx >= 9)
                        error(gettext(
                        "input file nesting too deep (9)"));
                if ((ifile[++ifx] = fopen(wstr2str(ap[1], 0), "r")) == NULL) {
                        --ifx;
                        if (noisy)
                                errorf(gettext("cannot open file: %s"),
                                    strerror(errno));
                } else {
                        ipstk[ifx] = ipflr = ip;
                        setfname(wstr2str(ap[1], 0));
                }
        }
}

static void
doincr(wchar_t **ap, int c)
{
        pbnum(ctol(arg(1))+1);
}

static void
doindex(wchar_t **ap, int c)
{
        wchar_t *subj = arg(1);
        wchar_t *obj  = arg(2);
        int     i;

        for (i = 0; *subj; ++i)
                if (leftmatch(subj++, obj)) {
                        pbnum((long)i);
                        return;
                }

        pbnum((long)-1);
}

static int
leftmatch(wchar_t *str, wchar_t *substr)
{
        while (*substr)
                if (*str++ != *substr++)
                        return (0);

        return (1);
}

static void
dolen(wchar_t **ap, int c)
{
        pbnum((long)wcslen(arg(1)));
}

static void
domake(wchar_t **ap, int c)
{
        char *path;

        if (c > 0) {
                path = wstr2str(ap[1], 1);
                mkpid(path);
                pbstr(str2wstr(path, 0));
                free(path);
        }
}

static void
dopopdef(wchar_t **ap, int c)
{
        int     i;

        for (i = 1; i <= c; ++i)
                (void) undef(ap[i]);
}

static void
dopushdef(wchar_t **ap, int c)
{
        def(ap, c, PUSH);
}

static void
doshift(wchar_t **ap, int c)
{
        if (c <= 1)
                return;

        for (;;) {
                pbstr(rquote);
                pbstr(ap[c--]);
                pbstr(lquote);

                if (c <= 1)
                        break;

                pbstr(L",");
        }
}

static void
dosincl(wchar_t **ap, int c)
{
        incl(ap, c, 0);
}

static void
dosubstr(wchar_t **ap, int c)
{
        wchar_t *str;
        int     inlen, outlen;
        int     offset, ix;

        inlen = wcslen(str = arg(1));
        offset = wstoi(arg(2));

        if (offset < 0 || offset >= inlen)
                return;

        outlen = c >= 3 ? wstoi(ap[3]) : inlen;
        ix = min(offset+outlen, inlen);

        while (ix > offset)
                putbak(str[--ix]);
}

static void
dosyscmd(wchar_t **ap, int c)
{
        sysrval = 0;
        if (c > 0) {
                (void) fflush(stdout);
                sysrval = system(wstr2str(ap[1], 0));
        }
}

/* ARGSUSED */
static void
dosysval(wchar_t **ap, int c)
{
        pbnum((long)(sysrval < 0 ? sysrval :
            (sysrval >> 8) & ((1 << 8) - 1)) |
            ((sysrval & ((1 << 8) - 1)) << 8));
}

static void
dotransl(wchar_t **ap, int c)
{
        wchar_t *sink, *fr, *sto;
        wchar_t *source, *to;

        if (c < 1)
                return;

        sink = ap[1];
        fr = arg(2);
        sto = arg(3);

        for (source = ap[1]; *source; source++) {
                wchar_t *i;
                to = sto;
                for (i = fr; *i; ++i) {
                        if (*source == *i)
                                break;
                        if (*to)
                                ++to;
                }
                if (*i) {
                        if (*to)
                                *sink++ = *to;
                } else
                        *sink++ = *source;
        }
        *sink = EOS;
        pbstr(ap[1]);
}

static void
dotroff(wchar_t **ap, int c)
{
        struct nlist    *np;

        trace = 0;

        while (c > 0)
                if ((np = lookup(ap[c--]))->name)
                        np->tflag = 0;
}

static void
dotron(wchar_t **ap, int c)
{
        struct nlist    *np;

        trace = !*arg(1);

        while (c > 0)
                if ((np = lookup(ap[c--]))->name)
                        np->tflag = 1;
}

void
doundef(wchar_t **ap, int c)
{
        int     i;

        for (i = 1; i <= c; ++i)
                while (undef(ap[i]))
                        ;
}

int
undef(wchar_t *nam)
{
        struct  nlist *np, *tnp;

        if ((np = lookup(nam))->name == NULL)
                return (0);
        tnp = hshtab[hshval];   /* lookup sets hshval */
        if (tnp == np)  /* it's in first place */
                hshtab[hshval] = tnp->next;
        else {
                while (tnp->next != np)
                        tnp = tnp->next;

                tnp->next = np->next;
        }
        free(np->name);
        free(np->def);
        free(np);
        return (1);
}

static void
doundiv(wchar_t **ap, int c)
{
        int i;

        if (c <= 0)
                for (i = 1; i < 10; i++)
                        undiv(i, OK);
        else
                while (--c >= 0)
                        undiv(wstoi(*++ap), OK);
}

/*
 * dowrap
 *
 * Process m4wrap macro.
 */
static void
dowrap(wchar_t **ap, int c)
{
        wchar_t *a = arg(1);
        struct Wrap *wrapentry;         /* entry for list of "m4wrap" strings */

        wrapentry = xmalloc(sizeof (struct Wrap));
        /* store m4wrap string */
        wrapentry->wrapstr = wstrdup(a);
        /* add this entry to the front of the list of Wrap entries */
        wrapentry->nxt = wrapstart;
        wrapstart = wrapentry;
}

static void
mkpid(char *as)
{
        char *s = as;
        char *l;
        char *first_X;
        unsigned xcnt = 0;
        char my_pid[32];
        int pid_len;
        int i = 0;

        /*
         * Count number of X.
         */
        l = &s[strlen(s)-1];
        while (l != as) {
                if (*l == 'X') {
                        first_X = l;
                        l--;
                        xcnt++;
                } else if (xcnt == 0)
                        l--;
                else {
                        break;
                }
        }

        /*
         *      1) If there is no X in the passed string,
         *              then it just return the passed string.
         *      2) If the length of the continuous right most X's of
         *         the string is shorter than the length of pid,
         *              then right most X's will be substitued with
         *              upper digits of pid.
         *      3) If the length of the continuous right most X's of
         *         the string is equat to the length of pid,
         *              then X's will be replaced with pid.
         *      4) If the lenght of the continuous right most X's of
         *         the string is longer than the length of pid,
         *              then X's will have leading 0 followed by
         *              pid.
         */

        /*
         * If there were no X, don't do anything.
         */
        if (xcnt == 0)
                return;

        /*
         * Get pid
         */
        (void) snprintf(my_pid, sizeof (my_pid), "%d", (int)getpid());
        pid_len = strlen(my_pid);

        if (pid_len > xcnt)
                my_pid[xcnt] = 0;
        else if (pid_len < xcnt) {
                while (xcnt != pid_len) {
                        *first_X++ = '0';
                        xcnt--;
                }
        }

        /*
         * Copy pid
         */
        while (i != xcnt)
                *first_X++ = my_pid[i++];
}

struct bs       barray[] = {
        dochcom,        L"changecom",
        docq,           L"changequote",
        dodecr,         L"decr",
        dodef,          L"define",
        dodefn,         L"defn",
        dodiv,          L"divert",
        dodivnum,       L"divnum",
        dodnl,          L"dnl",
        dodump,         L"dumpdef",
        doerrp,         L"errprint",
        doeval,         L"eval",
        doexit,         L"m4exit",
        doif,           L"ifelse",
        doifdef,        L"ifdef",
        doincl,         L"include",
        doincr,         L"incr",
        doindex,        L"index",
        dolen,          L"len",
        domake,         L"maketemp",
        dopopdef,       L"popdef",
        dopushdef,      L"pushdef",
        doshift,        L"shift",
        dosincl,        L"sinclude",
        dosubstr,       L"substr",
        dosyscmd,       L"syscmd",
        dosysval,       L"sysval",
        dotransl,       L"translit",
        dotroff,        L"traceoff",
        dotron,         L"traceon",
        doundef,        L"undefine",
        doundiv,        L"undivert",
        dowrap,         L"m4wrap",
        0,              0
};