xref: /netbsd/lib/libform/field.c (revision 13876d4e)
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