GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.bin/mg/buffer.c Lines: 0 462 0.0 %
Date: 2017-11-07 Branches: 0 317 0.0 %

Line Branch Exec Source
1
/*	$OpenBSD: buffer.c,v 1.104 2017/08/06 04:39:45 bcallah Exp $	*/
2
3
/* This file is in the public domain. */
4
5
/*
6
 *		Buffer handling.
7
 */
8
9
#include <sys/queue.h>
10
#include <errno.h>
11
#include <libgen.h>
12
#include <signal.h>
13
#include <stdarg.h>
14
#include <stdio.h>
15
#include <stdlib.h>
16
#include <string.h>
17
#include <unistd.h>
18
19
#include "def.h"
20
#include "kbd.h"		/* needed for modes */
21
22
#define DIFFTOOL "/usr/bin/diff"
23
24
static struct buffer  *makelist(void);
25
static struct buffer *bnew(const char *);
26
27
static int usebufname(const char *);
28
29
/* Flag for global working dir */
30
extern int globalwd;
31
32
/* ARGSUSED */
33
int
34
togglereadonly(int f, int n)
35
{
36
	int s;
37
38
	if ((s = checkdirty(curbp)) != TRUE)
39
		return (s);
40
	if (!(curbp->b_flag & BFREADONLY))
41
		curbp->b_flag |= BFREADONLY;
42
	else {
43
		curbp->b_flag &= ~BFREADONLY;
44
		if (curbp->b_flag & BFCHG)
45
			ewprintf("Warning: Buffer was modified");
46
	}
47
	curwp->w_rflag |= WFMODE;
48
49
	return (TRUE);
50
}
51
52
/* Switch to the named buffer.
53
 * If no name supplied, switch to the default (alternate) buffer.
54
 */
55
int
56
usebufname(const char *bufp)
57
{
58
	struct buffer *bp = NULL;
59
60
	if (bufp == NULL) {
61
		if ((bp = bfind("*scratch*", TRUE)) == NULL)
62
			return(FALSE);
63
	} else if (bufp[0] == '\0' && curbp->b_altb != NULL)
64
			bp = curbp->b_altb;
65
	else if ((bp = bfind(bufp, TRUE)) == NULL)
66
		return (FALSE);
67
68
	/* and put it in current window */
69
	curbp = bp;
70
	return (showbuffer(bp, curwp, WFFRAME | WFFULL));
71
}
72
73
/*
74
 * Attach a buffer to a window. The values of dot and mark come
75
 * from the buffer if the use count is 0. Otherwise, they come
76
 * from some other window.  *scratch* is the default alternate
77
 * buffer.
78
 */
79
/* ARGSUSED */
80
int
81
usebuffer(int f, int n)
82
{
83
	char    bufn[NBUFN], *bufp;
84
85
	/* Get buffer to use from user */
86
	if (curbp->b_altb == NULL)
87
		bufp = eread("Switch to buffer: ", bufn, NBUFN, EFNEW | EFBUF);
88
	else
89
		bufp = eread("Switch to buffer (default %s): ", bufn, NBUFN,
90
		    EFNUL | EFNEW | EFBUF, curbp->b_altb->b_bname);
91
92
	if (bufp == NULL)
93
		return FALSE;
94
95
	return (usebufname(bufp));
96
}
97
98
/*
99
 * pop to buffer asked for by the user.
100
 */
101
/* ARGSUSED */
102
int
103
poptobuffer(int f, int n)
104
{
105
	struct buffer *bp;
106
	struct mgwin  *wp;
107
	char    bufn[NBUFN], *bufp;
108
109
	/* Get buffer to use from user */
110
	if ((curbp->b_altb == NULL) &&
111
	    ((curbp->b_altb = bfind("*scratch*", TRUE)) == NULL))
112
		bufp = eread("Switch to buffer in other window: ", bufn, NBUFN,
113
		    EFNEW | EFBUF);
114
	else
115
		bufp = eread("Switch to buffer in other window (default %s): ",
116
		    bufn, NBUFN, EFNUL | EFNEW | EFBUF, curbp->b_altb->b_bname);
117
	if (bufp == NULL)
118
		return (ABORT);
119
	if (bufp[0] == '\0' && curbp->b_altb != NULL)
120
		bp = curbp->b_altb;
121
	else if ((bp = bfind(bufn, TRUE)) == NULL)
122
		return (FALSE);
123
	if (bp == curbp)
124
		return (splitwind(f, n));
125
	/* and put it in a new, non-ephemeral window */
126
	if ((wp = popbuf(bp, WNONE)) == NULL)
127
		return (FALSE);
128
	curbp = bp;
129
	curwp = wp;
130
	return (TRUE);
131
}
132
133
/*
134
 * Dispose of a buffer, by name.
135
 * Ask for the name (unless called by dired mode). Look it up (don't
136
 * get too upset if it isn't there at all!). Clear the buffer (ask
137
 * if the buffer has been changed). Then free the header
138
 * line and the buffer header. Bound to "C-x k".
139
 */
