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 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 = 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