Produce an octal escape code for \177
[libs/core.git] / examples / ls.cpp
1 #include <msp/core/algorithm.h>
2 #include <msp/core/application.h>
3 #include <msp/core/getopt.h>
4 #include <msp/fs/dir.h>
5 #include <msp/fs/stat.h>
6 #include <msp/fs/utils.h>
7 #include <msp/io/print.h>
8 #include <msp/time/datetime.h>
9
10 using namespace std;
11 using namespace Msp;
12
13 class Ls: public RegisteredApplication<Ls>
14 {
15 private:
16         typedef vector<string> Row;
17
18         bool long_format;
19         vector<string> args;
20
21 public:
22         Ls(int, char **);
23
24         virtual int main();
25 private:
26         Row create_row(const string &, const FS::Stat &);
27         void print_rows(const vector<Row> &);
28 };
29
30 Ls::Ls(int argc, char **argv):
31         long_format(false)
32 {
33         GetOpt getopt;
34         getopt.add_option('l', "long", long_format, GetOpt::NO_ARG);
35         getopt.add_argument("file", args, GetOpt::OPTIONAL_ARG);
36         getopt(argc, argv);
37
38         if(args.empty())
39                 args.push_back(".");
40 }
41
42 int Ls::main()
43 {
44         vector<Row> file_rows;
45         bool first = true;
46         for(const string &a: args)
47         {
48                 FS::Path path = a;
49                 FS::Stat stat = FS::lstat(path);
50                 if(stat.is_directory())
51                 {
52                         vector<Row> rows;
53                         vector<string> files = FS::list_files(path);
54                         sort(files);
55                         for(const string &fn: files)
56                         {
57                                 if(long_format)
58                                         rows.push_back(create_row(fn, FS::lstat(path/fn)));
59                                 else
60                                 {
61                                         Row r;
62                                         r.push_back(fn);
63                                         rows.push_back(r);
64                                 }
65                         }
66
67                         if(!first)
68                                 IO::print("\n");
69                         if(args.size()>1)
70                                 IO::print("%s:\n", a);
71                         print_rows(rows);
72
73                         first = false;
74                 }
75                 else if(long_format)
76                         file_rows.push_back(create_row(a, stat));
77                 else
78                 {
79                         Row r;
80                         r.push_back(a);
81                         file_rows.push_back(r);
82                 }
83         }
84
85         if(!file_rows.empty())
86         {
87                 if(!first)
88                         IO::print("\n");
89                 print_rows(file_rows);
90         }
91
92         return 0;
93 }
94
95 Ls::Row Ls::create_row(const string &name, const FS::Stat &st)
96 {
97         Row result;
98         if(st.is_regular())
99                 result.push_back("-");
100         else if(st.is_directory())
101                 result.push_back("D");
102         else if(st.is_symlink())
103                 result.push_back("L");
104         else
105                 result.push_back("?");
106
107         result.push_back(st.get_owner());
108         result.push_back(st.get_group());
109         result.push_back(format("%d", st.get_size()));
110         result.push_back(Time::DateTime(st.get_modify_time()).format("%Y-%m-%d %H:%M:%S"));
111         result.push_back(name);
112
113         return result;
114 }
115
116 void Ls::print_rows(const vector<Row> &rows)
117 {
118         vector<unsigned> col_width;
119
120         for(const Row &row: rows)
121         {
122                 if(row.size()>col_width.size())
123                         col_width.resize(row.size(), 0);
124                 for(unsigned j=0; j<row.size(); ++j)
125                         col_width[j] = max<unsigned>(col_width[j], row[j].size());
126         }
127
128         for(const Row &row: rows)
129         {
130                 string line;
131                 for(unsigned j=0; j<row.size(); ++j)
132                 {
133                         if(j>0)
134                                 line += ' ';
135                         unsigned padding = col_width[j]-row[j].size();
136                         if(j==3)
137                                 line += string(padding, ' ');
138                         line += row[j];
139                         if(j!=3)
140                                 line += string(padding, ' ');
141                 }
142                 line += '\n';
143                 IO::print(line);
144         }
145 }