file.cxx

Go to the documentation of this file.
00001 //
00002 // Copyright 2002, Lowell Boggs Jr.
00003 //
00004 // This file or directory, containing source code for a computer program,
00005 // is Copyrighted by Lowell Boggs, Jr.  987 Regency Drive, Lewisville
00006 // TX (USA), 75067.  You may use, copy, modify, and distribute this
00007 // source file without charge or obligation so long as you agree to
00008 // the following:
00009 //
00010 //  1.  You must indemnify Lowell Boggs against any and all financial
00011 //      obligations caused by its use, misuse, function, or malfunction.
00012 //      Further, you acknowledge that there is no warranty of any kind,
00013 //      whatsoever.
00014 //
00015 //  2.  You agree not to attempt to patent any portion of this original
00016 //      work -- though you may attempt to patent your own extensions to
00017 //      it if you so choose.
00018 //
00019 //  3.  You keep this copyright notice with the file and all copies
00020 //      of the file and do not change it anyway except language translation.
00021 //
00022 // You are responsible for enforcing your own compliance with these
00023 // conditions and may not use this source file if you cannot agree to the
00024 // above terms and conditions.
00025 
00028 
00029 #include <cxxtls/file.h>
00030 #include <cxxtls/options.h>
00031 #include <cxxtls/strtool.h>
00032 #include <portable_io.h>
00033 
00034 #include <time.h>
00035 #include <errno.h>
00036 #include <algorithm>
00037 #include <fcntl.h>
00038 #include <memory.h>
00039 #include <string.h>
00040 #include <cxxtls/user.h>
00041 #include <set>
00042 
00043 using namespace std;
00044 
00045 namespace cxxtls
00046 {
00047 
00048 #ifdef _MSC_VER
00049 #  include <io.h>
00050 #  include <direct.h>
00051 #  define R_OK    4       /* Test for Read permission    */
00052 #  define W_OK    2       /* Test for Write permission   */
00053 #  define X_OK    1       /* Test for eXecute permission */
00054 #  define F_OK    0       /* Test for existence of File  */
00055 #  define lstat   stat    /* no such beast on nt         */
00056 #  define S_ISLNK(x)   0  /* ditto                       */
00057 #  define S_IFLNK      0  /* more ditto                  */
00058 # include <wchar.h>
00059 # include <wtypes.h>
00060 # include <direct.h>
00061 # include <process.h>
00062 
00063 #  define  SET_MODE(a,b)  setmode(a,b)
00064 
00065 
00066 #ifndef NAME_MAX
00067 #  define NAME_MAX 255
00068 #endif
00069 
00070 #define __DIRENT_COOKIE 0xfefeabab
00071 
00072 
00073 
00074 struct dirent
00075 {
00076   ino_t d_ino;                  /* unused - no equivalent on WIN32 */
00077   char d_name[NAME_MAX+1];
00078 };
00079                 
00080 typedef struct dir_struct
00081 {
00082   ULONG   dir_ulCookie;
00083   HANDLE  dir_hDirHandle;
00084   DWORD   dir_nNumFiles;
00085   char    dir_pDirectoryName[NAME_MAX+1];
00086   struct dirent dir_sdReturn;
00087 }DIR;
00088                                         
00089 DIR *opendir(const char *);
00090 struct dirent *readdir(DIR *);
00091 void closedir(DIR *);
00092 
00093 struct passwd { char* pw_name; };
00094 
00095 struct passwd* getpwuid(int) { return 0; }
00096                                                         
00097 #else
00098 #  include <dirent.h>
00099 #  include <unistd.h>
00100 #  include <pwd.h>
00101 #  include <grp.h>
00102 #  define  O_BINARY  0 /* not needed on unix */
00103 #  define  SET_MODE(a,b)
00104 #endif
00105 
00106 #include <sys/stat.h>
00107 #include <time.h>
00108 #include <portable_io.h>
00109 
00110 extern "C"
00111 {
00112 #include <fnmatch.h>
00113 };
00114 
00115 typedef FileName::rep_t rep_t;
00116 
00117 
00118 FileTime::
00119 operator string() const
00120 {
00121   char buffer[256];
00122 
00123   sprintf(buffer, "%02d/%02d/%02d %02d:%02d:%02d",
00124           year % 100,
00125           month +1,  // so as not to confuse people who are used to a 1 based month system
00126           day,
00127           hour,
00128           minute,
00129           second
00130          );
00131         
00132   return buffer;
00133 
00134 }
00135 
00136 char const *months[]=
00137 {
00138   "Jan",
00139   "Feb",
00140   "Mar",
00141   "Apr",
00142   "May",
00143   "Jun",
00144   "Jul",
00145   "Aug",
00146   "Sep",
00147   "Oct",
00148   "Nov",
00149   "Dec"
00150 };
00151 
00152 string
00153 FileTime::
00154 short_string() const
00155 {
00156 
00157    time_t curtime;
00158 
00159    time(&curtime);
00160 
00161    struct tm *p = localtime(&curtime);
00162 
00163    if(p)
00164    {
00165      long operand_month = year * 12 + month;
00166 
00167      long current_month = p->tm_year * 12 + p->tm_mon;
00168 
00169      long delta = current_month - operand_month;
00170 
00171      if(delta < -6 || delta > 6)
00172      {
00173        char buffer[256];
00174 
00175        sprintf(buffer, "%s %2d  %d", months[month], day, year + 1900);
00176 
00177        return buffer;
00178 
00179      }
00180      else
00181      {
00182        char buffer[256];
00183 
00184        sprintf(buffer, "%s %2d %02d:%02d", months[month], day, hour, minute);
00185 
00186        return buffer;
00187      }
00188 
00189    }
00190    else
00191    {
00192      char buffer[256];
00193 
00194      sprintf(buffer, "%s %2d  %d", months[month], day, year + 1900);
00195 
00196      return buffer;
00197 
00198    }
00199 }
00200 
00201 
00202 bool
00203 FileTime::
00204 construct_from_string(string s)
00205 {
00206   // looking for MMM DD (YYYY/HR::MM)
00207 
00208   char month_buf[2048];
00209   int  day_buf        ;
00210   char year_buf [2048];
00211 
00212   if(3 != sscanf(s.c_str(), "%s %d %s", month_buf, &day_buf, year_buf))
00213     return true;
00214 
00215   int i;
00216 
00217   for(i=0; i < 12; ++i)
00218   {
00219     if(strcmp(month_buf, months[i]) == 0)
00220     {
00221       break;
00222     }
00223   }
00224 
00225   if(i == 12)
00226     return true;
00227 
00228   month = i;
00229 
00230   day = day_buf;
00231 
00232   if(year_buf[1] != ':' && year_buf[2] != ':')
00233   {
00234     // user specified   MMM DD year
00235 
00236     if(1 != sscanf(year_buf, "%d", &day_buf) )
00237       return true;
00238 
00239     hour   = 0;
00240     minute = 0;
00241     second = 0;
00242 
00243     year = day_buf - 1900;
00244 
00245     return false;  //  !! yeah !!
00246 
00247 
00248   }
00249 
00250   // user did not specify the year but did give the hour and minute
00251 
00252   struct tm *buf;
00253 
00254   time_t curtime;
00255 
00256   time(&curtime);
00257 
00258   if(  0 != (buf = localtime( &curtime )) )
00259   {
00260      year = buf->tm_year;
00261 
00262   }
00263   else
00264     return true;
00265 
00266   if(2 != sscanf(year_buf, "%d:%d", &day_buf, &i))
00267     return true;
00268 
00269   second = 0;
00270   hour   = day_buf;
00271   minute = i;
00272 
00273   return false;
00274 
00275 }
00276 
00277 
00278 
00279 FileTime::
00280 FileTime(struct tm const *p)
00281 {
00282   if(p == 0)
00283   {
00284     year    = 0;
00285     month   = 0;
00286     day     = 0;
00287     hour    = 0;
00288     minute  = 0;
00289     second  = 0;
00290   }
00291   else
00292   {
00293     year    = p->tm_year;
00294     month   = p->tm_mon;
00295     day     = p->tm_mday;
00296     hour    = p->tm_hour;
00297     minute  = p->tm_min;
00298     second  = p->tm_sec;
00299   }
00300 
00301 }
00302 
00303 FileMode::
00304 operator string() const
00305   {
00306     char buffer[40];
00307 
00308     char *scan = buffer;
00309 
00310     *scan++ = ( (mode&FileMode::link)            ? 'L' : ' ' );
00311     *scan++ = ( (mode&FileMode::directory)       ? 'd' : '-' );
00312     *scan++ = ( (mode&FileMode::user_readable)   ? 'r' : '-' );
00313     *scan++ = ( (mode&FileMode::user_writeable)  ? 'w' : '-' );
00314     *scan++ = ( (mode&FileMode::user_executable) ? (mode &FileMode:: set_uid ? 'S' : 'x' ) : ('-') );
00315     *scan++ = ( (mode&FileMode::group_readable)   ? 'r' : '-' );
00316     *scan++ = ( (mode&FileMode::group_writeable)  ? 'w' : '-' );
00317     *scan++ = ( (mode&FileMode::group_executable) ? (mode &FileMode:: set_gid ? 'S' : 'x' ) : ('-') );
00318     *scan++ = ( (mode&FileMode::world_readable)   ? 'r' : '-' );
00319     *scan++ = ( (mode&FileMode::world_writeable)  ? 'w' : '-' );
00320     *scan++ = ( (mode&FileMode::world_executable) ? 'x' : '-' );
00321 
00322     *scan   = 0;
00323 
00324     return buffer;
00325   }
00326 
00327 void
00328 FileMode::
00329 construct_from_string(char const *permissions)
00330 {
00331   int l = strlen(permissions);
00332 
00333   mode=0;
00334 
00335   if(l >= 10)
00336   {
00337     //
00338     // explicit detail specified
00339     //
00340 
00341     if(permissions[0] == 'l' || permissions[0] == 'L' )
00342       mode = FileMode::link;
00343 
00344     if(l == 10)
00345       --permissions;  // first char was missing
00346 
00347     if(permissions[1] == 'd')
00348       mode = FileMode::directory;
00349 
00350 
00351     if(permissions[2] == 'r') mode += FileMode::user_readable;
00352     if(permissions[3] == 'w') mode += FileMode::user_writeable;
00353     if(permissions[4] == 'x') mode += FileMode::user_executable;
00354     if(permissions[4] == 's') mode += FileMode::user_executable + FileMode::set_uid;
00355     if(permissions[4] == 'S') mode += FileMode::user_executable + FileMode::set_uid;
00356 
00357     if(permissions[5] == 'r') mode += FileMode::group_readable;
00358     if(permissions[6] == 'w') mode += FileMode::group_writeable;
00359     if(permissions[7] == 'x') mode += FileMode::group_executable;
00360     if(permissions[7] == 's') mode += FileMode::group_executable + FileMode::set_gid;
00361     if(permissions[7] == 'S') mode += FileMode::group_executable + FileMode::set_gid;
00362 
00363     if(permissions[8] == 'r') mode += FileMode::world_readable;
00364     if(permissions[9] == 'w') mode += FileMode::world_writeable;
00365     if(permissions[10] == 'x') mode += FileMode::world_executable;
00366   }
00367   else
00368   {
00369     while(*permissions == ' ')
00370       ++permissions;
00371 
00372     if(*permissions == '0')
00373     {
00374       int value=0;
00375 
00376       sscanf(permissions, "%o", &value);
00377 
00378       int user   = (value >> 6) & 7;
00379       int group  = (value >> 3) & 7;
00380       int world  = (value     ) & 7;
00381 
00382       if(user & 4) mode |= FileMode::user_readable;
00383       if(user & 2) mode |= FileMode::user_writeable;
00384       if(user & 1) mode |= FileMode::user_executable;
00385 
00386       if(group & 4) mode |= FileMode::group_readable;
00387       if(group & 2) mode |= FileMode::group_writeable;
00388       if(group & 1) mode |= FileMode::group_executable;
00389 
00390       if(world & 4) mode |= FileMode::world_readable;
00391       if(world & 2) mode |= FileMode::world_writeable;
00392       if(world & 1) mode |= FileMode::world_executable;
00393 
00394     }
00395   }
00396 
00397 }
00398 
00399 
00400 FileMode::
00401 FileMode(int m)
00402   // m is assumed to be a struct stat member st_mode
00403 {
00404   mode = 0;
00405 
00406 #if _MSC_VER
00407 
00408   if(m & _S_IFDIR)     mode |= directory     | user_executable| group_executable | world_executable;
00409   if(m & _S_IREAD)     mode |= user_readable | group_readable | world_readable;
00410   if(m & _S_IWRITE)    mode |= user_writeable| group_writeable| world_writeable;
00411 
00412 #else
00413   if(S_ISLNK(m))      mode |= link;
00414   if(S_ISDIR(m))      mode |= directory;
00415   if(m & S_ISUID)     mode |= set_uid;
00416   if(m & S_ISGID)     mode |= set_gid;
00417 
00418   if(m & S_IRUSR)     mode |= user_readable;
00419   if(m & S_IWUSR)     mode |= user_writeable;
00420   if(m & S_IXUSR)     mode |= user_executable;
00421 
00422   if(m & S_IRGRP)     mode |= group_readable;
00423   if(m & S_IWGRP)     mode |= group_writeable;
00424   if(m & S_IXGRP)     mode |= group_executable;
00425 
00426   if(m & S_IROTH)     mode |= world_readable;
00427   if(m & S_IWOTH)     mode |= world_writeable;
00428   if(m & S_IXOTH)     mode |= world_executable;
00429 #endif
00430 }
00431 
00432 
00433 
00434 rep_t
00435 FileName::
00436 basename(bool remove_extension) const
00437   // return the file name, without the directory part -- and optionally
00438   // without the file name extension
00439 {
00440 
00441   const_iterator first = begin();
00442   const_iterator last  = end();
00443 
00444   while(last != first)
00445   {
00446     --last;
00447 
00448     if(*last == '/' || *last == '\\')
00449     {
00450       ++last;
00451       const_iterator tail = end();
00452 
00453       if(remove_extension)
00454       {
00455          // code duplicated below, sigh.
00456 
00457          while(tail != last)
00458          {
00459            --tail;
00460 
00461            if(*tail == '.')
00462              break;
00463 
00464          }
00465 
00466          if(tail == last)
00467            tail = end();  // no '.' was found
00468 
00469       }
00470 
00471       return rep_t(last, tail);
00472     }
00473 
00474   }
00475 
00476   // no pathname was included return all that's left or 
00477   // optionally remove the file name extension
00478 
00479   {
00480       if(remove_extension)
00481       {
00482          // code duplicated above, sigh.
00483 
00484          const_iterator last = begin();
00485          const_iterator tail = end();
00486 
00487          while(tail != last)
00488          {
00489            --tail;
00490 
00491            if(*tail == '.')
00492              break;
00493 
00494          }
00495 
00496          if(tail == last)
00497            tail = end();  // no '.' was found
00498 
00499          return rep_t(last, tail);
00500 
00501       }
00502 
00503   }
00504 
00505   return *this;
00506 
00507 }
00508 
00509 rep_t
00510 FileName::
00511 dirname() const
00512   // return only the directory part of the file name (trailing / is include)
00513 {
00514   const_iterator first = begin();
00515   const_iterator last  = end();
00516 
00517   while(last != first)
00518   {
00519     --last;
00520 
00521     if(*last == '/' || *last == '\\')
00522     {
00523       return rep_t(first, ++last);
00524     }
00525 
00526   }
00527 
00528   // file has no directory specified -- return './' because all directories
00529   // from this function must end in a valid path separator
00530 
00531   return rep_t("./");
00532 
00533 }
00534 
00535 //[ FileStatus
00536 
00537 string
00538 FileStatus::
00539 convert_to_string(bool resolve_id_names) const
00540      //- convert_to_string()  Convert the FileStatus to a string.
00544 {
00545    string rv;
00546 
00547 
00548    rv.append(mode);
00549    rv.append(" ");
00550    rv.append(time);
00551 
00552    char buffer[256];
00553 
00554 
00555    if(resolve_id_names)
00556    {
00557       sprintf(buffer, " %-12.12s %-12.12s %12d",
00558                       id2string(owner_id).c_str(),
00559                       group2string(group_id).c_str(),
00560                       (int)(size)
00561              );
00562 
00563    }
00564    else
00565    {
00566      sprintf(buffer, " %12d %12d %12d", owner_id, group_id, (int)(size));
00567    }
00568 
00569    rv.append(buffer);
00570 
00571    return rv;
00572 
00573 }
00574 
00575 
00576 void
00577 FileName::
00578 convert_to_absolute_path()
00579   // if this is not an absolute path, make it one by adding the
00580   // current directory to it -- and normalize to unix style pathnames
00581 {
00582 
00583   iterator first;
00584   iterator last ;
00585 
00586 
00587   // if the first character is a path separator, then we already have one
00588 
00589   size_t siz = size();
00590 
00591   char c = siz < 1 ? 0 : begin()[0];
00592   char d = siz < 2 ? 0 : begin()[1];
00593   char e = siz < 3 ? 0 : begin()[2];
00594 
00595   bool is_full_path =   (c == '/') 
00596                      || (c == '\\') 
00597                      || (     (d == ':') 
00598                           &&  ( e == '/' || (e == '\\') )
00599                         );
00600 
00601 
00602   if( ! is_full_path  )
00603   {
00604     // otherwise, read the current directory name
00605 
00606     char buffer[2048];
00607 
00608 
00609     if(0 == ::getcwd(buffer, 2048) )
00610     {
00611         buffer[0] = '.';
00612         buffer[1] = '/';
00613         buffer[2] = 0;
00614     }
00615 
00616     string new_name(buffer);
00617 
00618     new_name.append("/");
00619     new_name.append(*this);
00620 
00621     *this = new_name;
00622 
00623   }
00624 
00625   // remove dosifications
00626 
00627   first = begin();
00628   last  = end();
00629 
00630   while(first != last)
00631   {
00632     char c = *first;
00633 
00634     if(c == '\\')
00635     {
00636       size_t offset = first - begin();
00637       erase(first);
00638       insert(begin() + offset, '/');
00639       first = begin() + offset;
00640     }
00641 
00642     ++first;
00643   }
00644 
00645   // remove ../ and ./ crapola
00646 
00647   { // first ./
00648 
00649     first = begin();
00650     last  = end();
00651 
00652     while(first != last)
00653     {
00654         char const *scan = "/./";
00655         
00656         first = search(first, last, scan, scan + 3);
00657         
00658         if(first != last)
00659         {
00660           size_t off = first - begin();
00661         
00662           erase(first, first + 2);
00663         
00664           first = begin() + off;
00665           last  = end();
00666         
00667         }
00668     }
00669 
00670     //
00671     // now, remove trailing /.
00672     //
00673 
00674     first = begin();
00675     last  = end();
00676 
00677     while( last - first > 2 )
00678     {
00679       --last;
00680       --last;
00681 
00682       if(last[0] == '/' && last[1] == '.')
00683       {
00684         erase(last, end());
00685       }
00686       else
00687         break;
00688 
00689     }
00690 
00691 
00692   }
00693   {
00694     first = begin();
00695     last  = end();
00696 
00697     while(first != last)
00698     {
00699         char const *scan = "/../";
00700         
00701         first = search(first, last, scan, scan + 4);
00702         
00703         if(first != last)
00704         {
00705           // we have found /../, but can we remove it?
00706         
00707           iterator scan = first - 1;
00708         
00709           while(scan != begin())
00710           {
00711             if(*scan == '/')
00712               break;
00713         
00714             --scan;
00715           }
00716         
00717           size_t off = scan - begin();
00718         
00719           erase(scan, first + 3);
00720         
00721           first = begin() + off;
00722           last  = end();
00723         
00724         }
00725     }
00726 
00727     //
00728     // now, remove trailing /..
00729     //
00730 
00731     first = begin();
00732     last  = end();
00733 
00734     while( last - first > 3 )
00735     {
00736       --last;
00737       --last;
00738       --last;
00739 
00740       if(last[0] == '/' && last[1] == '.' && last[2] == '.')
00741       {
00742         erase(last, end());
00743         *this = this->dirname();
00744         if(size())
00745           erase(begin() + size()-1);
00746       }
00747       else
00748         break;
00749 
00750     }
00751   }
00752 
00753 #ifdef _MSC_VER
00754 
00755   if(this->operator[](0) == '/')
00756   {
00757     *this = "c:" + *this;
00758   }
00759 #endif
00760 
00761 
00762 }
00763 
00764 bool
00765 FileName::
00766 is_syntactically_valid() const
00767   // contains only valid characters and is of the right length
00768 {
00769   if(size() < 1)
00770     return false;
00771 
00772   if(size() > 2048)
00773     return false;
00774 
00775   const_iterator first = begin();
00776   const_iterator last  = end();
00777 
00778   while(first != last)
00779   {
00780     char c = *first++;
00781 
00782     if(c < ' ' || c > 0x7e)
00783       return false;
00784 
00785   }
00786 
00787   return true;
00788 }
00789 
00790 bool
00791 FileName::
00792 exists() const
00793   // return true if file by this name exists
00794 {
00795   char const *name = c_str();
00796   int  check_mode = F_OK;
00797   int  access_code = access(name, check_mode);
00798 
00799    return 0 == access_code;
00800 }
00801 
00802 bool
00803 FileName::
00804 file_stat(FileStatus* s, bool read_link_info) const
00805   // Get file stat from os -- return true if error
00806   // If 'read_link_info' is true, read the symbolic link info
00807   // or just the file info if the named file is not a symbolic
00808   // link.
00809 {
00810 
00811   string fixedName = *this;  // remove trailing / or \ because windows is screwed up (stat malfunctions on windows in this case)
00812 
00813   size_t nameLength = fixedName.size();
00814 
00815   if(nameLength)
00816   {
00817     char lastChar = fixedName[nameLength-1];
00818 
00819     if(lastChar == '/' || lastChar == '\\')
00820     {
00821       // remove trailing / unless that would make the name be empty
00822 
00823       if(nameLength != 1)
00824       {
00825         char secondChar = fixedName[1];
00826 
00827         if(secondChar == ':')
00828         {
00829           if(nameLength >4)
00830           {
00831             fixedName.erase(nameLength-1, 1);
00832           }
00833         }
00834         else
00835         if(nameLength > 2)
00836         {
00837           fixedName.erase(nameLength-1, 1);
00838         }
00839 
00840       }
00841     }
00842   }
00843 
00844   char const *p = fixedName.c_str();
00845 
00846   struct stat buf;
00847 
00848   memset(&buf, 0, sizeof(buf));
00849 
00850 
00851   s->mode.mode = 0;
00852   s->size      = -1;
00853   s->owner_id  = -1;
00854   s->group_id  = -1;
00855 
00856 
00857   bool is_a_link = false;
00858 
00859   if(read_link_info && lstat(p, &buf))
00860   {
00861     // error reading the status
00862 
00863     return true;
00864 
00865   }
00866   else
00867   {
00868      is_a_link = S_ISLNK(buf.st_mode);
00869   }
00870 
00871   // if we get here it means either we did not try to read the status of the
00872   // link using lstat, OR, it means that we did read it successfully.
00873 
00874   if(     (    (!read_link_info)
00875            ||  is_a_link
00876           )
00877       &&  stat(p,&buf) 
00878     )
00879   {
00880     return true;  // we called stat and it failed
00881   }
00882 
00883   // if we get here, we are in one of the following states:
00884   //
00885   //   read_link_info was false and we read buf using stat()
00886   //
00887   //   read_link_info was true and we have read the link using lstat()
00888   //   with the following optional situation:
00889   //
00890   //      if lstat() was successful and tells us that we have read a 
00891   //      symbolic link file, then we want to read the same status using
00892   //      stat() and we are going to OR INTO the mode the fact that this 
00893   //      is link file.
00894 
00895 
00896 
00897   s->size = buf.st_size;
00898 
00899   (&s->mode)->~FileMode();   new (&s->mode) FileMode(buf.st_mode);
00900   (&s->time)->~FileTime();   new (&s->time) FileTime(localtime(&buf.st_mtime));
00901 
00902   s->group_id = buf.st_gid;
00903   s->owner_id  = buf.st_uid;
00904 
00905   if(is_a_link)
00906     s->mode.mode |= FileMode::link;   // here are creating an un-unix like file mode.
00907                                       // in unix, the is-link bit is not compatible with
00908                                       // other bits such as the is-directory bit.  You
00909                                       // can, in unix parlance, have a directory OR you can
00910                                       // have a symbolic link.  But here, we make no such
00911                                       // rules.  If the link bit is set, then the rest of the
00912                                       // status tells us the permisions of the thing pointed
00913                                       // to by the link.
00914 
00915 
00916 
00917 
00918   return false;
00919 }
00920 
00921 
00922 bool
00923 FileName::
00924 is_dir() const
00925   // return true if there is a directory by this name
00926 {
00927   FileStatus s;
00928 
00929   if(file_stat(&s))
00930     return false;
00931 
00932   return s.mode.is_dir();
00933 
00934 }
00935 
00936 
00937 bool
00938 FileName::
00939 is_executable() const
00940   // return true if there is an executable by this name
00941 {
00942   return 0 == access( c_str(), X_OK);
00943 
00944 }
00945 
00946 bool
00947 FileName::
00948 is_writeable() const
00949   // return true if the file exists and is writeable
00950 {
00951   return 0 == access( c_str(), W_OK);
00952 }
00953 
00954 bool
00955 FileName::
00956 is_readable() const
00957   // return true if the file exists and is writeable
00958 {
00959   return 0 == access( c_str(), R_OK);
00960 }
00961 
00962 bool
00963 FileName::
00964 is_createable() const
00965   // return true if the file exists and is writeable
00966 {
00967    if( is_writeable() )
00968      return true;
00969 
00970    FileName dir(dirname());
00971 
00972    if(dir.is_writeable())
00973      return true;
00974 
00975    return false;
00976 
00977 
00978 }
00979 
00980 FileMode
00981 FileName::
00982 file_mode() const
00983   // get the corresponding file's mode bits as defined in FileName::mode_bits.
00984   // a value of 0 means that the file doesn't exist -- it is highly unlikely that
00985   // a useful file has 0 as its mode.
00986 {
00987   FileStatus  s;
00988 
00989   if(file_stat(&s))
00990     return 0;
00991 
00992   return s.mode;
00993 
00994 }
00995 
00996 FileName::off_t
00997 FileName::
00998 file_size() const
00999   // get the length of the file corresponding to this name
01000 {
01001   FileStatus s;
01002 
01003   if(file_stat(&s))
01004     return -1;
01005 
01006   return s.size;
01007 
01008 }
01009 
01010 bool
01011 FileName::
01012 file_time(FileTime *rv) const
01013 {
01014   FileStatus s;
01015 
01016   if(file_stat(&s))
01017     return true;
01018 
01019   *rv = s.time;
01020 
01021   return false;
01022 
01023 }
01024 
01025 bool
01026 FileName::
01027 matches(char const *pattern) const
01028 {
01029    char *p = const_cast<char*>(pattern);
01030    char *s = const_cast<char*>(c_str());
01031 
01032     return FNM_NOMATCH != fnmatch(p, s, FNM_PATHNAME);
01033 }
01034 
01035 
01036 
01037 string
01038 FileStatus::id2string(int id)
01039 {
01040   passwd *p = getpwuid(id);
01041 
01042   if(p)
01043   {
01044      return p->pw_name;
01045   }
01046 
01047   char buffer[20];
01048 
01049   sprintf(buffer, "%d", id);
01050 
01051   return buffer;
01052 
01053 }
01054 
01055 
01056 string
01057 FileStatus::group2string(int id)
01058 {
01059 #ifdef _MSC_VER
01060   return "group";
01061 #else
01062   struct group *p = getgrgid(id);
01063 
01064   if(p)
01065   {
01066      return p->gr_name;
01067   }
01068 
01069   char buffer[20];
01070 
01071   sprintf(buffer, "%d", id);
01072 
01073   return buffer;
01074 #endif
01075 
01076 }
01077 
01078 int
01079 FileName::
01080 find_matching(char const *pattern, sorted_names_t *l, int options)
01081 {
01082   int count=0;
01083 
01084   enum flags { include_paths=1,
01085                only_dirs=    2,
01086                not_dirs=     4
01087              };
01088 
01089   FileName f(pattern);
01090 
01091   string dir = f.dirname();
01092   string base= f.basename();
01093 
01094 
01095   if(dir.size() > 1 && dir[dir.size()-1] == '/')
01096   {
01097     dir.erase( dir.begin() + dir.size() - 1 );
01098   }
01099 
01100 #ifdef _MSC_VER
01101 
01102   if(dir.size() == 2 && dir[dir.size()-1] == ':')
01103   {
01104     dir += '/';
01105   }
01106 
01107   if(dir.size() == 0 || dir == "/")
01108   {
01109     dir = "c:/";
01110   }
01111 
01112 #else
01113 
01114   if(dir.size() == 0)
01115   {
01116     dir = "/";
01117   }
01118 
01119 #endif
01120 
01121 
01122 
01123   DIR    *d   = opendir(dir.c_str());
01124   dirent *e;
01125 
01126   while(d && (e = readdir(d) ) )
01127   {
01128     char *pat = const_cast<char*>(base.c_str());
01129 
01130     if( fnmatch(pat, e->d_name, FNM_PATHNAME) == 0 )
01131     {
01132        FileName rv(e->d_name);
01133        FileName path(rv);
01134 
01135        if(dir.size() != 0)
01136           path = dir + string("/") + rv;
01137 
01138        FileStatus fs;
01139 
01140        path.file_stat(&fs);
01141 
01142        if(fs.mode.is_dir())
01143        {
01144          if( options & not_dirs )
01145          {
01146             continue;  // skipping dirs
01147          }
01148 
01149          if( options & include_paths )
01150            rv = path;
01151 
01152 
01153        }
01154        else
01155        {
01156          if( options & only_dirs )
01157          {
01158             continue;  // skipping non-dirs
01159          }
01160 
01161          if( options & include_paths )
01162            rv = path;
01163        }
01164 
01165        l->insert(rv);
01166 
01167        ++count;
01168 
01169     }
01170   }
01171 
01172   if(d)
01173     closedir(d);
01174 
01175   return count;
01176 }
01177 
01178 #ifdef _MSC_VER
01179 DIR*
01180 opendir(const char* pDirName)
01181 {
01182         struct stat sb;
01183         DIR*    pDir;
01184         char*   pEndDirName;
01185         int     nBufferLen;
01186 
01187         std::string adjustedDirName(pDirName);
01188 
01189         size_t dirNameLength = adjustedDirName.size();
01190 
01191         if(dirNameLength)
01192         {
01193 
01194            // remove trailing \ unless we are looking at c:\, in which casae
01195            // you have to leave it on.
01196 
01197            if(adjustedDirName[dirNameLength-1] == '/' ||
01198               adjustedDirName[dirNameLength-1] == '\\'
01199              )
01200            {
01201               adjustedDirName.erase(dirNameLength-1);
01202 
01203               --dirNameLength;
01204 
01205               if(dirNameLength == 2 && adjustedDirName[1] == ':')
01206               {
01207                  adjustedDirName += '\\';
01208                  ++dirNameLength;
01209               }
01210 
01211            }
01212         }
01213 
01214         pDirName = adjustedDirName.c_str();
01215 
01216 
01217         /* sanity checks */
01218         if (!pDirName) {
01219                 errno = EINVAL;
01220                 return NULL;
01221         }
01222         if (stat(pDirName, &sb) != 0) {
01223                 errno = ENOENT;
01224                 return NULL;
01225         }
01226         if ((sb.st_mode & S_IFMT) != S_IFDIR) {
01227                 errno = ENOTDIR;
01228                 return NULL;
01229         }
01230 
01231         /* allocate a DIR structure to return */
01232         pDir = (DIR *) malloc(sizeof (DIR));
01233 
01234         if (!pDir)
01235                 return NULL;
01236 
01237         /* input directory name length */
01238         nBufferLen = strlen(pDirName);
01239 
01240         /* copy input directory name to DIR buffer */
01241         strcpy(pDir->dir_pDirectoryName, pDirName);
01242 
01243         /* point to end of the copied directory name */
01244         pEndDirName = &pDir->dir_pDirectoryName[nBufferLen - 1];
01245 
01246         /* if directory name did not end in '/' or '\', add '/' */
01247         if ((*pEndDirName != '/') && (*pEndDirName != '\\')) {
01248                 pEndDirName++;
01249                 *pEndDirName = '/';
01250         }
01251 
01252         /* now append the wildcard character to the buffer */
01253         pEndDirName++;
01254         *pEndDirName = '*';
01255         pEndDirName++;
01256         *pEndDirName = '\0';
01257 
01258         /* other values defaulted */
01259         pDir->dir_nNumFiles = 0;
01260         pDir->dir_hDirHandle = INVALID_HANDLE_VALUE;
01261         pDir->dir_ulCookie = __DIRENT_COOKIE;
01262 
01263         return pDir;
01264 }
01265 
01266 void
01267 closedir(DIR *pDir)
01268 {
01269         /* got a valid pointer? */
01270         if (!pDir) {
01271                 errno = EINVAL;
01272                 return;
01273         }
01274 
01275         /* sanity check that this is a DIR pointer */
01276         if (pDir->dir_ulCookie != __DIRENT_COOKIE) {
01277                 errno = EINVAL;
01278                 return;
01279         }
01280 
01281         /* close the WIN32 directory handle */
01282         if (pDir->dir_hDirHandle != INVALID_HANDLE_VALUE)
01283                 FindClose(pDir->dir_hDirHandle);
01284 
01285         free(pDir);
01286 
01287         return;
01288 }
01289 
01290 struct dirent *
01291 readdir(DIR* pDir)
01292 {
01293         WIN32_FIND_DATA wfdFindData;
01294 
01295         if (!pDir) {
01296                 errno = EINVAL;
01297                 return NULL;
01298         }
01299 
01300         /* sanity check that this is a DIR pointer */
01301         if (pDir->dir_ulCookie != __DIRENT_COOKIE) {
01302                 errno = EINVAL;
01303                 return NULL;
01304         }
01305 
01306         if (pDir->dir_nNumFiles == 0) {
01307                 pDir->dir_hDirHandle = FindFirstFile(pDir->dir_pDirectoryName, &wfdFindData);
01308                 if (pDir->dir_hDirHandle == INVALID_HANDLE_VALUE)
01309                         return NULL;
01310         } else if (!FindNextFile(pDir->dir_hDirHandle, &wfdFindData))
01311                         return NULL;
01312 
01313         /* bump count for next call to readdir() or telldir() */
01314         pDir->dir_nNumFiles++;
01315 
01316         /* fill in struct dirent values */
01317         pDir->dir_sdReturn.d_ino = -1;
01318         strcpy(pDir->dir_sdReturn.d_name, wfdFindData.cFileName);
01319 
01320         return &pDir->dir_sdReturn;
01321 }
01322 
01323 #endif
01324 
01325 void
01326 FileName::
01327 slurp(FileContents *c) const
01328   // read the named file into c
01329 {
01330   FILE *f = fopen(c_str(), "rb");
01331 
01332   if(!f)
01333   {
01334     c->set_error("fopen failed");
01335   }
01336   else
01337   {
01338      FileStatus s;
01339 
01340      if(file_stat(&s))
01341      {
01342        c->set_error("couldn't get file status");
01343      }
01344      else
01345      {
01346         c->reserve(s.size);
01347         
01348         long size = fread(c->begin(), 1, s.size, f);
01349 
01350         if(size != s.size)
01351         {
01352           c->reserve(0);
01353         
01354           c->set_error("reading file resulted in fewer bytes that the file status indicated");
01355         }
01356         else
01357         {
01358           c->clear_error();
01359         }
01360         
01361      }
01362 
01363      fclose(f);
01364 
01365   }
01366 
01367 }
01368 
01369 FileName
01370 FileName::
01371 mktemp(FileName dir)
01372 {
01373   char buffer[40];
01374 
01375   int count;
01376 
01377   for(count=1; count < 100; ++count)
01378   {
01379     if(count > 1)
01380       sprintf(buffer, "%d.tmp.%d", getpid(), count);
01381     else
01382       sprintf(buffer, "%d.tmp", getpid());
01383 
01384     FileName rv = dir + buffer;
01385 
01386     if(! rv.exists() )
01387       return rv;
01388   }
01389 
01390   static int badcount=0;
01391 
01392   sprintf(buffer, "%d-%d", getpid(), badcount++);
01393 
01394   return dir + buffer;
01395 
01396 }
01397 
01398 struct Is_Path_Space
01400 {
01401   bool operator() (char c) const
01402   {
01403     return c == ' '  ||
01404            c == '\t' ||
01405            c == '\n' ||
01406            c == '\r' ||
01407            c == FileName::PATH_separator
01408            ;
01409   }
01410 
01411 };
01412 
01413 
01414 FileName
01415 FileName::
01416 find_executable(string progname, string path)
01417 {
01418   //
01419   // make sure that 'path' has something it in
01420   //
01421 
01422   if(path.size() == 0)
01423   {
01424 
01425     char const *p;
01426 
01427     if(ProgramOptions::global_options_)
01428       p = ProgramOptions::global_options_->getenv("PATH").c_str();
01429     else
01430       p = getenv("PATH"); // if it doesn't, use the path
01431                                     // environment variable
01432     if(p)
01433       path = p;
01434   }
01435 
01436   StrTool::stringlist_t splitpath;
01437 
01438   Is_Path_Space functor;
01439 
01440 
01441   StrTool::parse_words(path,
01442                        &splitpath,
01443                        2000000000U,
01444                        functor
01445                       );
01446 
01447   StrTool::stringlist_t::iterator f = splitpath.begin();
01448   StrTool::stringlist_t::iterator l = splitpath.end();
01449 
01450   while(f != l)
01451   {
01452 #ifdef _MSC_VER
01453 
01454      FileName progpath(*f++);
01455 
01456      progpath += "\\";
01457      progpath += progname;
01458 
01459      for(size_t i=0; i < progpath.size(); ++i)
01460        if(progpath[i] == '/')
01461           progpath[i] = '\\';
01462 
01463      if(progpath.exists())  // this really should check for is executable...
01464      {
01465        return progpath;
01466      }
01467 
01468      FileName exefile(progpath + ".exe");
01469 
01470      if(exefile.exists())
01471      {
01472        return exefile;
01473      }
01474 
01475      FileName batfile(progpath + ".bat");
01476 
01477      if(batfile.exists())
01478      {
01479        return batfile;
01480      }
01481 
01482      FileName cmdfile(progpath + ".cmd");
01483 
01484      if(cmdfile.exists())  // this really should check for is executable...
01485      {
01486        return cmdfile;
01487      }
01488 
01489      FileName comfile(progpath + ".com");
01490 
01491      if(comfile.exists())  // this really should check for is executable...
01492      {
01493        return comfile;
01494      }
01495 
01496 #else
01497      FileName progpath(*f++);
01498 
01499      progpath += "/";
01500      progpath += progname;
01501 
01502      if(progpath.exists())  // this really should check for is executable...
01503      {
01504        return progpath;
01505      }
01506 #endif
01507 
01508   }
01509 
01510   return "";  // not found
01511                                 
01512 }
01513 
01514 
01515 FileName
01516 FileName::
01517 find_file_in_path(string progname, string path)
01518 {
01519   //
01520   // make sure that 'path' has something it in
01521   //
01522 
01523   if(path.size() == 0)
01524   {
01525 
01526     // if it doesn't, use the path environment variable
01527 
01528     char const *p;
01529 
01530     if(ProgramOptions::global_options_)
01531       p = ProgramOptions::global_options_->getenv("PATH").c_str();
01532     else
01533       p = getenv("PATH");
01534 
01535     if(p)
01536       path = p;
01537   }
01538 
01539 
01540   StrTool::stringlist_t splitpath;
01541 
01542   Is_Path_Space functor;
01543 
01544   StrTool::parse_words(path,
01545                        &splitpath,
01546                        2000000000U,
01547                        functor
01548                       );
01549 
01550   StrTool::stringlist_t::iterator f = splitpath.begin();
01551   StrTool::stringlist_t::iterator l = splitpath.end();
01552 
01553   StrTool::stringlist_t  expandedList; // a copy of the splitpath with * and ? expanded
01554 
01555   {
01556     // expand * and ? in the input path so that things like this work:
01557     //
01558     //  /home/lboggs/*
01559     //
01560     // Becomes all the directories underneath /home/lboggs (top level only)
01561 
01562     while(f != l)
01563     {
01564       if(f->find_first_of("*?") < f->size())
01565       {
01566         sorted_names_t matches;
01567 
01568         find_matching(f->c_str(), &matches, 3); // find only dirs and include the paths
01569 
01570         sorted_names_t::iterator a = matches.begin(), b = matches.end();
01571 
01572         while(a != b) expandedList.push_back(*a++);
01573 
01574 
01575       }
01576       else
01577         expandedList.push_back(*f);
01578 
01579       ++f;
01580 
01581     }
01582 
01583     // setup for the next algorithm to search for files in the directory list
01584     // just computed
01585 
01586     f = expandedList.begin();
01587     l = expandedList.end();
01588 
01589   }
01590 
01591 
01592   while(f != l)
01593   {
01594      FileName progpath(*f++);
01595 
01596      progpath += "/";
01597      progpath += progname;
01598 
01599      if(progpath.exists())
01600      {
01601        progpath.convert_to_absolute_path();
01602        return progpath;
01603      }
01604 
01605   }
01606 
01607   return "";  // not found
01608                                 
01609 }
01610 
01611 
01612 #ifdef _MSC_VER
01613 char FileName::PATH_separator = ';';
01614 #else
01615 char FileName::PATH_separator = ':';
01616 #endif
01617 
01618 
01619 bool
01620 FileName::
01621 remove() const
01622 {
01623   char const *n = c_str();
01624 
01625   if(access(n, W_OK) != 0)
01626     return true;
01627 
01628   return 0 != unlink(n);
01629 }
01630 
01631 bool
01632 FileName::
01633 rmdir() const
01634 {
01635   errno = 0;
01636   return 0 != ::rmdir(c_str()) || errno != 0;
01637 }
01638 
01639 bool
01640 FileName::
01641 mkdir( FileMode permissions ) const
01642 {
01643 #ifdef _MSC_VER
01644    return 0 != ::mkdir(c_str());
01645 #else
01646    return 0 != ::mkdir(c_str(), permissions.os_mode());
01647 #endif
01648 }
01649 
01650 int
01651 FileMode::
01652 os_mode() const
01653 {
01654   int rv=0;
01655 
01656 #if _MSC_VER
01657 
01658   if(mode & directory)      rv |= _S_IFDIR;
01659   if(mode & user_readable)  rv |= _S_IREAD;
01660   if(mode & user_writeable) rv |= _S_IWRITE;
01661 
01662 #else
01663 
01664   if(mode & link)           rv |= S_IFLNK;
01665   if(mode & directory)      rv |= S_IFDIR;
01666   if(mode & set_uid)        rv |= S_ISUID;
01667   if(mode & set_gid)        rv |= S_ISGID;
01668   if(mode & user_readable)  rv |= S_IRUSR;
01669   if(mode & user_writeable) rv |= S_IWUSR;
01670   if(mode & user_executable)rv |= S_IXUSR;
01671   if(mode & group_readable)  rv |= S_IRGRP;
01672   if(mode & group_writeable) rv |= S_IWGRP;
01673   if(mode & group_executable)rv |= S_IXGRP;
01674   if(mode & world_readable)  rv |= S_IROTH;
01675   if(mode & world_writeable) rv |= S_IWOTH;
01676   if(mode & world_executable)rv |= S_IXOTH;
01677 
01678 #endif
01679 
01680   return rv;
01681 
01682 }
01683 
01684 bool
01685 FileName::
01686 copy(FileName const &destination) const
01687 {
01688   char buffer[32768];
01689 
01690   FileStatus stat;
01691 
01692   if(file_stat(&stat))
01693     return true;   // error reading source file attributes
01694 
01695   int fh_in = open(c_str(), O_RDONLY | O_BINARY);
01696 
01697   if(fh_in < 0)
01698     return true;
01699 
01700   destination.remove();  // make sure its gone
01701 
01702   int fh  = creat(destination.c_str(), stat.mode.os_mode());
01703                 
01704   if(fh < 0)
01705     return true;
01706 
01707   SET_MODE(fh, O_BINARY);
01708 
01709   int bytes_read;
01710   int bytes_written;
01711 
01712   bool rv = false;
01713 
01714   for(;;)
01715   {
01716     bytes_read = read(fh_in, buffer, sizeof(buffer));
01717 
01718     if(bytes_read == 0)
01719       break;
01720 
01721     bytes_written = ::write(fh, buffer, bytes_read);
01722 
01723     if(bytes_written != bytes_read)
01724     {
01725       rv = true;
01726       break;
01727     }
01728 
01729   }
01730 
01731   close(fh);
01732   close(fh_in);
01733 
01734 
01735   return rv;
01736 }
01737 
01738 bool
01739 FileName::
01740 chmod( FileMode mode ) const
01741 {
01742    int err = ::chmod( c_str(), mode.os_mode() );
01743 
01744    if(err)
01745      return true;
01746 
01747    return false;
01748 
01749 }
01750 
01751 bool
01752 FileName::
01753 rename( FileName const &destination ) const
01754 {
01755    if(destination.exists() && !destination.is_writeable())
01756    {
01757      return true;
01758    }
01759 
01760    int err = ::rename( c_str(), destination.c_str() );
01761 
01762    if(err)
01763      return true;
01764 
01765    return false;
01766 
01767 }
01768 
01769 string
01770 FileName::
01771 extension() const
01772 {
01773   const_iterator first = begin(),
01774                  last  = end();
01775                 
01776   while(last != first)
01777   {
01778     --last;
01779 
01780     char c = *last;
01781 
01782     if(c == '.')
01783       return string(last, end());
01784     else
01785     if(c == '/')
01786       break;
01787     else
01788     if(c == '\\')
01789       break;
01790   }
01791 
01792   return "";
01793                 
01794 }
01795 
01796 size_t
01797 FileName::
01798 slurp(list<string> *lines) const
01799 {
01800   //
01801   // This function reads the file into a list of strings.  A
01802   // list of strings was chosen over vector so as to guarantee O(1) inserts
01803   // at the rear of the container.
01804   //
01805   // To achieve high speed, the readline function is not employed.  Readline
01806   // uses the string operator += method on each character in the file.  This is a
01807   // lot of overhead on large files.  To reduce that overhead, instead of invoking
01808   // one string method call per character, we reduce that to one per line -- often
01809   // an 80 to one reduction, more likely a 40 to one.  This is accomplished by
01810   // reading big blocks from the file and parsing the data into lines then appending
01811   // the entire line to the file in a single string method call.  Of course, you have
01812   // handle the occaisional partial line which occurs when an end of block occurrs
01813   // that is not exactly matched to the end of a line (ie the new line character).
01814   //
01815   // Now, I could have chosen to slurp the entire file into memory then parse the
01816   // in-memory image -- thus never needing to handle partial lines.  But this would
01817   // double the memory requirements.
01818   //
01819   // Parsing the data into lines is done using the memchr() function which is often
01820   // written in assembly language.  It will give maximum performance across multiple
01821   // machines and os's.
01822 
01823 
01824   bool incomplete_line=false;    // flag indicating part of line parsed in one block
01825                                  // and the rest resides in the next block
01826 
01827   size_t line_count=0;
01828 
01829   int fh = open(c_str(), O_RDONLY);
01830 
01831   for(;;)
01832   {
01833     //
01834     // Read blocks from the file, parse the lines in the block, append each
01835     // line into the target list.
01836     //
01837     char buffer[32768];
01838 
01839     int bytes_read = read(fh, buffer, sizeof(buffer));
01840 
01841     if(bytes_read < 1)
01842       break;
01843 
01844     char const *next = buffer;
01845     char const *end  = buffer + bytes_read;
01846 
01847     while(next < end)
01848     {
01849       char * eol = (char*)memchr(next, '\n', end - next);
01850 
01851       if(eol == 0)
01852       {
01853         // no complete line exists, put the rest of the
01854         // next in a partial line
01855 
01856         if(incomplete_line)
01857           lines->back().append(next, end - next);
01858         else
01859         {
01860           lines->push_back(string(next, end-next));
01861           incomplete_line = true;
01862         }
01863         
01864         break;
01865       }
01866       else
01867       {
01868          int line_fragment_length = eol - next + 1 ; // put the \n in the data
01869         
01870          if(incomplete_line)
01871          {
01872            lines->back().append(next, line_fragment_length);
01873            incomplete_line = false;
01874          }
01875          else
01876          {
01877           lines->push_back(string(next, line_fragment_length));
01878          }
01879         
01880          next = eol + 1; // skip the \n
01881         
01882          ++line_count;
01883       }
01884     }
01885   }
01886 
01887   return line_count;
01888 
01889 }
01890 
01891 bool
01892 FileName::
01893 is_absolute_path() const
01894 {
01895   string::const_iterator first = begin();
01896 
01897   size_t len = size();
01898 
01899   if(len < 1)
01900     return false;
01901 
01902   if( first[0] == '/' || first[0] == '\\')
01903     return true;
01904 
01905   if(len < 3)
01906     return false;
01907 
01908   return first[1] == ':' && (first[2] == '\\' || first[2] == '/');
01909 
01910 }
01911 
01912 bool
01913 FileName::
01914 is_root_dir() const
01915 {
01916   string::const_iterator first = begin();
01917 
01918   size_t len = size();
01919 
01920   if(   len == 1 
01921      && (*first == '/' || *first == '\\') 
01922     )
01923   {
01924      return true;
01925   }
01926 
01927   if(   len == 3
01928      && operator[](1) == ':'
01929      && (   operator[](2) == '/'
01930          || operator[](2) == '\\'
01931         )
01932     )
01933   {
01934      return true;  // windows top dir
01935   }
01936 
01937 
01938 
01939   return false;
01940 
01941 
01942 
01943 }
01944 
01945 
01946 string
01947 FileName::
01948 shorten(int length) const
01950 {
01951   size_t s = size();
01952 
01953   // if source is small enough, just return it
01954 
01955   if( s <= (size_t) length )
01956     return *this;
01957 
01958   // ok, its too big -- shorten it.
01959 
01960   string me = *this;
01961 
01962   if(length < 5)
01963   {
01964      me.erase(0, me.size() - length); // not enough room to beautify shortened
01965                                       // version, just return short version as is
01966      return me;
01967   }
01968 
01969   // shorten the return value to the desired size and include elipsis
01970   // to beautify (so to speak) the plug taken out
01971 
01972   length -= 3;  // the elipses
01973 
01974   static string elipsis("...");
01975 
01976   size_t excess = s - length;
01977 
01978   size_t start = s - excess;  start /= 2;
01979 
01980   me.erase(start, excess);
01981 
01982   me.insert(start, elipsis);
01983 
01984   return me;
01985 
01986 }
01987 
01988 
01989 string
01990 FileName::
01991 read_lines(FileName::accumulate_lines& accum, size_t* line_count_ptr, bool removeCR)
01992 {
01993   bool incomplete_line=false;
01994 
01995   size_t line_count=0;
01996 
01997   if(line_count_ptr) *line_count_ptr = 0;
01998 
01999   int fh = open(c_str(), O_RDONLY);  // on dos this is not binary!
02000 
02001   if(fh < 0)
02002     return string(strerror(errno));
02003 
02004   int partial_lines=0;
02005 
02006   for(;;)
02007   {
02008     char buffer[32768];
02009 
02010     int bytes_read = read(fh, buffer, sizeof(buffer));
02011 
02012     if(bytes_read < 1)
02013     {
02014       if(bytes_read == 0)
02015         break;
02016         
02017       string rv(strerror(errno));
02018 
02019       close(fh);
02020 
02021       return rv;
02022     }
02023 
02024     char const *next = buffer;
02025     char const *end  = buffer + bytes_read;
02026 
02027     while(next < end)
02028     {
02029       char * eol = (char*)memchr(next, '\n', end - next);
02030 
02031       if(eol == 0)
02032       {
02033         // no complete line exists, put the rest of the
02034         // next in a partial line
02035         
02036         ++partial_lines;
02037 
02038         if(incomplete_line)
02039           accum.back().append(next, end - next);
02040         else
02041         {
02042           accum.push_back(string(next, end-next));
02043           incomplete_line = true;
02044         }
02045         
02046         break;
02047       }
02048       else
02049       {
02050          int line_fragment_length = eol - next;
02051 
02052          bool hasRemovedCR = false;
02053 
02054          if(   removeCR 
02055             && line_fragment_length >= 1
02056             && eol[-1] == '\r'
02057            )
02058          {
02059              hasRemovedCR = true;
02060              --line_fragment_length;
02061          }
02062 
02063         
02064          if(incomplete_line)
02065          {
02066            accum.back().append(next, line_fragment_length);
02067            incomplete_line = false;
02068          }
02069          else
02070          {
02071            accum.push_back(string(next, line_fragment_length));
02072          }
02073 
02074          if(hasRemovedCR)
02075          {
02076            accum.setBackHasCR(true);  // tell the accumulator that 
02077                                       // we have removed a trailing
02078                                       // CR -- presumably only on unix/linux
02079                                       // because on windows we will never
02080                                       // see \r at the end of the line because
02081                                       // the read() call removes them.
02082          }
02083 
02084         
02085          next = eol + 1; // skip the \n
02086         
02087          ++line_count;
02088       }
02089     }
02090   }
02091 
02092   if(close(fh) < 0)
02093   {
02094      return strerror(errno);
02095   }
02096 
02097   if(line_count_ptr) *line_count_ptr = line_count;
02098 
02099 
02100   return string();
02101 }
02102 
02103 FileName
02104 FileName::
02105 getcwd()
02106 {
02107   char buffer[2048];
02108 
02109   if(::getcwd(buffer, sizeof(buffer)) == 0)
02110   {
02111     return ".";
02112   }
02113 
02114   return buffer;
02115 }
02116 
02117 bool
02118 FileName::
02119 write(string const &file_contents, bool binary_mode)
02120 {
02121    return write(file_contents.data(),
02122                 file_contents.data() + file_contents.size(),
02123                 binary_mode
02124                );
02125 }
02126 
02127 bool
02128 FileName::
02129 write(char const* begin, char const* end, bool binary_mode)
02130 {
02131   size_t bytes = (end - begin);
02132 
02133   FILE * f = fopen(c_str(), binary_mode ? "wb" : "w");
02134 
02135   if(!f)
02136     return true;
02137 
02138   if(1 != fwrite(begin, bytes, 1, f))
02139     return true;
02140 
02141   if(fflush(f))
02142     return true;
02143 
02144   fclose(f);
02145 
02146   return false;
02147 }
02148 
02149 
02150 std::auto_ptr<FileName::Tree>
02151 FileName::
02152 tree_helper(string const &path_string, bool only_dirs, int depth) const
02153   //
02154   //  Tree helper is invoked with two pathnames.  '*this' is a pathname
02155   //  that will be given to the tree node returned.  The 'parent' refers
02156   //  to the 'real' physical pathname referred to by *this.  Note that they
02157   //  may not be the same.  parent may include more of the path.  For example,
02158   //  suppose we are in the middle of a deep recursive directory, *this, will
02159   //  refer to the current node, whose children we will discover, but parent
02160   //  will refer to the full pathname to this point.
02161   //
02162 {
02163   Tree* tree = new Tree;
02164   
02165   tree->name_ = *this;
02166   tree->depth_ = depth;
02167 
02168   FileName path(path_string);
02169 
02170   path.file_stat(&tree->status_);
02171 
02172   if(tree->status_.mode.is_dir())
02173   {
02174      // read the subtree and append it to tree
02175 
02176      sorted_names_t children;
02177 
02178      FileName child_pattern(path);  child_pattern.convertToDirectorySearchPattern();
02179 
02180      find_matching(child_pattern, &children);
02181 
02182      sorted_names_t::iterator f=children.begin(), l=children.end();
02183 
02184      FileName me(".");
02185      FileName mommy("..");
02186 
02187      while(f != l)
02188      {
02189        FileName const& curname = *f++;
02190 
02191        if( (curname[0] == '.') && (curname == me || curname == mommy) )
02192        {
02193          continue;
02194        }
02195 
02196        FileName child(path);  
02197        
02198        if(child == ".")
02199          child = "";
02200        else
02201        if(child[child.size()-1] != '/')
02202          child += "/"; 
02203          
02204        child += curname;
02205 
02206        FileStatus status;
02207 
02208        if(only_dirs)
02209           child.file_stat(&status);
02210 
02211        std::auto_ptr<Tree> tmp(0);
02212 
02213        if(!only_dirs || status.mode.is_dir())
02214        {
02215          tmp.reset(curname.tree_helper(child, only_dirs, depth+1).release());
02216 
02217          tmp->parent_ = tree;
02218 
02219          tree->add_child(tmp.release());
02220        }
02221 
02222      }
02223   }
02224 
02225   return std::auto_ptr<Tree>(tree);
02226 }
02227 
02228 std::auto_ptr<FileName::Tree>
02229 FileName::
02230 tree(bool only_dirs) const
02231 {
02232   return tree_helper(*this, only_dirs);
02233 }
02234 
02235 FileName::Tree::
02236 ~Tree()
02237 {
02238   children_t::iterator f =children_.begin(), l = children_.end();
02239 
02240   while(f != l)
02241   {
02242     delete *f++;
02243   }
02244 }
02245 
02246 FileName::Tree::
02247 Tree(Tree const &r)
02248 : status_(r.status_),
02249   name_(r.name_),
02250   parent_(r.parent_),
02251   depth_(r.depth_)
02252 {
02253   children_t::iterator f =children_.begin(), l = children_.end();
02254 
02255   while(f != l)
02256   {
02257     Tree* t = *f++;
02258     children_.push_back( new Tree(*t) );
02259   }
02260   
02261 }
02262 
02263 
02264 void 
02265 FileName::Tree::
02266 remove_child(FileName const &name)
02267 {
02268   children_t::iterator f =children_.begin(), l = children_.end();
02269 
02270   while(f != l)
02271   {
02272     Tree* cur = *f;
02273 
02274     if(cur->name_ == name)
02275     {
02276       delete cur;
02277 
02278       children_.erase(f);
02279       return;
02280     }
02281     ++f;
02282   }
02283 
02284 }
02285 
02286 FileName
02287 FileName::Tree::
02288 path() const
02289 {
02290   FileName rv = name();
02291 
02292   Tree const* scan = this;
02293 
02294   while(scan->parent_)
02295   {
02296     string pname = scan->parent_->name();
02297 
02298     char c=' ';
02299 
02300     if(pname.size())
02301       c = pname[ pname.size() -1 ];
02302 
02303     if(c != '/')
02304       rv.insert(0, "/");
02305 
02306     rv.insert(0, pname);
02307     scan = scan->parent_;
02308   }
02309 
02310   return rv;
02311 
02312 }
02313 
02314 using namespace std;                         
02315 
02316 static set<string> tilde_cache;
02317 
02318 static string gethomepath()
02319 {
02320   {
02321     char const *home = getenv("HOME");
02322 
02323 #ifdef _MSC_VER
02324     // ignore HOME variable on nt
02325     {
02326       char const *homepathp= getenv("HOMEPATH");
02327 
02328       string homepath;
02329 
02330       if(homepathp)
02331       {
02332         homepath = homepathp;
02333         
02334         char const *homedrive= getenv("HOMEDRIVE");
02335 
02336         if(homedrive)
02337         {
02338           char buffer[1024];
02339 
02340           sprintf(buffer, "%s%s", homedrive, homepath.c_str());
02341 
02342           set<string>::iterator f = tilde_cache.find(buffer);
02343 
02344           if(f == tilde_cache.end())
02345           {
02346             tilde_cache.insert(buffer);
02347             f = tilde_cache.find(buffer);
02348           }
02349 
02350           return f->c_str();
02351 
02352         }
02353         else
02354           return homepath;
02355       }
02356     }
02357 #endif
02358 
02359     if(home == 0)
02360       home=".";
02361 
02362     return home;
02363   }
02364 }
02365 
02366 
02367 FileName
02368 FileName::
02369 expand_tildes() const
02370 {
02371   if(size() < 1 ||  *begin() != '~')
02372     return *this;
02373 
02374   // at this point, we know that the first character of the current filename
02375   // is '~'.
02376 
02377   FileName const &me = *this;
02378 
02379   if(size() == 1 )
02380   {
02381     return gethomepath();
02382   }
02383   else
02384   {
02385     // size() > 1
02386 
02387 
02388     if(me[1] == '/' || me[1] == '\\')
02389     {
02390       // home directory relative path
02391 
02392       string home = gethomepath();
02393 
02394       FileName prefix;
02395       FileName homevalue;
02396 
02397       homevalue = home;
02398 
02399       homevalue += me.substr(1,me.size());
02400 
02401       return homevalue; 
02402       
02403     }
02404     else
02405     {
02406       // random user home directorty relative path
02407 
02408       string user;
02409 
02410       size_t i;
02411 
02412       for(i=1; i < me.size(); ++i)
02413       {
02414         char c = me[i];
02415 
02416         if(c == '/' || c == '\\')
02417           break;
02418         else
02419           user += c;
02420       }
02421 
02422       string rest = me.substr(i, me.size());
02423 
02424 #ifdef _MSC_VER
02425 
02426       {
02427         FileName homepath = gethomepath();
02428         FileName parent = homepath.dirname();
02429 
02430         return parent + user + rest;
02431       }
02432 
02433 #else
02434       UserInfo ui = UserInfo::get(user);
02435       return ui.home_dir_ + rest;
02436 #endif
02437 
02438 
02439 
02440     }
02441   }
02442 
02443   return *this;
02444 
02445 }
02446 
02447 void
02448 FileName::
02449 convertToDirectorySearchPattern(char const *pattern)
02450 {
02451   if(size() == 0)
02452     *this = pattern;
02453   else
02454   {
02455     char lastChar = (*this)[size()-1];
02456 
02457     if(lastChar == '/' || lastChar == '\\' || is_dir())
02458     {
02459       if(lastChar != '/' && lastChar != '\\')
02460         (*this) += '/';
02461 
02462       *this += pattern;
02463     }
02464 
02465   }
02466 }
02467 
02468 
02469 FileName
02470 FileName::
02471 readlink(int *mode) const
02472 {
02473 #ifdef _MSC_VER
02474    return ""; 
02475 #else
02476    
02477    char const *p = this->c_str();
02478 
02479    char buffer[3000];
02480 
02481 
02482    ssize_t rv = ::readlink(p, buffer, sizeof(buffer));
02483 
02484    if(rv < 1)
02485       return "";
02486 
02487    if(rv == sizeof(buffer))
02488    {
02489       // name truncation is almost guaranteed.
02490 
02491       return "";  
02492 
02493    }
02494 
02495    buffer[rv]=0;
02496 
02497    FileName tmp(buffer);
02498 
02499    tmp.convert_to_absolute_path();
02500 
02501    if(mode)
02502    {
02503        struct stat buf;
02504        
02505        if(lstat(p, &buf))
02506        {
02507            *mode=0;
02508        }
02509        else
02510        {
02511           *mode = buf.st_mode;
02512        }
02513    }
02514 
02515    return tmp;
02516 #endif
02517 
02518 }
02519 
02520 
02521 
02522 
02523 void
02524 FileMode::
02525 unpackPermissions(string &user, string &group, string &world)
02526 {
02527   //
02528   //  turn the file mode into 3 strings -- each of which represents the file permissions.  One each for the
02529   //  user, group, and world permissions.
02530   //
02531 
02532   user="";
02533   group="";
02534   world="";
02535 
02536   if(mode & user_readable)    user  += 'r';
02537   if(mode & user_writeable)   user  += 'w';
02538   if(mode & user_executable)  user  += 'x';
02539 
02540   if(mode & group_readable)   group += 'r';
02541   if(mode & group_writeable)  group += 'w';
02542   if(mode & group_executable) group += 'x';
02543 
02544   if(mode & world_readable)   world += 'r';
02545   if(mode & world_writeable)  world += 'w';
02546   if(mode & world_executable) world += 'x';
02547   
02548 }
02549 
02550 
02551 static int unpackPermString(string const &s, int readable, int writeable, int executable)
02552 {
02553   // this function takes a string of the form rwx and returns an integer which is an "oring" of the
02554   // specified readable, writeable, and executable bits passed in.  
02555 
02556   string::const_iterator begin = s.begin(), end = s.end();
02557 
02558   int rv = 0;
02559 
02560   while(begin != end)
02561   {
02562     char c = *begin++;
02563 
02564     switch(c)
02565     {
02566       case 'r':
02567       case 'R':
02568         rv |= readable;
02569         break;
02570 
02571       case 'w':
02572       case 'W':
02573         rv |= writeable;
02574         break;
02575 
02576       case 'x':
02577       case 'X':
02578         rv |= executable;
02579         break;
02580     }
02581 
02582   }
02583 
02584   return rv;
02585 
02586 }
02587 
02588 void
02589 FileMode::
02590 packPermissions(string const &user, string const &group, string const &world)
02591 {
02592   int userMode  = unpackPermString(user,  user_readable,  user_writeable,  user_executable);
02593   int groupMode = unpackPermString(group, group_readable, group_writeable, group_executable);
02594   int worldMode = unpackPermString(world, world_readable, world_writeable, world_executable);
02595 
02596   mode &= ~(
02597               user_readable    |
02598               user_writeable   | 
02599               user_executable  |
02600               group_readable   |
02601               group_writeable  | 
02602               group_executable |
02603               world_readable   |
02604               world_writeable  | 
02605               world_executable 
02606            );
02607 
02608   mode |= userMode | groupMode | worldMode;
02609 
02610 
02611 }
02612 
02613 
02614 bool
02615 FileMode::
02616 permStringValid(string const &s)
02617 {
02618 
02619   int rcount = 0;
02620   int wcount = 0;
02621   int xcount = 0;
02622 
02623   string::const_iterator begin = s.begin(), end = s.end();
02624 
02625   while(begin != end)
02626   {
02627     char c = *begin++;
02628 
02629     switch(c)
02630     {
02631       case 'r':
02632       case 'R':
02633         ++rcount;
02634         if(rcount > 1)
02635           return false;
02636         break;
02637 
02638       case 'w':
02639       case 'W':
02640         ++wcount;
02641         if(wcount > 1)
02642           return false;
02643         break;
02644 
02645       case 'x':
02646       case 'X':
02647         ++xcount;
02648         if(xcount > 1)
02649           return false;
02650         break;
02651 
02652       default:
02653         return false;
02654         break;
02655 
02656     }
02657 
02658   }
02659 
02660   return true;
02661 }
02662 
02663 } // namespace cxxtls
Generated on Wed Feb 29 22:50:05 2012 for CXXUtilities by  doxygen 1.6.3