diff --git a/src/browser.c b/src/browser.c index 430deb03e3418d129dd958b9af8651349418d329..f3bded6a031d8f5690475011e76269328c5ce09d 100644 --- a/src/browser.c +++ b/src/browser.c @@ -285,7 +285,7 @@ void showBrowser(void) { int ch, change; struct dir *n; - bcur = dat.sub; + bcur = dat->sub; bgraph = 1; nodelay(stdscr, 0); bflags = BF_SIZE | BF_DESC | BF_NDIRF; diff --git a/src/calc.c b/src/calc.c index 8bcd6d8df4f9efc1f8bd3028e050c4ffbb56c7bb..9143ae53f03816d91ffceca024b35ed4377f2073 100644 --- a/src/calc.c +++ b/src/calc.c @@ -26,6 +26,19 @@ #include "ncdu.h" +/* parent dir we are calculating */ +struct dir *parent; +/* current device we are on */ +dev_t curdev; +/* path of the last dir we couldn't read */ +char lasterr[PATH_MAX]; +/* and for the animation... */ +int anpos; +char antext[15] = "Calculating..."; +suseconds_t lastupdate; + + + /* My own implementation of realpath() - assumes that *every* possible path fits in PATH_MAX bytes - does not set errno on error @@ -120,239 +133,272 @@ char *rpath(const char *from, char *to) { } -WINDOW* calcWin() { - WINDOW *calc; - calc = newwin(10, 60, winrows/2 - 5, wincols/2 - 30); - keypad(calc, TRUE); - box(calc, 0, 0); - wattron(calc, A_BOLD); - mvwaddstr(calc, 0, 4, "Calculating..."); - wattroff(calc, A_BOLD); - mvwaddstr(calc, 2, 2, "Total files:"); - mvwaddstr(calc, 2, 24, "dirs:"); - mvwaddstr(calc, 2, 39, "size:"); - mvwaddstr(calc, 3, 2, "Current dir:"); - mvwaddstr(calc, 8, 43, "Press q to quit"); - return(calc); +/* the progress window */ +static void drawProgress(char *cdir) { + WINDOW *prg; + char ani[15]; + int i; + + prg = newwin(10, 60, winrows/2-3, wincols/2-30); + box(prg, 0, 0); + wattron(prg, A_BOLD); + mvwaddstr(prg, 0, 4, "Calculating..."); + wattroff(prg, A_BOLD); + + mvwprintw(prg, 2, 2, "Total files: %-8d dirs: %-8d size: %s", + parent->files, parent->dirs, cropsize(parent->size)); + mvwprintw(prg, 3, 2, "Current dir: %s", cropdir(cdir, 43)); + mvwaddstr(prg, 8, 43, "Press q to quit"); + + /* show warning if we couldn't open a dir */ + if(lasterr[0] != '\0') { + wattron(prg, A_BOLD); + mvwaddstr(prg, 5, 2, "Warning:"); + wattroff(prg, A_BOLD); + mvwprintw(prg, 5, 11, "could not open %-32s", cropdir(lasterr, 32)); + mvwaddstr(prg, 6, 3, "some directory sizes may not be correct"); + } + + /* animation - but only if the screen refreshes more than or once every second */ + if(sdelay <= 1000) { + if(++anpos == 28) anpos = 0; + strcpy(ani, " "); + if(anpos < 14) + for(i=0; i<=anpos; i++) + ani[i] = antext[i]; + else + for(i=13; i>anpos-14; i--) + ani[i] = antext[i]; + } else + strcpy(ani, antext); + mvwaddstr(prg, 8, 3, ani); + + wrefresh(prg); + delwin(prg); +} + + +/* show error if can't open parent dir */ +static void drawError(char *dir) { + WINDOW *err; + + err = newwin(10, 60, winrows/2-3, wincols/2-30); + box(err, 0, 0); + wattron(err, A_BOLD); + mvwaddstr(err, 0, 4, "Error!"); + + mvwaddstr(err, 5, 2, "Error:"); + wattroff(err, A_BOLD); + mvwprintw(err, 5, 9, "could not open %s", cropdir(dir, 34)); + mvwaddstr(err, 6, 3, "press any key to continue..."); + + wrefresh(err); + delwin(err); } -int calcUsage() { - WINDOW *calc; + +/* checks for input and calls drawProgress */ +int updateProgress(char *path) { + struct timeval tv; + int ch; + + /* check for input or screen resizes */ + nodelay(stdscr, 1); + while((ch = getch()) != ERR) { + if(ch == 'q') + return(0); + if(ch == KEY_RESIZE) { + ncresize(); + drawProgress(path); + } + } + nodelay(stdscr, 0); + + /* don't update the screen with shorter intervals than sdelay */ + gettimeofday(&tv, (void *)NULL); + tv.tv_usec = (1000*(tv.tv_sec % 1000) + (tv.tv_usec / 1000)) / sdelay; + if(lastupdate != tv.tv_usec) { + drawProgress(path); + lastupdate = tv.tv_usec; + } + return(1); +} + + +/* recursive */ +int calcDir(struct dir *dest, char *path) { + struct dir *d, *t; + struct stat fs; DIR *dir; - char antext[15] = "Calculating..."; - int ch, anpos = 0, level = 0, i, cdir1len, namelen; - char cdir[PATH_MAX], emsg[PATH_MAX], tmp[PATH_MAX], err = 0, *f, - *cdir1, direrr, staterr; - dev_t dev = (dev_t) NULL; struct dirent *dr; - struct stat fs; - struct dir *d, *dirs[512]; /* 512 recursive directories should be enough for everyone! */ - struct timeval tv; suseconds_t l; - gettimeofday(&tv, (void *)NULL); - l = (1000*(tv.tv_sec % 1000) + (tv.tv_usec / 1000)) / sdelay - 1; + char *f, tmp[PATH_MAX]; + int len, derr = 0, serr = 0; - calc = calcWin(); - wrefresh(calc); - - memset(dirs, 0, sizeof(struct dir *)*512); - level = 0; - dirs[level] = &dat; - memset(&dat, 0, sizeof(dat)); - - if(rpath(sdir, tmp) == NULL || stat(tmp, &fs) != 0 || !S_ISDIR(fs.st_mode)) { - mvwaddstr(calc, 8, 1, " "); - wattron(calc, A_BOLD); - mvwaddstr(calc, 5, 2, "Error:"); - wattroff(calc, A_BOLD); - mvwprintw(calc, 5, 9, "could not open %s", cropdir(tmp, 34)); - mvwaddstr(calc, 6, 3, "press any key to continue..."); - wrefresh(calc); - wgetch(calc); - delwin(calc); + if(!updateProgress(path)) + return(0); + + /* open directory */ + if((dir = opendir(path)) == NULL) { + strcpy(lasterr, path); + dest->flags |= FF_ERR; + t = dest; + while((t = t->parent) != NULL) + t->flags |= FF_SERR; return(1); } - if(sflags & SF_AS) dat.size = fs.st_size; - else dat.size = fs.st_blocks * 512; - if(sflags & SF_SMFS) dev = fs.st_dev; - dat.name = malloc(strlen(tmp)+1); - strcpy(dat.name, tmp); - - nodelay(calc, 1); - /* main loop */ - while((ch = wgetch(calc)) != 'q') { - direrr = staterr = 0; - cdir1 = cdir; + len = strlen(path); - if(ch == KEY_RESIZE) { - delwin(calc); - ncresize(); - calc = calcWin(); - nodelay(calc, 1); - erase(); - refresh(); - } + /* add leading slash */ + if(path[len-1] != '/') { + path[len] = '/'; + path[++len] = '\0'; + } + + /* read directory */ + while((dr = readdir(dir)) != NULL) { + f = dr->d_name; + if(f[0] == '.' && (f[1] == '\0' || (f[1] == '.' && f[2] == '\0'))) + continue; - /* calculate full path of the dir */ - cdir[0] = '\0'; - for(i=0; i<=level; i++) { - if(i > 0 && !(i == 1 && dat.name[strlen(dat.name)-1] == '/')) strcat(cdir, "/"); - strcat(cdir, dirs[i]->name); - } - /* avoid lstat("//name", .) -- Linux:OK, Cygwin:UNC path, POSIX:Implementation-defined */ - if(cdir[0] == '/' && cdir[1] == '\0') - cdir1++; - cdir1len = strlen(cdir1); - /* opendir */ - if((dir = opendir(cdir)) == NULL) { - dirs[level]->flags |= FF_ERR; - for(i=level; i-->0;) - dirs[i]->flags |= FF_SERR; - err = 1; - strcpy(emsg, cdir); - dirs[++level] = NULL; - goto showstatus; - } - dirs[++level] = NULL; - /* readdir */ - errno = 0; - while((dr = readdir(dir)) != NULL) { - f = dr->d_name; - if(f[0] == '.' && f[1] == '\0') - continue; - if(f[0] == '.' && f[1] == '.' && f[2] == '\0' && level == 1) - continue; - namelen = strlen(f); - if(cdir1len+namelen+1 >= PATH_MAX) { - direrr = 1; - errno = 0; - continue; - } - d = calloc(sizeof(struct dir), 1); - d->name = malloc(namelen+1); - strcpy(d->name, f); - if(dirs[level] != NULL) dirs[level]->next = d; - d->prev = dirs[level]; - d->parent = dirs[level-1]; - dirs[level-1]->sub = d; - dirs[level] = d; - /* reference to parent directory, no need to stat */ - if(f[0] == '.' && f[1] == '.' && f[2] == '\0') { - d->flags = FF_PAR; - continue; - } - sprintf(tmp, "%s/%s", cdir1, d->name); - /* stat */ - if(lstat(tmp, &fs)) { - staterr = 1; - d->flags = FF_ERR; - errno = 0; - continue; - } - /* check if file/dir is excluded */ - if(matchExclude(tmp)) - d->flags = FF_EXL; - /* check filetype */ - if(sflags & SF_SMFS && dev != fs.st_dev) - d->flags |= FF_OTHFS; - if(S_ISREG(fs.st_mode)) { - d->flags |= FF_FILE; - if(!(d->flags & FF_EXL)) - for(i=level; i-->0;) - dirs[i]->files++; - } else if(S_ISDIR(fs.st_mode)) { - d->flags |= FF_DIR; - if(!(d->flags & FF_EXL)) - for(i=level; i-->0;) - dirs[i]->dirs++; - } else - d->flags |= FF_OTHER; - if(d->flags & FF_EXL) - continue; - /* count size */ - if(sflags & SF_AS) - d->size = fs.st_size; - else - d->size = fs.st_blocks * 512; - if(d->flags & FF_OTHFS) d->size = 0; - for(i=level; i-->0;) - dirs[i]->size += d->size; + /* path too long - ignore file */ + if(len+strlen(f)+1 > PATH_MAX) { + derr = 1; errno = 0; + continue; } - /* empty dir - remove the reference to the parent dir */ - if(dirs[level]->next == NULL && dirs[level]->prev == NULL) { - dirs[level]->parent->sub = NULL; - free(dirs[level]->name); - free(dirs[level]); - dirs[level] = NULL; - } - /* check for errors */ - if(errno) - direrr = 1; - closedir(dir); - if(direrr || staterr) { - dirs[level-1]->flags |= (direrr ? FF_ERR : FF_SERR); - for(i=level-1; i-->0;) - dirs[i]->flags |= FF_SERR; + + /* allocate dir and fix references */ + d = calloc(sizeof(struct dir), 1); + d->parent = dest; + if(dest->sub != NULL) { + d->prev = dest->sub; + d->prev->next = d; } + d->parent->sub = d; - /* show progress status */ - showstatus: - gettimeofday(&tv, (void *)NULL); - tv.tv_usec = (1000*(tv.tv_sec % 1000) + (tv.tv_usec / 1000)) / sdelay; - if(l == tv.tv_usec) goto newdir; - mvwprintw(calc, 3, 15, "%-43s", cropdir(cdir, 43)); - mvwprintw(calc, 2, 15, "%d", dat.files); - mvwprintw(calc, 2, 30, "%d", dat.dirs); - mvwaddstr(calc, 2, 45, cropsize(dat.size)); - - if(err == 1) { - wattron(calc, A_BOLD); - mvwaddstr(calc, 5, 2, "Warning:"); - wattroff(calc, A_BOLD); - mvwprintw(calc, 5, 11, "could not open %-32s", cropdir(emsg, 32)); - mvwaddstr(calc, 6, 3, "some directory sizes may not be correct"); + /* set d->name */ + d->name = malloc(strlen(f)+1); + strcpy(d->name, f); + + /* get full path */ + strcpy(tmp, path); + strcat(tmp, f); + + /* lstat */ + if(lstat(tmp, &fs)) { + serr = 1; + errno = 0; + d->flags |= FF_ERR; + continue; } - /* animation */ - if(sdelay < 1000) { - if(++anpos == 28) anpos = 0; - mvwaddstr(calc, 8, 3, " "); - if(anpos < 14) - for(i=0; i<=anpos; i++) - mvwaddch(calc, 8, i+3, antext[i]); - else - for(i=13; i>anpos-14; i--) - mvwaddch(calc, 8, i+3, antext[i]); + /* check for excludes and same filesystem */ + if(matchExclude(tmp)) + d->flags |= FF_EXL; + + if(sflags & SF_SMFS && curdev != fs.st_dev) + d->flags |= FF_OTHFS; + + /* determine type of this item and update parent dirs */ + if(S_ISREG(fs.st_mode)) { + d->flags |= FF_FILE; + if(!(d->flags & FF_EXL)) + for(t = dest; t != NULL; t = t->parent) + t->files++; + } else if(S_ISDIR(fs.st_mode)) { + d->flags |= FF_DIR; + if(!(d->flags & FF_EXL)) + for(t = dest; t != NULL; t = t->parent) + t->dirs++; } else - mvwaddstr(calc, 8, 3, antext); - wmove(calc, 8, 58); - wrefresh(calc); - - - newdir: - l = tv.tv_usec; - /* select new directory */ - while(dirs[level] == NULL || !(dirs[level]->flags & FF_DIR) || dirs[level]->flags & FF_OTHFS || dirs[level]->flags & FF_EXL) { - if(dirs[level] != NULL && dirs[level]->prev != NULL) - dirs[level] = dirs[level]->prev; - else { - while(level-- > 0) { - if(dirs[level]->prev != NULL) { - dirs[level] = dirs[level]->prev; - break; - } - } - if(level < 1) goto endloop; - } + d->flags |= FF_OTHER; + + /* count the size */ + if(!(d->flags & FF_EXL || d->flags & FF_OTHFS)) { + d->size = sflags & SF_AS ? fs.st_size : fs.st_blocks * 512; + for(t = dest; t != NULL; t = t->parent) + t->size += d->size; } + + /* show status */ + if(!updateProgress(tmp)) + return(0); + + errno = 0; + } + derr = derr || errno; + closedir(dir); + + /* error occured while reading this dir, update parent dirs */ + if(derr || serr) { + dest->flags |= derr ? FF_ERR : FF_SERR; + for(t = dest; (t = t->parent) != NULL; ) + t->flags |= FF_SERR; + } + + if(dest->sub) { + /* update dest->sub to point to the first item */ + while(dest->sub->prev) + dest->sub = dest->sub->prev; + + /* add reference to parent dir */ + d = calloc(sizeof(struct dir), 1); + d->flags |= FF_PAR; + d->name = malloc(3); + strcpy(d->name, ".."); + d->next = dest->sub; + d->parent = dest; + d->next->prev = d; + dest->sub = d; + + /* calculate subdirectories */ + while((d = d->next) != NULL) + if(d->flags & FF_DIR && !(d->flags & FF_EXL || d->flags & FF_OTHFS)) { + strcpy(tmp, path); + strcat(tmp, d->name); + if(!calcDir(d, tmp)) + return(0); + } } - endloop: - nodelay(calc, 0); - delwin(calc); - erase(); - refresh(); - if(ch == 'q') - return(2); - return(0); + + return(1); } + +struct dir *showCalc(char *path) { + char tmp[PATH_MAX]; + struct stat fs; + + /* init/reset global vars */ + *lasterr = '\0'; + anpos = 0; + lastupdate = 999; + + /* init parent dir */ + if(rpath(path, tmp) == NULL || lstat(tmp, &fs) != 0) { + do { + ncresize(); + drawError(path); + } while (getch() == KEY_RESIZE); + return(NULL); + } + parent = calloc(sizeof(struct dir), 1); + parent->size = sflags & SF_AS ? fs.st_size : fs.st_blocks * 512; + curdev = fs.st_dev; + parent->name = malloc(strlen(tmp)+1); + strcpy(parent->name, tmp); + + /* start calculating */ + if(!calcDir(parent, tmp)) { + freedir(parent); + return(NULL); + } + return(parent); +} + + + + + + + diff --git a/src/delete.c b/src/delete.c index ce62951faf08b33ab39f491d8f9b27a2df92eb8f..60bafff60fbccdb7d2d933a903169f40526a8e92 100644 --- a/src/delete.c +++ b/src/delete.c @@ -61,7 +61,7 @@ void drawConfirm(struct dir *del, int sel) { /* show progress */ -void drawProgress(char *file) { +static void drawProgress(char *file) { WINDOW *prg; prg = newwin(6, 60, winrows/2-3, wincols/2-30); @@ -80,7 +80,7 @@ void drawProgress(char *file) { /* show error dialog */ -void drawError(int sel, char *file) { +static void drawError(int sel, char *file) { WINDOW *err; err = newwin(6, 60, winrows/2-3, wincols/2-30); diff --git a/src/main.c b/src/main.c index 0bf11bbad97bc3a669e29fc0952ede001ea7dc20..2edae42b177026eb9a925e721185f9ddb6493e31 100644 --- a/src/main.c +++ b/src/main.c @@ -26,14 +26,15 @@ #include "ncdu.h" /* check ncdu.h what these are for */ -struct dir dat; +struct dir *dat; int winrows, wincols; char sdir[PATH_MAX]; int sflags, bflags, sdelay, bgraph; /* main program */ int main(int argc, char **argv) { - int r, gd; + int gd; + gd = settingsCli(argc, argv); initscr(); cbreak(); @@ -42,11 +43,13 @@ int main(int argc, char **argv) { keypad(stdscr, TRUE); ncresize(); - if(gd && settingsWin()) goto mainend; - while((r = calcUsage()) != 0) { - if(r == 1 && settingsWin()) goto mainend; - else if(r == 2) goto mainend; - } + if(gd && settingsWin()) + goto mainend; + + while((dat = showCalc(sdir)) == NULL) + if(settingsWin()) + goto mainend; + showBrowser(); mainend: diff --git a/src/ncdu.h b/src/ncdu.h index cca87ba9d03fa62a98868bd4eeaf0602d9165c7a..7a74630c20d9bd4a005bc4da721a412c0b03a4dd 100644 --- a/src/ncdu.h +++ b/src/ncdu.h @@ -124,7 +124,7 @@ struct dir { * (all defined in main.c) */ /* main directory data */ -extern struct dir dat; +extern struct dir *dat; /* updated when window is resized */ extern int winrows, wincols; /* global settings */ @@ -145,7 +145,7 @@ extern char *getpath(struct dir *, char *); extern int settingsCli(int, char **); extern int settingsWin(void); /* calc.c */ -extern int calcUsage(); +extern struct dir *showCalc(char *); /* browser.c */ extern void drawBrowser(int); extern void showBrowser(void); diff --git a/src/util.c b/src/util.c index 605a3309c406230628aa70b7651f01e677ec4be5..46f0f92e0a9e2b2f16b01db73d07f6d6c27b45cd 100644 --- a/src/util.c +++ b/src/util.c @@ -131,7 +131,7 @@ struct dir *freedir(struct dir *dr) { if(cur != NULL) cur->flags |= FF_BSEL; - if(dr->parent->sub == dr) { + if(dr->parent && dr->parent->sub == dr) { if(dr->prev != NULL) dr->parent->sub = dr->prev; else if(dr->next != NULL)