From 59a9c6b8772c9674e876ee751ad1bd4197c6498c Mon Sep 17 00:00:00 2001 From: Yorhel Date: Tue, 28 Aug 2012 17:18:33 +0200 Subject: [PATCH] Added -o option to export dir structure to a JSON-encoded file !WARNING! The export option is experimental, and the file format is not final. I make no promise that a future version of ncdu will be able to read the current format. There's also quite a few TODO's left. --- Makefile.am | 1 + src/dir.h | 3 + src/dir_export.c | 146 +++++++++++++++++++++++++++++++++++++++++++++++ src/main.c | 31 ++++++++-- 4 files changed, 176 insertions(+), 5 deletions(-) create mode 100644 src/dir_export.c diff --git a/Makefile.am b/Makefile.am index 9ae67a3..870f73f 100644 --- a/Makefile.am +++ b/Makefile.am @@ -5,6 +5,7 @@ ncdu_SOURCES=\ src/delete.c\ src/dirlist.c\ src/dir_common.c\ + src/dir_export.c\ src/dir_mem.c\ src/dir_scan.c\ src/exclude.c\ diff --git a/src/dir.h b/src/dir.h index e9d42f9..eded6ef 100644 --- a/src/dir.h +++ b/src/dir.h @@ -94,6 +94,9 @@ struct dir_output { */ void dir_mem_init(struct dir *); +/* Initializes the SCAN state and dir_output for exporting to a file. */ +int dir_export_init(const char *fn); + /* Scanning a live directory */ extern int dir_scan_smfs; diff --git a/src/dir_export.c b/src/dir_export.c new file mode 100644 index 0000000..11cff16 --- /dev/null +++ b/src/dir_export.c @@ -0,0 +1,146 @@ +/* ncdu - NCurses Disk Usage + + Copyright (c) 2007-2012 Yoran Heling + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +*/ + +#include "global.h" + +#include +#include + + +static FILE *stream; +static int level; /* Current level of nesting */ + + +static void output_string(const char *str) { + for(; *str; str++) { + switch(*str) { + case '\n': fputs("\\n", stream); break; + case '\r': fputs("\\r", stream); break; + case '\b': fputs("\\b", stream); break; + case '\t': fputs("\\t", stream); break; + case '\f': fputs("\\f", stream); break; + case '\\': fputs("\\\\", stream); break; + case '"': fputs("\\\"", stream); break; + default: + if((unsigned char)*str <= 31 || (unsigned char)*str == 127) + fprintf(stream, "\\u00%02x", *str); + else + fputc(*str, stream); + break; + } + } +} + + +static void output_int(uint64_t n) { + char tmp[20]; + int i = 0; + + do + tmp[i++] = n % 10; + while((n /= 10) > 0); + + while(i--) + fputc(tmp[i]+'0', stream); +} + + +static void output_info(struct dir *d) { + fputs("{\"name\":\"", stream); + output_string(d->name); + fputc('"', stream); + /* No need for asize/dsize if they're 0 (which happens with excluded or failed-to-stat files) */ + if(d->asize) { + fputs(",\"asize\":", stream); + output_int((uint64_t)d->asize); + } + if(d->size) { + fputs(",\"dsize\":", stream); + output_int((uint64_t)d->size); + } + /* TODO: No need to include a dev is it's the same as the parent dir. */ + fputs(",\"dev\":", stream); + output_int(d->dev); + fputs(",\"ino\":", stream); + output_int(d->ino); + if(d->flags & FF_HLNKC) /* TODO: Including the actual number of links would be nicer. */ + fputs(",\"hlnkc\":true", stream); + if(d->flags & FF_ERR) + fputs(",\"read_error\":true", stream); + if(!(d->flags & (FF_DIR|FF_FILE))) + fputs(",\"notreg\":true", stream); + if(d->flags & FF_EXL) + fputs(",\"excluded\":\"pattern", stream); + else if(d->flags & FF_OTHFS) + fputs(",\"excluded\":\"othfs", stream); + fputc('}', stream); +} + + +/* TODO: Error handling / reporting! */ +static void item(struct dir *item) { + if(!item) { + if(!--level) { /* closing of the root item */ + fputs("]]", stream); + fclose(stream); + } else /* closing of a regular directory item */ + fputs("]", stream); + return; + } + + dir_output.items++; + + /* File header. + * TODO: Add scan options? */ + if(item->flags & FF_DIR && !level++) + fputs("[1,0,{\"progname\":\""PACKAGE"\",\"progver\":\""PACKAGE_VERSION"\"}", stream); + + fputs(",\n", stream); + if(item->flags & FF_DIR) + fputc('[', stream); + + output_info(item); +} + + +static int final(int fail) { + return fail ? 1 : 1; /* Silences -Wunused-parameter */ +} + + +int dir_export_init(const char *fn) { + /* TODO: stdout support */ + if((stream = fopen(fn, "w")) == NULL) + return 1; + + level = 0; + pstate = ST_CALC; + dir_output.item = item; + dir_output.final = final; + dir_output.size = 0; + dir_output.items = 0; + return 0; +} + diff --git a/src/main.c b/src/main.c index 4088f59..176d51f 100644 --- a/src/main.c +++ b/src/main.c @@ -103,14 +103,16 @@ int input_handle(int wait) { /* parse command line */ static char *argv_parse(int argc, char **argv) { int i, j, len; + char *export = NULL; char *dir = NULL; - dir_ui = 2; + dir_ui = -1; /* read from commandline */ for(i=1; i= argc) { printf("Option %s requires an argument\n", argv[i]); exit(1); @@ -121,7 +123,9 @@ static char *argv_parse(int argc, char **argv) { exit(1); } dir_ui = argv[i][0]-'0'; - } else if(strcmp(argv[i], "--exclude") == 0) + } else if(strcmp(argv[i], "-o") == 0) + export = argv[++i]; + else if(strcmp(argv[i], "--exclude") == 0) exclude_add(argv[++i]); else if(exclude_addfile(argv[++i])) { printf("Can't open %s: %s\n", argv[i], strerror(errno)); @@ -144,6 +148,7 @@ static char *argv_parse(int argc, char **argv) { printf(" -v Print version\n"); printf(" -x Same filesystem\n"); printf(" -r Read only\n"); + printf(" -o FILE Export scanned directory to FILE\n"); printf(" -u <0-2> UI to use when scanning (0=minimal,2=verbose)\n"); printf(" --exclude PATTERN Exclude files that match PATTERN\n"); printf(" -X, --exclude-from FILE Exclude files that match any pattern in FILE\n"); @@ -158,6 +163,20 @@ static char *argv_parse(int argc, char **argv) { } else dir = argv[i]; } + + if(export) { + /* TODO: Support exporting to stdout */ + if(dir_export_init(export)) { + printf("Can't open %s: %s\n", export, strerror(errno)); + exit(1); + } + } else + dir_mem_init(NULL); + + /* Use the single-line scan feedback by default when exporting. */ + if(dir_ui == -1) + dir_ui = export ? 1 : 2; + return dir; } @@ -186,7 +205,6 @@ int main(int argc, char **argv) { if((dir = argv_parse(argc, argv)) == NULL) dir = "."; - dir_mem_init(NULL); dir_scan_init(dir); if(dir_ui == 2) @@ -202,8 +220,11 @@ int main(int argc, char **argv) { } if(pstate == ST_CALC) { - if(dir_scan_process()) + if(dir_scan_process()) { + if(dir_ui == 1) + fputc('\n', stderr); break; + } } else if(pstate == ST_DEL) delete_process(); else if(input_handle(0))