/* * cmd.c  * * All the Techinfo browser functions, followed by a table of their invoking  * commands, and a call to lookup a function (proc pointer) given its name.  *//*  Copyright (C) 1989 by the Massachusetts Institute of Technology    Export of this software from the United States of America is assumed   to require a specific license from the United States Government.   It is the responsibility of any person or organization contemplating   export to obtain such a license before exporting. WITHIN THAT CONSTRAINT, permission to use, copy, modify, anddistribute this software and its documentation for any purpose andwithout fee is hereby granted, provided that the above copyrightnotice appear in all copies and that both that copyright notice andthis permission notice appear in supporting documentation, and thatthe name of M.I.T. not be used in advertising or publicity pertainingto distribution of the software without specific, written priorpermission.  M.I.T. makes no representations about the suitability ofthis software for any purpose.  It is provided "as is" without expressor implied warranty.   */ #include <curses.h>#include <ctype.h>#include <sys/file.h>#include <sys/types.h>#include <sys/time.h>#include <sys/stat.h>#include "network.h"  #include "pdb.h" #ifdef CLIENT#include "nlist_c.h" #else#include "web.h" #include "nlist.h"#endif #include "disp.h" #define	MAXARGS	8 #define	CMD_OK		0#define	CMD_FAIL	1#define	CMD_HIDE	1 #define MIN        0  /* command sets */#define ADV        1  /* command sets */ static char    *Argv[MAXARGS + 1];static int      Argc;static int      curlist = 0; static void	(*select_func)();static short    cflag = MIN; /* current command set */static short    jumped = FALSE;static long     cur_doc_id = 0;static short    cur_scr_type = 0; /* current thing shown - LIST,TEXT,HELP... */static int list_depends = 0; /* list depended on previous list */extern int menu;     /* is the 1st line being used as a menu title */extern int disptype; /* full or simple */extern int save_cmd;extern int cur_sock; /*  */typedef struct prevcmd HIST; struct prevcmd {	HIST      *h_next;	char      *h_cmd;	int       h_listno;}; typedef struct list_cmd LIST_CMD; struct list_cmd {  LIST_CMD    *next;  char        *cmd;}; LIST_CMD  *lists[100]; HIST        *first_cmd = 0;static int	numhist = 0;static LIST_CMD *last_cmd = 0; hist_add(cmdline)char           cmdline[];{	static HIST *last_cmd = 0; 	HIST        *h = (HIST *) domalloc(sizeof(HIST)); 	if (!h)		return;	h->h_next = (HIST *) 0;	if (h->h_cmd = domalloc(strlen(cmdline) + 1))		strcpy(h->h_cmd, cmdline);	if (!first_cmd)		first_cmd = h;	if (last_cmd)		last_cmd->h_next = h;	last_cmd = h;	numhist++;} NODE * is_node_cmd(char *cmdstr); static char *hist_text_line(lineno)int	lineno;{	static HIST	*lasth = 0;	static int	lastline;	static char	buf[64];	NODE    *anode;	HIST	*h;	char thecmd[80]; 	if (lineno >= numhist)		return ""; 	if (lasth && lineno == lastline + 1) {		h = lasth->h_next;		lasth = h;		lastline = lineno;	} else {		int	line;				for (h = first_cmd, line = 0; h; h = h->h_next, line++)			if (line == lineno)				break;		lasth = h;		lastline = line;	}	/* if a node specific cmd */	strcpy(thecmd,h->h_cmd);  /* does tokenizing the string wreck it ? */	anode = is_node_cmd(thecmd);	if (!anode)	   sprintf(buf, "%8d - %s", lineno + 1, h->h_cmd);	else	   sprintf(buf, "%8d - %s - %s", lineno + 1, h->h_cmd,n_title(anode));	return buf;}  static voidhist_select(int old_cmd_num){	char	cmdbuf[64]; 	sprintf(cmdbuf, "history %d", old_cmd_num);	disp_setmsg("You selected previous command #%d.", old_cmd_num);	parse_cmd(cmdbuf);}cmd_history(prevcmd)int	prevcmd;{	HIST        *h;	int          i, numhist; 	if (Argc < 2) {		hist_add("history"); 		for (numhist = 0, h = first_cmd; h; h = h->h_next)			numhist++; 		disp_set(numhist, hist_text_line);		disp_set_title("Command History");		disp_use_pointer(4);		disp_pointer_set(numhist - 1);		/* Start at end */		select_func = hist_select;		cur_scr_type = HISTORY;		return CMD_HIDE;	}	if (prevcmd < 0) {		for (numhist = 0, h = first_cmd; h; h = h->h_next)			numhist++;		prevcmd = numhist + prevcmd + 1;	}	for (h = first_cmd, i = 0; h; h = h->h_next, i++)		if (prevcmd == i + 1) {			parse_cmd(h->h_cmd);			break;		}	return CMD_HIDE;} cmd_begin(){				/* This is a complete kludge ... */	/* initialize(); */ 	parse_cmd("*below 0 1");	return CMD_HIDE;} cmd_pagedown(int pagecnt)		/* Scroll down */{	disp_page_dn(pagecnt);	return CMD_HIDE;} cmd_pageup(int pagecnt)			/* Scroll up */{	disp_page_up(pagecnt);	return CMD_HIDE;} cmd_up(int cnt){  if (disp_is_text()){     disp_page_up(1);    return CMD_HIDE;} 	disp_pointer_up(cnt);	return CMD_HIDE;} cmd_down(int cnt){  if (disp_is_text()){     disp_page_dn(1);    return CMD_HIDE; } 	disp_pointer_dn(cnt);	return CMD_HIDE; } cmd_top(){	disp_goto_top();	return CMD_OK;} staticcmd_bottom(){	disp_goto_bot();	return CMD_OK;}  /* * This here is a slight kludge to take advantage of our already * wonderful argument parsing code. */ static voidnl_select(long nid){	char	cmdbuf[32];	NODE	*n;			if (!nid || nid > nlist_numnode() - menu) {  /* */		n = web_node(nid);	} else {	  if (menu) /* menu kludge */		n = nlist_node(nid);	  else 	        n = nlist_node(nid - 1);	  disp_pointer_set(nid - 1);	}	jumped = FALSE;	if (text_node(n))		sprintf(cmdbuf, "*show %ld", nid);	else		sprintf(cmdbuf, "*below %ld", nid);	parse_cmd(cmdbuf);}   static voidnl_disp_set(int amenu,int type){	extern int	topic_fld_len, title_fld_len;       	char	buf[256];	int     col,lines; 	menu = amenu;        lines = nlist_numnode();        if (amenu == MENU) { 	    sprintf(buf,"%s",n_title(nlist_node(0)));	    lines--;	}	else	    sprintf(buf,"Titles");        if (type == SIMPLE)	    col = 14; 	else  /* full display */	    col = 0;         disp_set(lines, nlist_fmt_line);	disp_set_title(buf);	disp_use_pointer(col);	select_func = nl_select;}cmd_full(){  disptype = FULL;  disp_setmsg("Full list display is now being used");  if (!disp_is_nlist())    return CMD_OK;  nl_disp_set(menu,disptype);   parse_cmd("clear");  menu = disp_ret_set(curlist);   return CMD_HIDE;}cmd_simple(){  disptype = SIMPLE;  disp_setmsg("Simple list display is now being used");  if (!disp_is_nlist())    return CMD_OK;  nl_disp_set(menu,disptype);  parse_cmd("clear");  menu = disp_ret_set(curlist);   return CMD_HIDE;}  /* * cmd_map handles path, content & map  */ cmd_map(startnode, levels)	NODE           *startnode;	u_short         levels;{	int             dirs,i; 	switch (*Argv[0]) {	case 'p':	        if (Argc < 3) /* have the default level be 99 */		  levels = 99;		dirs = TRAV_UP;		break;		/* above, or path */	case 'o':	        if (Argc < 3) /* have the default level be 2 */		  levels = 2;		dirs = TRAV_DOWN;		break;		/* below */	case '*':		dirs = TRAV_DOWN;		break;		/* below */	default:		dirs = TRAV_OUT;	} 	nlist_reset();	nlist_build(dirs, startnode, levels);  if ((dirs == TRAV_DOWN) && (levels == 1)){        i = 0;	nl_disp_set(MENU,disptype);}else {	nl_disp_set(NOMENU,disptype);	/* try to put pointer on startnode */        i =  nlist_find_id(n_id(startnode),nlist_numnode());       }        disp_pointer_set(i);	return CMD_OK;} cmd_expand(levels)u_short	levels;{	short	osize = nlist_numnode(); 	nlist_expand(levels);	nl_disp_set(NOMENU,disptype);	if (nlist_numnode() == osize) {		disp_setmsg("Nothing below current level of expansion.");		curlist++;cmd_back();		return CMD_FAIL;	}	disp_pointer_set(0);	list_depends = TRUE;	return CMD_OK;}  /* ---------------------------------------------------------------------- *//* ---------------------------------------------------------------------- *//* ---------------------------------------------------------------------- */ /* * The following is a kludge, and needs to be implemented in some nice, * buffered manner so that infinite length files shouldn't be a problem. * Right now, if your file is more then 10000 lines, we'll bomb... *  */  static int	textbuf_numline;static int      textbuf_size = 0;char *lineptrs[10000];char *text_buf = NULL; char *textbuf_line(int lineno){	if (lineno == textbuf_numline)	  return "---------------------------- END OF DOCUMENT ---------------------";	if (lineno > textbuf_numline)		return ""; 	return lineptrs[lineno];} static inttextbuf_load(char filename[]){	struct stat   st;	int	fd, line = 0, red,i;	char *cp;  	textbuf_numline = 0;	fd = open(filename, O_RDONLY,0644);	if (fd < 0) {		disp_syserr(filename);		return 0;	}	stat(filename,&st);	free(text_buf); 	text_buf = (char *) malloc(st.st_size);	red = read(fd, text_buf, st.st_size);	if (red == -1){	  disp_syserr(filename);	  return 0;	}	lineptrs[0] = cp = text_buf; /* now make ptrs to lines */	line = 1;	for ( i = 0; i < red; i++) {	  if (*cp == LF) {	    *cp = '\0';	    lineptrs[line] = cp + 1;	    line++;	  }	  cp++;	}	close(fd);	return textbuf_numline = --line;      } #ifndef GETFILESstatic int /* have the client get the text from the server */textbuf_node_load(NODE *n){	int	line = 0, red,i;	char *cp; 		textbuf_numline = 0;        if (text_buf){	     free(text_buf);	     text_buf = NULL;	   }	red = get_text(n);	if (red == 0)	  return textbuf_numline = red;	lineptrs[0] = cp = text_buf;	line = 1;	for ( i = 0; i < red; i++) {	  if (*cp == LF) {	    *cp = '\0';	    lineptrs[line] = cp + 1;	    line++;	  }	  cp++;	}	return textbuf_numline = --line;      }#endif /* ---------------------------------------------------------------------- *//* ---------------------------------------------------------------------- *//* ---------------------------------------------------------------------- */  cmd_show(n)NODE	*n;{	extern int	Debug;		/* From main.c */	char		log_line[100];	int		lines; 	if (strncasecmp(Argv[0],"text",3) == 0)	  n = web_node(cur_doc_id);	if (!text_node(n)) {		disp_setmsg("%s (%ld): There is no document attached here.",		       n_title(n), n_id(n));		return CMD_FAIL;	} #ifdef GETFILES /* get files from the network yourself */	ensure_access(n_file(n), n_locker(n), Debug);	lines = textbuf_load(n_file(n));        if (lines > 0) { 	  sprintf(log_line, "%d:%s:%s", n_id(n), n_title(n),n_source(n));	  log_read(log_line); 	}#else /* get files from the server */	lines = textbuf_node_load(n);#endif        if (lines > 0) {	  disp_set(lines, textbuf_line);	  disp_set_title(n_title(n));	  if (cur_doc_id == n_id(n)) 	    disp_ret_wind(TEXT);	  cur_scr_type = TEXT; 	  cur_doc_id = n_id(n); 	  select_func = 0;	  return CMD_OK;      }	else  {	  disp_setmsg("Could not get this document");	  return CMD_FAIL;	}} cmd_select(int lineno){	if (lineno < 0) {		disp_setmsg("Line number must be greater than or equal to 0.");		return CMD_FAIL;	}		if (!select_func) {	        if (Argc < 2) {		  cmd_redraw();		  return CMD_FAIL;		}		else		  select_func = nl_select;	} 	if (Argc < 2)		(*select_func)(disp_pointer_index() + 1);	else		(*select_func)(lineno);	return CMD_HIDE;} cmd_save(file)	char            file[];{	nlist_fio(file, NL_SAVE, FALSE); 	/* Say something! */ 	return CMD_OK;}  cmd_retrieve(file)	char            file[];{	nlist_fio(file, NL_RESTORE, FALSE); 	/* Say something! */	nl_disp_set(NOMENU,disptype);	disp_pointer_set(0);	return CMD_OK;}cmd_retrieve_o(file)	char            file[];{	nlist_fio(file, NL_RESTORE_OR, FALSE); 	/* Say something! */	nl_disp_set(NOMENU,disptype);	disp_pointer_set(0);	return CMD_OK;}cmd_retrieve_a(file)	char            file[];{	nlist_fio(file, NL_RESTORE_AND, FALSE); 	/* Say something! */	nl_disp_set(NOMENU,disptype);	disp_pointer_set(0);	return CMD_OK;}  LIST_CMD *add_cmd(char *cmd){  char *command;  LIST_CMD *new_cmd;   new_cmd = (LIST_CMD *) malloc(sizeof(LIST_CMD));  command = (char *) malloc(strlen(cmd)+1);  strcpy(command,cmd);  if (last_cmd)    last_cmd->next = new_cmd;  new_cmd->next = 0;  new_cmd->cmd = command;  last_cmd = new_cmd;    return new_cmd;} intcmd_back(){LIST_CMD *cmd_ptr,*ptr,*next_to_last = 0; 	if (!disp_is_nlist())	  {	    parse_cmd("*list");	    return CMD_HIDE;	  }	if (curlist < 2) {		disp_setmsg("There is no previous menu.");		return CMD_FAIL;	}save_cmd = FALSE;cmd_ptr = lists[--curlist]; for (ptr = cmd_ptr; ptr != last_cmd; ptr = ptr->next){  if (ptr->next == last_cmd)    next_to_last = ptr;   parse_cmd(ptr->cmd);} /* now free up */  /* pop last_cmd !! */   free(last_cmd->cmd);  free(last_cmd); /* assume storage still there */if (next_to_last){  next_to_last->next = 0;  last_cmd = next_to_last;}menu = disp_ret_set(curlist);nl_disp_set(menu,disptype);menu = disp_ret_set(curlist); return CMD_HIDE;} _save_cmdlist(char *cmd){LIST_CMD *last;  if (list_depends){    last = lists[curlist];    curlist++;      lists[curlist] = last;    add_cmd(cmd);  }  else    lists[++curlist] = add_cmd(cmd);  list_depends = FALSE;  /* where else is this set to false ?? */} cmd_find(subject) /* the search command */	char            subject[];{	/*	 * Must Move to nlist.c, set curdepth, CurRoot, this is a kluldge. 	 */ 	disp_msgnow("Looking for topics \"%s\"...", subject); 	nlist_reset();	#ifdef CLIENT	  nlist_search(subject);	#else	  web_find_topic(subject, nlist_add);	#endif 	if (nlist_numnode() <= 0) {		disp_setmsg("No matches were found.");		/* curlist++, cmd_back(); */		save_cmd = FALSE;		parse_cmd(last_cmd->cmd);		return CMD_FAIL;	}	nl_disp_set(NOMENU,disptype);	disp_setmsg("Found %d matches.", nlist_numnode());	disp_pointer_set(0);	return CMD_OK;} cmd_word(char str[]){	if (disp_search(str))		return CMD_OK;	return CMD_FAIL;}cmd_nextdoc(){int oldpos,ismenu;  if (!disp_is_nlist()) {    disp_ptr_on();/*     disp_save_wind(TEXT); should already be saved now */    ismenu = disp_ret_set(curlist);    disp_ret_wind(LIST);     oldpos = disp_pointer_index();    disp_pointer_dn(1);    while (!text_node(nlist_node(disp_pointer_index()+ismenu)) &&		      (disp_pointer_index() < disp_numlines()-1))  {        disp_pointer_dn(1);      }  if ((disp_pointer_index() <= oldpos)  ||      (!text_node(nlist_node(disp_pointer_index()+ismenu))) ) {     disp_setmsg("End of the current list");    disp_save_wind(LIST);    disp_save_set(curlist,menu);    disp_ptr_off();    disp_ret_wind(TEXT);}  else {    disp_save_set(curlist,menu);    disp_save_wind(LIST);    parse_cmd("*show"); }   disp_ptr_off();  return CMD_HIDE;  }disp_setmsg("This command only works when viewing a document");return CMD_FAIL;} cmd_prevdoc(){int oldpos,ismenu;  if (!disp_is_nlist()) { /*   disp_save_wind(TEXT); */    ismenu = disp_ret_set(curlist);    disp_ret_wind(LIST);    disp_ptr_on();    oldpos = disp_pointer_index();    disp_pointer_up(1);    while (!text_node(nlist_node(disp_pointer_index() + ismenu)) &&	   (disp_pointer_index() > 0)) {        disp_pointer_up(1);      }  if ((disp_pointer_index() == oldpos) ||      (!text_node(nlist_node(disp_pointer_index() + ismenu)))) {    disp_save_wind(LIST);    disp_ptr_off();    disp_setmsg("Top of the current list");    disp_ret_wind(TEXT); }  else {    disp_save_set(curlist,menu);    disp_save_wind(LIST);    parse_cmd("*show"); }   disp_ptr_off();  return CMD_HIDE;  }disp_setmsg("This command only works when viewing a document");return CMD_FAIL;} cmd_scan(char str[],int col){	if (disp_search_col(str,col))		return CMD_OK;	return CMD_FAIL;} cmd_list(){if (!disp_is_nlist()) {	menu = disp_ret_set(curlist);	nl_disp_set(menu,disptype);	menu = disp_ret_set(curlist);      }return CMD_OK; } cmd_help(){	int	lines; 	if (cflag == MIN)	  lines = textbuf_load(HELP_FILE);			else	  lines = textbuf_load(HELP_FILE_ADV);	disp_set(lines, textbuf_line);	disp_set_title("*** Help Screen ***");	cur_scr_type = HELP;	return CMD_OK;} cmd_describe(n)NODE	*n;{	int	line = 0;	char	buf[256];	struct stat st;	struct   tm *thedate;	char theday[20];	char *aline = "          More Information on sources can be found under - About Techinfo";  #define	tprintf(fmt, arg)	sprintf(buf, fmt, arg),\					disp_set_heading(line++, buf)       disp_clear_headings();	tprintf("           Id: %ld", n_id(n));	tprintf("        Topic: [%s]", n_topic(n));	tprintf("        Title: [%s]", n_title(n));	tprintf("       Source: [%s]", n_source(n)); 	if (text_node(n)) {	  ensure_access(n_file(n),n_locker(n),0);          stat(n_file(n),&st);          thedate = localtime(&st.st_mtime);	sprintf(theday,"%2d/%2d/%2d",thedate->tm_mon +1,thedate->tm_mday, thedate->tm_year);	  tprintf("Last modified: %s",theday);	  tprintf("       Locker: [%s]", n_locker(n));	  tprintf("         File: [%s]", n_file(n));	}	tprintf("%s",aline);	disp_set_heading(line, "\\-");	if (cur_scr_type == TEXT)	  disp_ret_wind(TEXT);	return CMD_OK;} cmd_redraw(){	disp_clear_headings();	disp_redraw();	return CMD_HIDE; /* */} cmd_min(){  cflag = MIN;  disp_setmsg("The basic command set is now being used.");} cmd_adv(){  cflag = ADV;  disp_setmsg("The advanced command set is now being used.");}cmd_format(){	extern int	optind;	extern char	*optarg; 	extern int	indent_per_lev;	extern int	topic_fld_len;	extern int	title_fld_len; 	int	c; 	if (Argc > 2)		while ((c = getopt(Argc, Argv, "i:s:t:")) != -1)			switch (c) {			case 'i':				indent_per_lev = atoi(optarg);				break;			case 's':				topic_fld_len = atoi(optarg);				break;			case 't':				title_fld_len = atoi(optarg);				break;			}	optind = 1; 	disp_setmsg("Cur Indent is %d, Subject Fld Len is %d, Title Fld Len is %d.",		indent_per_lev, topic_fld_len, title_fld_len); 	cmd_list(); 	return CMD_OK;}cmd_quit(){  do_quit();/* not reached */} /*static int	dfd; static void_cmd_dump_aux(n)NODE	*n;{	char	*nstr;	int	len; 	 nstr = web_node_str(n); 	len = strlen(nstr);	nstr[len] = LF;	write(dfd, nstr, len + 1);} #include <sys/file.h> cmd_dump(){	char	*dump_file = "nodes.txt"; 	if ((dfd = open(dump_file, O_WRONLY | O_CREAT | O_TRUNC)) == 0) {		disp_syserr(dump_file);		return CMD_FAIL;	}	disp_msgnow("Dumping to %s...", dump_file);        web_allnodes(_cmd_dump_aux);  	close(dfd);	return CMD_OK;} */static char *usercmd;static char *username; cmd_alias(NODE *n, char *name){ usercmd = (char *) malloc(20); username = (char *) malloc(20); strcpy(username,name); sprintf(usercmd,"%ld",n_id(n));  return CMD_OK;}cmd_kermit(NODE *n){  char str[MAXPATHLEN];	if (!text_node(n)) {		disp_setmsg("%s (%ld): This is not a document.",		       n_title(n), n_id(n));		return CMD_FAIL;	}sprintf(str,"/usr/new/kermit -s %s",n_file(n));system(str);cmd_redraw();return CMD_OK;}cmd_ftp(NODE *n){  char str[MAXPATHLEN];sprintf(str,"ftp put %s",n_file(n));system(str);cmd_redraw();return CMD_OK;} #define	A_OPTNUM		1	/* Optional Integer Arg */#define	A_STR			2	/* string (char *) argument */#define	A_OPTSTR		3	/* Opt string (char *) argument */#define A_STR_OPTNUM            5 /* need a node */#define A_NODE_STR              7       /* reguired node and str */#define	A_OPTNODE		9	/* Optional Node Arg (abs or rel) */#define A_NODE			10	/* Required Node Arg (abs or rel) */#define	A_NODE_OPTNUM		11	/* Node w/optional node indicator */#define A_OPTNODE_OPTNUM	13 /* * Note that integer arguments automatically default to 1. */ typedef struct command COMMAND; extern int	initialize(); struct command {	char	*c_name;	char	*c_keys[8];		/* Max 6 */	int	(*c_func)();	short   c_set;	short	c_argset;} cmdtab[] = {"*alias",       {},                        cmd_alias,   ADV,   A_NODE_STR,"*format",	{},			   cmd_format,	ADV,   0,"*init",	{},			   initialize,	ADV,   0,"advanced",     {},                        cmd_adv,     MIN,   0,"basic",        {},                        cmd_min,     ADV,   0,"path", 	{ "^A" },		   cmd_map,	ADV,   A_OPTNODE_OPTNUM,"return",	{"^R"},			   cmd_back,	MIN,   0,"main",	        {},			   cmd_begin,	MIN,   0,"outline",	{ "^B" },		   cmd_map,	MIN,   A_OPTNODE_OPTNUM,"*below",	{ "^B" },		   cmd_map,	MIN,   A_OPTNODE_OPTNUM,"bottom",	{"^[>"},	           cmd_bottom,	MIN,   0,"clear",	{ "^L" },		   cmd_redraw,	MIN,   0,"full",         {},                        cmd_full,    ADV,   0,"simple",       {},                        cmd_simple,  ADV,   0,"source",	{},			   cmd_describe,MIN,    A_OPTNODE,"*kermit",	{},			   cmd_kermit,  MIN,    A_OPTNODE,"*ftp",	        {},			   cmd_ftp,     MIN,    A_OPTNODE,"down",		{ "^[[C","^[OC","^[C", "^V","^[[6~" },   cmd_pagedown,MIN,    A_OPTNUM,"*space",       {},                        cmd_pagedown,MIN,    A_OPTNUM,"expand",	{ "^E" },		   cmd_expand,	ADV,    A_OPTNUM,"search",      	{ "^[[1~" },		   cmd_find,	ADV,    A_STR,"help",		{ "^[[28~", "?" },	   cmd_help,	MIN,    0,"history",	{ "!" },		   cmd_history,	ADV,    A_OPTNUM,/* "list",		{},			   cmd_list,	ADV,    0,*/"*list",	{},			   cmd_list,	MIN,    0,/* "map",		{},			   cmd_map,	ADV,    A_OPTNODE_OPTNUM, */"ptrup",	{ "^[[A","^[OA","^[A", "^P" },   cmd_up,	MIN,	A_OPTNUM,"ptrdown",	{ "^[[B","^[OB","^[B", "^N" },   cmd_down,	MIN,    A_OPTNUM,"quit",         {},                        cmd_quit,    MIN,    0,"get",	        {},			   cmd_retrieve,ADV,	A_OPTSTR,"*and",  	{},			   cmd_retrieve_a,ADV,	A_OPTSTR,"*or",   	{},			   cmd_retrieve_o,ADV,	A_OPTSTR,"save",		{},			   cmd_save,	ADV,    A_OPTSTR,/* "select",	{ "^[[4~","^K","^[[29~" }, cmd_select,	ADV,    A_OPTNUM, */"*select",	{ "^[[4~","^K","^[[29~" }, cmd_select,	MIN,    A_OPTNUM,"text   ",	{},			   cmd_show,	MIN,    A_OPTNODE,"*show",       	{},			   cmd_show,	MIN,    A_OPTNODE,"top",		{"^[<"},		   cmd_top,	MIN,    0,"next",         {},                        cmd_nextdoc, ADV,    0,"previous",     {},                        cmd_prevdoc, ADV,    0,"up",		{ "^[[D","^[OD", "^[[5~","^[D","^[V" }, cmd_pageup,	MIN,    A_OPTNUM,"find",		{ "^\\" },		   cmd_word,	MIN,    A_OPTSTR,"scan",         {},                        cmd_scan,    ADV,    A_STR_OPTNUM,0,		{},			   0,		0,      0 };  char *match_meta(char	cmd_str[]){	int	i;	char	**cp; 	for (i = 0; cmdtab[i].c_name; i++)		for (cp = cmdtab[i].c_keys; *cp; cp++)		      if ((!strcasecmp(cmd_str, *cp))&&(cmdtab[i].c_set<=cflag))				return cmdtab[i].c_name;	return 0;} char *complete_cmd(cmd_str)	char	cmd_str[];{	char	*fullcmd = 0;	int	i, len; 	len = strlen(cmd_str);	for (i = 0; cmdtab[i].c_name; i++)		if ((strncasecmp(cmd_str, cmdtab[i].c_name, len) == 0)	  &&(cmdtab[i].c_set<=cflag)){			if (fullcmd)				return (char *) 0;			fullcmd = cmdtab[i].c_name;		}	return fullcmd;} static COMMAND *lookup_cmd(cmd_str)	char	cmd_str[];{	COMMAND	*cmd_ptr = 0;	char	*cmp_str;	int	i, len; 	len = strlen(cmd_str);	for (i = 0; cmdtab[i].c_name; i++) {		cmp_str = cmdtab[i].c_name;		if (cmp_str && !strncasecmp(cmd_str, cmp_str, len)		    && (cmdtab[i].c_set<=cflag)) {			if (cmd_ptr)				return 0;			cmd_ptr = &cmdtab[i];		}	}	return cmd_ptr;} NODE *is_node_cmd(char *cmd_str){COMMAND *acmd = 0;char *id,*cp;char *the_args[MAXARGS + 1];int cnt;   cnt = tokenize(cmd_str, the_args, MAXARGS);  acmd = lookup_cmd(the_args[0]);  if (acmd->c_argset > 6)       return web_node(atol(the_args[1])); return NULL; } static NODE *nidstr_to_node(str)		/* The existence of this routine is somehwat */	char	str[];		/* of a kludge, but we can live with it. */{	long	node_id;	NODE    *n; 	if (!inttest(str))		return 0; 	node_id = atol(str);	if (!node_id || node_id > nlist_numnode() - menu)		return web_node(node_id);	disp_pointer_set(node_id - 1);	/* Point here (page to it if needed) */	return nlist_node(node_id - 1 + menu);} intis_node(n) /* is this just a dummy node ? */NODE *n;{ if (strncmp(n_file(n),"/dev/null",8) == 0)  return FALSE;return TRUE;}/*static charopt_int_msg[]	= "[%s]: The optional argument to `%s' must be a positive integer.",opt_node_msg[]	= "[%s]: The optional argument to `%s' must be a valid node ID.",need_node_msg[]	= "[%s]: The command `%s' needs a valid node ID as an argument.";*/static charopt_int_msg[]	= "The optional argument must be a positive integer.",opt_node_msg[]	= "Not a valid menu number, or node id.",need_node_msg[]	= "Not a valid menu number, or node id."; /* * This is the major workhorse command parsing routine. * It takes a line, splits it up into an argv, argc type format, * finds a the command to run, ensures the appropriate arguments * have been provided, and calls the command, and then deals with * possible history & display changes (that havent already been called). * * This may be called RECURSIVELY, and is in a few places.  Relatively * safe to do, cept that the global Argc, Argv vars that are set may not * be what they were before in a routine AFTER a recursive call to parse_cmd. * It's unlikely this is a problem, as the global Argc/Argv is rarely used. */ intparse_cmd(cmdline)	char	cmdline[];{	COMMAND	*cmd;	NODE	*node;	long	numarg;	int	noint, argc;	char	cmdbuf[128], fullcmd[128], *fcp,line[100];	int     status,redis; 	int	numnode = nlist_numnode();	/* This stuff is for our  */	NODE	*topnode = nlist_node(0),	/* shortcut heuristic --  */		*botnode = nlist_node(numnode - 1);/* see end of parse_cmd.  */ 	if (!cmdline)		return;	/* disp_clear_headings();   */	/*	 * Kludge for select (show/below,history) aliasing with just a node	 * ID, absolute or relative.  This is fake-out/simulated input code.	 */ /*	if (!*cmdline)		strcpy(cmdbuf, "clear"); why make screen redraw when enter hit? 		else if ... *//*	if (strncasecmp(username,cmdline,2) == 0)  hacked for alias 		sprintf(cmdbuf, "*select %s", usercmd); 	else */ 	if (inttest(cmdline) | (*cmdline == '-'))		sprintf(cmdbuf, "*select %s", cmdline);	else		strcpy(cmdbuf, cmdline);  	/*	 * Now we actually get around to begin parsing the command up.	 */  	if ((Argc = argc = tokenize(cmdbuf, Argv, MAXARGS)) == 0)		return; 	cmd = lookup_cmd(Argv[0]); 	if (!cmd) {		disp_setmsg("Unknown or incomplete command: `%s' -- Type help for a list of commands.",		       Argv[0]);		return;	}	strcpy(fcp = fullcmd, cmd->c_name); 	if ((save_cmd)&&(cur_scr_type==LIST))  /*   */	  disp_save_set(curlist,menu);	while (*fcp)		fcp++; #define	addarg(fmt, arg)	sprintf(fcp, fmt, arg); while (*fcp) fcp++;#define	maxarg(n)		if (Argc - 1 > n) disp_setmsg("%d extraneous arguments were ignored.", (Argc - 1) - n); 	switch (cmd->c_argset) {	case A_OPTNUM:		noint = YES;		if (argc > 1 && (noint = !inttest(Argv[1]))) {			disp_setmsg(opt_int_msg, cmdline, cmd->c_name);			return;		}		if (!noint)			addarg(" %s", Argv[1]);		maxarg(1);		status = (*cmd->c_func) (noint ? 1L : atol(Argv[1]));		break;	case A_NODE_OPTNUM:                node = nidstr_to_node(Argv[1]);    		if (argc < 2 || !(is_node(node))) {			disp_setmsg(need_node_msg, cmdline, cmd->c_name);			return;		}		addarg(" %05ld", n_id(node));		if (argc > 2 && (noint = !inttest(Argv[2]))) {			disp_setmsg(opt_int_msg, cmdline, cmd->c_name);			return;		}		if (!noint)			addarg(" %s", Argv[2]);		maxarg(2);		status = (*cmd->c_func) (node, noint ? 1L : atol(Argv[2]));		break;	case A_NODE_STR:   /* just a test */                node = nidstr_to_node(Argv[1]);    		if (argc < 3 || !(is_node(node))) {			disp_setmsg(need_node_msg, cmdline, cmd->c_name);			return;		}		addarg(" %05ld", n_id(node));		addarg(" %s", Argv[2]);		maxarg(2);		status = (*cmd->c_func) (node, Argv[2]);		break;	case A_OPTNODE_OPTNUM:		node = 0;		if (argc > 1) {                        if (!inttest(Argv[1])) {				disp_setmsg(opt_node_msg, cmdline, cmd->c_name);				return; }			  			node = nidstr_to_node(Argv[1]);			if (!is_node(node) && *Argv[1] != '.') {			  disp_setmsg(need_node_msg, cmdline, cmd->c_name);			  return; }		}		else {		        if (!disp_is_nlist())		        menu = disp_ret_set(curlist); 			node = nlist_node(disp_pointer_index()+menu);		      }		addarg(" %05ld", n_id(node));		noint = YES;		if (argc > 2 && (noint = !inttest(Argv[2]))) {			disp_setmsg(opt_int_msg, cmdline, cmd->c_name);			return;		}		if (!noint)			addarg(" %s", Argv[2]);		maxarg(2);		status = (*cmd->c_func) (node, noint ? 1L : atol(Argv[2]));		break;	case A_OPTNODE:		if (argc > 1) {                        if (!inttest(Argv[1])) {				disp_setmsg(opt_node_msg, cmdline, cmd->c_name);				return; }			node = nidstr_to_node(Argv[1]);            			if (!is_node(node)) {				disp_setmsg(opt_node_msg, cmdline, cmd->c_name);				return;			}		} else {		        if (!disp_is_nlist())		        menu = disp_ret_set(curlist);  			node = nlist_node(disp_pointer_index()+menu);}		addarg(" %05ld", n_id(node));		maxarg(1);		status = (*cmd->c_func) (node);		break;	case A_STR:		if (argc < 2) {			disp_setmsg("%s: The command `%s' needs a string argument.", cmdline, cmd->c_name);			return;		}		addarg(" %s", Argv[1]);		maxarg(1);		status = (*cmd->c_func) (Argv[1]);		break;	case A_STR_OPTNUM:		if (argc < 2) {			disp_setmsg("%s: The command `%s' needs a string argument.", cmdline, cmd->c_name);			return;		}		addarg(" %s", Argv[1]);		noint = YES;		if (argc > 2 && (noint = !inttest(Argv[2]))) {			disp_setmsg(opt_int_msg, cmdline, cmd->c_name);			return;		}		if (!noint)			addarg(" %s", Argv[2]);		maxarg(2);		status = (*cmd->c_func) (Argv[1], noint ? 1L : atoi(Argv[2]));		break;	case A_OPTSTR:		maxarg(1);		if (argc > 1) {			addarg(" %s", Argv[1]);			status = (*cmd->c_func) (Argv[1]);		} else			status = (*cmd->c_func) ((char *) 0);		break;	default:		if (cmd->c_argset){			disp_setmsg("Don't understand argument set (%d)",			       cmd->c_argset);			/* maxarg(cmd->c_argset); */		      }		status = (*cmd->c_func)();	}        if (disp_is_nlist())	  cur_scr_type = LIST;	if (cur_scr_type == TEXT)  /* only case it matters now */	  disp_save_wind(cur_scr_type); 	if ((strncasecmp(Argv[0],"source",2) != 0) && (status == CMD_OK))	  	  disp_clear_headings();  	if (status != CMD_OK)		return;	hist_add(fullcmd); 	if (!curlist && cmd->c_func != cmd_back && save_cmd)	/* First time only */		_save_cmdlist(fullcmd);			/*	 * The following messing conditional is to try and see if the	 * nodelist has changed at all (I would rather not maintain a flag	 * for this purpose).  I'm betting it works most all the time, but I	 * can't gurantee its will catch all changes, especially in the event	 * of increased functionality in the area of nlist manipulation. 	 */ 	/*	 * This is all a big mess, will change later 	 */ 		if (numnode != nlist_numnode() || /* if the list changed */	    topnode != nlist_node(0) ||	    botnode != nlist_node(numnode - 1)) { 		if (curlist && cmd->c_func != cmd_back && save_cmd) {			_save_cmdlist(fullcmd); 		      }	}	return;}    °üÁÄ˜uWKE)  2½KE)  2½KE)  2½