415ad45
#define _GNU_SOURCE
415ad45
415ad45
#include <stdio.h>
415ad45
#include <stdlib.h>
415ad45
#include <unistd.h>
415ad45
#include <errno.h>
415ad45
#include <assert.h>
415ad45
#include <argp.h>
415ad45
#include <rpm/rpmlib.h>
415ad45
#include <err.h>
415ad45
415ad45
typedef enum {
415ad45
        RPMNVRCMP,
415ad45
        VERSNVRCMP,
415ad45
        RPMVERCMP,
415ad45
        STRVERSCMP,
415ad45
} comparitors;
415ad45
415ad45
static comparitors comparitor = RPMNVRCMP;
415ad45
415ad45
static inline void *xmalloc(size_t sz)
415ad45
{
415ad45
        void *ret = malloc(sz);
415ad45
415ad45
        assert(sz == 0 || ret != NULL);
415ad45
        return ret;
415ad45
}
415ad45
415ad45
static inline void *xrealloc(void *p, size_t sz)
415ad45
{
415ad45
        void *ret = realloc(p, sz);
415ad45
415ad45
        assert(sz == 0 || ret != NULL);
415ad45
        return ret;
415ad45
}
415ad45
415ad45
static inline char *xstrdup(const char * const s)
415ad45
{
415ad45
        void *ret = strdup(s);
415ad45
415ad45
        assert(s == NULL || ret != NULL);
415ad45
        return ret;
415ad45
}
415ad45
415ad45
static size_t
415ad45
read_file (const char *input, char **ret)
415ad45
{
415ad45
  FILE *in;
415ad45
  size_t s;
415ad45
  size_t sz = 2048;
415ad45
  size_t offset = 0;
415ad45
  char *text;
415ad45
415ad45
  if (!strcmp(input, "-"))
415ad45
    in = stdin;
415ad45
  else
415ad45
    in = fopen(input, "r");
415ad45
415ad45
  text = xmalloc (sz);
415ad45
415ad45
  if (!in)
415ad45
    err(1, "cannot open `%s'", input);
415ad45
415ad45
  while ((s = fread (text + offset, 1, sz - offset, in)) != 0)
415ad45
    {
415ad45
      offset += s;
415ad45
      if (sz - offset == 0)
415ad45
	{
415ad45
	  sz += 2048;
415ad45
	  text = xrealloc (text, sz);
415ad45
	}
415ad45
    }
415ad45
415ad45
  text[offset] = '\0';
415ad45
  *ret = text;
415ad45
415ad45
  if (in != stdin)
415ad45
    fclose(in);
415ad45
415ad45
  return offset + 1;
415ad45
}
415ad45
415ad45
/* returns name/version/release */
415ad45
/* NULL string pointer returned if nothing found */
415ad45
static void
415ad45
split_package_string (char *package_string, char **name,
415ad45
                     char **version, char **release)
415ad45
{
415ad45
  char *package_version, *package_release;
415ad45
415ad45
  /* Release */
415ad45
  package_release = strrchr (package_string, '-');
415ad45
415ad45
  if (package_release != NULL)
415ad45
      *package_release++ = '\0';
415ad45
415ad45
  *release = package_release;
415ad45
415ad45
  /* Version */
415ad45
  package_version = strrchr(package_string, '-');
415ad45
415ad45
  if (package_version != NULL)
415ad45
      *package_version++ = '\0';
415ad45
415ad45
  *version = package_version;
415ad45
  /* Name */
415ad45
  *name = package_string;
415ad45
415ad45
  /* Bubble up non-null values from release to name */
415ad45
  if (*name == NULL)
415ad45
    {
415ad45
      *name = (*version == NULL ? *release : *version);
415ad45
      *version = *release;
415ad45
      *release = NULL;
415ad45
    }
415ad45
  if (*version == NULL)
415ad45
    {
415ad45
      *version = *release;
415ad45
      *release = NULL;
415ad45
    }
415ad45
}
415ad45
415ad45
static int
415ad45
cmprpmversp(const void *p1, const void *p2)
415ad45
{
415ad45
        return rpmvercmp(*(char * const *)p1, *(char * const *)p2);
415ad45
}
415ad45
415ad45
static int
415ad45
cmpstrversp(const void *p1, const void *p2)
415ad45
{
415ad45
        return strverscmp(*(char * const *)p1, *(char * const *)p2);
415ad45
}
415ad45
415ad45
/*
415ad45
 * package name-version-release comparator for qsort
415ad45
 * expects p, q which are pointers to character strings (char *)
415ad45
 * which will not be altered in this function
415ad45
 */
