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