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