put periods after some of the messages
[nvi.git] / common / util.c
blobb0d9558ae517b8c81eb1c06e527b8be81483b245
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.23 1993/11/20 10:05:25 bostic Exp $ (Berkeley) $Date: 1993/11/20 10:05:25 $";
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 len = snprintf(msgbuf, sizeof(msgbuf), "Error: %s%s%s.",
83 fmt == NULL ? "" : fmt, fmt == NULL ? "" : ": ",
84 strerror(errno));
85 else
86 len = vsnprintf(msgbuf, sizeof(msgbuf), fmt, ap);
87 if (len > sizeof(msgbuf))
88 len = sizeof(msgbuf);
90 msg_app(__global_list, sp, mt == M_ERR ? 1 : 0, msgbuf, len);
94 * msg_app --
95 * Append a message into the queue. This can fail, but there's
96 * nothing we can do if it does.
98 void
99 msg_app(gp, sp, inv_video, p, len)
100 GS *gp;
101 SCR *sp;
102 int inv_video;
103 char *p;
104 size_t len;
106 static int reenter; /* STATIC: Re-entrancy check. */
107 MSG *mp, *nmp;
110 * It's possible to reenter msg when it allocates space.
111 * We're probably dead anyway, but no reason to drop core.
113 if (reenter)
114 return;
115 reenter = 1;
118 * Find an empty structure, or allocate a new one. Use the
119 * screen structure if possible, otherwise the global one.
121 if (sp != NULL) {
122 if ((mp = sp->msgq.lh_first) == NULL) {
123 if ((mp = malloc(sizeof(MSG))) == NULL)
124 goto ret;
125 memset(mp, 0, sizeof(MSG));
126 LIST_INSERT_HEAD(&sp->msgq, mp, q);
127 goto store;
129 } else if ((mp = gp->msgq.lh_first) == NULL) {
130 if ((mp = malloc(sizeof(MSG))) == NULL)
131 goto ret;
132 memset(mp, 0, sizeof(MSG));
133 LIST_INSERT_HEAD(&gp->msgq, mp, q);
134 goto store;
136 while (!F_ISSET(mp, M_EMPTY) && mp->q.le_next != NULL)
137 mp = mp->q.le_next;
138 if (!F_ISSET(mp, M_EMPTY)) {
139 if ((nmp = malloc(sizeof(MSG))) == NULL)
140 goto ret;
141 memset(nmp, 0, sizeof(MSG));
142 LIST_INSERT_AFTER(mp, nmp, q);
143 mp = nmp;
146 /* Get enough memory for the message. */
147 store: if (len > mp->blen && binc(sp, &mp->mbuf, &mp->blen, len))
148 goto ret;
150 /* Store the message. */
151 memmove(mp->mbuf, p, len);
152 mp->len = len;
153 mp->flags = inv_video ? M_INV_VIDEO : 0;
155 ret: reenter = 0;
159 * msgrpt --
160 * Report on the lines that changed.
162 * !!!
163 * Historic vi documentation (USD:15-8) claimed that "The editor will also
164 * always tell you when a change you make affects text which you cannot see."
165 * This isn't true -- edit a large file and do "100d|1". We don't implement
166 * this semantic as it would require that we track each line that changes
167 * during a command instead of just keeping count.
170 msg_rpt(sp, is_message)
171 SCR *sp;
172 int is_message;
174 static const char *const action[] = {
175 "added", "changed", "copied", "deleted", "joined", "moved",
176 "put", "left shifted", "right shifted", "yanked", NULL,
178 recno_t total;
179 u_long rval;
180 int first, cnt;
181 size_t blen, len;
182 const char *const *ap;
183 char *bp, *p, number[40];
185 if ((rval = O_VAL(sp, O_REPORT)) == 0)
186 goto norpt;
188 GET_SPACE(sp, bp, blen, 512);
189 p = bp;
191 total = 0;
192 for (ap = action, cnt = 0, first = 1; *ap != NULL; ++ap, ++cnt)
193 if (sp->rptlines[cnt] != 0) {
194 total += sp->rptlines[cnt];
195 len = snprintf(number, sizeof(number),
196 "%s%lu line%s %s", first ? "" : "; ",
197 sp->rptlines[cnt],
198 sp->rptlines[cnt] > 1 ? "s" : "", *ap);
199 memmove(p, number, len);
200 p += len;
201 first = 0;
205 * If nothing to report, return. Note that the number of lines
206 * must be > than the user's value, not >=. This is historic
207 * practice and means that users cannot report on single line
208 * changes.
210 if (total > rval) {
211 *p = '\0';
213 if (is_message)
214 msgq(sp, M_INFO, "%s", bp);
215 else
216 ex_printf(EXCOOKIE, "%s\n", bp);
219 FREE_SPACE(sp, bp, blen);
221 /* Clear after each report. */
222 norpt: memset(sp->rptlines, 0, sizeof(sp->rptlines));
223 return (0);
227 * binc --
228 * Increase the size of a buffer.
231 binc(sp, argp, bsizep, min)
232 SCR *sp; /* MAY BE NULL */
233 void *argp;
234 size_t *bsizep, min;
236 void *bpp;
237 size_t csize;
239 /* If already larger than the minimum, just return. */
240 csize = *bsizep;
241 if (min && csize >= min)
242 return (0);
244 csize += MAX(min, 256);
245 bpp = *(char **)argp;
247 /* For non-ANSI C realloc implementations. */
248 if (bpp == NULL)
249 bpp = malloc(csize);
250 else
251 bpp = realloc(bpp, csize);
252 if (bpp == NULL) {
253 msgq(sp, M_SYSERR, NULL);
254 *bsizep = 0;
255 return (1);
257 *(char **)argp = bpp;
258 *bsizep = csize;
259 return (0);
263 * nonblank --
264 * Set the column number of the first non-blank character
265 * including or after the starting column. On error, set
266 * the column to 0, it's safest.
269 nonblank(sp, ep, lno, cnop)
270 SCR *sp;
271 EXF *ep;
272 recno_t lno;
273 size_t *cnop;
275 char *p;
276 size_t cnt, len, off;
278 /* Default. */
279 off = *cnop;
280 *cnop = 0;
282 /* Get the line. */
283 if ((p = file_gline(sp, ep, lno, &len)) == NULL) {
284 if (file_lline(sp, ep, &lno))
285 return (1);
286 if (lno == 0)
287 return (0);
288 GETLINE_ERR(sp, lno);
289 return (1);
292 /* Set the offset. */
293 if (len == 0 || off >= len)
294 return (0);
296 for (cnt = off, p = &p[off],
297 len -= off; len && isblank(*p); ++cnt, ++p, --len);
299 /* Set the return. */
300 *cnop = len ? cnt : cnt - 1;
301 return (0);
305 * tail --
306 * Return tail of a path.
308 char *
309 tail(path)
310 char *path;
312 char *p;
314 if ((p = strrchr(path, '/')) == NULL)
315 return (path);
316 return (p + 1);
320 * set_window_size --
321 * Set the window size, the row may be provided as an argument.
324 set_window_size(sp, set_row, ign_env)
325 SCR *sp;
326 u_int set_row;
327 int ign_env;
329 struct winsize win;
330 size_t col, row;
331 int user_set;
332 char *argv[2], *s, buf[2048];
334 row = 24;
335 col = 80;
338 * Get the screen rows and columns. If the values are wrong, it's
339 * not a big deal -- as soon as the user sets them explicitly the
340 * environment will be set and the screen package will use the new
341 * values.
343 * Try TIOCGWINSZ, followed by the termcap entry.
345 if (ioctl(STDERR_FILENO, TIOCGWINSZ, &win) != -1 &&
346 win.ws_row != 0 && win.ws_col != 0) {
347 row = win.ws_row;
348 col = win.ws_col;
349 } else {
350 s = NULL;
351 if (F_ISSET(&sp->opts[O_TERM], OPT_SET))
352 s = O_STR(sp, O_TERM);
353 else
354 s = getenv("TERM");
355 if (s != NULL && tgetent(buf, s) == 1) {
356 row = tgetnum("li");
357 col = tgetnum("co");
362 * POSIX 1003.2 requires the environment to override, however,
363 * if we're here because of a signal, we don't want to use the
364 * old values.
366 if (!ign_env) {
367 if ((s = getenv("ROWS")) != NULL)
368 row = strtol(s, NULL, 10);
369 if ((s = getenv("COLUMNS")) != NULL)
370 col = strtol(s, NULL, 10);
373 /* But, if we got an argument for the rows, use it. */
374 if (set_row)
375 row = set_row;
377 argv[0] = buf;
378 argv[1] = NULL;
381 * Tell the options code that the screen size has changed.
382 * Since the user didn't do the set, clear the set bits.
384 user_set = F_ISSET(&sp->opts[O_LINES], OPT_SET);
385 (void)snprintf(buf, sizeof(buf), "ls=%u", row);
386 if (opts_set(sp, argv))
387 return (1);
388 if (user_set)
389 F_CLR(&sp->opts[O_LINES], OPT_SET);
390 user_set = F_ISSET(&sp->opts[O_COLUMNS], OPT_SET);
391 (void)snprintf(buf, sizeof(buf), "co=%u", col);
392 if (opts_set(sp, argv))
393 return (1);
394 if (user_set)
395 F_CLR(&sp->opts[O_COLUMNS], OPT_SET);
396 return (0);
400 * set_alt_name --
401 * Set the alternate file name.
403 * Swap the alternate file name. It's a routine because I wanted some place
404 * to hang this comment. The alternate file name (normally referenced using
405 * the special character '#' during file expansion) is set by many
406 * operations. In the historic vi, the commands "ex", and "edit" obviously
407 * set the alternate file name because they switched the underlying file.
408 * Less obviously, the "read", "file", "write" and "wq" commands set it as
409 * well. In this implementation, some new commands have been added to the
410 * list. Where it gets interesting is that the alternate file name is set
411 * multiple times by some commands. If an edit attempt fails (for whatever
412 * reason, like the current file is modified but as yet unwritten), it is
413 * set to the file name that the user was unable to edit. If the edit
414 * succeeds, it is set to the last file name that was edited. Good fun.
416 void
417 set_alt_name(sp, name)
418 SCR *sp;
419 char *name;
421 if (sp->alt_name != NULL)
422 FREE(sp->alt_name, strlen(sp->alt_name) + 1);
423 if ((sp->alt_name = strdup(name)) == NULL)
424 msgq(sp, M_SYSERR, NULL);
428 * baud_from_bval --
429 * Return the baud rate using the standard defines.
431 u_long
432 baud_from_bval(sp)
433 SCR *sp;
435 speed_t v;
437 switch (v = cfgetospeed(&sp->gp->original_termios)) {
438 case B50:
439 return (50);
440 case B75:
441 return (75);
442 case B110:
443 return (110);
444 case B134:
445 return (134);
446 case B150:
447 return (150);
448 case B200:
449 return (200);
450 case B300:
451 return (300);
452 case B600:
453 return (600);
454 case B1200:
455 return (1200);
456 case B1800:
457 return (1800);
458 case B2400:
459 return (2400);
460 case B4800:
461 return (4800);
462 case B0: /* Hangup -- ignore. */
463 case B9600:
464 return (9600);
465 case B19200:
466 return (19200);
467 case B38400:
468 return (38400);
469 default:
471 * EXTA and EXTB aren't required by POSIX 1003.1, and
472 * are almost certainly the same as some of the above
473 * values, so they can't be part of the case statement.
475 #ifdef EXTA
476 if (v == EXTA)
477 return (19200);
478 #endif
479 #ifdef EXTB
480 if (v == EXTB)
481 return (38400);
482 #endif
483 msgq(sp, M_ERR, "Unknown terminal baud rate %u.\n", v);
484 return (9600);