fix global code by turning it into two passes
[nvi.git] / common / util.c
blobb6d9aafbfcedb69cd4eb111509c6212086f6542b
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.34 1993/12/23 18:41:25 bostic Exp $ (Berkeley) $Date: 1993/12/23 18:41: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 /* FALLTHROUGH */
72 case M_INFO:
73 if (F_ISSET(sp, S_EXSILENT))
74 return;
75 break;
76 case M_ERR:
77 case M_SYSERR:
78 break;
79 default:
80 abort();
83 /* Length is the min length of the message or the buffer. */
84 if (mt == M_SYSERR)
85 if (sp->if_name != NULL)
86 len = snprintf(msgbuf, sizeof(msgbuf),
87 "Error: %s, %d: %s%s%s.", sp->if_name, sp->if_lno,
88 fmt == NULL ? "" : fmt, fmt == NULL ? "" : ": ",
89 strerror(errno));
90 else
91 len = snprintf(msgbuf, sizeof(msgbuf),
92 "Error: %s%s%s.",
93 fmt == NULL ? "" : fmt, fmt == NULL ? "" : ": ",
94 strerror(errno));
95 else {
96 len = sp->if_name == NULL ? 0 : snprintf(msgbuf, sizeof(msgbuf),
97 "%s, %d: ", sp->if_name, sp->if_lno);
98 len += vsnprintf(msgbuf + len, sizeof(msgbuf) - len, fmt, ap);
102 * If len >= the size, some characters were discarded.
103 * Ignore trailing nul.
105 if (len >= sizeof(msgbuf))
106 len = sizeof(msgbuf) - 1;
108 msg_app(__global_list, sp, mt == M_ERR ? 1 : 0, msgbuf, len);
112 * msg_app --
113 * Append a message into the queue. This can fail, but there's
114 * nothing we can do if it does.
116 void
117 msg_app(gp, sp, inv_video, p, len)
118 GS *gp;
119 SCR *sp;
120 int inv_video;
121 char *p;
122 size_t len;
124 static int reenter; /* STATIC: Re-entrancy check. */
125 MSG *mp, *nmp;
128 * It's possible to reenter msg when it allocates space.
129 * We're probably dead anyway, but no reason to drop core.
131 if (reenter)
132 return;
133 reenter = 1;
136 * Find an empty structure, or allocate a new one. Use the
137 * screen structure if possible, otherwise the global one.
139 if (sp != NULL) {
140 if ((mp = sp->msgq.lh_first) == NULL) {
141 CALLOC(sp, mp, MSG *, 1, sizeof(MSG));
142 if (mp == NULL)
143 goto ret;
144 LIST_INSERT_HEAD(&sp->msgq, mp, q);
145 goto store;
147 } else if ((mp = gp->msgq.lh_first) == NULL) {
148 CALLOC(sp, mp, MSG *, 1, sizeof(MSG));
149 if (mp == NULL)
150 goto ret;
151 LIST_INSERT_HEAD(&gp->msgq, mp, q);
152 goto store;
154 while (!F_ISSET(mp, M_EMPTY) && mp->q.le_next != NULL)
155 mp = mp->q.le_next;
156 if (!F_ISSET(mp, M_EMPTY)) {
157 CALLOC(sp, nmp, MSG *, 1, sizeof(MSG));
158 if (nmp == NULL)
159 goto ret;
160 LIST_INSERT_AFTER(mp, nmp, q);
161 mp = nmp;
164 /* Get enough memory for the message. */
165 store: if (len > mp->blen && binc(sp, &mp->mbuf, &mp->blen, len))
166 goto ret;
168 /* Store the message. */
169 memmove(mp->mbuf, p, len);
170 mp->len = len;
171 mp->flags = inv_video ? M_INV_VIDEO : 0;
173 ret: reenter = 0;
177 * msgrpt --
178 * Report on the lines that changed.
180 * !!!
181 * Historic vi documentation (USD:15-8) claimed that "The editor will also
182 * always tell you when a change you make affects text which you cannot see."
183 * This isn't true -- edit a large file and do "100d|1". We don't implement
184 * this semantic as it would require that we track each line that changes
185 * during a command instead of just keeping count.
188 msg_rpt(sp, is_message)
189 SCR *sp;
190 int is_message;
192 static const char *const action[] = {
193 "added", "changed", "copied", "deleted", "joined", "moved",
194 "put", "left shifted", "right shifted", "yanked", NULL,
196 recno_t total;
197 u_long rval;
198 int first, cnt;
199 size_t blen, len;
200 const char *const *ap;
201 char *bp, *p, number[40];
203 if (F_ISSET(sp, S_EXSILENT))
204 return (0);
206 if ((rval = O_VAL(sp, O_REPORT)) == 0)
207 goto norpt;
209 GET_SPACE_RET(sp, bp, blen, 512);
210 p = bp;
212 total = 0;
213 for (ap = action, cnt = 0, first = 1; *ap != NULL; ++ap, ++cnt)
214 if (sp->rptlines[cnt] != 0) {
215 total += sp->rptlines[cnt];
216 len = snprintf(number, sizeof(number),
217 "%s%lu line%s %s", first ? "" : "; ",
218 sp->rptlines[cnt],
219 sp->rptlines[cnt] > 1 ? "s" : "", *ap);
220 memmove(p, number, len);
221 p += len;
222 first = 0;
226 * If nothing to report, return. Note that the number of lines
227 * must be > than the user's value, not >=. This is historic
228 * practice and means that users cannot report on single line
229 * changes.
231 if (total > rval) {
232 *p = '\0';
234 if (is_message)
235 msgq(sp, M_INFO, "%s", bp);
236 else
237 ex_printf(EXCOOKIE, "%s\n", bp);
240 FREE_SPACE(sp, bp, blen);
242 /* Clear after each report. */
243 norpt: memset(sp->rptlines, 0, sizeof(sp->rptlines));
244 return (0);
248 * binc --
249 * Increase the size of a buffer.
252 binc(sp, argp, bsizep, min)
253 SCR *sp; /* MAY BE NULL */
254 void *argp;
255 size_t *bsizep, min;
257 void *bpp;
258 size_t csize;
260 /* If already larger than the minimum, just return. */
261 csize = *bsizep;
262 if (min && csize >= min)
263 return (0);
265 csize += MAX(min, 256);
266 bpp = *(char **)argp;
268 /* For non-ANSI C realloc implementations. */
269 if (bpp == NULL)
270 bpp = malloc(csize * sizeof(CHAR_T));
271 else
272 bpp = realloc(bpp, csize * sizeof(CHAR_T));
273 if (bpp == NULL) {
274 msgq(sp, M_SYSERR, NULL);
275 *bsizep = 0;
276 return (1);
278 *(char **)argp = bpp;
279 *bsizep = csize;
280 return (0);
284 * nonblank --
285 * Set the column number of the first non-blank character
286 * including or after the starting column. On error, set
287 * the column to 0, it's safest.
290 nonblank(sp, ep, lno, cnop)
291 SCR *sp;
292 EXF *ep;
293 recno_t lno;
294 size_t *cnop;
296 char *p;
297 size_t cnt, len, off;
299 /* Default. */
300 off = *cnop;
301 *cnop = 0;
303 /* Get the line. */
304 if ((p = file_gline(sp, ep, lno, &len)) == NULL) {
305 if (file_lline(sp, ep, &lno))
306 return (1);
307 if (lno == 0)
308 return (0);
309 GETLINE_ERR(sp, lno);
310 return (1);
313 /* Set the offset. */
314 if (len == 0 || off >= len)
315 return (0);
317 for (cnt = off, p = &p[off],
318 len -= off; len && isblank(*p); ++cnt, ++p, --len);
320 /* Set the return. */
321 *cnop = len ? cnt : cnt - 1;
322 return (0);
326 * tail --
327 * Return tail of a path.
329 char *
330 tail(path)
331 char *path;
333 char *p;
335 if ((p = strrchr(path, '/')) == NULL)
336 return (path);
337 return (p + 1);
341 * set_window_size --
342 * Set the window size, the row may be provided as an argument.
345 set_window_size(sp, set_row, ign_env)
346 SCR *sp;
347 u_int set_row;
348 int ign_env;
350 struct winsize win;
351 size_t col, row;
352 int user_set;
353 ARGS *argv[2], a, b;
354 char *s, buf[2048];
357 * Get the screen rows and columns. If the values are wrong, it's
358 * not a big deal -- as soon as the user sets them explicitly the
359 * environment will be set and the screen package will use the new
360 * values.
362 * Try TIOCGWINSZ.
364 row = col = 0;
365 #ifdef TIOCGWINSZ
366 if (ioctl(STDERR_FILENO, TIOCGWINSZ, &win) != -1) {
367 row = win.ws_row;
368 col = win.ws_col;
370 #endif
372 /* If TIOCGWINSZ failed, or had entries of 0, try termcap. */
373 if (row == 0 || col == 0) {
374 s = NULL;
375 if (F_ISSET(&sp->opts[O_TERM], OPT_SET))
376 s = O_STR(sp, O_TERM);
377 else
378 s = getenv("TERM");
379 if (s != NULL && tgetent(buf, s) == 1) {
380 if (row == 0)
381 row = tgetnum("li");
382 if (col == 0)
383 col = tgetnum("co");
386 /* If nothing else, well, it's probably a VT100. */
387 if (row == 0)
388 row = 24;
389 if (col == 0)
390 col = 80;
393 * POSIX 1003.2 requires the environment to override, however,
394 * if we're here because of a signal, we don't want to use the
395 * old values.
397 if (!ign_env) {
398 if ((s = getenv("LINES")) != NULL)
399 row = strtol(s, NULL, 10);
400 if ((s = getenv("COLUMNS")) != NULL)
401 col = strtol(s, NULL, 10);
404 /* But, if we got an argument for the rows, use it. */
405 if (set_row)
406 row = set_row;
408 a.bp = buf;
409 b.bp = NULL;
410 b.len = 0;
411 argv[0] = &a;
412 argv[1] = &b;;
415 * Tell the options code that the screen size has changed.
416 * Since the user didn't do the set, clear the set bits.
418 user_set = F_ISSET(&sp->opts[O_LINES], OPT_SET);
419 a.len = snprintf(buf, sizeof(buf), "lines=%u", row);
420 if (opts_set(sp, argv))
421 return (1);
422 if (user_set)
423 F_CLR(&sp->opts[O_LINES], OPT_SET);
424 user_set = F_ISSET(&sp->opts[O_COLUMNS], OPT_SET);
425 a.len = snprintf(buf, sizeof(buf), "columns=%u", col);
426 if (opts_set(sp, argv))
427 return (1);
428 if (user_set)
429 F_CLR(&sp->opts[O_COLUMNS], OPT_SET);
430 return (0);
434 * set_alt_name --
435 * Set the alternate file name.
437 * Swap the alternate file name. It's a routine because I wanted some place
438 * to hang this comment. The alternate file name (normally referenced using
439 * the special character '#' during file expansion) is set by many
440 * operations. In the historic vi, the commands "ex", and "edit" obviously
441 * set the alternate file name because they switched the underlying file.
442 * Less obviously, the "read", "file", "write" and "wq" commands set it as
443 * well. In this implementation, some new commands have been added to the
444 * list. Where it gets interesting is that the alternate file name is set
445 * multiple times by some commands. If an edit attempt fails (for whatever
446 * reason, like the current file is modified but as yet unwritten), it is
447 * set to the file name that the user was unable to edit. If the edit
448 * succeeds, it is set to the last file name that was edited. Good fun.
450 * If the user edits a temporary file, there are time when there isn't an
451 * alternative file name. A name argument of NULL turns it off.
453 void
454 set_alt_name(sp, name)
455 SCR *sp;
456 char *name;
458 if (sp->alt_name != NULL)
459 FREE(sp->alt_name, strlen(sp->alt_name) + 1);
460 if (name == NULL)
461 sp->alt_name = NULL;
462 else if ((sp->alt_name = strdup(name)) == NULL)
463 msgq(sp, M_SYSERR, NULL);
467 * baud_from_bval --
468 * Return the baud rate using the standard defines.
470 u_long
471 baud_from_bval(sp)
472 SCR *sp;
474 speed_t v;
476 switch (v = cfgetospeed(&sp->gp->original_termios)) {
477 case B50:
478 return (50);
479 case B75:
480 return (75);
481 case B110:
482 return (110);
483 case B134:
484 return (134);
485 case B150:
486 return (150);
487 case B200:
488 return (200);
489 case B300:
490 return (300);
491 case B600:
492 return (600);
493 case B1200:
494 return (1200);
495 case B1800:
496 return (1800);
497 case B2400:
498 return (2400);
499 case B4800:
500 return (4800);
501 case B0: /* Hangup -- ignore. */
502 case B9600:
503 return (9600);
504 case B19200:
505 return (19200);
506 case B38400:
507 return (38400);
508 default:
510 * EXTA and EXTB aren't required by POSIX 1003.1, and
511 * are almost certainly the same as some of the above
512 * values, so they can't be part of the case statement.
514 #ifdef EXTA
515 if (v == EXTA)
516 return (19200);
517 #endif
518 #ifdef EXTB
519 if (v == EXTB)
520 return (38400);
521 #endif
522 msgq(sp, M_ERR, "Unknown terminal baud rate %u.\n", v);
523 return (9600);
528 * v_strdup --
529 * Strdup for wide character strings with an associated length.
531 CHAR_T *
532 v_strdup(sp, str, len)
533 SCR *sp;
534 CHAR_T *str;
535 size_t len;
537 CHAR_T *copy;
539 MALLOC(sp, copy, CHAR_T *, len);
540 if (copy == NULL)
541 return (NULL);
542 memmove(copy, str, len * sizeof(CHAR_T));
543 return (copy);