master xplshn/aruu / cmd / dev / ninqu / fs.c
  1#include "ninqu.h"
  2
  3#include <ctype.h>
  4#include <fnmatch.h>
  5#include <glob.h>
  6#include <stdio.h>
  7#include <stdlib.h>
  8#include <string.h>
  9#include <sys/stat.h>
 10#include <unistd.h>
 11
 12long
 13mtime_of(const char *path)
 14{
 15  struct stat st;
 16  if (stat(path, &st) != 0)
 17    return -1;
 18  return (long)st.st_mtime;
 19}
 20
 21int
 22file_exists(const char *path)
 23{
 24  return mtime_of(path) >= 0;
 25}
 26
 27int
 28file_contains(const char *path, const char *needle)
 29{
 30  FILE  *fp;
 31  char   buf[8192];
 32  size_t n, needlen = strlen(needle), taillen = 0;
 33  char   tail[256];
 34  int    found = 0;
 35
 36  if (!needle[0])
 37    return 1;
 38  fp = fopen(path, "r");
 39  if (!fp)
 40    return 0;
 41  tail[0] = '\0';
 42  while (!found && (n = fread(buf, 1, sizeof buf - 1, fp)) > 0) {
 43    char joined[8192 + 256];
 44    buf[n] = '\0';
 45    snprintf(joined, sizeof joined, "%s%s", tail, buf);
 46    if (strstr(joined, needle))
 47      found = 1;
 48    taillen = strlen(buf);
 49    if (taillen > needlen)
 50      taillen = needlen - 1;
 51    snprintf(tail, sizeof tail, "%s", buf + strlen(buf) - taillen);
 52  }
 53  fclose(fp);
 54  return found;
 55}
 56
 57/* recurse_dir lacks a userdata slot, use statics for one call */
 58static const char     *rg_suffix;
 59static struct StrList *rg_out;
 60
 61static void
 62rg_visit(const char *path)
 63{
 64  struct stat st;
 65
 66  if (lstat(path, &st) != 0)
 67    return;
 68  if (S_ISDIR(st.st_mode)) {
 69    recurse_dir(path, rg_visit);
 70    return;
 71  }
 72  if (S_ISREG(st.st_mode) && fnmatch(rg_suffix, base_of(path), 0) == 0)
 73    sl_push(rg_out, path);
 74}
 75
 76static void
 77recurse_glob(const char *dir, const char *suffix, struct StrList *out)
 78{
 79  rg_suffix = suffix;
 80  rg_out    = out;
 81  recurse_dir(dir, rg_visit);
 82}
 83
 84/* double-star-slash recurses, anything else goes to glob(3) */
 85void
 86glob_expand(const char *pattern, struct StrList *out)
 87{
 88  const char *rec = strstr(pattern, "**/");
 89  if (rec) {
 90    char   prefix[1024], suffix[1024];
 91    size_t plen = (size_t)(rec - pattern);
 92    if (plen >= sizeof prefix)
 93      plen = sizeof prefix - 1;
 94    memcpy(prefix, pattern, plen);
 95    prefix[plen] = '\0';
 96    if (plen > 0 && prefix[plen - 1] == '/')
 97      prefix[plen - 1] = '\0';
 98    strlcpy(suffix, rec + 3, sizeof suffix);
 99    recurse_glob(prefix[0] ? prefix : ".", suffix, out);
100  } else {
101    glob_t g;
102    size_t i;
103    if (glob(pattern, 0, NULL, &g) == 0)
104      for (i = 0; i < g.gl_pathc; i++)
105        sl_push(out, g.gl_pathv[i]);
106    globfree(&g);
107  }
108}
109
110void
111glob_all(struct Rule *r, struct StrList *out)
112{
113  int i;
114  for (i = 0; i < r->globs.n; i++) {
115    char *pat = kv_expand(r->globs.v[i]);
116    glob_expand(pat, out);
117    free(pat);
118  }
119}
120
121int
122is_wildcard_pattern(const char *pattern)
123{
124  return strpbrk(pattern, "*?[") != NULL;
125}
126
127/* true if every glob on r is a literal path */
128int
129rule_is_literal(struct Rule *r)
130{
131  int i;
132  if (r->globs.n == 0)
133    return 0;
134  for (i = 0; i < r->globs.n; i++)
135    if (is_wildcard_pattern(r->globs.v[i]))
136      return 0;
137  return 1;
138}
139
140/* broad wildcard steps aside for literal paths so overrides need no (skip) */
141int
142path_claimed_elsewhere(const char *path, struct Rule *self)
143{
144  int i, j;
145  for (i = 0; i < nrules; i++) {
146    struct Rule *other = &rules[i];
147    if (other == self || !rule_is_literal(other))
148      continue;
149    for (j = 0; j < other->globs.n; j++) {
150      char *exp = kv_expand(other->globs.v[j]);
151      int   hit = strcmp(exp, path) == 0;
152      free(exp);
153      if (hit)
154        return 1;
155    }
156  }
157  return 0;
158}
159
160void
161splitext(const char *base, char *stem, size_t stemsz, char *ext, size_t extsz)
162{
163  const char *dot = strrchr(base, '.');
164  if (dot && dot != base) {
165    size_t sl = (size_t)(dot - base);
166    if (sl >= stemsz)
167      sl = stemsz - 1;
168    memcpy(stem, base, sl);
169    stem[sl] = '\0';
170    strlcpy(ext, dot, extsz);
171  } else {
172    strlcpy(stem, base, stemsz);
173    ext[0] = '\0';
174  }
175}
176
177/* normalize so a gate keying off BASESTEM matches genconfig output */
178void
179to_ident(const char *s, char *out, size_t outsz)
180{
181  size_t i;
182  for (i = 0; s[i] && i + 1 < outsz; i++)
183    out[i] = (s[i] == '-' || s[i] == '.') ? '_' : s[i];
184  out[i] = '\0';
185}