Home | History | Annotate | Line # | Download | only in cvslatest
cvslatest.c revision 1.8
      1 /*	$NetBSD: cvslatest.c,v 1.8 2019/03/09 16:18:22 christos Exp $	*/
      2 
      3 /*-
      4  * Copyright (c) 2016 The NetBSD Foundation, Inc.
      5  * All rights reserved.
      6  *
      7  * This code is derived from software contributed to The NetBSD Foundation
      8  * by Christos Zoulas.
      9  *
     10  * Redistribution and use in source and binary forms, with or without
     11  * modification, are permitted provided that the following conditions
     12  * are met:
     13  * 1. Redistributions of source code must retain the above copyright
     14  *    notice, this list of conditions and the following disclaimer.
     15  * 2. Redistributions in binary form must reproduce the above copyright
     16  *    notice, this list of conditions and the following disclaimer in the
     17  *    documentation and/or other materials provided with the distribution.
     18  *
     19  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
     20  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     21  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     22  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
     23  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     24  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     29  * POSSIBILITY OF SUCH DAMAGE.
     30  */
     31 
     32 #ifdef __linux__
     33 #define _GNU_SOURCE
     34 #endif
     35 
     36 #ifdef HAVE_NBTOOL_CONFIG_H
     37 #include "nbtool_config.h"
     38 #endif
     39 
     40 #include <sys/cdefs.h>
     41 __RCSID("$NetBSD: cvslatest.c,v 1.8 2019/03/09 16:18:22 christos Exp $");
     42 
     43 /*
     44  * Find the latest timestamp in a set of CVS trees, by examining the
     45  * Entries files
     46  */
     47 
     48 #include <sys/param.h>
     49 #include <sys/types.h>
     50 
     51 #include <stdio.h>
     52 #include <string.h>
     53 #include <stdlib.h>
     54 #include <unistd.h>
     55 #include <dirent.h>
     56 #include <err.h>
     57 #include <fts.h>
     58 #include <time.h>
     59 
     60 static int debug = 0;
     61 static int ignore = 0;
     62 
     63 struct latest {
     64 	time_t time;
     65 	char path[MAXPATHLEN];
     66 };
     67 
     68 static void
     69 printlat(const struct latest *lat)
     70 {
     71 	fprintf(stderr, "%s %s", lat->path, ctime(&lat->time));
     72 }
     73 
     74 static void
     75 getrepo(const FTSENT *e, char *repo, size_t maxrepo)
     76 {
     77 	FILE *fp;
     78 	char name[MAXPATHLEN], ename[MAXPATHLEN];
     79 	char *ptr;
     80 
     81 	snprintf(name, sizeof(name), "%s/Repository", e->fts_accpath);
     82 	snprintf(ename, sizeof(ename), "%s/Repository", e->fts_path);
     83 	if ((fp = fopen(name, "r")) == NULL)
     84 		err(EXIT_FAILURE, "Can't open `%s'", ename);
     85 	if (fgets(repo, (int)maxrepo, fp) == NULL)
     86 		err(EXIT_FAILURE, "Can't read `%s'", ename);
     87 	if ((ptr = strchr(repo, '\n')) == NULL)
     88 		errx(EXIT_FAILURE, "Malformed line in `%s'", ename);
     89 	*ptr = '\0';
     90 	fclose(fp);
     91 }
     92 
     93 static void
     94 getlatest(const FTSENT *e, const char *repo, struct latest *lat)
     95 {
     96 	static const char fmt[] = "%a %b %d %H:%M:%S %Y";
     97 	char name[MAXPATHLEN], ename[MAXPATHLEN];
     98 	char entry[MAXPATHLEN * 2];
     99 	char *fn, *dt, *p;
    100 	time_t t;
    101 	struct tm tm;
    102 	FILE *fp;
    103 
    104 	snprintf(name, sizeof(name), "%s/Entries", e->fts_accpath);
    105 	snprintf(ename, sizeof(ename), "%s/Entries", e->fts_path);
    106 	if ((fp = fopen(name, "r")) == NULL)
    107 		err(EXIT_FAILURE, "Can't open `%s'", ename);
    108 
    109 	while (fgets(entry, (int)sizeof(entry), fp) != NULL) {
    110 		if (entry[0] != '/')
    111 		    continue;
    112 		if ((fn = strtok(entry, "/")) == NULL)
    113 			goto mal;
    114 		if (strtok(NULL, "/") == NULL)
    115 			goto mal;
    116 		if ((dt = strtok(NULL, "/")) == NULL)
    117 			goto mal;
    118 		if ((p = strptime(dt, fmt, &tm)) == NULL || *p) {
    119 			warnx("Malformed time `%s' in `%s'", dt, ename);
    120 			if (!ignore)
    121 				exit(EXIT_FAILURE);
    122 		}
    123 		tm.tm_isdst = 0;	// We are in GMT anyway
    124 		if ((t = mktime(&tm)) == (time_t)-1)
    125 			errx(EXIT_FAILURE, "Time conversion `%s' in `%s'",
    126 			    dt, ename);
    127 		if (lat->time == 0 || lat->time < t) {
    128 			lat->time = t;
    129 			snprintf(lat->path, sizeof(lat->path),
    130 			    "%s/%s", repo, fn);
    131 			if (debug > 1)
    132 				printlat(lat);
    133 		}
    134 	}
    135 
    136 	fclose(fp);
    137 	return;
    138 
    139 mal:
    140 	errx(EXIT_FAILURE, "Malformed line in `%s'", ename);
    141 }
    142 
    143 static void
    144 cvsscan(char **pathv, const char *name, struct latest *lat)
    145 {
    146         FTS *dh;
    147 	char repo[MAXPATHLEN];
    148         FTSENT *entry;
    149 
    150 	lat->time = 0;
    151 
    152         dh = fts_open(pathv, FTS_PHYSICAL, NULL);
    153         if (dh == NULL)
    154 		err(EXIT_FAILURE, "fts_open `%s'", pathv[0]);
    155 
    156         while ((entry = fts_read(dh)) != NULL) {
    157                 if (entry->fts_info != FTS_D)
    158 			continue;
    159 
    160 		if (strcmp(entry->fts_name, name) != 0)
    161                         continue;
    162 
    163 		getrepo(entry, repo, sizeof(repo));
    164 		getlatest(entry, repo, lat);
    165         }
    166 
    167         (void)fts_close(dh);
    168 }
    169 
    170 static __dead void
    171 usage(void)
    172 {
    173 	fprintf(stderr, "Usage: %s [-di] [-n <name>] <path> ...\n",
    174 	    getprogname());
    175 	exit(EXIT_FAILURE);
    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 	strlcpy(path, n, pathlen);
    216 	closedir(dirp);
    217 	return path;
    218 }
    219 
    220 
    221 int
    222 main(int argc, char *argv[])
    223 {
    224 	struct latest lat;
    225 	const char *name = NULL;
    226 	char path[MAXPATHLEN];
    227 	int c;
    228 
    229 	while ((c = getopt(argc, argv, "din:")) != -1)
    230 		switch (c) {
    231 		case 'i':
    232 			ignore++;
    233 			break;
    234 		case 'd':
    235 			debug++;
    236 			break;
    237 		case 'n':
    238 			name = optarg;
    239 			break;
    240 		default:
    241 			usage();
    242 		}
    243 
    244 	if (argc == optind)
    245 		usage();
    246 
    247 	// So that mktime behaves consistently
    248 	setenv("TZ", "UTC", 1);
    249 
    250 	if (name == NULL)
    251 		name = findCVSDir(path, sizeof(path), argv[optind]);
    252 
    253 	cvsscan(argv + optind, name, &lat);
    254 	if (debug)
    255 		printlat(&lat);
    256 	printf("%jd\n", (intmax_t)lat.time);
    257 	return 0;
    258 }
    259