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