msg_sys.def revision 1.26 1 /* $NetBSD: msg_sys.def,v 1.26 2003/09/25 18:32:10 dsl 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. All advertising materials mentioning features or use of this software
18 * must display the following acknowledgement:
19 * This product includes software develooped for the NetBSD Project by
20 * Piermont Information Systems Inc.
21 * 4. The name of Piermont Information Systems Inc. may not be used to endorse
22 * or promote products derived from this software without specific prior
23 * written permission.
24 *
25 * THIS SOFTWARE IS PROVIDED BY PIERMONT INFORMATION SYSTEMS INC. ``AS IS''
26 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
28 * ARE DISCLAIMED. IN NO EVENT SHALL PIERMONT INFORMATION SYSTEMS INC. BE
29 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
30 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
31 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
32 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
33 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
34 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
35 * THE POSSIBILITY OF SUCH DAMAGE.
36 *
37 */
38
39 static WINDOW *msg_win = NULL;
40 static char *cbuffer;
41 static size_t cbuffersize;
42
43 static int last_i_was_nl, last_i_was_space;
44 static int last_o_was_punct, last_o_was_space;
45
46 static void _msg_beep(void);
47 static int _msg_vprintf(int, const char *, va_list);
48 static void _msg_vprompt(const char *, int, const char *, char *,
49 size_t, va_list);
50
51 static char *msgmap = MAP_FAILED;
52 static size_t msgmapsz;
53 static int msgmapcount;
54
55 /* Routines */
56
57 static void
58 _msg_beep(void)
59 {
60
61 fprintf(stderr, "\a");
62 }
63
64 WINDOW *
65 msg_window(WINDOW *window)
66 {
67 size_t ncbuffersize;
68 char *ncbuffer;
69 WINDOW *old;
70
71 old = msg_win;
72 if (!window)
73 return old;
74 msg_win = window;
75
76 ncbuffersize = getmaxx(window) * getmaxy(window) + 1;
77 while (ncbuffersize > cbuffersize) {
78 ncbuffer = malloc(ncbuffersize);
79 if (ncbuffer == NULL) {
80 /* we might get truncated messages... */
81 ncbuffersize <<= 1;
82 continue;
83 }
84 if (cbuffer != NULL)
85 free(cbuffer);
86 cbuffer = ncbuffer;
87 cbuffersize = ncbuffersize;
88 break;
89 }
90 last_o_was_punct = 0;
91 last_o_was_space = 1;
92 return old;
93 }
94
95 int
96 msg_file(const char *file)
97 {
98 int fd;
99
100 if (msgmap != MAP_FAILED)
101 munmap(msgmap, msgmapsz);
102 msgmap = MAP_FAILED;
103 if (!file)
104 return 0;
105 fd = open(file, O_RDONLY, 0);
106 if (fd == -1)
107 return -1;
108 msgmapsz = lseek(fd, 0, SEEK_END);
109 msgmap = mmap(0, msgmapsz, PROT_READ, MAP_SHARED, fd, 0);
110 close(fd);
111 if (msgmap == MAP_FAILED)
112 return -1;
113 /* check_magic */
114 if (strcmp(msgmap, "MSGTXTS") != 0) {
115 msg_file(NULL);
116 return -1;
117 }
118 msgmapcount = atoi(msgmap + 8);
119 return 0;
120 }
121
122 const char *
123 msg_string(msg msg_no)
124 {
125 int m = (intptr_t)msg_no;
126
127 if (m > sizeof msg_list / sizeof msg_list[0])
128 /* guess that we were passed a text string */
129 return msg_no;
130
131 if (msgmap != MAP_FAILED && m != 0 && m <= msgmapcount) {
132 unsigned int offset = atoi(msgmap + 8 + 8 * m);
133 if (offset != 0 && offset < msgmapsz)
134 return msgmap + offset;
135 }
136
137 return msg_list[m];
138 }
139
140 void
141 msg_clear(void)
142 {
143
144 wclear(msg_win);
145 last_o_was_punct = 0;
146 last_o_was_space = 1;
147 }
148
149 void
150 msg_standout(void)
151 {
152
153 wstandout(msg_win);
154 }
155
156 void
157 msg_standend(void)
158 {
159
160 wstandend(msg_win);
161 }
162
163 static int
164 _msg_vprintf(int auto_fill, const char *fmt, va_list ap)
165 {
166 const char *wstart, *afterw;
167 int wordlen, nspaces;
168 int ret;
169
170 ret = vsnprintf(cbuffer, cbuffersize, fmt, ap);
171
172 if (!auto_fill) {
173 waddstr(msg_win, cbuffer);
174
175 /*
176 * nothing is perfect if they flow text after a table,
177 * but this may be decent.
178 */
179 last_i_was_nl = last_i_was_space = 1;
180 last_o_was_punct = 0;
181 last_o_was_space = 1;
182 goto out;
183 }
184
185 for (wstart = afterw = cbuffer; *wstart; wstart = afterw) {
186
187 /* eat one space, or a whole word of non-spaces */
188 if (isspace(*afterw))
189 afterw++;
190 else
191 while (*afterw && !isspace(*afterw))
192 afterw++;
193
194 /* this is an nl: special formatting necessary */
195 if (*wstart == '\n') {
196 if (last_i_was_nl || last_i_was_space) {
197
198 if (getcurx(msg_win) != 0)
199 waddch(msg_win, '\n');
200 if (last_i_was_nl) {
201 /* last was an nl: paragraph break */
202 waddch(msg_win, '\n');
203 } else {
204 /* last was space: line break */
205 }
206 last_o_was_punct = 0;
207 last_o_was_space = 1;
208 } else {
209 /* last_o_was_punct unchanged */
210 /* last_o_was_space unchanged */
211 }
212 last_i_was_space = 1;
213 last_i_was_nl = 1;
214 continue;
215 }
216
217 /* this is a tab: special formatting necessary. */
218 if (*wstart == '\t') {
219 if (last_i_was_nl) {
220 /* last was an nl: list indent */
221 if (getcurx(msg_win) != 0)
222 waddch(msg_win, '\n');
223 } else {
224 /* last was not an nl: columns */
225 }
226 waddch(msg_win, '\t');
227 last_i_was_nl = 0;
228 last_i_was_space = 1;
229 last_o_was_punct = 0;
230 last_o_was_space = 1;
231 continue;
232 }
233
234 /* this is a space: ignore it but set flags */
235 last_i_was_nl = 0; /* all newlines handled above */
236 last_i_was_space = isspace(*wstart);
237 if (last_i_was_space)
238 continue;
239
240 /*
241 * we have a real "word," i.e. a sequence of non-space
242 * characters. wstart is now the start of the word,
243 * afterw is the next character after the end.
244 */
245 wordlen = afterw - wstart;
246 nspaces = last_o_was_space ? 0 : (last_o_was_punct ? 2 : 1);
247 if ((getcurx(msg_win) + nspaces + wordlen) >=
248 getmaxx(msg_win) &&
249 wordlen < (getmaxx(msg_win) / 3)) {
250 /* wrap the line */
251 waddch(msg_win, '\n');
252 nspaces = 0;
253 }
254
255 /* output the word, preceded by spaces if necessary */
256 while (nspaces-- > 0)
257 waddch(msg_win, ' ');
258 waddbytes(msg_win, wstart, wordlen);
259
260 /* set up the 'last' state for the next time around */
261 switch (afterw[-1]) {
262 case '.':
263 case '?':
264 case '!':
265 last_o_was_punct = 1;
266 break;
267 default:
268 last_o_was_punct = 0;
269 break;
270 }
271 last_o_was_space = 0;
272
273 /* ... and do it all again! */
274 }
275
276 /* String ended with a newline. They really want a line break. */
277 if (last_i_was_nl) {
278 if (getcurx(msg_win) != 0)
279 waddch(msg_win, '\n');
280 last_o_was_punct = 0;
281 last_o_was_space = 1;
282 }
283
284 out:
285 wrefresh(msg_win);
286 return ret;
287 }
288
289 void
290 msg_display(msg msg_no, ...)
291 {
292 va_list ap;
293
294 msg_clear();
295
296 va_start(ap, msg_no);
297 (void)_msg_vprintf(1, msg_string(msg_no), ap);
298 va_end(ap);
299 }
300
301 void
302 msg_display_add(msg msg_no, ...)
303 {
304 va_list ap;
305
306 va_start(ap, msg_no);
307 (void)_msg_vprintf(1, msg_string(msg_no), ap);
308 va_end(ap);
309 }
310
311 static void
312 _erase_ch(void)
313 {
314 int y, x;
315
316 getyx(msg_win, y, x);
317 x--;
318 wmove(msg_win, y, x);
319 waddch(msg_win, ' ');
320 wmove(msg_win, y, x);
321 }
322
323 static void
324 _msg_vprompt(const char *fmt, int do_echo, const char *def, char *val,
325 size_t max_chars, va_list ap)
326 {
327 int ch;
328 int count = 0;
329 char *ibuf = alloca(max_chars);
330
331 _msg_vprintf(0, fmt, ap);
332 if (def != NULL && *def) {
333 waddstr(msg_win, " [");
334 waddstr(msg_win, def);
335 waddstr(msg_win, "]");
336 }
337 waddstr(msg_win, ": ");
338 wrefresh(msg_win);
339
340 while ((ch = wgetch(msg_win)) != '\n') {
341 if (ch == 0x08 || ch == 0x7f) { /* bs or del */
342 if (count > 0) {
343 count--;
344 if (do_echo)
345 _erase_ch();
346 } else
347 _msg_beep();
348 } else if (ch == 0x15) { /* ^U; line kill */
349 while (count > 0) {
350 count--;
351 if (do_echo)
352 _erase_ch();
353 }
354 } else if (ch == 0x17) { /* ^W; word kill */
355 /*
356 * word kill kills the spaces and the 'word'
357 * (non-spaces) last typed. the spaces before
358 * the 'word' aren't killed.
359 */
360 while (count > 0 && isspace(ibuf[count - 1])) {
361 count--;
362 if (do_echo)
363 _erase_ch();
364 }
365 while (count > 0 && !isspace(ibuf[count - 1])) {
366 count--;
367 if (do_echo)
368 _erase_ch();
369 }
370 } else if (count < (max_chars - 1) && isprint(ch)) {
371 if (do_echo)
372 waddch(msg_win, ch);
373 ibuf[count++] = ch;
374 } else
375 _msg_beep();
376 if (do_echo)
377 wrefresh(msg_win);
378 }
379 if (do_echo) {
380 waddch(msg_win, '\n');
381 last_o_was_punct = 0;
382 last_o_was_space = 1;
383 }
384
385 /* copy the appropriate string to the output */
386 if (count != 0) {
387 ibuf[count] = '\0';
388 strlcpy(val, ibuf, max_chars);
389 } else if (def != NULL && val != def) {
390 strlcpy(val, def, max_chars);
391 }
392 }
393
394 void
395 msg_prompt(msg msg_no, const char *def, char *val, size_t max_chars, ...)
396 {
397 va_list ap;
398
399 msg_clear();
400
401 va_start(ap, max_chars);
402 _msg_vprompt(msg_string(msg_no), 1, def, val, max_chars, ap);
403 va_end(ap);
404 }
405
406 void
407 msg_prompt_win(msg msg_no, int x, int y, int w, int h,
408 const char *def, char *val, size_t max_chars, ...)
409 {
410 va_list ap;
411 WINDOW *win, *svwin;
412 int maxx, maxy;
413
414 maxx = getmaxx(msg_win);
415 maxy = getmaxy(msg_win);
416 if (w == 0) {
417 va_start(ap, max_chars);
418 w = vsnprintf(NULL, 0, msg_string(msg_no), ap);
419 va_end(ap);
420 if (def != NULL && *def != 0)
421 w += 2 + strlen(def) + 1;
422 w += 1 + 2 + max_chars + 1;
423 if (w > maxx)
424 w = maxx;
425 }
426
427 if (x == -1)
428 x = (maxx - w) / 2;
429 if (h < 3)
430 h = 3;
431 if (y < 3)
432 y = (maxy - h) / 2;
433 if (y + h > maxy)
434 y = maxy - h;
435
436 win = subwin(msg_win, h, w, y, x);
437 if (win == NULL)
438 wprintw(msg_win, "msg_prompt_win: "
439 "newwin(%d, %d, %d, %d) failed\n",
440 h, w, y, x);
441 else {
442 wbkgd(win, getbkgd(msg_win));
443 wattrset(win, getattrs(msg_win));
444 box(win, 0, 0);
445 wrefresh(win);
446
447 svwin = msg_window(subwin(msg_win, h - 2, w - 2, y + 1, x + 1));
448 wbkgd(msg_win, getbkgd(win));
449 wattrset(msg_win, getattrs(win));
450
451 msg_clear();
452 }
453
454 va_start(ap, max_chars);
455 _msg_vprompt(msg_string(msg_no), 1, def, val, max_chars, ap);
456 va_end(ap);
457
458 if (win != NULL) {
459 wclear(win);
460 wrefresh(win);
461 delwin(msg_window(svwin));
462 delwin(win);
463 }
464 }
465
466 void
467 msg_prompt_add(msg msg_no, const char *def, char *val, size_t max_chars, ...)
468 {
469 va_list ap;
470
471 va_start(ap, max_chars);
472 _msg_vprompt(msg_string(msg_no), 1, def, val, max_chars, ap);
473 va_end(ap);
474 }
475
476 void
477 msg_prompt_noecho(msg msg_no, const char *def, char *val, size_t max_chars, ...)
478 {
479 va_list ap;
480
481 msg_clear();
482
483 va_start(ap, max_chars);
484 _msg_vprompt(msg_string(msg_no), 0, def, val, max_chars, ap);
485 va_end(ap);
486 }
487
488 void
489 msg_table_add(msg msg_no, ...)
490 {
491 va_list ap;
492
493 va_start(ap, msg_no);
494 (void)_msg_vprintf(0, msg_string(msg_no), ap);
495 va_end(ap);
496 }
497
498 int
499 msg_row(void)
500 {
501
502 return getcury(msg_win) + getbegy(msg_win);
503 }
504