unix/SysFileSystem.cpp
Go to the documentation of this file.
1 /*----------------------------------------------------------------------------*/
2 /* */
3 /* Copyright (c) 1995, 2004 IBM Corporation. All rights reserved. */
4 /* Copyright (c) 2005-2009 Rexx Language Association. All rights reserved. */
5 /* */
6 /* This program and the accompanying materials are made available under */
7 /* the terms of the Common Public License v1.0 which accompanies this */
8 /* distribution. A copy is also available at the following address: */
9 /* http://www.ibm.com/developerworks/oss/CPLv1.0.htm */
10 /* */
11 /* Redistribution and use in source and binary forms, with or */
12 /* without modification, are permitted provided that the following */
13 /* conditions are met: */
14 /* */
15 /* Redistributions of source code must retain the above copyright */
16 /* notice, this list of conditions and the following disclaimer. */
17 /* Redistributions in binary form must reproduce the above copyright */
18 /* notice, this list of conditions and the following disclaimer in */
19 /* the documentation and/or other materials provided with the distribution. */
20 /* */
21 /* Neither the name of Rexx Language Association nor the names */
22 /* of its contributors may be used to endorse or promote products */
23 /* derived from this software without specific prior written permission. */
24 /* */
25 /* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS */
26 /* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT */
27 /* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS */
28 /* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT */
29 /* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, */
30 /* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED */
31 /* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, */
32 /* OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY */
33 /* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING */
34 /* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS */
35 /* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
36 /* */
37 /*----------------------------------------------------------------------------*/
38 /******************************************************************************/
39 /* REXX Kernel */
40 /* */
41 /* Unix implementation of the SysFileSystem class. */
42 /* */
43 /******************************************************************************/
44 
45 #include "RexxCore.h"
46 
47 #ifdef HAVE_CONFIG_H
48 # include "config.h"
49 #endif
50 
51 #include <stdlib.h>
52 #include <stddef.h>
53 #include <string.h>
54 #include <stdio.h>
55 #include <sys/types.h>
56 #include <sys/stat.h>
57 #include <unistd.h>
58 #include <utime.h>
59 #include <pwd.h>
60 #include <errno.h>
61 #include "SysFileSystem.hpp"
62 #include "Utilities.hpp"
63 
64 const char SysFileSystem::EOF_Marker = 0x1A;
65 const char *SysFileSystem::EOL_Marker = "\n";
66 const char SysFileSystem::PathDelimiter = '/';
67 
68 #if defined __APPLE__
69 # define open64 open
70 // avoid warning: '(l)stat64' is deprecated: first deprecated in macOS 10.6
71 # define stat64 stat
72 # define lstat64 lstat
73 #endif
74 
75 /*********************************************************************/
76 /* */
77 /* FUNCTION : SearchFileName */
78 /* */
79 /* DESCRIPTION : Search for a given filename, returning the fully */
80 /* resolved name if it is found. */
81 /* */
82 /*********************************************************************/
83 
85  const char* name, /* name of rexx proc to check */
86  char * fullName ) /* fully resolved name */
87 {
88  size_t nameLength; /* length of name */
89  char tempPath[MaximumFileNameBuffer];// temporary place to store the path/name
90  char * currentpath; // current path
91  char * sep; // next colon in the path
92 
93  nameLength = strlen(name); /* get length of incoming name */
94 
95  /* if name is too small or big */
96  if (nameLength < 1 || nameLength > MaximumFileNameBuffer)
97  {
98  return false;
99  }
100 
101  /* does the filename already have a path? */
102  /* or does it start with "~" ? */
103  /* (beware, don't test "." because files like ".hidden" alone are candidate for search in PATH */
104  if (strstr(name, "/") != NULL || name[0] == '~' || name[0] == '.')
105  {
106  bool done = SysFileSystem::canonicalizeName(tempPath);
107  if (done == false || fileExists(tempPath) == false)
108  {
109  fullName[0] = '\0';
110  return false;
111  }
112  strcpy(fullName, tempPath);
113  return true;
114  }
115 
116  /* there was no leading path so try the current directory */
117  if (getcwd(tempPath, MaximumFileNameBuffer) == NULL)
118  {
119  return false;
120  }
121  strcat(tempPath, "/");
122  strcat(tempPath, name);
123  if (fileExists(name) == true)
124  {
125  strcpy(fullName, name);
126  return true;
127  }
128 
129  /* it was not in the current directory so search the PATH */
130  currentpath = getenv("PATH");
131  if (currentpath == NULL)
132  {
133  fullName[0] = '\0';
134  return false;
135  }
136  sep = strchr(currentpath, ':');
137  while (sep != NULL)
138  {
139  /* try each entry in the PATH */
140  int i = sep - currentpath;
141  strncpy(tempPath, currentpath, i);
142  tempPath[i] = '\0';
143  strcat(tempPath, "/");
144  strcat(tempPath, name);
145  if (fileExists(tempPath) == true)
146  {
147  strcpy(fullName, tempPath);
148  return true;
149  }
150  currentpath = sep + 1;
151  sep = strchr(currentpath, ':');
152  }
153  /* the last entry in the PATH may not be terminated by a colon */
154  if (*currentpath != '\0')
155  {
156  strcpy(tempPath, currentpath);
157  strcat(tempPath, "/");
158  strcat(tempPath, name);
159  if (fileExists(tempPath) == true)
160  {
161  strcpy(fullName, tempPath);
162  return true;
163  }
164  }
165 
166  /* file not found */
167  fullName[0] = '\0';
168  return false;
169 }
170 
171 /*********************************************************************/
172 /* */
173 /* FUNCTION : getTempFileName */
174 /* */
175 /* DESCRIPTION : Returns a temp file name. */
176 /* */
177 /*********************************************************************/
179 {
180  return tmpnam(NULL);
181 }
182 
183 
185  const char *name, // input name
186  char *fullName, // the output name
187  size_t bufferSize) // size of the output buffer
188 /*******************************************************************/
189 /* Function: Qualify a stream name for this system */
190 /*******************************************************************/
191 {
192  char tempPath[MaximumFileNameBuffer]; // temporary place to store the path/name
193 
194  /* already expanded? */
195  if (*fullName != '\0')
196  {
197  return; /* nothing more to do */
198  }
199 
200  // Too long?
201  size_t len = strlen(name);
202  if ( len >= bufferSize || len >= MaximumFileNameBuffer)
203  {
204  fullName[0] = '\0';
205  return;
206  }
207 
208  strcpy(tempPath, name);
209  bool done = SysFileSystem::canonicalizeName(tempPath);
210  if (done)
211  {
212  strcpy(fullName, tempPath);
213  }
214  else
215  {
216  fullName[0] = '\0'; // or leave it unchanged ?
217  }
218  return;
219 }
220 
221 /**
222  * Test if a given file exists.
223  *
224  * @param fname The target file name.
225  *
226  * @return true if the file exists, false otherwise.
227  */
228 bool SysFileSystem::fileExists(const char * fname)
229 {
230  struct stat64 filestat; // file attributes
231  int rc; // stat function return code
232 
233  rc = stat64(fname, &filestat);
234  if (rc == 0)
235  {
236  if (S_ISREG(filestat.st_mode))
237  {
238  return true;
239  }
240  }
241  return false;
242 }
243 
244 
245 /**
246  * Extract directory information from a file name.
247  *
248  * @param file The input file name. If this represents a real source file,
249  * this will be fully resolved.
250  *
251  * @return The directory portion of the file name. If the file name
252  * does not include a directory portion, then OREF_NULL is returned.
253  */
255 {
256  const char *pathName = file->getStringData();
257  const char *endPtr = pathName + file->getLength() - 1;
258 
259  // scan backwards looking for a directory delimiter. This name should
260  // be fully qualified, so we don't have to deal with drive letters
261  while (pathName < endPtr)
262  {
263  // find the first directory element?
264  if (*endPtr == '/')
265  {
266  // extract the directory information, including the final delimiter
267  // and return as a string object.
268  return new_string(pathName, stringsize_t(endPtr - pathName + 1));
269  }
270  endPtr--;
271  }
272  return OREF_NULL; // not available
273 }
274 
275 
276 /**
277  * Extract extension information from a file name.
278  *
279  * @param file The input file name. If this represents a real source file,
280  * this will be fully resolved.
281  *
282  * @return The extension portion of the file name. If the file
283  * name does not include an extension portion, then
284  * OREF_NULL is returned.
285  */
287 {
288  const char *pathName = file->getStringData();
289  const char *endPtr = pathName + file->getLength() - 1;
290 
291  // scan backwards looking for a directory delimiter. This name should
292  // be fully qualified, so we don't have to deal with drive letters
293  while (pathName < endPtr)
294  {
295  // find the first directory element?
296  if (*endPtr == '/')
297  {
298  return OREF_NULL; // found a directory portion before an extension...we're extensionless
299  }
300  // is this the extension dot?
301  else if (*endPtr == '.')
302  {
303  // return everything from the period on. Keeping the period on is a convenience.
304  return new_string(endPtr);
305  }
306  endPtr--;
307  }
308  return OREF_NULL; // not available
309 }
310 
311 
312 /**
313  * Extract file information from a file name.
314  *
315  * @param file The input file name. If this represents a real source file,
316  * this will be fully resolved.
317  *
318  * @return The file portion of the file name. If the file name
319  * does not include a directory portion, then the entire
320  * string is returned
321  */
323 {
324  const char *pathName = file->getStringData();
325  const char *endPtr = pathName + file->getLength() - 1;
326 
327  // scan backwards looking for a directory delimiter. This name should
328  // be fully qualified, so we don't have to deal with drive letters
329  while (pathName < endPtr)
330  {
331  // find the first directory element?
332  if (*endPtr == '/')
333  {
334  // extract the directory information, including the final delimiter
335  // and return as a string object.
336  return new_string(endPtr);
337  }
338  endPtr--;
339  }
340  return file; // this is all filename
341 }
342 
343 
344 /**
345  * Test if a filename has an extension.
346  *
347  * @param name The name to check.
348  *
349  * @return true if an extension was found on the file, false if there
350  * is no extension.
351  */
352 bool SysFileSystem::hasExtension(const char *name)
353 {
354  const char *endPtr = name + strlen(name) - 1;
355 
356  // scan backwards looking for a directory delimiter. This name should
357  // be fully qualified, so we don't have to deal with drive letters
358  while (name < endPtr)
359  {
360  // find the first directory element?
361  if (*endPtr == '/')
362  {
363  return false; // found a directory portion before an extension...we're extensionless
364  }
365  // is this the extension dot?
366  else if (*endPtr == '.')
367  {
368  // return everything from the period on. Keeping the period on is a convenience.
369  return true;
370  }
371  endPtr--;
372  }
373  return false; // not available
374 }
375 
376 
377 /**
378  * Test if a filename has a directory portion
379  *
380  * @param name The name to check.
381  *
382  * @return true if a directory was found on the file, false if
383  * there is no directory.
384  */
385 bool SysFileSystem::hasDirectory(const char *name)
386 {
387  // hasDirectory() means we have enough absolute directory
388  // information at the beginning to bypass performing path searches.
389  // We really only need to look at the first character.
390  return name[0] == '~' || name[0] == '/' || name[0] == '.';
391 }
392 
393 
394 /**
395  * Do a search for a single variation of a filename.
396  *
397  * @param name The name to search for.
398  * @param directory A specific directory to look in first (can be NULL).
399  * @param extension A potential extension to add to the file name (can be NULL).
400  * @param resolvedName
401  * The buffer used to return the resolved file name.
402  *
403  * @return true if the file was located. A true returns indicates the
404  * resolved file name has been placed in the provided buffer.
405  */
406 bool SysFileSystem::searchName(const char *name, const char *path, const char *extension, char *resolvedName)
407 {
408  UnsafeBlock releaser;
409  return primitiveSearchName(name, path, extension, resolvedName);
410 }
411 
412 
413 /**
414  * Do a search for a single variation of a filename.
415  *
416  * @param name The name to search for.
417  * @param directory A specific directory to look in first (can be NULL).
418  * @param extension A potential extension to add to the file name (can be NULL).
419  * @param resolvedName
420  * The buffer used to return the resolved file name.
421  *
422  * @return true if the file was located. A true returns indicates the
423  * resolved file name has been placed in the provided buffer.
424  */
425 bool SysFileSystem::primitiveSearchName(const char *name, const char *path, const char *extension, char *resolvedName)
426 {
427  // this is for building a temporary name
428  char tempName[PATH_MAX + 3];
429 
430  // construct the search name, potentially adding on an extension
431  strncpy(tempName, name, sizeof(tempName));
432  if (extension != NULL)
433  {
434  strncat(tempName, extension, sizeof(tempName) - strlen(tempName) - 1);
435  }
436 
437  // only do the direct search if this is qualified enough that
438  // it should not be located on the path
439  if (hasDirectory(tempName))
440  {
441  for (int i = 0; i < 2; i++)
442  {
443  // check the file as is first
444  if (checkCurrentFile(tempName, resolvedName))
445  {
446  return true;
447  }
448  // try again in lower case
449  Utilities::strlower(tempName);
450  }
451  return false;
452  }
453  else
454  {
455  // for each name, check in both the provided case and lower case.
456  for (int i = 0; i < 2; i++)
457  {
458  // go search along the path
459  if (searchPath(tempName, path, resolvedName))
460  {
461  return true;
462  }
463  // try again in lower case
464  Utilities::strlower(tempName);
465  }
466  return false;
467  }
468 }
469 
470 
471 /**
472  * Try to locate a file using just the raw name passed in, as
473  * opposed to searching along a path for the name.
474  *
475  * @param name The name to use for the search.
476  *
477  * @return An RexxString version of the file name, iff the file was located. Returns
478  * OREF_NULL if the file did not exist.
479  */
480 bool SysFileSystem::checkCurrentFile(const char *name, char *resolvedName)
481 {
482  // validate that this is a name that can even be located.
483  size_t nameLength = strlen(name);
484 
485  if (nameLength < 1 || nameLength > PATH_MAX + 1)
486  {
487  return false;
488  }
489 
490  // make a copy of the input name
491  strcpy(resolvedName, name);
492  // take care of any special conditions in the name structure
493  // a failure here means an invalid name of some sort
494  if (!canonicalizeName(resolvedName))
495  {
496  return false;
497  }
498 
499  struct stat64 dummy; /* structure for stat system calls */
500 
501  // ok, if this exists, life is good. Return it.
502  if (stat64(resolvedName, &dummy) == 0) /* look for file */
503  {
504  // this needs to be a regular file
505  if (S_ISREG(dummy.st_mode))
506  {
507  return true;
508  }
509  return false;
510  }
511  // not found
512  return false;
513 }
514 
515 
516 /**
517  * Do a path search for a file.
518  *
519  * @param name The name to search for.
520  * @param path The search path to use.
521  * @param resolvedName
522  * A buffer used for returning the resolved name.
523  *
524  * @return Returns true if the file was located. If true, the resolvedName
525  * buffer will contain the returned name.
526  */
527 bool SysFileSystem::searchPath(const char *name, const char *path, char *resolvedName)
528 {
529  // get an end pointer
530  const char *pathEnd = path + strlen(path);
531 
532  const char *p = path;
533  const char *q = strchr(p, ':');
534  /* For every dir in searchpath*/
535  for (; p < pathEnd; p = q + 1, q = strchr(p, ':'))
536  {
537  // it's possible we've hit the end, in which case, point the delimiter marker past the end of the
538  // string
539  if (q == NULL)
540  {
541  q = pathEnd;
542  }
543  size_t sublength = q - p;
544 
545  memcpy(resolvedName, p, sublength);
546  resolvedName[sublength] = '/';
547  resolvedName[sublength + 1] = '\0';
548  strncat(resolvedName, name, PATH_MAX - strlen(resolvedName));
549 
550  // take care of any special conditions in the name structure
551  // a failure here means an invalid name of some sort
552  if (canonicalizeName(resolvedName))
553  {
554  struct stat64 dummy;
555  if (stat64(resolvedName, &dummy) == 0) /* If file is found, */
556  {
557  // this needs to be a regular file
558  if (S_ISREG(dummy.st_mode))
559  {
560  return true;
561  }
562  return false;
563  }
564  }
565  }
566  return false;
567 }
568 
569 
570 /**
571  * Process a file name to add the current working directory
572  * or the home directory, as needed, then remove all of the
573  * . and .. elements.
574  *
575  * @param name The current working name.
576  *
577  * @return true if this was valid enough to normalize.
578  */
580 {
581  // does it start with the user home marker?
582  if (name[0] == '~')
583  {
584  // this is the typical case. This is a directory based off of
585  // the current users home directory.
586  if (name[1] == '\0' || name[1] == '/')
587  {
588 
589  char tempName[PATH_MAX + 3];
590  // make a copy of the name
591  strncpy(tempName, name, PATH_MAX + 1);
592  strcpy(name, getenv("HOME"));
593  // We don't need to add a slash : If we have "~" alone, then no final slash expected (same as for "~user"). If "~/..." then we have the slash already
594  strncat(name, tempName + 1, PATH_MAX - strlen(name));
595  }
596  else
597  {
598  // referencing a file in some other user's home directory.
599  // we need to extract the username and resolve that home directory
600  char tempName[PATH_MAX + 3];
601  char userName[PATH_MAX + 3];
602 
603  // make a copy of the name
604  strncpy(tempName, name, PATH_MAX - strlen(tempName));
605  // look for the start of a directory
606  char *slash = strchr(tempName,'/');
607  // if there is a directory after the username, we need
608  // to copy just the name piece
609  if (slash != NULL)
610  {
611  size_t nameLength = slash - tempName - 1;
612  memcpy(userName, tempName + 1, nameLength);
613  userName[nameLength] = '\0';
614  }
615  // all username, just copy
616  else
617  {
618  strcpy(userName, tempName + 1);
619  }
620 
621  // see if we can retrieve the information
622  struct passwd *ppwd = getpwnam(userName);
623  if (ppwd == NULL)
624  {
625  // this is not valid without user information, so just fail the operation
626  // if we can't get this.
627  return false; /* nothing happend */
628  }
629 
630  strncpy(name, ppwd->pw_dir, PATH_MAX - strlen(name));
631  // if we have a directory after the username, copy the whole thing
632  if (slash != NULL)
633  {
634  strncat(name, slash, PATH_MAX - strlen(name));
635  }
636  }
637  }
638 
639  // if we're not starting with root, we need to add the
640  // current working directory. This will also handle the
641  // "." and ".." cases, which will be removed by the canonicalization
642  // process.
643  else if (name[0] != '/')
644  {
645  char tempName[PATH_MAX + 2];
646  // make a copy of the name
647  strncpy(tempName, name, PATH_MAX + 1);
648  if (getcwd(name, PATH_MAX + 1) == NULL)
649  {
650  return false;
651  }
652  strncat(name, "/", PATH_MAX - strlen(name));
653  strncat(name, tempName, PATH_MAX - strlen(name));
654  }
655 
656  // NOTE: realpath() is more portable than canonicalize_file_name().
657  // However, they both have problems in that they both fail if the file does
658  // not exist. There are a number of places where the interpreter needs to
659  // canonicalize a path name whether the file exists or not. So we use our
660  // own function to normalize the name.
661  char tempName[PATH_MAX + 2];
662  if ( normalizePathName(name, tempName) )
663  {
664  strcpy(name, tempName);
665  return true;
666  }
667  return false;
668 }
669 
670 
671 /**
672  * Normalize an absolute Unix path name. Removes duplicate and trailing
673  * slashes, resolves and removes ./ or ../ This works for any path name,
674  * whether the resovled name exists or not.
675  *
676  * @param name The path name to normalize.
677  * @param resolved On success the normalized name is returned here.
678  *
679  * @return True on success, otherwise false.
680  *
681  * @assumes Name is null-terminated and that resolved is an adequate buffer.
682  */
683 bool SysFileSystem::normalizePathName(const char *name, char *resolved)
684 {
685  // Path name has to be absolute.
686  if ( *name != '/' )
687  {
688  return false;
689  }
690 
691  char *dest = resolved;
692  char *prevSl = dest;
693  *dest = '/';
694 
695  // For each character in the path name, decide whether, and where, to copy.
696  for ( const char *p = name; *p; p++ )
697  {
698  if ( *p == '/' )
699  {
700  // Only adjust prevSl if we don't have a "." coming up next.
701  if ( *(p + 1) != '.' )
702  {
703  prevSl = dest;
704  }
705  if ( *dest == '/' )
706  {
707  // Remove double "/"
708  continue;
709  }
710  *++dest = *p;
711  }
712  else if ( *p == '.' )
713  {
714  if ( *dest == '/' )
715  {
716  char next = *(p + 1);
717  if ( next == '\0' || next == '/' )
718  {
719  // Don't copy the ".", if at the end, the trailing "/" will
720  // be removed in the final step. If it is: "./", the double
721  // "//" will be removed in the next iteration.
722  continue;
723  }
724  else if ( next == '.' )
725  {
726  // We have "..", but we don't do anything unless the next
727  // position is a "/" or the end. (In case of a file like:
728  // ..my.file)
729  next = *(p + 2);
730  if ( next == '\0' || next == '/' )
731  {
732  p++;
733  dest = prevSl;
734 
735  // Now we probably have to push prevSl back, unless we
736  // are at the root of the file system.
737  while ( prevSl > resolved )
738  {
739  if ( *--prevSl == '/' )
740  {
741  break;
742  }
743  }
744  continue;
745  }
746  }
747  *++dest = *p;
748  }
749  else
750  {
751  *++dest = *p;
752  }
753  }
754  else
755  {
756  *++dest = *p;
757  }
758  }
759 
760  // Terminate. Where, depends on several things.
761  (*dest == '/' && dest != resolved) ? *dest = '\0' : *++dest = '\0';
762 
763  return true;
764 }
765 
766 
767 /**
768  * Delete a file from the file system.
769  *
770  * @param name The fully qualified name of the file.
771  *
772  * @return The return code from the delete operation.
773  */
774 bool SysFileSystem::deleteFile(const char *name)
775 {
776  return unlink(name) == 0;
777 }
778 
779 /**
780  * Delete a directory from the file system.
781  *
782  * @param name The name of the target directory.
783  *
784  * @return The return code from the delete operation.
785  */
786 bool SysFileSystem::deleteDirectory(const char *name)
787 {
788  return remove(name) == 0;
789 }
790 
791 
792 /**
793  * Test if a given file name is for a directory.
794  *
795  * @param name The target name.
796  *
797  * @return true if the file is a directory, false for any other
798  * type of entity.
799  */
800 bool SysFileSystem::isDirectory(const char *name)
801 {
802  struct stat64 finfo; /* return buf for the finfo */
803 
804  int rc = stat64(name, &finfo); /* read the info about it */
805  return rc == 0 && S_ISDIR(finfo.st_mode);
806 }
807 
808 
809 /**
810  * Test is a file is read only.
811  *
812  * @param name The target file name.
813  *
814  * @return true if the file is marked as read-only.
815  */
816 bool SysFileSystem::isReadOnly(const char *name)
817 {
818  return access(name, W_OK) != 0;
819 }
820 
821 
822 /**
823  * Test if a file is marked as write-only.
824  *
825  * @param name The target file name.
826  *
827  * @return true if the file is only writeable. false if read
828  * operations are permitted.
829  */
830 bool SysFileSystem::isWriteOnly(const char *name)
831 {
832  return access(name, R_OK) != 0;
833 }
834 
835 
836 /**
837  * Test if a give file name is for a real file (not
838  * a directory).
839  *
840  * @param name The target file name.
841  *
842  * @return true if the file is a real file, false if some other
843  * filesystem entity.
844  */
845 bool SysFileSystem::isFile(const char *name)
846 {
847  struct stat64 finfo; /* return buf for the finfo */
848 
849  int rc = stat64(name, &finfo); /* read the info about it */
850  return rc == 0 && (S_ISREG(finfo.st_mode) || S_ISBLK(finfo.st_mode));
851 }
852 
853 
854 /**
855  * Test if a file exists using a fully qualified name.
856  *
857  * @param name The target file name.
858  *
859  * @return True if the file exists, false if it is unknown.
860  */
861 bool SysFileSystem::exists(const char *name)
862 {
863  struct stat64 finfo; /* return buf for the finfo */
864 
865  int rc = stat64(name, &finfo); /* read the info about it */
866  return rc == 0;
867 }
868 
869 
870 /**
871  * Get the last modified file date as a file time value.
872  *
873  * @param name The target name.
874  *
875  * @return the file time value for the modified date, or -1 for any
876  * errors. The time is returned in ticks units
877  */
879 {
880  struct stat64 st;
881  tzset ();
882 
883  if (stat64(name, &st))
884  {
885  return -1;
886  }
887  return (int64_t)st.st_mtime;
888 }
889 
890 
891 /**
892  * Retrieve the size of a file.
893  *
894  * @param name The name of the target file.
895  *
896  * @return the 64-bit file size.
897  */
899 {
900  struct stat64 st;
901  if (stat64(name, &st) != 0)
902  {
903  return 0;
904  }
905  return st.st_size;
906 }
907 
908 
909 /**
910  * Create a directory in the file system.
911  *
912  * @param name The target name.
913  *
914  * @return The success/failure flag.
915  */
916 bool SysFileSystem::makeDirectory(const char *name)
917 {
918  return mkdir(name, S_IRWXU | S_IRWXG | S_IRWXO) != -1;
919 }
920 
921 
922 /**
923  * Move (rename) a file.
924  *
925  * @param oldName The name of an existing file.
926  * @param newName The new file name.
927  *
928  * @return A success/failure flag.
929  */
930 bool SysFileSystem::moveFile(const char *oldName, const char *newName)
931 {
932  return rename(oldName, newName) == 0;
933 }
934 
935 
936 /**
937  * Test if a given file or directory is hidden.
938  *
939  * @param name The target name.
940  *
941  * @return true if the file or directory is hidden, false otherwise.
942  */
943 bool SysFileSystem::isHidden(const char *name)
944 {
945  // it must exist
946  if (!exists(name))
947  {
948  return false;
949  }
950 
951  size_t length = strlen(name);
952  for (size_t index = length - 1; index > 0; index--)
953  {
954  if (name[index] == '.' && (index > 0 && name[index - 1] == '/'))
955  {
956  return true;
957  }
958  }
959 
960  return false;
961 }
962 
963 
964 /**
965  * Set the last modified date for a file.
966  *
967  * @param name The target name.
968  * @param time The new file time (in ticks).
969  *
970  * @return true if the filedate was set correctly, false otherwise.
971  */
972 bool SysFileSystem::setLastModifiedDate(const char *name, int64_t time)
973 {
974  struct stat64 statbuf;
975  struct utimbuf timebuf;
976  if (stat64(name, &statbuf) != 0)
977  {
978  return false;
979  }
980 
981  timebuf.actime = statbuf.st_atime;
982  timebuf.modtime = (time_t)time;
983  return utime(name, &timebuf) == 0;
984 }
985 
986 
987 
988 /**
989  * Set the read-only attribute on a file or directory.
990  *
991  * @param name The target name.
992  *
993  * @return true if the attribute was set, false otherwise.
994  */
995 bool SysFileSystem::setFileReadOnly(const char *name)
996 {
997  struct stat64 buffer;
998  if (stat64(name, &buffer) != 0)
999  {
1000  return false;
1001  }
1002  mode_t mode = buffer.st_mode;
1003  // this really turns off the write permissions
1004  mode = mode & 07555;
1005  return chmod(name, mode) == 0;
1006 }
1007 
1008 
1009 /**
1010  * indicate whether the file system is case sensitive.
1011  *
1012  * @return For Unix systems, always returns true.
1013  */
1015 {
1016  return true;
1017 }
1018 
1019 
1020 /**
1021  * Retrieve the file system root elements. On Windows,
1022  * each of the drives is a root element.
1023  *
1024  * @return The number of roots located.
1025  */
1026 int SysFileSystem::getRoots(char *roots)
1027 {
1028  // just one root to return
1029  strcpy(roots, "/");
1030  return 1;
1031 }
1032 
1033 
1034 /**
1035  * Return the separator used for separating path names.
1036  *
1037  * @return The ASCII-Z version of the path separator.
1038  */
1040 {
1041  return "/";
1042 }
1043 
1044 
1045 /**
1046  * Return the separator used for separating search path elements
1047  *
1048  * @return The ASCII-Z version of the path separator.
1049  */
1051 {
1052  return ":";
1053 }
1054 
1055 
1056 /**
1057  * Create a new SysFileIterator instance.
1058  *
1059  * @param p The directory we're iterating over.
1060  */
1062 {
1063  // this assumes we'll fail...if we find something,
1064  // we'll flip this
1065  completed = true;
1066  handle = opendir(p);
1067  // if didn't open, this either doesn't exist or
1068  // isn't a directory
1069  if (handle == NULL)
1070  {
1071  return;
1072  }
1073  entry = readdir(handle);
1074  if (entry == NULL)
1075  {
1076  closedir(handle);
1077  return;
1078  }
1079  // we have a value
1080  completed = false;
1081 }
1082 
1083 /**
1084  * Destructor for the iteration operation.
1085  */
1087 {
1088  close();
1089 }
1090 
1091 
1092 /**
1093  * close the iterator.
1094  */
1096 {
1097  if (handle != 0)
1098  {
1099  closedir(handle);
1100  handle = 0;
1101  }
1102 }
1103 
1104 
1105 /**
1106  * Check if the iterator has new results it can return.
1107  *
1108  * @return true if the iterator has another value to return, false if
1109  * the iteration is complete.
1110  */
1112 {
1113  return !completed;
1114 }
1115 
1116 
1117 /**
1118  * Retrieve the next iteration value.
1119  *
1120  * @param buffer The buffer used to return the value.
1121  */
1122 void SysFileIterator::next(char *buffer)
1123 {
1124  if (completed)
1125  {
1126  strcpy(buffer, "");
1127  }
1128  else
1129  {
1130  // copy our current result over
1131  strcpy(buffer, entry->d_name);
1132  }
1133  entry = readdir(handle);
1134  if (entry == NULL)
1135  {
1136  // we're done once we hit a failure
1137  completed = true;
1138  close();
1139  }
1140 }
#define OREF_NULL
Definition: RexxCore.h:61
RexxString * new_string(const char *s, stringsize_t l)
size_t getLength()
const char * getStringData()
void next(char *buffer)
SysFileIterator(const char *pattern)
struct dirent * entry
static bool fileExists(const char *name)
static bool checkCurrentFile(const char *name, char *resolvedName)
static bool moveFile(const char *oldName, const char *newName)
static bool setLastModifiedDate(const char *name, int64_t time)
static bool canonicalizeName(char *name)
static const char * getSeparator()
static bool deleteFile(const char *name)
static bool normalizePathName(const char *name, char *resolved)
static bool deleteDirectory(const char *name)
static RexxString * extractFile(RexxString *file)
static bool setFileReadOnly(const char *name)
static const char * EOL_Marker
static bool hasDirectory(const char *name)
static const char * getTempFileName()
static int64_t getLastModifiedDate(const char *name)
static const char * getPathSeparator()
static int getRoots(char *roots)
static bool primitiveSearchName(const char *name, const char *path, const char *extension, char *resolvedName)
static bool searchName(const char *name, const char *path, const char *extension, char *resolvedName)
static bool makeDirectory(const char *name)
static RexxString * extractDirectory(RexxString *file)
static RexxString * extractExtension(RexxString *file)
static const char PathDelimiter
static bool isDirectory(const char *name)
static bool searchPath(const char *name, const char *path, char *resolvedName)
static bool isCaseSensitive()
static uint64_t getFileLength(const char *name)
static bool isWriteOnly(const char *name)
static void qualifyStreamName(const char *unqualifiedName, char *qualifiedName, size_t bufferSize)
static bool hasExtension(const char *name)
static const char EOF_Marker
static bool searchFileName(const char *name, char *fullName)
static bool isHidden(const char *name)
static bool isReadOnly(const char *name)
static bool exists(const char *name)
static bool isFile(const char *name)
static void strlower(char *str)
Definition: Utilities.cpp:171
size_t stringsize_t
Definition: rexx.h:228
signed __int64 int64_t
unsigned __int64 uint64_t