1 /* @(#)abbrev.c	1.74 18/06/26 Copyright 1985-2018 J. Schilling */
2 #include <schily/mconfig.h>
3 #ifndef lint
4 static	UConst char sccsid[] =
5 	"@(#)abbrev.c	1.74 18/06/26 Copyright 1985-2018 J. Schilling";
6 #endif
7 /*
8  *	Abbreviation symbol handling
9  *
10  *	Copyright (c) 1985-2018 J. Schilling
11  *
12  *	.global & .local alias abbreviations are handled here
13  *
14  *	Exported functions:
15  *		ab_getaltowner(tab)		Get alternate file owner that
16  *						is trusted
17  *		ab_getaltoname(tab)		Get name of alternate file
18  *						owner that is trusted
19  *		ab_setaltowner(tab, usrname)	Set alternate file owner that
20  *						is trusted
21  *		ab_read(tab, fname)		Read new abbrev table from fname
22  *		ab_use(tab, fname)		Use new abbrev table from fname
23  * 		ab_close(tab)			Shut down abbrev table
24  *		ab_delete(tab, name, flags)	Pop or delete abbrev from table
25  *						flags & AB_POP allows to
26  *						implement the POSIX "unalias -a"
27  *						without modifying
28  *						.globals/.locals
29  *		ab_deleteall(tab, flags)	Pop or delete all entries
30  *						flags & AB_POP allows to
31  *						implement the POSIX "unalias -a"
32  *						without modifying
33  *						.globals/.locals
34  *		ab_dump(tab, f, flags)		Print all abbrev entries to file
35  *						flags & AB_HISTORY sends a copy
36  *						to the command history
37  *		ab_insert(tab, name, val, flag)	Insert n/v pair to abbrev table
38  *						flags & AB_BEGIN defines a begin
39  *						alias
40  *		ab_list(tab, pat, f, flags)	Print matched entries to file
41  *						flags & AB_HISTORY sends a copy
42  *						to the command history
43  *		ab_push(tab, name, val, flag)	Push n/v pair to abbrev table
44  *						flags & AB_BEGIN defines a begin
45  *						alias
46  *		ab_sname(tab, fname)		Set filename for _ab_output
47  *		ab_gname(tab)			Get filename from tab
48  *		ab_value(tab, name, seen, flag)	Perform n/v translation for tab
49  *						flags & AB_BEGIN looks for begin
50  *						alias only
51  *
52  *	Imported functions:
53  *		any_match			from expand.c
54  *		append_line			from inputc.c
55  *		berror				from bsh.c
56  *
57  *	Imported vars:
58  *		ctlc				from bsh.c
59  *		ebadpattern			from str.c
60  *		for_ru				.
61  *		for_wct				.
62  *		sn_badfile			.
63  *		sn_badtab			.
64  *		sn_no_mem			.
65  */
66 /*
67  * The contents of this file are subject to the terms of the
68  * Common Development and Distribution License, Version 1.0 only
69  * (the "License").  You may not use this file except in compliance
70  * with the License.
71  *
72  * See the file CDDL.Schily.txt in this distribution for details.
73  * A copy of the CDDL is also available via the Internet at
74  * http://www.opensource.org/licenses/cddl1.txt
75  *
76  * When distributing Covered Code, include this CDDL HEADER in each
77  * file and include the License file CDDL.Schily.txt from this distribution.
78  */
79 
80 #ifdef	BOURNE_SHELL
81 
82 #include "defs.h"
83 #undef	tab
84 #define	LOCAL	static
85 #define	EXPORT
86 #include "abbrev.h"
87 #include <schily/fcntl.h>
88 #include <schily/stat.h>
89 #include <schily/pwd.h>
90 #include <schily/shedit.h>
91 
92 LOCAL	char	sn_badtab[]	= "bad_astab_number";
93 LOCAL	char	sn_no_mem[]	= "no_memory";
94 LOCAL	char	sn_badfile[]	= "bad_sym_file";
95 
96 /*
97  * The Bourne shell does not use stdio, so we need to redefine some functions.
98  * Be sure to first #undef things that might be #define'd in schily/schily.h.
99  */
100 #undef	filemopen
101 #define	filemopen(n, m, c)	open(n, m, c)
102 #undef	fileopen
103 #define	fileopen(n, m)	open(n, m, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)
104 #define	fileread	read
105 #undef	filestat
106 #define	filestat	fstat
107 #define	fclose		close
108 #define	fflush(f)
109 #undef	filesize
110 #define	filesize	ab_fsize
111 #define	open_failed(f)	(f < 0)
112 
113 #define	for_wct		(O_WRONLY|O_CREAT|O_TRUNC)
114 #define	for_ru		(O_RDONLY)
115 
116 #define	raisecond(n, v)	error(n)
117 #define	malloc		alloc
118 
119 #define	ctlc		bosh.intrcnt
120 
121 #ifdef	HAVE_SNPRINTF
122 	/*
123 	 * Try to avoid js_snprintf() in the Bourne Shell
124 	 * to allow to lazyload libschily
125 	 */
126 #undef	snprintf
127 #define	js_snprintf	snprintf
128 #endif
129 
130 #else	/* !BOURNE_SHELL */
131 #include <schily/stdio.h>
132 #include <schily/string.h>
133 #include <schily/stdlib.h>
134 #include <schily/time.h>
135 #include <schily/stat.h>
136 #include <schily/pwd.h>
137 #include "bsh.h"
138 #include "abbrev.h"
139 #include "str.h"
140 #include "strsubs.h"
141 #include <schily/patmatch.h>
142 
143 #define	open_failed(f)	(f == (FILE *)0)
144 #endif	/* !BOURNE_SHELL */
145 
146 /*
147  * Replacement node entry, one is allocated for each abbrev/alias replacement
148  *
149  *	ab_name is only set in first node of the ab_push list
150  *	ab_name is NULL in further nodes of the ab_push list
151  *	ab_value is NULL if the symbol has been deleted
152  *	ab_flags & ABF_POP is != 0 if the symbol has been popped but not deleted
153  *			ab_value is kept in that case to be able to keep the
154  *			content of .globals/.locals
155  */
156 typedef	struct abent	abent_t;
157 
158 struct abent {
159 	abent_t	*ab_next;		/* next entry in list		 */
160 	abent_t *ab_push;		/* next in list of old values	 */
161 	abent_t *ab_seen;		/* next in list of seen values	 */
162 	char	*ab_name;		/* abbreviation/alias name	 */
163 	char	*ab_value;		/* replacement value		 */
164 	int	ab_flags;		/* various flags		 */
165 };
166 
167 /*
168  * Definitions for ab_flags:
169  */
170 #define	ABF_BEGIN	1	/* replace only if begin of cmd		   */
171 #define	ABF_POP		2	/* pop only, but keep in .globals/.locals  */
172 
173 #define	AB_NULL	(abent_t *)0
174 
175 /*
176  * Table entry, one is allocated for each abbreviation type.
177  *
178  *	at_ent contains a list of nodes for the related abbreviation table
179  */
180 typedef struct abtab {
181 	abent_t	*at_ent;		/* first entry in list		 */
182 	char	*at_fname;		/* name of file for _ab_output() */
183 	char	*at_blk;		/* start of monolitic malloc()	 */
184 	char	*at_blkend;		/* end of monolitic malloc()	 */
185 	time_t	at_mtime;		/* our time stamp for at_fname	 */
186 	uid_t	at_altowner;		/* alternate permitted file owner */
187 } abtab_t;
188 
189 LOCAL	abtab_t	ab_tabs[ABTABS]	= {	{0, 0, 0, 0, 0, (uid_t)-1},
190 					{0, 0, 0, 0, 0, (uid_t)-1}};
191 
192 EXPORT	abidx_t	deftab		= GLOBAL_AB;	/* Use .globals by default */
193 
194 LOCAL	abtab_t	*_ab_down	__PR((abidx_t tab));
195 LOCAL	abent_t	*_ab_lookup	__PR((abtab_t *ap, char *name, BOOL new));
196 LOCAL	abent_t	*_ab_newnode	__PR((void));
197 LOCAL	void	_ab_output	__PR((abtab_t *ap));
198 LOCAL	BOOL	_ab_print	__PR((abidx_t tab, char *name, FILE_p f,
199 								int aflags));
200 LOCAL	void	_ab_dump	__PR((abent_t *np, FILE_p f, int aflags));
201 LOCAL	void	_ab_list	__PR((abent_t *np, FILE_p f, int aflags));
202 LOCAL	BOOL	_ab_match	__PR((abent_t *np, FILE_p f, int aflags,
203 				char *pattern, int *aux, int alt, int *state));
204 LOCAL	void	_ab_close	__PR((abent_t *np, abidx_t tab));
205 LOCAL	char	*ab_beginword	__PR((char *p, abtab_t *ap));
206 LOCAL	char	*ab_endword	__PR((char *p, abtab_t *ap));
207 LOCAL	char	*ab_endline	__PR((char *p, abtab_t *ap));
208 LOCAL	BOOL	ab_inblock	__PR((abidx_t tab, char *p));
209 LOCAL	time_t	ab_statmtime	__PR((struct stat *sp));
210 LOCAL	time_t	ab_filemtime	__PR((char *fname));
211 EXPORT	void	ab_read		__PR((abidx_t tab, char *fname));
212 EXPORT	void	ab_sname	__PR((abidx_t tab, char *fname));
213 EXPORT	char	*ab_gname	__PR((abidx_t tab));
214 EXPORT	void	ab_use		__PR((abidx_t tab, char *fname));
215 EXPORT	void	ab_close	__PR((abidx_t tab));
216 EXPORT	BOOL	ab_insert	__PR((abidx_t tab, char *name, char *val,
217 								int aflags));
218 EXPORT	BOOL	ab_push		__PR((abidx_t tab, char *name, char *val,
219 								int aflags));
220 LOCAL	void	_ab_delete	__PR((abent_t *np, abidx_t tab, int aflags));
221 EXPORT	BOOL	ab_delete	__PR((abidx_t tab, char *name, int aflags));
222 LOCAL	void	_ab_deleteall	__PR((abent_t *np, abidx_t tab, int aflags));
223 EXPORT	void	ab_deleteall	__PR((abidx_t tab, int aflags));
224 EXPORT	char	*ab_value	__PR((abidx_t tab, char *name, void **seen,
225 								int aflags));
226 EXPORT	void	ab_dump		__PR((abidx_t tab, FILE_p f, int aflags));
227 EXPORT	BOOL	ab_list		__PR((abidx_t tab, char *pattern, FILE_p f,
228 								int aflags));
229 LOCAL	void	ab_eupdated	__PR((abtab_t *ap));
230 LOCAL	void	_ab_pr		__PR((abent_t *np, FILE_p f, int aflags));
231 LOCAL	void	_ab_prposix	__PR((abent_t *np, FILE_p f, int aflags));
232 #ifdef	INTERACTIVE
233 LOCAL	void	_ab_phist	__PR((abent_t *np, FILE_p f, int aflags));
234 #endif
235 #ifdef	BOURNE_SHELL
236 LOCAL	off_t	filesize	__PR((int f));
237 LOCAL	BOOL	any_match	__PR((char *s));
238 #endif
239 
240 /*
241  * Do range check for 'tab' and return abtab_t structure for 'tab'.
242  */
243 LOCAL abtab_t *
_ab_down(tab)244 _ab_down(tab)
245 	abidx_t	tab;
246 {
247 	if (tab < 0 || tab >= ABTABS)
248 		raisecond(sn_badtab, 0L);
249 	return (&ab_tabs[tab]);
250 }
251 
252 #ifdef	PROTOTYPES
253 LOCAL abent_t *
_ab_lookup(abtab_t * ap,char * name,BOOL new)254 _ab_lookup(abtab_t *ap, char *name, BOOL new)
255 #else
256 LOCAL abent_t *
257 _ab_lookup(ap, name, new)
258 	abtab_t	*ap;
259 	char	*name;
260 	BOOL	new;
261 #endif
262 {
263 	register abent_t	*np;	/* Current node pointer	*/
264 	register abent_t	*lp;	/* Last node pinter	*/
265 #ifdef	notdef
266 	register int		cmp;
267 #endif
268 
269 	for (lp = AB_NULL, np = ap->at_ent; np != AB_NULL; ) {
270 		register char	*s1;
271 		register char	*s2;
272 
273 #ifndef notdef
274 		s1 = name;
275 		s2 = np->ab_name;
276 		for (; *s1 == *s2; s1++, s2++) /* viel schneller als strcmp */
277 			if (*s1 == '\0')
278 				return (np);
279 		if (*s1 > *s2)
280 			break;
281 #else
282 		if ((cmp = strcmp(name, np->ab_name)) == 0)
283 			return (np);
284 		if (cmp > 0)
285 			break;
286 #endif
287 		lp = np;
288 		np = np->ab_next;
289 	}
290 	if (!new)
291 		return (AB_NULL);
292 
293 	np = _ab_newnode();
294 	np->ab_name = name;
295 	if (lp == AB_NULL) {			/* make first in chain */
296 		np->ab_next = ap->at_ent;
297 		ap->at_ent = np;
298 	} else {				/* in middle of chain */
299 		np->ab_next = lp->ab_next;
300 		lp->ab_next = np;
301 	}
302 	return (np);
303 }
304 
305 
306 LOCAL abent_t *
_ab_newnode()307 _ab_newnode()
308 {
309 	register abent_t	*new;
310 
311 	if ((new = (abent_t *)malloc(sizeof (abent_t))) == AB_NULL)
312 		raisecond(sn_no_mem, (long)"_ab_newnode");
313 	new->ab_next = AB_NULL;
314 	new->ab_push = AB_NULL;
315 	new->ab_seen = AB_NULL;
316 	new->ab_name = NULL;
317 	new->ab_value = NULL;
318 	new->ab_flags = 0;
319 
320 	return (new);
321 }
322 
323 /*
324  * Output the current list of entries to ap->at_fname
325  * This funktioon is used to update $HOME/.globals and .locals
326  */
327 LOCAL void
_ab_output(ap)328 _ab_output(ap)
329 	register abtab_t *ap;
330 {
331 	register FILE_p f;
332 		time_t	mtime;
333 		struct stat sb;
334 
335 #ifdef DEBUG
336 	berror("updating: %s", ap->at_fname);
337 #endif
338 	if (ap->at_fname == NULL)
339 		return;
340 	mtime = ab_filemtime(ap->at_fname);
341 	/*
342 	 * If ap->at_mtime == 0, the file did not exist for us.
343 	 */
344 	if (mtime > ap->at_mtime) {
345 		ab_eupdated(ap);
346 		return;
347 	}
348 	if (lstat(ap->at_fname, &sb) < 0) /* Check whether a symlink */
349 		/* EMPTY */;		/* No file yet		   */
350 	else if (S_ISLNK(sb.st_mode))
351 		return;
352 
353 	f = filemopen(ap->at_fname, for_wct, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
354 	if (open_failed(f)) {
355 		raisecond(sn_badfile, (long)ap->at_fname);
356 	} else {
357 #ifdef	BOURNE_SHELL
358 		int	oldf = setb(f);	/* Set new fd for prs() */
359 #endif
360 		/*
361 		 *			AB_PERSIST -> no pushed entries
362 		 *			non interruptable
363 		 *			no copy into history
364 		 */
365 		_ab_dump(ap->at_ent, f, AB_PERSIST);
366 #ifdef	BOURNE_SHELL
367 		setb(oldf);		/* Restore fd for prs() */
368 #endif
369 		fclose(f);
370 		ap->at_mtime = ab_filemtime(ap->at_fname);
371 	}
372 }
373 
374 /*
375  * Print a single name/value replacement entry to an open file.
376  */
377 LOCAL BOOL
_ab_print(tab,name,f,aflags)378 _ab_print(tab, name, f, aflags)
379 	abidx_t	tab;
380 	char	*name;
381 	FILE_p	f;
382 	int	aflags;		/* should push into History? */
383 {
384 	register abent_t *np;
385 
386 	np = _ab_lookup(_ab_down(tab), name, FALSE);
387 	if (np == NULL)
388 		return (FALSE);
389 
390 	_ab_list(np, f, aflags);
391 	fflush(f);
392 	return (TRUE);
393 }
394 
395 /*
396  * Dump the whole list.
397  * Flags controls whether pushed entries appear in the output, whether
398  * interruptable and whether a copy is pushed into the command history.
399  */
400 LOCAL void
_ab_dump(np,f,aflags)401 _ab_dump(np, f, aflags)
402 	register abent_t *np;
403 		FILE_p	f;
404 		int	aflags;
405 {
406 	register abent_t *tp;
407 
408 	if (np == AB_NULL)
409 		return;
410 	_ab_dump(np->ab_next, f, aflags);
411 	tp = np;
412 	/*
413 	 * With AB_PERSIST skip pushed values and only dump the
414 	 * persistent base entry.
415 	 */
416 	if (aflags & AB_PERSIST)
417 		while (tp->ab_push != AB_NULL)
418 			tp = tp->ab_push;
419 	if (!(ctlc && (aflags & AB_INTR)))
420 		_ab_list(tp, f, aflags);
421 }
422 
423 /*
424  * The argument "f" is not used in the Bourne Shell
425  */
426 /* ARGSUSED */
427 LOCAL void
_ab_list(np,f,aflags)428 _ab_list(np, f, aflags)
429 	register abent_t *np;
430 		FILE_p	f;
431 		int	aflags;	/* should push into History? */
432 {
433 	/*
434 	 * With AB_PERSIST skip pushed values and only list the
435 	 * persistent base entry.
436 	 */
437 	if (np != AB_NULL && (aflags & AB_PERSIST)) {
438 		while (np->ab_push != AB_NULL)
439 			np = np->ab_push;
440 	}
441 	if (np != AB_NULL && np->ab_value != NULL &&
442 	    ((np->ab_flags & ABF_POP) == 0 || aflags & AB_PERSIST)) {
443 		if ((aflags & AB_POSIX)) {
444 			_ab_prposix(np, f, aflags);
445 		} else {
446 			_ab_pr(np, f, aflags);
447 		}
448 #ifdef	INTERACTIVE
449 		if (aflags & AB_HISTORY) {
450 			_ab_phist(np, f, aflags);
451 		}
452 #endif
453 	}
454 }
455 
456 
457 LOCAL BOOL
_ab_match(np,f,aflags,pattern,aux,alt,state)458 _ab_match(np, f, aflags, pattern, aux, alt, state)
459 	register abent_t *np;
460 		FILE_p	f;
461 		int	aflags;		/* should push into History? */
462 		char	*pattern;
463 		int	*aux;
464 		int	alt;
465 		int	*state;
466 {
467 	register abent_t *tp;
468 #ifndef	BOURNE_SHELL
469 	register char	*p;
470 #endif
471 	BOOL		ok;
472 
473 	if (np == AB_NULL)
474 		return (FALSE);
475 	ok = _ab_match(np->ab_next, f, aflags, pattern, aux, alt, state);
476 	if (ctlc)
477 		return (FALSE);
478 	tp = np;
479 	while (tp->ab_push != AB_NULL)
480 		tp = tp->ab_push;
481 #ifdef	BOURNE_SHELL
482 	if (gmatch(tp->ab_name, pattern)) {
483 		_ab_list(tp, f, aflags);
484 		return (TRUE);
485 	}
486 #else
487 	p = (char *)patmatch((unsigned char *)pattern, aux,
488 		(unsigned char *)tp->ab_name, 0, strlen(tp->ab_name),
489 		alt, state);
490 	if (p && *p == '\0') {
491 		_ab_list(tp, f, aflags);
492 		return (TRUE);
493 	}
494 #endif
495 	return (ok);
496 }
497 
498 /*
499  * Free all symbols associated with this table.
500  */
501 LOCAL void
_ab_close(np,tab)502 _ab_close(np, tab)
503 	register abent_t *np;
504 	abidx_t	tab;
505 {
506 	register abent_t *tp;
507 	register abent_t *sp;
508 
509 	while (np != AB_NULL) {
510 		if (!ab_inblock(tab, np->ab_name))
511 			free(np->ab_name);
512 		tp = np;
513 		np = np->ab_next;		/* remember next ptr now */
514 		/*
515 		 * Now free all pushed values including this one.
516 		 */
517 		do {
518 			if (tp->ab_value != NULL &&
519 			    !ab_inblock(tab, tp->ab_value))
520 				free(tp->ab_value);
521 			sp = tp->ab_push;	/* Save push ptr */
522 			free((char *)tp);
523 		} while ((tp = sp) != AB_NULL);	/* Use remembered push ptr */
524 	}
525 }
526 
527 
528 /*
529  * Find the next begin of word inside the allocated monolitic block.
530  */
531 LOCAL char *
ab_beginword(p,ap)532 ab_beginword(p, ap)
533 	register char	*p;
534 		abtab_t	*ap;
535 {
536 	register char	*rend = ap->at_blkend;
537 
538 	while (p < rend && (*p == ' ' || *p == '\t' || *p == '\n'))
539 		p++;
540 	return (p);
541 }
542 
543 
544 /*
545  * Find the next end of word inside the allocated monolitic block.
546  */
547 LOCAL char *
ab_endword(p,ap)548 ab_endword(p, ap)
549 	register char	*p;
550 		abtab_t	*ap;
551 {
552 	register char	*rend = ap->at_blkend;
553 
554 	while (p < rend && (*p != ' ' && *p != '\t' && *p != '\n'))
555 		p++;
556 	return (p);
557 }
558 
559 /*
560  * Find the next end of line inside the allocated monolitic block.
561  */
562 LOCAL char *
ab_endline(p,ap)563 ab_endline(p, ap)
564 	register char	*p;
565 		abtab_t	*ap;
566 {
567 	register char	*rend = ap->at_blkend;
568 
569 	while (p < rend && *p != '\n')
570 		p++;
571 	return (p);
572 }
573 
574 /*
575  * Check whether a character pointer points into the monolitic block
576  * that is allocated when the file is read by ab_read().
577  */
578 LOCAL BOOL
ab_inblock(tab,p)579 ab_inblock(tab, p)
580 	abidx_t	tab;
581 	char	*p;
582 {
583 	register abtab_t *ap = _ab_down(tab);
584 
585 	return ((p >= ap->at_blk) && (p <= ap->at_blkend));
586 }
587 
588 LOCAL time_t
ab_statmtime(sp)589 ab_statmtime(sp)
590 	struct stat	*sp;
591 {
592 	if (sp->st_mtime == (time_t)0)
593 		sp->st_mtime++;
594 	return (sp->st_mtime);
595 }
596 
597 LOCAL time_t
ab_filemtime(fname)598 ab_filemtime(fname)
599 	char	*fname;
600 {
601 	struct stat	sb;
602 
603 	if (stat(fname, &sb) < 0)
604 		return ((time_t)0);
605 
606 	return (ab_statmtime(&sb));
607 }
608 
609 /*
610  * Get alternate file owner that is trusted in addition to the current
611  * shell user.
612  */
613 EXPORT uid_t
ab_getaltowner(tab)614 ab_getaltowner(tab)
615 	abidx_t	tab;
616 {
617 	register abtab_t *ap = _ab_down(tab);
618 
619 	return (ap->at_altowner);
620 }
621 
622 EXPORT char *
ab_getaltoname(tab)623 ab_getaltoname(tab)
624 	abidx_t	tab;
625 {
626 	register abtab_t *ap = _ab_down(tab);
627 	register struct passwd *pw;
628 static	char oname[12];
629 
630 	pw = getpwuid(ap->at_altowner);
631 	endpwent();
632 	if (pw)
633 		return (pw->pw_name);
634 	oname[0] = '\0';
635 
636 #if	defined(BOURNE_SHELL) && defined(HAVE_SNPRINTF)
637 	/*
638 	 * Try to avoid js_snprintf() in the Bourne Shell
639 	 * to allow to lazyload libschily
640 	 */
641 	snprintf(oname, sizeof (oname),
642 		"%d", ap->at_altowner);
643 #else
644 	js_snprintf(oname, sizeof (oname),
645 		"%d", ap->at_altowner);
646 #endif
647 	return (oname);
648 }
649 
650 /*
651  * Set alternate file owner that is trusted in addition to the current
652  * shell user.
653  */
654 EXPORT void
ab_setaltowner(tab,usrname)655 ab_setaltowner(tab, usrname)
656 	abidx_t	tab;
657 	char	*usrname;
658 {
659 	register abtab_t *ap = _ab_down(tab);
660 	register struct passwd *pw;
661 	register char	*p = usrname;
662 
663 	if (*usrname == '\0') {
664 		ap->at_altowner = (uid_t)-1;
665 		return;
666 	}
667 	pw = getpwnam(usrname);
668 	endpwent();
669 
670 	if (pw) {
671 		ap->at_altowner = pw->pw_uid;
672 		return;
673 	}
674 	while (*p) {
675 		if (*p < '0')
676 			return;
677 		if (*p++ > '9')
678 			return;
679 	}
680 	ap->at_altowner = atoi(usrname);
681 }
682 
683 /*
684  * Read file 'fname' and build a new abbreviation/alias replacement table
685  */
686 EXPORT void
ab_read(tab,fname)687 ab_read(tab, fname)
688 	abidx_t	tab;
689 	char	*fname;
690 {
691 	register char	*line;
692 	register char	*name;
693 	register char	*val;
694 		FILE_p	f;
695 	register abtab_t *ap = _ab_down(tab);
696 		off_t	fsize;
697 	register int	beg;
698 		struct stat sb;
699 
700 	/*
701 	 * Make sure that ap->at_fname is NULL to avoid writing back to the
702 	 * file when calling ab_insert().
703 	 */
704 	ab_close(tab);
705 
706 	if (fname == NULL)
707 		return;
708 
709 	if (lstat(fname, &sb) < 0)	/* Check whether a symlink */
710 		return;			/* No file to open, return */
711 	if (S_ISLNK(sb.st_mode))
712 		return;
713 
714 	f = fileopen(fname, for_ru);
715 	if (open_failed(f))
716 		return;
717 
718 	if (filestat(f, &sb) >= 0) {
719 		if (geteuid() == sb.st_uid) {
720 			if (sb.st_mode & (S_IWGRP|S_IWOTH)) {
721 				fclose(f);
722 				return;
723 			}
724 		} else if ((ap->at_altowner == (uid_t)-1 ||
725 		    ap->at_altowner != sb.st_uid)) {
726 			fclose(f);
727 			return;
728 		}
729 		ap->at_mtime = ab_statmtime(&sb);
730 	}
731 	fsize = filesize(f);
732 	if (ap->at_mtime == 0)
733 		ap->at_mtime = ab_filemtime(fname);
734 #ifdef DEBUG
735 	berror("ab_read(%d, %s)-> %d %.24s", tab, fname,
736 			ap->at_mtime, ctime(&ap->at_mtime));
737 #endif
738 	if ((ap->at_blk = malloc((size_t)fsize)) == NULL) {
739 		raisecond(sn_no_mem, (long)"ab_read");
740 		fclose(f);
741 		return;
742 	}
743 	ap->at_blkend = ap->at_blk + fsize - 1;
744 	if (fileread(f, ap->at_blk, (int)fsize) != fsize) {
745 		free(ap->at_blk);
746 		ap->at_blk = NULL;
747 		fclose(f);
748 		return;
749 	}
750 	fclose(f);
751 
752 	line = ap->at_blk;
753 	while (line < ap->at_blkend) {
754 		line = ab_beginword(line, ap);
755 		if (*line++ != '#') {
756 			line = ab_endline(line, ap);
757 			continue;
758 		}
759 		if (*line == 'a') {
760 			beg = 0;
761 		} else if (*line == 'b') {
762 			beg = AB_BEGIN;
763 		} else {
764 			line = ab_endline(line, ap);
765 			continue;
766 		}
767 		name = line = ab_beginword(ab_endword(line, ap), ap);
768 		line = ab_endword(line, ap);
769 		*line++ = '\0';
770 		val = line = ab_beginword(line, ap);
771 		line = ab_endline(line, ap);
772 		*line++ = '\0';
773 		ab_insert(tab, name, val, beg);
774 	}
775 }
776 
777 
778 /*
779  * Set filename to use for _ab_output()
780  */
781 EXPORT void
ab_sname(tab,fname)782 ab_sname(tab, fname)
783 	abidx_t	tab;
784 	char	*fname;
785 {
786 	abtab_t *ap = _ab_down(tab);
787 #ifdef	BOURNE_SHELL
788 	free(ap->at_fname);	/* Bourne Shell only frees what need free() */
789 #endif
790 	ap->at_fname = fname;
791 }
792 
793 /*
794  * Get filename from tab
795  */
796 EXPORT char *
ab_gname(tab)797 ab_gname(tab)
798 	abidx_t	tab;
799 {
800 	abtab_t *ap = _ab_down(tab);
801 
802 	return (ap->at_fname);
803 }
804 
805 /*
806  * Use new abbrev file
807  */
808 EXPORT void
ab_use(tab,fname)809 ab_use(tab, fname)
810 	abidx_t	tab;
811 	char	*fname;
812 {
813 	ab_read(tab, fname);
814 	ab_sname(tab, fname);
815 }
816 
817 /*
818  * Shut down a table, remove all name/value translations from this tab before.
819  */
820 EXPORT void
ab_close(tab)821 ab_close(tab)
822 	abidx_t	tab;
823 {
824 	register abtab_t *ap = _ab_down(tab);
825 
826 	_ab_close(ap->at_ent, tab);
827 	ap->at_fname = NULL;
828 	ap->at_mtime = (time_t)0;
829 	ap->at_ent = AB_NULL;
830 	if (ap->at_blk != NULL) {
831 		free(ap->at_blk);
832 		ap->at_blk = NULL;
833 	}
834 }
835 
836 
837 /*
838  * Insert a new name/value pair into an abbreviation/alias table.
839  */
840 EXPORT BOOL
ab_insert(tab,name,val,aflags)841 ab_insert(tab, name, val, aflags)
842 	abidx_t	tab;
843 	char	*name;
844 	char	*val;
845 	int	aflags;
846 {
847 		abtab_t	*ap = _ab_down(tab);
848 	register abent_t *np;
849 
850 	np = _ab_lookup(ap, name, TRUE);
851 	if (np == NULL)
852 		return (FALSE);
853 	if (np->ab_value != NULL && !ab_inblock(tab, np->ab_value))
854 		free(np->ab_value);
855 	np->ab_value = val;
856 	if (aflags & AB_BEGIN)
857 		np->ab_flags |= ABF_BEGIN;
858 	else
859 		np->ab_flags &= ~ABF_BEGIN;
860 	/*
861 	 * If this entry has not been pushed on top of old replacements,
862 	 * update the underlying file storage.
863 	 */
864 	if (np->ab_push == AB_NULL)
865 		_ab_output(ap);
866 	return (TRUE);
867 }
868 
869 
870 /*
871  * Push a new name/value pair on top of an abbreviation/alias table entry.
872  */
873 EXPORT BOOL
ab_push(tab,name,val,aflags)874 ab_push(tab, name, val, aflags)
875 	abidx_t	tab;
876 	char	*name;
877 	char	*val;
878 	int	aflags;
879 {
880 		abtab_t		*ap = _ab_down(tab);
881 	register abent_t	*np;
882 	register abent_t	*new;
883 
884 	np = _ab_lookup(ap, name, TRUE);
885 	if (np == NULL)
886 		return (FALSE);
887 	new = _ab_newnode();		/* Get space for node to push	*/
888 	if (new == NULL)
889 		return (FALSE);
890 	new->ab_name = np->ab_name;	/* Dup to allow to list all	*/
891 	new->ab_value = np->ab_value;	/* First save old node data	*/
892 	new->ab_flags = np->ab_flags;
893 	new->ab_push = np->ab_push;
894 	np->ab_push = new;		/* Then make pushed node active	*/
895 	np->ab_value = val;
896 	if (aflags & AB_BEGIN)
897 		np->ab_flags |= ABF_BEGIN;
898 	else
899 		np->ab_flags &= ~ABF_BEGIN;
900 	np->ab_flags &= ~ABF_POP;
901 	return (TRUE);
902 }
903 
904 
905 /*
906  * Pop a new name/value pair from the top of an abbreviation/alias table entry.
907  * If there is no pushed entry left over, then the whole entry is deleted
908  * unless this is a POP operation. A POP on the last entry for that name just
909  * sets the ABF_POP flag to be able to keep the content in .globals and .locals
910  * Deletion is done by setting ab_value to NULL.
911  */
912 LOCAL void
_ab_delete(np,tab,aflags)913 _ab_delete(np, tab, aflags)
914 	register abent_t	*np;
915 	abidx_t	tab;
916 	int	aflags;
917 {
918 	register abent_t	*op;
919 
920 	if (np != AB_NULL && np->ab_value != NULL) {
921 		if (np->ab_push != AB_NULL) {	/* If saved old value */
922 			do {
923 				op = np->ab_push;
924 				if (!ab_inblock(tab, np->ab_value))
925 					free(np->ab_value);
926 				np->ab_value = op->ab_value; /* Pop top entry */
927 				np->ab_flags = op->ab_flags;
928 				np->ab_push = op->ab_push;
929 				free((char *)op);
930 			} while (np->ab_push && (aflags & AB_POPALL));
931 		}
932 
933 		if (np->ab_push == AB_NULL) {	/* The last definition */
934 			if (aflags & AB_POP) {
935 				np->ab_flags |= ABF_POP;
936 			} else {
937 				if (!ab_inblock(tab, np->ab_value))
938 					free(np->ab_value);
939 				np->ab_value = NULL;
940 				if ((np->ab_flags & ABF_POP) == 0)
941 					_ab_output(_ab_down(tab));
942 			}
943 		}
944 	}
945 }
946 
947 EXPORT BOOL
ab_delete(tab,name,aflags)948 ab_delete(tab, name, aflags)
949 	abidx_t	tab;
950 	char	*name;
951 	int	aflags;
952 {
953 	abtab_t	*ap = _ab_down(tab);
954 	abent_t	*np;
955 
956 	np = _ab_lookup(ap, name, FALSE);
957 	if (np == NULL)
958 		return (FALSE);
959 	_ab_delete(np, tab, aflags);
960 	return (TRUE);
961 }
962 
963 LOCAL void
_ab_deleteall(np,tab,aflags)964 _ab_deleteall(np, tab, aflags)
965 	register abent_t *np;
966 		abidx_t	tab;
967 		int	aflags;
968 {
969 	if (np == AB_NULL)
970 		return;
971 	_ab_deleteall(np->ab_next, tab, aflags);
972 	if (!(ctlc && (aflags & AB_INTR)))
973 		_ab_delete(np, tab, aflags | AB_POPALL);
974 }
975 
976 EXPORT void
ab_deleteall(tab,aflags)977 ab_deleteall(tab, aflags)
978 	abidx_t	tab;
979 	int	aflags;
980 {
981 	_ab_deleteall(_ab_down(tab)->at_ent, tab, aflags);
982 }
983 
984 /*
985  * Perform a name/value translation for a named abbreviation/alias table.
986  * The seen pointer holds a list of already expanded aliases and is used
987  * to avoid endless loops in alias expansion in a POSIX compliant way.
988  */
989 EXPORT char *
ab_value(tab,name,seen,aflags)990 ab_value(tab, name, seen, aflags)
991 	abidx_t	tab;
992 	char	*name;
993 	void	**seen;
994 	int	aflags;			/* lookup begin abbreviations also? */
995 {
996 	register abent_t	*np;
997 
998 	np = _ab_lookup(_ab_down(tab), name, FALSE);
999 	if (np == AB_NULL)
1000 		return (NULL);
1001 	if (np->ab_flags & ABF_POP)
1002 		return (NULL);
1003 	if ((np->ab_flags & ABF_BEGIN) == 0 || (aflags & AB_BEGIN)) {
1004 		if (seen) {
1005 			register abent_t	*sp = *seen;
1006 
1007 			while (sp) {
1008 				if (sp == np)
1009 					return (NULL);
1010 				sp = sp->ab_seen;
1011 			}
1012 			np->ab_seen = *seen;
1013 			*seen = np;
1014 		}
1015 		return (np->ab_value);
1016 	}
1017 	return (NULL);
1018 }
1019 
1020 
1021 /*
1022  * Print all name/value replacement entries to an open file.
1023  */
1024 EXPORT void
ab_dump(tab,f,aflags)1025 ab_dump(tab, f, aflags)
1026 	abidx_t	tab;
1027 	FILE_p	f;
1028 	int	aflags;		/* should push into History? */
1029 {
1030 	/*
1031 	 *	Don't use AB_PERSIST -> output pushed entries
1032 	 *					 be interruptable
1033 	 */
1034 	_ab_dump(_ab_down(tab)->at_ent, f, aflags | AB_INTR);
1035 	fflush(f);
1036 }
1037 
1038 
1039 /*
1040  * Print all name/value replacements with matched entries to an open file.
1041  */
1042 EXPORT BOOL
ab_list(tab,pattern,f,aflags)1043 ab_list(tab, pattern, f, aflags)
1044 		abidx_t	tab;
1045 	register char	*pattern;
1046 		FILE_p	f;
1047 		int	aflags;		/* should push into History? */
1048 {
1049 	register int	*aux = NULL;	/* auxiliary array */
1050 	register int	*state = NULL;	/* state array */
1051 	register int	alt = 0;	/* outermost alternate */
1052 #ifndef	BOURNE_SHELL
1053 	register int	patlen;		/* pattern lenght */
1054 #endif
1055 
1056 	if ((aflags & AB_POSIX) == 0 && (tab != deftab)) {
1057 		if (tab == GLOBAL_AB)
1058 			aflags |= AB_PGLOBAL;
1059 		else
1060 			aflags |= AB_PLOCAL;
1061 	}
1062 
1063 	if (!any_match(pattern)) {
1064 		return (_ab_print(tab, pattern, f, aflags));
1065 	} else {
1066 		BOOL	ok;
1067 
1068 #ifndef	BOURNE_SHELL
1069 		patlen = strlen(pattern);
1070 		aux = (int *)malloc((size_t)patlen * sizeof (int));
1071 		state = (int *)malloc((size_t)(patlen+1) * sizeof (int));
1072 		alt = patcompile((unsigned char *)pattern, patlen, aux);
1073 		if (alt) {
1074 			ok = _ab_match(_ab_down(tab)->at_ent,
1075 					f, aflags, pattern, aux, alt, state);
1076 			fflush(f);
1077 		} else {
1078 			berror("%s", ebadpattern);
1079 			free((char *)aux);
1080 			free((char *)state);
1081 			return (FALSE);
1082 		}
1083 		free((char *)aux);
1084 		free((char *)state);
1085 #else
1086 		ok = _ab_match(_ab_down(tab)->at_ent,
1087 				f, aflags, pattern, aux, alt, state);
1088 		fflush(f);
1089 #endif
1090 		return (ok);
1091 	}
1092 }
1093 
1094 LOCAL void
ab_eupdated(ap)1095 ab_eupdated(ap)
1096 	abtab_t *ap;
1097 {
1098 #ifdef	BOURNE_SHELL
1099 	gfailure(UC ap->at_fname,
1100 		"updated by another shell, cannot write back.");
1101 #else
1102 	berror("'%s' was updated by another shell, cannot write back.",
1103 		ap->at_fname);
1104 #endif
1105 }
1106 
1107 /*
1108  * The argument "f" is not used in the Bourne Shell
1109  */
1110 /* ARGSUSED */
1111 LOCAL void
_ab_pr(np,f,aflags)1112 _ab_pr(np, f, aflags)
1113 	register abent_t	*np;
1114 		FILE_p		f;
1115 		int		aflags;
1116 {
1117 #ifdef	BOURNE_SHELL
1118 	int	len;
1119 #endif
1120 
1121 	if (np->ab_value == NULL)
1122 		return;
1123 	if (np->ab_push && (aflags & AB_ALL))
1124 		_ab_pr(np->ab_push, f, aflags);
1125 
1126 #ifdef	BOURNE_SHELL
1127 	prc_buff('#');
1128 	if (np->ab_push)
1129 		prc_buff('p');
1130 	prc_buff((np->ab_flags & ABF_BEGIN) ? 'b':'a');
1131 	if (aflags & AB_PLOCAL)
1132 		prc_buff('l');
1133 	else if (aflags & AB_PGLOBAL)
1134 		prc_buff('g');
1135 	prc_buff(' ');
1136 	prs_buff(UC np->ab_name);
1137 	len = length(UC np->ab_name);
1138 	do {
1139 		prc_buff(' ');
1140 	} while (len++ <= 8);
1141 	prs_buff(UC np->ab_value);
1142 	prc_buff(NL);
1143 #else
1144 	fprintf(f, "#%s%c%s %-8s %s\n",
1145 				np->ab_push ? "p":"",
1146 				np->ab_flags & ABF_BEGIN ? 'b':'a',
1147 				(aflags & AB_PLOCAL) ? "l":
1148 				(aflags & AB_PGLOBAL) ? "g":"",
1149 				np->ab_name, np->ab_value);
1150 #endif
1151 }
1152 
1153 /*
1154  * The argument "f" is not used in the Bourne Shell
1155  */
1156 /* ARGSUSED */
1157 LOCAL void
_ab_prposix(np,f,aflags)1158 _ab_prposix(np, f, aflags)
1159 	register abent_t	*np;
1160 		FILE_p		f;
1161 		int		aflags;
1162 {
1163 	if (np->ab_value == NULL)
1164 		return;
1165 	if (aflags & AB_PARSE) {
1166 		if (np->ab_push && (aflags & AB_ALL))
1167 			_ab_prposix(np->ab_push, f, aflags);
1168 
1169 #ifdef	BOURNE_SHELL
1170 		prs_buff(UC "alias ");
1171 		if ((np->ab_flags & ABF_BEGIN) == 0)
1172 			prs_buff(UC "-a ");
1173 		if (np->ab_push)
1174 			prs_buff(UC "-p ");
1175 		if (aflags & AB_PGLOBAL)
1176 			prs_buff(UC "-g ");
1177 		if (aflags & AB_PLOCAL)
1178 			prs_buff(UC "-l ");
1179 #else
1180 		fprintf(f, "alias %s%s%s%s",
1181 				(np->ab_flags & ABF_BEGIN) ? "":"-a ",
1182 				np->ab_push ? "-p ":"",
1183 				(aflags & AB_PGLOBAL) ? "-g ":"",
1184 				(aflags & AB_PLOCAL) ? "-l ":"");
1185 #endif
1186 	}
1187 #ifdef	BOURNE_SHELL
1188 	prs_buff(UC np->ab_name);
1189 	prs_buff(UC "='");
1190 	{	unsigned char *p = UC np->ab_value;
1191 		while (*p) {
1192 			if (*p == '\'')
1193 				prs_buff(UC "'\\'");
1194 			prc_buff(*p++);
1195 		}
1196 	}
1197 	if ((aflags & AB_PARSE) == 0 &&
1198 	    (np->ab_flags & ABF_BEGIN) == 0)
1199 		prs_buff(UC "' # allexpand\n");
1200 	else
1201 		prs_buff(UC "'\n");
1202 #else
1203 	fprintf(f, "'%s=", np->ab_name);
1204 	{	char *p = np->ab_value;
1205 		while (*p) {
1206 			if (*p == '\'')
1207 				fprintf(f, "\\");
1208 			fputc(*p++, f);
1209 		}
1210 	}
1211 	fprintf(f, "'%s\n",
1212 			((np->ab_flags & ABF_BEGIN) == 0) ? " # allexpand":"");
1213 #endif
1214 }
1215 
1216 #ifdef	INTERACTIVE
1217 /*
1218  * The argument "f" is not used in the Bourne Shell
1219  */
1220 /* ARGSUSED */
1221 LOCAL void
_ab_phist(np,f,aflags)1222 _ab_phist(np, f, aflags)
1223 	register abent_t	*np;
1224 		FILE_p		f;
1225 		int		aflags;
1226 {
1227 #ifdef	BOURNE_SHELL
1228 #define	append_line	shedit_append_line
1229 #endif
1230 	if (np->ab_value == NULL)
1231 		return;
1232 	if (np->ab_push && (aflags & AB_ALL))
1233 		_ab_phist(np->ab_push, f, aflags);
1234 
1235 	{	char		buf[8192];
1236 		unsigned int	len;
1237 
1238 		js_snprintf(buf, sizeof (buf),
1239 				"#%s%c%s %-8s %s",
1240 				np->ab_push ? "p":"",
1241 				np->ab_flags & ABF_BEGIN ? 'b':'a',
1242 				(aflags & AB_PLOCAL) ? "l":
1243 				(aflags & AB_PGLOBAL) ? "g":"",
1244 				np->ab_name, np->ab_value);
1245 		len = strlen(buf);
1246 		append_line(buf, len + 1, len);
1247 	}
1248 }
1249 #endif	/* INTERACTIVE */
1250 
1251 #ifdef	BOURNE_SHELL
1252 LOCAL off_t
filesize(f)1253 filesize(f)
1254 	int	f;
1255 {
1256 	struct stat	sb;
1257 
1258 	if (fstat(f, &sb) < 0)
1259 		return ((off_t)-1);
1260 
1261 	return (sb.st_size);
1262 }
1263 
1264 LOCAL BOOL
any_match(s)1265 any_match(s)
1266 	register char	*s;
1267 {
1268 	register unsigned char	*rm = UC "[]?*";
1269 
1270 	while (*s && !any(*s, rm))
1271 		s++;
1272 	return ((BOOL) *s);
1273 }
1274 #endif
1275