diff --git a/lib/Makefile b/lib/Makefile index 493a8234ed59..cc2357973dee 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -10,9 +10,9 @@ SUBDIR=csu/${MACHINE} .endif # XXX MISSING: libmp libplot -SUBDIR+= libc libcompat libcrypt libcurses libedit libf2c libkvm libmd \ - libmytinfo libncurses libresolv librpcsvc libskey libtelnet \ - libtermcap libutil liby +SUBDIR+= libc libcompat libcrypt libcurses libedit libf2c libforms \ + libkvm libmd libmytinfo libncurses libresolv librpcsvc libskey \ + libtelnet libtermcap libutil liby .if !defined(WANT_MSUN) SUBDIR+= libm diff --git a/lib/libforms/Makefile b/lib/libforms/Makefile new file mode 100644 index 000000000000..9dae66d6643d --- /dev/null +++ b/lib/libforms/Makefile @@ -0,0 +1,13 @@ +LIB = forms + +SRCS = forms.c yacc.c lex.c +CLEANFILES += y.tab.h lex.c yacc.c + +CFLAGS = -I. -I${.CURDIR} + +beforeinstall: + @(cd ${.CURDIR}; cmp -s forms.h ${DESTDIR}/usr/include/forms.h || \ + install -c -o ${BINOWN} -g ${BINGRP} -m 444 forms.h \ + ${DESTDIR}/usr/include/forms.h;) + +.include diff --git a/lib/libforms/examples/Makefile b/lib/libforms/examples/Makefile new file mode 100644 index 000000000000..5e175b5c4398 --- /dev/null +++ b/lib/libforms/examples/Makefile @@ -0,0 +1,7 @@ +PROG = tform + +CFLAGS = -Wall -I. -I${.CURDIR} +LDADD = -lforms -lncurses -ll +DPADD = ${LIBFORMS} ${LIBNCURSES} ${LIBL} + +.include diff --git a/lib/libforms/examples/example.frm b/lib/libforms/examples/example.frm new file mode 100644 index 000000000000..f09ded5ea535 --- /dev/null +++ b/lib/libforms/examples/example.frm @@ -0,0 +1,6 @@ +Form template: 0 0 24 79 +Text 0 0 "This is non-editable text field" +Input 1 2 4 2 3 3 2 2 8 "Prompt1:" 2 20 10 25 "First" +Input 2 3 1 4 2 2 5 2 8 "Prompt2:" 5 20 10 25 "Second" +Input 3 4 2 4 1 1 2 40 8 "Prompt3:" 2 50 10 25 "Third" +Input 4 1 2 1 4 4 10 2 8 "Prompt4:" 10 20 10 25 "Fourth" diff --git a/lib/libforms/examples/tform.c b/lib/libforms/examples/tform.c new file mode 100644 index 000000000000..48f4d48bab0a --- /dev/null +++ b/lib/libforms/examples/tform.c @@ -0,0 +1,18 @@ +#include +#include +#include +#include +#include + +extern struct form *form; + +void +main() +{ + printf("Testing forms code\n"); + + if (init_forms("example.frm") == -1) + exit(1); + + edit_form(form); +} diff --git a/lib/libforms/forms.c b/lib/libforms/forms.c new file mode 100644 index 000000000000..5c29b10f3aff --- /dev/null +++ b/lib/libforms/forms.c @@ -0,0 +1,333 @@ +#include +#include +#include + +extern FILE *yyin; + +struct form *form; +unsigned int keymap[FORM_NO_KEYS] = { + KEY_BTAB, + 9, + KEY_UP, + KEY_DOWN, + '\r', + '\033', + KEY_HOME, + KEY_END, + KEY_LEFT, + KEY_RIGHT, + KEY_BACKSPACE, + KEY_DC +}; + +int +edit_field(WINDOW *window, struct field *field) +{ + int len; + int key = 0; + int fpos, dispos, curpos; + int i; + int done = 0; + + len = strlen(field->entry.input.field); + if (len < field->entry.input.field_width) { + fpos = len; + curpos = len; + dispos = 0; + } else { + fpos = field->entry.input.field_width; + curpos = field->entry.input.field_width; + dispos = len - field->entry.input.field_width; + }; + + field->entry.input.field_attr = FORM_SELECTED_ATTR; + do { + wattrset(window, field->entry.input.field_attr); + wmove(window, field->entry.input.y_field, field->entry.input.x_field); + for (i=0; i < field->entry.input.field_width; i++) + if (i < (len - dispos)) + waddch(window, field->entry.input.field[dispos+i]); + else + waddch(window, ' '); + wmove(window, field->entry.input.y_field, field->entry.input.x_field + curpos); + wrefresh(window); + + key = wgetch(window); + if (key == keymap[FORM_LEFT] || + key == keymap[FORM_RIGHT] || + key == keymap[FORM_UP] || + key == keymap[FORM_DOWN] || + key == keymap[FORM_EXIT] || + key == '\n' || + key == '\r') { + done = 1; + } else if (key == keymap[FORM_FIELD_HOME]) { + if (len < field->entry.input.field_width) { + fpos = len; + curpos = len; + dispos = 0; + } else { + fpos = field->entry.input.field_width; + curpos = field->entry.input.field_width; + dispos = len - field->entry.input.field_width; + }; + } else if (key == keymap[FORM_FIELD_END]) { + if (len < field->entry.input.field_width) { + dispos = 0; + curpos = len - 1; + } else { + dispos = len - field->entry.input.field_width - 1; + curpos = field->entry.input.field_width - 1; + } + fpos = len - 1; + } else if (key == keymap[FORM_FIELD_LEFT]) { + if ((!curpos) && (!dispos)) { + beep(); + } else { + if (--curpos < 0) { + curpos = 0; + if (--dispos < 0) + dispos = 0; + } + if (--fpos < 0) + fpos = 0; + } + } else if (key == keymap[FORM_FIELD_RIGHT]) { + if ((curpos + dispos) == len) { + beep(); + } else if ((curpos == (field->entry.input.field_width-1)) && + (dispos == (field->entry.input.max_field_width - field->entry.input.field_width -1))) { + beep(); + } else { + if (++curpos >= field->entry.input.field_width) { + curpos = field->entry.input.field_width - 1; + dispos++; + } + if (dispos >= len) + dispos = len - 1; + if (++fpos >= len) { + fpos = len; + } + } + } else if (key == keymap[FORM_FIELD_BACKSPACE]) { + if ((!curpos) && (!dispos)) { + beep(); + } else if (fpos > 0) { + memmove(&field->entry.input.field[fpos-1], &field->entry.input.field[fpos], len - fpos); + len--; + fpos--; + if (curpos > 0) + --curpos; + if (!curpos) + --dispos; + if (dispos < 0) + dispos = 0; + } else + beep(); + } else { + if (len < field->entry.input.max_field_width - 1) { + memmove(&field->entry.input.field[fpos+1], &field->entry.input.field[fpos], len - fpos); + field->entry.input.field[fpos] = key; + len++; + fpos++; + if (++curpos == field->entry.input.field_width) { + --curpos; + dispos++; + } + if (len == (field->entry.input.max_field_width - 1)) { + dispos = (field->entry.input.max_field_width - field->entry.input.field_width - 1); + } + } else + beep(); + } + } while (!done); + + field->entry.input.field_attr = FORM_DEFAULT_ATTR; + wattrset(window, field->entry.input.field_attr); + wmove(window, field->entry.input.y_field, field->entry.input.x_field); + for (i=0; i < field->entry.input.field_width; i++) + if (i < (len - dispos)) + waddch(window, field->entry.input.field[dispos+i]); + else + waddch(window, ' '); + wmove(window, field->entry.input.y_field, field->entry.input.x_field + curpos); + wrefresh(window); + + field->entry.input.field[len] = 0; + delwin(window); + refresh(); + return (key); +} + +int +init_forms(char *template) +{ + FILE *fd; + struct field *link, *next; + + /* Intialise lex input */ + if (!(fd = fopen(template, "r"))) { + fprintf(stderr, "Couldn't open template file %s\n", template); + return(-1); + } + + if (!initscr()) { + fprintf(stderr, "Failed to initialise curses\n"); + return(-1); + } + + cbreak(); + noecho(); + nonl(); + + yyin = fd; + yyparse(); + + /* Setup up links to/from fields */ + + for (next = form->fields; next; next = next->link) { + /* Ignore the link values of text fields */ + if (next->type == FORM_FTYPE_TEXT) + continue; + link = find_link((int)next->next); + if (!link) { + fprintf(stderr, "Bad link (next) from %d to %d\n", + next->field_id, (int)next->next); + next->next = 0; + } else + next->next = link; + link = find_link((int)next->up); + if (!link) { + fprintf(stderr, "Bad link (up) from %d to %d\n", + next->field_id, (int)next->up); + next->up = 0; + } else + next->up = link; + link = find_link((int)next->down); + if (!link) { + fprintf(stderr, "Bad link (down) from %d to %d\n", + next->field_id, (int)next->down); + next->down = 0; + } else + next->down = link; + link = find_link((int)next->left); + if (!link) { + fprintf(stderr, "Bad link (left) from %d to %d\n", + next->field_id, (int)next->left); + next->left = 0; + } else + next->left = link; + link = find_link((int)next->right); + if (!link) { + fprintf(stderr, "Bad link (right) from %d to %d\n", + next->field_id, (int)next->right); + next->right = 0; + } else + next->right = link; + } +} + +struct field * +find_link(int id) +{ + struct field *next; + + for (next=form->fields; next; next=next->link) + /* You can't move into a text field */ + if ((id == next->field_id) && (next->type != FORM_FTYPE_TEXT)) + return (next); + return(0); +} + +void +edit_form(struct form *form) +{ + WINDOW *window; + struct field *cur_field; + int key; + + window = newwin(form->height, form->width, form->y, form->x); + keypad(window, TRUE); + + refresh_form(window, form); + + cur_field = form->fields; + + do { + /* Just skip over text fields */ + if (cur_field->type == FORM_FTYPE_TEXT) { + cur_field = cur_field->link; + continue; + } + switch (cur_field->type) { + case FORM_FTYPE_INPUT: + key = edit_field(window, cur_field); + break; + case FORM_FTYPE_MENU: + case FORM_FTYPE_BUTTON: + case FORM_FTYPE_TEXT: /* Should never happen */ + default: + break; + } + if (key == keymap[FORM_UP]) { + if (cur_field->up) + cur_field = cur_field->up; + else + beep(); + } else if (key == keymap[FORM_DOWN]) { + if (cur_field->down) + cur_field = cur_field->down; + else + beep(); + } else if (key == keymap[FORM_LEFT]) { + if (cur_field->left) + cur_field = cur_field->left; + else + beep(); + } else if (key == keymap[FORM_RIGHT]) { + if (cur_field->right) + cur_field = cur_field->right; + else + beep(); + } else if (key == keymap[FORM_NEXT]) { + if (cur_field->next) + cur_field = cur_field->next; + else + cur_field = form->fields; + } else + beep(); + } while (key != keymap[FORM_EXIT]); +} + +void +refresh_form(WINDOW *window, struct form *form) +{ + struct field *cur_field; + + cur_field = form->fields; + + while (cur_field) { + switch (cur_field->type) { + case FORM_FTYPE_INPUT: + wattrset(window, cur_field->entry.input.prompt_attr); + mvwprintw(window, cur_field->entry.input.y_prompt, + cur_field->entry.input.x_prompt, + "%s", cur_field->entry.input.prompt); + wattrset(window, cur_field->entry.input.field_attr); + mvwprintw(window, cur_field->entry.input.y_field, + cur_field->entry.input.x_field, + "%s", cur_field->entry.input.field); + break; + case FORM_FTYPE_TEXT: + wattrset(window, cur_field->entry.text.attr); + mvwprintw(window, cur_field->entry.text.y, + cur_field->entry.text.x, + "%s", cur_field->entry.text.text); + break; + default: + break; + } + cur_field = cur_field->link; + } + wrefresh(window); +} diff --git a/lib/libforms/forms.h b/lib/libforms/forms.h new file mode 100644 index 000000000000..606bd1cd04e9 --- /dev/null +++ b/lib/libforms/forms.h @@ -0,0 +1,81 @@ +#define FORM_NO_KEYS 12 +#define FORM_LEFT 0 +#define FORM_RIGHT 1 +#define FORM_UP 2 +#define FORM_DOWN 3 +#define FORM_NEXT 4 +#define FORM_EXIT 5 +#define FORM_FIELD_HOME 6 +#define FORM_FIELD_END 7 +#define FORM_FIELD_LEFT 8 +#define FORM_FIELD_RIGHT 9 +#define FORM_FIELD_BACKSPACE 10 +#define FORM_FIELD_DELETE 11 + +/* Attribute values */ +#define FORM_DEFAULT_ATTR 0 +#define FORM_SELECTED_ATTR 0 + +/* Field types */ +#define FORM_FTYPE_INPUT 0 +#define FORM_FTYPE_MENU 1 +#define FORM_FTYPE_BUTTON 2 +#define FORM_FTYPE_TEXT 3 + +#define MAX_FIELD_SIZE 80 + +struct form { + int x; + int y; + int height; + int width; + struct field *fields; +}; + +struct input_field { + int y_prompt; + int x_prompt; + int prompt_width; + int prompt_attr; + char *prompt; + int y_field; + int x_field; + int field_width; + int max_field_width; + int field_attr; + char *field; +}; + +struct text_field { + int y; + int x; + int attr; + char *text; +}; + +struct field { + int field_id; + int type; + union { + struct input_field input; + struct text_field text; +#ifdef notyet + struct menu_field menu + struct button_field button; +#endif + } entry; + struct field *link; + struct field *next; + struct field *up; + struct field *down; + struct field *left; + struct field *right; +}; + +extern unsigned int keymap[FORM_NO_KEYS]; + +int init_forms(); +int edit_line(WINDOW *window, struct field *); +void edit_form(struct form *); +void refresh_form(WINDOW *, struct form *); +struct field *find_link(int); diff --git a/lib/libforms/lex.l b/lib/libforms/lex.l new file mode 100644 index 000000000000..deb6c8d655d0 --- /dev/null +++ b/lib/libforms/lex.l @@ -0,0 +1,19 @@ +%{ +#include "y.tab.h" +%} + +%% +"Form template:" { yylval.ival = FORM; return FORM; } +Input { yylval.ival = INPUT; return INPUT; } +Text { yylval.ival = TEXT; return TEXT; } + +[0-9]+ { yylval.ival = atoi(yytext); return NUMBER; } +\"[^"]* { + if (yytext[yyleng-1] == '\\') { + yymore(); + } else { + input(); + yylval.sval = yytext+1; + return STRING; + } + } diff --git a/lib/libforms/yacc.y b/lib/libforms/yacc.y new file mode 100644 index 000000000000..2bc953e23cee --- /dev/null +++ b/lib/libforms/yacc.y @@ -0,0 +1,149 @@ +%{ +#include +#include +#include +#include +#include + +extern struct form *form; + +struct field *cur_field; +int id,next, up, down, left, right; +%} + +%start file + +%union { + int ival; + char *sval; +} + +%token STRING +%token NUMBER +%token INPUT +%token TEXT +%token FORM + +%type String + +%% + +file : /* Empty */ + | file form + | file fields + | error + ; + +form : FORM NUMBER NUMBER NUMBER NUMBER + { + form = (struct form *) malloc(sizeof(struct form)); + if (!form) + return(-1); + form->x = $2; + form->y = $3; + form->height = $4; + form->width = $5; + form->fields = 0; + } + ; + +fields : input + | text + ; + +input : INPUT NUMBER NUMBER NUMBER NUMBER NUMBER NUMBER NUMBER NUMBER NUMBER String NUMBER NUMBER NUMBER NUMBER String + { + if (alloc_field() == -1) + return(-1); + cur_field->field_id = $2; + /* + * These will hold addresses but store + * the field id's in them temporarily + */ + cur_field->next = (struct field *) $3; + cur_field->up = (struct field *) $4; + cur_field->down = (struct field *) $5; + cur_field->left = (struct field *) $6; + cur_field->right = (struct field *) $7; + cur_field->type = FORM_FTYPE_INPUT; + cur_field->entry.input.y_prompt = $8; + cur_field->entry.input.x_prompt = $9; + cur_field->entry.input.prompt_width = $10; + cur_field->entry.input.prompt_attr = FORM_DEFAULT_ATTR; + cur_field->entry.input.prompt = (char *) malloc($10+1); + if (!cur_field->entry.input.prompt) + return(-1); + strncpy(cur_field->entry.input.prompt, $11, $10); + cur_field->entry.input.prompt[$10] = 0; + cur_field->entry.input.y_field = $12; + cur_field->entry.input.x_field = $13; + cur_field->entry.input.field_width = $14; + cur_field->entry.input.max_field_width = $15; + cur_field->entry.input.field = (char *) malloc(strlen($16)); + cur_field->entry.input.field_attr = FORM_DEFAULT_ATTR; + if (!cur_field->entry.input.field) + return(-1); + strcpy(cur_field->entry.input.field, $16); + } + ; + +text : TEXT NUMBER NUMBER String + { + if (alloc_field() == -1) + return(-1); + cur_field->field_id = 0; + cur_field->type = FORM_FTYPE_TEXT; + cur_field->entry.text.y = $2; + cur_field->entry.text.x = $3; + cur_field->entry.text.attr = FORM_DEFAULT_ATTR; + cur_field->entry.text.text = (char *) malloc(strlen($4)); + if (!cur_field->entry.text.text) + return (-1); + strcpy(cur_field->entry.text.text, $4); + } + ; + +String : STRING + { + char *t, *old, *new; + + t = strdup($1); + free($1); + /* + * Deal with any escaped characters, + * only works for " and \ really. + */ + for (old=t, new=t; *old; old++, new++) { + if (*old == '\\') + old++; + *new = *old; + } + *new = '\0'; + $$ = t; + } + +%% +void +yyerror(char *s) +{ + printf("%s\n", s); + exit(1); +} + +int +alloc_field() +{ + if (!form->fields) { + form->fields = (struct field *) malloc(sizeof(struct field)); + if (!form->fields) + return(-1); + cur_field = form->fields; + } else { + cur_field->link = (struct field *) malloc(sizeof(struct field)); + if (!cur_field->link) + return(-1); + cur_field = cur_field->link; + } + + return(0); +}