diff --git a/src/main.c b/src/main.c
index 27f145c5..a5600391 100644
--- a/src/main.c
+++ b/src/main.c
@@ -136,6 +136,7 @@ int verify_flag;
 
 static int silent_flag;
 static const int default_silent_flag = 0;
+static enum variable_origin silent_origin = o_default;
 
 /* Nonzero means either -s was given, or .SILENT-with-no-deps was seen.  */
 
@@ -190,6 +191,7 @@ int no_builtin_variables_flag = 0;
 
 int keep_going_flag;
 static const int default_keep_going_flag = 0;
+static enum variable_origin keep_going_origin = o_default;
 
 /* Nonzero means check symlink mtimes.  */
 
@@ -197,6 +199,7 @@ int check_symlink_flag = 0;
 
 static int print_directory_flag = -1;
 static const int default_print_directory_flag = -1;
+static enum variable_origin print_directory_origin = o_default;
 
 /* Nonzero means print version information.  */
 
@@ -405,30 +408,31 @@ static int trace_flag = 0;
 
 struct command_switch
   {
-    int c;                      /* The switch character.  */
+    int c;                        /* The switch character.  */
 
-    enum                        /* Type of the value.  */
+    enum                          /* Type of the value.  */
       {
-        flag,                   /* Turn int flag on.  */
-        flag_off,               /* Turn int flag off.  */
-        string,                 /* One string per invocation.  */
-        strlist,                /* One string per switch.  */
-        filename,               /* A string containing a file name.  */
-        positive_int,           /* A positive integer.  */
-        floating,               /* A floating-point number (double).  */
-        ignore                  /* Ignored.  */
+        flag,                     /* Turn int flag on.  */
+        flag_off,                 /* Turn int flag off.  */
+        string,                   /* One string per invocation.  */
+        strlist,                  /* One string per switch.  */
+        filename,                 /* A string containing a file name.  */
+        positive_int,             /* A positive integer.  */
+        floating,                 /* A floating-point number (double).  */
+        ignore                    /* Ignored.  */
       } type;
 
-    void *value_ptr;    /* Pointer to the value-holding variable.  */
+    void *value_ptr;              /* Pointer to the value-holding variable.  */
 
-    unsigned int env:1;         /* Can come from MAKEFLAGS.  */
-    unsigned int toenv:1;       /* Should be put in MAKEFLAGS.  */
-    unsigned int no_makefile:1; /* Don't propagate when remaking makefiles.  */
+    unsigned int env:1;           /* Can come from MAKEFLAGS.  */
+    unsigned int toenv:1;         /* Should be put in MAKEFLAGS.  */
+    unsigned int no_makefile:1;  /* Don't propagate when remaking makefiles.  */
 
-    const void *noarg_value;    /* Pointer to value used if no arg given.  */
-    const void *default_value;  /* Pointer to default value.  */
+    const void *noarg_value;      /* Pointer to value used if no arg given.  */
+    const void *default_value;    /* Pointer to default value.  */
 
-    const char *long_name;      /* Long option name.  */
+    const char *long_name;        /* Long option name.  */
+    enum variable_origin *origin; /* Origin of the value.  */
   };
 
 /* The table of command switches.
@@ -439,61 +443,62 @@ struct command_switch
 
 static const struct command_switch switches[] =
   {
-    { 'b', ignore, 0, 0, 0, 0, 0, 0, 0 },
-    { 'B', flag, &always_make_set, 1, 1, 0, 0, 0, "always-make" },
-    { 'd', flag, &debug_flag, 1, 1, 0, 0, 0, 0 },
-    { 'e', flag, &env_overrides, 1, 1, 0, 0, 0, "environment-overrides", },
-    { 'E', strlist, &eval_strings, 1, 0, 0, 0, 0, "eval" },
-    { 'h', flag, &print_usage_flag, 0, 0, 0, 0, 0, "help" },
-    { 'i', flag, &ignore_errors_flag, 1, 1, 0, 0, 0, "ignore-errors" },
+    { 'b', ignore, 0, 0, 0, 0, 0, 0, 0, 0 },
+    { 'B', flag, &always_make_set, 1, 1, 0, 0, 0, "always-make", 0 },
+    { 'd', flag, &debug_flag, 1, 1, 0, 0, 0, 0, 0 },
+    { 'e', flag, &env_overrides, 1, 1, 0, 0, 0, "environment-overrides", 0 },
+    { 'E', strlist, &eval_strings, 1, 0, 0, 0, 0, "eval", 0 },
+    { 'h', flag, &print_usage_flag, 0, 0, 0, 0, 0, "help", 0 },
+    { 'i', flag, &ignore_errors_flag, 1, 1, 0, 0, 0, "ignore-errors", 0 },
     { 'k', flag, &keep_going_flag, 1, 1, 0, 0, &default_keep_going_flag,
-      "keep-going" },
-    { 'L', flag, &check_symlink_flag, 1, 1, 0, 0, 0, "check-symlink-times" },
-    { 'm', ignore, 0, 0, 0, 0, 0, 0, 0 },
-    { 'n', flag, &just_print_flag, 1, 1, 1, 0, 0, "just-print" },
-    { 'p', flag, &print_data_base_flag, 1, 1, 0, 0, 0, "print-data-base" },
-    { 'q', flag, &question_flag, 1, 1, 1, 0, 0, "question" },
-    { 'r', flag, &no_builtin_rules_flag, 1, 1, 0, 0, 0, "no-builtin-rules" },
+      "keep-going", &keep_going_origin },
+    { 'L', flag, &check_symlink_flag, 1, 1, 0, 0, 0, "check-symlink-times", 0 },
+    { 'm', ignore, 0, 0, 0, 0, 0, 0, 0, 0 },
+    { 'n', flag, &just_print_flag, 1, 1, 1, 0, 0, "just-print", 0 },
+    { 'p', flag, &print_data_base_flag, 1, 1, 0, 0, 0, "print-data-base", 0 },
+    { 'q', flag, &question_flag, 1, 1, 1, 0, 0, "question", 0 },
+    { 'r', flag, &no_builtin_rules_flag, 1, 1, 0, 0, 0, "no-builtin-rules", 0 },
     { 'R', flag, &no_builtin_variables_flag, 1, 1, 0, 0, 0,
-      "no-builtin-variables" },
-    { 's', flag, &silent_flag, 1, 1, 0, 0, &default_silent_flag, "silent" },
+      "no-builtin-variables", 0 },
+    { 's', flag, &silent_flag, 1, 1, 0, 0, &default_silent_flag, "silent",
+      &silent_origin },
     { 'S', flag_off, &keep_going_flag, 1, 1, 0, 0, &default_keep_going_flag,
-      "no-keep-going" },
-    { 't', flag, &touch_flag, 1, 1, 1, 0, 0, "touch" },
-    { 'v', flag, &print_version_flag, 1, 0, 0, 0, 0, "version" },
+      "no-keep-going", &keep_going_origin },
+    { 't', flag, &touch_flag, 1, 1, 1, 0, 0, "touch", 0 },
+    { 'v', flag, &print_version_flag, 1, 0, 0, 0, 0, "version", 0 },
     { 'w', flag, &print_directory_flag, 1, 1, 0, 0,
-      &default_print_directory_flag, "print-directory" },
+      &default_print_directory_flag, "print-directory", &print_directory_origin },
 
     /* These options take arguments.  */
