root/usr/src/cmd/listen/nstoa.c
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (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 2005 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

/*      Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
/*        All Rights Reserved   */

/*
        stoa - convert string to address

        If a string begins in \o or \O, the following address is octal
        "  "   "       "    " \x or \X, the following address is hex
        Otherwise, a string is considered text. Text may be quoted
        with double quotes and the C-like escapes \n, \b, \t, \v, \r, and \nnn
        (nnn = octal char) are recognized.
        A \ followed by a newline causes the newline
        to vanish. A \ followed by any other char causes any "magic" of
        any other char to disappear.

        Other escape sequences recognized are:
                \!cmd args [ \! || EOL ]
        which is replaced by the raw output of the execution of cmd.
        This may only be used in a string.

                \$cmd args [ \$ || EOL ]
        which is replaced by the output of the execution of cmd and
        is then reprocessed.

        A  NULL is returned on any error(s).
*/

#include <stdio.h>
#include <memory.h>
#include <ctype.h>
#include "nsaddr.h"


#define toupper(c)      (islower(c) ? _toupper(c) : (c))
#define todigit(c)      ((int)((c) - '0'))      /* char to digit */
#define toxdigit(c)     ((isdigit(c))?todigit(c):(toupper(c)-(int)'A'+10))
#define isodigit(c)     (isdigit(c) && ((c) != '9') && ((c) != '8'))
#define itoac(i)        (((i) > 9) ? ((char)((i)-10) + 'A'):((char)(i) + '0'))
#define MASK(n)         ((1 << (n)) - 1)

#define MAXRLEVEL       10      /* maximum recursion level */

#define TRUE    1;
#define FALSE   0;

char    scanbuf[SBUFSIZE];
int     sbp = 0;
int     rec = 0;        /* Recursion level */

char    sbuf[SBUFSIZE];

extern void free();

struct netbuf *
stoa(str, addr)                 /* Return 0 for success, -1 for error */
char    *str;
struct netbuf   *addr;
{
        char    *xfer(), *prescan();

        int     myadr;          /* was netbuf struct allocated here ? */
        int     quote;          /* quoted string ? */

        myadr = FALSE;

        if (!str)
                return NULL;
        while (*str && isspace(*str))   /* leading whites are OK */
                ++str;

        str = prescan(str);             /* Do all \$ ... \$ */

        if (!str || !*str) return NULL;         /* Nothing to convert */

        if (!addr) {
                if ((addr = (struct netbuf *)malloc(sizeof(struct netbuf))) == NULL)
                        return NULL;
                myadr = TRUE;
                addr->buf = NULL;
                addr->maxlen = 0;
                addr->len = 0;
        }

        /* Now process the address */
        quote = 0;

        if (*str == '\\') {
                ++str;
                switch (*str) {

                case 'X':       /* hex */
                case 'x':
                        addr->len = dobase(++str, sbuf, HEX);
                        break;

                case 'o':       /* octal */
                case 'O':
                        addr->len = dobase(++str, sbuf, OCT);
                        break;

                case '\0':      /* No address given!, length is 0 */
                        addr->len = dostring(str, sbuf, 0);
                        break;

                default:        /* \ is handled by dostring */
                        addr->len = dostring(--str, sbuf, quote);
                        break;
                }
        }
        else {
                if (*str == '"') {      /* quoted string */
                        quote = 1;
                        ++str;
                }
                addr->len = dostring(str, sbuf, quote);
        }

        if (addr->len == 0) {   /* Error in conversion */
                if (myadr)
                        free(addr);
                return NULL;
        }
        if ((addr->buf = xfer(addr->buf, sbuf, addr->len, addr->maxlen)) == NULL)
                return NULL;
        else
                return addr;
}


/*
        dostring:       Copy string at s to buf translating
                escaped characters and shell escapes.
        return length of string.
*/

int
dostring(s, buf, quote)         /* read in a raw address */
char    *s, *buf;
int     quote;
{
        char    *xcmd();

        int     oc, ch, len = 0;
        int     l = 0;
        char    *rout;

        while (*s) {
                if (len >= SBUFSIZE) {
                        fprintf(stderr, "dostring: string too long\n");
                        break;
                }
                else if (*s == '\\')
                        switch(*++s) {

                        case '!':       /* raw shell escape */
                                if (rout = xcmd(s+1, '!', &s, &l)) {
                                        if (len + l < SBUFSIZE)
                                                memcpy(buf+len, rout, l);
                                        len += l;
                                        free(rout);
                                }
                                break;

                        case '\n':      /* ignore newline */
                                ++s;
                                break;

                        case 'b':       /* backspace */
                                buf[len++] = '\b'; s++;
                                break;

                        case 'n':       /* newline */
                                buf[len++] = '\n'; s++;
                                break;

                        case 'r':       /* return */
                                buf[len++] = '\r'; s++;
                                break;

                        case 't':       /* horiz. tab */
                                buf[len++] = '\t'; s++;
                                break;

                        case 'v':       /* vert. tab */
                                buf[len++] = '\v'; s++;
                                /* FALLTHROUGH */

                        case '0':
                        case '1':
                        case '2':
                        case '3':
                                for(oc=ch=0; (*s >= '0' && *s <= '7') && oc++ < 3; ++s)
                                        ch = (ch << 3) | (*s - '0');
                                buf[len++] = ch;
                                break;

                        case 0:         /* end of string -- terminate */
                                break;

                        default:        /* take the character blindly */
                                buf[len++] = *s++;
                                break;
                        }
                else if ((quote && (*s == '"')) || (!quote && isspace(*s)))
                        break;

                else
                        buf[len++] = *s++;
        }
        return (len >= SBUFSIZE) ? 0 : len;
}


/*
        dobase :        converts a hex or octal ASCII string
                to a binary address. Only HEX or OCT may be used
                for type.
        return length of binary string (in bytes), 0 if error.
        The binary result is placed at buf.
*/

int
dobase(s, buf, type)    /* read in an address */
char    *s, *buf;       /* source ASCII, result binary string */
int     type;
{
        void    memcp();
        int     bp = SBUFSIZE - 1;
        int     shift = 0;
        char    *end;

        for (end = s; *end && ((type == OCT) ? isodigit(*end) :
                isxdigit(*end)); ++end) ;

        /* any non-white, non-digits cause address to be rejected,
           other fields are ignored */

        if ((*s == 0) || (end == s) || (!isspace(*end) && *end)) {
                fprintf(stderr, "dobase: Illegal trailer on address string\n");
                buf[0] = '\0';
                return 0;
        }
        --end;

        buf[bp] = '\0';

        while (bp > 0 && end >= s) {
                buf[bp] |= toxdigit(*end) << shift;
                if (type == OCT) {
                        if (shift > 5) {
                                buf[--bp] = (todigit(*end) >> (8 - shift))
                                        & MASK(shift-5);
                        }
                        if ((shift = (shift + 3) % 8) == 0)
                                buf[--bp] = 0;
                }
                else    /* hex */
                        if ((shift = (shift) ? 0 : 4) == 0)
                                buf[--bp] = 0;;
                --end;
        }
        if (bp == 0) {
                fprintf(stderr, "stoa: dobase: number to long\n");
                return 0;
        }

        /* need to catch end case to avoid extra 0's in front   */
        if (!shift)
                bp++;
        memcp(buf, &buf[bp], (SBUFSIZE - bp));
        return (SBUFSIZE - bp);
}

#ifdef NOTUSED


/*

        atos(str, addr, type)

        convert address to ASCII form with address in hex, octal,
        or character form.
        return pointer to buffer (NULL on failure).
*/


char *
atos(str, addr, type)
char    *str;
struct netbuf   *addr;
int     type;
{
        char    *xfer();
        int     mystr = 0;      /* was str allocated here ? */
        unsigned        x_atos(), o_atos();
        void    memcp();

        char    *base;

        if (addr == NULL)
                return NULL;

        if (str == NULL)
                if ((str = malloc(SBUFSIZE)) == NULL)
                        return NULL;
                else
                        mystr = 1;

        switch (type) {

        case OCT:
                /* first add \o */
                sbuf[0] = '\\';
                sbuf[1] = 'o';

                return xfer(str, sbuf, o_atos(sbuf+2, addr->buf, addr->len) + 2,
                        mystr ? SBUFSIZE : 0);

        case HEX:
                /* first add \x */
                sbuf[0] = '\\';
                sbuf[1] = 'x';

                return xfer(str, sbuf, x_atos(sbuf+2, addr->buf, addr->len) + 2,
                        mystr ? SBUFSIZE : 0);

        case RAW:
                base = xfer(str, addr->buf,
                         addr->len + 1, mystr ? SBUFSIZE : 0);
                if (base)
                        base[addr->len] = '\0'; /* terminate*/
                return base;

        default:
                return NULL;
        }
}


/*
        x_atos, o_atos
        return the number of bytes occupied by string + NULL*/

/*
        x_atos :        convert an address string a, length s
                to hex ASCII in s */


unsigned
x_atos(s, a, l)
char    *s, *a;
unsigned        l;
{
        char    *b;

        b = s;
        while (l--) {
                *s++ = itoac(((*a >> 4) & MASK (4)));
                *s++ = itoac((*a & MASK(4)));
                ++a;
        }
        *s = '\0';
        return (s - b + 1);
}


/*
        o_atos :        convert an address a, length l
                to octal ASCII in s   */


unsigned
o_atos(s, a, l)
char    *s, *a;
unsigned        l;
{
        int     i, shift;
        char    *b;

        b = s;
        if (l == 0) {
                *s = '\0';
                return 0;
        }

        /* take care of partial bits and set shift factor for next 3  */

        i = l % 3;
        *s++ = itoac((*a>>(i+5)) & MASK(3-i));
        shift = 2 + i;

        while (l)
                if (shift <= 5) {
                        *s++ = itoac((*a >> shift) & MASK(3));
                        if (shift == 0) {
                                ++a;
                                --l;
                        }
                        shift += (shift < 3) ? 5 : -3;
                }
                else {
                        i = (*a & MASK(shift-5)) << (8-shift);
                        i |= (*++a >> shift) & MASK(8-shift);
                        *s++ = itoac(i);
                        shift -= 3;
                        --l;
                }
        *s++ = '\0';
        return (s - b + 1);
}

#endif /* NOTUSED */

void
memcp(d, s, n)  /* safe memcpy for overlapping regions */
char    *d, *s;
int     n;
{
        while (n--)
                *d++ = *s++;
}


/* transfer block to a given destination or allocate one of the
    right size
    if max = 0 : ignore max
*/

char *
xfer(dest, src, len, max)
char    *dest, *src;
unsigned        len, max;
{
        if (max && dest && max < len) {         /* No room */
                fprintf(stderr, "xfer: destination not long enough\n");
                return NULL;
        }
        if (!dest)
                if ((dest = (char *)malloc(len)) == NULL) {
                        fprintf(stderr, "xfer: malloc failed\n");
                        return NULL;
                }

        memcpy(dest, src, (int)len);
        return dest;
}

/*
        prescan:        scan through string s, expanding all \$...\$
                as shell escapes.
        Return pointer to string of expanded text.
*/

char *
prescan(s)
char    *s;
{
        int     scan();

        rec = sbp = 0;
        if (!s || !*s || !scan(s))
                return NULL;
        scanbuf[sbp] = '\0';
        return scanbuf;
}


/*
        scan:   scan through string s, expanding all \$...\$.
        (Part II of prescan)
        Return 0 if anything failed, else 1.
*/

int
scan(s)
char    *s;
{
        char    *xcmd();
        char    *cmd;
        int     len;
        int     esc = 0;                /* Keep lookout for \\$ */

        while (*s) {
                if (!esc && (*s == '\\' && *(s+1) == '$')) {
                        if (rec++ == MAXRLEVEL) {
                                fprintf(stderr, "scan: Recursion \
level past %d on shell escape\n", rec);
                                return 0;
                        }
                        if ((cmd = xcmd(s+2, '$', &s, &len)) != NULL) {
                                cmd[len] = '\0';
                                if (*cmd != '\0')
                                        scan(cmd);
                                free(cmd);
                        }
                        else
                                return 0;
                }

                else if (sbp == SBUFSIZE) {
                        fprintf(stderr, "Overflow on shell esc expansion\n");
                        return 0;
                }
                else if (sbp < SBUFSIZE)
                        esc = ((scanbuf[sbp++] = *s++) == '\\');
        }
        return 1;
}


/*
        xcmd :  extract command line for shell escape and execute it
                return pointer to output of command
*/

char *
xcmd(s, ec, ps, len)
char    *s;             /* input string */
char    ec;             /* escape char ( $ or ! ) */
char    **ps;           /* address of input string pointer */
int     *len;           /* Number of bytes of output from command */
{
        FILE    *popen();
        int     pclose();

        FILE    *pfp;           /* pipe for process */
        char    *cmd;           /* command buffer */
        char    *cmdp;          /* pointer along cmd */
        char    *ocmd;          /* output of command buffer */
        int     esc = 0;        /* escaped escape shell */

        *len = 0;

        if ((cmd = cmdp = (char *)malloc(SBUFSIZE)) == NULL) {
                fprintf(stderr, "xcmd: malloc failed\n");
                return NULL;
        }

        if ((ocmd = (char *)malloc(SBUFSIZE)) == NULL) {
                fprintf(stderr, "xcmd: malloc failed\n");
                free(cmd);
                return NULL;
        }
        while (*s) {
                if (!esc && *s == '\\' && *(s+1) == ec) {
                        s += 2;
                        break;
                }
                else
                        esc = (*cmdp++ = *s++) == '\\';
        }
        *cmdp = '\0';
        *ps = s;

        if ((pfp = popen(cmd, "r")) == NULL)
                fprintf(stderr, "xcmd: popen failed\n");
        while (fread(&ocmd[*len], 1, 1, pfp))
                if ((*len += 1) >= SBUFSIZE) {
                        fprintf(stderr, "xcmd: command output too long\n");
                        break;
                }
        pclose(pfp);
        free(cmd);

        return ocmd;
}