field.c revision 1.19
1/* $NetBSD: field.c,v 1.19 2002/08/03 14:34:22 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 36extern FORM _formi_default_form; 37 38FIELD _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 */ 80static FIELD * 81_formi_create_field(FIELD *, int, int, int, int, int, int); 82 83 84/* 85 * Set the userptr for the field 86 */ 87int 88set_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 101void * 102field_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 */ 113int 114set_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 /* if appropriate, redraw the field */ 135 if ((field != NULL) && (field->parent != NULL) 136 && (field->parent->posted == 1)) { 137 _formi_redraw_field(field->parent, field->index); 138 pos_form_cursor(field->parent); 139 wrefresh(field->parent->scrwin); 140 } 141 142 return E_OK; 143} 144 145/* 146 * Turn on the passed field options. 147 */ 148int 149field_opts_on(FIELD *field, Form_Options options) 150{ 151 int i; 152 153 FIELD *fp = (field == NULL) ? &_formi_default_field : field; 154 155 /* not allowed to set opts if the field is the current one */ 156 if ((field != NULL) && (field->parent != NULL) && 157 (field->parent->cur_field == field->index)) 158 return E_CURRENT; 159 160 if ((options & O_STATIC) == O_STATIC) { 161 for (i = 0; i < field->nbuf; i++) { 162 if (field->buffers[i].length > field->cols) 163 field->buffers[i].string[field->cols] = '\0'; 164 } 165 } 166 167 fp->opts |= options; 168 169 /* if appropriate, redraw the field */ 170 if ((field != NULL) && (field->parent != NULL) 171 && (field->parent->posted == 1)) { 172 _formi_redraw_field(field->parent, field->index); 173 pos_form_cursor(field->parent); 174 wrefresh(field->parent->scrwin); 175 } 176 177 return E_OK; 178} 179 180/* 181 * Turn off the passed field options. 182 */ 183int 184field_opts_off(FIELD *field, Form_Options options) 185{ 186 FIELD *fp = (field == NULL) ? &_formi_default_field : field; 187 188 /* not allowed to set opts if the field is the current one */ 189 if ((field != NULL) && (field->parent != NULL) && 190 (field->parent->cur_field == field->index)) 191 return E_CURRENT; 192 193 fp->opts &= ~options; 194 195 /* if appropriate, redraw the field */ 196 if ((field != NULL) && (field->parent != NULL) 197 && (field->parent->posted == 1)) { 198 _formi_redraw_field(field->parent, field->index); 199 pos_form_cursor(field->parent); 200 wrefresh(field->parent->scrwin); 201 } 202 203 return E_OK; 204} 205 206/* 207 * Return the field options associated with the passed field. 208 */ 209Form_Options 210field_opts(FIELD *field) 211{ 212 if (field == NULL) 213 return _formi_default_field.opts; 214 else 215 return field->opts; 216} 217 218/* 219 * Set the justification for the passed field. 220 */ 221int 222set_field_just(FIELD *field, int justification) 223{ 224 FIELD *fp = (field == NULL) ? &_formi_default_field : field; 225 226 /* 227 * not allowed to set justification if the field is 228 * the current one 229 */ 230 if ((field != NULL) && (field->parent != NULL) && 231 (field->parent->cur_field == field->index)) 232 return E_CURRENT; 233 234 if ((justification < MIN_JUST_STYLE) /* check justification valid */ 235 || (justification > MAX_JUST_STYLE)) 236 return E_BAD_ARGUMENT; 237 238 /* only allow justification on static, single row fields */ 239 if (((fp->opts & O_STATIC) != O_STATIC) || 240 ((fp->rows + fp->nrows) > 1)) 241 return E_BAD_ARGUMENT; 242 243 fp->justification = justification; 244 245 _formi_init_field_xpos(fp); 246 247 return E_OK; 248} 249 250/* 251 * Return the justification style of the field passed. 252 */ 253int 254field_just(FIELD *field) 255{ 256 if (field == NULL) 257 return _formi_default_field.justification; 258 else 259 return field->justification; 260} 261 262/* 263 * Return information about the field passed. 264 */ 265int 266field_info(FIELD *field, int *rows, int *cols, int *frow, int *fcol, 267 int *nrow, int *nbuf) 268{ 269 if (field == NULL) 270 return E_BAD_ARGUMENT; 271 272 *rows = field->rows; 273 *cols = field->cols; 274 *frow = field->form_row; 275 *fcol = field->form_col; 276 *nrow = field->nrows; 277 *nbuf = field->nbuf; 278 279 return E_OK; 280} 281 282/* 283 * Report the dynamic field information. 284 */ 285int 286dynamic_field_info(FIELD *field, int *drows, int *dcols, int *max) 287{ 288 if (field == NULL) 289 return E_BAD_ARGUMENT; 290 291 if ((field->opts & O_STATIC) == O_STATIC) { 292 *drows = field->rows; 293 *dcols = field->cols; 294 } else { 295 *drows = field->drows; 296 *dcols = field->dcols; 297 } 298 299 *max = field->max; 300 301 return E_OK; 302} 303 304/* 305 * Set the value of the field buffer to the value given. 306 */ 307 308int 309set_field_buffer(FIELD *field, int buffer, char *value) 310{ 311 unsigned len; 312 int status; 313 314 if (field == NULL) 315 return E_BAD_ARGUMENT; 316 317 if (buffer >= field->nbuf) /* make sure buffer is valid */ 318 return E_BAD_ARGUMENT; 319 320 len = strlen(value); 321 if (((field->opts & O_STATIC) == O_STATIC) && (len > field->cols) 322 && ((field->rows + field->nrows) == 1)) 323 len = field->cols; 324 325#ifdef DEBUG 326 if (_formi_create_dbg_file() != E_OK) 327 return E_SYSTEM_ERROR; 328 329 fprintf(dbg, 330 "set_field_buffer: entry: len = %d, value = %s, buffer=%d\n", 331 len, value, buffer); 332 fprintf(dbg, "set_field_buffer: entry: string = "); 333 if (field->buffers[buffer].string != NULL) 334 fprintf(dbg, "%s, len = %d\n", field->buffers[buffer].string, 335 field->buffers[buffer].length); 336 else 337 fprintf(dbg, "(null), len = 0\n"); 338 fprintf(dbg, "set_field_buffer: entry: lines.len = %d\n", 339 field->lines[0].length); 340#endif 341 342 if ((field->buffers[buffer].string = 343 (char *) realloc(field->buffers[buffer].string, len + 1)) == NULL) 344 return E_SYSTEM_ERROR; 345 346 strlcpy(field->buffers[buffer].string, value, len + 1); 347 field->buffers[buffer].length = len; 348 field->buffers[buffer].allocated = len + 1; 349 350 if (buffer == 0) { 351 field->start_char = 0; 352 field->start_line = 0; 353 field->row_xpos = 0; 354 field->cursor_xpos = 0; 355 field->cursor_ypos = 0; 356 field->row_count = 1; /* must be at least one row */ 357 field->lines[0].start = 0; 358 field->lines[0].end = (len > 0)? (len - 1) : 0; 359 field->lines[0].length = 360 _formi_tab_expanded_length(field->buffers[0].string, 361 0, field->lines[0].end); 362 363 /* we have to hope the wrap works - if it does not then the 364 buffer is pretty much borked */ 365 status = _formi_wrap_field(field, 0); 366 if (status != E_OK) 367 return status; 368 369 /* 370 * calculate the tabs for a single row field, the 371 * multiline case is handled when the wrap is done. 372 */ 373 if (field->row_count == 1) 374 _formi_calculate_tabs(field, 0); 375 376 /* redraw the field to reflect the new contents. If the field 377 * is attached.... 378 */ 379 if ((field->parent != NULL) && (field->parent->posted == 1)) { 380 _formi_redraw_field(field->parent, field->index); 381 /* make sure cursor goes back to current field */ 382 pos_form_cursor(field->parent); 383 } 384 385 } 386 387#ifdef DEBUG 388 fprintf(dbg, "set_field_buffer: exit: len = %d, value = %s\n", 389 len, value); 390 fprintf(dbg, "set_field_buffer: exit: string = %s, len = %d\n", 391 field->buffers[buffer].string, field->buffers[buffer].length); 392 fprintf(dbg, "set_field_buffer: exit: lines.len = %d\n", 393 field->lines[0].length); 394#endif 395 396 return E_OK; 397} 398 399/* 400 * Return the requested field buffer to the caller. 401 */ 402char * 403field_buffer(FIELD *field, int buffer) 404{ 405 406 if (field == NULL) 407 return NULL; 408 409 if (buffer >= field->nbuf) 410 return NULL; 411 412 return field->buffers[buffer].string; 413} 414 415/* 416 * Set the buffer 0 field status. 417 */ 418int 419set_field_status(FIELD *field, int status) 420{ 421 422 if (field == NULL) 423 return E_BAD_ARGUMENT; 424 425 if (status != FALSE) 426 field->buf0_status = TRUE; 427 else 428 field->buf0_status = FALSE; 429 430 return E_OK; 431} 432 433/* 434 * Return the buffer 0 status flag for the given field. 435 */ 436int 437field_status(FIELD *field) 438{ 439 440 if (field == NULL) /* the default buffer 0 never changes :-) */ 441 return FALSE; 442 443 return field->buf0_status; 444} 445 446/* 447 * Set the maximum growth for a dynamic field. 448 */ 449int 450set_max_field(FIELD *fptr, int max) 451{ 452 FIELD *field = (fptr == NULL)? &_formi_default_field : fptr; 453 454 if ((field->opts & O_STATIC) == O_STATIC) /* check if field dynamic */ 455 return E_BAD_ARGUMENT; 456 457 if (max < 0) /* negative numbers are bad.... */ 458 return E_BAD_ARGUMENT; 459 460 field->max = max; 461 return E_OK; 462} 463 464/* 465 * Set the field foreground character attributes. 466 */ 467int 468set_field_fore(FIELD *fptr, chtype attribute) 469{ 470 FIELD *field = (fptr == NULL)? &_formi_default_field : fptr; 471 472 field->fore = attribute; 473 return E_OK; 474} 475 476/* 477 * Return the foreground character attribute for the given field. 478 */ 479chtype 480field_fore(FIELD *field) 481{ 482 if (field == NULL) 483 return _formi_default_field.fore; 484 else 485 return field->fore; 486} 487 488/* 489 * Set the background character attribute for the given field. 490 */ 491int 492set_field_back(FIELD *field, chtype attribute) 493{ 494 if (field == NULL) 495 _formi_default_field.back = attribute; 496 else 497 field->back = attribute; 498 499 return E_OK; 500} 501 502/* 503 * Get the background character attribute for the given field. 504 */ 505chtype 506field_back(FIELD *field) 507{ 508 if (field == NULL) 509 return _formi_default_field.back; 510 else 511 return field->back; 512} 513 514/* 515 * Set the pad character for the given field. 516 */ 517int 518set_field_pad(FIELD *field, int pad) 519{ 520 if (field == NULL) 521 _formi_default_field.pad = pad; 522 else 523 field->pad = pad; 524 525 return E_OK; 526} 527 528/* 529 * Return the padding character for the given field. 530 */ 531int 532field_pad(FIELD *field) 533{ 534 if (field == NULL) 535 return _formi_default_field.pad; 536 else 537 return field->pad; 538} 539 540/* 541 * Set the field initialisation function hook. 542 */ 543int 544set_field_init(FORM *form, Form_Hook function) 545{ 546 if (form == NULL) 547 _formi_default_form.field_init = function; 548 else 549 form->field_init = function; 550 551 return E_OK; 552} 553 554/* 555 * Return the function hook for the field initialisation. 556 */ 557Form_Hook 558field_init(FORM *form) 559{ 560 if (form == NULL) 561 return _formi_default_form.field_init; 562 else 563 return form->field_init; 564} 565 566/* 567 * Set the field termination function hook. 568 */ 569int 570set_field_term(FORM *form, Form_Hook function) 571{ 572 if (form == NULL) 573 _formi_default_form.field_term = function; 574 else 575 form->field_term = function; 576 577 return E_OK; 578} 579 580/* 581 * Return the function hook defined for the field termination. 582 */ 583Form_Hook 584field_term(FORM *form) 585{ 586 if (form == NULL) 587 return _formi_default_form.field_term; 588 else 589 return form->field_term; 590} 591 592/* 593 * Set the page flag on the given field to indicate it is the start of a 594 * new page. 595 */ 596int 597set_new_page(FIELD *fptr, int page) 598{ 599 FIELD *field = (fptr == NULL)? &_formi_default_field : fptr; 600 601 if (field->parent != NULL) /* check if field is connected to a form */ 602 return E_CONNECTED; 603 604 field->page_break = (page != FALSE); 605 return E_OK; 606} 607 608/* 609 * Return the page status for the given field. TRUE is returned if the 610 * field is the start of a new page. 611 */ 612int 613new_page(FIELD *field) 614{ 615 if (field == NULL) 616 return _formi_default_field.page_break; 617 else 618 return field->page_break; 619} 620 621/* 622 * Return the index of the field in the form fields array. 623 */ 624int 625field_index(FIELD *field) 626{ 627 if (field == NULL) 628 return E_BAD_ARGUMENT; 629 630 if (field->parent == NULL) 631 return E_NOT_CONNECTED; 632 633 return field->index; 634} 635 636/* 637 * Internal function that does most of the work to create a new field. 638 * The new field is initialised from the information in the prototype 639 * field passed. 640 * Returns NULL on error. 641 */ 642static FIELD * 643_formi_create_field(FIELD *prototype, int rows, int cols, int frow, 644 int fcol, int nrows, int nbuf) 645{ 646 FIELD *new; 647 648 if ((rows <= 0) || (cols <= 0) || (frow < 0) || (fcol < 0) || 649 (nrows < 0) || (nbuf < 0)) 650 return NULL; 651 652 if ((new = (FIELD *)malloc(sizeof(FIELD))) == NULL) { 653 return NULL; 654 } 655 656 /* copy in the default field info */ 657 bcopy(prototype, new, sizeof(FIELD)); 658 659 new->nbuf = nbuf + 1; 660 new->rows = rows; 661 new->cols = cols; 662 new->form_row = frow; 663 new->form_col = fcol; 664 new->nrows = nrows; 665 new->link = new; 666 return new; 667} 668 669/* 670 * Create a new field structure. 671 */ 672FIELD * 673new_field(int rows, int cols, int frow, int fcol, int nrows, int nbuf) 674{ 675 FIELD *new; 676 size_t buf_len; 677 int i; 678 679 680 if ((new = _formi_create_field(&_formi_default_field, rows, cols, 681 frow, fcol, nrows, nbuf)) == NULL) 682 return NULL; 683 684 buf_len = (nbuf + 1) * sizeof(FORM_STR); 685 686 if ((new->buffers = (FORM_STR *)malloc(buf_len)) == NULL) { 687 free(new); 688 return NULL; 689 } 690 691 /* Initialise the strings to a zero length string */ 692 for (i = 0; i < nbuf + 1; i++) { 693 if ((new->buffers[i].string = 694 (char *) malloc(sizeof(char))) == NULL) { 695 free(new->buffers); 696 free(new); 697 return NULL; 698 } 699 new->buffers[i].string[0] = '\0'; 700 new->buffers[i].length = 0; 701 new->buffers[i].allocated = 1; 702 } 703 704 if ((new->lines = (_FORMI_FIELD_LINES *) 705 malloc(sizeof(struct _formi_field_lines))) == NULL) { 706 free(new->buffers); 707 free(new); 708 return NULL; 709 } 710 711 new->lines_alloced = 1; 712 new->lines[0].length = 0; 713 new->lines[0].start = 0; 714 new->lines[0].end = 0; 715 new->lines[0].tabs = NULL; 716 717 return new; 718} 719 720/* 721 * Duplicate the given field, including it's buffers. 722 */ 723FIELD * 724dup_field(FIELD *field, int frow, int fcol) 725{ 726 FIELD *new; 727 size_t row_len, buf_len; 728 729 if (field == NULL) 730 return NULL; 731 732 /* XXXX this right???? */ 733 if ((new = _formi_create_field(field, (int) field->rows, 734 (int ) field->cols, 735 frow, fcol, (int) field->nrows, 736 field->nbuf - 1)) == NULL) 737 return NULL; 738 739 row_len = (field->rows + field->nrows + 1) * field->cols; 740 buf_len = (field->nbuf + 1) * row_len * sizeof(FORM_STR); 741 742 if ((new->buffers = (FORM_STR *)malloc(buf_len)) == NULL) { 743 free(new); 744 return NULL; 745 } 746 747 /* copy the buffers from the source field into the new copy */ 748 bcopy(field->buffers, new->buffers, buf_len); 749 750 return new; 751} 752 753/* 754 * Create a new field at the specified location by duplicating the given 755 * field. The buffers are shared with the parent field. 756 */ 757FIELD * 758link_field(FIELD *field, int frow, int fcol) 759{ 760 FIELD *new; 761 762 if (field == NULL) 763 return NULL; 764 765 if ((new = _formi_create_field(field, (int) field->rows, 766 (int) field->cols, 767 frow, fcol, (int) field->nrows, 768 field->nbuf - 1)) == NULL) 769 return NULL; 770 771 new->link = field->link; 772 field->link = new; 773 774 /* we are done. The buffer pointer was copied during the field 775 creation. */ 776 return new; 777} 778 779/* 780 * Release all storage allocated to the field 781 */ 782int 783free_field(FIELD *field) 784{ 785 FIELD *flink; 786 int i; 787 _formi_tab_t *ts, *nts; 788 789 if (field == NULL) 790 return E_BAD_ARGUMENT; 791 792 if (field->parent != NULL) 793 return E_CONNECTED; 794 795 if (field->link == field) { /* check if field linked */ 796 /* no it is not - release the buffers */ 797 free(field->buffers); 798 /* free the tab structures */ 799 for (i = 0; i < field->row_count - 1; i++) { 800 if (field->lines[i].tabs != NULL) { 801 ts = field->lines[i].tabs; 802 while (ts != NULL) { 803 nts = ts->fwd; 804 free(ts); 805 ts = nts; 806 } 807 } 808 } 809 } else { 810 /* is linked, traverse the links to find the field referring 811 * to the one to be freed. 812 */ 813 for (flink = field->link; flink != field; flink = flink->link); 814 flink->link = field->link; 815 } 816 817 free(field); 818 return E_OK; 819} 820 821 822