1 /* $NetBSD: field.c,v 1.29 2015/09/07 15:50:49 joerg 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.29 2015/09/07 15:50:49 joerg Exp $");
34 #include <sys/param.h>
39 #include "internals.h"
41 extern FORM _formi_default_form
;
43 FIELD _formi_default_field
= {
44 0, /* rows in the field */
45 0, /* columns in the field */
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 */
85 /* internal function prototypes */
87 field_buffer_init(FIELD
*field
, int buffer
, unsigned int len
);
89 _formi_create_field(FIELD
*, int, int, int, int, int, int);
93 * Set the userptr for the field
96 set_field_userptr(FIELD
*field
, void *ptr
)
98 FIELD
*fp
= (field
== NULL
) ? &_formi_default_field
: field
;
106 * Return the userptr for the field.
110 field_userptr(FIELD
*field
)
113 return _formi_default_field
.userptr
;
115 return field
->userptr
;
119 * Set the options for the designated field.
122 set_field_opts(FIELD
*field
, Form_Options options
)
126 FIELD
*fp
= (field
== NULL
) ? &_formi_default_field
: field
;
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
))
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';
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
);
154 * Turn on the passed field options.
157 field_opts_on(FIELD
*field
, Form_Options options
)
161 FIELD
*fp
= (field
== NULL
) ? &_formi_default_field
: field
;
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
))
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';
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
);
189 * Turn off the passed field options.
192 field_opts_off(FIELD
*field
, Form_Options options
)
194 FIELD
*fp
= (field
== NULL
) ? &_formi_default_field
: field
;
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
))
201 fp
->opts
&= ~options
;
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
);
215 * Return the field options associated with the passed field.
218 field_opts(FIELD
*field
)
221 return _formi_default_field
.opts
;
227 * Set the justification for the passed field.
230 set_field_just(FIELD
*field
, int justification
)
232 FIELD
*fp
= (field
== NULL
) ? &_formi_default_field
: field
;
235 * not allowed to set justification if the field is
238 if ((field
!= NULL
) && (field
->parent
!= NULL
) &&
239 (field
->parent
->cur_field
== field
->index
))
242 if ((justification
< MIN_JUST_STYLE
) /* check justification valid */
243 || (justification
> MAX_JUST_STYLE
))
244 return E_BAD_ARGUMENT
;
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
;
251 fp
->justification
= justification
;
253 _formi_init_field_xpos(fp
);
259 * Return the justification style of the field passed.
262 field_just(FIELD
*field
)
265 return _formi_default_field
.justification
;
267 return field
->justification
;
271 * Return information about the field passed.
274 field_info(FIELD
*field
, int *rows
, int *cols
, int *frow
, int *fcol
,
275 int *nrow
, int *nbuf
)
278 return E_BAD_ARGUMENT
;
282 *frow
= field
->form_row
;
283 *fcol
= field
->form_col
;
284 *nrow
= field
->nrows
;
291 * Report the dynamic field information.
294 dynamic_field_info(FIELD
*field
, int *drows
, int *dcols
, int *max
)
297 return E_BAD_ARGUMENT
;
299 if ((field
->opts
& O_STATIC
) == O_STATIC
) {
300 *drows
= field
->rows
;
301 *dcols
= field
->cols
;
303 *drows
= field
->drows
;
304 *dcols
= field
->dcols
;
313 * Init all the field variables, perform wrapping and other tasks
314 * after the field buffer is set.
317 field_buffer_init(FIELD
*field
, int buffer
, unsigned int len
)
323 field
->start_char
= 0;
324 field
->start_line
= 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
,
337 field
->alines
->expanded
=
338 _formi_tab_expanded_length(field
->alines
->string
,
339 0, field
->alines
->length
);
341 field
->start_line
= field
->alines
;
342 field
->cur_line
= field
->alines
;
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
);
351 * calculate the tabs for a single row field, the
352 * multiline case is handled when the wrap is done.
354 if (field
->row_count
== 1)
355 _formi_calculate_tabs(field
->alines
);
357 /* redraw the field to reflect the new contents. If the field
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
);
372 * Set the field buffer to the string that results from processing
373 * the given format (fmt) using sprintf.
376 set_field_printf(FIELD
*field
, int buffer
, char *fmt
, ...)
382 return E_BAD_ARGUMENT
;
384 if (buffer
>= field
->nbuf
)
385 return E_BAD_ARGUMENT
;
388 /* check for buffer already existing, free the storage */
389 if (field
->buffers
[buffer
].allocated
!= 0)
390 free(field
->buffers
[buffer
].string
);
392 len
= vasprintf(&field
->buffers
[buffer
].string
, fmt
, args
);
395 return E_SYSTEM_ERROR
;
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))
403 field
->buffers
[buffer
].string
[len
] = '\0';
404 return field_buffer_init(field
, buffer
, (unsigned int) len
);
408 * Set the value of the field buffer to the value given.
412 set_field_buffer(FIELD
*field
, int buffer
, const char *value
)
418 return E_BAD_ARGUMENT
;
420 if (buffer
>= field
->nbuf
) /* make sure buffer is valid */
421 return E_BAD_ARGUMENT
;
423 len
= (unsigned int) strlen(value
);
424 if (((field
->opts
& O_STATIC
) == O_STATIC
) && (len
> field
->cols
)
425 && ((field
->rows
+ field
->nrows
) == 1))
429 if (_formi_create_dbg_file() != E_OK
)
430 return E_SYSTEM_ERROR
;
433 "set_field_buffer: entry: len = %d, value = %s, buffer=%d\n",
435 fprintf(dbg
, "set_field_buffer: entry: string = ");
436 if (field
->buffers
[buffer
].string
!= NULL
)
437 fprintf(dbg
, "%s, len = %d\n", field
->buffers
[buffer
].string
,
438 field
->buffers
[buffer
].length
);
440 fprintf(dbg
, "(null), len = 0\n");
441 fprintf(dbg
, "set_field_buffer: entry: lines.len = %d\n",
442 field
->alines
[0].length
);
445 if ((field
->buffers
[buffer
].string
=
446 (char *) realloc(field
->buffers
[buffer
].string
,
447 (size_t) len
+ 1)) == NULL
)
448 return E_SYSTEM_ERROR
;
450 strlcpy(field
->buffers
[buffer
].string
, value
, (size_t) len
+ 1);
451 field
->buffers
[buffer
].length
= len
;
452 field
->buffers
[buffer
].allocated
= len
+ 1;
453 status
= field_buffer_init(field
, buffer
, len
);
456 fprintf(dbg
, "set_field_buffer: exit: len = %d, value = %s\n",
458 fprintf(dbg
, "set_field_buffer: exit: string = %s, len = %d\n",
459 field
->buffers
[buffer
].string
, field
->buffers
[buffer
].length
);
460 fprintf(dbg
, "set_field_buffer: exit: lines.len = %d\n",
461 field
->alines
[0].length
);
468 * Return the requested field buffer to the caller.
471 field_buffer(FIELD
*field
, int buffer
)
475 _FORMI_FIELD_LINES
*linep
;
481 if (buffer
>= field
->nbuf
)
485 * We force a sync from the line structs to the buffer here.
486 * Traditional libform say we don't need to because it is
487 * done on a REQ_VALIDATE but NetBSD libform previously did
488 * not enforce this because the buffer contents were always
489 * current. Changes to line handling make this no longer so
490 * - the line structs may contain different data to the
491 * buffer if unsynced.
493 if (_formi_sync_buffer(field
) != E_OK
)
496 if ((field
->opts
& O_REFORMAT
) != O_REFORMAT
)
497 return field
->buffers
[buffer
].string
;
499 if (field
->row_count
<= 1)
500 return strdup(field
->buffers
[buffer
].string
);
503 * create a single string containing each line,
504 * separated by newline, last line having no
505 * newline, but NUL terminated.
509 for (linep
= field
->alines
; linep
; linep
= linep
->next
) {
510 size_t len
= strlen(linep
->string
);
511 if (len
+ 1 >= bufsize
- pos
) {
512 bufsize
+= MAX(1024, 2 * len
);
513 p
= realloc(reformat
, bufsize
);
520 memcpy(reformat
+ pos
, linep
->string
, len
);
522 reformat
[pos
++] = linep
->next
? '\n' : '\0';
528 * Set the buffer 0 field status.
531 set_field_status(FIELD
*field
, int status
)
535 return E_BAD_ARGUMENT
;
538 field
->buf0_status
= TRUE
;
540 field
->buf0_status
= FALSE
;
546 * Return the buffer 0 status flag for the given field.
549 field_status(FIELD
*field
)
552 if (field
== NULL
) /* the default buffer 0 never changes :-) */
555 return field
->buf0_status
;
559 * Set the maximum growth for a dynamic field.
562 set_max_field(FIELD
*fptr
, int max
)
564 FIELD
*field
= (fptr
== NULL
)? &_formi_default_field
: fptr
;
566 if ((field
->opts
& O_STATIC
) == O_STATIC
) /* check if field dynamic */
567 return E_BAD_ARGUMENT
;
569 if (max
< 0) /* negative numbers are bad.... */
570 return E_BAD_ARGUMENT
;
577 * Set the field foreground character attributes.
580 set_field_fore(FIELD
*fptr
, chtype attribute
)
582 FIELD
*field
= (fptr
== NULL
)? &_formi_default_field
: fptr
;
584 field
->fore
= attribute
;
589 * Return the foreground character attribute for the given field.
592 field_fore(FIELD
*field
)
595 return _formi_default_field
.fore
;
601 * Set the background character attribute for the given field.
604 set_field_back(FIELD
*field
, chtype attribute
)
607 _formi_default_field
.back
= attribute
;
609 field
->back
= attribute
;
615 * Get the background character attribute for the given field.
618 field_back(FIELD
*field
)
621 return _formi_default_field
.back
;
627 * Set the pad character for the given field.
630 set_field_pad(FIELD
*field
, int pad
)
633 _formi_default_field
.pad
= pad
;
641 * Return the padding character for the given field.
644 field_pad(FIELD
*field
)
647 return _formi_default_field
.pad
;
653 * Set the field initialisation function hook.
656 set_field_init(FORM
*form
, Form_Hook function
)
659 _formi_default_form
.field_init
= function
;
661 form
->field_init
= function
;
667 * Return the function hook for the field initialisation.
670 field_init(FORM
*form
)
673 return _formi_default_form
.field_init
;
675 return form
->field_init
;
679 * Set the field termination function hook.
682 set_field_term(FORM
*form
, Form_Hook function
)
685 _formi_default_form
.field_term
= function
;
687 form
->field_term
= function
;
693 * Return the function hook defined for the field termination.
696 field_term(FORM
*form
)
699 return _formi_default_form
.field_term
;
701 return form
->field_term
;
705 * Set the page flag on the given field to indicate it is the start of a
709 set_new_page(FIELD
*fptr
, int page
)
711 FIELD
*field
= (fptr
== NULL
)? &_formi_default_field
: fptr
;
713 if (field
->parent
!= NULL
) /* check if field is connected to a form */
716 field
->page_break
= (page
!= FALSE
);
721 * Return the page status for the given field. TRUE is returned if the
722 * field is the start of a new page.
725 new_page(FIELD
*field
)
728 return _formi_default_field
.page_break
;
730 return field
->page_break
;
734 * Return the index of the field in the form fields array.
737 field_index(FIELD
*field
)
740 return E_BAD_ARGUMENT
;
742 if (field
->parent
== NULL
)
743 return E_NOT_CONNECTED
;
749 * Internal function that does most of the work to create a new field.
750 * The new field is initialised from the information in the prototype
752 * Returns NULL on error.
755 _formi_create_field(FIELD
*prototype
, int rows
, int cols
, int frow
,
756 int fcol
, int nrows
, int nbuf
)
760 if ((rows
<= 0) || (cols
<= 0) || (frow
< 0) || (fcol
< 0) ||
761 (nrows
< 0) || (nbuf
< 0))
764 if ((new = (FIELD
*)malloc(sizeof(FIELD
))) == NULL
) {
768 /* copy in the default field info */
769 bcopy(prototype
, new, sizeof(FIELD
));
771 new->nbuf
= nbuf
+ 1;
774 new->form_row
= frow
;
775 new->form_col
= fcol
;
782 * Create a new field structure.
785 new_field(int rows
, int cols
, int frow
, int fcol
, int nrows
, int nbuf
)
792 if ((new = _formi_create_field(&_formi_default_field
, rows
, cols
,
793 frow
, fcol
, nrows
, nbuf
)) == NULL
)
796 buf_len
= (nbuf
+ 1) * sizeof(FORM_STR
);
798 if ((new->buffers
= (FORM_STR
*)malloc(buf_len
)) == NULL
) {
803 /* Initialise the strings to a zero length string */
804 for (i
= 0; i
< nbuf
+ 1; i
++) {
805 if ((new->buffers
[i
].string
=
806 (char *) malloc(sizeof(char))) == NULL
) {
811 new->buffers
[i
].string
[0] = '\0';
812 new->buffers
[i
].length
= 0;
813 new->buffers
[i
].allocated
= 1;
816 if ((new->alines
= (_FORMI_FIELD_LINES
*)
817 malloc(sizeof(struct _formi_field_lines
))) == NULL
) {
823 new->alines
->prev
= NULL
;
824 new->alines
->next
= NULL
;
825 new->alines
->allocated
= 0;
826 new->alines
->length
= 0;
827 new->alines
->expanded
= 0;
828 new->alines
->string
= NULL
;
829 new->alines
->hard_ret
= FALSE
;
830 new->alines
->tabs
= NULL
;
831 new->start_line
= new->alines
;
832 new->cur_line
= new->alines
;
838 * Duplicate the given field, including its buffers.
841 dup_field(FIELD
*field
, int frow
, int fcol
)
844 size_t row_len
, buf_len
;
849 /* XXXX this right???? */
850 if ((new = _formi_create_field(field
, (int) field
->rows
,
852 frow
, fcol
, (int) field
->nrows
,
853 field
->nbuf
- 1)) == NULL
)
856 row_len
= (field
->rows
+ field
->nrows
+ 1) * field
->cols
;
857 buf_len
= (field
->nbuf
+ 1) * row_len
* sizeof(FORM_STR
);
859 if ((new->buffers
= (FORM_STR
*)malloc(buf_len
)) == NULL
) {
864 /* copy the buffers from the source field into the new copy */
865 bcopy(field
->buffers
, new->buffers
, buf_len
);
871 * Create a new field at the specified location by duplicating the given
872 * field. The buffers are shared with the parent field.
875 link_field(FIELD
*field
, int frow
, int fcol
)
882 if ((new = _formi_create_field(field
, (int) field
->rows
,
884 frow
, fcol
, (int) field
->nrows
,
885 field
->nbuf
- 1)) == NULL
)
888 new->link
= field
->link
;
891 /* we are done. The buffer pointer was copied during the field
897 * Release all storage allocated to the field
900 free_field(FIELD
*field
)
904 _formi_tab_t
*ts
, *nts
;
907 return E_BAD_ARGUMENT
;
909 if (field
->parent
!= NULL
)
912 if (field
->link
== field
) { /* check if field linked */
913 /* no it is not - release the buffers */
914 free(field
->buffers
);
915 /* free the tab structures */
916 for (i
= 0; i
< field
->row_count
- 1; i
++) {
917 if (field
->alines
[i
].tabs
!= NULL
) {
918 ts
= field
->alines
[i
].tabs
;
927 /* is linked, traverse the links to find the field referring
928 * to the one to be freed.
930 for (flink
= field
->link
; flink
!= field
; flink
= flink
->link
);
931 flink
->link
= field
->link
;