140
/* ARGSUSED */
141
int
142
killbuffer_cmd(int f, int n)
143
{
144
	struct buffer *bp;
145
	char    bufn[NBUFN], *bufp;
146
147
	if (f & FFRAND) /* dired mode 'q' */
148
		bp = curbp;
149
	else if ((bufp = eread("Kill buffer (default %s): ", bufn, NBUFN,
150
	    EFNUL | EFNEW | EFBUF, curbp->b_bname)) == NULL)
151
		return (ABORT);
152
	else if (bufp[0] == '\0')
153
		bp = curbp;
154
	else if ((bp = bfind(bufn, FALSE)) == NULL)
155
		return (FALSE);
156
	return (killbuffer(bp));
157
}
158
159
int
160
killbuffer(struct buffer *bp)
161
{
162
	struct buffer *bp1;
163
	struct buffer *bp2;
164
	struct mgwin  *wp;
165
	int s;
166
	struct undo_rec *rec;
167
168
	/*
169
	 * Find some other buffer to display. Try the alternate buffer,
170
	 * then the first different buffer in the buffer list.  If there's
171
	 * only one buffer, create buffer *scratch* and make it the alternate
172
	 * buffer.  Return if *scratch* is only buffer...
173
	 */
174
	if ((bp1 = bp->b_altb) == NULL) {
175
		/* only one buffer. see if it's *scratch* */
176
		if (bp == bfind("*scratch*", FALSE))
177
			return (TRUE);
178
		/* create *scratch* for alternate buffer */
179
		if ((bp1 = bfind("*scratch*", TRUE)) == NULL)
180
			return (FALSE);
181
	}
182
	if ((s = bclear(bp)) != TRUE)
183
		return (s);
184
	for (wp = wheadp; bp->b_nwnd > 0; wp = wp->w_wndp) {
185
		if (wp->w_bufp == bp) {
186
			bp2 = bp1->b_altb;	/* save alternate buffer */
187
			if (showbuffer(bp1, wp, WFMODE | WFFRAME | WFFULL))
188
				bp1->b_altb = bp2;
189
			else
190
				bp1 = bp2;
191
		}
192
	}
193
	if (bp == curbp)
194
		curbp = bp1;
195
	free(bp->b_headp);			/* Release header line.  */
196
	bp2 = NULL;				/* Find the header.	 */
197
	bp1 = bheadp;
198
	while (bp1 != bp) {
199
		if (bp1->b_altb == bp)
200
			bp1->b_altb = (bp->b_altb == bp1) ? NULL : bp->b_altb;
201
		bp2 = bp1;
202
		bp1 = bp1->b_bufp;
203
	}
204
	bp1 = bp1->b_bufp;			/* Next one in chain.	 */
205
	if (bp2 == NULL)			/* Unlink it.		 */
206
		bheadp = bp1;
207
	else
208
		bp2->b_bufp = bp1;
209
	while (bp1 != NULL) {			/* Finish with altb's	 */
210
		if (bp1->b_altb == bp)
211
			bp1->b_altb = (bp->b_altb == bp1) ? NULL : bp->b_altb;
212
		bp1 = bp1->b_bufp;
213
	}
214
215
	while ((rec = TAILQ_FIRST(&bp->b_undo))) {
216
		TAILQ_REMOVE(&bp->b_undo, rec, next);
217
		free_undo_record(rec);
218
	}
219
220
	free(bp->b_bname);			/* Release name block	 */
221
	free(bp);				/* Release buffer block */
222
	return (TRUE);
223
}
224
225
/*
226
 * Save some buffers - just call anycb with the arg flag.
227
 */
228
/* ARGSUSED */
229
int
230
savebuffers(int f, int n)
231
{
232
	if (anycb(f) == ABORT)
233
		return (ABORT);
234
	return (TRUE);
235
}
236
237
/*
238
 * Listing buffers.
239
 */
