xref: /aoo4110/main/soltools/mkdepend/include.c (revision b1cdbd2c)
1*b1cdbd2cSJim Jagielski /* $XConsortium: include.c,v 1.17 94/12/05 19:33:08 gildea Exp $ */
2*b1cdbd2cSJim Jagielski /*
3*b1cdbd2cSJim Jagielski 
4*b1cdbd2cSJim Jagielski Copyright (c) 1993, 1994  X Consortium
5*b1cdbd2cSJim Jagielski 
6*b1cdbd2cSJim Jagielski Permission is hereby granted, free of charge, to any person obtaining a copy
7*b1cdbd2cSJim Jagielski of this software and associated documentation files (the "Software"), to deal
8*b1cdbd2cSJim Jagielski in the Software without restriction, including without limitation the rights
9*b1cdbd2cSJim Jagielski to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10*b1cdbd2cSJim Jagielski copies of the Software, and to permit persons to whom the Software is
11*b1cdbd2cSJim Jagielski furnished to do so, subject to the following conditions:
12*b1cdbd2cSJim Jagielski 
13*b1cdbd2cSJim Jagielski The above copyright notice and this permission notice shall be included in
14*b1cdbd2cSJim Jagielski all copies or substantial portions of the Software.
15*b1cdbd2cSJim Jagielski 
16*b1cdbd2cSJim Jagielski THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17*b1cdbd2cSJim Jagielski IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18*b1cdbd2cSJim Jagielski FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
19*b1cdbd2cSJim Jagielski X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
20*b1cdbd2cSJim Jagielski AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
21*b1cdbd2cSJim Jagielski CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22*b1cdbd2cSJim Jagielski 
23*b1cdbd2cSJim Jagielski Except as contained in this notice, the name of the X Consortium shall not be
24*b1cdbd2cSJim Jagielski used in advertising or otherwise to promote the sale, use or other dealings
25*b1cdbd2cSJim Jagielski in this Software without prior written authorization from the X Consortium.
26*b1cdbd2cSJim Jagielski 
27*b1cdbd2cSJim Jagielski */
28*b1cdbd2cSJim Jagielski 
29*b1cdbd2cSJim Jagielski 
30*b1cdbd2cSJim Jagielski #include "def.h"
31*b1cdbd2cSJim Jagielski #include <string.h>
32*b1cdbd2cSJim Jagielski 
33*b1cdbd2cSJim Jagielski void remove_dotdot( char * );
34*b1cdbd2cSJim Jagielski int isdot( char * );
35*b1cdbd2cSJim Jagielski int isdotdot( char * );
36*b1cdbd2cSJim Jagielski int issymbolic(char * dir, char * component);
37*b1cdbd2cSJim Jagielski int exists_path(struct IncludesCollection*, char*);
38*b1cdbd2cSJim Jagielski 
39*b1cdbd2cSJim Jagielski 
40*b1cdbd2cSJim Jagielski extern struct	inclist	inclist[ MAXFILES ],
41*b1cdbd2cSJim Jagielski 			*inclistp;
42*b1cdbd2cSJim Jagielski extern char	*includedirs[ ];
43*b1cdbd2cSJim Jagielski extern char	*notdotdot[ ];
44*b1cdbd2cSJim Jagielski extern boolean show_where_not;
45*b1cdbd2cSJim Jagielski extern boolean warn_multiple;
46*b1cdbd2cSJim Jagielski 
inc_path(file,include,dot,incCollection)47*b1cdbd2cSJim Jagielski struct inclist *inc_path(file, include, dot, incCollection)
48*b1cdbd2cSJim Jagielski 	register char	*file,
49*b1cdbd2cSJim Jagielski 			*include;
50*b1cdbd2cSJim Jagielski 	boolean	dot;
51*b1cdbd2cSJim Jagielski     struct IncludesCollection* incCollection;
52*b1cdbd2cSJim Jagielski {
53*b1cdbd2cSJim Jagielski 	static char	path[ BUFSIZ ];
54*b1cdbd2cSJim Jagielski 	register char		**pp, *p;
55*b1cdbd2cSJim Jagielski 	register struct inclist	*ip;
56*b1cdbd2cSJim Jagielski 	struct stat	st;
57*b1cdbd2cSJim Jagielski 	boolean	found = FALSE;
58*b1cdbd2cSJim Jagielski 
59*b1cdbd2cSJim Jagielski 	/*
60*b1cdbd2cSJim Jagielski 	 * Check all previously found include files for a path that
61*b1cdbd2cSJim Jagielski 	 * has already been expanded.
62*b1cdbd2cSJim Jagielski 	 */
63*b1cdbd2cSJim Jagielski 	for (ip = inclist; ip->i_file; ip++)
64*b1cdbd2cSJim Jagielski 	    if ((strcmp(ip->i_incstring, include) == 0) && !ip->i_included_sym)
65*b1cdbd2cSJim Jagielski 	    {
66*b1cdbd2cSJim Jagielski 		found = TRUE;
67*b1cdbd2cSJim Jagielski 		break;
68*b1cdbd2cSJim Jagielski 	    }
69*b1cdbd2cSJim Jagielski 
70*b1cdbd2cSJim Jagielski 	/*
71*b1cdbd2cSJim Jagielski 	 * If the path was surrounded by "" or is an absolute path,
72*b1cdbd2cSJim Jagielski 	 * then check the exact path provided.
73*b1cdbd2cSJim Jagielski 	 */
74*b1cdbd2cSJim Jagielski // FIXME: creates duplicates in the dependency files if absolute paths are
75*b1cdbd2cSJim Jagielski // given, which certainly is not the intended behavior. Also it slows down
76*b1cdbd2cSJim Jagielski // makedepend performance considerably.
77*b1cdbd2cSJim Jagielski //	if (!found && (dot || *include == '/')) {
78*b1cdbd2cSJim Jagielski //
79*b1cdbd2cSJim Jagielski //		if ((exists_path(incCollection, include)) && stat(include, &st) == 0 && !( st.st_mode & S_IFDIR)) {
80*b1cdbd2cSJim Jagielski //			ip = newinclude(include, include);
81*b1cdbd2cSJim Jagielski //			found = TRUE;
82*b1cdbd2cSJim Jagielski //		}
83*b1cdbd2cSJim Jagielski //		else if (show_where_not)
84*b1cdbd2cSJim Jagielski //			warning1("\tnot in %s\n", include);
85*b1cdbd2cSJim Jagielski //	}
86*b1cdbd2cSJim Jagielski 
87*b1cdbd2cSJim Jagielski 	/*
88*b1cdbd2cSJim Jagielski 	 * See if this include file is in the directory of the
89*b1cdbd2cSJim Jagielski 	 * file being compiled.
90*b1cdbd2cSJim Jagielski 	 */
91*b1cdbd2cSJim Jagielski 	if (!found) {
92*b1cdbd2cSJim Jagielski 		for (p=file+strlen(file); p>file; p--)
93*b1cdbd2cSJim Jagielski 			if (*p == '/')
94*b1cdbd2cSJim Jagielski 				break;
95*b1cdbd2cSJim Jagielski 		if (p == file)
96*b1cdbd2cSJim Jagielski 			strcpy(path, include);
97*b1cdbd2cSJim Jagielski 		else {
98*b1cdbd2cSJim Jagielski 			strncpy(path, file, (p-file) + 1);
99*b1cdbd2cSJim Jagielski 			path[ (p-file) + 1 ] = '\0';
100*b1cdbd2cSJim Jagielski 			strcpy(path + (p-file) + 1, include);
101*b1cdbd2cSJim Jagielski 		}
102*b1cdbd2cSJim Jagielski 		remove_dotdot(path);
103*b1cdbd2cSJim Jagielski 		if ((exists_path(incCollection, path)) && stat(path, &st) == 0 && !( st.st_mode & S_IFDIR)) {
104*b1cdbd2cSJim Jagielski 			ip = newinclude(path, include);
105*b1cdbd2cSJim Jagielski 			found = TRUE;
106*b1cdbd2cSJim Jagielski 		}
107*b1cdbd2cSJim Jagielski 		else if (show_where_not)
108*b1cdbd2cSJim Jagielski 			warning1("\tnot in %s\n", path);
109*b1cdbd2cSJim Jagielski 	}
110*b1cdbd2cSJim Jagielski 
111*b1cdbd2cSJim Jagielski 	/*
112*b1cdbd2cSJim Jagielski 	 * Check the include directories specified. (standard include dir
113*b1cdbd2cSJim Jagielski 	 * should be at the end.)
114*b1cdbd2cSJim Jagielski 	 */
115*b1cdbd2cSJim Jagielski 	if (!found)
116*b1cdbd2cSJim Jagielski 		for (pp = includedirs; *pp; pp++) {
117*b1cdbd2cSJim Jagielski 			sprintf(path, "%s/%s", *pp, include);
118*b1cdbd2cSJim Jagielski 			remove_dotdot(path);
119*b1cdbd2cSJim Jagielski 			if ((exists_path(incCollection, path)) && stat(path, &st) == 0 && !(st.st_mode & S_IFDIR)) {
120*b1cdbd2cSJim Jagielski 				ip = newinclude(path, include);
121*b1cdbd2cSJim Jagielski 				found = TRUE;
122*b1cdbd2cSJim Jagielski 				break;
123*b1cdbd2cSJim Jagielski 			}
124*b1cdbd2cSJim Jagielski 			else if (show_where_not)
125*b1cdbd2cSJim Jagielski 				warning1("\tnot in %s\n", path);
126*b1cdbd2cSJim Jagielski 		}
127*b1cdbd2cSJim Jagielski 
128*b1cdbd2cSJim Jagielski 	if (!found)
129*b1cdbd2cSJim Jagielski 		ip = NULL;
130*b1cdbd2cSJim Jagielski 	return(ip);
131*b1cdbd2cSJim Jagielski }
132*b1cdbd2cSJim Jagielski 
exists_path(incCollection,path)133*b1cdbd2cSJim Jagielski int exists_path(incCollection, path)
134*b1cdbd2cSJim Jagielski     struct IncludesCollection* incCollection;
135*b1cdbd2cSJim Jagielski     char* path;
136*b1cdbd2cSJim Jagielski {
137*b1cdbd2cSJim Jagielski     convert_slashes(path);
138*b1cdbd2cSJim Jagielski     return call_IncludesCollection_exists(incCollection, path);
139*b1cdbd2cSJim Jagielski }
140*b1cdbd2cSJim Jagielski 
141*b1cdbd2cSJim Jagielski /*
142*b1cdbd2cSJim Jagielski  * Occasionally, pathnames are created that look like .../x/../y
143*b1cdbd2cSJim Jagielski  * Any of the 'x/..' sequences within the name can be eliminated.
144*b1cdbd2cSJim Jagielski  * (but only if 'x' is not a symbolic link!!)
145*b1cdbd2cSJim Jagielski  */
remove_dotdot(path)146*b1cdbd2cSJim Jagielski void remove_dotdot(path)
147*b1cdbd2cSJim Jagielski 	char	*path;
148*b1cdbd2cSJim Jagielski {
149*b1cdbd2cSJim Jagielski 	register char	*end, *from, *to, **cp;
150*b1cdbd2cSJim Jagielski 	char		*components[ MAXFILES ],
151*b1cdbd2cSJim Jagielski 			newpath[ BUFSIZ ];
152*b1cdbd2cSJim Jagielski 	boolean		component_copied;
153*b1cdbd2cSJim Jagielski 
154*b1cdbd2cSJim Jagielski 	/*
155*b1cdbd2cSJim Jagielski 	 * slice path up into components.
156*b1cdbd2cSJim Jagielski 	 */
157*b1cdbd2cSJim Jagielski 	to = newpath;
158*b1cdbd2cSJim Jagielski 	if (*path == '/')
159*b1cdbd2cSJim Jagielski 		*to++ = '/';
160*b1cdbd2cSJim Jagielski 	*to = '\0';
161*b1cdbd2cSJim Jagielski 	cp = components;
162*b1cdbd2cSJim Jagielski 	for (from=end=path; *end; end++)
163*b1cdbd2cSJim Jagielski 		if (*end == '/') {
164*b1cdbd2cSJim Jagielski 			while (*end == '/')
165*b1cdbd2cSJim Jagielski 				*end++ = '\0';
166*b1cdbd2cSJim Jagielski 			if (*from)
167*b1cdbd2cSJim Jagielski 				*cp++ = from;
168*b1cdbd2cSJim Jagielski 			from = end;
169*b1cdbd2cSJim Jagielski 		}
170*b1cdbd2cSJim Jagielski 	*cp++ = from;
171*b1cdbd2cSJim Jagielski 	*cp = NULL;
172*b1cdbd2cSJim Jagielski 
173*b1cdbd2cSJim Jagielski 	/*
174*b1cdbd2cSJim Jagielski 	 * Recursively remove all 'x/..' component pairs.
175*b1cdbd2cSJim Jagielski 	 */
176*b1cdbd2cSJim Jagielski 	cp = components;
177*b1cdbd2cSJim Jagielski 	while(*cp) {
178*b1cdbd2cSJim Jagielski 		if (!isdot(*cp) && !isdotdot(*cp) && isdotdot(*(cp+1))
179*b1cdbd2cSJim Jagielski 		    && !issymbolic(newpath, *cp))
180*b1cdbd2cSJim Jagielski 		{
181*b1cdbd2cSJim Jagielski 		    char **fp = cp + 2;
182*b1cdbd2cSJim Jagielski 		    char **tp = cp;
183*b1cdbd2cSJim Jagielski 
184*b1cdbd2cSJim Jagielski 		    do
185*b1cdbd2cSJim Jagielski 			*tp++ = *fp; /* move all the pointers down */
186*b1cdbd2cSJim Jagielski 		    while (*fp++);
187*b1cdbd2cSJim Jagielski 		    if (cp != components)
188*b1cdbd2cSJim Jagielski 			cp--;	/* go back and check for nested ".." */
189*b1cdbd2cSJim Jagielski 		} else {
190*b1cdbd2cSJim Jagielski 		    cp++;
191*b1cdbd2cSJim Jagielski 		}
192*b1cdbd2cSJim Jagielski 	}
193*b1cdbd2cSJim Jagielski 	/*
194*b1cdbd2cSJim Jagielski 	 * Concatenate the remaining path elements.
195*b1cdbd2cSJim Jagielski 	 */
196*b1cdbd2cSJim Jagielski 	cp = components;
197*b1cdbd2cSJim Jagielski 	component_copied = FALSE;
198*b1cdbd2cSJim Jagielski 	while(*cp) {
199*b1cdbd2cSJim Jagielski 		if (component_copied)
200*b1cdbd2cSJim Jagielski 			*to++ = '/';
201*b1cdbd2cSJim Jagielski 		component_copied = TRUE;
202*b1cdbd2cSJim Jagielski 		for (from = *cp; *from; )
203*b1cdbd2cSJim Jagielski 			*to++ = *from++;
204*b1cdbd2cSJim Jagielski 		*to = '\0';
205*b1cdbd2cSJim Jagielski 		cp++;
206*b1cdbd2cSJim Jagielski 	}
207*b1cdbd2cSJim Jagielski 	*to++ = '\0';
208*b1cdbd2cSJim Jagielski 
209*b1cdbd2cSJim Jagielski 	/*
210*b1cdbd2cSJim Jagielski 	 * copy the reconstituted path back to our pointer.
211*b1cdbd2cSJim Jagielski 	 */
212*b1cdbd2cSJim Jagielski 	strcpy(path, newpath);
213*b1cdbd2cSJim Jagielski }
214*b1cdbd2cSJim Jagielski 
isdot(p)215*b1cdbd2cSJim Jagielski int isdot(p)
216*b1cdbd2cSJim Jagielski 	register char	*p;
217*b1cdbd2cSJim Jagielski {
218*b1cdbd2cSJim Jagielski 	if(p && *p++ == '.' && *p++ == '\0')
219*b1cdbd2cSJim Jagielski 		return(TRUE);
220*b1cdbd2cSJim Jagielski 	return(FALSE);
221*b1cdbd2cSJim Jagielski }
222*b1cdbd2cSJim Jagielski 
isdotdot(p)223*b1cdbd2cSJim Jagielski int isdotdot(p)
224*b1cdbd2cSJim Jagielski 	register char	*p;
225*b1cdbd2cSJim Jagielski {
226*b1cdbd2cSJim Jagielski 	if(p && *p++ == '.' && *p++ == '.' && *p++ == '\0')
227*b1cdbd2cSJim Jagielski 		return(TRUE);
228*b1cdbd2cSJim Jagielski 	return(FALSE);
229*b1cdbd2cSJim Jagielski }
230*b1cdbd2cSJim Jagielski 
issymbolic(dir,component)231*b1cdbd2cSJim Jagielski int issymbolic(dir, component)
232*b1cdbd2cSJim Jagielski 	register char	*dir, *component;
233*b1cdbd2cSJim Jagielski {
234*b1cdbd2cSJim Jagielski #ifdef S_IFLNK
235*b1cdbd2cSJim Jagielski 	struct stat	st;
236*b1cdbd2cSJim Jagielski 	char	buf[ BUFSIZ ], **pp;
237*b1cdbd2cSJim Jagielski 
238*b1cdbd2cSJim Jagielski 	sprintf(buf, "%s%s%s", dir, *dir ? "/" : "", component);
239*b1cdbd2cSJim Jagielski 	for (pp=notdotdot; *pp; pp++)
240*b1cdbd2cSJim Jagielski 		if (strcmp(*pp, buf) == 0)
241*b1cdbd2cSJim Jagielski 			return (TRUE);
242*b1cdbd2cSJim Jagielski 	if (lstat(buf, &st) == 0
243*b1cdbd2cSJim Jagielski 	&& (st.st_mode & S_IFMT) == S_IFLNK) {
244*b1cdbd2cSJim Jagielski 		*pp++ = copy(buf);
245*b1cdbd2cSJim Jagielski 		if (pp >= &notdotdot[ MAXDIRS ])
246*b1cdbd2cSJim Jagielski 			fatalerr("out of .. dirs, increase MAXDIRS\n");
247*b1cdbd2cSJim Jagielski 		return(TRUE);
248*b1cdbd2cSJim Jagielski 	}
249*b1cdbd2cSJim Jagielski #endif
250*b1cdbd2cSJim Jagielski 	return(FALSE);
251*b1cdbd2cSJim Jagielski }
252*b1cdbd2cSJim Jagielski 
253*b1cdbd2cSJim Jagielski /*
254*b1cdbd2cSJim Jagielski  * Add an include file to the list of those included by 'file'.
255*b1cdbd2cSJim Jagielski  */
newinclude(newfile,incstring)256*b1cdbd2cSJim Jagielski struct inclist *newinclude(newfile, incstring)
257*b1cdbd2cSJim Jagielski 	register char	*newfile, *incstring;
258*b1cdbd2cSJim Jagielski {
259*b1cdbd2cSJim Jagielski 	register struct inclist	*ip;
260*b1cdbd2cSJim Jagielski 
261*b1cdbd2cSJim Jagielski 	/*
262*b1cdbd2cSJim Jagielski 	 * First, put this file on the global list of include files.
263*b1cdbd2cSJim Jagielski 	 */
264*b1cdbd2cSJim Jagielski 	ip = inclistp++;
265*b1cdbd2cSJim Jagielski 	if (inclistp == inclist + MAXFILES - 1)
266*b1cdbd2cSJim Jagielski 		fatalerr("out of space: increase MAXFILES\n");
267*b1cdbd2cSJim Jagielski 	ip->i_file = copy(newfile);
268*b1cdbd2cSJim Jagielski 	ip->i_included_sym = FALSE;
269*b1cdbd2cSJim Jagielski 	if (incstring == NULL)
270*b1cdbd2cSJim Jagielski 		ip->i_incstring = ip->i_file;
271*b1cdbd2cSJim Jagielski 	else
272*b1cdbd2cSJim Jagielski 		ip->i_incstring = copy(incstring);
273*b1cdbd2cSJim Jagielski 
274*b1cdbd2cSJim Jagielski 	return(ip);
275*b1cdbd2cSJim Jagielski }
276*b1cdbd2cSJim Jagielski 
included_by(ip,newfile)277*b1cdbd2cSJim Jagielski void included_by(ip, newfile)
278*b1cdbd2cSJim Jagielski 	register struct inclist	*ip, *newfile;
279*b1cdbd2cSJim Jagielski {
280*b1cdbd2cSJim Jagielski 	register int i;
281*b1cdbd2cSJim Jagielski 
282*b1cdbd2cSJim Jagielski 	if (ip == NULL)
283*b1cdbd2cSJim Jagielski 		return;
284*b1cdbd2cSJim Jagielski 	/*
285*b1cdbd2cSJim Jagielski 	 * Put this include file (newfile) on the list of files included
286*b1cdbd2cSJim Jagielski 	 * by 'file'.  If 'file' is NULL, then it is not an include
287*b1cdbd2cSJim Jagielski 	 * file itself (i.e. was probably mentioned on the command line).
288*b1cdbd2cSJim Jagielski 	 * If it is already on the list, don't stick it on again.
289*b1cdbd2cSJim Jagielski 	 */
290*b1cdbd2cSJim Jagielski 	if (ip->i_list == NULL)
291*b1cdbd2cSJim Jagielski 		ip->i_list = (struct inclist **)
292*b1cdbd2cSJim Jagielski 			malloc(sizeof(struct inclist *) * ++ip->i_listlen);
293*b1cdbd2cSJim Jagielski 	else {
294*b1cdbd2cSJim Jagielski 		for (i=0; i<ip->i_listlen; i++)
295*b1cdbd2cSJim Jagielski 			if (ip->i_list[ i ] == newfile) {
296*b1cdbd2cSJim Jagielski 			    i = strlen(newfile->i_file);
297*b1cdbd2cSJim Jagielski 			    if (!ip->i_included_sym &&
298*b1cdbd2cSJim Jagielski 				!(i > 2 &&
299*b1cdbd2cSJim Jagielski 				  newfile->i_file[i-1] == 'c' &&
300*b1cdbd2cSJim Jagielski 				  newfile->i_file[i-2] == '.'))
301*b1cdbd2cSJim Jagielski 			    {
302*b1cdbd2cSJim Jagielski 				/* only complain if ip has */
303*b1cdbd2cSJim Jagielski 				/* no #include SYMBOL lines  */
304*b1cdbd2cSJim Jagielski 				/* and is not a .c file */
305*b1cdbd2cSJim Jagielski 				if (warn_multiple)
306*b1cdbd2cSJim Jagielski 				{
307*b1cdbd2cSJim Jagielski 					warning("%s includes %s more than once!\n",
308*b1cdbd2cSJim Jagielski 						ip->i_file, newfile->i_file);
309*b1cdbd2cSJim Jagielski 					warning1("Already have\n");
310*b1cdbd2cSJim Jagielski 					for (i=0; i<ip->i_listlen; i++)
311*b1cdbd2cSJim Jagielski 						warning1("\t%s\n", ip->i_list[i]->i_file);
312*b1cdbd2cSJim Jagielski 				}
313*b1cdbd2cSJim Jagielski 			    }
314*b1cdbd2cSJim Jagielski 			    return;
315*b1cdbd2cSJim Jagielski 			}
316*b1cdbd2cSJim Jagielski 		ip->i_list = (struct inclist **) realloc(ip->i_list,
317*b1cdbd2cSJim Jagielski 			sizeof(struct inclist *) * ++ip->i_listlen);
318*b1cdbd2cSJim Jagielski 	}
319*b1cdbd2cSJim Jagielski 	ip->i_list[ ip->i_listlen-1 ] = newfile;
320*b1cdbd2cSJim Jagielski }
321*b1cdbd2cSJim Jagielski 
inc_clean()322*b1cdbd2cSJim Jagielski void inc_clean ()
323*b1cdbd2cSJim Jagielski {
324*b1cdbd2cSJim Jagielski 	register struct inclist *ip;
325*b1cdbd2cSJim Jagielski 
326*b1cdbd2cSJim Jagielski 	for (ip = inclist; ip < inclistp; ip++) {
327*b1cdbd2cSJim Jagielski 		ip->i_marked = FALSE;
328*b1cdbd2cSJim Jagielski 	}
329*b1cdbd2cSJim Jagielski }
330