1 /* $NetBSD: driver.c,v 1.18 2013/11/26 01:17:00 christos Exp $ */
2
3 /*-
4 * Copyright (c) 1998-1999 Brett Lymn
5 * (blymn@baea.com.au, brett_lymn@yahoo.com.au)
6 * All rights reserved.
7 *
8 * This code has been donated to The NetBSD Foundation by the Author.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. The name of the author may not be used to endorse or promote products
16 * derived from this software without specific prior written permission
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 *
29 *
30 */
31
32 #include <sys/cdefs.h>
33 __RCSID("$NetBSD: driver.c,v 1.18 2013/11/26 01:17:00 christos Exp $");
34
35 #include <ctype.h>
36 #include "form.h"
37 #include "internals.h"
38
39 static int
40 traverse_form_links(FORM *form, int direction);
41
42 /*
43 * Traverse the links of the current field in the given direction until
44 * either a active & visible field is found or we return to the current
45 * field. Direction is the REQ_{LEFT,RIGHT,UP,DOWN}_FIELD driver commands.
46 * The function returns E_OK if a valid field is found, E_REQUEST_DENIED
47 * otherwise.
48 */
49 static int
traverse_form_links(FORM * form,int direction)50 traverse_form_links(FORM *form, int direction)
51 {
52 unsigned idx;
53
54 idx = form->cur_field;
55
56 do {
57 switch (direction) {
58 case REQ_LEFT_FIELD:
59 if (form->fields[idx]->left == NULL)
60 return E_REQUEST_DENIED;
61 idx = form->fields[idx]->left->index;
62 break;
63
64 case REQ_RIGHT_FIELD:
65 if (form->fields[idx]->right == NULL)
66 return E_REQUEST_DENIED;
67 idx = form->fields[idx]->right->index;
68 break;
69
70 case REQ_UP_FIELD:
71 if (form->fields[idx]->up == NULL)
72 return E_REQUEST_DENIED;
73 idx = form->fields[idx]->up->index;
74 break;
75
76 case REQ_DOWN_FIELD:
77 if (form->fields[idx]->down == NULL)
78 return E_REQUEST_DENIED;
79 idx = form->fields[idx]->down->index;
80 break;
81
82 default:
83 return E_REQUEST_DENIED;
84 }
85
86 if ((form->fields[idx]->opts & (O_ACTIVE | O_VISIBLE))
87 == (O_ACTIVE | O_VISIBLE)) {
88 form->cur_field = idx;
89 return E_OK;
90 }
91 } while (idx != form->cur_field);
92
93 return E_REQUEST_DENIED;
94 }
95
96 int
form_driver(FORM * form,int c)97 form_driver(FORM *form, int c)
98 {
99 FIELD *fieldp;
100 int update_page, update_field, old_field, old_page, status;
101 int start_field;
102 unsigned int pos;
103
104 if (form == NULL)
105 return E_BAD_ARGUMENT;
106
107 if ((form->fields == NULL) || (*(form->fields) == NULL))
108 return E_INVALID_FIELD;
109
110 if (form->posted != 1)
111 return E_NOT_POSTED;
112
113 if (form->in_init == 1)
114 return E_BAD_STATE;
115
116
117 old_field = start_field = form->cur_field;
118 fieldp = form->fields[form->cur_field];
119 update_page = update_field = 0;
120 status = E_OK;
121
122 if (c < REQ_MIN_REQUEST) {
123 if (isprint(c) || isblank(c)) {
124 do {
125 pos = fieldp->start_char + fieldp->row_xpos;
126
127 /* check if we are allowed to edit this field */
128 if ((fieldp->opts & O_EDIT) != O_EDIT)
129 return E_REQUEST_DENIED;
130
131 if ((status =
132 (_formi_add_char(fieldp, pos, c)))
133 == E_REQUEST_DENIED) {
134
135 /*
136 * Need to check here if we
137 * want to autoskip. we
138 * call the form driver
139 * recursively to pos us on
140 * the next field and then
141 * we loop back to ensure
142 * the next field selected
143 * can have data added to it
144 */
145 if ((fieldp->opts & O_AUTOSKIP)
146 != O_AUTOSKIP)
147 return E_REQUEST_DENIED;
148 status = form_driver(form,
149 REQ_NEXT_FIELD);
150 if (status != E_OK)
151 return status;
152
153 /*
154 * check if we have looped
155 * around all the fields.
156 * This can easily happen if
157 * all the fields are full.
158 */
159 if (start_field == form->cur_field)
160 return E_REQUEST_DENIED;
161
162 old_field = form->cur_field;
163 fieldp = form->fields[form->cur_field];
164 status = _formi_add_char(fieldp,
165 fieldp->start_char
166 + fieldp->cursor_xpos,
167 c);
168 } else if (status == E_INVALID_FIELD)
169 /* char failed validation, just
170 * return the status.
171 */
172 return status;
173 else if (status == E_NO_ROOM)
174 /* we will get this if the line
175 * wrapping fails. Deny the
176 * request.
177 */
178 return E_REQUEST_DENIED;
179 }
180 while (status != E_OK);
181 update_field = (status == E_OK);
182 } else
183 return E_REQUEST_DENIED;
184 } else {
185 if (c > REQ_MAX_COMMAND)
186 return E_UNKNOWN_COMMAND;
187
188 if ((c >= REQ_NEXT_PAGE) && (c <= REQ_DOWN_FIELD)) {
189 /* first check the field we are in is ok */
190 if (_formi_validate_field(form) != E_OK)
191 return E_INVALID_FIELD;
192
193 if (form->field_term != NULL)
194 form->field_term(form);
195
196 /*
197 * if we have a page movement then the form term
198 * needs to be called too
199 */
200 if ((c <= REQ_LAST_PAGE) && (form->form_term != NULL))
201 form->form_term(form);
202 }
203
204
205 switch (c) {
206 case REQ_NEXT_PAGE:
207 if (form->page < form->max_page) {
208 old_page = form->page;
209 form->page++;
210 update_page = 1;
211 if (_formi_pos_first_field(form) != E_OK) {
212 form->page = old_page;
213 status = E_REQUEST_DENIED;
214 }
215 } else
216 status = E_REQUEST_DENIED;
217 break;
218
219 case REQ_PREV_PAGE:
220 if (form->page > 0) {
221 old_page = form->page;
222 form->page--;
223 update_page = 1;
224 if (_formi_pos_first_field(form) != E_OK) {
225 form->page = old_page;
226 status = E_REQUEST_DENIED;
227 }
228 } else
229 status = E_REQUEST_DENIED;
230 break;
231
232 case REQ_FIRST_PAGE:
233 old_page = form->page;
234 form->page = 0;
235 update_page = 1;
236 if (_formi_pos_first_field(form) != E_OK) {
237 form->page = old_page;
238 status = E_REQUEST_DENIED;
239 }
240 break;
241
242 case REQ_LAST_PAGE:
243 old_page = form->page;
244 form->page = form->max_page - 1;
245 update_page = 1;
246 if (_formi_pos_first_field(form) != E_OK) {
247 form->page = old_page;
248 status = E_REQUEST_DENIED;
249 }
250 break;
251
252 case REQ_NEXT_FIELD:
253 status = _formi_pos_new_field(form, _FORMI_FORWARD,
254 FALSE);
255 update_field = 1;
256 break;
257
258 case REQ_PREV_FIELD:
259 status = _formi_pos_new_field(form, _FORMI_BACKWARD,
260 FALSE);
261 update_field = 1;
262 break;
263
264 case REQ_FIRST_FIELD:
265 form->cur_field = 0;
266 update_field = 1;
267 break;
268
269 case REQ_LAST_FIELD:
270 form->cur_field = form->field_count - 1;
271 update_field = 1;
272 break;
273
274 case REQ_SNEXT_FIELD:
275 status = _formi_pos_new_field(form, _FORMI_FORWARD,
276 TRUE);
277 update_field = 1;
278 break;
279
280 case REQ_SPREV_FIELD:
281 status = _formi_pos_new_field(form, _FORMI_BACKWARD,
282 TRUE);
283 update_field = 1;
284 break;
285
286 case REQ_SFIRST_FIELD:
287 fieldp = TAILQ_FIRST(&form->sorted_fields);
288 form->cur_field = fieldp->index;
289 update_field = 1;
290 break;
291
292 case REQ_SLAST_FIELD:
293 fieldp = TAILQ_LAST(&form->sorted_fields,
294 _formi_sort_head);
295 form->cur_field = fieldp->index;
296 update_field = 1;
297 break;
298
299 /*
300 * The up, down, left and right field traversals
301 * are rolled up into a single function, allow a
302 * fall through to that function.
303 */
304 case REQ_LEFT_FIELD:
305 case REQ_RIGHT_FIELD:
306 case REQ_UP_FIELD:
307 case REQ_DOWN_FIELD:
308 status = traverse_form_links(form, c);
309 update_field = 1;
310 break;
311
312 /* the following commands modify the buffer, check if
313 this is allowed first before falling through. */
314
315 case REQ_DEL_PREV:
316 /*
317 * need to check for the overloading of this
318 * request. If overload flag set and we are
319 * at the start of field this request turns
320 * into a previous field request. Otherwise
321 * fallthrough to the field handler.
322 */
323 if ((form->opts & O_BS_OVERLOAD) == O_BS_OVERLOAD) {
324 if ((fieldp->start_char == 0) &&
325 (fieldp->start_line == 0) &&
326 (fieldp->row_xpos == 0)) {
327 update_field =
328 _formi_manipulate_field(form,
329 REQ_PREV_FIELD);
330 break;
331 }
332 }
333
334 /* FALLTHROUGH */
335 case REQ_NEW_LINE:
336 /*
337 * need to check for the overloading of this
338 * request. If overload flag set and we are
339 * at the start of field this request turns
340 * into a next field request. Otherwise
341 * fallthrough to the field handler.
342 */
343 if ((form->opts & O_NL_OVERLOAD) == O_NL_OVERLOAD) {
344 if ((fieldp->start_char == 0) &&
345 (fieldp->start_line == 0) &&
346 (fieldp->row_xpos == 0)) {
347 update_field =
348 _formi_manipulate_field(form,
349 REQ_NEXT_FIELD);
350 break;
351 }
352 }
353
354 /* FALLTHROUGH */
355 case REQ_INS_CHAR:
356 case REQ_INS_LINE:
357 case REQ_DEL_CHAR:
358 case REQ_DEL_LINE:
359 case REQ_DEL_WORD:
360 case REQ_CLR_EOL:
361 case REQ_CLR_EOF:
362 case REQ_CLR_FIELD:
363 case REQ_OVL_MODE:
364 case REQ_INS_MODE:
365 /* check if we are allowed to edit the field and fall
366 * through if we are.
367 */
368 if ((form->fields[form->cur_field]->opts & O_EDIT) != O_EDIT)
369 return E_REQUEST_DENIED;
370
371 /* the following manipulate the field contents, bundle
372 them into one function.... */
373 /* FALLTHROUGH */
374 case REQ_NEXT_CHAR:
375 case REQ_PREV_CHAR:
376 case REQ_NEXT_LINE:
377 case REQ_PREV_LINE:
378 case REQ_NEXT_WORD:
379 case REQ_PREV_WORD:
380 case REQ_BEG_FIELD:
381 case REQ_END_FIELD:
382 case REQ_BEG_LINE:
383 case REQ_END_LINE:
384 case REQ_LEFT_CHAR:
385 case REQ_RIGHT_CHAR:
386 case REQ_UP_CHAR:
387 case REQ_DOWN_CHAR:
388 case REQ_SCR_FLINE:
389 case REQ_SCR_BLINE:
390 case REQ_SCR_FPAGE:
391 case REQ_SCR_BPAGE:
392 case REQ_SCR_FHPAGE:
393 case REQ_SCR_BHPAGE:
394 case REQ_SCR_FCHAR:
395 case REQ_SCR_BCHAR:
396 case REQ_SCR_HFLINE:
397 case REQ_SCR_HBLINE:
398 case REQ_SCR_HFHALF:
399 case REQ_SCR_HBHALF:
400 update_field = _formi_manipulate_field(form, c);
401 break;
402
403 case REQ_VALIDATION:
404 return _formi_validate_field(form);
405 /* NOTREACHED */
406 break;
407
408 case REQ_PREV_CHOICE:
409 case REQ_NEXT_CHOICE:
410 update_field = _formi_field_choice(form, c);
411 /* reinit the cursor pos just in case */
412 if (update_field == 1) {
413 _formi_init_field_xpos(fieldp);
414 fieldp->row_xpos = 0;
415 }
416 break;
417
418 default: /* should not need to do this, but.... */
419 return E_UNKNOWN_COMMAND;
420 /* NOTREACHED */
421 break;
422 }
423 }
424
425 /* call the field and form init functions if required. */
426 if ((c >= REQ_NEXT_PAGE) && (c <= REQ_DOWN_FIELD)) {
427 if (form->field_init != NULL)
428 form->field_init(form);
429
430 /*
431 * if we have a page movement then the form init
432 * needs to be called too
433 */
434 if ((c <= REQ_LAST_PAGE) && (form->form_init != NULL))
435 form->form_init(form);
436
437 /*
438 * if there was an error just return now...
439 */
440 if (status != E_OK)
441 return status;
442
443 /* if we have no error, reset the various offsets */
444 fieldp = form->fields[form->cur_field];
445 fieldp->start_char = 0;
446 fieldp->start_line = fieldp->alines;
447 fieldp->cur_line = fieldp->alines;
448 fieldp->row_xpos = 0;
449 fieldp->cursor_ypos = 0;
450 _formi_init_field_xpos(fieldp);
451 }
452
453 if (update_field < 0)
454 return update_field;
455
456 if (update_field == 1)
457 update_page |= _formi_update_field(form, old_field);
458
459 if (update_page == 1)
460 _formi_draw_page(form);
461
462 pos_form_cursor(form);
463
464 if ((update_page == 1) || (update_field == 1))
465 wrefresh(form->scrwin);
466
467 return E_OK;
468 }
469