1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License, Version 1.0 only
6 * (the "License").  You may not use this file except in compliance
7 * with the License.
8 *
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
13 *
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
19 *
20 * CDDL HEADER END
21 */
22/*
23 * Copyright (c) 1996, by Sun Microsystems, Inc.
24 * All Rights Reserved.
25 */
26
27#pragma ident	"%Z%%M%	%I%	%E% SMI"
28
29#include <locale.h>
30#include <sys/types.h>
31#include <stdio.h>
32#include <sys/param.h>
33#include <string.h>
34#include <unistd.h>
35#include <errno.h>
36#include <fcntl.h>
37#include <sys/stat.h>
38#include <stdlib.h>
39
40#include "rules.h"
41
42char * lex(FILE *);
43
44extern char *mstrdup(const char *);
45extern void *mmalloc(size_t size);
46
47void
48read_rules(FILE *file, int (*rulefunc)())
49{
50	char *s;
51	int base_active = 0;
52	int list_ent_cnt = 0;
53	int gign_ent_cnt = 0;
54	int lign_ent_cnt = 0;
55	struct item *add_item();
56	struct item *fitem, *sitem;
57	char version[20];
58
59	last_gign = &gign_hd;
60	gign_hd.i_next = (struct item *)0;
61	gign_hd.i_str = (char *)0;
62	list_hd.i_next = (struct item *)0;
63	list_hd.i_str = (char *)0;
64	while (s = lex(file)) {
65		if (s == (char *)0)
66			break;
67		if (*s == '#')
68			continue;
69		if (*s == '*')
70			continue;
71		if (strcmp(s, BASE) == 0) {
72#ifdef DEBUG
73			printf("BASE base_active = %d\n", base_active);
74#endif /* DEBUG */
75			if (base_active) {
76				/*
77				 * Tack local IGNORE strings to end of globals
78				 */
79				if (lign_hd.i_next != (struct item *)0) {
80					last_gign->i_next = &lign_hd;
81				}
82				/*
83				 * Process directives for previous BASE command
84				 * if there was one. Also free up LIST items
85				 * and local IGNORE items.
86				 */
87				do_base_dir(basedir, &list_hd, &gign_hd,
88				    rulefunc);
89				/*
90				 * Free up space from LIST item list
91				 */
92				fitem  = list_hd.i_next;
93				if (fitem != (struct item *)0) {
94					while (fitem != (struct item *)0) {
95						free(fitem->i_str);
96						sitem = fitem->i_next;
97						free(fitem);
98						fitem = sitem;
99					}
100				}
101				/*
102				 * Free up space from local IGNORE item list
103				 */
104				fitem  = lign_hd.i_next;
105				if (fitem != (struct item *)0) {
106					while (fitem != (struct item *)0) {
107						free(fitem->i_str);
108						sitem = fitem->i_next;
109						free(fitem);
110						fitem = sitem;
111					}
112				}
113				last_gign->i_next = (struct item *)0;
114			}
115			base_active = 1;
116			/*
117			 * Reset LIST item list and local IGNORE item
118			 * list to be empty.
119			 */
120			last_list = &list_hd;
121			list_hd.i_next = (struct item *)0;
122			list_hd.i_str = (char *)0;
123			last_lign = &lign_hd;
124			lign_hd.i_next = (struct item *)0;
125			lign_hd.i_str = (char *)0;
126			/*
127			 * Get BASE directory specified
128			 */
129			s = lex(0);
130			if (s == (char *)0) {
131				fprintf(stderr, gettext("cachefspack: "));
132				fprintf(stderr, gettext(
133				    "illegal BASE command\n"));
134				return;
135			}
136
137			if (*s == '$') {
138				/*
139				 * String starts with a '$', it must be an
140				 * environment variable
141				 */
142				s = getenv(&s[1]);
143				if (s == (char *)NULL) {
144					fprintf(stderr,
145					    gettext("cachefspack: "));
146					fprintf(stderr,
147					    gettext("Can't find "
148					    "environment variable\n"));
149					exit(1);
150				}
151			}
152			basedir = mstrdup(s);
153#ifdef DEBUG
154			printf("basedir = %s\n", basedir);
155#endif /* DEBUG */
156			continue;
157		}
158		if (strcmp(s, IGNORE) == 0) {
159#ifdef DEBUG
160			printf("IGNORE - base_active = %d\n", base_active);
161#endif /* DEBUG */
162			if (base_active) {
163				/*
164				 * Local IGNORE rule
165				 */
166				while ((s = lex(0))
167				    != 0) {
168					last_lign = add_item(last_lign, s,
169					    def_lign_flags);
170				}
171			} else {
172				/*
173				 * Global IGNORE rule
174				 */
175				while ((s = lex(0)) != 0) {
176					last_gign = add_item(last_gign, s,
177					    def_gign_flags);
178				}
179			}
180			continue;
181		}
182		if (strcmp(s, LIST) == 0) {
183#ifdef DEBUG
184			printf("LIST\n");
185#endif /* DEBUG */
186			if (!base_active) {
187				fprintf(stderr,
188				    gettext(
189				    "cachefspack: skipping LIST command - "));
190				fprintf(stderr,
191				    gettext(" no active base\n"));
192				continue;
193			}
194			while ((s = lex(0)) != 0) {
195				last_list = add_item(last_list, s,
196				    def_list_flags);
197			}
198			continue;
199		}
200		if (strcmp(s, VERSION) == 0) {
201			sprintf(version, "%d.%d", VERMAJOR, VERMINOR);
202			s = lex(0);
203			if (s == (char *)0) {
204				fprintf(stderr, gettext("cachefspack: "));
205				fprintf(stderr, gettext("missing version\n"));
206				fprintf(stderr, gettext("cachefspack: "));
207				fprintf(stderr, gettext(
208				    "version = %d.%d\n"), VERMAJOR, VERMINOR);
209				exit(1);
210			}
211			if (strcmp(version, s) != 0) {
212				fprintf(stderr, gettext(
213				    "cachefspack: "));
214				fprintf(stderr, gettext(
215				    "WARNING - version of packing rules "));
216				fprintf(stderr, gettext(
217				    "does not match cachefspack version\n"));
218				fprintf(stderr, gettext(
219				    "version = %d.%d\n"), VERMAJOR, VERMINOR);
220			}
221		}
222	}
223	/*
224	 * Tack local IGNORE strings to end of globals
225	 */
226	if (lign_hd.i_next != (struct item *)0) {
227		last_gign->i_next = &lign_hd;
228	}
229	do_base_dir(basedir, &list_hd, &gign_hd, rulefunc);
230}
231
232struct item *
233add_item(struct item *last_item, char *str, int flags)
234{
235	struct item * add_cmd_items();
236
237	if (*str == CMDCHAR) {
238		last_item = add_cmd_items(last_item, &str[1], bang_list_flags);
239	} else {
240		last_item->i_next = (struct item *)mmalloc(
241		    sizeof (struct item));
242		last_item = last_item->i_next;
243		last_item->i_str = mstrdup(str);
244		last_item->i_flag = flags;
245		last_item->i_next = (struct item *)0;
246	}
247	return (last_item);
248}
249
250struct item *
251add_cmd_items(struct item *last_item, char *str, int flags)
252{
253	FILE *fd;
254	char inbuf[MAX_RULE_SZ];
255	char *olddir = NULL;
256	char *s;
257	void getcmd(char *, char *);
258
259	if ((basedir != NULL) && (basedir[0] != '\0')) {
260		olddir = getcwd(NULL, MAXPATHLEN + 1);
261		if (olddir == NULL) {
262			fprintf(stderr, gettext("cannot malloc buffer\n"));
263			exit(1);
264		}
265
266		if (chdir(basedir) != 0) {
267			fprintf(stderr, gettext("cannot chdir to %s: %s\n"),
268			    basedir, strerror(errno));
269			exit(1);
270		}
271	}
272
273	getcmd(str, inbuf);
274	fd = popen(inbuf, "r");
275	if (fd == NULL) {
276		fprintf(stderr, gettext("cachefspack: LIST can't execute - "));
277		fprintf(stderr, "%s\n", inbuf);
278		exit(1);
279	}
280
281	while (s = lex(fd)) {
282		last_item = add_item(last_item, s, flags);
283		while (s = lex(0)) {
284			last_item = add_item(last_item, s, flags);
285		}
286	}
287	if (pclose(fd) < 0) {
288		fprintf(stderr, gettext("cachefspack: can't close pipe\n"));
289	}
290
291	if (olddir != NULL) {
292		if (chdir(olddir) != 0) {
293			fprintf(stderr, gettext("cannot return to %s: %s\n"),
294			    olddir, strerror(errno));
295			exit(1);
296		}
297		free(olddir);
298	}
299
300	return (last_item);
301}
302
303void
304getcmd(char *str, char *buf)
305{
306	char *s;
307
308	strcpy(buf, str);
309	strcat(buf, " ");
310	while (s = lex(0)) {
311		strcat(buf, s);
312		strcat(buf, " ");
313	}
314#ifdef DEBUG
315	printf("getcmd: cmd = %s\n", buf);
316#endif /* DEBUG */
317}
318
319/*
320 * routine:
321 *	lex
322 *
323 * purpose:
324 *	my own version of strtok that handles quoting and escaping
325 *
326 * parameters:
327 *	string to be lexed (or 0 for same string)
328 *
329 * returns:
330 *	pointer to next token
331 *
332 * notes:
333 *	this routine makes no changes to the string it is passed,
334 *	copying tokens into a static buffer.
335 */
336char *
337lex(FILE *fd)
338{	char c, delim;
339	char *p;
340	const char *s;
341	static const char *savep = 0;
342	static char namebuf[MAX_RULE_SZ];
343	static char inbuf[MAX_RULE_SZ];
344	int len, space_left;
345	char *err;
346
347	/*
348	 * if the file descriptor is non-zero read a new command. Otherwise
349	 * get fields from current line.
350	 */
351	if (fd != 0) {
352		len = 0;
353		space_left = sizeof (inbuf);
354		while ((err = fgets(&inbuf[len], space_left, fd)) != NULL) {
355			len = strlen(inbuf);
356			if (len == 1) {
357				/*
358				 * must be a blank line starting command.
359				 * If a blank line occurs after the start of
360				 * a command, blanks will be included in the
361				 * command.
362				 */
363				len = 0;
364				continue;
365			}
366			len -= 2;
367			space_left -= len;
368			s = (char *)((int)inbuf + len);
369			/*
370			 * Continuation character
371			 */
372			if (strcmp(s, "\\\n") == 0) {
373				continue;
374			}
375			break;
376		}
377		if (err == NULL) {
378			return (err);
379		}
380		s = inbuf;
381	} else {
382		if (savep == 0)
383			return (0);
384		s = savep;
385	}
386	savep = 0;
387
388	/* skip over leading white space	*/
389	while (isspace(*s))
390		s++;
391	if (*s == 0) {
392		return (0);
393	}
394
395	/* see if this is a quoted string	*/
396	c = *s;
397	if (c == '\'' || c == '"') {
398		delim = c;
399		s++;
400	} else
401		delim = 0;
402
403	/* copy the token into the buffer	*/
404	for (p = namebuf; (c = *s) != 0; s++) {
405		if ((p - namebuf) >= sizeof (namebuf)) {
406			savep = 0;
407			return (0);
408		}
409		/* literal escape		*/
410		if (c == '\\') {
411			s++;
412			*p++ = *s;
413			continue;
414		}
415
416		/* closing delimiter		*/
417		if (c == delim) {
418			s++;
419			break;
420		}
421
422		/* delimiting white space	*/
423		if (delim == 0 && isspace(c))
424			break;
425
426		/* ordinary characters		*/
427		*p++ = *s;
428	}
429
430
431	/* remember where we left off		*/
432	savep = *s ? s : 0;
433
434	/* null terminate and return the buffer	*/
435	*p = 0;
436	return (namebuf);
437}
438
439char *
440mk_base_dir(char *path, char *linkpath)
441{
442	static char pathb[MAXPATHLEN];
443	char *dnam;
444	char *get_dirname(char *);
445	int len;
446
447	/*
448	 * absolute path name
449	 */
450	if (*linkpath == '/') {
451		strcpy(pathb, linkpath);
452	} else {
453		/*
454		 * relative path
455		 */
456		dnam = get_dirname(path);
457		if (dnam == (char *)0) {
458			return ((char *) 0);
459		}
460		strcpy(pathb, dnam);
461		len = strlen(pathb);
462		if (len == 0)
463			return (pathb);
464		if (pathb[len-1] != '/')
465		    strcat(pathb, "/");
466		if (strncmp(linkpath, "../", 3) == 0) {
467			/*
468			 * path is relative to directory containing sym link
469			 * remove "../" from beginning of linkpath
470			 */
471			strcat(pathb, &linkpath[3]);
472		} else {
473			/*
474			 * path is relative to directory containing sym link
475			 */
476			strcat(pathb, linkpath);
477		}
478	}
479	return (pathb);
480}
481