Import Upstream version 0.23
This commit is contained in:
commit
55979f7c15
|
@ -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
|
|
@ -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"
|
|
@ -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;
|
||||
}
|
|
@ -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__
|
|
@ -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>.
|
|
@ -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>.
|
|
@ -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>.
|
|
@ -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
|
|
@ -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 ++ "\""
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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:
|
|
@ -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:
|
|
@ -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()
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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")
|
|
@ -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))
|
|
@ -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
|
|
@ -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))
|
|
@ -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",
|
||||
)
|
|
@ -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()
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
||||
}
|
|
@ -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
|
|
@ -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
|
|
@ -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"
|
Loading…
Reference in New Issue