Annotation of src/usr.bin/mg/file.c, Revision 1.21
1.21 ! vincent 1: /* $OpenBSD: file.c,v 1.20 2002/07/01 14:33:44 vincent Exp $ */
1.5 niklas 2:
1.1 deraadt 3: /*
1.4 millert 4: * File commands.
1.1 deraadt 5: */
6:
1.6 art 7: #include <libgen.h>
1.4 millert 8: #include "def.h"
9:
1.1 deraadt 10: /*
1.4 millert 11: * Insert a file into the current buffer. Real easy - just call the
1.1 deraadt 12: * insertfile routine with the file name.
13: */
1.3 millert 14: /* ARGSUSED */
15: int
1.15 vincent 16: fileinsert(int f, int n)
1.1 deraadt 17: {
1.4 millert 18: int s;
19: char fname[NFILEN];
1.1 deraadt 20:
1.3 millert 21: s = eread("Insert file: ", fname, NFILEN, EFNEW | EFCR | EFFILE);
22: if (s != TRUE)
1.1 deraadt 23: return (s);
1.8 art 24: return insertfile(adjustname(fname), NULL, FALSE);
1.3 millert 25: /* don't set buffer name */
1.1 deraadt 26: }
27:
28: /*
1.9 mickey 29: * Select a file for editing. Look around to see if you can find the file
1.4 millert 30: * in another buffer; if you can find it, just switch to the buffer. If
1.9 mickey 31: * you cannot find the file, create a new buffer, read in the text, and
1.4 millert 32: * switch to the new buffer.
1.1 deraadt 33: */
1.3 millert 34: /* ARGSUSED */
35: int
1.15 vincent 36: filevisit(int f, int n)
1.1 deraadt 37: {
1.4 millert 38: BUFFER *bp;
39: int s;
40: char fname[NFILEN];
41: char *adjf;
1.1 deraadt 42:
1.3 millert 43: s = eread("Find file: ", fname, NFILEN, EFNEW | EFCR | EFFILE);
44: if (s != TRUE)
1.1 deraadt 45: return s;
46: adjf = adjustname(fname);
1.3 millert 47: if ((bp = findbuffer(adjf)) == NULL)
48: return FALSE;
1.1 deraadt 49: curbp = bp;
1.3 millert 50: if (showbuffer(bp, curwp, WFHARD) != TRUE)
51: return FALSE;
1.1 deraadt 52: if (bp->b_fname[0] == 0)
1.4 millert 53: return readin(adjf);
1.1 deraadt 54: return TRUE;
55: }
56:
1.17 vincent 57: int
58: filevisitro(int f, int n)
59: {
60: int error;
61:
62: error = filevisit(f, n);
63: if (error != TRUE)
64: return (error);
65: curbp->b_flag |= BFREADONLY;
66: return (TRUE);
67: }
1.1 deraadt 68: /*
1.4 millert 69: * Pop to a file in the other window. Same as the last function, but uses
1.1 deraadt 70: * popbuf instead of showbuffer.
71: */
1.3 millert 72: /* ARGSUSED */
73: int
1.15 vincent 74: poptofile(int f, int n)
1.1 deraadt 75: {
1.4 millert 76: BUFFER *bp;
77: MGWIN *wp;
78: int s;
79: char fname[NFILEN];
80: char *adjf;
1.1 deraadt 81:
1.3 millert 82: if ((s = eread("Find file in other window: ", fname, NFILEN,
1.13 deraadt 83: EFNEW | EFCR | EFFILE)) != TRUE)
1.1 deraadt 84: return s;
85: adjf = adjustname(fname);
1.3 millert 86: if ((bp = findbuffer(adjf)) == NULL)
87: return FALSE;
88: if ((wp = popbuf(bp)) == NULL)
89: return FALSE;
1.1 deraadt 90: curbp = bp;
91: curwp = wp;
92: if (bp->b_fname[0] == 0)
1.4 millert 93: return readin(adjf);
1.1 deraadt 94: return TRUE;
95: }
96:
97: /*
98: * given a file name, either find the buffer it uses, or create a new
99: * empty buffer to put it in.
100: */
101: BUFFER *
1.14 vincent 102: findbuffer(char *fname)
1.1 deraadt 103: {
1.4 millert 104: BUFFER *bp;
1.10 vincent 105: char bname[NBUFN];
1.20 vincent 106: unsigned int count, remain, i;
1.1 deraadt 107:
1.3 millert 108: for (bp = bheadp; bp != NULL; bp = bp->b_bufp) {
1.6 art 109: if (strcmp(bp->b_fname, fname) == 0)
1.1 deraadt 110: return bp;
111: }
1.14 vincent 112: i = strlcpy(bname, basename(fname), sizeof(bname));
113: remain = sizeof(bname) - i;
114: for (count = 2; bfind(bname, FALSE) != NULL; count++)
115: snprintf(&bname[i], remain, "<%d>", count);
1.10 vincent 116:
1.1 deraadt 117: return bfind(bname, TRUE);
118: }
119:
120: /*
1.9 mickey 121: * Read the file "fname" into the current buffer. Make all of the text
122: * in the buffer go away, after checking for unsaved changes. This is
123: * called by the "read" command, the "visit" command, and the mainline
1.19 vincent 124: * (for "mg file").
1.1 deraadt 125: */
1.3 millert 126: int
1.19 vincent 127: readin(char *fname)
1.3 millert 128: {
1.4 millert 129: MGWIN *wp;
1.18 vincent 130: int status, i;
1.20 vincent 131: PF *ael;
1.1 deraadt 132:
1.4 millert 133: /* might be old */
134: if (bclear(curbp) != TRUE)
1.1 deraadt 135: return TRUE;
1.3 millert 136: status = insertfile(fname, fname, TRUE);
1.18 vincent 137:
138: /*
139: * Call auto-executing function if we need to.
140: */
141: if ((ael = find_autoexec(fname)) != NULL) {
142: for (i = 0; ael[i] != NULL; i++)
143: (*ael[i])(0, 1);
144: free(ael);
145: }
1.4 millert 146:
147: /* no change */
148: curbp->b_flag &= ~BFCHG;
1.3 millert 149: for (wp = wheadp; wp != NULL; wp = wp->w_wndp) {
1.1 deraadt 150: if (wp->w_bufp == curbp) {
1.3 millert 151: wp->w_dotp = wp->w_linep = lforw(curbp->b_linep);
152: wp->w_doto = 0;
1.1 deraadt 153: wp->w_markp = NULL;
154: wp->w_marko = 0;
155: }
156: }
1.16 vincent 157:
158: /* We need to set the READONLY flag after we insert the file */
159: if (access(fname, W_OK) && errno != ENOENT)
160: curbp->b_flag |= BFREADONLY;
161: else
162: curbp->b_flag &=~ BFREADONLY;
163:
1.1 deraadt 164: return status;
165: }
1.4 millert 166:
1.1 deraadt 167: /*
168: * NB, getting file attributes is done here under control of a flag
169: * rather than in readin, which would be cleaner. I was concerned
170: * that some operating system might require the file to be open
171: * in order to get the information. Similarly for writing.
172: */
173:
174: /*
1.9 mickey 175: * Insert a file in the current buffer, after dot. Set mark at the end of
176: * the text inserted; point at the beginning. Return a standard status.
177: * Print a summary (lines read, error message) out as well. If the BACKUP
178: * conditional is set, then this routine also does the read end of backup
179: * processing. The BFBAK flag, if set in a buffer, says that a backup
180: * should be taken. It is set when a file is read in, but not on a new
1.4 millert 181: * file. (You don't need to make a backup copy of nothing.)
1.1 deraadt 182: */
1.9 mickey 183: static char *line = NULL;
184: static int linesize = 0;
1.1 deraadt 185:
1.3 millert 186: int
1.20 vincent 187: insertfile(char *fname, char *newname, int needinfo)
1.3 millert 188: {
1.4 millert 189: BUFFER *bp;
190: LINE *lp1, *lp2;
191: LINE *olp; /* line we started at */
192: MGWIN *wp;
1.19 vincent 193: int nbytes, s, nline, siz;
194: int opos; /* offset we started at */
1.4 millert 195:
196: lp1 = NULL;
1.1 deraadt 197: if (line == NULL) {
198: line = malloc(NLINE);
1.21 ! vincent 199: if (line == NULL)
! 200: panic("out of memory");
1.1 deraadt 201: linesize = NLINE;
202: }
1.4 millert 203:
204: /* cheap */
205: bp = curbp;
1.8 art 206: if (newname != NULL)
1.10 vincent 207: (void)strlcpy(bp->b_fname, newname, sizeof bp->b_fname);
1.4 millert 208:
209: /* hard file open */
1.8 art 210: if ((s = ffropen(fname, needinfo ? bp : NULL)) == FIOERR)
1.1 deraadt 211: goto out;
1.4 millert 212: if (s == FIOFNF) {
213: /* file not found */
1.1 deraadt 214: if (newname != NULL)
215: ewprintf("(New file)");
1.3 millert 216: else
217: ewprintf("(File not found)");
1.1 deraadt 218: goto out;
219: }
220: opos = curwp->w_doto;
1.4 millert 221:
222: /* open a new line, at point, and start inserting after it */
1.20 vincent 223: (void)lnewline();
1.1 deraadt 224: olp = lback(curwp->w_dotp);
1.3 millert 225: if (olp == curbp->b_linep) {
1.1 deraadt 226: /* if at end of buffer, create a line to insert before */
1.7 art 227: (void)lnewline();
1.1 deraadt 228: curwp->w_dotp = lback(curwp->w_dotp);
229: }
1.4 millert 230:
231: /* don't count fake lines at the end */
232: nline = 0;
1.19 vincent 233: siz = 0;
1.3 millert 234: while ((s = ffgetline(line, linesize, &nbytes)) != FIOERR) {
1.1 deraadt 235: doneread:
1.19 vincent 236: siz += nbytes + 1;
1.3 millert 237: switch (s) {
238: case FIOSUC:
239: ++nline;
240: /* and continue */
1.4 millert 241: case FIOEOF:
242: /* the last line of the file */
1.3 millert 243: if ((lp1 = lalloc(nbytes)) == NULL) {
1.4 millert 244: /* keep message on the display */
245: s = FIOERR;
246: goto endoffile;
1.3 millert 247: }
248: bcopy(line, <ext(lp1)[0], nbytes);
249: lp2 = lback(curwp->w_dotp);
250: lp2->l_fp = lp1;
251: lp1->l_fp = curwp->w_dotp;
252: lp1->l_bp = lp2;
253: curwp->w_dotp->l_bp = lp1;
254: if (s == FIOEOF)
255: goto endoffile;
256: break;
1.19 vincent 257: case FIOLONG: {
1.4 millert 258: /* a line too long to fit in our buffer */
1.9 mickey 259: char *cp;
260: int newsize;
1.3 millert 261:
262: newsize = linesize * 2;
263: if (newsize < 0 ||
1.4 millert 264: (cp = malloc((unsigned)newsize)) == NULL) {
1.3 millert 265: ewprintf("Could not allocate %d bytes",
1.4 millert 266: newsize);
267: s = FIOERR;
268: goto endoffile;
1.3 millert 269: }
270: bcopy(line, cp, linesize);
271: free(line);
272: line = cp;
1.9 mickey 273: s = ffgetline(line + linesize, linesize,
1.4 millert 274: &nbytes);
1.3 millert 275: nbytes += linesize;
276: linesize = newsize;
277: if (s == FIOERR)
278: goto endoffile;
279: goto doneread;
280: }
281: default:
282: ewprintf("Unknown code %d reading file", s);
283: s = FIOERR;
284: break;
1.1 deraadt 285: }
286: }
287: endoffile:
1.19 vincent 288: undo_add_insert(olp, opos, siz);
289:
1.4 millert 290: /* ignore errors */
1.8 art 291: ffclose(NULL);
1.4 millert 292: /* don't zap an error */
293: if (s == FIOEOF) {
1.3 millert 294: if (nline == 1)
295: ewprintf("(Read 1 line)");
296: else
297: ewprintf("(Read %d lines)", nline);
1.1 deraadt 298: }
1.4 millert 299: /* set mark at the end of the text */
1.1 deraadt 300: curwp->w_dotp = curwp->w_markp = lback(curwp->w_dotp);
301: curwp->w_marko = llength(curwp->w_markp);
1.7 art 302: (void)ldelnewline();
1.1 deraadt 303: curwp->w_dotp = olp;
304: curwp->w_doto = opos;
1.3 millert 305: if (olp == curbp->b_linep)
306: curwp->w_dotp = lforw(olp);
1.1 deraadt 307: #ifndef NO_BACKUP
308: if (newname != NULL)
1.3 millert 309: bp->b_flag |= BFCHG | BFBAK; /* Need a backup. */
310: else
311: bp->b_flag |= BFCHG;
1.4 millert 312: #else /* !NO_BACKUP */
1.1 deraadt 313: bp->b_flag |= BFCHG;
1.4 millert 314: #endif /* !NO_BACKUP */
1.3 millert 315: /*
316: * if the insert was at the end of buffer, set lp1 to the end of
317: * buffer line, and lp2 to the beginning of the newly inserted text.
318: * (Otherwise lp2 is set to NULL.) This is used below to set
319: * pointers in other windows correctly if they are also at the end of
320: * buffer.
1.1 deraadt 321: */
322: lp1 = bp->b_linep;
323: if (curwp->w_markp == lp1) {
324: lp2 = curwp->w_dotp;
325: } else {
1.4 millert 326: /* delete extraneous newline */
1.7 art 327: (void)ldelnewline();
1.1 deraadt 328: out: lp2 = NULL;
329: }
1.3 millert 330: for (wp = wheadp; wp != NULL; wp = wp->w_wndp) {
1.1 deraadt 331: if (wp->w_bufp == curbp) {
1.3 millert 332: wp->w_flag |= WFMODE | WFEDIT;
1.1 deraadt 333: if (wp != curwp && lp2 != NULL) {
1.3 millert 334: if (wp->w_dotp == lp1)
335: wp->w_dotp = lp2;
336: if (wp->w_markp == lp1)
337: wp->w_markp = lp2;
338: if (wp->w_linep == lp1)
339: wp->w_linep = lp2;
1.1 deraadt 340: }
341: }
342: }
1.4 millert 343: /* return false if error */
344: return s != FIOERR;
1.1 deraadt 345: }
346:
347: /*
1.9 mickey 348: * Ask for a file name and write the contents of the current buffer to that
349: * file. Update the remembered file name and clear the buffer changed flag.
1.4 millert 350: * This handling of file names is different from the earlier versions and
351: * is more compatable with Gosling EMACS than with ITS EMACS.
1.1 deraadt 352: */
1.3 millert 353: /* ARGSUSED */
354: int
1.20 vincent 355: filewrite(int f, int n)
1.1 deraadt 356: {
1.4 millert 357: int s;
358: char fname[NFILEN];
359: char *adjfname;
1.1 deraadt 360:
1.3 millert 361: if ((s = eread("Write file: ", fname, NFILEN,
1.13 deraadt 362: EFNEW | EFCR | EFFILE)) != TRUE)
1.1 deraadt 363: return (s);
364: adjfname = adjustname(fname);
365: /* old attributes are no longer current */
366: bzero(&curbp->b_fi, sizeof(curbp->b_fi));
1.3 millert 367: if ((s = writeout(curbp, adjfname)) == TRUE) {
1.10 vincent 368: (void)strlcpy(curbp->b_fname, adjfname, sizeof curbp->b_fname);
1.1 deraadt 369: #ifndef NO_BACKUP
370: curbp->b_flag &= ~(BFBAK | BFCHG);
1.4 millert 371: #else /* !NO_BACKUP */
1.1 deraadt 372: curbp->b_flag &= ~BFCHG;
1.4 millert 373: #endif /* !NO_BACKUP */
1.1 deraadt 374: upmodes(curbp);
375: }
376: return s;
377: }
378:
379: /*
1.4 millert 380: * Save the contents of the current buffer back into its associated file.
1.1 deraadt 381: */
382: #ifndef NO_BACKUP
383: #ifndef MAKEBACKUP
384: #define MAKEBACKUP TRUE
1.4 millert 385: #endif /* !MAKEBACKUP */
1.9 mickey 386: static int makebackup = MAKEBACKUP;
1.4 millert 387: #endif /* !NO_BACKUP */
1.1 deraadt 388:
1.3 millert 389: /* ARGSUSED */
390: int
1.20 vincent 391: filesave(int f, int n)
1.1 deraadt 392: {
393: return buffsave(curbp);
394: }
395:
396: /*
1.9 mickey 397: * Save the contents of the buffer argument into its associated file. Do
398: * nothing if there have been no changes (is this a bug, or a feature?).
399: * Error if there is no remembered file name. If this is the first write
1.4 millert 400: * since the read or visit, then a backup copy of the file is made.
1.9 mickey 401: * Allow user to select whether or not to make backup files by looking at
1.4 millert 402: * the value of makebackup.
1.1 deraadt 403: */
1.3 millert 404: int
1.20 vincent 405: buffsave(BUFFER *bp)
1.3 millert 406: {
1.4 millert 407: int s;
1.1 deraadt 408:
1.4 millert 409: /* return, no changes */
410: if ((bp->b_flag & BFCHG) == 0) {
1.1 deraadt 411: ewprintf("(No changes need to be saved)");
412: return TRUE;
413: }
1.4 millert 414:
415: /* must have a name */
416: if (bp->b_fname[0] == '\0') {
1.1 deraadt 417: ewprintf("No file name");
418: return (FALSE);
419: }
1.4 millert 420:
1.1 deraadt 421: #ifndef NO_BACKUP
1.3 millert 422: if (makebackup && (bp->b_flag & BFBAK)) {
1.1 deraadt 423: s = fbackupfile(bp->b_fname);
1.4 millert 424: /* hard error */
425: if (s == ABORT)
1.1 deraadt 426: return FALSE;
1.4 millert 427: /* softer error */
1.9 mickey 428: if (s == FALSE &&
1.4 millert 429: (s = eyesno("Backup error, save anyway")) != TRUE)
1.1 deraadt 430: return s;
431: }
1.4 millert 432: #endif /* !NO_BACKUP */
1.3 millert 433: if ((s = writeout(bp, bp->b_fname)) == TRUE) {
1.1 deraadt 434: #ifndef NO_BACKUP
435: bp->b_flag &= ~(BFCHG | BFBAK);
1.4 millert 436: #else /* !NO_BACKUP */
1.1 deraadt 437: bp->b_flag &= ~BFCHG;
1.4 millert 438: #endif /* !NO_BACKUP */
1.1 deraadt 439: upmodes(bp);
440: }
441: return s;
442: }
443:
444: #ifndef NO_BACKUP
1.3 millert 445: /*
446: * Since we don't have variables (we probably should) this is a command
447: * processor for changing the value of the make backup flag. If no argument
448: * is given, sets makebackup to true, so backups are made. If an argument is
449: * given, no backup files are made when saving a new version of a file. Only
450: * used when BACKUP is #defined.
1.1 deraadt 451: */
1.3 millert 452: /* ARGSUSED */
453: int
1.20 vincent 454: makebkfile(int f, int n)
1.1 deraadt 455: {
1.3 millert 456: if (f & FFARG)
457: makebackup = n > 0;
458: else
459: makebackup = !makebackup;
1.1 deraadt 460: ewprintf("Backup files %sabled", makebackup ? "en" : "dis");
461: return TRUE;
462: }
1.4 millert 463: #endif /* !NO_BACKUP */
1.1 deraadt 464:
465: /*
466: * NB: bp is passed to both ffwopen and ffclose because some
467: * attribute information may need to be updated at open time
468: * and others after the close. This is OS-dependent. Note
469: * that the ff routines are assumed to be able to tell whether
470: * the attribute information has been set up in this buffer
471: * or not.
472: */
473:
474: /*
1.9 mickey 475: * This function performs the details of file writing; writing the file
476: * in buffer bp to file fn. Uses the file management routines in the
1.4 millert 477: * "fileio.c" package. Most of the grief is checking of some sort.
1.1 deraadt 478: */
1.3 millert 479: int
1.20 vincent 480: writeout(BUFFER *bp, char *fn)
1.3 millert 481: {
1.4 millert 482: int s;
1.1 deraadt 483:
1.4 millert 484: /* open writes message */
485: if ((s = ffwopen(fn, bp)) != FIOSUC)
1.1 deraadt 486: return (FALSE);
487: s = ffputbuf(bp);
1.4 millert 488: if (s == FIOSUC) {
489: /* no write error */
1.1 deraadt 490: s = ffclose(bp);
1.3 millert 491: if (s == FIOSUC)
1.1 deraadt 492: ewprintf("Wrote %s", fn);
1.4 millert 493: } else
494: /* ignore close error if it is a write error */
1.7 art 495: (void)ffclose(bp);
1.1 deraadt 496: return s == FIOSUC;
497: }
498:
499: /*
1.4 millert 500: * Tag all windows for bp (all windows if bp == NULL) as needing their
1.1 deraadt 501: * mode line updated.
502: */
1.7 art 503: void
1.20 vincent 504: upmodes(BUFFER *bp)
1.3 millert 505: {
1.4 millert 506: MGWIN *wp;
1.1 deraadt 507:
508: for (wp = wheadp; wp != NULL; wp = wp->w_wndp)
1.3 millert 509: if (bp == NULL || curwp->w_bufp == bp)
510: wp->w_flag |= WFMODE;
1.1 deraadt 511: }