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