| @@ -1,14 +1,14 @@ | | | @@ -1,14 +1,14 @@ |
1 | /* $NetBSD: cvslatest.c,v 1.6 2017/09/24 09:43:27 joerg Exp $ */ | | 1 | /* $NetBSD: cvslatest.c,v 1.7 2018/03/11 14:59:41 christos Exp $ */ |
2 | | | 2 | |
3 | /*- | | 3 | /*- |
4 | * Copyright (c) 2016 The NetBSD Foundation, Inc. | | 4 | * Copyright (c) 2016 The NetBSD Foundation, Inc. |
5 | * All rights reserved. | | 5 | * All rights reserved. |
6 | * | | 6 | * |
7 | * This code is derived from software contributed to The NetBSD Foundation | | 7 | * This code is derived from software contributed to The NetBSD Foundation |
8 | * by Christos Zoulas. | | 8 | * by Christos Zoulas. |
9 | * | | 9 | * |
10 | * Redistribution and use in source and binary forms, with or without | | 10 | * Redistribution and use in source and binary forms, with or without |
11 | * modification, are permitted provided that the following conditions | | 11 | * modification, are permitted provided that the following conditions |
12 | * are met: | | 12 | * are met: |
13 | * 1. Redistributions of source code must retain the above copyright | | 13 | * 1. Redistributions of source code must retain the above copyright |
14 | * notice, this list of conditions and the following disclaimer. | | 14 | * notice, this list of conditions and the following disclaimer. |
| @@ -28,40 +28,41 @@ | | | @@ -28,40 +28,41 @@ |
28 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | | 28 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
29 | * POSSIBILITY OF SUCH DAMAGE. | | 29 | * POSSIBILITY OF SUCH DAMAGE. |
30 | */ | | 30 | */ |
31 | | | 31 | |
32 | #ifdef __linux__ | | 32 | #ifdef __linux__ |
33 | #define _GNU_SOURCE | | 33 | #define _GNU_SOURCE |
34 | #endif | | 34 | #endif |
35 | | | 35 | |
36 | #ifdef HAVE_NBTOOL_CONFIG_H | | 36 | #ifdef HAVE_NBTOOL_CONFIG_H |
37 | #include "nbtool_config.h" | | 37 | #include "nbtool_config.h" |
38 | #endif | | 38 | #endif |
39 | | | 39 | |
40 | #include <sys/cdefs.h> | | 40 | #include <sys/cdefs.h> |
41 | __RCSID("$NetBSD: cvslatest.c,v 1.6 2017/09/24 09:43:27 joerg Exp $"); | | 41 | __RCSID("$NetBSD: cvslatest.c,v 1.7 2018/03/11 14:59:41 christos Exp $"); |
42 | | | 42 | |
43 | /* | | 43 | /* |
44 | * Find the latest timestamp in a set of CVS trees, by examining the | | 44 | * Find the latest timestamp in a set of CVS trees, by examining the |
45 | * Entries files | | 45 | * Entries files |
46 | */ | | 46 | */ |
47 | | | 47 | |
48 | #include <sys/param.h> | | 48 | #include <sys/param.h> |
49 | #include <sys/types.h> | | 49 | #include <sys/types.h> |
50 | | | 50 | |
51 | #include <stdio.h> | | 51 | #include <stdio.h> |
52 | #include <string.h> | | 52 | #include <string.h> |
53 | #include <stdlib.h> | | 53 | #include <stdlib.h> |
54 | #include <unistd.h> | | 54 | #include <unistd.h> |
| | | 55 | #include <dirent.h> |
55 | #include <err.h> | | 56 | #include <err.h> |
56 | #include <fts.h> | | 57 | #include <fts.h> |
57 | #include <time.h> | | 58 | #include <time.h> |
58 | | | 59 | |
59 | static int debug = 0; | | 60 | static int debug = 0; |
60 | static int ignore = 0; | | 61 | static int ignore = 0; |
61 | | | 62 | |
62 | struct latest { | | 63 | struct latest { |
63 | time_t time; | | 64 | time_t time; |
64 | char path[MAXPATHLEN]; | | 65 | char path[MAXPATHLEN]; |
65 | }; | | 66 | }; |
66 | | | 67 | |
67 | static void | | 68 | static void |
| @@ -164,47 +165,94 @@ cvsscan(char **pathv, const char *name, | | | @@ -164,47 +165,94 @@ cvsscan(char **pathv, const char *name, |
164 | } | | 165 | } |
165 | | | 166 | |
166 | (void)fts_close(dh); | | 167 | (void)fts_close(dh); |
167 | } | | 168 | } |
168 | | | 169 | |
169 | static __dead void | | 170 | static __dead void |
170 | usage(void) | | 171 | usage(void) |
171 | { | | 172 | { |
172 | fprintf(stderr, "Usage: %s [-di] [-n <name>] <path> ...\n", | | 173 | fprintf(stderr, "Usage: %s [-di] [-n <name>] <path> ...\n", |
173 | getprogname()); | | 174 | getprogname()); |
174 | exit(EXIT_FAILURE); | | 175 | exit(EXIT_FAILURE); |
175 | } | | 176 | } |
176 | | | 177 | |
| | | 178 | static int |
| | | 179 | checkDir(char *path, size_t pathlen, const char *name) |
| | | 180 | { |
| | | 181 | static const char *files[] = { |
| | | 182 | "Entries", "Root", "Repository", |
| | | 183 | }; |
| | | 184 | size_t i; |
| | | 185 | |
| | | 186 | for (i = 0; i < __arraycount(files); i++) { |
| | | 187 | snprintf(path, pathlen, "%s/%s", name, files[i]); |
| | | 188 | if (access(path, F_OK) == -1) |
| | | 189 | return 0; |
| | | 190 | } |
| | | 191 | |
| | | 192 | return 1; |
| | | 193 | } |
| | | 194 | |
| | | 195 | static const char * |
| | | 196 | findCVSDir(char *path, size_t pathlen, const char *name) |
| | | 197 | { |
| | | 198 | DIR *dirp; |
| | | 199 | struct dirent *dp; |
| | | 200 | const char *n; |
| | | 201 | |
| | | 202 | if ((dirp = opendir(name)) == NULL) |
| | | 203 | err(EXIT_FAILURE, "Can't open `%s'", name); |
| | | 204 | |
| | | 205 | while ((dp = readdir(dirp)) != NULL) { |
| | | 206 | n = dp->d_name; |
| | | 207 | if (n[0] == '.' && (n[1] == '\0' || |
| | | 208 | (n[1] == '.' && n[2] == '\0'))) |
| | | 209 | continue; |
| | | 210 | if (checkDir(path, pathlen, n)) |
| | | 211 | goto out; |
| | | 212 | } |
| | | 213 | n = "CVS"; |
| | | 214 | out: |
| | | 215 | closedir(dirp); |
| | | 216 | strlcpy(path, n, pathlen); |
| | | 217 | return path; |
| | | 218 | } |
| | | 219 | |
| | | 220 | |
177 | int | | 221 | int |
178 | main(int argc, char *argv[]) | | 222 | main(int argc, char *argv[]) |
179 | { | | 223 | { |
180 | struct latest lat; | | 224 | struct latest lat; |
181 | const char *name = "CVS"; | | 225 | const char *name = NULL; |
| | | 226 | char path[MAXPATHLEN]; |
182 | int c; | | 227 | int c; |
183 | | | 228 | |
184 | while ((c = getopt(argc, argv, "din:")) != -1) | | 229 | while ((c = getopt(argc, argv, "din:")) != -1) |
185 | switch (c) { | | 230 | switch (c) { |
186 | case 'i': | | 231 | case 'i': |
187 | ignore++; | | 232 | ignore++; |
188 | break; | | 233 | break; |
189 | case 'd': | | 234 | case 'd': |
190 | debug++; | | 235 | debug++; |
191 | break; | | 236 | break; |
192 | case 'n': | | 237 | case 'n': |
193 | name = optarg; | | 238 | name = optarg; |
194 | break; | | 239 | break; |
195 | default: | | 240 | default: |
196 | usage(); | | 241 | usage(); |
197 | } | | 242 | } |
198 | | | 243 | |
199 | if (argc == optind) | | 244 | if (argc == optind) |
200 | usage(); | | 245 | usage(); |
201 | | | 246 | |
202 | // So that mktime behaves consistently | | 247 | // So that mktime behaves consistently |
203 | setenv("TZ", "UTC", 1); | | 248 | setenv("TZ", "UTC", 1); |
204 | | | 249 | |
| | | 250 | if (name == NULL) |
| | | 251 | name = findCVSDir(path, sizeof(path), argv[optind]); |
| | | 252 | |
205 | cvsscan(argv + optind, name, &lat); | | 253 | cvsscan(argv + optind, name, &lat); |
206 | if (debug) | | 254 | if (debug) |
207 | printlat(&lat); | | 255 | printlat(&lat); |
208 | printf("%jd\n", (intmax_t)lat.time); | | 256 | printf("%jd\n", (intmax_t)lat.time); |
209 | return 0; | | 257 | return 0; |
210 | } | | 258 | } |