1 /* $NetBSD: field.c,v 1.27 2013/11/21 15:40:17 christos 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 <sys/cdefs.h> 32 __RCSID("$NetBSD: field.c,v 1.27 2013/11/21 15:40:17 christos Exp $"); 33 34 #include <sys/param.h> 35 #include <stdlib.h> 36 #include <strings.h> 37 #include <stdarg.h> 38 #include <form.h> 39 #include "internals.h" 40 41 extern FORM _formi_default_form; 42 43 FIELD _formi_default_field = { 44 0, /* rows in the field */ 45 0, /* columns in the field */ 46 0, /* dynamic rows */ 47 0, /* dynamic columns */ 48 0, /* maximum growth */ 49 0, /* starting row in the form subwindow */ 50 0, /* starting column in the form subwindow */ 51 0, /* number of off screen rows */ 52 0, /* index of this field in form fields array. */ 53 0, /* number of buffers associated with this field */ 54 FALSE, /* set to true if buffer 0 has changed. */ 55 NO_JUSTIFICATION, /* justification style of the field */ 56 FALSE, /* set to true if field is in overlay mode */ 57 NULL, /* pointer to the current line cursor is on */ 58 0, /* starting char in string (horiz scroll) */ 59 NULL, /* starting line in field (vert scroll) */ 60 0, /* number of rows actually used in field */ 61 0, /* actual pos of cursor in row, not same as x pos due to tabs */ 62 0, /* x pos of cursor in field */ 63 0, /* y pos of cursor in field */ 64 0, /* start of a new page on the form if 1 */ 65 0, /* number of the page this field is on */ 66 A_NORMAL, /* character attributes for the foreground */ 67 A_NORMAL, /* character attributes for the background */ 68 ' ', /* padding character */ 69 DEFAULT_FORM_OPTS, /* options for the field */ 70 NULL, /* the form this field is bound to, if any */ 71 NULL, /* field above this one */ 72 NULL, /* field below this one */ 73 NULL, /* field to the left of this one */ 74 NULL, /* field to the right of this one */ 75 NULL, /* user defined pointer. */ 76 NULL, /* used if fields are linked */ 77 NULL, /* type struct for the field */ 78 {NULL, NULL}, /* circle queue glue for sorting fields */ 79 NULL, /* args for field type. */ 80 NULL, /* pointer to the array of lines structures. */ 81 NULL, /* list of lines available for reuse */ 82 NULL, /* array of buffers for the field */ 83 }; 84 85 /* internal function prototypes */ 86 static int 87 field_buffer_init(FIELD *field, int buffer, unsigned int len); 88 static FIELD * 89 _formi_create_field(FIELD *, int, int, int, int, int, int); 90 91 92 /* 93 * Set the userptr for the field 94 */ 95 int 96 set_field_userptr(FIELD *field, void *ptr) 97 { 98 FIELD *fp = (field == NULL) ? &_formi_default_field : field; 99 100 fp->userptr = ptr; 101 102 return E_OK; 103 } 104 105 /* 106 * Return the userptr for the field. 107 */ 108 109 void * 110 field_userptr(FIELD *field) 111 { 112 if (field == NULL) 113 return _formi_default_field.userptr; 114 else 115 return field->userptr; 116 } 117 118 /* 119 * Set the options for the designated field. 120 */ 121 int 122 set_field_opts(FIELD *field, Form_Options options) 123 { 124 int i; 125 126 FIELD *fp = (field == NULL) ? &_formi_default_field : field; 127 128 /* not allowed to set opts if the field is the current one */ 129 if ((field != NULL) && (field->parent != NULL) && 130 (field->parent->cur_field == field->index)) 131 return E_CURRENT; 132 133 if ((options & O_STATIC) == O_STATIC) { 134 for (i = 0; i < fp->nbuf; i++) { 135 if (fp->buffers[i].length > fp->cols) 136 fp->buffers[i].string[fp->cols] = '\0'; 137 } 138 } 139 140 fp->opts = options; 141 142 /* if appropriate, redraw the field */ 143 if ((field != NULL) && (field->parent != NULL) 144 && (field->parent->posted == 1)) { 145 _formi_redraw_field(field->parent, field->index); 146 pos_form_cursor(field->parent); 147 wrefresh(field->parent->scrwin); 148 } 149 150 return E_OK; 151 } 152 153 /* 154 * Turn on the passed field options. 155 */ 156 int 157 field_opts_on(FIELD *field, Form_Options options) 158 { 159 int i; 160 161 FIELD *fp = (field == NULL) ? &_formi_default_field : field; 162 163 /* not allowed to set opts if the field is the current one */ 164 if ((field != NULL) && (field->parent != NULL) && 165 (field->parent->cur_field == field->index)) 166 return E_CURRENT; 167 168 if ((options & O_STATIC) == O_STATIC) { 169 for (i = 0; i < fp->nbuf; i++) { 170 if (fp->buffers[i].length > fp->cols) 171 fp->buffers[i].string[fp->cols] = '\0'; 172 } 173 } 174 175 fp->opts |= options; 176 177 /* if appropriate, redraw the field */ 178 if ((field != NULL) && (field->parent != NULL) 179 && (field->parent->posted == 1)) { 180 _formi_redraw_field(field->parent, field->index); 181 pos_form_cursor(field->parent); 182 wrefresh(field->parent->scrwin); 183 } 184 185 return E_OK; 186 } 187 188 /* 189 * Turn off the passed field options. 190 */ 191 int 192 field_opts_off(FIELD *field, Form_Options options) 193 { 194 FIELD *fp = (field == NULL) ? &_formi_default_field : field; 195 196 /* not allowed to set opts if the field is the current one */ 197 if ((field != NULL) && (field->parent != NULL) && 198 (field->parent->cur_field == field->index)) 199 return E_CURRENT; 200 201 fp->opts &= ~options; 202 203 /* if appropriate, redraw the field */ 204 if ((field != NULL) && (field->parent != NULL) 205 && (field->parent->posted == 1)) { 206 _formi_redraw_field(field->parent, field->index); 207 pos_form_cursor(field->parent); 208 wrefresh(field->parent->scrwin); 209 } 210 211 return E_OK; 212 } 213 214 /* 215 * Return the field options associated with the passed field. 216 */ 217 Form_Options 218 field_opts(FIELD *field) 219 { 220 if (field == NULL) 221 return _formi_default_field.opts; 222 else 223 return field->opts; 224 } 225 226 /* 227 * Set the justification for the passed field. 228 */ 229 int 230 set_field_just(FIELD *field, int justification) 231 { 232 FIELD *fp = (field == NULL) ? &_formi_default_field : field; 233 234 /* 235 * not allowed to set justification if the field is 236 * the current one 237 */ 238 if ((field != NULL) && (field->parent != NULL) && 239 (field->parent->cur_field == field->index)) 240 return E_CURRENT; 241 242 if ((justification < MIN_JUST_STYLE) /* check justification valid */ 243 || (justification > MAX_JUST_STYLE)) 244 return E_BAD_ARGUMENT; 245 246 /* only allow justification on static, single row fields */ 247 if (((fp->opts & O_STATIC) != O_STATIC) || 248 ((fp->rows + fp->nrows) > 1)) 249 return E_BAD_ARGUMENT; 250 251 fp->justification = justification; 252 253 _formi_init_field_xpos(fp); 254 255 return E_OK; 256 } 257 258 /* 259 * Return the justification style of the field passed. 260 */ 261 int 262 field_just(FIELD *field) 263 { 264 if (field == NULL) 265 return _formi_default_field.justification; 266 else 267 return field->justification; 268 } 269 270 /* 271 * Return information about the field passed. 272 */ 273 int 274 field_info(FIELD *field, int *rows, int *cols, int *frow, int *fcol, 275 int *nrow, int *nbuf) 276 { 277 if (field == NULL) 278 return E_BAD_ARGUMENT; 279 280 *rows = field->rows; 281 *cols = field->cols; 282 *frow = field->form_row; 283 *fcol = field->form_col; 284 *nrow = field->nrows; 285 *nbuf = field->nbuf; 286 287 return E_OK; 288 } 289 290 /* 291 * Report the dynamic field information. 292 */ 293 int 294 dynamic_field_info(FIELD *field, int *drows, int *dcols, int *max) 295 { 296 if (field == NULL) 297 return E_BAD_ARGUMENT; 298 299 if ((field->opts & O_STATIC) == O_STATIC) { 300 *drows = field->rows; 301 *dcols = field->cols; 302 } else { 303 *drows = field->drows; 304 *dcols = field->dcols; 305 } 306 307 *max = field->max; 308 309 return E_OK; 310 } 311 312 /* 313 * Init all the field variables, perform wrapping and other tasks 314 * after the field buffer is set. 315 */ 316 static int 317 field_buffer_init(FIELD *field, int buffer, unsigned int len) 318 { 319 int status; 320 char *newp; 321 322 if (buffer == 0) { 323 field->start_char = 0; 324 field->start_line = 0; 325 field->row_xpos = 0; 326 field->cursor_xpos = 0; 327 field->cursor_ypos = 0; 328 field->row_count = 1; /* must be at least one row XXX need to shift old rows (if any) to free list??? */ 329 field->alines->length = len; 330 if ((newp = realloc(field->alines->string, 331 (size_t) len + 1)) == NULL) 332 return E_SYSTEM_ERROR; 333 field->alines->string = newp; 334 field->alines->allocated = len + 1; 335 strlcpy(field->alines->string, field->buffers[buffer].string, 336 (size_t) len + 1); 337 field->alines->expanded = 338 _formi_tab_expanded_length(field->alines->string, 339 0, field->alines->length); 340 341 field->start_line = field->alines; 342 field->cur_line = field->alines; 343 344 /* we have to hope the wrap works - if it does not then the 345 buffer is pretty much borked */ 346 status = _formi_wrap_field(field, field->cur_line); 347 if (status != E_OK) 348 return status; 349 350 /* 351 * calculate the tabs for a single row field, the 352 * multiline case is handled when the wrap is done. 353 */ 354 if (field->row_count == 1) 355 _formi_calculate_tabs(field->alines); 356 357 /* redraw the field to reflect the new contents. If the field 358 * is attached.... 359 */ 360 if ((field->parent != NULL) && (field->parent->posted == 1)) { 361 _formi_redraw_field(field->parent, field->index); 362 /* make sure cursor goes back to current field */ 363 pos_form_cursor(field->parent); 364 } 365 } 366 367 return E_OK; 368 } 369 370 371 /* 372 * Set the field buffer to the string that results from processing 373 * the given format (fmt) using sprintf. 374 */ 375 int 376 set_field_printf(FIELD *field, int buffer, char *fmt, ...) 377 { 378 int len; 379 va_list args; 380 381 if (field == NULL) 382 return E_BAD_ARGUMENT; 383 384 if (buffer >= field->nbuf) 385 return E_BAD_ARGUMENT; 386 387 va_start(args, fmt); 388 /* check for buffer already existing, free the storage */ 389 if (field->buffers[buffer].allocated != 0) 390 free(field->buffers[buffer].string); 391 392 len = vasprintf(&field->buffers[buffer].string, fmt, args); 393 va_end(args); 394 if (len < 0) 395 return E_SYSTEM_ERROR; 396 397 field->buffers[buffer].length = len; 398 field->buffers[buffer].allocated = len + 1; 399 if (((field->opts & O_STATIC) == O_STATIC) && (len > field->cols) 400 && ((field->rows + field->nrows) == 1)) 401 len = field->cols; 402 403 field->buffers[buffer].string[len] = '\0'; 404 return field_buffer_init(field, buffer, (unsigned int) len); 405 } 406 407 /* 408 * Set the value of the field buffer to the value given. 409 */ 410 411 int 412 set_field_buffer(FIELD *field, int buffer, char *value) 413 { 414 unsigned int len; 415 int status; 416 417 if (field == NULL) 418 return E_BAD_ARGUMENT; 419 420 if (buffer >= field->nbuf) /* make sure buffer is valid */ 421 return E_BAD_ARGUMENT; 422 423 len = (unsigned int) strlen(value); 424 if (((field->opts & O_STATIC) == O_STATIC) && (len > field->cols) 425 && ((field->rows + field->nrows) == 1)) 426 len = field->cols; 427 428 #ifdef DEBUG 429 if (_formi_create_dbg_file() != E_OK) 430 return E_SYSTEM_ERROR; 431 432 fprintf(dbg, 433 "set_field_buffer: entry: len = %d, value = %s, buffer=%d\n", 434 len, value, buffer); 435 fprintf(dbg, "set_field_buffer: entry: string = "); 436 if (field->buffers[buffer].string != NULL) 437 fprintf(dbg, "%s, len = %d\n", field->buffers[buffer].string, 438 field->buffers[buffer].length); 439 else 440 fprintf(dbg, "(null), len = 0\n"); 441 fprintf(dbg, "set_field_buffer: entry: lines.len = %d\n", 442 field->alines[0].length); 443 #endif 444 445 if ((field->buffers[buffer].string = 446 (char *) realloc(field->buffers[buffer].string, 447 (size_t) len + 1)) == NULL) 448 return E_SYSTEM_ERROR; 449 450 strlcpy(field->buffers[buffer].string, value, (size_t) len + 1); 451 field->buffers[buffer].length = len; 452 field->buffers[buffer].allocated = len + 1; 453 status = field_buffer_init(field, buffer, len); 454 455 #ifdef DEBUG 456 fprintf(dbg, "set_field_buffer: exit: len = %d, value = %s\n", 457 len, value); 458 fprintf(dbg, "set_field_buffer: exit: string = %s, len = %d\n", 459 field->buffers[buffer].string, field->buffers[buffer].length); 460 fprintf(dbg, "set_field_buffer: exit: lines.len = %d\n", 461 field->alines[0].length); 462 #endif 463 464 return status; 465 } 466 467 /* 468 * Return the requested field buffer to the caller. 469 */ 470 char * 471 field_buffer(FIELD *field, int buffer) 472 { 473 474 char *reformat, *p; 475 _FORMI_FIELD_LINES *linep; 476 size_t bufsize, pos; 477 478 if (field == NULL) 479 return NULL; 480 481 if (buffer >= field->nbuf) 482 return NULL; 483 484 /* 485 * We force a sync from the line structs to the buffer here. 486 * Traditional libform say we don't need to because it is 487 * done on a REQ_VALIDATE but NetBSD libform previously did 488 * not enforce this because the buffer contents were always 489 * current. Changes to line handling make this no longer so 490 * - the line structs may contain different data to the 491 * buffer if unsynced. 492 */ 493 if (_formi_sync_buffer(field) != E_OK) 494 return NULL; 495 496 if ((field->opts & O_REFORMAT) != O_REFORMAT) 497 return field->buffers[buffer].string; 498 499 if (field->row_count <= 1) 500 return strdup(field->buffers[buffer].string); 501 502 /* 503 * create a single string containing each line, 504 * separated by newline, last line having no 505 * newline, but NUL terminated. 506 */ 507 bufsize = pos = 0; 508 reformat = NULL; 509 for (linep = field->alines; linep; linep = linep->next) { 510 size_t len = strlen(linep->string); 511 if (len + 1 >= bufsize - pos) { 512 bufsize += MAX(1024, 2 * len); 513 p = realloc(reformat, bufsize); 514 if (p == NULL) { 515 free(reformat); 516 return NULL; 517 } 518 reformat = p; 519 } 520 memcpy(reformat + pos, linep->string, len); 521 pos += len; 522 reformat[pos++] = linep->next ? '\n' : '\0'; 523 } 524 return reformat; 525 } 526 527 /* 528 * Set the buffer 0 field status. 529 */ 530 int 531 set_field_status(FIELD *field, int status) 532 { 533 534 if (field == NULL) 535 return E_BAD_ARGUMENT; 536 537 if (status != FALSE) 538 field->buf0_status = TRUE; 539 else 540 field->buf0_status = FALSE; 541 542 return E_OK; 543 } 544 545 /* 546 * Return the buffer 0 status flag for the given field. 547 */ 548 int 549 field_status(FIELD *field) 550 { 551 552 if (field == NULL) /* the default buffer 0 never changes :-) */ 553 return FALSE; 554 555 return field->buf0_status; 556 } 557 558 /* 559 * Set the maximum growth for a dynamic field. 560 */ 561 int 562 set_max_field(FIELD *fptr, int max) 563 { 564 FIELD *field = (fptr == NULL)? &_formi_default_field : fptr; 565 566 if ((field->opts & O_STATIC) == O_STATIC) /* check if field dynamic */ 567 return E_BAD_ARGUMENT; 568 569 if (max < 0) /* negative numbers are bad.... */ 570 return E_BAD_ARGUMENT; 571 572 field->max = max; 573 return E_OK; 574 } 575 576 /* 577 * Set the field foreground character attributes. 578 */ 579 int 580 set_field_fore(FIELD *fptr, chtype attribute) 581 { 582 FIELD *field = (fptr == NULL)? &_formi_default_field : fptr; 583 584 field->fore = attribute; 585 return E_OK; 586 } 587 588 /* 589 * Return the foreground character attribute for the given field. 590 */ 591 chtype 592 field_fore(FIELD *field) 593 { 594 if (field == NULL) 595 return _formi_default_field.fore; 596 else 597 return field->fore; 598 } 599 600 /* 601 * Set the background character attribute for the given field. 602 */ 603 int 604 set_field_back(FIELD *field, chtype attribute) 605 { 606 if (field == NULL) 607 _formi_default_field.back = attribute; 608 else 609 field->back = attribute; 610 611 return E_OK; 612 } 613 614 /* 615 * Get the background character attribute for the given field. 616 */ 617 chtype 618 field_back(FIELD *field) 619 { 620 if (field == NULL) 621 return _formi_default_field.back; 622 else 623 return field->back; 624 } 625 626 /* 627 * Set the pad character for the given field. 628 */ 629 int 630 set_field_pad(FIELD *field, int pad) 631 { 632 if (field == NULL) 633 _formi_default_field.pad = pad; 634 else 635 field->pad = pad; 636 637 return E_OK; 638 } 639 640 /* 641 * Return the padding character for the given field. 642 */ 643 int 644 field_pad(FIELD *field) 645 { 646 if (field == NULL) 647 return _formi_default_field.pad; 648 else 649 return field->pad; 650 } 651 652 /* 653 * Set the field initialisation function hook. 654 */ 655 int 656 set_field_init(FORM *form, Form_Hook function) 657 { 658 if (form == NULL) 659 _formi_default_form.field_init = function; 660 else 661 form->field_init = function; 662 663 return E_OK; 664 } 665 666 /* 667 * Return the function hook for the field initialisation. 668 */ 669 Form_Hook 670 field_init(FORM *form) 671 { 672 if (form == NULL) 673 return _formi_default_form.field_init; 674 else 675 return form->field_init; 676 } 677 678 /* 679 * Set the field termination function hook. 680 */ 681 int 682 set_field_term(FORM *form, Form_Hook function) 683 { 684 if (form == NULL) 685 _formi_default_form.field_term = function; 686 else 687 form->field_term = function; 688 689 return E_OK; 690 } 691 692 /* 693 * Return the function hook defined for the field termination. 694 */ 695 Form_Hook 696 field_term(FORM *form) 697 { 698 if (form == NULL) 699 return _formi_default_form.field_term; 700 else 701 return form->field_term; 702 } 703 704 /* 705 * Set the page flag on the given field to indicate it is the start of a 706 * new page. 707 */ 708 int 709 set_new_page(FIELD *fptr, int page) 710 { 711 FIELD *field = (fptr == NULL)? &_formi_default_field : fptr; 712 713 if (field->parent != NULL) /* check if field is connected to a form */ 714 return E_CONNECTED; 715 716 field->page_break = (page != FALSE); 717 return E_OK; 718 } 719 720 /* 721 * Return the page status for the given field. TRUE is returned if the 722 * field is the start of a new page. 723 */ 724 int 725 new_page(FIELD *field) 726 { 727 if (field == NULL) 728 return _formi_default_field.page_break; 729 else 730 return field->page_break; 731 } 732 733 /* 734 * Return the index of the field in the form fields array. 735 */ 736 int 737 field_index(FIELD *field) 738 { 739 if (field == NULL) 740 return E_BAD_ARGUMENT; 741 742 if (field->parent == NULL) 743 return E_NOT_CONNECTED; 744 745 return field->index; 746 } 747 748 /* 749 * Internal function that does most of the work to create a new field. 750 * The new field is initialised from the information in the prototype 751 * field passed. 752 * Returns NULL on error. 753 */ 754 static FIELD * 755 _formi_create_field(FIELD *prototype, int rows, int cols, int frow, 756 int fcol, int nrows, int nbuf) 757 { 758 FIELD *new; 759 760 if ((rows <= 0) || (cols <= 0) || (frow < 0) || (fcol < 0) || 761 (nrows < 0) || (nbuf < 0)) 762 return NULL; 763 764 if ((new = (FIELD *)malloc(sizeof(FIELD))) == NULL) { 765 return NULL; 766 } 767 768 /* copy in the default field info */ 769 bcopy(prototype, new, sizeof(FIELD)); 770 771 new->nbuf = nbuf + 1; 772 new->rows = rows; 773 new->cols = cols; 774 new->form_row = frow; 775 new->form_col = fcol; 776 new->nrows = nrows; 777 new->link = new; 778 return new; 779 } 780 781 /* 782 * Create a new field structure. 783 */ 784 FIELD * 785 new_field(int rows, int cols, int frow, int fcol, int nrows, int nbuf) 786 { 787 FIELD *new; 788 size_t buf_len; 789 int i; 790 791 792 if ((new = _formi_create_field(&_formi_default_field, rows, cols, 793 frow, fcol, nrows, nbuf)) == NULL) 794 return NULL; 795 796 buf_len = (nbuf + 1) * sizeof(FORM_STR); 797 798 if ((new->buffers = (FORM_STR *)malloc(buf_len)) == NULL) { 799 free(new); 800 return NULL; 801 } 802 803 /* Initialise the strings to a zero length string */ 804 for (i = 0; i < nbuf + 1; i++) { 805 if ((new->buffers[i].string = 806 (char *) malloc(sizeof(char))) == NULL) { 807 free(new->buffers); 808 free(new); 809 return NULL; 810 } 811 new->buffers[i].string[0] = '\0'; 812 new->buffers[i].length = 0; 813 new->buffers[i].allocated = 1; 814 } 815 816 if ((new->alines = (_FORMI_FIELD_LINES *) 817 malloc(sizeof(struct _formi_field_lines))) == NULL) { 818 free(new->buffers); 819 free(new); 820 return NULL; 821 } 822 823 new->alines->prev = NULL; 824 new->alines->next = NULL; 825 new->alines->allocated = 0; 826 new->alines->length = 0; 827 new->alines->expanded = 0; 828 new->alines->string = NULL; 829 new->alines->hard_ret = FALSE; 830 new->alines->tabs = NULL; 831 new->start_line = new->alines; 832 new->cur_line = new->alines; 833 834 return new; 835 } 836 837 /* 838 * Duplicate the given field, including it's buffers. 839 */ 840 FIELD * 841 dup_field(FIELD *field, int frow, int fcol) 842 { 843 FIELD *new; 844 size_t row_len, buf_len; 845 846 if (field == NULL) 847 return NULL; 848 849 /* XXXX this right???? */ 850 if ((new = _formi_create_field(field, (int) field->rows, 851 (int ) field->cols, 852 frow, fcol, (int) field->nrows, 853 field->nbuf - 1)) == NULL) 854 return NULL; 855 856 row_len = (field->rows + field->nrows + 1) * field->cols; 857 buf_len = (field->nbuf + 1) * row_len * sizeof(FORM_STR); 858 859 if ((new->buffers = (FORM_STR *)malloc(buf_len)) == NULL) { 860 free(new); 861 return NULL; 862 } 863 864 /* copy the buffers from the source field into the new copy */ 865 bcopy(field->buffers, new->buffers, buf_len); 866 867 return new; 868 } 869 870 /* 871 * Create a new field at the specified location by duplicating the given 872 * field. The buffers are shared with the parent field. 873 */ 874 FIELD * 875 link_field(FIELD *field, int frow, int fcol) 876 { 877 FIELD *new; 878 879 if (field == NULL) 880 return NULL; 881 882 if ((new = _formi_create_field(field, (int) field->rows, 883 (int) field->cols, 884 frow, fcol, (int) field->nrows, 885 field->nbuf - 1)) == NULL) 886 return NULL; 887 888 new->link = field->link; 889 field->link = new; 890 891 /* we are done. The buffer pointer was copied during the field 892 creation. */ 893 return new; 894 } 895 896 /* 897 * Release all storage allocated to the field 898 */ 899 int 900 free_field(FIELD *field) 901 { 902 FIELD *flink; 903 int i; 904 _formi_tab_t *ts, *nts; 905 906 if (field == NULL) 907 return E_BAD_ARGUMENT; 908 909 if (field->parent != NULL) 910 return E_CONNECTED; 911 912 if (field->link == field) { /* check if field linked */ 913 /* no it is not - release the buffers */ 914 free(field->buffers); 915 /* free the tab structures */ 916 for (i = 0; i < field->row_count - 1; i++) { 917 if (field->alines[i].tabs != NULL) { 918 ts = field->alines[i].tabs; 919 while (ts != NULL) { 920 nts = ts->fwd; 921 free(ts); 922 ts = nts; 923 } 924 } 925 } 926 } else { 927 /* is linked, traverse the links to find the field referring 928 * to the one to be freed. 929 */ 930 for (flink = field->link; flink != field; flink = flink->link); 931 flink->link = field->link; 932 } 933 934 free(field); 935 return E_OK; 936 } 937 938 939