mirror of
https://code.blicky.net/yorhel/ncdu.git
synced 2026-01-13 01:08:41 -09:00
Re-added scanning UI and improved error handling
This commit is contained in:
parent
0fd7dec7b0
commit
5064b4d651
5 changed files with 156 additions and 32 deletions
12
src/dir.h
12
src/dir.h
|
|
@ -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");
|
||||
|
||||
if(!dir_fatalerr && !(dir = dir_read(&fail)))
|
||||
dir_seterr("Error reading directory: %s", strerror(errno));
|
||||
|
||||
/* Special case: empty directory = error */
|
||||
if(!dir_fatalerr && !*dir)
|
||||
dir_seterr("Directory empty");
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
dir = dir_read(&fail);
|
||||
if(!dir) {
|
||||
/* TODO */
|
||||
}
|
||||
|
||||
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);
|
||||
|
|
|
|||
Loading…
Reference in a new issue