if no messages waiting, always display the file status line on
[nvi.git] / common / util.c
blob64d23305c2e4a8691f2abee7999cb468fa4a8605
1 /*-
2 * Copyright (c) 1991, 1993
3 * The Regents of the University of California. All rights reserved.
5 * %sccs.include.redist.c%
6 */
8 #ifndef lint
9 static char sccsid[] = "$Id: util.c,v 8.28 1993/11/27 15:53:19 bostic Exp $ (Berkeley) $Date: 1993/11/27 15:53:19 $";
10 #endif /* not lint */
12 #include <sys/types.h>
13 #include <sys/ioctl.h>
15 #include <ctype.h>
16 #include <curses.h>
17 #include <errno.h>
18 #include <stdlib.h>
19 #include <string.h>
20 #include <unistd.h>
22 #ifdef __STDC__
23 #include <stdarg.h>
24 #else
25 #include <varargs.h>
26 #endif
28 #include "vi.h"
31 * msgq --
32 * Display a message.
34 void
35 #ifdef __STDC__
36 msgq(SCR *sp, enum msgtype mt, const char *fmt, ...)
37 #else
38 msgq(sp, mt, fmt, va_alist)
39 SCR *sp;
40 enum msgtype mt;
41 char *fmt;
42 va_dcl
43 #endif
45 va_list ap;
46 int len;
47 char msgbuf[1024];
49 #ifdef __STDC__
50 va_start(ap, fmt);
51 #else
52 va_start(ap);
53 #endif
55 * It's possible to enter msg when there's no screen to hold
56 * the message. Always check sp before using it, and, if it's
57 * NULL, use __global_list.
59 switch (mt) {
60 case M_BERR:
61 if (sp != NULL && !O_ISSET(sp, O_VERBOSE)) {
62 F_SET(sp, S_BELLSCHED);
63 return;
65 mt = M_ERR;
66 break;
67 case M_VINFO:
68 if (sp != NULL && !O_ISSET(sp, O_VERBOSE))
69 return;
70 mt = M_INFO;
71 break;
72 case M_ERR:
73 case M_INFO:
74 case M_SYSERR:
75 break;
76 default:
77 abort();
80 /* Length is the min length of the message or the buffer. */
81 if (mt == M_SYSERR)
82 if (sp->if_name != NULL)
83 len = snprintf(msgbuf, sizeof(msgbuf),
84 "Error: %s, %d: %s%s%s.", sp->if_name, sp->if_lno,
85 fmt == NULL ? "" : fmt, fmt == NULL ? "" : ": ",
86 strerror(errno));
87 else
88 len = snprintf(msgbuf, sizeof(msgbuf),
89 "Error: %s%s%s.",
90 fmt == NULL ? "" : fmt, fmt == NULL ? "" : ": ",
91 strerror(errno));
92 else {
93 len = sp->if_name == NULL ? 0 : snprintf(msgbuf, sizeof(msgbuf),
94 "%s, %d: ", sp->if_name, sp->if_lno);
95 len += vsnprintf(msgbuf + len, sizeof(msgbuf) - len, fmt, ap);
99 * If len >= the size, some characters were discarded.
100 * Ignore trailing nul.
102 if (len >= sizeof(msgbuf))
103 len = sizeof(msgbuf) - 1;
105 msg_app(__global_list, sp, mt == M_ERR ? 1 : 0, msgbuf, len);
109 * msg_app --
110 * Append a message into the queue. This can fail, but there's
111 * nothing we can do if it does.
113 void
114 msg_app(gp, sp, inv_video, p, len)
115 GS *gp;
116 SCR *sp;
117 int inv_video;
118 char *p;
119 size_t len;
121 static int reenter; /* STATIC: Re-entrancy check. */
122 MSG *mp, *nmp;
125 * It's possible to reenter msg when it allocates space.
126 * We're probably dead anyway, but no reason to drop core.
128 if (reenter)
129 return;
130 reenter = 1;
133 * Find an empty structure, or allocate a new one. Use the
134 * screen structure if possible, otherwise the global one.
136 if (sp != NULL) {
137 if ((mp = sp->msgq.lh_first) == NULL) {
138 if ((mp = malloc(sizeof(MSG))) == NULL)
139 goto ret;
140 memset(mp, 0, sizeof(MSG));
141 LIST_INSERT_HEAD(&sp->msgq, mp, q);
142 goto store;
144 } else if ((mp = gp->msgq.lh_first) == NULL) {
145 if ((mp = malloc(sizeof(MSG))) == NULL)
146 goto ret;
147 memset(mp, 0, sizeof(MSG));
148 LIST_INSERT_HEAD(&gp->msgq, mp, q);
149 goto store;
151 while (!F_ISSET(mp, M_EMPTY) && mp->q.le_next != NULL)
152 mp = mp->q.le_next;
153 if (!F_ISSET(mp, M_EMPTY)) {
154 if ((nmp = malloc(sizeof(MSG))) == NULL)
155 goto ret;
156 memset(nmp, 0, sizeof(MSG));
157 LIST_INSERT_AFTER(mp, nmp, q);
158 mp = nmp;
161 /* Get enough memory for the message. */
162 store: if (len > mp->blen && binc(sp, &mp->mbuf, &mp->blen, len))
163 goto ret;
165 /* Store the message. */
166 memmove(mp->mbuf, p, len);
167 mp->len = len;
168 mp->flags = inv_video ? M_INV_VIDEO : 0;
170 ret: reenter = 0;
174 * msgrpt --
175 * Report on the lines that changed.
177 * !!!
178 * Historic vi documentation (USD:15-8) claimed that "The editor will also
179 * always tell you when a change you make affects text which you cannot see."
180 * This isn't true -- edit a large file and do "100d|1". We don't implement
181 * this semantic as it would require that we track each line that changes
182 * during a command instead of just keeping count.
185 msg_rpt(sp, is_message)
186 SCR *sp;
187 int is_message;
189 static const char *const action[] = {
190 "added", "changed", "copied", "deleted", "joined", "moved",
191 "put", "left shifted", "right shifted", "yanked", NULL,
193 recno_t total;
194 u_long rval;
195 int first, cnt;
196 size_t blen, len;
197 const char *const *ap;
198 char *bp, *p, number[40];
200 if ((rval = O_VAL(sp, O_REPORT)) == 0)
201 goto norpt;
203 GET_SPACE(sp, bp, blen, 512);
204 p = bp;
206 total = 0;
207 for (ap = action, cnt = 0, first = 1; *ap != NULL; ++ap, ++cnt)
208 if (sp->rptlines[cnt] != 0) {
209 total += sp->rptlines[cnt];
210 len = snprintf(number, sizeof(number),
211 "%s%lu line%s %s", first ? "" : "; ",
212 sp->rptlines[cnt],
213 sp->rptlines[cnt] > 1 ? "s" : "", *ap);
214 memmove(p, number, len);
215 p += len;
216 first = 0;
220 * If nothing to report, return. Note that the number of lines
221 * must be > than the user's value, not >=. This is historic
222 * practice and means that users cannot report on single line
223 * changes.
225 if (total > rval) {
226 *p = '\0';
228 if (is_message)
229 msgq(sp, M_INFO, "%s", bp);
230 else
231 ex_printf(EXCOOKIE, "%s\n", bp);
234 FREE_SPACE(sp, bp, blen);
236 /* Clear after each report. */
237 norpt: memset(sp->rptlines, 0, sizeof(sp->rptlines));
238 return (0);
242 * binc --
243 * Increase the size of a buffer.
246 binc(sp, argp, bsizep, min)
247 SCR *sp; /* MAY BE NULL */
248 void *argp;
249 size_t *bsizep, min;
251 void *bpp;
252 size_t csize;
254 /* If already larger than the minimum, just return. */
255 csize = *bsizep;
256 if (min && csize >= min)
257 return (0);
259 csize += MAX(min, 256);
260 bpp = *(char **)argp;
262 /* For non-ANSI C realloc implementations. */
263 if (bpp == NULL)
264 bpp = malloc(csize);
265 else
266 bpp = realloc(bpp, csize);
267 if (bpp == NULL) {
268 msgq(sp, M_SYSERR, NULL);
269 *bsizep = 0;
270 return (1);
272 *(char **)argp = bpp;
273 *bsizep = csize;
274 return (0);
278 * nonblank --
279 * Set the column number of the first non-blank character
280 * including or after the starting column. On error, set
281 * the column to 0, it's safest.
284 nonblank(sp, ep, lno, cnop)
285 SCR *sp;
286 EXF *ep;
287 recno_t lno;
288 size_t *cnop;
290 char *p;
291 size_t cnt, len, off;
293 /* Default. */
294 off = *cnop;
295 *cnop = 0;
297 /* Get the line. */
298 if ((p = file_gline(sp, ep, lno, &len)) == NULL) {
299 if (file_lline(sp, ep, &lno))
300 return (1);
301 if (lno == 0)
302 return (0);
303 GETLINE_ERR(sp, lno);
304 return (1);
307 /* Set the offset. */
308 if (len == 0 || off >= len)
309 return (0);
311 for (cnt = off, p = &p[off],
312 len -= off; len && isblank(*p); ++cnt, ++p, --len);
314 /* Set the return. */
315 *cnop = len ? cnt : cnt - 1;
316 return (0);
320 * tail --
321 * Return tail of a path.
323 char *
324 tail(path)
325 char *path;
327 char *p;
329 if ((p = strrchr(path, '/')) == NULL)
330 return (path);
331 return (p + 1);
335 * set_window_size --
336 * Set the window size, the row may be provided as an argument.
339 set_window_size(sp, set_row, ign_env)
340 SCR *sp;
341 u_int set_row;
342 int ign_env;
344 struct winsize win;
345 size_t col, row;
346 int user_set;
347 char *argv[2], *s, buf[2048];
350 * Get the screen rows and columns. If the values are wrong, it's
351 * not a big deal -- as soon as the user sets them explicitly the
352 * environment will be set and the screen package will use the new
353 * values.
355 * Try TIOCGWINSZ.
357 row = col = 0;
358 if (ioctl(STDERR_FILENO, TIOCGWINSZ, &win) != -1) {
359 row = win.ws_row;
360 col = win.ws_col;
363 /* If TIOCGWINSZ failed, or had entries of 0, try termcap. */
364 if (row == 0 || col == 0) {
365 s = NULL;
366 if (F_ISSET(&sp->opts[O_TERM], OPT_SET))
367 s = O_STR(sp, O_TERM);
368 else
369 s = getenv("TERM");
370 if (s != NULL && tgetent(buf, s) == 1) {
371 if (row == 0)
372 row = tgetnum("li");
373 if (col == 0)
374 col = tgetnum("co");
377 /* If nothing else, well, it's probably a VT100. */
378 if (row == 0)
379 row = 24;
380 if (col == 0)
381 col = 80;
384 * POSIX 1003.2 requires the environment to override, however,
385 * if we're here because of a signal, we don't want to use the
386 * old values.
388 if (!ign_env) {
389 if ((s = getenv("ROWS")) != NULL)
390 row = strtol(s, NULL, 10);
391 if ((s = getenv("COLUMNS")) != NULL)
392 col = strtol(s, NULL, 10);
395 /* But, if we got an argument for the rows, use it. */
396 if (set_row)
397 row = set_row;
399 argv[0] = buf;
400 argv[1] = NULL;
403 * Tell the options code that the screen size has changed.
404 * Since the user didn't do the set, clear the set bits.
406 user_set = F_ISSET(&sp->opts[O_LINES], OPT_SET);
407 (void)snprintf(buf, sizeof(buf), "ls=%u", row);
408 if (opts_set(sp, argv))
409 return (1);
410 if (user_set)
411 F_CLR(&sp->opts[O_LINES], OPT_SET);
412 user_set = F_ISSET(&sp->opts[O_COLUMNS], OPT_SET);
413 (void)snprintf(buf, sizeof(buf), "co=%u", col);
414 if (opts_set(sp, argv))
415 return (1);
416 if (user_set)
417 F_CLR(&sp->opts[O_COLUMNS], OPT_SET);
418 return (0);
422 * set_alt_name --
423 * Set the alternate file name.
425 * Swap the alternate file name. It's a routine because I wanted some place
426 * to hang this comment. The alternate file name (normally referenced using
427 * the special character '#' during file expansion) is set by many
428 * operations. In the historic vi, the commands "ex", and "edit" obviously
429 * set the alternate file name because they switched the underlying file.
430 * Less obviously, the "read", "file", "write" and "wq" commands set it as
431 * well. In this implementation, some new commands have been added to the
432 * list. Where it gets interesting is that the alternate file name is set
433 * multiple times by some commands. If an edit attempt fails (for whatever
434 * reason, like the current file is modified but as yet unwritten), it is
435 * set to the file name that the user was unable to edit. If the edit
436 * succeeds, it is set to the last file name that was edited. Good fun.
438 * If the user edits a temporary file, there are time when there isn't an
439 * alternative file name. A name argument of NULL turns it off.
441 void
442 set_alt_name(sp, name)
443 SCR *sp;
444 char *name;
446 if (sp->alt_name != NULL)
447 FREE(sp->alt_name, strlen(sp->alt_name) + 1);
448 if (name == NULL)
449 sp->alt_name = NULL;
450 else if ((sp->alt_name = strdup(name)) == NULL)
451 msgq(sp, M_SYSERR, NULL);
455 * baud_from_bval --
456 * Return the baud rate using the standard defines.
458 u_long
459 baud_from_bval(sp)
460 SCR *sp;
462 speed_t v;
464 switch (v = cfgetospeed(&sp->gp->original_termios)) {
465 case B50:
466 return (50);
467 case B75:
468 return (75);
469 case B110:
470 return (110);
471 case B134:
472 return (134);
473 case B150:
474 return (150);
475 case B200:
476 return (200);
477 case B300:
478 return (300);
479 case B600:
480 return (600);
481 case B1200:
482 return (1200);
483 case B1800:
484 return (1800);
485 case B2400:
486 return (2400);
487 case B4800:
488 return (4800);
489 case B0: /* Hangup -- ignore. */
490 case B9600:
491 return (9600);
492 case B19200:
493 return (19200);
494 case B38400:
495 return (38400);
496 default:
498 * EXTA and EXTB aren't required by POSIX 1003.1, and
499 * are almost certainly the same as some of the above
500 * values, so they can't be part of the case statement.
502 #ifdef EXTA
503 if (v == EXTA)
504 return (19200);
505 #endif
506 #ifdef EXTB
507 if (v == EXTB)
508 return (38400);
509 #endif
510 msgq(sp, M_ERR, "Unknown terminal baud rate %u.\n", v);
511 return (9600);