Import Upstream version 0.23

This commit is contained in:
openKylinBot 2022-05-13 20:14:07 +08:00
commit 55979f7c15
32 changed files with 4591 additions and 0 deletions

45
Makefile Normal file
View File

@ -0,0 +1,45 @@
define \n
endef
PREFIX ?= /usr
VENDOR ?= $(shell dpkg-vendor --query Vendor | tr '[:upper:]' '[:lower:]')
CPPFLAGS = $(shell dpkg-buildflags --get CPPFLAGS)
CFLAGS = $(shell dpkg-buildflags --get CFLAGS)
CFLAGS += -Wall -Wextra -g -O2 -std=gnu99
LDFLAGS = $(shell dpkg-buildflags --get LDFLAGS)
build: debian-distro-info ubuntu-distro-info
%-distro-info: %-distro-info.c distro-info-util.*
$(CC) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) -o $@ $<
install: debian-distro-info ubuntu-distro-info
install -d $(DESTDIR)$(PREFIX)/bin
install -m 755 $^ $(DESTDIR)$(PREFIX)/bin
ln -s $(VENDOR)-distro-info $(DESTDIR)$(PREFIX)/bin/distro-info
install -d $(DESTDIR)$(PREFIX)/share/man/man1
install -m 644 $(wildcard doc/*.1) $(DESTDIR)$(PREFIX)/share/man/man1
install -d $(DESTDIR)$(PREFIX)/share/perl5/Debian
install -m 644 $(wildcard perl/Debian/*.pm) $(DESTDIR)$(PREFIX)/share/perl5/Debian
cd python && python3 setup.py install --root="$(DESTDIR)" --no-compile --install-layout=deb
test: test-commandline test-perl test-python
test-commandline: debian-distro-info ubuntu-distro-info
./test-debian-distro-info
./test-ubuntu-distro-info
test-perl:
cd perl && ./test.pl
test-python:
$(foreach python,$(shell py3versions -r),cd python && $(python) setup.py test$(\n))
clean:
rm -rf debian-distro-info ubuntu-distro-info python/build python/*.egg-info python/.pylint.d
find python -name '*.pyc' -delete
.PHONY: build clean install test test-commandline test-perl test-python

66
debian-distro-info.c Normal file
View File

@ -0,0 +1,66 @@
/*
* Copyright (C) 2012-2013, Benjamin Drung <bdrung@debian.org>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#define DEBIAN
#define CSV_NAME "debian"
#define CSV_HEADER "version,codename,series,created,release,eol"
#define DISTRO_NAME "Debian"
#define NAME "debian-distro-info"
// C standard libraries
#include <stdlib.h>
#include "distro-info-util.h"
static bool filter_devel(const date_t *date, const distro_t *distro) {
return created(date, distro) && !released(date, distro) &&
*distro->version == '\0';
}
static bool filter_oldstable(const date_t *date, const distro_t *distro) {
return created(date, distro) && released(date, distro);
}
static bool filter_testing(const date_t *date, const distro_t *distro) {
return created(date, distro) && !released(date, distro);
}
static const distro_t *select_first(const distro_elem_t *distro_list) {
return distro_list->distro;
}
static const distro_t *select_oldstable(const distro_elem_t *distro_list) {
const distro_t *newest;
const distro_t *second = NULL;
newest = distro_list->distro;
while(distro_list != NULL) {
distro_list = distro_list->next;
if(distro_list) {
if(date_ge(distro_list->distro->milestones[MILESTONE_RELEASE],
newest->milestones[MILESTONE_RELEASE])) {
second = newest;
newest = distro_list->distro;
} else if(second && date_ge(distro_list->distro->milestones[MILESTONE_RELEASE],
second->milestones[MILESTONE_RELEASE])) {
second = distro_list->distro;
}
}
}
return second;
}
#include "distro-info-util.c"

947
distro-info-util.c Normal file
View File

@ -0,0 +1,947 @@
/*
* Copyright (C) 2012-2014, Benjamin Drung <bdrung@debian.org>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
// C standard libraries
#include <assert.h>
#include <errno.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <getopt.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include "distro-info-util.h"
/* All recognised dated database tags for milestones
* (corresponding to distro_t date_t's).
*
* NOTE: Must be kept in sync with MILESTONE enum.
*/
static char *milestones[] = {"created"
,"release"
,"eol"
#ifdef UBUNTU
,"eol-server"
,"eol-esm"
#endif
};
static unsigned int days_in_month[] = {31, 28, 31, 30, 31, 30,
31, 31, 30, 31, 30, 31};
static inline int milestone_to_index(const char *milestone) {
int i;
assert(milestone);
for(i = 0; i < (int)MILESTONE_COUNT; i++) {
if (!strcmp(milestone, milestones[i])) {
return i;
}
}
return -1;
}
static inline char *index_to_milestone(unsigned int i) {
return milestones[i];
}
static char *read_full_file(const char *filename) {
char *content;
FILE *f;
size_t size;
struct stat stat;
f = fopen(filename, "r");
if(unlikely(f == NULL)) {
fprintf(stderr, NAME ": Failed to open %s: %s\n", filename,
strerror(errno));
return NULL;
}
fstat(fileno(f), &stat);
if(unlikely(stat.st_size < 0)) {
fprintf(stderr, NAME ": %s has a negative file size.\n", filename);
return NULL;
}
size = stat.st_size;
content = malloc(size + 1);
if(unlikely(fread(content, sizeof(char), size, f) != size)) {
fprintf(stderr, NAME ": Failed to read %zu bytes from %s.\n", size,
filename);
free(content);
return NULL;
}
content[size] = '\0';
fclose(f);
return content;
}
static inline bool is_leap_year(const int year) {
return (year % 4 == 0 && year % 100 != 0) || year % 400 == 0;
}
static inline bool is_valid_date(const date_t *date) {
return date->month >= 1 && date->month <= 12 &&
date->day >= 1 &&
date->day <= (is_leap_year(date->year) &&
date->month == 2 ? 29 : days_in_month[date->month-1]);
}
static time_t date_to_secs(const date_t *date) {
time_t seconds = 0;
struct tm tm;
assert(date);
memset(&tm, '\0', sizeof(struct tm));
tm.tm_year = date->year - 1900;
tm.tm_mon = date->month - 1;
tm.tm_mday = date->day;
seconds = mktime(&tm);
return seconds;
}
static inline size_t date_diff(const date_t *date1, const date_t *date2) {
time_t time1;
time_t time2;
time_t diff;
unsigned int days;
time1 = date_to_secs(date1);
time2 = date_to_secs(date2);
diff = (time_t)difftime(time1, time2);
days = (unsigned int)diff / (60 * 60 * 24);
return days;
}
static inline bool is_valid_codename(const char *codename) {
// Only codenames with lowercase ASCII letters are accepted
return strlen(codename) > 0 &&
strspn(codename, "abcdefghijklmnopqrstuvwxyz") == strlen(codename);
}
// Read an ISO 8601 formatted date
static date_t *read_date(const char *s, int *failures, const char *filename,
const int lineno, const char *column) {
date_t *date = NULL;
int n;
if(s) {
date = malloc(sizeof(date_t));
n = sscanf(s, "%u-%u-%u", &date->year, &date->month, &date->day);
if(unlikely(n != 3 || !is_valid_date(date))) {
fprintf(stderr, NAME ": Invalid date `%s' in file `%s' at line %i "
"in column `%s'.\n", s, filename, lineno, column);
(*failures)++;
free(date);
date = NULL;
}
}
return date;
}
static inline bool date_ge(const date_t *date1, const date_t *date2) {
return date1->year > date2->year ||
(date1->year == date2->year && date1->month > date2->month) ||
(date1->year == date2->year && date1->month == date2->month &&
date1->day >= date2->day);
}
static inline bool created(const date_t *date, const distro_t *distro) {
return distro->milestones[MILESTONE_CREATED] &&
date_ge(date, distro->milestones[MILESTONE_CREATED]);
}
static inline bool released(const date_t *date, const distro_t *distro) {
return *distro->version != '\0' &&
distro->milestones[MILESTONE_RELEASE] &&
date_ge(date, distro->milestones[MILESTONE_RELEASE]);
}
static inline bool eol(const date_t *date, const distro_t *distro) {
return distro->milestones[MILESTONE_EOL] &&
date_ge(date, distro->milestones[MILESTONE_EOL])
#ifdef UBUNTU
&& (!distro->milestones[MILESTONE_EOL_SERVER] ||
(distro->milestones[MILESTONE_EOL_SERVER] &&
date_ge(date, distro->milestones[MILESTONE_EOL_SERVER])))
#endif
;
}
#ifdef UBUNTU
static inline bool eol_esm(const date_t *date, const distro_t *distro) {
return distro->milestones[MILESTONE_EOL] &&
date_ge(date, distro->milestones[MILESTONE_EOL])
&& (!distro->milestones[MILESTONE_EOL_ESM] ||
(distro->milestones[MILESTONE_EOL_ESM] &&
date_ge(date, distro->milestones[MILESTONE_EOL_ESM])))
;
}
#endif
// Filter callbacks
static bool filter_all(unused(const date_t *date),
unused(const distro_t *distro)) {
return true;
}
static bool filter_stable(const date_t *date, const distro_t *distro) {
return released(date, distro) && !eol(date, distro);
}
static bool filter_supported(const date_t *date, const distro_t *distro) {
return created(date, distro) && !eol(date, distro);
}
#ifdef UBUNTU
static bool filter_esm_supported(const date_t *date, const distro_t *distro) {
return created(date, distro) && !eol_esm(date, distro) &&
strstr(distro->version, "LTS") != NULL;
}
#endif
static bool filter_unsupported(const date_t *date, const distro_t *distro) {
return created(date, distro) && eol(date, distro);
}
// Select callbacks
static const distro_t *select_latest_created(const distro_elem_t *distro_list) {
const distro_t *selected;
selected = distro_list->distro;
while(distro_list != NULL) {
distro_list = distro_list->next;
if(distro_list && date_ge(distro_list->distro->milestones[MILESTONE_CREATED],
selected->milestones[MILESTONE_CREATED])) {
selected = distro_list->distro;
}
}
return selected;
}
static const distro_t *select_latest_release(const distro_elem_t *distro_list) {
const distro_t *selected;
selected = distro_list->distro;
while(distro_list != NULL) {
distro_list = distro_list->next;
if(distro_list && date_ge(distro_list->distro->milestones[MILESTONE_RELEASE],
selected->milestones[MILESTONE_RELEASE])) {
selected = distro_list->distro;
}
}
return selected;
}
static const distro_t *select_series(const distro_elem_t *distro_list, const char *series) {
const distro_t *selected;
selected = NULL;
while(distro_list != NULL) {
if(distro_list && (strcmp(distro_list->distro->series, series) == 0)) {
selected = distro_list->distro;
break;
}
distro_list = distro_list->next;
}
return selected;
}
static bool calculate_days(const distro_t *distro, const date_t *date,
int date_index, ssize_t *days) {
const date_t *first;
const date_t *second;
int direction;
assert(date_index >= 0 && date_index < MILESTONE_COUNT);
/* distro may not have specified a particular milestone date
* (yet).
*/
if(!distro->milestones[date_index]) {
return false;
}
if(date_ge(date, distro->milestones[date_index])) {
first = date;
second = distro->milestones[date_index];
direction = -1;
} else {
first = distro->milestones[date_index];
second = date;
direction = 1;
}
*days = (ssize_t)date_diff(first, second) * direction;
return true;
}
// Print callbacks
static bool print_codename(const distro_t *distro, const date_t *date,
int date_index, int just_days) {
ssize_t days;
if(date_index == -1) {
printf("%s\n", distro->series);
} else {
if(!calculate_days(distro, date, date_index, &days)) {
printf("%s%s%s\n",
just_days ? "" : distro->series,
just_days ? "" : " ",
UNKNOWN_DAYS);
} else {
printf("%s%s%zd\n",
just_days ? "" : distro->series,
just_days ? "" : " ",
days);
}
}
return true;
}
static bool print_fullname(const distro_t *distro, const date_t *date,
int date_index, int just_days) {
ssize_t days;
if(date_index == -1) {
printf(DISTRO_NAME " %s \"%s\"\n", distro->version, distro->codename);
} else {
if(!calculate_days(distro, date, date_index, &days)) {
if(just_days) {
printf("%s\n", UNKNOWN_DAYS);
} else {
printf(DISTRO_NAME " %s \"%s\" %s\n",
distro->version, distro->codename, UNKNOWN_DAYS);
}
} else {
if(just_days) {
printf("%zd\n", days);
} else {
printf(DISTRO_NAME " %s \"%s\" %zd\n",
distro->version, distro->codename, days);
}
}
}
return true;
}
static bool print_release(const distro_t *distro, const date_t *date,
int date_index, int just_days) {
ssize_t days;
char *str;
str = unlikely(*distro->version == '\0') ? distro->series : distro->version;
if(date_index == -1) {
printf("%s\n", str);
} else {
if(!calculate_days(distro, date, date_index, &days)) {
printf("%s%s%s\n",
just_days ? "" : str,
just_days ? "" : " ",
UNKNOWN_DAYS);
} else {
printf("%s%s%zd\n",
just_days ? "" : str,
just_days ? "" : " ",
days);
}
}
return true;
}
// End of callbacks
static void free_data(distro_elem_t *list, char **content) {
distro_elem_t *next;
while(list != NULL) {
next = list->next;
free(list->distro->milestones[MILESTONE_CREATED]);
free(list->distro->milestones[MILESTONE_RELEASE]);
free(list->distro->milestones[MILESTONE_EOL]);
#ifdef UBUNTU
free(list->distro->milestones[MILESTONE_EOL_SERVER]);
free(list->distro->milestones[MILESTONE_EOL_ESM]);
#endif
free(list->distro);
free(list);
list = next;
}
free(*content);
*content = NULL;
}
static distro_elem_t *read_data(const char *filename, char **content) {
char *data;
char *line;
distro_elem_t *current;
distro_elem_t *distro_list = NULL;
distro_elem_t *last = NULL;
distro_t *distro;
int lineno;
int failures = 0;
data = *content = read_full_file(filename);
line = strsep(&data, "\n");
lineno = 1;
if(unlikely(strcmp(CSV_HEADER, line) != 0)) {
fprintf(stderr, NAME ": Header `%s' in file `%s' does not match "
"exactly `" CSV_HEADER "'.\n", line, filename);
failures++;
}
while((line = strsep(&data, "\n")) != NULL) {
lineno++;
// Ignore empty lines and comments (starting with #).
if(likely(*line != '\0' && *line != '#')) {
int milestone_index;
distro = malloc(sizeof(distro_t));
distro->version = strsep(&line, ",");
distro->codename = strsep(&line, ",");
distro->series = strsep(&line, ",");
for(milestone_index = 0; milestone_index < (int)MILESTONE_COUNT;
milestone_index++) {
distro->milestones[milestone_index] =
read_date(strsep(&line, ","), &failures, filename,
lineno, milestones[milestone_index]);
}
current = malloc(sizeof(distro_elem_t));
current->distro = distro;
current->next = NULL;
if(last == NULL) {
distro_list = current;
} else {
last->next = current;
}
last = current;
}
}
if(unlikely(distro_list == NULL)) {
fprintf(stderr, NAME ": No data found in file `%s'.\n", filename);
failures++;
}
if(unlikely(failures > 0)) {
free_data(distro_list, content);
distro_list = NULL;
}
return distro_list;
}
static bool filter_data(const distro_elem_t *distro_list, const date_t *date,
int date_index, int just_days,
bool (*filter_cb)(const date_t*, const distro_t*),
bool (*print_cb)(const distro_t*, const date_t*, int, int)) {
while(distro_list != NULL) {
if(filter_cb(date, distro_list->distro)) {
if(!print_cb(distro_list->distro, date, date_index, just_days)) {
return false;
}
}
distro_list = distro_list->next;
}
return false;
}
static const distro_t *get_distro(const distro_elem_t *distro_list,
const date_t *date,
bool (*filter_cb)(const date_t*, const distro_t*),
const distro_t *(*select_cb)(const distro_elem_t*)) {
distro_elem_t *current;
distro_elem_t *filtered_list = NULL;
distro_elem_t *last = NULL;
const distro_t *selected;
while(distro_list != NULL) {
if(filter_cb(date, distro_list->distro)) {
current = malloc(sizeof(distro_elem_t));
current->distro = distro_list->distro;
current->next = NULL;
if(last == NULL) {
filtered_list = current;
} else {
last->next = current;
}
last = current;
}
distro_list = distro_list->next;
}
if(filtered_list == NULL) {
selected = NULL;
} else {
selected = select_cb(filtered_list);
}
while(filtered_list != NULL) {
current = filtered_list->next;
free(filtered_list);
filtered_list = current;
}
return selected;
}
static void print_help(void) {
int i;
printf("Usage: " NAME " [options]\n"
"\n"
"Options:\n"
" -h --help show this help message and exit\n"
" --date=DATE date for calculating the version (default: today)\n"
" --series=SERIES series to calculate the version for\n"
" -y[MILESTONE] additionally, display days until milestone\n"
" --days=[MILESTONE] ("
);
for(i = 0; i < (int)MILESTONE_COUNT; i++) {
printf("%s%s", milestones[i], i + 1 == MILESTONE_COUNT ? ")\n" : ", ");
}
printf(""
#ifdef DEBIAN
" --alias=DIST print the alias (oldstable, stable, testing, unstable)\n"
" relative to the given distribution codename\n"
#endif
" -a --all list all known versions\n"
" -d --devel latest development version\n"
#ifdef UBUNTU
" --lts latest long term support (LTS) version\n"
#endif
#ifdef DEBIAN
" -o --oldstable latest oldstable version\n"
#endif
" -s --stable latest stable version\n"
" --supported list of all supported stable versions\n"
#ifdef UBUNTU
" --supported-esm list of all Ubuntu Advantage supported stable versions\n"
#endif
#ifdef DEBIAN
" -t --testing current testing version\n"
#endif
" --unsupported list of all unsupported stable versions\n"
" -c --codename print the codename (default)\n"
" -f --fullname print the full name\n"
" -r --release print the release version\n"
"\n"
"See " NAME "(1) for more info.\n");
}
static inline int not_exactly_one(void) {
fprintf(stderr, NAME ": You have to select exactly one of "
#ifdef DEBIAN
"--alias, "
#endif
"--all, --devel, "
#ifdef UBUNTU
"--latest, --lts, "
#endif
#ifdef DEBIAN
"--oldstable, "
#endif
"--stable, --supported, "
#ifdef UBUNTU
"--supported-esm, "
#endif
"--series, "
#ifdef DEBIAN
"--testing, "
#endif
"--unsupported.\n");
return EXIT_FAILURE;
}
int main(int argc, char *argv[]) {
bool show_days = false;
bool just_days = true;
char *content;
date_t *date = NULL;
distro_elem_t *distro_list;
const distro_t *selected;
int i;
int date_index = -1;
char *series_name = NULL;
int option;
int option_index;
int return_value = EXIT_SUCCESS;
int selected_filters = 0;
bool (*filter_cb)(const date_t*, const distro_t*) = NULL;
const distro_t *(*select_cb)(const distro_elem_t*) = NULL;
bool (*print_cb)(const distro_t*, const date_t*, int, int) = print_codename;
#ifdef DEBIAN
char *alias_codename = NULL;
#endif
#ifdef UBUNTU
bool filter_latest = false;
#endif
const struct option long_options[] = {
{"help", no_argument, NULL, 'h' },
{"date", required_argument, NULL, 'D' },
{"series", required_argument, NULL, 'R' },
{"all", no_argument, NULL, 'a' },
{"days", optional_argument, NULL, 'y' },
{"devel", no_argument, NULL, 'd' },
{"stable", no_argument, NULL, 's' },
{"supported", no_argument, NULL, 'S' },
#ifdef UBUNTU
{"supported-esm", no_argument, NULL, 'e' },
#endif
{"unsupported", no_argument, NULL, 'U' },
{"codename", no_argument, NULL, 'c' },
{"fullname", no_argument, NULL, 'f' },
{"release", no_argument, NULL, 'r' },
#ifdef DEBIAN
{"alias", required_argument, NULL, 'A' },
{"oldstable", no_argument, NULL, 'o' },
{"testing", no_argument, NULL, 't' },
#endif
#ifdef UBUNTU
{"latest", no_argument, NULL, 'l' },
{"lts", no_argument, NULL, 'L' },
#endif
{NULL, 0, NULL, '\0' }
};
#ifdef UBUNTU
const char *short_options = "hadscrfly::";
#endif
#ifdef DEBIAN
const char *short_options = "hadscrfoty::";
#endif
// Suppress error messages from getopt_long
opterr = 0;
while ((option = getopt_long(argc, argv, short_options,
long_options, &option_index)) != -1) {
switch (option) {
#ifdef DEBIAN
case 'A':
// Only long option --alias is used
if(unlikely(alias_codename != NULL)) {
fprintf(stderr, NAME ": --alias requested multiple times.\n");
free(date);
return EXIT_FAILURE;
}
if(!is_valid_codename(optarg)) {
fprintf(stderr, NAME ": invalid distribution codename `%s'\n",
optarg);
free(date);
return EXIT_FAILURE;
}
selected_filters++;
alias_codename = optarg;
break;
#endif
case 'a':
selected_filters++;
filter_cb = filter_all;
select_cb = NULL;
break;
case 'c':
just_days = false;
print_cb = print_codename;
break;
case 'd':
selected_filters++;
filter_cb = filter_devel;
#ifdef UBUNTU
select_cb = select_latest_created;
#endif
#ifdef DEBIAN
select_cb = select_first;
#endif
break;
case 'D':
// Only long option --date is used
if(unlikely(date != NULL)) {
fprintf(stderr, NAME ": Date specified multiple times.\n");
free(date);
return EXIT_FAILURE;
}
date = malloc(sizeof(date_t));
i = sscanf(optarg, "%u-%u-%u", &date->year, &date->month,
&date->day);
if(i != 3 || !is_valid_date(date)) {
fprintf(stderr, NAME ": invalid date `%s'\n", optarg);
free(date);
return EXIT_FAILURE;
}
break;
case 'R':
// Only long option --series is used
if(unlikely(series_name != NULL)) {
fprintf(stderr, NAME ": series requested multiple times.\n");
return EXIT_FAILURE;
}
if(!optarg || !is_valid_codename(optarg)) {
fprintf(stderr, NAME ": invalid distribution series `%s'\n",
optarg);
free(date);
return EXIT_FAILURE;
}
selected_filters++;
series_name = optarg;
break;
case 'f':
just_days = false;
print_cb = print_fullname;
break;
case 'h':
print_help();
free(date);
return EXIT_SUCCESS;
#ifdef UBUNTU
case 'l':
selected_filters++;
filter_latest = true;
filter_cb = filter_devel;
select_cb = select_latest_created;
break;
case 'L':
// Only long option --lts is used
selected_filters++;
filter_cb = filter_lts;
select_cb = select_latest_release;
break;
#endif
#ifdef DEBIAN
case 'o':
selected_filters++;
filter_cb = filter_oldstable;
select_cb = select_oldstable;
break;
#endif
case 'r':
just_days = false;
print_cb = print_release;
break;
case 's':
selected_filters++;
filter_cb = filter_stable;
select_cb = select_latest_release;
break;
case 'S':
// Only long option --supported is used
selected_filters++;
filter_cb = filter_supported;
select_cb = NULL;
break;
#ifdef UBUNTU
case 'e':
// Only long option --supported-esm is used
selected_filters++;
filter_cb = filter_esm_supported;
select_cb = NULL;
break;
#endif
#ifdef DEBIAN
case 't':
selected_filters++;
filter_cb = filter_testing;
select_cb = select_latest_created;
break;
#endif
case 'U':
// Only long option --unsupported is used
selected_filters++;
filter_cb = filter_unsupported;
select_cb = NULL;
break;
case 'y':
show_days = true;
if(optarg) {
date_index = milestone_to_index(optarg);
if(date_index < 0) {
fprintf(stderr, NAME ": invalid milestone: %s\n",
optarg);
return EXIT_FAILURE;
}
}
break;
case '?':
if(optopt == '\0') {
// Long option failed
fprintf(stderr, NAME ": unrecognized option `%s'\n",
argv[optind-1]);
#ifdef DEBIAN
} else if(optopt == 'A') {
fprintf(stderr, NAME ": option `--alias' requires "
"an argument DIST\n");
#endif
} else if(optopt == 'D') {
fprintf(stderr, NAME ": option `--date' requires "
"an argument DATE\n");
} else if(optopt == 'R') {
fprintf(stderr, NAME ": option `--series' requires "
"an argument SERIES\n");
} else {
fprintf(stderr, NAME ": unrecognized option `-%c'\n",
optopt);
}
free(date);
return EXIT_FAILURE;
break;
default:
fprintf(stderr, NAME ": getopt returned character code %i. "
"Please file a bug report.\n", option);
free(date);
return EXIT_FAILURE;
}
}
if(show_days && date_index < 0) {
date_index = MILESTONE_RELEASE;
}
if(unlikely(optind < argc)) {
fprintf(stderr, NAME ": unrecognized arguments: %s", argv[optind]);
for(i = optind + 1; i < argc; i++) {
fprintf(stderr, " %s", argv[i]);
}
fprintf(stderr, "\n");
free(date);
return EXIT_FAILURE;
}
if(unlikely(selected_filters != 1)) {
free(date);
return not_exactly_one();
}
if(unlikely(date == NULL)) {
time_t time_now = time(NULL);
struct tm *now = gmtime(&time_now);
date = malloc(sizeof(date_t));
date->year = 1900 + now->tm_year;
date->month = 1 + now->tm_mon;
date->day = now->tm_mday;
}
distro_list = read_data(DATA_DIR "/" CSV_NAME ".csv", &content);
if(unlikely(distro_list == NULL)) {
free(date);
return EXIT_FAILURE;
}
#ifdef DEBIAN
if(alias_codename) {
const distro_t *oldstable = get_distro(distro_list, date, filter_oldstable,
select_oldstable);
const distro_t *stable = get_distro(distro_list, date, filter_stable,
select_latest_release);
const distro_t *testing = get_distro(distro_list, date, filter_testing,
select_latest_created);
const distro_t *unstable = get_distro(distro_list, date, filter_devel,
select_first);
if(unlikely(oldstable == NULL || stable == NULL || testing == NULL ||
unstable == NULL)) {
fprintf(stderr, NAME ": " OUTDATED_ERROR "\n");
return_value = EXIT_FAILURE;
} else if(strcmp(oldstable->series, alias_codename) == 0) {
printf("oldstable\n");
} else if(strcmp(stable->series, alias_codename) == 0) {
printf("stable\n");
} else if(strcmp(testing->series, alias_codename) == 0) {
printf("testing\n");
} else if(strcmp(unstable->series, alias_codename) == 0) {
printf("unstable\n");
} else {
printf("%s\n", alias_codename);
}
free(date);
free_data(distro_list, &content);
return return_value;
}
#endif
if(select_cb == NULL && !series_name) {
filter_data(distro_list, date, date_index, just_days, filter_cb, print_cb);
} else {
if(series_name) {
selected = select_series(distro_list, series_name);
} else {
selected = get_distro(distro_list, date, filter_cb, select_cb);
#ifdef UBUNTU
if(selected == NULL && filter_latest) {
selected = get_distro(distro_list, date, filter_stable, select_latest_release);
}
#endif
}
if(selected == NULL) {
if(series_name) {
fprintf(stderr, NAME ": unknown distribution series `%s'\n", series_name);
} else {
fprintf(stderr, NAME ": " OUTDATED_ERROR "\n");
}
return_value = EXIT_FAILURE;
} else {
if(!print_cb(selected, date, date_index, just_days)) {
return_value = EXIT_FAILURE;
}
}
}
free(date);
free_data(distro_list, &content);
return return_value;
}

79
distro-info-util.h Normal file
View File

@ -0,0 +1,79 @@
/*
* Copyright (C) 2012-2013, Benjamin Drung <bdrung@debian.org>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#ifndef __DISTRO_INFO_UTIL_H__
#define __DISTRO_INFO_UTIL_H__
// C standard libraries
#include <stdbool.h>
#ifdef __GNUC__
#define likely(x) __builtin_expect((x),1)
#define unlikely(x) __builtin_expect((x),0)
#define unused(x) x __attribute ((unused))
#else
#define likely(x) (x)
#define unlikely(x) (x)
#define unused(x) x
#endif
/* NOTE: Must be kept in sync with milestones array. */
enum MILESTONE {MILESTONE_CREATED
,MILESTONE_RELEASE
,MILESTONE_EOL
#ifdef UBUNTU
,MILESTONE_EOL_SERVER
,MILESTONE_EOL_ESM
#endif
,MILESTONE_COUNT
};
#define UNKNOWN_DAYS "(unknown)"
#define DATA_DIR "/usr/share/distro-info"
#define OUTDATED_ERROR "Distribution data outdated.\n" \
"Please check for an update for distro-info-data. " \
"See /usr/share/doc/distro-info-data/README.Debian for details."
typedef struct {
unsigned int year;
unsigned int month;
unsigned int day;
} date_t;
typedef struct {
char *version;
char *codename;
char *series;
date_t *milestones[MILESTONE_COUNT];
} distro_t;
typedef struct distro_elem_s {
distro_t *distro;
struct distro_elem_s *next;
} distro_elem_t;
static inline bool date_ge(const date_t *date1, const date_t *date2);
static inline bool created(const date_t *date, const distro_t *distro);
static inline bool released(const date_t *date, const distro_t *distro);
static inline bool eol(const date_t *date, const distro_t *distro);
#ifdef UBUNTU
static inline bool eol_esm(const date_t *date, const distro_t *distro);
#endif
static inline int milestone_to_index(const char *milestone);
#endif // __DISTRO_INFO_UTIL_H__

87
doc/debian-distro-info.1 Normal file
View File

@ -0,0 +1,87 @@
.\" Copyright (c) 2009-2014, Benjamin Drung <bdrung@debian.org>
.\"
.\" Permission to use, copy, modify, and/or distribute this software for any
.\" purpose with or without fee is hereby granted, provided that the above
.\" copyright notice and this permission notice appear in all copies.
.\"
.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
.\"
.TH DEBIAN\-DISTRO\-INFO "1" "August 2013" "distro\-info"
.SH NAME
debian\-distro\-info \- provides information about Debian's distributions
.SH SYNOPSIS
.B debian\-distro\-info
[\fIOPTIONS\fR]
.SH OPTIONS
.TP
\fB\-\-date\fR=\fIDATE
date for calculating the version (default: today)
.TP
\fB\-h\fR, \fB\-\-help\fR
display help message and exit
.TP
\fB\-\-alias\fR=\fIDIST
print the alias (oldstable, stable, testing, unstable) relative to
the distribution codename passed as an argument.
Only distribution codenames composed of lower case ASCII letters are accepted,
and if the distribution does not qualify as oldstable, stable, testing, or
unstable, then the same codename passed as argument is returned.
.TP
\fB\-a\fR, \fB\-\-all\fR
list all known versions
.TP
\fB\-y\fR[\fIMILESTONE\fR], \fB\-\-days\fR[=\fIMILESTONE\fR]
display number of days until specified version reaches the specified milestone.
.I MILESTONE
may be one of
.IR created ", "
.IR release ", or "
.IR eol "."
If no milestone is specified, assume \fIrelease\fP.
For options that return a list, display the normal output followed by
whitespace and the number of days until the specified milestone.
If milestone cannot be calculated, the string \(aq(unknown)\(aq is displayed.
Unless one of \fB\-c\fP, \fB\-f\fP or \fB\-r\fP is specified,
only the number of days will be displayed.
.TP
\fB\-d\fR, \fB\-\-devel\fR
latest development version
.TP
\fB\-o\fR, \fB\-\-old\fR
latest old (stable) version
.TP
\fB\-\-series\fR=\fISERIES
series to calculate the version for
.TP
\fB\-s\fR, \fB\-\-stable\fR
latest stable version
.TP
\fB\-\-supported\fR
list of all supported stable versions
.TP
\fB\-t\fR, \fB\-\-testing\fR
latest testing version
.TP
\fB\-\-unsupported\fR
list of all unsupported stable versions
.TP
\fB\-c\fR, \fB\-\-codename\fR
print the codename (default)
.TP
\fB\-r\fR, \fB\-\-release\fR
print the release version
.TP
\fB\-f\fR, \fB\-\-fullname\fR
print the full name
.SH SEE ALSO
.BR distro\-info (1),
.BR ubuntu\-distro\-info (1)
.SH AUTHOR
The script and this manual page was written by
Benjamin Drung <bdrung@debian.org>.

82
doc/distro-info.1 Normal file
View File

@ -0,0 +1,82 @@
.\" Copyright (c) 2009-2014, Benjamin Drung <bdrung@debian.org>
.\"
.\" Permission to use, copy, modify, and/or distribute this software for any
.\" purpose with or without fee is hereby granted, provided that the above
.\" copyright notice and this permission notice appear in all copies.
.\"
.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
.\"
.TH DISTRO\-INFO "1" "August 2013" "distro\-info"
.SH NAME
distro\-info \- provides information about the distributions' releases
.SH SYNOPSIS
.B distro\-info
[\fIOPTIONS\fR]
.SH DESCRIPTION
.B distro\-info
is a symlink to the distro\-info command for your distribution.
On Debian it links to
.B debian\-distro\-info
and on Ubuntu it links to \fBubuntu\-distro\-info\fR.
All options described in this manual page are available in all
.B distro\-info
commands. All other options, which are not described here, are distribution
specific.
.SH OPTIONS
.TP
\fB\-\-date\fR=\fIDATE
date for calculating the version (default: today)
.TP
\fB\-h\fR, \fB\-\-help\fR
display help message and exit
.TP
\fB\-a\fR, \fB\-\-all\fR
list all known versions
.TP
\fB\-y\fR[\fIMILESTONE\fR], \fB\-\-days\fR[=\fIMILESTONE\fR]
display number of days until specified version reaches the specified milestone.
.I MILESTONE
may be one of
.IR created ", "
.IR release ", "
.IR eol ", or "
.IR eol\-server "."
If no milestone is specified, assume \fIrelease\fP.
For options that return a list, display the normal output followed by
whitespace and the number of days until the specified milestone.
.TP
\fB\-d\fR, \fB\-\-devel\fR
latest development version
.TP
\fB\-\-series\fR=\fISERIES
series to calculate the version for
.TP
\fB\-s\fR, \fB\-\-stable\fR
latest stable version
.TP
\fB\-\-supported\fR
list of all supported stable versions
.TP
\fB\-\-unsupported\fR
list of all unsupported stable versions
.TP
\fB\-c\fR, \fB\-\-codename\fR
print the codename (default)
.TP
\fB\-r\fR, \fB\-\-release\fR
print the release version
.TP
\fB\-f\fR, \fB\-\-fullname\fR
print the full name
.SH SEE ALSO
.BR debian\-distro\-info (1),
.BR ubuntu\-distro\-info (1)
.SH AUTHOR
The script and this manual page was written by
Benjamin Drung <bdrung@debian.org>.

85
doc/ubuntu-distro-info.1 Normal file
View File

@ -0,0 +1,85 @@
.\" Copyright (c) 2009-2014, Benjamin Drung <bdrung@debian.org>
.\"
.\" Permission to use, copy, modify, and/or distribute this software for any
.\" purpose with or without fee is hereby granted, provided that the above
.\" copyright notice and this permission notice appear in all copies.
.\"
.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
.\"
.TH UBUNTU\-DISTRO\-INFO "1" "August 2013" "distro\-info"
.SH NAME
ubuntu\-distro\-info \- provides information about Ubuntu's distributions
.SH SYNOPSIS
.B ubuntu\-distro\-info
[\fIOPTIONS\fR]
.SH OPTIONS
.TP
\fB\-\-date\fR=\fIDATE
date for calculating the version (default: today)
.TP
\fB\-h\fR, \fB\-\-help\fR
display help message and exit
.TP
\fB\-a\fR, \fB\-\-all\fR
list all known versions
.TP
\fB\-y\fR[\fIMILESTONE\fR], \fB\-\-days\fR[=\fIMILESTONE\fR]
display number of days until specified version reaches the specified milestone.
.I MILESTONE
may be one of
.IR created ", "
.IR release ", "
.IR eol ", or "
.IR eol\-server "."
If no milestone is specified, assume \fIrelease\fP.
For options that return a list, display the normal output followed by
whitespace and the number of days until the specified milestone.
If milestone cannot be calculated (for example if the
.I eol\-server
milestone is specified for a non-server release), the string \(aq(unknown)\(aq
is displayed.
Unless one of \fB\-c\fP, \fB\-f\fP or \fB\-r\fP
is specified, only the number of days will be displayed.
.TP
\fB\-d\fR, \fB\-\-devel\fR
latest development version
.TP
\fB\-l\fR, \fB\-\-latest\fR
prints the latest development version. In case of outdated distribution data,
the latest stable version will be printed.
.TP
\fB\-\-lts\fR
latest long term support (LTS) version
.TP
\fB\-\-series\fR=\fISERIES
series to calculate the version for
.TP
\fB\-s\fR, \fB\-\-stable\fR
latest stable version
.TP
\fB\-\-supported\fR
list of all supported stable versions
.TP
\fB\-\-unsupported\fR
list of all unsupported stable versions
.TP
\fB\-c\fR, \fB\-\-codename\fR
print the codename (default)
.TP
\fB\-r\fR, \fB\-\-release\fR
print the release version
.TP
\fB\-f\fR, \fB\-\-fullname\fR
print the full name
.SH SEE ALSO
.BR debian\-distro\-info (1),
.BR distro\-info (1)
.SH AUTHOR
The script and this manual page was written by
Benjamin Drung <bdrung@debian.org>.

125
haskell/DebianDistroInfo.hs Normal file
View File

@ -0,0 +1,125 @@
{- Copyright (C) 2011, Benjamin Drung <bdrung@debian.org>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-}
module DebianDistroInfo where
import Data.List
import Data.Time
import System.Console.GetOpt
import System.Environment
import System.Exit
import System.IO
import System.Locale
import Text.CSV
import DistroInfo
-- | Trim whitespaces from given string (similar to Python function)
trim :: String -> String
trim = reverse . trimLeft . reverse . trimLeft
where trimLeft = dropWhile (`elem` " \t\n\r")
data Options = Options { optDate :: Day
, optFilter :: Maybe (Day -> [DebianEntry] -> [DebianEntry])
, optFormat :: DebianEntry -> String
}
startOptions :: IO Options
startOptions = do
today <- getCurrentTime
return Options { optDate = utctDay today,
optFilter = Nothing,
optFormat = debSeries
}
onlyOneFilter :: a
onlyOneFilter = error ("You have to select exactly one of --all, " ++
"--devel, --oldstable, --stable, --supported, " ++
"--testing, --unsupported.")
options :: [OptDescr (Options -> IO Options)]
options =
let
debVersionOrSeries e =
let
version = debVersion e
in
if version /= ""
then version
else debSeries e
readDate arg opt =
return opt { optDate = readTime defaultTimeLocale "%F" arg }
printHelp _ =
do
prg <- getProgName
hPutStr stderr ("Usage: " ++ prg ++ " [options]\n\nOptions:")
hPutStrLn stderr (usageInfo "" options)
hPutStrLn stderr ("See " ++ prg ++ "(1) for more info.")
exitWith ExitSuccess
setFilter debianFilter opt =
case optFilter opt of
Nothing -> return opt { optFilter = Just debianFilter }
Just _ -> onlyOneFilter
in
[ Option "h" ["help"] (NoArg printHelp) "show this help message and exit"
, Option "" ["date"] (ReqArg readDate "DATE")
"date for calculating the version (default: today)"
, Option "a" ["all"] (NoArg (setFilter debianAll))
"list all known versions"
, Option "d" ["devel"] (NoArg (setFilter debianDevel))
"latest development version"
, Option "o" ["oldstable"] (NoArg (setFilter debianOldstable))
"latest oldstable version"
, Option "s" ["stable"] (NoArg (setFilter debianStable))
"latest stable version"
, Option "" ["supported"] (NoArg (setFilter debianSupported))
"list of all supported stable versions"
, Option "t" ["testing"] (NoArg (setFilter debianTesting))
"current testing version"
, Option "" ["unsupported"] (NoArg (setFilter debianUnsupported))
"list of all unsupported stable versions"
, Option "c" ["codename"]
(NoArg (\ opt -> return opt { optFormat = debSeries }))
"print the codename (default)"
, Option "r" ["release"]
(NoArg (\ opt -> return opt { optFormat = debVersionOrSeries }))
"print the release version"
, Option "f" ["fullname"]
(NoArg (\ opt -> return opt { optFormat = debFull }))
"print the full name"
]
main :: IO ()
main = do
args <- getArgs
case getOpt RequireOrder options args of
(actions, [], []) -> do
Options { optDate = date, optFilter = maybeFilter,
optFormat = format } <- foldl (>>=) startOptions actions
maybeCsv <- parseCSVFromFile "/usr/share/distro-info/debian.csv"
case maybeFilter of
Nothing -> onlyOneFilter
Just debianFilter ->
case maybeCsv of
Left errorMsg -> error $ show errorMsg
Right csvData ->
let
result = map format $ debianFilter date $ debianEntry csvData
in
putStrLn $ concat $ intersperse "\n" result
(_, nonOptions, []) ->
error $ "unrecognized arguments: " ++ unwords nonOptions
(_, _, msgs) -> error $ trim $ concat msgs

249
haskell/DistroInfo.hs Normal file
View File

@ -0,0 +1,249 @@
{- Copyright (C) 2011, Benjamin Drung <bdrung@debian.org>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-}
module DistroInfo (DebianEntry, debVersion, debSeries, debFull,
debianEntry,
debianAll,
debianDevel,
debianOldstable,
debianStable,
debianSupported,
debianTesting,
debianUnsupported,
UbuntuEntry, ubuVersion, ubuSeries, ubuFull,
ubuntuEntry,
ubuntuAll,
ubuntuDevel,
ubuntuLTS,
ubuntuStable,
ubuntuSupported,
ubuntuUnsupported,
) where
import Data.Map hiding (map, filter, lookup)
import qualified Data.Map as Map
import Data.List
import Data.Time
import Text.CSV
-- | Represents one Debian release data set
-- (corresponds to one row of the debian.csv file).
data DebianEntry = DebianEntry { debVersion :: String,
debCodename :: String,
debSeries :: String,
debCreated :: Day,
debRelease :: Maybe Day,
debEol :: Maybe Day
} deriving(Eq, Show)
-- | Represents one Ubuntu release data set
-- (corresponds to one row of the ubuntu.csv file).
data UbuntuEntry = UbuntuEntry { ubuVersion :: String,
ubuCodename :: String,
ubuSeries :: String,
ubuCreated :: Day,
ubuRelease :: Day,
ubuEol :: Day,
ubuEolServer :: Maybe Day
} deriving(Eq, Show)
-- | Splits a string by a given character (similar to the Python split function)
-- splitS '-' "2010-05-23" = ["2010", "05", "23"]
splitS :: Eq a => a -> [a] -> [[a]]
splitS _ [] = [[]]
splitS c (x : xs) =
let
rest = splitS c xs
in
if x == c
then [] : rest
else (x : head rest) : tail rest
-- | Convert a given date string in ISO 8601 format into a Day
convertDate :: String -> Day
convertDate date =
case map (read :: String -> Int) (splitS '-' date) of
year : month : day : [] -> fromGregorian (toInteger year) month day
year : month : [] ->
if month == 12
then fromGregorian (toInteger year) month 31
else addDays (-1) (fromGregorian (toInteger year) (month + 1) 1)
_ -> error ("Date \"" ++ date ++ "\" not in ISO 8601 format.")
-- | Drop empty rows from CSV files
dropEmptyRows :: [Record] -> [Record]
dropEmptyRows = filter (\ r -> r /= [""])
-- | Converts a String into a Day if it exists
maybeDate :: Maybe String -> Maybe Day
maybeDate Nothing = Nothing
maybeDate (Just date) = Just (convertDate date)
-- | Converts a given CSV data into a list of Ubuntu entries
ubuntuEntry :: CSV -> [UbuntuEntry]
ubuntuEntry [] = error "Empty CSV file."
ubuntuEntry (heading : content) =
map (toEntry . fromList . zip heading) $ dropEmptyRows content
where
toEntry :: Map String String -> UbuntuEntry
toEntry m =
UbuntuEntry (m ! "version") (m ! "codename") (m ! "series")
(convertDate $ m ! "created") (convertDate $ m ! "release")
(convertDate $ m ! "eol")
(maybeDate $ Map.lookup "eol-server" m)
-- | Converts a given CSV data into a list of Debian entries
debianEntry :: CSV -> [DebianEntry]
debianEntry [] = error "Empty CSV file."
debianEntry (heading : content) =
map (toEntry . fromList . zip heading) $ dropEmptyRows content
where
toEntry :: Map String String -> DebianEntry
toEntry m =
DebianEntry (m ! "version") (m ! "codename") (m ! "series")
(convertDate $ m ! "created")
(maybeDate $ Map.lookup "release" m)
(maybeDate $ Map.lookup "eol" m)
-------------------
-- Debian Filter --
-------------------
-- | Return the latest entry of a given list.
latest :: Int -> [a] -> [a]
latest i m =
if length m < i
then error "Distribution data outdated."
else [m !! (length m - i)]
-- | List all known Debian distributions.
debianAll :: Day -> [DebianEntry] -> [DebianEntry]
debianAll _ = id
-- | Get latest development distribution based on the given date.
debianDevel :: Day -> [DebianEntry] -> [DebianEntry]
debianDevel date = latest 2 . filter isUnreleased
where
isUnreleased DebianEntry { debCreated = created, debRelease = release } =
date >= created && maybe True (date <=) release
-- | Get oldstable Debian distribution based on the given date.
debianOldstable :: Day -> [DebianEntry] -> [DebianEntry]
debianOldstable date = latest 2 . filter isReleased
where
isReleased DebianEntry { debRelease = release } =
maybe False (date >=) release
-- | Get latest stable distribution based on the given date.
debianStable :: Day -> [DebianEntry] -> [DebianEntry]
debianStable date = latest 1 . filter isReleased
where
isReleased DebianEntry { debRelease = release, debEol = eol } =
maybe False (date >=) release && maybe True (date <=) eol
-- | Get list of all supported distributions based on the given date.
debianSupported :: Day -> [DebianEntry] -> [DebianEntry]
debianSupported date = filter isSupported
where
isSupported DebianEntry { debCreated = created, debEol = eol } =
date >= created && maybe True (date <=) eol
-- | Get latest testing Debian distribution based on the given date.
debianTesting :: Day -> [DebianEntry] -> [DebianEntry]
debianTesting date = filter isUnreleased
where
isUnreleased DebianEntry { debVersion = version, debCreated = created,
debRelease = release } =
date >= created && maybe True (date <=) release && version /= ""
-- | Get list of all unsupported distributions based on the given date.
debianUnsupported :: Day -> [DebianEntry] -> [DebianEntry]
debianUnsupported date = filter isUnsupported
where
isUnsupported DebianEntry { debCreated = created, debEol = eol } =
date >= created && maybe False (date >) eol
-------------------
-- Ubuntu Filter --
-------------------
-- | Return the newest entry (based on the release date) of a given list.
ubuntuNewest :: [UbuntuEntry] -> [UbuntuEntry]
ubuntuNewest [] = error "Distribution data outdated."
ubuntuNewest (a : []) = [a]
ubuntuNewest (a : b : rs) =
if ubuRelease a > ubuRelease b
then ubuntuNewest (a : rs)
else ubuntuNewest (b : rs)
-- | Evaluates if a given Ubuntu release is already release and still supported.
ubuntuIsReleased :: Day -> UbuntuEntry -> Bool
ubuntuIsReleased date UbuntuEntry { ubuRelease = release, ubuEol = eol,
ubuEolServer = eolServer } =
date >= release && (date <= eol || maybe False (date <=) eolServer)
-- | List all known Ubuntu distributions.
ubuntuAll :: Day -> [UbuntuEntry] -> [UbuntuEntry]
ubuntuAll _ = id
-- | Get latest development distribution based on the given date.
ubuntuDevel :: Day -> [UbuntuEntry] -> [UbuntuEntry]
ubuntuDevel date = ubuntuNewest . filter isUnreleased
where
isUnreleased UbuntuEntry { ubuCreated = created, ubuRelease = release } =
date >= created && date < release
-- | Get latest long term support (LTS) Ubuntu distribution based on the given
-- date.
ubuntuLTS :: Day -> [UbuntuEntry] -> [UbuntuEntry]
ubuntuLTS date = ubuntuNewest . filter isLTS . filter (ubuntuIsReleased date)
where
isLTS UbuntuEntry { ubuVersion = version } = "LTS" `isInfixOf` version
-- | Get latest stable distribution based on the given date.
ubuntuStable :: Day -> [UbuntuEntry] -> [UbuntuEntry]
ubuntuStable date = ubuntuNewest . filter (ubuntuIsReleased date)
-- | Get list of all supported distributions based on the given date.
ubuntuSupported :: Day -> [UbuntuEntry] -> [UbuntuEntry]
ubuntuSupported date = filter isSupported
where
isSupported UbuntuEntry { ubuCreated = created, ubuEol = eol,
ubuEolServer = eolServer } =
date >= created && (date <= eol || maybe False (date <=) eolServer)
-- | Get list of all unsupported distributions based on the given date.
ubuntuUnsupported :: Day -> [UbuntuEntry] -> [UbuntuEntry]
ubuntuUnsupported date = filter isUnsupported
where
isUnsupported UbuntuEntry { ubuCreated = created, ubuEol = eol,
ubuEolServer = eolServer } =
date >= created && (date > eol && maybe True (date >) eolServer)
------------
-- Output --
------------
debFull :: DebianEntry -> String
debFull DebianEntry { debVersion = version, debCodename = codename,
debSeries = series } =
if version /= ""
then "Debian " ++ version ++ " \"" ++ codename ++ "\""
else "Debian " ++ series
ubuFull :: UbuntuEntry -> String
ubuFull UbuntuEntry { ubuVersion = version, ubuCodename = codename } =
"Ubuntu " ++ version ++ " \"" ++ codename ++ "\""

27
haskell/Makefile Normal file
View File

@ -0,0 +1,27 @@
PREFIX ?= /usr
VENDOR ?= $(shell dpkg-vendor --query Vendor | tr '[:upper:]' '[:lower:]')
GHC_FLAGS := -Wall -O2
build: debian-distro-info ubuntu-distro-info
debian-distro-info: DebianDistroInfo.hs DistroInfo.hs
ghc $(GHC_FLAGS) -o $@ --make -main-is DebianDistroInfo $<
test-distro-info: TestDistroInfo.hs DistroInfo.hs
ghc $(GHC_FLAGS) -o $@ --make -main-is TestDistroInfo $<
ubuntu-distro-info: UbuntuDistroInfo.hs DistroInfo.hs
ghc $(GHC_FLAGS) -o $@ --make -main-is UbuntuDistroInfo $<
install: debian-distro-info ubuntu-distro-info
install -d $(DESTDIR)$(PREFIX)/bin
install -m 755 $^ $(DESTDIR)$(PREFIX)/bin
ln -s $(VENDOR)-distro-info $(DESTDIR)$(PREFIX)/bin/distro-info
test: test-distro-info
./test-distro-info
clean:
rm -f *-distro-info *.hi *.o
.PHONY: build clean install test

156
haskell/TestDistroInfo.hs Normal file
View File

@ -0,0 +1,156 @@
{- Copyright (C) 2011, Benjamin Drung <bdrung@debian.org>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-}
module TestDistroInfo (main) where
import Data.List
import Data.Time
import System.Exit
import Test.HUnit
import Text.CSV
import DistroInfo
date1 :: Day
date1 = fromGregorian 2011 01 10
------------------
-- Debian tests --
------------------
testDebianAll :: [DebianEntry] -> Test
testDebianAll d = TestCase (assertEqual "Debian all" [] (expected \\ result))
where
expected = ["buzz", "rex", "bo", "hamm", "slink", "potato", "woody",
"sarge", "etch", "lenny", "squeeze", "sid", "experimental"]
result = map debSeries $ debianAll date1 d
testDebianDevel :: [DebianEntry] -> Test
testDebianDevel d = TestCase (assertEqual "Debian devel" expected result)
where
expected = ["sid"]
result = map debSeries $ debianDevel date1 d
testDebianOldstable :: [DebianEntry] -> Test
testDebianOldstable d = TestCase (assertEqual "Debian oldstable" expected result)
where
expected = ["etch"]
result = map debSeries $ debianOldstable date1 d
testDebianStable :: [DebianEntry] -> Test
testDebianStable d = TestCase (assertEqual "Debian stable" expected result)
where
expected = ["lenny"]
result = map debSeries $ debianStable date1 d
testDebianSupported :: [DebianEntry] -> Test
testDebianSupported d = TestCase (assertEqual "Debian supported" expected result)
where
expected = ["lenny", "squeeze", "sid", "experimental"]
result = map debSeries $ debianSupported date1 d
testDebianTesting :: [DebianEntry] -> Test
testDebianTesting d = TestCase (assertEqual "Debian testing" expected result)
where
expected = ["squeeze"]
result = map debSeries $ debianTesting date1 d
testDebianUnsupported :: [DebianEntry] -> Test
testDebianUnsupported d = TestCase (assertEqual "Debian unsupported" expected result)
where
expected = ["buzz", "rex", "bo", "hamm", "slink", "potato", "woody",
"sarge", "etch"]
result = map debSeries $ debianUnsupported date1 d
------------------
-- Ubuntu tests --
------------------
testUbuntuAll :: [UbuntuEntry] -> Test
testUbuntuAll u = TestCase (assertEqual "Ubuntu all" [] (expected \\ result))
where
expected = ["warty", "hoary", "breezy", "dapper", "edgy", "feisty",
"gutsy", "hardy", "intrepid", "jaunty", "karmic", "lucid",
"maverick", "natty"]
result = map ubuSeries $ ubuntuAll date1 u
testUbuntuDevel :: [UbuntuEntry] -> Test
testUbuntuDevel u = TestCase (assertEqual "Ubuntu devel" expected result)
where
expected = ["natty"]
result = map ubuSeries $ ubuntuDevel date1 u
testUbuntuLTS :: [UbuntuEntry] -> Test
testUbuntuLTS u = TestCase (assertEqual "Ubuntu LTS" expected result)
where
expected = ["lucid"]
result = map ubuSeries $ ubuntuLTS date1 u
testUbuntuStable :: [UbuntuEntry] -> Test
testUbuntuStable u = TestCase (assertEqual "Ubuntu stable" expected result)
where
expected = ["maverick"]
result = map ubuSeries $ ubuntuStable date1 u
testUbuntuSupported :: [UbuntuEntry] -> Test
testUbuntuSupported u = TestCase (assertEqual "Ubuntu supported" expected result)
where
expected = ["dapper", "hardy", "karmic", "lucid", "maverick", "natty"]
result = map ubuSeries $ ubuntuSupported date1 u
testUbuntuUnsupported :: [UbuntuEntry] -> Test
testUbuntuUnsupported u = TestCase (assertEqual "Ubuntu unsupported" expected result)
where
expected = ["warty", "hoary", "breezy", "edgy", "feisty", "gutsy",
"intrepid", "jaunty"]
result = map ubuSeries $ ubuntuUnsupported date1 u
-----------
-- Tests --
-----------
tests :: [DebianEntry] -> [UbuntuEntry] -> Test
tests d u = TestList [
testDebianAll d,
testDebianDevel d,
testDebianOldstable d,
testDebianStable d,
testDebianSupported d,
testDebianTesting d,
testDebianUnsupported d,
testUbuntuAll u,
testUbuntuDevel u,
testUbuntuLTS u,
testUbuntuStable u,
testUbuntuSupported u,
testUbuntuUnsupported u
]
main :: IO ()
main = do
maybeDebianCsv <- parseCSVFromFile "/usr/share/distro-info/debian.csv"
maybeUbuntuCsv <- parseCSVFromFile "/usr/share/distro-info/ubuntu.csv"
case maybeDebianCsv of
Left errorMsg -> error $ show errorMsg
Right csvDebianData ->
case maybeUbuntuCsv of
Left errorMsg -> error $ show errorMsg
Right csvUbuntuData -> do
count <- runTestTT $ tests (debianEntry csvDebianData)
(ubuntuEntry csvUbuntuData)
case count of
Counts _ _ 0 0 -> exitWith ExitSuccess
_ -> exitWith $ ExitFailure 1

115
haskell/UbuntuDistroInfo.hs Normal file
View File

@ -0,0 +1,115 @@
{- Copyright (C) 2011, Benjamin Drung <bdrung@debian.org>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-}
module UbuntuDistroInfo where
import Data.List
import Data.Time
import System.Console.GetOpt
import System.Environment
import System.Exit
import System.IO
import System.Locale
import Text.CSV
import DistroInfo
-- | Trim whitespaces from given string (similar to Python function)
trim :: String -> String
trim = reverse . trimLeft . reverse . trimLeft
where trimLeft = dropWhile (`elem` " \t\n\r")
data Options = Options { optDate :: Day
, optFilter :: Maybe (Day -> [UbuntuEntry] -> [UbuntuEntry])
, optFormat :: UbuntuEntry -> String
}
startOptions :: IO Options
startOptions = do
today <- getCurrentTime
return Options { optDate = utctDay today,
optFilter = Nothing,
optFormat = ubuSeries
}
onlyOneFilter :: a
onlyOneFilter = error ("You have to select exactly one of --all, --devel, " ++
"--lts, --stable, --supported, --unsupported.")
options :: [OptDescr (Options -> IO Options)]
options =
let
readDate arg opt =
return opt { optDate = readTime defaultTimeLocale "%F" arg }
printHelp _ =
do
prg <- getProgName
hPutStr stderr ("Usage: " ++ prg ++ " [options]\n\nOptions:")
hPutStrLn stderr (usageInfo "" options)
hPutStrLn stderr ("See " ++ prg ++ "(1) for more info.")
exitWith ExitSuccess
setFilter ubuntuFilter opt =
case optFilter opt of
Nothing -> return opt { optFilter = Just ubuntuFilter }
Just _ -> onlyOneFilter
in
[ Option "h" ["help"] (NoArg printHelp) "show this help message and exit"
, Option "" ["date"] (ReqArg readDate "DATE")
"date for calculating the version (default: today)"
, Option "a" ["all"] (NoArg (setFilter ubuntuAll))
"list all known versions"
, Option "d" ["devel"] (NoArg (setFilter ubuntuDevel))
"latest development version"
, Option "" ["lts"] (NoArg (setFilter ubuntuLTS))
"latest long term support (LTS) version"
, Option "s" ["stable"] (NoArg (setFilter ubuntuStable))
"latest stable version"
, Option "" ["supported"] (NoArg (setFilter ubuntuSupported))
"list of all supported stable versions"
, Option "" ["unsupported"] (NoArg (setFilter ubuntuUnsupported))
"list of all unsupported stable versions"
, Option "c" ["codename"]
(NoArg (\ opt -> return opt { optFormat = ubuSeries }))
"print the codename (default)"
, Option "r" ["release"]
(NoArg (\ opt -> return opt { optFormat = ubuVersion }))
"print the release version"
, Option "f" ["fullname"]
(NoArg (\ opt -> return opt { optFormat = ubuFull }))
"print the full name"
]
main :: IO ()
main = do
args <- getArgs
case getOpt RequireOrder options args of
(actions, [], []) -> do
Options { optDate = date, optFilter = maybeFilter,
optFormat = format } <- foldl (>>=) startOptions actions
maybeCsv <- parseCSVFromFile "/usr/share/distro-info/ubuntu.csv"
case maybeFilter of
Nothing -> onlyOneFilter
Just ubuntuFilter ->
case maybeCsv of
Left errorMsg -> error $ show errorMsg
Right csvData ->
let
result = map format $ ubuntuFilter date $ ubuntuEntry csvData
in
putStrLn $ concat $ intersperse "\n" result
(_, nonOptions, []) ->
error $ "unrecognized arguments: " ++ unwords nonOptions
(_, _, msgs) -> error $ trim $ concat msgs

346
perl/Debian/DistroInfo.pm Normal file
View File

@ -0,0 +1,346 @@
# Copyright (C) 2011, Stefano Rivera <stefanor@debian.org>
#
# Permission to use, copy, modify, and/or distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
# copyright notice and this permission notice appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
package Debian::DistroInfo;
=head1 NAME
Debian::DistroInfo - provides information about Ubuntu's and Debian's distributions
=cut
use strict;
use warnings;
use Exporter;
our @EXPORT_OK = qw(convert_date);
our $VERSION = '0.2.2';
my $outdated_error = "Distribution data outdated. "
. "Please check for an update for distro-info-data. "
. "See /usr/share/doc/distro-info-data/README.Debian for details.";
sub convert_date {
my ($date) = @_;
my @parts = split(/-/, $date);
if (scalar(@parts) == 2) {
$date .= '-01';
}
$date = Time::Piece->strptime($date, "%Y-%m-%d");
if (scalar(@parts) == 2) {
$date->add_months(1);
$date -= Time::Piece->ONE_DAY;
}
return $date;
}
{
package DistroInfo;
use Time::Piece;
sub _get_data_dir {
return '/usr/share/distro-info';
}
sub new {
my ($class, $distro) = @_;
my @rows;
my $self = {'distro' => $distro,
'rows' => \@rows,
'date' => Time::Piece->new(),
};
open(my $fh, '<', $class->_get_data_dir . "/$distro.csv")
or die "Unable to open ${distro}'s data file.";
my $line = <$fh>;
chomp($line);
my @header = split(/,/, $line);
while (<$fh>) {
chomp;
my @raw_row = split (/,/);
my %row;
for my $col (@header) {
$row{$col} = shift(@raw_row) or undef;
}
for my $col ('created', 'release', 'eol', 'eol-server', 'eol-esm') {
if(defined($row{$col})) {
$row{$col} = Debian::DistroInfo::convert_date($row{$col});
}
}
push(@rows, \%row);
}
close($fh);
bless($self, $class);
return $self;
}
sub all {
my ($self) = @_;
my @all;
my @rows = @{$self->{'rows'}};
for my $row (@rows) {
push(@all, $row->{'series'})
}
return @all;
}
sub _avail {
my ($self, $date) = @_;
my @avail;
for my $row (@{$self->{'rows'}}) {
push(@avail, $row) if $date > $row->{'created'}
}
return @avail;
}
sub codename {
my ($self, $release, $date, $default) = @_;
return $release;
}
sub version {
my ($self, $codename, $default) = @_;
for my $row (@{$self->{'rows'}}) {
if ($row->{'codename'} eq $codename
|| $row->{'series'} eq $codename) {
return $row->{'version'};
}
}
return $default;
}
sub devel {
my ($self, $date) = @_;
$date = $self->{'date'} if (!defined($date));
my @distros;
for my $row ($self->_avail($date)) {
if (!defined($row->{'release'})
|| ($date < $row->{'release'}
&& (!defined($row->{'release'})
|| $date <= $row->{'eol'}))) {
push(@distros, $row);
}
}
if (scalar(@distros) == 0) {
warn $outdated_error;
return 0;
}
return $distros[-1]{'series'};
}
sub stable {
my ($self, $date) = @_;
$date = $self->{'date'} if (!defined($date));
my @distros;
for my $row ($self->_avail($date)) {
if (defined($row->{'release'}) && $date >= $row->{'release'}
&& (!defined($row->{'eol'}) || $date <= $row->{'eol'})) {
push(@distros, $row);
}
}
if (scalar(@distros) == 0) {
warn $outdated_error;
return 0;
}
return $distros[-1]{'series'};
}
sub supported {
die "Not implemented";
}
sub valid {
my ($self, $codename) = @_;
return !! grep {$_ eq $codename} $self->all;
}
sub unsupported {
my ($self, $date) = @_;
$date = $self->{'date'} if (!defined($date));
my @supported = $self->supported($date);
my @unsupported = ();
for my $row ($self->_avail($date)) {
push(@unsupported, $row->{'series'}) if ! grep {$_ eq $row->{'series'}} @supported;
}
return @unsupported;
}
}
{
package DebianDistroInfo;
use parent -norequire, 'DistroInfo';
sub new {
my ($class) = @_;
my $self = $class->SUPER::new('debian');
bless($self, $class);
return $self;
}
sub codename {
my ($self, $release, $date, $default) = @_;
$date = $self->{'date'} if (!defined($date));
return $self->devel($date) if ($release eq 'unstable');
return $self->testing($date) if ($release eq 'testing');
return $self->stable($date) if ($release eq 'stable');
return $self->old($date) if ($release eq 'oldstable');
return $default;
}
sub devel {
my ($self, $date) = @_;
$date = $self->{'date'} if (!defined($date));
my @distros;
for my $row ($self->_avail($date)) {
if (!defined($row->{'release'})
|| ($date < $row->{'release'}
&& !defined($row->{'eol'}) || $date <= $row->{'eol'})) {
push(@distros, $row);
}
}
if (scalar(@distros) < 2) {
warn $outdated_error;
return 0;
}
return $distros[-2]{'series'};
}
sub old {
my ($self, $date) = @_;
$date = $self->{'date'} if (!defined($date));
my @distros;
for my $row ($self->_avail($date)) {
if (defined($row->{'release'}) && $date >= $row->{'release'}) {
push(@distros, $row);
}
}
if (scalar(@distros) < 2) {
warn $outdated_error;
return 0;
}
return $distros[-2]{'series'};
}
sub supported {
my ($self, $date) = @_;
$date = $self->{'date'} if (!defined($date));
my @distros;
for my $row ($self->_avail($date)) {
if (!defined($row->{'eol'}) || $date <= $row->{'eol'}) {
push(@distros, $row->{'series'});
}
}
return @distros;
}
sub testing {
my ($self, $date) = @_;
$date = $self->{'date'} if (!defined($date));
my @distros;
for my $row ($self->_avail($date)) {
if ((!defined($row->{'release'}) && $row->{'version'})
|| (defined($row->{'release'}) && $date < $row->{'release'}
&& (!defined($row->{'eol'}) || $date <= $row->{'eol'}))) {
push(@distros, $row);
}
}
if (scalar(@distros) == 0) {
warn $outdated_error;
return 0;
}
return $distros[-1]{'series'};
}
sub valid {
my ($self, $codename) = @_;
my %codenames = (
'unstable' => 1,
'testing' => 1,
'stable' => 1,
'oldstable' => 1,
);
return $self->SUPER::valid($codename) || exists($codenames{$codename});
}
}
{
package UbuntuDistroInfo;
use parent -norequire, 'DistroInfo';
sub new {
my ($class) = @_;
my $self = $class->SUPER::new('ubuntu');
bless($self, $class);
return $self;
}
sub lts {
my ($self, $date) = @_;
$date = $self->{'date'} if (!defined($date));
my @distros;
for my $row ($self->_avail($date)) {
if ($row->{'version'} =~ m/LTS/ && $date >= $row->{'release'}
&& $date <= $row->{'eol'}) {
push(@distros, $row);
}
}
if (scalar(@distros) == 0) {
warn $outdated_error;
return 0;
}
return $distros[-1]{'series'};
}
sub is_lts {
my ($self, $codename) = @_;
for my $row (@{$self->{'rows'}}) {
if ($row->{'series'} eq $codename) {
return ($row->{'version'} =~ m/LTS/);
}
}
return 0;
}
sub supported {
my ($self, $date) = @_;
$date = $self->{'date'} if (!defined($date));
my @distros;
for my $row ($self->_avail($date)) {
if ($date <= $row->{'eol'}
|| (defined($row->{'eol-server'}) && $date <= $row->{'eol-server'})) {
push(@distros, $row->{'series'});
}
}
return @distros;
}
sub supported_esm {
my ($self, $date) = @_;
$date = $self->{'date'} if (!defined($date));
my @distros;
for my $row ($self->_avail($date)) {
if (defined($row->{'eol-esm'}) && $date <= $row->{'eol-esm'}) {
push(@distros, $row->{'series'});
}
}
return @distros;
}
}
1;
# vi: set et sta sw=4 ts=4:

121
perl/test.pl Executable file
View File

@ -0,0 +1,121 @@
#!/usr/bin/perl
# Copyright (C) 2011-2012, Stefano Rivera <stefanor@debian.org>
#
# Permission to use, copy, modify, and/or distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
# copyright notice and this permission notice appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
use strict;
use warnings;
use Test::Simple tests => 32;
use lib '.';
use Debian::DistroInfo;
sub unique {
my ($needles, $haystack) = @_;
my $unique = 0;
my %hash = ();
@hash{@$haystack}=();
for my $needle (@$needles) {
$unique++ if not exists($hash{$needle});
}
return $unique;
}
sub symmetric_difference {
my ($a, $b) = @_;
return unique($a, $b) + unique($b, $a);
}
my @all = ();
my @returned = ();
# Test our helpers:
@all = ('a', 'b', 'c');
@returned = ('a', 'b', 'c');
ok(unique(\@all, \@returned) == 0, 'unique: Matching lists');
ok(symmetric_difference(\@all, \@returned) == 0,
'symmetric_difference: Matching lists');
@returned = ('a', 'b');
ok(unique(\@all, \@returned) == 1, 'unique: 1 Unique Item');
ok(unique(\@returned, \@all) == 0, 'unique: 1 Unique Item in the haystack');
ok(symmetric_difference(\@all, \@returned) == 1,
'symmetric_difference: 1 Unique Item');
# Test DistroInfo:
my @expected = ();
my $date = Debian::DistroInfo::convert_date('2011-01-10');
my $deb = DebianDistroInfo->new();
@all = ('buzz', 'rex', 'bo', 'hamm', 'slink', 'potato', 'woody', 'sarge',
'etch', 'lenny', 'squeeze', 'sid', 'experimental');
@returned = $deb->all($date);
ok(unique(\@all, \@returned) == 0, 'Debian all');
ok($deb->devel($date) eq 'sid', 'Debian devel');
ok($deb->old($date) eq 'etch', 'Debian oldstable');
ok($deb->stable($date) eq 'lenny', 'Debian stable');
ok($deb->testing($date) eq 'squeeze', 'Debian testing');
ok($deb->valid('sid'), 'Debian valid');
ok($deb->valid('stable'), 'Debian valid');
ok(!$deb->valid('foobar'), 'Debian invalid');
@expected = ('lenny', 'squeeze', 'sid', 'experimental');
@returned = $deb->supported($date);
ok(symmetric_difference(\@expected, \@returned) == 0,
'Debian supported');
@expected = ('buzz', 'rex', 'bo', 'hamm', 'slink', 'potato', 'woody', 'sarge',
'etch');
@returned = $deb->unsupported($date);
ok(symmetric_difference(\@expected, \@returned) == 0,
'Debian unsupported');
ok(!defined($deb->codename('foo')), 'Debian codename, invalid');
ok($deb->codename('testing', $date) eq $deb->testing($date),
'Debian codename');
ok(!defined($deb->version('foo')), 'Debian version, invalid');
ok($deb->version('lenny') eq '5.0', 'Debian version');
my $ubu = UbuntuDistroInfo->new();
@all = ('warty', 'hoary', 'breezy', 'dapper', 'edgy', 'feisty', 'gutsy',
'hardy', 'intrepid', 'jaunty', 'karmic', 'lucid', 'maverick', 'natty');
@returned = $ubu->all($date);
ok(unique(\@all, \@returned) == 0, 'Ubuntu all');
ok($ubu->version('Maverick Meerkat') eq '10.10', 'Ubuntu version');
ok($ubu->version('lucid') eq '10.04 LTS', 'Ubuntu LTS version');
ok($ubu->devel($date) eq 'natty', 'Ubuntu devel');
ok($ubu->lts($date) eq 'lucid', 'Ubuntu LTS');
ok($ubu->stable($date) eq 'maverick', 'Ubuntu stable');
ok($ubu->valid('lucid'), 'Ubuntu valid');
ok(!$ubu->valid(42), 'Ubuntu invalid');
ok($ubu->is_lts('lucid'), 'Ubuntu is_lts');
ok(!$ubu->is_lts(42), 'Ubuntu !is_lts');
ok(!$ubu->is_lts('warty'), 'Ubuntu !is_lts');
@expected = ('dapper', 'hardy', 'karmic', 'lucid', 'maverick', 'natty');
@returned = $ubu->supported($date);
ok(symmetric_difference(\@expected, \@returned) == 0,
'Ubuntu supported');
@expected = ('warty', 'hoary', 'breezy', 'edgy', 'feisty', 'gutsy', 'intrepid',
'jaunty');
@returned = $ubu->unsupported($date);
ok(symmetric_difference(\@expected, \@returned) == 0,
'Ubuntu unsupported');
# vi: set et sta sw=4 ts=4:

91
python/debian-distro-info Executable file
View File

@ -0,0 +1,91 @@
#!/usr/bin/python
# Copyright (C) 2009-2011, Benjamin Drung <bdrung@debian.org>
#
# Permission to use, copy, modify, and/or distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
# copyright notice and this permission notice appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
# pylint: disable=invalid-name
# pylint: enable=invalid-name
"""provides information about Debian's distributions"""
import argparse
import os
import sys
from distro_info import convert_date, DebianDistroInfo
def parse_args():
script_name = os.path.basename(sys.argv[0])
usage = "%s [options]" % (script_name)
epilog = "See %s(1) for more info." % (script_name)
parser = argparse.ArgumentParser(usage=usage, epilog=epilog)
parser.add_argument("--date", dest="date", default=None,
help="date for calculating the version (default: today).")
parser.add_argument("-a", "--all", dest="all", action="store_true",
help="list all known versions")
parser.add_argument("-d", "--devel", dest="devel", action="store_true",
help="latest development version")
parser.add_argument("-o", "--old", dest="old", action="store_true",
help="latest old (stable) version")
parser.add_argument("-s", "--stable", dest="stable", action="store_true",
help="latest stable version")
parser.add_argument("--supported", dest="supported", action="store_true",
help="list of all supported stable versions")
parser.add_argument("-t", "--testing", dest="testing", action="store_true",
help="current testing version")
parser.add_argument("--unsupported", dest="unsupported",
help="list of all unsupported stable versions")
args = parser.parse_args()
versions = [args.all, args.devel, args.old, args.stable,
args.supported, args.testing, args.unsupported]
if len([x for x in versions if x]) != 1:
parser.error("You have to select exactly one of --all, --devel, --old, "
"--stable, --supported, --testing, --unsupported.")
if args.date is not None:
try:
args.date = convert_date(args.date)
except ValueError:
parser.error("Option --date needs to be a date in ISO 8601 "
"format.")
return args
def main():
args = parse_args()
if args.all:
for distro in DebianDistroInfo().all:
sys.stdout.write(distro + "\n")
elif args.devel:
sys.stdout.write(DebianDistroInfo().devel(args.date) + "\n")
elif args.old:
sys.stdout.write(DebianDistroInfo().old(args.date) + "\n")
elif args.stable:
sys.stdout.write(DebianDistroInfo().stable(args.date) + "\n")
elif args.supported:
for distro in DebianDistroInfo().supported(args.date):
sys.stdout.write(distro + "\n")
elif args.testing:
sys.stdout.write(DebianDistroInfo().testing(args.date) + "\n")
elif args.unsupported:
for distro in DebianDistroInfo().unsupported(args.date):
sys.stdout.write(distro + "\n")
if __name__ == "__main__":
main()

288
python/distro_info.py Normal file
View File

@ -0,0 +1,288 @@
# Copyright (C) 2009-2012, Benjamin Drung <bdrung@debian.org>
#
# Permission to use, copy, modify, and/or distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
# copyright notice and this permission notice appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
"""provides information about Ubuntu's and Debian's distributions"""
import csv
import datetime
import os
def convert_date(string):
"""Convert a date string in ISO 8601 into a datetime object."""
if not string:
date = None
else:
parts = [int(x) for x in string.split("-")]
if len(parts) == 3:
(year, month, day) = parts
date = datetime.date(year, month, day)
elif len(parts) == 2:
(year, month) = parts
if month == 12:
date = datetime.date(year, month, 31)
else:
date = datetime.date(year, month + 1, 1) - datetime.timedelta(1)
else:
raise ValueError("Date not in ISO 8601 format.")
return date
def _get_data_dir():
"""Get the data directory based on the module location."""
return "/usr/share/distro-info"
class DistroDataOutdated(Exception):
"""Distribution data outdated."""
def __init__(self):
super(DistroDataOutdated, self).__init__(
"Distribution data outdated. "
"Please check for an update for distro-info-data. See "
"/usr/share/doc/distro-info-data/README.Debian for details.")
class DistroRelease(object):
"""Represents a distributions release"""
# pylint: disable=too-few-public-methods
# pylint: disable=too-many-instance-attributes
def __init__(self, version, codename, series, created=None, release=None, eol=None,
eol_server=None, eol_esm=None):
# pylint: disable=too-many-arguments
self.version = version
self.codename = codename
self.series = series
self.created = created
self.release = release
self.eol = eol
self.eol_server = eol_server
self.eol_esm = eol_esm
def is_supported(self, date):
"""Check whether this release is supported on the given date."""
return date >= self.created and (self.eol is None or date <= self.eol or (
self.eol_server is not None and date <= self.eol_server))
def _get_date(row, column):
return convert_date(row[column]) if column in row else None
class DistroInfo(object):
"""Base class for distribution information.
Use DebianDistroInfo or UbuntuDistroInfo instead of using this directly.
"""
def __init__(self, distro):
self._distro = distro
filename = os.path.join(_get_data_dir(), distro.lower() + ".csv")
csvfile = open(filename)
csv_reader = csv.DictReader(csvfile)
self._releases = []
for row in csv_reader:
release = DistroRelease(row['version'], row['codename'], row['series'],
_get_date(row, 'created'), _get_date(row, 'release'),
_get_date(row, 'eol'), _get_date(row, 'eol-server'),
_get_date(row, 'eol-esm'))
self._releases.append(release)
csvfile.close()
self._date = datetime.date.today()
@property
def all(self):
"""List codenames of all known distributions."""
return [x.series for x in self._releases]
def get_all(self, result="codename"):
"""List all known distributions."""
return [self._format(result, x) for x in self._releases]
def _avail(self, date):
"""Return all distributions that were available on the given date."""
return [x for x in self._releases if date >= x.created]
def codename(self, release, date=None, default=None):
"""Map codename aliases to the codename they describe."""
# pylint: disable=no-self-use,unused-argument
return release
def version(self, name, default=None):
"""Map codename or series to version"""
for release in self._releases:
if name in (release.codename, release.series):
return release.version
return default
def devel(self, date=None, result="codename"):
"""Get latest development distribution based on the given date."""
if date is None:
date = self._date
distros = [x for x in self._avail(date) if x.release is None or
(date < x.release and (x.eol is None or date <= x.eol))]
if not distros:
raise DistroDataOutdated()
return self._format(result, distros[-1])
def _format(self, format_string, release):
"""Format a given distribution entry."""
if format_string == "object":
return release
if format_string == "codename":
return release.series
if format_string == "fullname":
return self._distro + " " + release.version + ' "' + release.codename + '"'
if format_string == "release":
return release.version
raise ValueError("Only codename, fullname, object, and release are allowed "
"result values, but not '" + format_string + "'.")
def stable(self, date=None, result="codename"):
"""Get latest stable distribution based on the given date."""
if date is None:
date = self._date
distros = [x for x in self._avail(date) if x.release is not None and
date >= x.release and (x.eol is None or date <= x.eol)]
if not distros:
raise DistroDataOutdated()
return self._format(result, distros[-1])
def supported(self, date=None, result=None):
"""Get list of all supported distributions based on the given date."""
raise NotImplementedError()
def valid(self, codename):
"""Check if the given codename is known."""
return codename in self.all
def unsupported(self, date=None, result="codename"):
"""Get list of all unsupported distributions based on the given date."""
if date is None:
date = self._date
supported = self.supported(date)
distros = [self._format(result, x) for x in self._avail(date)
if x.series not in supported]
return distros
class DebianDistroInfo(DistroInfo):
"""provides information about Debian's distributions"""
def __init__(self):
super(DebianDistroInfo, self).__init__("Debian")
def codename(self, release, date=None, default=None):
"""Map 'unstable', 'testing', etc. to their codenames."""
if release == "unstable":
codename = self.devel(date)
elif release == "testing":
codename = self.testing(date)
elif release == "stable":
codename = self.stable(date)
elif release == "oldstable":
codename = self.old(date)
else:
codename = default
return codename
def devel(self, date=None, result="codename"):
"""Get latest development distribution based on the given date."""
if date is None:
date = self._date
distros = [x for x in self._avail(date) if x.release is None or
(date < x.release and (x.eol is None or date <= x.eol))]
if len(distros) < 2:
raise DistroDataOutdated()
return self._format(result, distros[-2])
def old(self, date=None, result="codename"):
"""Get old (stable) Debian distribution based on the given date."""
if date is None:
date = self._date
distros = [x for x in self._avail(date)
if x.release is not None and date >= x.release]
if len(distros) < 2:
raise DistroDataOutdated()
return self._format(result, distros[-2])
def supported(self, date=None, result="codename"):
"""Get list of all supported Debian distributions based on the given
date."""
if date is None:
date = self._date
distros = [self._format(result, x) for x in self._avail(date)
if x.eol is None or date <= x.eol]
return distros
def testing(self, date=None, result="codename"):
"""Get latest testing Debian distribution based on the given date."""
if date is None:
date = self._date
distros = [x for x in self._avail(date) if (x.release is None and x.version) or
(x.release is not None and date < x.release and
(x.eol is None or date <= x.eol))]
if not distros:
raise DistroDataOutdated()
return self._format(result, distros[-1])
def valid(self, codename):
"""Check if the given codename is known."""
return (DistroInfo.valid(self, codename) or
codename in ["unstable", "testing", "stable", "oldstable"])
class UbuntuDistroInfo(DistroInfo):
"""provides information about Ubuntu's distributions"""
def __init__(self):
super(UbuntuDistroInfo, self).__init__("Ubuntu")
def lts(self, date=None, result="codename"):
"""Get latest long term support (LTS) Ubuntu distribution based on the
given date."""
if date is None:
date = self._date
distros = [x for x in self._releases if x.version.find("LTS") >= 0 and
x.release <= date <= x.eol]
if not distros:
raise DistroDataOutdated()
return self._format(result, distros[-1])
def is_lts(self, codename):
"""Is codename an LTS release?"""
distros = [x for x in self._releases if x.series == codename]
if not distros:
return False
return "LTS" in distros[0].version
def supported(self, date=None, result="codename"):
"""Get list of all supported Ubuntu distributions based on the given
date."""
if date is None:
date = self._date
distros = [self._format(result, x) for x in self._avail(date)
if date <= x.eol or
(x.eol_server is not None and date <= x.eol_server)]
return distros
def supported_esm(self, date=None, result="codename"):
"""Get list of all ESM supported Ubuntu distributions based on the
given date."""
if date is None:
date = self._date
distros = [self._format(result, x) for x in self._avail(date)
if x.eol_esm is not None and date <= x.eol_esm]
return distros

View File

@ -0,0 +1,62 @@
# Copyright (C) 2017, Benjamin Drung <benjamin.drung@profitbricks.com>
#
# Permission to use, copy, modify, and/or distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
# copyright notice and this permission notice appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
"""Test suite for distro-info"""
import inspect
import os
import site
import sys
import unittest
def get_source_files():
"""Return a list of sources files/directories (to check with flake8/pylint)"""
scripts = ["debian-distro-info", "ubuntu-distro-info"]
modules = []
py_files = ["distro_info.py", "setup.py"]
files = []
for code_file in scripts + modules + py_files:
is_script = code_file in scripts
if not os.path.exists(code_file): # pragma: no cover
# The alternative path in the OLDPWD environment is needed for Debian's pybuild
# Use installed files as fallback
for alternative_path in [os.environ.get("OLDPWD", "")] + site.getsitepackages():
alternative = os.path.join(alternative_path, code_file)
if os.path.exists(alternative):
code_file = alternative
break
if is_script:
with open(code_file, "rb") as script_file:
shebang = script_file.readline().decode("utf-8")
if ((sys.version_info[0] == 3 and "python3" in shebang)
or ("python" in shebang and "python3" not in shebang)):
files.append(code_file)
else:
files.append(code_file)
return files
def unittest_verbosity():
"""Return the verbosity setting of the currently running unittest
program, or None if none is running.
"""
frame = inspect.currentframe()
while frame:
self = frame.f_locals.get("self")
if isinstance(self, unittest.TestProgram):
return self.verbosity
frame = frame.f_back
return None # pragma: no cover

View File

@ -0,0 +1,40 @@
[MASTER]
# Pickle collected data for later comparisons.
persistent=no
[MESSAGES CONTROL]
# Disable the message, report, category or checker with the given id(s). You
# can either give multiple identifiers separated by comma (,) or put this
# option multiple times (only on the command line, not in the configuration
# file where it should appear only once).You can also use "--disable=all" to
# disable everything first and then reenable specific checks. For example, if
# you want to run only the similarities checker, you can use "--disable=all
# --enable=similarities". If you want to run only the classes checker, but have
# no Warning level messages displayed, use"--disable=all --enable=classes
# --disable=W"
disable=locally-disabled,locally-enabled,missing-docstring,useless-object-inheritance
[REPORTS]
# Tells whether to display a full report or only the messages
reports=no
[FORMAT]
# Maximum number of characters on a single line.
max-line-length=99
# String used as indentation unit. This is usually " " (4 spaces) or "\t" (1
# tab).
indent-string=' '
[SIMILARITIES]
# Minimum lines number of a similarity.
min-similarity-lines=11

View File

@ -0,0 +1,183 @@
# test_distro_info.py - Test suite for distro_info
#
# Copyright (C) 2011, Benjamin Drung <bdrung@debian.org>
#
# Permission to use, copy, modify, and/or distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
# copyright notice and this permission notice appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
"""Test suite for distro_info"""
import datetime
import unittest
from distro_info import DebianDistroInfo, UbuntuDistroInfo
class DebianDistroInfoTestCase(unittest.TestCase): # pylint: disable=too-many-public-methods
"""TestCase object for distro_info.DebianDistroInfo"""
def setUp(self): # pylint: disable=invalid-name
self._distro_info = DebianDistroInfo()
self._date = datetime.date(2011, 1, 10)
def test_all(self):
"""Test: List all known Debian distributions."""
all_distros = set(["buzz", "rex", "bo", "hamm", "slink", "potato",
"woody", "sarge", "etch", "lenny", "squeeze", "sid",
"experimental"])
self.assertEqual(all_distros - set(self._distro_info.all), set())
def test_devel(self):
"""Test: Get latest development Debian distribution."""
self.assertEqual(self._distro_info.devel(self._date), "sid")
def test_old(self):
"""Test: Get old (stable) Debian distribution."""
self.assertEqual(self._distro_info.old(self._date), "etch")
def test_stable(self):
"""Test: Get latest stable Debian distribution."""
self.assertEqual(self._distro_info.stable(self._date), "lenny")
def test_supported(self):
"""Test: List all supported Debian distribution."""
self.assertEqual(self._distro_info.supported(self._date),
["lenny", "squeeze", "sid", "experimental"])
def test_testing(self):
"""Test: Get latest testing Debian distribution."""
self.assertEqual(self._distro_info.testing(self._date), "squeeze")
def test_valid(self):
"""Test: Check for valid Debian distribution."""
self.assertTrue(self._distro_info.valid("sid"))
self.assertTrue(self._distro_info.valid("stable"))
self.assertFalse(self._distro_info.valid("foobar"))
def test_unsupported(self):
"""Test: List all unsupported Debian distribution."""
unsupported = ["buzz", "rex", "bo", "hamm", "slink", "potato", "woody",
"sarge", "etch"]
self.assertEqual(self._distro_info.unsupported(self._date), unsupported)
def test_codename(self):
"""Test: Codename decoding"""
self.assertIsNone(self._distro_info.codename('foobar'))
self.assertEqual(self._distro_info.codename('testing', self._date),
self._distro_info.testing(self._date))
def test_version(self):
"""Test: Version decoding"""
self.assertIsNone(self._distro_info.version('foobar'))
self.assertEqual(self._distro_info.version('lenny'), '5.0')
def test_codename_result(self):
"""Test: Check result set to codename."""
self.assertEqual(self._distro_info.old(self._date, "codename"), "etch")
self.assertEqual(self._distro_info.devel(self._date, result="codename"),
"sid")
def test_fullname(self):
"""Test: Check result set to fullname."""
self.assertEqual(self._distro_info.stable(self._date, "fullname"),
'Debian 5.0 "Lenny"')
result = self._distro_info.testing(self._date, result="fullname")
self.assertEqual(result, 'Debian 6.0 "Squeeze"')
def test_release(self):
"""Test: Check result set to release."""
self.assertEqual(self._distro_info.devel(self._date, "release"), "")
self.assertEqual(self._distro_info.testing(self._date, "release"),
"6.0")
self.assertEqual(self._distro_info.stable(self._date, result="release"),
"5.0")
class UbuntuDistroInfoTestCase(unittest.TestCase): # pylint: disable=too-many-public-methods
"""TestCase object for distro_info.UbuntuDistroInfo"""
def setUp(self): # pylint: disable=invalid-name
self._distro_info = UbuntuDistroInfo()
self._date = datetime.date(2011, 1, 10)
def test_all(self):
"""Test: List all known Ubuntu distributions."""
all_distros = set(["warty", "hoary", "breezy", "dapper", "edgy",
"feisty", "gutsy", "hardy", "intrepid", "jaunty",
"karmic", "lucid", "maverick", "natty"])
self.assertEqual(all_distros - set(self._distro_info.all), set())
def test_devel(self):
"""Test: Get latest development Ubuntu distribution."""
self.assertEqual(self._distro_info.devel(self._date), "natty")
def test_lts(self):
"""Test: Get latest long term support (LTS) Ubuntu distribution."""
self.assertEqual(self._distro_info.lts(self._date), "lucid")
def test_stable(self):
"""Test: Get latest stable Ubuntu distribution."""
self.assertEqual(self._distro_info.stable(self._date), "maverick")
def test_supported(self):
"""Test: List all supported Ubuntu distribution."""
supported = ["dapper", "hardy", "karmic", "lucid", "maverick", "natty"]
self.assertEqual(self._distro_info.supported(self._date), supported)
def test_unsupported(self):
"""Test: List all unsupported Ubuntu distributions."""
unsupported = ["warty", "hoary", "breezy", "edgy", "feisty", "gutsy",
"intrepid", "jaunty"]
self.assertEqual(self._distro_info.unsupported(self._date), unsupported)
def test_current_unsupported(self):
"""Test: List all unsupported Ubuntu distributions today."""
unsupported = set(["warty", "hoary", "breezy", "edgy", "feisty",
"gutsy", "intrepid", "jaunty"])
self.assertEqual(unsupported -
set(self._distro_info.unsupported()), set())
def test_valid(self):
"""Test: Check for valid Ubuntu distribution."""
self.assertTrue(self._distro_info.valid("lucid"))
self.assertFalse(self._distro_info.valid("42"))
def test_is_lts(self):
"""Test: Check if Ubuntu distribution is an LTS."""
self.assertTrue(self._distro_info.is_lts("lucid"))
self.assertFalse(self._distro_info.is_lts("42"))
self.assertFalse(self._distro_info.is_lts("warty"))
def test_codename(self):
"""Test: Check result set to codename."""
self.assertEqual(self._distro_info.lts(self._date, "codename"), "lucid")
self.assertEqual(self._distro_info.devel(self._date, result="codename"),
"natty")
def test_version(self):
"""Test: Check result set to version."""
self.assertEqual(self._distro_info.version("lucid"), '10.04 LTS')
self.assertEqual(self._distro_info.version("Maverick Meerkat"), '10.10')
def test_fullname(self):
"""Test: Check result set to fullname."""
self.assertEqual(self._distro_info.stable(self._date, "fullname"),
'Ubuntu 10.10 "Maverick Meerkat"')
self.assertEqual(self._distro_info.lts(self._date, result="fullname"),
'Ubuntu 10.04 LTS "Lucid Lynx"')
def test_release(self):
"""Test: Check result set to release."""
self.assertEqual(self._distro_info.devel(self._date, "release"),
"11.04")
self.assertEqual(self._distro_info.lts(self._date, result="release"),
"10.04 LTS")

View File

@ -0,0 +1,51 @@
# Copyright (C) 2017-2018, Benjamin Drung <bdrung@debian.org>
#
# Permission to use, copy, modify, and/or distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
# copyright notice and this permission notice appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
"""test_flake8.py - Run flake8 check"""
import subprocess
import sys
import unittest
from . import get_source_files, unittest_verbosity
class Flake8TestCase(unittest.TestCase):
"""
This unittest class provides a test that runs the flake8 code
checker (which combines pycodestyle and pyflakes) on the Python
source code. The list of source files is provided by the
get_source_files() function.
"""
def test_flake8(self):
"""Test: Run flake8 on Python source code"""
cmd = [sys.executable, "-m", "flake8", "--ignore", "H301,H403,H405,W504", "--max-line-length=99"] + get_source_files()
if unittest_verbosity() >= 2:
sys.stderr.write("Running following command:\n{}\n".format(" ".join(cmd)))
process = subprocess.Popen(cmd, stdout=subprocess.PIPE,
stderr=subprocess.PIPE, close_fds=True)
out, err = process.communicate()
if process.returncode != 0: # pragma: no cover
msgs = []
if err:
msgs.append("flake8 exited with code {} and has unexpected output on stderr:\n{}"
.format(process.returncode, err.decode().rstrip()))
if out:
msgs.append("flake8 found issues:\n{}".format(out.decode().rstrip()))
if not msgs:
msgs.append("flake8 exited with code {} and has no output on stdout or stderr."
.format(process.returncode))
self.fail("\n".join(msgs))

View File

@ -0,0 +1,71 @@
# test_help.py - Ensure scripts can run --help.
#
# Copyright (C) 2010, Stefano Rivera <stefanor@debian.org>
#
# Permission to use, copy, modify, and/or distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
# copyright notice and this permission notice appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
# AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
# LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
# OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
# PERFORMANCE OF THIS SOFTWARE.
import fcntl
import os
import select
import signal
import subprocess
import time
import setup
from distro_info_test import unittest
TIMEOUT = 5
class HelpTestCase(unittest.TestCase):
@classmethod
def populate(cls):
for script in setup.SCRIPTS:
setattr(cls, 'test_' + script, cls.make_help_tester(script))
@classmethod
def make_help_tester(cls, script):
def tester(self):
null = open('/dev/null', 'r')
process = subprocess.Popen(['./' + script, '--help'],
close_fds=True, stdin=null,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
started = time.time()
out = []
fds = [process.stdout.fileno(), process.stderr.fileno()]
for file_descriptor in fds:
fcntl.fcntl(file_descriptor, fcntl.F_SETFL,
fcntl.fcntl(file_descriptor, fcntl.F_GETFL) | os.O_NONBLOCK)
while time.time() - started < TIMEOUT:
for file_descriptor in select.select(fds, [], fds, TIMEOUT)[0]:
out.append(os.read(file_descriptor, 1024))
if process.poll() is not None:
break
if process.poll() is None:
os.kill(process.pid, signal.SIGTERM)
time.sleep(1)
if process.poll() is None:
os.kill(process.pid, signal.SIGKILL)
null.close()
self.assertEqual(process.poll(), 0,
"%s failed to return usage within %i seconds.\n"
"Output:\n%s"
% (script, TIMEOUT, ''.encode('ascii').join(out)))
process.stdout.close()
process.stderr.close()
return tester

View File

@ -0,0 +1,68 @@
# Copyright (C) 2010, Stefano Rivera <stefanor@debian.org>
# Copyright (C) 2017-2018, Benjamin Drung <bdrung@debian.org>
#
# Permission to use, copy, modify, and/or distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
# copyright notice and this permission notice appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
# AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
# LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
# OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
# PERFORMANCE OF THIS SOFTWARE.
"""test_pylint.py - Run pylint"""
import os
import re
import subprocess
import sys
import unittest
from . import get_source_files, unittest_verbosity
CONFIG = os.path.join(os.path.dirname(__file__), "pylint.conf")
class PylintTestCase(unittest.TestCase):
"""
This unittest class provides a test that runs the pylint code check
on the Python source code. The list of source files is provided by
the get_source_files() function and pylint is purely configured via
a config file.
"""
def test_pylint(self):
"""Test: Run pylint on Python source code"""
cmd = [sys.executable, "-m", "pylint", "--rcfile=" + CONFIG, "--"] + get_source_files()
if unittest_verbosity() >= 2:
sys.stderr.write("Running following command:\n{}\n".format(" ".join(cmd)))
process = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
close_fds=True)
out, err = process.communicate()
if process.returncode != 0: # pragma: no cover
# Strip trailing summary (introduced in pylint 1.7). This summary might look like:
#
# ------------------------------------
# Your code has been rated at 10.00/10
#
out = re.sub("^(-+|Your code has been rated at .*)$", "", out.decode(),
flags=re.MULTILINE).rstrip()
# Strip logging of used config file (introduced in pylint 1.8)
err = re.sub("^Using config file .*\n", "", err.decode()).rstrip()
msgs = []
if err:
msgs.append("pylint exited with code {} and has unexpected output on stderr:\n{}"
.format(process.returncode, err))
if out:
msgs.append("pylint found issues:\n{}".format(out))
if not msgs:
msgs.append("pylint exited with code {} and has no output on stdout or stderr."
.format(process.returncode))
self.fail("\n".join(msgs))

36
python/setup.py Executable file
View File

@ -0,0 +1,36 @@
#!/usr/bin/python
import os
import re
from setuptools import setup
PACKAGES = []
PY_MODULES = ['distro_info']
SCRIPTS = [
'debian-distro-info',
'ubuntu-distro-info',
]
def get_debian_version():
"""look what Debian version we have"""
version = None
changelog = "../debian/changelog"
if os.path.exists(changelog):
head = open(changelog, "rb").readline().decode("utf-8")
match = re.compile(r".*\((.*)\).*").match(head)
if match:
version = match.group(1)
return version
if __name__ == '__main__':
setup(
name='distro-info',
version=get_debian_version(),
py_modules=PY_MODULES,
packages=PACKAGES,
test_suite="distro_info_test",
)

88
python/ubuntu-distro-info Executable file
View File

@ -0,0 +1,88 @@
#!/usr/bin/python
# Copyright (C) 2009-2011, Benjamin Drung <bdrung@debian.org>
#
# Permission to use, copy, modify, and/or distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
# copyright notice and this permission notice appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
# pylint: disable=invalid-name
# pylint: enable=invalid-name
"""provides information about Ubuntu's distributions"""
import argparse
import os
import sys
from distro_info import convert_date, UbuntuDistroInfo
def parse_args():
script_name = os.path.basename(sys.argv[0])
usage = "%s [options]" % (script_name)
epilog = "See %s(1) for more info." % (script_name)
parser = argparse.ArgumentParser(usage=usage, epilog=epilog)
parser.add_argument("--date", dest="date", default=None,
help="date for calculating the version (default: today).")
parser.add_argument("-a", "--all", dest="all", action="store_true",
help="list all known versions")
parser.add_argument("-d", "--devel", dest="devel", action="store_true",
help="latest development version")
parser.add_argument("--lts", dest="lts", action="store_true",
help="latest long term support (LTS) version")
parser.add_argument("-s", "--stable", dest="stable", action="store_true",
help="latest stable version")
parser.add_argument("--supported", dest="supported", action="store_true",
help="list of all supported stable versions")
parser.add_argument("--unsupported", dest="unsupported", action="store_true",
help="list of all unsupported stable versions")
args = parser.parse_args()
versions = [args.all, args.devel, args.lts, args.stable,
args.supported, args.unsupported]
if len([x for x in versions if x]) != 1:
parser.error("You have to select exactly one of --all, --devel, --lts, "
"--stable, --supported, --unsupported.")
if args.date is not None:
try:
args.date = convert_date(args.date)
except ValueError:
parser.error("Option --date needs to be a date in ISO 8601 "
"format.")
return args
def main():
args = parse_args()
if args.all:
for distro in UbuntuDistroInfo().all:
sys.stdout.write(distro + "\n")
elif args.devel:
sys.stdout.write(UbuntuDistroInfo().devel(args.date) + "\n")
elif args.lts:
sys.stdout.write(UbuntuDistroInfo().lts(args.date) + "\n")
elif args.stable:
sys.stdout.write(UbuntuDistroInfo().stable(args.date) + "\n")
elif args.supported:
for distro in UbuntuDistroInfo().supported(args.date):
sys.stdout.write(distro + "\n")
elif args.unsupported:
for distro in UbuntuDistroInfo().unsupported(args.date):
sys.stdout.write(distro + "\n")
if __name__ == "__main__":
main()

20
shell/Makefile Normal file
View File

@ -0,0 +1,20 @@
PREFIX ?= /usr
VENDOR ?= $(shell dpkg-vendor --query Vendor | tr '[:upper:]' '[:lower:]')
build: debian-distro-info ubuntu-distro-info
%-distro-info: debian-distro-info.in distro-info-util.sh
sed -e '/^\. .*distro-info-util.sh\"$$/r distro-info-util.sh' $< | \
sed -e '/^##/d;/^\. .*distro-info-util.sh\"$$/d' | \
python -c 'import re,sys;print re.sub("(?<=\n)#BEGIN \w*#\n(.|\n)*?\n#END \w*#\n", "", re.sub("(?<=\n)#(BEGIN|END) $*#\n", "", sys.stdin.read())),' > $@
chmod +x $@
install: debian-distro-info ubuntu-distro-info
install -d $(DESTDIR)$(PREFIX)/bin
install -m 755 $^ $(DESTDIR)$(PREFIX)/bin
ln -s $(VENDOR)-distro-info $(DESTDIR)$(PREFIX)/bin/distro-info
clean:
rm -f debian-distro-info ubuntu-distro-info
.PHONY: build clean install

65
shell/debian-distro-info.in Executable file
View File

@ -0,0 +1,65 @@
#!/bin/sh
set -f
# Copyright (C) 2012 Canonical Ltd.
# Author: Scott Moser <smoser@ubuntu.com>
#
# Permission to use, copy, modify, and/or distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
# copyright notice and this permission notice appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
DISTRO_INFO_NAME="Debian"
DISTRO_INFO_ARGS="--all --devel --oldstable --stable --supported
--testing --unsupported"
DISTRO_INFO_DATA="/usr/share/distro-info/debian.csv"
. "${0%/*}/distro-info-util.sh"
Usage() {
cat <<EOF
Usage: debian-distro-info [options]
Options:
-h --help show this help message and exit
--date=DATE date for calculating the version (default: today)
-a --all list all known versions
-d --devel latest development version
-o --oldstable latest oldstable version
-s --stable latest stable version
--supported list of all supported stable versions
-t --testing current testing version
--unsupported list of all unsupported stable versions
-c --codename print the codename (default)
-r --release print the release version
-f --fullname print the full name
See debian-distro-info(1) for more info.
EOF
}
cb_devel() {
[ "$series" = "sid" ] && store
return 1
}
cb_oldstable() {
released && next_is released && store
return 1;
}
cb_testing() {
date_ge "$CMP_DATE" "$created" &&
{ [ -z "$release" -a -n "$version" ] || date_ge "$release" "$CMP_DATE"; } &&
store
return 1;
}
main "$@"
# vi: ts=4 noexpandtab

212
shell/distro-info-util.sh Normal file
View File

@ -0,0 +1,212 @@
## Copyright (C) 2012 Canonical Ltd.
## Author: Scott Moser <smoser@ubuntu.com>
##
## Permission to use, copy, modify, and/or distribute this software for any
## purpose with or without fee is hereby granted, provided that the above
## copyright notice and this permission notice appear in all copies.
##
## THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
## WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
## MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
## ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
## WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
## ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
## OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
STORED="0"
store() {
# store this result.
# Sets the global 'STORED'. if STORED is set, fmt will be called
# once at the end with the data that is stored.
STORED=1
s_version=$version;
s_codename=$codename;
s_series=$series;
s_created=$created;
s_release=$release;
s_eol=$eol;
s_eols=$eols;
}
restore() {
# restore data previously stored with store
version=$s_version;
codename=$s_codename;
series=$s_series;
created=$s_created;
release=$s_release;
eol=$s_eol;
eols=$s_eols;
}
created() {
[ -n "$created" ] && date_ge "$CMP_DATE" "$created"
}
date_ge() {
# compare 2 dates of format YYYY-MM or YYYY-MM-DD
# assume that YYYY-MM is the 30th of the month
local IFS="-" clean1 clean2 d1="$1" d2="$2"
set -- ${d1} 30
clean1="$1$2$3"
set -- ${d2} 30
clean2="$1$2$3"
[ "$clean1" -ge "$clean2" ]
}
devel() {
created && ! released
}
next_is() {
# call a function as if you were calling it for next
local version=$n_version codename=$n_codename series=$n_series
local created=$n_created release=$n_release eol=$n_eol eols=$n_eols
"$@"
}
released() {
[ -n "$version" -a -n "$release" ] && date_ge "$CMP_DATE" "$release"
}
cb_all() {
:
}
cb_stable() {
released && [ -n "$n_version" ] && ! next_is released && store
return 1;
}
cb_supported() {
date_ge "$eols" "$CMP_DATE" && created
}
cb_unsupported() {
created && ! cb_supported
}
print_codename() {
echo "$series"
}
print_fullname() {
echo "${DISTRO_INFO_NAME} $version \"$codename\""
}
print_release() {
echo "${version:-${series}}"
}
filter_data() {
local OIFS="$IFS" tmpvar=""
local callback="$1" fmt="$2" found=0
shift 2;
IFS=","
local version codename series created release eol eols
local n_version n_codename n_series n_created n_release n_eol n_eols
{
read tmpvar # header of file
read version codename series created release eol eols
[ -n "$eol" ] || eol="9999-99-99"
[ -n "$eols" ] || eols=$eol
while read n_version n_codename n_series n_created n_release n_eol n_eols; do
[ -n "$n_eol" ] || n_eol="9999-99-99"
[ -n "$n_eols" ] || n_eols=$n_eol
"$callback" && found=$(($found+1)) && "$fmt"
version=$n_version; codename=$n_codename; series=$n_series
created=$n_created; release=$n_release; eol=$n_eol;
eols=$n_eols
done
} < "$DISTRO_INFO_DATA"
"$callback" && found=$(($found+1)) && "$fmt"
[ "$STORED" = "0" ] || { restore; found=$(($found+1)); "$fmt"; }
[ $found -ne 0 ]
}
data_outdated() {
error "${0##*/}: Distribution data outdated." \
"Please check for an update for distro-info-data." \
"See /usr/share/doc/distro-info-data/README.Debian for details."
}
date_requires_arg() {
error "${0##*/}: option \`--date' requires an argument DATE"
}
error() { echo "$@" >&2; }
not_exactly_one() {
local arg="" msg="You have to select exactly one of"
for arg in $DISTRO_INFO_ARGS; do
msg="$msg ${arg},"
done
msg="${msg%,}."
error "${0##*/}: ${msg}"
}
main() {
local CMP_DATE="" callback="" fmt="print_codename" date="now"
local tmp tokenized
while [ $# -ne 0 ]; do
if [ "${1#-[a-z][a-z]}" != "$1" ]; then
# support combined shortformat arguments, by exploding
cur="${1#-}"
while [ -n "$cur" ]; do
tmp=${cur#?};
tokenized="${tokenized} -${cur%${tmp}}"
cur=${tmp}
done
shift
set -- ${tokenized} "$@"
fi
case "$1" in
-a|--all)
[ -z "$callback" ] || { not_exactly_one; return 1; }
callback="all";;
--date=*)
date=${1#*=};
[ -n "$date" ] || { date_requires_arg; return 1; }
;;
--date)
date="$2";
[ -n "$2" ] || { date_requires_arg; return 1; }
shift;;
-d|--devel)
[ -z "$callback" ] || { not_exactly_one; return 1; }
callback="devel";;
-s|--stable)
[ -z "$callback" ] || { not_exactly_one; return 1; }
callback="stable";;
--supported|--unsupported)
[ -z "$callback" ] || { not_exactly_one; return 1; }
callback="${1#--}";;
-c|--codename) fmt="print_codename";;
-r|--release) fmt="print_release";;
-f|--fullname) fmt="print_fullname";;
#BEGIN ubuntu#
--lts)
[ -z "$callback" ] || { not_exactly_one; return 1; }
callback="lts";;
#END ubuntu#
#BEGIN debian#
-o|--oldstable|--old)
[ -z "$callback" ] || { not_exactly_one; return 1; }
callback="oldstable";;
-t|--testing)
[ -z "$callback" ] || { not_exactly_one; return 1; }
callback="testing";;
#END debian#
-h|--help) Usage; exit 0;;
--*|-*)
error "${0##*/}: unrecognized option \`$1'";
return 1;;
*) error "${0##*/}: unrecognized arguments: $*";
return 1;;
esac
shift;
done
[ -n "$callback" ] || { not_exactly_one; return 1; }
CMP_DATE=$(date --utc +"%Y-%m-%d" "--date=$date" 2>/dev/null) ||
{ error "${0##*/}: invalid date \`${date}'"; return 1; }
filter_data "cb_$callback" "$fmt" || { data_outdated; return 1; }
return
}
## vi: ts=4 syntax=sh noexpandtab

