1 /*
2  * $Id: pathfun.c,v 1.1 2005-09-18 22:04:16 dhmunro Exp $
3  * Implement path name expansion and utility functions required by Yorick.
4  */
5 /* Copyright (c) 2005, The Regents of the University of California.
6  * All rights reserved.
7  * This file is part of yorick (http://yorick.sourceforge.net).
8  * Read the accompanying LICENSE file for details.
9  */
10 
11 #include "yio.h"
12 #include "pstdlib.h"
13 #include <string.h>
14 
15 #define DIR_SEP '/'
16 #define DIR_SEP_S "/"
17 
18 char *yCWD = 0;
19 char *yHOME = "~/";
20 
21 /* YSetCWD returns non-0 if operation fails, resets yCWD on success.  */
22 int
YSetCWD(const char * name)23 YSetCWD(const char *name)
24 {
25   int notOK = name? p_chdir(name) : 0;
26   if (!notOK) {
27     p_free(yCWD);
28     yCWD = p_strcpy(p_getcwd());
29     if (yCWD) YNameToHead(&yCWD);
30     else notOK = 1;
31   }
32   return notOK;
33 }
34 
35 void
YGetHOME(void)36 YGetHOME(void)
37 {
38   char *hnm = Ygetenv("HOME");
39   if (hnm && hnm[0]) {
40     yHOME = p_strcpy(hnm);
41     YNameToHead(&yHOME);
42   } else {
43     yHOME = p_strcpy(ySiteDir);
44   }
45   if (hnm) p_free(hnm);
46 }
47 
48 /* convert path name which may be relative, or begin with ., .., ~, or
49  * an environment variable, into an absolute pathname */
50 char *
YExpandName(const char * name)51 YExpandName(const char *name)
52 {
53   char *path, *head = 0, *tail0 = 0;
54   char *tail = (char *)name;  /* I promise not to write to the original... */
55   int freeHead = 0;
56   int nRemove = 0;   /* count number of leading ..s */
57 
58   if (!name) return 0;
59 
60   /* try to take care of simple environment variable in first position */
61   if (tail[0]=='$') {
62     char *env;
63     char delim = *(++tail);
64     if (delim=='(') { delim = ')'; tail++; }
65     else if (delim=='{') { delim='}'; tail++; }
66     else delim = DIR_SEP;
67     env = tail;
68     while (*tail && *tail!=delim) tail++;
69     head = Ygetenv( (env = p_strncat(0, env, tail-env)) );
70     p_free(env);
71     if (*tail && delim!=DIR_SEP) tail++;
72     tail = tail0 = p_strncat(head, tail, 0);
73     p_free(head);
74     head = 0;
75   }
76 
77   /* handle paths beginning with . or ~ or relative pathnames */
78   if (tail[0]=='.') {
79     head = yCWD;
80     if (!tail[1]) tail++;
81     else if (tail[1]==DIR_SEP) tail += 2;
82   } else if (tail[0]=='~') {
83     char *user = tail++;
84     while (tail[0]) if ((tail++)[0] == DIR_SEP) break;
85     user = p_strncat(0, user, tail-user);
86     head = p_native(user);
87     freeHead = 1;
88     p_free(user);
89   } else if (!YIsAbsolute(tail)) {
90     head = yCWD;
91   }
92 
93   /* count number of leading ..s */
94   while (tail[0]=='.') {
95     if (tail[1]=='.') {
96       if (!tail[2]) {
97         tail += 2;
98         nRemove++;
99       } else if (tail[2]==DIR_SEP) {
100         tail += 3;
101         nRemove++;
102       }
103     } else if (tail[1]==DIR_SEP) {
104       tail += 2;
105     } else if (!tail[1]) {
106       tail++;
107     } else {
108       break;
109     }
110   }
111 
112   /* strip nRemove parent directories (but stop at root) */
113   if (nRemove && head) {
114     char *old = head;
115     head = p_strcpy(head);
116     if (freeHead) p_free(old);
117     else freeHead = 1;
118     path = head+strlen(head)-1;   /* guaranteed to point to DIR_SEP */
119     do {
120       while (path>head && *(--path)!=DIR_SEP);
121     } while (--nRemove);
122     path[1] = '\0';
123   }
124 
125   path = p_strncat(head, tail, 0);
126   if (freeHead) p_free(head);
127   if (tail0) p_free(tail0);
128   return path;
129 }
130 
131 /* strip leading directory names from a pathname */
132 char *
YNameTail(const char * name)133 YNameTail(const char *name)
134 {
135   const char *nm = name;
136   if (!nm) return 0;
137   nm += strlen(nm);
138   while (nm>name && *nm!=DIR_SEP) nm--;
139   if (*nm!=DIR_SEP) return p_strcpy(nm);
140   else return p_strcpy(nm+1);
141 }
142 
143 /* return leading directory names of a pathname, including trailing /
144  * returns 0 if no occurrences of / in pathname */
145 char *
YNameHead(const char * name)146 YNameHead(const char *name)
147 {
148   const char *nm = name;
149   if (!nm) return 0;
150   nm += strlen(nm);
151   while (nm>name && *nm!=DIR_SEP) nm--;
152   if (*nm!=DIR_SEP) return 0;
153   else return p_strncat(0, name, nm-name+1);
154 }
155 
156 /* ensure that a name ends in /, so it can be used as a path prefix */
157 void
YNameToHead(char ** name)158 YNameToHead(char **name)
159 {
160   char *head = *name;
161   long n = head? strlen(head) : 0;
162   if (n<1 || head[n-1]!=DIR_SEP) {
163     head = p_strncat(head, DIR_SEP_S, 0);
164     p_free(*name);
165     *name = head;
166   }
167 }
168 
169 int
YIsAbsolute(const char * name)170 YIsAbsolute(const char *name)
171 {
172   if (name[0]==DIR_SEP || name[0]=='~') return 1;
173   /* handle MS Windows a:/blahblah */
174   if (name[0] && name[1]==':' && name[2]==DIR_SEP &&
175       ((name[0]>='A' && name[0]<='Z') || (name[0]>='a' && name[0]<='z')))
176     return 1;
177   /* handle MS Windows \\server\path */
178   if (name[0]=='\\' && name[1]=='\\') return 1;
179   return 0;
180 }
181 
182 int
YIsDotRelative(const char * name)183 YIsDotRelative(const char *name)
184 {
185   return name[0]=='.' &&
186     (!name[1] || (name[1]==DIR_SEP ||
187                   (name[1]=='.' && (!name[2] || name[2]==DIR_SEP))));
188 }
189