1 #pragma ident	"%Z%%M%	%I%	%E% SMI"
2 /*
3  * prof_file.c ---- routines that manipulate an individual profile file.
4  */
5 
6 #include <autoconf.h>
7 
8 #include <stdio.h>
9 #ifdef HAVE_STDLIB_H
10 #include <stdlib.h>
11 #endif
12 #ifdef HAVE_UNISTD_H
13 #include <unistd.h>
14 #endif
15 #include <string.h>
16 
17 #include "prof_int.h"
18 
19 #ifndef NO_SYS_TYPES_H
20 #include <sys/types.h>
21 #endif
22 #ifndef NO_SYS_STAT_H
23 #include <sys/stat.h>
24 #endif
25 #include <errno.h>
26 
27 
28 #if defined(_MSDOS) || defined(_WIN32)
29 #include <io.h>
30 #define HAVE_STAT
31 #define stat _stat
32 #endif
33 
34 #ifndef PROFILE_USES_PATHS
35 #include <FSp_fopen.h>
36 
37 static OSErr GetMacOSTempFilespec (
38 	const	FSSpec*	inFilespec,
39 			FSSpec*	outFilespec);
40 
41 #endif
42 
43 static int rw_access(filespec)
44 	profile_filespec_t filespec;
45 {
46 #ifdef HAVE_ACCESS
47 	if (access(filespec, W_OK) == 0)
48 		return 1;
49 	else
50 		return 0;
51 #else
52 	/*
53 	 * We're on a substandard OS that doesn't support access.  So
54 	 * we kludge a test using stdio routines, and hope fopen
55 	 * checks the r/w permissions.
56 	 */
57 	FILE	*f;
58 
59 #ifdef PROFILE_USES_PATHS
60 	f = fopen(filespec, "r+");
61 #else
62 	f = FSp_fopen(&filespec, "r+");
63 #endif
64 	if (f) {
65 		fclose(f);
66 		return 1;
67 	}
68 	return 0;
69 #endif
70 }
71 
72 errcode_t profile_open_file(filespec, ret_prof)
73 	const_profile_filespec_t filespec;
74 	prf_file_t *ret_prof;
75 {
76 	prf_file_t	prf;
77 	errcode_t	retval;
78 	char		*home_env = 0;
79 	int		len;
80 
81 	prf = (prf_file_t) malloc(sizeof(struct _prf_file_t));
82 	if (!prf)
83 		return ENOMEM;
84 	memset(prf, 0, sizeof(struct _prf_file_t));
85 
86 #ifndef macintosh
87 	len = strlen(filespec)+1;
88 	if (filespec[0] == '~' && filespec[1] == '/') {
89 		home_env = getenv("HOME");
90 		if (home_env)
91 			len += strlen(home_env);
92 	}
93 	prf->filespec = (char *) malloc(len);
94 	if (!prf->filespec) {
95 		free(prf);
96 		return ENOMEM;
97 	}
98 	if (home_env) {
99 		strcpy(prf->filespec, home_env);
100 		strcat(prf->filespec, filespec+1);
101 	} else
102 		strcpy(prf->filespec, filespec);
103 	prf->magic = PROF_MAGIC_FILE;
104 #else
105 	prf->filespec = filespec;
106 	prf->magic = PROF_MAGIC_FILE;
107 #endif
108 
109 	retval = profile_update_file(prf);
110 	if (retval) {
111 		profile_close_file(prf);
112 		return retval;
113 	}
114 
115 	*ret_prof = prf;
116 	return 0;
117 }
118 
119 errcode_t profile_update_file(prf)
120 	prf_file_t prf;
121 {
122 	errcode_t retval;
123 #ifdef HAVE_STAT
124 	struct stat st;
125 #endif
126 	FILE *f;
127 
128 #ifdef HAVE_STAT
129 	if (stat(prf->filespec, &st))
130 		return errno;
131 	if (st.st_mtime == prf->timestamp)
132 		return 0;
133 	if (prf->root) {
134 		profile_free_node(prf->root);
135 		prf->root = 0;
136 	}
137 	if (prf->comment) {
138 		free(prf->comment);
139 		prf->comment = 0;
140 	}
141 #else
142 	/*
143 	 * If we don't have the stat() call, assume that our in-core
144 	 * memory image is correct.  That is, we won't reread the
145 	 * profile file if it changes.
146 	 */
147 	if (prf->root)
148 		return 0;
149 #endif
150 	errno = 0;
151 #ifdef PROFILE_USES_PATHS
152 	f = fopen(prf->filespec, "r");
153 #else
154 	f = FSp_fopen (&prf->filespec, "r");
155 #endif
156 	if (f == NULL) {
157 		retval = errno;
158 		if (retval == 0)
159 			retval = ENOENT;
160 		return retval;
161 	}
162 	prf->upd_serial++;
163 	prf->flags = 0;
164 	if (rw_access(prf->filespec))
165 		prf->flags |= PROFILE_FILE_RW;
166 	retval = profile_parse_file(f, &prf->root);
167 	fclose(f);
168 	if (retval)
169 		return retval;
170 #ifdef HAVE_STAT
171 	prf->timestamp = st.st_mtime;
172 #endif
173 	return 0;
174 }
175 
176 #ifndef PROFILE_USES_PATHS
177 OSErr GetMacOSTempFilespec (
178 	const	FSSpec*	inFileSpec,
179 			FSSpec*	outFileSpec)
180 {
181 	OSErr	err;
182 
183 	err = FindFolder (inFileSpec -> vRefNum, kTemporaryFolderType,
184 		kCreateFolder, &(outFileSpec -> vRefNum), &(outFileSpec -> parID));
185 	if (err != noErr)
186 		return err;
187 
188 	BlockMoveData (&(inFileSpec -> name), &(outFileSpec -> name), StrLength (inFileSpec -> name) + 1);
189 	return noErr;
190 }
191 #endif
192 
193 
194 errcode_t profile_flush_file(prf)
195 	prf_file_t prf;
196 {
197 	FILE		*f;
198 	profile_filespec_t new_file;
199 	profile_filespec_t old_file;
200 	errcode_t	retval = 0;
201 
202 	if (!prf || prf->magic != PROF_MAGIC_FILE)
203 		return PROF_MAGIC_FILE;
204 
205 	if ((prf->flags & PROFILE_FILE_DIRTY) == 0)
206 		return 0;
207 
208 	retval = ENOMEM;
209 
210 #ifdef PROFILE_USES_PATHS
211 	new_file = old_file = 0;
212 	new_file = (char *) malloc(strlen(prf->filespec) + 5);
213 	if (!new_file)
214 		goto errout;
215 	old_file = (char *) malloc(strlen(prf->filespec) + 5);
216 	if (!old_file)
217 		goto errout;
218 
219 	sprintf(new_file, "%s.$$$", prf->filespec);
220 	sprintf(old_file, "%s.bak", prf->filespec);
221 
222 	errno = 0;
223 
224 	f = fopen(new_file, "w");
225 #else
226 	/* On MacOS, we do this by writing to a new file and then atomically
227 	swapping the files with a file system call */
228 	GetMacOSTempFilespec (&prf->filespec, &new_file);
229 	f = FSp_fopen (&new_file, "w");
230 #endif
231 
232 	if (!f) {
233 		retval = errno;
234 		if (retval == 0)
235 			retval = PROF_FAIL_OPEN;
236 		goto errout;
237 	}
238 
239 	profile_write_tree_file(prf->root, f);
240 	if (fclose(f) != 0) {
241 		retval = errno;
242 		goto errout;
243 	}
244 
245 #ifdef PROFILE_USES_PATHS
246 	unlink(old_file);
247 	if (rename(prf->filespec, old_file)) {
248 		retval = errno;
249 		goto errout;
250 	}
251 	if (rename(new_file, prf->filespec)) {
252 		retval = errno;
253 		rename(old_file, prf->filespec); /* back out... */
254 		goto errout;
255 	}
256 #else
257 	{
258 		OSErr err = FSpExchangeFiles (&prf->filespec, &new_file);
259 		if (err != noErr) {
260 			retval = ENFILE;
261 			goto errout;
262 		}
263 		FSpDelete (&new_file);
264 	}
265 #endif
266 
267 
268 	prf->flags = 0;
269 	if (rw_access(prf->filespec))
270 		prf->flags |= PROFILE_FILE_RW;
271 	retval = 0;
272 
273 errout:
274 #ifdef PROFILE_USES_PATHS
275 	if (new_file)
276 		free(new_file);
277 	if (old_file)
278 		free(old_file);
279 #endif
280 	return retval;
281 }
282 
283 
284 void profile_free_file(prf)
285 	prf_file_t prf;
286 {
287 #ifdef PROFILE_USES_PATHS
288 	if (prf->filespec)
289 		free(prf->filespec);
290 #endif
291 	if (prf->root)
292 		profile_free_node(prf->root);
293 	if (prf->comment)
294 		free(prf->comment);
295 	prf->magic = 0;
296 	free(prf);
297 
298 	return;
299 }
300 
301 errcode_t profile_close_file(prf)
302 	prf_file_t prf;
303 {
304 	errcode_t	retval;
305 
306 	retval = profile_flush_file(prf);
307 	if (retval)
308 		return retval;
309 	profile_free_file(prf);
310 	return 0;
311 }
312 
313