2010-06-11 07:09:25 +00:00
|
|
|
/* print.c - Alpine Package Keeper (APK)
|
2010-03-05 08:13:25 +00:00
|
|
|
*
|
|
|
|
* Copyright (C) 2005-2008 Natanael Copa <n@tanael.org>
|
2011-09-13 08:53:01 +00:00
|
|
|
* Copyright (C) 2008-2011 Timo Teräs <timo.teras@iki.fi>
|
2010-03-05 08:13:25 +00:00
|
|
|
* All rights reserved.
|
|
|
|
*
|
2020-04-22 13:33:41 +00:00
|
|
|
* SPDX-License-Identifier: GPL-2.0-only
|
2010-03-05 08:13:25 +00:00
|
|
|
*/
|
|
|
|
|
2018-10-11 11:39:25 +00:00
|
|
|
#include <assert.h>
|
2010-03-05 08:13:25 +00:00
|
|
|
#include <stdio.h>
|
2021-12-27 12:34:01 +00:00
|
|
|
#include <stdarg.h>
|
2010-03-05 08:13:25 +00:00
|
|
|
#include <stdlib.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <errno.h>
|
2011-07-22 09:08:35 +00:00
|
|
|
#include <sys/ioctl.h>
|
2018-10-11 11:39:25 +00:00
|
|
|
#include <sys/types.h>
|
2010-03-05 08:13:25 +00:00
|
|
|
|
|
|
|
#include "apk_defines.h"
|
|
|
|
#include "apk_print.h"
|
2021-06-21 09:55:32 +00:00
|
|
|
#include "apk_io.h"
|
2010-03-05 08:13:25 +00:00
|
|
|
|
2020-10-05 15:52:51 +00:00
|
|
|
const char *apk_error_str(int error)
|
2010-03-05 08:13:25 +00:00
|
|
|
{
|
2020-10-05 15:52:51 +00:00
|
|
|
if (error < 0)
|
|
|
|
error = -error;
|
|
|
|
switch (error) {
|
2021-06-19 13:09:30 +00:00
|
|
|
case ECONNABORTED: return "network connection aborted";
|
|
|
|
case ECONNREFUSED: return "could not connect to server (check repositories file)";
|
|
|
|
case ENETUNREACH: return "network error (check Internet connection and firewall)";
|
|
|
|
case EAGAIN: return "temporary error (try again later)";
|
|
|
|
case APKE_EOF: return "unexpected end of file";
|
|
|
|
case APKE_DNS: return "DNS error (try again later)";
|
|
|
|
case APKE_URL_FORMAT: return "invalid URL (check your repositories file)";
|
|
|
|
case APKE_CRYPTO_ERROR: return "crypto error";
|
|
|
|
case APKE_CRYPTO_NOT_SUPPORTED: return "cryptographic algorithm not supported";
|
|
|
|
case APKE_CRYPTO_KEY_FORMAT: return "cryptographic key format not recognized";
|
|
|
|
case APKE_SIGNATURE_FAIL: return "signing failure";
|
|
|
|
case APKE_SIGNATURE_UNTRUSTED: return "UNTRUSTED signature";
|
|
|
|
case APKE_SIGNATURE_INVALID: return "BAD signature";
|
2021-07-26 13:25:03 +00:00
|
|
|
case APKE_FORMAT_NOT_SUPPORTED: return "file format not supported (in this applet)";
|
2022-02-01 12:47:27 +00:00
|
|
|
case APKE_PKGVERSION_FORMAT: return "package version is invalid";
|
|
|
|
case APKE_DEPENDENCY_FORMAT: return "dependency format is invalid";
|
2021-07-16 13:31:59 +00:00
|
|
|
case APKE_ADB_COMPRESSION: return "ADB compression not supported";
|
2021-06-19 13:09:30 +00:00
|
|
|
case APKE_ADB_HEADER: return "ADB header error";
|
2021-07-21 17:43:26 +00:00
|
|
|
case APKE_ADB_VERSION: return "incompatible ADB version";
|
2021-06-19 13:09:30 +00:00
|
|
|
case APKE_ADB_SCHEMA: return "ADB schema error";
|
|
|
|
case APKE_ADB_BLOCK: return "ADB block error";
|
|
|
|
case APKE_ADB_SIGNATURE: return "ADB signature block error";
|
|
|
|
case APKE_ADB_NO_FROMSTRING: return "ADB schema error (no fromstring)";
|
|
|
|
case APKE_ADB_LIMIT: return "ADB schema limit reached";
|
|
|
|
case APKE_ADB_PACKAGE_FORMAT: return "ADB package format";
|
|
|
|
case APKE_V2DB_FORMAT: return "v2 database format error";
|
|
|
|
case APKE_V2PKG_FORMAT: return "v2 package format error";
|
|
|
|
case APKE_V2PKG_INTEGRITY: return "v2 package integrity error";
|
|
|
|
case APKE_V2NDX_FORMAT: return "v2 index format error";
|
|
|
|
case APKE_PACKAGE_NOT_FOUND: return "could not find a repo which provides this package (check repositories file and run 'apk update')";
|
|
|
|
case APKE_INDEX_STALE: return "package mentioned in index not found (try 'apk update')";
|
|
|
|
case APKE_FILE_INTEGRITY: return "file integrity error";
|
2021-07-23 11:39:38 +00:00
|
|
|
case APKE_CACHE_NOT_AVAILABLE: return "cache not available";
|
2021-11-05 11:20:19 +00:00
|
|
|
case APKE_UVOL_NOT_AVAILABLE: return "uvol manager not available";
|
|
|
|
case APKE_UVOL_ERROR: return "uvol error";
|
|
|
|
case APKE_UVOL_ROOT: return "uvol not supported with --root";
|
2021-12-13 18:31:13 +00:00
|
|
|
case APKE_REMOTE_IO: return "remote server returned error (try 'apk update')";
|
2020-10-05 15:52:51 +00:00
|
|
|
default:
|
|
|
|
return strerror(error);
|
2011-07-22 09:08:35 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-06-02 19:35:58 +00:00
|
|
|
static const char *size_units[] = {"B", "KiB", "MiB", "GiB", "TiB"};
|
|
|
|
|
|
|
|
int apk_get_human_size_unit(apk_blob_t b)
|
|
|
|
{
|
|
|
|
for (int i = 0, s = 1; i < ARRAY_SIZE(size_units); i++, s *= 1024)
|
|
|
|
if (apk_blob_compare(b, APK_BLOB_STR(size_units[i])) == 0)
|
|
|
|
return s;
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2018-10-11 11:39:25 +00:00
|
|
|
const char *apk_get_human_size(off_t size, off_t *dest)
|
|
|
|
{
|
|
|
|
size_t i;
|
|
|
|
off_t s;
|
|
|
|
|
|
|
|
assert(size >= 0);
|
|
|
|
|
2020-10-05 15:52:51 +00:00
|
|
|
for (i = 0, s = size; s >= 10000 && i < ARRAY_SIZE(size_units); i++)
|
2018-10-11 11:39:25 +00:00
|
|
|
s /= 1024;
|
|
|
|
|
|
|
|
if (dest) *dest = s;
|
2020-10-05 15:52:51 +00:00
|
|
|
return size_units[min(i, ARRAY_SIZE(size_units) - 1)];
|
|
|
|
}
|
|
|
|
|
|
|
|
void apk_url_parse(struct apk_url_print *urlp, const char *url)
|
|
|
|
{
|
|
|
|
const char *authority, *path_or_host, *pw;
|
|
|
|
|
|
|
|
*urlp = (struct apk_url_print) {
|
|
|
|
.url = "",
|
|
|
|
.pwmask = "",
|
|
|
|
.url_or_host = url,
|
|
|
|
};
|
|
|
|
|
|
|
|
if (!(authority = strstr(url, "://"))) return;
|
|
|
|
authority += 3;
|
|
|
|
path_or_host = strpbrk(authority, "/@");
|
|
|
|
if (!path_or_host || *path_or_host == '/') return;
|
|
|
|
pw = strpbrk(authority, "@:");
|
|
|
|
if (!pw || *pw == '@') return;
|
|
|
|
*urlp = (struct apk_url_print) {
|
|
|
|
.url = url,
|
|
|
|
.pwmask = "*",
|
|
|
|
.url_or_host = path_or_host,
|
|
|
|
.len_before_pw = pw - url + 1,
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
void apk_out_reset(struct apk_out *out)
|
|
|
|
{
|
|
|
|
out->width = 0;
|
|
|
|
out->last_change++;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int apk_out_get_width(struct apk_out *out)
|
|
|
|
{
|
|
|
|
struct winsize w;
|
|
|
|
|
|
|
|
if (out->width == 0) {
|
|
|
|
out->width = 50;
|
|
|
|
if (ioctl(fileno(out->out), TIOCGWINSZ, &w) == 0 &&
|
|
|
|
w.ws_col > 25)
|
|
|
|
out->width = w.ws_col;
|
|
|
|
}
|
|
|
|
|
|
|
|
return out->width;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void log_internal(FILE *dest, const char *prefix, const char *format, va_list va)
|
|
|
|
{
|
|
|
|
if (dest != stdout) fflush(stdout);
|
2021-04-02 10:22:25 +00:00
|
|
|
if (prefix != NULL && prefix != APK_OUT_LOG_ONLY) fprintf(dest, "%s", prefix);
|
2020-10-05 15:52:51 +00:00
|
|
|
vfprintf(dest, format, va);
|
|
|
|
fprintf(dest, "\n");
|
|
|
|
fflush(dest);
|
|
|
|
}
|
|
|
|
|
|
|
|
void apk_out_fmt(struct apk_out *out, const char *prefix, const char *format, ...)
|
|
|
|
{
|
|
|
|
va_list va;
|
2021-02-14 15:01:02 +00:00
|
|
|
if (prefix != APK_OUT_LOG_ONLY) {
|
|
|
|
va_start(va, format);
|
|
|
|
log_internal(prefix ? out->err : out->out, prefix, format, va);
|
|
|
|
out->last_change++;
|
|
|
|
va_end(va);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (out->log) {
|
|
|
|
va_start(va, format);
|
|
|
|
log_internal(out->log, prefix, format, va);
|
|
|
|
va_end(va);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void apk_out_log_argv(struct apk_out *out, char **argv)
|
|
|
|
{
|
|
|
|
char when[32];
|
|
|
|
struct tm tm;
|
|
|
|
time_t now = time(NULL);
|
|
|
|
|
|
|
|
if (!out->log) return;
|
|
|
|
fprintf(out->log, "\nRunning `");
|
|
|
|
for (int i = 0; argv[i]; ++i) {
|
|
|
|
fprintf(out->log, "%s%s", argv[i], argv[i+1] ? " " : "");
|
|
|
|
}
|
|
|
|
|
|
|
|
gmtime_r(&now, &tm);
|
|
|
|
strftime(when, sizeof(when), "%Y-%m-%d %H:%M:%S", &tm);
|
|
|
|
fprintf(out->log, "` at %s\n", when);
|
2018-10-11 11:39:25 +00:00
|
|
|
}
|
|
|
|
|
2020-10-05 13:47:24 +00:00
|
|
|
void apk_print_progress(struct apk_progress *p, size_t done, size_t total)
|
2013-06-17 13:47:49 +00:00
|
|
|
{
|
2013-06-19 08:47:09 +00:00
|
|
|
int bar_width;
|
|
|
|
int bar = 0;
|
|
|
|
char buf[64]; /* enough for petabytes... */
|
|
|
|
int i, percent = 0;
|
2020-10-05 15:52:51 +00:00
|
|
|
FILE *out;
|
2013-06-17 13:47:49 +00:00
|
|
|
|
2020-10-05 15:52:51 +00:00
|
|
|
if (p->last_done == done && (!p->out || p->last_out_change == p->out->last_change)) return;
|
2020-10-05 13:47:24 +00:00
|
|
|
if (p->fd != 0) {
|
2013-06-19 08:47:09 +00:00
|
|
|
i = snprintf(buf, sizeof(buf), "%zu/%zu\n", done, total);
|
2021-06-21 09:55:32 +00:00
|
|
|
if (apk_write_fully(p->fd, buf, i) != i) {
|
|
|
|
close(p->fd);
|
|
|
|
p->fd = 0;
|
|
|
|
}
|
2013-06-19 08:47:09 +00:00
|
|
|
}
|
2020-10-05 13:47:24 +00:00
|
|
|
p->last_done = done;
|
2013-06-17 13:47:49 +00:00
|
|
|
|
2020-10-05 15:52:51 +00:00
|
|
|
if (!p->out) return;
|
|
|
|
out = p->out->out;
|
2020-10-05 13:47:24 +00:00
|
|
|
if (!out) return;
|
2013-06-17 13:47:49 +00:00
|
|
|
|
2020-10-05 15:52:51 +00:00
|
|
|
bar_width = apk_out_get_width(p->out) - 6;
|
2013-06-19 08:47:09 +00:00
|
|
|
if (total > 0) {
|
|
|
|
bar = muldiv(bar_width, done, total);
|
|
|
|
percent = muldiv(100, done, total);
|
2013-06-17 13:47:49 +00:00
|
|
|
}
|
|
|
|
|
2020-10-05 15:52:51 +00:00
|
|
|
if (bar == p->last_bar && percent == p->last_percent && p->last_out_change == p->out->last_change)
|
2013-06-19 08:47:09 +00:00
|
|
|
return;
|
|
|
|
|
2020-10-05 13:47:24 +00:00
|
|
|
p->last_bar = bar;
|
|
|
|
p->last_percent = percent;
|
2020-10-05 15:52:51 +00:00
|
|
|
p->last_out_change = p->out->last_change;
|
2013-06-19 08:47:09 +00:00
|
|
|
|
2020-10-05 13:47:24 +00:00
|
|
|
fprintf(out, "\e7%3i%% ", percent);
|
2017-06-01 05:25:56 +00:00
|
|
|
|
2013-06-19 08:47:09 +00:00
|
|
|
for (i = 0; i < bar; i++)
|
2020-10-05 13:47:24 +00:00
|
|
|
fputs(p->progress_char, out);
|
2013-06-19 08:47:09 +00:00
|
|
|
for (; i < bar_width; i++)
|
2020-10-05 13:47:24 +00:00
|
|
|
fputc(' ', out);
|
2017-06-01 05:25:56 +00:00
|
|
|
|
2020-10-05 13:47:24 +00:00
|
|
|
fflush(out);
|
|
|
|
fputs("\e8\e[0K", out);
|
2013-06-17 13:47:49 +00:00
|
|
|
}
|
|
|
|
|
2021-12-27 12:34:01 +00:00
|
|
|
void apk_print_indented_init(struct apk_indent *i, struct apk_out *out, int err)
|
|
|
|
{
|
|
|
|
*i = (struct apk_indent) {
|
|
|
|
.f = err ? out->err : out->out,
|
|
|
|
.width = apk_out_get_width(out),
|
|
|
|
};
|
|
|
|
out->last_change++;
|
|
|
|
}
|
|
|
|
|
|
|
|
void apk_print_indented_line(struct apk_indent *i, const char *fmt, ...)
|
|
|
|
{
|
|
|
|
va_list va;
|
|
|
|
|
|
|
|
va_start(va, fmt);
|
|
|
|
vfprintf(i->f, fmt, va);
|
|
|
|
va_end(va);
|
|
|
|
i->x = i->indent = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void apk_print_indented_group(struct apk_indent *i, int indent, const char *fmt, ...)
|
|
|
|
{
|
|
|
|
va_list va;
|
|
|
|
|
|
|
|
va_start(va, fmt);
|
|
|
|
i->x = vfprintf(i->f, fmt, va);
|
|
|
|
i->indent = indent ?: (i->x + 1);
|
|
|
|
if (fmt[strlen(fmt)-1] == '\n') i->x = 0;
|
|
|
|
va_end(va);
|
|
|
|
}
|
|
|
|
|
|
|
|
void apk_print_indented_end(struct apk_indent *i)
|
|
|
|
{
|
|
|
|
if (i->x) {
|
|
|
|
fprintf(i->f, "\n");
|
|
|
|
i->x = i->indent = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-07-22 09:08:35 +00:00
|
|
|
int apk_print_indented(struct apk_indent *i, apk_blob_t blob)
|
|
|
|
{
|
2018-11-02 13:33:49 +00:00
|
|
|
if (i->x <= i->indent)
|
2021-12-27 12:34:01 +00:00
|
|
|
i->x += fprintf(i->f, "%*s" BLOB_FMT, i->indent - i->x, "", BLOB_PRINTF(blob));
|
|
|
|
else if (i->x + blob.len + 1 >= i->width)
|
|
|
|
i->x = fprintf(i->f, "\n%*s" BLOB_FMT, i->indent, "", BLOB_PRINTF(blob)) - 1;
|
2011-09-09 17:06:10 +00:00
|
|
|
else
|
2021-12-27 12:34:01 +00:00
|
|
|
i->x += fprintf(i->f, " " BLOB_FMT, BLOB_PRINTF(blob));
|
2010-03-05 08:13:25 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void apk_print_indented_words(struct apk_indent *i, const char *text)
|
|
|
|
{
|
|
|
|
apk_blob_for_each_segment(APK_BLOB_STR(text), " ",
|
|
|
|
(apk_blob_cb) apk_print_indented, i);
|
|
|
|
}
|
|
|
|
|
2013-06-13 15:20:39 +00:00
|
|
|
void apk_print_indented_fmt(struct apk_indent *i, const char *fmt, ...)
|
|
|
|
{
|
|
|
|
char tmp[256];
|
|
|
|
size_t n;
|
|
|
|
va_list va;
|
|
|
|
|
|
|
|
va_start(va, fmt);
|
|
|
|
n = vsnprintf(tmp, sizeof(tmp), fmt, va);
|
|
|
|
apk_print_indented(i, APK_BLOB_PTR_LEN(tmp, n));
|
|
|
|
va_end(va);
|
|
|
|
}
|