-    { 'C', filename, &directories, 0, 0, 0, 0, 0, "directory" },
-    { 'f', filename, &makefiles, 0, 0, 0, 0, 0, "file" },
+    { 'C', filename, &directories, 0, 0, 0, 0, 0, "directory", 0 },
+    { 'f', filename, &makefiles, 0, 0, 0, 0, 0, "file", 0 },
     { 'I', filename, &include_dirs, 1, 1, 0, 0, 0,
-      "include-dir" },
+      "include-dir", 0 },
     { 'j', positive_int, &arg_job_slots, 1, 1, 0, &inf_jobs, &default_job_slots,
-      "jobs" },
+      "jobs", 0 },
     { 'l', floating, &max_load_average, 1, 1, 0, &default_load_average,
-      &default_load_average, "load-average" },
-    { 'o', filename, &old_files, 0, 0, 0, 0, 0, "old-file" },
-    { 'O', string, &output_sync_option, 1, 1, 0, "target", 0, "output-sync" },
-    { 'W', filename, &new_files, 0, 0, 0, 0, 0, "what-if" },
+      &default_load_average, "load-average", 0 },
+    { 'o', filename, &old_files, 0, 0, 0, 0, 0, "old-file", 0 },
+    { 'O', string, &output_sync_option, 1, 1, 0, "target", 0, "output-sync", 0 },
+    { 'W', filename, &new_files, 0, 0, 0, 0, 0, "what-if", 0 },
 
     /* These are long-style options.  */
