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 (¤t_page);
157 break;
158
159 case KEY_DOWN:
160 case 'j':
161 file_key_down (¤t_page, num_pages);
162 break;
163
164 case CONTROL_L:
165 file_redraw (¤t_page);
166 break;
167
168 case KEY_F(5):
169 if (color_term != -1)
170 color_term ^= 1;
171 file_redraw (¤t_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 (¤t_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 (¤t_page);
192 break;
193
194 case KEY_HOME:
195 file_home (¤t_page);
196 break;
197
198 case KEY_END:
199 file_end (¤t_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 (¤t_page);
213 break;
214
215 case 'B':
216 file_key_down (¤t_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 (¤t_page);
227 break;
228
229 case 52: /* end */
230 file_end (¤t_page, num_pages);
231 break;
232
233 case 53: /* page up */
234 file_pageup (¤t_page);
235 break;
236
237 case 54: /* page down */
238 file_pagedown (¤t_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