]> pd.if.org Git - pd_readline/blob - mg/file.c
Added mg from an OpenBSD mirror site. Many thanks to the OpenBSD team and the mg...
[pd_readline] / mg / file.c
1 /*      $OpenBSD: file.c,v 1.81 2012/06/18 09:19:21 lum Exp $   */
2
3 /* This file is in the public domain. */
4
5 /*
6  *      File commands.
7  */
8
9 #include "def.h"
10
11 #include <sys/stat.h>
12
13 #include <libgen.h>
14
15 size_t xdirname(char *, const char *, size_t);
16
17 /*
18  * Insert a file into the current buffer.  Real easy - just call the
19  * insertfile routine with the file name.
20  */
21 /* ARGSUSED */
22 int
23 fileinsert(int f, int n)
24 {
25         char     fname[NFILEN], *bufp, *adjf;
26
27         if (getbufcwd(fname, sizeof(fname)) != TRUE)
28                 fname[0] = '\0';
29         bufp = eread("Insert file: ", fname, NFILEN,
30             EFNEW | EFCR | EFFILE | EFDEF);
31         if (bufp == NULL)
32                 return (ABORT);
33         else if (bufp[0] == '\0')
34                 return (FALSE);
35         adjf = adjustname(bufp, TRUE);
36         if (adjf == NULL)
37                 return (FALSE);
38         return (insertfile(adjf, NULL, FALSE));
39 }
40
41 /*
42  * Select a file for editing.  Look around to see if you can find the file
43  * in another buffer; if you can find it, just switch to the buffer.  If
44  * you cannot find the file, create a new buffer, read in the text, and
45  * switch to the new buffer.
46  */
47 /* ARGSUSED */
48 int
49 filevisit(int f, int n)
50 {
51         struct buffer   *bp;
52         char     fname[NFILEN], *bufp, *adjf;
53         int      status;
54
55         if (getbufcwd(fname, sizeof(fname)) != TRUE)
56                 fname[0] = '\0';
57         bufp = eread("Find file: ", fname, NFILEN,
58             EFNEW | EFCR | EFFILE | EFDEF);
59         if (bufp == NULL)
60                 return (ABORT);
61         else if (bufp[0] == '\0')
62                 return (FALSE);
63         adjf = adjustname(fname, TRUE);
64         if (adjf == NULL)
65                 return (FALSE);
66         if ((bp = findbuffer(adjf)) == NULL)
67                 return (FALSE);
68         curbp = bp;
69         if (showbuffer(bp, curwp, WFFULL) != TRUE)
70                 return (FALSE);
71         if (bp->b_fname[0] == '\0') {
72                 if ((status = readin(adjf)) != TRUE)
73                         killbuffer(bp);
74                 return (status);
75         }
76         return (TRUE);
77 }
78
79 /*
80  * Replace the current file with an alternate one. Semantics for finding
81  * the replacement file are the same as 'filevisit', except the current
82  * buffer is killed before the switch. If the kill fails, or is aborted,
83  * revert to the original file.
84  */
85 /* ARGSUSED */
86 int
87 filevisitalt(int f, int n)
88 {
89         struct buffer   *bp;
90         char     fname[NFILEN], *bufp, *adjf;
91         int      status;
92
93         if (getbufcwd(fname, sizeof(fname)) != TRUE)
94                 fname[0] = '\0';
95         bufp = eread("Find alternate file: ", fname, NFILEN,
96             EFNEW | EFCR | EFFILE | EFDEF);
97         if (bufp == NULL)
98                 return (ABORT);
99         else if (bufp[0] == '\0')
100                 return (FALSE);
101
102         status = killbuffer(curbp);
103         if (status == ABORT || status == FALSE)
104                 return (ABORT);
105
106         adjf = adjustname(fname, TRUE);
107         if (adjf == NULL)
108                 return (FALSE);
109         if ((bp = findbuffer(adjf)) == NULL)
110                 return (FALSE);
111         curbp = bp;
112         if (showbuffer(bp, curwp, WFFULL) != TRUE)
113                 return (FALSE);
114         if (bp->b_fname[0] == '\0') {
115                 if ((status = readin(adjf)) != TRUE)
116                         killbuffer(bp);
117                 return (status);
118         }
119         return (TRUE);
120 }
121
122 int
123 filevisitro(int f, int n)
124 {
125         int error;
126
127         error = filevisit(f, n);
128         if (error != TRUE)
129                 return (error);
130         curbp->b_flag |= BFREADONLY;
131         return (TRUE);
132 }
133
134 /*
135  * Pop to a file in the other window.  Same as the last function, but uses
136  * popbuf instead of showbuffer.
137  */
138 /* ARGSUSED */
139 int
140 poptofile(int f, int n)
141 {
142         struct buffer   *bp;
143         struct mgwin    *wp;
144         char     fname[NFILEN], *adjf, *bufp;
145         int      status;
146
147         if (getbufcwd(fname, sizeof(fname)) != TRUE)
148                 fname[0] = '\0';
149         if ((bufp = eread("Find file in other window: ", fname, NFILEN,
150             EFNEW | EFCR | EFFILE | EFDEF)) == NULL)
151                 return (ABORT);
152         else if (bufp[0] == '\0')
153                 return (FALSE);
154         adjf = adjustname(fname, TRUE);
155         if (adjf == NULL)
156                 return (FALSE);
157         if ((bp = findbuffer(adjf)) == NULL)
158                 return (FALSE);
159         if (bp == curbp)
160                 return (splitwind(f, n));
161         if ((wp = popbuf(bp, WNONE)) == NULL)
162                 return (FALSE);
163         curbp = bp;
164         curwp = wp;
165         if (bp->b_fname[0] == '\0') {
166                 if ((status = readin(adjf)) != TRUE)
167                         killbuffer(bp);
168                 return (status);
169         }
170         return (TRUE);
171 }
172
173 /*
174  * Given a file name, either find the buffer it uses, or create a new
175  * empty buffer to put it in.
176  */
177 struct buffer *
178 findbuffer(char *fn)
179 {
180         struct buffer   *bp;
181         char            bname[NBUFN], fname[NBUFN];
182
183         if (strlcpy(fname, fn, sizeof(fname)) >= sizeof(fname)) {
184                 ewprintf("filename too long");
185                 return (NULL);
186         }
187
188         for (bp = bheadp; bp != NULL; bp = bp->b_bufp) {
189                 if (strcmp(bp->b_fname, fname) == 0)
190                         return (bp);
191         }
192         /* Not found. Create a new one, adjusting name first */
193         if (augbname(bname, fname, sizeof(bname)) == FALSE)
194                 return (NULL);
195
196         bp = bfind(bname, TRUE);
197         return (bp);
198 }
199
200 /*
201  * Read the file "fname" into the current buffer.  Make all of the text
202  * in the buffer go away, after checking for unsaved changes.  This is
203  * called by the "read" command, the "visit" command, and the mainline
204  * (for "mg file").
205  */
206 int
207 readin(char *fname)
208 {
209         struct mgwin    *wp;
210         int      status, i, ro = FALSE;
211         PF      *ael;
212
213         /* might be old */
214         if (bclear(curbp) != TRUE)
215                 return (TRUE);
216         /* Clear readonly. May be set by autoexec path */
217         curbp->b_flag &= ~BFREADONLY;
218         if ((status = insertfile(fname, fname, TRUE)) != TRUE) {
219                 ewprintf("File is not readable: %s", fname);
220                 return (FALSE);
221         }
222
223         for (wp = wheadp; wp != NULL; wp = wp->w_wndp) {
224                 if (wp->w_bufp == curbp) {
225                         wp->w_dotp = wp->w_linep = bfirstlp(curbp);
226                         wp->w_doto = 0;
227                         wp->w_markp = NULL;
228                         wp->w_marko = 0;
229                 }
230         }
231
232         /*
233          * Call auto-executing function if we need to.
234          */
235         if ((ael = find_autoexec(fname)) != NULL) {
236                 for (i = 0; ael[i] != NULL; i++)
237                         (*ael[i])(0, 1);
238                 free(ael);
239         }
240
241         /* no change */
242         curbp->b_flag &= ~BFCHG;
243
244         /*
245          * We need to set the READONLY flag after we insert the file,
246          * unless the file is a directory.
247          */
248         if (access(fname, W_OK) && errno != ENOENT)
249                 ro = TRUE;
250         if (fisdir(fname) == TRUE)
251                 ro = TRUE;
252         if (ro == TRUE)
253                 curbp->b_flag |= BFREADONLY;
254
255         if (startrow) {
256                 gotoline(FFARG, startrow);
257                 startrow = 0;
258         }
259
260         undo_add_modified();
261         return (status);
262 }
263
264 /*
265  * NB, getting file attributes is done here under control of a flag
266  * rather than in readin, which would be cleaner.  I was concerned
267  * that some operating system might require the file to be open
268  * in order to get the information.  Similarly for writing.
269  */
270
271 /*
272  * Insert a file in the current buffer, after dot. If file is a directory,
273  * and 'replacebuf' is TRUE, invoke dired mode, else die with an error.
274  * If file is a regular file, set mark at the end of the text inserted;
275  * point at the beginning.  Return a standard status. Print a summary
276  * (lines read, error message) out as well. This routine also does the
277  * read end of backup processing.  The BFBAK flag, if set in a buffer,
278  * says that a backup should be taken.  It is set when a file is read in,
279  * but not on a new file. You don't need to make a backup copy of nothing.
280  */
281
282 static char     *line = NULL;
283 static int      linesize = 0;
284
285 int
286 insertfile(char *fname, char *newname, int replacebuf)
287 {
288         struct buffer   *bp;
289         struct line     *lp1, *lp2;
290         struct line     *olp;                   /* line we started at */
291         struct mgwin    *wp;
292         int      nbytes, s, nline = 0, siz, x, x2;
293         int      opos;                  /* offset we started at */
294         int      oline;                 /* original line number */
295         FILE    *ffp;
296
297         if (replacebuf == TRUE)
298                 x = undo_enable(FFRAND, 0);
299         else
300                 x = undo_enabled();
301
302         lp1 = NULL;
303         if (line == NULL) {
304                 line = malloc(NLINE);
305                 if (line == NULL)
306                         panic("out of memory");
307                 linesize = NLINE;
308         }
309
310         /* cheap */
311         bp = curbp;
312         if (newname != NULL) {
313                 (void)strlcpy(bp->b_fname, newname, sizeof(bp->b_fname));
314                 (void)xdirname(bp->b_cwd, newname, sizeof(bp->b_cwd));
315                 (void)strlcat(bp->b_cwd, "/", sizeof(bp->b_cwd));
316         }
317
318         /* hard file open */
319         if ((s = ffropen(&ffp, fname, (replacebuf == TRUE) ? bp : NULL))
320             == FIOERR)
321                 goto out;
322         if (s == FIOFNF) {
323                 /* file not found */
324                 if (newname != NULL)
325                         ewprintf("(New file)");
326                 else
327                         ewprintf("(File not found)");
328                 goto out;
329         } else if (s == FIODIR) {
330                 /* file was a directory */
331                 if (replacebuf == FALSE) {
332                         ewprintf("Cannot insert: file is a directory, %s",
333                             fname);
334                         goto cleanup;
335                 }
336                 killbuffer(bp);
337                 bp = dired_(fname);
338                 undo_enable(FFRAND, x);
339                 if (bp == NULL)
340                         return (FALSE);
341                 curbp = bp;
342                 return (showbuffer(bp, curwp, WFFULL | WFMODE));
343         } else {
344                 (void)xdirname(bp->b_cwd, fname, sizeof(bp->b_cwd));
345                 (void)strlcat(bp->b_cwd, "/", sizeof(bp->b_cwd));
346         }
347         opos = curwp->w_doto;
348         oline = curwp->w_dotline;
349         /*
350          * Open a new line at dot and start inserting after it.
351          * We will delete this newline after insertion.
352          * Disable undo, as we create the undo record manually.
353          */
354         x2 = undo_enable(FFRAND, 0);
355         (void)lnewline();
356         olp = lback(curwp->w_dotp);
357         undo_enable(FFRAND, x2);
358
359         nline = 0;
360         siz = 0;
361         while ((s = ffgetline(ffp, line, linesize, &nbytes)) != FIOERR) {
362 retry:
363                 siz += nbytes + 1;
364                 switch (s) {
365                 case FIOSUC:
366                         /* FALLTHRU */
367                 case FIOEOF:
368                         ++nline;
369                         if ((lp1 = lalloc(nbytes)) == NULL) {
370                                 /* keep message on the display */
371                                 s = FIOERR;
372                                 undo_add_insert(olp, opos,
373                                     siz - nbytes - 1 - 1);
374                                 goto endoffile;
375                         }
376                         bcopy(line, &ltext(lp1)[0], nbytes);
377                         lp2 = lback(curwp->w_dotp);
378                         lp2->l_fp = lp1;
379                         lp1->l_fp = curwp->w_dotp;
380                         lp1->l_bp = lp2;
381                         curwp->w_dotp->l_bp = lp1;
382                         if (s == FIOEOF) {
383                                 undo_add_insert(olp, opos, siz - 1);
384                                 goto endoffile;
385                         }
386                         break;
387                 case FIOLONG: {
388                                 /* a line too long to fit in our buffer */
389                                 char    *cp;
390                                 int     newsize;
391
392                                 newsize = linesize * 2;
393                                 if (newsize < 0 ||
394                                     (cp = malloc(newsize)) == NULL) {
395                                         ewprintf("Could not allocate %d bytes",
396                                             newsize);
397                                                 s = FIOERR;
398                                                 goto endoffile;
399                                 }
400                                 bcopy(line, cp, linesize);
401                                 free(line);
402                                 line = cp;
403                                 s = ffgetline(ffp, line + linesize, linesize,
404                                     &nbytes);
405                                 nbytes += linesize;
406                                 linesize = newsize;
407                                 if (s == FIOERR)
408                                         goto endoffile;
409                                 goto retry;
410                         }
411                 default:
412                         ewprintf("Unknown code %d reading file", s);
413                         s = FIOERR;
414                         break;
415                 }
416         }
417 endoffile:
418         /* ignore errors */
419         (void)ffclose(ffp, NULL);
420         /* don't zap an error */
421         if (s == FIOEOF) {
422                 if (nline == 1)
423                         ewprintf("(Read 1 line)");
424                 else
425                         ewprintf("(Read %d lines)", nline);
426         }
427         /* set mark at the end of the text */
428         curwp->w_dotp = curwp->w_markp = lback(curwp->w_dotp);
429         curwp->w_marko = llength(curwp->w_markp);
430         curwp->w_markline = oline + nline + 1;
431         /*
432          * if we are at the end of the file, ldelnewline is a no-op,
433          * but we still need to decrement the line and markline counts
434          * as we've accounted for this fencepost in our arithmetic
435          */
436         if (lforw(curwp->w_dotp) == curwp->w_bufp->b_headp) {
437                 curwp->w_bufp->b_lines--;
438                 curwp->w_markline--;
439         } else
440                 (void)ldelnewline();
441         curwp->w_dotp = olp;
442         curwp->w_doto = opos;
443         curwp->w_dotline = oline;
444         if (olp == curbp->b_headp)
445                 curwp->w_dotp = lforw(olp);
446         if (newname != NULL)
447                 bp->b_flag |= BFCHG | BFBAK;    /* Need a backup.        */
448         else
449                 bp->b_flag |= BFCHG;
450         /*
451          * If the insert was at the end of buffer, set lp1 to the end of
452          * buffer line, and lp2 to the beginning of the newly inserted text.
453          * (Otherwise lp2 is set to NULL.)  This is used below to set
454          * pointers in other windows correctly if they are also at the end of
455          * buffer.
456          */
457         lp1 = bp->b_headp;
458         if (curwp->w_markp == lp1) {
459                 lp2 = curwp->w_dotp;
460         } else {
461                 /* delete extraneous newline */
462                 (void)ldelnewline();
463 out:            lp2 = NULL;
464         }
465         for (wp = wheadp; wp != NULL; wp = wp->w_wndp) {
466                 if (wp->w_bufp == curbp) {
467                         wp->w_rflag |= WFMODE | WFEDIT;
468                         if (wp != curwp && lp2 != NULL) {
469                                 if (wp->w_dotp == lp1)
470                                         wp->w_dotp = lp2;
471                                 if (wp->w_markp == lp1)
472                                         wp->w_markp = lp2;
473                                 if (wp->w_linep == lp1)
474                                         wp->w_linep = lp2;
475                         }
476                 }
477         }
478         bp->b_lines += nline;
479 cleanup:
480         undo_enable(FFRAND, x);
481
482         /* return FALSE if error */
483         return (s != FIOERR);
484 }
485
486 /*
487  * Ask for a file name and write the contents of the current buffer to that
488  * file.  Update the remembered file name and clear the buffer changed flag.
489  * This handling of file names is different from the earlier versions and
490  * is more compatible with Gosling EMACS than with ITS EMACS.
491  */
492 /* ARGSUSED */
493 int
494 filewrite(int f, int n)
495 {
496         struct stat     statbuf;
497         int      s;
498         char     fname[NFILEN], bn[NBUFN], tmp[NFILEN + 25];
499         char    *adjfname, *bufp;
500         FILE    *ffp;
501
502         if (getbufcwd(fname, sizeof(fname)) != TRUE)
503                 fname[0] = '\0';
504         if ((bufp = eread("Write file: ", fname, NFILEN,
505             EFDEF | EFNEW | EFCR | EFFILE)) == NULL)
506                 return (ABORT);
507         else if (bufp[0] == '\0')
508                 return (FALSE);
509
510         adjfname = adjustname(fname, TRUE);
511         if (adjfname == NULL)
512                 return (FALSE);
513
514         /* Check if file exists; write checks done later */
515         if (stat(adjfname, &statbuf) == 0) {
516                 snprintf(tmp, sizeof(tmp), "File `%s' exists; overwrite",
517                     adjfname);
518                 if ((s = eyorn(tmp)) != TRUE)
519                         return (s);
520         }
521
522         /* old attributes are no longer current */
523         bzero(&curbp->b_fi, sizeof(curbp->b_fi));
524         if ((s = writeout(&ffp, curbp, adjfname)) == TRUE) {
525                 (void)strlcpy(curbp->b_fname, adjfname, sizeof(curbp->b_fname));
526                 if (getbufcwd(curbp->b_cwd, sizeof(curbp->b_cwd)) != TRUE)
527                         (void)strlcpy(curbp->b_cwd, "/", sizeof(curbp->b_cwd));
528                 if (augbname(bn, curbp->b_fname, sizeof(bn))
529                     == FALSE)
530                         return (FALSE);
531                 free(curbp->b_bname);
532                 if ((curbp->b_bname = strdup(bn)) == NULL)
533                         return (FALSE);
534                 (void)fupdstat(curbp);
535                 curbp->b_flag &= ~(BFBAK | BFCHG);
536                 upmodes(curbp);
537         }
538         return (s);
539 }
540
541 /*
542  * Save the contents of the current buffer back into its associated file.
543  */
544 #ifndef MAKEBACKUP
545 #define MAKEBACKUP TRUE
546 #endif /* !MAKEBACKUP */
547 static int      makebackup = MAKEBACKUP;
548
549 /* ARGSUSED */
550 int
551 filesave(int f, int n)
552 {
553         if (curbp->b_fname[0] == '\0')
554                 return (filewrite(f, n));
555         else
556                 return (buffsave(curbp));
557 }
558
559 /*
560  * Save the contents of the buffer argument into its associated file.  Do
561  * nothing if there have been no changes (is this a bug, or a feature?).
562  * Error if there is no remembered file name. If this is the first write
563  * since the read or visit, then a backup copy of the file is made.
564  * Allow user to select whether or not to make backup files by looking at
565  * the value of makebackup.
566  */
567 int
568 buffsave(struct buffer *bp)
569 {
570         int      s;
571         FILE    *ffp;
572
573         /* return, no changes */
574         if ((bp->b_flag & BFCHG) == 0) {
575                 ewprintf("(No changes need to be saved)");
576                 return (TRUE);
577         }
578
579         /* must have a name */
580         if (bp->b_fname[0] == '\0') {
581                 ewprintf("No file name");
582                 return (FALSE);
583         }
584
585         /* Ensure file has not been modified elsewhere */
586         /* We don't use the ignore flag here */
587         if (fchecktime(bp) != TRUE) {
588                 if ((s = eyesno("File has changed on disk since last save. "
589                     "Save anyway")) != TRUE)
590                         return (s);
591         }
592         
593         if (makebackup && (bp->b_flag & BFBAK)) {
594                 s = fbackupfile(bp->b_fname);
595                 /* hard error */
596                 if (s == ABORT)
597                         return (FALSE);
598                 /* softer error */
599                 if (s == FALSE &&
600                     (s = eyesno("Backup error, save anyway")) != TRUE)
601                         return (s);
602         }
603         if ((s = writeout(&ffp, bp, bp->b_fname)) == TRUE) {
604                 (void)fupdstat(bp);
605                 bp->b_flag &= ~(BFCHG | BFBAK);
606                 upmodes(bp);
607         }
608         return (s);
609 }
610
611 /*
612  * Since we don't have variables (we probably should) this is a command
613  * processor for changing the value of the make backup flag.  If no argument
614  * is given, sets makebackup to true, so backups are made.  If an argument is
615  * given, no backup files are made when saving a new version of a file.
616  */
617 /* ARGSUSED */
618 int
619 makebkfile(int f, int n)
620 {
621         if (f & FFARG)
622                 makebackup = n > 0;
623         else
624                 makebackup = !makebackup;
625         ewprintf("Backup files %sabled", makebackup ? "en" : "dis");
626         return (TRUE);
627 }
628
629 /*
630  * NB: bp is passed to both ffwopen and ffclose because some
631  * attribute information may need to be updated at open time
632  * and others after the close.  This is OS-dependent.  Note
633  * that the ff routines are assumed to be able to tell whether
634  * the attribute information has been set up in this buffer
635  * or not.
636  */
637
638 /*
639  * This function performs the details of file writing; writing the file
640  * in buffer bp to file fn. Uses the file management routines in the
641  * "fileio.c" package. Most of the grief is checking of some sort.
642  * You may want to call fupdstat() after using this function.
643  */
644 int
645 writeout(FILE ** ffp, struct buffer *bp, char *fn)
646 {
647         int      s;
648
649         /* open writes message */
650         if ((s = ffwopen(ffp, fn, bp)) != FIOSUC)
651                 return (FALSE);
652         s = ffputbuf(*ffp, bp);
653         if (s == FIOSUC) {
654                 /* no write error */
655                 s = ffclose(*ffp, bp);
656                 if (s == FIOSUC)
657                         ewprintf("Wrote %s", fn);
658         } else {
659                 /* print a message indicating write error */
660                 (void)ffclose(*ffp, bp);
661                 ewprintf("Unable to write %s", fn);
662         }
663         return (s == FIOSUC);
664 }
665
666 /*
667  * Tag all windows for bp (all windows if bp == NULL) as needing their
668  * mode line updated.
669  */
670 void
671 upmodes(struct buffer *bp)
672 {
673         struct mgwin    *wp;
674
675         for (wp = wheadp; wp != NULL; wp = wp->w_wndp)
676                 if (bp == NULL || curwp->w_bufp == bp)
677                         wp->w_rflag |= WFMODE;
678 }
679
680 /*
681  * dirname using strlcpy semantic.
682  * Like dirname() except an empty string is returned in
683  * place of "/". This means we can always add a trailing
684  * slash and be correct.
685  * Address portability issues by copying argument
686  * before using. Some implementations modify the input string.
687  */
688 size_t
689 xdirname(char *dp, const char *path, size_t dplen)
690 {
691         char ts[NFILEN];
692         size_t len;
693
694         (void)strlcpy(ts, path, NFILEN);
695         len = strlcpy(dp, dirname(ts), dplen);
696         if (dplen > 0 && dp[0] == '/' && dp[1] == '\0') {
697                 dp[0] = '\0';
698                 len = 0;
699         }
700         return (len);
701 }
702
703 /*
704  * basename using strlcpy/strlcat semantic.
705  * Address portability issue by copying argument
706  * before using: some implementations modify the input string.
707  */
708 size_t
709 xbasename(char *bp, const char *path, size_t bplen)
710 {
711         char ts[NFILEN];
712
713         (void)strlcpy(ts, path, NFILEN);
714         return (strlcpy(bp, basename(ts), bplen));
715 }