240
static int listbuf_ncol;
241
242
static int	listbuf_goto_buffer(int f, int n);
243
static int	listbuf_goto_buffer_one(int f, int n);
244
static int	listbuf_goto_buffer_helper(int f, int n, int only);
245
246
static PF listbuf_pf[] = {
247
	listbuf_goto_buffer
248
};
249
static PF listbuf_one[] = {
250
	listbuf_goto_buffer_one
251
};
252
253
254
static struct KEYMAPE (2) listbufmap = {
255
	2,
256
	2,
257
	rescan,
258
	{
259
		{
260
			CCHR('M'), CCHR('M'), listbuf_pf, NULL
261
		},
262
		{
263
			'1', '1', listbuf_one, NULL
264
		}
265
	}
266
};
267
268
/*
269
 * Display the buffer list. This is done
270
 * in two parts. The "makelist" routine figures out
271
 * the text, and puts it in a buffer. "popbuf"
272
 * then pops the data onto the screen. Bound to
273
 * "C-X C-B".
274
 */
275
/* ARGSUSED */
276
int
277
listbuffers(int f, int n)
278
{
279
	static int		 initialized = 0;
280
	struct buffer		*bp;
281
	struct mgwin		*wp;
282
283
	if (!initialized) {
284
		maps_add((KEYMAP *)&listbufmap, "listbufmap");
285
		initialized = 1;
286
	}
287
288
	if ((bp = makelist()) == NULL || (wp = popbuf(bp, WNONE)) == NULL)
289
		return (FALSE);
290
	wp->w_dotp = bp->b_dotp; /* fix up if window already on screen */
291
	wp->w_doto = bp->b_doto;
292
	bp->b_modes[0] = name_mode("fundamental");
293
	bp->b_modes[1] = name_mode("listbufmap");
294
	bp->b_nmodes = 1;
295
296
	return (TRUE);
297
}
298
299
/*
300
 * This routine rebuilds the text for the
301
 * list buffers command. Return pointer
302
 * to new list if everything works.
303
 * Return NULL if there is an error (if
304
 * there is no memory).
305
 */
306
static struct buffer *
307
makelist(void)
308
{
309
	int		w = ncol / 2;
310
	struct buffer	*bp, *blp;
311
	struct line	*lp;
312
313
	if ((blp = bfind("*Buffer List*", TRUE)) == NULL)
314
		return (NULL);
315
	if (bclear(blp) != TRUE)
316
		return (NULL);
317
	blp->b_flag &= ~BFCHG;		/* Blow away old.	 */
318
	blp->b_flag |= BFREADONLY;
319
320
	listbuf_ncol = ncol;		/* cache ncol for listbuf_goto_buffer */
321
322
	if (addlinef(blp, "%-*s%s", w, " MR Buffer", "Size   File") == FALSE ||
323
	    addlinef(blp, "%-*s%s", w, " -- ------", "----   ----") == FALSE)
324
		return (NULL);
325
326
	for (bp = bheadp; bp != NULL; bp = bp->b_bufp) {
327
		RSIZE nbytes;
328
329
		nbytes = 0;			/* Count bytes in buf.	 */
330
		if (bp != blp) {
331
			lp = bfirstlp(bp);
332
			while (lp != bp->b_headp) {
333
				nbytes += llength(lp) + 1;
334
				lp = lforw(lp);
335
			}
336
			if (nbytes)
337
				nbytes--;	/* no bonus newline	 */
338
		}
339
340
		if (addlinef(blp, "%c%c%c %-*.*s%c%-6d %-*s",
341
		    (bp == curbp) ? '.' : ' ',	/* current buffer ? */
342
		    ((bp->b_flag & BFCHG) != 0) ? '*' : ' ',	/* changed ? */
343
		    ((bp->b_flag & BFREADONLY) != 0) ? ' ' : '*',
344
		    w - 5,		/* four chars already written */
345
		    w - 5,		/* four chars already written */
346
		    bp->b_bname,	/* buffer name */
347
		    strlen(bp->b_bname) < w - 5 ? ' ' : '$', /* truncated? */
348
		    nbytes,		/* buffer size */
349
		    w - 7,		/* seven chars already written */
350
		    bp->b_fname) == FALSE)
351
			return (NULL);
352
	}
353
	blp->b_dotp = bfirstlp(blp);		/* put dot at beginning of
354
						 * buffer */
355
	blp->b_doto = 0;
356
	return (blp);				/* All done		 */
357
}
358
359
static int
360
listbuf_goto_buffer(int f, int n)
361
{
362
	return (listbuf_goto_buffer_helper(f, n, 0));
363
}
364
365
static int
366
listbuf_goto_buffer_one(int f, int n)
367
{
368
	return (listbuf_goto_buffer_helper(f, n, 1));
369
}
370
371
static int
372
listbuf_goto_buffer_helper(int f, int n, int only)
373
{
374
	struct buffer	*bp;
375
	struct mgwin	*wp;
376
	char		*line = NULL;
377
	int		 i, ret = FALSE;
378
379
	if (curwp->w_dotp->l_text[listbuf_ncol/2 - 1] == '$') {
380
		dobeep();
381
		ewprintf("buffer name truncated");
382
		return (FALSE);
383
	}
384
385
	if ((line = malloc(listbuf_ncol/2)) == NULL)
386
		return (FALSE);
387
388
	memcpy(line, curwp->w_dotp->l_text + 4, listbuf_ncol/2 - 5);
389
	for (i = listbuf_ncol/2 - 6; i > 0; i--) {
390
		if (line[i] != ' ') {
391
			line[i + 1] = '\0';
392
			break;
393
		}
394
	}
395
	if (i == 0)
396
		goto cleanup;
397
398
	for (bp = bheadp; bp != NULL; bp = bp->b_bufp) {
399
		if (strcmp(bp->b_bname, line) == 0)
400
			break;
401
	}
402
	if (bp == NULL)
403
		goto cleanup;
404
405
	if ((wp = popbuf(bp, WNONE)) == NULL)
406
		goto cleanup;
407
	curbp = bp;
408
	curwp = wp;
409
410
	if (only)
411
		ret = (onlywind(FFRAND, 1));
412
	else
413
		ret = TRUE;
414
415
cleanup:
416
	free(line);
417
418
	return (ret);
419
}
420
421
/*
422
 * The argument "fmt" points to a format string.  Append this line to the
423
 * buffer. Handcraft the EOL on the end.  Return TRUE if it worked and
424
 * FALSE if you ran out of room.
425
 */
