diff options
Diffstat (limited to 'neols.c')
-rw-r--r-- | neols.c | 148 |
1 files changed, 148 insertions, 0 deletions
diff --git a/neols.c b/neols.c new file mode 100644 index 0000000..06dc66e --- /dev/null +++ b/neols.c @@ -0,0 +1,148 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#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; +} |