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