Add support for %q in --massif-out-file.  Todo: use this mechanism for the
core and Cachegrind.



git-svn-id: svn://svn.valgrind.org/valgrind/trunk@7200 a5019735-40e9-0310-863c-91ae7b9d1cf9
diff --git a/coregrind/m_options.c b/coregrind/m_options.c
index febf82c..cd35f66 100644
--- a/coregrind/m_options.c
+++ b/coregrind/m_options.c
@@ -96,6 +96,7 @@
    VG_(clo_log_fd) = 2; /* stderr */
 }
 
+__attribute__((noreturn))
 void VG_(err_bad_option) ( Char* opt )
 {
    revert_to_stderr();
@@ -104,6 +105,7 @@
    VG_(exit)(1);
 }
 
+__attribute__((noreturn))
 void VG_(err_missing_prog) ( void  )
 {
    revert_to_stderr();
@@ -112,6 +114,7 @@
    VG_(exit)(1);
 }
 
+__attribute__((noreturn))
 void VG_(err_config_error) ( Char* msg )
 {
    revert_to_stderr();
diff --git a/coregrind/pub_core_options.h b/coregrind/pub_core_options.h
index 15d33f6..39947db 100644
--- a/coregrind/pub_core_options.h
+++ b/coregrind/pub_core_options.h
@@ -191,10 +191,12 @@
 
 /* Call this if the executable is missing.  This function prints an
    error message, then shuts down the entire system. */
+__attribute__((noreturn))
 extern void VG_(err_missing_prog) ( void );
 
 /* Similarly - complain and stop if there is some kind of config
    error. */
+__attribute__((noreturn))
 extern void VG_(err_config_error) ( Char* msg );
 
 
diff --git a/include/pub_tool_options.h b/include/pub_tool_options.h
index 364206d..369f8a2 100644
--- a/include/pub_tool_options.h
+++ b/include/pub_tool_options.h
@@ -124,6 +124,7 @@
    'False' from VG_(tdict).tool_process_cmd_line_option) to indicate
    that.  This function prints an error message, then shuts down the
    entire system. */
+__attribute__((noreturn))
 extern void VG_(err_bad_option) ( Char* opt );
 
 
diff --git a/massif/ms_main.c b/massif/ms_main.c
index 76b43d9..578bc16 100644
--- a/massif/ms_main.c
+++ b/massif/ms_main.c
@@ -2054,7 +2054,7 @@
 
 // Copies the string, prepending it with the startup working directory, and
 // expanding %p and %q entries.  Returns a new, malloc'd string.
-static Char* expand_file_name(Char* format)
+static Char* VG_(expand_file_name)(Char* option_name, Char* format)
 {
    static Char base_dir[VKI_PATH_MAX];
    Int len, i = 0, j = 0;
@@ -2069,49 +2069,85 @@
    out = VG_(malloc)( len );
    VG_(strcpy)(out, base_dir);
 
-#define GROW_IF_j_IS_GEQ_THAN(x) \
-   if (j >= x) { \
-      len *= 2; \
+#define ENSURE_THIS_MUCH_SPACE(x) \
+   if (j + x >= len) { \
+      len += (10 + x); \
       out = VG_(realloc)(out, len); \
-      OINK(len);\
    }
 
    out[j++] = '/';
    while (format[i]) {
       if (format[i] != '%') {
-         GROW_IF_j_IS_GEQ_THAN(len);
+         ENSURE_THIS_MUCH_SPACE(1);
          out[j++] = format[i++];
          
       } else {
          // We saw a '%'.  What's next...
          i++;
-         if      (0   == format[i]) {
-            // At end of string, stop.
-            break;
-         }
-         else if ('%' == format[i]) {
+         if      ('%' == format[i]) {
             // Replace '%%' with '%'.
-            GROW_IF_j_IS_GEQ_THAN(len);
+            ENSURE_THIS_MUCH_SPACE(1);
             out[j++] = format[i++];
          }
          else if ('p' == format[i]) {
-            // Print the PID.
-            GROW_IF_j_IS_GEQ_THAN(len - 10);
-            j += VG_(sprintf)(&out[j], "%d", VG_(getpid)());
+            // Print the PID.  Assume that it's not longer than 10 chars --
+            // reasonable since 'pid' is an Int (ie. 32 bits).
+            Int pid = VG_(getpid)();
+            ENSURE_THIS_MUCH_SPACE(10);
+            j += VG_(sprintf)(&out[j], "%d", pid);
             i++;
          } 
+         else if ('q' == format[i] && '{' == format[i+1]) {
+            // Get the env var name, print its contents.
+            Char* qualname;
+            Char* qual;
+            i += 2;
+            qualname = &format[i];
+            while (True) {
+               if (0 == format[i]) {
+                  VG_(message)(Vg_UserMsg, "%s: malformed %%q specifier",
+                     option_name);
+                  goto bad;
+               } else if ('}' == format[i]) {
+                  // Temporarily replace the '}' with NUL to extract var name.
+                  format[i] = 0;
+                  qual = VG_(getenv)(qualname);
+                  if (NULL == qual) {
+                     VG_(message)(Vg_UserMsg,
+                        "%s: environment variable %s is not set",
+                        option_name, qualname);
+                     goto bad;
+                  }
+                  format[i] = '}';     // Put the '}' back.
+                  i++;
+                  break;
+               }
+               i++;
+            }
+            ENSURE_THIS_MUCH_SPACE(VG_(strlen)(qual));
+            j += VG_(sprintf)(&out[j], "%s", qual);
+         } 
          else {
-            // Other char, treat both the '%' and its subsequent normally.
-            GROW_IF_j_IS_GEQ_THAN(len - 1);
-            out[j++] = '%';
-            out[j++] = format[i++];
+            // Something else, abort.
+            VG_(message)(Vg_UserMsg,
+               "%s: expected 'p' or 'q' or '%%' after '%%'", option_name);
+            goto bad;
          }
       }
    }
-   GROW_IF_j_IS_GEQ_THAN(len);
+   ENSURE_THIS_MUCH_SPACE(1);
    out[j++] = 0;
 
    return out;
+
+  bad: {
+   Char* opt =    // 2:  1 for the '=', 1 for the NUL.
+      VG_(malloc)( VG_(strlen)(option_name) + VG_(strlen)(format) + 2 );
+   VG_(strcpy)(opt, option_name);
+   VG_(strcat)(opt, "=");
+   VG_(strcat)(opt, format);
+   VG_(err_bad_option)(opt);
+  }
 }
 
 
@@ -2174,7 +2210,8 @@
    sanity_check_snapshots_array();
 
    // Setup output filename.
-   massif_out_file = expand_file_name(clo_massif_out_file);
+   massif_out_file =
+      VG_(expand_file_name)("--massif-out-file", clo_massif_out_file);
 }
 
 static void ms_pre_clo_init(void)