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