1 /* $NetBSD: field.c,v 1.23 2004/11/24 11:57:09 blymn Exp $ */
3 * Copyright (c) 1998-1999 Brett Lymn
4 * (blymn@baea.com.au, brett_lymn@yahoo.com.au)
7 * This code has been donated to The NetBSD Foundation by the Author.
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
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
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.
31 #include <sys/cdefs.h>
32 __RCSID("$NetBSD: field.c,v 1.23 2004/11/24 11:57:09 blymn Exp $");
38 #include "internals.h"
40 extern FORM _formi_default_form
;
42 FIELD _formi_default_field
= {
43 0, /* rows in the field */
44 0, /* columns in the field */
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 */
84 /* internal function prototypes */
86 field_buffer_init(FIELD
*field
, int buffer
, unsigned int len
);
88 _formi_create_field(FIELD
*, int, int, int, int, int, int);
92 * Set the userptr for the field
95 set_field_userptr(FIELD
*field
, void *ptr
)
97 FIELD
*fp
= (field
== NULL
) ? &_formi_default_field
: field
;
105 * Return the userptr for the field.
109 field_userptr(FIELD
*field
)
112 return _formi_default_field
.userptr
;
114 return field
->userptr
;
118 * Set the options for the designated field.
121 set_field_opts(FIELD
*field
, Form_Options options
)
125 FIELD
*fp
= (field
== NULL
) ? &_formi_default_field
: field
;
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
))
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';
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
);
153 * Turn on the passed field options.
156 field_opts_on(FIELD
*field
, Form_Options options
)
160 FIELD
*fp
= (field
== NULL
) ? &_formi_default_field
: field
;
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
))
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';
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
);
188 * Turn off the passed field options.
191 field_opts_off(FIELD
*field
, Form_Options options
)
193 FIELD
*fp
= (field
== NULL
) ? &_formi_default_field
: field
;
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
))
200 fp
->opts
&= ~options
;
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
);
214 * Return the field options associated with the passed field.
217 field_opts(FIELD
*field
)
220 return _formi_default_field
.opts
;
226 * Set the justification for the passed field.
229 set_field_just(FIELD
*field
, int justification
)
231 FIELD
*fp
= (field
== NULL
) ? &_formi_default_field
: field
;
234 * not allowed to set justification if the field is
237 if ((field
!= NULL
) && (field
->parent
!= NULL
) &&
238 (field
->parent
->cur_field
== field
->index
))
241 if ((justification
< MIN_JUST_STYLE
) /* check justification valid */
242 || (justification
> MAX_JUST_STYLE
))
243 return E_BAD_ARGUMENT
;
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
;
250 fp
->justification
= justification
;
252 _formi_init_field_xpos(fp
);
258 * Return the justification style of the field passed.
261 field_just(FIELD
*field
)
264 return _formi_default_field
.justification
;
266 return field
->justification
;
270 * Return information about the field passed.
273 field_info(FIELD
*field
, int *rows
, int *cols
, int *frow
, int *fcol
,
274 int *nrow
, int *nbuf
)
277 return E_BAD_ARGUMENT
;
281 *frow
= field
->form_row
;
282 *fcol
= field
->form_col
;
283 *nrow
= field
->nrows
;
290 * Report the dynamic field information.
293 dynamic_field_info(FIELD
*field
, int *drows
, int *dcols
, int *max
)
296 return E_BAD_ARGUMENT
;
298 if ((field
->opts
& O_STATIC
) == O_STATIC
) {
299 *drows
= field
->rows
;
300 *dcols
= field
->cols
;
302 *drows
= field
->drows
;
303 *dcols
= field
->dcols
;
312 * Init all the field variables, perform wrapping and other tasks
313 * after the field buffer is set.
316 field_buffer_init(FIELD
*field
, int buffer
, unsigned int len
)
322 field
->start_char
= 0;
323 field
->start_line
= 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
->lines
->length
= len
;
329 if ((newp
= realloc(field
->lines
->string
,
330 (size_t) len
+ 1)) == NULL
)
331 return E_SYSTEM_ERROR
;
332 field
->lines
->string
= newp
;
333 field
->lines
->allocated
= len
+ 1;
334 strlcpy(field
->lines
->string
, field
->buffers
[buffer
].string
,
336 field
->lines
->expanded
=
337 _formi_tab_expanded_length(field
->lines
->string
,
338 0, field
->lines
->length
);
340 field
->start_line
= field
->lines
;
341 field
->cur_line
= field
->lines
;
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
);
350 * calculate the tabs for a single row field, the
351 * multiline case is handled when the wrap is done.
353 if (field
->row_count
== 1)
354 _formi_calculate_tabs(field
->lines
);
356 /* redraw the field to reflect the new contents. If the field
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
);
371 * Set the field buffer to the string that results from processing
372 * the given format (fmt) using sprintf.
375 set_field_printf(FIELD
*field
, int buffer
, char *fmt
, ...)
381 return E_BAD_ARGUMENT
;
383 if (buffer
>= field
->nbuf
)
384 return E_BAD_ARGUMENT
;
387 /* check for buffer already existing, free the storage */
388 if (field
->buffers
[buffer
].allocated
!= 0)
389 free(field
->buffers
[buffer
].string
);
391 len
= vasprintf(&field
->buffers
[buffer
].string
, fmt
, args
);
394 return E_SYSTEM_ERROR
;
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))
402 field
->buffers
[buffer
].string
[len
] = '\0';
403 return field_buffer_init(field
, buffer
, (unsigned int) len
);
407 * Set the value of the field buffer to the value given.
411 set_field_buffer(FIELD
*field
, int buffer
, char *value
)
417 return E_BAD_ARGUMENT
;
419 if (buffer
>= field
->nbuf
) /* make sure buffer is valid */
420 return E_BAD_ARGUMENT
;
422 len
= (unsigned int) strlen(value
);
423 if (((field
->opts
& O_STATIC
) == O_STATIC
) && (len
> field
->cols
)
424 && ((field
->rows
+ field
->nrows
) == 1))
428 if (_formi_create_dbg_file() != E_OK
)
429 return E_SYSTEM_ERROR
;
432 "set_field_buffer: entry: len = %d, value = %s, buffer=%d\n",
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
);
439 fprintf(dbg
, "(null), len = 0\n");
440 fprintf(dbg
, "set_field_buffer: entry: lines.len = %d\n",
441 field
->lines
[0].length
);
444 if ((field
->buffers
[buffer
].string
=
445 (char *) realloc(field
->buffers
[buffer
].string
,
446 (size_t) len
+ 1)) == NULL
)
447 return E_SYSTEM_ERROR
;
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
);
455 fprintf(dbg
, "set_field_buffer: exit: len = %d, value = %s\n",
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
->lines
[0].length
);
467 * Return the requested field buffer to the caller.
470 field_buffer(FIELD
*field
, int buffer
)
474 _FORMI_FIELD_LINES
*linep
;
479 if (buffer
>= field
->nbuf
)
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.
491 if (_formi_sync_buffer(field
) != E_OK
)
494 if ((field
->opts
& O_REFORMAT
) != O_REFORMAT
) {
495 return field
->buffers
[buffer
].string
;
497 if (field
->row_count
> 1) {
500 malloc(strlen(field
->buffers
[buffer
].string
)
501 + ((field
->row_count
- 1)
502 * sizeof(char)) + 1);
504 if (reformat
== NULL
)
508 * foreach row copy line, append newline, no
509 * newline on last row.
512 linep
= field
->lines
;
516 if (linep
->length
!= 0) {
517 strncpy(p
, linep
->string
,
518 (size_t) linep
->length
);
527 while (linep
!= NULL
);
532 asprintf(&reformat
, "%s",
533 field
->buffers
[buffer
].string
);
540 * Set the buffer 0 field status.
543 set_field_status(FIELD
*field
, int status
)
547 return E_BAD_ARGUMENT
;
550 field
->buf0_status
= TRUE
;
552 field
->buf0_status
= FALSE
;
558 * Return the buffer 0 status flag for the given field.
561 field_status(FIELD
*field
)
564 if (field
== NULL
) /* the default buffer 0 never changes :-) */
567 return field
->buf0_status
;
571 * Set the maximum growth for a dynamic field.
574 set_max_field(FIELD
*fptr
, int max
)
576 FIELD
*field
= (fptr
== NULL
)? &_formi_default_field
: fptr
;
578 if ((field
->opts
& O_STATIC
) == O_STATIC
) /* check if field dynamic */
579 return E_BAD_ARGUMENT
;
581 if (max
< 0) /* negative numbers are bad.... */
582 return E_BAD_ARGUMENT
;
589 * Set the field foreground character attributes.
592 set_field_fore(FIELD
*fptr
, chtype attribute
)
594 FIELD
*field
= (fptr
== NULL
)? &_formi_default_field
: fptr
;
596 field
->fore
= attribute
;
601 * Return the foreground character attribute for the given field.
604 field_fore(FIELD
*field
)
607 return _formi_default_field
.fore
;
613 * Set the background character attribute for the given field.
616 set_field_back(FIELD
*field
, chtype attribute
)
619 _formi_default_field
.back
= attribute
;
621 field
->back
= attribute
;
627 * Get the background character attribute for the given field.
630 field_back(FIELD
*field
)
633 return _formi_default_field
.back
;
639 * Set the pad character for the given field.
642 set_field_pad(FIELD
*field
, int pad
)
645 _formi_default_field
.pad
= pad
;
653 * Return the padding character for the given field.
656 field_pad(FIELD
*field
)
659 return _formi_default_field
.pad
;
665 * Set the field initialisation function hook.
668 set_field_init(FORM
*form
, Form_Hook function
)
671 _formi_default_form
.field_init
= function
;
673 form
->field_init
= function
;
679 * Return the function hook for the field initialisation.
682 field_init(FORM
*form
)
685 return _formi_default_form
.field_init
;
687 return form
->field_init
;
691 * Set the field termination function hook.
694 set_field_term(FORM
*form
, Form_Hook function
)
697 _formi_default_form
.field_term
= function
;
699 form
->field_term
= function
;
705 * Return the function hook defined for the field termination.
708 field_term(FORM
*form
)
711 return _formi_default_form
.field_term
;
713 return form
->field_term
;
717 * Set the page flag on the given field to indicate it is the start of a
721 set_new_page(FIELD
*fptr
, int page
)
723 FIELD
*field
= (fptr
== NULL
)? &_formi_default_field
: fptr
;
725 if (field
->parent
!= NULL
) /* check if field is connected to a form */
728 field
->page_break
= (page
!= FALSE
);
733 * Return the page status for the given field. TRUE is returned if the
734 * field is the start of a new page.
737 new_page(FIELD
*field
)
740 return _formi_default_field
.page_break
;
742 return field
->page_break
;
746 * Return the index of the field in the form fields array.
749 field_index(FIELD
*field
)
752 return E_BAD_ARGUMENT
;
754 if (field
->parent
== NULL
)
755 return E_NOT_CONNECTED
;
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
764 * Returns NULL on error.
767 _formi_create_field(FIELD
*prototype
, int rows
, int cols
, int frow
,
768 int fcol
, int nrows
, int nbuf
)
772 if ((rows
<= 0) || (cols
<= 0) || (frow
< 0) || (fcol
< 0) ||
773 (nrows
< 0) || (nbuf
< 0))
776 if ((new = (FIELD
*)malloc(sizeof(FIELD
))) == NULL
) {
780 /* copy in the default field info */
781 bcopy(prototype
, new, sizeof(FIELD
));
783 new->nbuf
= nbuf
+ 1;
786 new->form_row
= frow
;
787 new->form_col
= fcol
;
794 * Create a new field structure.
797 new_field(int rows
, int cols
, int frow
, int fcol
, int nrows
, int nbuf
)
804 if ((new = _formi_create_field(&_formi_default_field
, rows
, cols
,
805 frow
, fcol
, nrows
, nbuf
)) == NULL
)
808 buf_len
= (nbuf
+ 1) * sizeof(FORM_STR
);
810 if ((new->buffers
= (FORM_STR
*)malloc(buf_len
)) == NULL
) {
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
) {
823 new->buffers
[i
].string
[0] = '\0';
824 new->buffers
[i
].length
= 0;
825 new->buffers
[i
].allocated
= 1;
828 if ((new->lines
= (_FORMI_FIELD_LINES
*)
829 malloc(sizeof(struct _formi_field_lines
))) == NULL
) {
835 new->lines
->prev
= NULL
;
836 new->lines
->next
= NULL
;
837 new->lines
->allocated
= 0;
838 new->lines
->length
= 0;
839 new->lines
->expanded
= 0;
840 new->lines
->string
= NULL
;
841 new->lines
->hard_ret
= FALSE
;
842 new->lines
->tabs
= NULL
;
843 new->start_line
= new->lines
;
844 new->cur_line
= new->lines
;
850 * Duplicate the given field, including it's buffers.
853 dup_field(FIELD
*field
, int frow
, int fcol
)
856 size_t row_len
, buf_len
;
861 /* XXXX this right???? */
862 if ((new = _formi_create_field(field
, (int) field
->rows
,
864 frow
, fcol
, (int) field
->nrows
,
865 field
->nbuf
- 1)) == NULL
)
868 row_len
= (field
->rows
+ field
->nrows
+ 1) * field
->cols
;
869 buf_len
= (field
->nbuf
+ 1) * row_len
* sizeof(FORM_STR
);
871 if ((new->buffers
= (FORM_STR
*)malloc(buf_len
)) == NULL
) {
876 /* copy the buffers from the source field into the new copy */
877 bcopy(field
->buffers
, new->buffers
, buf_len
);
883 * Create a new field at the specified location by duplicating the given
884 * field. The buffers are shared with the parent field.
887 link_field(FIELD
*field
, int frow
, int fcol
)
894 if ((new = _formi_create_field(field
, (int) field
->rows
,
896 frow
, fcol
, (int) field
->nrows
,
897 field
->nbuf
- 1)) == NULL
)
900 new->link
= field
->link
;
903 /* we are done. The buffer pointer was copied during the field
909 * Release all storage allocated to the field
912 free_field(FIELD
*field
)
916 _formi_tab_t
*ts
, *nts
;
919 return E_BAD_ARGUMENT
;
921 if (field
->parent
!= NULL
)
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
->lines
[i
].tabs
!= NULL
) {
930 ts
= field
->lines
[i
].tabs
;
939 /* is linked, traverse the links to find the field referring
940 * to the one to be freed.
942 for (flink
= field
->link
; flink
!= field
; flink
= flink
->link
);
943 flink
->link
= field
->link
;