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;
 | 
						|
}
 | 
						|
 |