1 /*
2  *                             io/utils.c
3  *
4  *               c  Ronny Lorenz
5  *               Vienna RNA package
6  */
7 
8 #ifdef HAVE_CONFIG_H
9 #include "config.h"
10 #endif
11 
12 #include <stdio.h>
13 #include <stdlib.h>
14 #include <ctype.h>
15 #include <time.h>
16 #include <string.h>
17 #include <sys/types.h>
18 #include <sys/stat.h>
19 #include <stdint.h>
20 #include <stdarg.h>
21 #include <errno.h>
22 #include <libgen.h>
23 
24 #include "ViennaRNA/utils/basic.h"
25 #include "ViennaRNA/utils/strings.h"
26 #include "ViennaRNA/io/utils.h"
27 
28 #define PRIVATE  static
29 #define PUBLIC
30 
31 /*
32  #################################
33  # GLOBAL VARIABLES              #
34  #################################
35  */
36 
37 
38 /*
39  #################################
40  # PRIVATE VARIABLES             #
41  #################################
42  */
43 #ifdef _WIN32
44 #ifdef __MINGW32__
45 #include <direct.h>
46 #endif
47 #define DIRSEPC '\\'
48 #define DIRSEPS "\\"
49 #else
50 #define DIRSEPC '/'
51 #define DIRSEPS "/"
52 #endif
53 
54 /*
55  #################################
56  # PRIVATE FUNCTION DECLARATIONS #
57  #################################
58  */
59 PRIVATE int is_absolute_path(const char *p);
60 
61 
62 /*
63  #################################
64  # BEGIN OF FUNCTION DEFINITIONS #
65  #################################
66  */
67 PUBLIC void
vrna_file_copy(FILE * from,FILE * to)68 vrna_file_copy(FILE *from,
69                FILE *to)
70 {
71   int c;
72 
73   while ((c = getc(from)) != EOF)
74     (void)putc(c, to);
75 }
76 
77 
78 PUBLIC char *
vrna_read_line(FILE * fp)79 vrna_read_line(FILE *fp)
80 {
81   /* reads lines of arbitrary length from fp */
82 
83   char  s[512], *line, *cp;
84   int   len = 0, size = 0, l, l2;
85 
86   line = NULL;
87   do {
88     if (fgets(s, 512, fp) == NULL)
89       break;
90 
91     cp = strchr(s, '\n');
92     if (cp != NULL)
93       *cp = '\0';
94 
95     l2  = (int)strlen(s);
96     l   = len + l2;
97     if (l + 1 > size) {
98       size  = (int)((l + 1) * 1.2);
99       line  = (char *)vrna_realloc(line, size * sizeof(char));
100     }
101     memcpy(line + len,
102            s,
103            sizeof(char) * l2);
104 
105     line[l] = '\0';
106     len = l;
107   } while (cp == NULL);
108 
109   return line;
110 }
111 
112 
113 PUBLIC int
vrna_mkdir_p(const char * path)114 vrna_mkdir_p(const char *path)
115 {
116   struct stat sb;
117   char        *slash, *ptr;
118   int         done = 0;
119 
120   if (!is_absolute_path(path))
121     ptr = vrna_strdup_printf(".%c%s", DIRSEPC, path);
122   else
123     ptr = strdup(path);
124 
125   slash = ptr;
126 
127   while (!done) {
128     slash += strspn(slash, DIRSEPS);
129     slash += strcspn(slash, DIRSEPS);
130 
131     done    = (*slash == '\0');
132     *slash  = '\0';
133 
134     if (stat(ptr, &sb)) {
135 #ifdef _WIN32
136       if (errno != ENOENT || (_mkdir(ptr) &&
137                               errno != EEXIST)) {
138 #else
139       if (errno != ENOENT || (mkdir(ptr, 0777) &&
140                               errno != EEXIST)) {
141 #endif
142         vrna_message_warning("Can't create directory %s", ptr);
143         free(ptr);
144         return -1;
145       }
146     } else if (!S_ISDIR(sb.st_mode)) {
147       vrna_message_warning("File exists but is not a directory %s: %s", ptr, strerror(ENOTDIR));
148       free(ptr);
149       return -1;
150     }
151 
152     *slash = DIRSEPC;
153   }
154 
155   free(ptr);
156   return 0;
157 }
158 
159 
160 PUBLIC char *
161 vrna_basename(const char *path)
162 {
163   char *name, *ptr;
164 
165   name = NULL;
166 
167   if (path) {
168     ptr = strrchr(path, DIRSEPC);
169 
170     if (ptr && (*(ptr + 1) != '\0'))
171       name = strdup(ptr + 1);
172     else if (!ptr)
173       name = strdup(path);
174   }
175 
176   return name;
177 }
178 
179 
180 PUBLIC char *
181 vrna_dirname(const char *path)
182 {
183   char  *name, *p, *ptr;
184   int   pos;
185 
186   name = NULL;
187 
188   if (path) {
189     if (!is_absolute_path(path))
190       ptr = vrna_strdup_printf(".%c%s", DIRSEPC, path);
191     else
192       ptr = strdup(path);
193 
194     pos = (int)strlen(ptr);
195     p   = ptr + pos;
196 
197     do  /* remove part after last separator */
198       *p = '\0';
199     while ((--p > ptr) && (*p != DIRSEPC));
200 
201     if (p > ptr)
202       name = ptr;
203   }
204 
205   return name;
206 }
207 
208 
209 PUBLIC char *
210 vrna_filename_sanitize(const char *name,
211                        const char *replacement)
212 {
213   if (name) {
214     const char    *ptr, *start, *illegal_chars;
215     char          *sanitized_name;
216     unsigned int  i, n;
217 
218     illegal_chars   = "\\/?%*:|\"<> ";
219     sanitized_name  = (char *)vrna_alloc(sizeof(char) * (strlen(name) + 1));
220     start           = name;
221     i               = 0;
222     while ((ptr = strpbrk(start, illegal_chars))) {
223       /* find illegal chars */
224       strncpy(sanitized_name + i, start, ptr - start);
225       i += ptr - start;
226       if (replacement && (*replacement))
227         sanitized_name[i++] = *replacement;
228 
229       start = ptr + 1; /* skip invalid character */
230     }
231     /* copy remaining part */
232     if (start < (name + strlen(name))) {
233       unsigned int diff = name - start + strlen(name);
234       strncpy(sanitized_name + i, start, diff);
235       i += diff;
236     }
237 
238     /* resize the output string to actual requirements */
239     sanitized_name    = (char *)vrna_realloc(sanitized_name, sizeof(char) * (i + 1));
240     sanitized_name[i] = '\0';
241 
242     /* check for reserved unix file names */
243     if ((!strcmp(sanitized_name, ".")) || (!strcmp(sanitized_name, ".."))) {
244       sanitized_name    = (char *)vrna_realloc(sanitized_name, sizeof(char));
245       sanitized_name[0] = '\0';
246     }
247 
248     /* check for length restrictions */
249     n = strlen(sanitized_name);
250     if (n > 255) {
251       char *suff = NULL;
252       /* try to leave file suffix, i.e. everything after last dot '.', intact */
253       if ((suff = strrchr(sanitized_name, '.')) && (sanitized_name + n - suff < 255)) {
254         unsigned int n_suff = sanitized_name + n - suff;
255         memmove(sanitized_name + (255 - n_suff), sanitized_name + n - n_suff,
256                 sizeof(char) * n_suff);
257       }
258 
259       sanitized_name      = (char *)vrna_realloc(sanitized_name, sizeof(char) * 256);
260       sanitized_name[255] = '\0';
261     }
262 
263     /* finally, return the sanitized file name */
264     return sanitized_name;
265   } else {
266     return NULL;
267   }
268 }
269 
270 
271 PUBLIC int
272 vrna_file_exists(const char *filename)
273 {
274   int           r = 0;
275 
276 #ifdef _WIN32
277   struct _stat  buf;
278   r = _stat(filename, &buf) == 0 ? 1 : 0;
279 #else
280   struct stat   buf;
281   r = stat(filename, &buf) == 0 ? 1 : 0;
282 #endif
283   return r;
284 }
285 
286 
287 #ifdef _WIN32
288 PRIVATE int
289 is_drive_char(const char c)
290 {
291   if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'))
292     return 1;
293 
294   return 0;
295 }
296 
297 
298 #endif
299 
300 
301 PRIVATE int
302 is_absolute_path(const char *p)
303 {
304   if (*p == DIRSEPC)
305     return 1;
306 
307 #ifdef _WIN32
308   if (is_drive_char((const char)*p) && (strlen(p) > 3))
309     if ((*(p + 1) == ':') && ((*(p + 2) == '\\') || (*(p + 2) == '/')))
310       return 1;
311 
312 #endif
313   return 0;
314 }
315