summaryrefslogtreecommitdiff
path: root/src/calc.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/calc.c')
-rw-r--r--src/calc.c326
1 files changed, 326 insertions, 0 deletions
diff --git a/src/calc.c b/src/calc.c
new file mode 100644
index 0000000..6bca13a
--- /dev/null
+++ b/src/calc.c
@@ -0,0 +1,326 @@
+/* ncdu - NCurses Disk Usage
+
+ Copyright (c) 2007 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 "ncdu.h"
+
+
+/* My own implementation of realpath()
+ - assumes that *every* possible path fits in PATH_MAX bytes
+ - does not set errno on error
+ - has not yet been fully tested
+*/
+char *rpath(const char *from, char *to) {
+ char tmp[PATH_MAX], cwd[PATH_MAX], cur[PATH_MAX], app[PATH_MAX];
+ int i, j, l, k, last, ll = 0;
+ struct stat st;
+
+ getcwd(cwd, PATH_MAX);
+ strcpy(cur, from);
+ app[0] = 0;
+
+ loop:
+ /* not an absolute path, add current directory */
+ if(cur[0] != '/') {
+ if(!(cwd[0] == '/' && cwd[1] == 0))
+ strcpy(tmp, cwd);
+ else
+ tmp[0] = 0;
+ strcat(tmp, "/");
+ strcat(tmp, cur);
+ } else
+ strcpy(tmp, cur);
+
+ /* now fix things like '.' and '..' */
+ i = j = last = 0;
+ l = strlen(tmp);
+ while(1) {
+ if(tmp[i] == 0)
+ break;
+ /* . */
+ if(l >= i+2 && tmp[i] == '/' && tmp[i+1] == '.' && (tmp[i+2] == 0 || tmp[i+2] == '/')) {
+ i+= 2;
+ continue;
+ }
+ /* .. */
+ if(l >= i+3 && tmp[i] == '/' && tmp[i+1] == '.' && tmp[i+2] == '.' && (tmp[i+3] == 0 || tmp[i+3] == '/')) {
+ for(k=j; --k>0;)
+ if(to[k] == '/' && k != j-1)
+ break;
+ j -= j-k;
+ if(j < 1) j = 1;
+ i += 3;
+ continue;
+ }
+ /* remove double slashes */
+ if(tmp[i] == '/' && i>0 && tmp[i-1] == '/') {
+ i++;
+ continue;
+ }
+ to[j++] = tmp[i++];
+ }
+ /* remove leading slashes */
+ while(--j > 0) {
+ if(to[j] != '/')
+ break;
+ }
+ to[j+1] = 0;
+ /* append 'app' */
+ if(app[0] != 0)
+ strcat(to, app);
+
+ j = strlen(to);
+ /* check for symlinks */
+ for(i=1; i<=j; i++) {
+ if(to[i] == '/' || to[i] == 0) {
+ strncpy(tmp, to, i);
+ tmp[i] = 0;
+ if(lstat(tmp, &st) < 0)
+ return(NULL);
+ if(S_ISLNK(st.st_mode)) {
+ if(++ll > LINK_MAX || (k = readlink(tmp, cur, PATH_MAX)) < 0)
+ return(NULL);
+ cur[k] = 0;
+ if(to[i] != 0)
+ strcpy(app, &to[i]);
+ strcpy(cwd, tmp);
+ for(k=strlen(cwd); --k>0;)
+ if(cwd[k] == '/')
+ break;
+ cwd[k] = 0;
+ goto loop;
+ }
+ if(!S_ISDIR(st.st_mode))
+ return(NULL);
+ }
+ }
+
+ return(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);
+}
+
+int calcUsage() {
+ WINDOW *calc;
+ DIR *dir;
+ char antext[15] = "Calculating...";
+ int ch, anpos = 0, level = 0, i;
+ char cdir[PATH_MAX], emsg[PATH_MAX], tmp[PATH_MAX], err = 0, *f;
+ dev_t dev;
+ struct dirent *dr;
+ struct stat fs;
+ struct dir *d, *dirs[512]; /* 512 recursive directories should be enough for everyone! */
+#ifdef HAVE_GETTIMEOFDAY
+ struct timeval tv; suseconds_t l;
+ gettimeofday(&tv, (void *)NULL);
+ tv.tv_usec = (1000*(tv.tv_sec % 1000) + (tv.tv_usec / 1000)) / sdelay - 1;
+#else
+ time_t tv; time_t l;
+ l = time(NULL) - 1;
+#endif
+
+ 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);
+ 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') {
+ if(ch == KEY_RESIZE) {
+ delwin(calc);
+ ncresize();
+ calc = calcWin();
+ nodelay(calc, 1);
+ erase();
+ refresh();
+ }
+
+ /* 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);
+ }
+ /* 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' || (f[1] == '.' && f[2] == '\0')))
+ continue;
+ d = calloc(sizeof(struct dir), 1);
+ d->name = malloc(strlen(f)+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;
+ sprintf(tmp, "%s/%s", cdir, d->name);
+ lstat(tmp, &fs);
+ /* check filetype */
+ if(sflags & SF_SMFS && dev != fs.st_dev)
+ d->flags |= FF_OTHFS;
+ if(S_ISREG(fs.st_mode)) {
+ d->flags |= FF_FILE;
+ for(i=level; i-->0;)
+ dirs[i]->files++;
+ } else if(S_ISDIR(fs.st_mode)) {
+ d->flags |= FF_DIR;
+ for(i=level; i-->0;)
+ dirs[i]->dirs++;
+ } else
+ d->flags |= FF_OTHER;
+ /* 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;
+ }
+ closedir(dir);
+ if(errno) {
+ dirs[level-1]->flags |= FF_ERR;
+ for(i=level-1; i-->0;)
+ dirs[i]->flags |= FF_SERR;
+ }
+
+ /* show progress status */
+ showstatus:
+#ifdef HAVE_GETTIMEOFDAY
+ gettimeofday(&tv, (void *)NULL);
+ tv.tv_usec = (1000*(tv.tv_sec % 1000) + (tv.tv_usec / 1000)) / sdelay;
+ if(l == tv.tv_usec) goto newdir;
+#else
+ time(&tv);
+ if(l == tv) goto newdir;
+#endif
+ 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");
+ }
+
+ /* 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]);
+ } else
+ mvwaddstr(calc, 8, 3, antext);
+ wmove(calc, 8, 58);
+ wrefresh(calc);
+
+ /* select new directory */
+ newdir:
+#ifdef HAVE_GETTIMEOFDAY
+ l = tv.tv_usec;
+#else
+ l = tv;
+#endif
+ while(dirs[level] == NULL || !(dirs[level]->flags & FF_DIR) || dirs[level]->flags & FF_OTHFS) {
+ 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;
+ }
+ }
+ }
+ endloop:
+ nodelay(calc, 0);
+ delwin(calc);
+ erase();
+ refresh();
+ if(ch == 'q')
+ return(2);
+ return(0);
+}
+