1 |
/* ASCEND modelling environment |
2 |
Copyright (C) 2006 Carnegie Mellon University |
3 |
|
4 |
This program is free software; you can redistribute it and/or modify |
5 |
it under the terms of the GNU General Public License as published by |
6 |
the Free Software Foundation; either version 2, or (at your option) |
7 |
any later version. |
8 |
|
9 |
This program is distributed in the hope that it will be useful, |
10 |
but WITHOUT ANY WARRANTY; without even the implied warranty of |
11 |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
12 |
GNU General Public License for more details. |
13 |
|
14 |
You should have received a copy of the GNU General Public License |
15 |
along with this program; if not, write to the Free Software |
16 |
Foundation, Inc., 59 Temple Place - Suite 330, |
17 |
Boston, MA 02111-1307, USA. |
18 |
*//** |
19 |
@file |
20 |
Platform-agnostic filesystem path manipulation functions. |
21 |
By John Pye. |
22 |
*/ |
23 |
|
24 |
#include <string.h> |
25 |
#include <malloc.h> |
26 |
#include <stdio.h> |
27 |
#include <ctype.h> |
28 |
|
29 |
#include "ospath.h" |
30 |
|
31 |
// to test this code, 'gcc -DTEST ospath.c && ./a' |
32 |
|
33 |
#if defined(WIN32) || defined(__WIN32) || defined(_MSC_VER) |
34 |
# ifndef __WIN32__ |
35 |
# define __WIN32__ |
36 |
# endif |
37 |
#endif |
38 |
|
39 |
#define VERBOSE |
40 |
|
41 |
#if !defined(TEST) && !defined(VERBOSE) |
42 |
# define NDEBUG |
43 |
#endif |
44 |
|
45 |
//#define TRY_GETPWUID |
46 |
|
47 |
#define DO_FIXSLASHES |
48 |
|
49 |
#ifndef NDEBUG |
50 |
# include <assert.h> |
51 |
# define M(MSG) fprintf(stderr,"%s:%d: (%s) %s\n",__FILE__,__LINE__,__FUNCTION__,MSG);fflush(stderr);fflush(stderr) |
52 |
# define MC(CLR,MSG) fprintf(stderr,"%c[%sm%s:%d: (%s) %s%c[0m\n",27,CLR,__FILE__,__LINE__,__FUNCTION__,MSG,27);fflush(stderr) |
53 |
# define MM(MSG) MC("34",MSG) |
54 |
# define X(VAR) fprintf(stderr,"%s:%d: (%s) %s=%s\n",__FILE__,__LINE__,__FUNCTION__,#VAR,VAR);fflush(stderr) |
55 |
# define C(VAR) fprintf(stderr,"%s:%d: (%s) %s=%c\n",__FILE__,__LINE__,__FUNCTION__,#VAR,VAR);fflush(stderr) |
56 |
# define V(VAR) fprintf(stderr,"%s:%d: (%s) %s=%d\n",__FILE__,__LINE__,__FUNCTION__,#VAR,(VAR));fflush(stderr) |
57 |
# define D(VAR) fprintf(stderr,"%s:%d: (%s) %s=",__FILE__,__LINE__,__FUNCTION__,#VAR);ospath_debug(VAR);fflush(stderr) |
58 |
# define DD(VAR) fprintf(stderr,"%c[34;1m%s:%d: (%s)%c[0m %s=",27,__FILE__,__LINE__,__FUNCTION__,27,#VAR);ospath_debug(VAR);fflush(stderr) |
59 |
#else |
60 |
# include <assert.h> |
61 |
# define M(MSG) ((void)0) |
62 |
# define MC(CLR,MSG) ((void)0) |
63 |
# define X(VAR) ((void)0) |
64 |
# define C(VAR) ((void)0) |
65 |
# define V(VAR) ((void)0) |
66 |
# define D(VAR) ((void)0) |
67 |
# define DD(VAR) ((void)0) |
68 |
# define MM(VAR) ((void)0) |
69 |
#endif |
70 |
|
71 |
#if defined(__WIN32__) && !defined(__MINGW32__) |
72 |
# define STRCPY strcpy |
73 |
# define STRNCPY(dest,src,n) strncpy_s(dest,n,src,n) |
74 |
# define STRCAT strcat |
75 |
# define STRNCAT strncat |
76 |
# define STRTOK(STR,PAT,VAR) strtok_s(STR,PAT,&VAR) |
77 |
# define STRTOKVAR(VAR) char *VAR |
78 |
# define GETCWD getcwd |
79 |
# define GETENV(VAR) getenv(VAR) |
80 |
#else |
81 |
# define STRCPY strcpy |
82 |
# define STRNCPY(dest,src,n) strncpy(dest,src,n) |
83 |
# define STRCAT strcat |
84 |
# define STRNCAT strncat |
85 |
# define STRTOK(STR,PAT,VAR) strtok(STR,PAT) |
86 |
# define STRTOKVAR(VAR) ((void)0) |
87 |
# define GETCWD getcwd |
88 |
# define GETENV(VAR) getenv(VAR) |
89 |
#endif |
90 |
|
91 |
// PATH_MAX is in ospath.h |
92 |
#define DRIVEMAX 3 |
93 |
#define LISTMAX 256 |
94 |
|
95 |
#ifdef __WIN32__ /* && !defined(__MINGW32__) */ |
96 |
# define WINPATHS |
97 |
#endif |
98 |
|
99 |
struct FilePath{ |
100 |
char path[PATH_MAX]; /// the string version of the represented POSIX path |
101 |
|
102 |
#ifdef WINPATHS |
103 |
char drive[DRIVEMAX]; /// the drive the path resides on (field is absent in POSIX systems) |
104 |
#endif |
105 |
}; |
106 |
|
107 |
#include <string.h> |
108 |
|
109 |
#if !defined(MALLOC) && !defined(FREE) |
110 |
# define MALLOC malloc |
111 |
# define FREE free |
112 |
#endif |
113 |
|
114 |
#define E(MSG) fprintf(stderr,"%s:%d: (%s) ERROR: %s\n",__FILE__,__LINE__,__FUNCTION__,MSG) |
115 |
|
116 |
#ifdef DO_FIXSLASHES |
117 |
void ospath_fixslash(char *path); |
118 |
#endif |
119 |
|
120 |
struct FilePath *ospath_getcwd(); |
121 |
|
122 |
void ospath_copy(struct FilePath *dest, struct FilePath *src); |
123 |
|
124 |
|
125 |
#ifdef WINPATHS |
126 |
/** |
127 |
This function splits out the drive letter in the path string, thus completing |
128 |
the correct construction of a FilePath object under Win32. |
129 |
*/ |
130 |
void ospath_extractdriveletter(struct FilePath *); |
131 |
#endif |
132 |
|
133 |
#ifdef WINPATHS |
134 |
# define PATH_SEPARATOR_STR "\\" |
135 |
# define PATH_SEPARATOR_CHAR '\\' |
136 |
# define PATH_LISTSEP_CHAR ';' |
137 |
# define PATH_LISTSEP_STR ";" |
138 |
# define PATH_WRONGSLASH_CHAR '/' |
139 |
# define PATH_WRONGSLASH_STR "/" |
140 |
#else |
141 |
# define PATH_SEPARATOR_STR "/" |
142 |
# define PATH_SEPARATOR_CHAR '/' |
143 |
# define PATH_LISTSEP_CHAR ':' |
144 |
# define PATH_LISTSEP_STR ":" |
145 |
# define PATH_WRONGSLASH_CHAR '\\' |
146 |
# define PATH_WRONGSLASH_STR "\\" |
147 |
#endif |
148 |
|
149 |
/** |
150 |
Create a new path structure from a string |
151 |
*/ |
152 |
struct FilePath *ospath_new(const char *path){ |
153 |
struct FilePath *fp; |
154 |
fp = ospath_new_noclean(path); |
155 |
|
156 |
#ifdef DO_FIXSLASHES |
157 |
ospath_fixslash(fp->path); |
158 |
#endif |
159 |
|
160 |
ospath_cleanup(fp); |
161 |
|
162 |
//D(fp); |
163 |
|
164 |
return fp; |
165 |
} |
166 |
|
167 |
|
168 |
|
169 |
/// Create but with no 'cleanup', and no fixing of / vs \. |
170 |
struct FilePath *ospath_new_noclean(const char *path){ |
171 |
struct FilePath *fp = (struct FilePath *)MALLOC(sizeof(struct FilePath)); |
172 |
STRNCPY(fp->path,path,PATH_MAX); |
173 |
assert(strcmp(fp->path,path)==0); |
174 |
#ifdef WINPATHS |
175 |
//X(fp->path); |
176 |
ospath_extractdriveletter(fp); |
177 |
#endif |
178 |
|
179 |
return fp; |
180 |
} |
181 |
|
182 |
struct FilePath *ospath_new_expand_env(const char *path, GetEnvFn *getenvptr){ |
183 |
struct FilePath *fp; |
184 |
|
185 |
char *pathnew = env_subst(path,getenvptr); |
186 |
fp = ospath_new(pathnew); |
187 |
FREE(pathnew); |
188 |
|
189 |
return fp; |
190 |
} |
191 |
|
192 |
|
193 |
/** |
194 |
This function will serve to allow #include-style file paths |
195 |
to be specified with platform-independent forward slashes then |
196 |
translated into the local filesystem format for subsequent use. |
197 |
|
198 |
This method should be identical to ospath_new on posix, right? |
199 |
|
200 |
@NOTE: on windows, we also want: |
201 |
C:dir/file --> c:$PWD\dir\file |
202 |
e:/hello --> e:\hello |
203 |
here/i/am --> here\i\am |
204 |
|
205 |
@NOTE: |
206 |
A path-search function should create full file paths by |
207 |
appending relative file to each component of the search path |
208 |
then performing a callback on each one to determine if the |
209 |
match is OK or not. |
210 |
*/ |
211 |
struct FilePath *ospath_new_from_posix(const char *posixpath){ |
212 |
struct FilePath *fp; |
213 |
fp = ospath_new_noclean(posixpath); |
214 |
|
215 |
#ifdef DO_FIXSLASHES |
216 |
ospath_fixslash(fp->path); |
217 |
#endif |
218 |
|
219 |
//X(fp->path); |
220 |
|
221 |
ospath_cleanup(fp); |
222 |
|
223 |
return fp; |
224 |
} |
225 |
|
226 |
void ospath_free(struct FilePath *fp){ |
227 |
FREE(fp); |
228 |
} |
229 |
|
230 |
void ospath_free_str(char *str){ |
231 |
FREE(str); |
232 |
} |
233 |
|
234 |
|
235 |
#ifdef DO_FIXSLASHES |
236 |
void ospath_fixslash(char *path){ |
237 |
|
238 |
char *p; |
239 |
char temp[PATH_MAX]; |
240 |
int startslash; |
241 |
int endslash; |
242 |
STRTOKVAR(nexttok); |
243 |
|
244 |
STRNCPY(temp,path,PATH_MAX); |
245 |
|
246 |
//X(path); |
247 |
|
248 |
startslash = (strlen(temp) > 0 && temp[0] == PATH_WRONGSLASH_CHAR); |
249 |
endslash = (strlen(temp) > 1 && temp[strlen(temp) - 1] == PATH_WRONGSLASH_CHAR); |
250 |
|
251 |
//V(startslash); |
252 |
//V(endslash); |
253 |
|
254 |
// reset fp->path as required. |
255 |
STRNCPY(path, (startslash ? PATH_SEPARATOR_STR : ""), PATH_MAX); |
256 |
|
257 |
//M("STARTING STRTOK"); |
258 |
for(p = STRTOK(temp, PATH_WRONGSLASH_STR,nexttok); |
259 |
p!=NULL; |
260 |
p = STRTOK(NULL,PATH_WRONGSLASH_STR,nexttok) |
261 |
){ |
262 |
// add a separator if we've already got some stuff |
263 |
if( |
264 |
strlen(path) > 0 |
265 |
&& path[strlen(path) - 1] != PATH_SEPARATOR_CHAR |
266 |
){ |
267 |
STRCAT(path,PATH_SEPARATOR_STR); |
268 |
} |
269 |
|
270 |
STRCAT(path,p); |
271 |
} |
272 |
//M("FINISHED STRTOK"); |
273 |
|
274 |
// put / on end as required, according to what the starting path had |
275 |
if(endslash && (strlen(path) > 0 ? (path[strlen(path) - 1] != PATH_SEPARATOR_CHAR) : 1)) |
276 |
{ |
277 |
//M("adding endslash!"); |
278 |
|
279 |
STRCAT(path, PATH_SEPARATOR_STR); |
280 |
} |
281 |
|
282 |
//X(path); |
283 |
} |
284 |
#endif |
285 |
|
286 |
struct FilePath *ospath_getcwd(void){ |
287 |
struct FilePath *fp = (struct FilePath *)MALLOC(sizeof(struct FilePath)); |
288 |
char *cwd; |
289 |
|
290 |
// get current working directory |
291 |
cwd = (char *)GETCWD(NULL, 0); |
292 |
|
293 |
// create new path with resolved working directory |
294 |
fp = ospath_new_noclean(cwd != NULL ? cwd : "."); |
295 |
|
296 |
D(fp); |
297 |
|
298 |
return fp; |
299 |
} |
300 |
|
301 |
/** |
302 |
Use getenv() function to retrieve HOME path, or if not set, use |
303 |
the password database and try to retrieve it that way (???) |
304 |
*/ |
305 |
struct FilePath *ospath_gethomepath(void){ |
306 |
|
307 |
const char *pfx = (const char *)getenv("HOME"); |
308 |
struct FilePath *fp; |
309 |
|
310 |
#ifndef __WIN32__ |
311 |
# ifdef TRY_GETPWUID |
312 |
struct passwd *pw; |
313 |
|
314 |
if(pfx==NULL){ |
315 |
pw = (struct passwd*)getpwuid(getuid()); |
316 |
|
317 |
if(pw){ |
318 |
pfx = pw->pw_dir; |
319 |
} |
320 |
} |
321 |
# endif |
322 |
#endif |
323 |
|
324 |
// create path object from HOME, but don't compress it |
325 |
// (because that would lead to an infinite loop) |
326 |
fp = ospath_new_noclean(pfx ? pfx : ""); |
327 |
|
328 |
#ifdef DO_FIXSLASHES |
329 |
ospath_fixslash(fp->path); |
330 |
#endif |
331 |
|
332 |
return fp; |
333 |
} |
334 |
|
335 |
#ifdef WINPATHS |
336 |
void ospath_extractdriveletter(struct FilePath *fp) |
337 |
{ |
338 |
char *p; |
339 |
//M("SOURCE"); |
340 |
//X(fp->path); |
341 |
//fprintf(stderr,"CHAR 1 = %c\n",fp->path[1]); |
342 |
|
343 |
// extract the drive the path resides on... |
344 |
if(strlen(fp->path) >= 2 && fp->path[1] == ':') |
345 |
{ |
346 |
STRNCPY(fp->drive,fp->path,2); |
347 |
fp->drive[2]='\0'; |
348 |
for(p=fp->path+2; *p!='\0'; ++p){ |
349 |
*(p-2)=*p; |
350 |
} |
351 |
*(p-2)='\0'; |
352 |
}else{ |
353 |
STRNCPY(fp->drive,"",DRIVEMAX); |
354 |
} |
355 |
//M("RESULT"); |
356 |
//X(fp->path); |
357 |
//X(fp->drive); |
358 |
} |
359 |
#endif |
360 |
|
361 |
void ospath_cleanup(struct FilePath *fp){ |
362 |
char path[PATH_MAX]; |
363 |
char *p; |
364 |
struct FilePath *home; |
365 |
struct FilePath *working; |
366 |
struct FilePath *parent; |
367 |
STRTOKVAR(nexttok); |
368 |
|
369 |
// compress the path, and resolve ~ |
370 |
int startslash = (strlen(fp->path) > 0 && fp->path[0] == PATH_SEPARATOR_CHAR); |
371 |
int endslash = (strlen(fp->path) > 1 && fp->path[strlen(fp->path) - 1] == PATH_SEPARATOR_CHAR); |
372 |
|
373 |
//fprintf(stderr,"FS ON START = %d\n",startslash); |
374 |
//fprintf(stderr,"FS ON END = %d\n",endslash); |
375 |
//fprintf(stderr,"FIRST CHAR = %c\n",fp->path[0]); |
376 |
|
377 |
home = ospath_gethomepath(); |
378 |
|
379 |
// create a copy of fp->path. |
380 |
STRCPY(path, fp->path); |
381 |
|
382 |
// reset fp->path as required. |
383 |
STRCPY(fp->path, (startslash ? PATH_SEPARATOR_STR : "")); |
384 |
|
385 |
D(fp); |
386 |
X(path); |
387 |
|
388 |
// split path into it tokens, using STRTOK which is NOT reentrant |
389 |
// so be careful! |
390 |
|
391 |
//M("STARTING STRTOK"); |
392 |
for(p = STRTOK(path, PATH_SEPARATOR_STR,nexttok); |
393 |
p!=NULL; |
394 |
p = STRTOK(NULL,PATH_SEPARATOR_STR,nexttok) |
395 |
){ |
396 |
//M("NEXT TOKEN"); |
397 |
//X(p); |
398 |
//X(path+strlen(p)+1); |
399 |
if(strcmp(p, "~")==0){ |
400 |
|
401 |
if(p == path){ // check that the ~ is the first character in the path |
402 |
if(ospath_isvalid(home)){ |
403 |
ospath_copy(fp,home); |
404 |
continue; |
405 |
}else{ |
406 |
E("HOME does not resolve to valid path"); |
407 |
} |
408 |
}else{ |
409 |
E("A tilde (~) present as a component in a file path must be at the start!"); |
410 |
} |
411 |
}else if(strcmp(p, ".") == 0){ |
412 |
|
413 |
if(p==path){// start of path: |
414 |
M("EXPANDING LEADING '.' IN PATH"); |
415 |
X(path); |
416 |
|
417 |
working = ospath_getcwd(); |
418 |
|
419 |
D(working); |
420 |
#ifdef WINPATHS |
421 |
X(working->drive); |
422 |
#endif |
423 |
X(p); |
424 |
X(path); |
425 |
|
426 |
ospath_copy(fp,working); |
427 |
|
428 |
|
429 |
D(fp); |
430 |
X(p); |
431 |
//X(path+strlen(p)+1); |
432 |
|
433 |
//ospath_free(working); |
434 |
continue; |
435 |
}else{// later in the path: just skip it |
436 |
M("SKIPPING '.' IN PATH"); |
437 |
continue; |
438 |
} |
439 |
|
440 |
}else if(strcmp(p, "..") == 0){ |
441 |
M("GOING TO PARENT"); |
442 |
parent = ospath_getparent(fp); |
443 |
if(ospath_isvalid(parent)){ |
444 |
ospath_copy(fp,parent); |
445 |
} |
446 |
//ospath_free(parent); |
447 |
continue; |
448 |
} |
449 |
|
450 |
// add a separator if we've already got some stuff |
451 |
if( |
452 |
strlen(fp->path) > 0 |
453 |
&& fp->path[strlen(fp->path) - 1] != PATH_SEPARATOR_CHAR |
454 |
){ |
455 |
STRCAT(fp->path,PATH_SEPARATOR_STR); |
456 |
} |
457 |
|
458 |
// add the present path component |
459 |
STRCAT(fp->path, p); |
460 |
} |
461 |
//M("FINISHED STRTOK"); |
462 |
|
463 |
// put / on end as required, according to what the starting path had |
464 |
if(endslash && (strlen(fp->path) > 0 ? (fp->path[strlen(fp->path) - 1] != PATH_SEPARATOR_CHAR) : 1)) |
465 |
{ |
466 |
STRCAT(fp->path, PATH_SEPARATOR_STR); |
467 |
} |
468 |
} |
469 |
|
470 |
|
471 |
int ospath_isvalid(struct FilePath *fp){ |
472 |
//if(fp==NULL) return 0; |
473 |
return strlen(fp->path) > 0 ? 1 : 0; |
474 |
} |
475 |
|
476 |
|
477 |
char *ospath_str(struct FilePath *fp){ |
478 |
char *s; |
479 |
#ifdef WINPATHS |
480 |
s = (char *)MALLOC(sizeof(char)*(strlen(fp->drive)+strlen(fp->path) +1) ); |
481 |
STRCPY(s,fp->drive); |
482 |
STRCAT(s,fp->path); |
483 |
#else |
484 |
s = MALLOC(sizeof(char)*(strlen(fp->path)+1)); |
485 |
STRCPY(s,fp->path); |
486 |
#endif |
487 |
return s; |
488 |
} |
489 |
|
490 |
void ospath_strcpy(struct FilePath *fp, char *dest, int destsize){ |
491 |
#ifdef WINPATHS |
492 |
STRNCPY(dest,fp->drive,destsize); |
493 |
STRNCAT(dest,fp->path,destsize-strlen(dest)); |
494 |
#else |
495 |
STRNCPY(dest,fp->path,destsize); |
496 |
#endif |
497 |
} |
498 |
|
499 |
|
500 |
void ospath_strcat(struct FilePath *fp, char *dest, int destsize){ |
501 |
int remaining = destsize - strlen(dest); |
502 |
V(remaining); |
503 |
#ifdef WINPATHS |
504 |
STRNCAT(dest,fp->drive,remaining); |
505 |
STRNCAT(dest,fp->path,remaining-strlen(dest)); |
506 |
#else |
507 |
STRNCAT(dest,fp->path,remaining); |
508 |
#endif |
509 |
D(fp); |
510 |
} |
511 |
|
512 |
void ospath_fwrite(struct FilePath *fp, FILE *dest){ |
513 |
#ifdef WINPATHS |
514 |
fprintf(dest,"%s%s",fp->drive,fp->path); |
515 |
#else |
516 |
fprintf(dest,"%s",fp->path); |
517 |
#endif |
518 |
} |
519 |
|
520 |
unsigned ospath_length(struct FilePath *fp){ |
521 |
#ifdef WINPATHS |
522 |
// we've already validated this path, so it's on to just add it up |
523 |
// (unless someone has been tinkering with the internal structure here) |
524 |
return (unsigned) (strlen(fp->drive) + strlen(fp->path)); |
525 |
#else |
526 |
return (unsigned) (strlen(fp->path)); |
527 |
#endif |
528 |
} |
529 |
|
530 |
struct FilePath *ospath_getparent(struct FilePath *fp) |
531 |
{ |
532 |
int length; |
533 |
int offset; |
534 |
char *pos; |
535 |
int len1; |
536 |
char sub[PATH_MAX]; |
537 |
struct FilePath *fp1, *fp2; |
538 |
|
539 |
D(fp); |
540 |
|
541 |
if(strlen(fp->path) == 0){ |
542 |
fp1 = ospath_getcwd(); |
543 |
fp2 = ospath_getparent(fp1); |
544 |
ospath_free(fp1); |
545 |
return fp2; |
546 |
}else if(ospath_isroot(fp)){ |
547 |
// stay at root |
548 |
return ospath_new("/"); |
549 |
} |
550 |
|
551 |
// reverse find a / ignoring the end / if it exists. |
552 |
/// FIXME |
553 |
length = strlen(fp->path); |
554 |
offset = ( |
555 |
fp->path[length - 1] == PATH_SEPARATOR_CHAR // last char is slash? |
556 |
&& length > 1 // and more than just leading slash... |
557 |
) ? length - 1 : length; // then remove last char |
558 |
|
559 |
for(pos = fp->path + offset - 1; *pos!=PATH_SEPARATOR_CHAR && pos>=fp->path; --pos){ |
560 |
//fprintf(stderr,"CURRENT CHAR: %c\n",*pos); |
561 |
} |
562 |
|
563 |
len1 = (int)pos - (int)fp->path; |
564 |
V(len1); |
565 |
//fprintf(stderr,"POS = %d\n",len1); |
566 |
|
567 |
if(*pos==PATH_SEPARATOR_CHAR){ |
568 |
#ifdef WINPATHS |
569 |
STRCPY(sub,fp->drive); |
570 |
STRNCAT(sub,fp->path,len1); |
571 |
#else |
572 |
STRNCPY(sub,fp->path,len1); |
573 |
sub[len1]='\0'; |
574 |
#endif |
575 |
X(sub); |
576 |
if(strcmp(sub,"")==0){ |
577 |
M("DIRECTORY IS EMPTY"); |
578 |
STRCAT(sub,PATH_SEPARATOR_STR); |
579 |
} |
580 |
}else{ |
581 |
E("NO PARENT DIR"); |
582 |
return ospath_new_noclean(fp->path); |
583 |
} |
584 |
|
585 |
fp1 = ospath_new_noclean(sub); |
586 |
D(fp1); |
587 |
return fp1; |
588 |
} |
589 |
|
590 |
struct FilePath *ospath_getparentatdepthn(struct FilePath *fp, unsigned depth) |
591 |
{ |
592 |
int startslash; |
593 |
char path[PATH_MAX]; |
594 |
char *temp; |
595 |
char *p; |
596 |
STRTOKVAR(nexttok); |
597 |
#ifdef WINPATHS |
598 |
char temp2[PATH_MAX]; |
599 |
#endif |
600 |
|
601 |
if( |
602 |
!ospath_isvalid(fp) |
603 |
|| depth >= ospath_depth(fp) |
604 |
){ |
605 |
return fp; |
606 |
} |
607 |
|
608 |
// create FilePath object to parent object at depth N relative to this |
609 |
// path object. |
610 |
startslash = (strlen(fp->path) > 0 && fp->path[0] == PATH_SEPARATOR_CHAR); |
611 |
|
612 |
// create a copy of fp->path. |
613 |
STRCPY(path, fp->path); |
614 |
|
615 |
// reset fp->path as required. |
616 |
temp = startslash ? PATH_SEPARATOR_STR : ""; |
617 |
|
618 |
// split path into it tokens. |
619 |
//M("STARTING STRTOK"); |
620 |
p = STRTOK(path, PATH_SEPARATOR_STR, nexttok); |
621 |
|
622 |
while(p && depth > 0) |
623 |
{ |
624 |
if(strlen(temp) > 0 && temp[strlen(temp) - 1] != PATH_SEPARATOR_CHAR) |
625 |
{ |
626 |
strcat(temp,PATH_SEPARATOR_STR); |
627 |
} |
628 |
|
629 |
STRCAT(temp,p); |
630 |
--depth; |
631 |
|
632 |
p = STRTOK(NULL, PATH_SEPARATOR_STR, nexttok); |
633 |
} |
634 |
//M("FINISHED STRTOK"); |
635 |
|
636 |
// put / on end as required |
637 |
if(strlen(temp) > 0 ? (temp[strlen(temp) - 1] != PATH_SEPARATOR_CHAR) : 1) |
638 |
{ |
639 |
strcat(temp,PATH_SEPARATOR_STR); |
640 |
} |
641 |
|
642 |
#ifdef WINPATHS |
643 |
STRCPY(temp2,fp->drive); |
644 |
STRCAT(temp2,temp); |
645 |
return ospath_new_noclean(temp2); |
646 |
#else |
647 |
return ospath_new_noclean(temp); |
648 |
#endif |
649 |
} |
650 |
|
651 |
char *ospath_getbasefilename(struct FilePath *fp){ |
652 |
char *temp; |
653 |
unsigned length, offset; |
654 |
char *pos; |
655 |
|
656 |
if(strlen(fp->path) == 0){ |
657 |
// return empty name. |
658 |
return ""; |
659 |
} |
660 |
|
661 |
if(fp->path[strlen(fp->path)-1]==PATH_SEPARATOR_CHAR){ |
662 |
return NULL; |
663 |
} |
664 |
|
665 |
// reverse find '/' but DON'T ignore a trailing slash |
666 |
// (this is changed from the original implementation) |
667 |
length = strlen(fp->path); |
668 |
offset = length; |
669 |
|
670 |
pos = strrchr(fp->path, PATH_SEPARATOR_CHAR); /* OFFSET! */ |
671 |
|
672 |
// extract filename given position of find / and return it. |
673 |
if(pos != NULL){ |
674 |
unsigned length1 = length - ((pos - fp->path) + 1); |
675 |
temp = (char *)MALLOC(sizeof(char)*length1); |
676 |
|
677 |
V(length1); |
678 |
STRNCPY(temp, pos + 1, length1); |
679 |
return temp; |
680 |
}else{ |
681 |
temp = (char *)MALLOC(sizeof(char)*length); |
682 |
STRNCPY(temp, fp->path, length); |
683 |
return temp; |
684 |
} |
685 |
} |
686 |
|
687 |
char *ospath_getfilestem(struct FilePath *fp){ |
688 |
char *temp; |
689 |
char *pos; |
690 |
|
691 |
if(!ospath_isvalid(fp)){ |
692 |
return NULL; |
693 |
} |
694 |
|
695 |
temp = ospath_getbasefilename(fp); |
696 |
if(temp==NULL){ |
697 |
// it's a directory |
698 |
return NULL; |
699 |
} |
700 |
|
701 |
pos = strrchr(temp,'.'); |
702 |
|
703 |
if(pos==NULL || pos==temp){ |
704 |
// no extension, or a filename starting with '.' |
705 |
// -- return the whole filename |
706 |
return temp; |
707 |
} |
708 |
|
709 |
// remove extension. |
710 |
*pos = '\0'; |
711 |
|
712 |
return temp; |
713 |
} |
714 |
|
715 |
char *ospath_getfileext(struct FilePath *fp){ |
716 |
char *temp, *temp2, *pos; |
717 |
int len1; |
718 |
|
719 |
if(!ospath_isvalid(fp)){ |
720 |
return NULL; |
721 |
} |
722 |
|
723 |
temp = ospath_getbasefilename(fp); |
724 |
if(temp==NULL){ |
725 |
// it's a directory |
726 |
return NULL; |
727 |
} |
728 |
|
729 |
// make sure there is no / on the end. |
730 |
/// FIXME: is this good policy, removing a trailing slash? |
731 |
if(temp[strlen(temp) - 1] == PATH_SEPARATOR_CHAR){ |
732 |
temp[strlen(temp)-1] = '\0'; |
733 |
} |
734 |
|
735 |
pos = strrchr(temp,'.'); |
736 |
|
737 |
if(pos != NULL && pos!=temp){ |
738 |
// extract extension. |
739 |
len1 = temp + strlen(temp) - pos + 1; |
740 |
temp2 = (char *)MALLOC(sizeof(char)*len1); |
741 |
STRNCPY(temp2, pos, len1); |
742 |
}else{ |
743 |
// no extension |
744 |
temp2 = NULL; |
745 |
} |
746 |
FREE(temp); |
747 |
return temp2; |
748 |
} |
749 |
|
750 |
int ospath_isroot(struct FilePath *fp) |
751 |
{ |
752 |
if(!ospath_isvalid(fp)) |
753 |
{ |
754 |
return 0; |
755 |
} |
756 |
|
757 |
return fp->path == PATH_SEPARATOR_STR ? 1 : 0; |
758 |
} |
759 |
|
760 |
unsigned ospath_depth(struct FilePath *fp){ |
761 |
unsigned length; |
762 |
unsigned depth; |
763 |
unsigned i; |
764 |
|
765 |
length = strlen(fp->path); |
766 |
depth = 0; |
767 |
|
768 |
for(i = 0; i < length; i++){ |
769 |
if(fp->path[i] == PATH_SEPARATOR_CHAR){ |
770 |
++depth; |
771 |
} |
772 |
} |
773 |
|
774 |
if( |
775 |
depth > 0 |
776 |
&& length > 0 |
777 |
&& fp->path[length - 1] == PATH_SEPARATOR_CHAR |
778 |
){ |
779 |
// PATH_SEPARATOR_CHAR on the end, reduce count by 1 |
780 |
--depth; |
781 |
} |
782 |
|
783 |
return depth; |
784 |
} |
785 |
|
786 |
struct FilePath *ospath_root(struct FilePath *fp){ |
787 |
#ifdef WINPATHS |
788 |
//M("WIN ROOT"); |
789 |
char *temp; |
790 |
struct FilePath *r; |
791 |
|
792 |
if(strlen(fp->drive)){ |
793 |
temp = (char *)MALLOC(sizeof(char)*strlen(fp->drive)+1); |
794 |
STRCPY(temp,fp->drive); |
795 |
STRCAT(temp,PATH_SEPARATOR_STR); |
796 |
X(temp); |
797 |
r = ospath_new(temp); |
798 |
FREE(temp); |
799 |
}else{ |
800 |
r = ospath_new(fp->path); |
801 |
} |
802 |
return r; |
803 |
#else |
804 |
//M("JUST RETURNING PATH SEP"); |
805 |
return ospath_new(PATH_SEPARATOR_STR); |
806 |
#endif |
807 |
} |
808 |
|
809 |
struct FilePath *ospath_getdir(struct FilePath *fp){ |
810 |
char *pos; |
811 |
char s[PATH_MAX]; |
812 |
|
813 |
pos = strrchr(fp->path,PATH_SEPARATOR_CHAR); |
814 |
if(pos==NULL){ |
815 |
return ospath_new("."); |
816 |
} |
817 |
#ifdef WINPATHS |
818 |
strncpy(s,fp->drive,PATH_MAX); |
819 |
strncat(s,fp->path,pos - fp->path); |
820 |
#else |
821 |
strncpy(s,fp->path,pos - fp->path); |
822 |
#endif |
823 |
return ospath_new(s); |
824 |
} |
825 |
|
826 |
int ospath_cmp(struct FilePath *fp1, struct FilePath *fp2){ |
827 |
char temp[2][PATH_MAX]; |
828 |
#ifdef WINPATHS |
829 |
char *p; |
830 |
struct FilePath *fp; |
831 |
#endif |
832 |
|
833 |
if(!ospath_isvalid(fp1)){ |
834 |
if(!ospath_isvalid(fp2)){ |
835 |
return 0; |
836 |
}else{ |
837 |
return -1; |
838 |
} |
839 |
}else if(!ospath_isvalid(fp2)){ |
840 |
return 1; |
841 |
} |
842 |
|
843 |
// now, both are valid... |
844 |
//M("BOTH ARE VALID"); |
845 |
|
846 |
//Check that paths both have drives, if applic. |
847 |
#ifdef WINPATHS |
848 |
if(strcmp(fp1->drive,"")==0){ |
849 |
M("PATH IS MISSING DRIVE LETTER"); |
850 |
D(fp1); |
851 |
fp = ospath_getcwd(); |
852 |
assert(strlen(fp->drive)!=0); |
853 |
X(fp->drive); |
854 |
STRCPY(temp[0],fp->drive); |
855 |
ospath_free(fp); |
856 |
}else{ |
857 |
STRCPY(temp[0],fp1->drive); |
858 |
} |
859 |
|
860 |
if(strcmp(fp2->drive,"")==0){ |
861 |
M("PATH IS MISSING DRIVE LETTER"); |
862 |
D(fp2); |
863 |
fp = ospath_getcwd(); |
864 |
assert(strlen(fp->drive)!=0); |
865 |
X(fp->drive); |
866 |
STRCPY(temp[1],fp->drive); |
867 |
ospath_free(fp); |
868 |
}else{ |
869 |
STRCPY(temp[1],fp2->drive); |
870 |
} |
871 |
|
872 |
STRCAT(temp[0],fp1->path); |
873 |
STRCAT(temp[1],fp2->path); |
874 |
#else |
875 |
STRCPY(temp[0], fp1->path); |
876 |
STRCPY(temp[1], fp2->path); |
877 |
#endif |
878 |
|
879 |
#ifdef WINPATHS |
880 |
X(temp[0]); |
881 |
for(p=temp[0];*p!='\0';++p){ |
882 |
*p=tolower(*p); |
883 |
//C(*p); |
884 |
} |
885 |
X(temp[1]); |
886 |
for(p=temp[1];*p!='\0';++p){ |
887 |
*p=tolower(*p); |
888 |
//C(*p); |
889 |
} |
890 |
X(temp[0]); |
891 |
X(temp[1]); |
892 |
#endif |
893 |
|
894 |
// we will count two paths that different only in a trailing slash to be the *same* |
895 |
// so we add trailing slashes to both now: |
896 |
if(temp[0][strlen(temp[0]) - 1] != PATH_SEPARATOR_CHAR){ |
897 |
STRCAT(temp[0],PATH_SEPARATOR_STR); |
898 |
} |
899 |
|
900 |
if(temp[1][strlen(temp[1]) - 1] != PATH_SEPARATOR_CHAR){ |
901 |
STRCAT(temp[1],PATH_SEPARATOR_STR); |
902 |
} |
903 |
|
904 |
//X(temp[0]); |
905 |
//X(temp[1]); |
906 |
|
907 |
return strcmp(temp[0],temp[1]); |
908 |
} |
909 |
|
910 |
struct FilePath *ospath_concat(struct FilePath *fp1, struct FilePath *fp2){ |
911 |
|
912 |
struct FilePath *fp; |
913 |
char temp[2][PATH_MAX]; |
914 |
char temp2[PATH_MAX]; |
915 |
struct FilePath *r; |
916 |
|
917 |
fp = (struct FilePath *)MALLOC(sizeof(struct FilePath)); |
918 |
|
919 |
if(!ospath_isvalid(fp1)){ |
920 |
if(ospath_isvalid(fp2)){ |
921 |
ospath_copy(fp,fp2); |
922 |
}else{ |
923 |
// both invalid |
924 |
ospath_copy(fp,fp1); |
925 |
} |
926 |
return fp; |
927 |
} |
928 |
|
929 |
if(!ospath_isvalid(fp2)){ |
930 |
ospath_copy(fp,fp1); |
931 |
return fp; |
932 |
} |
933 |
|
934 |
// not just a copy of one or the other... |
935 |
ospath_free(fp); |
936 |
|
937 |
// now, both paths are valid... |
938 |
|
939 |
#ifdef WINPATHS |
940 |
STRNCPY(temp[0],fp1->drive,PATH_MAX); |
941 |
STRNCAT(temp[0],fp1->path,PATH_MAX-strlen(temp[0])); |
942 |
#else |
943 |
STRNCPY(temp[0], fp1->path,PATH_MAX); |
944 |
#endif |
945 |
|
946 |
STRNCPY(temp[1], fp2->path,PATH_MAX); |
947 |
|
948 |
// make sure temp has a / on the end. |
949 |
if(temp[0][strlen(temp[0]) - 1] != PATH_SEPARATOR_CHAR) |
950 |
{ |
951 |
STRNCAT(temp[0],PATH_SEPARATOR_STR,PATH_MAX-strlen(temp[0])); |
952 |
} |
953 |
|
954 |
#ifdef DO_FIXSLASHES |
955 |
ospath_fixslash(temp[0]); |
956 |
ospath_fixslash(temp[1]); |
957 |
#endif |
958 |
|
959 |
//V(strlen(temp[0])); |
960 |
//X(temp[0]); |
961 |
//V(strlen(temp[1])); |
962 |
//X(temp[1]); |
963 |
|
964 |
// make sure rhs path has NOT got a / at the start. |
965 |
if(temp[1][0] == PATH_SEPARATOR_CHAR){ |
966 |
return NULL; |
967 |
} |
968 |
|
969 |
// create a new path object with the two path strings appended together. |
970 |
STRNCPY(temp2,temp[0],PATH_MAX); |
971 |
STRNCAT(temp2,temp[1],PATH_MAX-strlen(temp2)); |
972 |
//V(strlen(temp2)); |
973 |
//X(temp2); |
974 |
r = ospath_new_noclean(temp2); |
975 |
D(r); |
976 |
|
977 |
/* ospath_cleanup(r);*/ |
978 |
return r; |
979 |
} |
980 |
|
981 |
void ospath_append(struct FilePath *fp, struct FilePath *fp1){ |
982 |
char *p; |
983 |
char temp[2][PATH_MAX]; |
984 |
struct FilePath fp2; |
985 |
|
986 |
ospath_copy(&fp2,fp1); |
987 |
#ifdef DO_FIXSLASHES |
988 |
ospath_fixslash(fp2.path); |
989 |
#endif |
990 |
|
991 |
if(!ospath_isvalid(&fp2)){ |
992 |
M("fp1 invalid"); |
993 |
return; |
994 |
} |
995 |
|
996 |
if(!ospath_isvalid(fp) && ospath_isvalid(&fp2)){ |
997 |
// set this object to be the same as the rhs |
998 |
M("fp invalid"); |
999 |
ospath_copy(fp,&fp2); |
1000 |
#ifdef DO_FIXSLASHES |
1001 |
ospath_fixslash(fp->path); |
1002 |
#endif |
1003 |
return; |
1004 |
} |
1005 |
|
1006 |
X(fp->path); |
1007 |
X(fp2.path); |
1008 |
|
1009 |
// both paths are valid... |
1010 |
//temp[0] = CALLOC(1+strlen(fp->path), sizeof(char)); |
1011 |
STRNCPY(temp[0], fp->path, PATH_MAX); |
1012 |
//temp[1] = CALLOC(strlen(fp2.path), sizeof(char)); |
1013 |
STRNCPY(temp[1], fp2.path, PATH_MAX); |
1014 |
|
1015 |
X(temp[0]); |
1016 |
X(temp[1]); |
1017 |
|
1018 |
// make sure temp has a / on the end. |
1019 |
if(temp[0][strlen(temp[0]) - 1] != PATH_SEPARATOR_CHAR) |
1020 |
{ |
1021 |
STRCAT(temp[0],PATH_SEPARATOR_STR); |
1022 |
} |
1023 |
|
1024 |
// make sure rhs path has NOT got a / at the start. |
1025 |
if(temp[1][0] == PATH_SEPARATOR_CHAR){ |
1026 |
for(p=temp[1]+1; *p != '\0'; ++p){ |
1027 |
*(p-1)=*p; |
1028 |
} |
1029 |
*(p-1)='\0'; |
1030 |
} |
1031 |
|
1032 |
X(temp[0]); |
1033 |
X(temp[1]); |
1034 |
|
1035 |
// create new path string. |
1036 |
STRNCPY(fp->path,temp[0], PATH_MAX); |
1037 |
STRNCAT(fp->path,temp[1], PATH_MAX-strlen(fp->path)); |
1038 |
|
1039 |
X(fp->path); |
1040 |
|
1041 |
//FREE(temp[0]); |
1042 |
//M("Freed temp[0]"); |
1043 |
//FREE(temp[1]); |
1044 |
//M("Freed temp[1]"); |
1045 |
|
1046 |
D(fp); |
1047 |
ospath_cleanup(fp); |
1048 |
} |
1049 |
|
1050 |
void ospath_copy(struct FilePath *dest, struct FilePath *src){ |
1051 |
STRCPY(dest->path,src->path); |
1052 |
#ifdef WINPATHS |
1053 |
STRCPY(dest->drive,src->drive); |
1054 |
#endif |
1055 |
} |
1056 |
|
1057 |
void ospath_debug(struct FilePath *fp){ |
1058 |
#ifdef WINPATHS |
1059 |
fprintf(stderr,"{\"%s\",\"%s\"}\n",fp->drive,fp->path); |
1060 |
#else |
1061 |
fprintf(stderr,"{\"%s\"}\n",fp->path); |
1062 |
#endif |
1063 |
} |
1064 |
|
1065 |
FILE *ospath_fopen(struct FilePath *fp, const char *mode){ |
1066 |
char s[PATH_MAX]; |
1067 |
if(!ospath_isvalid(fp)){ |
1068 |
E("Invalid path"); |
1069 |
return NULL; |
1070 |
} |
1071 |
ospath_strcpy(fp,s,PATH_MAX); |
1072 |
FILE *f = fopen(s,mode); |
1073 |
return f; |
1074 |
} |
1075 |
|
1076 |
//------------------------ |
1077 |
// SEARCH PATH FUNCTIONS |
1078 |
|
1079 |
struct FilePath **ospath_searchpath_new(const char *path){ |
1080 |
char *p; |
1081 |
char *list[LISTMAX]; |
1082 |
unsigned n=0; |
1083 |
char *c; |
1084 |
unsigned i; |
1085 |
struct FilePath **pp; |
1086 |
STRTOKVAR(nexttok); |
1087 |
|
1088 |
char path1[PATH_MAX]; |
1089 |
strncpy(path1,path,PATH_MAX); |
1090 |
|
1091 |
X(path1); |
1092 |
X(PATH_LISTSEP_STR); |
1093 |
|
1094 |
V(strlen(path1)); |
1095 |
V(strlen(PATH_LISTSEP_STR)); |
1096 |
|
1097 |
/* |
1098 |
c = strstr(path,PATH_LISTSEP_CHAR); |
1099 |
if(c==NULL){ |
1100 |
E("NO TOKEN FOUND"); |
1101 |
} |
1102 |
*/ |
1103 |
|
1104 |
p=STRTOK(path1,PATH_LISTSEP_STR,nexttok); |
1105 |
X(p); |
1106 |
for(; p!= NULL; p=STRTOK(NULL,PATH_LISTSEP_STR,nexttok)){ |
1107 |
c = (char *)MALLOC(sizeof(char)*strlen(p)); |
1108 |
X(p); |
1109 |
STRCPY(c,p); |
1110 |
if(n>=LISTMAX){ |
1111 |
E("IGNORING SOME PATH COMPONENTS"); |
1112 |
break; |
1113 |
} |
1114 |
list[n++]=c; |
1115 |
} |
1116 |
|
1117 |
/* |
1118 |
for(i=0;i<n;++i){ |
1119 |
X(list[i]); |
1120 |
} |
1121 |
V(n); |
1122 |
*/ |
1123 |
|
1124 |
pp = (struct FilePath **)MALLOC(sizeof(struct FilePath*)*(n+1)); |
1125 |
for(i=0; i<n; ++i){ |
1126 |
//V(i); |
1127 |
//X(list[i]); |
1128 |
pp[i] = ospath_new_noclean(list[i]); |
1129 |
//D(pp[i]); |
1130 |
} |
1131 |
pp[n] = NULL; |
1132 |
|
1133 |
for(i=0;i<n;++i){ |
1134 |
#ifdef DO_FIXSLASHES |
1135 |
ospath_fixslash(pp[i]->path); |
1136 |
#endif |
1137 |
D(pp[i]); |
1138 |
} |
1139 |
|
1140 |
return pp; |
1141 |
} |
1142 |
|
1143 |
void ospath_searchpath_free(struct FilePath **searchpath){ |
1144 |
struct FilePath **p; |
1145 |
for(p=searchpath; *p!=NULL; ++p){ |
1146 |
ospath_free(*p); |
1147 |
} |
1148 |
FREE(searchpath); |
1149 |
} |
1150 |
|
1151 |
struct FilePath *ospath_searchpath_iterate( |
1152 |
struct FilePath **searchpath |
1153 |
, FilePathTestFn *testfn |
1154 |
, void *searchdata |
1155 |
){ |
1156 |
struct FilePath **p; |
1157 |
|
1158 |
p = searchpath; |
1159 |
|
1160 |
M("SEARCHING IN..."); |
1161 |
for(p=searchpath; *p!=NULL; ++p){ |
1162 |
D(*p); |
1163 |
} |
1164 |
|
1165 |
for(p=searchpath; *p!=NULL; ++p){ |
1166 |
D(*p); |
1167 |
if((*testfn)(*p,searchdata)){ |
1168 |
return *p; |
1169 |
} |
1170 |
} |
1171 |
return NULL; |
1172 |
} |