1 /* $NetBSD: field.c,v 1.15 2002/05/20 15:00:11 blymn Exp $ */ 2 /*- 3 * Copyright (c) 1998-1999 Brett Lymn 4 * (blymn@baea.com.au, brett_lymn@yahoo.com.au) 5 * All rights reserved. 6 * 7 * This code has been donated to The NetBSD Foundation by the Author. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. The name of the author may not be used to endorse or promote products 15 * derived from this software without specific prior written permission 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 * 28 * 29 */ 30 31 #include <stdlib.h> 32 #include <strings.h> 33 #include <form.h> 34 #include "internals.h" 35 36 extern FORM _formi_default_form; 37 38 FIELD _formi_default_field = { 39 0, /* rows in the field */ 40 0, /* columns in the field */ 41 0, /* dynamic rows */ 42 0, /* dynamic columns */ 43 0, /* maximum growth */ 44 0, /* starting row in the form subwindow */ 45 0, /* starting column in the form subwindow */ 46 0, /* number of off screen rows */ 47 0, /* index of this field in form fields array. */ 48 0, /* number of buffers associated with this field */ 49 FALSE, /* set to true if buffer 0 has changed. */ 50 NO_JUSTIFICATION, /* justification style of the field */ 51 FALSE, /* set to true if field is in overlay mode */ 52 0, /* starting char in string (horiz scroll) */ 53 0, /* starting line in field (vert scroll) */ 54 0, /* number of rows actually used in field */ 55 0, /* actual pos of cursor in row, not same as x pos due to tabs */ 56 0, /* x pos of cursor in field */ 57 0, /* y pos of cursor in field */ 58 0, /* start of a new page on the form if 1 */ 59 0, /* number of the page this field is on */ 60 A_NORMAL, /* character attributes for the foreground */ 61 A_NORMAL, /* character attributes for the background */ 62 ' ', /* padding character */ 63 DEFAULT_FORM_OPTS, /* options for the field */ 64 NULL, /* the form this field is bound to, if any */ 65 NULL, /* field above this one */ 66 NULL, /* field below this one */ 67 NULL, /* field to the left of this one */ 68 NULL, /* field to the right of this one */ 69 NULL, /* user defined pointer. */ 70 NULL, /* used if fields are linked */ 71 NULL, /* type struct for the field */ 72 {NULL, NULL}, /* circle queue glue for sorting fields */ 73 NULL, /* args for field type. */ 74 0, /* number of allocated slots in lines array */ 75 NULL, /* pointer to the array of lines structures. */ 76 NULL, /* array of buffers for the field */ 77 }; 78 79 /* internal function prototypes */ 80 static FIELD * 81 _formi_create_field(FIELD *, int, int, int, int, int, int); 82 83 84 /* 85 * Set the userptr for the field 86 */ 87 int 88 set_field_userptr(FIELD *field, void *ptr) 89 { 90 FIELD *fp = (field == NULL) ? &_formi_default_field : field; 91 92 fp->userptr = ptr; 93 94 return E_OK; 95 } 96 97 /* 98 * Return the userptr for the field. 99 */ 100 101 void * 102 field_userptr(FIELD *field) 103 { 104 if (field == NULL) 105 return _formi_default_field.userptr; 106 else 107 return field->userptr; 108 } 109 110 /* 111 * Set the options for the designated field. 112 */ 113 int 114 set_field_opts(FIELD *field, Form_Options options) 115 { 116 int i; 117 118 FIELD *fp = (field == NULL) ? &_formi_default_field : field; 119 120 /* not allowed to set opts if the field is the current one */ 121 if ((field != NULL) && (field->parent != NULL) && 122 (field->parent->cur_field == field->index)) 123 return E_CURRENT; 124 125 if ((options & O_STATIC) == O_STATIC) { 126 for (i = 0; i < field->nbuf; i++) { 127 if (field->buffers[i].length > field->cols) 128 field->buffers[i].string[field->cols] = '\0'; 129 } 130 } 131 132 fp->opts = options; 133 134 return E_OK; 135 } 136 137 /* 138 * Turn on the passed field options. 139 */ 140 int 141 field_opts_on(FIELD *field, Form_Options options) 142 { 143 int i; 144 145 FIELD *fp = (field == NULL) ? &_formi_default_field : field; 146 147 /* not allowed to set opts if the field is the current one */ 148 if ((field != NULL) && (field->parent != NULL) && 149 (field->parent->cur_field == field->index)) 150 return E_CURRENT; 151 152 if ((options & O_STATIC) == O_STATIC) { 153 for (i = 0; i < field->nbuf; i++) { 154 if (field->buffers[i].length > field->cols) 155 field->buffers[i].string[field->cols] = '\0'; 156 } 157 } 158 159 fp->opts |= options; 160 161 return E_OK; 162 } 163 164 /* 165 * Turn off the passed field options. 166 */ 167 int 168 field_opts_off(FIELD *field, Form_Options options) 169 { 170 FIELD *fp = (field == NULL) ? &_formi_default_field : field; 171 172 /* not allowed to set opts if the field is the current one */ 173 if ((field != NULL) && (field->parent != NULL) && 174 (field->parent->cur_field == field->index)) 175 return E_CURRENT; 176 177 fp->opts &= ~options; 178 return E_OK; 179 } 180 181 /* 182 * Return the field options associated with the passed field. 183 */ 184 Form_Options 185 field_opts(FIELD *field) 186 { 187 if (field == NULL) 188 return _formi_default_field.opts; 189 else 190 return field->opts; 191 } 192 193 /* 194 * Set the justification for the passed field. 195 */ 196 int 197 set_field_just(FIELD *field, int justification) 198 { 199 FIELD *fp = (field == NULL) ? &_formi_default_field : field; 200 201 /* 202 * not allowed to set justification if the field is 203 * the current one 204 */ 205 if ((field != NULL) && (field->parent != NULL) && 206 (field->parent->cur_field == field->index)) 207 return E_CURRENT; 208 209 if ((justification < MIN_JUST_STYLE) /* check justification valid */ 210 || (justification > MAX_JUST_STYLE)) 211 return E_BAD_ARGUMENT; 212 213 fp->justification = justification; 214 return E_OK; 215 } 216 217 /* 218 * Return the justification style of the field passed. 219 */ 220 int 221 field_just(FIELD *field) 222 { 223 if (field == NULL) 224 return _formi_default_field.justification; 225 else 226 return field->justification; 227 } 228 229 /* 230 * Return information about the field passed. 231 */ 232 int 233 field_info(FIELD *field, int *rows, int *cols, int *frow, int *fcol, 234 int *nrow, int *nbuf) 235 { 236 if (field == NULL) 237 return E_BAD_ARGUMENT; 238 239 *rows = field->rows; 240 *cols = field->cols; 241 *frow = field->form_row; 242 *fcol = field->form_col; 243 *nrow = field->nrows; 244 *nbuf = field->nbuf; 245 246 return E_OK; 247 } 248 249 /* 250 * Report the dynamic field information. 251 */ 252 int 253 dynamic_field_info(FIELD *field, int *drows, int *dcols, int *max) 254 { 255 if (field == NULL) 256 return E_BAD_ARGUMENT; 257 258 if ((field->opts & O_STATIC) == O_STATIC) { 259 *drows = field->rows; 260 *dcols = field->cols; 261 } else { 262 *drows = field->drows; 263 *dcols = field->dcols; 264 } 265 266 *max = field->max; 267 268 return E_OK; 269 } 270 271 /* 272 * Set the value of the field buffer to the value given. 273 */ 274 275 int 276 set_field_buffer(FIELD *field, int buffer, char *value) 277 { 278 unsigned len; 279 int status; 280 281 if (field == NULL) 282 return E_BAD_ARGUMENT; 283 284 if (buffer >= field->nbuf) /* make sure buffer is valid */ 285 return E_BAD_ARGUMENT; 286 287 len = strlen(value); 288 if (((field->opts & O_STATIC) == O_STATIC) && (len > field->cols) 289 && ((field->rows + field->nrows) == 1)) 290 len = field->cols; 291 292 #ifdef DEBUG 293 if (_formi_create_dbg_file() != E_OK) 294 return E_SYSTEM_ERROR; 295 296 fprintf(dbg, 297 "set_field_buffer: entry: len = %d, value = %s, buffer=%d\n", 298 len, value, buffer); 299 fprintf(dbg, "set_field_buffer: entry: string = "); 300 if (field->buffers[buffer].string != NULL) 301 fprintf(dbg, "%s, len = %d\n", field->buffers[buffer].string, 302 field->buffers[buffer].length); 303 else 304 fprintf(dbg, "(null), len = 0\n"); 305 fprintf(dbg, "set_field_buffer: entry: lines.len = %d\n", 306 field->lines[0].length); 307 #endif 308 309 if ((field->buffers[buffer].string = 310 (char *) realloc(field->buffers[buffer].string, len + 1)) == NULL) 311 return E_SYSTEM_ERROR; 312 313 strlcpy(field->buffers[buffer].string, value, len + 1); 314 field->buffers[buffer].length = len; 315 field->buffers[buffer].allocated = len + 1; 316 317 if (buffer == 0) { 318 field->start_char = 0; 319 field->start_line = 0; 320 field->row_xpos = 0; 321 field->cursor_xpos = 0; 322 field->cursor_ypos = 0; 323 field->row_count = 1; /* must be at least one row */ 324 field->lines[0].start = 0; 325 field->lines[0].end = (len > 0)? (len - 1) : 0; 326 field->lines[0].length = 327 _formi_tab_expanded_length(field->buffers[0].string, 328 0, field->lines[0].end); 329 330 /* we have to hope the wrap works - if it does not then the 331 buffer is pretty much borked */ 332 status = _formi_wrap_field(field, 0); 333 if (status != E_OK) 334 return status; 335 336 /* 337 * calculate the tabs for a single row field, the 338 * multiline case is handled when the wrap is done. 339 */ 340 if (field->row_count == 1) 341 _formi_calculate_tabs(field, 0); 342 343 /* redraw the field to reflect the new contents. If the field 344 * is attached.... 345 */ 346 if (field->parent != NULL) 347 _formi_redraw_field(field->parent, field->index); 348 } 349 350 #ifdef DEBUG 351 fprintf(dbg, "set_field_buffer: exit: len = %d, value = %s\n", 352 len, value); 353 fprintf(dbg, "set_field_buffer: exit: string = %s, len = %d\n", 354 field->buffers[buffer].string, field->buffers[buffer].length); 355 fprintf(dbg, "set_field_buffer: exit: lines.len = %d\n", 356 field->lines[0].length); 357 #endif 358 359 return E_OK; 360 } 361 362 /* 363 * Return the requested field buffer to the caller. 364 */ 365 char * 366 field_buffer(FIELD *field, int buffer) 367 { 368 369 if (field == NULL) 370 return NULL; 371 372 if (buffer >= field->nbuf) 373 return NULL; 374 375 return field->buffers[buffer].string; 376 } 377 378 /* 379 * Set the buffer 0 field status. 380 */ 381 int 382 set_field_status(FIELD *field, int status) 383 { 384 385 if (field == NULL) 386 return E_BAD_ARGUMENT; 387 388 if (status != FALSE) 389 field->buf0_status = TRUE; 390 else 391 field->buf0_status = FALSE; 392 393 return E_OK; 394 } 395 396 /* 397 * Return the buffer 0 status flag for the given field. 398 */ 399 int 400 field_status(FIELD *field) 401 { 402 403 if (field == NULL) /* the default buffer 0 never changes :-) */ 404 return FALSE; 405 406 return field->buf0_status; 407 } 408 409 /* 410 * Set the maximum growth for a dynamic field. 411 */ 412 int 413 set_max_field(FIELD *fptr, int max) 414 { 415 FIELD *field = (fptr == NULL)? &_formi_default_field : fptr; 416 417 if ((field->opts & O_STATIC) == O_STATIC) /* check if field dynamic */ 418 return E_BAD_ARGUMENT; 419 420 if (max < 0) /* negative numbers are bad.... */ 421 return E_BAD_ARGUMENT; 422 423 field->max = max; 424 return E_OK; 425 } 426 427 /* 428 * Set the field foreground character attributes. 429 */ 430 int 431 set_field_fore(FIELD *fptr, chtype attribute) 432 { 433 FIELD *field = (fptr == NULL)? &_formi_default_field : fptr; 434 435 field->fore = attribute; 436 return E_OK; 437 } 438 439 /* 440 * Return the foreground character attribute for the given field. 441 */ 442 chtype 443 field_fore(FIELD *field) 444 { 445 if (field == NULL) 446 return _formi_default_field.fore; 447 else 448 return field->fore; 449 } 450 451 /* 452 * Set the background character attribute for the given field. 453 */ 454 int 455 set_field_back(FIELD *field, chtype attribute) 456 { 457 if (field == NULL) 458 _formi_default_field.back = attribute; 459 else 460 field->back = attribute; 461 462 return E_OK; 463 } 464 465 /* 466 * Get the background character attribute for the given field. 467 */ 468 chtype 469 field_back(FIELD *field) 470 { 471 if (field == NULL) 472 return _formi_default_field.back; 473 else 474 return field->back; 475 } 476 477 /* 478 * Set the pad character for the given field. 479 */ 480 int 481 set_field_pad(FIELD *field, int pad) 482 { 483 if (field == NULL) 484 _formi_default_field.pad = pad; 485 else 486 field->pad = pad; 487 488 return E_OK; 489 } 490 491 /* 492 * Return the padding character for the given field. 493 */ 494 int 495 field_pad(FIELD *field) 496 { 497 if (field == NULL) 498 return _formi_default_field.pad; 499 else 500 return field->pad; 501 } 502 503 /* 504 * Set the field initialisation function hook. 505 */ 506 int 507 set_field_init(FORM *form, Form_Hook function) 508 { 509 if (form == NULL) 510 _formi_default_form.field_init = function; 511 else 512 form->field_init = function; 513 514 return E_OK; 515 } 516 517 /* 518 * Return the function hook for the field initialisation. 519 */ 520 Form_Hook 521 field_init(FORM *form) 522 { 523 if (form == NULL) 524 return _formi_default_form.field_init; 525 else 526 return form->field_init; 527 } 528 529 /* 530 * Set the field termination function hook. 531 */ 532 int 533 set_field_term(FORM *form, Form_Hook function) 534 { 535 if (form == NULL) 536 _formi_default_form.field_term = function; 537 else 538 form->field_term = function; 539 540 return E_OK; 541 } 542 543 /* 544 * Return the function hook defined for the field termination. 545 */ 546 Form_Hook 547 field_term(FORM *form) 548 { 549 if (form == NULL) 550 return _formi_default_form.field_term; 551 else 552 return form->field_term; 553 } 554 555 /* 556 * Set the page flag on the given field to indicate it is the start of a 557 * new page. 558 */ 559 int 560 set_new_page(FIELD *fptr, int page) 561 { 562 FIELD *field = (fptr == NULL)? &_formi_default_field : fptr; 563 564 if (field->parent != NULL) /* check if field is connected to a form */ 565 return E_CONNECTED; 566 567 field->page_break = (page != FALSE); 568 return E_OK; 569 } 570 571 /* 572 * Return the page status for the given field. TRUE is returned if the 573 * field is the start of a new page. 574 */ 575 int 576 new_page(FIELD *field) 577 { 578 if (field == NULL) 579 return _formi_default_field.page_break; 580 else 581 return field->page_break; 582 } 583 584 /* 585 * Return the index of the field in the form fields array. 586 */ 587 int 588 field_index(FIELD *field) 589 { 590 if (field == NULL) 591 return E_BAD_ARGUMENT; 592 593 if (field->parent == NULL) 594 return E_NOT_CONNECTED; 595 596 return field->index; 597 } 598 599 /* 600 * Internal function that does most of the work to create a new field. 601 * The new field is initialised from the information in the prototype 602 * field passed. 603 * Returns NULL on error. 604 */ 605 static FIELD * 606 _formi_create_field(FIELD *prototype, int rows, int cols, int frow, 607 int fcol, int nrows, int nbuf) 608 { 609 FIELD *new; 610 611 if ((rows <= 0) || (cols <= 0) || (frow < 0) || (fcol < 0) || 612 (nrows < 0) || (nbuf < 0)) 613 return NULL; 614 615 if ((new = (FIELD *)malloc(sizeof(FIELD))) == NULL) { 616 return NULL; 617 } 618 619 /* copy in the default field info */ 620 bcopy(prototype, new, sizeof(FIELD)); 621 622 new->nbuf = nbuf + 1; 623 new->rows = rows; 624 new->cols = cols; 625 new->form_row = frow; 626 new->form_col = fcol; 627 new->nrows = nrows; 628 new->link = new; 629 return new; 630 } 631 632 /* 633 * Create a new field structure. 634 */ 635 FIELD * 636 new_field(int rows, int cols, int frow, int fcol, int nrows, int nbuf) 637 { 638 FIELD *new; 639 size_t buf_len; 640 int i; 641 642 643 if ((new = _formi_create_field(&_formi_default_field, rows, cols, 644 frow, fcol, nrows, nbuf)) == NULL) 645 return NULL; 646 647 buf_len = (nbuf + 1) * sizeof(FORM_STR); 648 649 if ((new->buffers = (FORM_STR *)malloc(buf_len)) == NULL) { 650 free(new); 651 return NULL; 652 } 653 654 /* Initialise the strings to a zero length string */ 655 for (i = 0; i < nbuf + 1; i++) { 656 if ((new->buffers[i].string = 657 (char *) malloc(sizeof(char))) == NULL) { 658 free(new->buffers); 659 free(new); 660 return NULL; 661 } 662 new->buffers[i].string[0] = '\0'; 663 new->buffers[i].length = 0; 664 new->buffers[i].allocated = 1; 665 } 666 667 if ((new->lines = (_FORMI_FIELD_LINES *) 668 malloc(sizeof(struct _formi_field_lines))) == NULL) { 669 free(new->buffers); 670 free(new); 671 return NULL; 672 } 673 674 new->lines_alloced = 1; 675 new->lines[0].length = 0; 676 new->lines[0].start = 0; 677 new->lines[0].end = 0; 678 new->lines[0].tabs = NULL; 679 680 return new; 681 } 682 683 /* 684 * Duplicate the given field, including it's buffers. 685 */ 686 FIELD * 687 dup_field(FIELD *field, int frow, int fcol) 688 { 689 FIELD *new; 690 size_t row_len, buf_len; 691 692 if (field == NULL) 693 return NULL; 694 695 /* XXXX this right???? */ 696 if ((new = _formi_create_field(field, (int) field->rows, 697 (int ) field->cols, 698 frow, fcol, (int) field->nrows, 699 field->nbuf - 1)) == NULL) 700 return NULL; 701 702 row_len = (field->rows + field->nrows + 1) * field->cols; 703 buf_len = (field->nbuf + 1) * row_len * sizeof(FORM_STR); 704 705 if ((new->buffers = (FORM_STR *)malloc(buf_len)) == NULL) { 706 free(new); 707 return NULL; 708 } 709 710 /* copy the buffers from the source field into the new copy */ 711 bcopy(field->buffers, new->buffers, buf_len); 712 713 return new; 714 } 715 716 /* 717 * Create a new field at the specified location by duplicating the given 718 * field. The buffers are shared with the parent field. 719 */ 720 FIELD * 721 link_field(FIELD *field, int frow, int fcol) 722 { 723 FIELD *new; 724 725 if (field == NULL) 726 return NULL; 727 728 if ((new = _formi_create_field(field, (int) field->rows, 729 (int) field->cols, 730 frow, fcol, (int) field->nrows, 731 field->nbuf - 1)) == NULL) 732 return NULL; 733 734 new->link = field->link; 735 field->link = new; 736 737 /* we are done. The buffer pointer was copied during the field 738 creation. */ 739 return new; 740 } 741 742 /* 743 * Release all storage allocated to the field 744 */ 745 int 746 free_field(FIELD *field) 747 { 748 FIELD *flink; 749 int i; 750 _formi_tab_t *ts, *nts; 751 752 if (field == NULL) 753 return E_BAD_ARGUMENT; 754 755 if (field->parent != NULL) 756 return E_CONNECTED; 757 758 if (field->link == field) { /* check if field linked */ 759 /* no it is not - release the buffers */ 760 free(field->buffers); 761 /* free the tab structures */ 762 for (i = 0; i < field->row_count - 1; i++) { 763 if (field->lines[i].tabs != NULL) { 764 ts = field->lines[i].tabs; 765 while (ts != NULL) { 766 nts = ts->fwd; 767 free(ts); 768 ts = nts; 769 } 770 } 771 } 772 } else { 773 /* is linked, traverse the links to find the field referring 774 * to the one to be freed. 775 */ 776 for (flink = field->link; flink != field; flink = flink->link); 777 flink->link = field->link; 778 } 779 780 free(field); 781 return E_OK; 782 } 783 784 785