/[ascend]/trunk/base/generic/general/ospath.c
ViewVC logotype

Annotation of /trunk/base/generic/general/ospath.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 534 - (hide annotations) (download) (as text)
Tue Apr 25 13:25:04 2006 UTC (13 years, 9 months ago) by johnpye
File MIME type: text/x-csrc
File size: 16621 byte(s)
Working on platform-independent pathnames for the IMPORT command.
Added 'ospath.c' to base/generic/general for this purpose.
Patched kvalues and sensitivity to use the 'IMPORT "libname";' syntax
instead of 'IMPORT registerfn FROM libname;'.
Fixed pathnames in create.nsi.
Added GPL header to coupla files.
Added quoting to 'IMPORT' syntax in ascParse.y.
Removed 'PackageOption' from Tcl/Tk related Scons options so that missing Tcl/Tk
doesn't cause breakage.
1 johnpye 534 #include <stdio.h>
2    
3     #include "ospath.h"
4    
5     #if defined(__WIN32__) /* && !defined(__MINGW32__) */
6     # define WINPATHS
7     #endif
8    
9     struct FilePath{
10     char path[PATHMAX]; /// the string version of the represented POSIX path
11    
12     #ifdef WINPATHS
13     char drive[2]; /// the drive the path resides on (field is absent in POSIX systems)
14     #endif
15     };
16    
17     #include <string.h>
18    
19     #define MALLOC malloc
20     #define FREE free
21     #define CALLOC calloc
22    
23     #define M(MSG) fprintf(stderr,"%s:%d: (%s) %s\n",__FILE__,__LINE__,__FUNCTION__,MSG)
24     #define E(MSG) fprintf(stderr,"%s:%d: (%s) ERROR: %s\n",__FILE__,__LINE__,__FUNCTION__,MSG)
25     #define X(VAR) fprintf(stderr,"%s:%d: (%s) %s=%s\n",__FILE__,__LINE__,__FUNCTION__,#VAR,VAR)
26     #define D(VAR) ospath_debug(VAR,#VAR)
27    
28     /**
29     Static function that can be used to retrieve the current users HOME path
30     */
31     struct FilePath *ospath_gethomepath(void);
32    
33     /**
34     This function cleans up the path string used to construct the FilePath object:
35     1. Get rid of multiple / 's one after the other...
36    
37     ie. "///usr/bin///hello/////there// --> "/usr/bin/hello/there/"
38    
39     2. Resolve any ~ directory found to the current user's HOME path
40    
41     3. @TODO what about "dir/../dir2/dir3" --> "dir2/dir3" ?
42     */
43     void ospath_cleanup(struct FilePath *);
44    
45     void ospath_copy(struct FilePath *dest, struct FilePath *src);
46    
47    
48     #ifdef WINPATHS
49     /**
50     This function splits out the drive letter in the path string, thus completing
51     the correct construction of a FilePath object under Win32.
52     */
53     void ospath_extractdriveletter(struct FilePath *);
54     #endif
55    
56     #ifdef WINPATHS
57     # define PATH_SEPARATOR_STR "\\"
58     # define PATH_SEPARATOR_CHAR '\\'
59     #else
60     # define PATH_SEPARATOR_STR "/"
61     # define PATH_SEPARATOR_CHAR '/'
62     #endif
63    
64     /**
65     Create a new path structure from a string
66     */
67     struct FilePath *ospath_new(const char *path){
68     struct FilePath *fp;
69     fp = MALLOC(sizeof(struct FilePath));
70     X(path);
71     strcpy(fp->path, path);
72    
73     #ifdef WINPATHS
74     ospath_extractdriveletter(fp);
75     #endif
76    
77     ospath_cleanup(fp);
78    
79     return fp;
80     }
81    
82     /**
83     As for ospath_new but the 'cleanup' call is now optional
84     */
85     struct FilePath *ospath_new_noclean(const char *path){
86     struct FilePath *fp = MALLOC(sizeof(struct FilePath));
87     strcpy(fp->path,path);
88    
89     #ifdef WINPATHS
90     ospath_extractdriveletter(fp);
91     #endif
92    
93     return fp;
94     }
95    
96     /**
97     Use getenv() function to retrieve HOME path, or if not set, use
98     the password database and try to retrieve it that way (???)
99     */
100     struct FilePath *ospath_gethomepath(void){
101    
102     const char *pfx = getenv("HOME");
103    
104     #ifndef __WIN32__
105     if(pfx==NULL){
106     struct passwd * pw = getpwuid(getuid());
107    
108     if(pw){
109     pfx = pw -> pw_dir;
110     }
111     }
112     #endif
113    
114     // create path object from HOME, but don't compress it!
115     return ospath_new_noclean(pfx ? pfx : "");
116     }
117    
118     #ifdef WINPATHS
119     void ospath_extractdriveletter(struct FilePath *fp)
120     {
121     // extract the drive the path resides on...
122     if(strlen(fp->path) >= 2 && fp->path[1] == ':')
123     {
124     char driveletter = '\0';
125    
126     char firsttwo[2];
127     strncpy(firsttwo,fp->path,2);
128    
129     if(sscanf(firsttwo, "%c:", &driveletter) == 1)
130     {
131     //M("FOUND LEADING DRIVE: %c\n",driveletter);
132     strncpy(fp->drive,fp->path,2);
133     strcpy(fp->path, fp->path+2);
134     }
135     }
136     }
137     #endif
138    
139     void ospath_cleanup(struct FilePath *fp){
140     char *pBuff;
141     char path[PATHMAX];
142     char *p;
143     struct FilePath *home;
144     struct FilePath *workingPath;
145     struct FilePath *parent;
146    
147     // compress the path, and resolve ~
148     int startslash = (strlen(fp->path) > 0 && fp->path[0] == PATH_SEPARATOR_CHAR);
149     int endslash = (strlen(fp->path) > 1 && fp->path[strlen(fp->path) - 1] == PATH_SEPARATOR_CHAR);
150    
151     fprintf(stderr,"FS ON START = %d\n",startslash);
152     fprintf(stderr,"FS ON END = %d\n",endslash);
153     fprintf(stderr,"FIRST CHAR = %c\n",fp->path[0]);
154    
155     home = ospath_gethomepath();
156    
157     // create a copy of fp->path.
158     strcpy(path, fp->path);
159    
160     // reset fp->path as required.
161     strcpy(fp->path, (startslash ? PATH_SEPARATOR_STR : ""));
162    
163     X(fp->path);
164    
165     // split path into it tokens, using strtok which is NOT reentrant
166     // so be careful!
167    
168     for(p = strtok(path, PATH_SEPARATOR_STR);
169     p!=NULL;
170     p = strtok(NULL,PATH_SEPARATOR_STR)
171     ){
172     X(p);
173     if(strcmp(p, "~")==0){
174    
175     if(p == path){ // check that the ~ is the first character in the path
176     if(ospath_isvalid(home)){
177     ospath_copy(fp,home);
178     continue;
179     }else{
180     E("HOME does not resolve to valid path");
181     }
182     }else{
183     E("A tilde (~) present as a component in a file path must be at the start!");
184     }
185     }else if(strcmp(p, ".") == 0){
186    
187     if(p==path){// start of path:
188     M("EXPANDING LEADING '.' IN PATH");
189     // get current working directory
190     pBuff = (char *)getcwd(NULL, 0);
191     X(pBuff);
192    
193     // create new path with resolved working directory
194     workingPath = ospath_new_noclean(pBuff != NULL ? pBuff : ".");
195    
196     if(pBuff == NULL){
197     E("Unable to resolve current working directory");
198     }else{
199     FREE(pBuff);
200     }
201    
202     ospath_copy(fp,workingPath);
203     FREE(workingPath);
204     continue;
205     }else{// later in the path: just skip it
206     M("SKIPPING '.' IN PATH");
207     continue;
208     }
209    
210     }else if(strcmp(p, "..") == 0){
211     M("GOING TO PARENT");
212     parent = ospath_getparent(fp);
213     if(ospath_isvalid(parent)){
214     ospath_copy(fp,parent);
215     }
216     FREE(parent);
217     continue;
218     }
219    
220     // add a separator if we've already got some stuff
221     if(
222     strlen(fp->path) > 0
223     && fp->path[strlen(fp->path) - 1] != PATH_SEPARATOR_CHAR
224     ){
225     strcat(fp->path,PATH_SEPARATOR_STR);
226     }
227    
228     // add the present path component
229     strcat(fp->path, p);
230     }
231    
232     // put / on end as required, according to what the starting path had
233     if(endslash && (strlen(fp->path) > 0 ? (fp->path[strlen(fp->path) - 1] != PATH_SEPARATOR_CHAR) : 1))
234     {
235     strcat(fp->path, PATH_SEPARATOR_STR);
236     }
237    
238     FREE(parent);
239     FREE(workingPath);
240     FREE(home);
241     }
242    
243    
244     int ospath_isvalid(struct FilePath *fp){
245     return strlen(fp->path) > 0 ? 1 : 0;
246     }
247    
248    
249     char *ospath_str(struct FilePath *fp){
250     char *s;
251     #ifdef WINPATHS
252     s = CALLOC(strlen(fp->drive)+strlen(fp->path),sizeof(char));
253     strcpy(s,fp->drive);
254     strcat(s,fp->path);
255     #else
256     s = CALLOC(strlen(fp->path),sizeof(char));
257     strcpy(s,fp->path);
258     #endif
259     return s;
260     }
261    
262     void ospath_fwrite(struct FilePath *fp, FILE *dest){
263     #ifdef WINPATHS
264     fprintf(dest,"%s%s",fp->drive,fp->path);
265     #else
266     fprintf(dest,"%s",fp->path);
267     #endif
268     }
269    
270     unsigned ospath_length(struct FilePath *fp){
271     #ifdef WINPATHS
272     // we've already validated this path, so it's on to just add it up
273     // (unless someone has been tinkering with the internal structure here)
274     return (unsigned) (strlen(fp->drive) + strlen(fp->path));
275     #else
276     return (unsigned) (strlen(fp->path));
277     #endif
278     }
279    
280     struct FilePath *ospath_getparent(struct FilePath *fp)
281     {
282     if(strlen(fp->path) == 0 || ospath_isroot(fp))
283     {
284     // return empty path.
285     return ospath_new("");
286     }
287    
288     // reverse find a / ignoring the end / if it exists.
289     int length = strlen(fp->path);
290     int offset = (
291     fp->path[length - 1] == PATH_SEPARATOR_CHAR
292     && length - 2 > 0
293     ) ? length - 2 : length ;
294     char *pos = strrchr(fp->path,PATH_SEPARATOR_CHAR);
295    
296     // create new path object given position of find / and return it.
297     if(pos != fp->path+length)
298     {
299     char temp[PATHMAX];
300     #ifdef WINPATHS
301     strcpy(temp,fp->drive);
302     #endif
303     strncat(temp,fp->path,(pos-fp->path)+1);
304     return ospath_new_noclean(temp);
305     }else{
306     // not parent path avaliable, return an empty path.
307     return ospath_new("");
308     }
309     }
310    
311     struct FilePath *ospath_getparentatdepthn(struct FilePath *fp, unsigned depth)
312     {
313     if(
314     !ospath_isvalid(fp)
315     || depth >= ospath_depth(fp)
316     ){
317     return fp;
318     }
319    
320     // create FilePath object to parent object at depth N relative to this
321     // path object.
322     int startslash = (strlen(fp->path) > 0 && fp->path[0] == PATH_SEPARATOR_CHAR);
323    
324     // create a copy of fp->path.
325     char path[PATHMAX];
326     strcpy(path, fp->path);
327    
328     // reset fp->path as required.
329     char *temp = startslash ? PATH_SEPARATOR_STR : "";
330    
331     // split path into it tokens.
332     char *p = strtok(path, PATH_SEPARATOR_STR);
333    
334     while(p && depth > 0)
335     {
336     if(strlen(temp) > 0 && temp[strlen(temp) - 1] != PATH_SEPARATOR_CHAR)
337     {
338     temp += PATH_SEPARATOR_CHAR;
339     }
340    
341     strcat(temp,p);
342     --depth;
343    
344     p = strtok(NULL, PATH_SEPARATOR_STR);
345     }
346    
347     // put / on end as required
348     if(strlen(temp) > 0 ? (temp[strlen(temp) - 1] != PATH_SEPARATOR_CHAR) : 1)
349     {
350     temp += PATH_SEPARATOR_CHAR;
351     }
352    
353     #ifdef WINPATHS
354     char temp2[PATHMAX];
355     strcpy(temp2,fp->drive);
356     strcat(temp2,temp);
357     return ospath_new_noclean(temp2);
358     #else
359     return ospath_new_noclean(temp);
360     #endif
361     }
362    
363     char *ospath_getbasefilename(struct FilePath *fp){
364     char *temp;
365    
366     if(strlen(fp->path) == 0){
367     // return empty name.
368     return "";
369     }
370    
371     // reverse find a / ignoring the end / if it exists.
372     unsigned length = strlen(fp->path);
373     unsigned offset = (
374     fp->path[length - 1] == PATH_SEPARATOR_CHAR
375     && length - 2 > 0
376     ) ? length - 2
377     : length;
378    
379     char *pos = strrchr(fp->path, PATH_SEPARATOR_CHAR); /* OFFSET! */
380    
381     // extract filename given position of find / and return it.
382     if(pos != fp->path + length){
383     int length1 = length - ((pos - fp->path) + 1) - (offset != length ? 1 : 0);
384     temp = CALLOC(length1,sizeof(char));
385    
386     strncpy(temp, pos + 1, length1);
387     return temp;
388     }else{
389     temp = CALLOC(length,sizeof(char));
390     strncpy(temp, fp->path, length);
391     return temp;
392     }
393     }
394    
395     char *ospath_getbasefiletitle(struct FilePath *fp){
396     if(!ospath_isvalid(fp)){
397     return NULL;
398     }
399    
400     char *temp = ospath_getbasefilename(fp);
401     char *pos = strrchr(temp,'.');
402    
403     if(pos != NULL){
404     // remove extension.
405     *pos = '\0';
406     }
407    
408     return temp;
409     }
410    
411     char *ospath_getbasefileextension(struct FilePath *fp){
412     if(!ospath_isvalid(fp)){
413     return NULL;
414     }
415    
416     char *temp = ospath_getbasefilename(fp);
417     char *temp2;
418    
419     // make sure there is no / on the end.
420     if(temp[strlen(temp) - 1] == PATH_SEPARATOR_CHAR){
421     temp[strlen(temp)-1] = '\0';
422     }
423    
424     char *pos = strrchr(temp,'.');
425    
426     if(pos != NULL)
427     {
428     // extract extension.
429     int len1 = temp + strlen(temp) - pos;
430     temp2 = CALLOC(len1, sizeof(char));
431     strncpy(temp2, pos, len1);
432     }else{
433     // no extension
434     temp2 = NULL;
435     }
436     FREE(temp);
437     return temp2;
438     }
439    
440     int ospath_isroot(struct FilePath *fp)
441     {
442     if(!ospath_isvalid(fp))
443     {
444     return 0;
445     }
446    
447     return fp->path == PATH_SEPARATOR_STR ? 1 : 0;
448     }
449    
450     unsigned ospath_depth(struct FilePath *fp){
451     unsigned length;
452     unsigned depth;
453     unsigned i;
454    
455     length = strlen(fp->path);
456     depth = 0;
457    
458     for(i = 0; i < length; i++){
459     if(fp->path[i] == PATH_SEPARATOR_CHAR){
460     ++depth;
461     }
462     }
463    
464     if(
465     depth > 0
466     && length > 0
467     && fp->path[length - 1] == PATH_SEPARATOR_CHAR
468     ){
469     // PATH_SEPARATOR_CHAR on the end, reduce count by 1
470     --depth;
471     }
472    
473     return depth;
474     }
475    
476     struct FilePath *ospath_root(struct FilePath *fp){
477     #ifdef WINPATHS
478     char *temp;
479     struct FilePath *r;
480     if(strlen(fp->drive)){
481     temp = CALLOC(strlen(fp->drive)+1, sizeof(char));
482     strcpy(temp,fp->drive);
483     strcat(temp,PATH_SEPARATOR_STR);
484     r = ospath_new(temp);
485     FREE(temp);
486     }else{
487     r = ospath_new(fp->drive);
488     }
489     return r;
490     #else
491     return ospath_new(PATH_SEPARATOR_STR);
492     #endif
493     }
494    
495     int ospath_cmp(struct FilePath *fp1, struct FilePath *fp2)
496     {
497     if(!ospath_isvalid(fp1)){
498     if(!ospath_isvalid(fp2)){
499     return 0;
500     }else{
501     return -1;
502     }
503     }else if(!ospath_isvalid(fp2)){
504     return 1;
505     }
506    
507     // now, both are valid...
508     M("BOTH ARE VALID");
509    
510     char *temp[2];
511     #ifdef WINPATHS
512     temp[0] = CALLOC(1+strlen(fp1->drive)+strlen(fp1->path), sizeof(char));
513     strcpy(temp[0],fp1->drive);
514     strcat(temp[0],fp1->path);
515     temp[1] = CALLOC(1+strlen(fp2->drive)+strlen(fp2->path), sizeof(char));
516     strcpy(temp[1],fp2->drive);
517     strcat(temp[1],fp2->path);
518     #else
519     temp[0] = CALLOC(1+strlen(fp1->path), sizeof(char));
520     strcpy(temp[0], fp1->path);
521     temp[1] = CALLOC(1+strlen(fp2->path), sizeof(char));
522     strcpy(temp[1], fp2->path);
523     #endif
524    
525     // we will count two paths that different only in a trailing slash to be the *same*
526     // so we add trailing slashes to both now:
527     if(temp[0][strlen(temp[0]) - 1] != PATH_SEPARATOR_CHAR){
528     strcat(temp[0],PATH_SEPARATOR_STR);
529     }
530    
531     if(temp[1][strlen(temp[1]) - 1] != PATH_SEPARATOR_CHAR){
532     strcat(temp[1],PATH_SEPARATOR_STR);
533     }
534    
535     //X(temp[0]);
536     //X(temp[1]);
537    
538     return strcmp(temp[0],temp[1]);
539     }
540    
541     struct FilePath *ospath_concat(struct FilePath *fp1, struct FilePath *fp2){
542    
543     struct FilePath *fp;
544     fp = MALLOC(sizeof(struct FilePath));
545    
546     if(!ospath_isvalid(fp1)){
547     if(ospath_isvalid(fp2)){
548     ospath_copy(fp2,fp);
549     }else{
550     // both invalid
551     ospath_copy(fp1,fp);
552     }
553     return fp;
554     }
555    
556     ospath_copy(fp1,fp);
557    
558     if(!ospath_isvalid(fp2)){
559     return fp;
560     }
561    
562     // not just a copy of one or the other...
563     FREE(fp);
564    
565     // now, both paths are valid...
566    
567     char *temp[2];
568     #ifdef WINPATHS
569     temp[0] = CALLOC(strlen(fp1->drive)+strlen(fp1->path), sizeof(char));
570     strcpy(temp[0],fp1->drive);
571     strcat(temp[0],fp1->path);
572     #else
573     temp[0] = CALLOC(strlen(fp1->path), sizeof(char));
574     strcpy(temp[0], fp1->path);
575     #endif
576     temp[1] = CALLOC(strlen(fp2->path), sizeof(char));
577     strcpy(temp[1], fp2->path);
578    
579     // make sure temp has a / on the end.
580     if(temp[0][strlen(temp[0]) - 1] != PATH_SEPARATOR_CHAR)
581     {
582     temp[0] += PATH_SEPARATOR_CHAR;
583     }
584    
585     // make sure rhs path has NOT got a / at the start.
586     if(temp[1][0] == PATH_SEPARATOR_CHAR){
587     FREE(temp[0]);
588     FREE(temp[1]);
589     return NULL;
590     }
591    
592     // create a new path object with the two path strings appended together.
593     char *temp2;
594     temp2 = CALLOC(strlen(temp[0])+strlen(temp[1]), sizeof(char));
595     strcpy(temp2,temp[0]);
596     strcat(temp2,temp[1]);
597     struct FilePath *r;
598     r = ospath_new_noclean(temp2);
599     FREE(temp2);
600     FREE(temp[0]);
601     FREE(temp[1]);
602     return r;
603     }
604    
605     void ospath_append(struct FilePath *fp, struct FilePath *fp1){
606     char *p;
607    
608     if(!ospath_isvalid(fp1)){
609     M("fp1 invalid");
610     return;
611     }
612    
613     if(!ospath_isvalid(fp) && ospath_isvalid(fp1)){
614     // set this object to be the same as the rhs
615     M("fp invalid");
616     ospath_copy(fp,fp1);
617     return;
618     }
619    
620     X(fp->path);
621     X(fp1->path);
622    
623     // both paths are valid...
624     char *temp[2];
625     temp[0] = CALLOC(1+strlen(fp->path), sizeof(char));
626     strcpy(temp[0], fp->path);
627     temp[1] = CALLOC(strlen(fp1->path), sizeof(char));
628     strcpy(temp[1], fp1->path);
629    
630     X(temp[0]);
631     X(temp[1]);
632    
633     // make sure temp has a / on the end.
634     if(temp[0][strlen(temp[0]) - 1] != PATH_SEPARATOR_CHAR)
635     {
636     strcat(temp[0],PATH_SEPARATOR_STR);
637     }
638    
639     // make sure rhs path has NOT got a / at the start.
640     if(temp[1][0] == PATH_SEPARATOR_CHAR){
641     for(p=temp[1]+1; *p != '\0'; ++p){
642     *(p-1)=*p;
643     }
644     *(p-1)='\0';
645     }
646    
647     X(temp[0]);
648     X(temp[1]);
649    
650     // create new path string.
651     strcpy(fp->path,temp[0]);
652     strcat(fp->path,temp[1]);
653    
654     FREE(temp[0]);
655     FREE(temp[1]);
656     }
657    
658     void ospath_copy(struct FilePath *dest, struct FilePath *src){
659     strcpy(dest->path,src->path);
660     #ifdef WINPATHS
661     strcpy(dest->drive,src->drive);
662     #endif
663     }
664    
665     void ospath_debug(struct FilePath *fp, char *label){
666     fprintf(stderr,"%s\n---------------------\n",label);
667     fprintf(stderr,"PATH = %s\n",fp->path);
668     #ifdef WINPATHS
669     fprintf(stderr,"DRIVE = %s\n",fp->drive);
670     #endif
671     fprintf(stderr,"\n");
672     }
673    
674     /*--------------------------------
675     some simple test routines...
676     */
677     #ifdef TEST
678     #include <assert.h>
679    
680     int main(void){
681     struct FilePath *fp1, *fp2, *fp3, *fp4;
682    
683     fp1 = ospath_new(".\\src\\.\\images\\..\\\\movies\\");
684     fp2 = ospath_new(".\\src\\movies");
685    
686     D(fp1);
687     D(fp2);
688    
689     assert(ospath_cmp(fp1,fp2)==0);
690     M("Passed 'cleanup' test");
691    
692     FREE(fp2);
693    
694     fp2 = ospath_new(".\\src\\movies\\kubrick");
695     fp3 = ospath_getparent(fp2);
696    
697     assert(ospath_cmp(fp1,fp3)==0);
698     M("Passed 'parent' test");
699    
700     FREE(fp2); FREE(fp3);
701    
702    
703     fp2 = ospath_new("\\home\\john");
704     fp3 = ospath_new("where\\mojo");
705    
706     D(fp2);
707     D(fp3);
708    
709     ospath_append(fp2,fp3);
710    
711     //fp4 = ospath_new("\\home\\john\\where\\mojo\\");
712    
713     D(fp2);
714     //assert(ospath_cmp(fp2,fp4)==0);
715     //M("Passed 'append' test");
716    
717     /*
718     ospath_append(fp2,fp3);
719     fp4 = ospath_root(fp2);
720     D(fp2);
721     D(fp4);
722    
723     assert(ospath_cmp(fp2,fp4)==0);
724     M("Passed 'concat' test");
725     */
726     /*
727     D(fp2);
728    
729     FREE(fp2);
730    
731     fp2 = ospath_new("~/.");
732    
733     assert(ospath_cmp(fp1,fp2)==0);
734    
735     D(fp2);
736    
737     FREE(fp1);
738     FREE(fp2);
739    
740     fp1 = ospath_new("/usr/local/include");
741     fp2 = ospath_new("/usr/include/../local/include");
742    
743     D(fp1);
744     D(fp2);
745     */
746    
747     }
748    
749     #endif

john.pye@anu.edu.au
ViewVC Help
Powered by ViewVC 1.1.22