Commit 4d6f7b72 authored by jan.koester's avatar jan.koester
Browse files

test

parent 6715f8fd
Loading
Loading
Loading
Loading
+221 −69
Changes for commands/filesystem/ls.cpp: 221 added lines, 69 removed lines.
Original line number Diff line number Diff line
@@ -37,6 +37,9 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <dirent.h>
#include <sys/stat.h>
#include <pwd.h>
#include <grp.h>
#include <ctime>
#include <sys/sysmacros.h>

#include "shell.h"

@@ -46,106 +49,255 @@ namespace fresh { namespace cmd {
    public:
        LsCommand():ShellCommand("ls"){}

        static void printLong(const std::string &dirpath,const std::string &name){
            std::string full=dirpath.empty() ? name : dirpath+"/"+name;
    private:
        struct Opts {
            bool all=false,almostAll=false,longFormat=false,numericIds=false,inode=false,
                 dirItself=false,recursive=false,sortTime=false,sortSize=false,noSort=false,
                 reverse=false,human=false,classify=false,slashDirs=false,follow=false;
        };

        struct Item {
            std::string name;   // as displayed
            std::string path;   // for stat/readlink
            struct stat st;
            if(lstat(full.c_str(),&st)!=0){
                std::cerr << "ls: " << full << ": " << strerror(errno) << std::endl;
                return;
            bool ok;
        };

        static std::string modeString(mode_t m){
            char p[11];
            p[0]=S_ISDIR(m) ? 'd' : S_ISLNK(m) ? 'l' : S_ISCHR(m) ? 'c' : S_ISBLK(m) ? 'b' :
                 S_ISFIFO(m) ? 'p' : S_ISSOCK(m) ? 's' : '-';
            const char *rwx="rwxrwxrwx";
            for(int k=0;k<9;++k) p[k+1]=(m & (0400>>k)) ? rwx[k] : '-';
            if(m & S_ISUID) p[3]=(m & S_IXUSR) ? 's' : 'S';
            if(m & S_ISGID) p[6]=(m & S_IXGRP) ? 's' : 'S';
            if(m & S_ISVTX) p[9]=(m & S_IXOTH) ? 't' : 'T';
            p[10]='\0';
            return p;
        }

            char perms[11];
            perms[0]=S_ISDIR(st.st_mode) ? 'd' : (S_ISLNK(st.st_mode) ? 'l' : '-');
            perms[1]=(st.st_mode & S_IRUSR) ? 'r' : '-';
            perms[2]=(st.st_mode & S_IWUSR) ? 'w' : '-';
            perms[3]=(st.st_mode & S_IXUSR) ? 'x' : '-';
            perms[4]=(st.st_mode & S_IRGRP) ? 'r' : '-';
            perms[5]=(st.st_mode & S_IWGRP) ? 'w' : '-';
            perms[6]=(st.st_mode & S_IXGRP) ? 'x' : '-';
            perms[7]=(st.st_mode & S_IROTH) ? 'r' : '-';
            perms[8]=(st.st_mode & S_IWOTH) ? 'w' : '-';
            perms[9]=(st.st_mode & S_IXOTH) ? 'x' : '-';
            perms[10]='\0';
        static std::string humanSize(off_t sz){
            const char *units="BKMGTPE";
            double v=(double)sz;
            int u=0;
            while(v>=1024 && u<6){ v/=1024; ++u; }
            char buf[32];
            if(u==0) snprintf(buf,sizeof buf,"%lld",(long long)sz);
            else if(v<10) snprintf(buf,sizeof buf,"%.1f%c",v,units[u]);
            else snprintf(buf,sizeof buf,"%.0f%c",v,units[u]);
            return buf;
        }

            struct passwd *pw=getpwuid(st.st_uid);
            std::string owner=pw ? pw->pw_name : std::to_string(st.st_uid);
        static char classifyChar(const struct stat &st,const Opts &o){
            if(S_ISDIR(st.st_mode)) return '/';
            if(!o.classify) return 0;
            if(S_ISLNK(st.st_mode)) return '@';
            if(S_ISFIFO(st.st_mode)) return '|';
            if(S_ISSOCK(st.st_mode)) return '=';
            if(st.st_mode & 0111) return '*';
            return 0;
        }

            std::cout << perms << " " << st.st_nlink << " " << owner << " "
                       << st.st_size << " " << name << std::endl;
        static void printItems(std::vector<Item> &items,const Opts &o){
            // column widths for -l
            size_t wLinks=1,wUser=1,wGroup=1,wSize=1,wIno=1;
            std::vector<std::string> users(items.size()),groups(items.size()),sizes(items.size());
            for(size_t k=0;k<items.size();++k){
                Item &it=items[k];
                if(!it.ok) continue;
                wIno=std::max(wIno,std::to_string((unsigned long long)it.st.st_ino).size());
                if(!o.longFormat) continue;
                struct passwd *pw=o.numericIds ? nullptr : getpwuid(it.st.st_uid);
                struct group *gr=o.numericIds ? nullptr : getgrgid(it.st.st_gid);
                users[k]=pw ? pw->pw_name : std::to_string(it.st.st_uid);
                groups[k]=gr ? gr->gr_name : std::to_string(it.st.st_gid);
                if(S_ISCHR(it.st.st_mode) || S_ISBLK(it.st.st_mode))
                    sizes[k]=std::to_string(major(it.st.st_rdev))+", "+std::to_string(minor(it.st.st_rdev));
                else
                    sizes[k]=o.human ? humanSize(it.st.st_size) : std::to_string((long long)it.st.st_size);
                wLinks=std::max(wLinks,std::to_string((unsigned long)it.st.st_nlink).size());
                wUser=std::max(wUser,users[k].size());
                wGroup=std::max(wGroup,groups[k].size());
                wSize=std::max(wSize,sizes[k].size());
            }
            time_t now=time(nullptr);
            for(size_t k=0;k<items.size();++k){
                Item &it=items[k];
                if(!it.ok) continue;
                std::string line;
                if(o.inode){
                    std::string ino=std::to_string((unsigned long long)it.st.st_ino);
                    line+=std::string(wIno-ino.size(),' ')+ino+" ";
                }
                if(o.longFormat){
                    std::string links=std::to_string((unsigned long)it.st.st_nlink);
                    char date[32];
                    struct tm tmv;
                    localtime_r(&it.st.st_mtime,&tmv);
                    // older than ~6 months or in the future: year instead of time
                    bool recent=it.st.st_mtime<=now && now-it.st.st_mtime<15552000;
                    strftime(date,sizeof date,recent ? "%b %e %H:%M" : "%b %e  %Y",&tmv);
                    line+=modeString(it.st.st_mode)+" "+std::string(wLinks-links.size(),' ')+links+" "+
                          users[k]+std::string(wUser-users[k].size(),' ')+" "+
                          groups[k]+std::string(wGroup-groups[k].size(),' ')+" "+
                          std::string(wSize-sizes[k].size(),' ')+sizes[k]+" "+date+" ";
                }
                line+=it.name;
                char c=(o.classify || o.slashDirs) ? classifyChar(it.st,o) : 0;
                if(c && !(o.longFormat && S_ISLNK(it.st.st_mode))) line+=c;
                if(o.longFormat && S_ISLNK(it.st.st_mode)){
                    char target[4096];
                    ssize_t n=readlink(it.path.c_str(),target,sizeof(target)-1);
                    if(n>=0){ target[n]='\0'; line+=" -> "; line+=target; }
                }
                std::cout << line << "\n";
            }
        }

        static void listOne(const std::string &path,bool showAll,bool longFormat){
            struct stat st;
            if(stat(path.c_str(),&st)!=0){
                std::cerr << "ls: " << path << ": " << strerror(errno) << std::endl;
                return;
        static void sortItems(std::vector<Item> &items,const Opts &o){
            if(o.noSort) return;
            std::sort(items.begin(),items.end(),[&](const Item &a,const Item &b){
                if(o.sortTime && a.st.st_mtime!=b.st.st_mtime) return a.st.st_mtime>b.st.st_mtime;
                if(o.sortSize && a.st.st_size!=b.st.st_size) return a.st.st_size>b.st.st_size;
                return a.name<b.name;
            });
            if(o.reverse) std::reverse(items.begin(),items.end());
        }

            if(!S_ISDIR(st.st_mode)){
                if(longFormat)
                    printLong("",path);
                else
                    std::cout << path << std::endl;
                return;
        bool statItem(Item &it,const Opts &o,bool commandLine){
            bool follow=o.follow || (commandLine && !o.longFormat && !o.dirItself);
            it.ok=(follow ? stat(it.path.c_str(),&it.st) : lstat(it.path.c_str(),&it.st))==0;
            if(!it.ok && follow) it.ok=lstat(it.path.c_str(),&it.st)==0;
            return it.ok;
        }

        // lists a directory's contents; returns false on error
        bool listDir(const std::string &path,const Opts &o,bool header,bool &first){
            DIR *dir=opendir(path.c_str());
            if(!dir){
                std::cerr << "ls: " << path << ": " << strerror(errno) << std::endl;
                return;
                std::cerr << "ls: cannot open directory '" << path << "': " << strerror(errno) << std::endl;
                return false;
            }

            std::vector<std::string> names;
            std::vector<Item> items;
            struct dirent *ent;
            while((ent=readdir(dir))!=NULL){
                std::string name=ent->d_name;
                if(!showAll && !name.empty() && name[0]=='.')
                    continue;
                names.push_back(name);
                if(!name.empty() && name[0]=='.'){
                    if(!o.all && !o.almostAll) continue;
                    if(o.almostAll && !o.all && (name=="." || name=="..")) continue;
                }
                Item it;
                it.name=name;
                it.path=path+(path.back()=='/' ? "" : "/")+name;
                statItem(it,o,false);
                items.push_back(it);
            }
            closedir(dir);
            sortItems(items,o);

            std::sort(names.begin(),names.end());
            if(header){
                if(!first) std::cout << "\n";
                std::cout << path << ":\n";
            }
            first=false;
            if(o.longFormat){
                long long blocks=0;
                for(auto &it: items) if(it.ok) blocks+=it.st.st_blocks;
                std::cout << "total " << blocks/2 << "\n";
            }
            printItems(items,o);

            for(size_t i=0; i<names.size(); ++i){
                if(longFormat)
                    printLong(path,names[i]);
                else
                    std::cout << names[i] << std::endl;
            bool ok=true;
            if(o.recursive){
                for(auto &it: items){
                    if(!it.ok || !S_ISDIR(it.st.st_mode) || it.name=="." || it.name=="..") continue;
                    if(!listDir(it.path,o,true,first)) ok=false;
                }
            }
            return ok;
        }

    public:
        int runCommand(int argc,const char **argv){
            optind=0;
            opterr=0;

            bool showAll=false;
            bool longFormat=false;
            int opt;
            while((opt=getopt(argc,const_cast<char *const *>(argv),"al"))!=-1){
                switch(opt){
                    case 'a':
                        showAll=true;
                        break;
                    case 'l':
                        longFormat=true;
                        break;
            Opts o;
            std::vector<std::string> operands;
            bool endOfOpts=false;
            for(int i=1; i<argc; ++i){
                std::string a=argv[i];
                if(endOfOpts || a.size()<2 || a[0]!='-'){ operands.push_back(a); continue; }
                if(a=="--"){ endOfOpts=true; continue; }
                if(a.compare(0,2,"--")==0){
                    if(a=="--all") o.all=true;
                    else if(a=="--almost-all") o.almostAll=true;
                    else if(a=="--inode") o.inode=true;
                    else if(a=="--directory") o.dirItself=true;
                    else if(a=="--recursive") o.recursive=true;
                    else if(a=="--reverse") o.reverse=true;
                    else if(a=="--human-readable") o.human=true;
                    else if(a=="--classify") o.classify=true;
                    else if(a=="--numeric-uid-gid"){ o.numericIds=true; o.longFormat=true; }
                    else if(a=="--dereference") o.follow=true;
                    else if(a.compare(0,7,"--color")==0 || a.compare(0,8,"--colour")==0 ||
                            a=="--group-directories-first") {}
                    else{
                        std::cerr << "ls: unrecognized option '" << a << "'" << std::endl;
                        return 2;
                    }
                    continue;
                }
                for(size_t k=1;k<a.size();++k){
                    switch(a[k]){
                        case 'a': o.all=true; break;
                        case 'A': o.almostAll=true; break;
                        case 'l': o.longFormat=true; break;
                        case 'n': o.numericIds=true; o.longFormat=true; break;
                        case 'i': o.inode=true; break;
                        case 'd': o.dirItself=true; break;
                        case 'R': o.recursive=true; break;
                        case 't': o.sortTime=true; break;
                        case 'S': o.sortSize=true; break;
                        case 'U': case 'f': o.noSort=true; if(a[k]=='f') o.all=true; break;
                        case 'r': o.reverse=true; break;
                        case 'h': o.human=true; break;
                        case 'F': o.classify=true; break;
                        case 'p': o.slashDirs=true; break;
                        case 'L': o.follow=true; break;
                        case '1': case 'C': case 'x': case 'm': break; // one entry per line
                        default:
                        std::cerr << "ls: invalid option" << std::endl;
                        return 1;
                            std::cerr << "ls: invalid option -- '" << a[k] << "'" << std::endl;
                            return 2;
                    }
                }

            if(optind>=argc){
                listOne(".",showAll,longFormat);
                return 0;
            }
            if(operands.empty()) operands.push_back(".");

            for(int i=optind; i<argc; ++i)
                listOne(argv[i],showAll,longFormat);

            return 0;
            int status=0;
            // files first, then directories (with headers if several)
            std::vector<Item> files;
            std::vector<std::string> dirs;
            for(auto &op: operands){
                Item it;
                it.name=op;
                it.path=op;
                if(!statItem(it,o,true)){
                    std::cerr << "ls: cannot access '" << op << "': " << strerror(errno) << std::endl;
                    status=2;
                    continue;
                }
                if(S_ISDIR(it.st.st_mode) && !o.dirItself) dirs.push_back(op);
                else files.push_back(it);
            }
            sortItems(files,o);
            printItems(files,o);
            bool first=files.empty();
            bool headers=(operands.size()>1) || o.recursive;
            if(!o.noSort) std::sort(dirs.begin(),dirs.end());
            if(o.reverse) std::reverse(dirs.begin(),dirs.end());
            for(auto &d: dirs){
                if(!listDir(d,o,headers,first)) status=status ? status : 1;
            }
            std::cout.flush();
            return status;
        }
    };

+43 −18
Changes for commands/filesystem/rm.cpp: 43 added lines, 18 removed lines.
Original line number Diff line number Diff line
@@ -26,6 +26,8 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*******************************************************************************/

#include <iostream>
#include <vector>
#include <string>
#include <string>
#include <cstring>
#include <cerrno>
@@ -87,28 +89,45 @@ namespace fresh { namespace cmd {
        }

        int runCommand(int argc,const char **argv){
            optind=0;
            opterr=0;

            bool recursive=false;
            bool force=false;
            int opt;
            while((opt=getopt(argc,const_cast<char *const *>(argv),"rRf"))!=-1){
                switch(opt){
                    case 'r':
                    case 'R':
                        recursive=true;
                        break;
                    case 'f':
                        force=true;
                        break;
            bool dirs=false;
            bool verbose=false;
            std::vector<const char*> operands;
            bool endOfOpts=false;
            // GNU-style: options anywhere before "--", long forms too
            for(int i=1; i<argc; ++i){
                std::string a=argv[i];
                if(endOfOpts || a.size()<2 || a[0]!='-'){ operands.push_back(argv[i]); continue; }
                if(a=="--"){ endOfOpts=true; continue; }
                if(a.compare(0,2,"--")==0){
                    if(a=="--force") force=true;
                    else if(a=="--recursive") recursive=true;
                    else if(a=="--dir") dirs=true;
                    else if(a=="--verbose") verbose=true;
                    else if(a=="--interactive=never" || a=="--no-preserve-root" || a=="--preserve-root" ||
                            a=="--one-file-system") {}
                    else{
                        std::cerr << "rm: unrecognized option '" << a << "'" << std::endl;
                        return 1;
                    }
                    continue;
                }
                for(size_t k=1;k<a.size();++k){
                    switch(a[k]){
                        case 'r': case 'R': recursive=true; break;
                        case 'f': force=true; break;
                        case 'd': dirs=true; break;
                        case 'v': verbose=true; break;
                        case 'i': case 'I': force=false; break;
                        default:
                        std::cerr << "rm: invalid option" << std::endl;
                            std::cerr << "rm: invalid option -- '" << a[k] << "'" << std::endl;
                            return 1;
                    }
                }
            }

            if(optind>=argc){
            if(operands.empty()){
                if(force)
                    return 0;
                std::cerr << "rm: missing operand" << std::endl;
@@ -117,8 +136,7 @@ namespace fresh { namespace cmd {

            int failed=0;

            for(int i=optind; i<argc; ++i){
                const char *path=argv[i];
            for(const char *path: operands){

                struct stat st;
                if(lstat(path,&st)!=0){
@@ -130,13 +148,20 @@ namespace fresh { namespace cmd {
                }

                if(S_ISDIR(st.st_mode) && !recursive){
                    std::cerr << "rm: " << path << ": Is a directory" << std::endl;
                    // -d: an empty directory may go without -r
                    if(dirs && rmdir(path)==0){
                        if(verbose) std::cout << "removed directory '" << path << "'" << std::endl;
                        continue;
                    }
                    std::cerr << "rm: " << path << ": " << (dirs ? strerror(errno) : "Is a directory") << std::endl;
                    failed=1;
                    continue;
                }

                if(removeRecursive(path,force)!=0)
                    failed=1;
                else if(verbose)
                    std::cout << "removed '" << path << "'" << std::endl;
            }

            return failed;
+340 −119

File changed.

Preview size limit exceeded, changes collapsed.