msg_sys.def revision 1.46 1 /* $NetBSD: msg_sys.def,v 1.46 2019/06/20 11:31:12 martin Exp $ */
2
3 /*
4 * Copyright 1997 Piermont Information Systems Inc.
5 * All rights reserved.
6 *
7 * Written by Philip A. Nelson for Piermont Information Systems Inc.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
17 * 3. The name of Piermont Information Systems Inc. may not be used to endorse
18 * or promote products derived from this software without specific prior
19 * written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY PIERMONT INFORMATION SYSTEMS INC. ``AS IS''
22 * AND 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 PIERMONT INFORMATION SYSTEMS INC. BE
25 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
26 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
27 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
29 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
30 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
31 * THE POSSIBILITY OF SUCH DAMAGE.
32 *
33 */
34
35 static WINDOW *msg_win = NULL;
36 static char *cbuffer;
37 static size_t cbuffersize;
38
39 static int last_i_was_nl, last_i_was_space;
40 static int last_o_was_punct, last_o_was_space;
41
42 static void _msg_beep(void);
43 static int _msg_vprintf(int, const char *, va_list);
44 #define MSG_PROMPT_ECHO 1
45 #define MSG_PROMPT_HIDE_DFLT 2
46 static void _msg_vprompt(const char *, int, const char *, char *,
47 size_t, va_list);
48
49 static char *msgmap = MAP_FAILED;
50 static size_t msgmapsz;
51 static unsigned int msgmapcount;
52
53 /* Routines */
54
55 static void
56 _msg_beep(void)
57 {
58
59 fprintf(stderr, "\a");
60 }
61
62 WINDOW *
63 msg_window(WINDOW *window)
64 {
65 size_t ncbuffersize;
66 char *ncbuffer;
67 WINDOW *old;
68
69 old = msg_win;
70 if (!window)
71 return old;
72 msg_win = window;
73
74 ncbuffersize = getmaxx(window) * getmaxy(window) + 1;
75 while (ncbuffersize > cbuffersize) {
76 ncbuffer = malloc(ncbuffersize);
77 if (ncbuffer == NULL) {
78 /* we might get truncated messages... */
79 ncbuffersize <<= 1;
80 continue;
81 }
82 if (cbuffer != NULL)
83 free(cbuffer);
84 cbuffer = ncbuffer;
85 cbuffersize = ncbuffersize;
86 break;
87 }
88 last_o_was_punct = 0;
89 last_o_was_space = 1;
90 return old;
91 }
92
93 int
94 msg_file(const char *file)
95 {
96 int fd;
97
98 if (msgmap != MAP_FAILED)
99 munmap(msgmap, msgmapsz);
100 msgmap = MAP_FAILED;
101 if (!file)
102 return 0;
103 fd = open(file, O_RDONLY, 0);
104 if (fd == -1)
105 return -1;
106 msgmapsz = lseek(fd, 0, SEEK_END);
107 msgmap = mmap(0, msgmapsz, PROT_READ, MAP_SHARED, fd, 0);
108 close(fd);
109 if (msgmap == MAP_FAILED)
110 return -1;
111 /* check_magic */
112 if (strcmp(msgmap, "MSGTXTS") != 0) {
113 msg_file(NULL);
114 return -1;
115 }
116 msgmapcount = atoi(msgmap + 8);
117 return 0;
118 }
119
120 const char *
121 msg_string(msg msg_no)
122 {
123 uintptr_t m = (uintptr_t)msg_no;
124
125 if (m > sizeof msg_list / sizeof msg_list[0])
126 /* guess that we were passed a text string */
127 return msg_no;
128
129 if (msgmap != MAP_FAILED && m != 0 && m <= msgmapcount) {
130 unsigned int offset = atoi(msgmap + 8 + 8 * m);
131 if (offset != 0 && offset < msgmapsz)
132 return msgmap + offset;
133 }
134
135 return msg_list[m];
136 }
137
138 void
139 msg_clear(void)
140 {
141
142 wclear(msg_win);
143 last_o_was_punct = 0;
144 last_o_was_space = 1;
145 }
146
147 void
148 msg_standout(void)
149 {
150
151 wstandout(msg_win);
152 }
153
154 void
155 msg_standend(void)
156 {
157
158 wstandend(msg_win);
159 }
160
161 static int __printflike(2, 0)
162 _msg_vprintf(int auto_fill, const char *fmt, va_list ap)
163 {
164 const char *wstart, *afterw;
165 int wordlen, nspaces;
166 int ret;
167
168 ret = vsnprintf(cbuffer, cbuffersize, fmt, ap);
169
170 if (!auto_fill) {
171 waddstr(msg_win, cbuffer);
172
173 /*
174 * nothing is perfect if they flow text after a table,
175 * but this may be decent.
176 */
177 last_i_was_nl = last_i_was_space = 1;
178 last_o_was_punct = 0;
179 last_o_was_space = 1;
180 goto out;
181 }
182
183 for (wstart = afterw = cbuffer; *wstart; wstart = afterw) {
184
185 /* eat one space, or a whole word of non-spaces */
186 if (isspace((unsigned char)*afterw))
187 afterw++;
188 else
189 while (*afterw && !isspace((unsigned char)*afterw))
190 afterw++;
191
192 /* this is an nl: special formatting necessary */
193 if (*wstart == '\n') {
194 if (last_i_was_nl || last_i_was_space) {
195
196 if (getcurx(msg_win) != 0)
197 waddch(msg_win, '\n');
198 if (last_i_was_nl) {
199 /* last was an nl: paragraph break */
200 waddch(msg_win, '\n');
201 } else {
202 /* last was space: line break */
203 }
204 last_o_was_punct = 0;
205 last_o_was_space = 1;
206 } else {
207 /* last_o_was_punct unchanged */
208 /* last_o_was_space unchanged */
209 }
210 last_i_was_space = 1;
211 last_i_was_nl = 1;
212 continue;
213 }
214
215 /* this is a tab: special formatting necessary. */
216 if (*wstart == '\t') {
217 if (last_i_was_nl) {
218 /* last was an nl: list indent */
219 if (getcurx(msg_win) != 0)
220 waddch(msg_win, '\n');
221 } else {
222 /* last was not an nl: columns */
223 }
224 waddch(msg_win, '\t');
225 last_i_was_nl = 0;
226 last_i_was_space = 1;
227 last_o_was_punct = 0;
228 last_o_was_space = 1;
229 continue;
230 }
231
232 /* this is a space: ignore it but set flags */
233 last_i_was_nl = 0; /* all newlines handled above */
234 last_i_was_space = isspace((unsigned char)*wstart);
235 if (last_i_was_space)
236 continue;
237
238 /*
239 * we have a real "word," i.e. a sequence of non-space
240 * characters. wstart is now the start of the word,
241 * afterw is the next character after the end.
242 */
243 wordlen = afterw - wstart;
244 nspaces = last_o_was_space ? 0 : (last_o_was_punct ? 2 : 1);
245 if ((getcurx(msg_win) + nspaces + wordlen) >=
246 getmaxx(msg_win) &&
247 wordlen < (getmaxx(msg_win) / 3)) {
248 /* wrap the line */
249 waddch(msg_win, '\n');
250 nspaces = 0;
251 }
252
253 /* output the word, preceded by spaces if necessary */
254 while (nspaces-- > 0)
255 waddch(msg_win, ' ');
256 waddbytes(msg_win, wstart, wordlen);
257
258 /* set up the 'last' state for the next time around */
259 switch (afterw[-1]) {
260 case '.':
261 case '?':
262 case '!':
263 last_o_was_punct = 1;
264 break;
265 default:
266 last_o_was_punct = 0;
267 break;
268 }
269 last_o_was_space = 0;
270
271 /* ... and do it all again! */
272 }
273
274 /* String ended with a newline. They really want a line break. */
275 if (last_i_was_nl) {
276 if (getcurx(msg_win) != 0)
277 waddch(msg_win, '\n');
278 last_o_was_punct = 0;
279 last_o_was_space = 1;
280 }
281
282 out:
283 wrefresh(msg_win);
284 return ret;
285 }
286
287 void
288 msg_display(msg msg_no)
289 {
290
291 msg_clear();
292 msg_printf("%s", msg_string(msg_no));
293 }
294
295 void __printflike(2, 3)
296 msg_fmt_display(msg msg_no, const char *fmt, ...)
297 {
298 va_list ap;
299
300 msg_clear();
301
302 va_start(ap, fmt);
303 (void)_msg_vprintf(1, fmtcheck(msg_string(msg_no), fmt), ap);
304 va_end(ap);
305 }
306
307 void
308 msg_display_add(msg msg_no)
309 {
310
311 msg_printf("%s", msg_string(msg_no));
312 }
313
314 void __printflike(2, 3)
315 msg_fmt_display_add(msg msg_no, const char *fmt, ...)
316 {
317 va_list ap;
318
319 va_start(ap, fmt);
320 (void)_msg_vprintf(1, fmtcheck(msg_string(msg_no), fmt), ap);
321 va_end(ap);
322 }
323
324 void __printflike(1, 2)
325 msg_printf(const char *fmt, ...)
326 {
327 va_list ap;
328
329 va_start(ap, fmt);
330 (void)_msg_vprintf(1, fmt, ap);
331 va_end(ap);
332 }
333
334 static void __printflike(1, 0)
335 _msg_vprompt(const char *fmt, int flags, const char *def, char *val,
336 size_t val_buf_len, va_list ap)
337 {
338 int ch;
339 int len, pos, npos, off;
340 int first;
341 int txt_y, txt_x;
342 char *ibuf;
343 int maxx;
344
345 if (val == NULL || val_buf_len == 0) {
346 /* No answer wanted */
347 val = NULL;
348 val_buf_len = 1;
349 }
350
351 ibuf = malloc(val_buf_len);
352
353 keypad(msg_win, TRUE);
354 _msg_vprintf(0, fmt, ap);
355 ibuf[0] = 0;
356 if (def != NULL && *def) {
357 if (flags & MSG_PROMPT_HIDE_DFLT)
358 strlcpy(ibuf, def, val_buf_len);
359 else {
360 waddstr(msg_win, " [");
361 waddstr(msg_win, def);
362 waddstr(msg_win, "]");
363 }
364 }
365 waddstr(msg_win, ": ");
366 len = strlen(ibuf);
367 pos = len;
368 getyx(msg_win, txt_y, txt_x);
369 maxx = getmaxx(msg_win) - txt_x - 1;
370 off = 0;
371
372 for (first = 1; ; first = 0) {
373
374 if (flags & MSG_PROMPT_ECHO) {
375 /* shift text right as we near the buffer start */
376 if (pos - off < 4)
377 off = pos - 4;
378 /* keep offset to a minimum if we are at the end */
379 if (pos == len)
380 off = pos - maxx;
381 if (off < 0 || len <= maxx)
382 off = 0;
383 /* shift text left as we near the buffer end */
384 npos = pos + 4;
385 if (npos > len)
386 npos = len;
387 if (npos - off > maxx)
388 off = npos - maxx;
389 /* calc. length to display */
390 npos = len - off;
391 if (npos > maxx)
392 npos = maxx;
393 mvwaddnstr(msg_win, txt_y, txt_x, ibuf + off, npos);
394 wclrtoeol(msg_win);
395 if (off != 0)
396 mvwaddstr(msg_win, txt_y, txt_x, "+");
397 wmove(msg_win, txt_y, txt_x + pos - off);
398 wrefresh(msg_win);
399 }
400
401 ch = wgetch(msg_win);
402 if (ch == '\n')
403 break;
404
405 switch (ch) {
406 case KEY_BACKSPACE:
407 case 'h' & 0x1f: case 0x7f: /* bs or del - delete left */
408 if (first) {
409 /* delete all of default string */
410 len = pos = 0;
411 break;
412 }
413 if (pos > 0) {
414 memmove(ibuf + pos - 1, ibuf + pos, len - pos);
415 len--;
416 pos--;
417 } else
418 _msg_beep();
419 break;
420 case 'l' & 0x1f:
421 endwin();
422 doupdate();
423 break;
424 case 'u' & 0x1f: /* ^U; line kill */
425 /* kill line */
426 len = pos = 0;
427 break;
428 case 'w' & 0x1f: /* ^W; word kill */
429 /*
430 * word kill kills the spaces and the 'word'
431 * (non-spaces) last typed. the spaces before
432 * the 'word' aren't killed.
433 */
434 npos = pos;
435 while (npos > 0 && isspace((unsigned char)ibuf[npos - 1]))
436 npos--;
437 while (npos > 0 && !isspace((unsigned char)ibuf[npos - 1]))
438 npos--;
439 memmove(ibuf + npos, ibuf + pos, len - pos);
440 len -= pos - npos;
441 pos = npos;
442 break;
443 case KEY_LEFT:
444 if (pos > 0)
445 pos--;
446 break;
447 case KEY_RIGHT:
448 if (len == 0 && pos == 0 && def != NULL) {
449 /* restore default! */
450 strlcpy(ibuf, def, val_buf_len);
451 len = pos = strlen(ibuf);
452 break;
453 }
454 if (pos < len)
455 pos++;
456 break;
457 default:
458 if (len < (int)(val_buf_len - 1) && isprint(ch)) {
459 memmove(ibuf + pos + 1, ibuf + pos, len - pos);
460 ibuf[pos++] = ch;
461 len++;
462 } else
463 _msg_beep();
464 break;
465 }
466 }
467
468 if (flags & MSG_PROMPT_ECHO) {
469 mvwaddch(msg_win, txt_y, txt_x + len - off, '\n');
470 last_o_was_punct = 0;
471 last_o_was_space = 1;
472 }
473
474 if (val != NULL) {
475 /* copy the appropriate string to the output */
476 if (len != 0 || flags & MSG_PROMPT_HIDE_DFLT) {
477 ibuf[len] = '\0';
478 strlcpy(val, ibuf, val_buf_len);
479 } else if (def != NULL && val != def) {
480 strlcpy(val, def, val_buf_len);
481 }
482 }
483 free(ibuf);
484 }
485
486 void
487 msg_prompt(msg msg_no, const char *def, char *val, size_t val_buf_len)
488 {
489
490 msg_fmt_prompt(msg_no, def, val, val_buf_len, "");
491 }
492
493 void __printflike(5, 6)
494 msg_fmt_prompt(msg msg_no, const char *def, char *val, size_t val_buf_len,
495 const char *fmt, ...)
496 {
497 va_list ap;
498
499 msg_clear();
500
501 va_start(ap, fmt);
502 _msg_vprompt(fmtcheck(msg_string(msg_no), fmt), MSG_PROMPT_ECHO,
503 def, val, val_buf_len, ap);
504 va_end(ap);
505 }
506
507 void
508 msg_prompt_win(msg msg_no, int x, int y, int w, int h,
509 const char *def, char *val, size_t val_buf_len)
510 {
511 msg_fmt_prompt_win(msg_no, x, y, w, h, def, val, val_buf_len, "");
512 }
513
514 void __printflike(9, 10)
515 msg_fmt_prompt_win(msg msg_no, int x, int y, int w, int h,
516 const char *def, char *val, size_t val_buf_len, const char *fmt, ...)
517 {
518 va_list ap;
519 WINDOW *win;
520 WINDOW *svmsg = NULL, *sv_win = NULL; /* XXX -Wuninitialized [many] */
521 int maxx, maxy;
522 int msg_flags = MSG_PROMPT_ECHO | MSG_PROMPT_HIDE_DFLT;
523 const char *np, *ep;
524
525 maxx = getmaxx(msg_win);
526 maxy = getmaxy(msg_win);
527 if (w == 0) {
528 va_start(ap, fmt);
529 w = vsnprintf(NULL, 0, fmtcheck(msg_string(msg_no), fmt), ap);
530 va_end(ap);
531 if (def != NULL && *def != 0 && w + (int)val_buf_len * 2 < maxx) {
532 w += 2 + strlen(def) + 1;
533 msg_flags &= ~MSG_PROMPT_HIDE_DFLT;
534 }
535 w += 1 + 2 + val_buf_len + 1;
536 if (w > maxx) {
537 if (!(msg_flags & MSG_PROMPT_HIDE_DFLT)) {
538 w -= 2 + strlen(def) + 1;
539 msg_flags |= MSG_PROMPT_HIDE_DFLT;
540 }
541 w = maxx;
542 }
543 } else if (w > 0 && def != NULL && *def != 0) {
544 size_t tl = strlen(def);
545 if (tl + 1 + 2 + val_buf_len + 1 < (unsigned)w)
546 msg_flags &= ~MSG_PROMPT_HIDE_DFLT;
547 }
548
549 if (x == -1)
550 x = (maxx - w) / 2 + 1;
551 if (h < 0) {
552 h = 3;
553 for (np = msg_string(msg_no); (ep = strchr(np, '\n'));
554 np = ep + 1)
555 h++;
556 }
557 if (h < 3)
558 h = 3;
559 if (y < 3)
560 y = (maxy - h) / 2;
561 if (y + h > maxy)
562 y = maxy - h;
563
564 win = subwin(msg_win, h, w, y, x);
565 if (win == NULL)
566 wprintw(msg_win, "msg_prompt_win: "
567 "newwin(%d, %d, %d, %d) failed\n",
568 h, w, y, x);
569 else {
570 /*
571 * Save screen contents from under our window
572 * Due to a mis-feature of NetBSD curses, curscr contains
573 * the data processed by doupdate() not that by wnoutrefresh().
574 * We must call doupdate() here to ensure we save the correct
575 * data. See PR 26660
576 */
577 doupdate();
578 sv_win = dupwin(win);
579 if (sv_win)
580 overwrite(curscr, sv_win);
581 wbkgd(win, getbkgd(msg_win));
582 wattrset(win, getattrs(msg_win));
583 box(win, 0, 0);
584 wrefresh(win);
585
586 /* Change message window to be our little box */
587 svmsg = msg_window(subwin(msg_win, h - 2, w - 2, y + 1, x + 1));
588 wbkgd(msg_win, getbkgd(win));
589 wattrset(msg_win, getattrs(win));
590
591 msg_clear();
592 }
593
594 va_start(ap, fmt);
595 _msg_vprompt(msg_string(msg_no), msg_flags, def, val, val_buf_len, ap);
596 va_end(ap);
597
598 if (win != NULL) {
599 wclear(win);
600 if (sv_win) {
601 /* Restore original screen contents */
602 overwrite(sv_win, win);
603 delwin(sv_win);
604 }
605 wnoutrefresh(win);
606 /* Restore normal message window */
607 delwin(msg_window(svmsg));
608 delwin(win);
609 }
610 }
611
612 void
613 msg_prompt_add(msg msg_no, const char *def, char *val, size_t val_buf_len)
614 {
615
616 msg_fmt_prompt_add(msg_no, def, val, val_buf_len, "");
617 }
618
619 void __printflike(5, 6)
620 msg_fmt_prompt_add(msg msg_no, const char *def, char *val, size_t val_buf_len,
621 const char *fmt, ...)
622 {
623 va_list ap;
624
625 va_start(ap, fmt);
626 _msg_vprompt(fmtcheck(msg_string(msg_no), fmt), MSG_PROMPT_ECHO, def,
627 val, val_buf_len, ap);
628 va_end(ap);
629 }
630
631 void
632 msg_prompt_noecho(msg msg_no, const char *def, char *val, size_t val_buf_len)
633 {
634 msg_fmt_prompt_noecho(msg_no, def, val, val_buf_len, "");
635 }
636
637 void __printflike(5, 6)
638 msg_fmt_prompt_noecho(msg msg_no, const char *def, char *val,
639 size_t val_buf_len, const char *fmt, ...)
640 {
641 va_list ap;
642
643 msg_clear();
644
645 va_start(ap, fmt);
646 _msg_vprompt(fmtcheck(msg_string(msg_no), fmt), 0, def, val,
647 val_buf_len, ap);
648 va_end(ap);
649 }
650
651 void
652 msg_table_add(msg msg_no)
653 {
654
655 msg_printf("%s", msg_string(msg_no));
656 }
657
658 void __printflike(2, 3)
659 msg_fmt_table_add(msg msg_no, const char *fmt, ...)
660 {
661 va_list ap;
662
663 va_start(ap, fmt);
664 (void)_msg_vprintf(0, fmtcheck(msg_string(msg_no), fmt), ap);
665 va_end(ap);
666 }
667
668 int
669 msg_row(void)
670 {
671
672 return getcury(msg_win) + getbegy(msg_win);
673 }
674