* updated ksmtp (21.12.1 -> 21.12.2), untested
[t2-trunk.git] / source / UpdateList.cc
blobbb90c50aadc7d1b7697cb8337d2ada69a069a506
1 /*
2 * --- T2-COPYRIGHT-NOTE-BEGIN ---
3 * This copyright note is auto-generated by scripts/Create-CopyPatch.
4 *
5 * T2 SDE: source/UpdateList.cc
6 * Copyright (C) 2004 - 2021 The T2 SDE Project
7 *
8 * More information can be found in the files COPYING and README.
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; version 2 of the License. A copy of the
13 * GNU General Public License can be found in the file COPYING.
14 * --- T2-COPYRIGHT-NOTE-END ---
17 #include <ostream>
18 #include <algorithm>
20 #include "desc-parser.hh"
21 #include "Curl.hh"
22 #include "ctype.h"
24 #include <sys/types.h>
25 #include <sys/stat.h>
26 #include <unistd.h>
28 #include "pstream.hh"
29 #include "Glob.hh"
31 using namespace Utility;
33 const bool debug = false;
35 std::vector <std::string> suffixes;
37 class subversion {
38 public:
39 std::string::size_type next_part (const std::string& s,
40 std::string::size_type i)
42 val.clear();
43 std::locale loc;
44 const std::ctype<char>& ct = std::use_facet<std::ctype<char> >(loc);
46 // catch range exceptions
47 try {
48 // type to search for transition
49 int type_mask = std::ctype<char>::alpha;
50 if (isdigit(s[i]))
51 type_mask = std::ctype<char>::digit;
53 for (; i < s.size() && ct.is(type_mask, s[i]); ++i) {
54 val += s[i];
57 if (!(ct.is((std::ctype<char>::alpha | std::ctype<char>::digit),
58 s[i])))
59 ++i;
61 catch (...) {}
63 if (debug)
64 std::cout << "extracted part: " << val << std::endl;
65 return i;
68 std::string::size_type size() const {
69 return val.size();
72 char operator[] (std::string::size_type i) const {
73 return val[i];
76 bool empty() const {
77 return val.empty();
80 const std::string& str () const {
81 return val;
84 bool same_cclass (char a, char b) const {
85 // I'm sure this could be done more elegant
86 return ( (isalpha(a) && isalpha(b)) ||
87 (isdigit(a) && isdigit(b)) );
90 bool operator< (const subversion& other) const {
91 // special case for char < number
92 try { // catch range exceptions ;-)
93 if (!same_cclass(val[0], other.val[0]))
94 return (isalpha(val[0]));
96 // special case for numbers only, so real values are compared
97 if (isdigit(val[0]) && isdigit(other.val[0])) {
98 int int_val, other_int_val;
99 int_val = atoi(val.c_str());
100 other_int_val = atoi(other.val.c_str());
101 if (debug)
102 std::cout << "Intergers only: " << int_val << " "
103 << other_int_val << std::endl;
105 // do not compare overly large versions - they are most probably
106 // a data (e.g. 3-... vs 2004-...)
107 if (std::abs(int_val - other_int_val) > 100) {
108 if (debug)
109 std::cout << "Version differ too much - skipped ..."
110 << std::endl;
111 return true; // always lower ,-)
114 return int_val < other_int_val;
117 catch (...) {}
118 return val < other.val;
121 bool operator> (const subversion& other) const {
122 // special case for char < number
123 try { // catch range exceptions ;-)
124 if (!same_cclass(val[0], other.val[0]))
125 return (isalpha(other.val[0]));
127 // special case for numbers only, so real values are compared
128 if (isdigit(val[0]) && isdigit(other.val[0])) {
129 int int_val, other_int_val;
130 int_val = atoi(val.c_str());
131 other_int_val = atoi(other.val.c_str());
132 if (debug)
133 std::cout << "Intergers only: " << int_val << " "
134 << other_int_val << std::endl;
136 // do not compare overly large versions - they are most probably
137 // a data (e.g. 3-... vs 2004-...)
138 if (std::abs(int_val - other_int_val) > 100) {
139 if (debug)
140 std::cout << "Version differ too much - skipped ..."
141 << std::endl;
142 return false;
145 return int_val > other_int_val;
148 catch (...) {
150 return val > other.val;
153 private:
154 std::string val;
157 // maybe inherit std::string? -ReneR
158 class Version
160 public:
162 Version () {
163 version = "";
166 Version (const std::string& i_version) {
167 version = i_version;
170 void ExtractFromFilename (const std::string& filename) {
171 version.clear();
172 // start scan at index once, since there must be a package name
173 for (std::string::size_type i = 1; i < filename.size(); ++i) {
174 // match the first digit or -digit if present
175 if (isdigit(filename[i])) {
176 if (version.empty())
177 version = filename.substr(i);
178 else if (filename[i-1] == '-' || filename[i-1] == '_') {
179 version = filename.substr(i);
180 break;
184 if (debug) {
185 if (version.empty())
186 std::cout << "No version extracted!" << std::endl;
187 else
188 std::cout << "Vesion set to: " << version << std::endl;
192 std::string::size_type size() const {
193 return version.size();
196 char operator[] (std::string::size_type i) const {
197 return version[i];
200 int compare (const Version& a, const Version& b) const
202 if (debug)
203 std::cout << "Comparing: " << a.str() << " with " << b.str()
204 << std::endl;
206 std::string::size_type i, j;
207 subversion subv_a, subv_b;
208 for (i = j = 0; i < a.size() && j < b.size();) {
210 i = subv_a.next_part(a.str(), i);
211 j = subv_b.next_part(b.str(), j);
213 if (debug)
214 std::cout << subv_a.str() << " vs " << subv_b.str() << ": ";
216 if (subv_a < subv_b) {
217 if (debug)
218 std::cout << "... <" << std::endl;
219 return -1;
221 if (subv_a > subv_b) {
222 if (debug)
223 std::cout << "... >" << std::endl;
224 return 1;
227 if (debug)
228 std::cout << "=, " << std::endl;
231 i = subv_a.next_part(a.str(), i);
232 j = subv_b.next_part(b.str(), j);
234 if (debug)
235 std::cout << "Final: " << subv_a.str() << " vs " << subv_b.str() << ": ";
237 if (!subv_a.empty())
239 if (isdigit(subv_a[0])) {
240 if (debug)
241 std::cout << "... >" << std::endl;
242 return 1;
244 else {
245 if (debug)
246 std::cout << "... <" << std::endl;
247 return -1;
251 if (!subv_b.empty())
253 if (isdigit(subv_a[0])) {
254 if (debug)
255 std::cout << "... <" << std::endl;
256 return 1;
258 else {
259 if (debug)
260 std::cout << "... >" << std::endl;
261 return -1;
265 if (debug)
266 std::cout << "... =" << std::endl;
267 return 0;
270 bool operator< (const Version& b) const
272 return compare(*this, b) == -1;
275 bool operator> (const Version& b) const
277 return compare(*this, b) == 1;
280 bool operator== (const Version& b) const
282 return version == b.version;
285 const std::string& str () const {
286 return version;
289 private:
291 std::string version;
295 void ParseList (std::string file, std::istream& s,
296 const bool odd = true, const bool noprefix = false, const bool beta = true, const bool nineties = false) {
297 // search for a matching extension
298 std::string templ = file;
299 std::string suffix = "";
301 for (unsigned int i = 0; i < suffixes.size(); ++i) {
302 std::string& test = suffixes[i];
303 std::string::size_type s_pos = templ.rfind(test);
304 if (s_pos != std::string::npos) {
305 suffix = test;
306 templ = templ.substr(0, s_pos);
307 break;
311 Version version;
312 std::vector <Version> versions;
313 std::vector <Version> newer_versions;
315 version.ExtractFromFilename (templ);
317 std::string prefix;
319 std::string::size_type idx = templ.rfind(version.str());
320 if (idx == std::string::npos)
321 prefix = templ;
322 else
323 prefix = templ.substr(0, idx);
325 if (noprefix) prefix = "";
327 std::cout << file << "(" << version.str() << ") ---> " << prefix << "???" << suffix << std::endl;
329 while (!s.eof()) {
330 // read a line and search for the prefix
331 std::string token;
332 s >> token;
333 idx = token.find(prefix);
335 if (idx != std::string::npos) {
336 std::string::size_type idx2;
337 if (suffix.length() > 0)
338 idx2 = token.find(suffix, idx+1);
339 else {
340 idx2 = token.find(' ', idx+1);
341 if (idx2 == std::string::npos)
342 idx2 = token.length() - 1;
345 if (idx2 != std::string::npos) {
346 std::string::size_type begin = idx;
347 std::string::size_type length = (idx2-idx);
349 std::string matched = token.substr(begin, length);
350 Version v;
351 v.ExtractFromFilename (matched);
352 if (v.size() > 0) {
353 if (std::find (versions.begin(), versions.end(), v) == versions.end()) {
354 std::string s = v.str();
355 std::transform(s.begin(), s.end(), s.begin(), ::tolower);
356 if (!beta) {
357 if (s.find("alpha") != std::string::npos ||
358 s.find("beta") != std::string::npos ||
359 // TODO: pre[0-9], rc[0-9], r987
360 // TODO: very high last version, like 1.2.99
361 s.find("pre") != std::string::npos ||
362 s.find("rc") != std::string::npos)
363 continue;
367 subversion subv;
368 std::string::size_type i;
369 i = subv.next_part(v.str(), 0);
370 if (!subv.empty()) {
371 // minor version
372 i = subv.next_part(v.str(), i);
373 int minorv = atoi(subv.str().c_str());
375 // only if it has patch level (e.g. 1.2.3, not 1.3)
376 i = subv.next_part(v.str(), i);
377 int patchv = subv.empty() ? -1 : atoi(subv.str().c_str());
378 if (debug)
379 std::cout << "subv> " << v.str() << " " << subv.str() << " minor: " << minorv << " patch: " << patchv << " = " << i << std::endl;
381 if (!odd && !subv.empty() && (minorv & 1))
382 continue;
384 // TODO: based on prev version context
385 if (!nineties && patchv >= 90)
386 continue;
390 versions.push_back(v);
397 for (unsigned int i = 0; i < versions.size(); ++i) {
398 int sign = version.compare(versions[i], version);
400 std::cout << "[MATCH] (" << versions[i].str() << ")";
402 // TODO: insert here
404 switch (sign) {
405 case 0:
406 std::cout << " [=]" << std::endl;
407 break;
408 case 1:
409 std::cout << " [+]" << std::endl;
410 newer_versions.push_back(versions[i]);
411 break;
412 case -1:
413 std::cout << " [-]" << std::endl;
414 break;
419 std::cout << "-----------------------" << std::endl;
421 std::sort(newer_versions.begin(), newer_versions.end());
422 if (newer_versions.size() > 0)
423 std::cout << "XXX " << prefix << newer_versions.back().str() << std::endl;
425 std::cout << "-----------------------" << std::endl;
428 void GenList (const DownloadInfo& info, const bool odd, const bool noprefix, const bool beta)
430 CurlWrapper dl;
431 dl.SetConnectTimeout(15);
432 dl.SetMaxTime(30);
434 try {
435 dl.Download(info.url); //, 0, 200000); // 416 Range Not Satisfiable (RFC 7233)
436 std::auto_ptr<std::ifstream> s = dl.OpenFile();
437 ParseList(info.file, *s, odd, noprefix, beta);
438 s->close();
441 catch (TimeoutException e) {
442 std::cout << "Operation timeout" << std::endl;
444 catch (UnsupportedProtocolException e) {
445 std::cout << "Unsupported protocol : " << info.protocol << std::endl;
447 catch (MalformedUrlException e) {
448 std::cout << "Malformed URL or invalid URL options "
449 << info.url << std::endl;
451 catch (ConnectErrorException e) {
452 std::cout << "Could not connect to host" << std::endl;
454 catch (AccessDeniedException e) {
455 std::cout << "Access denied or invalid user/pass" << std::endl;
457 catch (DoesNotExistException e) {
458 std::cout << "File does not exist" << std::endl;
460 catch (CurlException e) {
461 std::cout << "operation canceled due to errors executing '"
462 << dl.GetCommand() << "'" << std::endl;
465 dl.RemoveFile();
468 void Check4Updates (const Package& package, const bool odd, const bool noprefix, const bool beta,
469 const std::string url = "")
471 unsigned int no_downloads = package.download.download_infos.size();
472 for (unsigned int dln = 0; dln < no_downloads; ++dln) {
473 DownloadInfo info = package.download.download_infos[dln];
474 if (info.protocol != "http" && info.protocol != "https" &&
475 info.protocol != "ftp")
476 continue;
478 // Apply translations ...
479 char* cmd[] = {"sed", "-f", "misc/share/CVTranslations", 0};
480 pstream sed ("sed", cmd);
482 if (!url.empty())
483 info.url = url;
484 else if (!package.cv_url.value.empty())
485 info.url = package.cv_url.value;
487 sed << info.url << std::endl;
488 sed.close_sink();
490 std::string translated_url;
491 sed >> translated_url;
493 if (translated_url != info.url) {
494 std::cout << "Download URL translated!" << std::endl;
495 info.url = translated_url;
498 std::cout << "Checking updates for " << info.url << std::endl;
500 GenList(info, odd, noprefix, beta);
504 int main (int argc, char* argv[])
506 Package package;
508 suffixes.push_back(".tar.zst");
509 suffixes.push_back(".tar.bz2");
510 suffixes.push_back(".tar.xz");
511 suffixes.push_back(".tar.gz");
512 suffixes.push_back(".tar.lz");
513 suffixes.push_back(".tzst");
514 suffixes.push_back(".tbz2");
515 suffixes.push_back(".tbz");
516 suffixes.push_back(".tgz");
517 suffixes.push_back(".bz2");
518 suffixes.push_back(".tlz");
519 suffixes.push_back(".gz");
520 suffixes.push_back(".zip");
521 suffixes.push_back(".xz");
522 suffixes.push_back(".zst");
523 suffixes.push_back(".lz");
525 #ifdef TESTING
526 std::vector<Version> versions;
527 versions.push_back(Version("1.2.2"));
528 versions.push_back(Version("1.2.12"));
529 versions.push_back(Version("1.2.4"));
530 versions.push_back(Version("1.2.3"));
531 versions.push_back(Version("1.2.3b"));
532 versions.push_back(Version("1.2.3a"));
533 versions.push_back(Version("1.2.3-pre9"));
534 versions.push_back(Version("1.2.3-pre12"));
535 versions.push_back(Version("1.2.3-beta"));
536 versions.push_back(Version("1.2.3-rc2"));
537 versions.push_back(Version("1.2.3-alpha"));
538 versions.push_back(Version("1.2.3-rc1"));
539 versions.push_back(Version("1.2.3.1"));
540 versions.push_back(Version("2004-12-24"));
542 std::cout << "-----------------------" << std::endl;
544 std::sort(versions.begin(), versions.end());
546 for (unsigned int i = 0; i < versions.size(); ++i)
547 std::cout << " " << versions[i].str() << std::endl;
549 std::cout << "-----------------------" << std::endl;
550 #endif
552 bool odd = false, noprefix = false, beta = false;
553 std::string url;
554 ++argv; --argc; // progname
555 while (argc > 0) {
556 const std::string opt(*argv);
557 if (opt == "--odd") {
558 odd = true;
559 --argc; // shift
560 ++argv;
561 } else if (opt == "--no-prefix") {
562 noprefix = true;
563 --argc; // shift
564 ++argv;
565 } else if (opt == "--url") {
566 --argc; // shift
567 ++argv;
568 if (argc > 0) {
569 url = *argv++;
570 --argc;
572 } else {
573 break;
577 for (int i = 0; i < argc; ++i)
579 package.Clear();
581 // check if package name or path is given
582 std::string fname = argv[i];
583 struct stat statbuf;
584 if (stat(fname.c_str(), &statbuf)) {
585 fname = "package/*/" + fname + "/" + fname + ".desc";
586 // std::cout << "Checking " << fname << std::endl;
588 Glob x(fname);
589 if (x.begin() != x.end()) {
590 fname = *x.begin();
591 // std::cout << "Found " << fname << std::endl;
595 // parse package ...
596 package.ParsePackage (fname);
597 Check4Updates (package, odd, noprefix, beta, url);