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