Line data Source code
1 : /* Copyright (C) 2000-2004 by George Williams */
2 : /*
3 : * Redistribution and use in source and binary forms, with or without
4 : * modification, are permitted provided that the following conditions are met:
5 :
6 : * Redistributions of source code must retain the above copyright notice, this
7 : * list of conditions and the following disclaimer.
8 :
9 : * Redistributions in binary form must reproduce the above copyright notice,
10 : * this list of conditions and the following disclaimer in the documentation
11 : * and/or other materials provided with the distribution.
12 :
13 : * The name of the author may not be used to endorse or promote products
14 : * derived from this software without specific prior written permission.
15 :
16 : * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
17 : * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18 : * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
19 : * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20 : * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21 : * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22 : * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23 : * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24 : * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25 : * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 : */
27 :
28 : #include <stdio.h>
29 : #include "inc/basics.h"
30 : #include "ustring.h"
31 : #include "fileutil.h"
32 : #include "gfile.h"
33 : #include <sys/param.h>
34 : #include <sys/types.h>
35 : #include <sys/stat.h> /* for mkdir */
36 : #include <unistd.h>
37 : #include <glib.h>
38 : #include <glib/gstdio.h>
39 : #include <errno.h> /* for mkdir_p */
40 :
41 : static char dirname_[MAXPATHLEN+1];
42 : #if !defined(__MINGW32__)
43 : #include <pwd.h>
44 : #else
45 : #include <windows.h>
46 : #include <shlobj.h>
47 : #endif
48 :
49 : /**
50 : * \brief Removes the extension from a file path, if it exists.
51 : * This method assumes that the path is already normalized.
52 : * \param path The path to be modified. Is modified in-place.
53 : * \return A pointer to the input path.
54 : */
55 0 : char *GFileRemoveExtension(char *path) {
56 0 : char *ext = strrchr(path, '.');
57 0 : if (ext) {
58 0 : char *fp = strrchr(path, '/');
59 0 : if (!fp || ext > fp) {
60 0 : *ext = '\0';
61 : }
62 : }
63 0 : return path;
64 : }
65 :
66 : /**
67 : * \brief Normalizes the file path as necessary.
68 : * On Windows, this means changing backlashes to slashes.
69 : *
70 : * \param path The file path to be modified. Is modified in-place.
71 : * \return A pointer to the input path
72 : */
73 0 : char *GFileNormalizePath(char *path) {
74 : #if defined(__MINGW32__)
75 : char *ptr;
76 : for(ptr = path; *ptr; ptr++) {
77 : if (*ptr == '\\') {
78 : *ptr = '/';
79 : }
80 : }
81 : #endif
82 0 : return path;
83 : }
84 :
85 : /**
86 : * \brief Normalizes the file path as necessary.
87 : * Unicode version of GFileNormalizePath.
88 : *
89 : * \param path The file path to be modified. Is modified in-place.
90 : * \return A pointer to the input path
91 : */
92 0 : unichar_t *u_GFileNormalizePath(unichar_t *path) {
93 : #if defined(__MINGW32__)
94 : unichar_t *ptr;
95 : for (ptr = path; *ptr; ptr++) {
96 : if (*ptr == '\\') {
97 : *ptr = '/';
98 : }
99 : }
100 : #endif
101 0 : return path;
102 : }
103 :
104 : /* make directories. make parent directories as needed, with no error if
105 : * the path already exists */
106 112 : int mkdir_p(const char *path, mode_t mode) {
107 : struct stat st;
108 : const char *e;
109 112 : char *p = NULL;
110 : char tmp[1024];
111 : size_t len;
112 : int r;
113 :
114 : /* ensure the path is valid */
115 112 : if(!(e = strrchr(path, '/')))
116 0 : return -EINVAL;
117 : /* ensure path is a directory */
118 112 : r = stat(path, &st);
119 112 : if (r == 0 && !S_ISDIR(st.st_mode))
120 0 : return -ENOTDIR;
121 :
122 : /* copy the pathname */
123 112 : snprintf(tmp, sizeof(tmp),"%s", path);
124 112 : len = strlen(tmp);
125 112 : if(tmp[len - 1] == '/')
126 0 : tmp[len - 1] = 0;
127 :
128 : /* iterate mkdir over the path */
129 3360 : for(p = tmp + 1; *p; p++)
130 3248 : if(*p == '/') {
131 336 : *p = 0;
132 336 : r = mkdir(tmp, mode);
133 336 : if (r < 0 && errno != EEXIST)
134 0 : return -errno;
135 336 : *p = '/';
136 : }
137 :
138 : /* try to make the whole path */
139 112 : r = mkdir(tmp, mode);
140 112 : if(r < 0 && errno != EEXIST)
141 0 : return -errno;
142 : /* creation successful or the file already exists */
143 112 : return EXIT_SUCCESS;
144 : }
145 :
146 : /* Wrapper for formatted variable list printing. */
147 112 : char *smprintf(const char *fmt, ...) {
148 : va_list fmtargs;
149 : char *ret;
150 : int len;
151 :
152 112 : va_start(fmtargs, fmt);
153 112 : len = vsnprintf(NULL, 0, fmt, fmtargs);
154 112 : va_end(fmtargs);
155 112 : ret = malloc(++len);
156 112 : if (ret == NULL) {
157 0 : perror("malloc");
158 0 : exit(EXIT_FAILURE);
159 : }
160 :
161 112 : va_start(fmtargs, fmt);
162 112 : vsnprintf(ret, len, fmt, fmtargs);
163 112 : va_end(fmtargs);
164 112 : return ret;
165 : }
166 :
167 0 : char *GFileGetHomeDir(void) {
168 : #if defined(__MINGW32__)
169 : char* dir = getenv("HOME");
170 : if(!dir)
171 : dir = getenv("USERPROFILE");
172 : if(dir){
173 : char* buffer = copy(dir);
174 : GFileNormalizePath(buffer);
175 : return buffer;
176 : }
177 : return NULL;
178 : #else
179 : static char *dir;
180 : uid_t uid;
181 : struct passwd *pw;
182 :
183 0 : dir = getenv("HOME");
184 0 : if ( dir!=NULL )
185 0 : return( copy(dir) );
186 :
187 0 : uid = getuid();
188 0 : while ( (pw=getpwent())!=NULL ) {
189 0 : if ( pw->pw_uid==uid ) {
190 0 : dir = copy(pw->pw_dir);
191 0 : endpwent();
192 0 : return( dir );
193 : }
194 : }
195 0 : endpwent();
196 0 : return( NULL );
197 : #endif
198 : }
199 :
200 0 : unichar_t *u_GFileGetHomeDir(void) {
201 0 : unichar_t* dir = NULL;
202 0 : char* tmp = GFileGetHomeDir();
203 0 : if( tmp ) {
204 0 : dir = uc_copy(tmp);
205 0 : free(tmp);
206 : }
207 0 : return dir;
208 : }
209 :
210 0 : static void savestrcpy(char *dest,const char *src) {
211 : for (;;) {
212 0 : *dest = *src;
213 0 : if ( *dest=='\0' )
214 0 : break;
215 0 : ++dest; ++src;
216 0 : }
217 0 : }
218 :
219 202249 : char *GFileGetAbsoluteName(const char *name, char *result, size_t rsiz) {
220 : /* result may be the same as name */
221 : char buffer[1000];
222 :
223 202249 : if ( ! GFileIsAbsolute(name) ) {
224 : char *pt, *spt, *rpt, *bpt;
225 :
226 4432 : if ( dirname_[0]=='\0' ) {
227 16 : getcwd(dirname_,sizeof(dirname_));
228 : }
229 4432 : strcpy(buffer,dirname_);
230 4432 : if ( buffer[strlen(buffer)-1]!='/' )
231 4432 : strcat(buffer,"/");
232 4432 : strcat(buffer,name);
233 : #if defined(__MINGW32__)
234 : GFileNormalizePath(buffer);
235 : #endif
236 :
237 : /* Normalize out any .. */
238 4432 : spt = rpt = buffer;
239 39888 : while ( *spt!='\0' ) {
240 31024 : if ( *spt=='/' ) {
241 31024 : if ( *++spt=='\0' )
242 0 : break;
243 : }
244 31024 : for ( pt = spt; *pt!='\0' && *pt!='/'; ++pt );
245 31024 : if ( pt==spt ) /* Found // in a path spec, reduce to / (we've*/
246 0 : savestrcpy(spt,spt+1); /* skipped past the :// of the machine name) */
247 31024 : else if ( pt==spt+1 && spt[0]=='.' && *pt=='/' ) { /* Noop */
248 0 : savestrcpy(spt,spt+2);
249 31024 : } else if ( pt==spt+2 && spt[0]=='.' && spt[1]=='.' ) {
250 0 : for ( bpt=spt-2 ; bpt>rpt && *bpt!='/'; --bpt );
251 0 : if ( bpt>=rpt && *bpt=='/' ) {
252 0 : savestrcpy(bpt,pt);
253 0 : spt = bpt;
254 : } else {
255 0 : rpt = pt;
256 0 : spt = pt;
257 : }
258 : } else
259 31024 : spt = pt;
260 : }
261 4432 : name = buffer;
262 4432 : if ( rsiz>sizeof(buffer)) rsiz = sizeof(buffer); /* Else valgrind gets unhappy */
263 : }
264 202249 : if (result!=name) {
265 202249 : strncpy(result,name,rsiz);
266 202249 : result[rsiz-1]='\0';
267 : #if defined(__MINGW32__)
268 : GFileNormalizePath(result);
269 : #endif
270 : }
271 202249 : return(result);
272 : }
273 :
274 0 : char *GFileMakeAbsoluteName(char *name) {
275 : char buffer[1025];
276 :
277 0 : GFileGetAbsoluteName(name,buffer,sizeof(buffer));
278 0 : return( copy(buffer));
279 : }
280 :
281 40 : char *GFileBuildName(char *dir,char *fname,char *buffer,size_t size) {
282 : int len;
283 :
284 40 : if ( dir==NULL || *dir=='\0' ) {
285 0 : if ( strlen( fname )<size-1 ) /* valgrind didn't like my strncpies but this complication makes it happy */
286 0 : savestrcpy(buffer,fname);
287 : else {
288 0 : strncpy(buffer,fname,size-1);
289 0 : buffer[size-1]='\0';
290 : }
291 : } else {
292 40 : if ( buffer!=dir ) {
293 40 : if ( strlen( dir )<size-3 )
294 40 : strcpy(buffer,dir);
295 : else {
296 0 : strncpy(buffer,dir,size-3);
297 0 : buffer[size-3]='\0';
298 : }
299 : }
300 40 : len = strlen(buffer);
301 40 : if ( buffer[len-1]!='/' )
302 40 : buffer[len++] = '/';
303 40 : if ( strlen( fname )<size-1 )
304 40 : strcpy(buffer+len,fname);
305 : else {
306 0 : strncpy(buffer+len,fname,size-len-1);
307 0 : buffer[size-1]='\0';
308 : }
309 : }
310 40 : return( buffer );
311 : }
312 :
313 : /* Given a filename in a directory, pick the directory out of it, and */
314 : /* create a new filename using that directory and the given nametail */
315 0 : char *GFileReplaceName(char *oldname,char *fname,char *buffer,size_t size) {
316 : int len;
317 : char *dirend;
318 :
319 0 : dirend = strrchr(oldname,'/');
320 0 : if ( dirend == NULL ) {
321 0 : strncpy(buffer,fname,size-1);
322 0 : buffer[size-1]='\0';
323 : } else {
324 0 : *dirend = '\0';
325 0 : if ( buffer!=oldname ) {
326 0 : strncpy(buffer,oldname,size-3);
327 0 : buffer[size-3]='\0';
328 : }
329 0 : len = strlen(buffer);
330 0 : *dirend = '/';
331 0 : buffer[len++] = '/';
332 0 : strncpy(buffer+len,fname,size-len-1);
333 0 : buffer[size-1]='\0';
334 : }
335 0 : return( buffer );
336 : }
337 :
338 63 : char *GFileNameTail(const char *oldname) {
339 63 : char *pt = 0;
340 :
341 63 : pt = strrchr(oldname,'/');
342 :
343 : // a final slash was found, so we know that p+1 is a valid
344 : // address in the string.
345 63 : if ( pt )
346 63 : return( pt+1);
347 :
348 0 : return( (char *)oldname );
349 : }
350 :
351 0 : char *GFileAppendFile(char *dir,char *name,int isdir) {
352 : char *ret, *pt;
353 :
354 0 : ret = (char *) malloc((strlen(dir)+strlen(name)+3));
355 0 : strcpy(ret,dir);
356 0 : pt = ret+strlen(ret);
357 0 : if ( pt>ret && pt[-1]!='/' )
358 0 : *pt++ = '/';
359 0 : strcpy(pt,name);
360 0 : if ( isdir ) {
361 0 : pt += strlen(pt);
362 0 : if ( pt>ret && pt[-1]!='/' ) {
363 0 : *pt++ = '/';
364 0 : *pt = '\0';
365 : }
366 : }
367 0 : return(ret);
368 : }
369 :
370 202249 : int GFileIsAbsolute(const char *file) {
371 : #if defined(__MINGW32__)
372 : if( (file[1]==':') && (('a'<=file[0] && file[0]<='z') || ('A'<=file[0] && file[0]<='Z')) )
373 : return ( true );
374 : #else
375 202249 : if ( *file=='/' )
376 197817 : return( true );
377 : #endif
378 4432 : if ( strstr(file,"://")!=NULL )
379 0 : return( true );
380 :
381 4432 : return( false );
382 : }
383 :
384 64 : int GFileIsDir(const char *file) {
385 : struct stat info;
386 64 : if ( stat(file, &info)==-1 )
387 0 : return 0;
388 : else
389 64 : return( S_ISDIR(info.st_mode) );
390 : }
391 :
392 10 : int GFileExists(const char *file) {
393 10 : return( access(file,0)==0 );
394 : }
395 :
396 0 : int GFileModifyable(const char *file) {
397 0 : return( access(file,02)==0 );
398 : }
399 :
400 0 : int GFileModifyableDir(const char *file) {
401 : char buffer[1025], *pt;
402 :
403 0 : buffer[1024]=0;
404 0 : strncpy(buffer,file,1024);
405 0 : pt = strrchr(buffer,'/');
406 0 : if ( pt==NULL )
407 0 : strcpy(buffer,".");
408 : else
409 0 : *pt='\0';
410 0 : return( GFileModifyable(buffer) );
411 : }
412 :
413 0 : int GFileReadable(const char *file) {
414 0 : return( access(file,04)==0 );
415 : }
416 :
417 : /**
418 : * Removes a file or folder.
419 : *
420 : * @param [in] path The path to be removed.
421 : * @param [in] recursive Specify true to remove a folder and all of its
422 : * sub-contents.
423 : * @return true if the deletion was successful or the path does not exist. It
424 : * will fail if trying to remove a directory that is not empty and
425 : * where `recursive` is false.
426 : */
427 0 : int GFileRemove(const char *path, int recursive) {
428 : GDir *dir;
429 : const gchar *entry;
430 :
431 0 : if (g_remove(path) != 0) {
432 0 : if (recursive && (dir = g_dir_open(path, 0, NULL))) {
433 0 : while ((entry = g_dir_read_name(dir))) {
434 0 : gchar *fpath = g_build_filename(path, entry, NULL);
435 0 : if (g_remove(fpath) != 0 && GFileIsDir(fpath)) {
436 0 : GFileRemove(fpath, recursive);
437 : }
438 0 : g_free(fpath);
439 : }
440 0 : g_dir_close(dir);
441 : }
442 0 : return (g_remove(path) == 0 || !GFileExists(path));
443 : }
444 :
445 0 : return true;
446 : }
447 :
448 6 : int GFileMkDir(const char *name) {
449 6 : return( mkdir(name,0755));
450 : }
451 :
452 0 : int GFileRmDir(const char *name) {
453 0 : return(rmdir(name));
454 : }
455 :
456 0 : int GFileUnlink(const char *name) {
457 0 : return(unlink(name));
458 : }
459 :
460 80 : char *_GFile_find_program_dir(char *prog) {
461 80 : char *pt, *path, *program_dir=NULL;
462 : char filename[2000];
463 :
464 : #if defined(__MINGW32__)
465 : char* pt1 = strrchr(prog, '/');
466 : char* pt2 = strrchr(prog, '\\');
467 : if(pt1<pt2) pt1=pt2;
468 : if(pt1)
469 : program_dir = copyn(prog, pt1-prog);
470 : else if( (path = getenv("PATH")) != NULL ){
471 : char* tmppath = copy(path);
472 : path = tmppath;
473 : for(;;){
474 : pt1 = strchr(path, ';');
475 : if(pt1) *pt1 = '\0';
476 : sprintf(filename,"%s/%s", path, prog);
477 : if ( access(filename,1)!= -1 ) {
478 : program_dir = copy(path);
479 : break;
480 : }
481 : if(!pt1) break;
482 : path = pt1+1;
483 : }
484 : free(tmppath);
485 : }
486 : #else
487 80 : if ( (pt = strrchr(prog,'/'))!=NULL )
488 80 : program_dir = copyn(prog,pt-prog);
489 0 : else if ( (path = getenv("PATH"))!=NULL ) {
490 0 : while ((pt = strchr(path,':'))!=NULL ) {
491 0 : sprintf(filename,"%.*s/%s", (int)(pt-path), path, prog);
492 : /* Under cygwin, applying access to "potrace" will find "potrace.exe" */
493 : /* no need for special check to add ".exe" */
494 0 : if ( access(filename,1)!= -1 ) {
495 0 : program_dir = copyn(path,pt-path);
496 0 : break;
497 : }
498 0 : path = pt+1;
499 : }
500 0 : if ( program_dir==NULL ) {
501 0 : sprintf(filename,"%s/%s", path, prog);
502 0 : if ( access(filename,1)!= -1 )
503 0 : program_dir = copy(path);
504 : }
505 : }
506 : #endif
507 :
508 80 : if ( program_dir==NULL )
509 0 : return( NULL );
510 80 : GFileGetAbsoluteName(program_dir,filename,sizeof(filename));
511 80 : free(program_dir);
512 80 : program_dir = copy(filename);
513 80 : return( program_dir );
514 : }
515 :
516 0 : unichar_t *u_GFileGetAbsoluteName(unichar_t *name, unichar_t *result, int rsiz) {
517 : /* result may be the same as name */
518 : unichar_t buffer[1000];
519 :
520 0 : if ( ! u_GFileIsAbsolute(name) ) {
521 : unichar_t *pt, *spt, *rpt, *bpt;
522 :
523 0 : if ( dirname_[0]=='\0' ) {
524 0 : getcwd(dirname_,sizeof(dirname_));
525 : }
526 0 : uc_strcpy(buffer,dirname_);
527 0 : if ( buffer[u_strlen(buffer)-1]!='/' )
528 0 : uc_strcat(buffer,"/");
529 0 : u_strcat(buffer,name);
530 0 : u_GFileNormalizePath(buffer);
531 :
532 : /* Normalize out any .. */
533 0 : spt = rpt = buffer;
534 0 : while ( *spt!='\0' ) {
535 0 : if ( *spt=='/' ) ++spt;
536 0 : for ( pt = spt; *pt!='\0' && *pt!='/'; ++pt );
537 0 : if ( pt==spt ) /* Found // in a path spec, reduce to / (we've*/
538 0 : u_strcpy(spt,pt); /* skipped past the :// of the machine name) */
539 0 : else if ( pt==spt+1 && spt[0]=='.' && *pt=='/' ) /* Noop */
540 0 : u_strcpy(spt,spt+2);
541 0 : else if ( pt==spt+2 && spt[0]=='.' && spt[1]=='.' ) {
542 0 : for ( bpt=spt-2 ; bpt>rpt && *bpt!='/'; --bpt );
543 0 : if ( bpt>=rpt && *bpt=='/' ) {
544 0 : u_strcpy(bpt,pt);
545 0 : spt = bpt;
546 : } else {
547 0 : rpt = pt;
548 0 : spt = pt;
549 : }
550 : } else
551 0 : spt = pt;
552 : }
553 0 : name = buffer;
554 : }
555 0 : if (result!=name) {
556 0 : u_strncpy(result,name,rsiz);
557 0 : result[rsiz-1]='\0';
558 0 : u_GFileNormalizePath(result);
559 : }
560 0 : return(result);
561 : }
562 :
563 0 : unichar_t *u_GFileBuildName(unichar_t *dir,unichar_t *fname,unichar_t *buffer,int size) {
564 : int len;
565 :
566 0 : if ( dir==NULL || *dir=='\0' ) {
567 0 : u_strncpy(buffer,fname,size-1);
568 0 : buffer[size-1]='\0';
569 : } else {
570 0 : if ( buffer!=dir ) {
571 0 : u_strncpy(buffer,dir,size-3);
572 0 : buffer[size-3]='\0';
573 : }
574 0 : len = u_strlen(buffer);
575 0 : if ( buffer[len-1]!='/' )
576 0 : buffer[len++] = '/';
577 0 : u_strncpy(buffer+len,fname,size-len-1);
578 0 : buffer[size-1]='\0';
579 : }
580 0 : return( buffer );
581 : }
582 :
583 : /* Given a filename in a directory, pick the directory out of it, and */
584 : /* create a new filename using that directory and the given nametail */
585 0 : unichar_t *u_GFileReplaceName(unichar_t *oldname,unichar_t *fname,unichar_t *buffer,int size) {
586 : int len;
587 : unichar_t *dirend;
588 :
589 0 : dirend = u_strrchr(oldname,'/');
590 0 : if ( dirend == NULL ) {
591 0 : u_strncpy(buffer,fname,size-1);
592 0 : buffer[size-1]='\0';
593 : } else {
594 0 : *dirend = '\0';
595 0 : if ( buffer!=oldname ) {
596 0 : u_strncpy(buffer,oldname,size-3);
597 0 : buffer[size-3]='\0';
598 : }
599 0 : len = u_strlen(buffer);
600 0 : *dirend = '/';
601 0 : buffer[len++] = '/';
602 0 : u_strncpy(buffer+len,fname,size-len-1);
603 0 : buffer[size-1]='\0';
604 : }
605 0 : return( buffer );
606 : }
607 :
608 0 : unichar_t *u_GFileNameTail(const unichar_t *oldname) {
609 : unichar_t *pt;
610 :
611 0 : pt = u_strrchr(oldname,'/');
612 0 : if ( pt !=NULL )
613 0 : return( pt+1);
614 : else
615 0 : return( (unichar_t *)oldname );
616 : }
617 :
618 : /**
619 : * Remove the 'root' part of the file path if it is absolute;
620 : * On Unix this is '/' and on Windows this is for e.g. 'C:/'
621 : */
622 0 : static unichar_t *u_GFileRemoveRoot(unichar_t *path) {
623 : //May happen on Windows too e.g. CygWin
624 0 : if (*path == '/') {
625 0 : path++;
626 : }
627 : #ifdef _WIN32
628 : //Check if it is a drive letter path
629 : else if (((path[0] >= 'A' && path[0] <= 'Z') ||
630 : (path[0] >= 'a' && path[0] <= 'z')) &&
631 : path[1] == ':' && path[2] == '/') {
632 :
633 : path += 3;
634 : }
635 : #endif
636 0 : return path;
637 : }
638 :
639 0 : unichar_t *u_GFileNormalize(unichar_t *name) {
640 : unichar_t *pt, *base, *ppt;
641 :
642 0 : if ( (pt = uc_strstr(name,"://"))!=NULL ) {
643 0 : base = u_strchr(pt+3,'/');
644 0 : if ( base==NULL )
645 0 : return( name );
646 0 : ++base;
647 : }
648 :
649 0 : base = u_GFileRemoveRoot(name);
650 0 : for ( pt=base; *pt!='\0'; ) {
651 0 : if ( *pt=='/' )
652 0 : u_strcpy(pt,pt+1);
653 0 : else if ( uc_strncmp(pt,"./",2)==0 )
654 0 : u_strcpy(pt,pt+2);
655 0 : else if ( uc_strncmp(pt,"../",2)==0 ) {
656 0 : for ( ppt=pt-2; ppt>=base && *ppt!='/'; --ppt );
657 0 : ++ppt;
658 0 : if ( ppt>=base ) {
659 0 : u_strcpy(ppt,pt+3);
660 0 : pt = ppt;
661 : } else
662 0 : pt += 3;
663 : } else {
664 0 : while ( *pt!='/' && *pt!='\0' ) ++pt;
665 0 : if ( *pt == '/' ) ++pt;
666 : }
667 : }
668 0 : return( name );
669 : }
670 :
671 0 : unichar_t *u_GFileAppendFile(unichar_t *dir,unichar_t *name,int isdir) {
672 : unichar_t *ret, *pt;
673 :
674 0 : ret = (unichar_t *) malloc((u_strlen(dir)+u_strlen(name)+3)*sizeof(unichar_t));
675 0 : u_strcpy(ret,dir);
676 0 : pt = ret+u_strlen(ret);
677 0 : if ( pt>ret && pt[-1]!='/' )
678 0 : *pt++ = '/';
679 0 : u_strcpy(pt,name);
680 0 : if ( isdir ) {
681 0 : pt += u_strlen(pt);
682 0 : if ( pt>ret && pt[-1]!='/' ) {
683 0 : *pt++ = '/';
684 0 : *pt = '\0';
685 : }
686 : }
687 0 : return(ret);
688 : }
689 :
690 0 : int u_GFileIsAbsolute(const unichar_t *file) {
691 : #if defined(__MINGW32__)
692 : if( (file[1]==':') && (('a'<=file[0] && file[0]<='z') || ('A'<=file[0] && file[0]<='Z')) )
693 : return ( true );
694 : #else
695 0 : if ( *file=='/' )
696 0 : return( true );
697 : #endif
698 0 : if ( uc_strstr(file,"://")!=NULL )
699 0 : return( true );
700 :
701 0 : return( false );
702 : }
703 :
704 0 : int u_GFileIsDir(const unichar_t *file) {
705 : char buffer[1024];
706 0 : u2def_strncpy(buffer,file,sizeof(buffer));
707 0 : return GFileIsDir(buffer);
708 : }
709 :
710 0 : int u_GFileExists(const unichar_t *file) {
711 : char buffer[1024];
712 0 : u2def_strncpy(buffer,file,sizeof(buffer));
713 0 : return( access(buffer,0)==0 );
714 : }
715 :
716 0 : int u_GFileModifyable(const unichar_t *file) {
717 : char buffer[1024];
718 0 : u2def_strncpy(buffer,file,sizeof(buffer));
719 0 : return( access(buffer,02)==0 );
720 : }
721 :
722 0 : int u_GFileModifyableDir(const unichar_t *file) {
723 : char buffer[1024], *pt;
724 :
725 0 : u2def_strncpy(buffer,file,sizeof(buffer));
726 0 : pt = strrchr(buffer,'/');
727 0 : if ( pt==NULL )
728 0 : strcpy(buffer,".");
729 : else
730 0 : *pt='\0';
731 0 : return( GFileModifyable(buffer));
732 : }
733 :
734 0 : int u_GFileReadable(unichar_t *file) {
735 : char buffer[1024];
736 0 : u2def_strncpy(buffer,file,sizeof(buffer));
737 0 : return( access(buffer,04)==0 );
738 : }
739 :
740 0 : int u_GFileMkDir(unichar_t *name) {
741 : char buffer[1024];
742 0 : u2def_strncpy(buffer,name,sizeof(buffer));
743 0 : return( mkdir(buffer,0755));
744 : }
745 :
746 0 : int u_GFileRmDir(unichar_t *name) {
747 : char buffer[1024];
748 0 : u2def_strncpy(buffer,name,sizeof(buffer));
749 0 : return(rmdir(buffer));
750 : }
751 :
752 0 : int u_GFileUnlink(unichar_t *name) {
753 : char buffer[1024];
754 0 : u2def_strncpy(buffer,name,sizeof(buffer));
755 0 : return(unlink(buffer));
756 : }
757 :
758 : static char *GResourceProgramDir = 0;
759 :
760 0 : char* getGResourceProgramDir(void) {
761 0 : return GResourceProgramDir;
762 : }
763 :
764 0 : char* getLibexecDir_NonWindows(void)
765 : {
766 : // FIXME this was indirectly introduced by
767 : // https://github.com/fontforge/fontforge/pull/1838 and is not
768 : // tested on Windows yet.
769 : //
770 : static char path[PATH_MAX+4];
771 0 : snprintf( path, PATH_MAX, "%s/../libexec/", getGResourceProgramDir());
772 0 : return path;
773 : }
774 :
775 :
776 :
777 40 : void FindProgDir(char *prog) {
778 : #if defined(__MINGW32__)
779 : char path[MAX_PATH+4];
780 : char* c = path;
781 : char* tail = 0;
782 : unsigned int len = GetModuleFileNameA(NULL, path, MAX_PATH);
783 : path[len] = '\0';
784 : for(; *c; *c++){
785 : if(*c == '\\'){
786 : tail=c;
787 : *c = '/';
788 : }
789 : }
790 : if(tail) *tail='\0';
791 : GResourceProgramDir = copy(path);
792 : #else
793 40 : GResourceProgramDir = _GFile_find_program_dir(prog);
794 40 : if ( GResourceProgramDir==NULL ) {
795 : char filename[1025];
796 0 : GFileGetAbsoluteName(".",filename,sizeof(filename));
797 0 : GResourceProgramDir = copy(filename);
798 : }
799 : #endif
800 40 : }
801 :
802 162 : char *getShareDir(void) {
803 : static char *sharedir=NULL;
804 : static int set=false;
805 : char *pt;
806 : int len;
807 :
808 162 : if ( set )
809 122 : return( sharedir );
810 :
811 40 : set = true;
812 :
813 : //Assume share folder is one directory up
814 40 : pt = strrchr(GResourceProgramDir, '/');
815 40 : if ( pt==NULL ) {
816 : #ifdef SHAREDIR
817 : return( sharedir = SHAREDIR );
818 : #elif defined( PREFIX )
819 : return( sharedir = PREFIX "/share" );
820 : #else
821 0 : pt = GResourceProgramDir + strlen(GResourceProgramDir);
822 : #endif
823 : }
824 40 : len = (pt-GResourceProgramDir)+strlen("/share/fontforge")+1;
825 40 : sharedir = malloc(len);
826 40 : strncpy(sharedir,GResourceProgramDir,pt-GResourceProgramDir);
827 40 : strcpy(sharedir+(pt-GResourceProgramDir),"/share/fontforge");
828 40 : return( sharedir );
829 : }
830 :
831 :
832 80 : char *getLocaleDir(void) {
833 : static char *sharedir=NULL;
834 : static int set=false;
835 :
836 80 : if ( set )
837 40 : return( sharedir );
838 :
839 40 : char* prefix = getShareDir();
840 40 : int len = strlen(prefix) + strlen("/../locale") + 2;
841 40 : sharedir = malloc(len);
842 40 : strcpy(sharedir,prefix);
843 40 : strcat(sharedir,"/../locale");
844 40 : set = true;
845 40 : return sharedir;
846 : }
847 :
848 2 : char *getPixmapDir(void) {
849 : static char *sharedir=NULL;
850 : static int set=false;
851 :
852 2 : if ( set )
853 1 : return( sharedir );
854 :
855 1 : char* prefix = getShareDir();
856 1 : int len = strlen(prefix) + strlen("/pixmaps") + 2;
857 1 : sharedir = malloc(len);
858 1 : strcpy(sharedir,prefix);
859 1 : strcat(sharedir,"/pixmaps");
860 1 : set = true;
861 1 : return sharedir;
862 : }
863 :
864 0 : char *getHelpDir(void) {
865 : static char *sharedir=NULL;
866 : static int set=false;
867 :
868 0 : if ( set )
869 0 : return( sharedir );
870 :
871 0 : char* prefix = getShareDir();
872 : #if defined(DOCDIR)
873 : prefix = DOCDIR;
874 : #endif
875 0 : const char* postfix = "/../doc/fontforge/";
876 0 : int len = strlen(prefix) + strlen(postfix) + 2;
877 0 : sharedir = malloc(len);
878 0 : strcpy(sharedir,prefix);
879 0 : strcat(sharedir,postfix);
880 0 : set = true;
881 0 : return sharedir;
882 : }
883 :
884 : /* reimplementation of GFileGetHomeDir, avoiding copy(). Returns NULL if home
885 : * directory cannot be found */
886 112 : char *getUserHomeDir(void) {
887 : #if defined(__MINGW32__)
888 : char* dir = getenv("APPDATA");
889 : if( dir==NULL )
890 : dir = getenv("USERPROFILE");
891 : if( dir!=NULL ) {
892 : GFileNormalizePath(dir);
893 : return dir;
894 : }
895 : return NULL;
896 : #else
897 : uid_t uid;
898 : struct passwd *pw;
899 112 : char *home = getenv("HOME");
900 :
901 112 : if( home!=NULL )
902 112 : return home;
903 :
904 0 : uid = getuid();
905 0 : while( (pw=getpwent())!=NULL ) {
906 0 : if ( pw->pw_uid==uid ) {
907 0 : home = pw->pw_dir;
908 0 : endpwent();
909 0 : return home;
910 : }
911 : }
912 0 : endpwent();
913 0 : return NULL;
914 : #endif
915 : }
916 :
917 : /* Find the directory in which FontForge places all of its configurations and
918 : * save files. On Unix-likes, the argument `dir` (see the below case switch,
919 : * enum in inc/gfile.h) determines which directory is returned according to the
920 : * XDG Base Directory Specification. On Windows, the argument is ignored--the
921 : * home directory as obtained by getUserHomeDir() appended with "/FontForge" is
922 : * returned. On error, NULL is returned.
923 : *
924 : * http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html
925 : */
926 112 : char *getFontForgeUserDir(int dir) {
927 : const char *def;
928 : const char *home, *xdg;
929 112 : char *buf = NULL;
930 :
931 : /* find home directory first, it is needed if any of the xdg env vars are
932 : * not set */
933 112 : if (!(home = getUserHomeDir())) {
934 : /* if getUserHomeDir returns NULL, pass NULL to calling function */
935 0 : fprintf(stderr, "%s\n", "cannot find home directory");
936 0 : return NULL;
937 : }
938 : #ifdef _WIN32
939 : /* Allow for preferences to be saved locally in a 'portable' configuration. */
940 : if (getenv("FF_PORTABLE") != NULL) {
941 : buf = smprintf("%s/preferences/", getShareDir());
942 : } else {
943 : buf = smprintf("%s/FontForge/", home);
944 : }
945 : return buf;
946 : #else
947 : /* Home directory exists, so check for environment variables. For each of
948 : * XDG_{CACHE,CONFIG,DATA}_HOME, assign `def` as the corresponding fallback
949 : * for if the environment variable does not exist. */
950 112 : switch(dir) {
951 : case Cache:
952 0 : xdg = getenv("XDG_CACHE_HOME");
953 0 : def = ".cache";
954 0 : break;
955 : case Config:
956 112 : xdg = getenv("XDG_CONFIG_HOME");
957 112 : def = ".config";
958 112 : break;
959 : case Data:
960 0 : xdg = getenv("XDG_DATA_HOME");
961 0 : def = ".local/share";
962 0 : break;
963 : default:
964 : /* for an invalid argument, return NULL */
965 0 : fprintf(stderr, "%s\n", "invalid input");
966 0 : return NULL;
967 : }
968 112 : if(xdg != NULL)
969 : /* if, for example, XDG_CACHE_HOME exists, assign the value
970 : * "$XDG_CACHE_HOME/fontforge" */
971 0 : buf = smprintf("%s/fontforge", xdg);
972 : else
973 : /* if, for example, XDG_CACHE_HOME does not exist, instead assign
974 : * the value "$HOME/.cache/fontforge" */
975 112 : buf = smprintf("%s/%s/fontforge", home, def);
976 112 : if(buf != NULL) {
977 : /* try to create buf. If creating the directory fails, return NULL
978 : * because nothing will get saved into an inaccessible directory. */
979 112 : if ( mkdir_p(buf, 0755) != EXIT_SUCCESS ) {
980 0 : free(buf);
981 0 : return NULL;
982 : }
983 112 : return buf;
984 : }
985 0 : return NULL;
986 : #endif
987 : }
988 :
989 0 : off_t GFileGetSize(char *name) {
990 : /* Get the binary file size for file 'name'. Return -1 if error. */
991 : struct stat buf;
992 : long rc;
993 :
994 0 : if ( (rc=stat(name,&buf)) )
995 0 : return( -1 );
996 0 : return( buf.st_size );
997 : }
998 :
999 0 : char *GFileReadAll(char *name) {
1000 : /* Read file 'name' all into one large string. Return 0 if error. */
1001 : char *ret;
1002 : long sz;
1003 :
1004 0 : if ( (sz=GFileGetSize(name))>=0 && \
1005 0 : (ret=calloc(1,sz+1))!=NULL ) {
1006 : FILE *fp;
1007 0 : if ( (fp=fopen(name,"rb"))!=NULL ) {
1008 0 : size_t bread=fread(ret,1,sz,fp);
1009 0 : fclose(fp);
1010 :
1011 0 : if( bread==(size_t)sz )
1012 0 : return( ret );
1013 : }
1014 0 : free(ret);
1015 : }
1016 0 : return( 0 );
1017 : }
1018 :
1019 : /*
1020 : * Write char string 'data' into file 'name'. Return -1 if error.
1021 : **/
1022 0 : int GFileWriteAll(char *filepath, char *data) {
1023 :
1024 0 : if( !data )
1025 0 : return -1;
1026 :
1027 0 : size_t bwrite = strlen(data);
1028 : FILE* fp;
1029 :
1030 0 : if ( (fp = fopen( filepath, "wb" )) != NULL ) {
1031 0 : if ( (fwrite( data, 1, bwrite, fp ) == bwrite) && \
1032 0 : (fflush(fp) == 0) )
1033 0 : return( (fclose(fp) == 0 ? 0: -1) );
1034 0 : fclose(fp);
1035 : }
1036 0 : return -1;
1037 : }
1038 :
1039 0 : const char *getTempDir(void)
1040 : {
1041 0 : return g_get_tmp_dir();
1042 : }
1043 :
1044 0 : char *GFileGetHomeDocumentsDir(void)
1045 : {
1046 : static char* ret = 0;
1047 0 : if( ret )
1048 0 : return ret;
1049 :
1050 : #if defined(__MINGW32__)
1051 :
1052 : CHAR my_documents[MAX_PATH+2];
1053 : HRESULT result = SHGetFolderPath(NULL, CSIDL_PERSONAL, NULL, SHGFP_TYPE_CURRENT, my_documents );
1054 : if (result != S_OK)
1055 : {
1056 : fprintf(stderr,"Error: Can't get My Documents path!'\n");
1057 : return ret;
1058 : }
1059 : int pos = strlen(my_documents);
1060 : my_documents[ pos++ ] = '\\';
1061 : my_documents[ pos++ ] = '\0';
1062 : ret = copy( my_documents );
1063 : GFileNormalizePath(ret);
1064 : return ret;
1065 : #endif
1066 :
1067 : // On GNU/Linux and OSX it was decided that this should be just the
1068 : // home directory itself.
1069 0 : ret = GFileGetHomeDir();
1070 0 : return ret;
1071 : }
1072 :
1073 0 : unichar_t *u_GFileGetHomeDocumentsDir(void) {
1074 0 : unichar_t* dir = NULL;
1075 0 : char* tmp = GFileGetHomeDocumentsDir();
1076 0 : if(tmp) {
1077 0 : dir = uc_copy(tmp);
1078 : }
1079 0 : return dir;
1080 : }
1081 :
1082 :
1083 0 : char *GFileDirNameEx(const char *path, int treat_as_file)
1084 : {
1085 0 : char *ret = NULL;
1086 0 : if (path != NULL) {
1087 : //Must allocate enough space to append a trailing slash.
1088 0 : size_t len = strlen(path);
1089 0 : ret = malloc(len + 2);
1090 :
1091 0 : if (ret != NULL) {
1092 : char *pt;
1093 :
1094 0 : strcpy(ret, path);
1095 0 : GFileNormalizePath(ret);
1096 0 : if (treat_as_file || !GFileIsDir(ret)) {
1097 0 : pt = strrchr(ret, '/');
1098 0 : if (pt != NULL) {
1099 0 : *pt = '\0';
1100 : }
1101 : }
1102 :
1103 : //Keep only one trailing slash
1104 0 : len = strlen(ret);
1105 0 : for (pt = ret + len - 1; pt >= ret && *pt == '/'; pt--) {
1106 0 : *pt = '\0';
1107 : }
1108 0 : *++pt = '/';
1109 0 : *++pt = '\0';
1110 : }
1111 : }
1112 0 : return ret;
1113 : }
1114 :
1115 0 : char *GFileDirName(const char *path) {
1116 0 : return GFileDirNameEx(path, 0);
1117 : }
1118 :
|