Serenity Operating System
1/*
2 * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
3 * Copyright (c) 2020, Shannon Booth <shannon.ml.booth@gmail.com>
4 *
5 * SPDX-License-Identifier: BSD-2-Clause
6 */
7
8#include <AK/DeprecatedString.h>
9#include <LibCore/ArgsParser.h>
10#include <LibCore/DateTime.h>
11#include <LibCore/System.h>
12#include <LibMain/Main.h>
13#include <grp.h>
14#include <pwd.h>
15#include <sys/sysmacros.h>
16#include <time.h>
17
18static ErrorOr<int> stat(StringView file, bool should_follow_links)
19{
20 auto st = TRY(should_follow_links ? Core::System::stat(file) : Core::System::lstat(file));
21 outln(" File: {}", file);
22 outln(" Inode: {}", st.st_ino);
23 if (S_ISCHR(st.st_mode) || S_ISBLK(st.st_mode))
24 outln(" Device: {},{}", major(st.st_rdev), minor(st.st_rdev));
25 else
26 outln(" Size: {}", st.st_size);
27 outln(" Links: {}", st.st_nlink);
28 outln(" Blocks: {}", st.st_blocks);
29 out(" UID: {}", st.st_uid);
30 if (auto* pwd = getpwuid(st.st_uid)) {
31 out(" ({})", pwd->pw_name);
32 }
33 outln("");
34 out(" GID: {}", st.st_gid);
35 if (auto* grp = getgrgid(st.st_gid)) {
36 out(" ({})", grp->gr_name);
37 }
38 outln("");
39 out(" Mode: ({:o}/", st.st_mode);
40
41 if (S_ISDIR(st.st_mode))
42 out("d");
43 else if (S_ISLNK(st.st_mode))
44 out("l");
45 else if (S_ISBLK(st.st_mode))
46 out("b");
47 else if (S_ISCHR(st.st_mode))
48 out("c");
49 else if (S_ISFIFO(st.st_mode))
50 out("f");
51 else if (S_ISSOCK(st.st_mode))
52 out("s");
53 else if (S_ISREG(st.st_mode))
54 out("-");
55 else
56 out("?");
57
58 out("{:c}{:c}{:c}{:c}{:c}{:c}{:c}{:c}",
59 st.st_mode & S_IRUSR ? 'r' : '-',
60 st.st_mode & S_IWUSR ? 'w' : '-',
61 st.st_mode & S_ISUID ? 's' : (st.st_mode & S_IXUSR ? 'x' : '-'),
62 st.st_mode & S_IRGRP ? 'r' : '-',
63 st.st_mode & S_IWGRP ? 'w' : '-',
64 st.st_mode & S_ISGID ? 's' : (st.st_mode & S_IXGRP ? 'x' : '-'),
65 st.st_mode & S_IROTH ? 'r' : '-',
66 st.st_mode & S_IWOTH ? 'w' : '-');
67
68 if (st.st_mode & S_ISVTX)
69 out("t");
70 else
71 out("{:c}", st.st_mode & S_IXOTH ? 'x' : '-');
72
73 outln(")");
74
75 auto print_time = [](timespec t) {
76 outln("{}.{:09}", Core::DateTime::from_timestamp(t.tv_sec).to_deprecated_string(), t.tv_nsec);
77 };
78
79 out("Accessed: ");
80 print_time(st.st_atim);
81 out("Modified: ");
82 print_time(st.st_mtim);
83 out(" Changed: ");
84 print_time(st.st_ctim);
85
86 return 0;
87}
88
89ErrorOr<int> serenity_main(Main::Arguments arguments)
90{
91 TRY(Core::System::pledge("stdio rpath"));
92
93 bool should_follow_links = false;
94 Vector<StringView> files;
95
96 auto args_parser = Core::ArgsParser();
97 args_parser.add_option(should_follow_links, "Follow links to files", nullptr, 'L');
98 args_parser.add_positional_argument(files, "File(s) to stat", "file", Core::ArgsParser::Required::Yes);
99 args_parser.parse(arguments);
100
101 bool had_error = false;
102 for (auto& file : files) {
103 auto r = stat(file, should_follow_links);
104 if (r.is_error()) {
105 had_error = true;
106 warnln("stat: cannot stat '{}': {}", file, strerror(r.error().code()));
107 }
108 }
109
110 return had_error;
111}