summaryrefslogtreecommitdiff
path: root/zwgc/substitute.c
diff options
context:
space:
mode:
authorGravatar Marc Horowitz <marc@mit.edu>1989-11-01 20:02:01 +0000
committerGravatar Marc Horowitz <marc@mit.edu>1989-11-01 20:02:01 +0000
commitd13d8a046838ce3d0e2643bb5b49f2ff77d679ca (patch)
tree05737bc11e3461836ce817939b9129ed58545ac7 /zwgc/substitute.c
parentfd994e4099ad66fb3bf26cd636ca5d5cae72da68 (diff)
Initial revision
Diffstat (limited to 'zwgc/substitute.c')
-rw-r--r--zwgc/substitute.c149
1 files changed, 149 insertions, 0 deletions
diff --git a/zwgc/substitute.c b/zwgc/substitute.c
new file mode 100644
index 0000000..5372fef
--- /dev/null
+++ b/zwgc/substitute.c
@@ -0,0 +1,149 @@
+#include "new_memory.h"
+#include "lexer.h"
+#include "substitute.h"
+
+/*
+ * Internal Routine:
+ *
+ * string eat_dollar_sign_stuff(string (*lookup)(string); string *text_ptr)
+ * Modifies: *text_ptr
+ * Effects: This routine deals with handling the stuff after a '$'
+ * for substitute. If *text_ptr starts with a valid
+ * variable reference (minus the leading '$'), we look up
+ * the variable using lookup and return its value.
+ * *text_ptr is also advanced past the variable reference.
+ * If a '$' starts *text_ptr, *text_ptr is advanced past it &
+ * "$" returned. (This handles "$$" -> "$") Otherwise,
+ * "$" is returned and *text_ptr is not advanced.
+ * The returned string must not be freed.
+ */
+
+static string eat_dollar_sign_stuff(lookup, text_ptr)
+ string (*lookup)();
+ string *text_ptr; /* Input/Output parameter */
+{
+ char c;
+ char closing_brace = 0;
+ char *p = *text_ptr;
+ char *variable_name_start;
+ int variable_name_length;
+
+ /*
+ * Handle "$$" -> "$" translation:
+ */
+ c = *p;
+ if (c=='$') {
+ *text_ptr = p+1;
+ return("$");
+ }
+
+ /*
+ * If opening brace present (i.e., '(' or '{'), skip it and save away
+ * what closing brace we must see at the end of the variable reference:
+ */
+ if (c=='{') {
+ closing_brace = '}';
+ c = *++p;
+ } else if (c=='(') {
+ closing_brace = ')';
+ c = *++p;
+ }
+
+ /*
+ * Eat {identifier_char}* keeping track of what we ate:
+ */
+ variable_name_start = p;
+ variable_name_length = 0;
+ while (c = *p, is_identifier_char(c)) {
+ p++;
+ variable_name_length++;
+ }
+
+ /*
+ * If there was an opening brace, there had better be a comparable
+ * closing brace. If so, skip it. If not, we have an invalid variable
+ * reference so return '$' without advancing *text_ptr.
+ */
+ if (closing_brace) {
+ if (c==closing_brace)
+ c = *++p;
+ else
+ return("$");
+ }
+
+ /*
+ * Zero length variable names are not valid:
+ */
+ if (!variable_name_length)
+ return("$");
+
+ /*
+ * We have a valid variable reference. Advance past it then lookup
+ * its value and return it:
+ */
+ *text_ptr = p;
+ if (variable_name_length > MAX_IDENTIFIER_LENGTH)
+ variable_name_length = MAX_IDENTIFIER_LENGTH;
+ variable_name_start = string_CreateFromData(variable_name_start,
+ variable_name_length);
+ p = lookup(variable_name_start);
+ free(variable_name_start);
+ return(p);
+}
+
+/*
+ * string substitute(string (*lookup)(string); string text)
+ * Effects: returns the result of expanding all variable
+ * references in text using lookup. Example:
+ * "test $foo.$bar baz" would be translated to
+ * "text <foo>.<bar> baz" where "<foo>" is the value of
+ * lookup("foo") and "<bar>" is the value of lookup("bar").
+ * Variables are case sensitive and have the form
+ * {identifier_char}+ where identifier_char is defined
+ * in lexer.h by is_identifier_char. $(foo) and
+ * ${foo} are alternate forms for $foo. In particular,
+ * ${foo}bar is a reference to foo followed by "bar" while
+ * $foobar is a reference to foobar. Incomplete variable
+ * references like $(foo bar are displayed as if they
+ * were not variable references. To allow quoting, "$$"
+ * is translated to "$". Only the first
+ * MAX_IDENTIFIER_LENGTH characters of an identifier are
+ * significant. The strings returned by lookup are not
+ * modified in any way or freed.
+ */
+
+string substitute(lookup, text)
+ string (*lookup)();
+ string text;
+{
+ string result_so_far = string_Copy("");
+ char *p, *temp;
+
+ for (;;) {
+ /*
+ * Move [^$]* from start of text to end of result_so_far:
+ */
+ for (p=text; *p && (*p)!='$'; p++) ;
+ if (text != p) {
+ temp = string_CreateFromData(text, p-text);
+ text = p;
+ result_so_far = string_Concat2(result_so_far, temp);
+ free(temp);
+ }
+
+ /*
+ * If text now empty, exit -- the result is in result_so_far:
+ */
+ if (!*text)
+ return(result_so_far);
+
+ /*
+ * Otherwise, text begins with a '$'. Eat it then call
+ * eat_dollar_sign_stuff to process stuff after it.
+ * Append result to result_so_far, update text, & continue.
+ */
+ text++;
+ p = eat_dollar_sign_stuff(lookup, &text);
+ result_so_far = string_Concat2(result_so_far, p);
+ }
+}