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