forked from google/walk
-
Notifications
You must be signed in to change notification settings - Fork 0
/
walk.c
147 lines (134 loc) · 3.89 KB
/
walk.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
// Copyright 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <errno.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <dirent.h>
#include <getopt.h>
static const char SHORT_USAGE[] = "Usage: walk [OPTION...] [DIRECTORY...]\n";
static const char HELP[] =
"Recursively walk the specified directories (or current directory, if none is\n"
"specified.\n\n"
" -0, --null separate filenames by a null character\n"
" --help display this help and exit\n";
static const char ASK_FOR_HELP[] = "Try 'walk --help' for more information.\n";
static const char *const JUST_CURRENT_DIRECTORY[] = {".", NULL};
// Like readdir(3), but resets errno(3) before the call to make it easy to
// check.
static struct dirent *readdir2(DIR *const dirp)
{
errno = 0;
return readdir(dirp);
}
// Like realloc(3), but exits the binary in an out-of-memory situation.
static void *xrealloc(void *ptr, const size_t size)
{
if (!(ptr = realloc(ptr, size))) {
perror("realloc");
exit(EXIT_FAILURE);
}
return ptr;
}
static void strcpy3(char *dest, const char *s1, const char *s2, const char *s3)
{
stpcpy(stpcpy(stpcpy(dest, s1), s2), s3);
}
static void put_filename(const char *filename, bool null_terminate)
{
if (null_terminate) {
fputs(filename, stdout);
fputc(0, stdout);
} else {
puts(filename);
}
}
// Walks the directory named dirname, printing the names of all files it
// contains (but not the name of the directory itself). Returns 2 if dirname is
// not a directory and 1 if another error occurs.
static int walk(const char dirname[], bool null_terminate)
{
DIR *const dir = opendir(dirname);
if (!dir) {
if (errno != ENOTDIR) {
perror(dirname);
return 1;
}
return 2;
}
int r = 0;
char *filename = NULL;
for (const struct dirent *f = readdir2(dir); f; f = readdir2(dir)) {
if (strcmp(f->d_name, ".") == 0 || strcmp(f->d_name, "..") == 0)
continue;
filename = xrealloc(
filename, strlen(dirname) + 1 + strlen(f->d_name) + 1);
strcpy3(filename, dirname, "/", f->d_name);
// TODO(bbaren@google.com): Emulate Plan 9's cleanname(3).
put_filename(filename, null_terminate);
// Walk the file if we can successfully open it as a directory.
// Don't worry about it if it's not one (walk(filename) == 2).
if ((f->d_type == DT_DIR || f->d_type == DT_UNKNOWN)
&& walk(filename, null_terminate) == 1)
r = 1;
}
if (errno) { // from readdir
perror(dirname);
r = 1;
}
free(filename);
if (closedir(dir)) {
perror(dirname);
r = 1;
}
return r;
}
int main(const int argc, char *const argv[])
{
static const struct option long_options[] = {
{"help", no_argument, NULL, 'h'},
{"null", no_argument, NULL, '0'},
{NULL, 0, NULL, 0},
};
bool null_terminate = false;
while (true) {
const int c = getopt_long(argc, argv, "0", long_options, NULL);
if (c == -1) break;
switch (c) {
case 'h':
fputs(SHORT_USAGE, stdout);
fputs(HELP, stdout);
return 0;
case '0':
null_terminate = true;
break;
case '?':
fputs(ASK_FOR_HELP, stderr);
return 1;
default:
fputs("Internal error; please report.\n", stderr);
return 1;
}
}
int r = 0;
const char *const *const dirs = argc == optind
? JUST_CURRENT_DIRECTORY
: (const char *const *)argv + optind;
for (int i = 0; dirs[i]; ++i) {
put_filename(dirs[i], null_terminate);
r |= walk(dirs[i], null_terminate);
}
return r;
}