mirror of
https://code.blicky.net/yorhel/ncdu.git
synced 2026-01-15 10:18:39 -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.
|
* Return value should be 0 to continue running ncdu, 1 to exit.
|
||||||
*/
|
*/
|
||||||
int (*final)(int);
|
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. */
|
/* Sets the path where the last error occured, or reset on NULL. */
|
||||||
void dir_setlasterr(const char *);
|
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
|
/* Return an empty struct dir with the given name, for use with
|
||||||
* dir_output.item(). Returned memory may be freed/overwritten on a subsequent
|
* dir_output.item(). Returned memory may be freed/overwritten on a subsequent
|
||||||
* call. */
|
* call. */
|
||||||
struct dir *dir_createstruct(const char *);
|
struct dir *dir_createstruct(const char *);
|
||||||
|
|
||||||
|
int dir_key(int);
|
||||||
|
void dir_draw();
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -27,10 +27,12 @@
|
||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
|
||||||
|
|
||||||
char *dir_curpath; /* Full path of the last seen item. */
|
char *dir_curpath; /* Full path of the last seen item. */
|
||||||
struct dir_output dir_output;
|
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 char *lasterr; /* Path where the last error occured. */
|
||||||
static int curpathl; /* Allocated length of dir_curpath */
|
static int curpathl; /* Allocated length of dir_curpath */
|
||||||
static int lasterrl; /* ^ of lasterr */
|
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) {
|
struct dir *dir_createstruct(const char *name) {
|
||||||
static struct dir *d = NULL;
|
static struct dir *d = NULL;
|
||||||
static size_t len = 0;
|
static size_t len = 0;
|
||||||
|
|
@ -98,3 +115,74 @@ struct dir *dir_createstruct(const char *name) {
|
||||||
strcpy(d->name, name);
|
strcpy(d->name, name);
|
||||||
return d;
|
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)
|
if(item->flags & FF_DIR)
|
||||||
curdir = item;
|
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
|
/* 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
|
* possible hard link, because hlnk_check() will take care of it in that
|
||||||
* case. */
|
* case. */
|
||||||
|
|
@ -157,6 +162,9 @@ static void item(struct dir *item) {
|
||||||
if(item->flags & FF_SERR || item->flags & FF_ERR)
|
if(item->flags & FF_SERR || item->flags & FF_ERR)
|
||||||
for(t=item->parent; t; t=t->parent)
|
for(t=item->parent; t; t=t->parent)
|
||||||
t->flags |= FF_SERR;
|
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.item = item;
|
||||||
dir_output.final = final;
|
dir_output.final = final;
|
||||||
|
dir_output.size = 0;
|
||||||
|
dir_output.items = 0;
|
||||||
|
|
||||||
/* Init hash table for hard link detection */
|
/* Init hash table for hard link detection */
|
||||||
links = kh_init(hl);
|
links = kh_init(hl);
|
||||||
|
|
|
||||||
|
|
@ -135,7 +135,11 @@ static int dir_scan_recurse(struct dir *d) {
|
||||||
d->flags |= FF_ERR;
|
d->flags |= FF_ERR;
|
||||||
dir_output.item(d);
|
dir_output.item(d);
|
||||||
dir_output.item(NULL);
|
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. */
|
/* readdir() failed halfway, not fatal. */
|
||||||
|
|
@ -147,8 +151,10 @@ static int dir_scan_recurse(struct dir *d) {
|
||||||
dir_output.item(NULL);
|
dir_output.item(NULL);
|
||||||
|
|
||||||
/* Not being able to chdir back is fatal */
|
/* Not being able to chdir back is fatal */
|
||||||
if(!fail && chdir(".."))
|
if(!fail && chdir("..")) {
|
||||||
return 1; /* TODO: Error reporting */
|
dir_seterr("Error going back to parent directory: %s", strerror(errno));
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
return fail;
|
return fail;
|
||||||
}
|
}
|
||||||
|
|
@ -182,14 +188,14 @@ static int dir_scan_item(struct dir *d) {
|
||||||
|
|
||||||
/* Recurse into the dir or output the item */
|
/* Recurse into the dir or output the item */
|
||||||
if(d->flags & FF_DIR && !(d->flags & (FF_ERR|FF_EXL|FF_OTHFS)))
|
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) {
|
else if(d->flags & FF_DIR) {
|
||||||
dir_output.item(d);
|
dir_output.item(d);
|
||||||
dir_output.item(NULL);
|
dir_output.item(NULL);
|
||||||
} else
|
} else
|
||||||
dir_output.item(d);
|
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 stat fs;
|
||||||
struct dir *d;
|
struct dir *d;
|
||||||
|
|
||||||
if((path = path_real(dir_curpath)) == NULL) {
|
if((path = path_real(dir_curpath)) == NULL)
|
||||||
/* TODO */
|
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) {
|
if(!dir_fatalerr && path_chdir(dir_curpath) < 0)
|
||||||
/* TODO */
|
dir_seterr("Error changing directory: %s", strerror(errno));
|
||||||
}
|
|
||||||
|
|
||||||
/* Can these even fail after a chdir? */
|
/* Can these even fail after a chdir? */
|
||||||
if(lstat(".", &fs) != 0 || !S_ISDIR(fs.st_mode)) {
|
if(!dir_fatalerr && lstat(".", &fs) != 0)
|
||||||
/* TODO */
|
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);
|
while(dir_fatalerr && !input_handle(0))
|
||||||
if(!dir) {
|
;
|
||||||
/* TODO */
|
return dir_output.final(dir_fatalerr || fail);
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void dir_scan_init(const char *path) {
|
void dir_scan_init(const char *path) {
|
||||||
dir_curpath_set(path);
|
dir_curpath_set(path);
|
||||||
dir_setlasterr(NULL);
|
dir_setlasterr(NULL);
|
||||||
|
dir_seterr(NULL);
|
||||||
pstate = ST_CALC;
|
pstate = ST_CALC;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -44,7 +44,7 @@ static long lastupdate = 999;
|
||||||
|
|
||||||
static void screen_draw() {
|
static void screen_draw() {
|
||||||
switch(pstate) {
|
switch(pstate) {
|
||||||
case ST_CALC: /* TODO */ break;
|
case ST_CALC: dir_draw(); break;
|
||||||
case ST_BROWSE: browse_draw(); break;
|
case ST_BROWSE: browse_draw(); break;
|
||||||
case ST_HELP: help_draw(); break;
|
case ST_HELP: help_draw(); break;
|
||||||
case ST_DEL: delete_draw(); break;
|
case ST_DEL: delete_draw(); break;
|
||||||
|
|
@ -82,7 +82,7 @@ int input_handle(int wait) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
switch(pstate) {
|
switch(pstate) {
|
||||||
case ST_CALC: return 0; /* TODO */
|
case ST_CALC: return dir_key(ch);
|
||||||
case ST_BROWSE: return browse_key(ch);
|
case ST_BROWSE: return browse_key(ch);
|
||||||
case ST_HELP: return help_key(ch);
|
case ST_HELP: return help_key(ch);
|
||||||
case ST_DEL: return delete_key(ch);
|
case ST_DEL: return delete_key(ch);
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue