1/*
2 * FreeBSD install - a package for the installation and maintainance
3 * of non-core utilities.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * Jordan K. Hubbard
15 * 23 Aug 1993
16 *
17 * Various display routines for the info module.
18 *
19 */
20
21#include <sys/cdefs.h>
22__FBSDID("$FreeBSD$");
23
24#include "lib.h"
25#include "info.h"
26#include <err.h>
27#include <stdlib.h>
28#include <sys/types.h>
29#include <sys/stat.h>
30#include <md5.h>
31
32void
33show_file(const char *title, const char *fname)
34{
35    FILE *fp;
36    char line[1024];
37    int n;
38
39    if (!Quiet)
40	printf("%s%s", InfoPrefix, title);
41    fp = fopen(fname, "r");
42    if (fp == (FILE *) NULL)
43	printf("ERROR: show_file: Can't open '%s' for reading!\n", fname);
44    else {
45	int append_nl = 0;
46	while ((n = fread(line, 1, 1024, fp)) != 0)
47	    fwrite(line, 1, n, stdout);
48	fclose(fp);
49	append_nl = (line[n - 1] != '\n');	/* Do we have a trailing \n ? */
50	if (append_nl)
51	   printf("\n");
52    }
53    printf("\n");	/* just in case */
54}
55
56void
57show_index(const char *title, const char *fname)
58{
59    FILE *fp;
60    char line[MAXINDEXSIZE+2];
61
62    strlcpy(line, "???\n", sizeof(line));
63
64    if (!Quiet) {
65        printf("%s%s", InfoPrefix, title);
66        fflush(stdout);
67    }
68    fp = fopen(fname, "r");
69    if (fp == (FILE *) NULL) {
70        warnx("show_file: can't open '%s' for reading", fname);
71    } else {
72    	if(fgets(line, MAXINDEXSIZE + 1, fp)) {
73		size_t line_length = strlen(line);
74
75		if (line[line_length - 1] != '\n') {	/* Do we have a trailing \n ? */
76			line[line_length] = '\n';	/* Add a trailing \n */
77			line[line_length + 1] = '\0';	/* Terminate string */
78		}
79	}
80	fclose(fp);
81    }
82    fputs(line, stdout);
83}
84
85/* Show a packing list item type.  If showall is TRUE, show all */
86void
87show_plist(const char *title, Package *plist, plist_t type, Boolean showall)
88{
89    PackingList p;
90    Boolean ign = FALSE;
91    char *prefix = NULL;
92
93    if (!Quiet) {
94	printf("%s%s", InfoPrefix, title);
95	fflush(stdout);
96    }
97    p = plist->head;
98    while (p) {
99	if (p->type != type && showall != TRUE) {
100	    p = p->next;
101	    continue;
102	}
103	switch(p->type) {
104	case PLIST_FILE:
105	    if (ign) {
106		printf(Quiet ? "%s\n" : "File: %s (ignored)\n", p->name);
107		ign = FALSE;
108	    }
109	    else
110		printf(Quiet ? "%s\n" : "File: %s\n", p->name);
111	    break;
112
113	case PLIST_CWD:
114	    if (!prefix)
115		prefix = p->name;
116	    printf(Quiet ? "@cwd %s\n" : "\tCWD to %s\n", (p->name == NULL) ? prefix : p->name);
117	    break;
118
119	case PLIST_SRC:
120	    printf(Quiet ? "@srcdir %s\n" : "\tSRCDIR to %s\n", p->name);
121	    break;
122
123	case PLIST_CMD:
124	    printf(Quiet ? "@exec %s\n" : "\tEXEC '%s'\n", p->name);
125	    break;
126
127	case PLIST_UNEXEC:
128	    printf(Quiet ? "@unexec %s\n" : "\tUNEXEC '%s'\n", p->name);
129	    break;
130
131	case PLIST_CHMOD:
132	    printf(Quiet ? "@chmod %s\n" : "\tCHMOD to %s\n",
133		   p->name ? p->name : "(clear default)");
134	    break;
135
136	case PLIST_CHOWN:
137	    printf(Quiet ? "@chown %s\n" : "\tCHOWN to %s\n",
138		   p->name ? p->name : "(clear default)");
139	    break;
140
141	case PLIST_CHGRP:
142	    printf(Quiet ? "@chgrp %s\n" : "\tCHGRP to %s\n",
143		   p->name ? p->name : "(clear default)");
144	    break;
145
146	case PLIST_COMMENT:
147	    printf(Quiet ? "@comment %s\n" : "\tComment: %s\n", p->name);
148	    break;
149
150	case PLIST_NOINST:
151	    printf(Quiet ? "@noinst %s\n" : "\tNot installed: %s\n", p->name);
152	    break;
153
154	case PLIST_IGNORE:
155	    ign = TRUE;
156	    break;
157
158	case PLIST_IGNORE_INST:
159	    printf(Quiet ? "@ignore_inst ??? doesn't belong here.\n" :
160		   "\tIgnore next file installation directive (doesn't belong)\n");
161	    ign = TRUE;
162	    break;
163
164	case PLIST_NAME:
165	    printf(Quiet ? "@name %s\n" : "\tPackage name: %s\n", p->name);
166	    break;
167
168	case PLIST_DISPLAY:
169	    printf(Quiet ? "@display %s\n" : "\tInstall message file: %s\n", p->name);
170	    break;
171
172	case PLIST_PKGDEP:
173	    printf(Quiet ? "@pkgdep %s\n" : "Dependency: %s\n", p->name);
174	    break;
175
176	case PLIST_DEPORIGIN:
177	    printf(Quiet ? "@comment DEPORIGIN:%s\n" :
178		"\tdependency origin: %s\n", p->name);
179	    break;
180
181	case PLIST_CONFLICTS:
182	    printf(Quiet ? "@conflicts %s\n" : "Conflicts: %s\n", p->name);
183	    break;
184
185	case PLIST_MTREE:
186	    printf(Quiet ? "@mtree %s\n" : "\tPackage mtree file: %s\n", p->name);
187	    break;
188
189	case PLIST_DIR_RM:
190	    printf(Quiet ? "@dirrm %s\n" : "\tDeinstall directory remove: %s\n", p->name);
191	    break;
192
193	case PLIST_OPTION:
194	    printf(Quiet ? "@option %s\n" :
195		"\tOption \"%s\" controlling package installation behaviour\n",
196		p->name);
197	    break;
198
199	case PLIST_ORIGIN:
200	    printf(Quiet ? "@comment ORIGIN:%s\n" :
201		"\tPackage origin: %s\n", p->name);
202	    break;
203
204	default:
205	    cleanup(0);
206	    errx(2, "%s: unknown command type %d (%s)",
207		__func__, p->type, p->name);
208	    break;
209	}
210	p = p->next;
211    }
212}
213
214static const char *
215elide_root(const char *dir)
216{
217    if (strcmp(dir, "/") == 0)
218	return "";
219    return dir;
220}
221
222/* Show all files in the packing list (except ignored ones) */
223void
224show_files(const char *title, Package *plist)
225{
226    PackingList p;
227    Boolean ign = FALSE;
228    char *prefix = NULL;
229    const char *dir = ".";
230
231    if (!Quiet)
232	printf("%s%s", InfoPrefix, title);
233    p = plist->head;
234    while (p) {
235	switch(p->type) {
236	case PLIST_FILE:
237	    if (!ign)
238		printf("%s/%s\n", elide_root(dir), p->name);
239	    ign = FALSE;
240	    break;
241
242	case PLIST_CWD:
243	    if (!prefix)
244		prefix = p->name;
245	    if (p->name == NULL)
246		dir = prefix;
247	    else
248		dir = p->name;
249	    break;
250
251	case PLIST_IGNORE:
252	    ign = TRUE;
253	    break;
254
255        /* Silence GCC in the -Wall mode */
256	default:
257	    break;
258	}
259	p = p->next;
260    }
261}
262
263/* Calculate and show size of all installed package files (except ignored ones) */
264void
265show_size(const char *title, Package *plist)
266{
267    PackingList p;
268    Boolean ign = FALSE;
269    const char *dir = ".";
270    struct stat sb;
271    char tmp[FILENAME_MAX];
272    unsigned long size = 0;
273    long blksize;
274    int headerlen;
275    char *descr;
276    char *prefix = NULL;
277
278    descr = getbsize(&headerlen, &blksize);
279    if (!Quiet) {
280	printf("%s%s", InfoPrefix, title);
281        fflush(stdout);
282    }
283    for (p = plist->head; p != NULL; p = p->next) {
284	switch (p->type) {
285	case PLIST_FILE:
286	    if (!ign) {
287		snprintf(tmp, FILENAME_MAX, "%s/%s", elide_root(dir), p->name);
288		if (!lstat(tmp, &sb)) {
289		    size += sb.st_size;
290		    if (Verbose)
291			printf("%lu\t%s\n", (unsigned long) howmany(sb.st_size, blksize), tmp);
292		}
293	    }
294	    ign = FALSE;
295	    break;
296
297	case PLIST_CWD:
298	    if (!prefix)
299		prefix = p->name;
300	    if (p->name == NULL)
301		dir = prefix;
302	    else
303		dir = p->name;
304	    break;
305
306	case PLIST_IGNORE:
307	    ign = TRUE;
308	    break;
309
310	/* Silence GCC in the -Wall mode */
311	default:
312	    break;
313	}
314    }
315    if (!Quiet)
316	printf("%lu\t(%s)\n", howmany(size, blksize), descr);
317    else
318	if (UseBlkSz)
319		printf("%lu\n", howmany(size, blksize));
320	else
321		printf("%lu\n", size);
322}
323
324/* Show files that don't match the recorded checksum */
325int
326show_cksum(const char *title, Package *plist)
327{
328    PackingList p;
329    const char *dir = ".";
330    char *prefix = NULL;
331    char tmp[FILENAME_MAX];
332    int errcode = 0;
333
334    if (!Quiet) {
335	printf("%s%s", InfoPrefix, title);
336	fflush(stdout);
337    }
338
339    for (p = plist->head; p != NULL; p = p->next)
340	if (p->type == PLIST_CWD) {
341	    if (!prefix)
342		prefix = p->name;
343	    if (p->name == NULL)
344		dir = prefix;
345	    else
346		dir = p->name;
347	} else if (p->type == PLIST_FILE) {
348	    snprintf(tmp, FILENAME_MAX, "%s/%s", elide_root(dir), p->name);
349	    if (!fexists(tmp)) {
350		warnx("%s doesn't exist", tmp);
351		errcode = 1;
352	    } else if (p->next && p->next->type == PLIST_COMMENT &&
353	             (strncmp(p->next->name, "MD5:", 4) == 0)) {
354		char *cp = NULL, buf[33];
355
356		/*
357		 * For packing lists whose version is 1.1 or greater, the md5
358		 * hash for a symlink is calculated on the string returned
359		 * by readlink().
360		 */
361		if (issymlink(tmp) && verscmp(plist, 1, 0) > 0) {
362		    int len;
363		    char linkbuf[FILENAME_MAX];
364
365		    if ((len = readlink(tmp, linkbuf, FILENAME_MAX)) > 0)
366			cp = MD5Data((unsigned char *)linkbuf, len, buf);
367		} else if (isfile(tmp) || verscmp(plist, 1, 1) < 0)
368		    cp = MD5File(tmp, buf);
369
370		if (cp != NULL) {
371		    /* Mismatch? */
372		    if (strcmp(cp, p->next->name + 4))
373			printf("%s fails the original MD5 checksum\n", tmp);
374		    else if (Verbose)
375			printf("%s matched the original MD5 checksum\n", tmp);
376		}
377	    }
378	}
379    return (errcode);
380}
381
382/* Show an "origin" path (usually category/portname) */
383void
384show_origin(const char *title, Package *plist)
385{
386
387    if (!Quiet) {
388	printf("%s%s", InfoPrefix, title);
389	fflush(stdout);
390    }
391    printf("%s\n", plist->origin != NULL ? plist->origin : "");
392}
393
394/* Show revision number of the packing list */
395void
396show_fmtrev(const char *title, Package *plist)
397{
398
399    if (!Quiet) {
400	printf("%s%s", InfoPrefix, title);
401	fflush(stdout);
402    }
403    printf("%d.%d\n", plist->fmtver_maj, plist->fmtver_mnr);
404}
405