xref: /netbsd-src/lib/libform/driver.c (revision 23c8222edbfb0f0932d88a8351d3a0cf817dfb9e)
1 /*	$NetBSD: driver.c,v 1.15 2003/03/09 00:57:17 lukem 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.15 2003/03/09 00:57:17 lukem 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
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
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 	       + fieldp->lines[fieldp->start_line + fieldp->cursor_ypos].start;
127 
128 			      /* check if we are allowed to edit this field */
129 				if ((fieldp->opts & O_EDIT) != O_EDIT)
130 					return E_REQUEST_DENIED;
131 
132 				if ((status =
133 				     (_formi_add_char(fieldp, pos, c)))
134 				    == E_REQUEST_DENIED) {
135 
136 					  /*
137 					   * Need to check here if we
138 					   * want to autoskip.  we
139 					   * call the form driver
140 					   * recursively to pos us on
141 					   * the next field and then
142 					   * we loop back to ensure
143 					   * the next field selected
144 					   * can have data added to it
145 					   */
146 					if ((fieldp->opts & O_AUTOSKIP)
147 					    != O_AUTOSKIP)
148 						return E_REQUEST_DENIED;
149 					status = form_driver(form,
150 							     REQ_NEXT_FIELD);
151 					if (status != E_OK)
152 						return status;
153 
154 					  /*
155 					   * check if we have looped
156                                            * around all the fields.
157                                            * This can easily happen if
158                                            * all the fields are full.
159 					   */
160 					if (start_field == form->cur_field)
161 						return E_REQUEST_DENIED;
162 
163 					old_field = form->cur_field;
164 					fieldp = form->fields[form->cur_field];
165 					status = _formi_add_char(fieldp,
166 							fieldp->start_char
167 							+ fieldp->cursor_xpos,
168 							c);
169 				} else if (status == E_INVALID_FIELD)
170 					  /* char failed validation, just
171 					   * return the status.
172 					   */
173 					return status;
174 				else if (status == E_NO_ROOM)
175 					  /* we will get this if the line
176 					   * wrapping fails.  Deny the
177 					   * request.
178 					   */
179 					return E_REQUEST_DENIED;
180 			}
181 			while (status != E_OK);
182 			update_field = (status == E_OK);
183 		} else
184 			return E_REQUEST_DENIED;
185 	} else {
186 		if (c > REQ_MAX_COMMAND)
187 			return E_UNKNOWN_COMMAND;
188 
189 		if ((c >= REQ_NEXT_PAGE) && (c <= REQ_DOWN_FIELD)) {
190 			  /* first check the field we are in is ok */
191 			if (_formi_validate_field(form) != E_OK)
192 				return E_INVALID_FIELD;
193 
194 			if (form->field_term != NULL)
195 				form->field_term(form);
196 
197 			  /*
198 			   * if we have a page movement then the form term
199 			   * needs to be called too
200 			   */
201 			if ((c <= REQ_LAST_PAGE) && (form->form_term != NULL))
202 				form->form_term(form);
203 		}
204 
205 
206 		switch (c) {
207 		case REQ_NEXT_PAGE:
208 			if (form->page < form->max_page) {
209 				old_page = form->page;
210 				form->page++;
211 				update_page = 1;
212 				if (_formi_pos_first_field(form) != E_OK) {
213 					form->page = old_page;
214 					status = E_REQUEST_DENIED;
215 				}
216 			} else
217 				status = E_REQUEST_DENIED;
218 			break;
219 
220 		case REQ_PREV_PAGE:
221 			if (form->page > 0) {
222 				old_page = form->page;
223 				form->page--;
224 				update_page = 1;
225 				if (_formi_pos_first_field(form) != E_OK) {
226 					form->page = old_page;
227 					status = E_REQUEST_DENIED;
228 				}
229 			} else
230 				status = E_REQUEST_DENIED;
231 			break;
232 
233 		case REQ_FIRST_PAGE:
234 			old_page = form->page;
235 			form->page = 0;
236 			update_page = 1;
237 			if (_formi_pos_first_field(form) != E_OK) {
238 				form->page = old_page;
239 				status = E_REQUEST_DENIED;
240 			}
241 			break;
242 
243 		case REQ_LAST_PAGE:
244 			old_page = form->page;
245 			form->page = form->max_page - 1;
246 			update_page = 1;
247 			if (_formi_pos_first_field(form) != E_OK) {
248 				form->page = old_page;
249 				status = E_REQUEST_DENIED;
250 			}
251 			break;
252 
253 		case REQ_NEXT_FIELD:
254 			status = _formi_pos_new_field(form, _FORMI_FORWARD,
255 						      FALSE);
256 			update_field = 1;
257 			break;
258 
259 		case REQ_PREV_FIELD:
260 			status = _formi_pos_new_field(form, _FORMI_BACKWARD,
261 						      FALSE);
262 			update_field = 1;
263 			break;
264 
265 		case REQ_FIRST_FIELD:
266 			form->cur_field = 0;
267 			update_field = 1;
268 			break;
269 
270 		case REQ_LAST_FIELD:
271 			form->cur_field = form->field_count - 1;
272 			update_field = 1;
273 			break;
274 
275 		case REQ_SNEXT_FIELD:
276 			status = _formi_pos_new_field(form, _FORMI_FORWARD,
277 						      TRUE);
278 			update_field = 1;
279 			break;
280 
281 		case REQ_SPREV_FIELD:
282 			status = _formi_pos_new_field(form, _FORMI_BACKWARD,
283 						      TRUE);
284 			update_field = 1;
285 			break;
286 
287 		case REQ_SFIRST_FIELD:
288 			fieldp = CIRCLEQ_FIRST(&form->sorted_fields);
289 			form->cur_field = fieldp->index;
290 			update_field = 1;
291 			break;
292 
293 		case REQ_SLAST_FIELD:
294 			fieldp = CIRCLEQ_LAST(&form->sorted_fields);
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 			  /* FALLTHROUGH */
305 		case REQ_LEFT_FIELD:
306 		case REQ_RIGHT_FIELD:
307 		case REQ_UP_FIELD:
308 		case REQ_DOWN_FIELD:
309 			status = traverse_form_links(form, c);
310 			update_field = 1;
311 			break;
312 
313 			  /* the following commands modify the buffer, check if
314 			     this is allowed first before falling through. */
315 			  /* FALLTHROUGH */
316 		case REQ_DEL_PREV:
317 			  /*
318 			   * need to check for the overloading of this
319 			   * request.  If overload flag set and we are
320 			   * at the start of field this request turns
321 			   * into a previous field request. Otherwise
322 			   * fallthrough to the field handler.
323 			   */
324 			if ((form->opts & O_BS_OVERLOAD) == O_BS_OVERLOAD) {
325 				if ((fieldp->start_char == 0) &&
326 				    (fieldp->start_line == 0) &&
327 				    (fieldp->row_xpos == 0)) {
328 					update_field =
329 						_formi_manipulate_field(form,
330 							REQ_PREV_FIELD);
331 					break;
332 				}
333 			}
334 
335 			  /* FALLTHROUGH */
336 		case REQ_NEW_LINE:
337 			  /*
338 			   * need to check for the overloading of this
339 			   * request.  If overload flag set and we are
340 			   * at the start of field this request turns
341 			   * into a next field request. Otherwise
342 			   * fallthrough to the field handler.
343 			   */
344 			if ((form->opts & O_NL_OVERLOAD) == O_NL_OVERLOAD) {
345 				if ((fieldp->start_char == 0) &&
346 				    (fieldp->start_line == 0) &&
347 				    (fieldp->row_xpos == 0)) {
348 					update_field =
349 						_formi_manipulate_field(form,
350 							REQ_NEXT_FIELD);
351 					break;
352 				}
353 			}
354 
355 			  /* FALLTHROUGH */
356 		case REQ_INS_CHAR:
357 		case REQ_INS_LINE:
358 		case REQ_DEL_CHAR:
359 		case REQ_DEL_LINE:
360 		case REQ_DEL_WORD:
361 		case REQ_CLR_EOL:
362 		case REQ_CLR_EOF:
363 		case REQ_CLR_FIELD:
364 		case REQ_OVL_MODE:
365 		case REQ_INS_MODE:
366 			  /* check if we are allowed to edit the field and fall
367 			   * through if we are.
368 			   */
369 			if ((form->fields[form->cur_field]->opts & O_EDIT) != O_EDIT)
370 				return E_REQUEST_DENIED;
371 
372 			  /* the following manipulate the field contents, bundle
373 			     them into one function.... */
374 			  /* FALLTHROUGH */
375 		case REQ_NEXT_CHAR:
376 		case REQ_PREV_CHAR:
377 		case REQ_NEXT_LINE:
378 		case REQ_PREV_LINE:
379 		case REQ_NEXT_WORD:
380 		case REQ_PREV_WORD:
381 		case REQ_BEG_FIELD:
382 		case REQ_END_FIELD:
383 		case REQ_BEG_LINE:
384 		case REQ_END_LINE:
385 		case REQ_LEFT_CHAR:
386 		case REQ_RIGHT_CHAR:
387 		case REQ_UP_CHAR:
388 		case REQ_DOWN_CHAR:
389 		case REQ_SCR_FLINE:
390 		case REQ_SCR_BLINE:
391 		case REQ_SCR_FPAGE:
392 		case REQ_SCR_BPAGE:
393 		case REQ_SCR_FHPAGE:
394 		case REQ_SCR_BHPAGE:
395 		case REQ_SCR_FCHAR:
396 		case REQ_SCR_BCHAR:
397 		case REQ_SCR_HFLINE:
398 		case REQ_SCR_HBLINE:
399 		case REQ_SCR_HFHALF:
400 		case REQ_SCR_HBHALF:
401 			update_field = _formi_manipulate_field(form, c);
402 			break;
403 
404 		case REQ_VALIDATION:
405 			return _formi_validate_field(form);
406 			  /* NOTREACHED */
407 			break;
408 
409 		case REQ_PREV_CHOICE:
410 		case REQ_NEXT_CHOICE:
411 			update_field = _formi_field_choice(form, c);
412 			  /* reinit the cursor pos just in case */
413 			if (update_field == 1) {
414 				_formi_init_field_xpos(fieldp);
415 				fieldp->row_xpos = 0;
416 			}
417 			break;
418 
419 		default: /* should not need to do this, but.... */
420 			return E_UNKNOWN_COMMAND;
421 			  /* NOTREACHED */
422 			break;
423 		}
424 	}
425 
426 	  /* call the field and form init functions if required. */
427 	if ((c >= REQ_NEXT_PAGE) && (c <= REQ_DOWN_FIELD)) {
428 		if (form->field_init != NULL)
429 			form->field_init(form);
430 
431 		  /*
432 		   * if we have a page movement then the form init
433 		   * needs to be called too
434 		   */
435 		if ((c <= REQ_LAST_PAGE) && (form->form_init != NULL))
436 			form->form_init(form);
437 
438 		  /*
439 		   * if there was an error just return now...
440 		   */
441 		if (status != E_OK)
442 			return status;
443 
444 		  /* if we have no error, reset the various offsets */
445 		fieldp = form->fields[form->cur_field];
446 		fieldp->start_char = 0;
447 		fieldp->start_line = 0;
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