616 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			616 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			C++
		
	
	
	
| /* dump_setup.cc
 | |
| 
 | |
|    Copyright 2001, 2002, 2003, 2004, 2005, 2008, 2010, 2011, 2012 Red Hat, Inc.
 | |
| 
 | |
| This file is part of Cygwin.
 | |
| 
 | |
| This software is a copyrighted work licensed under the terms of the
 | |
| Cygwin license.  Please consult the file "CYGWIN_LICENSE" for
 | |
| details. */
 | |
| 
 | |
| #include <stdio.h>
 | |
| #include <stdlib.h>
 | |
| #include <ctype.h>
 | |
| #include <stdlib.h>
 | |
| #include <string.h>
 | |
| #include <io.h>
 | |
| #include <sys/stat.h>
 | |
| #include <errno.h>
 | |
| #define WIN32_NO_STATUS	/* Disable status codes in winnt.h since we include
 | |
| 			   ntstatus.h for extended status codes below. */
 | |
| #include <windows.h>
 | |
| #undef WIN32_NO_STATUS
 | |
| #include <winternl.h>
 | |
| #include <ntstatus.h>
 | |
| #include "path.h"
 | |
| #include <zlib.h>
 | |
| 
 | |
| static int package_len = 20;
 | |
| static unsigned int version_len = 10;
 | |
| 
 | |
| 
 | |
| typedef struct
 | |
| {
 | |
|   char pkgtar[MAX_PATH + 1];
 | |
|   char pkg[MAX_PATH + 1];
 | |
|   char ver[MAX_PATH + 1];
 | |
|   char tail[MAX_PATH + 1];
 | |
|   char what[16];
 | |
| } fileparse;
 | |
| 
 | |
| static int
 | |
| find_tar_ext (const char *path)
 | |
