xref: /netbsd-src/usr.bin/msgc/msg_sys.def (revision d710132b4b8ce7f7cccaaf660cb16aa16b4077a0)
1/*	$NetBSD: msg_sys.def,v 1.20 2003/06/10 17:24:17 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
39static WINDOW *msg_win = NULL;
40static char *cbuffer;
41static size_t cbuffersize;
42
43static int last_i_was_nl, last_i_was_space;
44static int last_o_was_punct, last_o_was_space;
45
46static void	_msg_beep(void);
47static int	_msg_vprintf(int auto_fill, const char *fmt, va_list ap);
48static void	_msg_vprompt(const char *msg, int do_echo, const char *def,
49		    char *val, size_t max_chars, va_list ap);
50
51/* Routines */
52
53static void
54_msg_beep(void)
55{
56	fprintf(stderr, "\a");
57}
58
59WINDOW *
60msg_window(WINDOW *window)
61{
62	size_t ncbuffersize;
63	char *ncbuffer;
64	WINDOW *old;
65
66	old = msg_win;
67	if (!window)
68		return old;
69	msg_win = window;
70
71	ncbuffersize = getmaxx(window) * getmaxy(window) + 1;
72	while (ncbuffersize > cbuffersize) {
73		ncbuffer = malloc(ncbuffersize);
74		if (ncbuffer == NULL) {
75			/* we might get truncated messages... */
76			ncbuffersize <<= 1;
77			continue;
78		}
79		if (cbuffer != NULL)
80			free(cbuffer);
81		cbuffer = ncbuffer;
82		cbuffersize = ncbuffersize;
83		break;
84	}
85	last_o_was_punct = 0;
86	last_o_was_space = 1;
87	return old;
88}
89
90const char *msg_string (msg msg_no)
91{
92	int m = (intptr_t)msg_no;
93
94	if (m > sizeof msg_list / sizeof msg_list[0])
95		/* guess that we were passed a text string */
96		return msg_no;
97	return msg_list[m];
98}
99
100void msg_clear(void)
101{
102	wclear (msg_win);
103	wrefresh (msg_win);
104	last_o_was_punct = 0;
105	last_o_was_space = 1;
106}
107
108void msg_standout(void)
109{
110	wstandout(msg_win);
111}
112
113void msg_standend(void)
114{
115	wstandend(msg_win);
116}
117
118static int
119_msg_vprintf(int auto_fill, const char *fmt, va_list ap)
120{
121	const char *wstart, *afterw;
122	int wordlen, nspaces;
123	int ret;
124
125	ret = vsnprintf (cbuffer, cbuffersize, fmt, ap);
126
127	if (!auto_fill) {
128		waddstr(msg_win, cbuffer);
129
130		/*
131		 * nothing is perfect if they flow text after a table,
132		 * but this may be decent.
133		 */
134		last_i_was_nl = last_i_was_space = 1;
135		last_o_was_punct = 0;
136		last_o_was_space = 1;
137		goto out;
138	}
139
140	for (wstart = afterw = cbuffer; *wstart; wstart = afterw) {
141
142		/* eat one space, or a whole word of non-spaces */
143		if (isspace(*afterw))
144			afterw++;
145		else
146			while (*afterw && !isspace(*afterw))
147				afterw++;
148
149		/* this is an nl: special formatting necessary */
150		if (*wstart == '\n') {
151			if (last_i_was_nl || last_i_was_space) {
152
153				if (getcurx(msg_win) != 0)
154					waddch(msg_win, '\n');
155				if (last_i_was_nl) {
156					/* last was an nl: paragraph break */
157					waddch(msg_win, '\n');
158				} else {
159					/* last was space: line break */
160				}
161				last_o_was_punct = 0;
162				last_o_was_space = 1;
163			} else {
164				/* last_o_was_punct unchanged */
165				/* last_o_was_space unchanged */
166			}
167			last_i_was_space = 1;
168			last_i_was_nl = 1;
169			continue;
170		}
171
172		/* this is a tab: special formatting necessary. */
173		if (*wstart == '\t') {
174			if (last_i_was_nl) {
175				/* last was an nl: list indent */
176				if (getcurx(msg_win) != 0)
177					waddch(msg_win, '\n');
178			} else {
179				/* last was not an nl: columns */
180			}
181			waddch(msg_win, '\t');
182			last_i_was_nl = 0;
183			last_i_was_space = 1;
184			last_o_was_punct = 0;
185			last_o_was_space = 1;
186			continue;
187		}
188
189		/* this is a space: ignore it but set flags */
190		last_i_was_nl = 0;	/* all newlines handled above */
191		last_i_was_space = isspace(*wstart);
192		if (last_i_was_space)
193			continue;
194
195		/*
196		 * we have a real "word," i.e. a sequence of non-space
197		 * characters.  wstart is now the start of the word,
198		 * afterw is the next character after the end.
199		 */
200		wordlen = afterw - wstart;
201		nspaces = last_o_was_space ? 0 : (last_o_was_punct ? 2 : 1);
202		if ((getcurx(msg_win) + nspaces + wordlen) >=
203		      getmaxx(msg_win) &&
204		    wordlen < (getmaxx(msg_win) / 3)) {
205			/* wrap the line */
206			waddch(msg_win, '\n');
207			nspaces = 0;
208		}
209
210		/* output the word, preceded by spaces if necessary */
211		while (nspaces-- > 0)
212			waddch(msg_win, ' ');
213		waddbytes(msg_win, wstart, wordlen);
214
215		/* set up the 'last' state for the next time around */
216		switch (afterw[-1]) {
217		case '.':
218		case '?':
219		case '!':
220			last_o_was_punct = 1;
221			break;
222		default:
223			last_o_was_punct = 0;
224			break;
225		}
226		last_o_was_space = 0;
227
228		/* ... and do it all again! */
229	}
230
231	/* String ended with a newline.  They really want a line break. */
232	if (last_i_was_nl) {
233		if (getcurx(msg_win) != 0)
234			waddch(msg_win, '\n');
235		last_o_was_punct = 0;
236		last_o_was_space = 1;
237	}
238
239out:
240	wrefresh (msg_win);
241	return ret;
242}
243
244void msg_display(msg msg_no, ...)
245{
246	va_list ap;
247
248	msg_clear();
249
250	va_start(ap, msg_no);
251	(void)_msg_vprintf(1, msg_string(msg_no), ap);
252	va_end(ap);
253}
254
255void msg_display_add(msg msg_no, ...)
256{
257	va_list ap;
258
259	va_start (ap, msg_no);
260	(void)_msg_vprintf(1, msg_string(msg_no), ap);
261	va_end (ap);
262}
263
264static void
265_erase_ch(void)
266{
267	int y, x;
268
269	getyx(msg_win, y, x);
270	x--;
271	wmove(msg_win, y, x);
272	waddch(msg_win, ' ');
273	wmove(msg_win, y, x);
274}
275
276static void
277_msg_vprompt(const char *msg, int do_echo, const char *def, char *val,
278    size_t max_chars, va_list ap)
279{
280	int ch;
281	int count = 0;
282	char *ibuf = alloca(max_chars);
283
284	_msg_vprintf(0, msg, ap);
285	if (def != NULL && *def) {
286		waddstr (msg_win, " [");
287		waddstr (msg_win, def);
288		waddstr (msg_win, "]");
289	}
290	waddstr (msg_win, ": ");
291	wrefresh (msg_win);
292
293	while ((ch = wgetch(msg_win)) != '\n') {
294		if (ch == 0x08 || ch == 0x7f) {  /* bs or del */
295			if (count > 0) {
296				count--;
297				if (do_echo)
298					_erase_ch();
299			} else
300				_msg_beep();
301		} else if (ch == 0x15) {	/* ^U; line kill */
302			while (count > 0) {
303				count--;
304				if (do_echo)
305					_erase_ch();
306			}
307		} else if (ch == 0x17) {        /* ^W; word kill */
308			/*
309			 * word kill kills the spaces and the 'word'
310			 * (non-spaces) last typed.  the spaces before
311			 * the 'word' aren't killed.
312			 */
313			while (count > 0 && isspace(ibuf[count - 1])) {
314				count--;
315				if (do_echo)
316					_erase_ch();
317			}
318			while (count > 0 && !isspace(ibuf[count - 1])) {
319				count--;
320				if (do_echo)
321					_erase_ch();
322			}
323		} else if (count < (max_chars - 1) && isprint(ch)) {
324			if (do_echo)
325				waddch (msg_win, ch);
326			ibuf[count++] = ch;
327		} else
328			_msg_beep();
329		if (do_echo)
330			wrefresh(msg_win);
331	}
332	if (do_echo) {
333		waddch(msg_win, '\n');
334		last_o_was_punct = 0;
335		last_o_was_space = 1;
336	}
337
338	/* copy the appropriate string to the output */
339	if (count != 0) {
340		ibuf[count] = '\0';
341		strcpy(val, ibuf);		/* size known to be OK */
342	} else if (def != NULL && val != def) {
343		strlcpy(val, def, max_chars);
344	}
345}
346
347void
348msg_prompt(msg msg_no, const char *def, char *val, size_t max_chars, ...)
349{
350	va_list ap;
351
352	msg_clear();
353
354	va_start (ap, max_chars);
355	_msg_vprompt(msg_string(msg_no), 1, def, val, max_chars, ap);
356	va_end (ap);
357}
358
359void
360msg_prompt_win(msg msg_no, int x, int y, int w, int h,
361	const char *def, char *val, size_t max_chars, ...)
362{
363	va_list ap;
364	WINDOW *win, *svwin;
365	int maxx, maxy;
366
367	maxx = getmaxx(stdscr);
368	maxy = getmaxy(stdscr);
369	if (w == 0) {
370		va_start(ap, max_chars);
371		w = vsnprintf(NULL, 0, msg_string(msg_no), ap);
372		va_end(ap);
373		if (def != NULL && *def != 0)
374			w += 2 + strlen(def);
375		w += 1 + 2 + max_chars + 1;
376		if (w > maxx)
377			w = maxx;
378	}
379
380	if (x == -1)
381		x = (maxx - w) / 2;
382	if (h < 3)
383		h = 3;
384	if (y < 3)
385		y = (maxy - h) / 2;
386	if (y + h > maxy)
387		y = maxy - h;
388
389	win = newwin(h, w, y, x);
390	if (win == NULL)
391		wprintw(msg_win, "msg_prompt_win: "
392			"newwin(%d, %d, %d, %d) failed\n",
393			h, w, y, x);
394	else {
395		wbkgd(win, getbkgd(msg_win));
396		wattrset(win, getattrs(msg_win));
397		box(win, 0, 0);
398		wrefresh(win);
399
400		svwin = msg_window(derwin(win, -1, -1, 1, 1));
401		wbkgd(msg_win, getbkgd(win));
402		wattrset(msg_win, getattrs(win));
403
404		msg_clear();
405	}
406
407	va_start(ap, max_chars);
408	_msg_vprompt(msg_string(msg_no), 1, def, val, max_chars, ap);
409	va_end(ap);
410
411	if (win != NULL) {
412		wclear(win);
413		wrefresh(win);
414		delwin(msg_window(svwin));
415		delwin(win);
416	}
417}
418
419void
420msg_prompt_add(msg msg_no, const char *def, char *val, size_t max_chars, ...)
421{
422	va_list ap;
423
424	va_start (ap, max_chars);
425	_msg_vprompt(msg_string(msg_no), 1, def, val, max_chars, ap);
426	va_end(ap);
427}
428
429void
430msg_prompt_noecho(msg msg_no, const char *def, char *val, size_t max_chars, ...)
431{
432	va_list ap;
433
434	msg_clear();
435
436	va_start (ap, max_chars);
437	_msg_vprompt(msg_string(msg_no), 0, def, val, max_chars, ap);
438	va_end (ap);
439}
440
441void msg_table_add(msg msg_no, ...)
442{
443	va_list ap;
444
445	va_start (ap, msg_no);
446	(void)_msg_vprintf(0, msg_string(msg_no), ap);
447	va_end (ap);
448}
449
450