426
int
427
addlinef(struct buffer *bp, char *fmt, ...)
428
{
429
	va_list		 ap;
430
	struct line	*lp;
431
432
	if ((lp = lalloc(0)) == NULL)
433
		return (FALSE);
434
	va_start(ap, fmt);
435
	if (vasprintf(&lp->l_text, fmt, ap) == -1) {
436
		lfree(lp);
437
		va_end(ap);
438
		return (FALSE);
439
	}
440
	lp->l_used = strlen(lp->l_text);
441
	va_end(ap);
442
443
	bp->b_headp->l_bp->l_fp = lp;		/* Hook onto the end	 */
444
	lp->l_bp = bp->b_headp->l_bp;
445
	bp->b_headp->l_bp = lp;
446
	lp->l_fp = bp->b_headp;
447
	bp->b_lines++;
448
449
	return (TRUE);
450
}
451
452
/*
453
 * Look through the list of buffers, giving the user a chance to save them.
454
 * Return TRUE if there are any changed buffers afterwards.  Buffers that don't
455
 * have an associated file don't count.  Return FALSE if there are no changed
456
 * buffers.  Return ABORT if an error occurs or if the user presses c-g.
457
 */
458
int
459
anycb(int f)
460
{
461
	struct buffer	*bp;
462
	int		 s = FALSE, save = FALSE, save2 = FALSE, ret;
463
	char		 pbuf[NFILEN + 11];
464
465
	for (bp = bheadp; bp != NULL; bp = bp->b_bufp) {
466
		if (*(bp->b_fname) != '\0' && (bp->b_flag & BFCHG) != 0) {
467
			ret = snprintf(pbuf, sizeof(pbuf), "Save file %s",
468
			    bp->b_fname);
469
			if (ret < 0 || ret >= sizeof(pbuf)) {
470
				dobeep();
471
				ewprintf("Error: filename too long!");
472
				return (UERROR);
473
			}
474
			if ((f == TRUE || (save = eyorn(pbuf)) == TRUE) &&
475
			    (save2 = buffsave(bp)) == TRUE) {
476
				bp->b_flag &= ~BFCHG;
477
				upmodes(bp);
478
			} else {
479
				if (save2 == FIOERR)
480
					return (save2);
481
				s = TRUE;
482
			}
483
			if (save == ABORT)
484
				return (save);
485
			save = TRUE;
486
		}
487
	}
488
	if (save == FALSE /* && kbdmop == NULL */ )	/* experimental */
489
		ewprintf("(No files need saving)");
490
	return (s);
491
}
492
493
/*
494
 * Search for a buffer, by name.
495
 * If not found, and the "cflag" is TRUE,
496
 * create a new buffer. Return pointer to the found
497
 * (or new) buffer.
498
 */
499
struct buffer *
500
bfind(const char *bname, int cflag)
501
{
502
	struct buffer	*bp;
503
504
	bp = bheadp;
505
	while (bp != NULL) {
506
		if (strcmp(bname, bp->b_bname) == 0)
507
			return (bp);
508
		bp = bp->b_bufp;
509
	}
510
	if (cflag != TRUE)
511
		return (NULL);
512
513
	bp = bnew(bname);
514
515
	return (bp);
516
}
517
518
/*
519
 * Create a new buffer and put it in the list of
520
 * all buffers.
521
 */
