]> CyberLeo.Net >> Repos - FreeBSD/releng/9.2.git/blob - usr.sbin/pkg_install/version/perform.c
- Copy stable/9 to releng/9.2 as part of the 9.2-RELEASE cycle.
[FreeBSD/releng/9.2.git] / usr.sbin / pkg_install / version / perform.c
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  * Jeremy D. Lea.
15  * 11 May 2002
16  *
17  * This is the version module. Based on pkg_version.pl by Bruce A. Mah.
18  *
19  */
20
21 #include <sys/cdefs.h>
22 __FBSDID("$FreeBSD$");
23
24 #include "lib.h"
25 #include "version.h"
26 #include <err.h>
27 #include <fetch.h>
28 #include <signal.h>
29
30 FILE *IndexFile;
31 char IndexPath[PATH_MAX] = "";
32 struct index_head Index = SLIST_HEAD_INITIALIZER(Index);
33
34 static int pkg_do(char *);
35 static void show_version(Package, const char *, const char *);
36
37 /*
38  * This is the traditional pkg_perform, except that the argument is _not_ a
39  * list of packages. It is the index file from the command line.
40  *
41  * We loop over the installed packages, matching them with the -s flag if
42  * needed and calling pkg_do(). Beforehand we set up a few things, and after
43  * we tear them down...
44  *
45  * Returns 0 on success, non-zero on failure, corresponding to the number of
46  * failed attempts to access the INDEX.
47  */
48 int
49 pkg_perform(char **indexarg)
50 {
51     char **pkgs, *pat[2], **patterns;
52     struct index_entry *ie;
53     int i, err_cnt = 0, rel_major_ver;
54     int MatchType;
55
56     struct utsname u;
57
58     if (uname(&u) == -1) {
59         warn("%s: failed to determine uname information", __func__);
60         return 1;
61     } else if ((rel_major_ver = (int) strtol(u.release, NULL, 10)) <= 0) {
62         warnx("%s: bad release version specified: %s", __func__, u.release);
63         return 1;
64     }
65
66     /*
67      * Try to find and open the INDEX. We only check IndexFile != NULL
68      * later, if we actually need the INDEX.
69      */
70     if (*indexarg == NULL) {
71         snprintf(IndexPath, sizeof(IndexPath), "%s/INDEX-%d", PORTS_DIR,
72             rel_major_ver);
73     } else
74         strlcpy(IndexPath, *indexarg, sizeof(IndexPath));
75     if (isURL(IndexPath))
76         IndexFile = fetchGetURL(IndexPath, "");
77     else
78         IndexFile = fopen(IndexPath, "r");
79
80     /* Get either a list of matching or all packages */
81     if (MatchName != NULL) {
82         pat[0] = MatchName;
83         pat[1] = NULL;
84         MatchType = RegexExtended ? MATCH_EREGEX : MATCH_REGEX;
85         patterns = pat;
86      } else {
87         MatchType = MATCH_ALL;
88         patterns = NULL;
89      }
90
91     if (LookUpOrigin != NULL)
92         pkgs = matchbyorigin(LookUpOrigin, &err_cnt);
93     else
94         pkgs = matchinstalled(MatchType, patterns, &err_cnt);
95
96     if (err_cnt != 0)
97         errx(2, "Unable to find package database directory!");
98     if (pkgs == NULL) {
99         if (LookUpOrigin != NULL) {
100             warnx("no packages recorded with this origin");
101             return (1);
102         } else {
103             switch (MatchType) {
104             case MATCH_ALL:
105                 warnx("no packages installed");
106                 return (0);
107             case MATCH_EREGEX:
108             case MATCH_REGEX:
109                 warnx("no packages match pattern");
110                 return (1);
111             default:
112                 break;
113             }
114         }
115     }
116
117     for (i = 0; pkgs[i] != NULL; i++)
118         err_cnt += pkg_do(pkgs[i]);
119
120     /* If we opened the INDEX in pkg_do(), clean up. */
121     while (!SLIST_EMPTY(&Index)) {
122         ie = SLIST_FIRST(&Index);
123         SLIST_REMOVE_HEAD(&Index, next);
124         if (ie->name != NULL)
125             free(ie->name);
126         if (ie->origin != NULL)
127             free(ie->origin);
128         free(ie);
129     }
130     if (IndexFile != NULL)
131         fclose(IndexFile);
132
133     return err_cnt;
134 }
135
136 /*
137  * Traditional pkg_do(). We take the package name we are passed and
138  * first slurp in the CONTENTS file, getting name and origin, then
139  * we look for it's corresponding Makefile. If that fails we pull in
140  * the INDEX, and check there.
141  */
142 static int
143 pkg_do(char *pkg)
144 {
145     char *ch, tmp[PATH_MAX], tmp2[PATH_MAX], *latest = NULL;
146     Package plist;
147     struct index_entry *ie;
148     FILE *fp;
149     size_t len;
150
151     /* Suck in the contents list. */
152     plist.head = plist.tail = NULL;
153     plist.name = plist.origin = NULL;
154     snprintf(tmp, PATH_MAX, "%s/%s/%s", LOG_DIR, pkg, CONTENTS_FNAME);
155     fp = fopen(tmp, "r");
156     if (!fp) {
157         warnx("the package info for package '%s' is corrupt", pkg);
158         return 1;
159     }
160     read_plist(&plist, fp);
161     fclose(fp);
162     if (plist.name == NULL) {
163         warnx("%s does not appear to be a valid package!", pkg);
164         return 1;
165     }
166
167     /*
168      * First we check if the installed package has an origin, and try
169      * looking for it's Makefile. If we find the Makefile we get the
170      * latest version from there. If we fail, we start looking in the
171      * INDEX, first matching the origin and then the package name.
172      */
173     if (plist.origin != NULL && !UseINDEXOnly) {
174         snprintf(tmp, PATH_MAX, "%s/%s", PORTS_DIR, plist.origin);
175         if (isdir(tmp) && chdir(tmp) != FAIL && isfile("Makefile")) {
176             if ((latest = vpipe("/usr/bin/make -V PKGNAME", tmp)) == NULL)
177                 warnx("Failed to get PKGNAME from %s/Makefile!", tmp);
178             else
179                 show_version(plist, latest, "port");
180         }
181     }
182     if (latest == NULL) {
183         /* Report package as not found in INDEX if the INDEX is not required. */
184         if (IndexFile == NULL && !UseINDEXOnly)
185                 show_version(plist, NULL, plist.origin);
186         else {
187         /* We only pull in the INDEX once, if needed. */
188         if (SLIST_EMPTY(&Index)) {
189             if (!IndexFile)
190                 errx(2, "Unable to open %s in %s.", IndexPath, __func__);
191             while ((ch = fgetln(IndexFile, &len)) != NULL) {
192                 /*
193                  * Don't use strlcpy() because fgetln() doesn't
194                  * return a valid C string.
195                  */
196                 strncpy(tmp, ch, MIN(len, PATH_MAX));
197                 tmp[PATH_MAX-1] = '\0';
198                 /* The INDEX has pkgname|portdir|... */
199                 if ((ch = strchr(tmp, '|')) != NULL)
200                     ch[0] = '\0';
201                 if (ch != NULL && (ch = strchr(&ch[1], '|')) != NULL)
202                     ch[0] = '\0';
203                 /* Look backwards for the last two dirs = origin */
204                 while (ch != NULL && *--ch != '/')
205                     if (ch[0] == '\0')
206                         ch = NULL;
207                 while (ch != NULL && *--ch != '/')
208                     if (ch[0] == '\0')
209                         ch = NULL;
210                 if (ch == NULL)
211                     errx(2, "The INDEX does not appear to be valid!");
212                 if ((ie = malloc(sizeof(struct index_entry))) == NULL)
213                     errx(2, "Unable to allocate memory in %s.", __func__);
214                 bzero(ie, sizeof(struct index_entry));
215                 ie->name = strdup(tmp);
216                 ie->origin = strdup(&ch[1]);
217                 /* Who really cares if we reverse the index... */
218                 SLIST_INSERT_HEAD(&Index, ie, next);
219             }
220         }
221         /* Now that we've slurped in the INDEX... */
222         SLIST_FOREACH(ie, &Index, next) {
223             if (plist.origin != NULL) {
224                 if (strcmp(plist.origin, ie->origin) == 0)
225                     latest = strdup(ie->name);
226             } else {
227                 strlcpy(tmp, ie->name, PATH_MAX);
228                 strlcpy(tmp2, plist.name, PATH_MAX);
229                 /* Chop off the versions and compare. */
230                 if ((ch = strrchr(tmp, '-')) == NULL)
231                     errx(2, "The INDEX does not appear to be valid!");
232                 ch[0] = '\0';
233                 if ((ch = strrchr(tmp2, '-')) == NULL)
234                     warnx("%s is not a valid package!", plist.name);
235                 else
236                     ch[0] = '\0';
237                 if (strcmp(tmp2, tmp) == 0) {
238                     if (latest != NULL) {
239                         /* Multiple matches */
240                         snprintf(tmp, PATH_MAX, "%s|%s", latest, ie->name);
241                         free(latest);
242                         latest = strdup(tmp);
243                     } else
244                         latest = strdup(ie->name);
245                 }
246             }
247         }
248         if (latest == NULL)
249             show_version(plist, NULL, NULL);
250         else
251             show_version(plist, latest, "index");
252         }
253     }
254     if (latest != NULL)
255         free(latest);
256     free_plist(&plist);
257     return 0;
258 }
259
260 #define OUTPUT(c) ((PreventChars != NULL && !strchr(PreventChars, (c))) || \
261                         (LimitChars != NULL && strchr(LimitChars, (c))) || \
262                         (PreventChars == NULL && LimitChars == NULL))
263
264 /*
265  * Do the work of comparing and outputing. Ugly, but well that's what
266  * You get when you try to match perl output in C ;-).
267  */
268 void
269 show_version(Package plist, const char *latest, const char *source)
270 {
271     char *ch, tmp[PATH_MAX];
272     const char *ver;
273     int cmp = 0;
274
275     if (!plist.name || strlen(plist.name) == 0)
276         return;
277     if (ShowOrigin != FALSE && plist.origin != NULL)
278         strlcpy(tmp, plist.origin, PATH_MAX);
279     else {
280         strlcpy(tmp, plist.name, PATH_MAX);
281         if (!Verbose) {
282             if ((ch = strrchr(tmp, '-')) != NULL)
283                 ch[0] = '\0';
284         }
285     }
286     if (latest == NULL) {
287         if (source == NULL && OUTPUT('!')) {
288             printf("%-34s  !", tmp);
289             if (Verbose)
290                 printf("   Comparison failed");
291             printf("\n");
292         } else if (OUTPUT('?')) {
293             printf("%-34s  ?", tmp);
294             if (Verbose)
295                 printf("   orphaned: %s", plist.origin);
296             printf("\n");
297         }
298     } else if (strchr(latest,'|') != NULL) {
299         if (OUTPUT('*')) {
300             printf("%-34s  *", tmp);
301             if (Verbose) {
302                 strlcpy(tmp, latest, PATH_MAX);
303                 ch = strchr(tmp, '|');
304                 ch[0] = '\0';
305
306                 ver = strrchr(tmp, '-');
307                 ver = ver ? &ver[1] : tmp;
308                 printf("   multiple versions (index has %s", ver);
309                 do {
310                     ver = strrchr(&ch[1], '-');
311                     ver = ver ? &ver[1] : &ch[1];
312                     if ((ch = strchr(&ch[1], '|')) != NULL)
313                             ch[0] = '\0';
314                     printf(", %s", ver);
315                 } while (ch != NULL);
316                 printf(")");
317             }
318             printf("\n");
319         }
320     } else {
321         cmp = version_cmp(plist.name, latest);
322         ver = strrchr(latest, '-');
323         ver = ver ? &ver[1] : latest;
324         if (cmp < 0 && OUTPUT('<')) {
325             if (Quiet)
326                 printf("%s", tmp);
327             else {
328                 printf("%-34s  <", tmp);
329                 if (Verbose)
330                     printf("   needs updating (%s has %s)", source, ver);
331             }
332             printf("\n");
333         } else if (cmp == 0 && OUTPUT('=')) {
334             if (Quiet)
335                 printf("%s", tmp);
336             else {
337                 printf("%-34s  =", tmp);
338                 if (Verbose)
339                     printf("   up-to-date with %s", source);
340             }
341             printf("\n");
342         } else if (cmp > 0 && OUTPUT('>')) {
343             if (Quiet)
344                 printf("%s", tmp);
345             else {
346                 printf("%-34s  >", tmp);
347                 if (Verbose)
348                     printf("   succeeds %s (%s has %s)", source, source, ver);
349             }
350             printf("\n");
351         }
352     }
353 }
354
355 int
356 version_match(char *pattern, const char *pkgname)
357 {
358     int ret = 0;
359     int matchstream = 0;
360     FILE *fp = NULL;
361     Boolean isTMP = FALSE;
362
363     if (isURL(pkgname)) {
364         fp = fetchGetURL(pkgname, "");
365         isTMP = TRUE;
366         matchstream = 1;
367         if (fp == NULL)
368             errx(2, "Unable to open %s.", pkgname);
369     } else if (pkgname[0] == '/') {
370         fp = fopen(pkgname, "r");
371         isTMP = TRUE;
372         matchstream = 1;
373         if (fp == NULL)
374             errx(2, "Unable to open %s.", pkgname);
375     } else if (strcmp(pkgname, "-") == 0) {
376         fp = stdin;
377         matchstream = 1;
378     } else if (isURL(pattern)) {
379         fp = fetchGetURL(pattern, "");
380         isTMP = TRUE;
381         matchstream = -1;
382         if (fp == NULL)
383             errx(2, "Unable to open %s.", pattern);
384     } else if (pattern[0] == '/') {
385         fp = fopen(pattern, "r");
386         isTMP = TRUE;
387         matchstream = -1;
388         if (fp == NULL)
389             errx(2, "Unable to open %s.", pattern);
390     } else if (strcmp(pattern, "-") == 0) {
391         fp = stdin;
392         matchstream = -1;
393     } else {
394         ret = pattern_match(MATCH_GLOB, pattern, pkgname);
395     }
396
397     if (fp != NULL) {
398         size_t len;
399         char *line;
400         while ((line = fgetln(fp, &len)) != NULL) {
401             int match;
402             char *ch, ln[2048];
403             size_t lnlen;
404             if (len > 0 && line[len-1] == '\n')
405                 len --;
406             lnlen = len;
407             if (lnlen > sizeof(ln)-1)
408                 lnlen = sizeof(ln)-1;
409             memcpy(ln, line, lnlen);
410             ln[lnlen] = '\0';
411             if ((ch = strchr(ln, '|')) != NULL)
412                 ch[0] = '\0';
413             if (matchstream > 0)
414                 match = pattern_match(MATCH_GLOB, pattern, ln);
415             else
416                 match = pattern_match(MATCH_GLOB, ln, pkgname);
417             if (match == 1) {
418                 ret = 1;
419                 printf("%.*s\n", (int)len, line);
420             }
421         }
422         if (isTMP)
423             fclose(fp);
424     }
425
426     return ret;
427 }
428
429 void
430 cleanup(int sig)
431 {
432     if (sig)
433         exit(1);
434 }