/*++ /* NAME /* mac_parse 3 /* SUMMARY /* locate macro references in string /* SYNOPSIS /* #include /* /* int mac_parse(string, action, context) /* const char *string; /* int (*action)(int type, VSTRING *buf, void *context); /* DESCRIPTION /* This module recognizes macro expressions in null-terminated /* strings. Macro expressions have the form $name, $(text) or /* ${text}. A macro name consists of alphanumerics and/or /* underscore. Text other than macro expressions is treated /* as literal text. /* /* mac_parse() breaks up its string argument into macro references /* and other text, and invokes the \fIaction\fR routine for each item /* found. With each action routine call, the \fItype\fR argument /* indicates what was found, \fIbuf\fR contains a copy of the text /* found, and \fIcontext\fR is passed on unmodified from the caller. /* The application is at liberty to clobber \fIbuf\fR. /* .IP MAC_PARSE_LITERAL /* The content of \fIbuf\fR is literal text. /* .IP MAC_PARSE_EXPR /* The content of \fIbuf\fR is a macro expression: either a /* bare macro name without the preceding "$", or all the text /* inside $() or ${}. /* .PP /* The action routine result value is the bit-wise OR of zero or more /* of the following: /* .IP MAC_PARSE_ERROR /* A parsing error was detected. /* .IP MAC_PARSE_UNDEF /* A macro was expanded but not defined. /* .PP /* Use the constant MAC_PARSE_OK when no error was detected. /* SEE ALSO /* dict(3) dictionary interface. /* DIAGNOSTICS /* Fatal errors: out of memory. malformed macro name. /* /* The result value is the bit-wise OR of zero or more of the /* following: /* .IP MAC_PARSE_ERROR /* A parsing error was detected. /* .IP MAC_PARSE_UNDEF /* A macro was expanded but not defined. /* LICENSE /* .ad /* .fi /* The Secure Mailer license must be distributed with this software. /* AUTHOR(S) /* Wietse Venema /* IBM T.J. Watson Research /* P.O. Box 704 /* Yorktown Heights, NY 10598, USA /*--*/ /* System library. */ #include #include /* Utility library. */ #include #include /* * Helper macro for consistency. Null-terminate the temporary buffer, * execute the action, and reset the temporary buffer for re-use. */ #define MAC_PARSE_ACTION(status, type, buf, context) \ do { \ VSTRING_TERMINATE(buf); \ status |= action((type), (buf), (context)); \ VSTRING_RESET(buf); \ } while(0) /* mac_parse - split string into literal text and macro references */ int mac_parse(const char *value, MAC_PARSE_FN action, void *context) { const char *myname = "mac_parse"; VSTRING *buf = vstring_alloc(1); /* result buffer */ const char *vp; /* value pointer */ const char *pp; /* open_paren pointer */ const char *ep; /* string end pointer */ static char open_paren[] = "({"; static char close_paren[] = ")}"; int level; int status = 0; #define SKIP(start, var, cond) do { \ for (var = start; *var && (cond); var++) \ /* void */; \ } while (0) if (msg_verbose > 1) msg_info("%s: %s", myname, value); for (vp = value; *vp;) { if (*vp != '$') { /* ordinary character */ VSTRING_ADDCH(buf, *vp); vp += 1; } else if (vp[1] == '$') { /* $$ becomes $ */ VSTRING_ADDCH(buf, *vp); vp += 2; } else { /* found bare $ */ if (VSTRING_LEN(buf) > 0) MAC_PARSE_ACTION(status, MAC_PARSE_LITERAL, buf, context); vp += 1; pp = open_paren; if (*vp == *pp || *vp == *++pp) { /* ${x} or $(x) */ level = 1; vp += 1; for (ep = vp; level > 0; ep++) { if (*ep == 0) { msg_warn("truncated macro reference: \"%s\"", value); status |= MAC_PARSE_ERROR; break; } if (*ep == *pp) level++; if (*ep == close_paren[pp - open_paren]) level--; } if (status & MAC_PARSE_ERROR) break; vstring_strncat(buf, vp, level > 0 ? ep - vp : ep - vp - 1); vp = ep; } else { /* plain $x */ SKIP(vp, ep, ISALNUM(*ep) || *ep == '_'); vstring_strncat(buf, vp, ep - vp); vp = ep; } if (VSTRING_LEN(buf) == 0) { status |= MAC_PARSE_ERROR; msg_warn("empty macro name: \"%s\"", value); break; } MAC_PARSE_ACTION(status, MAC_PARSE_EXPR, buf, context); } } if (VSTRING_LEN(buf) > 0 && (status & MAC_PARSE_ERROR) == 0) MAC_PARSE_ACTION(status, MAC_PARSE_LITERAL, buf, context); /* * Cleanup. */ vstring_free(buf); return (status); } #ifdef TEST /* * Proof-of-concept test program. Read strings from stdin, print parsed * result to stdout. */ #include /* mac_parse_print - print parse tree */ static int mac_parse_print(int type, VSTRING *buf, void *unused_context) { char *type_name; switch (type) { case MAC_PARSE_EXPR: type_name = "MAC_PARSE_EXPR"; break; case MAC_PARSE_LITERAL: type_name = "MAC_PARSE_LITERAL"; break; default: msg_panic("unknown token type %d", type); } vstream_printf("%s \"%s\"\n", type_name, vstring_str(buf)); return (0); } int main(int unused_argc, char **unused_argv) { VSTRING *buf = vstring_alloc(1); while (vstring_fgets_nonl(buf, VSTREAM_IN)) { mac_parse(vstring_str(buf), mac_parse_print, (void *) 0); vstream_fflush(VSTREAM_OUT); } vstring_free(buf); return (0); } #endif