522
static struct buffer *
523
bnew(const char *bname)
524
{
525
	struct buffer	*bp;
526
	struct line	*lp;
527
	int		 i;
528
	size_t		len;
529
530
	bp = calloc(1, sizeof(struct buffer));
531
	if (bp == NULL) {
532
		dobeep();
533
		ewprintf("Can't get %d bytes", sizeof(struct buffer));
534
		return (NULL);
535
	}
536
	if ((lp = lalloc(0)) == NULL) {
537
		free(bp);
538
		return (NULL);
539
	}
540
	bp->b_altb = bp->b_bufp = NULL;
541
	bp->b_dotp = lp;
542
	bp->b_doto = 0;
543
	bp->b_markp = NULL;
544
	bp->b_marko = 0;
545
	bp->b_flag = defb_flag;
546
	/* if buffer name starts and ends with '*', we ignore changes */
547
	len = strlen(bname);
548
	if (len) {
549
		if (bname[0] == '*' && bname[len - 1] == '*')
550
			bp->b_flag |= BFIGNDIRTY;
551
	}
552
	bp->b_nwnd = 0;
553
	bp->b_headp = lp;
554
	bp->b_nmodes = defb_nmodes;
555
	TAILQ_INIT(&bp->b_undo);
556
	bp->b_undoptr = NULL;
557
	i = 0;
558
	do {
559
		bp->b_modes[i] = defb_modes[i];
560
	} while (i++ < defb_nmodes);
561
	bp->b_fname[0] = '\0';
562
	bp->b_cwd[0] = '\0';
563
	bzero(&bp->b_fi, sizeof(bp->b_fi));
564
	lp->l_fp = lp;
565
	lp->l_bp = lp;
566
	bp->b_bufp = bheadp;
567
	bheadp = bp;
568
	bp->b_dotline = bp->b_markline = 1;
569
	bp->b_lines = 1;
570
	if ((bp->b_bname = strdup(bname)) == NULL) {
571
		dobeep();
572
		ewprintf("Can't get %d bytes", strlen(bname) + 1);
573
		return (NULL);
574
	}
575
576
	return (bp);
577
}
578
579
/*
580
 * This routine blows away all of the text
581
 * in a buffer. If the buffer is marked as changed
582
 * then we ask if it is ok to blow it away; this is
583
 * to save the user the grief of losing text. The
584
 * window chain is nearly always wrong if this gets
585
 * called; the caller must arrange for the updates
586
 * that are required. Return TRUE if everything
587
 * looks good.
588
 */
589
int
590
bclear(struct buffer *bp)
591
{
592
	struct line	*lp;
593
	int		 s;
594
595
	/* Has buffer changed, and do we care? */
596
	if (!(bp->b_flag & BFIGNDIRTY) && (bp->b_flag & BFCHG) != 0 &&
597
	    (s = eyesno("Buffer modified; kill anyway")) != TRUE)
598
		return (s);
599
	bp->b_flag &= ~BFCHG;	/* Not changed		 */
600
	while ((lp = lforw(bp->b_headp)) != bp->b_headp)
601
		lfree(lp);
602
	bp->b_dotp = bp->b_headp;	/* Fix dot */
603
	bp->b_doto = 0;
604
	bp->b_markp = NULL;	/* Invalidate "mark"	 */
605
	bp->b_marko = 0;
606
	bp->b_dotline = bp->b_markline = 1;
607
	bp->b_lines = 1;
608
609
	return (TRUE);
610
}
611
612
/*
613
 * Display the given buffer in the given window. Flags indicated
614
 * action on redisplay. Update modified flag so insert loop can check it.
615
 */
