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