1 /***************************************************************************
2                                  file.cpp  -  file related library functions
3                              -------------------
4     begin                : July 22 2004
5     copyright            : (C) 2004 by Marc Schellens
6     email                : m_schellens@users.sourceforge.net
7 ***************************************************************************/
8 
9 /***************************************************************************
10  *                                                                         *
11  *   This program is free software; you can redistribute it and/or modify  *
12  *   it under the terms of the GNU General Public License as published by  *
13  *   the Free Software Foundation; either version 2 of the License, or     *
14  *   (at your option) any later version.                                   *
15  *                                                                         *
16  ***************************************************************************/
17 
18 #include "includefirst.hpp"
19 
20 #ifndef _MSC_VER
21 #   include <libgen.h>
22 #   include <sys/types.h>
23 #endif
24 #include <sys/fcntl.h>
25 #include <sys/stat.h>
26 
27 #ifndef _MSC_VER
28 #   include <unistd.h>
29 #endif
30 
31 #include "basegdl.hpp"
32 #include "str.hpp"
33 
34 
35 #include "envt.hpp"
36 #include "file.hpp"
37 #include "objects.hpp"
38 
39 #include "dinterpreter.hpp"
40 
41 
42 #include <zlib.h>
43 
44 #include <climits> // PATH_MAX
45 #include <list> //unique path elements
46 //patch #90
47 #ifndef PATH_MAX
48 #define PATH_MAX 4096
49 #endif
50 //#ifndef _MSC_VER
51 #ifndef _WIN32
52 #   include <fnmatch.h>
53 #   include <glob.h> // glob in MinGW ok for mingw >=3.21 11/2014
54 #else
55 #   include <shlwapi.h>
56 #    if !defined(S_IFLNK)
57 #   define S_IFLNK 0xA000
58 #   define S_ISLNK(mode) (((mode) & S_IFLNK) == S_IFLNK)
59 #    endif
60 #   if !defined(S_ISLNK)
61 #   define S_ISLNK(mode) (((mode) & S_IFLNK) == S_IFLNK)
62 #   endif
63 #   define u_int64_t uint64_t
64 #endif
65 
66 #ifndef _MSC_VER
67 #   include <dirent.h>
68 #else
69 #   include <io.h>
70 
71 #   define access _access
72 
73 #   define R_OK    4       /* Test for read permission.  */
74 #   define W_OK    2       /* Test for write permission.  */
75 //# define   X_OK    1       /* execute permission - unsupported in windows*/
76 #   define F_OK    0       /* Test for existence.  */
77 
78 #   define PATH_MAX 255  // MAX_PATH is a windows-only macro.
79 
80 #   include <direct.h>
81 
82 
83 #   if !defined(S_ISDIR)
84 
85 #   define __S_ISTYPE(mode, mask)   (((mode) & S_IFMT) == (mask))
86 #   define S_ISDIR(mode)     __S_ISTYPE((mode), S_IFDIR)
87 #   define S_ISREG(mode)    __S_ISTYPE((mode), S_IFREG)
88 
89 #endif
90 
91 
92 #endif
93 
94 // workaround for HP-UX. A better solution is needed i think
95 //#if defined(__hpux__) || defined(__sun__)
96 #if !defined(GLOB_TILDE)
97 #  define GLOB_TILDE 0
98 #endif
99 #if !defined(GLOB_BRACE)
100 #  define GLOB_BRACE 0
101 #endif
102 //#if defined(__hpux__) || defined(__sun__) || defined(__CYGWIN__) || defined(__OpenBSD__)
103 #if !defined(GLOB_ONLYDIR)
104 #  define GLOB_ONLYDIR 0
105 #endif
106 #if !defined(GLOB_PERIOD)
107 #  define GLOB_PERIOD 0
108 #endif
109 
110 #ifdef _MSC_VER
111 
112 /*
113   Implementation of POSIX directory browsing functions and types for Win32.
114 
115   Author:  Kevlin Henney (kevlin@acm.org, kevlin@curbralan.com)
116   History: Created March 1997. Updated June 2003 and July 2012.
117   Rights:  See end of file.
118 */
119 
120 #include <errno.h>
121 #include <io.h> /* _findfirst and _findnext set errno iff they return -1 */
122 #include <stdlib.h>
123 #include <string.h>
124 
125 struct dirent
126 {
127   char *d_name;
128 };
129 
130 #ifdef __cplusplus
131 extern "C"
132 {
133 #endif
134 
135   typedef ptrdiff_t handle_type; /* C99's intptr_t not sufficiently portable */
136 
137   struct DIR
138   {
139     handle_type         handle; /* -1 for failed rewind */
140     struct _finddata_t  info;
141     struct dirent       result; /* d_name null iff first time */
142     char                *name;  /* null-terminated char string */
143   };
144 
145 
opendir(const char * name)146   DIR *opendir(const char *name)
147   {
148     DIR *dir = 0;
149 
150     if(name && name[0])
151       {
152         size_t base_length = strlen(name);
153         const char *all = /* search pattern must end with suitable wildcard */
154       strchr("/\\", name[base_length - 1]) ? "*" : "/*";
155 
156         if((dir = (DIR *) malloc(sizeof *dir)) != 0 &&
157            (dir->name = (char *) malloc(base_length + strlen(all) + 1)) != 0)
158       {
159             strcat(strcpy(dir->name, name), all);
160 
161             if((dir->handle =
162                 (handle_type) _findfirst(dir->name, &dir->info)) != -1)
163           {
164                 dir->result.d_name = 0;
165           }
166             else /* rollback */
167           {
168                 free(dir->name);
169                 free(dir);
170                 dir = 0;
171           }
172       }
173         else /* rollback */
174       {
175             free(dir);
176             dir   = 0;
177             errno = ENOMEM;
178       }
179       }
180     else
181       {
182         errno = EINVAL;
183       }
184 
185     return dir;
186   }
187 
closedir(DIR * dir)188   int closedir(DIR *dir)
189   {
190     int result = -1;
191 
192     if(dir)
193       {
194         if(dir->handle != -1)
195       {
196             result = _findclose(dir->handle);
197       }
198         free(dir->name);
199         free(dir);
200       }
201     if(result == -1) /* map all errors to EBADF */
202       {
203         errno = EBADF;
204       }
205     return result;
206   }
207 
readdir(DIR * dir)208   struct dirent *readdir(DIR *dir)
209   {
210     struct dirent *result = 0;
211 
212     if(dir && dir->handle != -1)
213       {
214         if(!dir->result.d_name || _findnext(dir->handle, &dir->info) != -1)
215       {
216             result         = &dir->result;
217             result->d_name = dir->info.name;
218       }
219       }
220     else
221       {
222         errno = EBADF;
223       }
224     return result;
225   }
226 
rewinddir(DIR * dir)227 static void rewinddir(DIR *dir)
228   {
229     if(dir && dir->handle != -1)
230       {
231         _findclose(dir->handle);
232         dir->handle = (handle_type) _findfirst(dir->name, &dir->info);
233         dir->result.d_name = 0;
234       }
235     else
236       {
237         errno = EBADF;
238       }
239   }
240 
241 #ifdef __cplusplus
242 }
243 #endif
244 /*
245   Copyright Kevlin Henney, 1997, 2003, 2012. All rights reserved.
246 
247   Permission to use, copy, modify, and distribute this software and its
248   documentation for any purpose is hereby granted without fee, provided
249   that this copyright and permissions notice appear in all copies and
250   derivatives.
251   This software is supplied "as is" without express or implied warranty.
252   But that said, if there are any problems please get in touch.
253 */
254 #endif
255 #define NTEST_SEARCH 7
256 #if defined(__CYGWIN__) || defined(__FreeBSD__) || defined(__APPLE__) || defined(__DragonFly__)
257 #define stat64 stat
258 #define lstat64 lstat
259 // for religious reasons, CYGWIN doesn't do lstat64
260 // FreeBSD doesn't do lstat64 because there is no need for it
261 #endif
262   using namespace std;
263 
264 
265 
266 #ifdef _WIN32
267   //
268 #ifdef _MSC_VER // MSVC uses 64bit internally
269 #   define stat64 stat
270 #   define lstat64(x,y) stat(x,y)
271 #else
272     // Patch by Greg Jung: Using _stati64 is acceptable down to winXP version and will
273     // result in a 64-bit st_size for both mingw-org and for mingw-w64.
274     // The times st_atime, st_mtime, etc. will be 32-bit in mingw-org.
275     #ifndef stat64 /* case of mingw-org .vs. mingw-w64 */
276     # define stat64 _stati64
277     #endif
278 
279     // trailing seperator makes stat fail on Windows
lstat64(const char * path,struct stat64 * buf)280     static inline int lstat64(const char *path, struct stat64 *buf) {
281       size_t l_path = strlen(path);
282       if (path[l_path-1]=='\\') {
283         char newpath[l_path]; memcpy(newpath, path, l_path); newpath[l_path-1] = 0;
284         return stat64(newpath, buf);
285       }
286       return stat64(path, buf);
287     }
288 #endif
289 
290 //     modifications        : 2014, 2015 by Greg Jung
291   // fstat_win32 used for symlink treatment
fstat_win32(const DString & DSpath,int & st_mode,DWORD & dwattrib)292 static void fstat_win32(const DString& DSpath, int& st_mode, DWORD &dwattrib)
293   {
294     DWORD      reparsetag;
295     WCHAR   filepath[MAX_PATH+1];
296     HANDLE  hFind;
297     BOOL    foundnext;
298     WIN32_FIND_DATAW FindFileData;
299     // Native NTFS symlinks (not Junctions) are found
300     // also cygwin symlinks are sniffed out.
301 
302     MultiByteToWideChar(CP_UTF8, 0,
303             (LPCSTR) DSpath.c_str(), -1,
304             filepath, MAX_PATH+1);
305     hFind = FindFirstFileExW( filepath,
306                   FindExInfoStandard,
307                   &FindFileData,
308                   FindExSearchNameMatch, NULL, 0);
309     if (hFind == INVALID_HANDLE_VALUE) {
310       FindClose(hFind);
311       return;
312     }
313     dwattrib = FindFileData.dwFileAttributes;
314     if( (dwattrib & FILE_ATTRIBUTE_REPARSE_POINT) != 0 )  {
315       reparsetag = FindFileData.dwReserved0;
316       if(reparsetag ==  IO_REPARSE_TAG_SYMLINK)
317     st_mode |= S_IFLNK;
318     } else
319       if ( (dwattrib & FILE_ATTRIBUTE_SYSTEM) != 0 ) {
320     // This is the first step to a cygwin symlink.
321     // next, '!<symlink>' is first 10 bytes.
322     // following this, 0xFF 0xFE <target name in UTF-8>
323     // for now, just assume it is a link.
324 
325         HANDLE hFile = CreateFileW( filepath,
326                     FILE_GENERIC_READ,
327                     FILE_SHARE_READ | FILE_SHARE_WRITE,         0,
328                     OPEN_EXISTING,
329                     FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_RANDOM_ACCESS,
330                     0);
331 
332         if (hFile == INVALID_HANDLE_VALUE) {
333       CloseHandle(hFile);
334       // happens for "hidden" files or protected files:
335       //              cout << " stat_win32: could not open " + DSpath << endl;
336       return;
337     }
338         char header[12];
339         DWORD nbytesread;
340 
341         BOOL result = ReadFile(hFile, header, 12,
342                                &nbytesread, 0 );
343         if (strncmp( header, "!<symlink>",10) == 0 ) st_mode |= S_IFLNK;
344     CloseHandle(hFile);
345       }
346     FindClose(hFind);
347     return;
348   }
349 
350 #endif
351 
352 namespace lib {
353 
path_sep(EnvT * e)354   BaseGDL* path_sep( EnvT* e)
355   {
356     static int PARENT_DIRECTORY=e->KeywordIx("PARENT_DIRECTORY");
357     static int SEARCH_PATH=e->KeywordIx("SEARCH_PATH");
358     if (e->KeywordSet(PARENT_DIRECTORY) && e->KeywordSet(SEARCH_PATH) )
359       e->Throw("Conflicting keywords.");
360     if (e->KeywordSet(PARENT_DIRECTORY)) return new DStringGDL(ParentDirectoryIndicator());
361     else if (e->KeywordSet(SEARCH_PATH)) return new DStringGDL(SearchPathSeparator());
362     return new DStringGDL(PathSeparator());
363   }
364 
GetCWD()365   DString GetCWD()
366   {
367     SizeT bufSize = PATH_MAX;
368     char *buf = new char[ bufSize];
369     for(;;)
370       {
371     char* value = getcwd( buf, bufSize);
372     if( value != NULL)
373       break;
374     delete[] buf;
375     if( bufSize > 32000)
376       throw GDLException("Cannot get CWD.");
377     bufSize += PATH_MAX;
378     buf = new char[ bufSize];
379       }
380 
381     DString cur( buf);
382     delete[] buf;
383 #ifdef _WIN32
384       size_t pp;  // This is to make path names uniform w.r.t. Unix
385                 //           and compliant with posix shell.
386       pp=0;
387     if (lib::posixpaths) for(;;)
388     {
389         pp=cur.find( "\\",pp);
390         if (pp==string::npos) break;
391         cur[pp]='/';
392       }
393 #endif
394  /* */
395     return cur;
396   }
397 
cd_pro(EnvT * e)398   void cd_pro( EnvT* e)
399   {
400     if( e->KeywordPresent( 0)) // CURRENT
401       {
402     DString cur = GetCWD();
403     e->SetKW( 0, new DStringGDL( cur));
404       }
405 #ifdef _WIN32 // backdoor setting of posixpaths
406     if( !lib::posixpaths && lib::gdlarg_present("posix") ) {
407         lib::posixpaths = true;
408         cout << "posixpaths turned on. !gdl.gdl_posiz was not set. (restart with --posix to set)" << endl;
409         }
410 #endif
411 
412     SizeT nParam=e->NParam();
413     if( nParam == 0) return;
414 
415     DString dir;
416     e->AssureScalarPar<DStringGDL>( 0, dir);
417 
418     WordExp( dir);
419 
420 
421     //     // expand tilde
422 #ifdef _WIN32
423     if( dir[0] == '~')
424       {
425     char* homeDir = getenv( "HOME");
426     if( homeDir == NULL) homeDir = getenv("HOMEPATH");
427 
428     if( homeDir != NULL){
429       dir = string( homeDir) + lib::PathSeparator() + dir.substr(1);
430           size_t pp;
431           pp=0;
432         if (lib::posixpaths) for(;;)
433         {
434             pp=dir.find( "\\",pp);
435             if (pp==string::npos) break;
436             dir[pp]='/';
437           }
438        }
439       }
440 #endif
441 
442     int success = chdir( dir.c_str());
443 
444     if( success != 0)
445       e->Throw( "Unable to change current directory to: "+dir+".");
446   }
447 
FindInDir(const DString & dirN,const DString & pat)448 static bool FindInDir( const DString& dirN, const DString& pat)
449   {
450 
451     DIR* dir = opendir( dirN.c_str());
452     if( dir == NULL) return false;
453 
454     struct stat64    statStruct;
455 #ifdef _WIN32
456 
457     // JP Mar 2015: Below code block is inspired by Greg's code to improve speed
458 
459     wchar_t entryWStr[PATH_MAX+1] = {0,};
460     wchar_t patW[PATH_MAX+1] = {0,};
461     MultiByteToWideChar(CP_UTF8, 0, pat.c_str(), -1, patW, MAX_PATH+1);
462 #endif
463     DString root = dirN;
464     AppendIfNeeded( root, PathSeparator());
465 
466     for(;;)
467       {
468     struct dirent* entry = readdir( dir);
469     if( entry == NULL) break;
470 
471     DString entryStr( entry->d_name);
472         if( entryStr == "." || entryStr == "..") continue;
473         DString testFile = root + entryStr;
474 
475         int actStat = lstat64( testFile.c_str(), &statStruct);
476 
477         if( S_ISDIR(statStruct.st_mode) == 0)
478 
479           { // only test non-dirs
480 
481 #ifdef _WIN32
482         MultiByteToWideChar(CP_UTF8, 0, entryStr.c_str(), -1, entryWStr, MAX_PATH+1);
483         int match = !PathMatchSpecW( entryWStr, patW );
484 #else
485         int match = fnmatch( pat.c_str(), entryStr.c_str(), 0);
486 #endif
487         if( match == 0)
488           {
489             closedir( dir);
490             return true;
491           }
492 
493       }
494       }
495 
496     closedir( dir);
497     return false;
498   }
499 
ExpandPathN(FileListT & result,const DString & dirN,const DString & pat,bool all_dirs)500 static void ExpandPathN( FileListT& result,
501             const DString& dirN,
502             const DString& pat,
503             bool all_dirs )
504 {
505     // expand "+"
506 
507     int fnFlags = 0;
508 
509     //    fnFlags |= FNM_PERIOD;
510     //    fnFlags |= FNM_NOESCAPE;
511 
512     DString root = dirN;
513     AppendIfNeeded( root, PathSeparator());
514 
515     struct stat64    statStruct;
516 
517     FileListT recurDir;
518 
519     bool notAdded = !all_dirs;
520 
521     DIR* dir = opendir( dirN.c_str());
522 
523     if( dir == NULL) return;
524 
525 
526     // JP Mar 2015: Below code block is inspired by Greg's code to improve speed
527 #if defined (_WIN32)
528     wchar_t entryWStr[PATH_MAX+1] = {0,};
529     wchar_t patW[PATH_MAX+1] = {0,};
530 
531     MultiByteToWideChar(CP_UTF8, 0, pat.c_str(), -1, patW, PATH_MAX+1);
532 #endif
533     for (;; ) {
534       struct dirent* entry = readdir( dir);
535       if( entry == NULL) break;
536 
537       DString entryStr( entry->d_name);
538         if( entryStr == "." || entryStr == "..") continue;
539 
540     DString testDir = root + entryStr;
541 
542         int actStat = lstat64(testDir.c_str(), &statStruct);
543 #ifdef _WIN32
544     DWORD dwattrib;
545         int addlink = 0;
546         fstat_win32(testDir, addlink, dwattrib);
547         statStruct.st_mode |= addlink;
548 #endif
549         bool isaSymLink = S_ISLNK(statStruct.st_mode);
550     //
551     //   follow for expand_path, as per documented behavior of IDL
552     //
553         if(isaSymLink) actStat = stat64(testDir.c_str(), &statStruct);
554         if( S_ISDIR(statStruct.st_mode) != 0) {
555 //    if( debug && isASymLink ) cout << " following a symlink directory: " << testDir << endl;
556           recurDir.push_back( testDir);
557     }     else if( notAdded)
558       {
559 #ifdef _WIN32
560             MultiByteToWideChar(CP_UTF8, 0, entryStr.c_str(),
561                 -1, entryWStr, PATH_MAX+1);
562             int match = !PathMatchSpecW(entryWStr, patW);
563 
564 #else
565         int match = fnmatch( pat.c_str(), entryStr.c_str(), 0);
566 #endif
567         if( match == 0)
568           notAdded = false;
569       }
570       }
571 
572 
573     int c = closedir( dir);
574     if( c == -1) return;
575 
576     // recursive search
577     SizeT nRecur = recurDir.size();
578     for ( SizeT d = 0; d < nRecur; ++d ) {
579       ExpandPathN( result, recurDir[d], pat, all_dirs);
580     }
581 
582     if( !notAdded)
583       result.push_back( dirN);
584   }
585 
586 //     modifications        : 2014, 2015 by Greg Jung
ExpandPath(FileListT & result,const DString & dirN,const DString & pat,bool all_dirs)587   void ExpandPath( FileListT& result,
588            const DString& dirN,
589            const DString& pat,
590            bool all_dirs)
591   {
592 
593     if( dirN == "")
594       return;
595 
596     if( StrUpCase( dirN) == "<GDL_DEFAULT>" ||
597     StrUpCase( dirN) == "<IDL_DEFAULT>")
598       {
599     // result.push_back( the default path here);
600     return;
601       }
602 
603     if( dirN[0] != '+' && dirN[0] != '~')
604       {
605     result.push_back( dirN);
606     return;
607       }
608 
609     if( dirN.length() == 1) {
610       // dirN == "+"
611       if (dirN[0] == '+') return;
612     }
613 
614     // dirN == "+DIRNAME"
615 
616 #ifdef _WIN32
617     DString initDir = dirN;
618     if(dirN[0] == '+')
619       initDir = dirN.substr(1);
620 
621 #else
622 
623     // do first a glob because of '~'
624 
625     int flags = GLOB_TILDE | GLOB_NOSORT;
626 
627     glob_t p;
628 
629     int offset_tilde=0;
630     if (dirN[0] == '+') offset_tilde=1;
631     int gRes = glob( dirN.substr(offset_tilde).c_str(), flags, NULL, &p);
632     if( gRes != 0 || p.gl_pathc == 0)
633       {
634     globfree( &p);
635     return;
636       }
637 
638 
639 
640     DString initDir = p.gl_pathv[ 0];
641     globfree( &p);
642 
643 #endif
644 
645     if (dirN[0] == '+')
646       {
647     ExpandPathN( result, initDir, pat, all_dirs);
648       }
649     else
650       {
651     result.push_back(initDir);
652       }
653 
654   }
655 
expand_path(EnvT * e)656   BaseGDL* expand_path( EnvT* e)
657   {
658     e->NParam( 1);
659 
660     DString pathString;
661     e->AssureStringScalarPar( 0, pathString);
662     WordExp(pathString);
663     FileListT sArr;
664 
665 
666     static int all_dirsIx = e->KeywordIx( "ALL_DIRS");
667     bool all_dirs = e->KeywordSet( all_dirsIx);
668 
669     static int arrayIx = e->KeywordIx( "ARRAY");
670     bool array = e->KeywordSet( arrayIx);
671 
672     static int countIx = e->KeywordIx( "COUNT");
673 
674     DString pattern;
675     static int typeIx = e->KeywordIx( "PATTERN");
676     if(e->KeywordPresent(typeIx)) {
677       e->AssureStringScalarKWIfPresent( typeIx, pattern);
678     }
679     else      pattern = "*.pro";
680 
681     SizeT d;
682     long   sPos=0;
683 #ifdef _WIN32
684     char pathsep[]=";";
685 #else
686     char pathsep[]=":";
687 #endif
688 
689     //correct bug #832 by eliminating duplicates
690     std::vector<std::string> pathList;
691     do {
692       d=pathString.find(pathsep[0], sPos);
693       std::string act = pathString.substr(sPos, d - sPos);
694       //add only if not already present -- clumsy
695       bool notPresent=true;
696       for (unsigned i = 0; i < pathList.size(); i++) {
697         if (pathList[i] == act) {
698           notPresent = false;
699           break;
700         }
701       }
702       if (notPresent) pathList.push_back(act);
703       sPos = d + 1;
704     }    while (d != pathString.npos);
705 
706     for (unsigned i = 0; i < pathList.size(); i++)  {
707       ExpandPath( sArr, pathList[i], pattern, all_dirs);
708     }
709 
710     SizeT nArr = sArr.size();
711 
712     if( e->KeywordPresent( countIx))
713       {
714     e->SetKW( countIx, new DLongGDL( nArr));
715       }
716 
717     if( nArr == 0)
718       return new DStringGDL( "");
719 
720     if( array)
721       {
722     DStringGDL* res = new DStringGDL( dimension( nArr), BaseGDL::NOZERO);
723     for( SizeT i=0; i<nArr; ++i)
724       (*res)[ i] = sArr[i];
725     return res;
726       }
727 
728     // set the path
729     DString cat = sArr[0];
730     for( SizeT i=1; i<nArr; ++i)
731       cat += pathsep + sArr[i];
732     return new DStringGDL( cat);
733  }
734 
735 #ifdef _WIN32
736 #define realpath(N,R) _fullpath((R),(N),_MAX_PATH)
737   // ref:http://sourceforge.net/p/mingw/patches/256/ Keith Marshall 2005-12-02
738   // http://msdn.microsoft.com/en-us/library/506720ff.aspx
739 #endif
740 
filestat(const char * actFile,struct stat64 & statStruct,bool & isaDir,bool & isaSymLink)741 static int filestat( const char *actFile, struct stat64 &statStruct,
742     bool &isaDir, bool &isaSymLink)
743 {   // populate a statStruct, determinewhether it is a SymLink or a Dir (or both).
744    struct stat64 statlink;
745 
746    int actStat = lstat64(actFile, &statStruct);
747    isaDir = false;
748    isaSymLink = false;
749    if(actStat != 0) return actStat;
750 #ifdef _WIN32
751 DWORD dwattrib;
752    int addlink = 0;
753    fstat_win32(actFile, addlink, dwattrib);
754    statStruct.st_mode |= addlink;
755 #endif
756 
757    isaDir = (S_ISDIR(statStruct.st_mode) != 0);
758    isaSymLink = S_ISLNK(statStruct.st_mode);
759 //   SizeT lenpath = statStruct.st_size; // if a symlink, this is path size.
760 
761    if (isaSymLink ) {
762      actStat = stat64(actFile, &statlink); // preserving the original actStat
763      if(actStat == 0) isaDir = (S_ISDIR(statlink.st_mode) != 0);
764     }
765     return 0;
766 }
767 
768 //     modifications        : 2014, 2015 by Greg Jung
769 
PatternSearch(FileListT & fL,const DString & dirN,const DString & pat,bool recursive,bool accErr,bool mark,bool quote,bool match_dot,bool forceAbsPath,bool fold_case,bool onlyDir,bool * tests=NULL)770 static void PatternSearch( FileListT& fL, const DString& dirN, const DString& pat,
771         bool recursive,
772         bool accErr,   bool mark,  bool quote,
773         bool match_dot,bool forceAbsPath,bool fold_case,
774         bool onlyDir,  bool *tests = NULL)
775   {
776     enum { testregular=3, testdir, testzero, testsymlink };
777     bool dotest = false;
778       if(tests != NULL) for( SizeT i=0; i < NTEST_SEARCH; i++) dotest |= tests[i];
779     int fnFlags = 0;
780 
781 #ifndef _WIN32
782 
783     if( !match_dot)
784       fnFlags |= FNM_PERIOD;
785 
786     if( !quote)
787       fnFlags |= FNM_NOESCAPE;
788 
789     if(fold_case) fnFlags |= FNM_CASEFOLD;
790 
791 #endif
792 
793     char PS =  '/';
794 #ifdef _WIN32
795     PS = '\\';
796 #endif
797 
798     std::string root = dirN;
799 
800     const char *rootC = root.c_str();
801     int endR = root.length()-1;
802     while(  (endR > 0) &&       // find end of root's viable name.
803         ( (rootC[ endR] == PS) ||  (rootC[ endR] == '/')
804                                 || (rootC[ endR] == ' '))) endR--;
805     if( endR >= 0)   root = root.substr( 0, endR+1);
806 
807     FileListT recurDir;
808 
809     DIR* dir;
810     if( root != "")
811       dir = opendir( dirN.c_str());
812     else
813       dir = opendir( ".");
814     if( dir == NULL) {
815       if( accErr)
816         throw GDLException( "FILE_SEARCH: Error opening dir: "+root);
817       else
818     return;
819     }
820 
821     DString prefix = root;
822 
823     if(root != "") AppendIfNeeded(prefix,PathSeparator());
824 // If Dir_specification does not have a "/" at end then we will include <dirspec>/..
825 // but this is a fix for other issues.
826 //    if(onlyDir) fL.push_back(prefix);
827     if( onlyDir && (pat == "" ) ) {
828         fL.push_back(prefix); return;
829     }
830 // file_search('nn','') != file_search('nn/','')
831 //  where 'nn' is a directory in CWD.
832 //
833     if(prefix == "./") prefix="";
834     int accessmode = 0;
835     if(dotest) {
836     if( tests[0]) accessmode = R_OK;
837     if( tests[1]) accessmode |= W_OK;
838 #ifndef _WIN32
839     if( tests[2]) accessmode |= X_OK;
840 #endif
841       }
842     const char* patC = pat.c_str();
843 //  if(pat == "") patC = "*";   // pat="" can be done by sending pat=" "
844     while(*patC == ' ')patC++;  // doesn't work with leading blanks.
845 #ifdef _WIN32
846     wchar_t patW[MAX_PATH+1];
847     wchar_t entryWstr[MAX_PATH+1];
848       MultiByteToWideChar(CP_UTF8, 0,
849                   (LPCSTR)patC, -1,
850               patW, MAX_PATH+1);
851 #endif
852     DString filepath;
853     const char* fpC;
854 //      if(trace_me) std::cout << " prefix:" << prefix;
855     struct stat64    statStruct, statlink;
856     for(;;)
857       {
858     struct dirent* entry = readdir( dir);
859     if( entry == NULL)
860       break;
861 
862     DString entryStr( entry->d_name);
863         if( entryStr == "." || entryStr == "..") continue;
864         const char* entryStrC = entryStr.c_str();
865 
866         filepath = prefix + entryStr; fpC = filepath.c_str();
867 //      if(trace_me) std::cout << "| "<< entryStr;
868 
869         int actStat = lstat64( fpC, &statStruct);
870 
871 #ifdef _WIN32
872         if(*entryStrC == '.' && !match_dot) continue;
873         MultiByteToWideChar(CP_UTF8, 0,
874                         (LPCSTR)entryStrC, -1,
875                                 entryWstr, MAX_PATH+1);
876         int match = !PathMatchSpecW(entryWstr, patW);
877 #else
878         int match = fnmatch( patC, entryStrC, fnFlags);
879 #endif
880 
881             if( match == 0) {
882             if(  onlyDir ) {
883                 if( mark ) filepath.append(PathSeparator());
884                 if(S_ISDIR(statStruct.st_mode) != 0) fL.push_back( filepath);
885                 continue;
886             }
887 #ifdef _WIN32
888         DWORD dwattrib;
889         int addlink = 0;
890             fstat_win32(filepath, addlink, dwattrib);
891         statStruct.st_mode |= addlink;
892 #endif
893 
894         bool isaDir = (S_ISDIR(statStruct.st_mode) != 0);
895             bool isaSymLink = (S_ISLNK(statStruct.st_mode) != 0);
896             if(isaSymLink) {
897                 actStat = stat64( fpC, &statlink);
898           statStruct.st_mode |= statlink.st_mode;
899           isaDir = (S_ISDIR(statlink.st_mode) != 0);
900         }
901             if(dotest) {
902                 if( tests[testregular] &&
903                     (S_ISREG( statStruct.st_mode) == 0)) continue;
904         if( tests[testdir] && !isaDir) continue;
905                 if( tests[testsymlink] && !isaSymLink) continue;
906 
907 
908         if( tests[testzero] &&
909             (statStruct.st_size != 0)) continue;
910         // now read, write, execute:
911                 if(accessmode != 0 &&
912                     (access(fpC, accessmode) != 0) ) continue;
913                 }
914             if( isaDir and mark) {
915                  filepath.append(PathSeparator()); fpC = filepath.c_str();
916           }
917             if(forceAbsPath) {
918         char actualpath [PATH_MAX+1];
919         char *ptr;
920                 ptr = realpath(fpC, actualpath);
921                 if( ptr != NULL ) {
922 #ifdef _WIN32
923     if (lib::posixpaths) for(int i=0;ptr[i] != 0;i++) if(ptr[i] == '\\') ptr[i] = '/';
924 #endif
925                     fL.push_back( string(ptr));
926                 }
927           }
928           else
929                   fL.push_back( filepath);
930             }
931 #ifndef _WIN32
932         if( root == "") continue;
933 #endif
934         if( (S_ISDIR(statStruct.st_mode) != 0) &&
935            ( S_ISLNK(statStruct.st_mode) == 0)
936                  ) recurDir.push_back( filepath);
937       }
938 
939     int c = closedir( dir);
940     if( c == -1) {
941       if( accErr)
942     throw GDLException( "FILE_SEARCH: Error closing dir: "+dirN);
943       else
944     return;
945     }
946     // recursive search
947     if( !recursive ) return;
948     SizeT nRecur = recurDir.size();
949 
950 
951     for( SizeT d=0; d<nRecur; ++d)
952       {
953 
954     PatternSearch( fL, recurDir[d], pat, true,
955               accErr,  mark, quote,
956               match_dot,  forceAbsPath,fold_case,
957             onlyDir,     tests);
958       }
959     return;
960   }
961 
962   // Make s string case-insensitive for glob()
makeInsensitive(const DString & s)963 static DString makeInsensitive(const DString &s)
964   {
965     DString insen="";
966     char coupleBracket[5]={'[',0,0,']',0};
967     char couple[3]={0};
968     bool bracket=false;
969 
970     for(size_t i=0;i<s.size();i++)
971       if((s[i]>='A' && s[i]<='Z') || (s[i]>='a' && s[i]<='z'))
972     {
973       char m,M;
974       if(s[i]>='a' && s[i]<='z')
975         m=s[i],M=m+'A'-'a';
976       else
977         M=s[i],m=M-'A'+'a';
978 
979       if(bracket) // If bracket is open, then don't add bracket
980         couple[0]=m,couple[1]=M,insen+=couple;
981       else // else [aA]
982         coupleBracket[1]=m,coupleBracket[2]=M,insen+=coupleBracket;
983     }
984       else
985     {
986       if(s[i]=='[')
987         {
988           bracket=false;
989           for( size_t ii=i;ii<s.size();ii++) // Looking for matching right bracket
990         if(s[ii]==']') { bracket=true; break; }
991 
992           if(bracket) insen+=s[i];
993           else insen+="[[]";
994         }
995       else if(s[i]==']' && s[(!i?0:i-1)]!='[')
996         bracket=false, insen+=s[i];
997       else
998         insen+=s[i];
999     }
1000     return insen;
1001   }
1002 #ifndef _WIN32
BeautifyPath(std::string st,bool removeMark=true)1003   std::string BeautifyPath(std::string st, bool removeMark=true)
1004   {
1005     //removes series of "//", "/./" and "/.." and adjust path accordingly.
1006      if ( st.length( ) > 0 ) {
1007       size_t pp;
1008       pp=0;
1009       do {
1010         pp=st.find( "/./");
1011         if (pp!=string::npos) { st.erase(pp, 2);}
1012       } while (pp!=string::npos);
1013       pp=0;
1014       do {
1015         pp=st.find( "//");
1016         if (pp!=string::npos) { st.erase(pp, 1);}
1017       } while (pp!=string::npos);
1018       //Last "/.."
1019       pp=st.rfind( "/.."); //remove and back if last
1020       if (pp!=string::npos && pp==st.size()-3) {
1021         //erase from previous "/" to pp+3. Unless there is no previous "/"!
1022         size_t prevdir = st.rfind("/",pp-1);
1023         if (prevdir != string::npos) {st.erase(prevdir, pp+3-prevdir);}
1024       }
1025       //Last "/."
1026       pp=st.rfind( "/."); //remove if last
1027       if (pp!=string::npos && pp==st.size()-2) st.erase(pp);
1028       //Last "/" if removeMark is true
1029       if (removeMark) {
1030         pp=st.rfind( "/"); //remove and back if last
1031         if (pp!=string::npos && pp==st.size()-1) st.erase(pp);
1032       }
1033       // other places for "/..": between directories
1034       pp=0;
1035       do {
1036         pp=st.find( "/../");
1037         if (pp!=string::npos) {
1038           //erase from previous "/" to pp+3. Unless there is no previous "/"!
1039           size_t prevdir = st.rfind("/",pp-1);
1040           if (prevdir != string::npos) {st.erase(prevdir, pp+3-prevdir);}
1041           else break; //what should I do?
1042         }
1043       } while (pp!=string::npos);
1044       //First "./"
1045       pp=st.find( "./"); //remove if first
1046       if (pp==0) st.erase(pp,2);
1047     }
1048   return st;
1049   }
1050 
1051 #include <stdlib.h>
FileSearch(FileListT & fileList,const DString & pathSpec,bool environment,bool tilde,bool accErr,bool mark,bool noSort,bool quote,bool period,bool forceAbsPath,bool fold_case,bool dir,bool * tests=NULL)1052 static void FileSearch( FileListT& fileList, const DString& pathSpec,
1053            bool environment,   bool tilde,
1054            bool accErr,  bool mark,  bool noSort,  bool quote,
1055            bool period,  bool forceAbsPath,   bool fold_case,
1056            bool dir,   bool *tests=NULL)
1057  {
1058 
1059   enum {
1060     testregular = 3, testdir, testzero, testsymlink
1061   };
1062   bool dotest = false;
1063   for ( SizeT i = 0; i < NTEST_SEARCH; i++ ) dotest |= tests[i];
1064   int globflags = 0;
1065   DString st;
1066 
1067   if ( environment )
1068     globflags |= GLOB_BRACE;
1069 
1070   if ( tilde )
1071     globflags |= GLOB_TILDE;
1072 
1073   if ( accErr )
1074     globflags |= GLOB_ERR;
1075 
1076   if ( mark && !dir ) // only mark directory if not in dir mode
1077     globflags |= GLOB_MARK;
1078 
1079 //    if( noSort) sorting is done again later in file_search.
1080     globflags |= GLOB_NOSORT;
1081 
1082 #if !defined(__APPLE__) && !defined(__FreeBSD__) && !defined(__DragonFly__)
1083   if ( !quote ) // n/a on OS X
1084     globflags |= GLOB_NOESCAPE;
1085 
1086   if ( dir ) // simulate with lstat()
1087     globflags |= GLOB_ONLYDIR;
1088 
1089   if ( period ) // n/a on OS X
1090     globflags |= GLOB_PERIOD;
1091 #endif
1092    if( fold_case)
1093       st=makeInsensitive(pathSpec);
1094     else
1095       st=pathSpec;
1096   glob_t p;
1097   int gRes;
1098   if ( !forceAbsPath ) {
1099     if ( st != "" ) gRes = glob( st.c_str( ), globflags, NULL, &p );
1100     else gRes = glob( "*", globflags, NULL, &p );
1101   } else {
1102 
1103     string pattern;
1104     if ( st == "" ) {
1105       pattern = GetCWD( );
1106       pattern.append( "/*" );
1107       gRes = glob( pattern.c_str( ), globflags, NULL, &p );
1108     } else {
1109       if (
1110       st.at( 0 ) != '/' &&
1111       !(tilde && st.at( 0 ) == '~') &&
1112       !(environment && st.at( 0 ) == '$')
1113         )
1114       {
1115         pattern = GetCWD( );
1116         pattern.append( "/" );
1117         if ( !(st.size( ) == 1 && st.at( 0 ) == '.') ) pattern.append( st );
1118 
1119 
1120 
1121         gRes = glob( pattern.c_str( ), globflags, NULL, &p );
1122       } else {
1123         gRes = glob( st.c_str( ), globflags, NULL, &p );
1124       }
1125     }
1126 
1127 
1128   }
1129 
1130 #ifndef __APPLE__
1131   if ( accErr && (gRes == GLOB_ABORTED || gRes == GLOB_NOSPACE) )
1132     throw GDLException( "FILE_SEARCH: Read error: " + pathSpec );
1133 #else
1134   if ( accErr && (gRes != 0 && p.gl_pathc > 0) ) // NOMATCH is no error
1135     throw GDLException( "FILE_SEARCH: Read error: " + pathSpec );
1136 #endif
1137 
1138   struct stat64 statStruct, statlink;
1139   int accessmode = 0;
1140   if ( tests[0] ) accessmode = R_OK;
1141   if ( tests[1] ) accessmode |= W_OK;
1142   if ( tests[2] ) accessmode |= X_OK;
1143 
1144   if ( gRes == 0 )
1145     for ( SizeT f = 0; f < p.gl_pathc; ++f ) {
1146       int actStat;
1147       std::string actFile = p.gl_pathv[ f];
1148       if ( dotest != 0 ) {
1149             bool isaDir, isaSymLink;
1150               actStat = filestat( actFile.c_str(), statStruct, isaDir, isaSymLink);
1151         if ( tests[testregular] && // (excludes dirs, sym)
1152         (S_ISREG( statStruct.st_mode ) == 0) ) continue;
1153 
1154         if ( tests[testdir] && !isaDir ) continue;
1155               if( tests[testsymlink] && !isaSymLink) continue;
1156 
1157         if ( tests[testzero] &&
1158         (statStruct.st_size != 0) ) continue;
1159         // now read, write, execute:
1160         if ( accessmode != 0 )
1161           if ( access( actFile.c_str(), accessmode ) != 0 ) continue;
1162       }
1163 
1164 
1165 #ifndef __APPLE__
1166       fileList.push_back( BeautifyPath(actFile, !mark) );
1167 #else
1168       if ( !dir )
1169         fileList.push_back(  BeautifyPath(actFile, !mark) );
1170       else { // push only if dir
1171         actStat = lstat64( actFile.c_str(), &statStruct );
1172         if ( S_ISDIR( statStruct.st_mode ) != 0 )
1173           fileList.push_back(  BeautifyPath(actFile, !mark) );
1174       }
1175 #endif
1176     }
1177   globfree( &p );
1178 
1179   if ( st == "" && dir )
1180     fileList.push_back( "" );
1181 }  // static void FileSearch
1182 #endif
1183 #ifdef _WIN32
1184 
1185 // the unix version (as of Oct 2017) is copied to here
BeautifyPath(std::string st,bool removeMark=true)1186   std::string BeautifyPath(std::string st, bool removeMark=true)
1187   {
1188     //removes series of "//", "/./" and "/.." and adjust path accordingly.
1189      if ( st.length( ) > 0 ) {
1190       size_t pp;
1191       pp=0;
1192       do {
1193         pp=st.find( "/./");
1194         if (pp!=string::npos) { st.erase(pp, 2);}
1195       } while (pp!=string::npos);
1196       pp=0;
1197       do {
1198         pp=st.find( "//");
1199         if (pp!=string::npos) { st.erase(pp, 1);}
1200       } while (pp!=string::npos);
1201       //Last "/.."
1202       pp=st.rfind( "/.."); //remove and back if last
1203       if (pp!=string::npos && pp==st.size()-3) {
1204         //erase from previous "/" to pp+3. Unless there is no previous "/"!
1205         size_t prevdir = st.rfind("/",pp-1);
1206         if (prevdir != string::npos) {st.erase(prevdir, pp+3-prevdir);}
1207       }
1208       //Last "/."
1209       pp=st.rfind( "/."); //remove if last
1210       if (pp!=string::npos && pp==st.size()-2) st.erase(pp);
1211       //Last "/" if removeMark is true
1212       if (removeMark) {
1213         pp=st.rfind( "/"); //remove and back if last
1214         if (pp!=string::npos && pp==st.size()-1) st.erase(pp);
1215       }
1216       // other places for "/..": between directories
1217       pp=0;
1218       do {
1219         pp=st.find( "/../");
1220         if (pp!=string::npos) {
1221           //erase from previous "/" to pp+3. Unless there is no previous "/"!
1222           size_t prevdir = st.rfind("/",pp-1);
1223           if (prevdir != string::npos) {st.erase(prevdir, pp+3-prevdir);}
1224           else break; //what should I do?
1225         }
1226       } while (pp!=string::npos);
1227       //First "./"
1228       pp=st.find( "./"); //remove if first
1229       if (pp==0) st.erase(pp,2);
1230     }
1231   return st;
1232   }
1233 
1234 #endif
1235 
1236 
Dirname(const string & in,bool mark_dir=false)1237 static string Dirname( const string& in, bool mark_dir=false ) {
1238 #if defined (_WIN32) && !defined(__CYGWIN__)
1239     char path_sep = '\\';
1240     char path_sep_diffos = '/';
1241 #else
1242     char path_sep = '/';
1243     char path_sep_diffos = '\\';
1244 #endif
1245 
1246     char buf[ PATH_MAX+1];
1247     strncpy( buf, in.c_str(), PATH_MAX+1 );
1248     std::replace( buf, buf+in.length(), path_sep_diffos, path_sep ); // replace separators in input
1249     string dname = dirname( buf );                                   // strip filename if present
1250 
1251     while( !dname.empty() && (dname.back() == path_sep) ) {          // strip trailing separator(s)
1252         dname.pop_back();
1253     }
1254 
1255     if( mark_dir ) dname.push_back( path_sep );                      // append separator if requested
1256 
1257     return dname;
1258 }
1259 
1260 
1261 
1262 
1263 //     modifications        : 2014, 2015 by Greg Jung
PathSearch(FileListT & fileList,const DString & pathSpec,bool noexpand_path=false,bool recursive=false,bool accErr=false,bool mark=false,bool quote=false,bool match_dot=false,bool forceAbsPath=false,bool fold_case=false,bool onlyDir=false,bool * tests=NULL)1264 static void PathSearch( FileListT& fileList,  const DString& pathSpec,
1265     bool noexpand_path=false,
1266         bool recursive=false, bool accErr=false, bool mark=false,
1267         bool quote=false,
1268         bool match_dot=false,
1269         bool  forceAbsPath=false,
1270         bool fold_case=false,
1271         bool onlyDir=false,   bool *tests = NULL)
1272 {
1273     string dir = pathSpec;
1274     size_t dlen = dir.length();
1275 
1276 
1277     if( forceAbsPath ) {
1278         if( dlen > 0) {
1279           if( dir[0] == '.' ) {
1280             if(dlen == 1) dir = GetCWD();
1281             else if (dir[1] == '/') dir = GetCWD() + dir.substr(1);
1282         #ifdef _WIN32
1283             else if (dir[1] == '\\') dir = GetCWD() + dir.substr(1);
1284         #endif
1285             else if (dlen >= 2 && dir[1] =='.') {
1286                 if( dlen == 2) dir = Dirname(GetCWD());
1287                 else if (dir[2] == '/') dir = Dirname(GetCWD()) + dir.substr(2);
1288             #ifdef _WIN32
1289                 else if (dir[2] == '\\') dir = Dirname(GetCWD()) + dir.substr(2);
1290             #endif
1291                 }   // (dlen >= 2 && dir[1]='.')
1292             }   // dir[0] == '.'
1293         }   // dlen > 0
1294         if ( dir.substr(0,2) == "./")
1295             dir = GetCWD() + dir.substr(1);
1296         else
1297         if( dir.substr(0,3) == "../") {
1298             char actualpath [PATH_MAX+1];
1299             char *ptr;
1300             ptr = realpath("../", actualpath);
1301 #ifdef _WIN32
1302                 if (lib::posixpaths) for(int i=0;ptr[i] != 0;i++) if(ptr[i] == '\\') ptr[i] = '/';
1303 #endif
1304             dir = string(ptr) + dir.substr(2);
1305             }
1306         }       // forceAbsPath
1307     size_t pp =  dir.rfind( " ");
1308     if (pp!=string::npos && pp== dir.size()-1)  dir.erase(pp);
1309 
1310 //      if(trace_me) std::cout << "PathSearch, dir=" << dir ;
1311 // always expanding tilde in same manner, WIN32 or not, ignoring "noexpand"
1312     if( dir[0] == '~') {
1313         char* homeDir = getenv( "HOME");
1314         if( homeDir == NULL) homeDir = getenv("HOMEPATH");
1315 
1316         if( homeDir != NULL) {
1317 //                dir = string( homeDir) + "/" + dir.substr(1);
1318             if(dlen == 1) dir = string( homeDir);
1319             else if (dir[1] == '/') dir = string( homeDir) + dir.substr(1);
1320         #ifdef _WIN32
1321                 else if (dir[1] == '\\') dir = string( homeDir) + dir.substr(1);
1322               size_t pp;  // This is to make path names uniform w.r.t. Unix
1323                         //           and compliant with posix shell.
1324               pp=0;
1325                 if (lib::posixpaths) for(;;)
1326                 {
1327                 pp=dir.find( "\\",pp);
1328                 if (pp==string::npos) break;
1329                 dir[pp]='/';
1330               }
1331         #endif
1332         }
1333     } // dir[0] == '~'
1334 #ifndef _WIN32
1335     if( fold_case)
1336       dir = BeautifyPath(makeInsensitive(dir));
1337     else
1338       dir = BeautifyPath(dir);
1339 #endif
1340     DString dirsearch = "";
1341 
1342 // Look for the last dir-separator at end of string.  i.e. file_search('/d/bld/gdl*')
1343     char PS0 = '/';
1344     char PS1 = '/';
1345     // win32 could be either PathSeparator, or both:
1346     #ifdef _WIN32
1347     PS1 =  '\\';
1348     #endif
1349     int dirsep=-1;
1350     int lenpath = dir.length();
1351     int ii=0;
1352     do {
1353         if((dir[ii] == PS0) || (dir[ii] == PS1)) dirsep=ii;
1354        } while( ii++ < lenpath );
1355     if(dirsep == -1) {
1356         dir = ".";
1357         dirsearch = pathSpec;
1358         }
1359     else if( dirsep != lenpath) {
1360         struct stat64    statStruct;
1361         int dirStat = lstat64( dir.c_str(), &statStruct);
1362 //        if(trace_me) cout << " <1>PathSearch:"<<  "quicky "
1363 //                << pathSpec <<" -simple? "<< dirStat<<" dir="<<dir<< endl ;
1364         if( dirStat == 0) {
1365         fileList.push_back(dir);
1366         return;
1367         } else {
1368             dirsearch = dir.substr(dirsep+1);
1369             if(dirsep >= 0) dir.resize(dirsep);
1370             }
1371         }
1372 
1373 
1374 //  if(trace_me) std::cout << "PathSearch:"<<pathSpec <<
1375 //      " dir:" << dir << ",search:" <<  dirsearch  << std::endl;
1376 
1377     PatternSearch( fileList, dir, dirsearch, false,
1378       accErr,   mark,  quote,  match_dot,  forceAbsPath,fold_case,
1379     onlyDir,   tests);
1380 //      if(trace_me) std::cout << "PathSearch: fileList.size()="
1381 //          << fileList.size() << std::endl;
1382 }
1383 
1384 
1385 //     modifications        : 2014, 2015 by Greg Jung
file_expand_path(EnvT * e)1386   BaseGDL* file_expand_path( EnvT* e)
1387   {
1388     // always 1
1389     SizeT nParam=e->NParam(1);
1390 
1391     // accepting only strings as parameters
1392     BaseGDL* p0 = e->GetParDefined(0);
1393     if( p0->Type() != GDL_STRING)
1394       e->Throw("String expression required in this context: " + e->GetParString(0));
1395     DStringGDL* p0S = static_cast<DStringGDL*>(p0);
1396 
1397     SizeT nPath = p0S->N_Elements();
1398 
1399     DStringGDL* res = new DStringGDL(p0S->Dim(), BaseGDL::NOZERO);
1400     for( SizeT r=0; r<nPath ; ++r) {
1401       string tmp=(*p0S)[r];
1402 
1403       if (tmp.length() == 0) {
1404     char* cwd;
1405     char buff[PATH_MAX + 1];
1406     cwd = getcwd( buff, PATH_MAX + 1 );
1407     if( cwd != NULL ){
1408       (*res)[r]= string(cwd);
1409     }
1410     else {
1411       (*res)[r]=""; //( errors are not managed ...)
1412     }
1413       } else {
1414     WordExp(tmp);
1415     char *symlinkpath =const_cast<char*> (tmp.c_str());
1416     char actualpath [PATH_MAX+1];
1417     char *ptr;
1418     ptr = realpath(symlinkpath, actualpath);
1419 #ifdef _WIN32
1420     if (lib::posixpaths) for(int i=0;ptr[i] != 0;i++) if(ptr[i] == '\\') ptr[i] = '/';
1421 #endif
1422     if( ptr != NULL ){
1423       (*res)[r] =string(ptr);
1424     } else {
1425       //( errors are not managed ...)
1426       (*res)[r] = tmp ;
1427     }
1428       }
1429     }
1430     return res;
1431   }
1432 /*
1433     Result = FILE_SEARCH(Path_Specification) (Standard)
1434     or for recursive searching,
1435     Result = FILE_SEARCH(Dir_Specification, Recur_Pattern)
1436     Standard: When called with a single Path_Specification argument, FILE_SEARCH returns
1437     all files that match that specification. This is the same operation, sometimes
1438     referred to as file globbing, performed by most operating system command interpreters
1439     when wildcard characters are used in file specifications.
1440     Recursive: When called with two arguments, FILE_SEARCH performs recursive searching
1441     of directory hierarchies. In a recursive search, FILE_SEARCH looks recursively for
1442     any and all subdirectories in the file hierarchy rooted at the Dir_Specification argument.
1443     Within each of these subdirectories, it returns the names of all files that match the
1444     pattern in the Recur_Pattern argument.
1445     This operation is similar to that performed by the UNIX find(1) command.
1446     NOTE: in order to avoid infinite reference loops,
1447     IDL policy states that symlinks are not followed.
1448     symlnk references should therefore be returned as found, without resolution, and
1449     can be processed by the FILE_READLINK function.
1450     NOTE: Contrary to above documented intention, acutal IDL behavior is that
1451     when called with two arguments the Dir_specification argument could itself be a pattern-search;
1452     hence the dance below where, for Nparam > 1, first duty is to search on the 1st parameter for directories.
1453 //     modifications        : 2014, 2015 by Greg Jung
1454    */
file_search(EnvT * e)1455   BaseGDL* file_search(EnvT* e) {
1456     enum {
1457         testregular = 3, testdir, testzero, testsymlink
1458       };
1459 
1460     bool tests[NTEST_SEARCH];
1461     for (SizeT i = 0; i < NTEST_SEARCH; i++) tests[i] = false;
1462     // keywords
1463     bool tilde = true;
1464     bool fold_case = false;
1465     bool environment = true;
1466     bool noexpand_path = false;
1467     bool accErr = false;
1468     bool mark = false;
1469     bool noSort = false;
1470     bool quote = false;
1471     bool match_dot = false;
1472     bool match_all_dot = false;
1473     bool forceAbsPath = false;
1474 
1475     // common with FINDFILE
1476     static int countIx = e->KeywordIx("COUNT");
1477     bool countKW = e->KeywordPresent(countIx);
1478     bool isFindFile=(e->GetProName() == "FINDFILE");
1479 
1480     if (!isFindFile) {
1481       static int TEST_READIx = e->KeywordIx("TEST_READ");
1482       static int TEST_WRITEIx = e->KeywordIx("TEST_WRITE");
1483       static int TEST_EXECUTABLEIx = e->KeywordIx("TEST_EXECUTABLE");
1484       static int TEST_REGULARIx = e->KeywordIx("TEST_REGULAR");
1485       static int TEST_DIRECTORYIx = e->KeywordIx("TEST_DIRECTORY");
1486       static int TEST_ZERO_LENGTHIx = e->KeywordIx("TEST_ZERO_LENGTH");
1487       static int TEST_SYMLINKIx = e->KeywordIx("TEST_SYMLINK");
1488       static int REGULARIx = e->KeywordIx("REGULAR");
1489       static int DIRECTORYIx = e->KeywordIx("DIRECTORY");
1490       static int ZERO_LENGTHIx = e->KeywordIx("ZERO_LENGTH");
1491       static int SYMLINKIx = e->KeywordIx("SYMLINK");
1492       const int test_kwIx[] = {
1493         TEST_READIx, TEST_WRITEIx, TEST_EXECUTABLEIx,
1494         TEST_REGULARIx, TEST_DIRECTORYIx, TEST_ZERO_LENGTHIx,
1495         TEST_SYMLINKIx
1496       };
1497 
1498       for (SizeT i = 0; i < NTEST_SEARCH; i++) {
1499         if (e->KeywordPresent(test_kwIx[i])) tests[i] = e->KeywordSet(test_kwIx[i]);
1500         else tests[i] = false;
1501       }
1502 
1503       // extra options for convenience:
1504       if (e->KeywordSet(DIRECTORYIx)) tests[testdir] = true;
1505       if (e->KeywordSet(SYMLINKIx)) tests[testsymlink] = true;
1506       if (e->KeywordSet(REGULARIx)) tests[testregular] = true;
1507       if (e->KeywordSet(ZERO_LENGTHIx)) tests[testzero] = true;
1508 
1509       // next three have default behaviour
1510       static int tildeIx = e->KeywordIx("EXPAND_TILDE");
1511       bool tildeKW = e->KeywordPresent(tildeIx);
1512       if (tildeKW) tilde = e->KeywordSet(tildeIx);
1513 
1514       static int environmentIx = e->KeywordIx("EXPAND_ENVIRONMENT");
1515       bool environmentKW = e->KeywordPresent(environmentIx);
1516       if (environmentKW) {
1517         bool Set = e->KeywordSet(environmentIx);
1518         if (Set) {
1519           environment = true;}
1520          else environment = false;
1521       }
1522 
1523     bool noexpand_path = !environment;
1524 
1525       static int fold_caseIx = e->KeywordIx("FOLD_CASE");
1526       bool fold_caseKW = e->KeywordPresent(fold_caseIx);
1527       if (fold_caseKW) fold_case = e->KeywordSet(fold_caseIx);
1528 
1529 
1530       static int accerrIx = e->KeywordIx("ISSUE_ACCESS_ERROR");
1531     accErr = e->KeywordSet( accerrIx);
1532 
1533       static int markIx = e->KeywordIx("MARK_DIRECTORY");
1534     mark = e->KeywordSet( markIx);
1535 
1536       static int nosortIx = e->KeywordIx("NOSORT");
1537     noSort = e->KeywordSet( nosortIx);
1538 
1539       static int quoteIx = e->KeywordIx("QUOTE");
1540     quote = e->KeywordSet( quoteIx);
1541 
1542       static int match_dotIx = e->KeywordIx("MATCH_INITIAL_DOT");
1543     match_dot = e->KeywordSet( match_dotIx);
1544 
1545       static int match_all_dotIx = e->KeywordIx("MATCH_ALL_INITIAL_DOT");
1546     match_all_dot = e->KeywordSet( match_all_dotIx);
1547 
1548       static int fully_qualified_pathIx = e->KeywordIx("FULLY_QUALIFY_PATH");
1549     forceAbsPath = e->KeywordSet( fully_qualified_pathIx);
1550 
1551       if (match_all_dot)
1552         Warning("FILE_SEARCH: MATCH_ALL_INITIAL_DOT keyword ignored (not supported).");
1553 
1554     } else {
1555 #ifndef _WIN32
1556       //Under Windows, FINDFILE appends a "\" character to the end of the returned file name if the file is a directory.
1557       mark=true;
1558 #endif
1559     }
1560     // SYNTAX:
1561     //  Result = FILE_SEARCH(Path_Specification)
1562     //      or for recursive searching,
1563     //  Result = FILE_SEARCH(Dir_Specification, Recur_Pattern)
1564     SizeT nParam = e->NParam(); // 0 -> "*"
1565 
1566     DStringGDL* pathSpec;
1567     SizeT nPath = 0;
1568     bool recursive_dirsearch = true;
1569     bool leading_nullst = true;
1570     DString Pattern = "";
1571     if (nParam > 0) {
1572       BaseGDL* p0 = e->GetParDefined(0);
1573       pathSpec = dynamic_cast<DStringGDL*> (p0);
1574       if (pathSpec == NULL)
1575         e->Throw("String expression required in this context.");
1576 
1577       nPath = pathSpec->N_Elements();
1578       leading_nullst = ((*pathSpec)[0] == "");
1579       if (leading_nullst) Pattern = "*";
1580       // Path_Specification A scalar or array variable of string type, containing file paths to match.
1581       // If Path_Specification is not supplied, or if it is supplied as an empty string,
1582       // FILE_SEARCH uses a default pattern of '*', which matches all files in the current directory
1583       if (nParam > 1) {
1584         e->AssureScalarPar< DStringGDL>(1, Pattern);
1585         // Dir_Specification A scalar or array variable of string type, containing directory paths
1586         // within which FILE_SEARCH will perform recursive searching for files matching the
1587         // Recur_Pattern argument. FILE_SEARCH examines Dir_Specification, and any directory found below it,
1588         // and returns the paths of any files in those directories that match Recur_Pattern.
1589         // 'If Dir_Specification is supplied as an empty string, FILE_SEARCH searches the current directory.'
1590         if ((nPath == 1) && leading_nullst) recursive_dirsearch = false;
1591       }
1592     }
1593 
1594     bool onlyDir = nParam > 1;
1595 
1596     FileListT fileList;
1597 
1598     DLong count;
1599 #ifndef _WIN32
1600     // The alternative can be used in Linux, also.
1601     // replace above with #if 0 to unify methods.
1602     //  Differences? please notify me (GVJ)
1603     //#if 0
1604     if (nPath == 0)
1605       FileSearch(fileList, "",
1606       environment, tilde,
1607       accErr, mark, noSort, quote,
1608       match_dot, forceAbsPath, fold_case,
1609       onlyDir, tests);
1610     else
1611       if (!recursive_dirsearch) fileList.push_back(string("./"));
1612     else // it appears glob is incapable of returning a symlink.!
1613       for (SizeT f = 0; f < nPath; ++f)
1614         FileSearch(fileList, (*pathSpec)[f],
1615         environment, tilde,
1616         accErr, mark, noSort, quote,
1617         match_dot, forceAbsPath, fold_case,
1618         onlyDir, tests);
1619 #else
1620     //       if(trace_me) std::cout << "file_search: nPath=" << nPath <<" nParam="
1621     //           << nParam << std::endl;
1622     if (nPath == 0 or (leading_nullst and nParam == 1))
1623       //      PathSearch(  fileList, "./*",   true, false,
1624       PatternSearch(fileList, "./", "*", false,
1625       accErr, mark, quote, match_dot, forceAbsPath, fold_case,
1626       onlyDir, tests);
1627     else if (!recursive_dirsearch) fileList.push_back(string("./"));
1628     else
1629       for (SizeT f = 0; f < nPath; ++f) {
1630         PathSearch(fileList, (*pathSpec)[f], true, false,
1631           //      PatternSearch(  fileList, "", (*pathSpec)[f],  false,
1632           accErr, mark, quote, match_dot, forceAbsPath, fold_case,
1633           onlyDir, tests);
1634       }
1635 #endif
1636     onlyDir = false;
1637     count = fileList.size();
1638 
1639 
1640     FileListT fileOut;
1641     for (SizeT f = 0; f < count; ++f) {
1642       if (nParam > 1)
1643         PatternSearch(fileOut, fileList[f], Pattern, recursive_dirsearch,
1644         accErr, mark, quote,
1645         match_dot, forceAbsPath, fold_case,
1646         onlyDir, tests);
1647       else
1648         fileOut.push_back(fileList[f]);
1649     }
1650 
1651     DLong pCount = fileOut.size();
1652 
1653     //special trick for findfile returning a single directory: list contents
1654     if (isFindFile && pCount == 1) {
1655       struct stat64 statStruct;
1656       bool isaDir, isaSymLink;
1657       int actStat = filestat(fileList[0].c_str(), statStruct, isaDir, isaSymLink);
1658       if (actStat == 0 && isaDir) {
1659         DIR* dir = opendir(fileList[0].c_str());
1660         if (dir != NULL) {
1661           pCount = 0;
1662           struct dirent* entry;
1663           fileOut.clear();
1664           while ((entry = readdir(dir)) != NULL) {
1665             //avoid copying twice in a string, first in entryStr, then in fileOut //small speedup?
1666 //            DString entryStr( entry->d_name);
1667 //            if( entryStr == "." || entryStr == "..") continue;
1668 //            pCount++;
1669 //            fileOut.push_back(entryStr);
1670             char* name=entry->d_name; //note d_name is supposedly 256 chars max, but this is not really true..
1671             size_t len=strlen(name);
1672             if ((len==1 && (strncmp(name,".",1)==0)) || (len==2 && (strncmp(name,"..",2)==0) ))  continue;
1673             pCount++;
1674             fileOut.push_back(name);
1675           }
1676         }
1677         closedir(dir);
1678       }
1679     }
1680 
1681     if (countKW)
1682       e->SetKW(countIx, new DLongGDL(pCount));
1683 
1684     if (pCount == 0) {
1685       return new DStringGDL("");
1686     }
1687 //bad idea:
1688 //    if (nParam > 2) { // provision for a third parameter = filecount return.
1689 //      e->AssureGlobalPar(2);
1690 //      e->SetPar(2, new DLongGDL(pCount));
1691 //    } // use this only for interactive sessions: not an IDL feature.
1692 
1693 
1694 
1695     if (!noSort)
1696       sort(fileOut.begin(), fileOut.end());
1697 
1698     // fileOut -> res
1699     DStringGDL* res = new DStringGDL(dimension(pCount), BaseGDL::NOZERO);
1700     for (SizeT r = 0; r < pCount; ++r)
1701       (*res)[r] = fileOut[ r];
1702 
1703     return res;
1704   }
1705 
file_basename(EnvT * e)1706   BaseGDL* file_basename( EnvT* e)
1707   {
1708 
1709     SizeT nParams=e->NParam( 1);
1710 
1711     // accepting only strings as parameters
1712     BaseGDL* p0 = e->GetParDefined(0);
1713     if( p0->Type() != GDL_STRING)
1714       e->Throw("String expression required in this context: " + e->GetParString(0));
1715     DStringGDL* p0S = static_cast<DStringGDL*>(p0);
1716 
1717     BaseGDL* p1;
1718     DStringGDL* p1S;
1719     bool DoRemoveSuffix = false;
1720 
1721     if (nParams == 2) {
1722       // shall we remove a suffix ?
1723       p1 = e->GetPar(1);
1724       if( p1 == NULL || p1->Type() != GDL_STRING)
1725     e->Throw("String expression required in this context: " + e->GetParString(1));
1726       p1S = static_cast<DStringGDL*>(p1);
1727       if (p1S->N_Elements() == 1) {
1728     if ((*p1S)[0].length() >0) DoRemoveSuffix=true;
1729       }
1730       if (p1S->N_Elements() > 1)
1731     e->Throw(" Expression must be a scalar or 1 element array in this context: " + e->GetParString(1));
1732     }
1733 
1734     dimension resDim;
1735     resDim=p0S->Dim();
1736     DStringGDL* res = new DStringGDL(resDim, BaseGDL::NOZERO);
1737 
1738     for (SizeT i = 0; i < p0S->N_Elements(); i++) {
1739 
1740       const string& tmp=(*p0S)[i];
1741       if (tmp.length() > 0) {
1742 
1743 #ifdef _WIN32
1744     char drive[_MAX_DRIVE];
1745     char dir[_MAX_DIR];
1746     char fname[_MAX_FNAME];
1747     char ext[_MAX_EXT];
1748 
1749     _splitpath( tmp.c_str(),drive,dir,fname,ext);
1750     string bname = string(fname)+ext;
1751 #else
1752     char buf[ PATH_MAX+1];
1753     strncpy(buf, tmp.c_str(), PATH_MAX+1);
1754     string bname = basename(buf);
1755 #endif
1756 
1757     (*res)[i] = bname;
1758       }
1759       else
1760       (*res)[i]="";
1761     }
1762 
1763     // managing suffixe
1764     if (DoRemoveSuffix) {
1765 
1766       string suffixe=(*p1S)[0];
1767       int suffLength=(*p1S)[0].length();
1768 
1769       static int fold_caseIx = e->KeywordIx( "FOLD_CASE");
1770       bool fold_case = e->KeywordSet( fold_caseIx);
1771 
1772       if (fold_case) suffixe=StrUpCase(suffixe);
1773 
1774       //cout << "suffixe :"<< suffixe << endl;
1775 
1776 
1777       string tmp1, fin_tmp;
1778       for (SizeT i = 0; i < p0S->N_Elements(); i++) {
1779     tmp1=(*res)[i];
1780 
1781     // Strickly greater : if equal, we keep it !
1782     if (tmp1.length() > suffLength) {
1783       fin_tmp=tmp1.substr(tmp1.length()-suffLength);
1784 
1785       if (fold_case) fin_tmp=StrUpCase(fin_tmp);
1786           if (fin_tmp.compare(suffixe) == 0)
1787         (*res)[i]=tmp1.substr(0,tmp1.length()-suffLength);
1788       }
1789     }
1790 
1791     }
1792 
1793     return res;
1794   }
1795 
1796 
file_dirname(EnvT * e)1797   BaseGDL* file_dirname( EnvT* e)
1798   {
1799     // accepting only strings as parameters
1800     BaseGDL* p0 = e->GetParDefined(0);
1801     if( p0->Type() != GDL_STRING)
1802       e->Throw("String expression required in this context: " + e->GetParString(0));
1803     DStringGDL* p0S = static_cast<DStringGDL*>(p0);
1804     bool mark_dir;
1805     dimension resDim;
1806     resDim=p0S->Dim();
1807     DStringGDL* res = new DStringGDL(resDim, BaseGDL::NOZERO);
1808 
1809     static int mark_dirIx   =   e->KeywordIx("MARK_DIRECTORY");
1810     mark_dir    = e->KeywordSet(mark_dirIx);// mark_dir = add a "/" at end of result.
1811     for (SizeT i = 0; i < p0S->N_Elements(); i++) {
1812       const string& tmp = (*p0S)[i];
1813          (*res)[i] = Dirname(tmp, mark_dir);
1814     }
1815     return res;
1816 
1817   }
1818 
file_same(EnvT * e)1819   BaseGDL* file_same( EnvT* e)
1820   {
1821     // assuring right number of parameters
1822     SizeT nParam=e->NParam(2);
1823 
1824     // accepting only strings as parameters
1825     BaseGDL* p0 = e->GetParDefined(0);
1826     DStringGDL* p0S = dynamic_cast<DStringGDL*>(p0);
1827     if (p0S == NULL) e->Throw("String expression required in this context: " + e->GetParString(0));
1828 
1829     BaseGDL* p1 = e->GetParDefined(1);
1830     DStringGDL* p1S = dynamic_cast<DStringGDL*>(p1);
1831     if (p1S == NULL) e->Throw("String expression required in this context: " + e->GetParString(1));
1832 
1833     // no empty strings accepted
1834     {
1835       int empty = 0;
1836       for (SizeT i = 0; i < p0S->N_Elements(); i++) empty += (*p0S)[i].empty();
1837       for (SizeT i = 0; i < p1S->N_Elements(); i++) empty += (*p1S)[i].empty();
1838       if (empty != 0) e->Throw("Null filename not allowed.");
1839     }
1840 
1841     // allocating memory for the comparison result
1842     DByteGDL* res;
1843     {
1844       dimension resDim;
1845       if (p0S->Rank() == 0 || p1S->Rank() == 0) {
1846         resDim = (p0S->N_Elements() > p1S->N_Elements() ? p0S : p1S)->Dim();
1847       } else {
1848         resDim = (p0S->N_Elements() < p1S->N_Elements() ? p0S : p1S)->Dim();
1849       }
1850       res = new DByteGDL(resDim); // zero
1851     }
1852 
1853     // comparison loop
1854     for (SizeT i = 0; i < res->N_Elements(); i++)
1855       {
1856     // deciding which filename to compare
1857     SizeT p0idx = p0S->Rank() == 0 ? 0 : i;
1858     SizeT p1idx = p1S->Rank() == 0 ? 0 : i;
1859 
1860     // checking for lexically identical paths
1861     if ((*p0S)[p0idx].compare((*p1S)[p1idx]) == 0)
1862       {
1863         (*res)[i] = 1;
1864         continue;
1865       }
1866 
1867     // expanding if needed (tilde, shell variables, etc)
1868     const char *file0, *file1;
1869     string tmp0, tmp1;
1870     static int noexpand_pathIx=e->KeywordIx("NOEXPAND_PATH");
1871     if (!e->KeywordSet(noexpand_pathIx))
1872       {
1873         tmp0 = (*p0S)[p0idx];
1874         WordExp(tmp0);
1875         tmp1 = (*p1S)[p1idx];
1876         WordExp(tmp1);
1877         // checking again for lexically identical paths (e.g. ~/ and $HOME)
1878         if (tmp0.compare(tmp1) == 0)
1879           {
1880         (*res)[i] = 1;
1881         continue;
1882           }
1883         file0 = tmp0.c_str();
1884         file1 = tmp1.c_str();
1885       }
1886     else
1887       {
1888         file0 = (*p0S)[p0idx].c_str();
1889         file1 = (*p1S)[p1idx].c_str();
1890       }
1891 
1892     // checking for the same inode/device numbers
1893     struct stat64 statStruct;
1894     dev_t file0dev;
1895     ino_t file0ino;
1896     int ret = stat64(file0, &statStruct);
1897     if (ret != 0) continue;
1898     file0dev = statStruct.st_dev;
1899     file0ino = statStruct.st_ino;
1900     ret = stat64(file1, &statStruct);
1901     if (ret != 0) continue;
1902     (*res)[i] = (file0dev == statStruct.st_dev && file0ino == statStruct.st_ino);
1903 
1904       }
1905 
1906     return res;
1907 
1908   }
file_test(EnvT * e)1909   BaseGDL* file_test( EnvT* e)
1910   {
1911     SizeT nParam=e->NParam( 1);
1912 
1913     BaseGDL* p0 = e->GetParDefined( 0);
1914 
1915     DStringGDL* p0S = dynamic_cast<DStringGDL*>( p0);
1916     if( p0S == NULL)
1917       e->Throw( "String expression required in this context: "+
1918         e->GetParString(0));
1919 
1920     static int directoryIx = e->KeywordIx( "DIRECTORY");
1921     bool directory = e->KeywordSet( directoryIx);
1922 
1923     static int executableIx = e->KeywordIx( "EXECUTABLE");
1924     bool executable = e->KeywordSet( executableIx);
1925 
1926     static int readIx = e->KeywordIx( "READ");
1927     bool read = e->KeywordSet( readIx);
1928 
1929     static int writeIx = e->KeywordIx( "WRITE");
1930     bool write = e->KeywordSet( writeIx);
1931 
1932     static int zero_lengthIx = e->KeywordIx( "ZERO_LENGTH");
1933     bool zero_length = e->KeywordSet( zero_lengthIx);
1934 
1935     static int get_modeIx = e->KeywordIx( "GET_MODE");
1936     bool get_mode = e->KeywordPresent( get_modeIx);
1937 
1938     static int regularIx = e->KeywordIx( "REGULAR");
1939     bool regular = e->KeywordSet( regularIx);
1940 
1941     static int block_specialIx = e->KeywordIx( "BLOCK_SPECIAL");
1942     bool block_special = e->KeywordSet( block_specialIx);
1943 
1944     static int character_specialIx = e->KeywordIx( "CHARACTER_SPECIAL");
1945     bool character_special = e->KeywordSet( character_specialIx);
1946 
1947     static int named_pipeIx = e->KeywordIx( "NAMED_PIPE");
1948     bool named_pipe = e->KeywordSet( named_pipeIx);
1949 
1950     static int socketIx = e->KeywordIx( "SOCKET");
1951     bool socket = e->KeywordSet( socketIx);
1952 
1953     static int symlinkIx = e->KeywordIx( "SYMLINK");
1954     bool symlink = e->KeywordSet( symlinkIx);
1955 
1956     static int dSymlinkIx = e->KeywordIx( "DANGLING_SYMLINK");
1957     bool dsymlink = e->KeywordSet( dSymlinkIx);
1958 
1959     static int noexpand_pathIx = e->KeywordIx( "NOEXPAND_PATH");
1960     bool noexpand_path = e->KeywordSet( noexpand_pathIx);
1961 
1962     DLongGDL* getMode = NULL;
1963     if( get_mode)
1964       {
1965     getMode = new DLongGDL( p0S->Dim()); // zero
1966     e->SetKW( get_modeIx, getMode);
1967       }
1968 
1969     DLongGDL* res = new DLongGDL( p0S->Dim()); // zero
1970 
1971     //     bool doStat =
1972     //       zero_length || get_mode || directory ||
1973     //       regular || block_special || character_special ||
1974     //       named_pipe || socket || symlink;
1975 
1976     SizeT nEl = p0S->N_Elements();
1977 
1978     for( SizeT f=0; f<nEl; ++f)
1979       {
1980     string actFile;
1981 
1982     if ( !noexpand_path ) {
1983       string tmp = (*p0S)[f];
1984           WordExp(tmp);
1985 
1986       // Ilia2015 : about "|" : see 2 places (file_test() and file_info()) in "file.cpp",
1987       // and one place in "str.cpp" same label
1988       tmp=tmp.substr(0, tmp.find("|", 0));
1989 
1990       if( tmp.length() > 1 && tmp[ tmp.length()-1] == '/')
1991         actFile = tmp.substr(0,tmp.length()-1);
1992       else
1993         actFile = tmp;
1994         }
1995         else
1996       {
1997         const string& tmp = (*p0S)[f];
1998         if( tmp.length() > 1 && tmp[ tmp.length()-1] == '/')
1999           actFile = tmp.substr(0,tmp.length()-1);
2000         else
2001           actFile = tmp;
2002       }
2003 
2004         struct stat64 statStruct, statlink;
2005         bool isaDir, isaSymLink;
2006         int actStat = filestat(actFile.c_str(), statStruct, isaDir, isaSymLink);
2007 
2008     if( actStat != 0)     continue;
2009 
2010         bool isaDanglingSymLink = false;
2011         if (isaSymLink ) isaDanglingSymLink =
2012             (stat64(actFile.c_str(), &statlink) != 0);
2013     //
2014     //be more precise in case of symlinks --- use stat
2015 // to check if target exists or is a dangling symlink
2016 
2017     if( read && access( actFile.c_str(), R_OK) != 0)  continue;
2018     if( write && access( actFile.c_str(), W_OK) != 0)  continue;
2019     if( zero_length && statStruct.st_size != 0)       continue;
2020 
2021 
2022 #ifndef _WIN32
2023 
2024     if( executable && access( actFile.c_str(), X_OK) != 0)    continue;
2025 
2026     if( get_mode)
2027       (*getMode)[ f] = statStruct.st_mode &
2028         (S_IRWXU | S_IRWXG | S_IRWXO);
2029 
2030     if( block_special && S_ISBLK(statStruct.st_mode) == 0)    continue;
2031 
2032     if( character_special && S_ISCHR(statStruct.st_mode) == 0) continue;
2033 
2034     if( named_pipe && S_ISFIFO(statStruct.st_mode) == 0)       continue;
2035 
2036     if( socket && S_ISSOCK(statStruct.st_mode) == 0)      continue;
2037 
2038 #endif
2039 
2040     if( dsymlink && !isaDanglingSymLink )     continue;
2041         if( symlink && !isaSymLink )      continue;
2042 
2043     if( directory && !isaDir)      continue;
2044 
2045     if( regular && S_ISREG(statStruct.st_mode) == 0)
2046       continue;
2047 
2048     (*res)[ f] = 1;
2049 
2050       }
2051     return res;
2052   }
2053 
file_lines(EnvT * e)2054   BaseGDL* file_lines( EnvT* e) {
2055     SizeT nParam = e->NParam(1); //, "FILE_LINES");
2056     DStringGDL* p0S = e->GetParAs<DStringGDL>(0); //, "FILE_LINES");
2057 
2058     SizeT nEl = p0S->N_Elements();
2059     if (nEl == 0)
2060       e->Throw("invalid argument");
2061 
2062     static int compressIx = e->KeywordIx("COMPRESS");
2063     bool compressed = e->KeywordSet(compressIx);
2064     static int noExpIx = e->KeywordIx("NOEXPAND_PATH");
2065     bool noExp = e->KeywordSet(noExpIx);
2066 
2067     DLongGDL* res = new DLongGDL( p0S->Dim(), BaseGDL::NOZERO);
2068 
2069 
2070 
2071     if (compressed) {
2072       char newinput, lastchar = 0;
2073       SizeT lines;
2074               gzFile gfd = NULL;
2075         for (SizeT i = 0; i < nEl; ++i) {
2076         std::string fname = (*p0S)[i];
2077 
2078         if (!noExp) WordExp(fname);
2079 
2080         if ((gfd = gzopen(fname.c_str(), "r")) == NULL) {
2081           e->Throw("Could not open file for reading "); // + p0[i]);
2082         }
2083         lines = 0;
2084         while (gzread(gfd, &newinput, 1) == 1) {
2085           if (newinput == '\n') {
2086             lines++;
2087             if (lastchar == '\r') lines--;
2088           } else if (newinput == '\r') lines++;
2089           lastchar = newinput;
2090         }
2091         gzclose(gfd);
2092         if (lastchar != '\n' && lastchar != '\r') lines++;
2093 
2094         (*res)[ i] = lines;
2095       }
2096     } else { //
2097       char* newinput=(char*) malloc(BUFSIZ);
2098       char lastchar = 0;
2099       SizeT lines;
2100               FILE* fd = NULL;
2101         for (SizeT i = 0; i < nEl; ++i) {
2102         std::string fname = (*p0S)[i];
2103 
2104         if (!noExp) WordExp(fname);
2105 
2106         if ((fd = fopen(fname.c_str(), "r")) == NULL) {
2107           e->Throw("Could not open file for reading "); // + p0[i]);
2108         }
2109         lines = 0;
2110         int count=0;
2111         count=fread(newinput, 1, BUFSIZ, fd);
2112         while (count != 0) {
2113           for (int i = 0; i < count; ++i) {
2114             if (newinput[i] == '\n') {
2115               lines++;
2116               if (lastchar == '\r') lines--;
2117             } else if (newinput[i] == '\r') lines++;
2118 
2119             lastchar = newinput[i];
2120           }
2121           count = fread(newinput, 1, BUFSIZ, fd);
2122         }
2123 
2124         fclose(fd);
2125         if (lastchar != '\n' && lastchar != '\r') lines++;
2126 
2127         (*res)[ i] = lines;
2128       }
2129       free(newinput);
2130     }
2131 
2132     return res;
2133   }
2134 
2135 
2136 
2137 
2138 // Result = FILE_READLINK(Path [, /ALLOW_NONEXISTENT] [, /ALLOW_NONSYMLINK] [, /NOEXPAND_PATH] )
2139 
file_readlink(EnvT * e)2140   BaseGDL* file_readlink( EnvT* e)
2141   {
2142     SizeT nParam=e->NParam( 1);
2143     DStringGDL* p0S = dynamic_cast<DStringGDL*>(e->GetParDefined(0));
2144     if( p0S == NULL)
2145       e->Throw( "String expression required in this context: "+e->GetParString(0));
2146 
2147     static int noexpand_pathIx = e->KeywordIx( "NOEXPAND_PATH");
2148     bool noexpand_path = e->KeywordSet(noexpand_pathIx);
2149     static int allow_nonexistIx = e->KeywordIx( "ALLOW_NONEXISTENT");
2150     bool allow_nonexist = e->KeywordSet(allow_nonexistIx);
2151     static int allow_nonsymlinkIx = e->KeywordIx( "ALLOW_NONSYMLINK");
2152     bool allow_nonsymlink = e->KeywordSet(allow_nonsymlinkIx);
2153 
2154     SizeT nPath = p0S->N_Elements();
2155 
2156     DStringGDL* res = new DStringGDL(p0S->Dim(), BaseGDL::NOZERO);
2157 
2158 
2159     {
2160 
2161       for( SizeT r=0; r<nPath ; ++r) {
2162         string tmp=(*p0S)[r];
2163 
2164         if (tmp.length() == 0) {
2165           (*res)[r]=""; //( errors are not managed ...)
2166         } else {
2167         if( !noexpand_path) WordExp(tmp);
2168         struct stat64 statStruct;
2169         int actStat = lstat64(tmp.c_str(), &statStruct);
2170         if(actStat != 0) {
2171             if(!allow_nonexist) e->Throw(" Link path does not exist "+tmp);
2172             (*res)[r]="";
2173             continue;
2174         }
2175 #ifdef _WIN32
2176     DWORD dwattrib;
2177         int addlink = 0;
2178         fstat_win32(tmp, addlink, dwattrib);
2179         statStruct.st_mode |= addlink;
2180 #endif
2181         SizeT lenpath = statStruct.st_size;
2182         bool isaSymLink = (S_ISLNK(statStruct.st_mode) != 0);
2183         if(!isaSymLink ) {
2184             if(!allow_nonsymlink) e->Throw(" Path provided is not a symlink "+tmp);
2185             (*res)[r]="";
2186             continue;
2187         }
2188           char *symlinkpath =const_cast<char*> (tmp.c_str());
2189           char actualpath [PATH_MAX+1];
2190           char *ptr;
2191 #ifndef _WIN32
2192 //      SizeT len; // doesn't work this way (opengroup doc):
2193 //      if( len = readlink(symlinkpath, actualpath, PATH_MAX) != -1)
2194 //                          actualpath[len] = '\0';
2195         if( readlink(symlinkpath, actualpath, PATH_MAX) != -1)
2196                 actualpath[lenpath]='\0';
2197         ptr = &actualpath[0];
2198 #else
2199           ptr = realpath(symlinkpath, actualpath);
2200     if (lib::posixpaths) for(int i=0;ptr[i] != 0;i++) if(ptr[i] == '\\') ptr[i] = '/';
2201 
2202 #endif
2203           if( ptr != NULL ){
2204         (*res)[r] =string(ptr);
2205           } else {
2206         (*res)[r] = tmp ;
2207           }
2208         }
2209       }
2210       return res;
2211 
2212     }
2213 
2214       }
2215 
2216 
file_info(EnvT * e)2217     BaseGDL* file_info( EnvT* e)
2218     {
2219       SizeT nParam=e->NParam( 1);
2220       DStringGDL* p0S = dynamic_cast<DStringGDL*>(e->GetParDefined(0));
2221       if( p0S == NULL)
2222     e->Throw( "String expression required in this context: "+
2223           e->GetParString(0));
2224 
2225     bool noexpand_path = e->KeywordSet( "NOEXPAND_PATH");
2226 
2227       DStructGDL* res = new DStructGDL(
2228                        FindInStructList(structList, "FILE_INFO"),
2229                        p0S->Rank() == 0 ? dimension(1) : p0S->Dim()
2230                        );
2231 
2232     static int tName = tName = res->Desc()->TagIndex("NAME");
2233     static int tExists, tRead, tWrite, tExecute, tRegular, tDirectory, tBlockSpecial,
2234     tCharacterSpecial, tNamedPipe, tSetuid, tSetgid, tSocket, tStickyBit,
2235     tSymlink, tDanglingSymlink, tMode, tAtime, tCtime, tMtime, tSize;
2236     static int indices_known = false;
2237       // checking struct tag indices (once)
2238 
2239       if (!indices_known)
2240 
2241         {
2242 
2243           tExists =           res->Desc()->TagIndex("EXISTS");
2244           tRead =             res->Desc()->TagIndex("READ");
2245           tWrite =            res->Desc()->TagIndex("WRITE");
2246           tRegular =          res->Desc()->TagIndex("REGULAR");
2247           tDirectory =        res->Desc()->TagIndex("DIRECTORY");
2248 
2249           tBlockSpecial =     res->Desc()->TagIndex("BLOCK_SPECIAL");
2250           tCharacterSpecial = res->Desc()->TagIndex("CHARACTER_SPECIAL");
2251           tNamedPipe =        res->Desc()->TagIndex("NAMED_PIPE");
2252           tExecute =          res->Desc()->TagIndex("EXECUTE");
2253           //#ifndef _WIN32
2254           tSetuid =           res->Desc()->TagIndex("SETUID");
2255           tSetgid =           res->Desc()->TagIndex("SETGID");
2256           //#else
2257           //          tSetuid =           res->Desc()->TagIndex("SYSTEM");
2258           //          tSetgid =           res->Desc()->TagIndex("HIDDEN");
2259           //#endif
2260           tSocket =           res->Desc()->TagIndex("SOCKET");
2261           tStickyBit =        res->Desc()->TagIndex("STICKY_BIT");
2262           tSymlink =          res->Desc()->TagIndex("SYMLINK");
2263           tDanglingSymlink =  res->Desc()->TagIndex("DANGLING_SYMLINK");
2264           tMode =             res->Desc()->TagIndex("MODE");
2265 
2266           tAtime =            res->Desc()->TagIndex("ATIME");
2267           tCtime =            res->Desc()->TagIndex("CTIME");
2268           tMtime =            res->Desc()->TagIndex("MTIME");
2269           tSize =             res->Desc()->TagIndex("SIZE");
2270 
2271           indices_known = true;
2272 
2273         }
2274 
2275     SizeT nEl = p0S->N_Elements();
2276 
2277     for (SizeT f = 0; f < nEl; f++)
2278     {
2279         // NAME
2280         const char* actFile;
2281         string p0Sf = (*p0S)[f];
2282         while(p0Sf.compare(0,1," ")==0) p0Sf.erase(p0Sf.begin()); // remove leading whitespaces
2283         if (!noexpand_path)
2284         {
2285 //          p0Sf = (*p0S)[f];
2286           WordExp(p0Sf);
2287 
2288           // Ilia2015 : about "|" : see 2 places (file_test() and file_info()) in "file.cpp",
2289           // and one place in "str.cpp" same label
2290           p0Sf=p0Sf.substr(0, p0Sf.find("|", 0)); //take the first file that corresponds the pattern.
2291           if( p0Sf.length() > 1 && p0Sf[ p0Sf.length()-1] == '/')
2292                     p0Sf = p0Sf.substr(0,p0Sf.length()-1);
2293             }
2294         actFile = p0Sf.c_str();
2295 
2296         *(res->GetTag(tName, f)) = DStringGDL(p0Sf);
2297         // stating the file (and moving on to the next file if failed)
2298        struct stat64 statStruct, statlink;
2299 /***
2300        bool isaDir, isaSymLink;
2301        int actStat = filestat(actFile, statStruct, isaDir, isaSymLink);
2302 ****/
2303        int actStat = lstat64(actFile, &statStruct);
2304        int addlink = 0;
2305 
2306 #ifdef _WIN32
2307     DWORD dwattrib;
2308        fstat_win32(actFile, addlink, dwattrib);
2309        statStruct.st_mode |= addlink;
2310 #endif
2311 
2312        bool isaDir = (S_ISDIR(statStruct.st_mode) != 0);
2313        bool isaSymLink = S_ISLNK(statStruct.st_mode);
2314        if (isaSymLink ) {
2315          addlink = stat64(actFile, &statlink);
2316          isaDir = (S_ISDIR(statlink.st_mode) != 0);
2317         }
2318 //     bool isaDanglingSymLink = ( isaSymLink && (addlink != 0) );
2319 
2320      if( actStat != 0 ) continue;
2321         if (isaSymLink)
2322         {
2323           *(res->GetTag(tSymlink, f)) = DByteGDL(1);
2324           if( addlink != 0 )
2325         *(res->GetTag(tDanglingSymlink, f)) = DByteGDL(1);
2326         }
2327 
2328 
2329       // EXISTS (would not reach here if stat failed)
2330       *(res->GetTag(tExists, f)) = DByteGDL(1);
2331 
2332       // READ, WRITE, EXECUTE
2333 
2334       *(res->GetTag(tRead, f)) =    DByteGDL(access(actFile, R_OK) == 0);
2335 
2336       *(res->GetTag(tWrite, f)) =   DByteGDL(access(actFile, W_OK) == 0);
2337 
2338 #ifndef _MSC_VER
2339 
2340       *(res->GetTag(tExecute, f)) = DByteGDL(access(actFile, X_OK) == 0);
2341 
2342 #endif
2343 
2344 
2345 
2346       // REGULAR, DIRECTORY, BLOCK_SPECIAL, CHARACTER_SPECIAL, NAMED_PIPE, SOCKET
2347 
2348       *(res->GetTag(tRegular, f)) =          DByteGDL(S_ISREG( statStruct.st_mode) != 0);
2349 
2350       *(res->GetTag(tDirectory, f)) =        DByteGDL(isaDir);
2351 
2352 #ifndef _WIN32
2353 
2354       *(res->GetTag(tBlockSpecial, f)) =     DByteGDL(S_ISBLK( statStruct.st_mode) != 0);
2355 
2356       *(res->GetTag(tCharacterSpecial, f)) = DByteGDL(S_ISCHR( statStruct.st_mode) != 0);
2357 
2358       *(res->GetTag(tNamedPipe, f)) =        DByteGDL(S_ISFIFO(statStruct.st_mode) != 0);
2359 #ifndef __MINGW32__
2360       *(res->GetTag(tSocket, f)) =           DByteGDL(S_ISSOCK(statStruct.st_mode) != 0);
2361 #endif
2362 
2363 #endif
2364 
2365       // SETUID, SETGID, STICKY_BIT
2366 
2367 #ifndef _WIN32
2368 
2369       *(res->GetTag(tSetuid, f)) =           DByteGDL((S_ISUID & statStruct.st_mode) != 0);
2370       *(res->GetTag(tSetgid, f)) =           DByteGDL((S_ISGID & statStruct.st_mode) != 0);
2371       *(res->GetTag(tStickyBit, f)) =        DByteGDL((S_ISVTX & statStruct.st_mode) != 0);
2372 
2373       // MODE
2374 
2375       *(res->GetTag(tMode, f)) = DLongGDL(
2376                           statStruct.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO | S_ISUID | S_ISGID | S_ISVTX)
2377                           );
2378 #else
2379         if(tSetuid != 0) *(res->GetTag(tSetuid, f)) =
2380           DByteGDL( (FILE_ATTRIBUTE_SYSTEM & dwattrib) != 0);
2381         if(tSetgid != 0) *(res->GetTag(tSetgid, f)) =
2382           DByteGDL( (FILE_ATTRIBUTE_HIDDEN & dwattrib) != 0);
2383       *(res->GetTag(tMode, f)) = DLongGDL(dwattrib);
2384 #endif
2385 
2386       // ATIME, CTIME, MTIME
2387       *(res->GetTag(tAtime, f)) = DLong64GDL(statStruct.st_atime);
2388       *(res->GetTag(tCtime, f)) = DLong64GDL(statStruct.st_ctime);
2389       *(res->GetTag(tMtime, f)) = DLong64GDL(statStruct.st_mtime);
2390 
2391       // SIZE
2392       *(res->GetTag(tSize, f)) = DLong64GDL(statStruct.st_size);
2393     }
2394 
2395       return res;
2396 
2397 } // file_info
2398 
file_mkdir(EnvT * e)2399 void file_mkdir( EnvT* e)
2400 {
2401       // sanity checks
2402       SizeT nParam=e->NParam( 1);
2403       for (int i=0; i<nParam; i++)
2404     {
2405       if (dynamic_cast<DStringGDL*>(e->GetParDefined(i)) == NULL)
2406         e->Throw( "All arguments must be string scalars/arrays, argument " + i2s(i+1) + " is: " + e->GetParString(i));
2407     }
2408 
2409       static int noexpand_pathIx = e->KeywordIx( "NOEXPAND_PATH");
2410       bool noexpand_path = e->KeywordSet( noexpand_pathIx);
2411         int status;
2412       for (int i=0; i<nParam; i++)
2413     {
2414       DStringGDL* pi = dynamic_cast<DStringGDL*>(e->GetParDefined(i));
2415       for (int j=0; j<pi->N_Elements(); j++)
2416         {
2417           string tmp = (*pi)[j];
2418           if (!noexpand_path) WordExp(tmp);
2419         #ifndef _WIN32
2420         status = mkdir(tmp.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
2421         #else
2422         status = mkdir(tmp.c_str());
2423         #endif
2424         if (status == 0) continue; // the easy case.
2425         if (errno == EEXIST) {
2426             ;
2427            struct stat64 statStruct;
2428            bool isaDir, isaSymLink;
2429 
2430            int actStat = filestat(tmp.c_str(), statStruct, isaDir, isaSymLink);
2431            if( isaDir) continue;
2432 //            std::cout << " File_mkdir error: a file (not directory) already exists:"<<tmp<<std::endl;
2433             throw GDLException( " a file (not directory) already exists:"+tmp);
2434 
2435         }  else if (errno == ENOENT) {
2436 
2437             vector<std::string> paths;
2438             paths.push_back(tmp);
2439             string dn = Dirname(tmp);
2440              while( dn != tmp ) {
2441                 tmp = dn;
2442                 #ifndef _WIN32
2443                 status = mkdir(tmp.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
2444         #else
2445                 status = mkdir(tmp.c_str());
2446         #endif
2447                 if( status == 0) break;
2448                 if (errno != ENOENT) break;
2449                 paths.push_back(tmp);
2450                  dn = Dirname(tmp);
2451         }
2452             if(status == 0) {
2453                 SizeT szp = paths.size();
2454                 for (int d = 0; d < szp; ++d) {
2455                     tmp = paths.back(); paths.pop_back();
2456                     #ifndef _WIN32
2457                     status = mkdir(tmp.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
2458                     #else
2459                     status = mkdir(tmp.c_str());
2460                     #endif
2461                     }
2462                 } else {
2463 //                std::cout << " File_mkdir: unable/unwilling to create "<<tmp<<std::endl;
2464                 throw GDLException( " unable/unwilling to create "+tmp);
2465             }
2466 
2467             } else
2468 //                std::cout << " !!!! File_mkdir: unable to create "<<tmp<<std::endl;
2469                 throw GDLException(   " !!!! unable to create "+tmp);
2470       } // for (int j=0; j<pi->N_Elements(); j++)
2471     }
2472 } // file_mkdir
2473 
FileDelete(DString & name,bool verbose,bool recursive)2474 static void FileDelete( DString& name, bool verbose, bool recursive)
2475 {
2476     struct stat64 statStruct;
2477        bool isaDir, isaSymLink;
2478 
2479        int actStat = filestat(name.c_str(), statStruct, isaDir, isaSymLink);
2480     if(actStat != 0) {
2481         cout << " (status="<<actStat<<") FileDelete ERROR: malformed: "+name<<std::endl;
2482         return;
2483     }
2484 //  if(trace_me) printf(" trace: FileDelete= %s \n",name.c_str());
2485     if(isaDir) {
2486         DIR* dir = opendir( name.c_str());
2487         if( dir == NULL) return;
2488         struct dirent* entry;
2489         int nument=0;
2490         while( (entry = readdir( dir)) != NULL) nument++;
2491         closedir( dir);
2492         if(nument > 2 && recursive) {
2493             dir = opendir( name.c_str());
2494             while( (entry = readdir( dir)) != NULL) {
2495                 DString entryStr( entry->d_name);
2496                 if( entryStr == "." || entryStr == "..") continue;
2497                 entryStr = name+"/"+entryStr;
2498                 FileDelete( entryStr, verbose, recursive);
2499                 }
2500             closedir(dir);
2501             }
2502         else if(nument > 2) {
2503             if(verbose)
2504                 cout << " /RECURSIVE keyword needed to remove non-empty directory"<<endl;
2505             return;
2506              }
2507       rmdir(name.c_str());
2508      if(verbose) cout << " FILE_DELETE: directory "+name<<endl;
2509          }
2510     else
2511       remove(name.c_str());
2512 
2513      if(verbose) cout << " FILE_DELETE: deleted "+name<<endl;
2514 } // static void FileDelete
file_delete(EnvT * e)2515 void file_delete( EnvT* e)
2516 {
2517     // sanity checks
2518     SizeT nParam=e->NParam( 1);
2519     static int noexpand_pathIx = e->KeywordIx( "NOEXPAND_PATH");
2520     bool noexpand_path = e->KeywordSet( noexpand_pathIx);
2521     static int noexistokIx = e->KeywordIx( "ALLOW_NONEXISTENT");
2522     bool noexistok = e->KeywordSet( noexistokIx);
2523     static int recursiveIx = e->KeywordIx( "RECURSIVE");
2524     bool recursive = e->KeywordSet( recursiveIx);
2525     static int quietIx = e->KeywordIx( "QUIET");
2526     bool quiet = e->KeywordSet( quietIx);
2527     static int verboseIx = e->KeywordIx( "VERBOSE");
2528     bool verbose = e->KeywordSet( verboseIx);
2529 
2530 
2531     EnvBaseT* caller = e->Caller();
2532 
2533 //  trace_me = trace_arg(); // set trace
2534 
2535     for (int i=0; i<nParam; i++)
2536     {
2537       DStringGDL* pi = dynamic_cast<DStringGDL*>(e->GetParDefined(i));
2538 
2539       if (pi == NULL) {
2540           if (quiet) continue;
2541 
2542           cout << " file_delete: error parameter "
2543                << caller->GetString( e->GetPar(i),false)
2544                <<" is not a string "<<endl;
2545                continue;
2546       }
2547 
2548       for (SizeT j=0; j<pi->N_Elements(); j++) {
2549         DString srctmp = (*pi)[j];
2550         FileListT fileList;
2551         PathSearch( fileList, srctmp, noexpand_path );
2552         for(SizeT k=0; k < fileList.size(); k++) {
2553             if(!noexpand_path) WordExp(fileList[k]);
2554            FileDelete( fileList[k], verbose, recursive);
2555            }
2556         }
2557     }
2558 
2559 }
2560 
2561 #include <utime.h>
2562 
copy_basic(const char * source,const char * dest)2563 static int copy_basic(const char *source, const char *dest)
2564 {
2565     u_int64_t tsize;
2566     size_t size;
2567 
2568     struct stat64 statStruct;
2569     int status = stat64(source, &statStruct);
2570     if(status != 0) return status;
2571     time_t src_mtime = statStruct.st_mtime;  // get mod time to stamp on dest
2572     tsize = statStruct.st_size;
2573     FILE* src = fopen(source, "rb");
2574 // overwrite is prevented in calling procedure (unless /OVERWRITE)
2575     FILE* dst = fopen(dest, "w+b");
2576     int doneyet = 0;
2577     int bufsize = BUFSIZ;
2578  //   if(trace_me) printf(" copy_basic: %s  to: %s size = %d \n",source,dest, tsize);
2579     if(tsize < BUFSIZ*16) {
2580         char buf[BUFSIZ];
2581         while ((size = fread( buf, 1, BUFSIZ, src )) > 0) {
2582 //              if( trace_me) printf(" 0:%d ",size );
2583             fwrite( buf, 1, size, dst);
2584         }
2585     }
2586     else if( tsize < BUFSIZ*1024) {
2587         char buf[BUFSIZ*16];
2588         while(1) {
2589             size = fread( buf, 1, BUFSIZ*16, src );
2590 //              if( trace_me) printf(" 1:%d ",size );
2591             if (size <= 0) break;
2592             fwrite( buf, 1, size, dst);
2593         }
2594     }
2595     else {
2596         char buf[BUFSIZ*1024];
2597         while ((size = fread( buf, 1, BUFSIZ*1024, src )) > 0) {
2598 //              if( trace_me) printf(" 2:%d ",size );
2599             fwrite( buf, 1, size, dst);
2600         }
2601     }
2602 //  if( trace_me) printf(" done \n");
2603     fclose(src);
2604     struct utimbuf times[2];
2605     times[0].actime = statStruct.st_atime;
2606     times[1].actime = statStruct.st_atime;
2607     times[0].modtime = statStruct.st_mtime;
2608     times[1].modtime = statStruct.st_mtime;
2609     fclose(dst);
2610     status = utime( dest, times);
2611 
2612     int srcmode = statStruct.st_mode;
2613     status = lstat64(dest, &statStruct);
2614     if( statStruct.st_mode != srcmode)
2615         status = chmod(dest, srcmode);
2616 
2617     return status;
2618 }
2619 
FileCopy(FileListT & fileList,const DString & destdir,bool overwrite,bool recursive=false,bool copy_symlink=false,bool verbose=true)2620 static void FileCopy(   FileListT& fileList, const DString& destdir,
2621             bool overwrite, bool recursive=false,
2622             bool copy_symlink=false,
2623             bool verbose = true)
2624 {
2625 //  trace_me = trace_arg();
2626     DString destination;
2627     string srctmp;
2628     struct stat64 statStruct;
2629     SizeT nmove = fileList.size();
2630     std::string PS = string("/");
2631     #ifdef _WIN32
2632          PS = string("\\");
2633     #endif
2634     string bname, dname;
2635     for(SizeT isrc = 0; isrc < nmove; isrc++) {
2636         char actualpath [PATH_MAX+1];
2637         const char* fileC = fileList[isrc].c_str();
2638         #ifdef _WIN32
2639             char drive[_MAX_DRIVE];
2640             char dir[_MAX_DIR];
2641             char fname[_MAX_FNAME];
2642             char ext[_MAX_EXT];
2643             _splitpath( fileC ,drive,dir,fname,ext);
2644             bname = string(fname)+ext;
2645         #else
2646             char buf[ PATH_MAX+1];
2647             strncpy(buf, fileC , PATH_MAX+1);
2648             bname = basename(buf);
2649         #endif
2650 //      if(trace_me && (isrc ==0)) cout <<
2651 //                  " fileCopy[0]: bname>"<< bname <<
2652 //                  " destdir>"<< destdir <<
2653 //                  " recursive?"<< recursive << std::endl;
2654         int actStat = lstat64(fileC , &statStruct);
2655         #ifdef _WIN32
2656             DWORD dwattrib;
2657             int addlink = 0;
2658             fstat_win32(fileList[isrc], addlink, dwattrib);
2659             statStruct.st_mode |= addlink;
2660         #endif
2661         bool isalink = S_ISLNK(statStruct.st_mode);
2662         bool isaDir = false;
2663         SizeT lenpath= statStruct.st_size;
2664 
2665         if(isalink) actStat = stat64(fileC , &statStruct);
2666         int srcmode = statStruct.st_mode;
2667         if(actStat == 0)  isaDir = (S_ISDIR(srcmode) != 0);
2668         if(!isaDir) {
2669             destination = destdir + PS + bname;
2670             int dstStat = lstat64(destination.c_str(), &statStruct);
2671             int result = 1;
2672             if(dstStat != 0 || overwrite) {
2673                 if( isalink && copy_symlink) {
2674                     if(verbose) cout << " FILE_COPY: symlink " << fileList[isrc];
2675                 #ifndef _WIN32
2676                     if(readlink(fileC, actualpath, PATH_MAX) != -1) {
2677                         actualpath[lenpath] = '\0';
2678                         result = symlink(actualpath,destination.c_str());
2679                     }
2680                     if(verbose)
2681                        printf(" to %s ->%s \n",destination.c_str(),actualpath);
2682                 #else
2683                     if(verbose)
2684                        cout << " detected for  _WIN32. no /copy_symlink option ! "<<endl;
2685             #endif
2686                 } else {
2687                     if(verbose) std::cout << " FILE_COPY: copy "
2688                     << fileList[isrc]+" to "<< destination;
2689 
2690                     result = copy_basic(fileC,destination.c_str());
2691                     if(verbose) cout << " done " << endl;
2692                 }
2693 
2694                 if(result != 0 && verbose)
2695                    std::cout << " FILE_COPYr: FAILED to copy "
2696                         << fileList[isrc]+" to "<< destination << endl;
2697                 } // (dstStat != 0 || overwrite)
2698                 else
2699                 printf(" FILE_COPY: %s overwrite not allowed \n",fileC);
2700          } // !isadir
2701           else {
2702             if(!recursive) {
2703                 if(verbose) cout << " FILE_COPY: it is an error to list "
2704                     << " a directory to copy and not include a /recursive"
2705                     << " keyword "<<endl;
2706             continue;
2707             }
2708 // -----
2709 // code to make "copy, srcdir/, newdir" create a new directory at same level.
2710             actStat = lstat64(destdir.c_str() , &statStruct);
2711             if(actStat == 0) destination = destdir + PS + bname;
2712             else destination = destdir; // + PS;
2713 // -----
2714             FileListT fL;
2715             PathSearch( fL, fileList[isrc]+"/*");
2716             #ifndef _WIN32
2717             int status = mkdir(destination.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
2718             #else
2719             int status = mkdir(destination.c_str());
2720             #endif
2721             if(status != 0) continue;
2722 
2723             FileCopy(fL, destination, overwrite, recursive,  copy_symlink,verbose);// || trace_me );
2724             }
2725         }
2726     return;
2727 }
2728 
file_copy(EnvT * e)2729 void file_copy( EnvT* e)
2730 {
2731     SizeT nParam=e->NParam( 2);
2732     DStringGDL* p0S = dynamic_cast<DStringGDL*>(e->GetParDefined(0));
2733     if( p0S == NULL)
2734       e->Throw( "String expression required in this context: "+
2735         e->GetParString(0));
2736     DStringGDL* p1S = dynamic_cast<DStringGDL*>(e->GetParDefined(1));
2737     if( p1S == NULL)
2738       e->Throw( "String expression required in this context: "+
2739         e->GetParString(1));
2740 
2741     struct stat64 statStruct, srcStruct;
2742 
2743 //    trace_me = trace_arg();
2744     bool recursive = e->KeywordSet( "RECURSIVE");
2745     bool noexpand_path = e->KeywordSet( "NOEXPAND_PATH");
2746     bool copy_symlink = e->KeywordSet( "COPY_SYMLINK");
2747     bool allow_same = e->KeywordSet( "ALLOW_SAME");
2748     bool overwrite = e->KeywordSet( "OVERWRITE");
2749     bool require_directory = e->KeywordSet( "REQUIRE_DIRECTORY");
2750     bool verbose = e->KeywordSet( "VERBOSE");
2751     int nsrc = p0S->N_Elements();
2752     int ndest = p1S->N_Elements();
2753     string dsttmp = string("");
2754     string srctmp = string("");
2755     string dstdir = string("");
2756     string cwd = GetCWD();
2757     if(ndest > 0) dstdir = (*p1S)[0];
2758 //      trace_me = trace_arg();
2759 /*  if(trace_me) {
2760         string test = dstdir;
2761          if(!noexpand_path) WordExp(test);
2762         std::cout << " file_copy:dstdir=<" << dstdir <<"> WordExp("")=<"
2763             << test <<">"<<std::endl;
2764     }*/
2765 
2766      if(!noexpand_path) WordExp(dstdir);
2767      if( dstdir[0] == '~')
2768        {
2769     char* homeDir = getenv( "HOME");
2770     if( homeDir == NULL) homeDir = getenv("HOMEPATH");
2771 
2772     if( homeDir != NULL)
2773             dstdir = string( homeDir) + "/" + dstdir.substr(1);
2774        }
2775     if(nsrc > 0) srctmp = (*p0S)[0];
2776     bool dest_is_directory= false;  // when parameter 2 is an existing directory.
2777     int actStat, result, dstStat;
2778     if(ndest == 1) {
2779 //      if(trace_me) std::cout << " dstdir: " << dstdir;
2780         size_t dlen = dstdir.length();
2781         if( dlen > 0) {
2782           if( dstdir[0] == '.' ) {
2783             if(dlen == 1) dstdir = GetCWD();
2784             else if (dstdir[1] == '/') dstdir = GetCWD() + dstdir.substr(1);
2785         #ifdef _WIN32
2786             else if (dstdir[1] == '\\') dstdir = GetCWD() + dstdir.substr(1);
2787         #endif
2788             else if (dlen >= 2 && dstdir[1] =='.') {
2789                 if( dlen == 2) dstdir = Dirname(GetCWD());
2790                 else if (dstdir[2] == '/') dstdir = Dirname(GetCWD()) + dstdir.substr(2);
2791             #ifdef _WIN32
2792                 else if (dstdir[2] == '\\') dstdir = Dirname(GetCWD()) + dstdir.substr(2);
2793             #endif
2794                 } // (dlen >= 2 && dstdir[1]='.')
2795             }// dstdir[0] == '.'
2796     // Now trim any marks off the trailing string
2797 //      if(trace_me) std::cout << " dstdir: " << dstdir;
2798             size_t pp = dstdir.rfind( "/"); //remove and back if last
2799             if (pp!=string::npos && pp==dstdir.size()-1) dstdir.erase(pp);
2800                 #ifdef _WIN32
2801                 pp = dstdir.rfind("\\");
2802             if (pp!=string::npos && pp==dstdir.size()-1) dstdir.erase(pp);
2803                 #endif
2804 //      if(trace_me) std::cout << " dstdir: " << dstdir
2805 //       << std::endl;
2806          }  // dlen > 0
2807          actStat = lstat64(dstdir.c_str(), &statStruct);
2808          if(actStat == 0) {
2809              dest_is_directory = (S_ISDIR(statStruct.st_mode) != 0);
2810              if(require_directory && !dest_is_directory)
2811                  e->Throw(" destination (arg #2) is not a directory, /REQUIRE_DIRECTORY specified");
2812                 }
2813          } // ndest == 1
2814 
2815 //  if(trace_me) {
2816 //      printf(" file_copy: nsrc= %d , 1st=%s ",nsrc,srctmp.c_str());
2817 //      printf("\n      ... ndest= %d dstdir = %s \n",ndest, dstdir.c_str());
2818 //      }
2819     if(nsrc != ndest && !dest_is_directory)
2820         e->Throw(" destination array must be same size as source ");
2821     for(int k=0; k < nsrc; k++) {
2822 
2823         srctmp = (*p0S)[k];
2824         FileListT fileList;
2825         PathSearch( fileList, srctmp, noexpand_path);
2826         SizeT nmove=fileList.size();
2827 //      if( trace_me)
2828 //          printf(" file_copy: srctmp=%s ; nmove= %d",srctmp.c_str(),nmove);
2829         if(nmove == 0) {
2830             cout << " FILE_COPY: Invalid source file specified:"+srctmp<<endl;
2831             continue;
2832             }
2833 
2834         bool dst_isadir = true;
2835         dstStat = 0;
2836         if( !dest_is_directory)  // Ruling out the single directory case.
2837         {   // parameter 2 is not an existing directory (scalar)
2838             dsttmp = (*p1S)[k];
2839             if(!noexpand_path)  WordExp(dsttmp);
2840             dstStat = stat64(dsttmp.c_str(), &statStruct);
2841             dst_isadir = (dstStat == 0) &&
2842                     (S_ISDIR(statStruct.st_mode) != 0);
2843 //          if( trace_me) cout << " XX dstStat, dsttmp " << dstStat, dsttmp ;
2844             }
2845             else
2846                 dsttmp = dstdir;
2847         // dsttmp = destination = a) the one and only directory (=dstdir, dst_isadir = true)
2848         //          b) an existing directory name (=(*p1S)[k], dst_isadir = true)
2849         //  c) a name of non-existant file/directory
2850         //
2851         //  d)
2852 //      if(trace_me)
2853 //          printf(" dest_is_directory? %d dst_isadir? %d \n",dest_is_directory, dst_isadir);
2854         if( !dst_isadir && recursive ) {
2855 //          if(trace_me) cout << " !dst_isadir && recursive " << std::endl;
2856             ; } else
2857         if( !dst_isadir ) {
2858 //          if( trace_me) cout << " !dst_isadir " << std::endl;
2859             if(require_directory )  {
2860                 printf(" FILE_COPY: require_directory, %s%s\n"
2861                         ,dsttmp.c_str()," is not a valid directory");
2862                 if(!verbose) continue;
2863             }
2864             if(nmove > 1) {
2865                 cout << " FILE_COPY: target for "<<nmove<<" multiple sources ("
2866                 <<srctmp<<") is not a directory:"+dsttmp<<endl;
2867                 continue;
2868                 }
2869 
2870             result = 1;
2871             char actualpath [PATH_MAX+1];
2872             const char* fileC = fileList[0].c_str();
2873             actStat = lstat64(fileC, &statStruct);
2874         #ifdef _WIN32
2875             DWORD dwattrib;
2876             int addlink = 0;
2877             fstat_win32(fileList[0], addlink, dwattrib);
2878             statStruct.st_mode |= addlink;
2879         #endif
2880             bool isalink = S_ISLNK(statStruct.st_mode);
2881             SizeT lenpath= statStruct.st_size;
2882             if(isalink) actStat = stat64(fileC , &statStruct);
2883 
2884             if(S_ISDIR(statStruct.st_mode) != 0) {
2885                 if(verbose) cout << " FILE_COPY: FAILED to copy (directory) "
2886                     << fileList[0]+" to "+dsttmp << endl;
2887                     continue;
2888                 }
2889             if( (strcmp(fileC, dsttmp.c_str()) == 0) ) {
2890                 if( allow_same && (dstStat == 0) ) continue;
2891                 printf(" FILE_COPY: '%s' and '%s' are the same file \n",
2892                     fileC, dsttmp.c_str());
2893                 continue;
2894             }
2895 
2896             if(!require_directory && ((dstStat != 0) || overwrite))
2897               if( isalink && copy_symlink) {
2898         #ifndef _WIN32
2899                 if(verbose) cout << " FILE_COPY: symlink " << srctmp;
2900                 if( readlink(fileC, actualpath, PATH_MAX) != -1) {
2901                         actualpath[lenpath] = '\0';
2902                     result = symlink(actualpath,dsttmp.c_str());
2903                     if(verbose) printf(" to %s->%s \n",
2904                         dsttmp.c_str(),actualpath);
2905                 }
2906         #else
2907                 if(verbose)
2908                    cout << " detected. WIN32 copy_symlink doesn't work"<<endl;
2909         #endif
2910                 } else {
2911                     if(verbose) cout << " FILE_COPY: copy "
2912                     << srctmp+" to "<< dsttmp << endl;
2913                     result = copy_basic(fileC,dsttmp.c_str());
2914                 } // (!require_directory && ((dstStat != 0) || overwrite)
2915 
2916             else if(result!=0)
2917                cout << " FILE_COPY0: we FAILED to copy "
2918                     << srctmp+" to "<< dsttmp << std::endl;
2919 // no dstdir
2920             continue;
2921             }  // !dst_isadir. We've provided a name, that is not a file, as target to directory.
2922             else {  // dst_isadir || dstStat != 0
2923 //          if( trace_me) cout << " dst_isadir " << dst_isadir
2924 //                          << " dsttmp: "<< dsttmp <<
2925 //                           " dstStat: "<< dstStat <<  std::endl;
2926                 if( dest_is_directory) ; else
2927                 if( dst_isadir && !recursive) continue;         // require /recursive, if appropriate, in FileCopy
2928 //           if(trace_me) cout << " file_copy, /recursive, dsttmp=" << dsttmp << std::endl;
2929             }
2930 
2931         FileCopy(fileList, dsttmp, overwrite, recursive,  copy_symlink, verbose);
2932         }
2933         return;
2934   }
2935 
2936 //exists as stub procedure for _WIN32 -- no need to compile here (and get errors) for WIN32.
2937 #ifndef _WIN32
file_link(EnvT * e)2938 void file_link( EnvT* e)
2939 { // code mostly originates in file_move (rename)
2940     SizeT nParam=e->NParam( 2);
2941     DStringGDL* p0S = dynamic_cast<DStringGDL*>(e->GetParDefined(0));
2942     if( p0S == NULL)
2943       e->Throw( "String expression required in this context: "+
2944         e->GetParString(0));
2945     DStringGDL* p1S = dynamic_cast<DStringGDL*>(e->GetParDefined(1));
2946     if( p1S == NULL)
2947       e->Throw( "String expression required in this context: "+
2948         e->GetParString(1));
2949     string srctmp, dsttmp, dstdir;
2950     struct stat64 statStruct;
2951 
2952     bool noexpand_path = e->KeywordSet( "NOEXPAND_PATH");
2953     bool allow_same = e->KeywordSet( "ALLOW_SAME");
2954     bool hardlink = e->KeywordSet( "HARDLINK");
2955     bool verbose = e->KeywordSet( "VERBOSE");
2956     int nsrc = p0S->N_Elements();
2957     int ndest = p1S->N_Elements();
2958     bool dest_is_directory= false;
2959     int actStat, result, dststat;
2960      if(ndest == 1) {
2961           dstdir = (*p1S)[0];
2962           if(!noexpand_path) WordExp(dstdir);
2963          actStat = lstat64(dstdir.c_str(), &statStruct);
2964          if(actStat == 0) {
2965             dest_is_directory = (S_ISDIR(statStruct.st_mode) != 0);
2966 
2967             if(dest_is_directory)
2968                  AppendIfNeeded(dstdir,lib::PathSeparator());
2969             else
2970                  e->Throw(" destination (arg #2) is not a directory, /REQUIRE_DIRECTORY specified");
2971 
2972         }
2973      }
2974 
2975     if(nsrc != ndest && !dest_is_directory)
2976         e->Throw(" destination array must be same size as source ");
2977     for(int k=0; k < nsrc; k++) {
2978         srctmp = (*p0S)[k];
2979 
2980         FileListT fileList;
2981         PathSearch( fileList, srctmp, noexpand_path);
2982 
2983         SizeT nmove=fileList.size();
2984         if(nmove == 0) {
2985             cout << " FILE_LINK: Invalid source file to link:"+srctmp<<endl;
2986             continue;
2987         }
2988         char actualpath [PATH_MAX+1];
2989         const char* fileC = fileList[0].c_str();
2990 
2991         bool dst_isadir = true;
2992         int dstStat = 0;
2993         if(dest_is_directory)
2994             dsttmp = dstdir;
2995         else {
2996             dsttmp = (*p1S)[k];
2997             if(!noexpand_path)  WordExp(dsttmp);
2998             dstStat = lstat64(dsttmp.c_str(), &statStruct);
2999             dst_isadir = (dstStat == 0) &&
3000                     (S_ISDIR(statStruct.st_mode) != 0);
3001             }
3002         result = 1;
3003         if(!dst_isadir) {
3004 
3005             if(nmove > 1) {
3006                 cout << " FILE_LINK: target for "<<nmove<<" multiple sources ("
3007                 <<srctmp<<") is not a directory:"+dsttmp<<endl;
3008                 continue;
3009             }
3010 // (Single file)
3011             if((dstStat != 0)) {
3012 //
3013 // this is not right but these links work much better:
3014 //              if(!noexpand_path) fileC = realpath(fileC, actualpath);
3015 #ifndef _WIN32
3016                 if(hardlink)
3017                     result = link(fileC,dsttmp.c_str());
3018                 else
3019                     result = symlink(fileC,dsttmp.c_str());
3020 #endif
3021                 if(verbose && result==0) cout << " FILE_LINK: linked "
3022                         << srctmp+" to "<< dsttmp << endl;
3023                 if(result != 0) cout << " FILE_LINK0: FAILED to link "
3024                         << srctmp+" to "<< dsttmp << endl;
3025                 }
3026             continue;
3027             }
3028 
3029         AppendIfNeeded(dsttmp,lib::PathSeparator());
3030         for(SizeT isrc = 0; isrc < nmove; isrc++) {
3031             fileC = fileList[isrc].c_str();
3032 //
3033 // this is not right but these links work much better:
3034 //          if(!noexpand_path) fileC = realpath(fileC, actualpath);
3035 #ifndef _WIN32
3036             char buf[ PATH_MAX+1];
3037             strncpy(buf, fileC, PATH_MAX+1);
3038             string bname = basename(buf);
3039             srctmp = dsttmp+bname;
3040             if(hardlink)
3041                 result = link(fileC,srctmp.c_str());
3042             else
3043                 result = symlink(fileC,srctmp.c_str());
3044 #endif
3045             if(verbose && result==0) printf(" FILE_LINK: linked %s %s %s \n"
3046                 ,fileC, " to ", srctmp.c_str());
3047             if(result != 0) cout << " FILE_LINK1: FAILED to link "
3048                     << fileList[isrc]+" to "<< srctmp << endl;
3049             }
3050         }
3051         return;
3052 //
3053 }
3054 #endif
3055 
file_move(EnvT * e)3056 void file_move( EnvT* e)
3057 {
3058     SizeT nParam=e->NParam( 2);
3059     DStringGDL* p0S = dynamic_cast<DStringGDL*>(e->GetParDefined(0));
3060     if( p0S == NULL)
3061       e->Throw( "String expression required in this context: "+
3062         e->GetParString(0));
3063     DStringGDL* p1S = dynamic_cast<DStringGDL*>(e->GetParDefined(1));
3064     if( p1S == NULL)
3065       e->Throw( "String expression required in this context: "+
3066         e->GetParString(1));
3067     string srctmp, dsttmp, dstdir;
3068     struct stat64 statStruct;
3069 
3070     bool noexpand_path = e->KeywordSet( "NOEXPAND_PATH");
3071     bool allow_same = e->KeywordSet( "ALLOW_SAME");
3072     bool overwrite = e->KeywordSet( "OVERWRITE");
3073     bool require_directory = e->KeywordSet( "REQUIRE_DIRECTORY");
3074     bool verbose = e->KeywordSet( "VERBOSE");
3075     int nsrc = p0S->N_Elements();
3076     int ndest = p1S->N_Elements();
3077     if(ndest > 0) dstdir = (*p1S)[0];
3078     if(!noexpand_path) WordExp(dstdir);
3079     if( dstdir[0] == '~') {
3080         char* homeDir = getenv( "HOME");
3081         if( homeDir == NULL) homeDir = getenv("HOMEPATH");
3082         if( homeDir != NULL)
3083             dstdir = string( homeDir) + lib::PathSeparator() + dstdir.substr(1);
3084        }
3085     bool dest_is_directory= false;
3086     int actStat, result, dststat;
3087     if(ndest == 1) {
3088         size_t dlen = dstdir.length();
3089         if( dlen > 0) {
3090           if( dstdir[0] == '.' ) {
3091             if(dlen == 1) dstdir = GetCWD();
3092             else if (dstdir[1] == '/') dstdir = GetCWD() + dstdir.substr(1);
3093         #ifdef _WIN32
3094             else if (dstdir[1] == '\\') dstdir = GetCWD() + dstdir.substr(1);
3095         #endif
3096             else if (dlen >= 2 && dstdir[1] =='.') {
3097                 if( dlen == 2) dstdir = Dirname(GetCWD());
3098                 else if (dstdir[2] == '/') dstdir = Dirname(GetCWD()) + dstdir.substr(2);
3099             #ifdef _WIN32
3100                 else if (dstdir[2] == '\\') dstdir = Dirname(GetCWD()) + dstdir.substr(2);
3101             #endif
3102                 } // (dlen >= 2 && dstdir[1]='.')
3103             }// dstdir[0] == '.'
3104             size_t pp = dstdir.rfind( lib::PathSeparator());//remove and back if last
3105             if (pp!=string::npos && pp==dstdir.size()-1) dstdir.erase(pp);
3106                 #ifdef _WIN32
3107                 pp = dstdir.rfind("\\");
3108             if (pp!=string::npos && pp==dstdir.size()-1) dstdir.erase(pp);
3109             #endif
3110          }  // dlen > 0
3111         actStat = lstat64(dstdir.c_str(), &statStruct);
3112         if(actStat == 0) {
3113              dest_is_directory = (S_ISDIR(statStruct.st_mode) != 0);
3114              if(require_directory && !dest_is_directory)
3115                  e->Throw(" destination (arg #2) is not a directory, /REQUIRE_DIRECTORY specified");
3116              if(dest_is_directory)
3117                  AppendIfNeeded(dstdir,lib::PathSeparator());
3118             }
3119         } // (ndest == 1)
3120 
3121     if(nsrc != ndest && !dest_is_directory) {
3122         e->Throw("Requested operation requires arguments to have same number of elements.");
3123     }
3124     for(int k=0; k < nsrc; k++) {
3125         srctmp = (*p0S)[k];
3126         FileListT fileList;
3127         PathSearch( fileList, srctmp, noexpand_path );
3128         SizeT nmove=fileList.size();
3129         if(nmove == 0) e->Throw("Unable to obtain file status. File: "+srctmp);
3130         bool dst_isadir = true;
3131         int dstStat = 0;
3132         if(dest_is_directory)
3133             dsttmp = dstdir;
3134         else {
3135             dsttmp = (*p1S)[k];
3136             if(!noexpand_path)  WordExp(dsttmp);
3137             dstStat = lstat64(dsttmp.c_str(), &statStruct);
3138             dst_isadir = (dstStat == 0) &&
3139                     (S_ISDIR(statStruct.st_mode) != 0);
3140         }
3141         result = 1;
3142         if(!dst_isadir) {
3143             if(require_directory && !verbose) continue;
3144             if(nmove > 1) e->Throw("Destination must be a directory. File: "+dsttmp);
3145 // Single file rename: rename(source, destination). Some rename() will not work, e.g., cross-device links.
3146 // to avoid this, if rename returns -1 (error) then we try silently a copy + delete.
3147             if(!require_directory && ((dstStat != 0) || overwrite)) {
3148                 result = rename(fileList[0].c_str(),dsttmp.c_str());
3149                 if(result != 0)
3150                 {
3151                   std::string initial_reason=std::string(strerror(errno));
3152                   //try a copy
3153                   try {
3154                     std::ifstream  src(fileList[0], std::ios::binary);
3155                     std::ofstream  dst(dsttmp,   std::ios::binary);
3156                     dst << src.rdbuf();
3157                   } catch( ... ) {
3158                     e->Throw("Unable to move file "+srctmp+", reason: "+std::string(strerror(errno)));
3159                   }
3160                   if( remove(fileList[0].c_str()) != 0 ) {
3161                     std::string further_reason=std::string(strerror(errno));
3162                     Warning("Unable to delete file "+fileList[0]+", reason: "+std::string(strerror(errno)));
3163                   }
3164                 }
3165                 if(verbose && result==0) cout << " FILE_MOVE: moved "
3166                         << srctmp+" to "<< dsttmp << endl;
3167             }
3168             continue;
3169             }
3170 //
3171         AppendIfNeeded(dsttmp,lib::PathSeparator());
3172         for(SizeT isrc = 0; isrc < nmove; isrc++) {
3173         #ifdef _WIN32
3174             char drive[_MAX_DRIVE];
3175             char dir[_MAX_DIR];
3176             char fname[_MAX_FNAME];
3177             char ext[_MAX_EXT];
3178 
3179             _splitpath( fileList[isrc].c_str(),drive,dir,fname,ext);
3180             string bname = string(fname)+ext;
3181         #else
3182             char buf[ PATH_MAX+1];
3183             strncpy(buf, fileList[isrc].c_str(), PATH_MAX+1);
3184             string bname = basename(buf);
3185         #endif
3186             srctmp = dsttmp+bname;
3187             result = rename(fileList[isrc].c_str(),srctmp.c_str());
3188             if(verbose && result==0) cout << " FILE_MOVE: moved "
3189                     << fileList[isrc]+" to "<< srctmp << endl;
3190             if(result != 0) cout << " FILE_MOVE: FAILED to move "
3191                     << fileList[isrc]+" to "<< srctmp << endl;
3192             }
3193         }
3194         return;
3195   }
3196 
routine_dir_fun(EnvT * e)3197   BaseGDL* routine_dir_fun( EnvT* e)
3198   {
3199     EnvStackT& callStack = e->Interpreter()->CallStack();
3200 
3201     string path=callStack.back()->GetFilename();
3202     string toto=Dirname(path, true);
3203 
3204     return new DStringGDL(toto);
3205   }
3206 
3207 } // namespace lib
3208