/* Zutils - Utilities dealing with compressed files Copyright (C) 2009-2021 Antonio Diaz Diaz. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ /* Returns true if full_name is a regular file with an enabled extension or (a link to) a directory. */ bool test_full_name( const std::string & full_name, const struct stat * stp, const bool follow ) { struct stat st, st2; if( follow && stat( full_name.c_str(), &st ) != 0 ) return false; if( !follow && lstat( full_name.c_str(), &st ) != 0 ) return false; if( S_ISREG( st.st_mode ) ) // regular file return enabled_format( extension_format( extension_index( full_name ) ) ); if( !S_ISDIR( st.st_mode ) ) return false; std::string prev_dir( full_name ); bool loop = ( stp && st.st_ino == stp->st_ino && st.st_dev == stp->st_dev ); if( !loop ) for( unsigned i = prev_dir.size(); i > 1; ) { while( i > 0 && prev_dir[i-1] != '/' ) --i; if( i == 0 ) break; if( i > 1 ) --i; // remove trailing slash except at root dir prev_dir.resize( i ); if( stat( prev_dir.c_str(), &st2 ) != 0 || !S_ISDIR( st2.st_mode ) || ( st.st_ino == st2.st_ino && st.st_dev == st2.st_dev ) ) { loop = true; break; } } if( loop ) // full_name already visited or above tree show_file_error( full_name.c_str(), "warning: Recursive directory loop." ); return !loop; // (link to) directory } /* Returns in input_filename the next filename, or "." for stdin. ("." was chosen because it is not a valid filename). Sets 'error' to true if a directory fails to open. */ bool next_filename( std::list< std::string > & filenames, std::string & input_filename, bool & error, const int recursive, const bool ignore_stdin = false, const bool no_messages = false ) { while( !filenames.empty() ) { input_filename = filenames.front(); filenames.pop_front(); if( input_filename == "-" ) { if( ignore_stdin ) continue; input_filename = "."; return true; } struct stat st; if( stat( input_filename.c_str(), &st ) == 0 && S_ISDIR( st.st_mode ) ) { if( recursive ) { DIR * const dirp = opendir( input_filename.c_str() ); if( !dirp ) { if( !no_messages ) show_file_error( input_filename.c_str(), "Can't open directory", errno ); error = true; continue; } for( unsigned i = input_filename.size(); i > 1 && input_filename[i-1] == '/'; --i ) input_filename.resize( i - 1 ); // remove trailing slashes struct stat stdot, *stdotp = 0; if( input_filename[0] != '/' ) // relative path { if( input_filename == "." ) input_filename.clear(); if( stat( ".", &stdot ) == 0 && S_ISDIR( stdot.st_mode ) ) stdotp = &stdot; } if( input_filename.size() && input_filename != "/" ) input_filename += '/'; std::list< std::string > tmp_list; while( true ) { const struct dirent * const entryp = readdir( dirp ); if( !entryp ) { closedir( dirp ); break; } const std::string tmp_name( entryp->d_name ); if( tmp_name == "." || tmp_name == ".." ) continue; const std::string full_name( input_filename + tmp_name ); if( test_full_name( full_name, stdotp, recursive == 2 ) ) tmp_list.push_back( full_name ); } filenames.splice( filenames.begin(), tmp_list ); } continue; } return true; } input_filename.clear(); return false; }