LCOV - code coverage report
Current view: top level - gutils - fsys.c (source / functions) Hit Total Coverage
Test: FontForge coverage report 2017-08-04 01:21:11+02:00 (commit d35f7e4107a9e1db65cce47c468fcc914cecb8fd) Lines: 138 507 27.2 %
Date: 2017-08-04 Functions: 16 58 27.6 %

          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             : 

Generated by: LCOV version 1.10