Home | History | Annotate | Line # | Download | only in cvslatest
cvslatest.c revision 1.2
      1 /*	$NetBSD: cvslatest.c,v 1.2 2016/01/24 20:14:44 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 HAVE_NBTOOL_CONFIG_H
     33 #include "nbtool_config.h"
     34 #endif
     35 
     36 #include <sys/cdefs.h>
     37 __RCSID("$NetBSD: cvslatest.c,v 1.2 2016/01/24 20:14:44 christos Exp $");
     38 
     39 /*
     40  * Find the latest timestamp in a set of CVS trees, by examining the
     41  * Entries files
     42  */
     43 
     44 #include <sys/param.h>
     45 #include <sys/types.h>
     46 
     47 #include <stdio.h>
     48 #include <string.h>
     49 #include <stdlib.h>
     50 #include <unistd.h>
     51 #include <err.h>
     52 #include <fts.h>
     53 #include <time.h>
     54 
     55 static int debug = 0;
     56 static int ignore = 0;
     57 
     58 struct latest {
     59 	time_t time;
     60 	char path[MAXPATHLEN];
     61 };
     62 
     63 static void
     64 printlat(const struct latest *lat)
     65 {
     66 	fprintf(stderr, "%s %s", lat->path, ctime(&lat->time));
     67 }
     68 
     69 static void
     70 getrepo(const char *path, char *repo, size_t maxrepo)
     71 {
     72 	FILE *fp;
     73 	char name[MAXPATHLEN];
     74 	char *ptr;
     75 
     76 	snprintf(name, sizeof(name), "%s/Repository", path);
     77 	if ((fp = fopen(name, "r")) == NULL)
     78 		err(EXIT_FAILURE, "Can't open `%s'", name);
     79 	if (fgets(repo, (int)maxrepo, fp) == NULL)
     80 		err(EXIT_FAILURE, "Can't read `%s'", name);
     81 	if ((ptr = strchr(repo, '\n')) == NULL)
     82 		errx(EXIT_FAILURE, "Malformed line in `%s'", name);
     83 	*ptr = '\0';
     84 	fclose(fp);
     85 }
     86 
     87 static void
     88 getlatest(const char *path, const char *repo, struct latest *lat)
     89 {
     90 	static const char fmt[] = "%a %b %d %H:%M:%S %Y";
     91 	char name[MAXPATHLEN];
     92 	char entry[MAXPATHLEN * 2];
     93 	char *fn, *dt, *p;
     94 	time_t t;
     95 	struct tm tm;
     96 	FILE *fp;
     97 
     98 	snprintf(name, sizeof(name), "%s/Entries", path);
     99 	if ((fp = fopen(name, "r")) == NULL)
    100 		err(EXIT_FAILURE, "Can't open `%s'", name);
    101 
    102 	while (fgets(entry, (int)sizeof(entry), fp) != NULL) {
    103 		if (entry[0] != '/')
    104 		    continue;
    105 		if ((fn = strtok(entry, "/")) == NULL)
    106 			goto mal;
    107 		if (strtok(NULL, "/") == NULL)
    108 			goto mal;
    109 		if ((dt = strtok(NULL, "/")) == NULL)
    110 			goto mal;
    111 		if ((p = strptime(dt, fmt, &tm)) == NULL || *p) {
    112 			warnx("Malformed time `%s' in `%s'", dt, name);
    113 			if (!ignore)
    114 				exit(EXIT_FAILURE);
    115 		}
    116 		if ((t = mktime(&tm)) == (time_t)-1)
    117 			errx(EXIT_FAILURE, "Time conversion `%s' in `%s'",
    118 			    dt, name);
    119 		if (lat->time == 0 || lat->time < t) {
    120 			lat->time = t;
    121 			snprintf(lat->path, sizeof(lat->path),
    122 			    "%s/%s", repo, fn);
    123 			if (debug)
    124 				printlat(lat);
    125 		}
    126 	}
    127 
    128 	fclose(fp);
    129 	return;
    130 
    131 mal:
    132 	errx(EXIT_FAILURE, "Malformed line in `%s'", name);
    133 }
    134 
    135 static void
    136 cvsscan(char **pathv, const char *name, struct latest *lat)
    137 {
    138         FTS *dh;
    139 	char repo[MAXPATHLEN];
    140         FTSENT *entry;
    141 
    142 	lat->time = 0;
    143 
    144         dh = fts_open(pathv, FTS_PHYSICAL, NULL);
    145         if (dh == NULL)
    146 		err(EXIT_FAILURE, "fts_open `%s'", pathv[0]);
    147 
    148         while ((entry = fts_read(dh)) != NULL) {
    149                 if (entry->fts_info != FTS_D)
    150 			continue;
    151 
    152 		if (strcmp(entry->fts_name, name) != 0)
    153                         continue;
    154 
    155 		getrepo(entry->fts_path, repo, sizeof(repo));
    156 		getlatest(entry->fts_path, repo, lat);
    157         }
    158 
    159         (void)fts_close(dh);
    160 }
    161 
    162 static __dead void
    163 usage(void)
    164 {
    165 	fprintf(stderr, "Usage: %s [-di] [-n <name>] <path> ...\n",
    166 	    getprogname());
    167 	exit(EXIT_FAILURE);
    168 }
    169 
    170 int
    171 main(int argc, char *argv[])
    172 {
    173 	struct latest lat;
    174 	const char *name = "CVS";
    175 	int c;
    176 
    177 	while ((c = getopt(argc, argv, "din:")) != -1)
    178 		switch (c) {
    179 		case 'i':
    180 			ignore++;
    181 			break;
    182 		case 'd':
    183 			debug++;
    184 			break;
    185 		case 'n':
    186 			name = optarg;
    187 			break;
    188 		default:
    189 			usage();
    190 		}
    191 
    192 	if (argc == optind)
    193 		usage();
    194 
    195 	cvsscan(argv + optind, name, &lat);
    196 	if (debug)
    197 		printlat(&lat);
    198 	printf("%jd\n", (intmax_t)lat.time);
    199 	return 0;
    200 }
    201