preproc: allow non-identifier character in environment variables
authorH. Peter Anvin <hpa@linux.intel.com>
Tue, 20 Jul 2010 21:56:30 +0000 (14:56 -0700)
committerH. Peter Anvin <hpa@linux.intel.com>
Tue, 20 Jul 2010 21:56:30 +0000 (14:56 -0700)
Allow non-identifier characters in the name of environment variables,
by surrounding them with string quotes (subject to ordinary
string-quoting rules.)

Signed-off-by: H. Peter Anvin <hpa@linux.intel.com>
doc/changes.src
doc/nasmdoc.src
preproc.c
test/ifenv.asm [new file with mode: 0644]

index 43a4997..e27fade 100644 (file)
@@ -9,7 +9,8 @@ since 2007.
 
 \S{cl-2.09} Version 2.09
 
-\b \c{%ifenv}, \c{%elifenv}, \c{%ifnenv}, and \c{%elifnenv} directives introduced.
+\b \c{%ifenv}, \c{%elifenv}, \c{%ifnenv}, and \c{%elifnenv} directives
+  introduced.  See \k{ifenv}.
 
 \b Fixed NULL dereference if environment variable is missed.
 
@@ -19,7 +20,8 @@ since 2007.
 
 \b Fix for encoding the LFS, LGS and LSS in 64-bit mode.
 
-\b Fixes for compatibility with OpenWatcom compiler and DOS 8.3 file format limitation.
+\b Fixes for compatibility with OpenWatcom compiler and DOS 8.3 file
+  format limitation.
 
 \b Macros parameters range expansion introduced. See \k{mlmacrange}.
 
@@ -29,14 +31,17 @@ since 2007.
 
 \b Short intersegment jumps are permitted now.
 
-\b An alignment more then 64 bytes are allowed for \c{win32}, \c{win64} output formats.
+\b An alignment more then 64 bytes are allowed for \c{win32},
+  \c{win64} output formats.
 
 \b \c{SECTALIGN} directive introduced. In most cases invisible to user.
 
-\b \c{nojmp} option introduced in \c{smartalign} package. See \k{pkg_smartalign}.
+\b \c{nojmp} option introduced in \c{smartalign} package. See
+  \k{pkg_smartalign}.
 
-\b Short aliases \c{win}, \c{elf} and \c{macho} for output formats are introduced.
-  Each stands for \c{win32}, \c{elf32} and \c{macho32} accordingly.
+\b Short aliases \c{win}, \c{elf} and \c{macho} for output formats are
+  introduced.  Each stands for \c{win32}, \c{elf32} and \c{macho32}
+  accordingly.
 
 \b Faster handling of missing directives implemented.
 
@@ -53,6 +58,10 @@ since 2007.
 \b Make \c{-Ox} the default optimization level.  For the legacy
   behavior, specify \c{-O0} explicitly.  See \k{opt-O}.
 
+\b Environment variables read with \c{%!} or tested with \c{%ifenv}
+  can now contain non-identifier characters if surrounded by quotes.
+  See \k{getenv}.
+
 
 \S{cl-2.08.02} Version 2.08.02
 
index 05dc5b5..eb1fe5e 100644 (file)
@@ -3158,6 +3158,10 @@ the \c{%!<env>} directive exists.
 The usual \i\c{%elifenv}, \i\c\{%ifnenv}, and \i\c{%elifnenv}
 variants are also provided.
 
+Just as for \c{%!<env>} the argument should be written as a string if
+it contains characters that would not be legal in an identifier.  See
+\k{getenv}.
+
 \H{rep} \i{Preprocessor Loops}\I{repeating code}: \i\c{%rep}
 
 NASM's \c{TIMES} prefix, though useful, cannot be used to invoke a
@@ -3714,10 +3718,16 @@ For example, suppose that you have an environment variable \c{FOO}, and
 you want the contents of \c{FOO} to be embedded in your program. You
 could do that as follows:
 
-\c %defstr FOO    %!FOO
+\c %defstr FOO          %!FOO
 
 See \k{defstr} for notes on the \c{%defstr} directive.
 
+If the name of the environment variable contains non-identifier
+characters, you can use string quotes to surround the name of the
+variable, for example:
+
+\c %defstr C_colon      %!'C:'
+
 
 \H{stdmac} \i{Standard Macros}
 
index ba1acd5..7fc3e74 100644 (file)
--- a/preproc.c
+++ b/preproc.c
@@ -468,6 +468,23 @@ static Token *delete_Token(Token * t);
 #define tok_isnt_(x,v)  ((x) && ((x)->type!=TOK_OTHER || strcmp((x)->text,(v))))
 
 /*
+ * nasm_unquote with error if the string contains NUL characters.
+ * If the string contains NUL characters, issue an error and return
+ * the C len, i.e. truncate at the NUL.
+ */
+static size_t nasm_unquote_cstr(char *qstr, enum preproc_token directive)
+{
+    size_t len = nasm_unquote(qstr, NULL);
+    size_t clen = strlen(qstr);
+
+    if (len != clen)
+        error(ERR_NONFATAL, "NUL character in `%s' directive",
+              pp_directives[directive]);
+
+    return clen;
+}
+
+/*
  * Handle TASM specific directives, which do not contain a % in
  * front of them. We do it here because I could not find any other
  * place to do it for the moment, and it is a hack (ideally it would
@@ -912,6 +929,24 @@ static Token *tokenize(char *line)
                     type = TOK_PREPROC_QQ; /* %?? */
                     p++;
                 }
