xref: /netbsd/lib/libform/driver.c (revision bf9ec67e)
1 /*	$NetBSD: driver.c,v 1.12 2002/05/20 15:00:11 blymn 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 <ctype.h>
33 #include "form.h"
34 #include "internals.h"
35 
36 static int
37 traverse_form_links(FORM *form, int direction);
38 
39 /*
40  * Traverse the links of the current field in the given direction until
41  * either a active & visible field is found or we return to the current
42  * field.  Direction is the REQ_{LEFT,RIGHT,UP,DOWN}_FIELD driver commands.
43  * The function returns E_OK if a valid field is found, E_REQUEST_DENIED
44  * otherwise.
45  */
46 static int
47 traverse_form_links(FORM *form, int direction)
48 {
49 	unsigned idx;
50 
51 	idx = form->cur_field;
52 
53 	do {
54 		switch (direction) {
55 		case REQ_LEFT_FIELD:
56 			if (form->fields[idx]->left == NULL)
57 				return E_REQUEST_DENIED;
58 			idx = form->fields[idx]->left->index;
59 			break;
60 
61 		case REQ_RIGHT_FIELD:
62 			if (form->fields[idx]->right == NULL)
63 				return E_REQUEST_DENIED;
64 			idx = form->fields[idx]->right->index;
65 			break;
66 
67 		case REQ_UP_FIELD:
68 			if (form->fields[idx]->up == NULL)
69 				return E_REQUEST_DENIED;
70 			idx = form->fields[idx]->up->index;
71 			break;
72 
73 		case REQ_DOWN_FIELD:
74 			if (form->fields[idx]->down == NULL)
75 				return E_REQUEST_DENIED;
76 			idx = form->fields[idx]->down->index;
77 			break;
78 
79 		default:
80 			return E_REQUEST_DENIED;
81 		}
82 
83 		if ((form->fields[idx]->opts & (O_ACTIVE | O_VISIBLE))
84 		    == (O_ACTIVE | O_VISIBLE)) {
85 			form->cur_field = idx;
86 			return E_OK;
87 		}
88 	} while (idx != form->cur_field);
89 
90 	return E_REQUEST_DENIED;
91 }
92 
93 int
94 form_driver(FORM *form, int c)
95 {
96 	FIELD *fieldp;
97 	int update_page, update_field, old_field, old_page, status;
98 	int start_field;
99 	unsigned int pos;
100 
101 	if (form == NULL)
102 		return E_BAD_ARGUMENT;
103 
104 	if ((form->fields == NULL) || (*(form->fields) == NULL))
105 		return E_INVALID_FIELD;
106 
107 	if (form->posted != 1)
108 		return E_NOT_POSTED;
109 
110 	if (form->in_init == 1)
111 		return E_BAD_STATE;
112 
113 
114 	old_field = start_field = form->cur_field;
115 	fieldp = form->fields[form->cur_field];
116 	update_page = update_field = 0;
117 	status = E_OK;
118 
119 	if (c < REQ_MIN_REQUEST) {
120 		if (isprint(c) || isblank(c)) {
121 			do {
122 				pos = fieldp->start_char + fieldp->row_xpos
123 	       + fieldp->lines[fieldp->start_line + fieldp->cursor_ypos].start;
124 
125 			      /* check if we are allowed to edit this field */
126 				if ((fieldp->opts & O_EDIT) != O_EDIT)
127 					return E_REQUEST_DENIED;
128 
129 				if ((status =
130 				     (_formi_add_char(fieldp, pos, c)))
131 				    == E_REQUEST_DENIED) {
132 
133 					  /*
134 					   * Need to check here if we
135 					   * want to autoskip.  we
136 					   * call the form driver
137 					   * recursively to pos us on
138 					   * the next field and then
139 					   * we loop back to ensure
140 					   * the next field selected
141 					   * can have data added to it
142 					   */
143 					if ((fieldp->opts & O_AUTOSKIP)
144 					    != O_AUTOSKIP)
145 						return E_REQUEST_DENIED;
146 					status = form_driver(form,
147 							     REQ_NEXT_FIELD);
148 					if (status != E_OK)
149 						return status;
150 
151 					  /*
152 					   * check if we have looped
153                                            * around all the fields.
154                                            * This can easily happen if
155                                            * all the fields are full.
156 					   */
157 					if (start_field == form->cur_field)
158 						return E_REQUEST_DENIED;
159 
160 					old_field = form->cur_field;
161 					fieldp = form->fields[form->cur_field];
162 					status = _formi_add_char(fieldp,
163 							fieldp->start_char
164 							+ fieldp->cursor_xpos,
165 							c);
166 				} else if (status == E_INVALID_FIELD)
167 					  /* char failed validation, just
168 					   * return the status.
169 					   */
170 					return status;
171 				else if (status == E_NO_ROOM)
172 					  /* we will get this if the line
173 					   * wrapping fails.  Deny the
174 					   * request.
175 					   */
176 					return E_REQUEST_DENIED;
177 			}
178 			while (status != E_OK);
179 			update_field = (status == E_OK);
180 		} else
181 			return E_REQUEST_DENIED;
182 	} else {
183 		if (c > REQ_MAX_COMMAND)
184 			return E_UNKNOWN_COMMAND;
185 
186 		if ((c >= REQ_NEXT_PAGE) && (c <= REQ_DOWN_FIELD)) {
187 			  /* first check the field we are in is ok */
188 			if (_formi_validate_field(form) != E_OK)
189 				return E_INVALID_FIELD;
190 
191 			if (form->field_term != NULL)
192 				form->field_term(form);
193 
194 			  /*
195 			   * if we have a page movement then the form term
196 			   * needs to be called too
197 			   */
198 			if ((c <= REQ_LAST_PAGE) && (form->form_term != NULL))
199 				form->form_term(form);
200 		}
201 
202 
203 		switch (c) {
204 		case REQ_NEXT_PAGE:
205 			if (form->page < form->max_page) {
206 				old_page = form->page;
207 				form->page++;
208 				update_page = 1;
209 				if (_formi_pos_first_field(form) != E_OK) {
210 					form->page = old_page;
211 					status = E_REQUEST_DENIED;
212 				}
213 			} else
214 				status = E_REQUEST_DENIED;
215 			break;
216 
217 		case REQ_PREV_PAGE:
218 			if (form->page > 0) {
219 				old_page = form->page;
220 				form->page--;
221 				update_page = 1;
222 				if (_formi_pos_first_field(form) != E_OK) {
223 					form->page = old_page;
224 					status = E_REQUEST_DENIED;
225 				}
226 			} else
227 				status = E_REQUEST_DENIED;
228 			break;
229 
230 		case REQ_FIRST_PAGE:
231 			old_page = form->page;
232 			form->page = 0;
233 			update_page = 1;
234 			if (_formi_pos_first_field(form) != E_OK) {
235 				form->page = old_page;
236 				status = E_REQUEST_DENIED;
237 			}
238 			break;
239 
240 		case REQ_LAST_PAGE:
241 			old_page = form->page;
242 			form->page = form->max_page - 1;
243 			update_page = 1;
244 			if (_formi_pos_first_field(form) != E_OK) {
245 				form->page = old_page;
246 				status = E_REQUEST_DENIED;
247 			}
248 			break;
249 
250 		case REQ_NEXT_FIELD:
251 			status = _formi_pos_new_field(form, _FORMI_FORWARD,
252 						      FALSE);
253 			update_field = 1;
254 			break;
255 
256 		case REQ_PREV_FIELD:
257 			status = _formi_pos_new_field(form, _FORMI_BACKWARD,
258 						      FALSE);
259 			update_field = 1;
260 			break;
261 
262 		case REQ_FIRST_FIELD:
263 			form->cur_field = 0;
264 			update_field = 1;
265 			break;
266 
267 		case REQ_LAST_FIELD:
268 			form->cur_field = form->field_count - 1;
269 			update_field = 1;
270 			break;
271 
272 		case REQ_SNEXT_FIELD:
273 			status = _formi_pos_new_field(form, _FORMI_FORWARD,
274 						      TRUE);
275 			update_field = 1;
276 			break;
277 
278 		case REQ_SPREV_FIELD:
279 			status = _formi_pos_new_field(form, _FORMI_BACKWARD,
280 						      TRUE);
281 			update_field = 1;
282 			break;
283 
284 		case REQ_SFIRST_FIELD:
285 			fieldp = CIRCLEQ_FIRST(&form->sorted_fields);
286 			form->cur_field = fieldp->index;
287 			update_field = 1;
288 			break;
289 
290 		case REQ_SLAST_FIELD:
291 			fieldp = CIRCLEQ_LAST(&form->sorted_fields);
292 			form->cur_field = fieldp->index;
293 			update_field = 1;
294 			break;
295 
296 			  /*
297 			   * The up, down, left and right field traversals
298 			   * are rolled up into a single function, allow a
299 			   * fall through to that function.
300 			   */
301 			  /* FALLTHROUGH */
302 		case REQ_LEFT_FIELD:
303 		case REQ_RIGHT_FIELD:
304 		case REQ_UP_FIELD:
305 		case REQ_DOWN_FIELD:
306 			status = traverse_form_links(form, c);
307 			update_field = 1;
308 			break;
309 
310 			  /* the following commands modify the buffer, check if
311 			     this is allowed first before falling through. */
312 			  /* FALLTHROUGH */
313 		case REQ_DEL_PREV:
314 			  /*
315 			   * need to check for the overloading of this
316 			   * request.  If overload flag set and we are
317 			   * at the start of field this request turns
318 			   * into a previous field request. Otherwise
319 			   * fallthrough to the field handler.
320 			   */
321 			if ((form->opts & O_BS_OVERLOAD) == O_BS_OVERLOAD) {
322 				if ((fieldp->start_char == 0) &&
323 				    (fieldp->start_line == 0) &&
324 				    (fieldp->cursor_xpos == 0)) {
325 					update_field =
326 						_formi_manipulate_field(form,
327 							REQ_PREV_FIELD);
328 					break;
329 				}
330 			}
331 
332 			  /* FALLTHROUGH */
333 		case REQ_NEW_LINE:
334 			  /*
335 			   * need to check for the overloading of this
336 			   * request.  If overload flag set and we are
337 			   * at the start of field this request turns
338 			   * into a next field request. Otherwise
339 			   * fallthrough to the field handler.
340 			   */
341 			if ((form->opts & O_NL_OVERLOAD) == O_NL_OVERLOAD) {
342 				if ((fieldp->start_char == 0) &&
343 				    (fieldp->start_line == 0) &&
344 				    (fieldp->cursor_xpos == 0)) {
345 					update_field =
346 						_formi_manipulate_field(form,
347 							REQ_NEXT_FIELD);
348 					break;
349 				}
350 			}
351 
352 			  /* FALLTHROUGH */
353 		case REQ_INS_CHAR:
354 		case REQ_INS_LINE:
355 		case REQ_DEL_CHAR:
356 		case REQ_DEL_LINE:
357 		case REQ_DEL_WORD:
358 		case REQ_CLR_EOL:
359 		case REQ_CLR_EOF:
360 		case REQ_CLR_FIELD:
361 		case REQ_OVL_MODE:
362 		case REQ_INS_MODE:
363 			  /* check if we are allowed to edit the field and fall
364 			   * through if we are.
365 			   */
366 			if ((form->fields[form->cur_field]->opts & O_EDIT) != O_EDIT)
367 				return E_REQUEST_DENIED;
368 
369 			  /* the following manipulate the field contents, bundle
370 			     them into one function.... */
371 			  /* FALLTHROUGH */
372 		case REQ_NEXT_CHAR:
373 		case REQ_PREV_CHAR:
374 		case REQ_NEXT_LINE:
375 		case REQ_PREV_LINE:
376 		case REQ_NEXT_WORD:
377 		case REQ_PREV_WORD:
378 		case REQ_BEG_FIELD:
379 		case REQ_END_FIELD:
380 		case REQ_BEG_LINE:
381 		case REQ_END_LINE:
382 		case REQ_LEFT_CHAR:
383 		case REQ_RIGHT_CHAR:
384 		case REQ_UP_CHAR:
385 		case REQ_DOWN_CHAR:
386 		case REQ_SCR_FLINE:
387 		case REQ_SCR_BLINE:
388 		case REQ_SCR_FPAGE:
389 		case REQ_SCR_BPAGE:
390 		case REQ_SCR_FHPAGE:
391 		case REQ_SCR_BHPAGE:
392 		case REQ_SCR_FCHAR:
393 		case REQ_SCR_BCHAR:
394 		case REQ_SCR_HFLINE:
395 		case REQ_SCR_HBLINE:
396 		case REQ_SCR_HFHALF:
397 		case REQ_SCR_HBHALF:
398 			update_field = _formi_manipulate_field(form, c);
399 			break;
400 
401 		case REQ_VALIDATION:
402 			return _formi_validate_field(form);
403 			  /* NOTREACHED */
404 			break;
405 
406 		case REQ_PREV_CHOICE:
407 		case REQ_NEXT_CHOICE:
408 			update_field = _formi_field_choice(form, c);
409 			break;
410 
411 		default: /* should not need to do this, but.... */
412 			return E_UNKNOWN_COMMAND;
413 			  /* NOTREACHED */
414 			break;
415 		}
416 	}
417 
418 	  /* call the field and form init functions if required. */
419 	if ((c >= REQ_NEXT_PAGE) && (c <= REQ_DOWN_FIELD)) {
420 		if (form->field_init != NULL)
421 			form->field_init(form);
422 
423 		  /*
424 		   * if we have a page movement then the form init
425 		   * needs to be called too
426 		   */
427 		if ((c <= REQ_LAST_PAGE) && (form->form_init != NULL))
428 			form->form_init(form);
429 
430 		  /*
431 		   * if there was an error just return now...
432 		   */
433 		if (status != E_OK)
434 			return status;
435 
436 		  /* if we have no error, reset the various offsets */
437 		fieldp = form->fields[form->cur_field];
438 		fieldp->start_char = 0;
439 		fieldp->start_line = 0;
440 		fieldp->row_xpos = 0;
441 		fieldp->cursor_xpos = 0;
442 		fieldp->cursor_ypos = 0;
443 	}
444 
445 	if (update_field < 0)
446 		return update_field;
447 
448 	if (update_field == 1)
449 		update_page |= _formi_update_field(form, old_field);
450 
451 	if (update_page == 1)
452 		_formi_draw_page(form);
453 
454 	pos_form_cursor(form);
455 	return E_OK;
456 }
457