/*
* 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 */
#pragma ident "%Z%%M% %I% %E% SMI"
/*
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++;
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;
}