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