#include #include #include #include "shared.h" /* A majority of lines in files.json are less than 80 characters long. */ #define GUESS 80 /* Each informative line has 6 spaces, a quote, then the data type, then a quote, then a colon, then a space, then a quote, then the data, terminated by a quote. The length of the data type's name plus 11 characters precedes the data on each line. */ #define PREFIX 11 void stripquote(char *str) { int i; for (i = 0; str[i] != '\0'; ++i) if (str[i] == '"') { str[i] = '\0'; break; } } int dircmp(char *dir, char *str) { int slashes = 0, i; for (i = 0; str[i] != '\0'; ++i) if (str[i] == '/') ++slashes; if (dir == NULL) { if (! slashes) return 1; return 0; } int diff = 0, dirslashes = 0, dirlen = 0; for (i = 0; dir[i] != '\0'; ++i) { if (dir[i] != str[i]) { diff = 1; break; } if (dir[i] == '/') ++dirslashes; ++dirlen; } if (diff) return 0; /* Account for a trailing slash. */ if (dir[dirlen - 1] == '/') --dirslashes; if (slashes - dirslashes < 2) return 1; return 0; } void printentry(int long_format, int size, char *path, int is_directory, char *sha1_hash, char *updated_at) { if (long_format) { /* The longest 31-bit int is 10 digits. If the file size is larger, there's worse problems. */ printf("%10d ", size); int i; for (i = 0; i < 20; ++i) putchar(updated_at[5 + i]); putchar(' '); } printf("%s", path); if (is_directory) putchar('/'); putchar('\n'); } char *infn = "files.json"; /* neols [-l] [wildcard] */ int main(int argc, char **argv) { FILE *in = fopen(infn, "r"); if (in == NULL) { puts("files.json isn't in the working directory."); return 1; } int i; int long_format = 0; char *dir = NULL; for (i = 1; i < argc; ++i) { if (strcmp(argv[i], "-l") == 0) long_format = 1; else dir = argv[i]; } char *line; /* The various statistics related to each entry are stored, one entry at a time. A path may be any size, so each line need be handled by realloc. */ char *path = NULL, *updated_at = NULL, *sha1_hash = NULL; int is_directory = 0, size = 0; int end, len; for (end = 0; ! end;) { line = storeline(in, &end, &len, GUESS); /* Only on lines terminating an entry, '}' is the fifth character. The ninth character of each data line is unique. */ if (line[4] == '}') { char *p = path + PREFIX + strlen("size"); if (dircmp(dir, p)) { printentry(long_format, size, p, is_directory, sha1_hash + PREFIX + strlen("sha1_hash"), updated_at + PREFIX + strlen("updated_at")); } free(path); free(updated_at); free(sha1_hash); sha1_hash = NULL; size = 0; free(line); } else if (line[8] == 'a') { path = line; stripquote(path + strlen("path") + PREFIX); } else if (line[8] == 's') { if (strstr(line, "false") == 0) is_directory = 1; else is_directory = 0; free(line); } else if (line[8] == 'i') { line[len - 1] = '\0'; size = atoi(line + PREFIX + strlen("size")); free(line); } else if (line[8] == 'p') { updated_at = line; stripquote(updated_at); } else if (line[8] == 'h') { sha1_hash = line; } } return 0; }