616
int
617
showbuffer(struct buffer *bp, struct mgwin *wp, int flags)
618
{
619
	struct buffer	*obp;
620
	struct mgwin	*owp;
621
622
	/* Ensure file has not been modified elsewhere */
623
	if (fchecktime(bp) != TRUE)
624
		bp->b_flag |= BFDIRTY;
625
626
	if (wp->w_bufp == bp) {	/* Easy case! */
627
		wp->w_rflag |= flags;
628
		return (TRUE);
629
	}
630
	/* First, detach the old buffer from the window */
631
	if ((bp->b_altb = obp = wp->w_bufp) != NULL) {
632
		if (--obp->b_nwnd == 0) {
633
			obp->b_dotp = wp->w_dotp;
634
			obp->b_doto = wp->w_doto;
635
			obp->b_markp = wp->w_markp;
636
			obp->b_marko = wp->w_marko;
637
			obp->b_dotline = wp->w_dotline;
638
			obp->b_markline = wp->w_markline;
639
		}
640
	}
641
	/* Now, attach the new buffer to the window */
642
	wp->w_bufp = bp;
643
644
	if (bp->b_nwnd++ == 0) {	/* First use.		 */
645
		wp->w_dotp = bp->b_dotp;
646
		wp->w_doto = bp->b_doto;
647
		wp->w_markp = bp->b_markp;
648
		wp->w_marko = bp->b_marko;
649
		wp->w_dotline = bp->b_dotline;
650
		wp->w_markline = bp->b_markline;
651
	} else
652
		/* already on screen, steal values from other window */
653
		for (owp = wheadp; owp != NULL; owp = wp->w_wndp)
654
			if (wp->w_bufp == bp && owp != wp) {
655
				wp->w_dotp = owp->w_dotp;
656
				wp->w_doto = owp->w_doto;
657
				wp->w_markp = owp->w_markp;
658
				wp->w_marko = owp->w_marko;
659
				wp->w_dotline = owp->w_dotline;
660
				wp->w_markline = owp->w_markline;
661
				break;
662
			}
663
	wp->w_rflag |= WFMODE | flags;
664
	return (TRUE);
665
}
666
667
/*
668
 * Augment a buffer name with a number, if necessary
669
 *
670
 * If more than one file of the same basename() is open,
671
 * the additional buffers are named "file<2>", "file<3>", and
672
 * so forth.  This function adjusts a buffer name to
673
 * include the number, if necessary.
674
 */
675
int
676
augbname(char *bn, const char *fn, size_t bs)
677
{
678
	int	 count;
679
	size_t	 remain, len;
680
681
	if ((len = xbasename(bn, fn, bs)) >= bs)
682
		return (FALSE);
683
684
	remain = bs - len;
685
	for (count = 2; bfind(bn, FALSE) != NULL; count++)
686
		snprintf(bn + len, remain, "<%d>", count);
687
688
	return (TRUE);
689
}
690
691
/*
692
 * Pop the buffer we got passed onto the screen.
693
 * Returns a status.
694
 */
695
struct mgwin *
696
popbuf(struct buffer *bp, int flags)
697
{
698
	struct mgwin	*wp;
699
700
	if (bp->b_nwnd == 0) {	/* Not on screen yet.	 */
701
		/*
702
		 * Pick a window for a pop-up.
703
		 * If only one window, split the screen.
704
		 * Flag the new window as ephemeral
705
		 */
706
		if (wheadp->w_wndp == NULL &&
707
		    splitwind(FFOTHARG, flags) == FALSE)
708
 			return (NULL);
709
710
		/*
711
		 * Pick the uppermost window that isn't
712
		 * the current window. An LRU algorithm
713
		 * might be better. Return a pointer, or NULL on error.
714
		 */
715
		wp = wheadp;
716
717
		while (wp != NULL && wp == curwp)
718
			wp = wp->w_wndp;
719
	} else {
720
		for (wp = wheadp; wp != NULL; wp = wp->w_wndp) {
721
			if (wp->w_bufp == bp) {
722
				wp->w_rflag |= WFFULL | WFFRAME;
723
				return (wp);
724
			}
725
		}
726
	}
727
	if (!wp)
728
		return (NULL);
729
730
	if (showbuffer(bp, wp, WFFULL) != TRUE)
731
		return (NULL);
732
	return (wp);
733
}
734
735
/*
736
 * Insert another buffer at dot.  Very useful.
737
 */
738
/* ARGSUSED */
739
int
740
bufferinsert(int f, int n)
741
{
742
	struct buffer *bp;
743
	struct line   *clp;
744
	int	clo, nline;
745
	char	bufn[NBUFN], *bufp;
746
747
	/* Get buffer to use from user */
748
	if (curbp->b_altb != NULL)
749
		bufp = eread("Insert buffer (default %s): ", bufn, NBUFN,
750
		    EFNUL | EFNEW | EFBUF, curbp->b_altb->b_bname);
751
	else
752
		bufp = eread("Insert buffer: ", bufn, NBUFN, EFNEW | EFBUF);
753
	if (bufp == NULL)
754
		return (ABORT);
755
	if (bufp[0] == '\0' && curbp->b_altb != NULL)
756
		bp = curbp->b_altb;
757
	else if ((bp = bfind(bufn, FALSE)) == NULL)
758
		return (FALSE);
759
760
	if (bp == curbp) {
761
		dobeep();
762
		ewprintf("Cannot insert buffer into self");
763
		return (FALSE);
764
	}
765
	/* insert the buffer */
766
	nline = 0;
767
	clp = bfirstlp(bp);
768
	for (;;) {
769
		for (clo = 0; clo < llength(clp); clo++)
770
			if (linsert(1, lgetc(clp, clo)) == FALSE)
771
				return (FALSE);
772
		if ((clp = lforw(clp)) == bp->b_headp)
773
			break;
774
		if (enewline(FFRAND, 1) == FALSE)	/* fake newline */
775
			return (FALSE);
776
		nline++;
777
	}
778
	if (nline == 1)
779
		ewprintf("[Inserted 1 line]");
780
	else
781
		ewprintf("[Inserted %d lines]", nline);
782
783
	clp = curwp->w_linep;		/* cosmetic adjustment	*/
784
	if (curwp->w_dotp == clp) {	/* for offscreen insert */
785
		while (nline-- && lback(clp) != curbp->b_headp)
786
			clp = lback(clp);
787
		curwp->w_linep = clp;	/* adjust framing.	*/
788
		curwp->w_rflag |= WFFULL;
789
	}
790
	return (TRUE);
791
}
792
793
/*
794
 * Turn off the dirty bit on this buffer.
795
 */
