1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*	Copyright (c) 1988 AT&T	*/
23 /*	  All Rights Reserved  	*/
24 
25 
26 /*
27  *      Copyright (c) 1997, by Sun Microsystems, Inc.
28  *      All rights reserved.
29  */
30 
31 #pragma ident	"%Z%%M%	%I%	%E% SMI"
32 
33 /*LINTLIBRARY*/
34 
35 #include <sys/types.h>
36 #include <stdlib.h>
37 #include "utility.h"
38 
39 #define	AT_BOTTOM(f)	(Y(f) == Ymax(f) - 1)		/* last line	*/
40 #define	AT_END(f)	(Y(f) == Ymax(f) - 1 && X(f) == Xmax(f) - 1)
41 							/* last char */
42 #define	AT_BEGINNING(f)	(Y(f) == 0 && X(f) == 0)	/* first char	*/
43 
44 static int
room_for_line(FORM * f)45 room_for_line(FORM *f)
46 {
47 	char *v;
48 
49 	_sync_buffer(f);
50 	v = LineBuf(C(f), Ymax(f) - 1);
51 	return (v == _data_end(v, Xmax(f)));	/* check for empty line */
52 }
53 
54 static int
room_for_char(FORM * f)55 room_for_char(FORM *f)
56 {
57 	WINDOW * w = W(f);
58 	int c;
59 
60 	(void) wmove(w, Y(f), Xmax(f) - 1);
61 	c = (int)(winch(w) & A_CHARTEXT);
62 	(void) wmove(w, Y(f), X(f));
63 	return (c == Pad(C(f)));	/* check for empty char */
64 }
65 
66 static int
extra_padding(char * str,int nstr)67 extra_padding(char *str, int nstr)		/* used for word wrapping */
68 {
69 	int c = *(str + nstr - 1);
70 
71 	if (c == '"' || c == '\'')
72 		c = *(str + nstr - 2);
73 
74 	return ((c == '.' || c == '?' || c == '!' || c == ':') ? 2 : 1);
75 
76 }
77 
78 BOOLEAN
_grow_field(FIELD * c,int chunks)79 _grow_field(FIELD *c, int chunks)
80 {
81 	/* This function handles the growth of dymanically growable fields */
82 	/* Returns TRUE if successful, FALSE otherwise	*/
83 
84 	FORM		*f = c->form;
85 	WINDOW		*w = W(f);
86 	BOOLEAN		current = Status(f, POSTED) && c == C(f);
87 	char		*old_buf;
88 	char		*new_buf;
89 	char		*save;
90 	int		old_len = BufSize(c);
91 	int		grow;
92 	int		lcv;
93 	int		max = c->maxgrow;
94 	int		i;
95 
96 	if (current && Status(f, WIN_CHG)) {
97 		_win_to_buf(w, c);
98 		Clr(f, WIN_CHG);
99 		Set(f, BUF_CHG);
100 	}
101 
102 	if (OneRow(c)) {
103 		grow = chunks * c->cols;
104 
105 		if (max)
106 			grow = MIN(max - c->dcols, grow);
107 
108 		c->dcols += grow;
109 
110 		if (c->dcols == max)
111 			Clr(c, GROWABLE);
112 	} else {
113 		grow = chunks * (c->rows + c->nrow);
114 
115 		if (max)
116 			grow = MIN(max - c->drows, grow);
117 
118 		c->drows += grow;
119 		grow *= c->cols;
120 
121 		if (c->drows == max)
122 			Clr(c, GROWABLE);
123 	}
124 
125 	save = old_buf = Buf(c);
126 	new_buf = Buf(c) = malloc(TotalBuf(c));
127 
128 	if (!new_buf)
129 		return (FALSE);
130 
131 	lcv = c->nbuf + 1;
132 
133 	for (i = 0; i < lcv; i++) {
134 		(void) memcpy(new_buf, old_buf, old_len);
135 		(void) memset(new_buf + old_len, ' ', grow);
136 		old_buf += old_len + 1;
137 		new_buf += old_len + grow;
138 		*new_buf++ = '\0';
139 	}
140 
141 	free(save);	/* delete old buffer */
142 
143 	if (current) {
144 		(void) delwin(w);
145 		W(f) = w = newwin(c->drows, c->dcols, 0, 0);
146 
147 		if (!w)
148 			return (FALSE);
149 
150 		wbkgdset(w, Pad(c) | Back(c));
151 		(void) wattrset(w, Fore(c));
152 		(void) werase(w);
153 		_buf_to_win(c, w);
154 		(void) untouchwin(w);
155 		(void) wmove(w, Y(f), X(f));
156 	}
157 
158 	if (c->link != c) {
159 		FIELD	*p = c->link;
160 
161 		while (p != c) {
162 			Buf(p) = Buf(c);
163 			p->drows = c->drows;
164 			p->dcols = c->dcols;
165 			/* _sync_field(p) */
166 			p = p->link;
167 		}
168 	}
169 
170 	return (TRUE);
171 }
172 
173 static int
insert_str(FORM * f,int y,int off,int nstr)174 insert_str(FORM *f, int y, int off, int nstr)	/* used for word wrapping */
175 {
176 	WINDOW		*w	= W(f);
177 	FIELD		*c	= C(f);
178 	char		*vbeg	= LineBuf(c, y);
179 	char		*v	= _data_end(vbeg, Xmax(f));
180 	int		x	= (int)(v - vbeg);
181 	int		n	= Xmax(f) - x;
182 	int		pad	= extra_padding(Buf(c) + off, nstr);
183 	int		siz	= nstr + 1 + pad;
184 	int		ret 	= E_REQUEST_DENIED;
185 
186 	if (n >= siz) {	/* check for fit on this line */
187 		(void) wmove(w, y, 0);
188 		(void) winsnstr(w, Buf(c) + off, nstr);
189 		(void) wmove(w, y, nstr);
190 		(void) winsnstr(w, "  ", pad);
191 	} else {		/* wrap */
192 		if (y == Ymax(f) - 1 && Status(c, GROWABLE)) {
193 			if (!_grow_field(c, 1))
194 				return (E_SYSTEM_ERROR);
195 
196 			vbeg = LineBuf(c, y);	/* grow changes buffer */
197 			w = W(f);		/* grow changes window */
198 		}
199 
200 		v = _data_beg(vbeg + Xmax(f) - siz, siz);
201 		v = _whsp_end(vbeg, (int)(v - vbeg));
202 		x = (int)(v - vbeg);
203 		n = Xmax(f) - x - n;
204 
205 		if (y < Ymax(f) - 1 && (ret =
206 		    insert_str(f, y+1, (int)(v - Buf(c)), n)) == E_OK) {
207 			(void) wmove(w, y, x);
208 			(void) wclrtoeol(w);
209 			(void) wmove(w, y, 0);
210 			(void) winsnstr(w, Buf(c) + off, nstr);
211 			(void) wmove(w, y, nstr);
212 			(void) winsnstr(w, "  ", pad);
213 		} else
214 			return (ret);	/* no room for wrap */
215 	}
216 	return (E_OK);
217 }
218 
219 static int
wrap_ok(FORM * f)220 wrap_ok(FORM *f)		/* used for word wrapping */
221 {
222 /*
223  * when this routine is called a char has already been added/inserted
224  * on the screen at Y(f), X(f).  this routine checks to see if the current
225  * line needs wrapping and if so attempts the wrap.  if unsuccessful
226  * it deletes the char at Y(f), X(f) and returns FALSE.
227  */
228 	FIELD		*c = C(f);
229 	BOOLEAN		at_bottom = AT_BOTTOM(f);
230 	int		ret = E_REQUEST_DENIED;
231 
232 	if (Opt(c, O_WRAP) && !OneRow(c) && !room_for_char(f) &&
233 	    (!at_bottom || Status(c, GROWABLE))) {
234 		WINDOW *w;
235 		char *vbeg;
236 		char *v;
237 		int x, n;
238 
239 		if (at_bottom && !_grow_field(c, 1))
240 			return (E_SYSTEM_ERROR);
241 
242 		vbeg = LineBuf(c, Y(f));
243 		w = W(f);
244 
245 		_win_to_buf(w, c);	/* sync buffer without changing flags */
246 
247 		v = _whsp_end(vbeg, Xmax(f));
248 		x = (int)(v - vbeg);
249 		n = Xmax(f) - x;
250 
251 		if (x && (ret = insert_str(f, Y(f)+1, (int)(v - Buf(c)), n)) ==
252 		    E_OK) {
253 			w = W(f);	/* window may change in insert_str */
254 			(void) wmove(w, Y(f), x);
255 			(void) wclrtoeol(w);
256 
257 			if (X(f) >= x) {
258 				++Y(f);
259 				X(f) = X(f) - x;
260 			}
261 		} else {	/* error condition */
262 			if (ret == E_SYSTEM_ERROR)
263 				return (E_SYSTEM_ERROR);
264 
265 			(void) wmove(w, Y(f), X(f));
266 			(void) wdelch(w);	/* delete the char */
267 			_win_to_buf(w, c);	/* restore buffer  */
268 			return (E_REQUEST_DENIED);
269 		}
270 	}
271 	return (E_OK);
272 }
273 
274 int
_new_line(FORM * f)275 _new_line(FORM *f)
276 {
277 /*
278  *		overloaded operation
279  *
280  *	if at beginning of field
281  *		move to next field
282  *
283  *	else if in OVERLAY mode
284  *		if on last line of field
285  *			clear to eol and move to next field
286  *		else
287  *			clear to eol and move to beginning of next line
288  *
289  *	else if in INSERT mode
290  *		if on last line of field
291  *			move to next field
292  *		else
293  *			move text from cursor to eol to new line
294  */
295 	BOOLEAN		at_bottom = AT_BOTTOM(f);
296 	FIELD *		c = C(f);
297 
298 	if (Opt(f, O_NL_OVERLOAD) && AT_BEGINNING(f))
299 		return (_field_navigation(_next_field, f));
300 
301 	if (!Opt(c, O_EDIT))
302 		return (E_REQUEST_DENIED);
303 
304 	if (Status(f, OVERLAY)) {		/* OVERLAY mode	*/
305 		if (at_bottom && (!Status(c, GROWABLE) || OneRow(c))) {
306 			if (Opt(f, O_NL_OVERLOAD)) {
307 				(void) wclrtoeol(W(f));
308 				Set(f, WIN_CHG);
309 				return (_field_navigation(_next_field, f));
310 			} else
311 				return (E_REQUEST_DENIED);
312 		}
313 
314 		if (at_bottom && !_grow_field(c, 1))
315 			return (E_SYSTEM_ERROR);
316 
317 		(void) wclrtoeol(W(f));
318 		++Y(f); X(f) = 0;
319 	} else {		/* INSERT mode	*/
320 		BOOLEAN		room;
321 
322 		if (at_bottom && (!Status(c, GROWABLE) || OneRow(c))) {
323 			if (Opt(f, O_NL_OVERLOAD))
324 				return (_field_navigation(_next_field, f));
325 			else
326 				return (E_REQUEST_DENIED);
327 		}
328 
329 		room = !at_bottom && room_for_line(f);
330 
331 		if (room || Status(c, GROWABLE)) {
332 			WINDOW	*w;
333 			char *v;
334 			char *vend;
335 
336 			if (!room && !_grow_field(c, 1))
337 				return (E_SYSTEM_ERROR);
338 
339 			w = W(f);
340 			v = LineBuf(c, Y(f)) + X(f);
341 			vend = _data_end(v, Xmax(f) - X(f));
342 
343 			(void) wclrtoeol(w);
344 			++Y(f); X(f) = 0;
345 			(void) wmove(w, Y(f), X(f));
346 			(void) winsertln(w);
347 			(void) waddnstr(w, v, (int)(vend - v));
348 		} else
349 			return (E_REQUEST_DENIED);
350 	}
351 	Set(f, WIN_CHG);
352 	return (E_OK);
353 }
354 
355 /* _ins_char - insert blank char with error on overflow */
356 int
_ins_char(FORM * f)357 _ins_char(FORM *f)
358 {
359 	FIELD	*c = C(f);
360 	BOOLEAN	room = room_for_char(f);
361 
362 	if (CheckChar(c, ' ') && (room || (OneRow(c) &&
363 	    Status(c, GROWABLE)))) {
364 		if (!room && !_grow_field(c, 1))
365 			return (E_SYSTEM_ERROR);
366 
367 		(void) winsch(W(f), ' ');
368 
369 		return (wrap_ok(f));
370 	}
371 	return (E_REQUEST_DENIED);
372 }
373 
374 /* _ins_line -  insert blank line with error on overflow */
375 int
_ins_line(FORM * f)376 _ins_line(FORM *f)
377 {
378 	BOOLEAN		room = !AT_BOTTOM(f) && room_for_line(f);
379 	FIELD		*c = C(f);
380 
381 	if (CheckChar(c, ' ') && !OneRow(c) && (room || Status(c, GROWABLE))) {
382 		if (!room && !_grow_field(c, 1))
383 			return (E_SYSTEM_ERROR);
384 
385 		X(f) = 0;
386 		(void) winsertln(W(f));
387 		return (E_OK);
388 	}
389 	return (E_REQUEST_DENIED);
390 }
391 
392 /* _del_char - delete char at cursor */
393 int
_del_char(FORM * f)394 _del_char(FORM *f)
395 {
396 	(void) wdelch(W(f));
397 	return (E_OK);
398 }
399 
400 int
_del_prev(FORM * f)401 _del_prev(FORM *f)
402 {
403 /*
404  *		overloaded operation
405  *
406  *	if at beginning of field
407  *		move to previous field
408  *
409  *	else if in OVERLAY mode
410  *		if at beginning of line
411  *			error
412  *		else
413  *			delete previous char
414  *
415  *	else if in INSERT mode
416  *		if at beginning of line
417  *			if current line can fit on preceding
418  *				join current line with preceding line
419  *			else
420  *				error
421  *		else
422  *			delete previous char
423  */
424 	WINDOW *	w = W(f);
425 	FIELD *		c = C(f);
426 
427 	if (AT_BEGINNING(f)) {
428 		if (Opt(f, O_BS_OVERLOAD))
429 			return (_field_navigation(_prev_field, f));
430 		else
431 			return (E_REQUEST_DENIED);
432 	}
433 	if (!Opt(c, O_EDIT))
434 		return (E_REQUEST_DENIED);
435 
436 	if (--X(f) < 0) {
437 		++X(f);
438 
439 		if (Status(f, OVERLAY))	/* OVERLAY mode	*/
440 			return (E_REQUEST_DENIED);
441 		else {			/* INSERT mode	*/
442 			char *p = LineBuf(c, Y(f) - 1);
443 			char *v = LineBuf(c, Y(f));
444 			char *pend;
445 			char *vend;
446 
447 			_sync_buffer(f);
448 			pend = _data_end(p, Xmax(f));
449 			vend = _data_end(v, Xmax(f));
450 
451 			if ((vend - v) > (Xmax(f) - (pend - p)))
452 				return (E_REQUEST_DENIED);
453 			else {
454 				(void) wdeleteln(w);
455 				_adjust_cursor(f, pend);
456 				(void) wmove(w, Y(f), X(f));
457 				(void) waddnstr(w, v, (int)(vend - v));
458 			}
459 		}
460 	} else {
461 		(void) wmove(w, Y(f), X(f));
462 		(void) wdelch(w);
463 	}
464 	Set(f, WIN_CHG);
465 	return (E_OK);
466 }
467 
468 /* _del_line - delete current line */
469 int
_del_line(FORM * f)470 _del_line(FORM *f)
471 {
472 	X(f) = 0;
473 	(void) wdeleteln(W(f));
474 	return (E_OK);
475 }
476 
477 /* _del_word - delete word under cursor plus trailing blanks */
478 int
_del_word(FORM * f)479 _del_word(FORM *f)
480 {
481 	FIELD *c = C(f);
482 	WINDOW *w = W(f);
483 	char *y = LineBuf(c, Y(f));
484 	char *t = y + Xmax(f);
485 	char *v = y + X(f);
486 	char *x = v;
487 
488 	_sync_buffer(f);
489 
490 	if (*v == ' ')
491 		return (E_REQUEST_DENIED);
492 
493 	_adjust_cursor(f, _whsp_end(y, X(f)));
494 	(void) wmove(w, Y(f), X(f));
495 	(void) wclrtoeol(w);
496 
497 	v = _whsp_beg(v, (int)(t - v));
498 	v = _data_beg(v, (int)(t - v));
499 
500 	if (v != x && *v != ' ')
501 		(void) waddnstr(w, v, (int)(_data_end(v, (int)(t - v)) - v));
502 
503 	return (E_OK);
504 }
505 
506 /* _clr_eol - clear to end of line */
507 int
_clr_eol(FORM * f)508 _clr_eol(FORM *f)
509 {
510 	(void) wclrtoeol(W(f));
511 	return (E_OK);
512 }
513 
514 /* _clr_eof - clear to end of field */
515 int
_clr_eof(FORM * f)516 _clr_eof(FORM *f)
517 {
518 	(void) wclrtobot(W(f));
519 	return (E_OK);
520 }
521 
522 /* _clr_field - clear entire field */
523 int
_clr_field(FORM * f)524 _clr_field(FORM *f)
525 {
526 	X(f) = 0; Y(f) = 0;
527 	(void) werase(W(f));
528 	return (E_OK);
529 }
530 
531 /* _ovl_mode - go into overlay mode */
532 int
_ovl_mode(FORM * f)533 _ovl_mode(FORM *f)
534 {
535 	Set(f, OVERLAY);
536 	return (E_OK);
537 }
538 
539 /* _ins_mode - go into insert mode */
540 int
_ins_mode(FORM * f)541 _ins_mode(FORM *f)
542 {
543 	Clr(f, OVERLAY);
544 	return (E_OK);
545 }
546 
547 /* _validation - apply validation function associated with field type */
548 int
_validation(FORM * f)549 _validation(FORM *f)
550 {
551 	return (_validate(f) ? E_OK : E_INVALID_FIELD);
552 }
553 
554 /* _next_choice - apply next choice function associated with field type */
555 int
_next_choice(FORM * f)556 _next_choice(FORM *f)
557 {
558 	_sync_buffer(f);
559 	return (NextChoice(C(f)) ? E_OK : E_REQUEST_DENIED);
560 }
561 
562 /* _prev_choice - apply previous choice function associated with field type */
563 int
_prev_choice(FORM * f)564 _prev_choice(FORM *f)
565 {
566 	_sync_buffer(f);
567 	return (PrevChoice(C(f)) ? E_OK : E_REQUEST_DENIED);
568 }
569 
570 /*
571  * _data_entry - enter printable ascii char ch
572  * in current field at cursor position
573  */
574 int
_data_entry(FORM * f,int ch)575 _data_entry(FORM *f, int ch)
576 {
577 	FIELD *		c = C(f);	/* current field	*/
578 	WINDOW *	w = W(f);	/* field window		*/
579 	BOOLEAN		at_end;
580 	int		ret;
581 
582 	if (!Opt(c, O_EDIT))
583 		return (E_REQUEST_DENIED);
584 
585 	if (AT_BEGINNING(f) && Opt(c, O_BLANK) && ! Status(f, BUF_CHG) &&
586 	    !Status(f, WIN_CHG))
587 		(void) werase(w);
588 
589 	if (Status(f, OVERLAY))	/* OVERLAY mode	*/
590 		(void) waddch(w, (chtype) ch);
591 	else {				/* INSERT mode	*/
592 		BOOLEAN	room = room_for_char(f);
593 
594 		if (room || (OneRow(c) && Status(c, GROWABLE))) {
595 			if (!room && !_grow_field(c, 1))
596 				return (E_SYSTEM_ERROR);
597 
598 			(void) winsch(w, (chtype) ch);
599 		} else
600 			return (E_REQUEST_DENIED);
601 	}
602 
603 	if ((ret = wrap_ok(f)) != E_OK)
604 		return (ret);
605 
606 	Set(f, WIN_CHG);
607 
608 	at_end = AT_END(f);
609 
610 	if (at_end && !Status(c, GROWABLE) && Opt(c, O_AUTOSKIP))
611 		return (_field_navigation(_next_field, f));
612 
613 	if (at_end && Status(c, GROWABLE) && !_grow_field(c, 1))
614 		return (E_SYSTEM_ERROR);
615 
616 	(void) _next_char(f);
617 	return (E_OK);
618 }
619