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