796
/* ARGSUSED */
797
int
798
notmodified(int f, int n)
799
{
800
	struct mgwin *wp;
801
802
	curbp->b_flag &= ~BFCHG;
803
	wp = wheadp;		/* Update mode lines.	 */
804
	while (wp != NULL) {
805
		if (wp->w_bufp == curbp)
806
			wp->w_rflag |= WFMODE;
807
		wp = wp->w_wndp;
808
	}
809
	ewprintf("Modification-flag cleared");
810
	return (TRUE);
811
}
812
813
/*
814
 * Popbuf and set all windows to top of buffer.
815
 */
816
int
817
popbuftop(struct buffer *bp, int flags)
818
{
819
	struct mgwin *wp;
820
821
	bp->b_dotp = bfirstlp(bp);
822
	bp->b_doto = 0;
823
	if (bp->b_nwnd != 0) {
824
		for (wp = wheadp; wp != NULL; wp = wp->w_wndp)
825
			if (wp->w_bufp == bp) {
826
				wp->w_dotp = bp->b_dotp;
827
				wp->w_doto = 0;
828
				wp->w_rflag |= WFFULL;
829
			}
830
	}
831
	return (popbuf(bp, flags) != NULL);
832
}
833
834
/*
835
 * Return the working directory for the current buffer, terminated
836
 * with a '/'. First, try to extract it from the current buffer's
837
 * filename. If that fails, use global cwd.
838
 */
839
int
840
getbufcwd(char *path, size_t plen)
841
{
842
	char cwd[NFILEN];
843
844
	if (plen == 0)
845
		return (FALSE);
846
847
	if (globalwd == FALSE && curbp->b_cwd[0] != '\0') {
848
		(void)strlcpy(path, curbp->b_cwd, plen);
849
	} else {
850
		if (getcwdir(cwd, sizeof(cwd)) == FALSE)
851
			goto error;
852
		(void)strlcpy(path, cwd, plen);
853
	}
854
	return (TRUE);
855
error:
856
	path[0] = '\0';
857
	return (FALSE);
858
}
859
860
/*
861
 * Ensures a buffer has not been modified elsewhere; e.g. on disk.
862
 * Prompt the user if it has.
863
 * Returns TRUE if it has NOT (i.e. buffer is ok to edit).
864
 * FALSE or ABORT otherwise
865
 */
866
int
867
checkdirty(struct buffer *bp)
868
{
869
	int s;
870
871
	if ((bp->b_flag & (BFCHG | BFDIRTY)) == 0)
872
		if (fchecktime(bp) != TRUE)
873
			bp->b_flag |= BFDIRTY;
874
875
	if ((bp->b_flag & (BFDIRTY | BFIGNDIRTY)) == BFDIRTY) {
876
		s = eynorr("File changed on disk; really edit the buffer");
877
		switch (s) {
878
		case TRUE:
879
			bp->b_flag &= ~BFDIRTY;
880
			bp->b_flag |= BFIGNDIRTY;
881
			return (TRUE);
882
		case REVERT:
883
			dorevert();
884
			return (FALSE);
885
		default:
886
			return (s);
887
		}
888
	}
889
890
	return (TRUE);
891
}
892
893
/*
894
 * Revert the current buffer to whatever is on disk.
895
 */
