hydra

Terminal replacement for Loopback — virtual audio devices and routing on macOS, from a ratatui TUI.
Log | Files | Refs | README | LICENSE

hydra_cfg.c (2708B)


      1 // Hydra driver configuration reader — see hydra_cfg.h.
      2 //
      3 // Deliberately dependency-free: a tiny hand-rolled scan for the first device's "name"
      4 // string, so the driver vendors no JSON library and stays trivial to audit. The manifest
      5 // is small and written by us, so we don't need a full parser — just locate
      6 //   "devices" : [ { ... "name" : "VALUE" ... } ... ]
      7 // and extract VALUE (handling \" and \\ escapes).
      8 
      9 #include "hydra_cfg.h"
     10 #include <stdio.h>
     11 #include <string.h>
     12 
     13 // Overridable at compile time for host-side testing; defaults to the real system path.
     14 #ifndef HYDRA_MANIFEST_PATH
     15 #define HYDRA_MANIFEST_PATH "/Library/Application Support/hydra/devices.json"
     16 #endif
     17 
     18 // Find the next `"key"` token at or after `p`, return a pointer just past the closing quote
     19 // of the key (i.e. at the ':' region), or NULL. Only matches the key name, not values.
     20 static const char *find_key(const char *p, const char *end, const char *key) {
     21     size_t klen = strlen(key);
     22     for (; p + klen + 1 < end; p++) {
     23         if (*p == '"' && (size_t)(end - p) > klen + 1 &&
     24             strncmp(p + 1, key, klen) == 0 && p[1 + klen] == '"') {
     25             return p + 1 + klen + 1; // just past the closing quote of the key
     26         }
     27     }
     28     return NULL;
     29 }
     30 
     31 // From `p`, skip to the next string value's opening quote and copy it (unescaped) into out.
     32 // Returns 1 on success.
     33 static int copy_next_string(const char *p, const char *end, char *out, int cap) {
     34     while (p < end && *p != '"') p++;       // find opening quote of the value
     35     if (p >= end) return 0;
     36     p++;                                     // past opening quote
     37     int n = 0;
     38     while (p < end && n < cap - 1) {
     39         char c = *p;
     40         if (c == '\\' && p + 1 < end) {      // escape: copy the next char verbatim (\" \\ etc.)
     41             out[n++] = p[1];
     42             p += 2;
     43             continue;
     44         }
     45         if (c == '"') {                      // unescaped quote ⇒ end of value
     46             break;
     47         }
     48         out[n++] = c;
     49         p++;
     50     }
     51     out[n] = '\0';
     52     return n > 0;
     53 }
     54 
     55 int hydra_cfg_device_name(char *out_name, int cap) {
     56     if (!out_name || cap <= 0) return 0;
     57 
     58     FILE *f = fopen(HYDRA_MANIFEST_PATH, "rb");
     59     if (!f) return 0;
     60 
     61     char buf[8192];
     62     size_t len = fread(buf, 1, sizeof(buf) - 1, f);
     63     fclose(f);
     64     if (len == 0) return 0;
     65     buf[len] = '\0';
     66 
     67     const char *end = buf + len;
     68     // Anchor to the devices array so we read a device's name, not some top-level field.
     69     const char *p = find_key(buf, end, "devices");
     70     if (!p) p = buf; // tolerate a flat shape too
     71     p = find_key(p, end, "name");
     72     if (!p) return 0;
     73     return copy_next_string(p, end, out_name, cap);
     74 }