1 /* $NetBSD: driver.c,v 1.17 2010/02/03 15:34:43 roy 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.17 2010/02/03 15:34:43 roy 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 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 = CIRCLEQ_FIRST(&form->sorted_fields); 288 form->cur_field = fieldp->index; 289 update_field = 1; 290 break; 291 292 case REQ_SLAST_FIELD: 293 fieldp = CIRCLEQ_LAST(&form->sorted_fields); 294 form->cur_field = fieldp->index; 295 update_field = 1; 296 break; 297 298 /* 299 * The up, down, left and right field traversals 300 * are rolled up into a single function, allow a 301 * fall through to that function. 302 */ 303 case REQ_LEFT_FIELD: 304 case REQ_RIGHT_FIELD: 305 case REQ_UP_FIELD: 306 case REQ_DOWN_FIELD: 307 status = traverse_form_links(form, c); 308 update_field = 1; 309 break; 310 311 /* the following commands modify the buffer, check if 312 this is allowed first before falling through. */ 313 314 case REQ_DEL_PREV: 315 /* 316 * need to check for the overloading of this 317 * request. If overload flag set and we are 318 * at the start of field this request turns 319 * into a previous field request. Otherwise 320 * fallthrough to the field handler. 321 */ 322 if ((form->opts & O_BS_OVERLOAD) == O_BS_OVERLOAD) { 323 if ((fieldp->start_char == 0) && 324 (fieldp->start_line == 0) && 325 (fieldp->row_xpos == 0)) { 326 update_field = 327 _formi_manipulate_field(form, 328 REQ_PREV_FIELD); 329 break; 330 } 331 } 332 333 /* FALLTHROUGH */ 334 case REQ_NEW_LINE: 335 /* 336 * need to check for the overloading of this 337 * request. If overload flag set and we are 338 * at the start of field this request turns 339 * into a next field request. Otherwise 340 * fallthrough to the field handler. 341 */ 342 if ((form->opts & O_NL_OVERLOAD) == O_NL_OVERLOAD) { 343 if ((fieldp->start_char == 0) && 344 (fieldp->start_line == 0) && 345 (fieldp->row_xpos == 0)) { 346 update_field = 347 _formi_manipulate_field(form, 348 REQ_NEXT_FIELD); 349 break; 350 } 351 } 352 353 /* FALLTHROUGH */ 354 case REQ_INS_CHAR: 355 case REQ_INS_LINE: 356 case REQ_DEL_CHAR: 357 case REQ_DEL_LINE: 358 case REQ_DEL_WORD: 359 case REQ_CLR_EOL: 360 case REQ_CLR_EOF: 361 case REQ_CLR_FIELD: 362 case REQ_OVL_MODE: 363 case REQ_INS_MODE: 364 /* check if we are allowed to edit the field and fall 365 * through if we are. 366 */ 367 if ((form->fields[form->cur_field]->opts & O_EDIT) != O_EDIT) 368 return E_REQUEST_DENIED; 369 370 /* the following manipulate the field contents, bundle 371 them into one function.... */ 372 /* FALLTHROUGH */ 373 case REQ_NEXT_CHAR: 374 case REQ_PREV_CHAR: 375 case REQ_NEXT_LINE: 376 case REQ_PREV_LINE: 377 case REQ_NEXT_WORD: 378 case REQ_PREV_WORD: 379 case REQ_BEG_FIELD: 380 case REQ_END_FIELD: 381 case REQ_BEG_LINE: 382 case REQ_END_LINE: 383 case REQ_LEFT_CHAR: 384 case REQ_RIGHT_CHAR: 385 case REQ_UP_CHAR: 386 case REQ_DOWN_CHAR: 387 case REQ_SCR_FLINE: 388 case REQ_SCR_BLINE: 389 case REQ_SCR_FPAGE: 390 case REQ_SCR_BPAGE: 391 case REQ_SCR_FHPAGE: 392 case REQ_SCR_BHPAGE: 393 case REQ_SCR_FCHAR: 394 case REQ_SCR_BCHAR: 395 case REQ_SCR_HFLINE: 396 case REQ_SCR_HBLINE: 397 case REQ_SCR_HFHALF: 398 case REQ_SCR_HBHALF: 399 update_field = _formi_manipulate_field(form, c); 400 break; 401 402 case REQ_VALIDATION: 403 return _formi_validate_field(form); 404 /* NOTREACHED */ 405 break; 406 407 case REQ_PREV_CHOICE: 408 case REQ_NEXT_CHOICE: 409 update_field = _formi_field_choice(form, c); 410 /* reinit the cursor pos just in case */ 411 if (update_field == 1) { 412 _formi_init_field_xpos(fieldp); 413 fieldp->row_xpos = 0; 414 } 415 break; 416 417 default: /* should not need to do this, but.... */ 418 return E_UNKNOWN_COMMAND; 419 /* NOTREACHED */ 420 break; 421 } 422 } 423 424 /* call the field and form init functions if required. */ 425 if ((c >= REQ_NEXT_PAGE) && (c <= REQ_DOWN_FIELD)) { 426 if (form->field_init != NULL) 427 form->field_init(form); 428 429 /* 430 * if we have a page movement then the form init 431 * needs to be called too 432 */ 433 if ((c <= REQ_LAST_PAGE) && (form->form_init != NULL)) 434 form->form_init(form); 435 436 /* 437 * if there was an error just return now... 438 */ 439 if (status != E_OK) 440 return status; 441 442 /* if we have no error, reset the various offsets */ 443 fieldp = form->fields[form->cur_field]; 444 fieldp->start_char = 0; 445 fieldp->start_line = fieldp->alines; 446 fieldp->cur_line = fieldp->alines; 447 fieldp->row_xpos = 0; 448 fieldp->cursor_ypos = 0; 449 _formi_init_field_xpos(fieldp); 450 } 451 452 if (update_field < 0) 453 return update_field; 454 455 if (update_field == 1) 456 update_page |= _formi_update_field(form, old_field); 457 458 if (update_page == 1) 459 _formi_draw_page(form); 460 461 pos_form_cursor(form); 462 463 if ((update_page == 1) || (update_field == 1)) 464 wrefresh(form->scrwin); 465 466 return E_OK; 467 } 468