896
/* ARGSUSED */
897
int
898
revertbuffer(int f, int n)
899
{
900
	char fbuf[NFILEN + 32];
901
902
	if (curbp->b_fname[0] == 0) {
903
		dobeep();
904
		ewprintf("Cannot revert buffer not associated with any files.");
905
		return (FALSE);
906
	}
907
908
	snprintf(fbuf, sizeof(fbuf), "Revert buffer from file %s",
909
	    curbp->b_fname);
910
911
	if (eyorn(fbuf) == TRUE)
912
		return dorevert();
913
914
	return (FALSE);
915
}
916
917
int
918
dorevert(void)
919
{
920
	int lineno;
921
	struct undo_rec *rec;
922
923
	if (access(curbp->b_fname, F_OK|R_OK) != 0) {
924
		dobeep();
925
		if (errno == ENOENT)
926
			ewprintf("File %s no longer exists!",
927
			    curbp->b_fname);
928
		else
929
			ewprintf("File %s is no longer readable!",
930
			    curbp->b_fname);
931
		return (FALSE);
932
	}
933
934
	/* Save our current line, so we can go back after reloading. */
935
	lineno = curwp->w_dotline;
936
937
	/* Prevent readin from asking if we want to kill the buffer. */
938
	curbp->b_flag &= ~BFCHG;
939
940
	/* Clean up undo memory */
941
	while ((rec = TAILQ_FIRST(&curbp->b_undo))) {
942
		TAILQ_REMOVE(&curbp->b_undo, rec, next);
943
		free_undo_record(rec);
944
	}
945
946
	if (readin(curbp->b_fname))
947
		return(setlineno(lineno));
948
	return (FALSE);
949
}
950
951
/*
952
 * Diff the current buffer to what is on disk.
953
 */
954
/*ARGSUSED */
955
int
956
diffbuffer(int f, int n)
957
{
958
	struct buffer	*bp;
959
	struct line	*lp, *lpend;
960
	size_t		 len;
961
	int		 ret;
962
	char		*text, *ttext;
963
	char		* const argv[] =
964
	    {DIFFTOOL, "-u", "-p", curbp->b_fname, "-", (char *)NULL};
965
966
	len = 0;
967
968
	/* C-u is not supported */
969
	if (n > 1)
970
		return (ABORT);
971
972
	if (access(DIFFTOOL, X_OK) != 0) {
973
		dobeep();
974
		ewprintf("%s not found or not executable.", DIFFTOOL);
975
		return (FALSE);
976
	}
977
978
	if (curbp->b_fname[0] == 0) {
979
		dobeep();
980
		ewprintf("Cannot diff buffer not associated with any files.");
981
		return (FALSE);
982
	}
983
984
	lpend = curbp->b_headp;
985
	for (lp = lforw(lpend); lp != lpend; lp = lforw(lp)) {
986
		len+=llength(lp);
987
		if (lforw(lp) != lpend)		/* no implied \n on last line */
988
			len++;
989
	}
990
	if ((text = calloc(len + 1, sizeof(char))) == NULL) {
991
		dobeep();
992
		ewprintf("Cannot allocate memory.");
993
		return (FALSE);
994
	}
995
	ttext = text;
996
997
	for (lp = lforw(lpend); lp != lpend; lp = lforw(lp)) {
998
		if (llength(lp) != 0) {
999
			memcpy(ttext, ltext(lp), llength(lp));
1000
			ttext += llength(lp);
1001
		}
1002
		if (lforw(lp) != lpend)		/* no implied \n on last line */
1003
			*ttext++ = '\n';
1004
	}
1005
1006
	bp = bfind("*Diff*", TRUE);
1007
	bp->b_flag |= BFREADONLY;
1008
	if (bclear(bp) != TRUE) {
1009
		free(text);
1010
		return (FALSE);
1011
	}
1012
1013
	ret = pipeio(DIFFTOOL, argv, text, len, bp);
1014
1015
	if (ret == TRUE) {
1016
		eerase();
1017
		if (lforw(bp->b_headp) == bp->b_headp)
1018
			addline(bp, "Diff finished (no differences).");
1019
	}
1020
1021
	free(text);
1022
	return (ret);
1023
}
1024
1025
/*
1026
 * Given a file name, either find the buffer it uses, or create a new
1027
 * empty buffer to put it in.
1028
 */
1029
struct buffer *
1030
findbuffer(char *fn)
1031
{
1032
	struct buffer	*bp;
1033
	char		bname[NBUFN], fname[NBUFN];
1034
1035
	if (strlcpy(fname, fn, sizeof(fname)) >= sizeof(fname)) {
1036
		dobeep();
1037
		ewprintf("filename too long");
1038
		return (NULL);
1039
	}
1040
1041
	for (bp = bheadp; bp != NULL; bp = bp->b_bufp) {
1042
		if (strcmp(bp->b_fname, fname) == 0)
1043
			return (bp);
1044
	}
1045
	/* Not found. Create a new one, adjusting name first */
1046
	if (augbname(bname, fname, sizeof(bname)) == FALSE)
1047
		return (NULL);
1048
1049
	bp = bfind(bname, TRUE);
1050
	return (bp);
1051
}