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