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