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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 534 - (show annotations) (download) (as text)
Tue Apr 25 13:25:04 2006 UTC (14 years, 1 month 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 #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