1 /* $NetBSD: internals.c,v 1.14 2001/04/06 05:03:22 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 withough 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 <stdio.h> 34 #include <stdlib.h> 35 #include <strings.h> 36 #include "internals.h" 37 #include "form.h" 38 39 #ifdef DEBUG 40 /* 41 * file handle to write debug info to, this will be initialised when 42 * the form is first posted. 43 */ 44 FILE *dbg = NULL; 45 #endif 46 47 /* define our own min function - this is not generic but will do here 48 * (don't believe me? think about what value you would get 49 * from min(x++, y++) 50 */ 51 #define min(a,b) (((a) > (b))? (b) : (a)) 52 53 /* for the line joining function... */ 54 #define JOIN_NEXT 1 55 #define JOIN_NEXT_NW 2 /* next join, don't wrap the joined line */ 56 #define JOIN_PREV 3 57 #define JOIN_PREV_NW 4 /* previous join, don't wrap the joined line */ 58 59 static void 60 _formi_do_char_validation(FIELD *field, FIELDTYPE *type, char c, int *ret_val); 61 static void 62 _formi_do_validation(FIELD *field, FIELDTYPE *type, int *ret_val); 63 static int 64 _formi_join_line(FIELD *field, char *str, unsigned int pos, int direction); 65 void 66 _formi_hscroll_back(FIELD *field, unsigned int amt); 67 void 68 _formi_hscroll_fwd(FIELD *field, unsigned int amt); 69 static void 70 _formi_scroll_back(FIELD *field, unsigned int amt); 71 static void 72 _formi_scroll_fwd(FIELD *field, unsigned int amt); 73 static int 74 find_sow(char *str, unsigned int offset); 75 static int 76 find_cur_line(FIELD *cur); 77 78 /* 79 * Open the debug file if it is not already open.... 80 */ 81 #ifdef DEBUG 82 int 83 _formi_create_dbg_file(void) 84 { 85 if (dbg == NULL) { 86 dbg = fopen("___form_dbg.out", "w"); 87 if (dbg == NULL) { 88 fprintf(stderr, "Cannot open debug file!\n"); 89 return E_SYSTEM_ERROR; 90 } 91 } 92 93 return E_OK; 94 } 95 #endif 96 97 /* 98 * Set the form's current field to the first valid field on the page. 99 * Assume the fields have been sorted and stitched. 100 */ 101 int 102 _formi_pos_first_field(FORM *form) 103 { 104 FIELD *cur; 105 int old_page; 106 107 old_page = form->page; 108 109 /* scan forward for an active page....*/ 110 while (form->page_starts[form->page].in_use == 0) { 111 form->page++; 112 if (form->page > form->max_page) { 113 form->page = old_page; 114 return E_REQUEST_DENIED; 115 } 116 } 117 118 /* then scan for a field we can use */ 119 cur = form->fields[form->page_starts[form->page].first]; 120 while ((cur->opts & (O_VISIBLE | O_ACTIVE)) 121 != (O_VISIBLE | O_ACTIVE)) { 122 cur = CIRCLEQ_NEXT(cur, glue); 123 if (cur == (void *) &form->sorted_fields) { 124 form->page = old_page; 125 return E_REQUEST_DENIED; 126 } 127 } 128 129 form->cur_field = cur->index; 130 return E_OK; 131 } 132 133 /* 134 * Set the field to the next active and visible field, the fields are 135 * traversed in index order in the direction given. If the parameter 136 * use_sorted is TRUE then the sorted field list will be traversed instead 137 * of using the field index. 138 */ 139 int 140 _formi_pos_new_field(FORM *form, unsigned direction, unsigned use_sorted) 141 { 142 FIELD *cur; 143 int i; 144 145 i = form->cur_field; 146 cur = form->fields[i]; 147 148 do { 149 if (direction == _FORMI_FORWARD) { 150 if (use_sorted == TRUE) { 151 if ((form->wrap == FALSE) && 152 (cur == CIRCLEQ_LAST(&form->sorted_fields))) 153 return E_REQUEST_DENIED; 154 cur = CIRCLEQ_NEXT(cur, glue); 155 i = cur->index; 156 } else { 157 if ((form->wrap == FALSE) && 158 ((i + 1) >= form->field_count)) 159 return E_REQUEST_DENIED; 160 i++; 161 if (i >= form->field_count) 162 i = 0; 163 } 164 } else { 165 if (use_sorted == TRUE) { 166 if ((form->wrap == FALSE) && 167 (cur == CIRCLEQ_FIRST(&form->sorted_fields))) 168 return E_REQUEST_DENIED; 169 cur = CIRCLEQ_PREV(cur, glue); 170 i = cur->index; 171 } else { 172 if ((form->wrap == FALSE) && (i <= 0)) 173 return E_REQUEST_DENIED; 174 i--; 175 if (i < 0) 176 i = form->field_count - 1; 177 } 178 } 179 180 if ((form->fields[i]->opts & (O_VISIBLE | O_ACTIVE)) 181 == (O_VISIBLE | O_ACTIVE)) { 182 form->cur_field = i; 183 return E_OK; 184 } 185 } 186 while (i != form->cur_field); 187 188 return E_REQUEST_DENIED; 189 } 190 191 /* 192 * Find the line in a field that the cursor is currently on. 193 */ 194 static int 195 find_cur_line(FIELD *cur) 196 { 197 unsigned start, end, pos, row; 198 const char *str; 199 200 str = cur->buffers[0].string; 201 pos = cur->start_char + cur->cursor_xpos; 202 203 start = 0; 204 end = 0; 205 206 for (row = 1; row < cur->row_count; row++) { 207 start = _formi_find_bol(str, start); 208 end = _formi_find_eol(str, end); 209 if ((pos >= start) && (pos <= end)) 210 return row; 211 } 212 213 return 0; 214 } 215 216 217 /* 218 * Word wrap the contents of the field's buffer 0 if this is allowed. 219 * If the wrap is successful, that is, the row count nor the buffer 220 * size is exceeded then the function will return E_OK, otherwise it 221 * will return E_REQUEST_DENIED. 222 */ 223 int 224 _formi_wrap_field(FIELD *field, unsigned int pos) 225 { 226 char *str, *new; 227 int width, length, allocated, row_count, sol, eol, wrapped; 228 size_t new_size; 229 230 wrapped = FALSE; 231 row_count = 0; 232 allocated = field->buffers[0].allocated; 233 length = field->buffers[0].length; 234 if ((str = (char *) malloc(sizeof(char) * allocated)) == NULL) 235 return E_SYSTEM_ERROR; 236 237 strcpy(str,field->buffers[0].string); 238 239 if ((field->opts & O_STATIC) == O_STATIC) { 240 width = field->cols + 1; 241 if ((field->rows + field->nrows) == 1) 242 return E_OK; /* cannot wrap a single line */ 243 } else { 244 if ((field->drows + field->nrows) == 1) 245 return E_OK; 246 width = field->dcols; 247 } 248 249 while (str[pos] != '\0') { 250 row_count++; 251 sol = _formi_find_bol(str, pos); 252 eol = _formi_find_eol(str, pos); 253 if ((eol - sol) <= width) { 254 /* line may be too short, try joining some lines */ 255 pos = eol; 256 if ((eol - sol) == width) { 257 /* if line is just right then don't wrap */ 258 pos++; 259 continue; 260 } 261 262 if (_formi_join_line(field, str, pos, JOIN_NEXT_NW) 263 == E_OK) { 264 row_count--; /* cuz we just joined a line */ 265 wrapped = TRUE; 266 } else 267 break; 268 } else { 269 /* line is too long, split it - maybe */ 270 /* split on first whitespace before current word */ 271 pos = sol + width; 272 if ((!isblank(str[pos])) && 273 ((field->opts & O_WRAP) == O_WRAP)) 274 pos = find_sow(str, pos); 275 276 if (pos != sol) { 277 if (length + 1 >= allocated) { 278 new_size = allocated + 64 279 - (allocated % 64); 280 281 if ((new = (char *) realloc(str, 282 sizeof(char) * new_size) 283 ) == NULL) { 284 free(str); 285 return E_SYSTEM_ERROR; 286 } 287 str = new; 288 allocated = new_size; 289 } 290 291 bcopy(&str[pos], &str[pos + 1], 292 (unsigned) length - pos - 1); 293 str[pos] = '\n'; 294 pos = pos + 1; 295 length++; 296 wrapped = TRUE; 297 } else 298 break; 299 } 300 } 301 302 if (row_count > field->rows) { 303 free(str); 304 return E_REQUEST_DENIED; 305 } 306 307 if (wrapped == TRUE) { 308 field->buffers[0].length = length; 309 field->buffers[0].allocated = allocated; 310 free(field->buffers[0].string); 311 field->buffers[0].string = str; 312 } else /* all that work was in vain.... */ 313 free(str); 314 315 return E_OK; 316 } 317 318 /* 319 * Join the two lines that surround the location pos, the type 320 * variable indicates the direction of the join. Note that pos is 321 * assumed to be at either the end of the line for a JOIN_NEXT or at 322 * the beginning of the line for a JOIN_PREV. We need to check the 323 * field options to ensure the join does not overflow the line limit 324 * (if wrap is off) or wrap the field buffer again. Returns E_OK if 325 * the join was successful or E_REQUEST_DENIED if the join cannot 326 * happen. 327 */ 328 static int 329 _formi_join_line(FIELD *field, char *str, unsigned int pos, int direction) 330 { 331 unsigned int len, eol, npos, start, dest; 332 333 npos = pos; 334 335 if ((direction == JOIN_NEXT) || (direction == JOIN_NEXT_NW)) { 336 npos++; 337 /* see if there is another line following... */ 338 if (str[npos] == '\0') 339 return E_REQUEST_DENIED; 340 eol = _formi_find_eol(str, npos); 341 342 start = npos; 343 dest = pos; 344 len = eol - npos; 345 } else { 346 if (pos == 0) 347 return E_REQUEST_DENIED; 348 eol = _formi_find_eol(str, pos); 349 npos--; 350 start = pos; 351 dest = npos; 352 len = eol - pos; 353 } 354 355 356 bcopy(&str[start], &str[dest], (unsigned) len); 357 358 /* wrap the field if required, if this fails undo the change */ 359 if ((direction == JOIN_NEXT) || (direction == JOIN_PREV)) { 360 if (_formi_wrap_field(field, (unsigned int) pos) != E_OK) { 361 bcopy(&str[dest], &str[start], (unsigned) len); 362 str[dest] = '\n'; 363 return E_REQUEST_DENIED; 364 } 365 } 366 367 return E_OK; 368 } 369 370 /* 371 * skip the blanks in the given string, start at the index start and 372 * continue forward until either the end of the string or a non-blank 373 * character is found. Return the index of either the end of the string or 374 * the first non-blank character. 375 */ 376 unsigned 377 _formi_skip_blanks(char *string, unsigned int start) 378 { 379 unsigned int i; 380 381 i = start; 382 383 while ((string[i] != '\0') && isblank(string[i])) 384 i++; 385 386 return i; 387 } 388 389 /* 390 * Return the index of the top left most field of the two given fields. 391 */ 392 static int 393 _formi_top_left(FORM *form, int a, int b) 394 { 395 /* lower row numbers always win here.... */ 396 if (form->fields[a]->form_row < form->fields[b]->form_row) 397 return a; 398 399 if (form->fields[a]->form_row > form->fields[b]->form_row) 400 return b; 401 402 /* rows must be equal, check columns */ 403 if (form->fields[a]->form_col < form->fields[b]->form_col) 404 return a; 405 406 if (form->fields[a]->form_col > form->fields[b]->form_col) 407 return b; 408 409 /* if we get here fields must be in exactly the same place, punt */ 410 return a; 411 } 412 413 /* 414 * Return the index to the field that is the bottom-right-most of the 415 * two given fields. 416 */ 417 static int 418 _formi_bottom_right(FORM *form, int a, int b) 419 { 420 /* check the rows first, biggest row wins */ 421 if (form->fields[a]->form_row > form->fields[b]->form_row) 422 return a; 423 if (form->fields[a]->form_row < form->fields[b]->form_row) 424 return b; 425 426 /* rows must be equal, check cols, biggest wins */ 427 if (form->fields[a]->form_col > form->fields[b]->form_col) 428 return a; 429 if (form->fields[a]->form_col < form->fields[b]->form_col) 430 return b; 431 432 /* fields in the same place, punt */ 433 return a; 434 } 435 436 /* 437 * Find the next '\n' character in the given string starting at offset 438 * if there are no newlines found then return the index to the end of the 439 * string. 440 */ 441 int 442 _formi_find_eol(const char *string, unsigned int offset) 443 { 444 char *location; 445 int eol; 446 447 if ((location = index(&string[offset], '\n')) != NULL) 448 eol = location - string; 449 else 450 eol = strlen(string); 451 452 if (eol > 0) 453 eol--; 454 455 return eol; 456 } 457 458 /* 459 * Find the previous '\n' character in the given string starting at offset 460 * if there are no newlines found then return 0. 461 */ 462 int 463 _formi_find_bol(const char *string, unsigned int offset) 464 { 465 int cnt; 466 467 cnt = offset; 468 while ((cnt > 0) && (string[cnt] != '\n')) 469 cnt--; 470 471 /* if we moved and found a newline go forward one to point at the 472 * actual start of the line.... 473 */ 474 if ((cnt != offset) && (string[cnt] == '\n')) 475 cnt++; 476 477 return cnt; 478 } 479 480 /* 481 * Find the end of the current word in the string str, starting at 482 * offset - the end includes any trailing whitespace. If the end of 483 * the string is found before a new word then just return the offset 484 * to the end of the string. 485 */ 486 static int 487 find_eow(char *str, unsigned int offset) 488 { 489 int start; 490 491 start = offset; 492 /* first skip any non-whitespace */ 493 while ((str[start] != '\0') && !isblank(str[start])) 494 start++; 495 496 /* see if we hit the end of the string */ 497 if (str[start] == '\0') 498 return start; 499 500 /* otherwise skip the whitespace.... */ 501 while ((str[start] != '\0') && isblank(str[start])) 502 start++; 503 504 return start; 505 } 506 507 /* 508 * Find the beginning of the current word in the string str, starting 509 * at offset. 510 */ 511 static int 512 find_sow(char *str, unsigned int offset) 513 { 514 int start; 515 516 start = offset; 517 518 if (start > 0) { 519 if (isblank(str[start]) || isblank(str[start - 1])) { 520 if (isblank(str[start - 1])) 521 start--; 522 /* skip the whitespace.... */ 523 while ((start >= 0) && isblank(str[start])) 524 start--; 525 } 526 } 527 528 /* see if we hit the start of the string */ 529 if (start < 0) 530 return 0; 531 532 /* now skip any non-whitespace */ 533 while ((start >= 0) && !isblank(str[start])) 534 start--; 535 536 if (start > 0) 537 start++; /* last loop has us pointing at a space, adjust */ 538 539 if (start < 0) 540 start = 0; 541 542 return start; 543 } 544 545 /* 546 * Scroll the field forward the given number of lines. 547 */ 548 static void 549 _formi_scroll_fwd(FIELD *field, unsigned int amt) 550 { 551 /* check if we have lines to scroll */ 552 if (field->row_count < (field->start_line + field->rows)) 553 return; 554 555 field->start_line += min(amt, 556 field->row_count - field->start_line 557 - field->rows); 558 } 559 560 /* 561 * Scroll the field backward the given number of lines. 562 */ 563 static void 564 _formi_scroll_back(FIELD *field, unsigned int amt) 565 { 566 if (field->start_line == 0) 567 return; 568 569 field->start_line -= min(field->start_line, amt); 570 } 571 572 /* 573 * Scroll the field forward the given number of characters. 574 */ 575 void 576 _formi_hscroll_fwd(FIELD *field, int unsigned amt) 577 { 578 int end, scroll_amt; 579 580 end = _formi_find_eol(field->buffers[0].string, 581 field->start_char + field->cursor_xpos) 582 - field->start_char - field->cursor_xpos; 583 584 scroll_amt = min(amt, end); 585 if (scroll_amt < 0) 586 scroll_amt = 0; 587 588 field->start_char += scroll_amt; 589 } 590 591 /* 592 * Scroll the field backward the given number of characters. 593 */ 594 void 595 _formi_hscroll_back(FIELD *field, unsigned int amt) 596 { 597 field->start_char -= min(field->start_char, amt); 598 } 599 600 /* 601 * Find the different pages in the form fields and assign the form 602 * page_starts array with the information to find them. 603 */ 604 int 605 _formi_find_pages(FORM *form) 606 { 607 int i, cur_page = 0; 608 609 if ((form->page_starts = (_FORMI_PAGE_START *) 610 malloc((form->max_page + 1) * sizeof(_FORMI_PAGE_START))) == NULL) 611 return E_SYSTEM_ERROR; 612 613 /* initialise the page starts array */ 614 memset(form->page_starts, 0, 615 (form->max_page + 1) * sizeof(_FORMI_PAGE_START)); 616 617 for (i =0; i < form->field_count; i++) { 618 if (form->fields[i]->page_break == 1) 619 cur_page++; 620 if (form->page_starts[cur_page].in_use == 0) { 621 form->page_starts[cur_page].in_use = 1; 622 form->page_starts[cur_page].first = i; 623 form->page_starts[cur_page].last = i; 624 form->page_starts[cur_page].top_left = i; 625 form->page_starts[cur_page].bottom_right = i; 626 } else { 627 form->page_starts[cur_page].last = i; 628 form->page_starts[cur_page].top_left = 629 _formi_top_left(form, 630 form->page_starts[cur_page].top_left, 631 i); 632 form->page_starts[cur_page].bottom_right = 633 _formi_bottom_right(form, 634 form->page_starts[cur_page].bottom_right, 635 i); 636 } 637 } 638 639 return E_OK; 640 } 641 642 /* 643 * Completely redraw the field of the given form. 644 */ 645 void 646 _formi_redraw_field(FORM *form, int field) 647 { 648 unsigned int pre, post, flen, slen, i, row, start, end; 649 char *str; 650 FIELD *cur; 651 #ifdef DEBUG 652 char buffer[100]; 653 #endif 654 655 cur = form->fields[field]; 656 str = cur->buffers[0].string; 657 flen = cur->cols; 658 slen = 0; 659 start = 0; 660 end = 0; 661 662 wmove(form->scrwin, (int) cur->form_row, (int) cur->form_col); 663 for (row = 1; row <= cur->row_count; row++) { 664 if (str == NULL) { 665 start = end = 0; 666 } else { 667 if ((str[end] == '\0') || (str[end + 1] == '\0') 668 || (row == 1)) 669 start = end; 670 else 671 start = end + 1; 672 } 673 674 if (cur->buffers[0].length > 0) { 675 end = _formi_find_eol(str, start); 676 slen = end - start + 1; 677 } else 678 slen = 0; 679 680 if ((cur->opts & O_STATIC) == O_STATIC) { 681 switch (cur->justification) { 682 case JUSTIFY_RIGHT: 683 post = 0; 684 if (flen < slen) 685 pre = 0; 686 else 687 pre = flen - slen; 688 break; 689 690 case JUSTIFY_CENTER: 691 if (flen < slen) { 692 pre = 0; 693 post = 0; 694 } else { 695 pre = flen - slen; 696 post = pre = pre / 2; 697 /* get padding right if 698 centring is not even */ 699 if ((post + pre + slen) < flen) 700 post++; 701 } 702 break; 703 704 case NO_JUSTIFICATION: 705 case JUSTIFY_LEFT: 706 default: 707 pre = 0; 708 if (flen <= slen) 709 post = 0; 710 else { 711 post = flen - slen; 712 if (post > flen) 713 post = flen; 714 } 715 break; 716 } 717 } else { 718 /* dynamic fields are not justified */ 719 pre = 0; 720 if (flen <= slen) 721 post = 0; 722 else { 723 post = flen - slen; 724 if (post > flen) 725 post = flen; 726 } 727 728 /* but they do scroll.... */ 729 730 if (pre > cur->start_char - start) 731 pre = pre - cur->start_char + start; 732 else 733 pre = 0; 734 735 if (slen > cur->start_char) { 736 slen -= cur->start_char; 737 post += cur->start_char; 738 if (post > flen) 739 post = flen; 740 } else { 741 slen = 0; 742 post = flen - pre; 743 } 744 } 745 746 if (form->cur_field == field) 747 wattrset(form->scrwin, cur->fore); 748 else 749 wattrset(form->scrwin, cur->back); 750 751 #ifdef DEBUG 752 if (_formi_create_dbg_file() == E_OK) { 753 fprintf(dbg, 754 "redraw_field: start=%d, pre=%d, slen=%d, flen=%d, post=%d, start_char=%d\n", 755 start, pre, slen, flen, post, cur->start_char); 756 if (str != NULL) { 757 strncpy(buffer, &str[cur->start_char], flen); 758 } else { 759 strcpy(buffer, "(null)"); 760 } 761 buffer[flen] = '\0'; 762 fprintf(dbg, "redraw_field: %s\n", buffer); 763 } 764 #endif 765 766 for (i = start + cur->start_char; i < pre; i++) 767 waddch(form->scrwin, cur->pad); 768 769 #ifdef DEBUG 770 fprintf(dbg, "redraw_field: will add %d chars\n", 771 min(slen, flen)); 772 #endif 773 for (i = 0; i < min(slen, flen); i++) 774 { 775 #ifdef DEBUG 776 fprintf(dbg, "adding char str[%d]=%c\n", 777 i + cur->start_char, str[i + cur->start_char]); 778 #endif 779 if (((cur->opts & O_PUBLIC) != O_PUBLIC)) { 780 waddch(form->scrwin, cur->pad); 781 } else if ((cur->opts & O_VISIBLE) == O_VISIBLE) { 782 waddch(form->scrwin, str[i + cur->start_char]); 783 } else { 784 waddch(form->scrwin, ' '); 785 } 786 } 787 788 for (i = 0; i < post; i++) 789 waddch(form->scrwin, cur->pad); 790 } 791 792 return; 793 } 794 795 /* 796 * Display the fields attached to the form that are on the current page 797 * on the screen. 798 * 799 */ 800 int 801 _formi_draw_page(FORM *form) 802 { 803 int i; 804 805 if (form->page_starts[form->page].in_use == 0) 806 return E_BAD_ARGUMENT; 807 808 wclear(form->scrwin); 809 810 for (i = form->page_starts[form->page].first; 811 i <= form->page_starts[form->page].last; i++) 812 _formi_redraw_field(form, i); 813 814 return E_OK; 815 } 816 817 /* 818 * Add the character c at the position pos in buffer 0 of the given field 819 */ 820 int 821 _formi_add_char(FIELD *field, unsigned int pos, char c) 822 { 823 char *new; 824 unsigned int new_size; 825 int status; 826 827 /* 828 * If buffer has not had a string before, set it to a blank 829 * string. Everything should flow from there.... 830 */ 831 if (field->buffers[0].string == NULL) { 832 set_field_buffer(field, 0, ""); 833 } 834 835 if (_formi_validate_char(field, c) != E_OK) { 836 #ifdef DEBUG 837 fprintf(dbg, "add_char: char %c failed char validation\n", c); 838 #endif 839 return E_INVALID_FIELD; 840 } 841 842 #ifdef DEBUG 843 fprintf(dbg, "add_char: pos=%d, char=%c\n", pos, c); 844 fprintf(dbg, 845 "add_char enter: xpos=%d, start=%d, length=%d(%d), allocated=%d\n", 846 field->cursor_xpos, field->start_char, 847 field->buffers[0].length, strlen(field->buffers[0].string), 848 field->buffers[0].allocated); 849 fprintf(dbg, "add_char enter: %s\n", field->buffers[0].string); 850 fprintf(dbg, "add_char enter: buf0_status=%d\n", field->buf0_status); 851 #endif 852 if (((field->opts & O_BLANK) == O_BLANK) && 853 (field->buf0_status == FALSE) && 854 ((field->cursor_xpos + field->start_char) == 0)) { 855 field->buffers[0].length = 0; 856 field->buffers[0].string[0] = '\0'; 857 pos = 0; 858 field->start_char = 0; 859 field->start_line = 0; 860 field->row_count = 1; 861 field->cursor_xpos = 0; 862 field->cursor_ypos = 0; 863 } 864 865 866 if ((field->overlay == 0) 867 || ((field->overlay == 1) && (pos >= field->buffers[0].length))) { 868 /* first check if the field can have more chars...*/ 869 if ((((field->opts & O_STATIC) == O_STATIC) && 870 (field->buffers[0].length >= field->cols)) || 871 (((field->opts & O_STATIC) != O_STATIC) && 872 /*XXXXX this is wrong - should check max row or col */ ((field->max > 0) && 873 (field->buffers[0].length >= field->max)))) 874 return E_REQUEST_DENIED; 875 876 if (field->buffers[0].length + 1 877 >= field->buffers[0].allocated) { 878 new_size = field->buffers[0].allocated + 64 879 - (field->buffers[0].allocated % 64); 880 if ((new = (char *) realloc(field->buffers[0].string, 881 new_size )) == NULL) 882 return E_SYSTEM_ERROR; 883 field->buffers[0].allocated = new_size; 884 field->buffers[0].string = new; 885 } 886 } 887 888 if ((field->overlay == 0) && (field->buffers[0].length > pos)) { 889 bcopy(&field->buffers[0].string[pos], 890 &field->buffers[0].string[pos + 1], 891 field->buffers[0].length - pos + 1); 892 } 893 894 field->buffers[0].string[pos] = c; 895 if (pos >= field->buffers[0].length) { 896 /* make sure the string is terminated if we are at the 897 * end of the string, the terminator would be missing 898 * if we are are at the end of the field. 899 */ 900 field->buffers[0].string[pos + 1] = '\0'; 901 } 902 903 /* only increment the length if we are inserting characters 904 * OR if we are at the end of the field in overlay mode. 905 */ 906 if ((field->overlay == 0) 907 || ((field->overlay == 1) && (pos >= field->buffers[0].length))) 908 field->buffers[0].length++; 909 910 /* wrap the field, if needed */ 911 status = _formi_wrap_field(field, pos); 912 if (status != E_OK) { 913 /* wrap failed for some reason, back out the char insert */ 914 bcopy(&field->buffers[0].string[pos + 1], 915 &field->buffers[0].string[pos], 916 field->buffers[0].length - pos); 917 field->buffers[0].length--; 918 } else { 919 field->buf0_status = TRUE; 920 921 if ((field->cursor_xpos < (field->cols - 1)) || 922 ((field->opts & O_STATIC) != O_STATIC)) 923 field->cursor_xpos++; 924 925 if (field->cursor_xpos > field->cols) { 926 field->start_char++; 927 field->cursor_xpos = field->cols; 928 } 929 } 930 931 #ifdef DEBUG 932 fprintf(dbg, 933 "add_char exit: xpos=%d, start=%d, length=%d(%d), allocated=%d\n", 934 field->cursor_xpos, field->start_char, 935 field->buffers[0].length, strlen(field->buffers[0].string), 936 field->buffers[0].allocated); 937 fprintf(dbg,"add_char exit: %s\n", field->buffers[0].string); 938 fprintf(dbg, "add_char exit: buf0_status=%d\n", field->buf0_status); 939 fprintf(dbg, "add_char exit: status = %s\n", 940 (status == E_OK)? "OK" : "FAILED"); 941 #endif 942 return status; 943 } 944 945 /* 946 * Manipulate the text in a field, this takes the given form and performs 947 * the passed driver command on the current text field. Returns 1 if the 948 * text field was modified. 949 */ 950 int 951 _formi_manipulate_field(FORM *form, int c) 952 { 953 FIELD *cur; 954 char *str; 955 unsigned int i, start, end, pos; 956 957 cur = form->fields[form->cur_field]; 958 959 #ifdef DEBUG 960 fprintf(dbg, 961 "entry: xpos=%d, start_char=%d, length=%d, allocated=%d\n", 962 cur->cursor_xpos, cur->start_char, cur->buffers[0].length, 963 cur->buffers[0].allocated); 964 fprintf(dbg, "entry: string="); 965 if (cur->buffers[0].string == NULL) 966 fprintf(dbg, "(null)\n"); 967 else 968 fprintf(dbg, "\"%s\"\n", cur->buffers[0].string); 969 #endif 970 971 /* Cannot manipulate a null string! */ 972 if (cur->buffers[0].string == NULL) 973 return E_REQUEST_DENIED; 974 975 switch (c) { 976 case REQ_NEXT_CHAR: 977 /* for a dynamic field allow an offset of one more 978 * char so we can insert chars after end of string. 979 * Static fields cannot do this so deny request if 980 * cursor is at the end of the field. 981 */ 982 if (((cur->opts & O_STATIC) == O_STATIC) && 983 (cur->cursor_xpos == cur->cols - 1)) 984 return E_REQUEST_DENIED; 985 986 if ((cur->cursor_xpos + cur->start_char + 1) 987 > cur->buffers[0].length) 988 return E_REQUEST_DENIED; 989 990 cur->cursor_xpos++; 991 if (cur->cursor_xpos >= cur->cols - 1) { 992 cur->cursor_xpos = cur->cols - 1; 993 if ((cur->opts & O_STATIC) != O_STATIC) 994 cur->start_char++; 995 } 996 break; 997 998 case REQ_PREV_CHAR: 999 if (cur->cursor_xpos == 0) { 1000 if (cur->start_char > 0) 1001 cur->start_char--; 1002 else 1003 return E_REQUEST_DENIED; 1004 } else 1005 cur->cursor_xpos--; 1006 break; 1007 1008 case REQ_NEXT_LINE: 1009 cur->cursor_ypos++; 1010 if (cur->cursor_ypos > cur->rows) { 1011 if ((cur->opts & O_STATIC) == O_STATIC) { 1012 if (cur->start_line + cur->cursor_ypos 1013 > cur->drows) { 1014 cur->cursor_ypos--; 1015 return E_REQUEST_DENIED; 1016 } 1017 } else { 1018 if (cur->start_line + cur->cursor_ypos 1019 > cur->nrows + cur->rows) { 1020 cur->cursor_ypos--; 1021 return E_REQUEST_DENIED; 1022 } 1023 } 1024 cur->start_line++; 1025 } 1026 break; 1027 1028 case REQ_PREV_LINE: 1029 if (cur->cursor_ypos == 0) { 1030 if (cur->start_line == 0) 1031 return E_REQUEST_DENIED; 1032 cur->start_line--; 1033 } else 1034 cur->cursor_ypos--; 1035 break; 1036 1037 case REQ_NEXT_WORD: 1038 start = cur->start_char + cur->cursor_xpos; 1039 str = cur->buffers[0].string; 1040 1041 start = find_eow(str, start); 1042 1043 /* check if we hit the end */ 1044 if (str[start] == '\0') 1045 return E_REQUEST_DENIED; 1046 1047 /* otherwise we must have found the start of a word...*/ 1048 if (start - cur->start_char < cur->cols) { 1049 cur->cursor_xpos = start; 1050 } else { 1051 cur->start_char = start; 1052 cur->cursor_xpos = 0; 1053 } 1054 break; 1055 1056 case REQ_PREV_WORD: 1057 start = cur->start_char + cur->cursor_xpos; 1058 if (cur->start_char > 0) 1059 start--; 1060 1061 if (start == 0) 1062 return E_REQUEST_DENIED; 1063 1064 str = cur->buffers[0].string; 1065 1066 start = find_sow(str, start); 1067 1068 if (start - cur->start_char > 0) { 1069 cur->cursor_xpos = start; 1070 } else { 1071 cur->start_char = start; 1072 cur->cursor_xpos = 0; 1073 } 1074 break; 1075 1076 case REQ_BEG_FIELD: 1077 cur->start_char = 0; 1078 cur->start_line = 0; 1079 cur->cursor_xpos = 0; 1080 cur->cursor_ypos = 0; 1081 break; 1082 1083 case REQ_BEG_LINE: 1084 start = cur->start_char + cur->cursor_xpos; 1085 if (cur->buffers[0].string[start] == '\n') { 1086 if (start > 0) 1087 start--; 1088 else 1089 return E_REQUEST_DENIED; 1090 } 1091 1092 while ((start > 0) 1093 && (cur->buffers[0].string[start] != '\n')) 1094 start--; 1095 1096 if (start > 0) 1097 start++; 1098 1099 cur->start_char = start; 1100 cur->cursor_xpos = 0; 1101 break; 1102 1103 case REQ_END_FIELD: 1104 if (cur->row_count > cur->rows) { 1105 cur->start_line = cur->row_count - cur->rows; 1106 cur->cursor_ypos = cur->rows - 1; 1107 } else { 1108 cur->start_line = 0; 1109 cur->cursor_ypos = cur->row_count - 1; 1110 } 1111 1112 if ((str = rindex(cur->buffers[0].string, '\n')) == NULL) { 1113 cur->start_char = 0; 1114 1115 } else { 1116 cur->start_char = (str - cur->buffers[0].string) + 1; 1117 } 1118 1119 cur->cursor_xpos = 0; 1120 /* we fall through here deliberately, we are on the 1121 * correct row, now we need to get to the end of the 1122 * line. 1123 */ 1124 /* FALLTHRU */ 1125 1126 case REQ_END_LINE: 1127 start = cur->start_char + cur->cursor_xpos; 1128 end = _formi_find_eol(cur->buffers[0].string, start); 1129 start = _formi_find_bol(cur->buffers[0].string, start); 1130 1131 if (end - start > cur->cols - 1) { 1132 cur->cursor_xpos = cur->cols - 1; 1133 cur->start_char = end - cur->cols; 1134 if ((cur->opts & O_STATIC) != O_STATIC) 1135 cur->start_char++; 1136 } else { 1137 cur->cursor_xpos = end - start + 1; 1138 if (((cur->opts & O_STATIC) == O_STATIC) && 1139 ((end - start) == (cur->cols - 1))) 1140 cur->cursor_xpos--; 1141 1142 cur->start_char = start; 1143 } 1144 break; 1145 1146 case REQ_LEFT_CHAR: 1147 if ((cur->cursor_xpos == 0) && (cur->start_char == 0)) 1148 return E_REQUEST_DENIED; 1149 1150 if (cur->cursor_xpos == 0) { 1151 cur->start_char--; 1152 if (cur->buffers[0].string[cur->start_char] == '\n') { 1153 if ((cur->cursor_ypos == 0) && 1154 (cur->start_line == 0)) 1155 { 1156 cur->start_char++; 1157 return E_REQUEST_DENIED; 1158 } 1159 1160 if (cur->cursor_ypos == 0) 1161 cur->start_line--; 1162 else 1163 cur->cursor_ypos--; 1164 1165 end = _formi_find_eol(cur->buffers[0].string, 1166 cur->start_char); 1167 start = _formi_find_bol(cur->buffers[0].string, 1168 cur->start_char); 1169 if (end - start >= cur->cols) { 1170 cur->cursor_xpos = cur->cols - 1; 1171 cur->start_char = end - cur->cols; 1172 } else { 1173 cur->cursor_xpos = end - start; 1174 cur->start_char = start; 1175 } 1176 } 1177 } else 1178 cur->cursor_xpos--; 1179 break; 1180 1181 case REQ_RIGHT_CHAR: 1182 pos = cur->start_char + cur->cursor_xpos; 1183 if (cur->buffers[0].string[pos] == '\0') 1184 return E_REQUEST_DENIED; 1185 1186 #ifdef DEBUG 1187 fprintf(dbg, "req_right_char enter: start=%d, xpos=%d, c=%c\n", 1188 cur->start_char, cur->cursor_xpos, 1189 cur->buffers[0].string[pos]); 1190 #endif 1191 1192 if (cur->buffers[0].string[pos] == '\n') { 1193 start = pos + 1; 1194 if (cur->buffers[0].string[start] == 0) 1195 return E_REQUEST_DENIED; 1196 end = _formi_find_eol(cur->buffers[0].string, start); 1197 if (end - start > cur->cols) { 1198 cur->cursor_xpos = cur->cols - 1; 1199 cur->start_char = end - cur->cols - 1; 1200 } else { 1201 cur->cursor_xpos = end - start; 1202 cur->start_char = start; 1203 } 1204 } else { 1205 if (cur->cursor_xpos == cur->cols - 1) 1206 cur->start_char++; 1207 else 1208 cur->cursor_xpos++; 1209 } 1210 #ifdef DEBUG 1211 fprintf(dbg, "req_right_char exit: start=%d, xpos=%d, c=%c\n", 1212 cur->start_char, cur->cursor_xpos, 1213 cur->buffers[0].string[cur->start_char + 1214 cur->cursor_xpos]); 1215 #endif 1216 break; 1217 1218 case REQ_UP_CHAR: 1219 if (cur->cursor_ypos == 0) { 1220 if (cur->start_line == 0) 1221 return E_REQUEST_DENIED; 1222 1223 cur->start_line--; 1224 } else 1225 cur->cursor_ypos--; 1226 1227 start = find_cur_line(cur); 1228 end = _formi_find_eol(cur->buffers[0].string, start); 1229 cur->start_char = start; 1230 if (cur->cursor_xpos > end - start) 1231 cur->cursor_xpos = end - start; 1232 break; 1233 1234 case REQ_DOWN_CHAR: 1235 if (cur->cursor_ypos == cur->rows - 1) { 1236 if (cur->start_line + cur->rows == cur->row_count) 1237 return E_REQUEST_DENIED; 1238 cur->start_line++; 1239 } else 1240 cur->cursor_ypos++; 1241 1242 start = find_cur_line(cur); 1243 end = _formi_find_eol(cur->buffers[0].string, start); 1244 cur->start_char = start; 1245 if (cur->cursor_xpos > end - start) 1246 cur->cursor_xpos = end - start; 1247 break; 1248 1249 case REQ_NEW_LINE: 1250 if (_formi_add_char(cur, cur->start_char + cur->cursor_xpos, 1251 '\n') == E_OK) 1252 cur->row_count++; 1253 break; 1254 1255 case REQ_INS_CHAR: 1256 _formi_add_char(cur, cur->start_char + cur->cursor_xpos, 1257 cur->pad); 1258 break; 1259 1260 case REQ_INS_LINE: 1261 start = _formi_find_bol(cur->buffers[0].string, cur->start_char); 1262 if (_formi_add_char(cur, start, '\n') == E_OK) 1263 cur->row_count++; 1264 break; 1265 1266 case REQ_DEL_CHAR: 1267 if (cur->buffers[0].string[cur->start_char + cur->cursor_xpos] 1268 == '\0') 1269 return E_REQUEST_DENIED; 1270 1271 start = cur->start_char + cur->cursor_xpos; 1272 end = cur->buffers[0].length; 1273 if (cur->buffers[0].string[start] == '\n') { 1274 if (cur->row_count > 1) { 1275 cur->row_count--; 1276 _formi_join_line(cur, cur->buffers[0].string, 1277 start, JOIN_NEXT); 1278 } else 1279 cur->buffers[0].string[start] = '\0'; 1280 } else { 1281 bcopy(&cur->buffers[0].string[start + 1], 1282 &cur->buffers[0].string[start], 1283 (unsigned) end - start + 1); 1284 } 1285 1286 cur->buffers[0].length--; 1287 break; 1288 1289 case REQ_DEL_PREV: 1290 if ((cur->cursor_xpos == 0) && (cur->start_char == 0)) 1291 return E_REQUEST_DENIED; 1292 1293 start = cur->cursor_xpos + cur->start_char; 1294 end = cur->buffers[0].length; 1295 1296 if (cur->buffers[0].string[cur->start_char + cur->cursor_xpos] == '\n') { 1297 _formi_join_line(cur, cur->buffers[0].string, 1298 cur->start_char + cur->cursor_xpos, 1299 JOIN_PREV); 1300 cur->row_count--; 1301 } else { 1302 bcopy(&cur->buffers[0].string[start], 1303 &cur->buffers[0].string[start - 1], 1304 (unsigned) end - start + 1); 1305 } 1306 1307 cur->buffers[0].length--; 1308 if ((cur->cursor_xpos == 0) && (cur->start_char > 0)) 1309 cur->start_char--; 1310 else if ((cur->cursor_xpos == cur->cols - 1) 1311 && (cur->start_char > 0)) 1312 cur->start_char--; 1313 else if (cur->cursor_xpos > 0) 1314 cur->cursor_xpos--; 1315 1316 break; 1317 1318 case REQ_DEL_LINE: 1319 start = cur->start_char + cur->cursor_xpos; 1320 end = _formi_find_eol(cur->buffers[0].string, start); 1321 start = _formi_find_bol(cur->buffers[0].string, start); 1322 bcopy(&cur->buffers[0].string[end + 1], 1323 &cur->buffers[0].string[start], 1324 (unsigned) cur->buffers[0].length - end + 1); 1325 if (cur->row_count > 1) 1326 cur->row_count--; 1327 break; 1328 1329 case REQ_DEL_WORD: 1330 start = cur->start_char + cur->cursor_xpos; 1331 end = find_eow(cur->buffers[0].string, start); 1332 start = find_sow(cur->buffers[0].string, start); 1333 bcopy(&cur->buffers[0].string[end + 1], 1334 &cur->buffers[0].string[start], 1335 (unsigned) cur->buffers[0].length - end + 1); 1336 cur->buffers[0].length -= end - start; 1337 break; 1338 1339 case REQ_CLR_EOL: 1340 /*XXXX this right or should we just toast the chars? */ 1341 start = cur->start_char + cur->cursor_xpos; 1342 end = _formi_find_eol(cur->buffers[0].string, start); 1343 for (i = start; i < end; i++) 1344 cur->buffers[0].string[i] = cur->pad; 1345 break; 1346 1347 case REQ_CLR_EOF: 1348 for (i = cur->start_char + cur->cursor_xpos; 1349 i < cur->buffers[0].length; i++) 1350 cur->buffers[0].string[i] = cur->pad; 1351 break; 1352 1353 case REQ_CLR_FIELD: 1354 for (i = 0; i < cur->buffers[0].length; i++) 1355 cur->buffers[0].string[i] = cur->pad; 1356 break; 1357 1358 case REQ_OVL_MODE: 1359 cur->overlay = 1; 1360 break; 1361 1362 case REQ_INS_MODE: 1363 cur->overlay = 0; 1364 break; 1365 1366 case REQ_SCR_FLINE: 1367 _formi_scroll_fwd(cur, 1); 1368 break; 1369 1370 case REQ_SCR_BLINE: 1371 _formi_scroll_back(cur, 1); 1372 break; 1373 1374 case REQ_SCR_FPAGE: 1375 _formi_scroll_fwd(cur, cur->rows); 1376 break; 1377 1378 case REQ_SCR_BPAGE: 1379 _formi_scroll_back(cur, cur->rows); 1380 break; 1381 1382 case REQ_SCR_FHPAGE: 1383 _formi_scroll_fwd(cur, cur->rows / 2); 1384 break; 1385 1386 case REQ_SCR_BHPAGE: 1387 _formi_scroll_back(cur, cur->rows / 2); 1388 break; 1389 1390 case REQ_SCR_FCHAR: 1391 _formi_hscroll_fwd(cur, 1); 1392 break; 1393 1394 case REQ_SCR_BCHAR: 1395 _formi_hscroll_back(cur, 1); 1396 break; 1397 1398 case REQ_SCR_HFLINE: 1399 _formi_hscroll_fwd(cur, cur->cols); 1400 break; 1401 1402 case REQ_SCR_HBLINE: 1403 _formi_hscroll_back(cur, cur->cols); 1404 break; 1405 1406 case REQ_SCR_HFHALF: 1407 _formi_hscroll_fwd(cur, cur->cols / 2); 1408 break; 1409 1410 case REQ_SCR_HBHALF: 1411 _formi_hscroll_back(cur, cur->cols / 2); 1412 break; 1413 1414 default: 1415 return 0; 1416 } 1417 1418 #ifdef DEBUG 1419 fprintf(dbg, "exit: xpos=%d, start_char=%d, length=%d, allocated=%d\n", 1420 cur->cursor_xpos, cur->start_char, cur->buffers[0].length, 1421 cur->buffers[0].allocated); 1422 fprintf(dbg, "exit: string=\"%s\"\n", cur->buffers[0].string); 1423 #endif 1424 return 1; 1425 } 1426 1427 /* 1428 * Validate the give character by passing it to any type character 1429 * checking routines, if they exist. 1430 */ 1431 int 1432 _formi_validate_char(FIELD *field, char c) 1433 { 1434 int ret_val; 1435 1436 if (field->type == NULL) 1437 return E_OK; 1438 1439 ret_val = E_INVALID_FIELD; 1440 _formi_do_char_validation(field, field->type, c, &ret_val); 1441 1442 return ret_val; 1443 } 1444 1445 1446 /* 1447 * Perform the validation of the character, invoke all field_type validation 1448 * routines. If the field is ok then update ret_val to E_OK otherwise 1449 * ret_val is not changed. 1450 */ 1451 static void 1452 _formi_do_char_validation(FIELD *field, FIELDTYPE *type, char c, int *ret_val) 1453 { 1454 if ((type->flags & _TYPE_IS_LINKED) == _TYPE_IS_LINKED) { 1455 _formi_do_char_validation(field, type->link->next, c, ret_val); 1456 _formi_do_char_validation(field, type->link->prev, c, ret_val); 1457 } else { 1458 if (type->char_check == NULL) 1459 *ret_val = E_OK; 1460 else { 1461 if (type->char_check((int)(unsigned char) c, 1462 field->args) == TRUE) 1463 *ret_val = E_OK; 1464 } 1465 } 1466 } 1467 1468 /* 1469 * Validate the current field. If the field validation returns success then 1470 * return E_OK otherwise return E_INVALID_FIELD. 1471 * 1472 */ 1473 int 1474 _formi_validate_field(FORM *form) 1475 { 1476 FIELD *cur; 1477 char *bp; 1478 int ret_val, count; 1479 1480 1481 if ((form == NULL) || (form->fields == NULL) || 1482 (form->fields[0] == NULL)) 1483 return E_INVALID_FIELD; 1484 1485 cur = form->fields[form->cur_field]; 1486 1487 bp = cur->buffers[0].string; 1488 count = _formi_skip_blanks(bp, 0); 1489 1490 /* check if we have a null field, depending on the nullok flag 1491 * this may be acceptable or not.... 1492 */ 1493 if (cur->buffers[0].string[count] == '\0') { 1494 if ((cur->opts & O_NULLOK) == O_NULLOK) 1495 return E_OK; 1496 else 1497 return E_INVALID_FIELD; 1498 } 1499 1500 /* check if an unmodified field is ok */ 1501 if (cur->buf0_status == 0) { 1502 if ((cur->opts & O_PASSOK) == O_PASSOK) 1503 return E_OK; 1504 else 1505 return E_INVALID_FIELD; 1506 } 1507 1508 /* if there is no type then just accept the field */ 1509 if (cur->type == NULL) 1510 return E_OK; 1511 1512 ret_val = E_INVALID_FIELD; 1513 _formi_do_validation(cur, cur->type, &ret_val); 1514 1515 return ret_val; 1516 } 1517 1518 /* 1519 * Perform the validation of the field, invoke all field_type validation 1520 * routines. If the field is ok then update ret_val to E_OK otherwise 1521 * ret_val is not changed. 1522 */ 1523 static void 1524 _formi_do_validation(FIELD *field, FIELDTYPE *type, int *ret_val) 1525 { 1526 if ((type->flags & _TYPE_IS_LINKED) == _TYPE_IS_LINKED) { 1527 _formi_do_validation(field, type->link->next, ret_val); 1528 _formi_do_validation(field, type->link->prev, ret_val); 1529 } else { 1530 if (type->field_check == NULL) 1531 *ret_val = E_OK; 1532 else { 1533 if (type->field_check(field, field_buffer(field, 0)) 1534 == TRUE) 1535 *ret_val = E_OK; 1536 } 1537 } 1538 } 1539 1540 /* 1541 * Select the next/previous choice for the field, the driver command 1542 * selecting the direction will be passed in c. Return 1 if a choice 1543 * selection succeeded, 0 otherwise. 1544 */ 1545 int 1546 _formi_field_choice(FORM *form, int c) 1547 { 1548 FIELDTYPE *type; 1549 FIELD *field; 1550 1551 if ((form == NULL) || (form->fields == NULL) || 1552 (form->fields[0] == NULL) || 1553 (form->fields[form->cur_field]->type == NULL)) 1554 return 0; 1555 1556 field = form->fields[form->cur_field]; 1557 type = field->type; 1558 1559 switch (c) { 1560 case REQ_NEXT_CHOICE: 1561 if (type->next_choice == NULL) 1562 return 0; 1563 else 1564 return type->next_choice(field, 1565 field_buffer(field, 0)); 1566 1567 case REQ_PREV_CHOICE: 1568 if (type->prev_choice == NULL) 1569 return 0; 1570 else 1571 return type->prev_choice(field, 1572 field_buffer(field, 0)); 1573 1574 default: /* should never happen! */ 1575 return 0; 1576 } 1577 } 1578 1579 /* 1580 * Update the fields if they have changed. The parameter old has the 1581 * previous current field as the current field may have been updated by 1582 * the driver. Return 1 if the form page needs updating. 1583 * 1584 */ 1585 int 1586 _formi_update_field(FORM *form, int old_field) 1587 { 1588 int cur, i; 1589 1590 cur = form->cur_field; 1591 1592 if (old_field != cur) { 1593 if (!((cur >= form->page_starts[form->page].first) && 1594 (cur <= form->page_starts[form->page].last))) { 1595 /* not on same page any more */ 1596 for (i = 0; i < form->max_page; i++) { 1597 if ((form->page_starts[i].in_use == 1) && 1598 (form->page_starts[i].first <= cur) && 1599 (form->page_starts[i].last >= cur)) { 1600 form->page = i; 1601 return 1; 1602 } 1603 } 1604 } 1605 } 1606 1607 _formi_redraw_field(form, old_field); 1608 _formi_redraw_field(form, form->cur_field); 1609 return 0; 1610 } 1611 1612 /* 1613 * Compare function for the field sorting 1614 * 1615 */ 1616 static int 1617 field_sort_compare(const void *one, const void *two) 1618 { 1619 const FIELD *a, *b; 1620 int tl; 1621 1622 /* LINTED const castaway; we don't modify these! */ 1623 a = (const FIELD *) *((const FIELD **) one); 1624 b = (const FIELD *) *((const FIELD **) two); 1625 1626 if (a == NULL) 1627 return 1; 1628 1629 if (b == NULL) 1630 return -1; 1631 1632 /* 1633 * First check the page, we want the fields sorted by page. 1634 * 1635 */ 1636 if (a->page != b->page) 1637 return ((a->page > b->page)? 1 : -1); 1638 1639 tl = _formi_top_left(a->parent, a->index, b->index); 1640 1641 /* 1642 * sort fields left to right, top to bottom so the top left is 1643 * the less than value.... 1644 */ 1645 return ((tl == a->index)? -1 : 1); 1646 } 1647 1648 /* 1649 * Sort the fields in a form ready for driver traversal. 1650 */ 1651 void 1652 _formi_sort_fields(FORM *form) 1653 { 1654 FIELD **sort_area; 1655 int i; 1656 1657 CIRCLEQ_INIT(&form->sorted_fields); 1658 1659 if ((sort_area = (FIELD **) malloc(sizeof(FIELD *) * form->field_count)) 1660 == NULL) 1661 return; 1662 1663 bcopy(form->fields, sort_area, sizeof(FIELD *) * form->field_count); 1664 qsort(sort_area, (unsigned) form->field_count, sizeof(FIELD *), 1665 field_sort_compare); 1666 1667 for (i = 0; i < form->field_count; i++) 1668 CIRCLEQ_INSERT_TAIL(&form->sorted_fields, sort_area[i], glue); 1669 1670 free(sort_area); 1671 } 1672 1673 /* 1674 * Set the neighbours for all the fields in the given form. 1675 */ 1676 void 1677 _formi_stitch_fields(FORM *form) 1678 { 1679 int above_row, below_row, end_above, end_below, cur_row, real_end; 1680 FIELD *cur, *above, *below; 1681 1682 /* 1683 * check if the sorted fields circle queue is empty, just 1684 * return if it is. 1685 */ 1686 if (CIRCLEQ_EMPTY(&form->sorted_fields)) 1687 return; 1688 1689 /* initially nothing is above..... */ 1690 above_row = -1; 1691 end_above = TRUE; 1692 above = NULL; 1693 1694 /* set up the first field as the current... */ 1695 cur = CIRCLEQ_FIRST(&form->sorted_fields); 1696 cur_row = cur->form_row; 1697 1698 /* find the first field on the next row if any */ 1699 below = CIRCLEQ_NEXT(cur, glue); 1700 below_row = -1; 1701 end_below = TRUE; 1702 real_end = TRUE; 1703 while (below != (void *)&form->sorted_fields) { 1704 if (below->form_row != cur_row) { 1705 below_row = below->form_row; 1706 end_below = FALSE; 1707 real_end = FALSE; 1708 break; 1709 } 1710 below = CIRCLEQ_NEXT(below, glue); 1711 } 1712 1713 /* walk the sorted fields, setting the neighbour pointers */ 1714 while (cur != (void *) &form->sorted_fields) { 1715 if (cur == CIRCLEQ_FIRST(&form->sorted_fields)) 1716 cur->left = NULL; 1717 else 1718 cur->left = CIRCLEQ_PREV(cur, glue); 1719 1720 if (cur == CIRCLEQ_LAST(&form->sorted_fields)) 1721 cur->right = NULL; 1722 else 1723 cur->right = CIRCLEQ_NEXT(cur, glue); 1724 1725 if (end_above == TRUE) 1726 cur->up = NULL; 1727 else { 1728 cur->up = above; 1729 above = CIRCLEQ_NEXT(above, glue); 1730 if (above_row != above->form_row) { 1731 end_above = TRUE; 1732 above_row = above->form_row; 1733 } 1734 } 1735 1736 if (end_below == TRUE) 1737 cur->down = NULL; 1738 else { 1739 cur->down = below; 1740 below = CIRCLEQ_NEXT(below, glue); 1741 if (below == (void *) &form->sorted_fields) { 1742 end_below = TRUE; 1743 real_end = TRUE; 1744 } else if (below_row != below->form_row) { 1745 end_below = TRUE; 1746 below_row = below->form_row; 1747 } 1748 } 1749 1750 cur = CIRCLEQ_NEXT(cur, glue); 1751 if ((cur != (void *) &form->sorted_fields) 1752 && (cur_row != cur->form_row)) { 1753 cur_row = cur->form_row; 1754 if (end_above == FALSE) { 1755 for (; above != CIRCLEQ_FIRST(&form->sorted_fields); 1756 above = CIRCLEQ_NEXT(above, glue)) { 1757 if (above->form_row != above_row) { 1758 above_row = above->form_row; 1759 break; 1760 } 1761 } 1762 } else if (above == NULL) { 1763 above = CIRCLEQ_FIRST(&form->sorted_fields); 1764 end_above = FALSE; 1765 above_row = above->form_row; 1766 } else 1767 end_above = FALSE; 1768 1769 if (end_below == FALSE) { 1770 while (below_row == below->form_row) { 1771 below = CIRCLEQ_NEXT(below, 1772 glue); 1773 if (below == 1774 (void *)&form->sorted_fields) { 1775 real_end = TRUE; 1776 end_below = TRUE; 1777 break; 1778 } 1779 } 1780 1781 if (below != (void *)&form->sorted_fields) 1782 below_row = below->form_row; 1783 } else if (real_end == FALSE) 1784 end_below = FALSE; 1785 1786 } 1787 } 1788 } 1789