main.c revision 173295
1/*-
2 * ----------------------------------------------------------------------------
3 * "THE BEER-WARE LICENSE" (Revision 42):
4 * <beat@chruetertee.ch> wrote this file. As long as you retain this notice you
5 * can do whatever you want with this stuff. If we meet some day, and you think
6 * this stuff is worth it, you can buy me a beer in return.          Beat G�tzi
7 * ----------------------------------------------------------------------------
8 */
9
10#include <sys/cdefs.h>
11__FBSDID("$FreeBSD: head/usr.sbin/pkg_install/updating/main.c 173295 2007-11-02 22:46:30Z krion $");
12
13#include <sys/stat.h>
14#include <sys/param.h>						/* For MAXPATHLEN */
15
16#include <dirent.h>
17#include <errno.h>
18#include <limits.h>
19#include <stdio.h>
20#include <stdlib.h>
21#include <string.h>
22#include <sysexits.h>
23#include <unistd.h>
24
25#include "pathnames.h"
26
27typedef struct installedport {
28	struct installedport *next;				/* List of installed ports. */
29	char name[LINE_MAX];					/* Name of the installed port. */
30} INSTALLEDPORT;
31
32int usage(void);
33
34/*
35 * Parse /usr/port/UPDATING for corresponding entries. If no argument is
36 * passed to pkg_updating all entries for all installed ports are displayed.
37 * If a list of portnames is passed to pkg_updating only entries for the
38 * given portnames are displayed. Use the -d option to define that only newer
39 * entries as this date are shown.
40 */
41int
42main(int argc, char *argv[])
43{
44	/* Keyword for searching portname in UPDATING. */
45	const char *affects = "AFFECTS";
46	/* Indicate a date -> end of a entry. Will fail on 2100-01-01... */
47	const char *end = "20";
48	/* Keyword for searching origin portname of installed port. */
49	const char *origin = "@comment ORIGIN:";
50	const char *pkgdbpath = LOG_DIR;		/* Location of pkgdb */
51	const char *updatingfile = UPDATING;	/* Location of UPDATING */
52
53	char *date = NULL; 						/* Passed -d argument */
54	char *dateline = NULL;					/* Saved date of an entry */
55	/* Tmp lines for parsing file */
56	char *tmpline1 = NULL;
57	char *tmpline2 = NULL;
58
59	char originline[LINE_MAX];				/* Line of +CONTENTS */
60	/* Temporary variable to create path to +CONTENTS for installed ports. */
61	char tmp_file[MAXPATHLEN];
62	char updatingline[LINE_MAX];			/* Line of UPDATING */
63
64	int ch;									/* Char used by getopt */
65	int found = 0;							/* Found an entry */
66	int linelength;							/* Length of parsed line */
67	int maxcharperline = LINE_MAX;			/* Max chars per line */
68	int dflag = 0;							/* -d option set */
69	/* If pflag = 0 UPDATING will be checked for all installed ports. */
70	int pflag = 0;
71
72	size_t n;								/* Offset to create path */
73
74	struct dirent *pkgdbdir;				/* pkgdb directory */
75	struct stat attribute;					/* attribute of pkgdb element */
76
77	/* Needed nodes for linked list with installed ports. */
78	INSTALLEDPORT *head = (INSTALLEDPORT *) NULL;
79	INSTALLEDPORT *curr = (INSTALLEDPORT *) NULL;
80
81	DIR *dir;
82	FILE *fd;
83
84	while ((ch = getopt(argc, argv, "f:p:d:")) != -1) {
85		switch (ch) {
86			case 'd':
87				dflag = 1;
88				date = optarg;
89				break;
90			case 'f':
91				updatingfile = optarg;
92				break;
93			case '?':
94			default:
95				usage();
96		}
97	}
98	argc -= optind;
99	argv += optind;
100
101	/* Check if passed date has a correct format. */
102	if (dflag == 1) {
103		linelength = strlen(date);
104		if (linelength != 8)
105			exit(EX_DATAERR);
106		if (strspn(date, "0123456789") != 8) {
107			fprintf(stderr, "unknown date format: %s\n", date);
108			exit(EX_DATAERR);
109		}
110	}
111
112	/* Save the list of passed portnames. */
113	if (argc != 0) {
114		pflag = 1;
115		while (*argv) {
116			if((curr = (INSTALLEDPORT *)
117				malloc(sizeof(INSTALLEDPORT))) == NULL)
118				(void)exit(EXIT_FAILURE);
119			strlcpy (curr->name, *argv, strlen(*argv) + 1);
120			curr->next = head;
121			head = curr;
122			(void)*argv++;
123		}
124	}
125
126	/*
127	 * UPDATING will be parsed for all installed ports
128	 * if no portname is passed.
129	 */
130	if (pflag == 0) {
131		/* Open /var/db/pkg and search for all installed ports. */
132		if((dir = opendir(pkgdbpath)) != NULL) {
133			while ((pkgdbdir = readdir(dir)) != NULL) {
134				if (strcmp(pkgdbdir->d_name, ".") != 0 &&
135					strcmp(pkgdbdir->d_name, "..") !=0) {
136
137					/* Create path to +CONTENTS file for each installed port */
138					n = strlcpy(tmp_file, pkgdbpath, strlen(pkgdbpath)+1);
139					n = strlcpy(tmp_file + n, "/", sizeof(tmp_file) - n);
140					n = strlcat(tmp_file + n, pkgdbdir->d_name,
141						sizeof(tmp_file) - n);
142					if(stat(tmp_file, &attribute) == -1) {
143						fprintf(stderr, "can't open %s: %s\n",
144							tmp_file, strerror(errno));
145						return EXIT_FAILURE;
146					}
147					if(attribute.st_mode & S_IFREG)
148						continue;
149					(void)strlcat(tmp_file + n, "/+CONTENTS",
150						sizeof(tmp_file) - n);
151
152					/* Open +CONTENT file */
153					fd = fopen(tmp_file, "r");
154					if(fd == NULL) {
155						fprintf(stderr, "warning: can't open %s: %s\n",
156						tmp_file, strerror(errno));
157						continue;
158					}
159
160					/*
161					 * Parses +CONTENT for ORIGIN line and
162					 * put element into linked list.
163					 */
164					while(fgets(originline, maxcharperline, fd) != NULL) {
165						tmpline1 = strstr(originline, origin);
166						if( tmpline1 != NULL ) {
167							/* Tmp variable to store port name. */
168							char *pname;
169							pname = strrchr(originline, (int)':');
170							pname++;
171							if((curr = (INSTALLEDPORT *)
172								malloc(sizeof(INSTALLEDPORT))) == NULL)
173								(void)exit(EXIT_FAILURE);
174							strlcpy (curr->name, pname, strlen(pname)+1);
175							curr->next = head;
176							head = curr;
177						}
178					}
179
180					if(ferror(fd)) {
181						fprintf(stderr, "error reading input\n");
182						exit(EX_IOERR);
183					}
184
185					(void)fclose(fd);
186				}
187			}
188			closedir(dir);
189		}
190	}
191
192	/* Open UPDATING file */
193	fd = fopen(updatingfile, "r");
194	if(fd == NULL) {
195		fprintf(stderr, "can't open %s: %s\n",
196		updatingfile, strerror(errno));
197		exit(EX_UNAVAILABLE);
198	}
199
200	/* Parse opened UPDATING file. */
201	while(fgets(updatingline, maxcharperline, fd) != NULL) {
202		/* No entry is found so far */
203		if (found == 0) {
204			/* Search for AFFECTS line to parse the portname. */
205			tmpline1 = strstr(updatingline, affects);
206
207			if( tmpline1 != NULL ) {
208				curr = head;
209				while(curr != NULL) {
210					tmpline2 = strstr(updatingline, curr->name);
211					if( tmpline2 != NULL )
212						break;
213					curr = curr->next;
214				}
215				if( tmpline2 != NULL ) {
216					/* If -d is set, check if entry is newer than the date. */
217					if ( (dflag == 1) && (strncmp(dateline, date, 8) < 0))
218						continue;
219					printf("%s", dateline);
220					printf("%s", updatingline);
221					found = 1;
222				}
223			}
224		}
225		/* Search for the end of an entry, if not found print the line. */
226		else {
227			tmpline1 = strstr(updatingline, end);
228			if( tmpline1 == NULL )
229				printf("%s", updatingline);
230			else {
231				linelength = strlen(updatingline);
232				if (linelength == 10)
233					found = 0;
234				else
235					printf("%s", updatingline);
236			}
237		}
238		/* Save the actual line, it could be a date. */
239		dateline = strdup(updatingline);
240	}
241
242	if(ferror(fd)) {
243		fprintf(stderr, "error reading input\n");
244		exit(EX_IOERR);
245	}
246	(void)fclose(fd);
247
248	exit(EX_OK);
249}
250
251int
252usage(void)
253{
254	fprintf(stderr,
255		"usage: pkg_updating [-d YYYYMMDD] [-f file] [portname ...]\n");
256	exit(EX_USAGE);
257}
258