chiark / gitweb /
checkpath.c: Move toplevel mutable state into a `state' structure.
[checkpath] / checkpath.c
index 8d03113789ca7f21912dd6c4dd17e5e2f98c9d60..e4d83e11d079d1ed4fa94ba7267860e0abc65bfd 100644 (file)
@@ -63,12 +63,14 @@ struct elt {
   char e_name[1];                      /* Name of the directory */
 };
 
+struct state {
+  struct elt *sp;                      /* Stack pointer for list */
+  dstr path;                           /* Current path string */
+};
 
 /*----- Static variables --------------------------------------------------*/
 
 static const struct elt rootnode = { 0, 0, 0 }; /* Root of the list */
-static struct elt *sp;                 /* Stack pointer for list */
-static dstr d = DSTR_INIT;             /* Current path string */
 
 /*----- Main code ---------------------------------------------------------*/
 
@@ -124,51 +126,54 @@ static struct elt *splitpath(const char *path, struct elt *tail)
 
 /* --- @pop@ --- *
  *
- * Arguments:  ---
+ * Arguments:  @struct state *state@ = working state
  *
  * Returns:    ---
  *
  * Use:                Removes the top item from the directory stack.
  */
 
-static void pop(void)
+static void pop(struct state *state)
 {
+  struct elt *sp = state->sp, *e;
+
   if (sp->e_link) {
-    struct elt *e = sp->e_link;
-    d.len = sp->e_offset;
-    DPUTZ(&d);
-    xfree(sp); sp = e;
+    e = sp->e_link;
+    state->path.len = sp->e_offset;
+    DPUTZ(&state->path);
+    xfree(state->sp); state->sp = e;
   }
 }
 
 /* --- @popall@ --- *
  *
- * Arguments:  ---
+ * Arguments:  @struct state *state@ = working state
  *
  * Returns:    ---
  *
  * Use:                Removes all the items from the directory stack.
  */
 
-static void popall(void)
-  { while (sp->e_link) pop(); }
+static void popall(struct state *state)
+  { while (state->sp->e_link) pop(state); }
 
 /* --- @push@ --- *
  *
- * Arguments:  @struct elt *e@ = pointer to directory element
+ * Arguments:  @struct state *state@ = working state
+ *             @struct elt *e@ = pointer to directory element
  *
  * Returns:    ---
  *
  * Use:                Pushes a new subdirectory onto the stack.
  */
 
-static void push(struct elt *e)
+static void push(struct state *state, struct elt *e)
 {
-  e->e_link = sp;
-  e->e_offset = d.len;
-  DPUTC(&d, '/');
-  DPUTS(&d, e->e_name);
-  sp = e;
+  e->e_link = state->sp;
+  e->e_offset = state->path.len;
+  DPUTC(&state->path, '/');
+  DPUTS(&state->path, e->e_name);
+  state->sp = e;
 }
 
 /* --- @report@ --- *
@@ -350,15 +355,16 @@ unsigned checkpath(const char *p, const struct checkpath *cp)
 {
   char cwd[PATH_MAX];
   struct elt *e, *ee;
+  struct state state;
   struct stat st;
   unsigned bad = 0;
   dstr buf = DSTR_INIT;
   int i;
 
-  /* --- Initialize stack pointer and path string --- */
+  /* --- Initialize the state --- */
 
-  sp = (/*unconst*/ struct elt *)&rootnode;
-  dstr_destroy(&d);
+  state.sp = (/*unconst*/ struct elt *)&rootnode;
+  dstr_create(&state.path);
 
   /* --- Try to find the current directory --- */
 
@@ -399,7 +405,7 @@ unsigned checkpath(const char *p, const struct checkpath *cp)
     /* --- Backtrack on `..' elements --- */
 
     else if (strcmp(ee->e_name, "..") == 0) {
-      pop();
+      pop(&state);
       xfree(ee);
       ee = e;
       continue;
@@ -407,13 +413,13 @@ unsigned checkpath(const char *p, const struct checkpath *cp)
 
     /* --- Everything else gets pushed on the end --- */
 
-    push(ee);
+    push(&state, ee);
     ee = e;
 
     /* --- Find out what sort of a thing this is --- */
 
-    if (lstat(d.buf, &st)) {
-      report(cp, CP_ERROR, 0, d.buf, "can't stat: %e");
+    if (lstat(state.path.buf, &st)) {
+      report(cp, CP_ERROR, 0, state.path.buf, "can't stat: %e");
       bad |= CP_ERROR;
       break;
     }
@@ -426,13 +432,13 @@ unsigned checkpath(const char *p, const struct checkpath *cp)
 
       dstr_reset(&buf);
       dstr_ensure(&buf, st.st_size + 1);
-      if ((i = readlink(d.buf, buf.buf, buf.sz)) < 0) {
-       report(cp, CP_ERROR, 0, d.buf, "can't readlink: %e");
+      if ((i = readlink(state.path.buf, buf.buf, buf.sz)) < 0) {
+       report(cp, CP_ERROR, 0, state.path.buf, "can't readlink: %e");
        bad |= CP_ERROR;
        break;
       }
       buf.buf[i] = 0;
-      report(cp, CP_SYMLINK, 2, d.buf, "symlink -> `%s'", buf.buf);
+      report(cp, CP_SYMLINK, 2, state.path.buf, "symlink -> `%s'", buf.buf);
 
       /* --- Handle sticky parents --- *
        *
@@ -442,31 +448,31 @@ unsigned checkpath(const char *p, const struct checkpath *cp)
        */
 
       if ((cp->cp_what & CP_WROTHUSR) &&
-         (sp->e_link->e_flags & EF_STICKY) &&
+         (state.sp->e_link->e_flags & EF_STICKY) &&
          st.st_uid != cp->cp_uid && st.st_uid != 0) {
        bad |= CP_WROTHUSR;
-       report(cp, CP_WROTHUSR, 1, d.buf,
+       report(cp, CP_WROTHUSR, 1, state.path.buf,
               "symlink modifiable by user %u", st.st_uid);
       }
 
       /* --- Sort out what to do from here --- */
 
       if (buf.buf[0] == '/')
-       popall();
+       popall(&state);
       else
-       pop();
+       pop(&state);
       ee = splitpath(buf.buf, ee);
       continue;
     }
 
     /* --- Run the sanity check on this path element --- */
 
-    bad |= sanity(d.buf, &st, cp, ee ? 0 : SF_LAST);
+    bad |= sanity(state.path.buf, &st, cp, ee ? 0 : SF_LAST);
 
     if (S_ISDIR(st.st_mode)) {
       if (st.st_mode & S_ISVTX)
-       sp->e_flags |= EF_STICKY;
-      report(cp, CP_REPORT, 4, d.buf, "directory");
+       state.sp->e_flags |= EF_STICKY;
+      report(cp, CP_REPORT, 4, state.path.buf, "directory");
       continue;
     }
 
@@ -487,7 +493,8 @@ unsigned checkpath(const char *p, const struct checkpath *cp)
     }
   }
 
-  popall();
+  popall(&state);
+  dstr_destroy(&state.path);
   dstr_destroy(&buf);
   return (bad);
 }