Repositories / jai.git

jai.git

Clone (read-only): git clone http://git.guha-anderson.com/git/jai.git

Branch

allow optional = in config files

Author
David Mazieres <dm@uun.org>
Date
2026-03-14 19:10:28 -0700
Commit
d85ca3c50fc0596f7df7d8ea93f5a1666321a179
jai.cc
index f7db2c0..1086a75 100644
--- a/jai.cc
+++ b/jai.cc
@@ -688,7 +688,7 @@ do_main(int argc, char **argv)
   opts("-u", [&] { opt_u = true; }, "Unmount sandboxed file systems");
   opts(
       "-n", "--name",
-      [&] (std::string optarg) {
+      [&](std::string optarg) {
         conf.sandbox_name_ = optarg;
         if (conf.sandbox_name_.is_absolute() ||
             std::ranges::distance(conf.sandbox_name_.begin(),
@@ -724,12 +724,6 @@ version 3 or later; see the file named COPYING for details.)",
   });
   option_help = opts.help();
 
-  restore.reset();
-
-  if (!set_mode)
-    conf.mode_ =
-        conf.sandbox_name_ == "default" ? Config::kCasual : Config::kStrict;
-
   std::vector<char *> cmd;
   try {
     cmd.append_range(opts.parse_argv(argc, argv));
@@ -737,6 +731,30 @@ version 3 or later; see the file named COPYING for details.)",
     std::println("{}", e.what());
     usage(2);
   }
+  if (!cmd.empty() && *cmd[0] != '.' && !strchr(cmd[0], '/')) {
+    path cfpath = std::format("{}.conf", cmd[0]);
+    Fd cf = openat(conf.home_jai(), cfpath.c_str(), O_RDONLY);
+    if (!cf) {
+      if (errno != ENOENT)
+        syserr("{}", fdpath(conf.home_jai(), cfpath));
+      cfpath = "default.conf";
+      Fd cf = openat(conf.home_jai(), cfpath.c_str(), O_RDONLY);
+      if (errno != ENOENT)
+        syserr("{}", fdpath(conf.home_jai(), cfpath));
+    }
+    if (cf)
+      try {
+        opts.parse_file(read_file(*cf));
+      } catch (Options::Error &e) {
+        err<Options::Error>("{}:{}", fdpath(*cf), e.what());
+      }
+  }
+
+  restore.reset();
+
+  if (!set_mode)
+    conf.mode_ =
+        conf.sandbox_name_ == "default" ? Config::kCasual : Config::kStrict;
 
   if (opt_u) {
     if (opt_D || !opt_d.empty() || !cmd.empty())
options.h
index 84c3c82..95d2151 100644
--- a/options.h
+++ b/options.h
@@ -261,13 +261,13 @@ public:
         auto ha = act.has_arg();
         if (!arg.empty()) {
           if (ha == kNoArg)
-            err<OptionError>("option {} takes no argument", opt);
+            err<Error>("option {} takes no argument", opt);
           act(arg.substr(1));
         }
         else if (ha != kArg)
           act();
         else if (i + 1 == args.size())
-          err<OptionError>("option {} requires an argument", opt);
+          err<Error>("option {} requires an argument", opt);
         else
           act(args[++i]);
       }
@@ -284,7 +284,7 @@ public:
           else if (ha == kOptArg)
             act();
           else if (i + 1 == args.size())
-            err<OptionError>("option -{} requires an argument", optarg[j]);
+            err<Error>("option -{} requires an argument", optarg[j]);
           else
             act(args[++i]);
           break;
@@ -302,61 +302,68 @@ public:
   {
     static constexpr std::string_view ws = " \t\r";
     static constexpr std::string_view wsnl = " \t\r\n";
+    static constexpr std::string_view wsnleq = " \t\r\n=";
     const size_t sz = text.size();
     auto clamp = [sz](size_t n) { return std::min(n, sz); };
     for (size_t pos = 0; pos < sz;) {
-      if ((pos = text.find_first_not_of(wsnl, pos)) >= sz)
-        break;
-      if (text[pos] == '#') {
-        pos = text.find('\n', pos);
-        continue;
-      }
-      auto optend = clamp(text.find_first_of(wsnl, pos));
-      std::string optarg = "--";
-      optarg += text.substr(pos, optend - pos);
-      if ((pos = text.find_first_not_of(ws, optend)) >= sz ||
-          text[pos] == '\n') {
-        parse_argspan(std::span{&optarg, 1});
-        continue;
-      }
-      optarg += '=';
-
-      bool escape = false, last_escaped = false;
-      for (; pos < sz && (escape || text[pos] != '\n'); ++pos) {
-        if (text[pos] == '\r')
+      try {
+        if ((pos = text.find_first_not_of(wsnl, pos)) >= sz)
+          break;
+        if (text[pos] == '#') {
+          pos = text.find('\n', pos);
           continue;
-        if (!escape) {
-          last_escaped = false;
-          if (text[pos] == '\\')
-            escape = true;
-          else
-            optarg += text[pos];
+        }
+        auto optend = clamp(text.find_first_of(wsnleq, pos));
+        std::string optarg = "--";
+        optarg += text.substr(pos, optend - pos);
+        if ((pos = text.find_first_not_of(ws, optend)) >= sz ||
+            text[pos] == '\n') {
+          parse_argspan(std::span{&optarg, 1});
           continue;
         }
-        escape = false;
-        last_escaped = true;
-        switch (text[pos]) {
-        case 't':
-          optarg += '\t';
-          break;
-        case 'r':
-          optarg += '\r';
-          break;
-        case 'n':
-          optarg += '\n';
-          break;
-        case '\n':
-          pos = clamp(text.find_first_not_of(ws, pos + 1) - 1);
-          break;
-        default:
-          optarg += text[pos];
-          break;
+        if (text[pos] != '=')
+          optarg += '=';
+
+        bool escape = false, last_escaped = false;
+        for (; pos < sz && (escape || text[pos] != '\n'); ++pos) {
+          if (text[pos] == '\r')
+            continue;
+          if (!escape) {
+            last_escaped = false;
+            if (text[pos] == '\\')
+              escape = true;
+            else
+              optarg += text[pos];
+            continue;
+          }
+          escape = false;
+          last_escaped = true;
+          switch (text[pos]) {
+          case 't':
+            optarg += '\t';
+            break;
+          case 'r':
+            optarg += '\r';
+            break;
+          case 'n':
+            optarg += '\n';
+            break;
+          case '\n':
+            pos = clamp(text.find_first_not_of(ws, pos + 1) - 1);
+            break;
+          default:
+            optarg += text[pos];
+            break;
+          }
         }
+        if (!last_escaped)
+          while (wsnl.contains(optarg.back()))
+            optarg.resize(optarg.size() - 1);
+        parse_argspan(std::span{&optarg, 1});
+      } catch (const Error &e) {
+        auto lineno = std::count(text.begin(), text.begin() + clamp(pos), '\n');
+        err<Error>("{}:{}", lineno, e.what());
       }
-      if (!last_escaped)
-        while (wsnl.contains(optarg.back()))
-          optarg.resize(optarg.size() - 1);
-      parse_argspan(std::span{&optarg, 1});
     }
   }