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