198766Smarkm/*
2228990Suqs * FreeBSD install - a package for the installation and maintenance
398766Smarkm * of non-core utilities.
498766Smarkm *
598766Smarkm * Redistribution and use in source and binary forms, with or without
698766Smarkm * modification, are permitted provided that the following conditions
798766Smarkm * are met:
898766Smarkm * 1. Redistributions of source code must retain the above copyright
998766Smarkm *    notice, this list of conditions and the following disclaimer.
1098766Smarkm * 2. Redistributions in binary form must reproduce the above copyright
1198766Smarkm *    notice, this list of conditions and the following disclaimer in the
1298766Smarkm *    documentation and/or other materials provided with the distribution.
1398766Smarkm *
1498766Smarkm * Jeremy D. Lea.
1598766Smarkm * 11 May 2002
1698766Smarkm *
1798766Smarkm * This is the version module. Based on pkg_version.pl by Bruce A. Mah.
1898766Smarkm *
1998766Smarkm */
2098766Smarkm
2198766Smarkm#include <sys/cdefs.h>
2298766Smarkm__FBSDID("$FreeBSD: releng/10.3/usr.sbin/pkg_install/version/perform.c 241737 2012-10-19 14:49:42Z ed $");
2398766Smarkm
24222035Sflz#include "lib.h"
2598766Smarkm#include "version.h"
2698766Smarkm#include <err.h>
2798766Smarkm#include <fetch.h>
2898766Smarkm#include <signal.h>
2998766Smarkm
30241737Sedstatic FILE *IndexFile;
31241737Sedstatic char IndexPath[PATH_MAX] = "";
32241737Sedstatic struct index_head Index = SLIST_HEAD_INITIALIZER(Index);
3398766Smarkm
3498766Smarkmstatic int pkg_do(char *);
35148435Skrionstatic void show_version(Package, const char *, const char *);
3698766Smarkm
3798766Smarkm/*
38206043Sflz * This is the traditional pkg_perform, except that the argument is _not_ a
39206043Sflz * list of packages. It is the index file from the command line.
4098766Smarkm *
41206043Sflz * We loop over the installed packages, matching them with the -s flag if
42206043Sflz * needed and calling pkg_do(). Beforehand we set up a few things, and after
43206043Sflz * we tear them down...
44206043Sflz *
45206043Sflz * Returns 0 on success, non-zero on failure, corresponding to the number of
46206043Sflz * failed attempts to access the INDEX.
4798766Smarkm */
4898766Smarkmint
4998766Smarkmpkg_perform(char **indexarg)
5098766Smarkm{
51173014Ssimon    char **pkgs, *pat[2], **patterns;
5298766Smarkm    struct index_entry *ie;
53206043Sflz    int i, err_cnt = 0, rel_major_ver;
54113079Srobert    int MatchType;
5598766Smarkm
56206043Sflz    struct utsname u;
57206043Sflz
58206043Sflz    if (uname(&u) == -1) {
59240682Sbapt	warn("%s: failed to determine uname information", __func__);
60206043Sflz	return 1;
61206043Sflz    } else if ((rel_major_ver = (int) strtol(u.release, NULL, 10)) <= 0) {
62240682Sbapt	warnx("%s: bad release version specified: %s", __func__, u.release);
63240682Sbapt	return 1;
64206043Sflz    }
65206043Sflz
6698766Smarkm    /*
6798766Smarkm     * Try to find and open the INDEX. We only check IndexFile != NULL
6898766Smarkm     * later, if we actually need the INDEX.
6998766Smarkm     */
70206043Sflz    if (*indexarg == NULL) {
71206043Sflz	snprintf(IndexPath, sizeof(IndexPath), "%s/INDEX-%d", PORTS_DIR,
72206043Sflz	    rel_major_ver);
73206043Sflz    } else
74173014Ssimon	strlcpy(IndexPath, *indexarg, sizeof(IndexPath));
75173014Ssimon    if (isURL(IndexPath))
76173014Ssimon	IndexFile = fetchGetURL(IndexPath, "");
7798766Smarkm    else
78173014Ssimon	IndexFile = fopen(IndexPath, "r");
7998766Smarkm
80113079Srobert    /* Get either a list of matching or all packages */
81113079Srobert    if (MatchName != NULL) {
82113079Srobert	pat[0] = MatchName;
83113079Srobert	pat[1] = NULL;
84131275Seik	MatchType = RegexExtended ? MATCH_EREGEX : MATCH_REGEX;
85113079Srobert	patterns = pat;
86148435Skrion     } else {
87113079Srobert	MatchType = MATCH_ALL;
88113079Srobert	patterns = NULL;
89148435Skrion     }
90113079Srobert
91148435Skrion    if (LookUpOrigin != NULL)
92148435Skrion	pkgs = matchbyorigin(LookUpOrigin, &err_cnt);
93148435Skrion    else
94148435Skrion	pkgs = matchinstalled(MatchType, patterns, &err_cnt);
95148435Skrion
9698766Smarkm    if (err_cnt != 0)
9798766Smarkm	errx(2, "Unable to find package database directory!");
98113079Srobert    if (pkgs == NULL) {
99148435Skrion	if (LookUpOrigin != NULL) {
100148435Skrion	    warnx("no packages recorded with this origin");
101113079Srobert	    return (1);
102148435Skrion	} else {
103148435Skrion	    switch (MatchType) {
104148435Skrion	    case MATCH_ALL:
105148435Skrion		warnx("no packages installed");
106148435Skrion		return (0);
107148435Skrion	    case MATCH_EREGEX:
108148435Skrion	    case MATCH_REGEX:
109148435Skrion		warnx("no packages match pattern");
110148435Skrion		return (1);
111148435Skrion	    default:
112148435Skrion		break;
113148435Skrion	    }
114113079Srobert	}
11598766Smarkm    }
11698766Smarkm
117113079Srobert    for (i = 0; pkgs[i] != NULL; i++)
118113079Srobert	err_cnt += pkg_do(pkgs[i]);
119113079Srobert
12098766Smarkm    /* If we opened the INDEX in pkg_do(), clean up. */
12198766Smarkm    while (!SLIST_EMPTY(&Index)) {
12298766Smarkm	ie = SLIST_FIRST(&Index);
12398766Smarkm	SLIST_REMOVE_HEAD(&Index, next);
124103119Ssobomax	if (ie->name != NULL)
125103119Ssobomax	    free(ie->name);
126103119Ssobomax	if (ie->origin != NULL)
127103119Ssobomax	    free(ie->origin);
12898766Smarkm	free(ie);
12998766Smarkm    }
13098766Smarkm    if (IndexFile != NULL)
13198766Smarkm	fclose(IndexFile);
13298766Smarkm
13398766Smarkm    return err_cnt;
13498766Smarkm}
13598766Smarkm
13698766Smarkm/*
13798766Smarkm * Traditional pkg_do(). We take the package name we are passed and
13898766Smarkm * first slurp in the CONTENTS file, getting name and origin, then
13998766Smarkm * we look for it's corresponding Makefile. If that fails we pull in
14098766Smarkm * the INDEX, and check there.
14198766Smarkm */
14298766Smarkmstatic int
14398766Smarkmpkg_do(char *pkg)
14498766Smarkm{
14598766Smarkm    char *ch, tmp[PATH_MAX], tmp2[PATH_MAX], *latest = NULL;
14698766Smarkm    Package plist;
14798766Smarkm    struct index_entry *ie;
14898766Smarkm    FILE *fp;
14998766Smarkm    size_t len;
15098766Smarkm
15198766Smarkm    /* Suck in the contents list. */
15298766Smarkm    plist.head = plist.tail = NULL;
15398766Smarkm    plist.name = plist.origin = NULL;
15498766Smarkm    snprintf(tmp, PATH_MAX, "%s/%s/%s", LOG_DIR, pkg, CONTENTS_FNAME);
15598766Smarkm    fp = fopen(tmp, "r");
15698766Smarkm    if (!fp) {
157131280Seik	warnx("the package info for package '%s' is corrupt", pkg);
15898766Smarkm	return 1;
15998766Smarkm    }
16098766Smarkm    read_plist(&plist, fp);
16198766Smarkm    fclose(fp);
162102438Sreg    if (plist.name == NULL) {
163102438Sreg    	warnx("%s does not appear to be a valid package!", pkg);
164102438Sreg    	return 1;
165102438Sreg    }
16698766Smarkm
16798766Smarkm    /*
16898766Smarkm     * First we check if the installed package has an origin, and try
16998766Smarkm     * looking for it's Makefile. If we find the Makefile we get the
17098766Smarkm     * latest version from there. If we fail, we start looking in the
17198766Smarkm     * INDEX, first matching the origin and then the package name.
17298766Smarkm     */
173146559Scperciva    if (plist.origin != NULL && !UseINDEXOnly) {
17498766Smarkm	snprintf(tmp, PATH_MAX, "%s/%s", PORTS_DIR, plist.origin);
17598766Smarkm	if (isdir(tmp) && chdir(tmp) != FAIL && isfile("Makefile")) {
176131285Seik	    if ((latest = vpipe("/usr/bin/make -V PKGNAME", tmp)) == NULL)
17798766Smarkm		warnx("Failed to get PKGNAME from %s/Makefile!", tmp);
17898766Smarkm	    else
179148435Skrion		show_version(plist, latest, "port");
18098766Smarkm	}
18198766Smarkm    }
18298766Smarkm    if (latest == NULL) {
183151200Skrion	/* Report package as not found in INDEX if the INDEX is not required. */
184151200Skrion	if (IndexFile == NULL && !UseINDEXOnly)
185151200Skrion		show_version(plist, NULL, plist.origin);
186151200Skrion	else {
18798766Smarkm	/* We only pull in the INDEX once, if needed. */
18898766Smarkm	if (SLIST_EMPTY(&Index)) {
18998766Smarkm	    if (!IndexFile)
190173014Ssimon		errx(2, "Unable to open %s in %s.", IndexPath, __func__);
19198766Smarkm	    while ((ch = fgetln(IndexFile, &len)) != NULL) {
19298766Smarkm		/*
19398766Smarkm		 * Don't use strlcpy() because fgetln() doesn't
19498766Smarkm		 * return a valid C string.
19598766Smarkm		 */
19698766Smarkm		strncpy(tmp, ch, MIN(len, PATH_MAX));
19798766Smarkm		tmp[PATH_MAX-1] = '\0';
19898766Smarkm		/* The INDEX has pkgname|portdir|... */
19998766Smarkm		if ((ch = strchr(tmp, '|')) != NULL)
20098766Smarkm		    ch[0] = '\0';
20198766Smarkm		if (ch != NULL && (ch = strchr(&ch[1], '|')) != NULL)
20298766Smarkm		    ch[0] = '\0';
20398766Smarkm		/* Look backwards for the last two dirs = origin */
20498766Smarkm		while (ch != NULL && *--ch != '/')
20598766Smarkm		    if (ch[0] == '\0')
20698766Smarkm			ch = NULL;
20798766Smarkm		while (ch != NULL && *--ch != '/')
20898766Smarkm		    if (ch[0] == '\0')
20998766Smarkm			ch = NULL;
21098766Smarkm		if (ch == NULL)
21198766Smarkm		    errx(2, "The INDEX does not appear to be valid!");
21298766Smarkm		if ((ie = malloc(sizeof(struct index_entry))) == NULL)
21398766Smarkm		    errx(2, "Unable to allocate memory in %s.", __func__);
214103119Ssobomax		bzero(ie, sizeof(struct index_entry));
215103119Ssobomax		ie->name = strdup(tmp);
216103119Ssobomax		ie->origin = strdup(&ch[1]);
21798766Smarkm		/* Who really cares if we reverse the index... */
21898766Smarkm		SLIST_INSERT_HEAD(&Index, ie, next);
21998766Smarkm	    }
22098766Smarkm	}
22198766Smarkm	/* Now that we've slurped in the INDEX... */
22298766Smarkm	SLIST_FOREACH(ie, &Index, next) {
22398766Smarkm	    if (plist.origin != NULL) {
22498766Smarkm		if (strcmp(plist.origin, ie->origin) == 0)
22598766Smarkm		    latest = strdup(ie->name);
22698766Smarkm	    } else {
22798766Smarkm		strlcpy(tmp, ie->name, PATH_MAX);
22898766Smarkm		strlcpy(tmp2, plist.name, PATH_MAX);
22998766Smarkm		/* Chop off the versions and compare. */
23098766Smarkm		if ((ch = strrchr(tmp, '-')) == NULL)
23198766Smarkm		    errx(2, "The INDEX does not appear to be valid!");
23298766Smarkm		ch[0] = '\0';
23398766Smarkm		if ((ch = strrchr(tmp2, '-')) == NULL)
23498766Smarkm		    warnx("%s is not a valid package!", plist.name);
23598766Smarkm		else
23698766Smarkm		    ch[0] = '\0';
23798766Smarkm		if (strcmp(tmp2, tmp) == 0) {
23898766Smarkm		    if (latest != NULL) {
23998766Smarkm			/* Multiple matches */
24098766Smarkm			snprintf(tmp, PATH_MAX, "%s|%s", latest, ie->name);
24198766Smarkm			free(latest);
24298766Smarkm			latest = strdup(tmp);
24398766Smarkm		    } else
24498766Smarkm			latest = strdup(ie->name);
24598766Smarkm		}
24698766Smarkm	    }
24798766Smarkm	}
24898766Smarkm	if (latest == NULL)
249148435Skrion	    show_version(plist, NULL, NULL);
25098766Smarkm	else
251148435Skrion	    show_version(plist, latest, "index");
252151200Skrion	}
25398766Smarkm    }
25498766Smarkm    if (latest != NULL)
25598766Smarkm	free(latest);
25698766Smarkm    free_plist(&plist);
25798766Smarkm    return 0;
25898766Smarkm}
25998766Smarkm
26098766Smarkm#define OUTPUT(c) ((PreventChars != NULL && !strchr(PreventChars, (c))) || \
26198766Smarkm			(LimitChars != NULL && strchr(LimitChars, (c))) || \
26298766Smarkm			(PreventChars == NULL && LimitChars == NULL))
26398766Smarkm
26498766Smarkm/*
26598766Smarkm * Do the work of comparing and outputing. Ugly, but well that's what
26698766Smarkm * You get when you try to match perl output in C ;-).
26798766Smarkm */
26898766Smarkmvoid
269148435Skrionshow_version(Package plist, const char *latest, const char *source)
27098766Smarkm{
27198766Smarkm    char *ch, tmp[PATH_MAX];
27298766Smarkm    const char *ver;
27398766Smarkm    int cmp = 0;
27498766Smarkm
275148435Skrion    if (!plist.name || strlen(plist.name) == 0)
27698766Smarkm	return;
277161251Skrion    if (ShowOrigin != FALSE && plist.origin != NULL)
278148435Skrion	strlcpy(tmp, plist.origin, PATH_MAX);
279155577Skrion    else {
280148435Skrion	strlcpy(tmp, plist.name, PATH_MAX);
281155577Skrion	if (!Verbose) {
282155577Skrion	    if ((ch = strrchr(tmp, '-')) != NULL)
283155577Skrion		ch[0] = '\0';
284155577Skrion	}
28598766Smarkm    }
28698766Smarkm    if (latest == NULL) {
28798766Smarkm	if (source == NULL && OUTPUT('!')) {
28898766Smarkm	    printf("%-34s  !", tmp);
28998766Smarkm	    if (Verbose)
29098766Smarkm		printf("   Comparison failed");
29198766Smarkm	    printf("\n");
292148435Skrion	} else if (OUTPUT('?')) {
29398766Smarkm	    printf("%-34s  ?", tmp);
29498766Smarkm	    if (Verbose)
295148435Skrion		printf("   orphaned: %s", plist.origin);
29698766Smarkm	    printf("\n");
29798766Smarkm	}
29898766Smarkm    } else if (strchr(latest,'|') != NULL) {
29998766Smarkm	if (OUTPUT('*')) {
30098766Smarkm	    printf("%-34s  *", tmp);
30198766Smarkm	    if (Verbose) {
30298766Smarkm		strlcpy(tmp, latest, PATH_MAX);
30398766Smarkm		ch = strchr(tmp, '|');
30498766Smarkm		ch[0] = '\0';
30598766Smarkm
306131274Seik		ver = strrchr(tmp, '-');
307131274Seik		ver = ver ? &ver[1] : tmp;
30898766Smarkm		printf("   multiple versions (index has %s", ver);
30998766Smarkm		do {
310131274Seik		    ver = strrchr(&ch[1], '-');
311131274Seik		    ver = ver ? &ver[1] : &ch[1];
31298766Smarkm		    if ((ch = strchr(&ch[1], '|')) != NULL)
31398766Smarkm			    ch[0] = '\0';
31498766Smarkm		    printf(", %s", ver);
31598766Smarkm		} while (ch != NULL);
31698766Smarkm		printf(")");
31798766Smarkm	    }
31898766Smarkm	    printf("\n");
31998766Smarkm	}
32098766Smarkm    } else {
321148435Skrion	cmp = version_cmp(plist.name, latest);
322131274Seik	ver = strrchr(latest, '-');
323131274Seik	ver = ver ? &ver[1] : latest;
32498766Smarkm	if (cmp < 0 && OUTPUT('<')) {
325240682Sbapt	    if (Quiet)
326240682Sbapt		printf("%s", tmp);
327240682Sbapt	    else {
328240682Sbapt		printf("%-34s  <", tmp);
329240682Sbapt		if (Verbose)
330240682Sbapt		    printf("   needs updating (%s has %s)", source, ver);
331240682Sbapt	    }
33298766Smarkm	    printf("\n");
33398766Smarkm	} else if (cmp == 0 && OUTPUT('=')) {
334240682Sbapt	    if (Quiet)
335240682Sbapt		printf("%s", tmp);
336240682Sbapt	    else {
337240682Sbapt		printf("%-34s  =", tmp);
338240682Sbapt		if (Verbose)
339240682Sbapt		    printf("   up-to-date with %s", source);
340240682Sbapt	    }
34198766Smarkm	    printf("\n");
34298766Smarkm	} else if (cmp > 0 && OUTPUT('>')) {
343240682Sbapt	    if (Quiet)
344240682Sbapt		printf("%s", tmp);
345240682Sbapt	    else {
346240682Sbapt		printf("%-34s  >", tmp);
347240682Sbapt		if (Verbose)
348240682Sbapt		    printf("   succeeds %s (%s has %s)", source, source, ver);
349240682Sbapt	    }
35098766Smarkm	    printf("\n");
35198766Smarkm	}
35298766Smarkm    }
35398766Smarkm}
35498766Smarkm
355131275Seikint
356131275Seikversion_match(char *pattern, const char *pkgname)
357131275Seik{
358131275Seik    int ret = 0;
359131275Seik    int matchstream = 0;
360131275Seik    FILE *fp = NULL;
361131275Seik    Boolean isTMP = FALSE;
362131275Seik
363131275Seik    if (isURL(pkgname)) {
364131275Seik	fp = fetchGetURL(pkgname, "");
365131275Seik	isTMP = TRUE;
366131275Seik	matchstream = 1;
367151200Skrion	if (fp == NULL)
368131275Seik	    errx(2, "Unable to open %s.", pkgname);
369131275Seik    } else if (pkgname[0] == '/') {
370131275Seik	fp = fopen(pkgname, "r");
371131275Seik	isTMP = TRUE;
372131275Seik	matchstream = 1;
373151200Skrion	if (fp == NULL)
374131275Seik	    errx(2, "Unable to open %s.", pkgname);
375131275Seik    } else if (strcmp(pkgname, "-") == 0) {
376131275Seik	fp = stdin;
377131275Seik	matchstream = 1;
378131275Seik    } else if (isURL(pattern)) {
379131275Seik	fp = fetchGetURL(pattern, "");
380131275Seik	isTMP = TRUE;
381131275Seik	matchstream = -1;
382151200Skrion	if (fp == NULL)
383131275Seik	    errx(2, "Unable to open %s.", pattern);
384131275Seik    } else if (pattern[0] == '/') {
385131275Seik	fp = fopen(pattern, "r");
386131275Seik	isTMP = TRUE;
387131275Seik	matchstream = -1;
388151200Skrion	if (fp == NULL)
389131275Seik	    errx(2, "Unable to open %s.", pattern);
390131275Seik    } else if (strcmp(pattern, "-") == 0) {
391131275Seik	fp = stdin;
392131275Seik	matchstream = -1;
393131275Seik    } else {
394131275Seik	ret = pattern_match(MATCH_GLOB, pattern, pkgname);
395131275Seik    }
396131275Seik
397131275Seik    if (fp != NULL) {
398131275Seik	size_t len;
399131275Seik	char *line;
400131275Seik	while ((line = fgetln(fp, &len)) != NULL) {
401131275Seik	    int match;
402131275Seik	    char *ch, ln[2048];
403131275Seik	    size_t lnlen;
404131275Seik	    if (len > 0 && line[len-1] == '\n')
405131275Seik		len --;
406131275Seik	    lnlen = len;
407131275Seik	    if (lnlen > sizeof(ln)-1)
408131275Seik		lnlen = sizeof(ln)-1;
409131275Seik	    memcpy(ln, line, lnlen);
410131275Seik	    ln[lnlen] = '\0';
411131275Seik	    if ((ch = strchr(ln, '|')) != NULL)
412131275Seik    		ch[0] = '\0';
413131275Seik	    if (matchstream > 0)
414131275Seik	    	match = pattern_match(MATCH_GLOB, pattern, ln);
415131275Seik	    else
416131275Seik	    	match = pattern_match(MATCH_GLOB, ln, pkgname);
417131275Seik	    if (match == 1) {
418131275Seik		ret = 1;
419131275Seik		printf("%.*s\n", (int)len, line);
420131275Seik	    }
421131275Seik	}
422131275Seik	if (isTMP)
423131275Seik	    fclose(fp);
424131275Seik    }
425131275Seik
426131275Seik    return ret;
427131275Seik}
428131275Seik
42998766Smarkmvoid
43098766Smarkmcleanup(int sig)
43198766Smarkm{
43298766Smarkm    if (sig)
43398766Smarkm	exit(1);
43498766Smarkm}
435