GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.bin/mg/buffer.c Lines: 0 449 0.0 %
Date: 2016-12-06 Branches: 0 319 0.0 %

Line Branch Exec Source
1
/*	$OpenBSD: buffer.c,v 1.100 2015/10/10 08:35:26 lum 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;
59
60
	if (bufp == NULL)
61
		return (ABORT);
62
	if (bufp[0] == '\0' && curbp->b_altb != NULL)
63
		bp = curbp->b_altb;
64
	else if ((bp = bfind(bufp, TRUE)) == NULL)
65
		return (FALSE);
66
67
	/* and put it in current window */
68
	curbp = bp;
69
	return (showbuffer(bp, curwp, WFFRAME | WFFULL));
70
}
71
72
/*
73
 * Attach a buffer to a window. The values of dot and mark come
74
 * from the buffer if the use count is 0. Otherwise, they come
75
 * from some other window.  *scratch* is the default alternate
76
 * buffer.
77
 */
78
/* ARGSUSED */
79
int
80
usebuffer(int f, int n)
81
{
82
	char    bufn[NBUFN], *bufp;
83
84
	/* Get buffer to use from user */
85
	if ((curbp->b_altb == NULL) &&
86
	    ((curbp->b_altb = bfind("*scratch*", TRUE)) == 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
	return (usebufname(bufp));
93
}
94
95
/*
96
 * pop to buffer asked for by the user.
97
 */
98
/* ARGSUSED */
99
int
100
poptobuffer(int f, int n)
101
{
102
	struct buffer *bp;
103
	struct mgwin  *wp;
104
	char    bufn[NBUFN], *bufp;
105
106
	/* Get buffer to use from user */
107
	if ((curbp->b_altb == NULL) &&
108
	    ((curbp->b_altb = bfind("*scratch*", TRUE)) == NULL))
109
		bufp = eread("Switch to buffer in other window: ", bufn, NBUFN,
110
		    EFNEW | EFBUF);
111
	else
112
		bufp = eread("Switch to buffer in other window: (default %s) ",
113
		    bufn, NBUFN, EFNUL | EFNEW | EFBUF, curbp->b_altb->b_bname);
114
	if (bufp == NULL)
115
		return (ABORT);
116
	if (bufp[0] == '\0' && curbp->b_altb != NULL)
117
		bp = curbp->b_altb;
118
	else if ((bp = bfind(bufn, TRUE)) == NULL)
119
		return (FALSE);
120
	if (bp == curbp)
121
		return (splitwind(f, n));
122
	/* and put it in a new, non-ephemeral window */
123
	if ((wp = popbuf(bp, WNONE)) == NULL)
124
		return (FALSE);
125
	curbp = bp;
126
	curwp = wp;
127
	return (TRUE);
128
}
129
130
/*
131
 * Dispose of a buffer, by name.
132
 * Ask for the name (unless called by dired mode). Look it up (don't
133
 * get too upset if it isn't there at all!). Clear the buffer (ask
134
 * if the buffer has been changed). Then free the header
135
 * line and the buffer header. Bound to "C-x k".
136
 */
137
/* ARGSUSED */
138
int
139
killbuffer_cmd(int f, int n)
140
{
141
	struct buffer *bp;
142
	char    bufn[NBUFN], *bufp;
143
144
	if (f & FFRAND) /* dired mode 'q' */
145
		bp = curbp;
146
	else if ((bufp = eread("Kill buffer: (default %s) ", bufn, NBUFN,
147
	    EFNUL | EFNEW | EFBUF, curbp->b_bname)) == NULL)
148
		return (ABORT);
149
	else if (bufp[0] == '\0')
150
		bp = curbp;
151
	else if ((bp = bfind(bufn, FALSE)) == NULL)
152
		return (FALSE);
153
	return (killbuffer(bp));
154
}
155
156
int
157
killbuffer(struct buffer *bp)
158
{
159
	struct buffer *bp1;
160
	struct buffer *bp2;
161
	struct mgwin  *wp;
162
	int s;
163
	struct undo_rec *rec;
164
165
	/*
166
	 * Find some other buffer to display. Try the alternate buffer,
167
	 * then the first different buffer in the buffer list.  If there's
168
	 * only one buffer, create buffer *scratch* and make it the alternate
169
	 * buffer.  Return if *scratch* is only buffer...
170
	 */
171
	if ((bp1 = bp->b_altb) == NULL) {
172
		bp1 = (bp == bheadp) ? bp->b_bufp : bheadp;
173
		if (bp1 == NULL) {
174
			/* only one buffer. see if it's *scratch* */
175
			if (bp == bfind("*scratch*", FALSE))
176
				return (TRUE);
177
			/* create *scratch* for alternate buffer */
178
			if ((bp1 = bfind("*scratch*", TRUE)) == NULL)
179
				return (FALSE);
180
		}
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
	if (showbuffer(bp, wp, WFFULL) != TRUE)
726
		return (NULL);
727
	return (wp);
728
}
729
730
/*
731
 * Insert another buffer at dot.  Very useful.
732
 */
733
/* ARGSUSED */
734
int
735
bufferinsert(int f, int n)
736
{
737
	struct buffer *bp;
738
	struct line   *clp;
739
	int	clo, nline;
740
	char	bufn[NBUFN], *bufp;
741
742
	/* Get buffer to use from user */
743
	if (curbp->b_altb != NULL)
744
		bufp = eread("Insert buffer: (default %s) ", bufn, NBUFN,
745
		    EFNUL | EFNEW | EFBUF, curbp->b_altb->b_bname);
746
	else
747
		bufp = eread("Insert buffer: ", bufn, NBUFN, EFNEW | EFBUF);
748
	if (bufp == NULL)
749
		return (ABORT);
750
	if (bufp[0] == '\0' && curbp->b_altb != NULL)
751
		bp = curbp->b_altb;
752
	else if ((bp = bfind(bufn, FALSE)) == NULL)
753
		return (FALSE);
754
755
	if (bp == curbp) {
756
		dobeep();
757
		ewprintf("Cannot insert buffer into self");
758
		return (FALSE);
759
	}
760
	/* insert the buffer */
761
	nline = 0;
762
	clp = bfirstlp(bp);
763
	for (;;) {
764
		for (clo = 0; clo < llength(clp); clo++)
765
			if (linsert(1, lgetc(clp, clo)) == FALSE)
766
				return (FALSE);
767
		if ((clp = lforw(clp)) == bp->b_headp)
768
			break;
769
		if (enewline(FFRAND, 1) == FALSE)	/* fake newline */
770
			return (FALSE);
771
		nline++;
772
	}
773
	if (nline == 1)
774
		ewprintf("[Inserted 1 line]");
775
	else
776
		ewprintf("[Inserted %d lines]", nline);
777
778
	clp = curwp->w_linep;		/* cosmetic adjustment	*/
779
	if (curwp->w_dotp == clp) {	/* for offscreen insert */
780
		while (nline-- && lback(clp) != curbp->b_headp)
781
			clp = lback(clp);
782
		curwp->w_linep = clp;	/* adjust framing.	*/
783
		curwp->w_rflag |= WFFULL;
784
	}
785
	return (TRUE);
786
}
787
788
/*
789
 * Turn off the dirty bit on this buffer.
790
 */
791
/* ARGSUSED */
792
int
793
notmodified(int f, int n)
794
{
795
	struct mgwin *wp;
796
797
	curbp->b_flag &= ~BFCHG;
798
	wp = wheadp;		/* Update mode lines.	 */
799
	while (wp != NULL) {
800
		if (wp->w_bufp == curbp)
801
			wp->w_rflag |= WFMODE;
802
		wp = wp->w_wndp;
803
	}
804
	ewprintf("Modification-flag cleared");
805
	return (TRUE);
806
}
807
808
/*
809
 * Popbuf and set all windows to top of buffer.
810
 */
811
int
812
popbuftop(struct buffer *bp, int flags)
813
{
814
	struct mgwin *wp;
815
816
	bp->b_dotp = bfirstlp(bp);
817
	bp->b_doto = 0;
818
	if (bp->b_nwnd != 0) {
819
		for (wp = wheadp; wp != NULL; wp = wp->w_wndp)
820
			if (wp->w_bufp == bp) {
821
				wp->w_dotp = bp->b_dotp;
822
				wp->w_doto = 0;
823
				wp->w_rflag |= WFFULL;
824
			}
825
	}
826
	return (popbuf(bp, flags) != NULL);
827
}
828
829
/*
830
 * Return the working directory for the current buffer, terminated
831
 * with a '/'. First, try to extract it from the current buffer's
832
 * filename. If that fails, use global cwd.
833
 */
834
int
835
getbufcwd(char *path, size_t plen)
836
{
837
	char cwd[NFILEN];
838
839
	if (plen == 0)
840
		return (FALSE);
841
842
	if (globalwd == FALSE && curbp->b_cwd[0] != '\0') {
843
		(void)strlcpy(path, curbp->b_cwd, plen);
844
	} else {
845
		if (getcwdir(cwd, sizeof(cwd)) == FALSE)
846
			goto error;
847
		(void)strlcpy(path, cwd, plen);
848
	}
849
	return (TRUE);
850
error:
851
	path[0] = '\0';
852
	return (FALSE);
853
}
854
855
/*
856
 * Ensures a buffer has not been modified elsewhere; e.g. on disk.
857
 * Prompt the user if it has.
858
 * Returns TRUE if it has NOT (i.e. buffer is ok to edit).
859
 * FALSE or ABORT otherwise
860
 */
861
int
862
checkdirty(struct buffer *bp)
863
{
864
	int s;
865
866
	if ((bp->b_flag & (BFCHG | BFDIRTY)) == 0)
867
		if (fchecktime(bp) != TRUE)
868
			bp->b_flag |= BFDIRTY;
869
870
	if ((bp->b_flag & (BFDIRTY | BFIGNDIRTY)) == BFDIRTY) {
871
		s = eynorr("File changed on disk; really edit the buffer");
872
		switch (s) {
873
		case TRUE:
874
			bp->b_flag &= ~BFDIRTY;
875
			bp->b_flag |= BFIGNDIRTY;
876
			return (TRUE);
877
		case REVERT:
878
			dorevert();
879
			return (FALSE);
880
		default:
881
			return (s);
882
		}
883
	}
884
885
	return (TRUE);
886
}
887
888
/*
889
 * Revert the current buffer to whatever is on disk.
890
 */
891
/* ARGSUSED */
892
int
893
revertbuffer(int f, int n)
894
{
895
	char fbuf[NFILEN + 32];
896
897
	if (curbp->b_fname[0] == 0) {
898
		dobeep();
899
		ewprintf("Cannot revert buffer not associated with any files.");
900
		return (FALSE);
901
	}
902
903
	snprintf(fbuf, sizeof(fbuf), "Revert buffer from file %s",
904
	    curbp->b_fname);
905
906
	if (eyorn(fbuf) == TRUE)
907
		return dorevert();
908
909
	return (FALSE);
910
}
911
912
int
913
dorevert(void)
914
{
915
	int lineno;
916
	struct undo_rec *rec;
917
918
	if (access(curbp->b_fname, F_OK|R_OK) != 0) {
919
		dobeep();
920
		if (errno == ENOENT)
921
			ewprintf("File %s no longer exists!",
922
			    curbp->b_fname);
923
		else
924
			ewprintf("File %s is no longer readable!",
925
			    curbp->b_fname);
926
		return (FALSE);
927
	}
928
929
	/* Save our current line, so we can go back after reloading. */
930
	lineno = curwp->w_dotline;
931
932
	/* Prevent readin from asking if we want to kill the buffer. */
933
	curbp->b_flag &= ~BFCHG;
934
935
	/* Clean up undo memory */
936
	while ((rec = TAILQ_FIRST(&curbp->b_undo))) {
937
		TAILQ_REMOVE(&curbp->b_undo, rec, next);
938
		free_undo_record(rec);
939
	}
940
941
	if (readin(curbp->b_fname))
942
		return(setlineno(lineno));
943
	return (FALSE);
944
}
945
946
/*
947
 * Diff the current buffer to what is on disk.
948
 */
949
/*ARGSUSED */
950
int
951
diffbuffer(int f, int n)
952
{
953
	struct buffer	*bp;
954
	struct line	*lp, *lpend;
955
	size_t		 len;
956
	int		 ret;
957
	char		*text, *ttext;
958
	char		* const argv[] =
959
	    {DIFFTOOL, "-u", "-p", curbp->b_fname, "-", (char *)NULL};
960
961
	len = 0;
962
963
	/* C-u is not supported */
964
	if (n > 1)
965
		return (ABORT);
966
967
	if (access(DIFFTOOL, X_OK) != 0) {
968
		dobeep();
969
		ewprintf("%s not found or not executable.", DIFFTOOL);
970
		return (FALSE);
971
	}
972
973
	if (curbp->b_fname[0] == 0) {
974
		dobeep();
975
		ewprintf("Cannot diff buffer not associated with any files.");
976
		return (FALSE);
977
	}
978
979
	lpend = curbp->b_headp;
980
	for (lp = lforw(lpend); lp != lpend; lp = lforw(lp)) {
981
		len+=llength(lp);
982
		if (lforw(lp) != lpend)		/* no implied \n on last line */
983
			len++;
984
	}
985
	if ((text = calloc(len + 1, sizeof(char))) == NULL) {
986
		dobeep();
987
		ewprintf("Cannot allocate memory.");
988
		return (FALSE);
989
	}
990
	ttext = text;
991
992
	for (lp = lforw(lpend); lp != lpend; lp = lforw(lp)) {
993
		if (llength(lp) != 0) {
994
			memcpy(ttext, ltext(lp), llength(lp));
995
			ttext += llength(lp);
996
		}
997
		if (lforw(lp) != lpend)		/* no implied \n on last line */
998
			*ttext++ = '\n';
999
	}
1000
1001
	bp = bfind("*Diff*", TRUE);
1002
	bp->b_flag |= BFREADONLY;
1003
	if (bclear(bp) != TRUE) {
1004
		free(text);
1005
		return (FALSE);
1006
	}
1007
1008
	ret = pipeio(DIFFTOOL, argv, text, len, bp);
1009
1010
	if (ret == TRUE) {
1011
		eerase();
1012
		if (lforw(bp->b_headp) == bp->b_headp)
1013
			addline(bp, "Diff finished (no differences).");
1014
	}
1015
1016
	free(text);
1017
	return (ret);
1018
}
1019
1020
/*
1021
 * Given a file name, either find the buffer it uses, or create a new
1022
 * empty buffer to put it in.
1023
 */
1024
struct buffer *
1025
findbuffer(char *fn)
1026
{
1027
	struct buffer	*bp;
1028
	char		bname[NBUFN], fname[NBUFN];
1029
1030
	if (strlcpy(fname, fn, sizeof(fname)) >= sizeof(fname)) {
1031
		dobeep();
1032
		ewprintf("filename too long");
1033
		return (NULL);
1034
	}
1035
1036
	for (bp = bheadp; bp != NULL; bp = bp->b_bufp) {
1037
		if (strcmp(bp->b_fname, fname) == 0)
1038
			return (bp);
1039
	}
1040
	/* Not found. Create a new one, adjusting name first */
1041
	if (augbname(bname, fname, sizeof(bname)) == FALSE)
1042
		return (NULL);
1043
1044
	bp = bfind(bname, TRUE);
1045
	return (bp);
1046
}