170 lines
5.1 KiB
C

/* This file is part of the Project Athena Zephyr Notification System.
* It is one of the source files comprising zwgc, the Zephyr WindowGram
* client.
*
* Created by: Marc Horowitz <marc@athena.mit.edu>
*
* $Id$
*
* Copyright (c) 1989 by the Massachusetts Institute of Technology.
* For copying and distribution information, see the file
* "mit-copyright.h".
*/
#if (!defined(lint) && !defined(SABER))
static const char rcsid_substitute_c[] = "$Id$";
#endif
#include <zephyr/mit-copyright.h>
#include <sysdep.h>
#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(string (*lookup)(string),
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(string (*lookup)(string),
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);
}
}