From 5064b4d6510c2430e06d2819796a279ef8aef4a1 Mon Sep 17 00:00:00 2001
From: Yorhel <git@yorhel.nl>
Date: Sun, 26 Aug 2012 16:53:37 +0200
Subject: [PATCH] Re-added scanning UI and improved error handling

---
 src/dir.h        | 12 +++++++
 src/dir_common.c | 88 ++++++++++++++++++++++++++++++++++++++++++++++++
 src/dir_mem.c    | 10 ++++++
 src/dir_scan.c   | 70 +++++++++++++++++++++++---------------
 src/main.c       |  4 +--
 5 files changed, 154 insertions(+), 30 deletions(-)

diff --git a/src/dir.h b/src/dir.h
index bd50579..b9ef0e1 100644
--- a/src/dir.h
+++ b/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
diff --git a/src/dir_common.c b/src/dir_common.c
index dec814b..c4ff819 100644
--- a/src/dir_common.c
+++ b/src/dir_common.c
@@ -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;
+}
diff --git a/src/dir_mem.c b/src/dir_mem.c
index 4549b61..ce67525 100644
--- a/src/dir_mem.c
+++ b/src/dir_mem.c
@@ -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);
diff --git a/src/dir_scan.c b/src/dir_scan.c
index 3ab172f..a511275 100644
--- a/src/dir_scan.c
+++ b/src/dir_scan.c
@@ -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;
 }
diff --git a/src/main.c b/src/main.c
index 81283f3..d2470d2 100644
--- a/src/main.c
+++ b/src/main.c
@@ -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);
-- 
GitLab