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