-    { CHAR_MAX+1, strlist, &db_flags, 1, 1, 0, "basic", 0, "debug" },
-    { CHAR_MAX+2, string, &jobserver_auth, 1, 1, 0, 0, 0, JOBSERVER_AUTH_OPT },
-    { CHAR_MAX+3, flag, &trace_flag, 1, 1, 0, 0, 0, "trace" },
+    { CHAR_MAX+1, strlist, &db_flags, 1, 1, 0, "basic", 0, "debug", 0 },
+    { CHAR_MAX+2, string, &jobserver_auth, 1, 1, 0, 0, 0, JOBSERVER_AUTH_OPT, 0 },
+    { CHAR_MAX+3, flag, &trace_flag, 1, 1, 0, 0, 0, "trace", 0 },
     { CHAR_MAX+4, flag_off, &print_directory_flag, 1, 1, 0, 0,
-      &default_print_directory_flag, "no-print-directory" },
+      &default_print_directory_flag, "no-print-directory", &print_directory_origin },
     { CHAR_MAX+5, flag, &warn_undefined_variables_flag, 1, 1, 0, 0, 0,
-      "warn-undefined-variables" },
-    { CHAR_MAX+7, string, &sync_mutex, 1, 1, 0, 0, 0, "sync-mutex" },
+      "warn-undefined-variables", 0 },
+    { CHAR_MAX+7, string, &sync_mutex, 1, 1, 0, 0, 0, "sync-mutex", 0 },
     { CHAR_MAX+8, flag_off, &silent_flag, 1, 1, 0, 0, &default_silent_flag,
-      "no-silent" },
-    { CHAR_MAX+9, string, &jobserver_auth, 1, 0, 0, 0, 0, "jobserver-fds" },
+      "no-silent", &silent_origin },
+    { CHAR_MAX+9, string, &jobserver_auth, 1, 0, 0, 0, 0, "jobserver-fds", 0 },
     /* There is special-case handling for this in decode_switches() as well.  */
-    { TEMP_STDIN_OPT, filename, &makefiles, 0, 0, 0, 0, 0, "temp-stdin" },
-    { CHAR_MAX+11, string, &shuffle_mode, 1, 1, 0, "random", 0, "shuffle" },
-    { CHAR_MAX+12, string, &jobserver_style, 1, 0, 0, 0, 0, "jobserver-style" },
-    { 0, 0, 0, 0, 0, 0, 0, 0, 0 }
+    { TEMP_STDIN_OPT, filename, &makefiles, 0, 0, 0, 0, 0, "temp-stdin", 0 },
+    { CHAR_MAX+11, string, &shuffle_mode, 1, 1, 0, "random", 0, "shuffle", 0 },
+    { CHAR_MAX+12, string, &jobserver_style, 1, 0, 0, 0, 0, "jobserver-style", 0 },
+    { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }
   };
 
 /* Secondary long names for options.  */
