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