415ad45
static int
415ad45
package_version_compare (const void *p, const void *q)
415ad45
{
415ad45
  char *local_p, *local_q;
415ad45
  char *lhs_name, *lhs_version, *lhs_release;
415ad45
  char *rhs_name, *rhs_version, *rhs_release;
415ad45
  int vercmpflag = 0;
415ad45
  int (*cmp)(const char *s1, const char *s2);
415ad45
415ad45
  switch(comparitor)
415ad45
    {
415ad45
    default: /* just to shut up -Werror=maybe-uninitialized */
415ad45
    case RPMNVRCMP:
415ad45
      cmp = rpmvercmp;
415ad45
      break;
415ad45
    case VERSNVRCMP:
415ad45
      cmp = strverscmp;
415ad45
      break;
415ad45
    case RPMVERCMP:
415ad45
      return cmprpmversp(p, q);
415ad45
      break;
415ad45
    case STRVERSCMP:
415ad45
      return cmpstrversp(p, q);
415ad45
      break;
415ad45
    }
415ad45
415ad45
  local_p = alloca (strlen (*(char * const *)p) + 1);
415ad45
  local_q = alloca (strlen (*(char * const *)q) + 1);
415ad45
415ad45
  /* make sure these allocated */
415ad45
  assert (local_p);
415ad45
  assert (local_q);
415ad45
415ad45
  strcpy (local_p, *(char * const *)p);
415ad45
  strcpy (local_q, *(char * const *)q);
415ad45
415ad45
  split_package_string (local_p, &lhs_name, &lhs_version, &lhs_release);
415ad45
  split_package_string (local_q, &rhs_name, &rhs_version, &rhs_release);
415ad45
415ad45
  /* Check Name and return if unequal */
415ad45
  vercmpflag = cmp ((lhs_name == NULL ? "" : lhs_name),
415ad45
                    (rhs_name == NULL ? "" : rhs_name));
415ad45
  if (vercmpflag != 0)
415ad45
    return vercmpflag;
415ad45
415ad45
  /* Check version and return if unequal */
415ad45
  vercmpflag = cmp ((lhs_version == NULL ? "" : lhs_version),
415ad45
                    (rhs_version == NULL ? "" : rhs_version));
415ad45
  if (vercmpflag != 0)
415ad45
    return vercmpflag;
415ad45
415ad45
  /* Check release and return the version compare value */
415ad45
  vercmpflag = cmp ((lhs_release == NULL ? "" : lhs_release),
415ad45
                    (rhs_release == NULL ? "" : rhs_release));
415ad45
415ad45
  return vercmpflag;
415ad45
}
415ad45
415ad45
static void
415ad45
add_input (const char *filename, char ***package_names, size_t *n_package_names)
415ad45
{
415ad45
  char *orig_input_buffer = NULL;
415ad45
  char *input_buffer;
415ad45
  char *position_of_newline;
415ad45
  char **names = *package_names;
415ad45
  char **new_names = NULL;
415ad45
  size_t n_names = *n_package_names;
415ad45
415ad45
  if (!*package_names)
415ad45
    new_names = names = xmalloc (sizeof (char *) * 2);
415ad45
415ad45
  if (read_file (filename, &orig_input_buffer) < 2)
415ad45
    {
415ad45
      if (new_names)
415ad45
	free (new_names);
415ad45
      if (orig_input_buffer)
415ad45
	free (orig_input_buffer);
415ad45
      return;
415ad45
    }
415ad45
415ad45
  input_buffer = orig_input_buffer;
415ad45
  while (input_buffer && *input_buffer &&
415ad45
	 (position_of_newline = strchrnul (input_buffer, '\n')))
415ad45
    {
415ad45
      size_t sz = position_of_newline - input_buffer;
415ad45
      char *new;
415ad45
415ad45
      if (sz == 0)
415ad45
	{
415ad45
	  input_buffer = position_of_newline + 1;
415ad45
	  continue;
415ad45
	}
415ad45
415ad45
      new = xmalloc (sz+1);
415ad45
      strncpy (new, input_buffer, sz);
415ad45
      new[sz] = '\0';
415ad45
415ad45
      names = xrealloc (names, sizeof (char *) * (n_names + 1));
415ad45
      names[n_names] = new;
415ad45
      n_names++;
415ad45
415ad45
      /* move buffer ahead to next line */
415ad45
      input_buffer = position_of_newline + 1;
415ad45
      if (*position_of_newline == '\0')
415ad45
	input_buffer = NULL;
415ad45
    }
415ad45
415ad45
  free (orig_input_buffer);
415ad45
415ad45
  *package_names = names;
415ad45
  *n_package_names = n_names;
415ad45
}
415ad45
415ad45
static char *
415ad45
help_filter (int key, const char *text, void *input __attribute__ ((unused)))
415ad45
{
415ad45
  return (char *)text;
415ad45
}
415ad45
415ad45
static struct argp_option options[] = {
415ad45
  { "comparitor", 'c', "COMPARITOR", 0, "[rpm-nvr-cmp|vers-nvr-cmp|rpmvercmp|strverscmp]", 0},
415ad45
  { 0, }
415ad45
};
415ad45
415ad45
struct arguments
415ad45
{
415ad45
  size_t ninputs;
415ad45
  size_t input_max;
415ad45
  char **inputs;
415ad45
};
415ad45
415ad45
static error_t
415ad45
argp_parser (int key, char *arg, struct argp_state *state)
415ad45
{
415ad45
  struct arguments *arguments = state->input;
415ad45
  switch (key)
415ad45
    {
415ad45
    case 'c':
415ad45
      if (!strcmp(arg, "rpm-nvr-cmp") || !strcmp(arg, "rpmnvrcmp"))
415ad45
        comparitor = RPMNVRCMP;
415ad45
      else if (!strcmp(arg, "vers-nvr-cmp") || !strcmp(arg, "versnvrcmp"))
415ad45
        comparitor = VERSNVRCMP;
415ad45
      else if (!strcmp(arg, "rpmvercmp"))
415ad45
        comparitor = RPMVERCMP;
415ad45
      else if (!strcmp(arg, "strverscmp"))
415ad45
        comparitor = STRVERSCMP;
415ad45
      else
415ad45
        err(1, "Invalid comparitor \"%s\"", arg);
415ad45
      break;
415ad45
    case ARGP_KEY_ARG:
415ad45
      assert (arguments->ninputs < arguments->input_max);
415ad45
      arguments->inputs[arguments->ninputs++] = xstrdup (arg);
415ad45
      break;
415ad45
    default:
415ad45
      return ARGP_ERR_UNKNOWN;
415ad45
    }
415ad45
  return 0;
415ad45
}
415ad45
415ad45
static struct argp argp = {
415ad45
  options, argp_parser, "[INPUT_FILES]",
415ad45
  "Sort a list of strings in RPM version sort order.",
415ad45
  NULL, help_filter, NULL
415ad45
};
415ad45
415ad45
int
415ad45
main (int argc, char *argv[])
415ad45
{
415ad45
  struct arguments arguments;
415ad45
  char **package_names = NULL;
415ad45
  size_t n_package_names = 0;
415ad45
  int i;
415ad45
415ad45
  memset (&arguments, 0, sizeof (struct arguments));
415ad45
  arguments.input_max = argc+1;
415ad45
  arguments.inputs = xmalloc ((arguments.input_max + 1)
415ad45
			      * sizeof (arguments.inputs[0]));
415ad45
  memset (arguments.inputs, 0, (arguments.input_max + 1)
415ad45
	  * sizeof (arguments.inputs[0]));
415ad45
415ad45
  /* Parse our arguments */
415ad45
  if (argp_parse (&argp, argc, argv, 0, 0, &arguments) != 0)
415ad45
    errx(1, "%s", "Error in parsing command line arguments\n");
415ad45
415ad45
  /* If there's no inputs in argv, add one for stdin */
415ad45
  if (!arguments.ninputs)
415ad45
    {
415ad45
      arguments.ninputs = 1;
415ad45
      arguments.inputs[0] = xmalloc (2);
415ad45
      strcpy(arguments.inputs[0], "-");
415ad45
    }
415ad45
415ad45
  for (i = 0; i < arguments.ninputs; i++)
415ad45
    add_input(arguments.inputs[i], &package_names, &n_package_names);
415ad45
415ad45
  if (package_names == NULL || n_package_names < 1)
415ad45
    errx(1, "Invalid input");
415ad45
415ad45
  qsort (package_names, n_package_names, sizeof (char *),
415ad45
	 package_version_compare);
415ad45
415ad45
  /* send sorted list to stdout */
415ad45
  for (i = 0; i < n_package_names; i++)
415ad45
    {
415ad45
      fprintf (stdout, "%s\n", package_names[i]);
415ad45
      free (package_names[i]);
415ad45
    }
415ad45
415ad45
  free (package_names);
415ad45
  for (i = 0; i < arguments.ninputs; i++)
415ad45
    free (arguments.inputs[i]);
415ad45
415ad45
  free (arguments.inputs);
415ad45
415ad45
  return 0;
415ad45
}