summaryrefslogtreecommitdiffstats
path: root/src/util/mac_parse.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/util/mac_parse.c')
-rw-r--r--src/util/mac_parse.c199
1 files changed, 199 insertions, 0 deletions
diff --git a/src/util/mac_parse.c b/src/util/mac_parse.c
new file mode 100644
index 0000000..45b717a
--- /dev/null
+++ b/src/util/mac_parse.c
@@ -0,0 +1,199 @@
+/*++
+/* NAME
+/* mac_parse 3
+/* SUMMARY
+/* locate macro references in string
+/* SYNOPSIS
+/* #include <mac_parse.h>
+/*
+/* 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 <sys_defs.h>
+#include <ctype.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <mac_parse.h>
+
+ /*
+ * 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 <vstring_vstream.h>
+
+/* 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