1 /* file.c by Adam Rogoyski <apoc@laker.net> Temperanc on EFNet irc
2  * Copyright (C) 1998, 1999 Adam Rogoyski
3  * --- GNU General Public License Disclamer ---
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  * This program is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11  * GNU General Public License for more details.
12  */
13 
14 #include "../config.h"
15 
16 #include <assert.h>
17 
18 #ifdef HAVE_DIRENT_H
19 #include <dirent.h>
20 #endif
21 
22 #include <sys/types.h>
23 #include <grp.h>
24 #include <pwd.h>
25 
26 #ifdef TM_IN_SYS_TIME
27 #include <sys/time.h>
28 #endif
29 
30 #include <time.h>
31 
32 #include "hexedit.h"
33 
34 struct FileNames *fp = NULL;     /* used to traverse the list of files */
35 struct FileNames **pages = NULL; /* node that starts a new page */
36 int current_page = 0;
37 int num_pages = 0;
38 
39 static void exit_p (int);
40 
41 static void
exit_p(int i)42 exit_p (int i)
43 {
44    endwin ();
45    exit (EXIT_SUCCESS);
46 }
47 
48 char *
fileSelect()49 fileSelect ()
50 {
51    char *p = NULL;
52    char *ret = NULL;
53    char dir[PATH_MAX + 1];
54    char previous_dir[PATH_MAX + 1];
55    char trunc_file[PATH_MAX + 1];
56    wchar_t in = 0;
57    int i = 0;
58    int result = 0;
59    struct FileNames *front = NULL;
60    struct stat filestats;
61    DIR *d = NULL;
62    struct sigaction sig_a, sig_old;
63 
64    sig_a.sa_handler = exit_p;
65    memset (&sig_a.sa_mask, 0x00, sizeof (sigset_t));
66    sig_a.sa_flags = 0;
67    sigaction (SIGINT, &sig_a, &sig_old);
68 
69    Globals.mode = FILE_MODE;
70 
71    memset (dir, 0x00, PATH_MAX + 1);
72    memset (previous_dir, 0x00, PATH_MAX + 1);
73 
74    getcwd (previous_dir, PATH_MAX);
75 
76    while (1)
77    {
78       int num = 0;
79       int rerr = 0;
80       int derr = 0;
81 
82       if (*dir)
83          strcpy (previous_dir, dir);
84       memset (dir, 0x00, PATH_MAX + 1);
85       p = getcwd (dir, PATH_MAX);
86       if (!p)
87          die_horribly (NULL, "getcwd");
88 
89 
90 
91       front = fp = getDirectory (dir);
92       if (!fp)
93       {
94          result = errno;
95          front = fp = getDirectory (previous_dir);
96          if (!fp)
97          {
98             errno = result;
99             die_horribly (NULL, previous_dir);
100          }
101          strcpy (dir, previous_dir);
102       }
103       while (fp)
104       {
105          fp = fp->p;
106          num++;
107       }
108       num_pages = ((num - 1) / (LINES - 2)) + 1;
109 
110       pages = malloc (sizeof (struct FileNames *) * num_pages);
111       memset (pages, 0x00, num_pages * sizeof (struct FileNames *));
112       *pages = front;
113 
114       fp = front;
115       for (i = 1; i < num_pages; i++)
116       {
117          int j = 0;
118          for (j = 0; (j < (LINES - 2)) && fp; j++)
119             fp = fp->p;
120          *(pages + i) = fp;
121       }
122       fp = front;
123 
124       current_page = 0;
125       cursor_y = MAIN_TOP_LINE;
126       werase (Globals.wstatus);
127       werase (Globals.wmain);
128       werase (Globals.whelp);
129       printPage (*pages);
130       strncpy (trunc_file, fp->filename, COLS - NAME_POS);
131       wmove (Globals.wmain, 0, NAME_POS);
132       wattrset (Globals.wmain, color_term ? COLOR_PAIR(2) | A_BOLD
133                                           : A_BOLD);
134       wprintw (Globals.wmain, "%s", trunc_file);
135       wattroff (Globals.wmain, color_term ? COLOR_PAIR(2) | A_BOLD
136                                           : A_BOLD);
137       statWindow (fp->filename);
138       helpWindow ("^C ^X  Exit   ^M  Select File   k  Up   j  Down");
139       wrefresh (Globals.wstatus);
140       wrefresh (Globals.wmain);
141       wrefresh (Globals.whelp);
142       move (cursor_y, NAME_POS);
143       refresh ();
144 
145       while ((in = getch ()) != '\n')
146       {
147          memset (trunc_file, 0x00, PATH_MAX + 1);
148          switch (in)
149          {
150             case CONTROL_C:
151             case CONTROL_X:
152                exit_p (0);
153 
154             case KEY_UP:
155             case 'k':
156                file_key_up (&current_page);
157                break;
158 
159             case KEY_DOWN:
160             case 'j':
161                file_key_down (&current_page, num_pages);
162                break;
163 
164             case CONTROL_L:
165                file_redraw (&current_page);
166                break;
167 
168             case KEY_F(5):
169                if (color_term != -1)
170                   color_term ^= 1;
171                file_redraw (&current_page);
172                break;
173 
174             case CONTROL_V:
175             case CONTROL_F:
176             case KEY_NPAGE:
177             case KEY_C3:
178             case ' ':
179                file_pagedown (&current_page, num_pages);
180                break;
181 
182             case CONTROL_Y:
183             case CONTROL_B:
184             case KEY_PPAGE:
185             case KEY_A3:
186             case '-':
187 #ifdef __PDCURSES__
188                case ALT_V:
189 #endif
190                /* Page up */
191                file_pageup (&current_page);
192                break;
193 
194             case KEY_HOME:
195                file_home (&current_page);
196                break;
197 
198             case KEY_END:
199                file_end (&current_page, num_pages);
200                break;
201 
202             case ESCAPE_CHARACTER:
203                in = getch ();
204 
205                switch (in)
206                {
207                   case 79:
208                      in = getch ();
209                      switch (in)
210                      {
211                         case 'A':
212                            file_key_up (&current_page);
213                            break;
214 
215                         case 'B':
216                            file_key_down (&current_page, num_pages);
217                            break;
218                      }
219                      break;
220 
221                   case 91:
222                      in = getch ();
223                      switch (in)
224                      {
225                         case 49: /* home */
226                            file_home (&current_page);
227                            break;
228 
229                         case 52: /* end */
230                            file_end (&current_page, num_pages);
231                            break;
232 
233                          case 53: /* page up */
234                             file_pageup (&current_page);
235                             break;
236 
237                          case 54: /* page down */
238                             file_pagedown (&current_page, num_pages);
239                             break;
240                       }
241                    getch ();  /* returns 126 */
242                }
243                break;
244 
245             default:
246                break;
247          }
248       }
249 
250       stat (fp->filename, &filestats);
251       if (!S_ISDIR (filestats.st_mode))
252          break;
253 
254       errno = 0;
255       d = opendir (fp->filename);
256       derr = errno;
257       closedir (d);
258       errno = 0;
259       result = chdir (fp->filename);
260       rerr = errno;
261       if (result || !d)
262       {
263 #define FILE_BOX_WIDTH  50
264 #define FILE_BOX_HEIGHT 6
265          WINDOW *wpopup = popupWindow (FILE_BOX_WIDTH, FILE_BOX_HEIGHT);
266          char pdir[PATH_MAX + 1];
267 
268          errno = derr ? derr : rerr;
269          werase (wpopup);
270          box (wpopup, 0, 0);
271          wmove (wpopup, 1, (FILE_BOX_WIDTH / 2)
272                  - (strlen ("Cannot Change Directory") / 2));
273          wprintw (wpopup, "Cannot Change Direcotry");
274          wmove (wpopup, FILE_BOX_HEIGHT - 3, (FILE_BOX_WIDTH / 2) -
275                 (strlen (strerror (errno)) + strlen ("Reason: ")) / 2);
276          wprintw (wpopup, "Reason: %s", strerror (errno));
277          wrefresh (wpopup);
278          getch ();
279          memset (pdir, 0x00, PATH_MAX + 1);
280          if (*previous_dir != '/' && *previous_dir != '.')
281             strcpy (pdir, "../");
282          strcat (pdir, previous_dir);
283          result = chdir (pdir);
284          if (result)
285             die_horribly ("Cannot change back to previous directory", pdir);
286       }
287       else
288       {
289          memset (dir, 0x00, PATH_MAX + 1);
290          strcpy (dir, fp->filename);
291       }
292 
293 
294       free (pages);
295       while (front)
296       {
297          fp = front->p;
298          free (front->filename);
299          free (front);
300          front = fp;
301       }
302 
303    }
304 
305       /* This needs to be freed when editing a new file. */
306    ret = malloc (strlen (fp->filename) + 1);
307    if (!ret)
308       die_horribly (NOT_ENOUGH_MEMORY, NULL);
309 
310    strcpy (ret, fp->filename);
311 
312    while (front)
313    {
314       fp = front->p;
315       free (front->filename);
316       free (front);
317       front = fp;
318    }
319    if (pages)
320       free (pages);
321 
322 /*   signal (SIGINT, sigp); */
323    sigaction (SIGINT, &sig_old, NULL);
324    return ret;
325 }
326 
327 
328 void
statWindow(const char * const filename)329 statWindow (const char * const filename)
330 {
331    int i = 0;
332    int l = 0;
333 
334    wattrset (Globals.wstatus, color_term ? COLOR_PAIR(4) | A_BOLD
335                                          : A_REVERSE);
336    wmove (Globals.wstatus, 0, 0);
337    wprintw (Globals.wstatus, "File: %s\n", filename);
338 
339    l = strlen (filename);
340    wmove (Globals.wstatus, 0, l + 6);
341    for (i = 0; i < (COLS - l - 6); i++)
342       wprintw (Globals.wstatus, " ");
343 
344 }
345 
346 
347 void
helpWindow(const char * const filename)348 helpWindow (const char * const filename)
349 {
350    wattrset (Globals.whelp, color_term ? COLOR_PAIR(3) | A_BOLD
351                                          : A_NORMAL);
352    wmove (Globals.whelp, 0, 0);
353    wprintw (Globals.whelp, "%s", filename);
354 }
355 
356 
357 struct FileNames *
getDirectory(const char * const dir)358 getDirectory (const char * const dir)
359 {
360    DIR *d = NULL;
361    int i = 0;
362    int num = 0;
363    struct dirent *dp = NULL;
364    struct FileNames *fnp = NULL;
365    struct FileNames *front = NULL;
366 
367    d = opendir (dir);
368    if (!d)
369    {
370       return NULL;
371    }
372 
373    while ((dp = readdir (d)))
374    {
375       if (!fnp)
376       {
377          front = fnp = malloc (sizeof (struct FileNames));
378          if (!fnp)
379             die_horribly (NOT_ENOUGH_MEMORY, NULL);
380       }
381       else
382       {
383          fnp->p = malloc (sizeof (struct FileNames));
384          if (!fnp->p)
385             die_horribly (NOT_ENOUGH_MEMORY, NULL);
386 
387          fnp = fnp->p;
388       }
389       fnp->filename = malloc (strlen (dp->d_name) + 1);
390       if (!fnp->filename)
391          die_horribly (NOT_ENOUGH_MEMORY, NULL);
392 
393       strcpy (fnp->filename, dp->d_name);
394       fnp->p = NULL;
395       num++;
396    }
397 
398    if (num > 1)
399       /* Split the list up in half for merge sort. */
400    {
401       fnp = front;
402       for (i = 0; i < (num / 2); i++)
403       {
404          assert (fnp && fnp->p);
405          if (i == ((num / 2) - 1))
406          {
407             struct FileNames *p = fnp;
408             fnp = fnp->p;
409             p->p = NULL;
410          }
411          else
412             fnp = fnp->p;
413       }
414       front = merge (msort (front), msort (fnp));
415    }
416 
417    closedir (d);
418 
419    return front;
420 }
421 
422 struct FileNames *
merge(struct FileNames * p1,struct FileNames * p2)423 merge (struct FileNames *p1, struct FileNames *p2)
424    /* Merge for merge sort of linked list of filenames. */
425 {
426    struct FileNames *files = NULL;
427    struct FileNames *front = NULL;
428 
429    while (p1 && p2)
430    {
431       if (!files)
432       {
433          if (strcmp (p1->filename, p2->filename) < 0)
434          {
435             files = p1;
436             p1 = p1->p;
437          }
438          else
439          {
440             files = p2;
441             p2 = p2->p;
442          }
443          front = files;
444       }
445       else
446       {
447          if (strcmp (p1->filename, p2->filename) < 0)
448          {
449             files->p = p1;
450             p1 = p1->p;
451          }
452          else
453          {
454             files->p = p2;
455             p2 = p2->p;
456          }
457          files = files->p;
458       }
459       files->p = NULL;
460    }
461    while (p1)
462    {
463       files->p = p1;
464       files = files->p;
465       p1 = p1->p;
466    }
467 
468    while (p2)
469    {
470       files->p = p2;
471       files = files->p;
472       p2 = p2->p;
473    }
474 
475    files->p = NULL;
476 
477    return front;
478 }
479 
480 
481 struct FileNames *
msort(struct FileNames * p)482 msort (struct FileNames *p)
483    /* The sort part of the merge sort. */
484 {
485    int i = 0;
486    int num = 0;
487    struct FileNames *fnp = p;
488 
489    if (!p)
490       return NULL;
491    if (!p->p)
492       return p;
493 
494    while (fnp)
495    {
496       fnp = fnp->p;
497       num++;
498    }
499 
500    fnp = p;
501    for (i = 0; i < (num / 2); i++)
502    {
503       assert (fnp && fnp->p);
504       if (i == ((num / 2) - 1))
505       {
506          struct FileNames *tp = fnp;
507          fnp = fnp->p;
508          tp->p = NULL;
509       }
510       else
511          fnp = fnp->p;
512    }
513 
514    return merge (msort (p), msort (fnp));
515 }
516 
517 
518 void
printPage(const struct FileNames * fp)519 printPage (const struct FileNames *fp)
520 {
521    int i = 0;
522    int result = 0;
523    char *bits[] = { "---", "--x", "-w-", "-wx",
524                     "r--", "r-x", "rw-", "rwx" };
525    char trunc_file[PATH_MAX + 1];
526    struct stat filestat;
527 
528 
529    if (color_term)
530       wattrset (Globals.wmain, COLOR_PAIR(1));
531 
532    for (i = 0; (i < (LINES - 2)) && fp; i++)
533    {
534       char modebits[11];
535       char s1[11];
536       char s2[11];
537       struct passwd *pw = NULL;
538       struct group  *gw = NULL;
539       struct tm *tp = NULL;
540 
541       memset (s1, 0x00, 11);
542       memset (s2, 0x00, 11);
543       result = stat (fp->filename, &filestat);
544       wmove (Globals.wmain, i, 0);
545       if (!result)
546          /* do the ls thingy with the file mode */
547       {
548          memset (modebits, 0x00, 11);
549          if (S_ISDIR (filestat.st_mode))
550             modebits[0] = 'd';
551          else if (S_ISCHR (filestat.st_mode))
552             modebits[0] = 'c';
553          else if (S_ISBLK (filestat.st_mode))
554             modebits[0] = 'b';
555          else if (S_ISFIFO (filestat.st_mode))
556             modebits[0] = 'p';
557 #ifndef __PDCURSES__
558          else if (S_ISSOCK (filestat.st_mode))
559             modebits[0] = 's';
560          else if (S_ISLNK (filestat.st_mode))
561             modebits[0] = 'l';
562 #endif
563          else
564             modebits[0] = '-';
565          strcat (modebits, bits[(filestat.st_mode & 0700) >> 6]);
566          strcat (modebits, bits[(filestat.st_mode & 0070) >> 3]);
567          strcat (modebits, bits[filestat.st_mode & 0007]);
568          if (filestat.st_mode & 01000)
569          {
570             if (filestat.st_mode & 0001)
571                modebits[9] = 't';
572             else
573                modebits[9] = 'T';
574          }
575          if (filestat.st_mode & 02000)
576          {
577             if (filestat.st_mode & 0010)
578                modebits[6] = 's';
579             else
580                modebits[6] = 'S';
581          }
582          if (filestat.st_mode & 01000)
583          {
584             if (filestat.st_mode & 0100)
585                modebits[3] = 's';
586             else
587                modebits[3] = 'S';
588          }
589          tp = localtime (&filestat.st_mtime);
590 
591          pw = getpwuid (filestat.st_uid);
592          gw = getgrgid (filestat.st_gid);
593 
594 #ifdef HAVE_SNPRINTF
595 
596          if (!pw)
597             snprintf (s1, 11, "%d", (int) filestat.st_uid);
598          else
599             snprintf (s1, 11, "%s", pw->pw_name);
600 
601          if (!gw)
602             snprintf (s2, 11, "%d", (int) filestat.st_gid);
603          else
604             snprintf (s2, 11, "%s", gw->gr_name);
605 
606 #else
607 
608          if (!pw)
609             sprintf (s1, "%d", (int) filestat.st_uid);
610          else
611             sprintf (s1, "%s", pw->pw_name);
612 
613          if (!gw)
614             sprintf (s2, "%d", (int) filestat.st_gid);
615          else
616             sprintf (s2, "%s", gw->gr_name);
617 
618 #endif
619 
620          wprintw (Globals.wmain,
621    /* why does it seem like on OpenBSD this is broken? */
622 #if defined(__OpenBSD__) || defined(BROKEN)
623             "%s %-8s %-8s %10ld %1d\b2d-%02d-%02d %02d:%02d ",
624 #else
625             "%s %-8s %-8s %10ld %d-%02d-%02d %02d:%02d ",
626 #endif
627             modebits,
628             s1,
629             s2,
630             filestat.st_size,
631             1900 + tp->tm_year, 1 + tp->tm_mon, tp->tm_mday,
632             tp->tm_hour, tp->tm_min);
633       }
634          /* stat failed, print enough spaces to align file anyways */
635       else
636       {
637          int j = 0;
638          for (j = 0; j < 57; j++)
639             wprintw (Globals.wmain, " ");
640       }
641 
642       memset (trunc_file, 0x00, PATH_MAX + 1);
643       strncpy (trunc_file, fp->filename, COLS - NAME_POS);
644       wprintw (Globals.wmain, "%s", trunc_file);
645       fp = fp->p;
646 
647       for (result = Globals.wmain->_curx; result < COLS; result++)
648          wprintw (Globals.wmain, " ");
649    }
650 
651       /* Fill in rest of the screen with default color. */
652    for (; i < (LINES - 2); i++)
653       for (result = 0; result < COLS; result++)
654          wprintw (Globals.wmain, " ");
655 }
656