* Implement a different way to delete a password from the cache.
[alpine.git] / pico / file.c
blob73bfe27ab73c4d55222862f28b0ea2ffbf8beeeb
1 /*
2 * ========================================================================
3 * Copyright 2006-2007 University of Washington
4 * Copyright 2013-2022 Eduardo Chappa
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
10 * http://www.apache.org/licenses/LICENSE-2.0
12 * ========================================================================
14 * Program: High level file input and output routines
19 * The routines in this file
20 * handle the reading and writing of
21 * disk files. All of details about the
22 * reading and writing of the disk are
23 * in "fileio.c".
25 #include "headers.h"
26 #include "../pith/charconv/filesys.h"
29 void init_insmsgchar_cbuf(void);
30 int insmsgchar(int);
31 char *file_split(char *, size_t, char *, int);
34 * Read a file into the current
35 * buffer. This is really easy; all you do it
36 * find the name of the file, and call the standard
37 * "read a file into the current buffer" code.
38 * Bound to "C-X C-R".
40 int
41 fileread(int f, int n)
43 register int s;
44 char fname[NFILEN];
45 EML eml;
47 if ((s=mlreply_utf8(_("Read file: "), fname, NFILEN, QNORML, NULL)) != TRUE)
48 return(s);
50 if(gmode&MDSCUR){
51 emlwrite(_("File reading disabled in secure mode"),NULL);
52 return(0);
55 if (strlen(fname) == 0) {
56 emlwrite(_("No file name entered"),NULL);
57 return(0);
60 if((gmode & MDTREE) && !in_oper_tree(fname)){
61 eml.s = opertree;
62 emlwrite(_("Can't read file from outside of %s"), &eml);
63 return(0);
66 return(readin(fname, TRUE, TRUE));
71 static char *inshelptext[] = {
72 /* TRANSLATORS: several lines of help text */
73 N_("Insert File Help Text"),
74 " ",
75 N_(" Type in a file name to have it inserted into your editing"),
76 N_(" buffer between the line that the cursor is currently on"),
77 N_(" and the line directly below it. You may abort this by "),
78 N_("~ typing the ~F~2 (~^~C) key after exiting help."),
79 " ",
80 N_("End of Insert File Help"),
81 " ",
82 NULL
85 static char *writehelp[] = {
86 /* TRANSLATORS: several lines of help text */
87 N_("Write File Help Text"),
88 " ",
89 N_(" Type in a file name to have it written out, thus saving"),
90 N_(" your buffer, to a file. You can abort this by typing "),
91 N_("~ the ~F~2 (~^~C) key after exiting help."),
92 " ",
93 N_("End of Write File Help"),
94 " ",
95 " ",
96 NULL
101 * Insert a file into the current
102 * buffer. This is really easy; all you do it
103 * find the name of the file, and call the standard
104 * "insert a file into the current buffer" code.
105 * Bound to "C-X C-I".
108 insfile(int f, int n)
110 register int s;
111 char fname[NLINE], dir[NLINE];
112 int retval = 0, bye = 0, msg = 0;
113 char prompt[64], *infile;
114 EXTRAKEYS menu_ins[10];
115 EML eml;
117 if (curbp->b_mode&MDVIEW) /* don't allow this command if */
118 return(rdonly()); /* we are in read only mode */
120 memset(&menu_ins, 0, 10*sizeof(EXTRAKEYS));
121 fname[0] = dir[0] = '\0';
122 while(!bye){
123 /* set up keymenu stuff */
124 if(!msg){
125 int last_menu = 0;
127 menu_ins[last_menu].name = "^T";
128 menu_ins[last_menu].key = (CTRL|'T');
129 menu_ins[last_menu].label = N_("To Files");
130 KS_OSDATASET(&menu_ins[last_menu], KS_NONE);
132 if(Pmaster && Pmaster->msgntext){
133 menu_ins[++last_menu].name = "^W";
134 menu_ins[last_menu].key = (CTRL|'W');
135 /* TRANSLATORS: Insert File is a key label for a command
136 that inserts a file into a message being composed.
137 InsertMsg means Insert Message and it inserts a different
138 message into the message being composed. */
139 menu_ins[last_menu].label = msg ? N_("InsertFile") : N_("InsertMsg");
140 KS_OSDATASET(&menu_ins[last_menu], KS_NONE);
143 #if !defined(DOS) && !defined(MAC)
144 if(Pmaster && Pmaster->upload){
145 menu_ins[++last_menu].name = "^Y";
146 menu_ins[last_menu].key = (CTRL|'Y');
147 menu_ins[last_menu].label = "RcvUpload";
148 KS_OSDATASET(&menu_ins[last_menu], KS_NONE);
150 #endif /* !(DOS || MAC) */
152 if(gmode & MDCMPLT){
153 menu_ins[++last_menu].name = msg ? "" : "TAB";
154 menu_ins[last_menu].key = (CTRL|'I');
155 menu_ins[last_menu].label = msg ? "" : N_("Complete");
156 KS_OSDATASET(&menu_ins[last_menu], KS_NONE);
159 menu_ins[++last_menu].name = NULL;
162 snprintf(prompt, sizeof(prompt), "%s to insert from %s %s: ",
163 msg ? "Number of message" : "File",
164 (msg || (gmode&MDCURDIR))
165 ? "current"
166 : ((gmode & MDTREE) || opertree[0]) ? opertree : "home",
167 msg ? "folder" : "directory");
168 s = mlreplyd_utf8(prompt, fname, NLINE, QDEFLT, msg ? NULL : menu_ins);
169 /* something to read and it was edited or the default accepted */
170 if(fname[0] && (s == TRUE || s == FALSE)){
171 bye++;
172 if(msg){
173 init_insmsgchar_cbuf();
174 if((*Pmaster->msgntext)(atol(fname), insmsgchar)){
175 eml.s = fname;
176 emlwrite(_("Message %s included"), &eml);
179 else{
180 bye++;
181 if(gmode&MDSCUR){
182 emlwrite(_("Can't insert file in restricted mode"),NULL);
184 else{
185 if((gmode & MDTREE)
186 && !compresspath(opertree, fname, sizeof(fname))){
187 eml.s = opertree;
188 emlwrite(
189 _("Can't insert file from outside of %s: too many ..'s"), &eml);
191 else{
192 fixpath(fname, sizeof(fname));
194 if((gmode & MDTREE) && !in_oper_tree(fname)){
195 eml.s = opertree;
196 emlwrite(_("Can't insert file from outside of %s"), &eml);
198 else
199 retval = ifile(fname);
204 else{
205 switch(s){
206 case (CTRL|'I') :
208 char *fn;
210 dir[0] = '\0';
211 fn = file_split(dir, sizeof(dir), fname, 0);
213 if(!pico_fncomplete(dir, fn, sizeof(fname)-(fn-fname)))
214 (*term.t_beep)();
217 break;
218 case (CTRL|'W'):
219 msg = !msg; /* toggle what to insert */
220 break;
221 case (CTRL|'T'):
222 if(msg){
223 emlwrite(_("Can't select messages yet!"), NULL);
225 else{
226 char *fn;
228 fn = file_split(dir, sizeof(dir), fname, 1);
229 if(!isdir(dir, NULL, NULL)){
230 strncpy(dir, (gmode&MDCURDIR)
231 ? (browse_dir[0] ? browse_dir : ".")
232 : ((gmode & MDTREE) || opertree[0])
233 ? opertree
234 : (browse_dir[0] ? browse_dir
235 :gethomedir(NULL)), sizeof(dir));
236 dir[sizeof(dir)-1] = '\0';
238 else{
239 if(*fn){
240 int dirlen;
242 dirlen = strlen(dir);
243 if(dirlen && dir[dirlen - 1] != C_FILESEP){
244 strncat(dir, S_FILESEP, sizeof(dir)-strlen(dir)-1);
245 dir[sizeof(dir)-1] = '\0';
248 strncat(dir, fn, sizeof(dir)-strlen(dir)-1);
249 dir[sizeof(dir)-1] = '\0';
250 if(!isdir(dir, NULL, NULL))
251 dir[MIN(dirlen,sizeof(dir)-1)] = '\0';
255 fname[0] = '\0';
256 if((s = FileBrowse(dir, sizeof(dir), fname, sizeof(fname),
257 NULL, 0, FB_READ, NULL)) == 1){
258 if(gmode&MDSCUR){
259 emlwrite(_("Can't insert in restricted mode"),
260 NULL);
261 sleep(2);
263 else{
264 size_t len;
266 len = strlen(dir)+strlen(S_FILESEP)+strlen(fname);
267 if ((infile = (char *)malloc((len+1)*sizeof(char))) != NULL){
268 strncpy(infile, dir, len);
269 infile[len] = '\0';
270 strncat(infile, S_FILESEP, len+1-1-strlen(infile));
271 infile[len] = '\0';
272 strncat(infile, fname, len+1-1-strlen(infile));
273 infile[len] = '\0';
274 retval = ifile(infile);
275 free((char *) infile);
277 else {
278 emlwrite("Trouble allocating space for insert!"
279 ,NULL);
280 sleep(3);
283 bye++;
285 else
286 fname[0] = '\0';
288 pico_refresh(FALSE, 1);
289 if(s != 1){
290 update(); /* redraw on return */
291 continue;
295 break;
297 #if !defined(DOS) && !defined(MAC)
298 case (CTRL|'Y') :
299 if(Pmaster && Pmaster->upload){
300 char tfname[NLINE];
302 if(gmode&MDSCUR){
303 emlwwrite(
304 _("Restricted mode disallows uploaded command"),
305 NULL);
306 return(0);
309 tfname[0] = '\0';
310 retval = (*Pmaster->upload)(tfname, sizeof(tfname), NULL);
312 pico_refresh(FALSE, 1);
313 update();
315 if(retval){
316 retval = ifile(tfname);
317 bye++;
319 else
320 sleep(3); /* problem, show error! */
322 if(tfname[0]) /* clean up temp file */
323 our_unlink(tfname);
325 else
326 (*term.t_beep)(); /* what? */
328 break;
329 #endif /* !(DOS || MAC) */
330 case HELPCH:
331 if(Pmaster){
332 VARS_TO_SAVE *saved_state;
334 saved_state = save_pico_state();
335 (*Pmaster->helper)(msg ? Pmaster->ins_m_help
336 : Pmaster->ins_help,
337 _("Help for Insert File"), 1);
338 if(saved_state){
339 restore_pico_state(saved_state);
340 free_pico_state(saved_state);
343 else
344 pico_help(inshelptext, _("Help for Insert File"), 1);
345 case (CTRL|'L'):
346 pico_refresh(FALSE, 1);
347 update();
348 continue;
349 default:
350 ctrlg(FALSE, 0);
351 retval = s;
352 bye++;
356 curwp->w_flag |= WFMODE|WFHARD;
358 return(retval);
362 * split out the file name from the path.
363 * Copy path into dirbuf, return filename,
364 * which is a pointer into orig_fname.
365 * is_for_browse - use browse dir if possible
366 * don't want to use it for TAB-completion
368 char *
369 file_split(char *dirbuf, size_t dirbuflen, char *orig_fname, int is_for_browse)
371 char *p, *fn;
372 size_t dirlen;
374 if(*orig_fname && (p = strrchr(orig_fname, C_FILESEP))){
375 fn = p + 1;
376 dirlen = p - orig_fname;
377 if(p == orig_fname){
378 strncpy(dirbuf, S_FILESEP, dirbuflen);
379 dirbuf[dirbuflen-1] = '\0';
381 #ifdef DOS
382 else if(orig_fname[0] == C_FILESEP
383 || (isalpha((unsigned char)orig_fname[0])
384 && orig_fname[1] == ':')){
385 if(orig_fname[1] == ':' && p == orig_fname+2)
386 dirlen = fn - orig_fname;
388 dirlen = MIN(dirlen, dirbuflen-1);
389 strncpy(dirbuf, orig_fname, dirlen);
390 dirbuf[dirlen] = '\0';
392 #else
393 else if (orig_fname[0] == C_FILESEP || orig_fname[0] == '~'){
394 dirlen = MIN(dirlen, dirbuflen-1);
395 strncpy(dirbuf, orig_fname, dirlen);
396 dirbuf[dirlen] = '\0';
398 #endif
399 else
400 snprintf(dirbuf, dirbuflen, "%s%c%.*s",
401 (gmode & MDCURDIR)
402 ? ((is_for_browse && browse_dir[0]) ? browse_dir : ".")
403 : ((gmode & MDTREE) || opertree[0])
404 ? opertree
405 : ((is_for_browse && browse_dir[0])
406 ? browse_dir : gethomedir(NULL)),
407 C_FILESEP, (int) (p - orig_fname), orig_fname);
409 else{
410 fn = orig_fname;
411 strncpy(dirbuf, (gmode & MDCURDIR)
412 ? ((is_for_browse && browse_dir[0]) ? browse_dir : ".")
413 : ((gmode & MDTREE) || opertree[0])
414 ? opertree
415 : ((is_for_browse && browse_dir[0])
416 ? browse_dir : gethomedir(NULL)), dirbuflen);
417 dirbuf[dirbuflen-1] = '\0';
420 return fn;
424 static CBUF_S insmsgchar_cb;
426 void
427 init_insmsgchar_cbuf(void)
429 insmsgchar_cb.cbuf[0] = '\0';
430 insmsgchar_cb.cbufp = insmsgchar_cb.cbuf;
431 insmsgchar_cb.cbufend = insmsgchar_cb.cbuf;
436 * This is called with a stream of UTF-8 characters.
437 * We change that stream into UCS characters and insert
438 * those characters.
441 insmsgchar(int c)
443 if(c == '\n'){
444 UCS *u;
446 lnewline();
447 for(u = glo_quote_str; u && *u; u++)
448 if(!linsert(1, *u))
449 return(0);
451 else if(c != '\r'){ /* ignore CR (likely CR of CRLF) */
452 int outchars;
453 UCS ucs;
455 if((outchars = utf8_to_ucs4_oneatatime(c, &insmsgchar_cb, &ucs, NULL)) != 0)
456 if(!linsert(1, ucs))
457 return(0);
460 return(1);
465 * Read file "fname" into the current
466 * buffer, blowing away any text found there. Called
467 * by both the read and find commands. Return the final
468 * status of the read. Also called by the mainline,
469 * to read in a file specified on the command line as
470 * an argument.
473 readin(char fname[], /* name of file to read */
474 int lockfl, /* check for file locks? */
475 int rename) /* don't rename if reading from, say, alt speller */
477 UCS line[NLINE], *linep;
478 long nline;
479 int s, done, newline;
481 curbp->b_linecnt = -1; /* Must be recalculated */
482 if ((s = bclear(curbp)) != TRUE) /* Might be old. */
483 return (s);
485 if(rename){
486 strncpy(curbp->b_fname, fname, sizeof(curbp->b_fname));
487 curbp->b_fname[sizeof(curbp->b_fname)-1] = '\0';
490 if ((s=ffropen(fname)) != FIOSUC){ /* Hard file open. */
491 if(s == FIOFNF) /* File not found. */
492 emlwrite(_("New file"), NULL);
493 else
494 fioperr(s, fname);
496 else{
497 size_t charsread = 0;
499 emlwrite(_("Reading file"), NULL);
500 nline = 0L;
501 done = newline = 0;
502 while(!done)
503 if((s = ffgetline(line, NLINE, &charsread, 1)) == FIOEOF){
504 char b[200];
506 curbp->b_flag &= ~(BFTEMP|BFCHG);
507 gotobob(FALSE, 1);
508 if(nline > 1)
509 snprintf(b, sizeof(b), _("Read %ld lines"), nline);
510 else
511 snprintf(b, sizeof(b), _("Read 1 line"));
513 emlwrite(b, NULL);
515 break;
517 else{
518 if(newline){
519 if(lnewline() == FALSE){
520 done++;
521 continue;
523 newline = 0;
526 switch(s){
527 case FIOSUC :
528 nline++;
529 newline = 1;
531 case FIOLNG :
532 for(linep = line; charsread-- > 0; linep++)
533 if(linsert(1, *linep) == FALSE) done++;
535 break;
537 default :
538 done++;
539 break;
543 ffclose(); /* Ignore errors. */
546 return(s != FIOERR && s != FIOFNF); /* true if success */
551 * Ask for a file name, and write the
552 * contents of the current buffer to that file.
553 * Update the remembered file name and clear the
554 * buffer changed flag. This handling of file names
555 * is different from the earlier versions, and
556 * is more compatible with Gosling EMACS than
557 * with ITS EMACS. Bound to "C-X C-W".
560 filewrite(int f, int n)
562 register WINDOW *wp;
563 register int s;
564 char fname[NFILEN];
565 char shows[NLINE], origshows[NLINE], *bufp;
566 EXTRAKEYS menu_write[10];
567 EML eml;
569 memset(&menu_write, 0, 10*sizeof(EXTRAKEYS));
570 if(curbp->b_fname[0] != 0){
571 strncpy(fname, curbp->b_fname, sizeof(curbp->b_fname));
572 curbp->b_fname[sizeof(curbp->b_fname)-1] = '\0';
574 else
575 fname[0] = '\0';
577 menu_write[0].name = "^T";
578 menu_write[0].label = N_("To Files");
579 menu_write[0].key = (CTRL|'T');
580 menu_write[1].name = "TAB";
581 menu_write[1].label = N_("Complete");
582 menu_write[1].key = (CTRL|'I');
583 menu_write[2].name = NULL;
584 for(;!(gmode & MDTOOL);){
585 /* TRANSLATORS: Asking for name of file to write data into */
586 s = mlreplyd_utf8(_("File Name to write : "), fname, NFILEN,
587 QDEFLT|QFFILE, menu_write);
589 switch(s){
590 case FALSE:
591 if(!fname[0]){ /* no file name to write to */
592 ctrlg(FALSE, 0);
593 return(s);
595 case TRUE:
596 if((gmode & MDTREE) && !compresspath(opertree, fname, sizeof(fname))){
597 eml.s = opertree;
598 emlwrite(_("Can't write outside of %s: too many ..'s"), &eml);
599 sleep(2);
600 continue;
602 else{
603 fixpath(fname, sizeof(fname)); /* fixup ~ in file name */
604 if((gmode & MDTREE) && !in_oper_tree(fname)){
605 eml.s = opertree;
606 emlwrite(_("Can't write outside of %s"), &eml);
607 sleep(2);
608 continue;
612 break;
614 case (CTRL|'I'):
616 char *fn, *p, dir[NFILEN];
617 int l = NFILEN;
619 dir[0] = '\0';
620 if(*fname && (p = strrchr(fname, C_FILESEP))){
621 fn = p + 1;
622 l -= fn - fname;
623 if(p == fname){
624 strncpy(dir, S_FILESEP, sizeof(dir));
625 dir[sizeof(dir)-1] = '\0';
627 #ifdef DOS
628 else if(fname[0] == C_FILESEP
629 || (isalpha((unsigned char)fname[0])
630 && fname[1] == ':'))
631 #else
632 else if (fname[0] == C_FILESEP || fname[0] == '~')
633 #endif
635 strncpy(dir, fname, MIN(p - fname, sizeof(dir)-1));
636 dir[MIN(p-fname, sizeof(dir)-1)] = '\0';
638 else
639 snprintf(dir, sizeof(dir), "%s%c%.*s",
640 (gmode & MDCURDIR)
641 ? "."
642 : ((gmode & MDTREE) || opertree[0])
643 ? opertree : gethomedir(NULL),
644 C_FILESEP, (int) (p - fname), fname);
646 else{
647 fn = fname;
648 strncpy(dir, (gmode & MDCURDIR)
649 ? "."
650 : ((gmode & MDTREE) || opertree[0])
651 ? opertree : gethomedir(NULL), sizeof(dir));
652 dir[sizeof(dir)-1] = '\0';
655 if(!pico_fncomplete(dir, fn, sizeof(fname)-(fn-fname)))
656 (*term.t_beep)();
659 continue;
661 case (CTRL|'T'):
662 /* If we have a file name, break up into path and file name.*/
663 *shows = 0;
664 if(*fname) {
665 if(isdir(fname, NULL, NULL)) {
666 /* fname is a directory. */
667 strncpy(shows, fname, sizeof(shows));
668 shows[sizeof(shows)-1] = '\0';
669 *fname = '\0';
671 else {
672 /* Find right most separator. */
673 bufp = strrchr (fname, C_FILESEP);
674 if (bufp != NULL) {
675 /* Copy directory part to 'shows', and file
676 * name part to front of 'fname'. */
677 *bufp = '\0';
678 strncpy(shows, fname, sizeof(shows));
679 shows[sizeof(shows)-1] = '\0';
680 strncpy(fname, bufp+1, MIN(strlen(bufp+1)+1, sizeof(fname)));
681 fname[sizeof(fname)-1] = '\0';
686 /* If we did not end up with a valid directory, use home. */
687 if (!*shows || !isdir (shows, NULL, NULL)){
688 strncpy(shows, ((gmode & MDTREE) || opertree[0])
689 ? opertree
690 : (browse_dir[0] ? browse_dir
691 : gethomedir(NULL)), sizeof(shows));
692 shows[sizeof(shows)-1] = '\0';
695 strncpy(origshows, shows, sizeof(origshows));
696 origshows[sizeof(origshows)-1] = '\0';
697 if ((s = FileBrowse(shows, sizeof(shows), fname, sizeof(fname), NULL, 0,
698 FB_SAVE, NULL)) == 1) {
699 if (strlen(shows)+strlen(S_FILESEP)+strlen(fname) < NLINE){
700 strncat(shows, S_FILESEP, sizeof(shows)-strlen(shows)-1);
701 shows[sizeof(shows)-1] = '\0';
702 strncat(shows, fname, sizeof(shows)-strlen(shows)-1);
703 shows[sizeof(shows)-1] = '\0';
704 strncpy(fname, shows, sizeof(fname));
705 fname[sizeof(fname)-1] = '\0';
707 else {
708 emlwrite("Cannot write. File name too long!!",NULL);
709 sleep(3);
712 else if (s == 0 && strcmp(shows, origshows)){
713 strncat(shows, S_FILESEP, sizeof(shows)-strlen(shows)-1);
714 shows[sizeof(shows)-1] = '\0';
715 strncat(shows, fname, sizeof(shows)-strlen(shows)-1);
716 shows[sizeof(shows)-1] = '\0';
717 strncpy(fname, shows, sizeof(fname));
718 fname[sizeof(fname)-1] = '\0';
720 else if (s == -1){
721 emlwrite("Cannot write. File name too long!!",NULL);
722 sleep(3);
725 pico_refresh(FALSE, 1);
726 update();
727 if(s == 1)
728 break;
729 else
730 continue;
732 case HELPCH:
733 pico_help(writehelp, "", 1);
734 case (CTRL|'L'):
735 pico_refresh(FALSE, 1);
736 update();
737 continue;
739 default:
740 return(s);
741 break;
744 if(strcmp(fname, curbp->b_fname) == 0)
745 break;
747 if((s=fexist(fname, "w", (off_t *)NULL)) == FIOSUC){
748 /*exists overwrite? */
750 snprintf(shows, sizeof(shows), _("File \"%s\" exists, OVERWRITE"), fname);
751 if((s=mlyesno_utf8(shows, FALSE)) == TRUE)
752 break;
754 else if(s == FIOFNF){
755 break; /* go write it */
757 else{ /* some error, can't write */
758 fioperr(s, fname);
759 return(ABORT);
763 emlwrite("Writing...", NULL);
765 if ((s=writeout(fname, 0)) != -1) {
766 if(!(gmode&MDTOOL)){
767 strncpy(curbp->b_fname, fname, sizeof(curbp->b_fname));
768 curbp->b_fname[sizeof(curbp->b_fname)-1] = '\0';
769 curbp->b_flag &= ~BFCHG;
771 wp = wheadp; /* Update mode lines. */
772 while (wp != NULL) {
773 if (wp->w_bufp == curbp)
774 if((Pmaster && s == TRUE) || Pmaster == NULL)
775 wp->w_flag |= WFMODE;
776 wp = wp->w_wndp;
780 if(s > 1){
781 eml.s = comatose(s);
782 emlwrite(_("Wrote %s lines"), &eml);
784 else
785 emlwrite(_("Wrote 1 line"), NULL);
788 return ((s == -1) ? FALSE : TRUE);
793 * Save the contents of the current
794 * buffer in its associated file. No nothing
795 * if nothing has changed (this may be a bug, not a
796 * feature). Error if there is no remembered file
797 * name for the buffer. Bound to "C-X C-S". May
798 * get called by "C-Z".
801 filesave(int f, int n)
803 register WINDOW *wp;
804 register int s;
805 EML eml;
807 if (curbp->b_mode&MDVIEW) /* don't allow this command if */
808 return(rdonly()); /* we are in read only mode */
809 if ((curbp->b_flag&BFCHG) == 0) /* Return, no changes. */
810 return (TRUE);
811 if (curbp->b_fname[0] == 0) { /* Must have a name. */
812 emlwrite(_("No file name"), NULL);
813 sleep(2);
814 return (FALSE);
817 emlwrite("Writing...", NULL);
818 if ((s=writeout(curbp->b_fname, 0)) != -1) {
819 curbp->b_flag &= ~BFCHG;
820 wp = wheadp; /* Update mode lines. */
821 while (wp != NULL) {
822 if (wp->w_bufp == curbp)
823 if(Pmaster == NULL)
824 wp->w_flag |= WFMODE;
825 wp = wp->w_wndp;
827 if(s > 1){
828 eml.s = comatose(s);
829 emlwrite(_("Wrote %s lines"), &eml);
831 else
832 emlwrite(_("Wrote 1 line"), NULL);
834 return (s);
839 * This function performs the details of file
840 * writing. Uses the file management routines in the
841 * "fileio.c" package. The number of lines written is
842 * displayed. Sadly, it looks inside a LINE; provide
843 * a macro for this. Most of the grief is error
844 * checking of some sort.
846 * If the argument readonly is set, the file is created with user read
847 * and write permission only if it doesn't already exist. Note that the
848 * word readonly is misleading. It is user r/w permission only.
850 * CHANGES: 1 Aug 91: returns number of lines written or -1 on error, MSS
853 writeout(char *fn, int readonly)
855 register int s;
856 register int t;
857 register LINE *lp;
858 register int nline;
860 if (!((s = ffwopen(fn, readonly)) == FIOSUC && ffelbowroom()))
861 return (-1); /* Open writes message. */
863 lp = lforw(curbp->b_linep); /* First line. */
864 nline = 0; /* Number of lines. */
865 while (lp != curbp->b_linep) {
866 if ((s=ffputline(&lp->l_text[0], llength(lp))) != FIOSUC)
867 break;
868 ++nline;
869 lp = lforw(lp);
872 t = ffclose(); /* ffclose complains if error */
874 if(s == FIOSUC)
875 s = t; /* report worst case */
877 return ((s == FIOSUC) ? nline : -1);
882 * writetmp - write a temporary file for message text, mindful of
883 * access restrictions and included text. If n is true, include
884 * lines that indicated included message text, otw forget them
885 * If dir is non-null, put the temp file in that directory.
887 char *
888 writetmp(int n, char *dir)
890 static char fn[NFILEN];
891 register int s;
892 register int t;
893 register LINE *lp;
894 register int nline;
896 tmpname(dir, fn);
898 /* Open writes message */
899 if (!fn[0] || (s=ffwopen(fn, TRUE)) != FIOSUC){
900 if(fn[0])
901 our_unlink(fn);
903 return(NULL);
906 lp = lforw(curbp->b_linep); /* First line. */
907 nline = 0; /* Number of lines. */
908 while (lp != curbp->b_linep) {
909 if(n || (!n && lp->l_text[0].c != '>'))
910 if ((s=ffputline(&lp->l_text[0], llength(lp))) != FIOSUC)
911 break;
913 ++nline;
914 lp = lforw(lp);
917 t = ffclose(); /* ffclose complains if error */
919 if(s == FIOSUC)
920 s = t; /* remember worst case */
922 if (s != FIOSUC){ /* Some sort of error. */
923 our_unlink(fn);
924 return(NULL);
927 return(fn);
932 * Insert file "fname" into the current
933 * buffer, Called by insert file command. Return the final
934 * status of the read.
937 ifile(char fname[])
939 UCS line[NLINE], *linep;
940 long nline;
941 int s, done, newline;
942 size_t charsread = 0;
943 EML eml;
945 if ((s=ffropen(fname)) != FIOSUC){ /* Hard file open. */
946 fioperr(s, fname);
947 return(FALSE);
950 gotobol(FALSE, 1);
952 curbp->b_flag |= BFCHG; /* we have changed */
953 curbp->b_flag &= ~BFTEMP; /* and are not temporary*/
954 curbp->b_linecnt = -1; /* must be recalculated */
956 eml.s = fname;
957 emlwrite(_("Inserting %s."), &eml);
958 done = newline = 0;
959 nline = 0L;
960 while(!done)
961 if((s = ffgetline(line, NLINE, &charsread, 1)) == FIOEOF){
962 char b[200];
964 if(llength(curwp->w_dotp) > curwp->w_doto)
965 lnewline();
966 else
967 forwchar(FALSE, 1);
969 if(nline > 1)
970 snprintf(b, sizeof(b), _("Inserted %ld lines"), nline);
971 else
972 snprintf(b, sizeof(b), _("Inserted 1 line"));
974 emlwrite(b, NULL);
975 break;
977 else{
978 if(newline){
979 if(lnewline() == FALSE){
980 done++;
981 continue;
983 newline = 0;
986 switch(s){
987 case FIOSUC : /* copy line into buf */
988 nline++;
989 newline = 1;
991 case FIOLNG :
992 for(linep = line; charsread-- > 0; linep++)
993 if(linsert(1, *linep) == FALSE) done++;
995 break;
997 default :
998 done++;
1002 ffclose(); /* Ignore errors. */
1004 return(s != FIOERR);
1009 * pico_fncomplete - pico's function to complete the given file name
1012 pico_fncomplete(char *dirarg, char *fn, size_t fnlen)
1014 char *p, *dlist, tmp[NLINE], dir[NLINE];
1015 int n, i, match = -1, orign = 0;
1016 #ifdef DOS
1017 #define FILECMP(x, y) (toupper((unsigned char)(x))\
1018 == toupper((unsigned char)(y)))
1019 #else
1020 #define FILECMP(x, y) ((x) == (y))
1021 #endif
1023 strncpy(dir, dirarg, sizeof(dir));
1024 dir[sizeof(dir)-1] = '\0';
1025 pfnexpand(dir, sizeof(dir));
1026 if((dlist = p = getfnames(dir, fn, &n, NULL, 0))){
1027 orign = n;
1028 memset(tmp, 0, sizeof(tmp));
1029 while(n--){ /* any names in it */
1030 if(strcmp(p, ".") == 0 || strcmp(p, "..") == 0){
1031 p += strlen(p) + 1;
1032 orign--;
1033 continue;
1036 for(i = 0; i < sizeof(tmp)-1 && fn[i] && FILECMP(p[i], fn[i]); i++)
1039 if(!fn[i]){ /* match or more? */
1040 if(tmp[0]){
1041 for(; p[i] && FILECMP(p[i], tmp[i]); i++)
1044 match = !p[i] && !tmp[i];
1045 tmp[i] = '\0'; /* longest common string */
1047 else{
1048 match = 1; /* may be it!?! */
1049 strncpy(tmp, p, sizeof(tmp));
1050 tmp[sizeof(tmp)-1] = '\0';
1054 p += strlen(p) + 1;
1057 free(dlist);
1060 if(fn[0] == '\0' && orign != 1)
1061 match = -1;
1063 if(match >= 0){
1064 strncpy(fn, tmp, fnlen);
1065 fn[fnlen-1] = '\0';
1066 if(match == 1){
1067 if ((strlen(dir)+strlen(S_FILESEP)+strlen(fn)) < sizeof(dir)){
1068 strncat(dir, S_FILESEP, sizeof(dir)-strlen(dir)-1);
1069 dir[sizeof(dir)-1] = '\0';
1070 strncat(dir, fn, sizeof(dir)-strlen(dir)-1);
1071 dir[sizeof(dir)-1] = '\0';
1072 if(isdir(dir, NULL, NULL)){
1073 strncat(fn, S_FILESEP, fnlen-strlen(fn)-1);
1074 fn[fnlen-1] = '\0';
1077 else{
1078 emlwrite("File name too BIG!!",0);
1079 sleep(3);
1080 *fn = '\0';
1086 return(match == 1);
1091 * in_oper_tree - returns true if file "f" does reside in opertree
1094 in_oper_tree(char *f)
1096 int end = strlen(opertree);
1098 return(!strncmp(opertree, f, end)
1099 && (opertree[end-1] == '/'
1100 || opertree[end-1] == '\\'
1101 || f[end] == '\0'
1102 || f[end] == '/'
1103 || f[end] == '\\'));