57
shell/ubuntu-distro-info.in Executable file
View File

@ -0,0 +1,57 @@
#!/bin/sh
set -f
# Copyright (C) 2012 Canonical Ltd.
# Author: Scott Moser <smoser@ubuntu.com>
#
# Permission to use, copy, modify, and/or distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
# copyright notice and this permission notice appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
DISTRO_INFO_NAME="Ubuntu"
DISTRO_INFO_ARGS="--all --devel --lts --stable --supported --unsupported"
DISTRO_INFO_DATA="/usr/share/distro-info/ubuntu.csv"
. "${0%/*}/distro-info-util.sh"
Usage() {
cat <<EOF
Usage: ${0##*/} [options]
Options:
-h --help show this help message and exit
--date=DATE date for calculating the version (default: today)
-a --all list all known versions
-d --devel latest development version
--lts latest long term support (LTS) version
-s --stable latest stable version
--supported list of all supported stable versions
--unsupported list of all unsupported stable versions
-c --codename print the codename (default)
-r --release print the release version
-f --fullname print the full name
See ubuntu-distro-info(1) for more info.
EOF
}
cb_devel() {
devel && store
return 1
}
cb_lts() {
[ "${version#*LTS}" != "${version}" ] && released && store
return 1;
}
main "$@"
# vi: ts=4 noexpandtab

View File

@ -0,0 +1,35 @@
# Copyright (C) 2012, Benjamin Drung <bdrung@debian.org>
#
# Permission to use, copy, modify, and/or distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
# copyright notice and this permission notice appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
runCommand() {
local param="$1"
local exp_stdout="$2"
local exp_stderr="$3"
local exp_retval=$4
local stdoutF="${SHUNIT_TMPDIR}/stdout"
local stderrF="${SHUNIT_TMPDIR}/stderr"
eval "${COMMAND} $param" > ${stdoutF} 2> ${stderrF}
retval=$?
assertEquals "standard output of ${COMMAND} $param\n" "$exp_stdout" "$(cat ${stdoutF})"
assertEquals "error output of ${COMMAND} $param\n" "$exp_stderr" "$(cat ${stderrF})"
assertEquals "return value of ${COMMAND} $param\n" $exp_retval $retval
}
success() {
runCommand "$1" "$2" "" 0
}
failure() {
runCommand "$1" "" "$2" 1
}

320
test-debian-distro-info Executable file
View File

@ -0,0 +1,320 @@
#!/bin/sh
# Copyright (C) 2012-2014, Benjamin Drung <bdrung@debian.org>
#
# Permission to use, copy, modify, and/or distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
# copyright notice and this permission notice appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
COMMAND="${COMMAND:-${0%/*}/debian-distro-info}"
. "${0%/*}/shunit2-helper-functions.sh"
testAlias() {
success "--alias sid" "unstable"
success "--date 2010-10-20 --alias squeeze" "testing"
success "--date 2011-11-20 --alias squeeze" "stable"
success "--alias whatever" "whatever"
}
testAll() {
local result="buzz
rex
bo
hamm
slink
potato
woody
sarge
etch
lenny
squeeze
sid
experimental"
local pattern=$(echo $result | sed "s/ /\\\\|/g")
success "--date 2007-07-07 --all | grep -w \"$pattern\"" "$result"
success "-a | grep -w \"$pattern\"" "$result"
}
testDevel() {
success "--date 2011-01-10 --devel" "sid"
success "-d --date=2002-01-10 --codename" "sid"
}
testOldstable() {
success "--oldstable --date=2011-01-10" "etch"
success "-c --date=2008-07-06 -o" "sarge"
# Compatibility with 0.2.2
success "--old --date=2011-01-10" "etch"
}
testStable() {
success "--date=2011-01-10 --stable" "lenny"
success "--date=2001-02-10 -s" "potato"
}
testSupported() {
local result="lenny
squeeze
sid
experimental"
success "--date=2011-01-10 --supported" "$result"
}
testUnsupported() {
local result="buzz
rex
bo
hamm
slink
potato
woody
sarge
etch"
success "--date=2011-01-10 --unsupported" "$result"
}
testTesting() {
success "--date=2011-01-10 --testing" "squeeze"
success "-t --date=2010-01-10" "squeeze"
}
testFullname() {
success "--date=2011-01-10 --stable --fullname" 'Debian 5.0 "Lenny"'
success "--date=2011-01-10 --stable --fullname -ycreated" \
'Debian 5.0 "Lenny" -1373'
success "--date=2011-01-10 --stable --fullname --days=created" \
'Debian 5.0 "Lenny" -1373'
success "--date=2011-01-10 --stable --fullname -y" \
'Debian 5.0 "Lenny" -695'
success "--date=2011-01-10 --stable --fullname --days" \
'Debian 5.0 "Lenny" -695'
success "--date=2011-01-10 --stable --fullname -yrelease" \
'Debian 5.0 "Lenny" -695'
success "--date=2011-01-10 --stable --fullname --days=release" \
'Debian 5.0 "Lenny" -695'
success "--date=2011-01-10 --stable --fullname -yeol" \
'Debian 5.0 "Lenny" 392'
success "--date=2011-01-10 --stable --fullname --days=eol" \
'Debian 5.0 "Lenny" 392'
success "--date=2011-01-10 -f --testing" 'Debian 6.0 "Squeeze"'
success "--date=2011-01-10 -f --testing -ycreated" \
'Debian 6.0 "Squeeze" -695'
success "--date=2011-01-10 -f --testing --days=created" \
'Debian 6.0 "Squeeze" -695'
success "--date=2011-01-10 -f --testing -y" \
'Debian 6.0 "Squeeze" 27'
success "--date=2011-01-10 -f --testing --days" \
'Debian 6.0 "Squeeze" 27'
success "--date=2011-01-10 -f --testing -yrelease" \
'Debian 6.0 "Squeeze" 27'
success "--date=2011-01-10 -f --testing --days=release" \
'Debian 6.0 "Squeeze" 27'
# sid is never released so can never go eol
success "--date=2011-01-10 -f --devel -yeol" \
'Debian "Sid" (unknown)'
success "--date=2011-01-10 -f --devel --days=eol" \
'Debian "Sid" (unknown)'
}
testRelease() {
success "--date=2011-01-10 -r --devel" "sid"
success "--date=2011-01-10 --testing --release" "6.0"
success "--date=2011-01-10 --release --stable" "5.0"
}
testSeries() {
success "-r --series rex" "1.2"
}
testCombinedShortform() {
success "-rd" "sid"
}
testReleaseDate() {
success "--date 2009-02-13 -o" "sarge"
success "--date 2009-02-13 -s" "etch"
success "--date 2009-02-13 -t" "lenny"
success "--date 2009-02-13 -d" "sid"
success "--date 2009-02-14 -o" "etch"
success "--date 2009-02-14 -s" "lenny"
success "--date 2009-02-14 -t" "squeeze"
success "--date 2009-02-14 -d" "sid"
}
testHelp() {
local help='Usage: debian-distro-info [options]
Options:
-h --help show this help message and exit
--date=DATE date for calculating the version (default: today)
--series=SERIES series to calculate the version for
-y[MILESTONE] additionally, display days until milestone
--days=[MILESTONE] (created, release, eol)
--alias=DIST print the alias (oldstable, stable, testing, unstable)
relative to the given distribution codename
-a --all list all known versions
-d --devel latest development version
-o --oldstable latest oldstable version
-s --stable latest stable version
--supported list of all supported stable versions
-t --testing current testing version
--unsupported list of all unsupported stable versions
-c --codename print the codename (default)
-f --fullname print the full name
-r --release print the release version
See debian-distro-info(1) for more info.'
success "--help" "$help"
success "-h" "$help"
}
testExactlyOne() {
local result='debian-distro-info: You have to select exactly one of --alias, --all, --devel, --oldstable, --stable, --supported, --series, --testing, --unsupported.'
failure "" "$result"
failure "-ad" "$result"
failure "--alias foo -a" "$result"
}
testUnrecognizedOption() {
failure "--foo" "debian-distro-info: unrecognized option \`--foo'"
failure "-x" "debian-distro-info: unrecognized option \`-x'"
failure "--lts" "debian-distro-info: unrecognized option \`--lts'"
}
testUnrecognizedArguments() {
failure "bar" "debian-distro-info: unrecognized arguments: bar"
failure "baz -s foo" "debian-distro-info: unrecognized arguments: baz foo"
}
testMissingArgumentAlias() {
failure "--alias" "debian-distro-info: option \`--alias' requires an argument DIST"
}
testMissingArgumentDate() {
failure "--date" "debian-distro-info: option \`--date' requires an argument DATE"
}
testMissingArgumentSeries() {
failure "--series" "debian-distro-info: option \`--series' requires an argument SERIES"
}
testInvalidAlias() {
failure "--alias Sid" "debian-distro-info: invalid distribution codename \`Sid'"
failure "--alias wr0ng" "debian-distro-info: invalid distribution codename \`wr0ng'"
}
testInvalidDate() {
failure "--date fail -s" "debian-distro-info: invalid date \`fail'"
failure "--date=2010-02-30 -d" "debian-distro-info: invalid date \`2010-02-30'"
}
testInvalidSeries() {
failure "--series wr0ng" "debian-distro-info: invalid distribution series \`wr0ng'"
}
testMultipleAlias() {
failure "--alias a --alias b" "debian-distro-info: --alias requested multiple times."
}
testMultipleDates() {
failure "--date 2007-06-05 -s --date 2004-03-02" "debian-distro-info: Date specified multiple times."
}
testMultipleSeries() {
failure "--series wheezy --series jessie" "debian-distro-info: series requested multiple times."
}
testUnknownSeries() {
failure "--series foobar" "debian-distro-info: unknown distribution series \`foobar'"
}
testDays() {
# day after lenny released
date=2009-02-15
success "--date=$date --stable " "lenny"
success "--date=$date --stable --days=created" "-679"
success "--date=$date --stable --days=created -c" "lenny -679"
success "--date=$date --stable --days=created -f" "Debian 5.0 \"Lenny\" -679"
success "--date=$date --stable --days=created -r" "5.0 -679"
success "--date=$date --stable --days=release" "-1"
success "--date=$date --stable --days=release -c" "lenny -1"
success "--date=$date --stable --days=release -f" "Debian 5.0 \"Lenny\" -1"
success "--date=$date --stable --days=release -r" "5.0 -1"
success "--date=$date --stable --days" "-1"
success "--date=$date --stable --days -c" "lenny -1"
success "--date=$date --stable --days -f" "Debian 5.0 \"Lenny\" -1"
success "--date=$date --stable --days -r" "5.0 -1"
success "--date=$date --stable --days=eol" "1086"
success "--date=$date --stable --days=eol -c" "lenny 1086"
success "--date=$date --stable --days=eol -f" "Debian 5.0 \"Lenny\" 1086"
success "--date=$date --stable --days=eol -r" "5.0 1086"
# date woody released
date=2002-07-19
success "--date=$date --stable" "woody"
success "--date=$date --stable --days=created" "-703"
success "--date=$date --stable --days=created -c" "woody -703"
success "--date=$date --stable --days=created -f" "Debian 3.0 \"Woody\" -703"
success "--date=$date --stable --days=created -r" "3.0 -703"
success "--date=$date --stable --days=release" "0"
success "--date=$date --stable --days=release -c" "woody 0"
success "--date=$date --stable --days=release -f" "Debian 3.0 \"Woody\" 0"
success "--date=$date --stable --days=release -r" "3.0 0"
success "--date=$date --stable --days" "0"
success "--date=$date --stable --days -c" "woody 0"
success "--date=$date --stable --days -f" "Debian 3.0 \"Woody\" 0"
success "--date=$date --stable --days -r" "3.0 0"
success "--date=$date --stable --days=eol" "1442"
success "--date=$date --stable --days=eol -c" "woody 1442"
success "--date=$date --stable --days=eol -f" "Debian 3.0 \"Woody\" 1442"
success "--date=$date --stable --days=eol -r" "3.0 1442"
# day before etch was released
date=2007-04-07
success "--testing --date=$date" "etch"
success "--testing --date=$date --days=created" "-670"
success "--testing --date=$date --days=created -c" "etch -670"
success "--testing --date=$date --days=created -f" "Debian 4.0 \"Etch\" -670"
success "--testing --date=$date --days=created -r" "4.0 -670"
success "--testing --date=$date --days" "1"
success "--testing --date=$date --days -c" "etch 1"
success "--testing --date=$date --days -f" "Debian 4.0 \"Etch\" 1"
success "--testing --date=$date --days -r" "4.0 1"
success "--testing --date=$date --days=release" "1"
success "--testing --date=$date --days=release -c" "etch 1"
success "--testing --date=$date --days=release -f" "Debian 4.0 \"Etch\" 1"
success "--testing --date=$date --days=release -r" "4.0 1"
success "--testing --date=$date --days=eol" "1045"
success "--testing --date=$date --days=eol -c" "etch 1045"
success "--testing --date=$date --days=eol -f" "Debian 4.0 \"Etch\" 1045"
success "--testing --date=$date --days=eol -r" "4.0 1045"
}
. shunit2

337
test-ubuntu-distro-info Executable file
View File

@ -0,0 +1,337 @@
#!/bin/sh
# Copyright (C) 2012-2014, Benjamin Drung <bdrung@debian.org>
#
# Permission to use, copy, modify, and/or distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
# copyright notice and this permission notice appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
COMMAND="${COMMAND:-${0%/*}/ubuntu-distro-info}"
. "${0%/*}/shunit2-helper-functions.sh"
testAll() {
local result="warty
hoary
breezy
dapper
edgy
feisty
gutsy
hardy
intrepid
jaunty
karmic
lucid
maverick
natty"
local pattern=$(echo $result | sed "s/ /\\\\|/g")
success "--date 2007-07-07 --all | grep -w \"$pattern\"" "$result"
success "-a | grep -w \"$pattern\"" "$result"
}
testDevel() {
success "--date 2011-01-10 --devel" "natty"
success "--date 2010-05-10 -d --codename" "maverick"
}
testLatest() {
success "--date 2011-01-10 --latest" "natty"
success "--date 2010-05-10 -l --codename" "maverick"
}
testLTS() {
success "--lts --date=2011-01-10" "lucid"
}
testStable() {
success "--date=2011-01-10 -c --stable" "maverick"
success "--date=2009-01-10 -s" "intrepid"
}
testSupported() {
local result="dapper
hardy
karmic
lucid
maverick
natty"
success "--date=2011-01-10 --supported" "$result"
}
testUnsupported() {
local result="warty
hoary
breezy
edgy
feisty
gutsy
intrepid
jaunty"
success "--date=2011-01-10 --unsupported" "$result"
}
testFullname() {
success "--date=2011-01-10 --stable -f" 'Ubuntu 10.10 "Maverick Meerkat"'
success "--date=2011-01-10 --stable -f -ycreated" \
'Ubuntu 10.10 "Maverick Meerkat" -256'
success "--date=2011-01-10 --stable -f --days=created" \
'Ubuntu 10.10 "Maverick Meerkat" -256'
success "--date=2011-01-10 --stable -f -y" \
'Ubuntu 10.10 "Maverick Meerkat" -92'
success "--date=2011-01-10 --stable -f --days" \
'Ubuntu 10.10 "Maverick Meerkat" -92'
success "--date=2011-01-10 --stable -f -yrelease" \
'Ubuntu 10.10 "Maverick Meerkat" -92'
success "--date=2011-01-10 --stable -f --days=release" \
'Ubuntu 10.10 "Maverick Meerkat" -92'
success "--date=2011-01-10 --stable -f -yeol" \
'Ubuntu 10.10 "Maverick Meerkat" 456'
success "--date=2011-01-10 --stable -f --days=eol" \
'Ubuntu 10.10 "Maverick Meerkat" 456'
success "--date=2011-01-10 --fullname --lts" \
'Ubuntu 10.04 LTS "Lucid Lynx"'
success "--date=2011-01-10 --fullname --lts -y" \
'Ubuntu 10.04 LTS "Lucid Lynx" -256'
success "--date=2011-01-10 --fullname --lts --days" \
'Ubuntu 10.04 LTS "Lucid Lynx" -256'
success "--date=2011-01-10 --fullname --lts -ycreated" \
'Ubuntu 10.04 LTS "Lucid Lynx" -438'
success "--date=2011-01-10 --fullname --lts --days=created" \
'Ubuntu 10.04 LTS "Lucid Lynx" -438'
success "--date=2011-01-10 --fullname --lts -yrelease" \
'Ubuntu 10.04 LTS "Lucid Lynx" -256'
success "--date=2011-01-10 --fullname --lts --days=release" \
'Ubuntu 10.04 LTS "Lucid Lynx" -256'
success "--date=2011-01-10 --fullname --lts -yeol" \
'Ubuntu 10.04 LTS "Lucid Lynx" 850'
success "--date=2011-01-10 --fullname --lts --days=eol" \
'Ubuntu 10.04 LTS "Lucid Lynx" 850'
success "--date=2011-01-10 --fullname --lts -yeol-server" \
'Ubuntu 10.04 LTS "Lucid Lynx" 1570'
success "--date=2011-01-10 --fullname --lts --days=eol-server" \
'Ubuntu 10.04 LTS "Lucid Lynx" 1570'
}
testRelease() {
success "--date=2011-01-10 --lts --release" "10.04 LTS"
success "--date=2011-01-10 --lts --release -ycreated" \
"10.04 LTS -438"
success "--date=2011-01-10 --lts --release --days=created" \
"10.04 LTS -438"
success "--date=2011-01-10 --lts --release --days" \
"10.04 LTS -256"
success "--date=2011-01-10 --lts --release -y" \
"10.04 LTS -256"
success "--date=2011-01-10 --lts --release --days=release" \
"10.04 LTS -256"
success "--date=2011-01-10 --lts --release -yrelease" \
"10.04 LTS -256"
success "--date=2011-01-10 --lts --release --days=eol" \
"10.04 LTS 850"
success "--date=2011-01-10 --lts --release -yeol" \
"10.04 LTS 850"
success "--date=2011-01-10 --lts --release --days=eol-server" \
"10.04 LTS 1570"
success "--date=2011-01-10 --lts --release -yeol-server" \
"10.04 LTS 1570"
success "--date=2011-01-10 -r --stable" "10.10"
}
testSeries() {
success "-r --series warty" "4.10"
}
testCombinedShortform() {
success "-fs --date=2011-01-10" 'Ubuntu 10.10 "Maverick Meerkat"'
}
testReleaseDate() {
success "--date 2010-04-28 -s" "karmic"
success "--date 2010-04-28 -d" "lucid"
success "--date 2010-04-29 -s" "lucid"
success "--date 2010-04-29 -d" "maverick"
}
testHelp() {
local help='Usage: ubuntu-distro-info [options]
Options:
-h --help show this help message and exit
--date=DATE date for calculating the version (default: today)
--series=SERIES series to calculate the version for
-y[MILESTONE] additionally, display days until milestone
--days=[MILESTONE] (created, release, eol, eol-server, eol-esm)
-a --all list all known versions
-d --devel latest development version
--lts latest long term support (LTS) version
-s --stable latest stable version
--supported list of all supported stable versions
--supported-esm list of all Ubuntu Advantage supported stable versions
--unsupported list of all unsupported stable versions
-c --codename print the codename (default)
-f --fullname print the full name
-r --release print the release version
See ubuntu-distro-info(1) for more info.'
success "--help" "$help"
success "-h" "$help"
}
testExactlyOne() {
local result='ubuntu-distro-info: You have to select exactly one of --all, --devel, --latest, --lts, --stable, --supported, --supported-esm, --series, --unsupported.'
failure "" "$result"
failure "--date=2009-01-10 -sad" "$result"
}
testUnrecognizedOption() {
failure "--bar" "ubuntu-distro-info: unrecognized option \`--bar'"
failure "-z" "ubuntu-distro-info: unrecognized option \`-z'"
failure "--testing" "ubuntu-distro-info: unrecognized option \`--testing'"
failure "--oldstable" "ubuntu-distro-info: unrecognized option \`--oldstable'"
}
testUnrecognizedArguments() {
failure "foo" "ubuntu-distro-info: unrecognized arguments: foo"
failure "foo --all bar" "ubuntu-distro-info: unrecognized arguments: foo bar"
}
testMissingArgumentDate() {
failure "--date" "ubuntu-distro-info: option \`--date' requires an argument DATE"
}
testMissingArgumentSeries() {
failure "--series" "ubuntu-distro-info: option \`--series' requires an argument SERIES"
}
testDistributionDataOutdated() {
local future_year=$(expr $(date +"%Y" --date=now) + 7)
local outdated_e="Distribution data outdated.
Please check for an update for distro-info-data. See /usr/share/doc/distro-info-data/README.Debian for details."
failure "--date 1970-10-03 --lts" "ubuntu-distro-info: $outdated_e"
failure "--date ${future_year}-10-03 -s" "ubuntu-distro-info: $outdated_e"
failure "--date ${future_year}-10-03 -d" "ubuntu-distro-info: $outdated_e"
failure "--date ${future_year}-10-03 -l" "ubuntu-distro-info: $outdated_e"
}
testInvalidDate() {
failure "--date fail -s" "ubuntu-distro-info: invalid date \`fail'"
failure "--date=2010-02-30 -d" "ubuntu-distro-info: invalid date \`2010-02-30'"
}
testInvalidSeries() {
failure "--series wr0ng" "ubuntu-distro-info: invalid distribution series \`wr0ng'"
}
testMultipleDates() {
failure "--date 2007-06-05 -s --date 2004-03-02" "ubuntu-distro-info: Date specified multiple times."
}
testMultipleSeries() {
failure "--series trusty --series utopic" "ubuntu-distro-info: series requested multiple times."
}
testUnknownSeries() {
failure "--series foobar" "ubuntu-distro-info: unknown distribution series \`foobar'"
}
testDays() {
# day after lucid released
date=2010-04-30
success "--date=$date --lts" "lucid"
success "--date=$date --lts --days=created" "-183"
success "--date=$date --lts --days=created -c" "lucid -183"
success "--date=$date --lts --days=created -f" "Ubuntu 10.04 LTS \"Lucid Lynx\" -183"
success "--date=$date --lts --days=created -r" "10.04 LTS -183"
success "--date=$date --lts --days=release" "-1"
success "--date=$date --lts --days=release -c" "lucid -1"
success "--date=$date --lts --days=release -f" "Ubuntu 10.04 LTS \"Lucid Lynx\" -1"
success "--date=$date --lts --days=release -r" "10.04 LTS -1"
success "--date=$date --lts --days" "-1"
success "--date=$date --lts --days -c" "lucid -1"
success "--date=$date --lts --days -f" "Ubuntu 10.04 LTS \"Lucid Lynx\" -1"
success "--date=$date --lts --days -r" "10.04 LTS -1"
success "--date=$date --lts --days=eol" "1105"
success "--date=$date --lts --days=eol -c" "lucid 1105"
success "--date=$date --lts --days=eol -f" "Ubuntu 10.04 LTS \"Lucid Lynx\" 1105"
success "--date=$date --lts --days=eol -r" "10.04 LTS 1105"
success "--date=$date --lts --days=eol-server" "1825"
success "--date=$date --lts --days=eol-server -c" "lucid 1825"
success "--date=$date --lts --days=eol-server -f" "Ubuntu 10.04 LTS \"Lucid Lynx\" 1825"
success "--date=$date --lts --days=eol-server -r" "10.04 LTS 1825"
# date precise released
date=2012-04-26
success "--date=$date --lts" "precise"
success "--date=$date --lts --days=created" "-196"
success "--date=$date --lts --days=created -c" "precise -196"
success "--date=$date --lts --days=created -f" "Ubuntu 12.04 LTS \"Precise Pangolin\" -196"
success "--date=$date --lts --days=created -r" "12.04 LTS -196"
success "--date=$date --lts --days=release" "0"
success "--date=$date --lts --days=release -c" "precise 0"
success "--date=$date --lts --days=release -f" "Ubuntu 12.04 LTS \"Precise Pangolin\" 0"
success "--date=$date --lts --days=release -r" "12.04 LTS 0"
success "--date=$date --lts --days" "0"
success "--date=$date --lts --days -c" "precise 0"
success "--date=$date --lts --days -f" "Ubuntu 12.04 LTS \"Precise Pangolin\" 0"
success "--date=$date --lts --days -r" "12.04 LTS 0"
success "--date=$date --lts --days=eol" "1826"
success "--date=$date --lts --days=eol -c" "precise 1826"
success "--date=$date --lts --days=eol -f" "Ubuntu 12.04 LTS \"Precise Pangolin\" 1826"
success "--date=$date --lts --days=eol -r" "12.04 LTS 1826"
success "--date=$date --lts --days=eol-server" "1826"
success "--date=$date --lts --days=eol-server -c" "precise 1826"
success "--date=$date --lts --days=eol-server -f" "Ubuntu 12.04 LTS \"Precise Pangolin\" 1826"
success "--date=$date --lts --days=eol-server -r" "12.04 LTS 1826"
# day before raring was released
date=2013-04-24
success "--devel --date=$date" "raring"
success "--devel --date=$date --days=created" "-188"
success "--devel --date=$date --days=created -c" "raring -188"
success "--devel --date=$date --days=created -r" "13.04 -188"
success "--devel --date=$date --days=created -f" "Ubuntu 13.04 \"Raring Ringtail\" -188"
success "--devel --date=$date --days" "1"
success "--devel --date=$date --days -c" "raring 1"
success "--devel --date=$date --days -r" "13.04 1"
success "--devel --date=$date --days -f" "Ubuntu 13.04 \"Raring Ringtail\" 1"
success "--devel --date=$date --days=release" "1"
success "--devel --date=$date --days=release -c" "raring 1"
success "--devel --date=$date --days=release -r" "13.04 1"
success "--devel --date=$date --days=release -f" "Ubuntu 13.04 \"Raring Ringtail\" 1"
success "--devel --date=$date --days=eol" "278"
success "--devel --date=$date --days=eol -c" "raring 278"
success "--devel --date=$date --days=eol -r" "13.04 278"
success "--devel --date=$date --days=eol -f" "Ubuntu 13.04 \"Raring Ringtail\" 278"
}
. shunit2

37
ubuntu-distro-info.c Normal file
View File

@ -0,0 +1,37 @@
/*
* Copyright (C) 2012-2013, Benjamin Drung <bdrung@debian.org>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#define UBUNTU
#define CSV_NAME "ubuntu"
#define CSV_HEADER "version,codename,series,created,release,eol,eol-server,eol-esm"
#define DISTRO_NAME "Ubuntu"
#define NAME "ubuntu-distro-info"
// C standard libraries
#include <string.h>
#include "distro-info-util.h"
static bool filter_devel(const date_t *date, const distro_t *distro) {
return created(date, distro) && !released(date, distro);
}
static bool filter_lts(const date_t *date, const distro_t *distro) {
return strstr(distro->version, "LTS") != NULL &&
released(date, distro) && !eol(date, distro);
}
#include "distro-info-util.c"