1 /* $NetBSD: mac_parse.c,v 1.1.1.1 2009/06/23 10:09:00 tron Exp $ */ 2 3 /*++ 4 /* NAME 5 /* mac_parse 3 6 /* SUMMARY 7 /* locate macro references in string 8 /* SYNOPSIS 9 /* #include <mac_parse.h> 10 /* 11 /* int mac_parse(string, action, context) 12 /* const char *string; 13 /* int (*action)(int type, VSTRING *buf, char *context); 14 /* DESCRIPTION 15 /* This module recognizes macro expressions in null-terminated 16 /* strings. Macro expressions have the form $name, $(text) or 17 /* ${text}. A macro name consists of alphanumerics and/or 18 /* underscore. Text other than macro expressions is treated 19 /* as literal text. 20 /* 21 /* mac_parse() breaks up its string argument into macro references 22 /* and other text, and invokes the \fIaction\fR routine for each item 23 /* found. With each action routine call, the \fItype\fR argument 24 /* indicates what was found, \fIbuf\fR contains a copy of the text 25 /* found, and \fIcontext\fR is passed on unmodified from the caller. 26 /* The application is at liberty to clobber \fIbuf\fR. 27 /* .IP MAC_PARSE_LITERAL 28 /* The content of \fIbuf\fR is literal text. 29 /* .IP MAC_PARSE_EXPR 30 /* The content of \fIbuf\fR is a macro expression: either a 31 /* bare macro name without the preceding "$", or all the text 32 /* inside $() or ${}. 33 /* .PP 34 /* The action routine result value is the bit-wise OR of zero or more 35 /* of the following: 36 /* .IP MAC_PARSE_ERROR 37 /* A parsing error was detected. 38 /* .IP MAC_PARSE_UNDEF 39 /* A macro was expanded but not defined. 40 /* .PP 41 /* Use the constant MAC_PARSE_OK when no error was detected. 42 /* SEE ALSO 43 /* dict(3) dictionary interface. 44 /* DIAGNOSTICS 45 /* Fatal errors: out of memory. malformed macro name. 46 /* 47 /* The result value is the bit-wise OR of zero or more of the 48 /* following: 49 /* .IP MAC_PARSE_ERROR 50 /* A parsing error was detected. 51 /* .IP MAC_PARSE_UNDEF 52 /* A macro was expanded but not defined. 53 /* LICENSE 54 /* .ad 55 /* .fi 56 /* The Secure Mailer license must be distributed with this software. 57 /* AUTHOR(S) 58 /* Wietse Venema 59 /* IBM T.J. Watson Research 60 /* P.O. Box 704 61 /* Yorktown Heights, NY 10598, USA 62 /*--*/ 63 64 /* System library. */ 65 66 #include <sys_defs.h> 67 #include <ctype.h> 68 69 /* Utility library. */ 70 71 #include <msg.h> 72 #include <mac_parse.h> 73 74 /* 75 * Helper macro for consistency. Null-terminate the temporary buffer, 76 * execute the action, and reset the temporary buffer for re-use. 77 */ 78 #define MAC_PARSE_ACTION(status, type, buf, context) \ 79 do { \ 80 VSTRING_TERMINATE(buf); \ 81 status |= action((type), (buf), (context)); \ 82 VSTRING_RESET(buf); \ 83 } while(0) 84 85 /* mac_parse - split string into literal text and macro references */ 86 87 int mac_parse(const char *value, MAC_PARSE_FN action, char *context) 88 { 89 const char *myname = "mac_parse"; 90 VSTRING *buf = vstring_alloc(1); /* result buffer */ 91 const char *vp; /* value pointer */ 92 const char *pp; /* open_paren pointer */ 93 const char *ep; /* string end pointer */ 94 static char open_paren[] = "({"; 95 static char close_paren[] = ")}"; 96 int level; 97 int status = 0; 98 99 #define SKIP(start, var, cond) \ 100 for (var = start; *var && (cond); var++); 101 102 if (msg_verbose > 1) 103 msg_info("%s: %s", myname, value); 104 105 for (vp = value; *vp;) { 106 if (*vp != '$') { /* ordinary character */ 107 VSTRING_ADDCH(buf, *vp); 108 vp += 1; 109 } else if (vp[1] == '$') { /* $$ becomes $ */ 110 VSTRING_ADDCH(buf, *vp); 111 vp += 2; 112 } else { /* found bare $ */ 113 if (VSTRING_LEN(buf) > 0) 114 MAC_PARSE_ACTION(status, MAC_PARSE_LITERAL, buf, context); 115 vp += 1; 116 pp = open_paren; 117 if (*vp == *pp || *vp == *++pp) { /* ${x} or $(x) */ 118 level = 1; 119 vp += 1; 120 for (ep = vp; level > 0; ep++) { 121 if (*ep == 0) { 122 msg_warn("truncated macro reference: \"%s\"", value); 123 status |= MAC_PARSE_ERROR; 124 break; 125 } 126 if (*ep == *pp) 127 level++; 128 if (*ep == close_paren[pp - open_paren]) 129 level--; 130 } 131 if (status & MAC_PARSE_ERROR) 132 break; 133 vstring_strncat(buf, vp, level > 0 ? ep - vp : ep - vp - 1); 134 vp = ep; 135 } else { /* plain $x */ 136 SKIP(vp, ep, ISALNUM(*ep) || *ep == '_'); 137 vstring_strncat(buf, vp, ep - vp); 138 vp = ep; 139 } 140 if (VSTRING_LEN(buf) == 0) { 141 status |= MAC_PARSE_ERROR; 142 msg_warn("empty macro name: \"%s\"", value); 143 break; 144 } 145 MAC_PARSE_ACTION(status, MAC_PARSE_EXPR, buf, context); 146 } 147 } 148 if (VSTRING_LEN(buf) > 0 && (status & MAC_PARSE_ERROR) == 0) 149 MAC_PARSE_ACTION(status, MAC_PARSE_LITERAL, buf, context); 150 151 /* 152 * Cleanup. 153 */ 154 vstring_free(buf); 155 156 return (status); 157 } 158 159 #ifdef TEST 160 161 /* 162 * Proof-of-concept test program. Read strings from stdin, print parsed 163 * result to stdout. 164 */ 165 #include <vstring_vstream.h> 166 167 /* mac_parse_print - print parse tree */ 168 169 static int mac_parse_print(int type, VSTRING *buf, char *unused_context) 170 { 171 char *type_name; 172 173 switch (type) { 174 case MAC_PARSE_EXPR: 175 type_name = "MAC_PARSE_EXPR"; 176 break; 177 case MAC_PARSE_LITERAL: 178 type_name = "MAC_PARSE_LITERAL"; 179 break; 180 default: 181 msg_panic("unknown token type %d", type); 182 } 183 vstream_printf("%s \"%s\"\n", type_name, vstring_str(buf)); 184 return (0); 185 } 186 187 int main(int unused_argc, char **unused_argv) 188 { 189 VSTRING *buf = vstring_alloc(1); 190 191 while (vstring_fgets_nonl(buf, VSTREAM_IN)) { 192 mac_parse(vstring_str(buf), mac_parse_print, (char *) 0); 193 vstream_fflush(VSTREAM_OUT); 194 } 195 vstring_free(buf); 196 return (0); 197 } 198 199 #endif 200