--- /dev/null
+#include <msp/core/application.h>
+#include <msp/core/getopt.h>
+#include <msp/fs/dir.h>
+#include <msp/fs/stat.h>
+#include <msp/fs/utils.h>
+#include <msp/io/print.h>
+#include <msp/time/datetime.h>
+
+using namespace std;
+using namespace Msp;
+
+class Ls: public RegisteredApplication<Ls>
+{
+private:
+ typedef vector<string> Row;
+
+ bool long_format;
+ vector<string> args;
+
+public:
+ Ls(int, char **);
+
+ virtual int main();
+private:
+ Row create_row(const string &, const FS::Stat &);
+ void print_rows(const list<Row> &);
+};
+
+Ls::Ls(int argc, char **argv):
+ long_format(false)
+{
+ GetOpt getopt;
+ getopt.add_option('l', "long", long_format, GetOpt::NO_ARG);
+ getopt(argc, argv);
+
+ args = getopt.get_args();
+ if(args.empty())
+ args.push_back(".");
+}
+
+int Ls::main()
+{
+ list<Row> file_rows;
+ bool first = true;
+ for(vector<string>::const_iterator i=args.begin(); i!=args.end(); ++i)
+ {
+ FS::Path path = *i;
+ FS::Stat stat = FS::lstat(path);
+ if(stat.is_directory())
+ {
+ list<Row> rows;
+ list<string> files = FS::list_files(path);
+ files.sort();
+ for(list<string>::iterator j=files.begin(); j!=files.end(); ++j)
+ {
+ if(long_format)
+ rows.push_back(create_row(*j, FS::lstat(path / *j)));
+ else
+ {
+ Row r;
+ r.push_back(*j);
+ rows.push_back(r);
+ }
+ }
+
+ if(!first)
+ IO::print("\n");
+ if(args.size()>1)
+ IO::print("%s:\n", *i);
+ print_rows(rows);
+
+ first = false;
+ }
+ else if(long_format)
+ file_rows.push_back(create_row(*i, stat));
+ else
+ {
+ Row r;
+ r.push_back(*i);
+ file_rows.push_back(r);
+ }
+ }
+
+ if(!file_rows.empty())
+ {
+ if(!first)
+ IO::print("\n");
+ print_rows(file_rows);
+ }
+
+ return 0;
+}
+
+Ls::Row Ls::create_row(const string &name, const FS::Stat &st)
+{
+ Row result;
+ if(st.is_regular())
+ result.push_back("-");
+ else if(st.is_directory())
+ result.push_back("D");
+ else if(st.is_symlink())
+ result.push_back("L");
+ else
+ result.push_back("?");
+
+ result.push_back(st.get_owner());
+ result.push_back(st.get_group());
+ result.push_back(format("%d", st.get_size()));
+ result.push_back(Time::DateTime(st.get_modify_time()).format("%Y-%m-%d %H:%M:%S"));
+ result.push_back(name);
+
+ return result;
+}
+
+void Ls::print_rows(const list<Row> &rows)
+{
+ vector<unsigned> col_width;
+
+ for(list<Row>::const_iterator i=rows.begin(); i!=rows.end(); ++i)
+ {
+ const Row &row = *i;
+ if(row.size()>col_width.size())
+ col_width.resize(row.size(), 0);
+ for(unsigned j=0; j<row.size(); ++j)
+ col_width[j] = max(col_width[j], row[j].size());
+ }
+
+ for(list<Row>::const_iterator i=rows.begin(); i!=rows.end(); ++i)
+ {
+ const Row &row = *i;
+ string line;
+ for(unsigned j=0; j<row.size(); ++j)
+ {
+ if(j>0)
+ line += ' ';
+ unsigned padding = col_width[j]-row[j].size();
+ if(j==3)
+ line += string(padding, ' ');
+ line += row[j];
+ if(j!=3)
+ line += string(padding, ' ');
+ }
+ line += '\n';
+ IO::print(line);
+ }
+}