xref: /freebsd/bin/sh/alias.c (revision 4f29da19bd44f0e99f021510460a81bf754c21d2)
1 /*-
2  * Copyright (c) 1993
3  *	The Regents of the University of California.  All rights reserved.
4  *
5  * This code is derived from software contributed to Berkeley by
6  * Kenneth Almquist.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 4. Neither the name of the University nor the names of its contributors
17  *    may be used to endorse or promote products derived from this software
18  *    without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30  * SUCH DAMAGE.
31  */
32 
33 #ifndef lint
34 #if 0
35 static char sccsid[] = "@(#)alias.c	8.3 (Berkeley) 5/4/95";
36 #endif
37 #endif /* not lint */
38 #include <sys/cdefs.h>
39 __FBSDID("$FreeBSD$");
40 
41 #include <stdlib.h>
42 #include "shell.h"
43 #include "input.h"
44 #include "output.h"
45 #include "error.h"
46 #include "memalloc.h"
47 #include "mystring.h"
48 #include "alias.h"
49 #include "options.h"	/* XXX for argptr (should remove?) */
50 
51 #define ATABSIZE 39
52 
53 STATIC struct alias *atab[ATABSIZE];
54 
55 STATIC void setalias(char *, char *);
56 STATIC int unalias(char *);
57 STATIC struct alias **hashalias(char *);
58 
59 STATIC
60 void
61 setalias(char *name, char *val)
62 {
63 	struct alias *ap, **app;
64 
65 	app = hashalias(name);
66 	for (ap = *app; ap; ap = ap->next) {
67 		if (equal(name, ap->name)) {
68 			INTOFF;
69 			ckfree(ap->val);
70 			ap->val	= savestr(val);
71 			INTON;
72 			return;
73 		}
74 	}
75 	/* not found */
76 	INTOFF;
77 	ap = ckmalloc(sizeof (struct alias));
78 	ap->name = savestr(name);
79 	/*
80 	 * XXX - HACK: in order that the parser will not finish reading the
81 	 * alias value off the input before processing the next alias, we
82 	 * dummy up an extra space at the end of the alias.  This is a crock
83 	 * and should be re-thought.  The idea (if you feel inclined to help)
84 	 * is to avoid alias recursions.  The mechanism used is: when
85 	 * expanding an alias, the value of the alias is pushed back on the
86 	 * input as a string and a pointer to the alias is stored with the
87 	 * string.  The alias is marked as being in use.  When the input
88 	 * routine finishes reading the string, it marks the alias not
89 	 * in use.  The problem is synchronization with the parser.  Since
90 	 * it reads ahead, the alias is marked not in use before the
91 	 * resulting token(s) is next checked for further alias sub.  The
92 	 * H A C K is that we add a little fluff after the alias value
93 	 * so that the string will not be exhausted.  This is a good
94 	 * idea ------- ***NOT***
95 	 */
96 #ifdef notyet
97 	ap->val = savestr(val);
98 #else /* hack */
99 	{
100 	int len = strlen(val);
101 	ap->val = ckmalloc(len + 2);
102 	memcpy(ap->val, val, len);
103 	ap->val[len] = ' ';	/* fluff */
104 	ap->val[len+1] = '\0';
105 	}
106 #endif
107 	ap->flag = 0;
108 	ap->next = *app;
109 	*app = ap;
110 	INTON;
111 }
112 
113 STATIC int
114 unalias(char *name)
115 {
116 	struct alias *ap, **app;
117 
118 	app = hashalias(name);
119 
120 	for (ap = *app; ap; app = &(ap->next), ap = ap->next) {
121 		if (equal(name, ap->name)) {
122 			/*
123 			 * if the alias is currently in use (i.e. its
124 			 * buffer is being used by the input routine) we
125 			 * just null out the name instead of freeing it.
126 			 * We could clear it out later, but this situation
127 			 * is so rare that it hardly seems worth it.
128 			 */
129 			if (ap->flag & ALIASINUSE)
130 				*ap->name = '\0';
131 			else {
132 				INTOFF;
133 				*app = ap->next;
134 				ckfree(ap->name);
135 				ckfree(ap->val);
136 				ckfree(ap);
137 				INTON;
138 			}
139 			return (0);
140 		}
141 	}
142 
143 	return (1);
144 }
145 
146 #ifdef mkinit
147 MKINIT void rmaliases(void);
148 
149 SHELLPROC {
150 	rmaliases();
151 }
152 #endif
153 
154 void
155 rmaliases(void)
156 {
157 	struct alias *ap, *tmp;
158 	int i;
159 
160 	INTOFF;
161 	for (i = 0; i < ATABSIZE; i++) {
162 		ap = atab[i];
163 		atab[i] = NULL;
164 		while (ap) {
165 			ckfree(ap->name);
166 			ckfree(ap->val);
167 			tmp = ap;
168 			ap = ap->next;
169 			ckfree(tmp);
170 		}
171 	}
172 	INTON;
173 }
174 
175 struct alias *
176 lookupalias(char *name, int check)
177 {
178 	struct alias *ap = *hashalias(name);
179 
180 	for (; ap; ap = ap->next) {
181 		if (equal(name, ap->name)) {
182 			if (check && (ap->flag & ALIASINUSE))
183 				return (NULL);
184 			return (ap);
185 		}
186 	}
187 
188 	return (NULL);
189 }
190 
191 /*
192  * TODO - sort output
193  */
194 int
195 aliascmd(int argc, char **argv)
196 {
197 	char *n, *v;
198 	int ret = 0;
199 	struct alias *ap;
200 
201 	if (argc == 1) {
202 		int i;
203 
204 		for (i = 0; i < ATABSIZE; i++)
205 			for (ap = atab[i]; ap; ap = ap->next) {
206 				if (*ap->name != '\0') {
207 					out1fmt("alias %s=", ap->name);
208 					out1qstr(ap->val);
209 					out1c('\n');
210 				}
211 			}
212 		return (0);
213 	}
214 	while ((n = *++argv) != NULL) {
215 		if ((v = strchr(n+1, '=')) == NULL) /* n+1: funny ksh stuff */
216 			if ((ap = lookupalias(n, 0)) == NULL) {
217 				outfmt(out2, "alias: %s not found\n", n);
218 				ret = 1;
219 			} else {
220 				out1fmt("alias %s=", n);
221 				out1qstr(ap->val);
222 				out1c('\n');
223 			}
224 		else {
225 			*v++ = '\0';
226 			setalias(n, v);
227 		}
228 	}
229 
230 	return (ret);
231 }
232 
233 int
234 unaliascmd(int argc __unused, char **argv __unused)
235 {
236 	int i;
237 
238 	while ((i = nextopt("a")) != '\0') {
239 		if (i == 'a') {
240 			rmaliases();
241 			return (0);
242 		}
243 	}
244 	for (i = 0; *argptr; argptr++)
245 		i |= unalias(*argptr);
246 
247 	return (i);
248 }
249 
250 STATIC struct alias **
251 hashalias(char *p)
252 {
253 	unsigned int hashval;
254 
255 	hashval = *p << 4;
256 	while (*p)
257 		hashval+= *p++;
258 	return &atab[hashval % ATABSIZE];
259 }
260