1 #if !defined(lint) && !defined(DOS)
2 static char rcsid[] = "$Id: browse.c 942 2008-03-04 18:21:33Z hubert@u.washington.edu $";
3 #endif
4
5 /*
6 * ========================================================================
7 * Copyright 2006-2008 University of Washington
8 * Copyright 2013-2021 Eduardo Chappa
9 *
10 * Licensed under the Apache License, Version 2.0 (the "License");
11 * you may not use this file except in compliance with the License.
12 * You may obtain a copy of the License at
13 *
14 * http://www.apache.org/licenses/LICENSE-2.0
15 *
16 * ========================================================================
17 *
18 * Program: Routines to support file browser in pico and Pine composer
19 *
20 *
21 * NOTES:
22 *
23 * Misc. thoughts (mss, 5 Apr 92)
24 *
25 * This is supposed to be just a general purpose browser, equally
26 * callable from either pico or the pine composer. Someday, it could
27 * even be used to "wrap" the unix file business for really novice
28 * users. The stubs are here for renaming, copying, creating directories,
29 * deleting, undeleting (thought is delete just moves files to
30 * ~/.pico.deleted directory or something and undelete offers the
31 * files in there for undeletion: kind of like the mac trashcan).
32 *
33 * Nice side effects
34 *
35 * Since the full path name is always maintained and referencing ".."
36 * stats the path stripped of its trailing name, the unpleasantness of
37 * symbolic links is hidden.
38 *
39 * Fleshed out the file managements stuff (mss, 24 June 92)
40 *
41 *
42 */
43 #include "headers.h"
44 #include "../c-client/mail.h"
45 #include "../c-client/rfc822.h"
46 #include "../pith/osdep/collate.h"
47 #include "../pith/charconv/filesys.h"
48 #include "../pith/conf.h"
49
50 #ifndef _WINDOWS
51
52 #if defined(bsd) || defined(lnx)
53 extern int errno;
54 #endif
55
56
57 /*
58 * directory cell structure
59 */
60 struct fcell {
61 char *fname; /* file name */
62 unsigned mode; /* file's mode */
63 char size[16]; /* file's size in s */
64 struct fcell *next;
65 struct fcell *prev;
66 };
67
68
69 /*
70 * master browser structure
71 */
72 static struct bmaster {
73 struct fcell *head; /* first cell in list */
74 struct fcell *bottom; /* last cell in list */
75 struct fcell *top; /* cell in top left */
76 struct fcell *current; /* currently selected */
77 int longest; /* longest file name (in screen width) */
78 int fpl; /* file names per line */
79 int cpf; /* chars / file / line */
80 int menu; /* current menu to display */
81 int flags;
82 char dname[NLINE]; /* this dir's name (UTF-8) */
83 char *names; /* malloc'd name array (UTF-8) */
84 LMLIST *lm;
85 } *gmp; /* global master ptr */
86
87
88 /*
89 * title for exported browser display
90 */
91 static char *browser_title = NULL;
92
93
94 struct bmaster *getfcells(char *, int);
95 void PaintCell(int, int, int, struct fcell *, int);
96 void PaintBrowser(struct bmaster *, int, int *, int *);
97 struct bmaster *RepaintBrowser(struct bmaster *, int);
98 void BrowserKeys(void);
99 void layoutcells(struct bmaster *);
100 void percdircells(struct bmaster *);
101 int PlaceCell(struct bmaster *, struct fcell *, int *, int *);
102 void zotfcells(struct fcell *);
103 void zotmaster(struct bmaster **);
104 struct fcell *FindCell(struct bmaster *, char *, int);
105 int sisin(char *, char *);
106 void p_chdir(struct bmaster *);
107 void BrowserAnchor(char *);
108 void ClearBrowserScreen(void);
109 void BrowserRunChild(char *, char *);
110 int fcell_is_selected(struct fcell *, struct bmaster *);
111 void add_cell_to_lmlist(struct fcell *, struct bmaster *);
112 void del_cell_from_lmlist(struct fcell *, struct bmaster *);
113
114
115 #define HELP_MENU {"?", N_("Get Help"), KS_SCREENHELP}
116 #define OTHER_MENU {"O", N_("OTHER CMDS"), KS_NONE}
117 static KEYMENU menu_browse[] = {
118 HELP_MENU, {NULL, NULL, KS_NONE},
119 {NULL, NULL, KS_NONE}, {"-", N_("Prev Pg"), KS_PREVPAGE},
120 {"D", N_("Delete"), KS_NONE}, {"C",N_("Copy"), KS_NONE},
121 OTHER_MENU, {NULL, NULL, KS_NONE},
122 {"W", N_("Where is"), KS_NONE}, {"Spc", N_("Next Pg"), KS_NEXTPAGE},
123 {"R", N_("Rename"), KS_NONE}, {NULL, NULL, KS_NONE}
124 };
125 static KEYMENU menu_other[] = {
126 HELP_MENU, {NULL, NULL, KS_NONE},
127 {NULL, NULL, KS_NONE}, {NULL, NULL, KS_NONE},
128 {NULL, NULL, KS_NONE}, {"1", N_("One Col"), KS_NONE},
129 OTHER_MENU, {NULL, NULL, KS_NONE},
130 {NULL, NULL, KS_NONE}, {NULL, NULL, KS_NONE},
131 {NULL, NULL, KS_NONE}, {".", N_("Dot files"), KS_NONE}
132 };
133 #define QUIT_KEY 1
134 #define EXEC_KEY 2
135 #define GOTO_KEY 10
136 #define SELECT_KEY 7
137 #define PICO_KEY 11
138
139
140 #define DIRWORD "dir"
141 #define PARENTDIR "parent"
142 #define SELECTWORD "SELECT"
143
144
145 /*
146 * Default pager used by the stand-alone file browser.
147 */
148 #define BROWSER_PAGER ((gmode & MDFKEY) ? "alpine -k -F" : "alpine -F")
149
150
151 /*
152 * function key mappings for callable browser
153 */
154 static UCS bfmappings[2][12][2] = { { { F1, '?'}, /* stand alone */
155 { F2, NODATA }, /* browser function */
156 { F3, 'q'}, /* key mappings... */
157 { F4, 'v'},
158 { F5, 'l'},
159 { F6, 'w'},
160 { F7, '-'},
161 { F8, ' '},
162 { F9, 'd'},
163 { F10, 'r'},
164 { F11, 'c'},
165 { F12, 'e'} },
166 { { F1, '?'}, /* callable browser */
167 { F2, NODATA }, /* function key */
168 { F3, 'e'}, /* mappings... */
169 { F4, 's'},
170 { F5, NODATA },
171 { F6, 'w'},
172 { F7, '-'},
173 { F8, ' '},
174 { F9, 'd'},
175 { F10, 'r'},
176 { F11, 'c'},
177 { F12, 'a'} } };
178
179
180 /*
181 * Browser help for pico (pine composer help handed to us by pine)
182 */
183 static char *BrowseHelpText[] = {
184 /* TRANSLATORS: The next several lines go together. The ~ characters
185 should be left in front of the characters they cause to be bold. */
186 N_("Help for Browse Command"),
187 " ",
188 N_(" Pico's file browser is used to select a file from the"),
189 N_(" file system for inclusion in the edited text."),
190 " ",
191 N_("~ Both directories and files are displayed. Press ~S"),
192 N_("~ or ~R~e~t~u~r~n to select a file or directory. When a file"),
193 N_(" is selected during the \"Read File\" command, it is"),
194 N_(" inserted into edited text. Answering \"yes\" to the"),
195 N_(" verification question after a directory is selected causes"),
196 N_(" the contents of that directory to be displayed for selection."),
197 " ",
198 N_(" The file named \"..\" is special, and means the \"parent\""),
199 N_(" of the directory being displayed. Select this directory"),
200 N_(" to move upward in the directory tree."),
201 " ",
202 N_("End of Browser Help."),
203 " ",
204 NULL
205 };
206
207 /*
208 * Help for standalone browser (pilot)
209 */
210 static char *sa_BrowseHelpText[] = {
211 /* TRANSLATORS: Some more help text */
212 N_("Help for Pilot (PIne's Looker-upper Of Things"),
213 " ",
214 N_(" Pilot is a simple, display-oriented file system browser based on the"),
215 N_("~ Alpine message system composer. As with Alpine, commands are displayed at"),
216 N_("~ the bottom of the screen, and context-sensitive help is provided."),
217 " ",
218 N_("~ Pilot displays the current working directory at the top of the screen."),
219 N_("~ The directory's contents are displayed in columns of file name, file"),
220 N_("~ size pairs. Names that are directories are indicated by the name"),
221 N_("~ ~(~d~i~r~) in place of the file size. The parent of the current working"),
222 N_("~ directory is indicated by the file name ~.~. and size of ~(~p~a~r~e~n~t~ ~d~i~r~)."),
223 N_("~ File names that are symbolic links to other files are displayed with a"),
224 N_("~ file size of ~-~-."),
225 " ",
226 N_(" The following function keys are available in Pilot:"),
227 " ",
228 N_("~ ~? Display this help text."),
229 N_("~ ~Q Quit Pilot."),
230 " ",
231 N_("~ ~V View the currently selected file or open the selected directory."),
232 N_("~ Note: Pilot invokes ~a~l~p~i~n~e ~-~F, or the program defined by the ~P~A~G~E~R"),
233 N_("~ environment variable, to view the file."),
234 N_("~ ~L Launch an external application program."),
235 " ",
236 N_("~ ~W Search for a file by name."),
237 N_("~ ~- Scroll up one page."),
238 N_("~ ~S~p~a~c~e Scroll down one page."),
239 N_("~ ~N,~^~F Move forward (right) one column."),
240 N_("~ ~P,~^~B Move back (left) one column."),
241 N_("~ ~^~N Move down one row."),
242 N_("~ ~^~P Move up one row."),
243 N_("~ ~. Switch show dot files."),
244 N_("~ ~1 Switch single column display."),
245 " ",
246 N_("~ ~D Delete the selected file."),
247 N_("~ ~R Rename the selected file or directory."),
248 N_("~ ~C Copy the selected file."),
249 N_("~ ~E Edit the selected file."),
250 N_("~ Note: Pilot invokes ~p~i~c~o, or the program defined by the ~E~D~I~T~O~R"),
251 N_("~ environment variable, to edit the file."),
252 " ",
253 N_("End of Pilot Help."),
254 NULL
255 };
256
257
258
259 /*
260 * pico_file_browse - Exported version of FileBrowse below.
261 */
262 int
pico_file_browse(PICO * pdata,char * dir,size_t dirlen,char * fn,size_t fnlen,char * sz,size_t szlen,int flags)263 pico_file_browse(PICO *pdata, char *dir, size_t dirlen, char *fn, size_t fnlen,
264 char *sz, size_t szlen, int flags)
265 {
266 int rv;
267 char title_buf[64];
268
269 Pmaster = pdata;
270 gmode = pdata->pine_flags | MDEXTFB;
271 km_popped = 0;
272
273 /* only init screen bufs for display and winch handler */
274 if(!vtinit())
275 return(-1);
276
277 if(Pmaster){
278 term.t_mrow = Pmaster->menu_rows;
279 if(Pmaster->oper_dir)
280 strncpy(opertree, Pmaster->oper_dir, NLINE);
281
282 if(*opertree)
283 fixpath(opertree, sizeof(opertree));
284 }
285
286 /* install any necessary winch signal handler */
287 ttresize();
288
289 snprintf(title_buf, sizeof(title_buf), "%s FILE", pdata->pine_anchor);
290 set_browser_title(title_buf);
291 rv = FileBrowse(dir, dirlen, fn, fnlen, sz, szlen, flags, NULL);
292 set_browser_title(NULL);
293 vttidy(); /* clean up tty handling */
294 zotdisplay(); /* and display structures */
295 Pmaster = NULL; /* and global config structure */
296 return(rv);
297 }
298
299
300
301 /*
302 * FileBrowse - display contents of given directory dir
303 *
304 * input:
305 * dir points to initial dir to browse.
306 * dirlen- buffer size of dir
307 * fn initial file name.
308 * fnlen- buffer size of fn
309 *
310 * returns:
311 * dir points to currently selected directory (without
312 * trailing file system delimiter)
313 * fn points to currently selected file
314 * sz points to size of file if ptr passed was non-NULL
315 * flags
316 *
317 * Special dispensation for FB_LMODE. If the caller sets
318 * FB_LMODEPOS, and the caller passes a non-null lmreturn,
319 * then the return values will be in lmreturn instead of
320 * in dir and fn. The caller is responsible for freeing
321 * the contents of lmreturn.
322 *
323 * 1 if a file's been selected
324 * 0 if no files selected
325 * -1 if there were problems
326 */
327 int
FileBrowse(char * dir,size_t dirlen,char * fn,size_t fnlen,char * sz,size_t szlen,int fb_flags,LMLIST ** lmreturn)328 FileBrowse(char *dir, size_t dirlen, char *fn, size_t fnlen,
329 char *sz, size_t szlen, int fb_flags, LMLIST **lmreturn)
330 {
331 UCS c, new_c;
332 int status, i, j;
333 int row, col, crow, ccol;
334 int flags;
335 int add_file;
336 char *p, *envp, child[2*NLINE+2], tmp[2*NLINE+1];
337 struct bmaster *mp;
338 struct fcell *tp;
339 EML eml;
340 #ifdef MOUSE
341 MOUSEPRESS mousep;
342 #endif
343
344 child[0] = '\0';
345
346 if((gmode&MDTREE) && !in_oper_tree(dir)){
347 eml.s = opertree;
348 emlwwrite(_("Can't read outside of %s in restricted mode"), &eml);
349 sleep(2);
350 return(0);
351 }
352
353 if(gmode&MDGOTO){
354 /* fix up function key mapping table */
355 /* fix up key menu labels */
356 }
357
358 /* build contents of cell structures */
359 if((gmp = getfcells(dir, fb_flags)) == NULL)
360 return(-1);
361
362 tp = NULL;
363 if(fn && *fn){
364 if((tp = FindCell(gmp, fn, 0)) != NULL){
365 gmp->current = tp;
366 PlaceCell(gmp, gmp->current, &row, &col);
367 }
368 }
369
370 /* paint screen */
371 PaintBrowser(gmp, 0, &crow, &ccol);
372
373 while(1){ /* the big loop */
374 if(!(gmode&MDSHOCUR)){
375 crow = term.t_nrow-term.t_mrow;
376 ccol = 0;
377 }
378
379 if(!(gmode&MDSHOCUR))
380 movecursor(crow, ccol);
381 else if(gmp->flags & FB_LMODEPOS){
382 if(gmp->flags & FB_LMODE && gmp->current->mode != FIODIR)
383 movecursor(crow, ccol+1);
384 else
385 movecursor(crow, ccol+4);
386 }
387 else
388 movecursor(crow, ccol);
389
390 if(km_popped){
391 km_popped--;
392 if(km_popped == 0)
393 /* cause bottom three lines to repaint */
394 PaintBrowser(gmp, 0, &crow, &ccol);
395 }
396
397 if(km_popped){ /* temporarily change to cause menu to paint */
398 term.t_mrow = 2;
399 movecursor(term.t_nrow-2, 0); /* clear status line */
400 peeol();
401 BrowserKeys();
402 term.t_mrow = 0;
403 }
404
405 (*term.t_flush)();
406
407 #ifdef MOUSE
408 mouse_in_content(KEY_MOUSE, -1, -1, 0, 0);
409 register_mfunc(mouse_in_content,2,0,term.t_nrow-(term.t_mrow+1),
410 term.t_ncol);
411 #endif
412 c = GetKey();
413 #ifdef MOUSE
414 clear_mfunc(mouse_in_content);
415 #endif
416
417 if(Pmaster){
418 if(Pmaster->newmail && (c == NODATA || time_to_check())){
419 if((*Pmaster->newmail)(c == NODATA ? 0 : 2, 1) >= 0){
420 int rv;
421
422 if(km_popped){ /* restore display */
423 km_popped = 0;
424 PaintBrowser(gmp, 0, &crow, &ccol);
425 }
426
427 clearcursor();
428 mlerase();
429 rv = (*Pmaster->showmsg)(c);
430 ttresize();
431 picosigs();
432 if(rv) /* Did showmsg corrupt the display? */
433 PaintBrowser(gmp, 0, &crow, &ccol); /* Yes, repaint */
434
435 mpresf = 1;
436 }
437
438 clearcursor();
439 if(gmp->flags & FB_LMODEPOS){
440 if(gmp->flags & FB_LMODE && gmp->current->mode != FIODIR)
441 movecursor(crow, ccol+1);
442 else
443 movecursor(crow, ccol+4);
444 }
445 else
446 movecursor(crow, ccol);
447 }
448 }
449 else{
450 if(get_input_timeout() && (c == NODATA || time_to_check()))
451 if(pico_new_mail())
452 emlwrite(_("You may possibly have new mail."), NULL);
453 }
454
455 if(km_popped)
456 switch(c){
457 case NODATA:
458 case (CTRL|'L'):
459 km_popped++;
460 break;
461
462 default:
463 /* clear bottom three lines */
464 movecursor(term.t_nrow-2, 0);
465 peeol();
466 movecursor(term.t_nrow-1, 0);
467 peeol();
468 movecursor(term.t_nrow, 0);
469 peeol();
470 break;
471 }
472
473 if(c == NODATA) /* GetKey timed out */
474 continue;
475 else if(Pmaster)
476 (*Pmaster->keybinput)();
477
478
479 if(mpresf){ /* blast old messages */
480 if(mpresf++ > MESSDELAY){ /* every few keystrokes */
481 mlerase();
482 }
483 }
484
485 /* process commands */
486 switch(new_c = normalize_cmd(c,bfmappings[(gmode&MDBRONLY)?0:1],2)){
487
488 case KEY_RIGHT: /* move right */
489 case (CTRL|'@'):
490 case (CTRL|'F'): /* forward */
491 case 'n' :
492 case 'N' :
493 if(gmp->current->next == NULL){
494 (*term.t_beep)();
495 break;
496 }
497
498 PlaceCell(gmp, gmp->current, &row, &col);
499 PaintCell(row, col, gmp->cpf, gmp->current, 0);
500 gmp->current = gmp->current->next;
501 if(PlaceCell(gmp, gmp->current, &row, &col)){
502 PaintBrowser(gmp, 1, &crow, &ccol);
503 }
504 else{
505 PaintCell(row, col, gmp->cpf, gmp->current, 1);
506 crow = row;
507 ccol = col;
508 }
509 break;
510
511 case KEY_LEFT: /* move left */
512 case (CTRL|'B'): /* back */
513 case 'p' :
514 case 'P' :
515 if(gmp->current->prev == NULL){
516 (*term.t_beep)();
517 break;
518 }
519
520 PlaceCell(gmp, gmp->current, &row, &col);
521 PaintCell(row, col, gmp->cpf, gmp->current, 0);
522 gmp->current = gmp->current->prev;
523 if(PlaceCell(gmp, gmp->current, &row, &col)){
524 PaintBrowser(gmp, 1, &crow, &ccol);
525 }
526 else{
527 PaintCell(row, col, gmp->cpf, gmp->current, 1);
528 crow = row;
529 ccol = col;
530 }
531 break;
532
533 case (CTRL|'A'): /* beginning of line */
534 tp = gmp->current;
535 i = col;
536 while(i > 0){
537 i -= gmp->cpf;
538 if(tp->prev != NULL)
539 tp = tp->prev;
540 }
541 PlaceCell(gmp, gmp->current, &row, &col);
542 PaintCell(row, col, gmp->cpf, gmp->current, 0);
543 gmp->current = tp;
544 if(PlaceCell(gmp, tp, &row, &col)){
545 PaintBrowser(gmp, 1, &crow, &ccol);
546 }
547 else{
548 PaintCell(row, col, gmp->cpf, gmp->current, 1);
549 crow = row;
550 ccol = col;
551 }
552 break;
553
554 case (CTRL|'E'): /* end of line */
555 tp = gmp->current;
556 i = col + gmp->cpf;
557 while(i+gmp->cpf <= gmp->cpf * gmp->fpl){
558 i += gmp->cpf;
559 if(tp->next != NULL)
560 tp = tp->next;
561 }
562
563 PlaceCell(gmp, gmp->current, &row, &col);
564 PaintCell(row, col, gmp->cpf, gmp->current, 0);
565 gmp->current = tp;
566 if(PlaceCell(gmp, tp, &row, &col)){
567 PaintBrowser(gmp, 1, &crow, &ccol);
568 }
569 else{
570 PaintCell(row, col, gmp->cpf, gmp->current, 1);
571 crow = row;
572 ccol = col;
573 }
574 break;
575
576 case (CTRL|'V'): /* page forward */
577 case ' ':
578 case KEY_PGDN :
579 case KEY_END :
580 tp = gmp->top;
581 i = term.t_nrow - term.t_mrow - 2;
582
583 while(i-- && tp->next != NULL){
584 j = 0;
585 while(++j <= gmp->fpl && tp->next != NULL)
586 tp = tp->next;
587 }
588
589 if(tp == NULL)
590 continue;
591
592 PlaceCell(gmp, gmp->current, &row, &col);
593 PaintCell(row, col, gmp->cpf, gmp->current, 0);
594 gmp->current = tp;
595 if(PlaceCell(gmp, tp, &row, &col)){
596 PaintBrowser(gmp, 1, &crow, &ccol);
597 }
598 else{
599 PaintCell(row, col, gmp->cpf, gmp->current, 1);
600 crow = row;
601 ccol = col;
602 }
603 break;
604
605 case '-' :
606 case (CTRL|'Y'): /* page backward */
607 case KEY_PGUP :
608 case KEY_HOME :
609 tp = gmp->top;
610 i = term.t_nrow - term.t_mrow - 4;
611 while(i-- && tp != NULL){
612 j = gmp->fpl;
613 while(j-- && tp != NULL)
614 tp = tp->prev;
615 }
616
617 if(tp || (gmp->current != gmp->top)){ /* clear old hilite */
618 PlaceCell(gmp, gmp->current, &row, &col);
619 PaintCell(row, col, gmp->cpf, gmp->current, 0);
620 }
621
622 if(tp) /* new page ! */
623 gmp->current = tp;
624 else if(gmp->current != gmp->top) /* goto top of page */
625 gmp->current = gmp->top;
626 else /* do nothing */
627 continue;
628
629 if(PlaceCell(gmp, gmp->current, &row, &col)){
630 PaintBrowser(gmp, 1, &crow, &ccol);
631 }
632 else{
633 PaintCell(row, col, gmp->cpf, gmp->current, 1);
634 crow = row;
635 ccol = col;
636 }
637
638 break;
639
640 case KEY_DOWN :
641 case (CTRL|'N'): /* next */
642 tp = gmp->current;
643 i = gmp->fpl;
644 while(i--){
645 if(tp->next == NULL){
646 (*term.t_beep)();
647 break;
648 }
649 else
650 tp = tp->next;
651 }
652 if(i != -1) /* can't go down */
653 break;
654
655 PlaceCell(gmp, gmp->current, &row, &col);
656 PaintCell(row, col, gmp->cpf, gmp->current, 0);
657 gmp->current = tp;
658 if(PlaceCell(gmp, tp, &row, &col)){
659 PaintBrowser(gmp, 1, &crow, &ccol);
660 }
661 else{
662 PaintCell(row, col, gmp->cpf, gmp->current, 1);
663 crow = row;
664 ccol = col;
665 }
666 break;
667
668 case KEY_UP :
669 case (CTRL|'P'): /* previous */
670 tp = gmp->current;
671 i = gmp->fpl;
672 while(i-- && tp)
673 tp = tp->prev;
674
675 if(tp == NULL)
676 break;
677
678 PlaceCell(gmp, gmp->current, &row, &col);
679 PaintCell(row, col, gmp->cpf, gmp->current, 0);
680 gmp->current = tp;
681 if(PlaceCell(gmp, tp, &row, &col)){
682 PaintBrowser(gmp, 1, &crow, &ccol);
683 }
684 else{
685 PaintCell(row, col, gmp->cpf, gmp->current, 1);
686 crow = row;
687 ccol = col;
688 }
689 break;
690
691 #ifdef MOUSE
692 case KEY_MOUSE:
693 mouse_get_last (NULL, &mousep);
694 if (mousep.doubleclick) {
695 goto Selected;
696 }
697 else {
698 row = mousep.row -= 2; /* Adjust for header*/
699 col = mousep.col;
700 i = row * gmp->fpl + (col / gmp->cpf); /* Count from top */
701 tp = gmp->top; /* start at top. */
702 for (; i > 0 && tp != NULL; --i) /* Count cells. */
703 tp = tp->next;
704 if (tp != NULL) { /* Valid cell? */
705 PlaceCell(gmp, gmp->current, &row, &col);
706 PaintCell(row, col, gmp->cpf, gmp->current, 0);
707 gmp->current = tp;
708 if(PlaceCell(gmp, tp, &row, &col)){
709 PaintBrowser(gmp, 1, &crow, &ccol);
710 }
711 else{
712 PaintCell(row, col, gmp->cpf, gmp->current, 1);
713 crow = row;
714 ccol = col;
715 }
716 }
717 }
718 break;
719 #endif
720
721 case 'e': /* exit or edit */
722 case 'E':
723 if(gmode&MDBRONLY){ /* run "pico" */
724 char *t;
725 snprintf(child, sizeof(child), "%.*s%c%.*s", NLINE, gmp->dname, C_FILESEP,
726 NLINE, gmp->current->fname);
727 /* make sure selected isn't a directory or executable */
728 if(!LikelyASCII(child)){
729 emlwrite(_("Can't edit non-text file. Try Launch."), NULL);
730 break;
731 }
732
733 if((envp = (char *) getenv("EDITOR")) != NULL){
734 t = fs_get(strlen(envp) + strlen(child) + 3 + 1);
735 sprintf(t, "%s \'%s\'", envp, child);
736 }
737 else{
738 t = fs_get(strlen(child) + 16 + 1);
739 sprintf(t, "pico%s%s%s \'%s\'",
740 (gmode & MDFKEY) ? " -f" : "",
741 (gmode & MDSHOCUR) ? " -g" : "",
742 (gmode & MDMOUSE) ? " -m" : "",
743 child);
744 }
745
746 BrowserRunChild(t, gmp->dname); /* spawn pico */
747 PaintBrowser(gmp, 0, &crow, &ccol); /* redraw browser */
748 if(t) fs_give((void **) &t);
749 }
750 else{
751 zotmaster(&gmp);
752 return(0);
753 }
754
755 break;
756
757 case 'q': /* user exits wrong */
758 case 'Q':
759 if(gmode&MDBRONLY){
760 zotmaster(&gmp);
761 return(0);
762 }
763
764 unknown_command(c);
765 break;
766
767 case 'x': /* toggle selection */
768 case 'X':
769 if(!(gmp->flags & FB_LMODE)){
770 if(gmp->flags & FB_LMODEPOS)
771 emlwwrite(_("Type L command to use ListMode"), NULL);
772 else
773 unknown_command(c);
774
775 break;
776 }
777
778 if(gmp->current->mode == FIODIR){
779 emlwwrite(_("Can't Set directories"), NULL);
780 break;
781 }
782
783 if(fcell_is_selected(gmp->current, gmp))
784 del_cell_from_lmlist(gmp->current, gmp);
785 else
786 add_cell_to_lmlist(gmp->current, gmp);
787
788 PlaceCell(gmp, gmp->current, &row, &col);
789 PaintCell(row, col, gmp->cpf, gmp->current, 1);
790 break;
791
792 case 'l': /* run Command */
793 case 'L': /* or ListMode */
794 if(gmp->flags & FB_LMODEPOS){
795 if(gmp->flags & FB_LMODE){
796 /*
797 * Unless we make it so you can get out of ListMode
798 * once you're in ListMode, this must be an error.
799 */
800 emlwwrite(_("Already in ListMode"), NULL);
801 break;
802 }
803 else{
804 gmp->flags |= FB_LMODE;
805 PaintBrowser(gmp, 0, &crow, &ccol);
806 }
807
808 break;
809 }
810
811 if(!(gmode&MDBRONLY)){
812 unknown_command(c);
813 break;
814 }
815
816 /* add subcommands to invoke pico and insert selected filename */
817 /* perhaps: add subcmd to scroll command history */
818
819 tmp[0] = '\0';
820 i = 0;
821 snprintf(child, sizeof(child), "%.*s%c%.*s", NLINE, gmp->dname, C_FILESEP,
822 NLINE, gmp->current->fname);
823 while(!i){
824 static EXTRAKEYS opts[] = {
825 {"^X", N_("Add Name"), CTRL|'X', KS_NONE},
826 {NULL, NULL, 0, KS_NONE},
827 {NULL, NULL, 0, KS_NONE},
828 {NULL, NULL, 0, KS_NONE},
829 {NULL, NULL, 0, KS_NONE},
830 {NULL, NULL, 0, KS_NONE},
831 {NULL, NULL, 0, KS_NONE},
832 {NULL, NULL, 0, KS_NONE},
833 {NULL, NULL, 0, KS_NONE},
834 {NULL, NULL, 0, KS_NONE}
835 };
836
837 status = mlreply_utf8(_("Command to execute: "),
838 tmp, NLINE, QNORML, opts);
839 switch(status){
840 case HELPCH:
841 emlwwrite(_("No help yet!"), NULL);
842 /* remove break and sleep after help text is installed */
843 sleep(3);
844 break;
845 case (CTRL|'X'):
846 strncat(tmp, child, sizeof(tmp)-strlen(tmp)-1);
847 tmp[sizeof(tmp)-1] = '\0';
848 break;
849 case (CTRL|'L'):
850 PaintBrowser(gmp, 0, &crow, &ccol);
851 break;
852 case ABORT:
853 emlwrite(_("Command cancelled"), NULL);
854 i++;
855 break;
856 case FALSE:
857 case TRUE:
858 i++;
859
860 if(tmp[0] == '\0'){
861 emlwrite(_("No command specified"), NULL);
862 break;
863 }
864
865 BrowserRunChild(tmp, gmp->dname);
866 PaintBrowser(gmp, 0, &crow, &ccol);
867 break;
868 default:
869 break;
870 }
871 }
872
873 BrowserKeys();
874 break;
875
876 case 'd': /* delete */
877 case 'D':
878 if(gmp->current->mode == FIODIR){
879 /* BUG: if dir is empty it should be deleted */
880 emlwwrite(_("Can't delete a directory"), NULL);
881 break;
882 }
883
884 if(gmode&MDSCUR){ /* not allowed! */
885 emlwrite(_("Delete not allowed in restricted mode"),NULL);
886 break;
887 }
888
889 snprintf(child, sizeof(child), "%s%c%s", gmp->dname, C_FILESEP,
890 gmp->current->fname);
891
892 i = 0;
893 while(i++ < 2){ /* verify twice!! */
894 if(i == 1){
895 if(fexist(child, "w", (off_t *)NULL) != FIOSUC){
896 strncpy(tmp, _("File is write protected! OVERRIDE"), sizeof(tmp));
897 tmp[sizeof(tmp)-1] = '\0';
898 }
899 else
900 /* TRANSLATORS: This is a question, Delete file <filename> */
901 snprintf(tmp, sizeof(tmp), _("Delete file \"%.*s\""), NLINE - 20, child);
902 }
903 else{
904 strncpy(tmp, _("File CANNOT be UNdeleted! Really delete"), sizeof(tmp));
905 tmp[sizeof(tmp)-1] = '\0';
906 }
907
908 if((status = mlyesno_utf8(tmp, FALSE)) != TRUE){
909 emlwrite((status == ABORT)
910 ? _("Delete Cancelled")
911 : _("File Not Deleted"),
912 NULL);
913 break;
914 }
915 }
916
917 if(status == TRUE){
918 if(our_unlink(child) < 0){
919 eml.s = errstr(errno);
920 emlwrite(_("Delete Failed: %s"), &eml);
921 }
922 else{ /* fix up pointers and redraw */
923 tp = gmp->current;
924 if(tp->next){
925 gmp->current = tp->next;
926 if((tp->next->prev = tp->prev) != NULL)
927 tp->prev->next = tp->next;
928 }
929 else if(tp->prev) {
930 gmp->current = tp->prev;
931 if((tp->prev->next = tp->next) != NULL)
932 tp->next->prev = tp->prev;
933 }
934
935 if(tp == gmp->head)
936 gmp->head = tp->next;
937
938 if(tp == gmp->bottom)
939 gmp->bottom = tp->prev;
940
941 tp->fname = NULL;
942 tp->next = tp->prev = NULL;
943 if(tp != gmp->current)
944 free((char *) tp);
945
946 if((tp = FindCell(gmp, gmp->current->fname, 0)) != NULL){
947 gmp->current = tp;
948 PlaceCell(gmp, gmp->current, &row, &col);
949 }
950
951 PaintBrowser(gmp, 1, &crow, &ccol);
952 mlerase();
953 }
954 }
955
956 BrowserKeys();
957 break;
958
959 case '?': /* HELP! */
960 case (CTRL|'G'):
961 if(term.t_mrow == 0){
962 if(km_popped == 0){
963 km_popped = 2;
964 break;
965 }
966 }
967
968 if(Pmaster){
969 VARS_TO_SAVE *saved_state;
970
971 saved_state = save_pico_state();
972 (*Pmaster->helper)(Pmaster->browse_help,
973 _("Help for Browsing"), 1);
974 if(saved_state){
975 restore_pico_state(saved_state);
976 free_pico_state(saved_state);
977 }
978 }
979 else if(gmode&MDBRONLY)
980 pico_help(sa_BrowseHelpText, _("Browser Help"), 1);
981 else
982 pico_help(BrowseHelpText, _("Help for Browsing"), 1);
983 /* fall thru to repaint everything */
984
985 case (CTRL|'L'):
986 PaintBrowser(gmp, 0, &crow, &ccol);
987 break;
988
989 case 'g': /* jump to a directory */
990 case 'G':
991
992 if(!(gmode&MDGOTO))
993 goto Default;
994
995 i = 0;
996 child[0] = '\0';
997
998 while(!i){
999
1000 /* TRANSLATORS: A prompt asking for a directory to
1001 move into */
1002 status = mlreply_utf8(_("Directory to go to: "), child, NLINE, QNORML,
1003 NULL);
1004
1005 switch(status){
1006 case HELPCH:
1007 emlwwrite(_("No help yet!"), NULL);
1008 /* remove break and sleep after help text is installed */
1009 sleep(3);
1010 break;
1011 case (CTRL|'L'):
1012 PaintBrowser(gmp, 0, &crow, &ccol);
1013 break;
1014 case ABORT:
1015 emlwrite(_("Goto cancelled"), NULL);
1016 i++;
1017 break;
1018 case FALSE:
1019 case TRUE:
1020 i++;
1021
1022 if(*child == '\0'){
1023 strncpy(child, gethomedir(NULL), sizeof(child));
1024 child[sizeof(child)-1] = '\0';
1025 }
1026
1027 if(!compresspath(gmp->dname, child, sizeof(child))){
1028 eml.s = child;
1029 emlwrite(_("Invalid Directory: %s"), &eml);
1030 break;
1031 }
1032
1033 if((gmode&MDSCUR) && homeless(child)){
1034 emlwrite(_("Restricted mode browsing limited to home directory"),NULL);
1035 break;
1036 }
1037
1038 if((gmode&MDTREE) && !in_oper_tree(child)){
1039 eml.s = opertree;
1040 emlwwrite(_("Can't go outside of %s in restricted mode"),
1041 &eml);
1042 break;
1043 }
1044
1045 if(isdir(child, (long *) NULL, NULL)){
1046 if((mp = getfcells(child, fb_flags)) == NULL){
1047 /* getfcells should explain what happened */
1048 i++;
1049 break;
1050 }
1051
1052 zotmaster(&gmp);
1053 gmp = mp;
1054 PaintBrowser(gmp, 0, &crow, &ccol);
1055 }
1056 else{
1057 eml.s = child;
1058 emlwwrite(_("Not a directory: \"%s\""), &eml);
1059 }
1060
1061 break;
1062 default:
1063 break;
1064 }
1065 }
1066 BrowserKeys();
1067 break;
1068
1069 case '.': /* display dot files */
1070 gmode ^= MDDOTSOK;
1071 gmp = RepaintBrowser(gmp, fb_flags);
1072 break;
1073
1074 case '1': /* One column mode */
1075 gmode ^= MDONECOL;
1076 gmp = RepaintBrowser(gmp, fb_flags);
1077 break;
1078
1079 case 'o': /* Other menu */
1080 case 'O':
1081 if (gmp){
1082 if(gmp->menu == 0){
1083 gmp->menu = 1;
1084 } else {
1085 gmp->menu = 0;
1086 }
1087 BrowserKeys();
1088 }
1089 break;
1090
1091 case 'a': /* Add */
1092 case 'A':
1093 if(gmode&MDSCUR){ /* not allowed! */
1094 emlwrite(_("Add not allowed in restricted mode"),NULL);
1095 break;
1096 }
1097
1098 add_file = 1;
1099 i = 0;
1100 child[0] = '\0';
1101 /* pass in default filename */
1102 if(fn && *fn){
1103 strncpy(child, fn, sizeof(child) - 1);
1104 child[sizeof(child) - 1] = '\0';
1105 }
1106
1107 while(!i){
1108 int repaint = 0;
1109 EXTRAKEYS opts[10];
1110
1111 memset((void *) &opts, 0, 10*sizeof(EXTRAKEYS));
1112 opts[0].name = "^X";
1113 opts[0].label = add_file ? N_("Add Dir") : N_("Add File");
1114 opts[0].key = (CTRL|'X');
1115
1116 switch(status=mlreply_utf8(add_file ? _("Name of file to add: ") : _("Name of directory to add: "), child, NLINE,
1117 QFFILE, opts)){
1118 case HELPCH:
1119 emlwwrite(_("No help yet!"), NULL);
1120 /* remove break and sleep after help text is installed */
1121 sleep(3);
1122 break;
1123 case (CTRL|'L'):
1124 PaintBrowser(gmp, 0, &crow, &ccol);
1125 break;
1126 case (CTRL|'X'):
1127 if(add_file > 0) add_file = 0; else add_file = 1;
1128 break;
1129 case ABORT:
1130 emlwrite(add_file > 0 ? _("Add File Cancelled") : _("Add Directory Cancelled"), NULL);
1131 i++;
1132 break;
1133 case FALSE:
1134 /*
1135 * Support default filename. A return of 'FALSE' means
1136 * 'No change in filename' which is fine if we have
1137 * provided a default.
1138 * John Berthels <john.berthels@nexor.co.uk>
1139 */
1140 /* FALLTHROUGH */
1141 case TRUE:
1142 i++;
1143
1144 if(child[0] == '\0'){
1145 emlwrite(add_file > 0 ? _("No file named. Add Cancelled.") : _("No directory named. Add Cancelled"), NULL);
1146 break;
1147 }
1148
1149 if(!compresspath(gmp->dname, child, sizeof(child))){
1150 emlwrite(_("Too many ..'s in name"), NULL);
1151 break;
1152 }
1153
1154 if((gmode&MDTREE) && !in_oper_tree(child)){
1155 eml.s = opertree;
1156 emlwwrite(_("Restricted mode allows Add in %s only"),
1157 &eml);
1158 break;
1159 }
1160
1161 if((status = fexist(child, "w", (off_t *)NULL)) == FIOSUC){
1162 snprintf(tmp, sizeof(tmp), _("%s \"%.*s\" already exists!"),
1163 add_file > 0 ? "File" : "Directory", NLINE - 20, child);
1164 emlwrite(tmp, NULL);
1165 break;
1166 }
1167 else if(status != FIOFNF){
1168 fioperr(status, child);
1169 break;
1170 }
1171
1172 if(add_file == 0){
1173 if(our_mkdir(child, (0700)) < 0){
1174 eml.s = child;
1175 emlwrite(_("Error adding Directory \"%s\""), &eml);
1176 }
1177 else /* success! Directory added! */
1178 repaint = 1;
1179 }
1180 else if(ffwopen(child, FALSE) != FIOSUC){
1181 /* ffwopen should've complained */
1182 break;
1183 }
1184 else{ /* highlight new file */
1185 ffclose();
1186 eml.s = child;
1187 emlwrite(_("Added File \"%s\""), &eml);
1188 repaint = 1;
1189 }
1190
1191 if(repaint > 0){
1192 if((p = strrchr(child, C_FILESEP)) == NULL){
1193 emlwrite(_("Problems refiguring browser"), NULL);
1194 break;
1195 }
1196
1197 *p = '\0';
1198 if(p != child){
1199 strncpy(tmp, child, sizeof(tmp));
1200 tmp[sizeof(tmp)-1] = '\0';
1201 j = 0;
1202 while((child[j++] = *++p) != '\0')
1203 ;
1204 }
1205 else{
1206 strncpy(tmp, S_FILESEP, sizeof(tmp));
1207 tmp[sizeof(tmp)-1] = '\0';
1208 }
1209
1210 /*
1211 * new file in same dir? if so, refigure files
1212 * and redraw...
1213 */
1214 if(!strcmp(tmp, gmp->dname)){
1215 if((mp = getfcells(gmp->dname, fb_flags)) == NULL)
1216 /* getfcells should explain what happened */
1217 break;
1218
1219 zotmaster(&gmp);
1220 gmp = mp;
1221 if((tp = FindCell(gmp, child, 0)) != NULL){
1222 gmp->current = tp;
1223 PlaceCell(gmp, gmp->current, &row, &col);
1224 }
1225
1226 PaintBrowser(gmp, 1, &crow, &ccol);
1227 }
1228 }
1229 break;
1230 default:
1231 break;
1232 }
1233 }
1234
1235 BrowserKeys();
1236 break;
1237
1238 case 'c': /* copy */
1239 case 'C':
1240 if(gmp->current->mode == FIODIR){
1241 emlwwrite(_("Can't copy a directory"), NULL);
1242 break;
1243 }
1244
1245 if(gmode&MDSCUR){ /* not allowed! */
1246 emlwrite(_("Copy not allowed in restricted mode"),NULL);
1247 break;
1248 }
1249
1250 i = 0;
1251 child[0] = '\0';
1252
1253 while(!i){
1254
1255 switch(status=mlreply_utf8(_("Name of new copy: "), child, NLINE,
1256 QFFILE, NULL)){
1257 case HELPCH:
1258 emlwwrite(_("No help yet!"), NULL);
1259 /* remove break and sleep after help text is installed */
1260 sleep(3);
1261 break;
1262 case (CTRL|'L'):
1263 PaintBrowser(gmp, 0, &crow, &ccol);
1264 break;
1265 case ABORT:
1266 emlwrite(_("Make Copy Cancelled"), NULL);
1267 i++;
1268 break;
1269 case FALSE:
1270 i++;
1271 mlerase();
1272 break;
1273 case TRUE:
1274 i++;
1275
1276 if(child[0] == '\0'){
1277 emlwrite(_("No destination, file not copied"), NULL);
1278 break;
1279 }
1280
1281 if(!strcmp(gmp->current->fname, child)){
1282 emlwwrite(_("Can't copy file on to itself!"), NULL);
1283 break;
1284 }
1285
1286 if(!compresspath(gmp->dname, child, sizeof(child))){
1287 emlwrite(_("Too many ..'s in name"), NULL);
1288 break;
1289 }
1290
1291 if((gmode&MDTREE) && !in_oper_tree(child)){
1292 eml.s = opertree;
1293 emlwwrite(_("Restricted mode allows Copy in %s only"),
1294 &eml);
1295 break;
1296 }
1297
1298 if((status = fexist(child, "w", (off_t *)NULL)) == FIOSUC){
1299 /* TRANSLATORS: A question: File <filename> exists! Replace? */
1300 snprintf(tmp, sizeof(tmp), _("File \"%.*s\" exists! OVERWRITE"),
1301 NLINE - 20, child);
1302 if((status = mlyesno_utf8(tmp, 0)) != TRUE){
1303 emlwrite((status == ABORT)
1304 ? _("Make copy cancelled")
1305 : _("File Not Renamed"),
1306 NULL);
1307 break;
1308 }
1309 }
1310 else if(status != FIOFNF){
1311 fioperr(status, child);
1312 break;
1313 }
1314
1315 snprintf(tmp, sizeof(tmp), "%.*s%c%.*s", NLINE, gmp->dname, C_FILESEP,
1316 NLINE, gmp->current->fname);
1317
1318 if(copy(tmp, child) < 0){
1319 /* copy() will report any error messages */
1320 break;
1321 }
1322 else{ /* highlight new file */
1323 eml.s = child;
1324 emlwrite(_("File copied to %s"), &eml);
1325
1326 if((p = strrchr(child, C_FILESEP)) == NULL){
1327 emlwrite(_("Problems refiguring browser"), NULL);
1328 break;
1329 }
1330
1331 *p = '\0';
1332 strncpy(tmp, (p == child) ? S_FILESEP: child, sizeof(tmp));
1333 tmp[sizeof(tmp)-1] = '\0';
1334
1335 /*
1336 * new file in same dir? if so, refigure files
1337 * and redraw...
1338 */
1339 if(!strcmp(tmp, gmp->dname)){
1340 strncpy(child, gmp->current->fname, sizeof(child));
1341 child[sizeof(child)-1] = '\0';
1342 if((mp = getfcells(gmp->dname, fb_flags)) == NULL)
1343 /* getfcells should explain what happened */
1344 break;
1345
1346 zotmaster(&gmp);
1347 gmp = mp;
1348 if((tp = FindCell(gmp, child, 0)) != NULL){
1349 gmp->current = tp;
1350 PlaceCell(gmp, gmp->current, &row, &col);
1351 }
1352
1353 PaintBrowser(gmp, 1, &crow, &ccol);
1354 }
1355 }
1356 break;
1357 default:
1358 break;
1359 }
1360 }
1361 BrowserKeys();
1362 break;
1363
1364 case 'r': /* rename */
1365 case 'R':
1366 i = 0;
1367
1368 if(!strcmp(gmp->current->fname, "..")){
1369 emlwwrite(_("Can't rename \"..\""), NULL);
1370 break;
1371 }
1372
1373 if(gmode&MDSCUR){ /* not allowed! */
1374 emlwrite(_("Rename not allowed in restricted mode"),NULL);
1375 break;
1376 }
1377
1378 strncpy(child, gmp->current->fname, sizeof(child));
1379 child[sizeof(child)-1] = '\0';
1380
1381 while(!i){
1382
1383 switch(status=mlreply_utf8(_("Rename file to: "), child, NLINE, QFFILE,
1384 NULL)){
1385 case HELPCH:
1386 emlwwrite(_("No help yet!"), NULL);
1387 /* remove break and sleep after help text is installed */
1388 sleep(3);
1389 break;
1390 case (CTRL|'L'):
1391 PaintBrowser(gmp, 0, &crow, &ccol);
1392 break;
1393 case ABORT:
1394 emlwrite(_("Rename cancelled"), NULL);
1395 i++;
1396 break;
1397 case FALSE:
1398 case TRUE:
1399 i++;
1400
1401 if(child[0] == '\0' || status == FALSE){
1402 mlerase();
1403 break;
1404 }
1405
1406 if(!compresspath(gmp->dname, child, sizeof(child))){
1407 emlwrite(_("Too many ..'s in name"), NULL);
1408 break;
1409 }
1410
1411 if((gmode&MDTREE) && !in_oper_tree(child)){
1412 eml.s = opertree;
1413 emlwwrite(_("Restricted mode allows Rename in %s only"),
1414 &eml);
1415 break;
1416 }
1417
1418 status = fexist(child, "w", (off_t *)NULL);
1419 if(status == FIOSUC || status == FIOFNF){
1420 if(status == FIOSUC){
1421 snprintf(tmp, sizeof(tmp), _("File \"%.*s\" exists! OVERWRITE"),
1422 NLINE - 20, child);
1423
1424 if((status = mlyesno_utf8(tmp, FALSE)) != TRUE){
1425 emlwrite((status == ABORT)
1426 ? _("Rename cancelled")
1427 : _("Not Renamed"),
1428 NULL);
1429 break;
1430 }
1431 }
1432
1433 snprintf(tmp, sizeof(tmp), "%.*s%c%.*s", NLINE, gmp->dname, C_FILESEP,
1434 NLINE, gmp->current->fname);
1435
1436 if(our_rename(tmp, child) < 0){
1437 eml.s = errstr(errno);
1438 emlwrite(_("Rename Failed: %s"), &eml);
1439 }
1440 else{
1441 if((p = strrchr(child, C_FILESEP)) == NULL){
1442 emlwrite(_("Problems refiguring browser"), NULL);
1443 break;
1444 }
1445
1446 *p = '\0';
1447 strncpy(tmp, (p == child) ? S_FILESEP: child, sizeof(tmp));
1448 tmp[sizeof(tmp)-1] = '\0';
1449
1450 if((mp = getfcells(tmp, fb_flags)) == NULL)
1451 /* getfcells should explain what happened */
1452 break;
1453
1454 zotmaster(&gmp);
1455 gmp = mp;
1456
1457 if((tp = FindCell(gmp, ++p, 0)) != NULL){
1458 gmp->current = tp;
1459 PlaceCell(gmp, gmp->current, &row, &col);
1460 }
1461
1462 PaintBrowser(gmp, 1, &crow, &ccol);
1463 mlerase();
1464 }
1465 }
1466 else{
1467 fioperr(status, child);
1468 }
1469 break;
1470 default:
1471 break;
1472 }
1473 }
1474 BrowserKeys();
1475 break;
1476
1477 case 'v': /* stand-alone */
1478 case 'V': /* browser "view" */
1479 case 's': /* user "select" */
1480 case 'S':
1481 case (CTRL|'M'):
1482 Selected:
1483
1484 if(((new_c == 'S' || new_c == 's') && (gmode&MDBRONLY))
1485 || ((new_c == 'V' || new_c == 'v') && !(gmode&MDBRONLY)))
1486 goto Default;
1487
1488 if(gmp->current->mode == FIODIR){
1489 *child = '\0';
1490 strncpy(tmp, gmp->dname, sizeof(tmp));
1491 tmp[sizeof(tmp)-1] = '\0';
1492 p = gmp->current->fname;
1493 if(p[0] == '.' && p[1] == '.' && p[2] == '\0'){
1494 if((p=strrchr(tmp, C_FILESEP)) != NULL){
1495 *p = '\0';
1496
1497 if((gmode&MDTREE) && !in_oper_tree(tmp)){
1498 eml.s = PARENTDIR;
1499 emlwwrite(
1500 _("Can't visit %s in restricted mode"),
1501 &eml);
1502 break;
1503 }
1504
1505 strncpy(child, &p[1], sizeof(child));
1506 child[sizeof(child)-1] = '\0';
1507
1508 if
1509 #if defined(DOS) || defined(OS2)
1510 (p == tmp || (tmp[1] == ':' && tmp[2] == '\0'))
1511 #else
1512 (p == tmp)
1513 #endif
1514 { /* is it root? */
1515 #if defined(DOS) || defined(OS2)
1516 if(*child){
1517 strncat(tmp, S_FILESEP, sizeof(tmp)-strlen(tmp)-1);
1518 tmp[sizeof(tmp)-1] = '\0';
1519 }
1520 #else
1521 if(*child){
1522 strncpy(tmp, S_FILESEP, sizeof(tmp));
1523 tmp[sizeof(tmp)-1] = '\0';
1524 }
1525 #endif
1526 else{
1527 emlwwrite(_("Can't move up a directory"),
1528 NULL);
1529 break;
1530 }
1531 }
1532 }
1533 }
1534 else if((fb_flags&FB_SAVE) && p[0] == '.' && p[1] == '\0'){
1535 if ((strlen(gmp->dname) < dirlen) &&
1536 (strlen(gmp->current->fname) < fnlen)){
1537 strncpy(dir, gmp->dname, dirlen);
1538 dir[dirlen-1] = '\0';
1539 }
1540
1541 zotmaster(&gmp);
1542 return(0); /* just change the directory, still return no selection */
1543 }
1544 else{
1545 if(tmp[1] != '\0'){ /* were in root? */
1546 strncat(tmp, S_FILESEP, sizeof(tmp)-strlen(tmp)-1);
1547 tmp[sizeof(tmp)-1] = '\0';
1548 }
1549
1550 strncat(tmp, gmp->current->fname, sizeof(tmp)-strlen(tmp)-1);
1551 tmp[sizeof(tmp)-1] = '\0';
1552 }
1553
1554 if((mp = getfcells(tmp, fb_flags)) == NULL)
1555 /* getfcells should explain what happened */
1556 break;
1557
1558 if(gmp->flags & FB_LMODE){
1559 mp->flags |= FB_LMODE;
1560 mp->lm = gmp->lm;
1561 gmp->lm = NULL;
1562 }
1563
1564 zotmaster(&gmp);
1565 gmp = mp;
1566 tp = NULL;
1567
1568 if(*child){
1569 if((tp = FindCell(gmp, child, 0)) != NULL){
1570 gmp->current = tp;
1571 PlaceCell(gmp, gmp->current, &row, &col);
1572 }
1573 else{
1574 eml.s = child;
1575 emlwwrite(_("Problem finding dir \"%s\""), &eml);
1576 }
1577 }
1578
1579 PaintBrowser(gmp, 0, &crow, &ccol);
1580 if(!*child){
1581 char b[100];
1582
1583 snprintf(b, sizeof(b), "Select/View \" .. %s %s\" to return to previous directory.", PARENTDIR, DIRWORD);
1584 emlwrite(b, NULL);
1585 }
1586
1587 break;
1588 }
1589 else if(gmode&MDBRONLY){
1590 snprintf(child, sizeof(child), "%.*s%c%.*s", NLINE, gmp->dname, C_FILESEP,
1591 NLINE, gmp->current->fname);
1592
1593 if(LikelyASCII(child)){
1594 char *t;
1595 envp = (char *) getenv("PAGER");
1596 t = fs_get((envp ? strlen(envp) : strlen(BROWSER_PAGER))
1597 + strlen(child) + 3 + 1);
1598 sprintf(t, "%s \'%s\'", envp ? envp : BROWSER_PAGER, child);
1599 BrowserRunChild(t, gmp->dname);
1600 PaintBrowser(gmp, 0, &crow, &ccol);
1601 if(t) fs_give((void **) &t);
1602 }
1603
1604 break;
1605 }
1606 else{ /* just return */
1607 if(gmp->flags & FB_LMODEPOS){
1608
1609 if(!lmreturn){
1610 emlwrite("Programming error, called FileBrowse with LMODEPOS but no lmreturn", NULL);
1611 zotmaster(&gmp);
1612 return(-1);
1613 }
1614
1615 /* user actually used ListMode, the list is finished */
1616 if(gmp->flags & FB_LMODE){
1617 if(!gmp->lm){
1618 (*term.t_beep)();
1619 emlwrite(_("No files are selected, use \"X\" to mark files for selection"), NULL);
1620 break;
1621 }
1622
1623 *lmreturn = gmp->lm;
1624 gmp->lm = NULL;
1625 }
1626 else{ /* construct an lmreturn for user */
1627 LMLIST *new;
1628 size_t flen, dlen;
1629
1630 if((new=(LMLIST *)malloc(sizeof(*new))) == NULL
1631 || (new->fname=malloc(gmp->current->fname ? (flen=strlen(gmp->current->fname))+1 : 1)) == NULL
1632 || (new->dir=malloc((dlen=strlen(gmp->dname))+1)) == NULL){
1633 emlwwrite(_("Can't malloc space for filename"), NULL);
1634 return(-1);
1635 }
1636
1637 strncpy(new->fname,
1638 gmp->current->fname ? gmp->current->fname : "", flen);
1639 new->fname[flen] = '\0';
1640 strncpy(new->dir, gmp->dname, dlen);
1641 new->dir[dlen] = '\0';
1642 strncpy(new->size, gmp->current->size, sizeof(new->size));
1643 new->size[sizeof(new->size)-1] = '\0';
1644 new->next = NULL;
1645 *lmreturn = new;
1646 }
1647
1648 zotmaster(&gmp);
1649 return(1);
1650 }
1651
1652 if ((strlen(gmp->dname) < dirlen) &&
1653 (strlen(gmp->current->fname) < fnlen)){
1654 strncpy(dir, gmp->dname, dirlen);
1655 dir[dirlen-1] = '\0';
1656 strncpy(fn, gmp->current->fname, fnlen);
1657 fn[fnlen-1] = '\0';
1658 }
1659 else {
1660 zotmaster(&gmp);
1661 return(-1);
1662 }
1663 if(sz != NULL){ /* size uninteresting */
1664 strncpy(sz, gmp->current->size, szlen);
1665 sz[szlen-1] = '\0';
1666 }
1667
1668 zotmaster (&gmp);
1669 return (1);
1670 }
1671 break;
1672
1673 case 'w': /* Where is */
1674 case 'W':
1675 case (CTRL|'W'):
1676 i = 0;
1677 flags = SR_ORIGMEN | SR_FORWARD | SR_NOEXACT;
1678
1679 while(!i){
1680 switch(readpattern(_("File name to find"), FALSE, flags)){
1681 case HELPCH:
1682 emlwwrite(_("No help yet!"), NULL);
1683 /* remove break and sleep after help text is installed */
1684 sleep(3);
1685 break;
1686 case (CTRL|'L'):
1687 PaintBrowser(gmp, 0, &crow, &ccol);
1688 break;
1689 case (CTRL|'P'):
1690 if(flags & SR_FORWARD){
1691 flags &= ~SR_FORWARD;
1692 flags |= SR_BACKWRD;
1693 } else {
1694 flags &= ~SR_BACKWRD;
1695 flags |= SR_FORWARD;
1696 }
1697 break;
1698 case (CTRL|'Y'): /* first first cell */
1699 for(tp = gmp->top; tp->prev; tp = tp->prev)
1700 ;
1701
1702 i++;
1703 /* fall thru to repaint */
1704 case (CTRL|'V'):
1705 if(!i){
1706 do{
1707 tp = gmp->top;
1708 if((i = term.t_nrow - term.t_mrow - 2) <= 0)
1709 break;
1710
1711 while(i-- && tp->next){
1712 j = 0;
1713 while(++j <= gmp->fpl && tp->next)
1714 tp = tp->next;
1715 }
1716
1717 if(i < 0)
1718 gmp->top = tp;
1719 }
1720 while(tp->next);
1721
1722 emlwrite(_("Searched to end of directory"), NULL);
1723 }
1724 else
1725 emlwrite(_("Searched to start of directory"), NULL);
1726
1727 if(tp){
1728 PlaceCell(gmp, gmp->current, &row, &col);
1729 PaintCell(row, col, gmp->cpf, gmp->current, 0);
1730 gmp->current = tp;
1731 if(PlaceCell(gmp, gmp->current, &row, &col)){
1732 PaintBrowser(gmp, 1, &crow, &ccol);
1733 }
1734 else{
1735 PaintCell(row, col, gmp->cpf, gmp->current, 1);
1736 crow = row;
1737 ccol = col;
1738 }
1739 }
1740
1741 i++; /* make sure we jump out */
1742 break;
1743 case ABORT:
1744 emlwrite(_("Whereis cancelled"), NULL);
1745 i++;
1746 break;
1747 case FALSE:
1748 mlerase();
1749 i++;
1750 break;
1751 case TRUE:
1752 {
1753 char *utf8 = NULL;
1754
1755 if(pat && pat[0])
1756 utf8 = ucs4_to_utf8_cpystr(pat);
1757
1758 if(utf8 && (tp = FindCell(gmp, utf8, flags & SR_BACKWRD)) != NULL){
1759 PlaceCell(gmp, gmp->current, &row, &col);
1760 PaintCell(row, col, gmp->cpf, gmp->current, 0);
1761 gmp->current = tp;
1762
1763 if(PlaceCell(gmp, tp, &row, &col)){ /* top changed */
1764 PaintBrowser(gmp, 1, &crow, &ccol);
1765 }
1766 else{
1767 PaintCell(row, col, gmp->cpf, gmp->current, 1);
1768 crow = row;
1769 ccol = col;
1770 }
1771 mlerase();
1772 }
1773 else{
1774 eml.s = utf8;
1775 emlwrite(_("\"%s\" not found"), &eml);
1776 }
1777
1778 if(utf8)
1779 fs_give((void **) &utf8);
1780 }
1781
1782 i++;
1783 break;
1784 default:
1785 break;
1786 }
1787 }
1788
1789 BrowserKeys();
1790 break;
1791
1792 case (CTRL|'\\') :
1793 #if defined MOUSE && !defined(_WINDOWS)
1794 toggle_xterm_mouse(0,1);
1795 #else
1796 unknown_command(c);
1797 #endif
1798 break;
1799
1800 case (CTRL|'Z'):
1801 if(gmode&MDSSPD){
1802 bktoshell(0, 1);
1803 PaintBrowser(gmp, 0, &crow, &ccol);
1804 break;
1805 } /* fall thru with error! */
1806
1807 default: /* what? */
1808 Default:
1809 unknown_command(c);
1810
1811 case NODATA: /* no op */
1812 break;
1813 }
1814 }
1815 }
1816
1817
1818 /*
1819 * getfcells - make a master browser struct and fill it in
1820 * return NULL if there's a problem.
1821 */
1822 struct bmaster *
getfcells(char * dname,int fb_flags)1823 getfcells(char *dname, int fb_flags)
1824 {
1825 int i, /* various return codes */
1826 flength,
1827 nentries = 0; /* number of dir ents */
1828 off_t attsz;
1829 char *np, /* names of files in dir */
1830 *dcp, /* to add file to path */
1831 *tmpstr,
1832 **filtnames, /* array filtered names */
1833 errbuf[NLINE];
1834 struct fcell *ncp, /* new cell pointer */
1835 *tcp = NULL; /* trailing cell ptr */
1836 struct bmaster *mp;
1837 EML eml;
1838
1839 errbuf[0] = '\0';
1840 if((mp=(struct bmaster *)malloc(sizeof(struct bmaster))) == NULL){
1841 emlwwrite(_("Can't malloc space for master filename cell"), NULL);
1842 return(NULL);
1843 }
1844
1845 memset(mp, 0, sizeof(*mp));
1846
1847 if(dname[0] == '.' && dname[1] == '\0'){ /* remember this dir */
1848 if(!getcwd(mp->dname, 256))
1849 mp->dname[0] = '\0';
1850 }
1851 else if(dname[0] == '.' && dname[1] == '.' && dname[2] == '\0'){
1852 if(!getcwd(mp->dname, 256))
1853 mp->dname[0] = '\0';
1854 else{
1855 if((np = (char *)strrchr(mp->dname, C_FILESEP)) != NULL)
1856 if(np != mp->dname)
1857 *np = '\0';
1858 }
1859 }
1860 else{
1861 strncpy(mp->dname, dname, sizeof(mp->dname));
1862 mp->dname[sizeof(mp->dname)-1] = '\0';
1863 }
1864
1865 mp->bottom = mp->head = mp->top = NULL;
1866 mp->menu = mp->cpf = mp->fpl = 0;
1867 mp->longest = 5; /* .. must be labeled! */
1868 mp->flags = fb_flags;
1869
1870 eml.s = mp->dname;
1871 emlwrite("Building file list of %s...", &eml);
1872
1873 if((mp->names = getfnames(mp->dname, NULL, &nentries, errbuf, sizeof(errbuf))) == NULL){
1874 free((char *) mp);
1875 if(*errbuf)
1876 emlwwrite(errbuf, NULL);
1877
1878 return(NULL);
1879 }
1880
1881 /*
1882 * this is the fun part. build an array of pointers to the fnames we're
1883 * interested in (i.e., do any filtering), then pass that off to be
1884 * sorted before building list of cells...
1885 *
1886 * right now default filtering on ".*" except "..", but this could
1887 * easily be made a user option later on...
1888 */
1889 if((filtnames=(char **)malloc((nentries+1) * sizeof(char *))) == NULL){
1890 emlwwrite(_("Can't malloc space for name array"), NULL);
1891 zotmaster(&mp);
1892 return(NULL);
1893 }
1894
1895 i = 0; /* index of filt'd array */
1896 np = mp->names;
1897 while(nentries--){
1898 int ii;
1899 int width;
1900
1901 /*
1902 * Filter dot files? Always filter ".", never filter "..",
1903 * and sometimes filter ".*"...
1904 */
1905 if(*np == '.' && (!(*(np+1) == '.' && *(np+2) == '\0')
1906 && !(*(np+1) == '\0' && (fb_flags&FB_SAVE)))
1907 && (*(np+1) == '\0' || !(gmode & MDDOTSOK))){
1908 np += strlen(np) + 1;
1909 continue;
1910 }
1911
1912 filtnames[i++] = np;
1913
1914 ii = (int) strlen(np);
1915 if((width = (int) utf8_width(np)) > mp->longest)
1916 mp->longest = width; /* remember longest */
1917
1918 np += ii + 1; /* advance name pointer */
1919 }
1920
1921 nentries = i; /* new # of entries */
1922
1923 /*
1924 * sort files case independently
1925 */
1926 qsort((qsort_t *)filtnames, (size_t)nentries, sizeof(char *), sstrcasecmp);
1927
1928 /*
1929 * this is so we use absolute path names for stats.
1930 * remember: be careful using dname as directory name, and fix mp->dname
1931 * when we're done
1932 */
1933 dcp = (char *) strchr(mp->dname, '\0');
1934 if(dcp == mp->dname || dcp[-1] != C_FILESEP){
1935 dcp[0] = C_FILESEP;
1936 dcp[1] = '\0';
1937 }
1938 else
1939 dcp--;
1940
1941 i = 0;
1942 while(nentries--){ /* stat filtered files */
1943 /* get a new cell */
1944 if((ncp=(struct fcell *)malloc(sizeof(struct fcell))) == NULL){
1945 emlwwrite(_("Can't malloc cells for browser!"), NULL);
1946 zotfcells(mp->head); /* clean up cells */
1947 free((char *) filtnames);
1948 free((char *) mp);
1949 return(NULL); /* bummer. */
1950 }
1951
1952 ncp->next = ncp->prev = NULL;
1953
1954 if(mp->head == NULL){ /* tie it onto the list */
1955 mp->head = mp->top = mp->current = ncp;
1956 }
1957 else{
1958 mp->bottom = ncp;
1959 tcp->next = ncp;
1960 ncp->prev = tcp;
1961 }
1962
1963 tcp = ncp;
1964
1965 /* fill in the new cell */
1966 ncp->fname = filtnames[i++];
1967
1968 /* fill in file's mode */
1969 if ((flength = strlen(ncp->fname) + 1 + strlen(dname)) < sizeof(mp->dname)){
1970 strncpy(&dcp[1], ncp->fname, sizeof(mp->dname)-(dcp+1-mp->dname)); /* use absolute path! */
1971 mp->dname[sizeof(mp->dname)-1] = '\0';
1972 tmpstr = mp->dname;
1973 }
1974 else{
1975 if((tmpstr = (char *)malloc((flength+1)*sizeof(char))) == NULL){
1976 emlwwrite(_("Can't malloc cells for temp buffer!"), NULL);
1977 zotfcells(mp->head); /* clean up cells */
1978 free((char *) filtnames);
1979 free((char *) mp);
1980 return(NULL); /* bummer. */
1981 }
1982
1983 strncpy(tmpstr, dname, flength);
1984 tmpstr[flength] = '\0';
1985 tmpstr = strncat(tmpstr, S_FILESEP, flength+1-1-strlen(tmpstr));
1986 tmpstr[flength] = '\0';
1987 tmpstr = strncat(tmpstr, ncp->fname, flength+1-1-strlen(tmpstr));
1988 tmpstr[flength] = '\0';
1989 }
1990
1991 switch(fexist(tmpstr, "t", &attsz)){
1992 case FIODIR :
1993 ncp->mode = FIODIR;
1994 snprintf(ncp->size, sizeof(ncp->size), "(%s%s%s)",
1995 (ncp->fname[0] == '.' && ncp->fname[1] == '.'
1996 && ncp->fname[2] == '\0') ? PARENTDIR :
1997 ((fb_flags&FB_SAVE) && ncp->fname[0] == '.' && ncp->fname[1] == '\0'
1998 ? SELECTWORD : ""),
1999 (ncp->fname[0] == '.' && ncp->fname[1] == '.'
2000 && ncp->fname[2] == '\0') ? " " :
2001 ((fb_flags&FB_SAVE) && ncp->fname[0] == '.' && ncp->fname[1] == '\0'
2002 ? " " : ""),
2003 DIRWORD);
2004 break;
2005
2006 case FIOSYM :
2007 ncp->mode = FIOSYM;
2008 strncpy(ncp->size, "--", sizeof(ncp->size));
2009 ncp->size[sizeof(ncp->size)-1] = '\0';
2010 break;
2011
2012 default :
2013 ncp->mode = FIOSUC; /* regular file */
2014 strncpy(ncp->size, prettysz(attsz), sizeof(ncp->size));
2015 ncp->size[sizeof(ncp->size)-1] = '\0';
2016 break;
2017 }
2018
2019 if (flength >= NLINE)
2020 free((char *) tmpstr);
2021 }
2022
2023 dcp[(dcp == mp->dname) ? 1 : 0] = '\0'; /* remember to cap dname */
2024 free((char *) filtnames); /* 'n blast filt'd array*/
2025
2026 percdircells(mp);
2027 layoutcells(mp);
2028 if(strlen(mp->dname) < sizeof(browse_dir)){
2029 strncpy(browse_dir, mp->dname, sizeof(browse_dir));
2030 browse_dir[sizeof(browse_dir)-1] = '\0';
2031 }
2032 else
2033 browse_dir[0] = '\0';
2034
2035 return(mp);
2036 }
2037
2038
2039 int
fcell_is_selected(struct fcell * cell,struct bmaster * mp)2040 fcell_is_selected(struct fcell *cell, struct bmaster *mp)
2041 {
2042 LMLIST *lm;
2043
2044 if(cell && cell->fname){
2045 for(lm = mp ? mp->lm : NULL; lm; lm = lm->next){
2046 /* directory has to match */
2047 if(!((mp->dname[0] == '\0' && (!lm->dir || lm->dir[0] =='\0'))
2048 || (mp->dname[0] != '\0' && lm->dir && lm->dir[0] !='\0'
2049 && !strcmp(mp->dname, lm->dir))))
2050 continue;
2051
2052 if(lm->fname && !strcmp(cell->fname, lm->fname))
2053 return(1);
2054 }
2055 }
2056
2057 return(0);
2058 }
2059
2060
2061 /*
2062 * Adds a new name to the head of the lmlist
2063 */
2064 void
add_cell_to_lmlist(struct fcell * cell,struct bmaster * mp)2065 add_cell_to_lmlist(struct fcell *cell, struct bmaster *mp)
2066 {
2067 LMLIST *new;
2068 size_t flen, dlen;
2069
2070 if(mp && cell && cell->fname && cell->fname[0]){
2071 if((new=(LMLIST *)malloc(sizeof(*new))) == NULL ||
2072 (new->fname=malloc(sizeof(char)*((flen=strlen(cell->fname))+1))) == NULL ||
2073 (new->dir=malloc(sizeof(char)*((dlen=strlen(mp->dname))+1))) == NULL){
2074 emlwwrite(_("Can't malloc space for filename"), NULL);
2075 return;
2076 }
2077
2078 strncpy(new->fname, cell->fname, flen);
2079 new->fname[flen] = '\0';
2080 strncpy(new->dir, mp->dname, dlen);
2081 new->dir[dlen] = '\0';
2082 new->size[0] = '\0';
2083 if(cell->size[0]){
2084 strncpy(new->size, cell->size, sizeof(new->size));
2085 new->size[sizeof(new->size)-1] = '\0';
2086 }
2087
2088 new->next = mp->lm;
2089 mp->lm = new;
2090 }
2091 }
2092
2093
2094 /*
2095 * Deletes a name from the lmlist
2096 */
2097 void
del_cell_from_lmlist(struct fcell * cell,struct bmaster * mp)2098 del_cell_from_lmlist(struct fcell *cell, struct bmaster *mp)
2099 {
2100 LMLIST *lm, *lmprev = NULL;
2101
2102 if(mp && cell && cell->fname && cell->fname[0])
2103 for(lm = mp ? mp->lm : NULL; lm; lm = lm->next){
2104 if(lm->fname && strcmp(cell->fname, lm->fname) == 0){
2105 free((char *) lm->fname);
2106 if(lm->dir)
2107 free((char *) lm->dir);
2108
2109 if(lmprev)
2110 lmprev->next = lm->next;
2111 else
2112 mp->lm = lm->next;
2113
2114 free((char *) lm);
2115
2116 break;
2117 }
2118
2119 lmprev = lm;
2120 }
2121 }
2122
2123
2124 /*
2125 * PaintCell - print the given cell at the given location on the display
2126 * the format of a printed cell is:
2127 *
2128 * "<fname> <size> "
2129 */
2130 void
PaintCell(int row,int col,int sc,struct fcell * cell,int inverted)2131 PaintCell(int row, int col,
2132 int sc, /* screen columns available for this cell */
2133 struct fcell *cell, int inverted)
2134 {
2135 char buf1[NLINE], buf2[NLINE];
2136 char lbuf[5];
2137 int need, l_wid, f_wid, f_to_s_wid, s_wid;
2138 UCS *ucs;
2139
2140 if(cell == NULL)
2141 return;
2142
2143 l_wid = (gmp && ((gmp->flags & FB_LMODEPOS) > 4)) ? 4 : 0;
2144 f_wid = utf8_width(cell->fname ? cell->fname : "");
2145 f_to_s_wid = 1;
2146 s_wid = utf8_width(cell->size ? cell->size : "");
2147
2148 /* the two is the space between cell columns */
2149 sc = MIN(sc-2, term.t_ncol-col);
2150
2151 if(l_wid){
2152 if(gmp && gmp->flags & FB_LMODE && cell->mode != FIODIR){
2153 /*
2154 * We have to figure out here if it is selected or not
2155 * and use that to write the X or space.
2156 */
2157 lbuf[0] = '[';
2158 if(fcell_is_selected(cell, gmp))
2159 lbuf[1] = 'X';
2160 else
2161 lbuf[1] = ' ';
2162
2163 lbuf[2] = ']';
2164 lbuf[3] = ' ';
2165 }
2166 else{
2167 lbuf[0] = lbuf[1] = lbuf[2] = lbuf[3] = ' ';
2168 }
2169
2170 lbuf[4] = '\0';
2171 }
2172
2173 sc -= l_wid;
2174
2175 need = f_wid+f_to_s_wid+s_wid;
2176
2177 /* space between name and size */
2178 if(need < sc)
2179 f_to_s_wid += (sc-need);
2180
2181 /*
2182 * If the width isn't enough to handle everything we're just putting a single
2183 * space between fname and size and truncating on the right. That means that
2184 * the sizes in the right hand column won't line up correctly when there is
2185 * a lack of space. Instead, we opt for displaying as much info as possible
2186 * in a slightly discordant way.
2187 */
2188
2189 movecursor(row, col);
2190 if(l_wid){
2191 ucs = utf8_to_ucs4_cpystr(lbuf);
2192 if(ucs){
2193 pputs(ucs, 0);
2194 fs_give((void **) &ucs);
2195 }
2196 }
2197
2198 utf8_snprintf(buf1, sizeof(buf1), "%*.*w%*.*w%*.*w",
2199 f_wid, f_wid, cell->fname ? cell->fname : "",
2200 f_to_s_wid, f_to_s_wid, "",
2201 s_wid, s_wid, cell->size ? cell->size : "");
2202
2203 utf8_snprintf(buf2, sizeof(buf2), "%*.*w", sc, sc, buf1);
2204
2205 if(inverted)
2206 (*term.t_rev)(1);
2207 else if (*term.t_eri)
2208 (*term.t_eri)();
2209
2210 ucs = utf8_to_ucs4_cpystr(buf2);
2211 if(ucs){
2212 pputs(ucs, 0);
2213 fs_give((void **) &ucs);
2214 }
2215
2216 if(inverted)
2217 (*term.t_rev)(0);
2218 }
2219
2220
2221 /*
2222 * PaintBrowse - with the current data, display the browser. if level == 0
2223 * paint the whole thing, if level == 1 just paint the cells
2224 * themselves
2225 */
2226 void
PaintBrowser(struct bmaster * mp,int level,int * row,int * col)2227 PaintBrowser(struct bmaster *mp, int level, int *row, int *col)
2228 {
2229 int i, cl;
2230 struct fcell *tp;
2231
2232 if(!level){
2233 ClearBrowserScreen();
2234 BrowserAnchor(mp->dname);
2235 }
2236
2237 i = 0;
2238 tp = mp->top;
2239 cl = COMPOSER_TOP_LINE; /* current display line */
2240 while(tp){
2241
2242 PaintCell(cl, mp->cpf * i, mp->cpf, tp, tp == mp->current);
2243
2244 if(tp == mp->current){
2245 if(row)
2246 *row = cl;
2247
2248 if(col)
2249 *col = mp->cpf * i;
2250 }
2251
2252 if(++i >= mp->fpl){
2253 i = 0;
2254 if(++cl > term.t_nrow-(term.t_mrow+1))
2255 break;
2256 }
2257
2258 tp = tp->next;
2259 }
2260
2261 if(level){
2262 while(cl <= term.t_nrow - (term.t_mrow+1)){
2263 if(!i)
2264 movecursor(cl, 0);
2265 peeol();
2266 movecursor(++cl, 0);
2267 }
2268 }
2269 else{
2270 BrowserKeys();
2271 }
2272 }
2273
2274 /*
2275 * RepaintBrowser - factorized method to just repaint everything
2276 */
2277 struct bmaster *
RepaintBrowser(struct bmaster * gmp,int fb_flags)2278 RepaintBrowser(struct bmaster *gmp, int fb_flags)
2279 {
2280 struct bmaster *mp;
2281 struct fcell *tp;
2282 int row, col;
2283 char *fn;
2284 fn = gmp->current->fname;
2285
2286 if((mp = getfcells(gmp->dname, fb_flags)) == NULL){
2287 /* getfcells should explain what happened */
2288 return(gmp);
2289 }
2290 zotmaster(&gmp);
2291 gmp = mp;
2292
2293 tp = NULL;
2294 if(*fn){
2295 if((tp = FindCell(gmp, fn, 0)) != NULL){
2296 gmp->current = tp;
2297 PlaceCell(gmp, gmp->current, &row, &col);
2298 }
2299 }
2300
2301 PaintBrowser(gmp, 0, NULL, NULL);
2302 return(gmp);
2303 }
2304
2305
2306 /*
2307 * BrowserKeys - just paints the keyhelp at the bottom of the display
2308 */
2309 void
BrowserKeys(void)2310 BrowserKeys(void)
2311 {
2312 if (gmp && gmp->menu == 1){
2313 menu_other[GOTO_KEY].name = (gmode&MDGOTO) ? "G" : NULL;
2314 menu_other[GOTO_KEY].label = (gmode&MDGOTO) ? N_("Goto") : NULL;
2315 wkeyhelp(menu_other);
2316 return;
2317 }
2318 menu_browse[QUIT_KEY].name = (gmode&MDBRONLY) ? "Q" : "E";
2319 /* TRANSLATORS: Brwsr is an abbreviation for Browser, which is
2320 a command used to look through something */
2321 menu_browse[QUIT_KEY].label = (gmode&MDBRONLY) ? N_("Quit") : N_("Exit Brwsr");
2322 if(gmode & MDBRONLY){
2323 menu_browse[EXEC_KEY].name = "L";
2324 menu_browse[EXEC_KEY].label = N_("Launch");
2325 menu_browse[SELECT_KEY].name = "V";
2326 menu_browse[SELECT_KEY].label = "[" N_("View") "]";
2327 menu_browse[PICO_KEY].name = "E";
2328 menu_browse[PICO_KEY].label = N_("Edit");
2329 }
2330 else{
2331 menu_browse[SELECT_KEY].name = "S";
2332 menu_browse[SELECT_KEY].label = "[" N_("Select") "]";
2333 menu_browse[PICO_KEY].name = "A";
2334 menu_browse[PICO_KEY].label = N_("Add");
2335
2336 if(gmp && gmp->flags & FB_LMODEPOS){
2337 if(gmp && gmp->flags & FB_LMODE){ /* ListMode is already on */
2338 menu_browse[EXEC_KEY].name = "X";
2339 menu_browse[EXEC_KEY].label = N_("Set/Unset");
2340 }
2341 else{ /* ListMode is possible */
2342 menu_browse[EXEC_KEY].name = "L";
2343 menu_browse[EXEC_KEY].label = N_("ListMode");
2344 }
2345 }
2346 else{ /* No ListMode possible */
2347 menu_browse[EXEC_KEY].name = NULL;
2348 menu_browse[EXEC_KEY].label = NULL;
2349 }
2350 }
2351
2352 wkeyhelp(menu_browse);
2353 }
2354
2355
2356 /*
2357 * layoutcells - figure out max length of cell and how many cells can
2358 * go on a line of the display
2359 */
2360 void
layoutcells(struct bmaster * mp)2361 layoutcells(struct bmaster *mp)
2362 {
2363 static int wid = -1;
2364 /*
2365 * Max chars/file. Actually this is max screen cells/file
2366 * Longest name + "(parent dir)"
2367 */
2368 if(wid < 0)
2369 wid = MAX(utf8_width(PARENTDIR),utf8_width(SELECTWORD)) + utf8_width(DIRWORD);
2370
2371 mp->cpf = mp->longest + wid + 3;
2372
2373 if(mp->flags & FB_LMODEPOS) /* "[X] " */
2374 mp->cpf += 4;
2375
2376 if(gmode & MDONECOL){
2377 mp->fpl = 1;
2378 }
2379 else{
2380 int i = 1;
2381
2382 while(i*mp->cpf - 2 <= term.t_ncol) /* no force... */
2383 i++; /* like brute force! */
2384
2385 mp->fpl = i - 1; /* files per line */
2386 }
2387
2388 if(mp->fpl == 0)
2389 mp->fpl = 1;
2390 }
2391
2392
2393 /*
2394 * percdircells - bubble all the directory cells to the top of the
2395 * list.
2396 */
2397 void
percdircells(struct bmaster * mp)2398 percdircells(struct bmaster *mp)
2399 {
2400 struct fcell *dirlp, /* dir cell list pointer */
2401 *lp, *nlp; /* cell list ptr and next */
2402
2403 dirlp = NULL;
2404 for(lp = mp->head; lp; lp = nlp){
2405 nlp = lp->next;
2406 if(lp->mode == FIODIR){
2407 if(lp->prev) /* clip from list */
2408 lp->prev->next = lp->next;
2409
2410 if(lp->next)
2411 lp->next->prev = lp->prev;
2412
2413 if((lp->prev = dirlp) != NULL){ /* tie it into dir portion */
2414 if((lp->next = dirlp->next) != NULL)
2415 lp->next->prev = lp;
2416
2417 dirlp->next = lp;
2418 dirlp = lp;
2419 }
2420 else{
2421 if((dirlp = lp) != mp->head)
2422 dirlp->next = mp->head;
2423
2424 if(dirlp->next)
2425 dirlp->next->prev = dirlp;
2426
2427 mp->head = mp->top = mp->current = dirlp;
2428 }
2429 }
2430 }
2431 }
2432
2433
2434 /*
2435 * PlaceCell - given a browser master and a cell, return row and col of the display that
2436 * it should go on.
2437 *
2438 * return 1 if mp->top has changed, x,y relative to new page
2439 * return 0 if otherwise (same page)
2440 * return -1 on error
2441 */
2442 int
PlaceCell(struct bmaster * mp,struct fcell * cp,int * x,int * y)2443 PlaceCell(struct bmaster *mp, struct fcell *cp, int *x, int *y)
2444 {
2445 int cl = COMPOSER_TOP_LINE; /* current line */
2446 int ci = 0; /* current index on line */
2447 int rv = 0;
2448 int secondtry = 0;
2449 struct fcell *tp;
2450
2451 /* will cp fit on screen? */
2452 tp = mp->top;
2453 while(1){
2454 if(tp == cp){ /* bingo! */
2455 *x = cl;
2456 *y = ci * mp->cpf;
2457 break;
2458 }
2459
2460 if((tp = tp->next) == NULL){ /* above top? */
2461 if(secondtry++){
2462 emlwwrite(_("Internal error: can't find fname cell"), NULL);
2463 return(-1);
2464 }
2465 else{
2466 tp = mp->top = mp->head; /* try from the top! */
2467 cl = COMPOSER_TOP_LINE;
2468 ci = 0;
2469 rv = 1;
2470 continue; /* start over! */
2471 }
2472 }
2473
2474 if(++ci >= mp->fpl){ /* next line? */
2475 ci = 0;
2476 if(++cl > term.t_nrow-(term.t_mrow+1)){ /* next page? */
2477 ci = mp->fpl; /* tp is at bottom right */
2478 while(ci--) /* find new top */
2479 tp = tp->prev;
2480 mp->top = tp;
2481 ci = 0;
2482 cl = COMPOSER_TOP_LINE; /* keep checking */
2483 rv = 1;
2484 }
2485 }
2486
2487 }
2488
2489 /* not on display! */
2490 return(rv);
2491 }
2492
2493
2494 /*
2495 * zotfcells - clean up malloc'd cells of file names
2496 */
2497 void
zotfcells(struct fcell * hp)2498 zotfcells(struct fcell *hp)
2499 {
2500 struct fcell *tp;
2501
2502 while(hp){
2503 tp = hp;
2504 hp = hp->next;
2505 tp->next = NULL;
2506 free((char *) tp);
2507 }
2508 }
2509
2510
2511 /*
2512 * zotmaster - blast the browser master struct
2513 */
2514 void
zotmaster(struct bmaster ** mp)2515 zotmaster(struct bmaster **mp)
2516 {
2517 if(mp && *mp){
2518 zotfcells((*mp)->head); /* free cells */
2519 zotlmlist((*mp)->lm); /* free lmlist */
2520 if((*mp)->names)
2521 free((char *)(*mp)->names); /* free file names */
2522
2523 free((char *)*mp); /* free master */
2524 *mp = NULL; /* make double sure */
2525 }
2526 }
2527
2528
2529 /*
2530 * FindCell - starting from the current cell find the first occurance of
2531 * the given string wrapping around if necessary
2532 */
2533 struct fcell *
FindCell(struct bmaster * mp,char * utf8string,int bsearch)2534 FindCell(struct bmaster *mp, char *utf8string, int bsearch)
2535 {
2536 struct fcell *tp, *fp;
2537
2538 if(*utf8string == '\0')
2539 return(NULL);
2540
2541 fp = NULL;
2542 tp = bsearch ? mp->current->prev : mp->current->next;
2543
2544 while(tp && !fp){
2545 if(sisin(tp->fname, utf8string))
2546 fp = tp;
2547 else
2548 tp = bsearch ? tp->prev : tp->next;
2549 }
2550
2551 tp = bsearch ? mp->bottom : mp->head;
2552 while(tp != mp->current && !fp){
2553 if(sisin(tp->fname, utf8string))
2554 fp = tp;
2555 else
2556 tp = bsearch ? tp->prev : tp->next;
2557 }
2558
2559 return(fp);
2560 }
2561
2562
2563 /*
2564 * sisin - case insensitive substring matching function
2565 *
2566 * We can't really do case-insensitive for non-ascii, so restrict
2567 * that to ascii characters. The strings will be utf8.
2568 */
2569 int
sisin(char * bigstr,char * utf8substr)2570 sisin(char *bigstr, char *utf8substr)
2571 {
2572 register int j;
2573
2574 while(*bigstr){
2575 j = 0;
2576 while(bigstr[j] == utf8substr[j]
2577 || ((unsigned char)bigstr[j] < 0x80 && (unsigned char)utf8substr[j] < 0x80
2578 && toupper((unsigned char)bigstr[j]) == toupper((unsigned char)utf8substr[j])))
2579 if(utf8substr[++j] == '\0') /* bingo! */
2580 return(1);
2581
2582 bigstr++;
2583 }
2584
2585 return(0);
2586 }
2587
2588
2589 /*
2590 * set_browser_title -
2591 */
2592 void
set_browser_title(char * s)2593 set_browser_title(char *s)
2594 {
2595 browser_title = s;
2596 }
2597
2598
2599 /*
2600 * BrowserAnchor - draw the browser's anchor line.
2601 */
2602 void
BrowserAnchor(char * utf8dir)2603 BrowserAnchor(char *utf8dir)
2604 {
2605 char *pdir;
2606 char titlebuf[NLINE];
2607 char buf[NLINE];
2608 char dirbuf[NLINE];
2609 char *dots = "...";
2610 char *br = "BROWSER";
2611 char *dir = "Dir: ";
2612 UCS *ucs;
2613 int need, extra, avail;
2614 int title_wid, b_wid, t_to_b_wid, b_to_d_wid, d_wid, dot_wid, dir_wid, after_dir_wid;
2615 COLOR_PAIR *lastc = NULL;
2616
2617 if(!utf8dir)
2618 utf8dir = "";
2619
2620 pdir = utf8dir;
2621
2622 if(browser_title)
2623 snprintf(titlebuf, sizeof(buf), " %s", browser_title);
2624 else if(Pmaster)
2625 snprintf(titlebuf, sizeof(buf), " ALPINE %s", Pmaster->pine_version);
2626 else
2627 snprintf(titlebuf, sizeof(buf), " UW PICO %s", (gmode&MDBRONLY) ? "BROWSER" : version);
2628
2629 title_wid = utf8_width(titlebuf);
2630 t_to_b_wid = 15;
2631 b_wid = utf8_width(br);
2632 b_to_d_wid = 4;
2633 d_wid = utf8_width(dir);
2634 dot_wid = 0;
2635 dir_wid = utf8_width(pdir);
2636
2637 need = title_wid+t_to_b_wid+b_wid+b_to_d_wid+d_wid+dot_wid+dir_wid;
2638
2639 if(need > term.t_ncol){
2640 extra = need - term.t_ncol;
2641 t_to_b_wid -= MIN(extra, 10);
2642 need -= MIN(extra, 10);
2643
2644 if(need > term.t_ncol){
2645 extra = need - term.t_ncol;
2646 b_to_d_wid -= MIN(extra, 2);
2647 need -= MIN(extra, 2);
2648 }
2649
2650 if(need > term.t_ncol){
2651 titlebuf[0] = titlebuf[1] = ' ';
2652 titlebuf[2] = '\0';
2653 title_wid = utf8_width(titlebuf);
2654 t_to_b_wid = 0;
2655 b_to_d_wid = 4;
2656 need = title_wid+t_to_b_wid+b_wid+b_to_d_wid+d_wid+dot_wid+dir_wid;
2657 if(need > term.t_ncol){
2658 extra = need - term.t_ncol;
2659 b_to_d_wid -= MIN(extra, 2);
2660 need -= MIN(extra, 2);
2661 }
2662
2663 need = title_wid+t_to_b_wid+b_wid+b_to_d_wid+d_wid+dot_wid+dir_wid;
2664 if(need > term.t_ncol){
2665 t_to_b_wid = 0;
2666 b_wid = 0;
2667 b_to_d_wid = 0;
2668 }
2669
2670 need = title_wid+t_to_b_wid+b_wid+b_to_d_wid+d_wid+dot_wid+dir_wid;
2671 if(need > term.t_ncol)
2672 d_wid = 0;
2673
2674 need = title_wid+t_to_b_wid+b_wid+b_to_d_wid+d_wid+dot_wid+dir_wid;
2675
2676 if(need > term.t_ncol && dir_wid > 0){
2677 dot_wid = utf8_width(dots);
2678 need = title_wid+t_to_b_wid+b_wid+b_to_d_wid+d_wid+dot_wid+dir_wid;
2679 while(need > term.t_ncol && (pdir = strchr(pdir+1, C_FILESEP))){
2680 dir_wid = utf8_width(pdir);
2681 need = title_wid+t_to_b_wid+b_wid+b_to_d_wid+d_wid+dot_wid+dir_wid;
2682 }
2683
2684 if(!pdir){ /* adjust other widths to fill up space */
2685 avail = term.t_ncol - (title_wid+t_to_b_wid+b_wid+b_to_d_wid+d_wid+dot_wid);
2686 if(avail > 2)
2687 utf8_to_width_rhs(dirbuf, utf8dir, sizeof(dirbuf), avail);
2688 else
2689 dirbuf[0] = '\0';
2690
2691 pdir = dirbuf;
2692 }
2693
2694 dir_wid = utf8_width(pdir);
2695 }
2696 }
2697 }
2698
2699 extra = term.t_ncol - (title_wid+t_to_b_wid+b_wid+b_to_d_wid+d_wid+dot_wid+dir_wid);
2700
2701 if(extra >= 0)
2702 after_dir_wid = extra;
2703 else
2704 after_dir_wid = 0;
2705
2706 utf8_snprintf(buf, sizeof(buf), "%*.*w%*.*w%*.*w%*.*w%*.*w%*.*w%*.*w%*.*w",
2707 title_wid, title_wid, titlebuf,
2708 t_to_b_wid, t_to_b_wid, "",
2709 b_wid, b_wid, br,
2710 b_to_d_wid, b_to_d_wid, "",
2711 d_wid, d_wid, dir,
2712 dot_wid, dot_wid, dots,
2713 dir_wid, dir_wid, pdir,
2714 after_dir_wid, after_dir_wid, "");
2715
2716 /* just making sure */
2717 utf8_snprintf(titlebuf, sizeof(titlebuf), "%-*.*w", term.t_ncol, term.t_ncol, buf);
2718
2719 movecursor(0, 0);
2720 if(Pmaster && Pmaster->colors && Pmaster->colors->tbcp
2721 && pico_is_good_colorpair(Pmaster->colors->tbcp)){
2722 lastc = pico_get_cur_color();
2723 (void) pico_set_colorp(Pmaster->colors->tbcp, PSC_NONE);
2724 }
2725 else
2726 (*term.t_rev)(1);
2727
2728 ucs = utf8_to_ucs4_cpystr(titlebuf);
2729 if(ucs){
2730 pputs(ucs, 0);
2731 fs_give((void **) &ucs);
2732 }
2733
2734 if(lastc){
2735 (void) pico_set_colorp(lastc, PSC_NONE);
2736 free_color_pair(&lastc);
2737 }
2738 else
2739 (*term.t_rev)(0);
2740 }
2741
2742
2743 /*
2744 * ResizeBrowser - handle a resize event
2745 */
2746 int
ResizeBrowser(void)2747 ResizeBrowser(void)
2748 {
2749 if(gmp){
2750 layoutcells(gmp);
2751 PaintBrowser(gmp, 0, NULL, NULL);
2752 return(1);
2753 }
2754 else
2755 return(0);
2756 }
2757
2758
2759 void
ClearBrowserScreen(void)2760 ClearBrowserScreen(void)
2761 {
2762 int i;
2763
2764 for(i = 0; i <= term.t_nrow; i++){ /* clear screen */
2765 movecursor(i, 0);
2766 peeol();
2767 }
2768 }
2769
2770
2771 void
BrowserRunChild(char * child,char * dir)2772 BrowserRunChild(char *child, char *dir)
2773 {
2774 int status;
2775 char tmp[NLINE];
2776 time_t t_in, t_out;
2777
2778 ClearBrowserScreen();
2779 movecursor(0, 0);
2780 (*term.t_close)();
2781 if(!isdir(dir, NULL, &t_in))
2782 t_in = 0;
2783
2784 fflush(stdout);
2785 status = system(child);
2786 (*term.t_open)();
2787 if(t_in && isdir(dir, NULL, &t_out) && t_in < t_out){
2788 struct bmaster *mp;
2789
2790 if((mp = getfcells(dir, 0)) != NULL){
2791 zotmaster(&gmp);
2792 gmp = mp;
2793 }
2794 /* else getfcells should explain what happened */
2795 }
2796
2797 /* complain about non-zero exit status */
2798 if((status >> 8) & 0xff){
2799
2800 movecursor(term.t_nrow - 1, 0);
2801 snprintf(tmp, sizeof(tmp), "[ \"%.30s\" exit with error value: %d ]",
2802 child, (status >> 8) & 0xff);
2803 pputs_utf8(tmp, 1);
2804 movecursor(term.t_nrow, 0);
2805 pputs_utf8("[ Hit RETURN to continue ]", 1);
2806 fflush(stdout);
2807
2808 while(GetKey() != (CTRL|'M')){
2809 (*term.t_beep)();
2810 fflush(stdout);
2811 }
2812 }
2813 }
2814
2815
2816 /*
2817 * imitate pc-pine memory for where we last called the file browser.
2818 */
2819 void
p_chdir(struct bmaster * mp)2820 p_chdir(struct bmaster *mp)
2821 {
2822 if(mp && mp->dname)
2823 chdir(mp->dname);
2824 }
2825 #else /* _WINDOWS */
2826
2827
2828 /*
2829 * pico_file_browse - Exported version of FileBrowse below.
2830 */
2831 int
pico_file_browse(PICO * pdata,char * dir,size_t dirlen,char * fn,size_t fnlen,char * sz,size_t szlen,int flags)2832 pico_file_browse(PICO *pdata, char *dir, size_t dirlen, char *fn, size_t fnlen,
2833 char *sz, size_t szlen, int flags)
2834 {
2835 return(FileBrowse(dir, dirlen, fn, fnlen, sz, szlen, flags, NULL));
2836 }
2837
2838 int
ResizeBrowser(void)2839 ResizeBrowser (void)
2840 {
2841 return (0);
2842 }
2843
2844 /*
2845 * FileBrowse - Windows version of above function
2846 */
2847 int
FileBrowse(char * dir,size_t dirlen,char * fn,size_t fnlen,char * sz,size_t szlen,int fb_flags,LMLIST ** lmreturn)2848 FileBrowse(char *dir, size_t dirlen, char *fn, size_t fnlen,
2849 char *sz, size_t szlen, int fb_flags, LMLIST **lmreturn)
2850 {
2851 struct stat sbuf;
2852 int rc;
2853 char lfn[NLINE];
2854
2855 if (fb_flags & FB_SAVE){
2856 rc = mswin_savefile(dir, dirlen, fn, MIN(dirlen,fnlen));
2857 }
2858 else{
2859 *fn = '\0'; /* No initial file names for
2860 * open. */
2861 if(fb_flags & FB_LMODEPOS){
2862 /*
2863 * We're going to allow multiple filenames to be returned so
2864 * we don't want to use the passed in fn for that. Instead, make
2865 * a bigger space and use that.
2866 */
2867 char f[20000];
2868 size_t flen, dlen;
2869
2870 memset(f, 0, sizeof(f));
2871
2872 rc = mswin_multopenfile(dir, dirlen, f, sizeof(f),
2873 (fb_flags & FB_ATTACH) ? NULL : "Text Files (*.txt)#*.txt");
2874
2875 if(rc == 1){
2876 LMLIST *lmhead = NULL, *new;
2877 char *p;
2878
2879 /*
2880 * Build an LMLIST to return to the caller.
2881 */
2882 for(p = f; *p; p += strlen(p)+1){
2883 flen = strlen(p);
2884 dlen = strlen(dir ? dir : "");
2885 new = (LMLIST *) fs_get(sizeof(*new));
2886 new->fname = (char *) fs_get((flen+1) * sizeof(char));
2887 new->dir = (char *) fs_get((dlen+1) * sizeof(char));
2888
2889 strncpy(new->fname, p, flen);
2890 new->fname[flen] = '\0';
2891 strncpy(new->dir, dir ? dir : "", dlen);
2892 new->dir[dlen] = '\0';
2893
2894 /* build full path to stat file. */
2895 if((strlen(new->dir) + strlen(S_FILESEP) +
2896 strlen(new->fname) + 1) < sizeof(lfn)){
2897 strncpy(lfn, new->dir, sizeof(lfn));
2898 lfn[sizeof(lfn)-1] = '\0';
2899 strncat(lfn, S_FILESEP, sizeof(lfn)-strlen(lfn)-1);
2900 strncat(lfn, new->fname, sizeof(lfn)-strlen(lfn)-1);
2901 lfn[sizeof(lfn)-1] = '\0';
2902 if(our_stat(lfn, &sbuf) < 0)
2903 strncpy(new->size, "0", 32);
2904 else
2905 strncpy(new->size, prettysz((off_t)sbuf.st_size), 32);
2906
2907 new->size[32-1] = '\0';
2908 }
2909
2910 new->next = lmhead;
2911 lmhead = new;
2912 }
2913
2914 *lmreturn = lmhead;
2915 return(1);
2916 }
2917 }
2918 else
2919 rc = mswin_openfile (dir, dirlen, fn, fnlen,
2920 (fb_flags & FB_ATTACH) ? NULL : "Text Files (*.txt)#*.txt");
2921 }
2922
2923 if(rc == 1){
2924 if(sz != NULL){
2925 /* build full path to stat file. */
2926 if((strlen(dir) + strlen(S_FILESEP) + strlen(fn) + 1) > NLINE)
2927 return -1;
2928
2929 strncpy(lfn, dir, sizeof(lfn));
2930 lfn[sizeof(lfn)-1] = '\0';
2931 strncat(lfn, S_FILESEP, sizeof(lfn)-strlen(lfn)-1);
2932 lfn[sizeof(lfn)-1] = '\0';
2933 strncat(lfn, fn, sizeof(lfn)-strlen(lfn)-1);
2934 lfn[sizeof(lfn)-1] = '\0';
2935 if(our_stat(lfn, &sbuf) < 0){
2936 strncpy(sz, "0", szlen);
2937 sz[szlen-1] = '\0';
2938 }
2939 else{
2940 strncpy(sz, prettysz ((off_t)sbuf.st_size), szlen);
2941 sz[szlen-1] = '\0';
2942 }
2943 }
2944
2945 return(1);
2946 }
2947
2948 return(rc ? -1 : 0);
2949 }
2950
2951 #endif /* _WINDOWS */
2952
2953
2954 /*
2955 * LikelyASCII - make a rough guess as to the displayability of the
2956 * given file.
2957 */
2958 int
LikelyASCII(char * file)2959 LikelyASCII(char *file)
2960 {
2961 #define LA_TEST_BUF 1024
2962 #define LA_LINE_LIMIT 300
2963 int n, i, line, rv = FALSE;
2964 unsigned char buf[LA_TEST_BUF];
2965 FILE *fp;
2966 EML eml;
2967
2968 if((fp = our_fopen(file, "rb")) != NULL){
2969 clearerr(fp);
2970 if((n = fread(buf, sizeof(char), LA_TEST_BUF * sizeof(char), fp)) > 0
2971 || !ferror(fp)){
2972 /*
2973 * If we don't hit any newlines in a reasonable number,
2974 * LA_LINE_LIMIT, of characters or the file contains NULLs,
2975 * bag out...
2976 */
2977 rv = TRUE;
2978 for(i = line = 0; i < n; i++)
2979 if((line = (buf[i] == '\n') ? 0 : line + 1) >= LA_LINE_LIMIT
2980 || !buf[i]){
2981 rv = FALSE;
2982 emlwrite(_("Can't display non-text file. Try \"Launch\"."),
2983 NULL);
2984 break;
2985 }
2986 }
2987 else{
2988 eml.s = file;
2989 emlwrite(_("Can't read file: %s"), &eml);
2990 }
2991
2992 fclose(fp);
2993 }
2994 else{
2995 eml.s = file;
2996 emlwrite(_("Can't open file: %s"), &eml);
2997 }
2998
2999 return(rv);
3000 }
3001
3002
3003 void
zotlmlist(LMLIST * lm)3004 zotlmlist(LMLIST *lm)
3005 {
3006 LMLIST *tp;
3007
3008 while(lm){
3009 if(lm->fname)
3010 free(lm->fname);
3011
3012 if(lm->dir)
3013 free(lm->dir);
3014
3015 tp = lm;
3016 lm = lm->next;
3017 tp->next = NULL;
3018 free((char *) tp);
3019 }
3020 }
3021
3022
3023 /*
3024 * time_to_check - checks the current time against the last time called
3025 * and returns true if the elapsed time is > below.
3026 * Newmail won't necessarily check, but we want to give it
3027 * a chance to check or do a keepalive.
3028 */
3029 int
time_to_check(void)3030 time_to_check(void)
3031 {
3032 static time_t lasttime = 0L;
3033
3034 if(!get_input_timeout())
3035 return(FALSE);
3036
3037 if(time((time_t *) 0) - lasttime > (Pmaster ? (time_t)(FUDGE-10) : get_input_timeout())){
3038 lasttime = time((time_t *) 0);
3039 return(TRUE);
3040 }
3041 else
3042 return(FALSE);
3043 }
3044