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