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