@@ -3148,7 +3153,9 @@ decode_switches (int argc, const char **argv, enum variable_origin origin)
                  this switch.  We test this individually inside the
                  switch below rather than just once outside it, so that
                  options which are to be ignored still consume args.  */
-              int doit = origin == o_command || cs->env;
+              int doit = (origin == o_command
+                          || (cs->env &&
+                              (cs->origin == NULL || origin >= *cs->origin)));
 
               switch (cs->type)
                 {
@@ -3161,7 +3168,11 @@ decode_switches (int argc, const char **argv, enum variable_origin origin)
                 case flag:
                 case flag_off:
                   if (doit)
-                    *(int *) cs->value_ptr = cs->type == flag;
+                    {
+                      *(int *) cs->value_ptr = cs->type == flag;
+                      if (cs->origin)
+                        *cs->origin = origin;
+                    }
                   break;
 
                 case string:
@@ -3194,6 +3205,8 @@ decode_switches (int argc, const char **argv, enum variable_origin origin)
                       char **val = (char **)cs->value_ptr;
                       free (*val);
                       *val = xstrdup (coptarg);
+                      if (cs->origin)
+                        *cs->origin = origin;
                       break;
                     }
 
@@ -3227,7 +3240,11 @@ decode_switches (int argc, const char **argv, enum variable_origin origin)
                     }
 
                   if (cs->type == strlist)
-                    sl->list[sl->idx++] = xstrdup (coptarg);
+                    {
+                      sl->list[sl->idx++] = xstrdup (coptarg);
+                      if (cs->origin)
+                        *cs->origin = origin;
+                    }
                   else if (cs->c == TEMP_STDIN_OPT)
                     {
                       if (stdin_offset > 0)
@@ -3235,9 +3252,15 @@ decode_switches (int argc, const char **argv, enum variable_origin origin)
                       /* We don't need to expand the temp file.  */
                       stdin_offset = sl->idx;
                       sl->list[sl->idx++] = strcache_add (coptarg);
+                      if (cs->origin)
+                        *cs->origin = origin;
                     }
                   else
-                    sl->list[sl->idx++] = expand_command_line_file (coptarg);
+                    {
+                      sl->list[sl->idx++] = expand_command_line_file (coptarg);
+                      if (cs->origin)
+                        *cs->origin = origin;
+                    }
                   sl->list[sl->idx] = 0;
                   break;
 
@@ -3269,11 +3292,19 @@ decode_switches (int argc, const char **argv, enum variable_origin origin)
                           bad = 1;
                         }
                       else
-                        *(unsigned int *) cs->value_ptr = i;
+                        {
+                          *(unsigned int *) cs->value_ptr = i;
+                          if (cs->origin)
+                            *cs->origin = origin;
+                        }
                     }
                   else
-                    *(unsigned int *) cs->value_ptr
-                      = *(unsigned int *) cs->noarg_value;
+                    {
+                      *(unsigned int *) cs->value_ptr
+                        = *(unsigned int *) cs->noarg_value;
+                      if (cs->origin)
+                        *cs->origin = origin;
+                    }
                   break;
 
                 case floating:
@@ -3282,8 +3313,12 @@ decode_switches (int argc, const char **argv, enum variable_origin origin)
                     coptarg = argv[optind++];
 
                   if (doit)
-                    *(double *) cs->value_ptr = (coptarg != 0 ? atof (coptarg)
-                                                 : *(double *) cs->noarg_value);
+                    {
+                      *(double *) cs->value_ptr = (coptarg != 0 ? atof (coptarg)
+                                                   : *(double *) cs->noarg_value);
+                      if (cs->origin)
+                        *cs->origin = origin;
+                    }
 
                   break;
                 }