1 /*        $NetBSD: field.c,v 1.32 2021/04/13 13:13:03 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.32 2021/04/13 13:13:03 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
set_field_userptr(FIELD * field,void * ptr)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 *
field_userptr(FIELD * field)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
set_field_opts(FIELD * field,Form_Options options)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
field_opts_on(FIELD * field,Form_Options options)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
field_opts_off(FIELD * field,Form_Options options)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
field_opts(FIELD * field)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
set_field_just(FIELD * field,int justification)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
field_just(FIELD * field)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
field_info(FIELD * field,int * rows,int * cols,int * frow,int * fcol,int * nrow,int * nbuf)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
dynamic_field_info(FIELD * field,int * drows,int * dcols,int * max)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
field_buffer_init(FIELD * field,int buffer,unsigned int len)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
set_field_printf(FIELD * field,int buffer,char * fmt,...)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
set_field_buffer(FIELD * field,int buffer,const char * value)412 set_field_buffer(FIELD *field, int buffer, const 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           _formi_dbg_printf( "%s: len = %d, value = %s, buffer=%d\n", __func__,
429               len, value, buffer);
430           if (field->buffers[buffer].string != NULL)
431                     _formi_dbg_printf("%s: string=%s, len = %d\n", __func__,
432                         field->buffers[buffer].string,
433                         field->buffers[buffer].length);
434           else
435                     _formi_dbg_printf("%s: string=(null), len = 0\n", __func__);
436           _formi_dbg_printf("%s: lines.len = %d\n", __func__,
437               field->alines[0].length);
438 
439           if ((field->buffers[buffer].string = realloc(
440               field->buffers[buffer].string, (size_t) len + 1)) == NULL)
441                     return E_SYSTEM_ERROR;
442 
443           strlcpy(field->buffers[buffer].string, value, (size_t) len + 1);
444           field->buffers[buffer].length = len;
445           field->buffers[buffer].allocated = len + 1;
446           status = field_buffer_init(field, buffer, len);
447 
448           _formi_dbg_printf("%s: len = %d, value = %s\n", __func__, len, value);
449           _formi_dbg_printf("%s: string = %s, len = %d\n", __func__,
450               field->buffers[buffer].string, field->buffers[buffer].length);
451           _formi_dbg_printf("%s: lines.len = %d\n", __func__,
452                     field->alines[0].length);
453 
454           return status;
455 }
456 
457 /*
458  * Return the requested field buffer to the caller.
459  */
460 char *
field_buffer(FIELD * field,int buffer)461 field_buffer(FIELD *field, int buffer)
462 {
463 
464           char *reformat, *p;
465           _FORMI_FIELD_LINES *linep;
466           size_t bufsize, pos;
467 
468           if (field == NULL)
469                     return NULL;
470 
471           if (buffer >= field->nbuf)
472                     return NULL;
473 
474             /*
475              * We force a sync from the line structs to the buffer here.
476              * Traditional libform say we don't need to because it is
477              * done on a REQ_VALIDATE but NetBSD libform previously did
478              * not enforce this because the buffer contents were always
479              * current.  Changes to line handling make this no longer so
480              * - the line structs may contain different data to the
481              * buffer if unsynced.
482              */
483           if (_formi_sync_buffer(field) != E_OK)
484                     return NULL;
485 
486           if ((field->opts & O_REFORMAT) != O_REFORMAT)
487                     return field->buffers[buffer].string;
488 
489           if (field->row_count <= 1)
490                     return strdup(field->buffers[buffer].string);
491 
492           /*
493            * create a single string containing each line,
494            * separated by newline, last line having no
495            * newline, but NUL terminated.
496            */
497           bufsize = pos = 0;
498           reformat = NULL;
499           for (linep = field->alines; linep; linep = linep->next) {
500                     size_t len = strlen(linep->string);
501                     if (len + 1 >= bufsize - pos) {
502                               bufsize += MAX(1024, 2 * len);
503                               p = realloc(reformat, bufsize);
504                               if (p == NULL) {
505                                         free(reformat);
506                                         return NULL;
507                               }
508                               reformat = p;
509                     }
510                     memcpy(reformat + pos, linep->string, len);
511                     pos += len;
512                     reformat[pos++] = linep->next ? '\n' : '\0';
513           }
514           return reformat;
515 }
516 
517 /*
518  * Set the buffer 0 field status.
519  */
520 int
set_field_status(FIELD * field,int status)521 set_field_status(FIELD *field, int status)
522 {
523 
524           if (field == NULL)
525                     return E_BAD_ARGUMENT;
526 
527           if (status != FALSE)
528                     field->buf0_status = TRUE;
529           else
530                     field->buf0_status = FALSE;
531 
532           return E_OK;
533 }
534 
535 /*
536  * Return the buffer 0 status flag for the given field.
537  */
538 int
field_status(FIELD * field)539 field_status(FIELD *field)
540 {
541 
542           if (field == NULL) /* the default buffer 0 never changes :-) */
543                     return FALSE;
544 
545           return field->buf0_status;
546 }
547 
548 /*
549  * Set the maximum growth for a dynamic field.
550  */
551 int
set_max_field(FIELD * fptr,int max)552 set_max_field(FIELD *fptr, int max)
553 {
554           FIELD *field = (fptr == NULL)? &_formi_default_field : fptr;
555 
556           if ((field->opts & O_STATIC) == O_STATIC) /* check if field dynamic */
557                     return E_BAD_ARGUMENT;
558 
559           if (max < 0) /* negative numbers are bad.... */
560                     return E_BAD_ARGUMENT;
561 
562           field->max = max;
563           return E_OK;
564 }
565 
566 /*
567  * Set the field foreground character attributes.
568  */
569 int
set_field_fore(FIELD * fptr,chtype attribute)570 set_field_fore(FIELD *fptr, chtype attribute)
571 {
572           FIELD *field = (fptr == NULL)? &_formi_default_field : fptr;
573 
574           field->fore = attribute;
575           return E_OK;
576 }
577 
578 /*
579  * Return the foreground character attribute for the given field.
580  */
581 chtype
field_fore(FIELD * field)582 field_fore(FIELD *field)
583 {
584           if (field == NULL)
585                     return _formi_default_field.fore;
586           else
587                     return field->fore;
588 }
589 
590 /*
591  * Set the background character attribute for the given field.
592  */
593 int
set_field_back(FIELD * field,chtype attribute)594 set_field_back(FIELD *field, chtype attribute)
595 {
596           if (field == NULL)
597                     _formi_default_field.back = attribute;
598           else
599                     field->back = attribute;
600 
601           return E_OK;
602 }
603 
604 /*
605  * Get the background character attribute for the given field.
606  */
607 chtype
field_back(FIELD * field)608 field_back(FIELD *field)
609 {
610           if (field == NULL)
611                     return _formi_default_field.back;
612           else
613                     return field->back;
614 }
615 
616 /*
617  * Set the pad character for the given field.
618  */
619 int
set_field_pad(FIELD * field,int pad)620 set_field_pad(FIELD *field, int pad)
621 {
622           if (field == NULL)
623                     _formi_default_field.pad = pad;
624           else
625                     field->pad = pad;
626 
627           return E_OK;
628 }
629 
630 /*
631  * Return the padding character for the given field.
632  */
633 int
field_pad(FIELD * field)634 field_pad(FIELD *field)
635 {
636           if (field == NULL)
637                     return _formi_default_field.pad;
638           else
639                     return field->pad;
640 }
641 
642 /*
643  * Set the field initialisation function hook.
644  */
645 int
set_field_init(FORM * form,Form_Hook function)646 set_field_init(FORM *form, Form_Hook function)
647 {
648           if (form == NULL)
649                     _formi_default_form.field_init = function;
650           else
651                     form->field_init = function;
652 
653           return E_OK;
654 }
655 
656 /*
657  * Return the function hook for the field initialisation.
658  */
659 Form_Hook
field_init(FORM * form)660 field_init(FORM *form)
661 {
662           if (form == NULL)
663                     return _formi_default_form.field_init;
664           else
665                     return form->field_init;
666 }
667 
668 /*
669  * Set the field termination function hook.
670  */
671 int
set_field_term(FORM * form,Form_Hook function)672 set_field_term(FORM *form, Form_Hook function)
673 {
674           if (form == NULL)
675                     _formi_default_form.field_term = function;
676           else
677                     form->field_term = function;
678 
679           return E_OK;
680 }
681 
682 /*
683  * Return the function hook defined for the field termination.
684  */
685 Form_Hook
field_term(FORM * form)686 field_term(FORM *form)
687 {
688           if (form == NULL)
689                     return _formi_default_form.field_term;
690           else
691                     return form->field_term;
692 }
693 
694 /*
695  * Set the page flag on the given field to indicate it is the start of a
696  * new page.
697  */
698 int
set_new_page(FIELD * fptr,int page)699 set_new_page(FIELD *fptr, int page)
700 {
701           FIELD *field = (fptr == NULL)? &_formi_default_field : fptr;
702 
703           if (field->parent != NULL) /* check if field is connected to a form */
704                     return E_CONNECTED;
705 
706           field->page_break = (page != FALSE);
707           return E_OK;
708 }
709 
710 /*
711  * Return the page status for the given field.  TRUE is returned if the
712  * field is the start of a new page.
713  */
714 int
new_page(FIELD * field)715 new_page(FIELD *field)
716 {
717           if (field == NULL)
718                     return _formi_default_field.page_break;
719           else
720                     return field->page_break;
721 }
722 
723 /*
724  * Return the index of the field in the form fields array.
725  */
726 int
field_index(FIELD * field)727 field_index(FIELD *field)
728 {
729           if (field == NULL)
730                     return E_BAD_ARGUMENT;
731 
732           if (field->parent == NULL)
733                     return E_NOT_CONNECTED;
734 
735           return field->index;
736 }
737 
738 /*
739  * Internal function that does most of the work to create a new field.
740  * The new field is initialised from the information in the prototype
741  * field passed.
742  * Returns NULL on error.
743  */
744 static FIELD *
_formi_create_field(FIELD * prototype,int rows,int cols,int frow,int fcol,int nrows,int nbuf)745 _formi_create_field(FIELD *prototype, int rows, int cols, int frow,
746                         int fcol, int nrows, int nbuf)
747 {
748           FIELD *new;
749 
750           if ((rows <= 0) || (cols <= 0) || (frow < 0) || (fcol < 0) ||
751               (nrows < 0) || (nbuf < 0))
752                     return NULL;
753 
754           if ((new = malloc(sizeof(*new))) == NULL) {
755                     return NULL;
756           }
757 
758             /* copy in the default field info */
759           memcpy(new, prototype, sizeof(*new));
760 
761           new->nbuf = nbuf + 1;
762           new->rows = rows;
763           new->cols = cols;
764           new->form_row = frow;
765           new->form_col = fcol;
766           new->nrows = nrows;
767           new->link = new;
768           return new;
769 }
770 
771 /*
772  * Create a new field structure.
773  */
774 FIELD *
new_field(int rows,int cols,int frow,int fcol,int nrows,int nbuf)775 new_field(int rows, int cols, int frow, int fcol, int nrows, int nbuf)
776 {
777           FIELD *new;
778           int i;
779 
780 
781           if ((new = _formi_create_field(&_formi_default_field, rows, cols,
782                                                frow, fcol, nrows, nbuf)) == NULL)
783                     return NULL;
784 
785           if ((new->buffers = calloc(nbuf + 1, sizeof(*new->buffers))) == NULL) {
786                     free(new);
787                     return NULL;
788           }
789 
790           /* Initialise the strings to a zero length string */
791           for (i = 0; i < nbuf + 1; i++) {
792                     if ((new->buffers[i].string =
793                         malloc(sizeof(*new->buffers[i].string))) == NULL) {
794                               goto out;
795                     }
796                     new->buffers[i].string[0] = '\0';
797                     new->buffers[i].length = 0;
798                     new->buffers[i].allocated = 1;
799           }
800 
801           if ((new->alines = malloc(sizeof(*new->alines))) == NULL) {
802                     goto out;
803           }
804 
805           new->alines->prev = NULL;
806           new->alines->next = NULL;
807           new->alines->allocated = 0;
808           new->alines->length = 0;
809           new->alines->expanded = 0;
810           new->alines->string = NULL;
811           new->alines->hard_ret = FALSE;
812           new->alines->tabs = NULL;
813           new->start_line = new->alines;
814           new->cur_line = new->alines;
815 
816           return new;
817 out:
818           while (--i >= 0) {
819                     free(new->buffers[i].string);
820           }
821           free(new->buffers);
822           free(new);
823           return NULL;
824 }
825 
826 /*
827  * Duplicate the given field, including its buffers.
828  */
829 FIELD *
dup_field(FIELD * field,int frow,int fcol)830 dup_field(FIELD *field, int frow, int fcol)
831 {
832           FIELD *new;
833           size_t row_len, buf_len;
834 
835           if (field == NULL)
836                     return NULL;
837 
838           /* XXX: this right???? */
839           if ((new = _formi_create_field(field, (int) field->rows,
840                                                (int) field->cols,
841                                                frow, fcol, (int) field->nrows,
842                                                field->nbuf - 1)) == NULL)
843                     return NULL;
844 
845           row_len = (field->rows + field->nrows + 1) * field->cols;
846           buf_len = (field->nbuf + 1) * row_len * sizeof(*new->buffers);
847 
848           /* XXX: dups buffers but not their strings? */
849           if ((new->buffers = malloc(buf_len)) == NULL) {
850                     free(new);
851                     return NULL;
852           }
853 
854             /* copy the buffers from the source field into the new copy */
855           memcpy(new->buffers, field->buffers, buf_len);
856 
857           return new;
858 }
859 
860 /*
861  * Create a new field at the specified location by duplicating the given
862  * field.  The buffers are shared with the parent field.
863  */
864 FIELD *
link_field(FIELD * field,int frow,int fcol)865 link_field(FIELD *field, int frow, int fcol)
866 {
867           FIELD *new;
868 
869           if (field == NULL)
870                     return NULL;
871 
872           if ((new = _formi_create_field(field, (int) field->rows,
873                                                (int) field->cols,
874                                                frow, fcol, (int) field->nrows,
875                                                field->nbuf - 1)) == NULL)
876                     return NULL;
877 
878           new->link = field->link;
879           field->link = new;
880 
881             /* we are done.  The buffer pointer was copied during the field
882                creation. */
883           return new;
884 }
885 
886 /*
887  * Release all storage allocated to the field
888  */
889 int
free_field(FIELD * field)890 free_field(FIELD *field)
891 {
892           FIELD *flink;
893           unsigned int i;
894           _formi_tab_t *ts, *nts;
895 
896           if (field == NULL)
897                     return E_BAD_ARGUMENT;
898 
899           if (field->parent != NULL)
900                     return E_CONNECTED;
901 
902           if (field->link == field) { /* check if field linked */
903                       /* no it is not - release the buffers */
904                     free(field->buffers);
905                       /* free the tab structures */
906                     for (i = 0; i + 1 < field->row_count; i++) {
907                               if (field->alines[i].tabs != NULL) {
908                                         ts = field->alines[i].tabs;
909                                         while (ts != NULL) {
910                                                   nts = ts->fwd;
911                                                   free(ts);
912                                                   ts = nts;
913                                         }
914                               }
915                     }
916           } else {
917                       /* is linked, traverse the links to find the field referring
918                        * to the one to be freed.
919                        */
920                     for (flink = field->link; flink != field; flink = flink->link);
921                     flink->link = field->link;
922           }
923 
924           free(field);
925           return E_OK;
926 }
927 
928 
929