| {
 | |
|   char *p = strchr (path, '\0') - 9;
 | |
|   if (p <= path)
 | |
|     return 0;
 | |
|   if ((p = strstr (p, ".tar")) != NULL)
 | |
|     return p - path;
 | |
|   else
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| static char *
 | |
| base (const char *s)
 | |
| {
 | |
|   if (!s)
 | |
|     return 0;
 | |
|   const char *rv = s;
 | |
|   while (*s)
 | |
|     {
 | |
|       if ((*s == '/' || *s == ':' || *s == '\\') && s[1])
 | |
| 	rv = s + 1;
 | |
|       s++;
 | |
|     }
 | |
|   return (char *) rv;
 | |
| }
 | |
| 
 | |
| /* Parse a filename into package, version, and extension components. */
 | |
| int
 | |
| parse_filename (const char *in_fn, fileparse& f)
 | |
| {
 | |
|   char *p, *ver;
 | |
|   char fn[strlen (in_fn) + 1];
 | |
| 
 | |
|   strcpy (fn, in_fn);
 | |
|   int n = find_tar_ext (fn);
 | |
| 
 | |
|   if (!n)
 | |
|     return 0;
 | |
| 
 | |
|   strcpy (f.tail, fn + n);
 | |
|   fn[n] = '\0';
 | |
|   f.pkg[0] = f.what[0] = '\0';
 | |
|   p = base (fn);
 | |
|   for (ver = p; *ver; ver++)
 | |
|     if (*ver != '-')
 | |
|       continue;
 | |
|     else if (isdigit (ver[1]))
 | |
|       {
 | |
| 	*ver++ = '\0';
 | |
| 	strcpy (f.pkg, p);
 | |
| 	break;
 | |
|       }
 | |
|     else if (strcasecmp (ver, "-src") == 0 ||
 | |
| 	     strcasecmp (ver, "-patch") == 0)
 | |
|       {
 | |
| 	*ver++ = '\0';
 | |
| 	strcpy (f.pkg, p);
 | |
| 	strcpy (f.what, strlwr (ver));
 | |
| 	strcpy (f.pkgtar, p);
 | |
| 	strcat (f.pkgtar, f.tail);
 | |
| 	ver = strchr (ver, '\0');
 | |
| 	break;
 | |
|       }
 | |
| 
 | |
|   if (!f.pkg[0])
 | |
|     strcpy (f.pkg, p);
 | |
| 
 | |
|   if (!f.what[0])
 | |
|     {
 | |
|       int n;
 | |
|       p = strchr (ver, '\0');
 | |
|       strcpy (f.pkgtar, in_fn);
 | |
|       if ((p -= 4) >= ver && strcasecmp (p, "-src") == 0)
 | |
| 	n = 4;
 | |
|       else if ((p -= 2) >= ver && strcasecmp (p, "-patch") == 0)
 | |
| 	n = 6;
 | |
|       else
 | |
| 	n = 0;
 | |
|       if (n)
 | |
| 	{
 | |
| 	  strcpy (f.what, p + 1);
 | |
| 	  *p = '\0';
 | |
| 	  p = f.pkgtar + (p - fn) + n;
 | |
| 	  memmove (p - 4, p, strlen (p));
 | |
| 	}
 | |
|     }
 | |
| 
 | |
|   strcpy (f.ver, *ver ? ver : "0.0");
 | |
|   return 1;
 | |
| }
 | |
| 
 | |
| static bool
 | |
| dump_file (const char *msg, const char *fn)
 | |
| {
 | |
|   char buf[4096];
 | |
|   bool printed = false;
 | |
|   bool found = false;
 | |
|   size_t len = strlen (fn);
 | |
|   char *path = cygpath ("/etc/setup/setup.rc", NULL);
 | |
|   FILE *fp = fopen (path, "rt");
 | |
| 
 | |
|   if (fp)
 | |
|     {
 | |
|       while (fgets (buf, 4096, fp))
 | |
|       	{
 | |
| 	  if (found)
 | |
| 	    {
 | |
| 	      char *bufp = buf;
 | |
| 
 | |
| 	      if (*bufp == '\t')
 | |
| 	      	++bufp;
 | |
| 	      char *p = strchr (bufp, '\0');
 | |
| 	      printf ("%s%s%s", msg, bufp,
 | |
| 				(p == bufp) || p[-1] != '\n' ? "\n" : "");
 | |
| 	      printed = true;
 | |
| 	      break;
 | |
| 	    }
 | |
| 	  if (!strncmp (buf, fn, len) && buf[len] == '\n')
 | |
| 	    found = true;
 | |
| 	}
 | |
|       fclose (fp);
 | |
|     }
 | |
|   return printed;
 | |
| }
 | |
| 
 | |
| struct pkgver
 | |
| {
 | |
|   char *name;
 | |
|   char *ver;
 | |
| };
 | |
| 
 | |
| extern "C" {
 | |
| int
 | |
| compar (const void *a, const void *b)
 | |
| {
 | |
|   const pkgver *pa = (const pkgver *) a;
 | |
|   const pkgver *pb = (const pkgver *) b;
 | |
|   return strcasecmp (pa->name, pb->name);
 | |
| }
 | |
| }
 | |
| 
 | |
| int
 | |
| match_argv (char **argv, const char *name)
 | |
| {
 | |
|   if (!argv || !*argv)
 | |
|     return -1;
 | |
|   for (char **a = argv; *a; a++)
 | |
|     if (strcasecmp (*a, name) == 0)
 | |
|       return a - argv + 1;
 | |
|   return 0;
 | |
| }
 | |
| 
 | |
| static bool
 | |
| could_not_access (int verbose, char *filename, char *package, const char *type)
 | |
| {
 | |
|   switch (errno)
 | |
|     {
 | |
|       case ENOTDIR:
 | |
| 	break;
 | |
|       case ENOENT:
 | |
| 	if (verbose)
 | |
| 	  printf ("Missing %s: /%s from package %s\n",
 | |
| 		  type, filename, package);
 | |
| 	return true;
 | |
|       case EACCES:
 | |
| 	if (verbose)
 | |
| 	  printf ("Unable to access %s /%s from package %s\n",
 | |
| 		  type, filename, package);
 | |
| 	return true;
 | |
|     }
 | |
|   return false;
 | |
| }
 | |
| 
 | |
| static const WCHAR tfx_chars[] = {
 | |
| 	    0, 0xf000 |   1, 0xf000 |   2, 0xf000 |   3,
 | |
|  0xf000 |   4, 0xf000 |   5, 0xf000 |   6, 0xf000 |   7,
 | |
|  0xf000 |   8, 0xf000 |   9, 0xf000 |  10, 0xf000 |  11,
 | |
|  0xf000 |  12, 0xf000 |  13, 0xf000 |  14, 0xf000 |  15,
 | |
|  0xf000 |  16, 0xf000 |  17, 0xf000 |  18, 0xf000 |  19,
 | |
|  0xf000 |  20, 0xf000 |  21, 0xf000 |  22, 0xf000 |  23,
 | |
|  0xf000 |  24, 0xf000 |  25, 0xf000 |  26, 0xf000 |  27,
 | |
|  0xf000 |  28, 0xf000 |  29, 0xf000 |  30, 0xf000 |  31,
 | |
| 	  ' ',          '!', 0xf000 | '"',          '#',
 | |
| 	  '$',          '%',          '&',           39,
 | |
| 	  '(',          ')', 0xf000 | '*',          '+',
 | |
| 	  ',',          '-',          '.',          '\\',
 | |
| 	  '0',          '1',          '2',          '3',
 | |
| 	  '4',          '5',          '6',          '7',
 | |
| 	  '8',          '9', 0xf000 | ':',          ';',
 | |
|  0xf000 | '<',          '=', 0xf000 | '>', 0xf000 | '?',
 | |
| 	  '@',          'A',          'B',          'C',
 | |
| 	  'D',          'E',          'F',          'G',
 | |
| 	  'H',          'I',          'J',          'K',
 | |
| 	  'L',          'M',          'N',          'O',
 | |
| 	  'P',          'Q',          'R',          'S',
 | |
| 	  'T',          'U',          'V',          'W',
 | |
| 	  'X',          'Y',          'Z',          '[',
 | |
| 	  '\\',          ']',          '^',          '_',
 | |
| 	  '`',          'a',          'b',          'c',
 | |
| 	  'd',          'e',          'f',          'g',
 | |
| 	  'h',          'i',          'j',          'k',
 | |
| 	  'l',          'm',          'n',          'o',
 | |
| 	  'p',          'q',          'r',          's',
 | |
| 	  't',          'u',          'v',          'w',
 | |
| 	  'x',          'y',          'z',          '{',
 | |
|  0xf000 | '|',          '}',          '~',          127
 | |
| };
 | |
| 
 | |
| static void
 | |
| transform_chars (PWCHAR path, PWCHAR path_end)
 | |
| {
 | |
|   for (; path <= path_end; ++path)
 | |
|     if (*path < 128)
 | |
|       *path = tfx_chars[*path];
 | |
| }
 | |
| 
 | |
| extern "C" NTAPI NTSTATUS NtQueryAttributesFile (POBJECT_ATTRIBUTES,
 | |
| 						 PFILE_BASIC_INFORMATION);
 | |
| 
 | |
| /* This function checks for file existance and fills the stat structure
 | |
|    with only the required mode info.  We're using a native NT function
 | |
|    here, otherwise we wouldn't be able to check for files with special
 | |
|    characters not valid in Win32, and espacially not valid using the
 | |
|    ANSI API. */
 | |
| static int
 | |
| simple_nt_stat (const char *filename, struct stat *st)
 | |
| {
 | |
|   size_t len = mbstowcs (NULL, filename, 0) + 1;
 | |
|   WCHAR path[len + 8];	/* Enough space for the NT prefix */
 | |
|   PWCHAR p = path;
 | |
|   UNICODE_STRING upath;
 | |
|   OBJECT_ATTRIBUTES attr;
 | |
|   FILE_BASIC_INFORMATION fbi;
 | |
|   NTSTATUS status;
 | |
| 
 | |
|   wcscpy (p, L"\\??\\");
 | |
|   p += 4;
 | |
|   if (filename[0] == '\\' && filename[1] == '\\')
 | |
|     {
 | |
|       wcscpy (p, L"UNC");
 | |
|       p += 3;
 | |
|       p += mbstowcs (p, filename + 1, len);
 | |
|     }
 | |
|   else
 | |
|     p += mbstowcs (p, filename, len);
 | |
|   /* Remove trailing backslashes.  NT functions don't like them. */
 | |
|   if (p[-1] == L'\\')
 | |
|     *--p = L'\0';
 | |
|   /* Skip prefix and drive, otherwise question marks and colons are converted
 | |
|      as well. */
 | |
|   transform_chars (path + 7, p);
 | |
|   RtlInitUnicodeString (&upath, path);
 | |
|   InitializeObjectAttributes (&attr, &upath, OBJ_CASE_INSENSITIVE, NULL, NULL);
 | |
|   status = NtQueryAttributesFile (&attr, &fbi);
 | |
|   if (NT_SUCCESS (status))
 | |
|     {
 | |
|       st->st_mode = (fbi.FileAttributes & FILE_ATTRIBUTE_DIRECTORY)
 | |
| 		    ? S_IFDIR : S_IFREG;
 | |
|       return 0;
 | |
|     }
 | |
|   if (status == STATUS_OBJECT_PATH_NOT_FOUND
 | |
|       || status == STATUS_OBJECT_NAME_INVALID
 | |
|       || status == STATUS_BAD_NETWORK_PATH
 | |
|       || status == STATUS_BAD_NETWORK_NAME
 | |
|       || status == STATUS_NO_MEDIA_IN_DEVICE
 | |
|       || status == STATUS_OBJECT_NAME_NOT_FOUND
 | |
|       || status == STATUS_NO_SUCH_FILE)
 | |
|     errno = ENOENT;
 | |
|   else
 | |
|     errno = EACCES;
 | |
|   return -1;
 | |
| }
 | |
| 
 | |
| static bool
 | |
| directory_exists (int verbose, char *filename, char *package)
 | |
| {
 | |
|   struct stat status;
 | |
|   if (simple_nt_stat(cygpath("/", filename, NULL), &status))
 | |
|     {
 | |
|       if (could_not_access (verbose, filename, package, "directory"))
 | |
| 	return false;
 | |
|     }
 | |
|   else if (!S_ISDIR(status.st_mode))
 | |
|     {
 | |
|       if (verbose)
 | |
| 	printf ("Directory/file mismatch: /%s from package %s\n", filename, package);
 | |
|       return false;
 | |
|     }
 | |
|   return true;
 | |
| }
 | |
| 
 | |
| static bool
 | |
| file_exists (int verbose, char *filename, const char *alt, char *package)
 | |
| {
 | |
|   struct stat status;
 | |
|   if (simple_nt_stat(cygpath("/", filename, NULL), &status) &&
 | |
|       (!alt || simple_nt_stat(cygpath("/", filename, alt, NULL), &status)))
 | |
|     {
 | |
|       if (could_not_access (verbose, filename, package, "file"))
 | |
| 	return false;
 | |
|     }
 | |
|   else if (!S_ISREG(status.st_mode))
 | |
|     {
 | |
|       if (verbose)
 | |
| 	printf ("File type mismatch: /%s from package %s\n", filename, package);
 | |
|       return false;
 | |
|     }
 | |
|   return true;
 | |
| }
 | |
| 
 | |
| static gzFile
 | |
| open_package_list (char *package)
 | |
| {
 | |
|   char filelist[MAX_PATH + 1] = "/etc/setup/";
 | |
|   strcat (strcat (filelist, package), ".lst.gz");
 | |
|   if (!file_exists (false, filelist + 1, NULL, NULL))
 | |
|     return NULL;
 | |
| 
 | |
|   gzFile fp;
 | |
| #ifndef ZLIB_VERSION
 | |
|   fp = NULL;
 | |
| #else
 | |
|   char *fn = cygpath (filelist, NULL);
 | |
|   fp = gzopen (fn, "rb9");
 | |
|   free (fn);
 | |
| #endif
 | |
| 
 | |
|   return fp;
 | |
| }
 | |
| 
 | |
| static bool
 | |
| check_package_files (int verbose, char *package)
 | |
| {
 | |
|   gzFile fp = open_package_list (package);
 | |
|   if (!fp)
 | |
|     {
 | |
|       if (verbose)
 | |
| 	printf ("Empty package %s\n", package);
 | |
|       return true;
 | |
|     }
 | |
| 
 | |
|   bool result = true;
 | |
|   char buf[MAX_PATH + 1];
 | |
|   while (gzgets (fp, buf, MAX_PATH))
 | |
|     {
 | |
|       char *filename = strtok(buf, "\n");
 | |
| 
 | |
|       if (*filename == '/')
 | |
| 	++filename;
 | |
|       else if (!strncmp (filename, "./", 2))
 | |
| 	filename += 2;
 | |
| 
 | |
|       if (filename[strlen (filename) - 1] == '/')
 | |
| 	{
 | |
| 	  if (!directory_exists (verbose, filename, package))
 | |
| 	    result = false;
 | |
| 	}
 | |
|       else if (strstr (filename, "/postinstall/"))
 | |
| 	{
 | |
| 	  if (!file_exists (verbose, filename, ".done", package))
 | |
| 	    result = false;
 | |
| 	}
 | |
|       else
 | |
| 	{
 | |
| 	  if (!file_exists (verbose, filename, ".lnk", package))
 | |
| 	    result = false;
 | |
| 	}
 | |
|     }
 | |
| 
 | |
|   gzclose (fp);
 | |
|   return result;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Returns a calloc'd sorted list of packages or NULL if no info.
 | |
|  * The last entry in the list is {NULL,NULL}.
 | |
|  */
 | |
| static pkgver *
 | |
| get_packages (char **argv)
 | |
| {
 | |
|   char *setup = cygpath ("/etc/setup/installed.db", NULL);
 | |
|   FILE *fp = fopen (setup, "rt");
 | |
| 
 | |
|   if (fp == NULL)
 | |
|     return NULL;
 | |
| 
 | |
|   int nlines;
 | |
|   nlines = 0;
 | |
|   char buf[4096];
 | |
|   while (fgets (buf, 4096, fp))
 | |
|     nlines += 2;	/* potentially binary + source */
 | |
|   if (!nlines)
 | |
|     {
 | |
|       fclose (fp);
 | |
|       return NULL;
 | |
|     }
 | |
|   rewind (fp);
 | |
| 
 | |
|   pkgver *packages;
 | |
| 
 | |
|   packages = (pkgver *) calloc (nlines + 1, sizeof(packages[0]));
 | |
|   int n;
 | |
|   for (n = 0; fgets (buf, 4096, fp) && n < nlines;)
 | |
|     {
 | |
|       char *package = strtok (buf, " ");
 | |
|       if (!package || !*package || !match_argv (argv, package))
 | |
| 	continue;
 | |
|       for (int i = 0; i < 2; i++)
 | |
| 	{
 | |
| 	  fileparse f;
 | |
| 	  char *tar = strtok (NULL, " ");
 | |
| 	  if (!tar || !*tar || !parse_filename (tar, f))
 | |
| 	    break;
 | |
| 
 | |
| 	  int len = strlen (package);
 | |
| 	  if (f.what[0])
 | |
| 	    len += strlen (f.what) + 1;
 | |
| 	  if (len > package_len)
 | |
| 	    package_len = len;
 | |
| 	  packages[n].name = (char *) malloc (len + 1);
 | |
| 	  strcpy (packages[n].name, package);
 | |
| 	  if (f.what[0])
 | |
| 	    strcat (strcat (packages[n].name, "-"), f.what);
 | |
| 	  packages[n].ver = strdup (f.ver);
 | |
| 	  if (strlen(f.ver) > version_len)
 | |
| 	    version_len = strlen(f.ver);
 | |
| 	  n++;
 | |
| 	  if (strtok (NULL, " ") == NULL)
 | |
| 	    break;
 | |
| 	}
 | |
|     }
 | |
| 
 | |
|   packages[n].name = packages[n].ver = NULL;
 | |
| 
 | |
|   qsort (packages, n, sizeof (packages[0]), compar);
 | |
| 
 | |
|   fclose (fp);
 | |
| 
 | |
|   return packages;
 | |
| }
 | |
| 
 | |
| void
 | |
| dump_setup (int verbose, char **argv, bool check_files)
 | |
| {
 | |
|   pkgver *packages = get_packages(argv);
 | |
| 
 | |
|   puts ("Cygwin Package Information");
 | |
|   if (packages == NULL)
 | |
|     {
 | |
|       puts ("No setup information found");
 | |
|       return;
 | |
|     }
 | |
| 
 | |
|   if (verbose)
 | |
|     {
 | |
|       bool need_nl = dump_file ("Last downloaded files to: ", "last-cache");
 | |
|       if (dump_file ("Last downloaded files from: ", "last-mirror") || need_nl)
 | |
| 	puts ("");
 | |
|     }
 | |
| 
 | |
|   printf ("%-*s %-*s%s\n", package_len, "Package",
 | |
| 			   check_files ? version_len : 7, "Version",
 | |
| 			   check_files ? "     Status" : "");
 | |
|   for (int i = 0; packages[i].name; i++)
 | |
|     {
 | |
|       if (check_files)
 | |
| 	printf ("%-*s %-*s%s\n", package_len, packages[i].name,
 | |
| 		version_len, packages[i].ver,
 | |
| 		check_package_files (verbose, packages[i].name)
 | |
| 		  ? "     OK" : "     Incomplete");
 | |
|       else
 | |
| 	printf ("%-*s %s\n", package_len, packages[i].name, packages[i].ver);
 | |
|       fflush(stdout);
 | |
|     }
 | |
| 
 | |
|   free (packages);
 | |
| 
 | |
|   return;
 | |
| }
 | |
| 
 | |
| void
 | |
| package_list (int verbose, char **argv)
 | |
| {
 | |
|   pkgver *packages = get_packages(argv);
 | |
|   if (packages == NULL)
 | |
|     {
 | |
|       puts ("No setup information found");
 | |
|       return;
 | |
|     }
 | |
| 
 | |
|   for (int i = 0; packages[i].name; i++)
 | |
|     {
 | |
|       gzFile fp = open_package_list (packages[i].name);
 | |
|       if (!fp)
 | |
| 	{
 | |
| 	  if (verbose)
 | |
| 	    printf ("Can't open file list /etc/setup/%s.lst.gz for package %s\n",
 | |
| 		packages[i].name, packages[i].name);
 | |
| 	  continue;
 | |
| 	}
 | |
| 
 | |
|       if (verbose)
 | |
| 	printf ("Package: %s-%s\n", packages[i].name, packages[i].ver);
 | |
| 
 | |
|       char buf[MAX_PATH + 1];
 | |
|       while (gzgets (fp, buf, MAX_PATH))
 | |
| 	{
 | |
| 	  char *lastchar = strchr(buf, '\n');
 | |
| 	  if (lastchar[-1] != '/')
 | |
| 	    printf ("%s/%s", (verbose?"    ":""), buf);
 | |
| 	}
 | |
| 
 | |
|       gzclose (fp);
 | |
|     }
 | |
| 
 | |
|   free (packages);
 | |
| 
 | |
|   return;
 | |
| }
 | |
| 
 | |
| void
 | |
| package_find (int verbose, char **argv)
 | |
| {
 | |
|   pkgver *packages = get_packages(NULL);
 | |
|   if (packages == NULL)
 | |
|     {
 | |
|       puts ("No setup information found");
 | |
|       return;
 | |
|     }
 | |
| 
 | |
|   for (int i = 0; packages[i].name; i++)
 | |
|     {
 | |
|       gzFile fp = open_package_list (packages[i].name);
 | |
|       if (!fp)
 | |
| 	continue;
 | |
| 
 | |
|       char buf[MAX_PATH + 2];
 | |
|       buf[0] = '/';
 | |
|       while (gzgets (fp, buf + 1, MAX_PATH))
 | |
| 	{
 | |
| 	  char *filename = strtok(buf, "\n");
 | |
| 	  int flen = strlen (filename);
 | |
| 	  if (filename[flen - 1] != '/')
 | |
| 	    {
 | |
| 	      // FIXME: verify that /bin is mounted on /usr/bin; ditto for /lib
 | |
| 	      bool is_alias = !strncmp(filename, "/usr/bin/", 9) ||
 | |
| 			      !strncmp(filename, "/usr/lib/", 9);
 | |
| 	      int a = match_argv (argv, filename);
 | |
| 	      if (!a && is_alias)
 | |
| 		a = match_argv (argv, filename + 4);
 | |
| 	      if (!a && !strcmp(filename + flen - 4, ".exe"))
 | |
| 		{
 | |
| 		  filename[flen - 4] = '\0';
 | |
| 		  a = match_argv (argv, filename);
 | |
| 		}
 | |
| 	      if (!a && is_alias)
 | |
| 		a = match_argv (argv, filename + 4);
 | |
| 	      if (a > 0)
 | |
| 		{
 | |
| 		  if (verbose)
 | |
| 		    printf ("%s: found in package ", filename);
 | |
| 		  printf ("%s-%s\n", packages[i].name, packages[i].ver);
 | |
| 		}
 | |
| 	    }
 | |
| 	}
 | |
| 
 | |
|       gzclose (fp);
 | |
|     }
 | |
| 
 | |
|   free (packages);
 | |
| 
 | |
|   return;
 | |
| }
 | |
| 
 |