2N/A/*
2N/A * CDDL HEADER START
2N/A *
2N/A * The contents of this file are subject to the terms of the
2N/A * Common Development and Distribution License, Version 1.0 only
2N/A * (the "License"). You may not use this file except in compliance
2N/A * with the License.
2N/A *
2N/A * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
2N/A * or http://www.opensolaris.org/os/licensing.
2N/A * See the License for the specific language governing permissions
2N/A * and limitations under the License.
2N/A *
2N/A * When distributing Covered Code, include this CDDL HEADER in each
2N/A * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
2N/A * If applicable, add the following below this CDDL HEADER, with the
2N/A * fields enclosed by brackets "[]" replaced with your own identifying
2N/A * information: Portions Copyright [yyyy] [name of copyright owner]
2N/A *
2N/A * CDDL HEADER END
2N/A */
2N/A/* Copyright (c) 1988 AT&T */
2N/A/* All Rights Reserved */
2N/A
2N/A
2N/A/*
2N/A * Copyright (c) 1997, by Sun Microsystems, Inc.
2N/A * All rights reserved.
2N/A */
2N/A
2N/A#pragma ident "%Z%%M% %I% %E% SMI"
2N/A
2N/A/*LINTLIBRARY*/
2N/A
2N/A#include <sys/types.h>
2N/A#include <stdlib.h>
2N/A#include "utility.h"
2N/A
2N/A#define AT_BOTTOM(f) (Y(f) == Ymax(f) - 1) /* last line */
2N/A#define AT_END(f) (Y(f) == Ymax(f) - 1 && X(f) == Xmax(f) - 1)
2N/A /* last char */
2N/A#define AT_BEGINNING(f) (Y(f) == 0 && X(f) == 0) /* first char */
2N/A
2N/Astatic int
2N/Aroom_for_line(FORM *f)
2N/A{
2N/A char *v;
2N/A
2N/A _sync_buffer(f);
2N/A v = LineBuf(C(f), Ymax(f) - 1);
2N/A return (v == _data_end(v, Xmax(f))); /* check for empty line */
2N/A}
2N/A
2N/Astatic int
2N/Aroom_for_char(FORM *f)
2N/A{
2N/A WINDOW * w = W(f);
2N/A int c;
2N/A
2N/A (void) wmove(w, Y(f), Xmax(f) - 1);
2N/A c = (int)(winch(w) & A_CHARTEXT);
2N/A (void) wmove(w, Y(f), X(f));
2N/A return (c == Pad(C(f))); /* check for empty char */
2N/A}
2N/A
2N/Astatic int
2N/Aextra_padding(char *str, int nstr) /* used for word wrapping */
2N/A{
2N/A int c = *(str + nstr - 1);
2N/A
2N/A if (c == '"' || c == '\'')
2N/A c = *(str + nstr - 2);
2N/A
2N/A return ((c == '.' || c == '?' || c == '!' || c == ':') ? 2 : 1);
2N/A
2N/A}
2N/A
2N/ABOOLEAN
2N/A_grow_field(FIELD *c, int chunks)
2N/A{
2N/A /* This function handles the growth of dymanically growable fields */
2N/A /* Returns TRUE if successful, FALSE otherwise */
2N/A
2N/A FORM *f = c->form;
2N/A WINDOW *w = W(f);
2N/A BOOLEAN current = Status(f, POSTED) && c == C(f);
2N/A char *old_buf;
2N/A char *new_buf;
2N/A char *save;
2N/A int old_len = BufSize(c);
2N/A int grow;
2N/A int lcv;
2N/A int max = c->maxgrow;
2N/A int i;
2N/A
2N/A if (current && Status(f, WIN_CHG)) {
2N/A _win_to_buf(w, c);
2N/A Clr(f, WIN_CHG);
2N/A Set(f, BUF_CHG);
2N/A }
2N/A
2N/A if (OneRow(c)) {
2N/A grow = chunks * c->cols;
2N/A
2N/A if (max)
2N/A grow = MIN(max - c->dcols, grow);
2N/A
2N/A c->dcols += grow;
2N/A
2N/A if (c->dcols == max)
2N/A Clr(c, GROWABLE);
2N/A } else {
2N/A grow = chunks * (c->rows + c->nrow);
2N/A
2N/A if (max)
2N/A grow = MIN(max - c->drows, grow);
2N/A
2N/A c->drows += grow;
2N/A grow *= c->cols;
2N/A
2N/A if (c->drows == max)
2N/A Clr(c, GROWABLE);
2N/A }
2N/A
2N/A save = old_buf = Buf(c);
2N/A new_buf = Buf(c) = malloc(TotalBuf(c));
2N/A
2N/A if (!new_buf)
2N/A return (FALSE);
2N/A
2N/A lcv = c->nbuf + 1;
2N/A
2N/A for (i = 0; i < lcv; i++) {
2N/A (void) memcpy(new_buf, old_buf, old_len);
2N/A (void) memset(new_buf + old_len, ' ', grow);
2N/A old_buf += old_len + 1;
2N/A new_buf += old_len + grow;
2N/A *new_buf++ = '\0';
2N/A }
2N/A
2N/A free(save); /* delete old buffer */
2N/A
2N/A if (current) {
2N/A (void) delwin(w);
2N/A W(f) = w = newwin(c->drows, c->dcols, 0, 0);
2N/A
2N/A if (!w)
2N/A return (FALSE);
2N/A
2N/A wbkgdset(w, Pad(c) | Back(c));
2N/A (void) wattrset(w, Fore(c));
2N/A (void) werase(w);
2N/A _buf_to_win(c, w);
2N/A (void) untouchwin(w);
2N/A (void) wmove(w, Y(f), X(f));
2N/A }
2N/A
2N/A if (c->link != c) {
2N/A FIELD *p = c->link;
2N/A
2N/A while (p != c) {
2N/A Buf(p) = Buf(c);
2N/A p->drows = c->drows;
2N/A p->dcols = c->dcols;
2N/A /* _sync_field(p) */
2N/A p = p->link;
2N/A }
2N/A }
2N/A
2N/A return (TRUE);
2N/A}
2N/A
2N/Astatic int
2N/Ainsert_str(FORM *f, int y, int off, int nstr) /* used for word wrapping */
2N/A{
2N/A WINDOW *w = W(f);
2N/A FIELD *c = C(f);
2N/A char *vbeg = LineBuf(c, y);
2N/A char *v = _data_end(vbeg, Xmax(f));
2N/A int x = (int)(v - vbeg);
2N/A int n = Xmax(f) - x;
2N/A int pad = extra_padding(Buf(c) + off, nstr);
2N/A int siz = nstr + 1 + pad;
2N/A int ret = E_REQUEST_DENIED;
2N/A
2N/A if (n >= siz) { /* check for fit on this line */
2N/A (void) wmove(w, y, 0);
2N/A (void) winsnstr(w, Buf(c) + off, nstr);
2N/A (void) wmove(w, y, nstr);
2N/A (void) winsnstr(w, " ", pad);
2N/A } else { /* wrap */
2N/A if (y == Ymax(f) - 1 && Status(c, GROWABLE)) {
2N/A if (!_grow_field(c, 1))
2N/A return (E_SYSTEM_ERROR);
2N/A
2N/A vbeg = LineBuf(c, y); /* grow changes buffer */
2N/A w = W(f); /* grow changes window */
2N/A }
2N/A
2N/A v = _data_beg(vbeg + Xmax(f) - siz, siz);
2N/A v = _whsp_end(vbeg, (int)(v - vbeg));
2N/A x = (int)(v - vbeg);
2N/A n = Xmax(f) - x - n;
2N/A
2N/A if (y < Ymax(f) - 1 && (ret =
2N/A insert_str(f, y+1, (int)(v - Buf(c)), n)) == E_OK) {
2N/A (void) wmove(w, y, x);
2N/A (void) wclrtoeol(w);
2N/A (void) wmove(w, y, 0);
2N/A (void) winsnstr(w, Buf(c) + off, nstr);
2N/A (void) wmove(w, y, nstr);
2N/A (void) winsnstr(w, " ", pad);
2N/A } else
2N/A return (ret); /* no room for wrap */
2N/A }
2N/A return (E_OK);
2N/A}
2N/A
2N/Astatic int
2N/Awrap_ok(FORM *f) /* used for word wrapping */
2N/A{
2N/A/*
2N/A * when this routine is called a char has already been added/inserted
2N/A * on the screen at Y(f), X(f). this routine checks to see if the current
2N/A * line needs wrapping and if so attempts the wrap. if unsuccessful
2N/A * it deletes the char at Y(f), X(f) and returns FALSE.
2N/A */
2N/A FIELD *c = C(f);
2N/A BOOLEAN at_bottom = AT_BOTTOM(f);
2N/A int ret = E_REQUEST_DENIED;
2N/A
2N/A if (Opt(c, O_WRAP) && !OneRow(c) && !room_for_char(f) &&
2N/A (!at_bottom || Status(c, GROWABLE))) {
2N/A WINDOW *w;
2N/A char *vbeg;
2N/A char *v;
2N/A int x, n;
2N/A
2N/A if (at_bottom && !_grow_field(c, 1))
2N/A return (E_SYSTEM_ERROR);
2N/A
2N/A vbeg = LineBuf(c, Y(f));
2N/A w = W(f);
2N/A
2N/A _win_to_buf(w, c); /* sync buffer without changing flags */
2N/A
2N/A v = _whsp_end(vbeg, Xmax(f));
2N/A x = (int)(v - vbeg);
2N/A n = Xmax(f) - x;
2N/A
2N/A if (x && (ret = insert_str(f, Y(f)+1, (int)(v - Buf(c)), n)) ==
2N/A E_OK) {
2N/A w = W(f); /* window may change in insert_str */
2N/A (void) wmove(w, Y(f), x);
2N/A (void) wclrtoeol(w);
2N/A
2N/A if (X(f) >= x) {
2N/A ++Y(f);
2N/A X(f) = X(f) - x;
2N/A }
2N/A } else { /* error condition */
2N/A if (ret == E_SYSTEM_ERROR)
2N/A return (E_SYSTEM_ERROR);
2N/A
2N/A (void) wmove(w, Y(f), X(f));
2N/A (void) wdelch(w); /* delete the char */
2N/A _win_to_buf(w, c); /* restore buffer */
2N/A return (E_REQUEST_DENIED);
2N/A }
2N/A }
2N/A return (E_OK);
2N/A}
2N/A
2N/Aint
2N/A_new_line(FORM *f)
2N/A{
2N/A/*
2N/A * overloaded operation
2N/A *
2N/A * if at beginning of field
2N/A * move to next field
2N/A *
2N/A * else if in OVERLAY mode
2N/A * if on last line of field
2N/A * clear to eol and move to next field
2N/A * else
2N/A * clear to eol and move to beginning of next line
2N/A *
2N/A * else if in INSERT mode
2N/A * if on last line of field
2N/A * move to next field
2N/A * else
2N/A * move text from cursor to eol to new line
2N/A */
2N/A BOOLEAN at_bottom = AT_BOTTOM(f);
2N/A FIELD * c = C(f);
2N/A
2N/A if (Opt(f, O_NL_OVERLOAD) && AT_BEGINNING(f))
2N/A return (_field_navigation(_next_field, f));
2N/A
2N/A if (!Opt(c, O_EDIT))
2N/A return (E_REQUEST_DENIED);
2N/A
2N/A if (Status(f, OVERLAY)) { /* OVERLAY mode */
2N/A if (at_bottom && (!Status(c, GROWABLE) || OneRow(c))) {
2N/A if (Opt(f, O_NL_OVERLOAD)) {
2N/A (void) wclrtoeol(W(f));
2N/A Set(f, WIN_CHG);
2N/A return (_field_navigation(_next_field, f));
2N/A } else
2N/A return (E_REQUEST_DENIED);
2N/A }
2N/A
2N/A if (at_bottom && !_grow_field(c, 1))
2N/A return (E_SYSTEM_ERROR);
2N/A
2N/A (void) wclrtoeol(W(f));
2N/A ++Y(f); X(f) = 0;
2N/A } else { /* INSERT mode */
2N/A BOOLEAN room;
2N/A
2N/A if (at_bottom && (!Status(c, GROWABLE) || OneRow(c))) {
2N/A if (Opt(f, O_NL_OVERLOAD))
2N/A return (_field_navigation(_next_field, f));
2N/A else
2N/A return (E_REQUEST_DENIED);
2N/A }
2N/A
2N/A room = !at_bottom && room_for_line(f);
2N/A
2N/A if (room || Status(c, GROWABLE)) {
2N/A WINDOW *w;
2N/A char *v;
2N/A char *vend;
2N/A
2N/A if (!room && !_grow_field(c, 1))
2N/A return (E_SYSTEM_ERROR);
2N/A
2N/A w = W(f);
2N/A v = LineBuf(c, Y(f)) + X(f);
2N/A vend = _data_end(v, Xmax(f) - X(f));
2N/A
2N/A (void) wclrtoeol(w);
2N/A ++Y(f); X(f) = 0;
2N/A (void) wmove(w, Y(f), X(f));
2N/A (void) winsertln(w);
2N/A (void) waddnstr(w, v, (int)(vend - v));
2N/A } else
2N/A return (E_REQUEST_DENIED);
2N/A }
2N/A Set(f, WIN_CHG);
2N/A return (E_OK);
2N/A}
2N/A
2N/A/* _ins_char - insert blank char with error on overflow */
2N/Aint
2N/A_ins_char(FORM *f)
2N/A{
2N/A FIELD *c = C(f);
2N/A BOOLEAN room = room_for_char(f);
2N/A
2N/A if (CheckChar(c, ' ') && (room || (OneRow(c) &&
2N/A Status(c, GROWABLE)))) {
2N/A if (!room && !_grow_field(c, 1))
2N/A return (E_SYSTEM_ERROR);
2N/A
2N/A (void) winsch(W(f), ' ');
2N/A
2N/A return (wrap_ok(f));
2N/A }
2N/A return (E_REQUEST_DENIED);
2N/A}
2N/A
2N/A/* _ins_line - insert blank line with error on overflow */
2N/Aint
2N/A_ins_line(FORM *f)
2N/A{
2N/A BOOLEAN room = !AT_BOTTOM(f) && room_for_line(f);
2N/A FIELD *c = C(f);
2N/A
2N/A if (CheckChar(c, ' ') && !OneRow(c) && (room || Status(c, GROWABLE))) {
2N/A if (!room && !_grow_field(c, 1))
2N/A return (E_SYSTEM_ERROR);
2N/A
2N/A X(f) = 0;
2N/A (void) winsertln(W(f));
2N/A return (E_OK);
2N/A }
2N/A return (E_REQUEST_DENIED);
2N/A}
2N/A
2N/A/* _del_char - delete char at cursor */
2N/Aint
2N/A_del_char(FORM *f)
2N/A{
2N/A (void) wdelch(W(f));
2N/A return (E_OK);
2N/A}
2N/A
2N/Aint
2N/A_del_prev(FORM *f)
2N/A{
2N/A/*
2N/A * overloaded operation
2N/A *
2N/A * if at beginning of field
2N/A * move to previous field
2N/A *
2N/A * else if in OVERLAY mode
2N/A * if at beginning of line
2N/A * error
2N/A * else
2N/A * delete previous char
2N/A *
2N/A * else if in INSERT mode
2N/A * if at beginning of line
2N/A * if current line can fit on preceding
2N/A * join current line with preceding line
2N/A * else
2N/A * error
2N/A * else
2N/A * delete previous char
2N/A */
2N/A WINDOW * w = W(f);
2N/A FIELD * c = C(f);
2N/A
2N/A if (AT_BEGINNING(f)) {
2N/A if (Opt(f, O_BS_OVERLOAD))
2N/A return (_field_navigation(_prev_field, f));
2N/A else
2N/A return (E_REQUEST_DENIED);
2N/A }
2N/A if (!Opt(c, O_EDIT))
2N/A return (E_REQUEST_DENIED);
2N/A
2N/A if (--X(f) < 0) {
2N/A ++X(f);
2N/A
2N/A if (Status(f, OVERLAY)) /* OVERLAY mode */
2N/A return (E_REQUEST_DENIED);
2N/A else { /* INSERT mode */
2N/A char *p = LineBuf(c, Y(f) - 1);
2N/A char *v = LineBuf(c, Y(f));
2N/A char *pend;
2N/A char *vend;
2N/A
2N/A _sync_buffer(f);
2N/A pend = _data_end(p, Xmax(f));
2N/A vend = _data_end(v, Xmax(f));
2N/A
2N/A if ((vend - v) > (Xmax(f) - (pend - p)))
2N/A return (E_REQUEST_DENIED);
2N/A else {
2N/A (void) wdeleteln(w);
2N/A _adjust_cursor(f, pend);
2N/A (void) wmove(w, Y(f), X(f));
2N/A (void) waddnstr(w, v, (int)(vend - v));
2N/A }
2N/A }
2N/A } else {
2N/A (void) wmove(w, Y(f), X(f));
2N/A (void) wdelch(w);
2N/A }
2N/A Set(f, WIN_CHG);
2N/A return (E_OK);
2N/A}
2N/A
2N/A/* _del_line - delete current line */
2N/Aint
2N/A_del_line(FORM *f)
2N/A{
2N/A X(f) = 0;
2N/A (void) wdeleteln(W(f));
2N/A return (E_OK);
2N/A}
2N/A
2N/A/* _del_word - delete word under cursor plus trailing blanks */
2N/Aint
2N/A_del_word(FORM *f)
2N/A{
2N/A FIELD *c = C(f);
2N/A WINDOW *w = W(f);
2N/A char *y = LineBuf(c, Y(f));
2N/A char *t = y + Xmax(f);
2N/A char *v = y + X(f);
2N/A char *x = v;
2N/A
2N/A _sync_buffer(f);
2N/A
2N/A if (*v == ' ')
2N/A return (E_REQUEST_DENIED);
2N/A
2N/A _adjust_cursor(f, _whsp_end(y, X(f)));
2N/A (void) wmove(w, Y(f), X(f));
2N/A (void) wclrtoeol(w);
2N/A
2N/A v = _whsp_beg(v, (int)(t - v));
2N/A v = _data_beg(v, (int)(t - v));
2N/A
2N/A if (v != x && *v != ' ')
2N/A (void) waddnstr(w, v, (int)(_data_end(v, (int)(t - v)) - v));
2N/A
2N/A return (E_OK);
2N/A}
2N/A
2N/A/* _clr_eol - clear to end of line */
2N/Aint
2N/A_clr_eol(FORM *f)
2N/A{
2N/A (void) wclrtoeol(W(f));
2N/A return (E_OK);
2N/A}
2N/A
2N/A/* _clr_eof - clear to end of field */
2N/Aint
2N/A_clr_eof(FORM *f)
2N/A{
2N/A (void) wclrtobot(W(f));
2N/A return (E_OK);
2N/A}
2N/A
2N/A/* _clr_field - clear entire field */
2N/Aint
2N/A_clr_field(FORM *f)
2N/A{
2N/A X(f) = 0; Y(f) = 0;
2N/A (void) werase(W(f));
2N/A return (E_OK);
2N/A}
2N/A
2N/A/* _ovl_mode - go into overlay mode */
2N/Aint
2N/A_ovl_mode(FORM *f)
2N/A{
2N/A Set(f, OVERLAY);
2N/A return (E_OK);
2N/A}
2N/A
2N/A/* _ins_mode - go into insert mode */
2N/Aint
2N/A_ins_mode(FORM *f)
2N/A{
2N/A Clr(f, OVERLAY);
2N/A return (E_OK);
2N/A}
2N/A
2N/A/* _validation - apply validation function associated with field type */
2N/Aint
2N/A_validation(FORM *f)
2N/A{
2N/A return (_validate(f) ? E_OK : E_INVALID_FIELD);
2N/A}
2N/A
2N/A/* _next_choice - apply next choice function associated with field type */
2N/Aint
2N/A_next_choice(FORM *f)
2N/A{
2N/A _sync_buffer(f);
2N/A return (NextChoice(C(f)) ? E_OK : E_REQUEST_DENIED);
2N/A}
2N/A
2N/A/* _prev_choice - apply previous choice function associated with field type */
2N/Aint
2N/A_prev_choice(FORM *f)
2N/A{
2N/A _sync_buffer(f);
2N/A return (PrevChoice(C(f)) ? E_OK : E_REQUEST_DENIED);
2N/A}
2N/A
2N/A/*
2N/A * _data_entry - enter printable ascii char ch
2N/A * in current field at cursor position
2N/A */
2N/Aint
2N/A_data_entry(FORM *f, int ch)
2N/A{
2N/A FIELD * c = C(f); /* current field */
2N/A WINDOW * w = W(f); /* field window */
2N/A BOOLEAN at_end;
2N/A int ret;
2N/A
2N/A if (!Opt(c, O_EDIT))
2N/A return (E_REQUEST_DENIED);
2N/A
2N/A if (AT_BEGINNING(f) && Opt(c, O_BLANK) && ! Status(f, BUF_CHG) &&
2N/A !Status(f, WIN_CHG))
2N/A (void) werase(w);
2N/A
2N/A if (Status(f, OVERLAY)) /* OVERLAY mode */
2N/A (void) waddch(w, (chtype) ch);
2N/A else { /* INSERT mode */
2N/A BOOLEAN room = room_for_char(f);
2N/A
2N/A if (room || (OneRow(c) && Status(c, GROWABLE))) {
2N/A if (!room && !_grow_field(c, 1))
2N/A return (E_SYSTEM_ERROR);
2N/A
2N/A (void) winsch(w, (chtype) ch);
2N/A } else
2N/A return (E_REQUEST_DENIED);
2N/A }
2N/A
2N/A if ((ret = wrap_ok(f)) != E_OK)
2N/A return (ret);
2N/A
2N/A Set(f, WIN_CHG);
2N/A
2N/A at_end = AT_END(f);
2N/A
2N/A if (at_end && !Status(c, GROWABLE) && Opt(c, O_AUTOSKIP))
2N/A return (_field_navigation(_next_field, f));
2N/A
2N/A if (at_end && Status(c, GROWABLE) && !_grow_field(c, 1))
2N/A return (E_SYSTEM_ERROR);
2N/A
2N/A (void) _next_char(f);
2N/A return (E_OK);
2N/A}