blob: 6973433ff8eedd934da2a7d16fdf3f9cc95d8305 [file] [log] [blame]
Alexandre Lisionddd731e2014-01-31 11:50:08 -05001// Copyright (C) 2010 David Sugar, Tycho Softworks.
2//
3// This file is part of GNU uCommon C++.
4//
5// GNU uCommon C++ is free software: you can redistribute it and/or modify
6// it under the terms of the GNU Lesser General Public License as published
7// by the Free Software Foundation, either version 3 of the License, or
8// (at your option) any later version.
9//
10// GNU uCommon C++ is distributed in the hope that it will be useful,
11// but WITHOUT ANY WARRANTY; without even the implied warranty of
12// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13// GNU Lesser General Public License for more details.
14//
15// You should have received a copy of the GNU Lesser General Public License
16// along with GNU uCommon C++. If not, see <http://www.gnu.org/licenses/>.
17
18#include <ucommon/secure.h>
19#include <sys/stat.h>
20
21using namespace UCOMMON_NAMESPACE;
22
23static shell::flagopt helpflag('h',"--help", _TEXT("display this list"));
24static shell::flagopt althelp('?', NULL, NULL);
25static shell::stringopt hash('d', "--digest", _TEXT("digest method (md5)"), "method", "md5");
26static shell::flagopt recursive('R', "--recursive", _TEXT("recursive directory scan"));
27static shell::flagopt altrecursive('r', NULL, NULL);
28static shell::flagopt hidden('s', "--hidden", _TEXT("show hidden files"));
29
30static int exit_code = 0;
31static const char *argv0 = "md";
32static digest_t md;
33
34static void result(const char *path, int code)
35{
36 const char *err = _TEXT("i/o error");
37
38 switch(code) {
39 case EACCES:
40 case EPERM:
41 err = _TEXT("permission denied");
42 break;
43 case EROFS:
44 err = _TEXT("read-only file system");
45 break;
46 case ENODEV:
47 case ENOENT:
48 err = _TEXT("no such file or directory");
49 break;
50 case ENOTDIR:
51 err = _TEXT("not a directory");
52 break;
53 case ENOTEMPTY:
54 err = _TEXT("directory not empty");
55 break;
56 case ENOSPC:
57 err = _TEXT("no space left on device");
58 break;
59 case EBADF:
60 case ENAMETOOLONG:
61 err = _TEXT("bad file path");
62 break;
63 case EBUSY:
64 case EINPROGRESS:
65 err = _TEXT("file or directory busy");
66 break;
67 case EINTR:
68 err = _TEXT("operation interupted");
69 break;
70 case EISDIR:
71 err = _TEXT("is a directory");
72 break;
73#ifdef ELOOP
74 case ELOOP:
75 err = _TEXT("too many sym links");
76 break;
77#endif
78 }
79
80 if(!code) {
81 if(!path)
82 path="-";
83 shell::printf("%s %s\n", *md, path);
84 return;
85 }
86
87 if(path)
88 shell::printf("%s: %s: %s\n", argv0, path, err);
89 else
90 shell::errexit(1, "*** %s: %s\n", argv0, err);
91
92 exit_code = 1;
93}
94
95static void digest(const char *path = NULL)
96{
97 fsys_t fs;
98 fsys::fileinfo_t ino;
99 unsigned char buffer[1024];
100
101 if(path) {
102 int err = fsys::info(path, &ino);
103
104 if(err) {
105 result(path, err);
106 return;
107 }
108
109 if(fsys::is_sys(&ino)) {
110 result(path, EBADF);
111 return;
112 }
113
114 fs.open(path, fsys::STREAM);
115 }
116 else
117 fs.assign(shell::input());
118
119 if(!is(fs)) {
120 result(path, fs.err());
121 return;
122 }
123
124 for(;;) {
125 ssize_t size = fs.read(buffer, sizeof(buffer));
126 if(size < 1)
127 break;
128 md.put(buffer, size);
129 }
130
131 fs.close();
132 result(path, fs.err());
133 md.reset();
134}
135
136static void scan(String path, bool top = true)
137{
138 char filename[128];
139 string_t filepath;
140 dir_t dir(path);
141
142 while(is(dir) && dir.read(filename, sizeof(filename))) {
143 if(*filename == '.' && (filename[1] == '.' || !filename[1]))
144 continue;
145
146 if(*filename == '.' && !is(hidden))
147 continue;
148
149 filepath = str(path) + str("/") + str(filename);
150 if(fsys::is_dir(filepath)) {
151 if(is(recursive) || is(altrecursive))
152 scan(filepath, false);
153 else
154 result(filepath, EISDIR);
155 }
156 else
157 digest(filepath);
158 }
159}
160
161PROGRAM_MAIN(argc, argv)
162{
163 shell::bind("mdsum");
164 shell args(argc, argv);
165 argv0 = args.argv0();
166 unsigned count = 0;
167
168 argv0 = args.argv0();
169
170 if(is(helpflag) || is(althelp)) {
171 printf("%s\n", _TEXT("Usage: mdsum [options] path..."));
172 printf("%s\n\n", _TEXT("Compute digests for files"));
173 printf("%s\n", _TEXT("Options:"));
174 shell::help();
175 printf("\n%s\n", _TEXT("Report bugs to dyfet@gnu.org"));
176 PROGRAM_EXIT(0);
177 }
178
179 secure::init();
180 if(!Digest::has(*hash))
181 shell::errexit(2, "*** %s: %s: %s\n",
182 argv0, *hash, _TEXT("unkown or unsupported digest method"));
183
184 md = *hash;
185
186 // we can symlink md as md5, etc, to set alternate default digest names
187 if(!is(hash) && Digest::has(argv0))
188 md = argv0;
189
190 if(!args())
191 digest();
192 else while(count < args()) {
193 if(fsys::is_dir(args[count]))
194 scan(str(args[count++]));
195 else
196 digest(args[count++]);
197 }
198
199 PROGRAM_EXIT(exit_code);
200}
201