Example #1
0
int	b_hist(int argc,char *argv[], void *extra)
{
	register History_t *hp;
	register char *arg;
	register int flag,fdo;
	register Shell_t *shp = ((Shbltin_t*)extra)->shp;
	Sfio_t *outfile;
	char *fname;
	int range[2], incr, index2, indx= -1;
	char *edit = 0;		/* name of editor */
	char *replace = 0;		/* replace old=new */
	int lflag = 0, nflag = 0, rflag = 0;
#if SHOPT_HISTEXPAND
	int pflag = 0;
#endif
	Histloc_t location;
	NOT_USED(argc);
	if(!sh_histinit((void*)shp))
		errormsg(SH_DICT,ERROR_system(1),e_histopen);
	hp = shp->gd->hist_ptr;
	while((flag = optget(argv,sh_opthist))) switch(flag)
	{
	    case 'e':
		edit = opt_info.arg;
		break;
	    case 'n':
		nflag++;
		break;
	    case 'l':
		lflag++;
		break;
	    case 'r':
		rflag++;
		break;
	    case 's':
		edit = "-";
		break;
#if SHOPT_HISTEXPAND
	    case 'p':
		pflag++;
		break;
#endif
	    case 'N':
		if(indx<=0)
		{
			if((flag = hist_max(hp) - opt_info.num-1) < 0)
				flag = 1;
			range[++indx] = flag;
			break;
		}
	    case ':':
		errormsg(SH_DICT,2, "%s", opt_info.arg);
		break;
	    case '?':
		errormsg(SH_DICT,ERROR_usage(2), "%s", opt_info.arg);
		break;
	}
	if(error_info.errors)
		errormsg(SH_DICT,ERROR_usage(2),"%s",optusage((char*)0));
	argv += (opt_info.index-1);
#if SHOPT_HISTEXPAND
	if(pflag)
	{
		hist_cancel(hp);
		pflag = 0;
		while(arg=argv[1])
		{
			flag = hist_expand(arg,&replace);
			if(!(flag & HIST_ERROR))
				sfputr(sfstdout, replace, '\n');
			else
				pflag = 1;
			if(replace)
				free(replace);
			argv++;
		}
		return pflag;
	}
#endif
	flag = indx;
	while(flag<1 && (arg=argv[1]))
	{
		/* look for old=new argument */
		if(!replace && strchr(arg+1,'='))
		{
			replace = arg;
			argv++;
			continue;
		}
		else if(isdigit(*arg) || *arg == '-')
		{
			/* see if completely numeric */
			do	arg++;
			while(isdigit(*arg));
			if(*arg==0)
			{
				arg = argv[1];
				range[++flag] = (int)strtol(arg, (char**)0, 10);
				if(*arg == '-')
					range[flag] += (hist_max(hp)-1);
				argv++;
				continue;
			}
		}
		/* search for last line starting with string */
		location = hist_find(hp,argv[1],hist_max(hp)-1,0,-1);
		if((range[++flag] = location.hist_command) < 0)
			errormsg(SH_DICT,ERROR_exit(1),e_found,argv[1]);
		argv++;
	}
	if(flag <0)
	{
		/* set default starting range */
		if(lflag)
		{
			flag = hist_max(hp)-16;
			if(flag<1)
				flag = 1;
		}
		else
			flag = hist_max(hp)-2;
		range[0] = flag;
		flag = 0;
	}
	index2 = hist_min(hp);
	if(range[0]<index2)
		range[0] = index2;
	if(flag==0)
		/* set default termination range */
		range[1] = ((lflag && !edit)?hist_max(hp)-1:range[0]);
	if(range[1]>=(flag=(hist_max(hp) - !lflag)))
		range[1] = flag;
	/* check for valid ranges */
	if(range[1]<index2 || range[0]>=flag)
		errormsg(SH_DICT,ERROR_exit(1),e_badrange,range[0],range[1]);
	if(edit && *edit=='-' && range[0]!=range[1])
		errormsg(SH_DICT,ERROR_exit(1),e_eneedsarg);
	/* now list commands from range[rflag] to range[1-rflag] */
	incr = 1;
	flag = rflag>0;
	if(range[1-flag] < range[flag])
		incr = -1;
	if(lflag)
	{
		outfile = sfstdout;
		arg = "\n\t";
	}
	else
	{
		if(!(fname=pathtmp(NIL(char*),0,0,NIL(int*))))
			errormsg(SH_DICT,ERROR_exit(1),e_create,"");
		if((fdo=open(fname,O_CREAT|O_RDWR,S_IRUSR|S_IWUSR)) < 0)
			errormsg(SH_DICT,ERROR_system(1),e_create,fname);
		outfile= sfnew(NIL(Sfio_t*),shp->outbuff,IOBSIZE,fdo,SF_WRITE);
		arg = "\n";
		nflag++;
	}
	while(1)
	{
		if(nflag==0)
			sfprintf(outfile,"%d\t",range[flag]);
		else if(lflag)
			sfputc(outfile,'\t');
		hist_list(shp->gd->hist_ptr,outfile,hist_tell(shp->gd->hist_ptr,range[flag]),0,arg);
		if(lflag)
			sh_sigcheck(shp);
		if(range[flag] == range[1-flag])
			break;
		range[flag] += incr;
	}
	if(lflag)
		return(0);
	sfclose(outfile);
	hist_eof(hp);
	arg = edit;
	if(!arg && !(arg=nv_getval(sh_scoped(shp,HISTEDIT))) && !(arg=nv_getval(sh_scoped(shp,FCEDNOD))))
		arg = (char*)e_defedit;
#ifdef apollo
	/*
	 * Code to support the FC using the pad editor.
	 * Exampled of how to use: HISTEDIT=pad
	 */
	if (strcmp (arg, "pad") == 0)
	{
		extern int pad_create(char*);
		sh_close(fdo);
		fdo = pad_create(fname);
		pad_wait(fdo);
		unlink(fname);
		strcat(fname, ".bak");
		unlink(fname);
		lseek(fdo,(off_t)0,SEEK_SET);
	}
	else
	{
#endif /* apollo */
	if(*arg != '-')
	{
		char *com[3];
		com[0] =  arg;
		com[1] =  fname;
		com[2] = 0;
		error_info.errors = sh_eval(sh_sfeval(com),0);
	}
	fdo = sh_chkopen(fname);
	unlink(fname);
	free((void*)fname);
#ifdef apollo
	}
#endif /* apollo */
	/* don't history fc itself unless forked */
	error_info.flags |= ERROR_SILENT;
	if(!sh_isstate(SH_FORKED))
		hist_cancel(hp);
	sh_onstate(SH_HISTORY);
	sh_onstate(SH_VERBOSE);	/* echo lines as read */
	if(replace)
		hist_subst(error_info.id,fdo,replace);
	else if(error_info.errors == 0)
	{
		char buff[IOBSIZE+1];
		Sfio_t *iop = sfnew(NIL(Sfio_t*),buff,IOBSIZE,fdo,SF_READ);
		/* read in and run the command */
		if(shp->hist_depth++ > HIST_RECURSE)
			errormsg(SH_DICT,ERROR_exit(1),e_toodeep,"history");
		sh_eval(iop,1);
		shp->hist_depth--;
	}
Example #2
0
int hist_expand(const char *ln, char **xp)
{
	int	off,	/* stack offset */
		q,	/* quotation flags */
		p,	/* flag */
		c,	/* current char */
		flag=0;	/* HIST_* flags */
	Sfoff_t	n,	/* history line number, counter, etc. */
		i,	/* counter */
		w[2];	/* word range */
	char	*sp,	/* stack pointer */
		*cp,	/* current char in ln */
		*str,	/* search string */
		*evp,	/* event/word designator string, for error msgs */
		*cc=0,	/* copy of current line up to cp; temp ptr */
		hc[3],	/* default histchars */
		*qc="\'\"`";	/* quote characters */
	Sfio_t	*ref=0,	/* line referenced by event designator */
		*tmp=0,	/* temporary line buffer */
		*tmp2=0;/* temporary line buffer */
	Histloc_t hl;	/* history location */
	static Namval_t *np = 0;	/* histchars variable */
	static struct subst	sb = {0,0};	/* substition strings */
	static Sfio_t	*wm=0;	/* word match from !?string? event designator */

	if(!wm)
		wm = sfopen(NULL, NULL, "swr");

	hc[0] = '!';
	hc[1] = '^';
	hc[2] = 0;
	if((np = nv_open("histchars",sh.var_tree,0)) && (cp = nv_getval(np)))
	{
		if(cp[0])
		{
			hc[0] = cp[0];
			if(cp[1])
			{
				hc[1] = cp[1];
				if(cp[2])
					hc[2] = cp[2];
			}
		}
	}

	/* save shell stack */
	if(off = staktell())
		sp = stakfreeze(0);

	cp = (char*)ln;

	while(cp && *cp)
	{
		/* read until event/quick substitution/comment designator */
		if((*cp != hc[0] && *cp != hc[1] && *cp != hc[2]) 
		   || (*cp == hc[1] && cp != ln))
		{
			if(*cp == '\\')	/* skip escaped designators */
				stakputc(*cp++);
			else if(*cp == '\'') /* skip quoted designators */
			{
				do
					stakputc(*cp);				
				while(*++cp && *cp != '\'');
			}
			stakputc(*cp++);
			continue;
		}

		if(hc[2] && *cp == hc[2]) /* history comment designator, skip rest of line */
		{
			stakputc(*cp++);
			stakputs(cp);
			DONE();
		}

		n = -1;
		str = 0;
		flag &= HIST_EVENT; /* save event flag for returning later */
		evp = cp;
		ref = 0;

		if(*cp == hc[1]) /* shortcut substitution */
		{
			flag |= HIST_QUICKSUBST;
			goto getline;
		}

		if(*cp == hc[0] && *(cp+1) == hc[0]) /* refer to line -1 */
		{
			cp += 2;
			goto getline;
		}

		switch(c = *++cp) {
		case ' ':
		case '\t':
		case '\n':
		case '\0':
		case '=':
		case '(':
			stakputc(hc[0]);
			continue;
		case '#': /* the line up to current position */
			flag |= HIST_HASH;
			cp++;
			n = staktell(); /* terminate string and dup */
			stakputc('\0');
			cc = strdup(stakptr(0));
			stakseek(n); /* remove null byte again */
			ref = sfopen(ref, cc, "s"); /* open as file */
			n = 0; /* skip history file referencing */
			break;
		case '-': /* back reference by number */
			if(!isdigit(*(cp+1)))
				goto string_event;
			cp++;
		case '0': /* reference by number */
		case '1':
		case '2':
		case '3':
		case '4':
		case '5':
		case '6':
		case '7':
		case '8':
		case '9':
			n = 0;
			while(isdigit(*cp))
				n = n * 10 + (*cp++) - '0';
			if(c == '-')
				n = -n;
			break;
		case '$':
			n = -1;
		case ':':
			break;
		case '?':
			cp++;
			flag |= HIST_QUESTION;
		string_event:
		default:
			/* read until end of string or word designator/modifier */
			str = cp;
			while(*cp)
			{
				cp++;
				if((!(flag&HIST_QUESTION) &&
				   (*cp == ':' || isspace(*cp)
				    || *cp == '^' || *cp == '$'
				    || *cp == '*' || *cp == '-'
				    || *cp == '%')
				   )
				   || ((flag&HIST_QUESTION) && (*cp == '?' || *cp == '\n')))
				{
					c = *cp;
					*cp = '\0';
				}
			}
			break;
		}

getline:
		flag |= HIST_EVENT;
		if(str)	/* !string or !?string? event designator */
		{

			/* search history for string */
			hl = hist_find(shgd->hist_ptr, str,
				       shgd->hist_ptr->histind,
				       flag&HIST_QUESTION, -1);
			if((n = hl.hist_command) == -1)
				n = 0;	/* not found */
		}
		if(n)
		{
			if(n < 0) /* determine index for backref */
				n = shgd->hist_ptr->histind + n;
			/* search and use history file if found */
			if(n > 0 && hist_seek(shgd->hist_ptr, n) != -1)
				ref = shgd->hist_ptr->histfp;

		}
		if(!ref)
		{
			/* string not found or command # out of range */
			c = *cp;
			*cp = '\0';
			errormsg(SH_DICT, ERROR_ERROR, "%s: event not found", evp);
			*cp = c;
			DONE();
		}

		if(str) /* string search: restore orig. line */
		{
			if(flag&HIST_QUESTION)
				*cp++ = c; /* skip second question mark */
			else
				*cp = c;
		}

		/* colon introduces either word designators or modifiers */
		if(*(evp = cp) == ':')
			cp++;

		w[0] = 0; /* -1 means last word, -2 means match from !?string? */
		w[1] = -1; /* -1 means last word, -2 means suppress last word */

		if(flag & HIST_QUICKSUBST) /* shortcut substitution */
			goto getsel;

		n = 0;
		while(n < 2)
		{
			switch(c = *cp++) {
			case '^': /* first word */
				if(n == 0)
				{
					w[0] = w[1] = 1;
					goto skip;
				}
				else
					goto skip2;
			case '$': /* last word */
				w[n] = -1;
				goto skip;
			case '%': /* match from !?string? event designator */
				if(n == 0)
				{
					if(!str)
					{
						w[0] = 0;
						w[1] = -1;
						ref = wm;
					}
					else
					{
						w[0] = -2;
						w[1] = sftell(ref) + hl.hist_char;
					}
					sfseek(wm, 0, SEEK_SET);
					goto skip;
				}
			default:
			skip2:
				cp--;
				n = 2;
				break;
			case '*': /* until last word */
				if(n == 0)
					w[0] = 1;
				w[1] = -1;
			skip:
				flag |= HIST_WORDDSGN;
				n = 2;
				break;
			case '-': /* until last word or specified index */
				w[1] = -2;
				flag |= HIST_WORDDSGN;
				n = 1;
				break;
			case '0':
			case '1':
			case '2':
			case '3':
			case '4':
			case '5':
			case '6':
			case '7':
			case '8':
			case '9': /* specify index */
				if((*evp == ':') || w[1] == -2)
				{
					w[n] = c - '0';
					while(isdigit(c=*cp++))
						w[n] = w[n] * 10 + c - '0';
					flag |= HIST_WORDDSGN;
					if(n == 0)
						w[1] = w[0];
					n++;
				}
				else
					n = 2;
				cp--;
				break;
			}
		}

		if(w[0] != -2 && w[1] > 0 && w[0] > w[1])
		{
			c = *cp;
			*cp = '\0';
			errormsg(SH_DICT, ERROR_ERROR, "%s: bad word specifier", evp);
			*cp = c;
			DONE();
		}

		/* no valid word designator after colon, rewind */
		if(!(flag & HIST_WORDDSGN) && (*evp == ':'))
			cp = evp;

getsel:
		/* open temp buffer, let sfio do the (re)allocation */
		tmp = sfopen(NULL, NULL, "swr");

		/* push selected words into buffer, squash 
		   whitespace into single blank or a newline */
		n = i = q = 0;

		while((c = sfgetc(ref)) > 0)
		{
			if(isspace(c))
			{
				flag |= (c == '\n' ? HIST_NEWLINE : 0);
				continue;
			}

			if(n >= w[0] && ((w[0] != -2) ? (w[1] < 0 || n <= w[1]) : 1))
			{
				if(w[0] < 0)
					sfseek(tmp, 0, SEEK_SET);
				else
					i = sftell(tmp);

				if(i > 0)
					sfputc(tmp, flag & HIST_NEWLINE ? '\n' : ' ');

				flag &= ~HIST_NEWLINE;
				p = 1;
			}
			else
				p = 0;

			do
			{
				cc = strchr(qc, c);
				q ^= cc ? 1<<(int)(cc - qc) : 0;
				if(p)
					sfputc(tmp, c);
			}
			while((c = sfgetc(ref)) > 0  && (!isspace(c) || q));

			if(w[0] == -2 && sftell(ref) > w[1])
				break;

			flag |= (c == '\n' ? HIST_NEWLINE : 0);
			n++;
		}
		if(w[0] != -2 && w[1] >= 0 && w[1] >= n)
		{
			c = *cp;
			*cp = '\0';
			errormsg(SH_DICT, ERROR_ERROR, "%s: bad word specifier", evp);
			*cp = c;
			DONE();
		}
		else if(w[1] == -2)	/* skip last word */
			sfseek(tmp, i, SEEK_SET);

		/* remove trailing newline */
		if(sftell(tmp))
		{
			sfseek(tmp, -1, SEEK_CUR);
			if(sfgetc(tmp) == '\n')
				sfungetc(tmp, '\n');
		}

		sfputc(tmp, '\0');

		if(str)
		{
			if(wm)
				sfclose(wm);
			wm = tmp;
		}

		if(cc && (flag&HIST_HASH))
		{
			/* close !# temp file */
			sfclose(ref);
			flag &= ~HIST_HASH;
			free(cc);
			cc = 0;
		}

		evp = cp;

		/* selected line/words are now in buffer, now go for the modifiers */
		while(*cp == ':' || (flag & HIST_QUICKSUBST))
		{
			if(flag & HIST_QUICKSUBST)
			{
				flag &= ~HIST_QUICKSUBST;
				c = 's';
				cp--;
			}
			else
				c = *++cp;

			sfseek(tmp, 0, SEEK_SET);
			tmp2 = sfopen(tmp2, NULL, "swr");

			if(c == 'g') /* global substitution */
			{
				flag |= HIST_GLOBALSUBST;
				c = *++cp;
			}

			if(cc = strchr(modifiers, c))
				flag |= mod_flags[cc - modifiers];
			else
			{
				errormsg(SH_DICT, ERROR_ERROR, "%c: unrecognized history modifier", c);
				DONE();
			}

			if(c == 'h' || c == 'r') /* head or base */
			{
				n = -1;
				while((c = sfgetc(tmp)) > 0)
				{	/* remember position of / or . */
					if((c == '/' && *cp == 'h') || (c == '.' && *cp == 'r'))
						n = sftell(tmp2);
					sfputc(tmp2, c);
				}
				if(n > 0)
				{	 /* rewind to last / or . */
					sfseek(tmp2, n, SEEK_SET);
					/* end string there */
					sfputc(tmp2, '\0');
				}
			}
			else if(c == 't' || c == 'e') /* tail or suffix */
			{
				n = 0;
				while((c = sfgetc(tmp)) > 0)
				{	/* remember position of / or . */
					if((c == '/' && *cp == 't') || (c == '.' && *cp == 'e'))
						n = sftell(tmp);
				}
				/* rewind to last / or . */
				sfseek(tmp, n, SEEK_SET);
				/* copy from there on */
				while((c = sfgetc(tmp)) > 0)
					sfputc(tmp2, c);
			}
			else if(c == 's' || c == '&')
			{
				cp++;

				if(c == 's')
				{
					/* preset old with match from !?string? */
					if(!sb.str[0] && wm)
						sb.str[0] = strdup(sfsetbuf(wm, (Void_t*)1, 0));
					cp = parse_subst(cp, &sb);
				}

				if(!sb.str[0] || !sb.str[1])
				{
					c = *cp;
					*cp = '\0';
					errormsg(SH_DICT, ERROR_ERROR, 
						 "%s%s: no previous substitution", 
						(flag & HIST_QUICKSUBST) ? ":s" : "",
						evp);
					*cp = c;
					DONE();
				}

				/* need pointer for strstr() */
				str = sfsetbuf(tmp, (Void_t*)1, 0);

				flag |= HIST_SUBSTITUTE;
				while(flag & HIST_SUBSTITUTE)
				{
					/* find string */
					if(cc = strstr(str, sb.str[0]))
					{	/* replace it */
						c = *cc;
						*cc = '\0';
						sfputr(tmp2, str, -1);
						sfputr(tmp2, sb.str[1], -1);
						*cc = c;
						str = cc + strlen(sb.str[0]);
					}
					else if(!sftell(tmp2))
					{	/* not successfull */
						c = *cp;
						*cp = '\0';
						errormsg(SH_DICT, ERROR_ERROR,
							 "%s%s: substitution failed",
							(flag & HIST_QUICKSUBST) ? ":s" : "",
							evp);
						*cp = c;
						DONE();
					}
					/* loop if g modifier specified */
					if(!cc || !(flag & HIST_GLOBALSUBST))
						flag &= ~HIST_SUBSTITUTE;
				}
				/* output rest of line */
				sfputr(tmp2, str, -1);
				if(*cp)
					cp--;
			}

			if(sftell(tmp2))
			{ /* if any substitions done, swap buffers */
				if(wm != tmp)
					sfclose(tmp);
				tmp = tmp2;
				tmp2 = 0;
			}
			cc = 0;
			if(*cp)
				cp++;
		}

		/* flush temporary buffer to stack */
		if(tmp)
		{
			sfseek(tmp, 0, SEEK_SET);

			if(flag & HIST_QUOTE)
				stakputc('\'');

			while((c = sfgetc(tmp)) > 0)
			{
				if(isspace(c))
				{
					flag = flag & ~HIST_NEWLINE;

					/* squash white space to either a 
					   blank or a newline */
					do
						flag |= (c == '\n' ? HIST_NEWLINE : 0);
					while((c = sfgetc(tmp)) > 0 && isspace(c));

					sfungetc(tmp, c);

					c = (flag & HIST_NEWLINE) ? '\n' : ' ';

					if(flag & HIST_QUOTE_BR)
					{
						stakputc('\'');
						stakputc(c);
						stakputc('\'');
					}
					else
						stakputc(c);
				}
				else if((c == '\'') && (flag & HIST_QUOTE))
				{
					stakputc('\'');
					stakputc('\\');
					stakputc(c);
					stakputc('\'');
				}
				else
					stakputc(c);
			}
			if(flag & HIST_QUOTE)
				stakputc('\'');
		}
	}

	stakputc('\0');

done:
	if(cc && (flag&HIST_HASH))
	{
		/* close !# temp file */
		sfclose(ref);
		free(cc);
		cc = 0;
	}

	/* error? */
	if(staktell() && !(flag & HIST_ERROR))
		*xp = strdup(stakfreeze(1));

	/* restore shell stack */
	if(off)
		stakset(sp,off);
	else
		stakseek(0);

	/* drop temporary files */

	if(tmp && tmp != wm)
		sfclose(tmp);
	if(tmp2)
		sfclose(tmp2);

	return (flag & HIST_ERROR ? HIST_ERROR : flag & HIST_FLAG_RETURN_MASK);
}