1 1.18 christos /* $NetBSD: driver.c,v 1.18 2013/11/26 01:17:00 christos Exp $ */ 2 1.1 blymn 3 1.1 blymn /*- 4 1.1 blymn * Copyright (c) 1998-1999 Brett Lymn 5 1.1 blymn * (blymn (at) baea.com.au, brett_lymn (at) yahoo.com.au) 6 1.1 blymn * All rights reserved. 7 1.1 blymn * 8 1.1 blymn * This code has been donated to The NetBSD Foundation by the Author. 9 1.1 blymn * 10 1.1 blymn * Redistribution and use in source and binary forms, with or without 11 1.1 blymn * modification, are permitted provided that the following conditions 12 1.1 blymn * are met: 13 1.1 blymn * 1. Redistributions of source code must retain the above copyright 14 1.1 blymn * notice, this list of conditions and the following disclaimer. 15 1.1 blymn * 2. The name of the author may not be used to endorse or promote products 16 1.10 wiz * derived from this software without specific prior written permission 17 1.1 blymn * 18 1.1 blymn * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 19 1.1 blymn * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 20 1.1 blymn * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 21 1.1 blymn * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 22 1.1 blymn * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 23 1.1 blymn * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 1.1 blymn * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 1.1 blymn * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 1.1 blymn * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 27 1.1 blymn * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 1.1 blymn * 29 1.1 blymn * 30 1.1 blymn */ 31 1.15 lukem 32 1.15 lukem #include <sys/cdefs.h> 33 1.18 christos __RCSID("$NetBSD: driver.c,v 1.18 2013/11/26 01:17:00 christos Exp $"); 34 1.1 blymn 35 1.1 blymn #include <ctype.h> 36 1.1 blymn #include "form.h" 37 1.1 blymn #include "internals.h" 38 1.1 blymn 39 1.1 blymn static int 40 1.1 blymn traverse_form_links(FORM *form, int direction); 41 1.1 blymn 42 1.1 blymn /* 43 1.1 blymn * Traverse the links of the current field in the given direction until 44 1.1 blymn * either a active & visible field is found or we return to the current 45 1.1 blymn * field. Direction is the REQ_{LEFT,RIGHT,UP,DOWN}_FIELD driver commands. 46 1.1 blymn * The function returns E_OK if a valid field is found, E_REQUEST_DENIED 47 1.1 blymn * otherwise. 48 1.1 blymn */ 49 1.1 blymn static int 50 1.1 blymn traverse_form_links(FORM *form, int direction) 51 1.1 blymn { 52 1.11 thorpej unsigned idx; 53 1.1 blymn 54 1.11 thorpej idx = form->cur_field; 55 1.1 blymn 56 1.1 blymn do { 57 1.1 blymn switch (direction) { 58 1.1 blymn case REQ_LEFT_FIELD: 59 1.11 thorpej if (form->fields[idx]->left == NULL) 60 1.1 blymn return E_REQUEST_DENIED; 61 1.11 thorpej idx = form->fields[idx]->left->index; 62 1.1 blymn break; 63 1.1 blymn 64 1.1 blymn case REQ_RIGHT_FIELD: 65 1.11 thorpej if (form->fields[idx]->right == NULL) 66 1.1 blymn return E_REQUEST_DENIED; 67 1.11 thorpej idx = form->fields[idx]->right->index; 68 1.1 blymn break; 69 1.1 blymn 70 1.1 blymn case REQ_UP_FIELD: 71 1.11 thorpej if (form->fields[idx]->up == NULL) 72 1.1 blymn return E_REQUEST_DENIED; 73 1.11 thorpej idx = form->fields[idx]->up->index; 74 1.1 blymn break; 75 1.1 blymn 76 1.1 blymn case REQ_DOWN_FIELD: 77 1.11 thorpej if (form->fields[idx]->down == NULL) 78 1.1 blymn return E_REQUEST_DENIED; 79 1.11 thorpej idx = form->fields[idx]->down->index; 80 1.1 blymn break; 81 1.1 blymn 82 1.1 blymn default: 83 1.1 blymn return E_REQUEST_DENIED; 84 1.1 blymn } 85 1.1 blymn 86 1.11 thorpej if ((form->fields[idx]->opts & (O_ACTIVE | O_VISIBLE)) 87 1.1 blymn == (O_ACTIVE | O_VISIBLE)) { 88 1.11 thorpej form->cur_field = idx; 89 1.1 blymn return E_OK; 90 1.1 blymn } 91 1.11 thorpej } while (idx != form->cur_field); 92 1.1 blymn 93 1.1 blymn return E_REQUEST_DENIED; 94 1.1 blymn } 95 1.1 blymn 96 1.1 blymn int 97 1.1 blymn form_driver(FORM *form, int c) 98 1.1 blymn { 99 1.1 blymn FIELD *fieldp; 100 1.1 blymn int update_page, update_field, old_field, old_page, status; 101 1.7 blymn int start_field; 102 1.1 blymn unsigned int pos; 103 1.1 blymn 104 1.1 blymn if (form == NULL) 105 1.1 blymn return E_BAD_ARGUMENT; 106 1.1 blymn 107 1.1 blymn if ((form->fields == NULL) || (*(form->fields) == NULL)) 108 1.1 blymn return E_INVALID_FIELD; 109 1.1 blymn 110 1.1 blymn if (form->posted != 1) 111 1.1 blymn return E_NOT_POSTED; 112 1.1 blymn 113 1.1 blymn if (form->in_init == 1) 114 1.1 blymn return E_BAD_STATE; 115 1.1 blymn 116 1.1 blymn 117 1.7 blymn old_field = start_field = form->cur_field; 118 1.4 blymn fieldp = form->fields[form->cur_field]; 119 1.1 blymn update_page = update_field = 0; 120 1.4 blymn status = E_OK; 121 1.1 blymn 122 1.1 blymn if (c < REQ_MIN_REQUEST) { 123 1.12 blymn if (isprint(c) || isblank(c)) { 124 1.6 blymn do { 125 1.16 blymn pos = fieldp->start_char + fieldp->row_xpos; 126 1.16 blymn 127 1.7 blymn /* check if we are allowed to edit this field */ 128 1.6 blymn if ((fieldp->opts & O_EDIT) != O_EDIT) 129 1.1 blymn return E_REQUEST_DENIED; 130 1.6 blymn 131 1.6 blymn if ((status = 132 1.6 blymn (_formi_add_char(fieldp, pos, c))) 133 1.6 blymn == E_REQUEST_DENIED) { 134 1.6 blymn 135 1.6 blymn /* 136 1.6 blymn * Need to check here if we 137 1.6 blymn * want to autoskip. we 138 1.6 blymn * call the form driver 139 1.6 blymn * recursively to pos us on 140 1.6 blymn * the next field and then 141 1.6 blymn * we loop back to ensure 142 1.6 blymn * the next field selected 143 1.6 blymn * can have data added to it 144 1.6 blymn */ 145 1.6 blymn if ((fieldp->opts & O_AUTOSKIP) 146 1.6 blymn != O_AUTOSKIP) 147 1.6 blymn return E_REQUEST_DENIED; 148 1.6 blymn status = form_driver(form, 149 1.6 blymn REQ_NEXT_FIELD); 150 1.6 blymn if (status != E_OK) 151 1.6 blymn return status; 152 1.7 blymn 153 1.7 blymn /* 154 1.7 blymn * check if we have looped 155 1.7 blymn * around all the fields. 156 1.7 blymn * This can easily happen if 157 1.7 blymn * all the fields are full. 158 1.7 blymn */ 159 1.7 blymn if (start_field == form->cur_field) 160 1.7 blymn return E_REQUEST_DENIED; 161 1.7 blymn 162 1.6 blymn old_field = form->cur_field; 163 1.6 blymn fieldp = form->fields[form->cur_field]; 164 1.7 blymn status = _formi_add_char(fieldp, 165 1.7 blymn fieldp->start_char 166 1.7 blymn + fieldp->cursor_xpos, 167 1.7 blymn c); 168 1.6 blymn } else if (status == E_INVALID_FIELD) 169 1.6 blymn /* char failed validation, just 170 1.6 blymn * return the status. 171 1.6 blymn */ 172 1.1 blymn return status; 173 1.9 blymn else if (status == E_NO_ROOM) 174 1.9 blymn /* we will get this if the line 175 1.9 blymn * wrapping fails. Deny the 176 1.9 blymn * request. 177 1.9 blymn */ 178 1.9 blymn return E_REQUEST_DENIED; 179 1.1 blymn } 180 1.6 blymn while (status != E_OK); 181 1.6 blymn update_field = (status == E_OK); 182 1.1 blymn } else 183 1.1 blymn return E_REQUEST_DENIED; 184 1.1 blymn } else { 185 1.1 blymn if (c > REQ_MAX_COMMAND) 186 1.1 blymn return E_UNKNOWN_COMMAND; 187 1.1 blymn 188 1.4 blymn if ((c >= REQ_NEXT_PAGE) && (c <= REQ_DOWN_FIELD)) { 189 1.4 blymn /* first check the field we are in is ok */ 190 1.4 blymn if (_formi_validate_field(form) != E_OK) 191 1.4 blymn return E_INVALID_FIELD; 192 1.4 blymn 193 1.4 blymn if (form->field_term != NULL) 194 1.4 blymn form->field_term(form); 195 1.4 blymn 196 1.4 blymn /* 197 1.4 blymn * if we have a page movement then the form term 198 1.4 blymn * needs to be called too 199 1.4 blymn */ 200 1.4 blymn if ((c <= REQ_LAST_PAGE) && (form->form_term != NULL)) 201 1.4 blymn form->form_term(form); 202 1.4 blymn } 203 1.4 blymn 204 1.4 blymn 205 1.1 blymn switch (c) { 206 1.1 blymn case REQ_NEXT_PAGE: 207 1.1 blymn if (form->page < form->max_page) { 208 1.1 blymn old_page = form->page; 209 1.1 blymn form->page++; 210 1.1 blymn update_page = 1; 211 1.1 blymn if (_formi_pos_first_field(form) != E_OK) { 212 1.1 blymn form->page = old_page; 213 1.4 blymn status = E_REQUEST_DENIED; 214 1.1 blymn } 215 1.1 blymn } else 216 1.4 blymn status = E_REQUEST_DENIED; 217 1.1 blymn break; 218 1.1 blymn 219 1.1 blymn case REQ_PREV_PAGE: 220 1.1 blymn if (form->page > 0) { 221 1.1 blymn old_page = form->page; 222 1.1 blymn form->page--; 223 1.1 blymn update_page = 1; 224 1.1 blymn if (_formi_pos_first_field(form) != E_OK) { 225 1.1 blymn form->page = old_page; 226 1.4 blymn status = E_REQUEST_DENIED; 227 1.1 blymn } 228 1.1 blymn } else 229 1.4 blymn status = E_REQUEST_DENIED; 230 1.1 blymn break; 231 1.1 blymn 232 1.1 blymn case REQ_FIRST_PAGE: 233 1.1 blymn old_page = form->page; 234 1.1 blymn form->page = 0; 235 1.1 blymn update_page = 1; 236 1.1 blymn if (_formi_pos_first_field(form) != E_OK) { 237 1.1 blymn form->page = old_page; 238 1.4 blymn status = E_REQUEST_DENIED; 239 1.1 blymn } 240 1.1 blymn break; 241 1.1 blymn 242 1.1 blymn case REQ_LAST_PAGE: 243 1.1 blymn old_page = form->page; 244 1.1 blymn form->page = form->max_page - 1; 245 1.1 blymn update_page = 1; 246 1.1 blymn if (_formi_pos_first_field(form) != E_OK) { 247 1.1 blymn form->page = old_page; 248 1.4 blymn status = E_REQUEST_DENIED; 249 1.1 blymn } 250 1.1 blymn break; 251 1.1 blymn 252 1.1 blymn case REQ_NEXT_FIELD: 253 1.1 blymn status = _formi_pos_new_field(form, _FORMI_FORWARD, 254 1.1 blymn FALSE); 255 1.1 blymn update_field = 1; 256 1.1 blymn break; 257 1.1 blymn 258 1.1 blymn case REQ_PREV_FIELD: 259 1.1 blymn status = _formi_pos_new_field(form, _FORMI_BACKWARD, 260 1.1 blymn FALSE); 261 1.1 blymn update_field = 1; 262 1.1 blymn break; 263 1.1 blymn 264 1.1 blymn case REQ_FIRST_FIELD: 265 1.1 blymn form->cur_field = 0; 266 1.1 blymn update_field = 1; 267 1.1 blymn break; 268 1.1 blymn 269 1.1 blymn case REQ_LAST_FIELD: 270 1.1 blymn form->cur_field = form->field_count - 1; 271 1.1 blymn update_field = 1; 272 1.1 blymn break; 273 1.1 blymn 274 1.1 blymn case REQ_SNEXT_FIELD: 275 1.1 blymn status = _formi_pos_new_field(form, _FORMI_FORWARD, 276 1.1 blymn TRUE); 277 1.1 blymn update_field = 1; 278 1.1 blymn break; 279 1.1 blymn 280 1.1 blymn case REQ_SPREV_FIELD: 281 1.1 blymn status = _formi_pos_new_field(form, _FORMI_BACKWARD, 282 1.1 blymn TRUE); 283 1.1 blymn update_field = 1; 284 1.1 blymn break; 285 1.1 blymn 286 1.1 blymn case REQ_SFIRST_FIELD: 287 1.18 christos fieldp = TAILQ_FIRST(&form->sorted_fields); 288 1.1 blymn form->cur_field = fieldp->index; 289 1.1 blymn update_field = 1; 290 1.1 blymn break; 291 1.1 blymn 292 1.1 blymn case REQ_SLAST_FIELD: 293 1.18 christos fieldp = TAILQ_LAST(&form->sorted_fields, 294 1.18 christos _formi_sort_head); 295 1.1 blymn form->cur_field = fieldp->index; 296 1.1 blymn update_field = 1; 297 1.1 blymn break; 298 1.1 blymn 299 1.1 blymn /* 300 1.1 blymn * The up, down, left and right field traversals 301 1.1 blymn * are rolled up into a single function, allow a 302 1.1 blymn * fall through to that function. 303 1.1 blymn */ 304 1.1 blymn case REQ_LEFT_FIELD: 305 1.1 blymn case REQ_RIGHT_FIELD: 306 1.1 blymn case REQ_UP_FIELD: 307 1.1 blymn case REQ_DOWN_FIELD: 308 1.1 blymn status = traverse_form_links(form, c); 309 1.1 blymn update_field = 1; 310 1.1 blymn break; 311 1.1 blymn 312 1.1 blymn /* the following commands modify the buffer, check if 313 1.1 blymn this is allowed first before falling through. */ 314 1.16 blymn 315 1.3 blymn case REQ_DEL_PREV: 316 1.3 blymn /* 317 1.3 blymn * need to check for the overloading of this 318 1.3 blymn * request. If overload flag set and we are 319 1.3 blymn * at the start of field this request turns 320 1.3 blymn * into a previous field request. Otherwise 321 1.3 blymn * fallthrough to the field handler. 322 1.3 blymn */ 323 1.3 blymn if ((form->opts & O_BS_OVERLOAD) == O_BS_OVERLOAD) { 324 1.3 blymn if ((fieldp->start_char == 0) && 325 1.3 blymn (fieldp->start_line == 0) && 326 1.13 blymn (fieldp->row_xpos == 0)) { 327 1.3 blymn update_field = 328 1.3 blymn _formi_manipulate_field(form, 329 1.3 blymn REQ_PREV_FIELD); 330 1.3 blymn break; 331 1.3 blymn } 332 1.3 blymn } 333 1.3 blymn 334 1.3 blymn /* FALLTHROUGH */ 335 1.3 blymn case REQ_NEW_LINE: 336 1.3 blymn /* 337 1.3 blymn * need to check for the overloading of this 338 1.3 blymn * request. If overload flag set and we are 339 1.3 blymn * at the start of field this request turns 340 1.3 blymn * into a next field request. Otherwise 341 1.3 blymn * fallthrough to the field handler. 342 1.3 blymn */ 343 1.3 blymn if ((form->opts & O_NL_OVERLOAD) == O_NL_OVERLOAD) { 344 1.3 blymn if ((fieldp->start_char == 0) && 345 1.3 blymn (fieldp->start_line == 0) && 346 1.13 blymn (fieldp->row_xpos == 0)) { 347 1.3 blymn update_field = 348 1.3 blymn _formi_manipulate_field(form, 349 1.3 blymn REQ_NEXT_FIELD); 350 1.3 blymn break; 351 1.3 blymn } 352 1.3 blymn } 353 1.3 blymn 354 1.3 blymn /* FALLTHROUGH */ 355 1.1 blymn case REQ_INS_CHAR: 356 1.1 blymn case REQ_INS_LINE: 357 1.1 blymn case REQ_DEL_CHAR: 358 1.1 blymn case REQ_DEL_LINE: 359 1.1 blymn case REQ_DEL_WORD: 360 1.1 blymn case REQ_CLR_EOL: 361 1.1 blymn case REQ_CLR_EOF: 362 1.1 blymn case REQ_CLR_FIELD: 363 1.1 blymn case REQ_OVL_MODE: 364 1.1 blymn case REQ_INS_MODE: 365 1.1 blymn /* check if we are allowed to edit the field and fall 366 1.1 blymn * through if we are. 367 1.1 blymn */ 368 1.1 blymn if ((form->fields[form->cur_field]->opts & O_EDIT) != O_EDIT) 369 1.1 blymn return E_REQUEST_DENIED; 370 1.1 blymn 371 1.1 blymn /* the following manipulate the field contents, bundle 372 1.1 blymn them into one function.... */ 373 1.1 blymn /* FALLTHROUGH */ 374 1.1 blymn case REQ_NEXT_CHAR: 375 1.1 blymn case REQ_PREV_CHAR: 376 1.1 blymn case REQ_NEXT_LINE: 377 1.1 blymn case REQ_PREV_LINE: 378 1.1 blymn case REQ_NEXT_WORD: 379 1.1 blymn case REQ_PREV_WORD: 380 1.1 blymn case REQ_BEG_FIELD: 381 1.1 blymn case REQ_END_FIELD: 382 1.1 blymn case REQ_BEG_LINE: 383 1.1 blymn case REQ_END_LINE: 384 1.1 blymn case REQ_LEFT_CHAR: 385 1.1 blymn case REQ_RIGHT_CHAR: 386 1.1 blymn case REQ_UP_CHAR: 387 1.1 blymn case REQ_DOWN_CHAR: 388 1.1 blymn case REQ_SCR_FLINE: 389 1.1 blymn case REQ_SCR_BLINE: 390 1.1 blymn case REQ_SCR_FPAGE: 391 1.1 blymn case REQ_SCR_BPAGE: 392 1.1 blymn case REQ_SCR_FHPAGE: 393 1.1 blymn case REQ_SCR_BHPAGE: 394 1.1 blymn case REQ_SCR_FCHAR: 395 1.1 blymn case REQ_SCR_BCHAR: 396 1.1 blymn case REQ_SCR_HFLINE: 397 1.1 blymn case REQ_SCR_HBLINE: 398 1.1 blymn case REQ_SCR_HFHALF: 399 1.1 blymn case REQ_SCR_HBHALF: 400 1.1 blymn update_field = _formi_manipulate_field(form, c); 401 1.1 blymn break; 402 1.1 blymn 403 1.1 blymn case REQ_VALIDATION: 404 1.1 blymn return _formi_validate_field(form); 405 1.1 blymn /* NOTREACHED */ 406 1.1 blymn break; 407 1.1 blymn 408 1.1 blymn case REQ_PREV_CHOICE: 409 1.1 blymn case REQ_NEXT_CHOICE: 410 1.1 blymn update_field = _formi_field_choice(form, c); 411 1.13 blymn /* reinit the cursor pos just in case */ 412 1.13 blymn if (update_field == 1) { 413 1.13 blymn _formi_init_field_xpos(fieldp); 414 1.13 blymn fieldp->row_xpos = 0; 415 1.13 blymn } 416 1.1 blymn break; 417 1.1 blymn 418 1.1 blymn default: /* should not need to do this, but.... */ 419 1.1 blymn return E_UNKNOWN_COMMAND; 420 1.1 blymn /* NOTREACHED */ 421 1.1 blymn break; 422 1.1 blymn } 423 1.4 blymn } 424 1.4 blymn 425 1.4 blymn /* call the field and form init functions if required. */ 426 1.4 blymn if ((c >= REQ_NEXT_PAGE) && (c <= REQ_DOWN_FIELD)) { 427 1.4 blymn if (form->field_init != NULL) 428 1.4 blymn form->field_init(form); 429 1.4 blymn 430 1.4 blymn /* 431 1.4 blymn * if we have a page movement then the form init 432 1.4 blymn * needs to be called too 433 1.4 blymn */ 434 1.4 blymn if ((c <= REQ_LAST_PAGE) && (form->form_init != NULL)) 435 1.4 blymn form->form_init(form); 436 1.4 blymn 437 1.4 blymn /* 438 1.4 blymn * if there was an error just return now... 439 1.4 blymn */ 440 1.4 blymn if (status != E_OK) 441 1.4 blymn return status; 442 1.5 blymn 443 1.5 blymn /* if we have no error, reset the various offsets */ 444 1.5 blymn fieldp = form->fields[form->cur_field]; 445 1.5 blymn fieldp->start_char = 0; 446 1.17 roy fieldp->start_line = fieldp->alines; 447 1.17 roy fieldp->cur_line = fieldp->alines; 448 1.12 blymn fieldp->row_xpos = 0; 449 1.5 blymn fieldp->cursor_ypos = 0; 450 1.13 blymn _formi_init_field_xpos(fieldp); 451 1.1 blymn } 452 1.1 blymn 453 1.1 blymn if (update_field < 0) 454 1.1 blymn return update_field; 455 1.1 blymn 456 1.1 blymn if (update_field == 1) 457 1.1 blymn update_page |= _formi_update_field(form, old_field); 458 1.1 blymn 459 1.1 blymn if (update_page == 1) 460 1.1 blymn _formi_draw_page(form); 461 1.1 blymn 462 1.1 blymn pos_form_cursor(form); 463 1.14 blymn 464 1.14 blymn if ((update_page == 1) || (update_field == 1)) 465 1.14 blymn wrefresh(form->scrwin); 466 1.14 blymn 467 1.1 blymn return E_OK; 468 1.1 blymn } 469