Source: dir.cpp

# Source: dir.cpp |
Annotated List
Files
Globals
Hierarchy
Index

    //-------------------------------------------------------------------
    // Projekt:   NDir, Nice Directory
    // Datei:     dir.cpp -- Main program file
    //------------------------------------------------------------------
    // Autor:           Michael Weers
    // Letzte Änderung: 2000-06-17
    // Version:         0.8.3
    // Rechner:         i386+, GNU Linux
    // Compiler:        GNU CC 2.91.66
    //-------------------------------------------------------------------

    #include
    #include
    #include
    #include
    #include
    #include
    #include
    #include

    #ifdef __ultrix__
    #include "Ultrix-Compatibility.hpp"
    #endif

    #include "DirectoryList.hpp"
    #include "StringUtils.hpp"  // for icompare()
    #include "ColorSetup.hpp"
    #include "text/format.h"

    using namespace std;

    struct Options {
      bool show_info;
      bool show_locale_info;

      bool show_fileinfo;
      enum FileFilter_t { NON_HIDDEN, ALMOST_ALL, ALL };
      FileFilter_t filter;   // show hidden files?
      bool format_long; // long output format
      bool more_compact; // *Allow* view to do a more compact output than by default
      bool show_groups;

      enum ShowTime_t { STATUS_CHANGE, ACCESS, MODIFICATION };
      ShowTime_t show_time;
      bool dirs_as_files; // list directories as files instead of their contents
      bool recursive;
      // bool numeric_IDs= false;
      CompareFunction_t compareFunction;
      bool reverse;

      enum Colorization_t { NEVER, IF_TERMINAL, ALWAYS };
      Colorization_t colorization;

      bool utc;

      Options(): show_info( false),
                 show_locale_info( false),
                 show_fileinfo( false),
                 filter( NON_HIDDEN),
                 format_long( false),
                 more_compact( false),
                 show_groups( false),
                 show_time( MODIFICATION),
                 dirs_as_files( false),
                 recursive( false),
                 compareFunction ( &NamePrecedence ),
                 reverse( false),
                 colorization( IF_TERMINAL),
                 utc( false)

                 {}
    };

    #include

    #include

    class Dir_view {
                      /** The stream the output is written to*/
                  ostream& out;
                      /** The DirectoryList to be printed */
                  DirectoryList* DL;
                      /** The options, mainly for output. Those options not affecting
                          the output are ignored of course */
          const   Options& options;
                      /** The color setup */
          const   ColorSetup& colors;

                  int width;  // The terminal width in characters (0 if unspecified)
                  bool is_terminal;

      public:
                  Dir_view( DirectoryList* DL, const Options&, const ColorSetup&);

                  int getWidth() { return width; }
                  bool isTerminal() { return is_terminal; }
                  void write();
      protected:
                  int getLineWidth();

                  void writeFileInfoList();
                  void writeFileInfo( const UnixFile& );
                  void writeListLong();
                  void writeListWide();
                  string longFormat( UnixFile& file);
      public:
          static  string StatisticsLine( long int, long int files, long long int size);
    };

    Dir_view::Dir_view( DirectoryList* DL, const Options& o, const ColorSetup& s)
                     : out( cout), options( o), colors(s) {

      this->DL= DL;
      width= getLineWidth();
      is_terminal= (isatty( STDOUT_FILENO) != 0);
    }

    void Dir_view::write() {
      if ( !DL->directoryExists())
        out << "\t" << text::clean( DL->getDirectory().getCanonicalPath() )
             << ": No such file or directory." << endl;
      else if ( options.show_fileinfo){
        writeFileInfoList();
      }
      else {
        out << "\t" << "Directory: " + text::clean( DL->getDirectory().getCanonicalPath()) << "\n";
        if ( !DL->isReadable())
          out << "\t" << "Unable to read directory." << endl;
        else if ( DL->isEmpty() ) {
          out << "\t" << "No matching file found." << endl;
        }
        else {
          if (options.format_long) writeListLong();
          else                     writeListWide();

          out << "\t";
          out << StatisticsLine( DL->getAllFilesCount(),
                                 DL->getRegFilesCount(),
                                 DL->getRegFilesSize() ) << endl;
        }
      }
    }

    int Dir_view::getLineWidth() {
    // Attempt 1: Determine Terminal line width from terminal itself.
    // Code taken from GNU ls
    #ifdef TIOCGWINSZ
      {
        struct winsize ws;

        if (ioctl (STDOUT_FILENO, TIOCGWINSZ, &ws) != -1 && ws.ws_col != 0)
          return ws.ws_col;
      }
    #endif
    // Attempt 2: Determine line width from Enviromnent variable
      char* value= getenv( "COLUMNS");
      if (value != NULL) {
        int i;
        int r= sscanf( value, "%d", &i);
        if (r == 1 && i > 0)
          return i;
      }
    // Attempt 3: default value
      return 80;
    }

    void Dir_view::writeFileInfoList() {
      out << endl;

      for ( vector::iterator pos= DL->file_list.begin();
            pos != DL->file_list.end();
            ++ pos )
      {
        writeFileInfo( UnixFile( (**pos).getFullName(), UnixFile::AS_IS, true ) );
      }
    }

    void Dir_view::writeFileInfo( const UnixFile& file) {
      out << "Name:                 " << text::clean( file.getName()) << endl
          << "Type:                 " ;

      switch (file.getType()) {
        case UnixFile::REGFILE:  out << "Regular file"; break;
        case UnixFile::DIRECTORY:  out << "Directory"; break;
        case UnixFile::SYMLINK: out << "Symbolic link";
                 if ( file.isNavigable())  out << " to a directory";
                 if ( file.isBrokenLink()) out << " (broken)";
                 break;
        case UnixFile::BLOCK_DEVICE:  out << "Block Device"; break;
        case UnixFile::CHAR_DEVICE:  out << "Character Device"; break;
        case UnixFile::PIPE:  out << "Named Pipe"; break;
        case UnixFile::SOCKET:  out << "Socket";
      }
      out << endl
          << "Path:                 " << text::clean( file.getFullName()) << endl;

      if (file.isFile()) {
      out << "Size:                 "
    #ifdef NO_THOUSANDS_GROUPING
      << text::format("%lld",file.getSize())
    #else
      << text::format("%'lld",file.getSize())
    #endif
      << " bytes" << endl ;
      }
      else if (file.isLink()) {
        string target;
        string link=file.getLinkName();
        if (link.size()>0 && link[0]=='/')  target= link;
        else target=
          UnixFile::getCanonicalPath( UnixFile::getAbsolutePath(
               file.getParent() + UnixFile::separator + link ) );

        out << "Link target:          " << text::clean(target) << endl;
      }

      out   << "Type and permissions: " << file.getAttributeString() << endl
            << "Owner:                " << text::clean( file.getOwner( true) )
            << " (" << text::clean(file.getOwnerName()) << ")"
            << "   Group: " << text::clean(file.getGroup( true)) << endl
            << "Last modified:        " << file.getLastModified().toString( options.utc) << endl
            << "Last accessed:        " << file.getLastAccessed().toString( options.utc) << endl
            << "Last Status change:   " << file.getLastStatusChanged().toString( options.utc) << endl
            << endl;
    }

    void Dir_view::writeListWide() {

      vector::iterator pos;

      int name_length= 0;          // find out longest file name
      for (pos= DL->file_list.begin(); pos != DL->file_list.end(); ++pos) {
        int l= (*pos)->getName().size();

        if (name_length < l) name_length= l;
      }

      const int spacing= 3;

      int lineWidth= getLineWidth();          // determie terminal width...

      int colWidth= name_length + spacing;
      int cols= lineWidth / colWidth;
      if (cols < 1)  cols= 1;
      int col= 0;               // # of current column (startig from 0)

      for (pos= DL->file_list.begin(); pos != DL->file_list.end(); ++pos) {
        UnixFile* f= (*pos);
        if (f != NULL) {
          string::size_type entry_length= f->getName().size();

          out << colors.formatFilename( *f);
          if (f->isNavigable()) {
            out << '/';
            entry_length++;
          }

          if ( col >= cols-1) { // letzte Spalte
            out << endl;
            col= 0;
          }
          else {
            string fill( colWidth - entry_length, ' ');
            out << fill;
            col++;
          }
        }
      } // for
      if (col != 0)  out << endl;
    }

    void Dir_view::writeListLong() {
        vector::iterator pos;

        for (pos= DL->file_list.begin(); pos != DL->file_list.end(); ++pos) {
          UnixFile* f= (*pos);
          if (f != NULL) {
            out << longFormat( *f) << "\n";
          }
        }
    }

    string Dir_view::longFormat( UnixFile& file) {
      string s;
      s.reserve( 160);
      s.append( file.getAttributeString()).append( options.more_compact ? " " : "  ");

      s += align( text::clean( file.getOwner( true)) , 9, LEFT);

      if (options.show_groups) {
        s += align( text::clean( file.getGroup( true)) , 9, LEFT);
      }

      Date date( 0);
      switch ( options.show_time) {
        case Options::STATUS_CHANGE: date= file.getLastStatusChanged();  break;
        case Options::ACCESS: date= file.getLastAccessed();  break;
        default: date= file.getLastModified();
      }
      s.append( date.toString( "%Y-%m-%d %H:%M", options.utc) );

      if ( file.isFile() ) {

      // format string: number: precision, ll: use long long int,
      //                d: print integers as decimal numbers, ': use thousands separator
    #ifdef NO_THOUSANDS_GROUPING
        s += text::format( "%16lld", file.getSize() ) + "  ";
    #else
        s += text::format( "%'16lld", file.getSize() ) + "  ";
    #endif
      }
      else if ( file.isNavigable() )
        if (file.isLink())                          // symlink to a directory
          s += align( "[-> Dir]", 18, CENTER);
        else
          s += align( "[Dir]", 18, CENTER);
      else
        s.append( 18, ' ');

      // determine name string length
      string::size_type name_length= file.getName().size();
      if (file.isLink())  name_length += 4 + file.getLinkName().size(); // 4 chars " -> "

      // now: if we use a terminal and the name string is too long, put it
      // in the next line, right justified.
      if (isTerminal() && s.size() + name_length > getWidth()) {
        s += "\n";
        // compiler warns about signed-unsigned-comp. but this is safe.
        // add space
        if (getWidth() > name_length)  s.append( getWidth() - name_length, ' ');
      }
      s += colors.formatFilename( file);
      if (file.isLink())  s.append(" -> ").append( file.getLinkName());
      return s;
    }

    string Dir_view::StatisticsLine( long int all_files, long int files, long long int size) {
    #ifdef NO_THOUSANDS_GROUPING
      string s = text::format( "%ld", all_files) + " entries,  "
        + text::format( "%ld", files ) + " regular files with "
        + text::format( "%lld", size ) + " bytes total size";
    #else
      string s = text::format( "%'ld", all_files) + " entries,  "
        + text::format( "%'ld", files ) + " regular files with "
        + text::format( "%'lld", size ) + " bytes total size";
    #endif
      return s;
    }

    class Dir_main {

      string progname;
      int argc;
      char** argv;

      vector file_args;
      vector L;  // List of directories

      Options options;
      ColorSetup colorsetup;
    //  APrecedence* precedence;

      long long int summary_filesize;
      long int summary_filecount, summary_entrycount, summary_dirs_listed;

      public:
                       int returncode;

                       Dir_main( int argc, char* argv[] );
                       ~Dir_main();

      protected:
                  void handle_file_arg( const string& arg);

           /** Output a directory, eventually recurring into subdirectories */
                  void handle_directory( DirectoryList& DL);

                  void handle_argument( const string& arg);

      public:
                  void run();
    }; // class Dir_main

    Dir_main::Dir_main( int argc, char* argv[] ):  returncode( 0) {
      progname= argv[0];
      this->argc= argc;
      this->argv= argv;
      summary_entrycount=0; summary_filecount=0; summary_filesize=0; summary_dirs_listed=0;
    }

    Dir_main::~Dir_main() {
      for( vector::size_type i= 0; i < L.size(); ++i) {
         delete L[i];  L[i]= NULL;
      }
    }

    void Dir_main::handle_file_arg( const string& arg) {

      bool handeled= false;             // is f handeled anywhere?
      UnixFile* f= new UnixFile( arg);

      if ( !f->exists() || ( f->isNavigable() && !options.dirs_as_files )) {
                              // create new DirectoryList and list it.

                              // ändern: nichtexistente Dateien schon
                              // hier fehlerbehandeln
        DirectoryList* DL= new DirectoryList( f );
        L.push_back( DL);
        handeled= true;
        return;
      }
      else {
                              // f is an ordinary file (maybe a directory
                              // if option 'dirs_as_files' is in effect);
                              // search DirectoryList where it belongs to...
        vector::iterator dir;
        for (dir= L.begin(); dir != L.end(); ++dir) {
          DirectoryList* DL = (*dir);
          UnixFile parent( f->getParent());
          if ( (*dir)->getDirectory().equals( parent) ) {
                                        // ...found!
            DL->insert_unique( f);
            handeled= true;
            return;
          }
        }
        if (!handeled) {            // ... not found, make new DirectoryList
                                    // appropriate to the file
          DirectoryList* DL= new DirectoryList( new UnixFile( f->getParent()), false );
          DL->insert( f);
          L.push_back( DL);
          handeled= true;
        }

      } // if-else
    }

       /** Output a directory, eventually recurring into subdirectories */
    void Dir_main::handle_directory( DirectoryList& DL) {
      DL.list( ! (options.filter == Options::NON_HIDDEN), options.filter == Options::ALL );
      DL.syncStatistics();
      DL.setCompareFunction( options.compareFunction);
      DL.reversed_order= options.reverse;
      DL.ReOrder();                             // sort directory

      if (&DL != L.front())  cout << endl;      // A separating line between directories

      Dir_view v( &DL, options, colorsetup);
      v.write();

      // now for the overall statistics:
      if (DL.isReadable()) {
        summary_entrycount += DL.getAllFilesCount();
        summary_filecount += DL.getRegFilesCount();
        summary_filesize += DL.getRegFilesSize();
        summary_dirs_listed++;
      }

      if (options.recursive) {
        vector::iterator f;
        for ( f= DL.file_list.begin(); f != DL.file_list.end(); ++f) {

          if ( (*f)->isSubNavigable() ) {  // prevents recurring into links that
                                           // point to parent directories.
            DirectoryList DL_sub( new UnixFile( (**f).getPath()));
            DL_sub.setCompareFunction( options.compareFunction);

            handle_directory( DL_sub);
          }
        }
      }
    }

    void Dir_main::handle_argument( const string& arg) {

      if (arg.size() > 0 && arg[0]=='-') {            // option
        if (arg=="-l")  options.format_long= true;
        else if (arg=="-a")  options.filter= Options::ALL;
        else if (arg=="-A")  options.filter= Options::ALMOST_ALL;
        else if (arg=="-?" || arg=="--help")  options.show_info= true;
        else if (arg=="-?l") options.show_locale_info= true;
        else if (arg=="-d")  options.dirs_as_files= true;
        else if (arg=="-g")  options.show_groups= options.more_compact= true;
        else if (arg=="-R")  options.recursive= true;
        else if (arg=="-t")  options.compareFunction= ModDatePrecedence;
        else if (arg=="-u")  options.show_time= Options::ACCESS;
        else if (arg=="-c")  options.show_time= Options::STATUS_CHANGE;
        else if (arg=="-S")  options.compareFunction= FileSizePrecedence;
        else if (arg=="-U")  options.compareFunction= 0;
        else if (arg=="-X")  options.compareFunction= ExtensionPrecedence;
        else if (arg=="-r")  options.reverse= true;
        else if (arg=="-cn")  options.colorization= Options::NEVER;
        else if (arg=="-ca")  options.colorization= Options::ALWAYS;
        else if (arg=="--info")  options.show_fileinfo= true;
        else if (arg=="--utc")  options.utc= true;
        // new options here ...
        else {
          cerr << "NDir:  Unrecognized option '" << arg << "'." << endl
               << "Aborting." << endl;
          returncode= 1;
          return;
        }
      }
      else
        file_args.push_back( arg);
    }

    void Dir_main::run() {
      // parsing arguments and set options

    //   if ( progname.size() >= 2 && progname.compare( progname.size()-2, 2, "lv")==0 )
        if (progname.substr( 0, 2)=="lv") {
          options.format_long= true;
        }

    //   char* options_from_env= getenv("NDIR_OPTIONS");
    //   if (options_from_env!=NULL) {
    //     StringTokenizer st( options_from_env, " \t");
    //     while (st.hasMoreTokens())  handle_option( st.getNextToken());
    //   }

      for ( int i=1; i < argc; ++i) {
        handle_argument( argv[i]);
        if (returncode!=0)  break;
      }
      // now "normalize" some options...

      if (options.compareFunction==ModDatePrecedence) {  // if -t was given,
        if (options.show_time==Options::ACCESS)      // and -s, then sort according to atime.
          options.compareFunction= AccessDatePrecedence;
        if (options.show_time==Options::STATUS_CHANGE)
          options.compareFunction= StatusChangeDatePrecedence;
      }
      if (options.show_fileinfo) {
        options.dirs_as_files= true;
      }
      switch (options.colorization) {
        case Options::NEVER:  colorsetup.do_colorization= false;  break;
        case Options::IF_TERMINAL:  colorsetup.do_colorization= (isatty( STDOUT_FILENO) != 0);  break;
        case Options::ALWAYS: colorsetup.do_colorization= true;  break;
      }

      for (vector::iterator i= file_args.begin(); i != file_args.end(); ++i)
        handle_file_arg( *i);

      // Now the program's main functions

      if (returncode!=0) {  // nothing...
      }
      else if (options.show_info) {
        cout << endl
          << "  NDir -- Nice Directory. Version 0.8.7" << endl
          << "  Copyright (c) 1997-2003 Michael Weers." << endl
          << endl
          << "  Displays contents of directories." << endl
          << endl
          << "  Usage:  ndir / lw / lv   " << endl
          << "  lw and lv are alternative names for NDir. Use lv for a detailed listing." << endl
          << "  See the manual page (man ndir) for details." << endl
          << endl;
      }
      else if (options.show_locale_info) {
        char* lc_collate= setlocale( LC_COLLATE, NULL);
        if (lc_collate != NULL)
          cout << "  NDir uses string comparison conventions for locale: "
               << lc_collate << endl
               << "  Latin characters are compared case-insensitive: "
               << ((icompare( "ax", "Ay") < 0) ? "Yes" : "No") << endl;
        char* lc_numeric= setlocale( LC_NUMERIC, NULL);
        if (lc_numeric != NULL)
          cout << "  NDir uses number display conventions for locale: "
               << lc_numeric << endl;

        cout << endl;
      }
      else {
          // now the usual -- print the directories
        if (L.empty()) {    // Special case: no DirectoryList created yet.
                                // create one with current directory
           handle_file_arg( ".");
        }
                // generate output.
                // note: recursive walk through directory tree is done within
                // handle_directroy()

        vector::iterator d;
        for (d= L.begin(); d != L.end(); ++d) {
          handle_directory( **d );
        }

        if ( !options.show_fileinfo && (L.size() > 1 || options.recursive) ) {
           cout << endl
                << colorsetup.format("Summary for "+text::format( "%ld", summary_dirs_listed)+" directories:", "01") << endl
                << "\t" << Dir_view::StatisticsLine( summary_entrycount, summary_filecount, summary_filesize) << endl;
        }
      }
    } // run()

    int main( int argc, char* argv[]) {
      setlocale( LC_ALL, "");                // i18n settings

      Dir_main application( argc, argv);
      application.run();
      return application.returncode;
    }

Generated by: mw on nemea on Thu Dec 4 21:20:44 2003, using kdoc 2.0a54.