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