if_help.c revision 7c478bd95313f5f23a4c958a745db2134aa03244
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
/* All Rights Reserved */
/*
* Copyright (c) 1985 AT&T
* All Rights Reserved
*/
/*
* Copyright (c) 2001 by Sun Microsystems, Inc.
* All rights reserved.
*/
#pragma ident "%Z%%M% %I% %E% SMI" /* SVr4.0 1.42 */
#include <stdio.h>
#include <string.h>
#include <curses.h>
#include <sys/types.h> /* EFT abs k16 */
#include "wish.h"
#include "vtdefs.h"
#include "ctl.h"
#include "token.h"
#include "winp.h"
#include "form.h"
#include "slk.h"
#include "actrec.h"
#include "typetab.h"
#include "fm_mn_par.h"
#include "objhelp.h"
#include "var_arrays.h"
#include "terror.h"
#include "moremacros.h"
#include "interrupt.h"
#include "vt.h" /* abs for headers */
#include "sizes.h"
#include "message.h"
#define HL_INTR PAR_INTR
#define HL_ONINTR PAR_ONINTR
#define HL_DONE PAR_DONE
#define HL_TITLE 3
#define HL_TEXT 4
#define HL_WRAP 5
#define HL_EDIT 6
#define HL_INIT 7
#define HL_LIFE 8
#define HL_ROWS 9
#define HL_COLUMNS 10
#define HL_BEGROW 11
#define HL_BEGCOL 12
#define HL_HELP 13
#define HL_REREAD 14
#define HL_CLOSE 15
#define HL_ALTSLKS 16
#define HL_FRMMSG 17
#define HL_HEADER 18 /* abs */
/* defined above
#define HL_INTR PAR_INTR
#define HL_ONINTR PAR_ONINTR
*/
#define HL_ACTI PAR_ACTION
#define HL_NAME PAR_NAME
#define HL_BUTT 4
#define HL_SHOW 5
#define HL_KEYS 19
static struct attribute Hl_tab[HL_KEYS] = {
{ "interrupt", RET_STR|EVAL_ALWAYS, NULL, NULL, 0 },
{ "oninterrupt",RET_STR|EVAL_ALWAYS, NULL, NULL, 0 },
{ "done", RET_ARGS|EVAL_ALWAYS, "", NULL, 0 }, /* abs */
{ "title", RET_STR|EVAL_ONCE, "Text", NULL, 0 },
{ "text", RET_STR|EVAL_ONCE, NULL, NULL, 0 },
{ "wrap", RET_BOOL|EVAL_ONCE, "", NULL, 0 },
{ "edit", RET_BOOL|EVAL_ONCE, NULL, NULL, 0 },
{ "init", RET_BOOL|EVAL_ALWAYS, "", NULL, 0 },
{ "lifetime", RET_STR|EVAL_ALWAYS, "longterm", NULL, 0 },
{ "rows", RET_INT|EVAL_ONCE, "10", NULL, 0 },
{ "columns", RET_INT|EVAL_ONCE, "30", NULL, 0 },
{ "begrow", RET_STR|EVAL_ONCE, "any", NULL, 0 },
{ "begcol", RET_STR|EVAL_ONCE, "any", NULL, 0 },
{ "help", RET_ARGS|EVAL_ALWAYS, NULL, NULL, 0 },
{ "reread", RET_BOOL|EVAL_ALWAYS, NULL, NULL, 0 },
{ "close", RET_BOOL|EVAL_ONCE, NULL, NULL, 0 },
{ "altslks", RET_BOOL|EVAL_ONCE, NULL, NULL, 0 },
{ "framemsg", RET_STR|EVAL_ONCE, "", NULL, 0 },
{ "header", RET_STR|EVAL_ONCE, NULL, NULL, 0 } /* abs */
};
#define HL_FLD_KEYS 6
static struct attribute Hl_fld_tab[HL_FLD_KEYS] = {
{ "interrupt", RET_STR|EVAL_ALWAYS, NULL, NULL, 0 },
{ "oninterrupt",RET_STR|EVAL_ALWAYS, NULL, NULL, 0 },
{ "action", RET_ARGS|EVAL_ALWAYS ,NULL, NULL, 0 },
{ "name", RET_STR|EVAL_ONCE, NULL, NULL, 0 },
{ "button", RET_INT|EVAL_ONCE, "0", NULL, 0 },
{ "show", RET_BOOL|EVAL_SOMETIMES,"", NULL, 0 }
};
#define CURhelp() (&(((helpinfo *) Cur_rec->odptr)->fm_mn))
#define CURhelpinfo() ((helpinfo *) Cur_rec->odptr)
#define ARGS() (((helpinfo *) Cur_rec->odptr)->args)
#define PTRS() (((helpinfo *) Cur_rec->odptr)->holdptrs)
extern int Vflag; /* abs k15 */
extern char *strnsave();
extern char *shrink_str();
static struct actrec *Cur_rec;
struct fm_mn parse_help();
static token bighelp_stream();
/*
** Returns a token so that the help object can be brought up.
*/
static token
objhelp_help(a)
struct actrec *a;
{
return(setaction(sing_eval(CURhelp(), HL_HELP)));
}
/*
** Frees up the structures and calls the close function.
*/
static int
objhelp_close(a)
struct actrec *a;
{
register int i, lcv;
char *p, *strchr();
Cur_rec = a;
copyAltenv(ARGS()); /* in case HL_CLOSE references $ARGs abs k14*/
form_close(a->id); /* free the form FIRST */
sing_eval(CURhelp(), HL_CLOSE);
objhelp_noncur(a, FALSE); /* remove ARGs from Altenv */
/*
* free information IN the helpinfo structure
*/
freeitup(CURhelp()); /* the text parse table */
if (PTRS()) /* holdptrs array */
free(PTRS());
lcv = array_len(ARGS()); /* the object specific variable */
for (i = 0; i < lcv; i++) { /* (e.g., $TEXT) */
char namebuf[BUFSIZ];
if (p = strchr(ARGS()[i], '='))
*p = '\0';
strncpy(namebuf, ARGS()[i], BUFSIZ);
namebuf[BUFSIZ-1] = '\0';
if (p)
*p = '=';
delaltenv(&ARGS(), namebuf);
delAltenv(namebuf);
}
array_destroy(ARGS()); /* the object variable array */
/*
* Free information in the activation record structure
*/
free(a->odptr); /* the helpinfo structure itself */
free(a->slks); /* the object specific SLKS */
free(a->path); /* the definition file path */
return(SUCCESS);
}
/*
** Checks to see whether to reread and if so, calls reread.
*/
static int
objhelp_reinit(a)
struct actrec *a;
{
Cur_rec = a;
if (sing_eval(CURhelp(), HL_REREAD))
return(objhelp_reread(a));
return(SUCCESS);
}
/*
** Front-end to parser(), which sets up defaults.
*/
static struct fm_mn
parse_help(flags, info_or_file, fp)
int flags;
char *info_or_file;
FILE *fp;
{
struct fm_mn fm_mn;
fm_mn.single.attrs = NULL;
fm_mn.multi = NULL;
filldef(&fm_mn.single, Hl_tab, HL_KEYS);
parser(flags, info_or_file, Hl_tab, HL_KEYS, &fm_mn.single,
Hl_fld_tab, HL_FLD_KEYS, &fm_mn.multi, fp);
return(fm_mn);
}
/*
** Frees contents of old help, and sets new one. Note: odptr
** is set either way since freeitup will not free anything if
** the single array is empty
*/
static int
objhelp_reread(a)
register struct actrec *a;
{
extern struct slk Defslk[MAX_SLK + 1];
extern struct slk Textslk[];
register int i;
register struct fm_mn *fm_mn;
register helpinfo *hi;
char *label, *intr, *onintr, *get_def();
int lcv, but;
FILE *fp = NULL;
Cur_rec = a;
fm_mn = CURhelp();
hi = CURhelpinfo();
/* make sure file exists and is readable (if there is a file)
* The "flags" say if a->path is the information
* itself or the file of where the information sits. abs k15
*/
if (!(hi->flags & INLINE))
if ((fp = fopen(a->path, "r")) == NULL)
{
if (a->id >= 0) /* if frame is already posted */
warn(NOT_UPDATED, a->path);
else
warn(FRAME_NOPEN, a->path);
return(FAIL);
}
if (a->id >= 0)
freeitup(fm_mn);
hi->fm_mn = parse_help(hi->flags, a->path, fp); /* abs k14.0 */
if (fm_mn->single.attrs == NULL) {
/*
* very strange indeed ...
*
if (a->id < 0)
sing_eval(fm_mn, HL_CLOSE);
*/
return(FAIL);
}
if (PTRS())
free(PTRS());
lcv = sing_eval(fm_mn, HL_HEADER) ? 2:1;
if ((PTRS() = (char **) calloc(lcv, sizeof(char *))) == NULL)
fatal(NOMEM, nil);
for (i = 0; i < lcv; i++)
PTRS()[i] = (char *) NULL;
fm_mn->seqno = 1;
hl_vislist(hi);
/*
* If "init=false" then clean-up
*/
if (!sing_eval(CURhelp(), HL_INIT))
{
if (a->id >= 0) /* form is already posted */
{
if (a->lifetime == AR_INITIAL)
{
mess_temp("can't close this frame");
mess_lock();
}
else
{
ar_close(a, FALSE);
return(FAIL);
}
}
else
{
sing_eval(CURhelp(),HL_CLOSE);
objhelp_noncur(a, TRUE); /* remove ARGs from Altenv */
freeitup(CURhelp());
return(FAIL);
}
}
/*
* update the interrupt descriptors in the activation rec
*/
ar_ctl(Cur_rec, CTSETINTR, get_sing_def(CURhelp(), HL_INTR), NULL, NULL, NULL, NULL, NULL);
ar_ctl(Cur_rec, CTSETONINTR, get_sing_def(CURhelp(), HL_ONINTR), NULL, NULL, NULL, NULL, NULL);
/*
* Set up object's SLK array
*/
set_top_slks(Textslk);
memcpy((char *)a->slks, (char *)Defslk, sizeof(Defslk));
lcv = array_len(hi->slks);
for (i = 0; i < lcv; i++) {
but = atoi(multi_eval(fm_mn, hi->slks[i], HL_BUTT)) - 1;
if (but < 0 || but >= MAX_SLK) /* abs */
continue;
label = multi_eval(fm_mn, hi->slks[i], HL_NAME);
intr = get_def(CURhelp(),hi->slks[i], HL_INTR);
onintr = get_def(CURhelp(),hi->slks[i], HL_ONINTR);
set_obj_slk(&(a->slks[but]), label, TOK_SLK1 + but, intr, onintr);
}
if (a->id >= 0)
form_ctl(a->id, CTSETDIRTY);
(void) ar_ctl(Cur_rec, CTSETMSG, FALSE, NULL, NULL, NULL, NULL, NULL); /* was AR_cur. abs k15 */
return(SUCCESS);
}
/*
** Takes this object's information out of the major altenv.
*/
static int
objhelp_noncur(a, all)
struct actrec *a;
bool all;
{
register int i;
register char *p;
int lcv;
Cur_rec = a;
lcv = array_len(ARGS());
for (i = 0; i < lcv; i++) {
char namebuf[BUFSIZ];
if (p = strchr(ARGS()[i], '='))
*p = '\0';
strncpy(namebuf, ARGS()[i], BUFSIZ);
namebuf[BUFSIZ - 1] = '\0';
if (p)
*p = '=';
delAltenv(namebuf);
}
if (all)
return(form_noncurrent(a->id));
else
return(SUCCESS);
}
/*
** Puts this object's altenv() into the major altenv().
*/
static int
objhelp_current(a)
struct actrec *a;
{
int ret;
Cur_rec = a;
copyAltenv(ARGS());
ret = form_current(a->id);
form_ctl(a->id, CTSETPOS, 1, 0, 0); /* `1' is "text=" field abs*/
return(ret);
}
/*
** Sets up SLK array, based on show functions.
*/
hl_vislist(hi)
helpinfo *hi;
{
int i;
struct fm_mn *ptr;
int lcv;
ptr = &(hi->fm_mn);
if (!hi->slks)
hi->slks = (int *) array_create(sizeof(int), array_len(ptr->multi));
else
array_trunc(hi->slks);
lcv = array_len(ptr->multi);
for (i = 0; i < lcv; i++)
if (multi_eval(ptr, i, HL_SHOW))
hi->slks = (int *) array_append(hi->slks, (char *) &i);
}
#define MIN_ROWS_TEXT 4 /* includes 2 rows of margin along frame border */
/* Size a text header by counting the newlines */
header_size(m)
formfield m;
{
register char *c;
register int linefeeds;
register struct vt *v;
int rows, cols;
if (*m.value == 0) /* null string */
return(0);
for (linefeeds = 1, c = m.value; *c != NULL; c++)
if (*c == '\n')
linefeeds++;
/* leave at least MIN_ROWS_TEXT rows for text */
v = &VT_array[VT_curid];
getmaxyx(v->win, rows, cols);
return(linefeeds > rows - MIN_ROWS_TEXT) ? rows - MIN_ROWS_TEXT : linefeeds;
}
/*
** Gives header and text as only fields, fields that have no names.
*/
static formfield
objhelp_disp(n, hi)
int n;
helpinfo *hi;
{
struct fm_mn *ptr;
char *readfile();
formfield m;
static int header_rows;
ptr = &(hi->fm_mn);
switch (n)
{
case 0: /* non-scrolling header field. abs8/88 */
m.name = strsave("");
m.value = sing_eval(ptr, HL_HEADER);
m.frow = 0;
m.fcol = 0;
m.nrow = VT_UNDEFINED;
m.ncol = VT_UNDEFINED;
m.rows = header_rows = header_size(m);
m.cols = atoi(sing_eval(CURhelp(), HL_COLUMNS));
m.flags = I_FANCY | I_NOEDIT | I_TEXT;
m.ptr = PTRS();
break;
case 1: /* text field */
m.name = strsave("");
m.value = sing_eval(ptr, HL_TEXT);
m.frow = header_rows; /* header has rows 0 -> header_rows - 1 */
m.fcol = 0;
m.nrow = VT_UNDEFINED;
m.ncol = VT_UNDEFINED;
m.rows = atoi(sing_eval(CURhelp(), HL_ROWS)) - header_rows;
m.cols = atoi(sing_eval(CURhelp(), HL_COLUMNS));
m.flags = I_FANCY|I_SCROLL|I_TEXT;
if (header_rows == 0) /* curses optimization.. */
m.flags |= I_FULLWIN; /* ..if no subwindows needed */
if (!sing_eval(CURhelp(), HL_EDIT))
m.flags |= I_NOEDIT;
if (sing_eval(CURhelp(), HL_WRAP))
m.flags |= I_WRAP;
m.ptr = PTRS() + 1;
break;
default:
m.name = NULL;
}
return(m);
}
/*
** There are no args, so return FAIL. Otherwise, pass it on.
*/
int
objhelp_ctl(rec, cmd, arg1, arg2, arg3, arg4, arg5, arg6)
struct actrec *rec;
int cmd;
int arg1, arg2, arg3, arg4, arg5, arg6;
{
if (cmd == CTGETARG)
return(FAIL);
else if (cmd == CTSETMSG) {
if (arg1 == TRUE) {
/*
* if arg1 == TRUE then the frame message was
* generated "externally" (i.e., via the message
* built-it). Update the "framemsg" descriptor
* accordingly.
*/
char *newmsg, *get_mess_frame();
newmsg = get_mess_frame();
set_single_default(CURhelp(), HL_FRMMSG, newmsg);
}
else
mess_frame(sing_eval(CURhelp(), HL_FRMMSG));
return(SUCCESS);
}
if (cmd == CTSETLIFE) {
char *life;
life = sing_eval((&(((helpinfo *) rec->odptr)->fm_mn)), HL_LIFE);
setlifetime(rec, life);
return(SUCCESS);
}
return(form_ctl(rec->id, cmd, arg1, arg2, arg3, arg4, arg5, arg6));
}
/*
** Uses path_to_ar and nextpath_to_ar to see if it is a reopen. If
** so, make it current. Otherwise, set up the actrec and call
** ar_create.
*/
int
IF_helpopen(args)
register char **args;
{
register int i;
int type, startrow, startcol;
char *begrow, *begcol;
struct actrec a, *first_rec, *ar_create(), *path_to_ar(), *nextpath_to_ar();
int do_inline;
struct fm_mn *fm_mn;
extern struct slk Defslk[MAX_SLK + 1];
extern char *filename();
helpinfo *hi;
char *life;
char buf[BUFSIZ], envbuf[6];
char *ptr;
if (strCcmp(args[0], "-i") == 0)
{
do_inline = TRUE;
Cur_rec = path_to_ar(args[1]);
}
else
{
do_inline = FALSE;
Cur_rec = path_to_ar(args[0]);
}
for (first_rec = Cur_rec; Cur_rec; ) {
char *env, *getaltenv();
strcpy(envbuf, "ARG1");
for (i = do_inline ? 2 : 1; (env = getaltenv(ARGS(), envbuf)) && args[i];
envbuf[3]++, i++)
if (strcmp(args[i], env))
break;
if (!args[i] && !env) {
ar_current(Cur_rec, TRUE); /* abs k15 */
return(SUCCESS);
}
Cur_rec = nextpath_to_ar(Cur_rec);
if (Cur_rec == first_rec) /* circular list */
break;
}
hi = (helpinfo *)new(helpinfo);
hi->flags = do_inline ? INLINE : 0;
hi->args = NULL;
a.id = -1;
a.odptr = (char *) hi;
fm_mn = &(hi->fm_mn);
fm_mn->single.attrs = NULL;
if (do_inline)
a.path = strsave(args[1]);
else
a.path = strsave(args[0]);
if ((a.slks = (struct slk *) malloc(sizeof(Defslk))) == NULL)
fatal(NOMEM, nil);
a.fcntbl[AR_CLOSE] = objhelp_close;
a.fcntbl[AR_REREAD] = objhelp_reread;
a.fcntbl[AR_REINIT] = objhelp_reinit;
a.fcntbl[AR_CURRENT] = objhelp_current;
a.fcntbl[AR_TEMP_CUR] = objhelp_current; /* abs k15. optimize later */
a.fcntbl[AR_NONCUR] = objhelp_noncur;
a.fcntbl[AR_ODSH] = (int (*)())bighelp_stream; /* added cast abs */
a.fcntbl[AR_HELP] = (int (*)())objhelp_help; /* added cast abs */
a.fcntbl[AR_CTL] = objhelp_ctl;
Cur_rec = &a;
setupenv(hi->flags, args, &ARGS());
if (objhelp_reread(&a) == FAIL)
return(FAIL);
ptr = strnsave("TEXT=", strlen(life = sing_eval(fm_mn, HL_TEXT)) + 6);
strcat(ptr, life);
putaltenv(&ARGS(), ptr);
putAltenv(ptr);
free(ptr);
begrow = sing_eval(fm_mn, HL_BEGROW);
begcol = sing_eval(fm_mn, HL_BEGCOL);
life = sing_eval(fm_mn, HL_LIFE);
life_and_pos(&a, life, begrow, begcol, &startrow, &startcol, &type);
if (Vflag) /* abs k15 */
strcpy(buf, shrink_str(filename(sing_eval(fm_mn, HL_TITLE)), MAX_TITLE));
else /* abs k15 */
strcpy(buf, shrink_str(sing_eval(fm_mn, HL_TITLE), MAX_TITLE));
a.id = form_default(buf, type, startrow, startcol,
objhelp_disp, (char *)hi);
if (a.id == FAIL)
return(FAIL);
if (sing_eval(fm_mn, HL_ALTSLKS))
a.flags = AR_ALTSLKS;
else
a.flags = 0;
return(ar_current(Cur_rec = ar_create(&a), FALSE)); /* abs k15 */
}
/*
** Intercepts SLKs after the editor. Also, TOK_SAVE is an exit.
*/
token
help_stream(tok)
register token tok;
{
char *buf, *s;
int *slks;
int lcv;
s = NULL;
if (tok >= TOK_SLK1 && tok <= TOK_SLK16) {
int num;
int i;
slks = CURhelpinfo()->slks;
num = tok - TOK_SLK1 + 1;
lcv = array_len(slks);
for (i = 0; i < lcv; i++)
if (atoi(multi_eval(CURhelp(), slks[i], HL_BUTT)) == num) {
form_ctl(Cur_rec->id, CTGETARG, &s);
if (sing_eval(CURhelp(), HL_EDIT))
set_sing_cur(CURhelp(), HL_TEXT, strsave(s));
buf = strnsave("TEXT=", strlen(s) + 6);
strcat(buf, s);
putaltenv(&ARGS(), buf);
putAltenv(buf);
tok = setaction(multi_eval(CURhelp(), slks[i], HL_ACTI));
free(buf);
break;
}
}
if (tok == TOK_SAVE)
tok = TOK_CLOSE;
if (tok == TOK_CLOSE) {
if (!s) {
form_ctl(Cur_rec->id, CTGETARG, &s);
buf = strnsave("TEXT=", strlen(s) + 6);
strcat(buf, s);
putaltenv(&ARGS(), buf);
putAltenv(buf);
free(buf);
}
/* tok = sing_eval(CURhelp(), HL_DONE) ? TOK_CLOSE : TOK_BADCHAR; abs */
tok = make_action(sing_eval(CURhelp(), HL_DONE));
}
return(tok);
}
/*
** Sets up stream and calls stream.
*/
static token
bighelp_stream(a, t)
struct actrec *a;
register token t;
{
token (*func[3])();
extern int field_stream();
register int olifetime;
Cur_rec = a;
olifetime = Cur_rec->lifetime;
Cur_rec->lifetime = AR_PERMANENT;
func[0] = (token (*)())field_stream; /* added cast abs */
func[1] = help_stream;
func[2] = NULL;
t = stream(t, func);
Cur_rec->lifetime = olifetime;
return(t);
}