This commit was generated by cvs2svn to track changes on a CVS vendor
[unix-history] / usr.bin / vi / util.c
CommitLineData
1e64b3ba
JH
1/*-
2 * Copyright (c) 1991, 1993
3 * The Regents of the University of California. All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. All advertising materials mentioning features or use of this software
14 * must display the following acknowledgement:
15 * This product includes software developed by the University of
16 * California, Berkeley and its contributors.
17 * 4. Neither the name of the University nor the names of its contributors
18 * may be used to endorse or promote products derived from this software
19 * without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 */
33
34#ifndef lint
35static char sccsid[] = "@(#)util.c 8.34 (Berkeley) 12/23/93";
36#endif /* not lint */
37
38#include <sys/types.h>
39#include <sys/ioctl.h>
40
41#include <ctype.h>
42#include <curses.h>
43#include <errno.h>
44#include <stdlib.h>
45#include <string.h>
46#include <unistd.h>
47
48#ifdef __STDC__
49#include <stdarg.h>
50#else
51#include <varargs.h>
52#endif
53
54#include "vi.h"
55
56/*
57 * msgq --
58 * Display a message.
59 */
60void
61#ifdef __STDC__
62msgq(SCR *sp, enum msgtype mt, const char *fmt, ...)
63#else
64msgq(sp, mt, fmt, va_alist)
65 SCR *sp;
66 enum msgtype mt;
67 char *fmt;
68 va_dcl
69#endif
70{
71 va_list ap;
72 int len;
73 char msgbuf[1024];
74
75#ifdef __STDC__
76 va_start(ap, fmt);
77#else
78 va_start(ap);
79#endif
80 /*
81 * It's possible to enter msg when there's no screen to hold
82 * the message. Always check sp before using it, and, if it's
83 * NULL, use __global_list.
84 */
85 switch (mt) {
86 case M_BERR:
87 if (sp != NULL && !O_ISSET(sp, O_VERBOSE)) {
88 F_SET(sp, S_BELLSCHED);
89 return;
90 }
91 mt = M_ERR;
92 break;
93 case M_VINFO:
94 if (sp != NULL && !O_ISSET(sp, O_VERBOSE))
95 return;
96 mt = M_INFO;
97 /* FALLTHROUGH */
98 case M_INFO:
99 if (F_ISSET(sp, S_EXSILENT))
100 return;
101 break;
102 case M_ERR:
103 case M_SYSERR:
104 break;
105 default:
106 abort();
107 }
108
109 /* Length is the min length of the message or the buffer. */
110 if (mt == M_SYSERR)
111 if (sp->if_name != NULL)
112 len = snprintf(msgbuf, sizeof(msgbuf),
113 "Error: %s, %d: %s%s%s.", sp->if_name, sp->if_lno,
114 fmt == NULL ? "" : fmt, fmt == NULL ? "" : ": ",
115 strerror(errno));
116 else
117 len = snprintf(msgbuf, sizeof(msgbuf),
118 "Error: %s%s%s.",
119 fmt == NULL ? "" : fmt, fmt == NULL ? "" : ": ",
120 strerror(errno));
121 else {
122 len = sp->if_name == NULL ? 0 : snprintf(msgbuf, sizeof(msgbuf),
123 "%s, %d: ", sp->if_name, sp->if_lno);
124 len += vsnprintf(msgbuf + len, sizeof(msgbuf) - len, fmt, ap);
125 }
126
127 /*
128 * If len >= the size, some characters were discarded.
129 * Ignore trailing nul.
130 */
131 if (len >= sizeof(msgbuf))
132 len = sizeof(msgbuf) - 1;
133
134 msg_app(__global_list, sp, mt == M_ERR ? 1 : 0, msgbuf, len);
135}
136
137/*
138 * msg_app --
139 * Append a message into the queue. This can fail, but there's
140 * nothing we can do if it does.
141 */
142void
143msg_app(gp, sp, inv_video, p, len)
144 GS *gp;
145 SCR *sp;
146 int inv_video;
147 char *p;
148 size_t len;
149{
150 static int reenter; /* STATIC: Re-entrancy check. */
151 MSG *mp, *nmp;
152
153 /*
154 * It's possible to reenter msg when it allocates space.
155 * We're probably dead anyway, but no reason to drop core.
156 */
157 if (reenter)
158 return;
159 reenter = 1;
160
161 /*
162 * Find an empty structure, or allocate a new one. Use the
163 * screen structure if possible, otherwise the global one.
164 */
165 if (sp != NULL) {
166 if ((mp = sp->msgq.lh_first) == NULL) {
167 CALLOC(sp, mp, MSG *, 1, sizeof(MSG));
168 if (mp == NULL)
169 goto ret;
170 LIST_INSERT_HEAD(&sp->msgq, mp, q);
171 goto store;
172 }
173 } else if ((mp = gp->msgq.lh_first) == NULL) {
174 CALLOC(sp, mp, MSG *, 1, sizeof(MSG));
175 if (mp == NULL)
176 goto ret;
177 LIST_INSERT_HEAD(&gp->msgq, mp, q);
178 goto store;
179 }
180 while (!F_ISSET(mp, M_EMPTY) && mp->q.le_next != NULL)
181 mp = mp->q.le_next;
182 if (!F_ISSET(mp, M_EMPTY)) {
183 CALLOC(sp, nmp, MSG *, 1, sizeof(MSG));
184 if (nmp == NULL)
185 goto ret;
186 LIST_INSERT_AFTER(mp, nmp, q);
187 mp = nmp;
188 }
189
190 /* Get enough memory for the message. */
191store: if (len > mp->blen && binc(sp, &mp->mbuf, &mp->blen, len))
192 goto ret;
193
194 /* Store the message. */
195 memmove(mp->mbuf, p, len);
196 mp->len = len;
197 mp->flags = inv_video ? M_INV_VIDEO : 0;
198
199ret: reenter = 0;
200}
201
202/*
203 * msgrpt --
204 * Report on the lines that changed.
205 *
206 * !!!
207 * Historic vi documentation (USD:15-8) claimed that "The editor will also
208 * always tell you when a change you make affects text which you cannot see."
209 * This isn't true -- edit a large file and do "100d|1". We don't implement
210 * this semantic as it would require that we track each line that changes
211 * during a command instead of just keeping count.
212 */
213int
214msg_rpt(sp, is_message)
215 SCR *sp;
216 int is_message;
217{
218 static const char *const action[] = {
219 "added", "changed", "copied", "deleted", "joined", "moved",
220 "put", "left shifted", "right shifted", "yanked", NULL,
221 };
222 recno_t total;
223 u_long rval;
224 int first, cnt;
225 size_t blen, len;
226 const char *const *ap;
227 char *bp, *p, number[40];
228
229 if (F_ISSET(sp, S_EXSILENT))
230 return (0);
231
232 if ((rval = O_VAL(sp, O_REPORT)) == 0)
233 goto norpt;
234
235 GET_SPACE_RET(sp, bp, blen, 512);
236 p = bp;
237
238 total = 0;
239 for (ap = action, cnt = 0, first = 1; *ap != NULL; ++ap, ++cnt)
240 if (sp->rptlines[cnt] != 0) {
241 total += sp->rptlines[cnt];
242 len = snprintf(number, sizeof(number),
243 "%s%lu line%s %s", first ? "" : "; ",
244 sp->rptlines[cnt],
245 sp->rptlines[cnt] > 1 ? "s" : "", *ap);
246 memmove(p, number, len);
247 p += len;
248 first = 0;
249 }
250
251 /*
252 * If nothing to report, return. Note that the number of lines
253 * must be > than the user's value, not >=. This is historic
254 * practice and means that users cannot report on single line
255 * changes.
256 */
257 if (total > rval) {
258 *p = '\0';
259
260 if (is_message)
261 msgq(sp, M_INFO, "%s", bp);
262 else
263 ex_printf(EXCOOKIE, "%s\n", bp);
264 }
265
266 FREE_SPACE(sp, bp, blen);
267
268 /* Clear after each report. */
269norpt: memset(sp->rptlines, 0, sizeof(sp->rptlines));
270 return (0);
271}
272
273/*
274 * binc --
275 * Increase the size of a buffer.
276 */
277int
278binc(sp, argp, bsizep, min)
279 SCR *sp; /* MAY BE NULL */
280 void *argp;
281 size_t *bsizep, min;
282{
283 void *bpp;
284 size_t csize;
285
286 /* If already larger than the minimum, just return. */
287 csize = *bsizep;
288 if (min && csize >= min)
289 return (0);
290
291 csize += MAX(min, 256);
292 bpp = *(char **)argp;
293
294 /* For non-ANSI C realloc implementations. */
295 if (bpp == NULL)
296 bpp = malloc(csize * sizeof(CHAR_T));
297 else
298 bpp = realloc(bpp, csize * sizeof(CHAR_T));
299 if (bpp == NULL) {
300 msgq(sp, M_SYSERR, NULL);
301 *bsizep = 0;
302 return (1);
303 }
304 *(char **)argp = bpp;
305 *bsizep = csize;
306 return (0);
307}
308
309/*
310 * nonblank --
311 * Set the column number of the first non-blank character
312 * including or after the starting column. On error, set
313 * the column to 0, it's safest.
314 */
315int
316nonblank(sp, ep, lno, cnop)
317 SCR *sp;
318 EXF *ep;
319 recno_t lno;
320 size_t *cnop;
321{
322 char *p;
323 size_t cnt, len, off;
324
325 /* Default. */
326 off = *cnop;
327 *cnop = 0;
328
329 /* Get the line. */
330 if ((p = file_gline(sp, ep, lno, &len)) == NULL) {
331 if (file_lline(sp, ep, &lno))
332 return (1);
333 if (lno == 0)
334 return (0);
335 GETLINE_ERR(sp, lno);
336 return (1);
337 }
338
339 /* Set the offset. */
340 if (len == 0 || off >= len)
341 return (0);
342
343 for (cnt = off, p = &p[off],
344 len -= off; len && isblank(*p); ++cnt, ++p, --len);
345
346 /* Set the return. */
347 *cnop = len ? cnt : cnt - 1;
348 return (0);
349}
350
351/*
352 * tail --
353 * Return tail of a path.
354 */
355char *
356tail(path)
357 char *path;
358{
359 char *p;
360
361 if ((p = strrchr(path, '/')) == NULL)
362 return (path);
363 return (p + 1);
364}
365
366/*
367 * set_window_size --
368 * Set the window size, the row may be provided as an argument.
369 */
370int
371set_window_size(sp, set_row, ign_env)
372 SCR *sp;
373 u_int set_row;
374 int ign_env;
375{
376 struct winsize win;
377 size_t col, row;
378 int user_set;
379 ARGS *argv[2], a, b;
380 char *s, buf[2048];
381
382 /*
383 * Get the screen rows and columns. If the values are wrong, it's
384 * not a big deal -- as soon as the user sets them explicitly the
385 * environment will be set and the screen package will use the new
386 * values.
387 *
388 * Try TIOCGWINSZ.
389 */
390 row = col = 0;
391#ifdef TIOCGWINSZ
392 if (ioctl(STDERR_FILENO, TIOCGWINSZ, &win) != -1) {
393 row = win.ws_row;
394 col = win.ws_col;
395 }
396#endif
397
398 /* If TIOCGWINSZ failed, or had entries of 0, try termcap. */
399 if (row == 0 || col == 0) {
400 s = NULL;
401 if (F_ISSET(&sp->opts[O_TERM], OPT_SET))
402 s = O_STR(sp, O_TERM);
403 else
404 s = getenv("TERM");
405 if (s != NULL && tgetent(buf, s) == 1) {
406 if (row == 0)
407 row = tgetnum("li");
408 if (col == 0)
409 col = tgetnum("co");
410 }
411 }
412 /* If nothing else, well, it's probably a VT100. */
413 if (row == 0)
414 row = 24;
415 if (col == 0)
416 col = 80;
417
418 /*
419 * POSIX 1003.2 requires the environment to override, however,
420 * if we're here because of a signal, we don't want to use the
421 * old values.
422 */
423 if (!ign_env) {
424 if ((s = getenv("LINES")) != NULL)
425 row = strtol(s, NULL, 10);
426 if ((s = getenv("COLUMNS")) != NULL)
427 col = strtol(s, NULL, 10);
428 }
429
430 /* But, if we got an argument for the rows, use it. */
431 if (set_row)
432 row = set_row;
433
434 a.bp = buf;
435 b.bp = NULL;
436 b.len = 0;
437 argv[0] = &a;
438 argv[1] = &b;;
439
440 /*
441 * Tell the options code that the screen size has changed.
442 * Since the user didn't do the set, clear the set bits.
443 */
444 user_set = F_ISSET(&sp->opts[O_LINES], OPT_SET);
445 a.len = snprintf(buf, sizeof(buf), "lines=%u", row);
446 if (opts_set(sp, argv))
447 return (1);
448 if (user_set)
449 F_CLR(&sp->opts[O_LINES], OPT_SET);
450 user_set = F_ISSET(&sp->opts[O_COLUMNS], OPT_SET);
451 a.len = snprintf(buf, sizeof(buf), "columns=%u", col);
452 if (opts_set(sp, argv))
453 return (1);
454 if (user_set)
455 F_CLR(&sp->opts[O_COLUMNS], OPT_SET);
456 return (0);
457}
458
459/*
460 * set_alt_name --
461 * Set the alternate file name.
462 *
463 * Swap the alternate file name. It's a routine because I wanted some place
464 * to hang this comment. The alternate file name (normally referenced using
465 * the special character '#' during file expansion) is set by many
466 * operations. In the historic vi, the commands "ex", and "edit" obviously
467 * set the alternate file name because they switched the underlying file.
468 * Less obviously, the "read", "file", "write" and "wq" commands set it as
469 * well. In this implementation, some new commands have been added to the
470 * list. Where it gets interesting is that the alternate file name is set
471 * multiple times by some commands. If an edit attempt fails (for whatever
472 * reason, like the current file is modified but as yet unwritten), it is
473 * set to the file name that the user was unable to edit. If the edit
474 * succeeds, it is set to the last file name that was edited. Good fun.
475 *
476 * If the user edits a temporary file, there are time when there isn't an
477 * alternative file name. A name argument of NULL turns it off.
478 */
479void
480set_alt_name(sp, name)
481 SCR *sp;
482 char *name;
483{
484 if (sp->alt_name != NULL)
485 FREE(sp->alt_name, strlen(sp->alt_name) + 1);
486 if (name == NULL)
487 sp->alt_name = NULL;
488 else if ((sp->alt_name = strdup(name)) == NULL)
489 msgq(sp, M_SYSERR, NULL);
490}
491
492/*
493 * baud_from_bval --
494 * Return the baud rate using the standard defines.
495 */
496u_long
497baud_from_bval(sp)
498 SCR *sp;
499{
500 speed_t v;
501
502 switch (v = cfgetospeed(&sp->gp->original_termios)) {
503 case B50:
504 return (50);
505 case B75:
506 return (75);
507 case B110:
508 return (110);
509 case B134:
510 return (134);
511 case B150:
512 return (150);
513 case B200:
514 return (200);
515 case B300:
516 return (300);
517 case B600:
518 return (600);
519 case B1200:
520 return (1200);
521 case B1800:
522 return (1800);
523 case B2400:
524 return (2400);
525 case B4800:
526 return (4800);
527 case B0: /* Hangup -- ignore. */
528 case B9600:
529 return (9600);
530 case B19200:
531 return (19200);
532 case B38400:
533 return (38400);
534 default:
535 /*
536 * EXTA and EXTB aren't required by POSIX 1003.1, and
537 * are almost certainly the same as some of the above
538 * values, so they can't be part of the case statement.
539 */
540#ifdef EXTA
541 if (v == EXTA)
542 return (19200);
543#endif
544#ifdef EXTB
545 if (v == EXTB)
546 return (38400);
11c35310
NW
547#endif
548#ifdef B57600
549 if (v == B57600)
550 return (57600);
551#endif
552#ifdef B115200
553 if (v == B115200)
554 return (115200);
1e64b3ba
JH
555#endif
556 msgq(sp, M_ERR, "Unknown terminal baud rate %u.\n", v);
557 return (9600);
558 }
559}
560
561/*
562 * v_strdup --
563 * Strdup for wide character strings with an associated length.
564 */
565CHAR_T *
566v_strdup(sp, str, len)
567 SCR *sp;
568 CHAR_T *str;
569 size_t len;
570{
571 CHAR_T *copy;
572
573 MALLOC(sp, copy, CHAR_T *, len);
574 if (copy == NULL)
575 return (NULL);
576 memmove(copy, str, len * sizeof(CHAR_T));
577 return (copy);
578}