1 /*************
2  * Various utility functions.
3  * 2002 R. Oktas, <roktas@omu.edu.tr>
4  ************/
5 
6 #include "ngspice/ngspice.h"
7 #include "util.h"
8 
9 /* **************************************************************** */
10 /*                                                                  */
11 /*                     Stuff for Filename Handling                  */
12 /*                                                                  */
13 /* **************************************************************** */
14 
15 /* Canonicalize PATH, and return a new path.  The new path differs from PATH
16    in that:
17        Multple `/'s are collapsed to a single `/'.
18        Leading `./'s and trailing `/.'s are removed.
19        Trailing `/'s are removed.
20        Non-leading `../'s and trailing `..'s are handled by removing
21        portions of the path.
22 
23     Stolen from Bash source (slightly modified).
24     Credit goes to Chet Ramey, et al. -- ro */
25 
26 char *
canonicalize_pathname(char * path)27 canonicalize_pathname(char *path)
28 {
29     int i, start;
30     char stub_char;
31     char *result;
32 
33     /* The result cannot be larger than the input PATH. */
34     result = copy(path);
35 
36     stub_char = '.';
37     if(*path == '/')
38         stub_char = '/';
39 
40     /* Walk along RESULT looking for things to compact. */
41     i = 0;
42     for (;;) {
43        if (!result[i])
44            break;
45 
46        while (result[i] && result[i] != '/')
47            i++;
48 
49        start = i++;
50 
51        /* If we didn't find any slashes, then there is nothing left to do. */
52        if (!result[start])
53            break;
54 
55        /* Handle multiple `/'s in a row. */
56        while (result[i] == '/')
57            i++;
58 
59 #if !defined (apollo)
60        if ((start + 1) != i)
61 #else
62        if ((start + 1) != i && (start != 0 || i != 2))
63 #endif /* apollo */
64        {
65            strcpy (result + start + 1, result + i);
66            i = start + 1;
67        }
68 
69 #if 0
70        /* Handle backslash-quoted `/'. */
71        if (start > 0 && result[start - 1] == '\\')
72            continue;
73 #endif
74 
75        /* Check for trailing `/'. */
76        if (start && !result[i]) {
77            zero_last:
78            result[--i] = '\0';
79            break;
80        }
81 
82        /* Check for `../', `./' or trailing `.' by itself. */
83        if (result[i] == '.') {
84            /* Handle trailing `.' by itself. */
85            if (!result[i + 1])
86                goto zero_last;
87 
88            /* Handle `./'. */
89            if (result[i + 1] == '/') {
90                strcpy(result + i, result + i + 1);
91                i = (start < 0) ? 0 : start;
92                continue;
93            }
94 
95            /* Handle `../' or trailing `..' by itself. */
96            if (result[i + 1] == '.' &&
97               (result[i + 2] == '/' || !result[i + 2])) {
98                while (--start > -1 && result[start] != '/')
99                    ;
100                strcpy(result + start + 1, result + i + 2);
101                i = (start < 0) ? 0 : start;
102                continue;
103            }
104        }
105     }
106 
107     if (!*result) {
108        *result = stub_char;
109        result[1] = '\0';
110     }
111     return (result);
112 }
113 
114 
115 /* Turn STRING (a pathname) into an absolute pathname, assuming that
116    DOT_PATH contains the symbolic location of `.'.  This always
117    returns a new string, even if STRING was an absolute pathname to
118    begin with.
119 
120    Stolen from Bash source (slightly modified).
121    Credit goes to Chet Ramey, et al. -- ro */
122 
absolute_pathname(char * string,char * dot_path)123 char * absolute_pathname(char *string, char *dot_path)
124 {
125   char *result;
126   size_t result_len;
127 
128   if (!dot_path || *string == '/')
129       result = copy(string);
130   else {
131       if (dot_path && dot_path[0]) {
132          result = TMALLOC(char, 2 + strlen(dot_path) + strlen(string));
133          strcpy(result, dot_path);
134          result_len = strlen(result);
135          if (result[result_len - 1] != '/') {
136              result[result_len++] = '/';
137              result[result_len] = '\0';
138          }
139       } else {
140          result = TMALLOC(char, 3 + strlen (string));
141          result[0] = '.'; result[1] = '/'; result[2] = '\0';
142          result_len = 2;
143       }
144 
145       strcpy(result + result_len, string);
146   }
147 
148   return (result);
149 }
150 
151 
152 /*
153 
154 char *
155 basename(const char *name)
156 {
157     char *base;
158     char *p;
159     static char *tmp = NULL;
160     int len;
161 
162     if (tmp) {
163         tfree(tmp);
164         tmp = NULL;
165     }
166 
167     if (!name || !strcmp(name, ""))
168         return "";
169 
170     if (!strcmp(name, "/"))
171         return "/";
172 
173     len = strlen(name);
174     if (name[len - 1] == '/') {
175         // ditch the trailing '/'
176         p = tmp = TMALLOC(char, len);
177         strncpy(p, name, len - 1);
178     } else {
179         p = (char *) name;
180     }
181 
182     for (base = p; *p; p++)
183         if (*p == '/')
184             base = p + 1;
185 
186     return base;
187 }
188 */
189 
190 
191 #if defined(HAS_WINGUI) || defined(_MSC_VER) || defined(__MINGW32__)
192 /* This function returns the path portion of name or "." if it is NULL.
193  * The returned string is a new allocation that must be freed by the
194  * caller */
ngdirname(const char * name)195 char *ngdirname(const char *name)
196 {
197     /* If name not given, return "." */
198     if (name == (char *) NULL) {
199         return dup_string(".", 1);
200     }
201 
202     /* Offset to start of path name after any drive letter present */
203     const int start = (((name[0] >= 'a' && name[0] <= 'z') ||
204             (name[0] >= 'A' && name[0] <= 'Z')) && name[1] == ':') ? 2 : 0;
205 
206     /* Find last dir separator, if any, and return the input directory up to
207      * that separator or including it if it is the 1st char after any drive
208      *specification. */
209     {
210         const char *p0 = name + start; /* 1st char past drive */
211         const char *p;
212         for (p = p0 + strlen(name + start) - 1; p >= p0; --p) {
213             const char ch_cur = *p;
214             if (ch_cur == '/' || ch_cur == '\\') { /* at last dir sep */
215                 /* Stop copy at last dir sep or right after if
216                  * it is the first char after any drive spec.
217                  * In the second case return "[drive]<dir sep>" */
218                 const char * const end = p + (p == p0);
219                 return copy_substring(name, end);
220             }
221         }
222     }
223 
224     /* No directory separator found so return "[drive]." */
225     {
226         char * const ret = TMALLOC(char, 2 + start);
227         char *p = ret;
228         if (start) { /* Add drive letter if found */
229             *p++ = name[0];
230             *p++ = name[1];
231         }
232         *p++ = '.';
233         *p = '\0';
234         return ret;
235     }
236 } /* end of function ngdirname */
237 
238 #else
239 
240 char *
ngdirname(const char * name)241 ngdirname(const char *name)
242 {
243     char *ret;
244     const char *end;
245 
246     end = name ? strrchr(name, '/') : NULL;
247 
248     if(end && end == name)
249         end++;
250 
251     if(end)
252         ret = copy_substring(name, end);
253     else
254         ret = copy(".");
255 
256     return ret;
257 }
258 
259 #endif
260 
261 /* Replacement for fopen, when using wide chars (utf-16) */
262 #ifndef EXT_ASC
263 #if defined(__MINGW32__) || defined(_MSC_VER)
264 #undef BOOLEAN
265 #include <windows.h>
266 FILE *
newfopen(const char * fn,const char * md)267 newfopen(const char *fn, const char* md)
268 {
269     FILE* fp;
270     if (fn == NULL)
271         return NULL;
272     wchar_t wfn[BSIZE_SP];
273     wchar_t wmd[16];
274     MultiByteToWideChar(CP_UTF8, 0, md, -1, wmd, 15);
275     if (MultiByteToWideChar(CP_UTF8, 0, fn, -1, wfn, BSIZE_SP - 1) == 0) {
276         fprintf(stderr, "UTF-8 to UTF-16 conversion failed with 0x%x\n", GetLastError());
277         fprintf(stderr, "%s could not be converted\n", fn);
278         return NULL;
279     }
280     fp = _wfopen(wfn, wmd);
281 
282 /* If wide char fails, at least fopen may try the potentially ANSI encoded special characters like � */
283 #undef fopen
284     if (fp == NULL)
285         fp = fopen(fn, md);
286 #define fopen newfopen
287 
288     return fp;
289 }
290 #endif
291 #endif
292 
293