Mercurial > ~dholland > hg > tradcpp > index.cgi
view macro.c @ 64:f50b4ea6cbfe
Prune single-line comments from (most) directive lines.
Also, don't pass the string length to the directive processing
functions, as half of them weren't honoring it. Instead, ensure that
the directive line is terminated at the place the directive processing
functions should stop looking at it.
author | David A. Holland |
---|---|
date | Sun, 31 Mar 2013 02:04:56 -0400 |
parents | 2e25e55dba6b |
children | bbbf71859a21 |
line wrap: on
line source
/*- * Copyright (c) 2010 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by David A. Holland. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #include <stdint.h> #include <stdlib.h> #include <string.h> #include "array.h" #include "mode.h" #include "place.h" #include "macro.h" #include "output.h" struct expansionitem { bool isstring; union { char *string; unsigned param; }; }; DECLARRAY(expansionitem, static __unused); DEFARRAY(expansionitem, static); struct macro { struct place defplace; struct place expansionplace; unsigned hash; char *name; bool hasparams; struct stringarray params; struct expansionitemarray expansion; bool inuse; }; DECLARRAY(macro, static __unused); DEFARRAY(macro, static); DECLARRAY(macroarray, static __unused); DEFARRAY(macroarray, static); static struct macroarrayarray macros; static unsigned total_macros; static unsigned hashmask; //////////////////////////////////////////////////////////// // macro structure ops static struct expansionitem * expansionitem_create_string(const char *string) { struct expansionitem *ei; ei = domalloc(sizeof(*ei)); ei->isstring = true; ei->string = dostrdup(string); return ei; } static struct expansionitem * expansionitem_create_stringlen(const char *string, size_t len) { struct expansionitem *ei; ei = domalloc(sizeof(*ei)); ei->isstring = true; ei->string = dostrndup(string, len); return ei; } static struct expansionitem * expansionitem_create_param(unsigned param) { struct expansionitem *ei; ei = domalloc(sizeof(*ei)); ei->isstring = false; ei->param = param; return ei; } static void expansionitem_destroy(struct expansionitem *ei) { if (ei->isstring) { dostrfree(ei->string); } dofree(ei, sizeof(*ei)); } static bool expansionitem_eq(const struct expansionitem *ei1, const struct expansionitem *ei2) { if (ei1->isstring != ei2->isstring) { return false; } if (ei1->isstring) { if (strcmp(ei1->string, ei2->string) != 0) { return false; } } else { if (ei1->param != ei2->param) { return false; } } return true; } static struct macro * macro_create(struct place *p1, const char *name, unsigned hash, struct place *p2) { struct macro *m; m = domalloc(sizeof(*m)); m->defplace = *p1; m->expansionplace = *p2; m->hash = hash; m->name = dostrdup(name); m->hasparams = false; stringarray_init(&m->params); expansionitemarray_init(&m->expansion); m->inuse = false; return m; } DESTROYALL_ARRAY(expansionitem, ); static void macro_destroy(struct macro *m) { expansionitemarray_destroyall(&m->expansion); expansionitemarray_cleanup(&m->expansion); dostrfree(m->name); dofree(m, sizeof(*m)); } static bool macro_eq(const struct macro *m1, const struct macro *m2) { unsigned num1, num2, i; struct expansionitem *ei1, *ei2; const char *p1, *p2; if (strcmp(m1->name, m2->name) != 0) { return false; } if (m1->hasparams != m2->hasparams) { return false; } num1 = expansionitemarray_num(&m1->expansion); num2 = expansionitemarray_num(&m2->expansion); if (num1 != num2) { return false; } for (i=0; i<num1; i++) { ei1 = expansionitemarray_get(&m1->expansion, i); ei2 = expansionitemarray_get(&m2->expansion, i); if (!expansionitem_eq(ei1, ei2)) { return false; } } num1 = stringarray_num(&m1->params); num2 = stringarray_num(&m2->params); if (num1 != num2) { return false; } for (i=0; i<num1; i++) { p1 = stringarray_get(&m1->params, i); p2 = stringarray_get(&m2->params, i); if (strcmp(p1, p2) != 0) { return false; } } return true; } //////////////////////////////////////////////////////////// // macro table /* * Unless I've screwed up, this is something called Fletcher's Checksum * that showed up in Dr. Dobbs in, according to my notes, May 1992. The * implementation is new. */ static unsigned hashfunc(const char *s, size_t len) { uint16_t x1, x2, a; size_t i; x1 = (uint16_t) (len >> 16); x2 = (uint16_t) (len); if (x1==0) { x1++; } if (x2==0) { x2++; } for (i=0; i<len; i+=2) { if (i==len-1) { a = (unsigned char)s[i]; /* don't run off the end of the array */ } else { a = (unsigned char)s[i] + ((uint16_t)(unsigned char)s[i+1] << 8); } x1 += a; if (x1 < a) { x1++; } x2 += x1; if (x2 < x1) { x2++; } } x1 ^= 0xffff; x2 ^= 0xffff; return ((uint32_t)x2)*65535U + x1; } static void macrotable_init(void) { unsigned i; macroarrayarray_init(¯os); macroarrayarray_setsize(¯os, 4); for (i=0; i<4; i++) { macroarrayarray_set(¯os, i, NULL); } total_macros = 0; hashmask = 0x3; } DESTROYALL_ARRAY(macro, ); static void macrotable_cleanup(void) { struct macroarray *bucket; unsigned numbuckets, i; numbuckets = macroarrayarray_num(¯os); for (i=0; i<numbuckets; i++) { bucket = macroarrayarray_get(¯os, i); if (bucket != NULL) { macroarray_destroyall(bucket); macroarray_destroy(bucket); } } macroarrayarray_setsize(¯os, 0); macroarrayarray_cleanup(¯os); } static struct macro * macrotable_findlen(const char *name, size_t len, bool remove) { unsigned hash; struct macroarray *bucket; struct macro *m, *m2; unsigned i, num; size_t mlen; hash = hashfunc(name, len); bucket = macroarrayarray_get(¯os, hash & hashmask); if (bucket == NULL) { return NULL; } num = macroarray_num(bucket); for (i=0; i<num; i++) { m = macroarray_get(bucket, i); if (hash != m->hash) { continue; } mlen = strlen(m->name); if (len == mlen && !memcmp(name, m->name, len)) { if (remove) { if (i < num-1) { m2 = macroarray_get(bucket, num-1); macroarray_set(bucket, i, m2); } macroarray_setsize(bucket, num-1); total_macros--; } return m; } } return NULL; } static struct macro * macrotable_find(const char *name, bool remove) { return macrotable_findlen(name, strlen(name), remove); } static void macrotable_rehash(void) { struct macroarray *newbucket, *oldbucket; struct macro *m; unsigned newmask, tossbit; unsigned numbuckets, i; unsigned oldnum, j, k; numbuckets = macroarrayarray_num(¯os); macroarrayarray_setsize(¯os, numbuckets*2); assert(hashmask == numbuckets - 1); newmask = (hashmask << 1) | 1U; tossbit = newmask && ~hashmask; hashmask = newmask; for (i=0; i<numbuckets; i++) { newbucket = NULL; oldbucket = macroarrayarray_get(¯os, i); oldnum = macroarray_num(oldbucket); for (j=0; j<oldnum; j++) { m = macroarray_get(oldbucket, j); if (m->hash & tossbit) { if (newbucket == NULL) { newbucket = macroarray_create(); } macroarray_set(oldbucket, j, NULL); macroarray_add(newbucket, m, NULL); } } for (j=k=0; j<oldnum; j++) { m = macroarray_get(oldbucket, j); if (m != NULL && k < j) { macroarray_set(oldbucket, k++, m); } } macroarray_setsize(oldbucket, k); macroarrayarray_set(¯os, numbuckets + i, newbucket); } } static void macrotable_add(struct macro *m) { unsigned hash; struct macroarray *bucket; unsigned numbuckets; numbuckets = macroarrayarray_num(¯os); if (total_macros > 0 && total_macros / numbuckets > 9) { macrotable_rehash(); } hash = hashfunc(m->name, strlen(m->name)); bucket = macroarrayarray_get(¯os, hash & hashmask); if (bucket == NULL) { bucket = macroarray_create(); macroarrayarray_set(¯os, hash & hashmask, bucket); } macroarray_add(bucket, m, NULL); total_macros++; } //////////////////////////////////////////////////////////// // external macro definition interface static struct macro * macro_define_common_start(struct place *p1, const char *macro, struct place *p2) { struct macro *m; unsigned hash; if (!is_identifier(macro)) { complain(p1, "Invalid macro name %s", macro); complain_fail(); } hash = hashfunc(macro, strlen(macro)); m = macro_create(p1, macro, hash, p2); return m; } static void macro_define_common_end(struct macro *m) { struct macro *oldm; bool ok; oldm = macrotable_find(m->name, false); if (oldm != NULL) { ok = macro_eq(m, oldm); if (ok) { complain(&m->defplace, "Warning: redefinition of %s", m->name); if (mode.werror) { complain_fail(); } } else { complain(&m->defplace, "Redefinition of %s is not identical", m->name); complain_fail(); } complain(&oldm->defplace, "Previous definition was here"); macro_destroy(m); return; } macrotable_add(m); } static void macro_parse_parameters(struct macro *m, struct place *p, const char *params) { size_t len; const char *s; char *param; while (params != NULL) { len = strspn(params, ws); params += len; p->column += len; s = strchr(params, ','); if (s) { len = s-params; param = dostrndup(params, len); s++; } else { len = strlen(params); param = dostrndup(params, len); } notrailingws(param, strlen(param)); if (!is_identifier(param)) { complain(p, "Invalid macro parameter name %s", param); complain_fail(); } else { stringarray_add(&m->params, param, NULL); } params = s; p->column += len; } } static bool isparam(struct macro *m, const char *name, size_t len, unsigned *num_ret) { unsigned num, i; const char *param; num = stringarray_num(&m->params); for (i=0; i<num; i++) { param = stringarray_get(&m->params, i); if (strlen(param) == len && !memcmp(name, param, len)) { *num_ret = i; return true; } } return false; } static void macro_parse_expansion(struct macro *m, const char *buf) { size_t blockstart, wordstart, pos; struct expansionitem *ei; unsigned param; pos = blockstart = 0; while (buf[pos] != '\0') { pos += strspn(buf+pos, ws); if (strchr(alnum, buf[pos])) { wordstart = pos; pos += strspn(buf+pos, alnum); if (isparam(m, buf+wordstart, pos-wordstart, ¶m)) { if (pos > blockstart) { ei = expansionitem_create_stringlen( buf + blockstart, wordstart - blockstart); expansionitemarray_add(&m->expansion, ei, NULL); } ei = expansionitem_create_param(param); expansionitemarray_add(&m->expansion, ei,NULL); blockstart = pos; continue; } continue; } pos++; } if (pos > blockstart) { ei = expansionitem_create_stringlen(buf + blockstart, pos - blockstart); expansionitemarray_add(&m->expansion, ei, NULL); } } void macro_define_plain(struct place *p1, const char *macro, struct place *p2, const char *expansion) { struct macro *m; struct expansionitem *ei; m = macro_define_common_start(p1, macro, p2); ei = expansionitem_create_string(expansion); expansionitemarray_add(&m->expansion, ei, NULL); macro_define_common_end(m); } void macro_define_params(struct place *p1, const char *macro, struct place *p2, const char *params, struct place *p3, const char *expansion) { struct macro *m; m = macro_define_common_start(p1, macro, p3); m->hasparams = true; macro_parse_parameters(m, p2, params); macro_parse_expansion(m, expansion); macro_define_common_end(m); } void macro_undef(const char *macro) { struct macro *m; m = macrotable_find(macro, true); if (m) { macro_destroy(m); } } bool macro_isdefined(const char *macro) { struct macro *m; m = macrotable_find(macro, false); return m != NULL; } //////////////////////////////////////////////////////////// // macro expansion struct expstate { bool honordefined; enum { ES_NORMAL, ES_WANTLPAREN, ES_NOARG, ES_HAVEARG } state; struct macro *curmacro; struct stringarray args; unsigned argparens; bool tobuf; char *buf; size_t bufpos, bufmax; }; static struct expstate mainstate; static void doexpand(struct expstate *es, struct place *p, char *buf, size_t len); static void expstate_init(struct expstate *es, bool tobuf, bool honordefined) { es->honordefined = honordefined; es->state = ES_NORMAL; es->curmacro = NULL; stringarray_init(&es->args); es->argparens = 0; es->tobuf = tobuf; es->buf = NULL; es->bufpos = 0; es->bufmax = 0; } static void expstate_cleanup(struct expstate *es) { assert(es->state == ES_NORMAL); stringarray_cleanup(&es->args); if (es->buf) { dofree(es->buf, es->bufmax); } } static void expstate_destroyargs(struct expstate *es) { unsigned i, num; num = stringarray_num(&es->args); for (i=0; i<num; i++) { dostrfree(stringarray_get(&es->args, i)); } stringarray_setsize(&es->args, 0); } static void expand_send(struct expstate *es, struct place *p, const char *buf, size_t len) { size_t oldmax; if (es->tobuf) { assert(es->bufpos <= es->bufmax); if (es->bufpos + len > es->bufmax) { oldmax = es->bufmax; if (es->bufmax == 0) { es->bufmax = 64; } while (es->bufpos + len > es->bufmax) { es->bufmax *= 2; } es->buf = dorealloc(es->buf, oldmax, es->bufmax); } memcpy(es->buf + es->bufpos, buf, len); es->bufpos += len; assert(es->bufpos <= es->bufmax); } else { output(p, buf, len); } } static void expand_send_eof(struct expstate *es, struct place *p) { if (es->tobuf) { expand_send(es, p, "", 1); es->bufpos--; } else { output_eof(); } } static void expand_newarg(struct expstate *es, char *buf, size_t len) { char *text; text = dostrndup(buf, len); stringarray_add(&es->args, text, NULL); } static void expand_appendarg(struct expstate *es, char *buf, size_t len) { unsigned num; char *text; size_t oldlen; num = stringarray_num(&es->args); assert(num > 0); text = stringarray_get(&es->args, num - 1); oldlen = strlen(text); text = dorealloc(text, oldlen + 1, oldlen + len + 1); memcpy(text + oldlen, buf, len); text[oldlen+len] = '\0'; stringarray_set(&es->args, num - 1, text); } static char * expand_substitute(struct place *p, struct expstate *es) { struct expansionitem *ei; unsigned i, num; size_t len; char *arg; char *ret; unsigned numargs, numparams; numargs = stringarray_num(&es->args); numparams = stringarray_num(&es->curmacro->params); if (numargs == 0 && numparams == 1) { /* no arguments <=> one empty argument */ stringarray_add(&es->args, dostrdup(""), NULL); numargs++; } if (numargs != numparams) { complain(p, "Wrong number of arguments for macro %s; " "found %u, expected %u", es->curmacro->name, numargs, numparams); complain_fail(); while (numargs < numparams) { stringarray_add(&es->args, dostrdup(""), NULL); numargs++; } } len = 0; num = expansionitemarray_num(&es->curmacro->expansion); for (i=0; i<num; i++) { ei = expansionitemarray_get(&es->curmacro->expansion, i); if (ei->isstring) { len += strlen(ei->string); } else { arg = stringarray_get(&es->args, ei->param); len += strlen(arg); } } ret = domalloc(len+1); *ret = '\0'; for (i=0; i<num; i++) { ei = expansionitemarray_get(&es->curmacro->expansion, i); if (ei->isstring) { strcat(ret, ei->string); } else { arg = stringarray_get(&es->args, ei->param); strcat(ret, arg); } } return ret; } static void expand_domacro(struct expstate *es, struct place *p) { struct macro *m; char *newbuf, *newbuf2; if (es->curmacro == NULL) { /* defined() */ if (stringarray_num(&es->args) != 1) { complain(p, "Too many arguments for defined()"); complain_fail(); expand_send(es, p, "0", 1); return; } m = macrotable_find(stringarray_get(&es->args, 0), false); expand_send(es, p, (m != NULL) ? "1" : "0", 1); expstate_destroyargs(es); return; } assert(es->curmacro->inuse == false); es->curmacro->inuse = true; newbuf = expand_substitute(p, es); newbuf2 = macroexpand(p, newbuf, strlen(newbuf), false); dostrfree(newbuf); expstate_destroyargs(es); doexpand(es, p, newbuf2, strlen(newbuf2)); dostrfree(newbuf2); es->curmacro->inuse = false; } static void expand_got_ws(struct expstate *es, struct place *p, char *buf, size_t len) { switch (es->state) { case ES_NORMAL: expand_send(es, p, buf, len); break; case ES_WANTLPAREN: break; case ES_NOARG: break; case ES_HAVEARG: expand_appendarg(es, buf, len); break; } } static void expand_got_word(struct expstate *es, struct place *p, char *buf, size_t len) { struct macro *m; struct expansionitem *ei; char *newbuf; switch (es->state) { case ES_NORMAL: if (es->honordefined && len == 7 && !memcmp(buf, "defined", 7)) { es->curmacro = NULL; es->state = ES_WANTLPAREN; break; } m = macrotable_findlen(buf, len, false); if (m == NULL || m->inuse) { expand_send(es, p, buf, len); } else if (!m->hasparams) { m->inuse = true; assert(expansionitemarray_num(&m->expansion) == 1); ei = expansionitemarray_get(&m->expansion, 0); assert(ei->isstring); newbuf = macroexpand(p, ei->string, strlen(ei->string), false); doexpand(es, p, newbuf, strlen(newbuf)); dostrfree(newbuf); m->inuse = false; } else { es->curmacro = m; es->state = ES_WANTLPAREN; } break; case ES_WANTLPAREN: if (es->curmacro != NULL) { complain(p, "Expected arguments for macro %s", es->curmacro->name); complain_fail(); } else { /* "defined foo" means "defined(foo)" */ expand_newarg(es, buf, len); es->state = ES_NORMAL; expand_domacro(es, p); break; } break; case ES_NOARG: expand_newarg(es, buf, len); es->state = ES_HAVEARG; break; case ES_HAVEARG: expand_appendarg(es, buf, len); break; } } static void expand_got_lparen(struct expstate *es, struct place *p, char *buf, size_t len) { switch (es->state) { case ES_NORMAL: expand_send(es, p, buf, len); break; case ES_WANTLPAREN: es->state = ES_NOARG; break; case ES_NOARG: expand_newarg(es, buf, len); es->state = ES_HAVEARG; es->argparens++; break; case ES_HAVEARG: expand_appendarg(es, buf, len); es->argparens++; break; } } static void expand_got_rparen(struct expstate *es, struct place *p, char *buf, size_t len) { switch (es->state) { case ES_NORMAL: expand_send(es, p, buf, len); break; case ES_WANTLPAREN: if (es->curmacro) { complain(p, "Expected arguments for macro %s", es->curmacro->name); } else { complain(p, "Expected arguments for defined()"); } complain_fail(); break; case ES_NOARG: assert(es->argparens == 0); es->state = ES_NORMAL; expand_domacro(es, p); break; case ES_HAVEARG: if (es->argparens > 0) { es->argparens--; expand_appendarg(es, buf, len); } else { es->state = ES_NORMAL; expand_domacro(es, p); } break; } } static void expand_got_comma(struct expstate *es, struct place *p, char *buf, size_t len) { switch (es->state) { case ES_NORMAL: expand_send(es, p, buf, len); break; case ES_WANTLPAREN: if (es->curmacro) { complain(p, "Expected arguments for macro %s", es->curmacro->name); } else { complain(p, "Expected arguments for defined()"); } complain_fail(); break; case ES_NOARG: assert(es->argparens == 0); expand_newarg(es, buf, 0); break; case ES_HAVEARG: if (es->argparens > 0) { expand_appendarg(es, buf, len); } else { es->state = ES_NOARG; } break; } } static void expand_got_other(struct expstate *es, struct place *p, char *buf, size_t len) { switch (es->state) { case ES_NORMAL: expand_send(es, p, buf, len); break; case ES_WANTLPAREN: if (es->curmacro) { complain(p, "Expected arguments for macro %s", es->curmacro->name); } else { complain(p, "Expected arguments for defined()"); } complain_fail(); break; case ES_NOARG: expand_newarg(es, buf, len); es->state = ES_HAVEARG; break; case ES_HAVEARG: expand_appendarg(es, buf, len); break; } } static void expand_got_eof(struct expstate *es, struct place *p) { switch (es->state) { case ES_NORMAL: expand_send_eof(es, p); break; case ES_WANTLPAREN: if (es->curmacro) { complain(p, "Expected arguments for macro %s", es->curmacro->name); } else { complain(p, "Expected arguments for defined()"); } complain_fail(); break; case ES_NOARG: case ES_HAVEARG: if (es->curmacro) { complain(p, "Unclosed argument list for macro %s", es->curmacro->name); } else { complain(p, "Unclosed argument list for defined()"); } complain_fail(); expstate_destroyargs(es); break; } es->state = ES_NORMAL; es->curmacro = NULL; es->argparens = 0; } static void doexpand(struct expstate *es, struct place *p, char *buf, size_t len) { size_t x; while (len > 0) { x = strspn(buf, ws); if (x > len) { /* XXX gross, need strnspn */ x = len; } if (x > 0) { expand_got_ws(es, p, buf, x); buf += x; len -= x; continue; } x = strspn(buf, alnum); if (x > len) { /* XXX gross, need strnspn */ x = len; } if (x > 0) { expand_got_word(es, p, buf, x); buf += x; len -= x; continue; } if (buf[0] == '(') { expand_got_lparen(es, p, buf, 1); buf++; len--; continue; } if (buf[0] == ')') { expand_got_rparen(es, p, buf, 1); buf++; len--; continue; } if (buf[0] == ',') { expand_got_comma(es, p, buf, 1); buf++; len--; continue; } expand_got_other(es, p, buf, 1); buf++; len--; } } char * macroexpand(struct place *p, char *buf, size_t len, bool honordefined) { struct expstate es; char *ret; expstate_init(&es, true, honordefined); doexpand(&es, p, buf, len); expand_got_eof(&es, p); /* trim to fit, so the malloc debugging won't complain */ es.buf = dorealloc(es.buf, es.bufmax, strlen(es.buf) + 1); ret = es.buf; es.buf = NULL; es.bufpos = es.bufmax = 0; expstate_cleanup(&es); return ret; } void macro_sendline(struct place *p, char *buf, size_t len) { doexpand(&mainstate, p, buf, len); output(p, "\n", 1); } void macro_sendeof(struct place *p) { expand_got_eof(&mainstate, p); } //////////////////////////////////////////////////////////// // module initialization void macros_init(void) { macrotable_init(); expstate_init(&mainstate, false, false); } void macros_cleanup(void) { expstate_cleanup(&mainstate); macrotable_cleanup(); }