1  /*
2  *	util.c
3  *
4  *	utility routines called from a number of different places
5  *
6  *	$Id: util.c,v 1.41 2012/03/27 07:19:27 conrads Exp $
7  *
8  *****************************************************************************/
9 
10 #include <fcntl.h>
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <string.h>
14 #include <unistd.h>
15 #include <sys/param.h>
16 #include <sys/stat.h>
17 
18 /* only reference in this file to this next set of pointers is in cleanup(),
19 	where they're freed just before the program exits, so skip the comments
20 	for each one :-)
21  */
22 extern	char   *index_buf;
23 extern	char   *template_buf_category;
24 extern	char   *template_buf_port;
25 extern	char   *template_buf_top;
26 extern	char *(*index_categories)[];
27 
28 extern	char	ports_dir_plus[PATH_MAX];	/* ports_dir + trailing slash */
29 
30 extern	int		normal_paths;		/* make relative paths relative to current
31 										working directory instead of ports_dir
32 									 */
33 extern	int		verbose;			/* increase the amount of output */
34 
35 /* prototypes for global functions */
36 
37 char   *load_file(const char *path);
38 char   *resolve_path(const char *path);
39 char   *skip_leading(char *str, const char *pattern);
40 int		validate_path(const char *path);
41 void	cleanup(void);
42 
43 /*****************************************************************************/
44 
cleanup(void)45 void cleanup(void)
46 /*
47 	final cleanup routine, to release all of the memory we allocated
48 
49 	registered with atexit(3) during initialization, to be called
50 	automatically when program exits
51 */
52 {
53 	if (index_buf != NULL)
54 		free(index_buf);
55 
56 	if (template_buf_top != NULL)
57 		free(template_buf_top);
58 
59 	if (template_buf_category != NULL)
60 		free(template_buf_category);
61 
62 	if (template_buf_port != NULL)
63 		free(template_buf_port);
64 
65 	if (index_categories != NULL)
66 		free(index_categories);
67 }
68 
69 /*****************************************************************************/
70 
load_file(const char * path)71 char *load_file(const char *path)
72 /*
73 	load the file named by 'path' into a dynamically allocated buffer
74 	(used for the index and template files)
75 
76 	buffer will have an extra nul character appended to mark end-of-file
77 
78 	returns a pointer to the buffer on success, or NULL on failure
79  */
80 {
81 	char   *buffer;		/* pointer to memory obtained via malloc(3) */
82 	int		fd;			/* file descriptor for open() and close() */
83 	size_t	size;		/* file's size, as returned by stat(2) */
84 	struct	stat sb;	/* will hold the results from stat(2) */
85 
86 	/* get the file's size */
87 	if (stat(path, &sb) == -1)
88 	{
89 		perror(path);
90 		return NULL;
91 	}
92 
93 	/* cast to squelch compiler warning */
94 	size = (unsigned) sb.st_size;
95 
96 	/* allocate (size + 1) bytes, so we can mark end of file
97 		with an extra nul character
98 	 */
99 	if ((buffer = malloc(size + 1)) == NULL)
100 	{
101 		perror("malloc failure");
102 		return NULL;
103 	}
104 
105 	/* display an info blurb at verbosity levels >= 2) */
106 	if (verbose > 1)
107 		fprintf(stderr, "Loading %s\n", path);
108 
109 	/* open and read the entire file into memory */
110 	if ((fd = open(path, O_RDONLY|O_DIRECT)) == -1)
111 	{
112 		perror(path);
113 		return NULL;
114 	}
115 
116 	/* another cast to squelch compiler warning */
117 	if (read(fd, buffer, size) != (ssize_t) size)
118 	{
119 		perror(path);
120 		close(fd);
121 		return NULL;
122 	}
123 
124 	close(fd);
125 
126 	/* add an extra nul to mark end of buffer */
127 	buffer[size] = '\0';
128 
129 	return buffer;
130 }
131 
132 /*****************************************************************************/
133 
skip_leading(char * str,const char * pattern)134 char *skip_leading(char *str, const char *pattern)
135 /*
136 	skip over 'pattern' at the beginning of 'str'
137 
138 	returns a pointer to the location in 'str' immediately following 'pattern',
139 	or 'str' if it does not begin with 'pattern'.  if the two strings are
140 	exactly equal, returns a pointer to the nul end-of-string marker for 'str'
141 
142 	(adding the 'const' qualifier to str is just more trouble than it's worth;
143 	all it does is cause a couple of compiler warnings below; not like we're
144 	gonna be messing it up anyway, so why bother?)
145  */
146 {
147     char *ptr = str;
148 
149 	while (*pattern)
150 	{
151 		if (*ptr != *pattern)
152 			break;
153 		++ptr;
154 		++pattern;
155 	}
156 
157 	/* (*pattern != '\0') means no match */
158 	return (*pattern) ? str : ptr;
159 }
160 
161 /******************************************************************************/
162 
validate_path(const char * path)163 int validate_path(const char *path)
164 /*
165 	verify that path exists and is a directory
166 
167 	returns 0 on success, or -1
168  */
169 {
170 	struct	stat sb;				/* information returned from stat() */
171 
172 	/* call stat to get info for path */
173 	if (stat(path, &sb) == -1)
174 	{
175 		perror(path);
176 		return -1;
177 	}
178 
179 	/* make sure it's a directory */
180 	if (!S_ISDIR(sb.st_mode))
181 	{
182 		fprintf(stderr, "%s is not a directory\n", path);
183 		return -1;
184 	}
185 
186 	return 0;
187 }
188 
189 /******************************************************************************/
190 
resolve_path(const char * path)191 char *resolve_path(const char *path)
192 /*
193 	Resolve the pathname pointed to by 'path' to a fully qualified pathname
194 
195 	As a convenience, let any relative path be relative to ports_dir,
196 	unless the user selected the "-n" option, in which case path will be
197 	relative to the current working directory
198 
199 	Returns a pointer to the fully qualified pathname returned by realpath(3),
200 	or NULL on error
201 
202 	Uses realpath(3) to do the grunt work
203 
204 	Caller should free() the returned pointer after it is no longer needed
205 */
206 {
207 	char *real_path;			/* result from realpath(3) */
208 	char pathbuf[PATH_MAX];		/* temporary buffer */
209 
210 	if (*path == '/')			/* absolute path, use "as is" (after running it
211 									through realpath() below
212 								 */
213 	{
214 		strcpy(pathbuf, path);
215 	}
216 	else if (*path == '.')		/* pathnames beginning with "." or ".." are
217 									always relative to the current working
218 									directory, regardless of the setting of the
219 									normal_paths flag
220 								 */
221 	{
222 		if (getcwd(pathbuf, (size_t)PATH_MAX) == NULL)
223 		{
224 			perror("getcwd");
225 			return NULL;
226 		}
227 		else
228 		{
229 			/* append path to current working directory in pathbuf */
230 			strcat(pathbuf, "/");
231 			strcat(pathbuf, path);
232 		}
233 	}
234 	else						/* relative path */
235 	{
236 		if (!normal_paths)		/* append path to ports_dir/ in pathbuf */
237 		{
238 			strcpy(pathbuf, ports_dir_plus);
239 			strcat(pathbuf, path);
240 		}
241 		else					/* append path to current working directory */
242 		{
243 			if (getcwd(pathbuf, (size_t)PATH_MAX) == NULL)
244 			{
245 				perror("getcwd");
246 				return NULL;
247 			}
248 			else
249 			{
250 				/* append path to current working directory in pathbuf */
251 				strcat(pathbuf, "/");
252 				strcat(pathbuf, path);
253 			}
254 		}
255 	}
256 
257 	/* Now run what we have through realpath() to "smooth it out"
258 		Note: paths returned by realpath will *never* have a trailing slash
259 	 */
260 	if ((real_path = realpath(pathbuf, NULL)) == NULL)
261 	{
262 		perror(pathbuf);
263 		return NULL;
264 	}
265 
266 	/* it's still possible that the last element of real_path doesn't actually
267 		exist (see realpath(3)), so do one more check, so that we'll produce
268 		consistent results for any non-existent paths ("no such file or
269 		directory"), otherwise the path may be passed along for further
270 		processing, which we don't want to happen
271 	 */
272 	if (validate_path(real_path) != 0)
273 		return NULL;
274 
275 	return real_path;
276 }
277 
278