+           } else if (*p == '!') {
+               type = TOK_PREPROC_ID;
+               p++;
+               if (isidchar(*p)) {
+                   do {
+                       p++;
+                   }
+                   while (isidchar(*p));
+               } else if (*p == '\'' || *p == '\"' || *p == '`') {
+                   p = nasm_skip_string(p);
+                   if (*p)
+                       p++;
+                   else
+                       error(ERR_NONFATAL|ERR_PASS1, "unterminated %! string");
+               } else {
+                   /* %! without string or identifier */
+                   type = TOK_OTHER; /* Legacy behavior... */
+               }
             } else if (isidchar(*p) ||
                        ((*p == '!' || *p == '%' || *p == '$') &&
                         isidchar(p[1]))) {
@@ -1180,16 +1215,33 @@ static char *detoken(Token * tlist, bool expand_locals)
 
     list_for_each(t, tlist) {
         if (t->type == TOK_PREPROC_ID && t->text[1] == '!') {
-            char *p = getenv(t->text + 2);
-            char *q = t->text;
-           if (!p) {
-                error(ERR_NONFATAL | ERR_PASS1,
-                     "nonexistent environment variable `%s'", q + 2);
-               p = "";
+           char *v;
+           char *q = t->text;
+
+           v = t->text + 2;
+           if (*v == '\'' || *v == '\"' || *v == '`') {
+               size_t len = nasm_unquote(v, NULL);
+               size_t clen = strlen(v);
+
+               if (len != clen) {
+                   error(ERR_NONFATAL | ERR_PASS1,
+                         "NUL character in %! string");
+                   v = NULL;
+               }
+           }
+
+           if (v) {
+               char *p = getenv(v);
+               if (!p) {
+                   error(ERR_NONFATAL | ERR_PASS1,
+                         "nonexistent environment variable `%s'", v);
+                   p = "";
+               }
+               t->text = nasm_strdup(p);
            }
-           t->text = nasm_strdup(p);
-            nasm_free(q);
+           nasm_free(q);
         }
+
         /* Expand local macros here and not during preprocessing */
         if (expand_locals &&
             t->type == TOK_PREPROC_ID && t->text &&
@@ -1610,7 +1662,7 @@ static bool if_condition(Token * tline, enum preproc_token ct)
     struct tokenval tokval;
     expr *evalresult;
     enum pp_token_type needtype;
-    const char *p;
+    char *p;
 
     origline = tline;
 
@@ -1656,14 +1708,19 @@ static bool if_condition(Token * tline, enum preproc_token ct)
         while (tline) {
             skip_white_(tline);
             if (!tline || (tline->type != TOK_ID &&
+                          tline->type != TOK_STRING &&
                            (tline->type != TOK_PREPROC_ID ||
-                              tline->text[1] != '!'))) {
+                           tline->text[1] != '!'))) {
                 error(ERR_NONFATAL,
                       "`%s' expects environment variable names",
                      pp_directives[ct]);
                 goto fail;
             }
-           p = tline->type == TOK_ID ? tline->text : tline->text + 2;
+           p = tline->text;
+           if (tline->type == TOK_PREPROC_ID)
+               p += 2;         /* Skip leading %! */
+           if (*p == '\'' || *p == '\"' || *p == '`')
+               nasm_unquote_cstr(p, ct);
            if (getenv(p))
                j = true;
             tline = tline->next;
@@ -2055,23 +2112,6 @@ static int parse_size(const char *str) {
     return sizes[bsii(str, size_names, ARRAY_SIZE(size_names))+1];
 }
 
-/*
- * nasm_unquote with error if the string contains NUL characters.
- * If the string contains NUL characters, issue an error and return
- * the C len, i.e. truncate at the NUL.
- */
-static size_t nasm_unquote_cstr(char *qstr, enum preproc_token directive)
-{
-    size_t len = nasm_unquote(qstr, NULL);
-    size_t clen = strlen(qstr);
-
-    if (len != clen)
-        error(ERR_NONFATAL, "NUL character in `%s' directive",
-              pp_directives[directive]);
-
-    return clen;
-}
-
 /**
  * find and process preprocessor directive in passed line
  * Find out if a line contains a preprocessor directive, and deal
diff --git a/test/ifenv.asm b/test/ifenv.asm
new file mode 100644 (file)
index 0000000..15f12f8
--- /dev/null
@@ -0,0 +1,31 @@
+%macro import 1
+       %defstr %%incfile %!PROJECTBASEDIR/%{1}.inc
+       %defstr %%decfile %!'PROJECTBASEDIR'/%{1}.dec
+       db %%incfile, `\n`
+       db %%decfile, `\n`
+%endmacro
+
+%ifenv PROJECTBASEDIR
+import foo
+%else
+%warning No PROJECTBASEDIR defined
+%endif
+
+%ifenv %!PROJECTBASEDIR
+import foo
+%else
+%warning No PROJECTBASEDIR defined
+%endif
+
+%ifenv 'PROJECTBASEDIR'
+import foo
+%else
+%warning No PROJECTBASEDIR defined
+%endif
+
+%ifenv %!'PROJECTBASEDIR'
+import foo
+%else
+%warning No PROJECTBASEDIR defined
+%endif
+