Skip to content
Snippets Groups Projects
Commit 5064b4d6 authored by Yorhel's avatar Yorhel
Browse files

Re-added scanning UI and improved error handling

parent 0fd7dec7
No related branches found
No related tags found
No related merge requests found
......@@ -76,6 +76,11 @@ struct dir_output {
* Return value should be 0 to continue running ncdu, 1 to exit.
*/
int (*final)(int);
/* The output code is responsible for updating these stats. Can be 0 when not
* available. */
off_t size;
long items;
};
......@@ -108,9 +113,16 @@ void dir_curpath_leave();
/* Sets the path where the last error occured, or reset on NULL. */
void dir_setlasterr(const char *);
/* Error message on fatal error, or NULL if there hasn't been a fatal error yet. */
extern char *dir_fatalerr;
void dir_seterr(const char *, ...);
/* Return an empty struct dir with the given name, for use with
* dir_output.item(). Returned memory may be freed/overwritten on a subsequent
* call. */
struct dir *dir_createstruct(const char *);
int dir_key(int);
void dir_draw();
#endif
......@@ -27,10 +27,12 @@
#include <string.h>
#include <stdlib.h>
#include <stdarg.h>
char *dir_curpath; /* Full path of the last seen item. */
struct dir_output dir_output;
char *dir_fatalerr; /* Error message on a fatal error. (NULL if there was no fatal error) */
static char *lasterr; /* Path where the last error occured. */
static int curpathl; /* Allocated length of dir_curpath */
static int lasterrl; /* ^ of lasterr */
......@@ -86,6 +88,21 @@ void dir_setlasterr(const char *path) {
}
void dir_seterr(const char *fmt, ...) {
free(dir_fatalerr);
dir_fatalerr = NULL;
if(!fmt)
return;
va_list va;
va_start(va, fmt);
dir_fatalerr = malloc(1024); /* Should be enough for everything... */
vsnprintf(dir_fatalerr, 1023, fmt, va);
dir_fatalerr[1023] = 0;
va_end(va);
}
struct dir *dir_createstruct(const char *name) {
static struct dir *d = NULL;
static size_t len = 0;
......@@ -98,3 +115,74 @@ struct dir *dir_createstruct(const char *name) {
strcpy(d->name, name);
return d;
}
static void draw_progress() {
static const char antext[] = "Scanning...";
static int anpos = 0;
char ani[20] = {};
int i;
int width = wincols-5;
nccreate(10, width, "Scanning...");
ncprint(2, 2, "Total items: %-8d size: %s", dir_output.items, formatsize(dir_output.size));
ncprint(3, 2, "Current item: %s", cropstr(dir_curpath, width-18));
ncaddstr(8, width-18, "Press q to abort");
/* show warning if we couldn't open a dir */
if(lasterr) {
attron(A_BOLD);
ncaddstr(5, 2, "Warning:");
attroff(A_BOLD);
ncprint(5, 11, "error scanning %-32s", cropstr(lasterr, width-28));
ncaddstr(6, 3, "some directory sizes may not be correct");
}
/* animation - but only if the screen refreshes more than or once every second */
if(update_delay <= 1000) {
if(++anpos == strlen(antext)*2)
anpos = 0;
memset(ani, ' ', strlen(antext));
if(anpos < strlen(antext))
for(i=0; i<=anpos; i++)
ani[i] = antext[i];
else
for(i=strlen(antext)-1; i>anpos-strlen(antext); i--)
ani[i] = antext[i];
} else
strcpy(ani, antext);
ncaddstr(8, 3, ani);
}
static void draw_error(char *cur, char *msg) {
int width = wincols-5;
nccreate(7, width, "Error!");
attron(A_BOLD);
ncaddstr(2, 2, "Error:");
attroff(A_BOLD);
ncprint(2, 9, "could not open %s", cropstr(cur, width-26));
ncprint(3, 4, "%s", cropstr(msg, width-8));
ncaddstr(5, width-30, "press any key to continue...");
}
void dir_draw() {
browse_draw();
if(dir_fatalerr)
draw_error(dir_curpath, dir_fatalerr);
else
draw_progress();
}
int dir_key(int ch) {
if(dir_fatalerr)
return 1;
if(ch == 'q')
return 1;
return 0;
}
......@@ -144,6 +144,11 @@ static void item(struct dir *item) {
if(item->flags & FF_DIR)
curdir = item;
/* Special-case the name of the root item to be empty instead of "/". This is
* what getpath() expects. */
if(item == root && strcmp(item->name, "/") == 0)
item->name[0] = 0;
/* Update stats of parents. Don't update the size/asize fields if this is a
* possible hard link, because hlnk_check() will take care of it in that
* case. */
......@@ -157,6 +162,9 @@ static void item(struct dir *item) {
if(item->flags & FF_SERR || item->flags & FF_ERR)
for(t=item->parent; t; t=t->parent)
t->flags |= FF_SERR;
dir_output.size = root->size;
dir_output.items = root->items;
}
......@@ -200,6 +208,8 @@ void dir_mem_init(struct dir *_orig) {
dir_output.item = item;
dir_output.final = final;
dir_output.size = 0;
dir_output.items = 0;
/* Init hash table for hard link detection */
links = kh_init(hl);
......
......@@ -135,7 +135,11 @@ static int dir_scan_recurse(struct dir *d) {
d->flags |= FF_ERR;
dir_output.item(d);
dir_output.item(NULL);
return chdir("..") ? 1 : 0; /* TODO: Error reporting */
if(chdir("..")) {
dir_seterr("Error going back to parent directory: %s", strerror(errno));
return 1;
} else
return 0;
}
/* readdir() failed halfway, not fatal. */
......@@ -147,8 +151,10 @@ static int dir_scan_recurse(struct dir *d) {
dir_output.item(NULL);
/* Not being able to chdir back is fatal */
if(!fail && chdir(".."))
return 1; /* TODO: Error reporting */
if(!fail && chdir("..")) {
dir_seterr("Error going back to parent directory: %s", strerror(errno));
return 1;
}
return fail;
}
......@@ -182,14 +188,14 @@ static int dir_scan_item(struct dir *d) {
/* Recurse into the dir or output the item */
if(d->flags & FF_DIR && !(d->flags & (FF_ERR|FF_EXL|FF_OTHFS)))
dir_scan_recurse(d);
fail = dir_scan_recurse(d);
else if(d->flags & FF_DIR) {
dir_output.item(d);
dir_output.item(NULL);
} else
dir_output.item(d);
return fail; /* TODO: UI */
return fail || input_handle(1);
}
......@@ -222,42 +228,50 @@ int dir_scan_process() {
struct stat fs;
struct dir *d;
if((path = path_real(dir_curpath)) == NULL) {
/* TODO */
if((path = path_real(dir_curpath)) == NULL)
dir_seterr("Error obtaining full path: %s", strerror(errno));
else {
dir_curpath_set(path);
free(path);
}
dir_curpath_set(path);
free(path);
if(path_chdir(dir_curpath) < 0) {
/* TODO */
}
if(!dir_fatalerr && path_chdir(dir_curpath) < 0)
dir_seterr("Error changing directory: %s", strerror(errno));
/* Can these even fail after a chdir? */
if(lstat(".", &fs) != 0 || !S_ISDIR(fs.st_mode)) {
/* TODO */
}
if(!dir_fatalerr && lstat(".", &fs) != 0)
dir_seterr("Error obtaining directory information: %s", strerror(errno));
if(!dir_fatalerr && !S_ISDIR(fs.st_mode))
dir_seterr("Not a directory");
dir = dir_read(&fail);
if(!dir) {
/* TODO */
}
if(!dir_fatalerr && !(dir = dir_read(&fail)))
dir_seterr("Error reading directory: %s", strerror(errno));
curdev = fs.st_dev;
d = dir_createstruct(dir_curpath);
if(fail)
d->flags |= FF_ERR;
stat_to_dir(d, &fs);
/* Special case: empty directory = error */
if(!dir_fatalerr && !*dir)
dir_seterr("Directory empty");
dir_output.item(d);
fail = dir_walk(dir);
dir_output.item(NULL);
if(!dir_fatalerr) {
curdev = fs.st_dev;
d = dir_createstruct(dir_curpath);
if(fail)
d->flags |= FF_ERR;
stat_to_dir(d, &fs);
dir_output.item(d);
fail = dir_walk(dir);
dir_output.item(NULL);
}
return dir_output.final(fail);
while(dir_fatalerr && !input_handle(0))
;
return dir_output.final(dir_fatalerr || fail);
}
void dir_scan_init(const char *path) {
dir_curpath_set(path);
dir_setlasterr(NULL);
dir_seterr(NULL);
pstate = ST_CALC;
}
......@@ -44,7 +44,7 @@ static long lastupdate = 999;
static void screen_draw() {
switch(pstate) {
case ST_CALC: /* TODO */ break;
case ST_CALC: dir_draw(); break;
case ST_BROWSE: browse_draw(); break;
case ST_HELP: help_draw(); break;
case ST_DEL: delete_draw(); break;
......@@ -82,7 +82,7 @@ int input_handle(int wait) {
continue;
}
switch(pstate) {
case ST_CALC: return 0; /* TODO */
case ST_CALC: return dir_key(ch);
case ST_BROWSE: return browse_key(ch);
case ST_HELP: return help_key(ch);
case ST_DEL: return delete_key(ch);
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment