1 /**********
2 Copyright 1991 Regents of the University of California.  All rights reserved.
3 Modified: 2002 R. Oktas, <roktas@omu.edu.tr>
4 **********/
5 
6 #include "ngspice/defines.h"
7 #include "ngspice/ngspice.h"
8 #include "ngspice/stringskip.h"
9 #include "tilde.h"
10 
11 #ifdef HAVE_PWD_H
12 #include <pwd.h>
13 #endif
14 
15 #ifdef _WIN32
16 #undef BOOLEAN
17 #include <windows.h> /* win32 functions */
18 #include "shlobj.h"  /* SHGetFolderPath */
19 #endif
20 
21 static inline int copy_home_to_buf(size_t n_byte_dst, char **p_dst,
22         const char *src);
23 
24 /* XXX To prevent a name collision with `readline's `tilde_expand',
25    the original name: `tilde_expand' has changed to `tildexpand'. This
26    situation naturally brings to mind that `tilde_expand' could be used
27    directly from `readline' (since it will already be included if we
28    wish to activate the `readline' support). Following implementation of
29    'tilde expanding' has some problems which constitutes another good
30    reason why it should be replaced: eg. it returns NULL which should
31    not behave this way, IMHO.  Anyway... Don't care for the moment, may
32    be in the future. -- ro */
tildexpand(const char * string)33 char *tildexpand(const char *string)
34 {
35     /* If no string passed, return NULL */
36     if (!string) {
37         return NULL;
38     }
39 
40     string = skip_ws(string); /* step past leading whitespace */
41 
42     /* If the string does not begin with a tilde, there is no ~ to expand */
43     if (*string != '~') {
44         return copy(string);
45     }
46 
47     ++string; /* step past tilde */
48 
49     /* Test for home of current user */
50     if (*string == '\0' || *string ==  DIR_TERM) {
51         char *sz_home;
52         const int n_char_home = get_local_home(0, &sz_home);
53         if (n_char_home < 0) {
54             return copy(string); /* Strip the ~ and return the rest */
55         }
56         const size_t n_char_rest = strlen(string);
57         sz_home = TREALLOC(char, sz_home, (size_t) n_char_home + n_char_rest + 1);
58         strcpy(sz_home + n_char_home, string);
59         return sz_home;
60     }
61 
62 #ifdef HAVE_PWD_H
63     /* ~bob -- Get name of user and find home for that user */
64     {
65         char buf_fixed[100];
66         char *buf = buf_fixed;
67         const char * const usr_start = string;
68         char c;
69         while ((c = *string) && c != '/') {
70             string++;
71         }
72         const char * const usr_end = string;
73         const size_t n_char_usr = (size_t) (usr_end - usr_start);
74         const size_t n_byte_usr = n_char_usr + 1;
75         if (n_byte_usr > sizeof buf_fixed) {
76             buf = TMALLOC(char, n_byte_usr);
77         }
78         (void) memcpy(buf, usr_start, n_char_usr);
79         buf[n_char_usr] = '\0';
80 
81 
82         char *sz_home;
83         const int n_char_home = get_usr_home(buf, 0, &sz_home);
84         if (buf != buf_fixed) { /* free allocated buffer for user name */
85             txfree(buf);
86         }
87         if (n_char_home < 0) {
88             return copy(usr_start); /* Strip the ~ and return the rest */
89         }
90         const size_t n_char_rest = strlen(string);
91         sz_home = TREALLOC(char, sz_home, (size_t) n_char_home + n_char_rest + 1);
92         strcpy(sz_home + n_char_home, string);
93         return sz_home;
94     }
95 
96 #else
97     /* ~abc is meaningless */
98     return copy(string); /* Again strip ~ and return rest */
99 #endif
100 } /* end of function tildexpand */
101 
102 
103 
104 
105 /* Get value of "HOME" for the current user and copy to *p_buf if
106  * the value is less than n_byte_buf characters long. Otherwise
107  * allocate a buffer of the minimum required size.
108  *
109  * Return values
110  * >0: Number of characters copied to *p_buf, excluding the trailing null
111  * -1: A value for HOME could not be obtained.
112  *
113  * Remarks:
114  * This function does not free any allocation at *p_buf, allowing a
115  * fixed buffer to be passed to it.
116  */
get_local_home(size_t n_byte_buf,char ** p_buf)117 int get_local_home(size_t n_byte_buf, char **p_buf)
118 {
119     char *sz_home = (char *) NULL;
120 #ifdef _WIN32
121     char buf_sh_path[MAX_PATH];
122 #endif
123 
124     do {
125         /* First thing to try is an environment variable HOME */
126         if ((sz_home = getenv("HOME")) != (char *) NULL) {
127             break;
128         }
129 
130 #if defined(_WIN32)
131         /* If Windows, try an env var USERPROFILE next */
132         if ((sz_home = getenv("USERPROFILE")) != (char *) NULL) {
133             break;
134         }
135 
136         /* For Windows, the folder path CSIDL_PERSONAL is tried next */
137         if (SUCCEEDED(SHGetFolderPath(NULL, CSIDL_PERSONAL, NULL, 0,
138                 buf_sh_path))) {
139             sz_home = buf_sh_path;
140             break;
141         }
142 #elif defined(HAVE_PWD_H) /* _WIN32 and HAVE_PWD_H are mutually exclusive */
143         /* Get home information for the current user */
144         {
145             struct passwd *pw;
146             pw = getpwuid(getuid());
147             if (pw) {
148                 sz_home = pw->pw_dir;
149             }
150         }
151 #endif
152     } while (0);
153 
154     if (sz_home == (char *) NULL) { /* did not find a HOME value */
155         return -1;
156     }
157 
158     /* Copy home value to buffer */
159     return copy_home_to_buf(n_byte_buf, p_buf, sz_home);
160 } /* end of function get_local_home */
161 
162 
163 
164 #ifdef HAVE_PWD_H
165 /* Get value of "HOME" for usr and copy to *p_buf if
166  * the value is less than n_byte_buf characters long. Otherwise
167  * allocate a buffer of the minimum required size.
168  *
169  * Return values
170  * >0: Number of characters copied to *pp_buf, excluding the trailing null
171  * -1: A value for HOME could not be obtained.
172  *
173  * Remarks:
174  * This function does not free any allocation at *p_buf, allowing a
175  * fixed buffer to be passed to it.
176  */
get_usr_home(const char * usr,size_t n_byte_buf,char ** p_buf)177 int get_usr_home(const char *usr, size_t n_byte_buf, char **p_buf)
178 {
179     struct passwd * const pw = getpwnam(usr);
180     if (pw) {
181         /* Copy home value to buffer */
182         return copy_home_to_buf(n_byte_buf, p_buf, pw->pw_dir);
183     }
184 
185     return -1;
186 } /* end of function get_usr_home */
187 #endif /* HAVE_PWD_H */
188 
189 
190 
191 /* This function copies home value src to the buffer at *p_dst, allocating
192  * a larger buffer if required.
193  *
194  * Parameters
195  * n_byte_dst: Size of supplied destination buffer
196  * p_dst: Address containing address of supplied buffer on input. May be
197  *      given the address of a larger buffer allocation if the input buffer
198  *      is too small.
199  * src: Address of HOME value
200  *
201  * Return values
202  * number of characters copied excluding terminating null
203  *
204  * Remarks:
205  * This function does not free any allocation at *p_dst, allowing a
206  * fixed buffer to be passed to it.
207  */
copy_home_to_buf(size_t n_byte_dst,char ** p_dst,const char * src)208 static inline int copy_home_to_buf(size_t n_byte_dst, char **p_dst,
209         const char *src)
210 {
211     const size_t n_char_src = strlen(src); /* Size of HOME value */
212     const size_t n_byte_src = n_char_src + 1;
213 
214     /* Allocate dst if input buffer too small */
215     if (n_byte_src > n_byte_dst) { /* too big */
216         *p_dst = TMALLOC(char, n_byte_src);
217     }
218 
219     (void) memcpy(*p_dst, src, n_byte_src);
220 
221     return (int) n_char_src;
222 } /* end of function copy_home_to_buf */
223 
224 
225 
226