URI:
       noice.c - noice - small file browser (mirror / fork from 2f30.org)
  HTML git clone git://git.codemadness.org/noice
   DIR Log
   DIR Files
   DIR Refs
   DIR README
   DIR LICENSE
       ---
       noice.c (15447B)
       ---
            1 /* See LICENSE file for copyright and license details. */
            2 #include <sys/stat.h>
            3 #include <sys/types.h>
            4 
            5 #include <curses.h>
            6 #include <dirent.h>
            7 #include <errno.h>
            8 #include <fcntl.h>
            9 #include <libgen.h>
           10 #include <limits.h>
           11 #include <locale.h>
           12 #include <regex.h>
           13 #include <signal.h>
           14 #include <stdarg.h>
           15 #include <stdio.h>
           16 #include <stdlib.h>
           17 #include <string.h>
           18 #include <unistd.h>
           19 
           20 #include "arg.h"
           21 #include "util.h"
           22 
           23 #define ISODD(x) ((x) & 1)
           24 #define CONTROL(c) ((c) ^ 0x40)
           25 #define META(c) ((c) ^ 0x80)
           26 
           27 struct cpair {
           28         int fg;
           29         int bg;
           30 };
           31 
           32 /* Supported actions */
           33 enum action {
           34         SEL_QUIT = 1,
           35         SEL_BACK,
           36         SEL_GOIN,
           37         SEL_FLTR,
           38         SEL_NEXT,
           39         SEL_PREV,
           40         SEL_PGDN,
           41         SEL_PGUP,
           42         SEL_HOME,
           43         SEL_END,
           44         SEL_CD,
           45         SEL_CDHOME,
           46         SEL_TOGGLEDOT,
           47         SEL_DSORT,
           48         SEL_MTIME,
           49         SEL_ICASE,
           50         SEL_VERS,
           51         SEL_REDRAW,
           52         SEL_RUN,
           53         SEL_RUNARG,
           54 };
           55 
           56 struct key {
           57         int sym;         /* Key pressed */
           58         enum action act; /* Action */
           59         char *run;       /* Program to run */
           60         char *env;       /* Environment variable override */
           61 };
           62 
           63 #include "noiceconf.h"
           64 
           65 struct entry {
           66         char name[PATH_MAX];
           67         mode_t mode;
           68         time_t t;
           69 };
           70 
           71 /* Global context */
           72 struct entry *dents;
           73 char *argv0;
           74 int ndents, cur;
           75 int idle;
           76 
           77 /*
           78  * Layout:
           79  * .---------
           80  * | /mnt/path
           81  * |
           82  * |    file0
           83  * |    file1
           84  * |  > file2
           85  * |    file3
           86  * |    file4
           87  *      ...
           88  * |    filen
           89  * |
           90  * | Permission denied
           91  * '------
           92  */
           93 
           94 void info(char *, ...);
           95 void warn(char *, ...);
           96 void fatal(char *, ...);
           97 
           98 void *
           99 xrealloc(void *p, size_t size)
          100 {
          101         p = realloc(p, size);
          102         if (p == NULL)
          103                 fatal("realloc");
          104         return p;
          105 }
          106 
          107 /* Some implementations of dirname(3) may modify `path' and some
          108  * return a pointer inside `path'. */
          109 char *
          110 xdirname(const char *path)
          111 {
          112         static char out[PATH_MAX];
          113         char tmp[PATH_MAX], *p;
          114 
          115         strlcpy(tmp, path, sizeof(tmp));
          116         p = dirname(tmp);
          117         if (p == NULL)
          118                 fatal("dirname");
          119         strlcpy(out, p, sizeof(out));
          120         return out;
          121 }
          122 
          123 char *
          124 xgetenv(char *name, char *fallback)
          125 {
          126         char *value;
          127 
          128         if (name == NULL)
          129                 return fallback;
          130         value = getenv(name);
          131         return value && value[0] ? value : fallback;
          132 }
          133 
          134 int
          135 setfilter(regex_t *regex, char *filter)
          136 {
          137         char errbuf[LINE_MAX];
          138         size_t len;
          139         int r;
          140 
          141         r = regcomp(regex, filter, REG_NOSUB | REG_EXTENDED | REG_ICASE);
          142         if (r != 0) {
          143                 len = COLS;
          144                 if (len > sizeof(errbuf))
          145                         len = sizeof(errbuf);
          146                 regerror(r, regex, errbuf, len);
          147                 info("%s", errbuf);
          148         }
          149         return r;
          150 }
          151 
          152 void
          153 freefilter(regex_t *regex)
          154 {
          155         regfree(regex);
          156 }
          157 
          158 void
          159 initfilter(int dot, char **ifilter)
          160 {
          161         *ifilter = dot ? "." : "^[^.]";
          162 }
          163 
          164 int
          165 visible(regex_t *regex, char *file)
          166 {
          167         return regexec(regex, file, 0, NULL, 0) == 0;
          168 }
          169 
          170 int
          171 dircmp(mode_t a, mode_t b)
          172 {
          173         if (S_ISDIR(a) && S_ISDIR(b))
          174                 return 0;
          175         if (!S_ISDIR(a) && !S_ISDIR(b))
          176                 return 0;
          177         if (S_ISDIR(a))
          178                 return -1;
          179         else
          180                 return 1;
          181 }
          182 
          183 int
          184 entrycmp(const void *va, const void *vb)
          185 {
          186         const struct entry *a = va, *b = vb;
          187 
          188         if (dirorder) {
          189                 if (dircmp(a->mode, b->mode) != 0)
          190                         return dircmp(a->mode, b->mode);
          191         }
          192 
          193         if (mtimeorder)
          194                 return b->t - a->t;
          195         if (icaseorder)
          196                 return strcasecmp(a->name, b->name);
          197         if (versorder)
          198                 return strverscmp(a->name, b->name);
          199         return strcmp(a->name, b->name);
          200 }
          201 
          202 void
          203 initcolor(void)
          204 {
          205         int i;
          206 
          207         start_color();
          208         use_default_colors();
          209         for (i = 1; i < LEN(pairs); i++)
          210                 init_pair(i, pairs[i].fg, pairs[i].bg);
          211 }
          212 
          213 void
          214 initcurses(void)
          215 {
          216         char *term;
          217 
          218         if (initscr() == NULL) {
          219                 term = getenv("TERM");
          220                 if (term != NULL)
          221                         fprintf(stderr, "error opening terminal: %s\n", term);
          222                 else
          223                         fprintf(stderr, "failed to initialize curses\n");
          224                 exit(1);
          225         }
          226         if (usecolor && has_colors())
          227                 initcolor();
          228         cbreak();
          229         noecho();
          230         nonl();
          231         intrflush(stdscr, FALSE);
          232         keypad(stdscr, TRUE);
          233         curs_set(FALSE); /* Hide cursor */
          234         timeout(1000); /* One second */
          235 }
          236 
          237 void
          238 exitcurses(void)
          239 {
          240         endwin(); /* Restore terminal */
          241 }
          242 
          243 /* Messages show up at the bottom */
          244 void
          245 info(char *fmt, ...)
          246 {
          247         char buf[LINE_MAX];
          248         va_list ap;
          249 
          250         va_start(ap, fmt);
          251         vsnprintf(buf, sizeof(buf), fmt, ap);
          252         va_end(ap);
          253         move(LINES - 1, 0);
          254         printw("%s\n", buf);
          255 }
          256 
          257 /* Display warning as a message */
          258 void
          259 warn(char *fmt, ...)
          260 {
          261         char buf[LINE_MAX];
          262         va_list ap;
          263 
          264         va_start(ap, fmt);
          265         vsnprintf(buf, sizeof(buf), fmt, ap);
          266         va_end(ap);
          267         move(LINES - 1, 0);
          268         printw("%s: %s\n", buf, strerror(errno));
          269 }
          270 
          271 /* Kill curses and display error before exiting */
          272 void
          273 fatal(char *fmt, ...)
          274 {
          275         va_list ap;
          276 
          277         exitcurses();
          278         va_start(ap, fmt);
          279         vfprintf(stderr, fmt, ap);
          280         fprintf(stderr, ": %s\n", strerror(errno));
          281         va_end(ap);
          282         exit(1);
          283 }
          284 
          285 /* Clear the last line */
          286 void
          287 clearprompt(void)
          288 {
          289         info("");
          290 }
          291 
          292 /* Print prompt on the last line */
          293 void
          294 printprompt(char *str)
          295 {
          296         clearprompt();
          297         info("%s", str);
          298 }
          299 
          300 int
          301 xgetch(void)
          302 {
          303         int c;
          304 
          305         c = getch();
          306         if (c == -1)
          307                 idle++;
          308         else
          309                 idle = 0;
          310         return c;
          311 }
          312 
          313 /* Returns SEL_* if key is bound and 0 otherwise.
          314  * Also modifies the run and env pointers (used on SEL_{RUN,RUNARG}) */
          315 int
          316 nextsel(char **run, char **env)
          317 {
          318         int c, i;
          319 
          320         c = xgetch();
          321         if (c == 033)
          322                 c = META(xgetch());
          323 
          324         for (i = 0; i < LEN(bindings); i++)
          325                 if (c == bindings[i].sym) {
          326                         *run = bindings[i].run;
          327                         *env = bindings[i].env;
          328                         return bindings[i].act;
          329                 }
          330         return 0;
          331 }
          332 
          333 char *
          334 readln(void)
          335 {
          336         static char ln[LINE_MAX];
          337 
          338         timeout(-1);
          339         echo();
          340         curs_set(TRUE);
          341         memset(ln, 0, sizeof(ln));
          342         wgetnstr(stdscr, ln, sizeof(ln) - 1);
          343         noecho();
          344         curs_set(FALSE);
          345         timeout(1000);
          346         return ln[0] ? ln : NULL;
          347 }
          348 
          349 int
          350 canopendir(char *path)
          351 {
          352         DIR *dirp;
          353 
          354         dirp = opendir(path);
          355         if (dirp == NULL)
          356                 return 0;
          357         closedir(dirp);
          358         return 1;
          359 }
          360 
          361 char *
          362 mkpath(char *dir, char *name, char *out, size_t n)
          363 {
          364         /* Handle absolute path */
          365         if (name[0] == '/') {
          366                 strlcpy(out, name, n);
          367         } else {
          368                 /* Handle root case */
          369                 if (strcmp(dir, "/") == 0) {
          370                         strlcpy(out, "/", n);
          371                         strlcat(out, name, n);
          372                 } else {
          373                         strlcpy(out, dir, n);
          374                         strlcat(out, "/", n);
          375                         strlcat(out, name, n);
          376                 }
          377         }
          378         return out;
          379 }
          380 
          381 void
          382 printent(struct entry *ent, int active)
          383 {
          384         char name[PATH_MAX];
          385         unsigned int len = COLS - strlen(CURSR) - 1;
          386         char cm = 0;
          387         int attr = 0;
          388 
          389         /* Copy name locally */
          390         strlcpy(name, ent->name, sizeof(name));
          391 
          392         /* No text wrapping in entries */
          393         if (strlen(name) < len)
          394                 len = strlen(name) + 1;
          395 
          396         if (S_ISDIR(ent->mode)) {
          397                 cm = '/';
          398                 attr |= DIR_ATTR;
          399         } else if (S_ISLNK(ent->mode)) {
          400                 cm = '@';
          401                 attr |= LINK_ATTR;
          402         } else if (S_ISSOCK(ent->mode)) {
          403                 cm = '=';
          404                 attr |= SOCK_ATTR;
          405         } else if (S_ISFIFO(ent->mode)) {
          406                 cm = '|';
          407                 attr |= FIFO_ATTR;
          408         } else if (ent->mode & S_IXUSR) {
          409                 cm = '*';
          410                 attr |= EXEC_ATTR;
          411         }
          412 
          413         if (active)
          414                 attr |= CURSR_ATTR;
          415 
          416         if (cm) {
          417                 name[len - 1] = cm;
          418                 name[len] = '\0';
          419         }
          420 
          421         attron(attr);
          422         printw("%s%s\n", active ? CURSR : EMPTY, name);
          423         attroff(attr);
          424 }
          425 
          426 int
          427 dentfill(char *path, struct entry **dents,
          428          int (*filter)(regex_t *, char *), regex_t *re)
          429 {
          430         char newpath[PATH_MAX];
          431         DIR *dirp;
          432         struct dirent *dp;
          433         struct stat sb;
          434         int r, n = 0;
          435 
          436         dirp = opendir(path);
          437         if (dirp == NULL)
          438                 return 0;
          439 
          440         while ((dp = readdir(dirp)) != NULL) {
          441                 /* Skip self and parent */
          442                 if (strcmp(dp->d_name, ".") == 0 ||
          443                     strcmp(dp->d_name, "..") == 0)
          444                         continue;
          445                 if (filter(re, dp->d_name) == 0)
          446                         continue;
          447                 *dents = xrealloc(*dents, (n + 1) * sizeof(**dents));
          448                 strlcpy((*dents)[n].name, dp->d_name, sizeof((*dents)[n].name));
          449                 /* Get mode flags */
          450                 mkpath(path, dp->d_name, newpath, sizeof(newpath));
          451                 r = lstat(newpath, &sb);
          452                 if (r == -1)
          453                         fatal("lstat");
          454                 (*dents)[n].mode = sb.st_mode;
          455                 (*dents)[n].t = sb.st_mtime;
          456                 n++;
          457         }
          458 
          459         /* Should never be null */
          460         r = closedir(dirp);
          461         if (r == -1)
          462                 fatal("closedir");
          463         return n;
          464 }
          465 
          466 void
          467 dentfree(struct entry *dents)
          468 {
          469         free(dents);
          470 }
          471 
          472 /* Return the position of the matching entry or 0 otherwise */
          473 int
          474 dentfind(struct entry *dents, int n, char *cwd, char *path)
          475 {
          476         char tmp[PATH_MAX];
          477         int i;
          478 
          479         if (path == NULL)
          480                 return 0;
          481         for (i = 0; i < n; i++) {
          482                 mkpath(cwd, dents[i].name, tmp, sizeof(tmp));
          483                 DPRINTF_S(path);
          484                 DPRINTF_S(tmp);
          485                 if (strcmp(tmp, path) == 0)
          486                         return i;
          487         }
          488         return 0;
          489 }
          490 
          491 int
          492 populate(char *path, char *oldpath, char *fltr)
          493 {
          494         regex_t re;
          495         int r;
          496 
          497         /* Can fail when permissions change while browsing */
          498         if (canopendir(path) == 0)
          499                 return -1;
          500 
          501         /* Search filter */
          502         r = setfilter(&re, fltr);
          503         if (r != 0)
          504                 return -1;
          505 
          506         dentfree(dents);
          507 
          508         ndents = 0;
          509         dents = NULL;
          510 
          511         ndents = dentfill(path, &dents, visible, &re);
          512         freefilter(&re);
          513         if (ndents == 0)
          514                 return 0; /* Empty result */
          515 
          516         qsort(dents, ndents, sizeof(*dents), entrycmp);
          517 
          518         /* Find cur from history */
          519         cur = dentfind(dents, ndents, path, oldpath);
          520         return 0;
          521 }
          522 
          523 void
          524 redraw(char *path)
          525 {
          526         char cwd[PATH_MAX], cwdresolved[PATH_MAX];
          527         size_t ncols;
          528         int nlines, odd;
          529         int i;
          530 
          531         nlines = MIN(LINES - 4, ndents);
          532 
          533         /* Clean screen */
          534         erase();
          535 
          536         /* Strip trailing slashes */
          537         for (i = strlen(path) - 1; i > 0; i--)
          538                 if (path[i] == '/')
          539                         path[i] = '\0';
          540                 else
          541                         break;
          542 
          543         DPRINTF_D(cur);
          544         DPRINTF_S(path);
          545 
          546         /* No text wrapping in cwd line */
          547         ncols = COLS;
          548         if (ncols > PATH_MAX)
          549                 ncols = PATH_MAX;
          550         strlcpy(cwd, path, ncols);
          551         cwd[ncols - strlen(CWD) - 1] = '\0';
          552         realpath(cwd, cwdresolved);
          553 
          554         printw(CWD "%s\n\n", cwdresolved);
          555 
          556         /* Print listing */
          557         odd = ISODD(nlines);
          558         if (cur < nlines / 2) {
          559                 for (i = 0; i < nlines; i++)
          560                         printent(&dents[i], i == cur);
          561         } else if (cur >= ndents - nlines / 2) {
          562                 for (i = ndents - nlines; i < ndents; i++)
          563                         printent(&dents[i], i == cur);
          564         } else {
          565                 for (i = cur - nlines / 2;
          566                      i < cur + nlines / 2 + odd; i++)
          567                         printent(&dents[i], i == cur);
          568         }
          569 }
          570 
          571 void
          572 browse(char *ipath, char *ifilter)
          573 {
          574         char path[PATH_MAX], oldpath[PATH_MAX], newpath[PATH_MAX];
          575         char fltr[LINE_MAX];
          576         char *dir, *tmp, *run, *env;
          577         struct stat sb;
          578         regex_t re;
          579         int r, fd;
          580 
          581         strlcpy(path, ipath, sizeof(path));
          582         strlcpy(fltr, ifilter, sizeof(fltr));
          583         oldpath[0] = '\0';
          584 begin:
          585         r = populate(path, oldpath, fltr);
          586         if (r == -1) {
          587                 warn("populate");
          588                 goto nochange;
          589         }
          590 
          591         for (;;) {
          592                 redraw(path);
          593 nochange:
          594                 switch (nextsel(&run, &env)) {
          595                 case SEL_QUIT:
          596                         dentfree(dents);
          597                         return;
          598                 case SEL_BACK:
          599                         /* There is no going back */
          600                         if (strcmp(path, "/") == 0 ||
          601                             strcmp(path, ".") == 0 ||
          602                             strchr(path, '/') == NULL)
          603                                 goto nochange;
          604                         dir = xdirname(path);
          605                         if (canopendir(dir) == 0) {
          606                                 warn("canopendir");
          607                                 goto nochange;
          608                         }
          609                         /* Save history */
          610                         strlcpy(oldpath, path, sizeof(oldpath));
          611                         strlcpy(path, dir, sizeof(path));
          612                         /* Reset filter */
          613                         strlcpy(fltr, ifilter, sizeof(fltr));
          614                         goto begin;
          615                 case SEL_GOIN:
          616                         /* Cannot descend in empty directories */
          617                         if (ndents == 0)
          618                                 goto nochange;
          619 
          620                         mkpath(path, dents[cur].name, newpath, sizeof(newpath));
          621                         DPRINTF_S(newpath);
          622 
          623                         /* Get path info */
          624                         fd = open(newpath, O_RDONLY | O_NONBLOCK);
          625                         if (fd == -1) {
          626                                 warn("open");
          627                                 goto nochange;
          628                         }
          629                         r = fstat(fd, &sb);
          630                         if (r == -1) {
          631                                 warn("fstat");
          632                                 close(fd);
          633                                 goto nochange;
          634                         }
          635                         close(fd);
          636                         DPRINTF_U(sb.st_mode);
          637 
          638                         switch (sb.st_mode & S_IFMT) {
          639                         case S_IFDIR:
          640                                 if (canopendir(newpath) == 0) {
          641                                         warn("canopendir");
          642                                         goto nochange;
          643                                 }
          644                                 strlcpy(path, newpath, sizeof(path));
          645                                 /* Reset filter */
          646                                 strlcpy(fltr, ifilter, sizeof(fltr));
          647                                 goto begin;
          648                         case S_IFREG:
          649                                 exitcurses();
          650                                 run = xgetenv("NOPEN", NOPEN);
          651                                 r = spawnlp(path, run, run, newpath, (void *)0);
          652                                 initcurses();
          653                                 if (r == -1) {
          654                                         info("Failed to execute plumber");
          655                                         goto nochange;
          656                                 }
          657                                 continue;
          658                         default:
          659                                 info("Unsupported file");
          660                                 goto nochange;
          661                         }
          662                 case SEL_FLTR:
          663                         /* Read filter */
          664                         printprompt("/");
          665                         tmp = readln();
          666                         if (tmp == NULL)
          667                                 tmp = ifilter;
          668                         /* Check and report regex errors */
          669                         r = setfilter(&re, tmp);
          670                         if (r != 0)
          671                                 goto nochange;
          672                         freefilter(&re);
          673                         strlcpy(fltr, tmp, sizeof(fltr));
          674                         DPRINTF_S(fltr);
          675                         /* Save current */
          676                         if (ndents > 0)
          677                                 mkpath(path, dents[cur].name, oldpath, sizeof(oldpath));
          678                         goto begin;
          679                 case SEL_NEXT:
          680                         if (cur < ndents - 1)
          681                                 cur++;
          682                         break;
          683                 case SEL_PREV:
          684                         if (cur > 0)
          685                                 cur--;
          686                         break;
          687                 case SEL_PGDN:
          688                         if (cur < ndents - 1)
          689                                 cur += MIN((LINES - 4) / 2, ndents - 1 - cur);
          690                         break;
          691                 case SEL_PGUP:
          692                         if (cur > 0)
          693                                 cur -= MIN((LINES - 4) / 2, cur);
          694                         break;
          695                 case SEL_HOME:
          696                         cur = 0;
          697                         break;
          698                 case SEL_END:
          699                         cur = ndents - 1;
          700                         break;
          701                 case SEL_CD:
          702                         /* Read target dir */
          703                         printprompt("chdir: ");
          704                         tmp = readln();
          705                         if (tmp == NULL) {
          706                                 clearprompt();
          707                                 goto nochange;
          708                         }
          709                         mkpath(path, tmp, newpath, sizeof(newpath));
          710                         if (canopendir(newpath) == 0) {
          711                                 warn("canopendir");
          712                                 goto nochange;
          713                         }
          714                         strlcpy(path, newpath, sizeof(path));
          715                         /* Reset filter */
          716                         strlcpy(fltr, ifilter, sizeof(fltr));
          717                         DPRINTF_S(path);
          718                         goto begin;
          719                 case SEL_CDHOME:
          720                         tmp = getenv("HOME");
          721                         if (tmp == NULL) {
          722                                 clearprompt();
          723                                 goto nochange;
          724                         }
          725                         if (canopendir(tmp) == 0) {
          726                                 warn("canopendir");
          727                                 goto nochange;
          728                         }
          729                         strlcpy(path, tmp, sizeof(path));
          730                         /* Reset filter */
          731                         strlcpy(fltr, ifilter, sizeof(fltr));
          732                         DPRINTF_S(path);
          733                         goto begin;
          734                 case SEL_TOGGLEDOT:
          735                         showhidden ^= 1;
          736                         initfilter(showhidden, &ifilter);
          737                         strlcpy(fltr, ifilter, sizeof(fltr));
          738                         goto begin;
          739                 case SEL_MTIME:
          740                         mtimeorder = !mtimeorder;
          741                         /* Save current */
          742                         if (ndents > 0)
          743                                 mkpath(path, dents[cur].name, oldpath, sizeof(oldpath));
          744                         goto begin;
          745                 case SEL_DSORT:
          746                         dirorder = !dirorder;
          747                         /* Save current */
          748                         if (ndents > 0)
          749                                 mkpath(path, dents[cur].name, oldpath, sizeof(oldpath));
          750                         goto begin;
          751                 case SEL_ICASE:
          752                         icaseorder = !icaseorder;
          753                         /* Save current */
          754                         if (ndents > 0)
          755                                 mkpath(path, dents[cur].name, oldpath, sizeof(oldpath));
          756                         goto begin;
          757                 case SEL_VERS:
          758                         versorder = !versorder;
          759                         /* Save current */
          760                         if (ndents > 0)
          761                                 mkpath(path, dents[cur].name, oldpath, sizeof(oldpath));
          762                         goto begin;
          763                 case SEL_REDRAW:
          764                         /* Save current */
          765                         if (ndents > 0)
          766                                 mkpath(path, dents[cur].name, oldpath, sizeof(oldpath));
          767                         goto begin;
          768                 case SEL_RUN:
          769                         /* Save current */
          770                         if (ndents > 0)
          771                                 mkpath(path, dents[cur].name, oldpath, sizeof(oldpath));
          772                         run = xgetenv(env, run);
          773                         exitcurses();
          774                         spawnlp(path, run, run, (void *)0);
          775                         initcurses();
          776                         goto begin;
          777                 case SEL_RUNARG:
          778                         /* Save current */
          779                         if (ndents > 0)
          780                                 mkpath(path, dents[cur].name, oldpath, sizeof(oldpath));
          781                         run = xgetenv(env, run);
          782                         exitcurses();
          783                         spawnlp(path, run, run, dents[cur].name, (void *)0);
          784                         initcurses();
          785                         goto begin;
          786                 }
          787                 /* Screensaver */
          788                 if (idletimeout != 0 && idle == idletimeout) {
          789                         idle = 0;
          790                         exitcurses();
          791                         spawnlp(NULL, idlecmd, idlecmd, (void *)0);
          792                         initcurses();
          793                 }
          794         }
          795 }
          796 
          797 void
          798 usage(void)
          799 {
          800         fprintf(stderr, "usage: %s [-c] [dir]\n", argv0);
          801         exit(1);
          802 }
          803 
          804 int
          805 main(int argc, char *argv[])
          806 {
          807         char cwd[PATH_MAX], *ipath;
          808         char *ifilter;
          809 
          810         ARGBEGIN {
          811         case 'c':
          812                 usecolor = 1;
          813                 break;
          814         default:
          815                 usage();
          816         } ARGEND
          817 
          818         if (argc > 1)
          819                 usage();
          820 
          821         /* Confirm we are in a terminal */
          822         if (!isatty(0) || !isatty(1)) {
          823                 fprintf(stderr, "stdin or stdout is not a tty\n");
          824                 exit(1);
          825         }
          826 
          827         if (getuid() == 0)
          828                 showhidden = 1;
          829         initfilter(showhidden, &ifilter);
          830 
          831         if (argv[0] != NULL) {
          832                 ipath = argv[0];
          833         } else {
          834                 ipath = getcwd(cwd, sizeof(cwd));
          835                 if (ipath == NULL)
          836                         ipath = "/";
          837         }
          838 
          839         signal(SIGINT, SIG_IGN);
          840 
          841         /* Test initial path */
          842         if (canopendir(ipath) == 0) {
          843                 fprintf(stderr, "%s: %s\n", ipath, strerror(errno));
          844                 exit(1);
          845         }
          846 
          847         /* Set locale before curses setup */
          848         setlocale(LC_ALL, "");
          849         initcurses();
          850         browse(ipath, ifilter);
          851         exitcurses();
          852         exit(0);
          853 }