glob.c (4488B)
1#include <sys/stat.h> 2#include <linux/limits.h> 3#include <sys/types.h> 4#include <dirent.h> 5#include <fcntl.h> 6#include <unistd.h> 7 8#include <errno.h> 9#include <string.h> 10#include <stdio.h> 11#include <stdbool.h> 12 13static bool show_hidden = false; 14 15static void glob_r(int fd, char *buf, size_t off, size_t cap, const char *pat); 16 17static bool 18append(char *path, const char *component, size_t *len, size_t cap, size_t off, bool final) 19{ 20 if (*len + !final + 1 > cap - off) { 21 fprintf(stderr, "glob: len > PATH_MAX\n"); 22 return false; 23 } 24 strncpy(path + off, component, *len); 25 while (*len > 0 && path[off+*len-1] == '/') 26 (*len)--; 27 if (!final) path[off+(*len)++] = '/'; 28 path[off+*len] = '\0'; 29 return true; 30} 31 32static int 33opendirat(int pfd, const char *full, const char *child) 34{ 35 int fd = openat(pfd, child, O_DIRECTORY); 36 if (fd < 0) { 37 fprintf(stderr, "glob: openat '%s': %s\n", 38 full, strerror(errno)); 39 return -1; 40 } 41 return fd; 42} 43 44static bool 45esc_strcmp(const char **esc, const char **str, const char *except) 46{ 47 const char *e, *s; 48 49 for (e = *esc, s = *str; *s && *e; s++, e++) { 50 if (*e == '\\') e++; 51 else if (strchr(except, *e)) return true; 52 if (*s != *e) return false; 53 } 54 *esc = e; 55 *str = s; 56 57 return (!*s && !*e) || strchr(except, *e); 58} 59 60static bool 61esc_strstr(const char **esc, const char **str, const char *except) 62{ 63 for (; **str; (*str)++) { 64 if (esc_strcmp(esc, str, except)) 65 return true; 66 } 67 68 return false; 69} 70 71static bool 72match(const char *pat, const char *str) 73{ 74 const char *p; 75 76 p = pat; 77 while (1) { 78 if (p == pat) { 79 if (!esc_strcmp(&p, &str, "*/")) 80 return false; 81 } else { 82 if (!esc_strstr(&p, &str, "*/")) 83 return false; 84 } 85 if (*p == '/' || !*p) 86 break; 87 p += 1; 88 } 89 90 return true; 91} 92 93static bool 94addprefix(char *buf, const char *pat, size_t cap, 95 size_t off, size_t *consumed, size_t *added) 96{ 97 const char *slash; 98 const char *star; 99 const char *tok; 100 size_t len; 101 102 *added = *consumed = 0; 103 104 star = strchr(pat, '*'); 105 if (!star) { 106 *consumed = *added = strlen(pat); 107 return append(buf, pat, added, cap, off, true); 108 } 109 110 slash = strchr(pat, '/'); 111 if (!slash || slash > star) 112 return true; 113 114 tok = pat; 115 do { 116 len = (size_t) (slash + 1 - tok); 117 *consumed += len; 118 if (slash == pat || len > 1) { 119 if (!append(buf, tok, &len, cap, off + *added, !slash)) 120 return false; 121 *added += len; 122 } 123 tok = slash + 1; 124 slash = strchr(slash + 1, '/'); 125 } while (slash && slash < star); 126 127 return true; 128} 129 130static void 131glob_r_match(int fd, char *buf, size_t off, size_t cap, const char *pat) 132{ 133 const char *slash; 134 struct dirent *ent; 135 DIR *d; 136 size_t len; 137 int sfd; 138 139 if (!(d = fdopendir(fd))) { 140 fprintf(stderr, "glob: opendir '%s': %s\n", 141 *buf ? buf : ".", strerror(errno)); 142 return; 143 } 144 145 slash = strchr(pat, '/'); 146 while ((ent = readdir(d))) { 147 if (!strcmp(ent->d_name, ".")) continue; 148 if (!strcmp(ent->d_name, "..")) continue; 149 150 if (*ent->d_name == '.' && !show_hidden) continue; 151 152 if (!match(pat, ent->d_name)) continue; 153 154 if (slash && ent->d_type == DT_DIR) { 155 len = strlen(ent->d_name); 156 if (!append(buf, ent->d_name, &len, cap, off, false)) 157 continue; 158 if ((sfd = opendirat(fd, buf, ent->d_name)) < 0) 159 continue; 160 glob_r(sfd, buf, off + len, cap, slash + 1); 161 close(sfd); 162 } else if (!slash) { 163 len = strlen(ent->d_name); 164 if (!append(buf, ent->d_name, &len, cap, off, true)) 165 continue; 166 puts(buf); 167 } 168 } 169 170 closedir(d); 171} 172 173static void 174glob_r(int fd, char *buf, size_t off, size_t cap, const char *pat) 175{ 176 struct stat stat; 177 size_t plen; 178 size_t len; 179 int sfd; 180 181 if (!addprefix(buf, pat, cap, off, &plen, &len)) 182 return; 183 184 if (len) { 185 if (!pat[plen]) { 186 if (!fstatat(fd, buf + off, &stat, 0)) { 187 puts(buf); 188 } 189 } else { 190 if ((sfd = opendirat(fd, buf, buf + off)) < 0) 191 return; 192 glob_r_match(sfd, buf, off + len, cap, pat + plen); 193 close(sfd); 194 } 195 } else { 196 glob_r_match(fd, buf, off, cap, pat + plen); 197 } 198} 199 200static void 201glob(char *pat) 202{ 203 char buf[PATH_MAX]; 204 char *end; 205 206 buf[0] = '\0'; 207 208 end = pat + strlen(pat); 209 while (end > pat && end[-1] == '/') 210 end--; 211 *end = '\0'; 212 213 glob_r(AT_FDCWD, buf, 0, PATH_MAX, pat); 214} 215 216int 217main(int argc, char **argv) 218{ 219 char **arg; 220 221 if (!argc) return 1; 222 223 for (arg = argv + 1; *arg; arg++) { 224 if (!strcmp(*arg, "-h")) { 225 printf("Usage: glob [-h] [-a]\n"); 226 return 0; 227 } else if (!strcmp(*arg, "-a")) { 228 show_hidden = true; 229 } else { 230 if (!strcmp(*arg, "--")) arg++; 231 break; 232 } 233 } 234 235 for (; *arg; arg++) glob(*arg); 236}