216 lines
5.1 KiB
C
216 lines
5.1 KiB
C
|
/* rpmsort: sort(1), but for RPM versions */
|
||
|
|
||
|
#include <popt.h>
|
||
|
#include <string.h>
|
||
|
|
||
|
#include <rpm/rpmlib.h>
|
||
|
|
||
|
#include "debug.h"
|
||
|
#include "system.h"
|
||
|
|
||
|
#define BUFSIZE 2048
|
||
|
|
||
|
static size_t read_file(const char *input, char **ret)
|
||
|
{
|
||
|
FILE *in;
|
||
|
size_t s, sz = BUFSIZE, offset = 0;
|
||
|
char *text;
|
||
|
|
||
|
if (!strcmp(input, "-"))
|
||
|
in = stdin;
|
||
|
else
|
||
|
in = fopen(input, "r");
|
||
|
|
||
|
text = xmalloc(sz);
|
||
|
|
||
|
if (!in) {
|
||
|
fprintf(stderr, "cannot open `%s'", input);
|
||
|
exit(EXIT_FAILURE);
|
||
|
}
|
||
|
|
||
|
while ((s = fread(text + offset, 1, sz - offset, in)) != 0) {
|
||
|
offset += s;
|
||
|
if (sz - offset == 0) {
|
||
|
sz += BUFSIZE;
|
||
|
text = xrealloc(text, sz);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
text[offset] = '\0';
|
||
|
*ret = text;
|
||
|
|
||
|
if (in != stdin)
|
||
|
fclose(in);
|
||
|
|
||
|
return offset + 1;
|
||
|
}
|
||
|
|
||
|
/* Returns name, version, and release, which will be NULL string pointers
|
||
|
* returned if nothing was found. */
|
||
|
static void split_package_string(char *package_string, char **name,
|
||
|
char **version, char **release)
|
||
|
{
|
||
|
char *package_version, *package_release;
|
||
|
|
||
|
/* Release */
|
||
|
package_release = strrchr(package_string, '-');
|
||
|
|
||
|
if (package_release != NULL)
|
||
|
*package_release++ = '\0';
|
||
|
|
||
|
*release = package_release;
|
||
|
|
||
|
/* Version */
|
||
|
package_version = strrchr(package_string, '-');
|
||
|
|
||
|
if (package_version != NULL)
|
||
|
*package_version++ = '\0';
|
||
|
|
||
|
*version = package_version;
|
||
|
/* Name */
|
||
|
*name = package_string;
|
||
|
|
||
|
/* Bubble up non-null values from release to name */
|
||
|
if (*name == NULL) {
|
||
|
*name = (*version == NULL ? *release : *version);
|
||
|
*version = *release;
|
||
|
*release = NULL;
|
||
|
}
|
||
|
if (*version == NULL) {
|
||
|
*version = *release;
|
||
|
*release = NULL;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* A package name-version-release comparator for qsort. It expects p, q which
|
||
|
* are pointers to character strings and will not be altered in this
|
||
|
* function. */
|
||
|
static int package_version_compare(const void *p, const void *q)
|
||
|
{
|
||
|
char *local_p, *local_q;
|
||
|
char *lhs_name, *lhs_version, *lhs_release;
|
||
|
char *rhs_name, *rhs_version, *rhs_release;
|
||
|
int vercmpflag = 0;
|
||
|
|
||
|
local_p = rstrdup(*(char * const *)p);
|
||
|
local_q = rstrdup(*(char * const *)q);
|
||
|
|
||
|
split_package_string(local_p, &lhs_name, &lhs_version, &lhs_release);
|
||
|
split_package_string(local_q, &rhs_name, &rhs_version, &rhs_release);
|
||
|
|
||
|
/* Check Name and return if unequal */
|
||
|
vercmpflag = strcmp((lhs_name == NULL ? "" : lhs_name),
|
||
|
(rhs_name == NULL ? "" : rhs_name));
|
||
|
if (vercmpflag != 0)
|
||
|
goto exit;
|
||
|
|
||
|
/* Check version and return if unequal */
|
||
|
vercmpflag = rpmvercmp((lhs_version == NULL ? "" : lhs_version),
|
||
|
(rhs_version == NULL ? "" : rhs_version));
|
||
|
if (vercmpflag != 0)
|
||
|
goto exit;
|
||
|
|
||
|
/* Check release and return the version compare value */
|
||
|
vercmpflag = rpmvercmp((lhs_release == NULL ? "" : lhs_release),
|
||
|
(rhs_release == NULL ? "" : rhs_release));
|
||
|
exit:
|
||
|
rfree(local_p);
|
||
|
rfree(local_q);
|
||
|
return vercmpflag;
|
||
|
}
|
||
|
|
||
|
static void add_input(const char *filename, char ***package_names,
|
||
|
size_t *n_package_names)
|
||
|
{
|
||
|
char *orig_input_buffer = NULL;
|
||
|
char *input_buffer;
|
||
|
char *position_of_newline;
|
||
|
char **names = *package_names;
|
||
|
char **new_names = NULL;
|
||
|
size_t n_names = *n_package_names;
|
||
|
|
||
|
if (!*package_names)
|
||
|
new_names = names = xmalloc(sizeof(char *) * 2);
|
||
|
|
||
|
if (read_file(filename, &orig_input_buffer) < 2) {
|
||
|
if (new_names)
|
||
|
free(new_names);
|
||
|
if (orig_input_buffer)
|
||
|
free(orig_input_buffer);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
input_buffer = orig_input_buffer;
|
||
|
while (input_buffer && *input_buffer &&
|
||
|
(position_of_newline = strchrnul(input_buffer, '\n'))) {
|
||
|
size_t sz = position_of_newline - input_buffer;
|
||
|
char *new;
|
||
|
|
||
|
if (sz == 0) {
|
||
|
input_buffer = position_of_newline + 1;
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
new = rstrndup(input_buffer, sz);
|
||
|
|
||
|
names = xrealloc(names, sizeof(char *) * (n_names + 1));
|
||
|
names[n_names] = new;
|
||
|
n_names++;
|
||
|
|
||
|
/* Move buffer ahead to next line. */
|
||
|
input_buffer = position_of_newline + 1;
|
||
|
if (*position_of_newline == '\0')
|
||
|
input_buffer = NULL;
|
||
|
}
|
||
|
|
||
|
free(orig_input_buffer);
|
||
|
|
||
|
*package_names = names;
|
||
|
*n_package_names = n_names;
|
||
|
}
|
||
|
|
||
|
static struct poptOption optionsTable[] = { POPT_AUTOHELP POPT_TABLEEND };
|
||
|
|
||
|
int main(int argc, const char *argv[])
|
||
|
{
|
||
|
poptContext optCon;
|
||
|
const char *arg;
|
||
|
char **package_names = NULL;
|
||
|
size_t n_package_names = 0;
|
||
|
char seen_file = 0;
|
||
|
|
||
|
optCon = poptGetContext(NULL, argc, argv, optionsTable, 0);
|
||
|
poptSetOtherOptionHelp(optCon, "<FILES>");
|
||
|
if (poptGetNextOpt(optCon) == 0) {
|
||
|
poptPrintUsage(optCon, stderr, 0);
|
||
|
exit(EXIT_FAILURE);
|
||
|
}
|
||
|
|
||
|
while ((arg = poptGetArg(optCon)) != NULL) {
|
||
|
add_input(arg, &package_names, &n_package_names);
|
||
|
seen_file = 1;
|
||
|
}
|
||
|
|
||
|
/* If there's no inputs in argv, add one for stdin. */
|
||
|
if (!seen_file)
|
||
|
add_input("-", &package_names, &n_package_names);
|
||
|
|
||
|
if (package_names == NULL || n_package_names < 1) {
|
||
|
fprintf(stderr, "Invalid input\n");
|
||
|
exit(EXIT_FAILURE);
|
||
|
}
|
||
|
|
||
|
qsort(package_names, n_package_names, sizeof(char *),
|
||
|
package_version_compare);
|
||
|
|
||
|
/* Send sorted list to stdout. */
|
||
|
for (int i = 0; i < n_package_names; i++) {
|
||
|
fprintf(stdout, "%s\n", package_names[i]);
|
||
|
free(package_names[i]);
|
||
|
}
|
||
|
|
||
|
free(package_names);
|
||
|
poptFreeContext(optCon);
|
||
|
return 0;
|
||
|
}
|