nasm/quote.c
H. Peter Anvin fbdd36cf72 quote: Change the definition of escp
Semi-arbitrary change of the definition of escp to the beginning of
the argument sequence instead of the initiator character.  This may
avoid an add in some code paths, and looks slightly cleaner to me.
2008-06-02 13:59:09 -07:00

422 lines
7.7 KiB
C

/* quote.c library routines for the Netwide Assembler
*
* The Netwide Assembler is copyright (C) 1996 Simon Tatham and
* Julian Hall. All rights reserved. The software is
* redistributable under the license given in the file "LICENSE"
* distributed in the NASM archive.
*/
#include "compiler.h"
#include <assert.h>
#include <stdlib.h>
#include "nasmlib.h"
#include "quote.h"
#define numvalue(c) ((c)>='a' ? (c)-'a'+10 : (c)>='A' ? (c)-'A'+10 : (c)-'0')
char *nasm_quote(char *str, size_t len)
{
char c, c1, *p, *q, *nstr, *ep;
bool sq_ok, dq_ok;
size_t qlen;
sq_ok = dq_ok = true;
ep = str+len;
qlen = 0; /* Length if we need `...` quotes */
for (p = str; p < ep; p++) {
c = *p;
switch (c) {
case '\'':
sq_ok = false;
qlen++;
break;
case '\"':
dq_ok = false;
qlen++;
break;
case '`':
case '\\':
qlen += 2;
break;
default:
if (c < ' ' || c > '~') {
sq_ok = dq_ok = false;
switch (c) {
case '\a':
case '\b':
case '\t':
case '\n':
case '\v':
case '\f':
case '\r':
case 27:
qlen += 2;
break;
default:
c1 = (p+1 < ep) ? p[1] : 0;
if (c > 077 || (c1 >= '0' && c1 <= '7'))
qlen += 4; /* Must use the full form */
else if (c > 07)
qlen += 3;
else
qlen += 2;
break;
}
} else {
qlen++;
}
break;
}
}
if (sq_ok || dq_ok) {
/* Use '...' or "..." */
nstr = nasm_malloc(len+3);
nstr[0] = nstr[len+1] = sq_ok ? '\'' : '\"';
nstr[len+2] = '\0';
memcpy(nstr+1, str, len);
} else {
/* Need to use `...` quoted syntax */
nstr = nasm_malloc(qlen+3);
q = nstr;
*q++ = '`';
for (p = str; p < ep; p++) {
c = *p;
switch (c) {
case '`':
case '\\':
*q++ = '\\';
*q++ = c;
break;
case 7:
*q++ = '\\';
*q++ = 'a';
break;
case 8:
*q++ = '\\';
*q++ = 'b';
break;
case 9:
*q++ = '\\';
*q++ = 't';
break;
case 10:
*q++ = '\\';
*q++ = 'n';
break;
case 11:
*q++ = '\\';
*q++ = 'v';
break;
case 12:
*q++ = '\\';
*q++ = 'f';
break;
case 13:
*q++ = '\\';
*q++ = 'r';
break;
case 27:
*q++ = '\\';
*q++ = 'e';
break;
default:
if (c < ' ' || c > '~') {
c1 = (p+1 < ep) ? p[1] : 0;
if (c1 >= '0' && c1 <= '7')
q += sprintf(q, "\\%03o", (unsigned char)c);
else
q += sprintf(q, "\\%o", (unsigned char)c);
} else {
*q++ = c;
}
break;
}
}
*q++ = '`';
*q++ = '\0';
assert((size_t)(q-nstr) == qlen+3);
}
return nstr;
}
static char *emit_utf8(char *q, int32_t v)
{
if (v < 0) {
/* Impossible - do nothing */
} else if (v <= 0x7f) {
*q++ = v;
} else if (v <= 0x000007ff) {
*q++ = 0xc0 | (v >> 6);
*q++ = 0x80 | (v & 63);
} else if (v <= 0x0000ffff) {
*q++ = 0xe0 | (v >> 12);
*q++ = 0x80 | ((v >> 6) & 63);
*q++ = 0x80 | (v & 63);
} else if (v <= 0x001fffff) {
*q++ = 0xf0 | (v >> 18);
*q++ = 0x80 | ((v >> 12) & 63);
*q++ = 0x80 | ((v >> 6) & 63);
*q++ = 0x80 | (v & 63);
} else if (v <= 0x03ffffff) {
*q++ = 0xf8 | (v >> 24);
*q++ = 0x80 | ((v >> 18) & 63);
*q++ = 0x80 | ((v >> 12) & 63);
*q++ = 0x80 | ((v >> 6) & 63);
*q++ = 0x80 | (v & 63);
} else {
*q++ = 0xfc | (v >> 30);
*q++ = 0x80 | ((v >> 24) & 63);
*q++ = 0x80 | ((v >> 18) & 63);
*q++ = 0x80 | ((v >> 12) & 63);
*q++ = 0x80 | ((v >> 6) & 63);
*q++ = 0x80 | (v & 63);
}
return q;
}
/*
* Do an *in-place* dequoting of the specified string, returning the
* resulting length (which may be containing embedded nulls.)
*
* In-place replacement is possible since the unquoted length is always
* shorter than or equal to the quoted length.
*/
size_t nasm_unquote(char *str)
{
size_t ln;
char bq, eq;
char *p, *q, *ep;
char *escp = NULL;
char c;
enum unq_state {
st_start,
st_backslash,
st_hex,
st_oct,
st_ucs,
} state;
int ndig = 0;
int32_t nval = 0;
bq = str[0];
if (!bq)
return 0;
ln = strlen(str);
eq = str[ln-1];
if ((bq == '\'' || bq == '\"') && bq == eq) {
/* '...' or "..." string */
memmove(str, str+1, ln-2);
str[ln-2] = '\0';
return ln-2;
}
if (bq == '`' || eq == '`') {
/* `...` string */
q = str;
p = str+1;
ep = str+ln-1;
state = st_start;
while (p < ep) {
c = *p++;
switch (state) {
case st_start:
if (c == '\\')
state = st_backslash;
else
*q++ = c;
break;
case st_backslash:
state = st_start;
escp = p; /* Beginning of argument sequence */
nval = 0;
switch (c) {
case 'a':
*q++ = 7;
break;
case 'b':
*q++ = 8;
break;
case 'e':
*q++ = 27;
break;
case 'f':
*q++ = 12;
break;
case 'n':
*q++ = 10;
break;
case 'r':
*q++ = 13;
break;
case 't':
*q++ = 9;
break;
case 'u':
state = st_ucs;
ndig = 4;
break;
case 'U':
state = st_ucs;
ndig = 8;
break;
case 'v':
*q++ = 11;
break;
case 'x':
case 'X':
state = st_hex;
ndig = 2;
break;
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
state = st_oct;
ndig = 2; /* Up to two more digits */
nval = c - '0';
break;
default:
*q++ = c;
break;
}
break;
case st_oct:
if (c >= '0' && c <= '7') {
nval = (nval << 3) + (c - '0');
if (!--ndig) {
*q++ = nval;
state = st_start;
}
} else {
p--; /* Process this character again */
*q++ = nval;
state = st_start;
}
break;
case st_hex:
if ((c >= '0' && c <= '9') ||
(c >= 'A' && c <= 'F') ||
(c >= 'a' && c <= 'f')) {
nval = (nval << 4) + numvalue(c);
if (--ndig) {
*q++ = nval;
state = st_start;
}
} else {
p--; /* Process this character again */
*q++ = (p > escp) ? nval : escp[-1];
state = st_start;
}
break;
case st_ucs:
if ((c >= '0' && c <= '9') ||
(c >= 'A' && c <= 'F') ||
(c >= 'a' && c <= 'f')) {
nval = (nval << 4) + numvalue(c);
if (!--ndig) {
q = emit_utf8(q, nval);
state = st_start;
}
} else {
p--; /* Process this character again */
if (p > escp)
q = emit_utf8(q, nval);
else
*q++ = escp[-1];
state = st_start;
}
break;
}
}
switch (state) {
case st_start:
case st_backslash:
break;
case st_oct:
*q++ = nval;
break;
case st_hex:
*q++ = (p > escp) ? nval : escp[-1];
break;
case st_ucs:
if (p > escp)
q = emit_utf8(q, nval);
else
*q++ = escp[-1];
break;
}
*q = '\0';
return q-str;
}
/* Otherwise, just return the input... */
return ln;
}
/*
* Find the end of a quoted string; returns the pointer to the terminating
* character (either the ending quote or the null character, if unterminated.)
*/
char *nasm_skip_string(char *str)
{
char bq;
char *p;
char c;
enum unq_state {
st_start,
st_backslash,
} state;
bq = str[0];
if (bq == '\'' || bq == '\"') {
/* '...' or "..." string */
for (p = str+1; *p && *p != bq; p++)
;
return p;
} else if (bq == '`') {
/* `...` string */
p = str+1;
state = st_start;
while ((c = *p++)) {
switch (state) {
case st_start:
switch (c) {
case '\\':
state = st_backslash;
break;
case '`':
return p-1; /* Found the end */
default:
break;
}
break;
case st_backslash:
/*
* Note: for the purpose of finding the end of the string,
* all successor states to st_backslash are functionally
* equivalent to st_start, since either a backslash or
* a backquote will force a return to the st_start state.
*/
state = st_start;
break;
}
}
return p; /* Unterminated string... */
} else {
return str; /* Not a string... */
}
}