xref: /netbsd-src/lib/libform/driver.c (revision 17dd36da8292193180754d5047c0926dbb56818c)
1 /*	$NetBSD: driver.c,v 1.6 2001/02/05 23:59:52 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 withough 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 index;
50 
51 	index = form->cur_field;
52 
53 	do {
54 		switch (direction) {
55 		case REQ_LEFT_FIELD:
56 			if (form->fields[index]->left == NULL)
57 				return E_REQUEST_DENIED;
58 			index = form->fields[index]->left->index;
59 			break;
60 
61 		case REQ_RIGHT_FIELD:
62 			if (form->fields[index]->right == NULL)
63 				return E_REQUEST_DENIED;
64 			index = form->fields[index]->right->index;
65 			break;
66 
67 		case REQ_UP_FIELD:
68 			if (form->fields[index]->up == NULL)
69 				return E_REQUEST_DENIED;
70 			index = form->fields[index]->up->index;
71 			break;
72 
73 		case REQ_DOWN_FIELD:
74 			if (form->fields[index]->down == NULL)
75 				return E_REQUEST_DENIED;
76 			index = form->fields[index]->down->index;
77 			break;
78 
79 		default:
80 			return E_REQUEST_DENIED;
81 		}
82 
83 		if ((form->fields[index]->opts & (O_ACTIVE | O_VISIBLE))
84 		    == (O_ACTIVE | O_VISIBLE)) {
85 			form->cur_field = index;
86 			return E_OK;
87 		}
88 	} while (index != 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 	unsigned int pos;
99 
100 	if (form == NULL)
101 		return E_BAD_ARGUMENT;
102 
103 	if ((form->fields == NULL) || (*(form->fields) == NULL))
104 		return E_INVALID_FIELD;
105 
106 	if (form->posted != 1)
107 		return E_NOT_POSTED;
108 
109 	if (form->in_init == 1)
110 		return E_BAD_STATE;
111 
112 
113 	old_field = form->cur_field;
114 	fieldp = form->fields[form->cur_field];
115 	update_page = update_field = 0;
116 	status = E_OK;
117 
118 	if (c < REQ_MIN_REQUEST) {
119 		if (isprint(c)) {
120 			do {
121 				pos = fieldp->start_char + fieldp->cursor_xpos
122 					+ fieldp->hscroll;
123 
124 				  /* check if we are allowed to edit this field */
125 				if ((fieldp->opts & O_EDIT) != O_EDIT)
126 					return E_REQUEST_DENIED;
127 
128 				if (fieldp->start_char > 0)
129 					pos--;
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 					old_field = form->cur_field;
153 					fieldp = form->fields[form->cur_field];
154 				} else if (status == E_INVALID_FIELD)
155 					  /* char failed validation, just
156 					   * return the status.
157 					   */
158 					return status;
159 			}
160 			while (status != E_OK);
161 			update_field = (status == E_OK);
162 		} else
163 			return E_REQUEST_DENIED;
164 	} else {
165 		if (c > REQ_MAX_COMMAND)
166 			return E_UNKNOWN_COMMAND;
167 
168 		if ((c >= REQ_NEXT_PAGE) && (c <= REQ_DOWN_FIELD)) {
169 			  /* first check the field we are in is ok */
170 			if (_formi_validate_field(form) != E_OK)
171 				return E_INVALID_FIELD;
172 
173 			if (form->field_term != NULL)
174 				form->field_term(form);
175 
176 			  /*
177 			   * if we have a page movement then the form term
178 			   * needs to be called too
179 			   */
180 			if ((c <= REQ_LAST_PAGE) && (form->form_term != NULL))
181 				form->form_term(form);
182 		}
183 
184 
185 		switch (c) {
186 		case REQ_NEXT_PAGE:
187 			if (form->page < form->max_page) {
188 				old_page = form->page;
189 				form->page++;
190 				update_page = 1;
191 				if (_formi_pos_first_field(form) != E_OK) {
192 					form->page = old_page;
193 					status = E_REQUEST_DENIED;
194 				}
195 			} else
196 				status = E_REQUEST_DENIED;
197 			break;
198 
199 		case REQ_PREV_PAGE:
200 			if (form->page > 0) {
201 				old_page = form->page;
202 				form->page--;
203 				update_page = 1;
204 				if (_formi_pos_first_field(form) != E_OK) {
205 					form->page = old_page;
206 					status = E_REQUEST_DENIED;
207 				}
208 			} else
209 				status = E_REQUEST_DENIED;
210 			break;
211 
212 		case REQ_FIRST_PAGE:
213 			old_page = form->page;
214 			form->page = 0;
215 			update_page = 1;
216 			if (_formi_pos_first_field(form) != E_OK) {
217 				form->page = old_page;
218 				status = E_REQUEST_DENIED;
219 			}
220 			break;
221 
222 		case REQ_LAST_PAGE:
223 			old_page = form->page;
224 			form->page = form->max_page - 1;
225 			update_page = 1;
226 			if (_formi_pos_first_field(form) != E_OK) {
227 				form->page = old_page;
228 				status = E_REQUEST_DENIED;
229 			}
230 			break;
231 
232 		case REQ_NEXT_FIELD:
233 			status = _formi_pos_new_field(form, _FORMI_FORWARD,
234 						      FALSE);
235 			update_field = 1;
236 			break;
237 
238 		case REQ_PREV_FIELD:
239 			status = _formi_pos_new_field(form, _FORMI_BACKWARD,
240 						      FALSE);
241 			update_field = 1;
242 			break;
243 
244 		case REQ_FIRST_FIELD:
245 			form->cur_field = 0;
246 			update_field = 1;
247 			break;
248 
249 		case REQ_LAST_FIELD:
250 			form->cur_field = form->field_count - 1;
251 			update_field = 1;
252 			break;
253 
254 		case REQ_SNEXT_FIELD:
255 			status = _formi_pos_new_field(form, _FORMI_FORWARD,
256 						      TRUE);
257 			update_field = 1;
258 			break;
259 
260 		case REQ_SPREV_FIELD:
261 			status = _formi_pos_new_field(form, _FORMI_BACKWARD,
262 						      TRUE);
263 			update_field = 1;
264 			break;
265 
266 		case REQ_SFIRST_FIELD:
267 			fieldp = CIRCLEQ_FIRST(&form->sorted_fields);
268 			form->cur_field = fieldp->index;
269 			update_field = 1;
270 			break;
271 
272 		case REQ_SLAST_FIELD:
273 			fieldp = CIRCLEQ_LAST(&form->sorted_fields);
274 			form->cur_field = fieldp->index;
275 			update_field = 1;
276 			break;
277 
278 			  /*
279 			   * The up, down, left and right field traversals
280 			   * are rolled up into a single function, allow a
281 			   * fall through to that function.
282 			   */
283 			  /* FALLTHROUGH */
284 		case REQ_LEFT_FIELD:
285 		case REQ_RIGHT_FIELD:
286 		case REQ_UP_FIELD:
287 		case REQ_DOWN_FIELD:
288 			status = traverse_form_links(form, c);
289 			update_field = 1;
290 			break;
291 
292 			  /* the following commands modify the buffer, check if
293 			     this is allowed first before falling through. */
294 			  /* FALLTHROUGH */
295 		case REQ_DEL_PREV:
296 			  /*
297 			   * need to check for the overloading of this
298 			   * request.  If overload flag set and we are
299 			   * at the start of field this request turns
300 			   * into a previous field request. Otherwise
301 			   * fallthrough to the field handler.
302 			   */
303 			if ((form->opts & O_BS_OVERLOAD) == O_BS_OVERLOAD) {
304 				if ((fieldp->start_char == 0) &&
305 				    (fieldp->start_line == 0) &&
306 				    (fieldp->hscroll == 0) &&
307 				    (fieldp->cursor_xpos == 0)) {
308 					update_field =
309 						_formi_manipulate_field(form,
310 							REQ_PREV_FIELD);
311 					break;
312 				}
313 			}
314 
315 			  /* FALLTHROUGH */
316 		case REQ_NEW_LINE:
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 next field request. Otherwise
322 			   * fallthrough to the field handler.
323 			   */
324 			if ((form->opts & O_NL_OVERLOAD) == O_NL_OVERLOAD) {
325 				if ((fieldp->start_char == 0) &&
326 				    (fieldp->start_line == 0) &&
327 				    (fieldp->hscroll == 0) &&
328 				    (fieldp->cursor_xpos == 0)) {
329 					update_field =
330 						_formi_manipulate_field(form,
331 							REQ_NEXT_FIELD);
332 					break;
333 				}
334 			}
335 
336 			  /* FALLTHROUGH */
337 		case REQ_INS_CHAR:
338 		case REQ_INS_LINE:
339 		case REQ_DEL_CHAR:
340 		case REQ_DEL_LINE:
341 		case REQ_DEL_WORD:
342 		case REQ_CLR_EOL:
343 		case REQ_CLR_EOF:
344 		case REQ_CLR_FIELD:
345 		case REQ_OVL_MODE:
346 		case REQ_INS_MODE:
347 			  /* check if we are allowed to edit the field and fall
348 			   * through if we are.
349 			   */
350 			if ((form->fields[form->cur_field]->opts & O_EDIT) != O_EDIT)
351 				return E_REQUEST_DENIED;
352 
353 			  /* the following manipulate the field contents, bundle
354 			     them into one function.... */
355 			  /* FALLTHROUGH */
356 		case REQ_NEXT_CHAR:
357 		case REQ_PREV_CHAR:
358 		case REQ_NEXT_LINE:
359 		case REQ_PREV_LINE:
360 		case REQ_NEXT_WORD:
361 		case REQ_PREV_WORD:
362 		case REQ_BEG_FIELD:
363 		case REQ_END_FIELD:
364 		case REQ_BEG_LINE:
365 		case REQ_END_LINE:
366 		case REQ_LEFT_CHAR:
367 		case REQ_RIGHT_CHAR:
368 		case REQ_UP_CHAR:
369 		case REQ_DOWN_CHAR:
370 		case REQ_SCR_FLINE:
371 		case REQ_SCR_BLINE:
372 		case REQ_SCR_FPAGE:
373 		case REQ_SCR_BPAGE:
374 		case REQ_SCR_FHPAGE:
375 		case REQ_SCR_BHPAGE:
376 		case REQ_SCR_FCHAR:
377 		case REQ_SCR_BCHAR:
378 		case REQ_SCR_HFLINE:
379 		case REQ_SCR_HBLINE:
380 		case REQ_SCR_HFHALF:
381 		case REQ_SCR_HBHALF:
382 			update_field = _formi_manipulate_field(form, c);
383 			break;
384 
385 		case REQ_VALIDATION:
386 			return _formi_validate_field(form);
387 			  /* NOTREACHED */
388 			break;
389 
390 		case REQ_PREV_CHOICE:
391 		case REQ_NEXT_CHOICE:
392 			update_field = _formi_field_choice(form, c);
393 			break;
394 
395 		default: /* should not need to do this, but.... */
396 			return E_UNKNOWN_COMMAND;
397 			  /* NOTREACHED */
398 			break;
399 		}
400 	}
401 
402 	  /* call the field and form init functions if required. */
403 	if ((c >= REQ_NEXT_PAGE) && (c <= REQ_DOWN_FIELD)) {
404 		if (form->field_init != NULL)
405 			form->field_init(form);
406 
407 		  /*
408 		   * if we have a page movement then the form init
409 		   * needs to be called too
410 		   */
411 		if ((c <= REQ_LAST_PAGE) && (form->form_init != NULL))
412 			form->form_init(form);
413 
414 		  /*
415 		   * if there was an error just return now...
416 		   */
417 		if (status != E_OK)
418 			return status;
419 
420 		  /* if we have no error, reset the various offsets */
421 		fieldp = form->fields[form->cur_field];
422 		fieldp->start_char = 0;
423 		fieldp->start_line = 0;
424 		fieldp->hscroll = 0;
425 		fieldp->cursor_xpos = 0;
426 		fieldp->cursor_ypos = 0;
427 	}
428 
429 	if (update_field < 0)
430 		return update_field;
431 
432 	if (update_field == 1)
433 		update_page |= _formi_update_field(form, old_field);
434 
435 	if (update_page == 1)
436 		_formi_draw_page(form);
437 
438 	pos_form_cursor(form);
439 	return E_OK;
440 }
441 
442