src/libpakfire/pakfire.c \
src/libpakfire/parser.c \
src/libpakfire/problem.c \
+ src/libpakfire/progress.c \
src/libpakfire/progressbar.c \
src/libpakfire/pwd.c \
src/libpakfire/repo.c \
src/libpakfire/include/pakfire/parser.h \
src/libpakfire/include/pakfire/private.h \
src/libpakfire/include/pakfire/problem.h \
+ src/libpakfire/include/pakfire/progress.h \
src/libpakfire/include/pakfire/progressbar.h \
src/libpakfire/include/pakfire/pwd.h \
src/libpakfire/include/pakfire/repo.h \
src/cli/lib/info.h \
src/cli/lib/install.h \
src/cli/lib/install.c \
+ src/cli/lib/progressbar.c \
+ src/cli/lib/progressbar.h \
src/cli/lib/provides.h \
src/cli/lib/provides.c \
src/cli/lib/remove.c \
--- /dev/null
+/*#############################################################################
+# #
+# Pakfire - The IPFire package management system #
+# Copyright (C) 2023 Pakfire development team #
+# #
+# This program is free software: you can redistribute it and/or modify #
+# it under the terms of the GNU General Public License as published by #
+# the Free Software Foundation, either version 3 of the License, or #
+# (at your option) any later version. #
+# #
+# This program is distributed in the hope that it will be useful, #
+# but WITHOUT ANY WARRANTY; without even the implied warranty of #
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
+# GNU General Public License for more details. #
+# #
+# You should have received a copy of the GNU General Public License #
+# along with this program. If not, see <http://www.gnu.org/licenses/>. #
+# #
+#############################################################################*/
+
+#include <errno.h>
+#include <pthread.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <pakfire/i18n.h>
+#include <pakfire/pakfire.h>
+#include <pakfire/progress.h>
+
+#include "progressbar.h"
+#include "terminal.h"
+
+#define DRAWS_PER_SECOND 20
+
+static const struct timespec TIMER = {
+ .tv_sec = 0,
+ .tv_nsec = 1000000000 / DRAWS_PER_SECOND,
+};
+
+struct cli_progressbar;
+
+struct cli_progressbar_widget {
+ STAILQ_ENTRY(cli_progressbar_widget) nodes;
+
+ int expandable;
+ void* data;
+
+ const char* (*print)(struct cli_progressbar* p,
+ struct cli_progressbar_widget* widget, unsigned int width, void* data);
+ void (*free)(void* data);
+
+ char buffer[1024];
+};
+
+struct cli_progressbar {
+ struct pakfire_progress* progress;
+
+ // Output
+ FILE* f;
+
+ // Reference to the thread that is drawing the progress bar
+ pthread_t renderer;
+ volatile unsigned int is_running;
+
+ // Widgets
+ STAILQ_HEAD(widgets, cli_progressbar_widget) widgets;
+ unsigned int num_widgets;
+};
+
+static int cli_progressbar_create(struct cli_progressbar** progressbar,
+ struct pakfire_progress* progress, FILE* f) {
+ struct cli_progressbar* p = NULL;
+
+ // Allocate some memory
+ p = calloc(1, sizeof(*p));
+ if (!p)
+ return -errno;
+
+ // Keep a reference to progress
+ p->progress = progress;
+
+ // Default writing to stdout
+ if (!f)
+ f = stdout;
+
+ // Where to write to?
+ p->f = f;
+
+ // Setup widgets
+ STAILQ_INIT(&p->widgets);
+
+ // Done
+ *progressbar = p;
+
+ return 0;
+}
+
+static void cli_progressbar_widget_free(struct cli_progressbar_widget* widget) {
+ // Call own free method
+ if (widget->free && widget->data)
+ widget->free(widget->data);
+
+ free(widget);
+}
+
+static void cli_progressbar_free_widgets(struct cli_progressbar* p) {
+ struct cli_progressbar_widget* widget = NULL;
+
+ // Free widgets
+ while (!STAILQ_EMPTY(&p->widgets)) {
+ widget = STAILQ_FIRST(&p->widgets);
+ STAILQ_REMOVE_HEAD(&p->widgets, nodes);
+
+ cli_progressbar_widget_free(widget);
+ }
+}
+
+static void cli_progressbar_free(struct cli_progressbar* p) {
+ cli_progressbar_free_widgets(p);
+ free(p);
+}
+
+static void __cli_progressbar_free(struct pakfire* pakfire,
+ struct pakfire_progress* progress, void* data) {
+ struct cli_progressbar* progressbar = data;
+
+ cli_progressbar_free(progressbar);
+}
+
+static int cli_progressbar_draw(struct cli_progressbar* p) {
+ struct cli_progressbar_widget* widget = NULL;
+ int rows;
+ int cols;
+ int r;
+
+ // Fetch terminal size
+ r = cli_term_get_dimensions(&rows, &cols);
+ if (r)
+ return r;
+
+ unsigned int cols_left = cols - p->num_widgets - 2;
+ unsigned int i = 0;
+
+ // Create an array with the result of all print functions
+ const char* elements[p->num_widgets];
+
+ // Reset all elements
+ for (i = 0; i < p->num_widgets; i++)
+ elements[i] = NULL;
+
+ // Reset i
+ i = 0;
+
+ // Process all non-expandable widgets in the first pass
+ STAILQ_FOREACH(widget, &p->widgets, nodes) {
+ const char* element = NULL;
+
+ if (!widget->expandable) {
+ element = widget->print(p, widget, 0, widget->data);
+ if (element)
+ cols_left -= strlen(element);
+ }
+
+ elements[i++] = element;
+ }
+
+ // How many expandable widgets are left?
+ int num_expandables = p->num_widgets - i;
+
+ // How much space do we allocate to each of them?
+ int width = cols_left - num_expandables;
+
+ i = 0;
+
+ // Process all expandable widgets
+ STAILQ_FOREACH(widget, &p->widgets, nodes) {
+ const char* element = elements[i];
+
+ if (widget->expandable)
+ element = widget->print(p, widget, width, widget->data);
+
+ elements[i++] = element;
+ }
+
+ // Reset the line
+ fputs("\r", p->f);
+
+ // Print all elements
+ for (i = 0; i < p->num_widgets; i++) {
+ // Skip anything that returned nothing
+ if (!elements[i])
+ continue;
+
+ fputs(" ", p->f);
+ fputs(elements[i], p->f);
+ }
+
+ // Flush everything
+ fflush(p->f);
+
+ return 0;
+}
+
+static void* cli_progressbar_renderer(void* data) {
+ struct cli_progressbar* progressbar = data;
+ int r;
+
+ do {
+ // Redraw the progress bar
+ r = cli_progressbar_draw(progressbar);
+ if (r)
+ goto ERROR;
+
+ // Sleep for a short moment
+ nanosleep(&TIMER, NULL);
+
+ // Keep doing this for as long as we are running
+ } while (progressbar->is_running);
+
+ // Perform a final draw
+ r = cli_progressbar_draw(progressbar);
+ if (r)
+ goto ERROR;
+
+ // Finish line
+ r = fputs("\n", progressbar->f);
+ if (r <= 0 || r == EOF) {
+ r = 1;
+ goto ERROR;
+ }
+
+ // Success
+ r = 0;
+
+ERROR:
+ return (void *)(intptr_t)r;
+}
+
+static int cli_progressbar_start(struct pakfire* pakfire, struct pakfire_progress* progress,
+ void* data, unsigned long int max_value) {
+ struct cli_progressbar* progressbar = data;
+
+ // Set as running
+ progressbar->is_running = 1;
+
+ // Launch the renderer thread
+ return pthread_create(&progressbar->renderer, NULL, cli_progressbar_renderer, progressbar);
+}
+
+static int cli_progressbar_finish(struct pakfire* pakfire, struct pakfire_progress* progress,
+ void* data) {
+ struct cli_progressbar* progressbar = data;
+ void* retval = NULL;
+ int r;
+
+ // We are no longer running
+ progressbar->is_running = 0;
+
+ // Wait until the render thread is done
+ if (progressbar->renderer) {
+ r = pthread_join(progressbar->renderer, &retval);
+ if (r)
+ return r;
+ }
+
+ return (intptr_t)retval;
+}
+
+static int cli_progressbar_add_widget(struct cli_progressbar* p,
+ const char* (*print)(struct cli_progressbar* p,
+ struct cli_progressbar_widget* widget, unsigned int width, void* data),
+ void (*free)(void* data), int expandable, void* data) {
+ // Allocate the widget
+ struct cli_progressbar_widget* widget = calloc(1, sizeof(*widget));
+ if (!widget)
+ return -ENOMEM;
+
+ // Assign everything
+ widget->print = print;
+ widget->free = free;
+ widget->expandable = expandable;
+ widget->data = data;
+
+ // Append it to the list
+ STAILQ_INSERT_TAIL(&p->widgets, widget, nodes);
+ p->num_widgets++;
+
+ return 0;
+}
+
+static const char* cli_progressbar_string(struct cli_progressbar* p,
+ struct cli_progressbar_widget* widget, unsigned int width, void* data) {
+ return (const char*)data;
+}
+
+static int cli_progressbar_add_string(struct cli_progressbar* p, const char* format, ...) {
+ char* s = NULL;
+ va_list args;
+
+ va_start(args, format);
+ int r = vasprintf(&s, format, args);
+ va_end(args);
+
+ if (r < 0)
+ return r;
+
+ return cli_progressbar_add_widget(p, cli_progressbar_string, free, 0, s);
+}
+
+static const char* cli_progressbar_counter(struct cli_progressbar* p,
+ struct cli_progressbar_widget* widget, unsigned int width, void* data) {
+ int r;
+
+ // Fetch progress
+ unsigned long int value = pakfire_progress_get_value(p->progress);
+ unsigned long int max_value = pakfire_progress_get_value(p->progress);
+
+ // Format the result
+ r = snprintf(widget->buffer, sizeof(widget->buffer), "%lu/%lu", value, max_value);
+ if (r < 0)
+ return NULL;
+
+ return widget->buffer;
+}
+
+static int cli_progressbar_add_counter(struct cli_progressbar* p) {
+ return cli_progressbar_add_widget(p, cli_progressbar_counter, NULL, 0, NULL);
+}
+
+static const char* cli_progressbar_percentage(struct cli_progressbar* p,
+ struct cli_progressbar_widget* widget, unsigned int width, void* data) {
+ int r;
+
+ // Fetch percentage
+ double percentage = pakfire_progress_get_percentage(p->progress);
+
+ // Format to string
+ r = snprintf(widget->buffer, sizeof(widget->buffer), "%3.0f%%", percentage);
+ if (r < 0)
+ return NULL;
+
+ return widget->buffer;
+}
+
+static int cli_progressbar_add_percentage(struct cli_progressbar* p) {
+ return cli_progressbar_add_widget(p, cli_progressbar_percentage, NULL, 0, NULL);
+}
+
+static const char* cli_progressbar_bar(struct cli_progressbar* p,
+ struct cli_progressbar_widget* widget, unsigned int width, void* data) {
+ if (width >= sizeof(widget->buffer) || width < 2)
+ return NULL;
+
+ // Remove the bar when we are finished so that the terminal is not so cluttered
+ if (!p->is_running) {
+ for (unsigned int i = 0; i < width; i++)
+ widget->buffer[i] = ' ';
+ widget->buffer[width] = '\0';
+
+ return widget->buffer;
+ }
+
+ unsigned int fill = pakfire_progress_get_percentage(p->progress) * (width - 2) / 100;
+
+ // Write brackets
+ widget->buffer[0] = '[';
+ widget->buffer[width-1] = ']';
+
+ // Write bar
+ for (unsigned int i = 1; i < width - 1; i++) {
+ if (i <= fill)
+ widget->buffer[i] = '#';
+ else
+ widget->buffer[i] = '-';
+ }
+
+ // Terminat the string
+ widget->buffer[width] = '\0';
+
+ return widget->buffer;
+}
+
+static int cli_progressbar_add_bar(struct cli_progressbar* p) {
+ return cli_progressbar_add_widget(p, cli_progressbar_bar, NULL, 1, NULL);
+}
+
+static const char* cli_progressbar_elapsed_time(struct cli_progressbar* p,
+ struct cli_progressbar_widget* widget, unsigned int width, void* data) {
+ int r;
+
+ // Fetch the elapsed time
+ time_t t = pakfire_progress_get_elapsed_time(p->progress);
+ if (t < 0)
+ return NULL;
+
+ // Format the time
+ r = snprintf(widget->buffer, sizeof(widget->buffer), "%02lu:%02lu", t / 60, t % 60);
+ if (r)
+ return NULL;
+
+ return widget->buffer;
+}
+
+static int cli_progressbar_add_elapsed_time(struct cli_progressbar* p) {
+ return cli_progressbar_add_widget(p, cli_progressbar_elapsed_time, NULL, 0, NULL);
+}
+
+static const char* cli_progressbar_eta(struct cli_progressbar* p,
+ struct cli_progressbar_widget* widget, unsigned int width, void* data) {
+ int r;
+
+ // Show total time when finished
+ if (!p->is_running)
+ return cli_progressbar_elapsed_time(p, widget, width, data);
+
+ // Fetch the ETA
+ time_t t = pakfire_progress_get_eta(p->progress);
+
+ // Print a placeholder when we have no ETA (yet)
+ if (t <= 0) {
+ r = snprintf(widget->buffer, sizeof(widget->buffer), "%-5s: --:--:--", _("ETA"));
+ if (r < 0)
+ return NULL;
+
+ // Otherwise show the ETA
+ } else {
+ r = snprintf(widget->buffer, sizeof(widget->buffer), "%-5s: %02lu:%02lu",
+ _("ETA"), t / 60, t % 60);
+ if (r < 0)
+ return NULL;
+ }
+
+ return widget->buffer;
+}
+
+static int cli_progressbar_add_eta(struct cli_progressbar* p) {
+ return cli_progressbar_add_widget(p, cli_progressbar_eta, NULL, 0, NULL);
+}
+
+#define cli_progressbar_format_speed(s, speed) \
+ __cli_progressbar_format_speed(s, sizeof(s), speed)
+
+static int __cli_progressbar_format_speed(char* s, const size_t length, double value) {
+ const char* units[] = {
+ "%4.0fB/s",
+ "%4.0fkB/s",
+ "%4.1fMB/s",
+ "%4.1fGB/s",
+ "%4.1fTB/s",
+ NULL
+ };
+ int r;
+
+ for (const char** unit = units; *unit; unit++) {
+ if (value >= 1024.0) {
+ value /= 1024.0;
+ continue;
+ }
+
+ // Format the string
+ r = snprintf(s, length, *unit, value);
+ if (r < 0)
+ return r;
+
+ return 0;
+ }
+
+ return 1;
+}
+
+#define cli_progressbar_format_size(s, size) \
+ __cli_progressbar_format_size(s, sizeof(s), size)
+
+static int __cli_progressbar_format_size(char* s, const size_t length, double value) {
+ const char* units[] = {
+ "%.0f ",
+ "%.0fk",
+ "%.1fM",
+ "%.1fG",
+ "%.1fT",
+ NULL
+ };
+ int r;
+
+ for (const char** unit = units; *unit; unit++) {
+ if (value >= 1024.0) {
+ value /= 1024.0;
+ continue;
+ }
+
+ // Format the string
+ r = snprintf(s, length, *unit, value);
+ if (r < 0)
+ return r;
+
+ return 0;
+ }
+
+ return 1;
+}
+
+static const char* cli_progressbar_bytes_transferred(struct cli_progressbar* p,
+ struct cli_progressbar_widget* widget, unsigned int width, void* data) {
+ char buffer[16];
+ int r;
+
+ // Fetch value
+ unsigned long int value = pakfire_progress_get_value(p->progress);
+
+ // Format the size
+ r = cli_progressbar_format_size(buffer, value);
+ if (r < 0)
+ return NULL;
+
+ // Add padding so that the string is always at least five characters long
+ r = snprintf(widget->buffer, sizeof(widget->buffer), "%-5s", buffer);
+ if (r < 0)
+ return NULL;
+
+ return widget->buffer;
+}
+
+static int cli_progressbar_add_bytes_transferred(struct cli_progressbar* p) {
+ return cli_progressbar_add_widget(p, cli_progressbar_bytes_transferred, NULL, 0, NULL);
+}
+
+static const char* cli_progressbar_transfer_speed(struct cli_progressbar* p,
+ struct cli_progressbar_widget* widget, unsigned int width, void* data) {
+ double speed = pakfire_progress_get_transfer_speed(p->progress);
+ if (speed < 0)
+ return NULL;
+
+ // Format the speed
+ int r = cli_progressbar_format_speed(widget->buffer, speed);
+ if (r < 0)
+ return NULL;
+
+ return widget->buffer;
+}
+
+static int cli_progressbar_add_transfer_speed(struct cli_progressbar* p) {
+ return cli_progressbar_add_widget(p, cli_progressbar_transfer_speed, NULL, 0, NULL);
+}
+
+int cli_setup_progressbar(struct pakfire* pakfire, struct pakfire_progress* p, void* data) {
+ struct cli_progressbar* progressbar = NULL;
+ const char* title = NULL;
+ int r;
+
+ // Allocate a new progressbar
+ r = cli_progressbar_create(&progressbar, p, NULL);
+ if (r)
+ return r;
+
+ // Set callback data
+ pakfire_progress_set_callback_data(p, progressbar);
+
+ // Set start callback
+ pakfire_progress_set_start_callback(p, cli_progressbar_start);
+
+ // Set finish callback
+ pakfire_progress_set_finish_callback(p, cli_progressbar_finish);
+
+ // Set free callback
+ pakfire_progress_set_free_callback(p, __cli_progressbar_free);
+
+ // Add the title
+ title = pakfire_progress_get_title(p);
+ if (title) {
+ r = cli_progressbar_add_string(progressbar, "%s", title);
+ if (r)
+ goto ERROR;
+ }
+
+ // Show the bar
+ r = cli_progressbar_add_bar(progressbar);
+ if (r)
+ goto ERROR;
+
+ // Show bytes transferred
+ if (pakfire_progress_has_flag(p, PAKFIRE_PROGRESS_SHOW_BYTES_TRANSFERRED)) {
+ r = cli_progressbar_add_bytes_transferred(progressbar);
+ if (r)
+ goto ERROR;
+ }
+
+ // Show transfer speed
+ if (pakfire_progress_has_flag(p, PAKFIRE_PROGRESS_SHOW_TRANSFER_SPEED)) {
+ r = cli_progressbar_add_transfer_speed(progressbar);
+ if (r)
+ goto ERROR;
+ }
+
+ // Show elapsed time
+ if (pakfire_progress_has_flag(p, PAKFIRE_PROGRESS_SHOW_ELAPSED_TIME)) {
+ r = cli_progressbar_add_elapsed_time(progressbar);
+ if (r)
+ goto ERROR;
+ }
+
+ // Show ETA
+ if (pakfire_progress_has_flag(p, PAKFIRE_PROGRESS_SHOW_ETA)) {
+ r = cli_progressbar_add_eta(progressbar);
+ if (r)
+ goto ERROR;
+ }
+
+ // Show counter
+ if (pakfire_progress_has_flag(p, PAKFIRE_PROGRESS_SHOW_COUNTER)) {
+ r = cli_progressbar_add_counter(progressbar);
+ if (r)
+ goto ERROR;
+ }
+
+ // Show percentage
+ if (pakfire_progress_has_flag(p, PAKFIRE_PROGRESS_SHOW_PERCENTAGE)) {
+ r = cli_progressbar_add_percentage(progressbar);
+ if (r)
+ goto ERROR;
+ }
+
+ return 0;
+
+ERROR:
+ if (progressbar)
+ cli_progressbar_free(progressbar);
+
+ return r;
+}
--- /dev/null
+/*#############################################################################
+# #
+# Pakfire - The IPFire package management system #
+# Copyright (C) 2023 Pakfire development team #
+# #
+# This program is free software: you can redistribute it and/or modify #
+# it under the terms of the GNU General Public License as published by #
+# the Free Software Foundation, either version 3 of the License, or #
+# (at your option) any later version. #
+# #
+# This program is distributed in the hope that it will be useful, #
+# but WITHOUT ANY WARRANTY; without even the implied warranty of #
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
+# GNU General Public License for more details. #
+# #
+# You should have received a copy of the GNU General Public License #
+# along with this program. If not, see <http://www.gnu.org/licenses/>. #
+# #
+#############################################################################*/
+
+#ifndef PAKFIRE_CLI_PROGRESSBAR_H
+#define PAKFIRE_CLI_PROGRESSBAR_H
+
+#include <pakfire/pakfire.h>
+#include <pakfire/progress.h>
+
+int cli_setup_progressbar(struct pakfire* pakfire, struct pakfire_progress* p, void* data);
+
+#endif /* PAKFIRE_CLI_PROGRESSBAR_H */
# #
#############################################################################*/
+#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
+#include <sys/ioctl.h>
#include <unistd.h>
#include <pakfire/i18n.h>
#include "terminal.h"
+// Store terminal dimensions globally
+static struct cli_term_dimensions {
+ int rows;
+ int cols;
+ int __signal_registered;
+} dimensions;
+
+static void cli_term_update_dimensions(int signum) {
+ struct winsize w;
+ const int fd = STDOUT_FILENO;
+
+ // Set defaults
+ dimensions.rows = 80;
+ dimensions.cols = 20;
+
+ // Check if output file is a TTY
+ int r = isatty(fd);
+ if (r != 1)
+ return;
+
+ // Fetch the window size
+ r = ioctl(fd, TIOCGWINSZ, &w);
+ if (r)
+ return;
+
+ // Save result
+ dimensions.rows = w.ws_row;
+ dimensions.cols = w.ws_col;
+}
+
+int cli_term_get_dimensions(int* rows, int* cols) {
+ // Update data if not done, yet
+ if (!dimensions.rows || !dimensions.cols)
+ cli_term_update_dimensions(SIGWINCH);
+
+ // Register signal handler to update terminal size
+ if (!dimensions.__signal_registered) {
+ signal(SIGWINCH, cli_term_update_dimensions);
+
+ // Store that we have registered the signal
+ dimensions.__signal_registered = 1;
+ }
+
+ // Copy the dimensions
+ *rows = dimensions.rows;
+ *cols = dimensions.cols;
+
+ return 0;
+}
+
int cli_term_is_interactive() {
return isatty(STDIN_FILENO) && isatty(STDOUT_FILENO) && isatty(STDERR_FILENO);
}
#include <pakfire/pakfire.h>
#include <pakfire/transaction.h>
+int cli_term_get_dimensions(int* rows, int* cols);
+
int cli_term_is_interactive(void);
int cli_term_confirm(struct pakfire* pakfire,
#include "lib/dist.h"
#include "lib/image.h"
#include "lib/info.h"
+#include "lib/progressbar.h"
#include "lib/provides.h"
#include "lib/repo.h"
#include "lib/repolist.h"
if (r)
goto ERROR;
+ // Setup progress callback
+ pakfire_set_setup_progress_callback(p, cli_setup_progressbar, NULL);
+
// Enable repositories
for (unsigned int i = 0; i < config->num_enable_repos; i++)
cli_set_repo_enabled(p, config->enable_repos[i], 1);
// The writer
struct archive* writer;
- // The progressbar
- struct pakfire_progressbar* progressbar;
+ // The progress indicator
+ struct pakfire_progress* progress;
};
-static int pakfire_extract_progressbar_create(struct pakfire_progressbar** progressbar,
- const char* message, int flags) {
- int r;
-
- // Create the progressbar
- r = pakfire_progressbar_create(progressbar, NULL);
- if (r)
- return r;
-
- // Add message
- if (message) {
- r = pakfire_progressbar_add_string(*progressbar, "%s", message);
- if (r)
- return r;
- }
-
- // Add bar
- r = pakfire_progressbar_add_bar(*progressbar);
- if (r)
- return r;
-
- // Add throughput
- if (flags & PAKFIRE_EXTRACT_SHOW_THROUGHPUT) {
- r = pakfire_progressbar_add_transfer_speed(*progressbar);
- if (r)
- return r;
- }
-
- // Add percentage
- r = pakfire_progressbar_add_percentage(*progressbar);
- if (r)
- return r;
-
- // Success
- return 0;
-}
-
static void pakfire_extract_progress(void* p) {
struct pakfire_extract* data = (struct pakfire_extract*)p;
// Fetch how many bytes have been read
const size_t position = archive_filter_bytes(data->archive, -1);
- // Update progressbar
- pakfire_progressbar_update(data->progressbar, position);
+ // Update progress
+ pakfire_progress_update(data->progress, position);
}
static int __pakfire_extract(struct pakfire* pakfire, struct archive* a,
size_t size, struct pakfire_filelist* filelist,
const char* prefix, const char* message,
pakfire_walk_filter_callback filter_callback, int flags) {
+ int progress_flags = PAKFIRE_PROGRESS_SHOW_PERCENTAGE;
int r = 1;
// Use / if no prefix is set
// Is this a dry run?
const int dry_run = flags & PAKFIRE_EXTRACT_DRY_RUN;
- // Should we show a progress bar?
- const int no_progress = flags & PAKFIRE_EXTRACT_NO_PROGRESS;
-
// Allocate writer
if (!dry_run) {
data.writer = pakfire_make_archive_disk_writer(pakfire, 1);
}
}
- // Create the progressbar
- if (!no_progress) {
- r = pakfire_extract_progressbar_create(&data.progressbar, message, flags);
- if (r)
- goto ERROR;
+ // Should we show any progress?
+ if (flags & PAKFIRE_EXTRACT_NO_PROGRESS)
+ progress_flags |= PAKFIRE_PROGRESS_NO_PROGRESS;
- // Register progress callback
- archive_read_extract_set_progress_callback(data.archive,
- pakfire_extract_progress, &data);
+ // Show throughput?
+ if (flags & PAKFIRE_EXTRACT_SHOW_THROUGHPUT)
+ progress_flags |= PAKFIRE_PROGRESS_SHOW_TRANSFER_SPEED;
- // Start progressbar
- pakfire_progressbar_start(data.progressbar, size);
- }
+ // Create the progressbar indicator
+ r = pakfire_progress_create(&data.progress, pakfire, message, progress_flags);
+ if (r)
+ goto ERROR;
+
+ // Register progress callback
+ archive_read_extract_set_progress_callback(data.archive,
+ pakfire_extract_progress, &data);
+
+ // Start progress
+ r = pakfire_progress_start(data.progress, size);
+ if (r)
+ goto ERROR;
// Walk through the entire archive
r = pakfire_walk(pakfire, archive, __pakfire_extract, filter_callback, &data);
goto ERROR;
// Finish the progressbar
- if (data.progressbar)
- pakfire_progressbar_finish(data.progressbar);
+ r = pakfire_progress_finish(data.progress);
+ if (r)
+ goto ERROR;
ERROR:
- if (data.progressbar)
- pakfire_progressbar_unref(data.progressbar);
+ if (data.progress)
+ pakfire_progress_unref(data.progress);
if (data.writer)
archive_write_free(data.writer);
#include <pakfire/key.h>
#include <pakfire/packagelist.h>
#include <pakfire/parser.h>
+#include <pakfire/progress.h>
#include <pakfire/repo.h>
#include <pakfire/repolist.h>
#include <pakfire/transaction.h>
void pakfire_set_pick_solution_callback(
struct pakfire* pakfire, pakfire_pick_solution_callback callback, void* data);
+typedef int (*pakfire_setup_progress_callback)
+ (struct pakfire* pakfire, struct pakfire_progress* progress, void* data);
+void pakfire_set_setup_progress_callback(
+ struct pakfire* pakfire, pakfire_setup_progress_callback callback, void* data);
+
const char* pakfire_get_path(struct pakfire* pakfire);
int pakfire_clean(struct pakfire* pakfire, int flags);
int pakfire_confirm(struct pakfire* pakfire, const char* message, const char* question);
int pakfire_pick_solution(struct pakfire* pakfire, struct pakfire_transaction* transaction);
+int pakfire_setup_progress(struct pakfire* pakfire, struct pakfire_progress* progress);
magic_t pakfire_get_magic(struct pakfire* pakfire);
--- /dev/null
+/*#############################################################################
+# #
+# Pakfire - The IPFire package management system #
+# Copyright (C) 2023 Pakfire development team #
+# #
+# This program is free software: you can redistribute it and/or modify #
+# it under the terms of the GNU General Public License as published by #
+# the Free Software Foundation, either version 3 of the License, or #
+# (at your option) any later version. #
+# #
+# This program is distributed in the hope that it will be useful, #
+# but WITHOUT ANY WARRANTY; without even the implied warranty of #
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
+# GNU General Public License for more details. #
+# #
+# You should have received a copy of the GNU General Public License #
+# along with this program. If not, see <http://www.gnu.org/licenses/>. #
+# #
+#############################################################################*/
+
+#ifndef PAKFIRE_PROGRESS_H
+#define PAKFIRE_PROGRESS_H
+
+#include <time.h>
+
+struct pakfire_progress;
+
+enum pakfire_progress_flags {
+ PAKFIRE_PROGRESS_NO_PROGRESS = (1 << 0),
+ PAKFIRE_PROGRESS_SHOW_PERCENTAGE = (1 << 1),
+ PAKFIRE_PROGRESS_SHOW_BYTES_TRANSFERRED = (1 << 2),
+ PAKFIRE_PROGRESS_SHOW_COUNTER = (1 << 3),
+ PAKFIRE_PROGRESS_SHOW_ELAPSED_TIME = (1 << 4),
+ PAKFIRE_PROGRESS_SHOW_ETA = (1 << 5),
+ PAKFIRE_PROGRESS_SHOW_TRANSFER_SPEED = (1 << 6),
+};
+
+int pakfire_progress_has_flag(struct pakfire_progress* p, int flag);
+
+/*
+ Callbacks
+*/
+typedef int (*pakfire_progress_start_callback)
+ (struct pakfire* pakfire, struct pakfire_progress* progress, void* data, unsigned long int value);
+typedef int (*pakfire_progress_finish_callback)
+ (struct pakfire* pakfire, struct pakfire_progress* progress, void* data);
+typedef int (*pakfire_progress_update_callback)
+ (struct pakfire* pakfire, struct pakfire_progress* progress, void* data, unsigned long int value);
+typedef void (*pakfire_progress_free_callback)
+ (struct pakfire* pakfire, struct pakfire_progress* progress, void* data);
+
+void pakfire_progress_set_callback_data(struct pakfire_progress* p, void* data);
+
+void pakfire_progress_set_start_callback(
+ struct pakfire_progress* p, pakfire_progress_start_callback callback);
+void pakfire_progress_set_finish_callback(
+ struct pakfire_progress* p, pakfire_progress_finish_callback callback);
+void pakfire_progress_set_update_callback(
+ struct pakfire_progress* p, pakfire_progress_update_callback callback);
+void pakfire_progress_set_free_callback(
+ struct pakfire_progress* p, pakfire_progress_free_callback callback);
+
+unsigned long int pakfire_progress_get_value(struct pakfire_progress* p);
+unsigned long int pakfire_progress_get_max_value(struct pakfire_progress* p);
+const char* pakfire_progress_get_title(struct pakfire_progress* p);
+double pakfire_progress_get_percentage(struct pakfire_progress* p);
+time_t pakfire_progress_get_elapsed_time(struct pakfire_progress* p);
+time_t pakfire_progress_get_eta(struct pakfire_progress* p);
+double pakfire_progress_get_transfer_speed(struct pakfire_progress* p);
+
+#ifdef PAKFIRE_PRIVATE
+
+#include <pakfire/pakfire.h>
+
+int pakfire_progress_create(struct pakfire_progress** progress,
+ struct pakfire* pakfire, const char* title, int flags);
+
+struct pakfire_progress* pakfire_progress_ref(struct pakfire_progress* p);
+struct pakfire_progress* pakfire_progress_unref(struct pakfire_progress* p);
+
+int pakfire_progress_start(struct pakfire_progress* p, unsigned long int value);
+int pakfire_progress_finish(struct pakfire_progress* p);
+int pakfire_progress_update(struct pakfire_progress* p, unsigned long int value);
+
+#endif
+
+#endif /* PAKFIRE_PROGRESS_H */
pakfire_set_confirm_callback;
pakfire_set_log_callback;
pakfire_set_pick_solution_callback;
+ pakfire_set_setup_progress_callback;
pakfire_unref;
pakfire_version_compare;
pakfire_whatprovides;
pakfire_problem_to_string;
pakfire_problem_unref;
+ # progress
+ pakfire_progress_get_elapsed_time;
+ pakfire_progress_get_eta;
+ pakfire_progress_get_max_value;
+ pakfire_progress_get_percentage;
+ pakfire_progress_get_title;
+ pakfire_progress_get_transfer_speed;
+ pakfire_progress_get_value;
+ pakfire_progress_has_flag;
+ pakfire_progress_set_callback_data;
+ pakfire_progress_set_finish_callback;
+ pakfire_progress_set_free_callback;
+ pakfire_progress_set_start_callback;
+ pakfire_progress_set_update_callback;
+
# progressbar
pakfire_progressbar_add_bar;
pakfire_progressbar_add_bytes_transferred;
// Pick Solution
pakfire_pick_solution_callback pick_solution;
void* pick_solution_data;
+
+ // Setup Progress
+ pakfire_setup_progress_callback setup_progress;
+ void* setup_progress_data;
} callbacks;
// Logging
return r;
}
+PAKFIRE_EXPORT void pakfire_set_setup_progress_callback(struct pakfire* pakfire,
+ pakfire_setup_progress_callback callback, void* data) {
+ pakfire->callbacks.setup_progress = callback;
+ pakfire->callbacks.setup_progress_data = data;
+}
+
+int pakfire_setup_progress(struct pakfire* pakfire, struct pakfire_progress* progress) {
+ int r = 0;
+
+ if (pakfire->callbacks.setup_progress)
+ r = pakfire->callbacks.setup_progress(pakfire,
+ progress, pakfire->callbacks.setup_progress_data);
+
+ return r;
+}
+
PAKFIRE_EXPORT int pakfire_has_flag(struct pakfire* pakfire, const int flag) {
return pakfire->flags & flag;
}
--- /dev/null
+/*#############################################################################
+# #
+# Pakfire - The IPFire package management system #
+# Copyright (C) 2023 Pakfire development team #
+# #
+# This program is free software: you can redistribute it and/or modify #
+# it under the terms of the GNU General Public License as published by #
+# the Free Software Foundation, either version 3 of the License, or #
+# (at your option) any later version. #
+# #
+# This program is distributed in the hope that it will be useful, #
+# but WITHOUT ANY WARRANTY; without even the implied warranty of #
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
+# GNU General Public License for more details. #
+# #
+# You should have received a copy of the GNU General Public License #
+# along with this program. If not, see <http://www.gnu.org/licenses/>. #
+# #
+#############################################################################*/
+
+#include <errno.h>
+#include <stdlib.h>
+#include <time.h>
+
+#include <pakfire/pakfire.h>
+#include <pakfire/private.h>
+#include <pakfire/progress.h>
+
+struct pakfire_progress {
+ struct pakfire* pakfire;
+ int nrefs;
+
+ // Title
+ const char* title;
+
+ // Flags what to show
+ int flags;
+
+ // Status
+ enum pakfire_progress_status {
+ PAKFIRE_PROGRESS_INIT = 0,
+ PAKFIRE_PROGRESS_RUNNING,
+ PAKFIRE_PROGRESS_FINISHED,
+ } status;
+
+ // Callbacks
+ struct pakfire_progress_callbacks {
+ void* data;
+
+ pakfire_progress_start_callback start;
+ pakfire_progress_finish_callback finish;
+ pakfire_progress_update_callback update;
+ pakfire_progress_free_callback free;
+ } callbacks;
+
+ // Start Time
+ struct timespec time_start;
+ struct timespec time_finished;
+
+ // Values
+ unsigned long int value;
+ unsigned long int max_value;
+};
+
+static void pakfire_progress_free(struct pakfire_progress* p) {
+ // Ensure this is finished
+ pakfire_progress_finish(p);
+
+ // Call the free callback
+ if (p->callbacks.free)
+ p->callbacks.free(p->pakfire, p, p->callbacks.data);
+
+ pakfire_unref(p->pakfire);
+ free(p);
+}
+
+int pakfire_progress_create(struct pakfire_progress** progress,
+ struct pakfire* pakfire, const char* title, int flags) {
+ struct pakfire_progress* p = NULL;
+ int r;
+
+ // Allocate a new object
+ p = calloc(1, sizeof(*p));
+ if (!p)
+ return -errno;
+
+ // Store a reference to Pakfire
+ p->pakfire = pakfire_ref(pakfire);
+
+ // Initialize the reference counter
+ p->nrefs = 1;
+
+ // Store title
+ p->title = title;
+
+ // Store the flags
+ p->flags = flags;
+
+ // Initialize status
+ p->status = PAKFIRE_PROGRESS_INIT;
+
+ // XXX Create a couple of default callbacks
+
+ // Call setup
+ if (!pakfire_progress_has_flag(p, PAKFIRE_PROGRESS_NO_PROGRESS)) {
+ r = pakfire_setup_progress(pakfire, p);
+ if (r)
+ goto ERROR;
+ }
+
+ // Success
+ *progress = p;
+ return 0;
+
+ERROR:
+ if (p)
+ pakfire_progress_unref(p);
+
+ return r;
+}
+
+struct pakfire_progress* pakfire_progress_ref(struct pakfire_progress* p) {
+ ++p->nrefs;
+
+ return p;
+}
+
+struct pakfire_progress* pakfire_progress_unref(struct pakfire_progress* p) {
+ if (--p->nrefs > 0)
+ return p;
+
+ pakfire_progress_free(p);
+ return NULL;
+}
+
+PAKFIRE_EXPORT int pakfire_progress_has_flag(struct pakfire_progress* p, int flag) {
+ return p->flags & flag;
+}
+
+/*
+ Callback Configuration
+*/
+
+PAKFIRE_EXPORT void pakfire_progress_set_callback_data(struct pakfire_progress* p, void* data) {
+ p->callbacks.data = data;
+}
+
+PAKFIRE_EXPORT void pakfire_progress_set_start_callback(
+ struct pakfire_progress* p, pakfire_progress_start_callback callback) {
+ p->callbacks.start = callback;
+}
+
+PAKFIRE_EXPORT void pakfire_progress_set_finish_callback(
+ struct pakfire_progress* p, pakfire_progress_finish_callback callback) {
+ p->callbacks.finish = callback;
+}
+
+PAKFIRE_EXPORT void pakfire_progress_set_update_callback(
+ struct pakfire_progress* p, pakfire_progress_update_callback callback) {
+ p->callbacks.update = callback;
+}
+
+PAKFIRE_EXPORT void pakfire_progress_set_free_callback(
+ struct pakfire_progress* p, pakfire_progress_free_callback callback) {
+ p->callbacks.free = callback;
+}
+
+int pakfire_progress_start(struct pakfire_progress* p, unsigned long int value) {
+ int r;
+
+ // This can only be called once
+ if (p->status == PAKFIRE_PROGRESS_RUNNING)
+ return -EINVAL;
+
+ // We are now running...
+ p->status = PAKFIRE_PROGRESS_RUNNING;
+
+ // Store the max value
+ p->max_value = value;
+
+ // Set start time
+ r = clock_gettime(CLOCK_REALTIME, &p->time_start);
+ if (r)
+ return r;
+
+ // No-op
+ if (pakfire_progress_has_flag(p, PAKFIRE_PROGRESS_NO_PROGRESS))
+ return 0;
+
+ // Call the start callback
+ if (p->callbacks.start) {
+ r = p->callbacks.start(p->pakfire, p, p->callbacks.data, value);
+ if (r)
+ return r;
+ }
+
+ // Call the first update
+ return pakfire_progress_update(p, 0);
+}
+
+int pakfire_progress_finish(struct pakfire_progress* p) {
+ int r;
+
+ // Do nothing if already finished
+ if (p->status == PAKFIRE_PROGRESS_FINISHED)
+ return 0;
+
+ // No-op
+ if (pakfire_progress_has_flag(p, PAKFIRE_PROGRESS_NO_PROGRESS))
+ return 0;
+
+ // Set finished time
+ r = clock_gettime(CLOCK_REALTIME, &p->time_finished);
+ if (r)
+ return r;
+
+ // Call the finish callback
+ if (p->callbacks.finish) {
+ r = p->callbacks.finish(p->pakfire, p, p->callbacks.data);
+ if (r)
+ return r;
+ }
+
+ return 0;
+}
+
+int pakfire_progress_update(struct pakfire_progress* p, unsigned long int value) {
+ int r;
+
+ // Store the new value
+ p->value = value;
+
+ // No-op
+ if (pakfire_progress_has_flag(p, PAKFIRE_PROGRESS_NO_PROGRESS))
+ return 0;
+
+ // Call the update callback
+ if (p->callbacks.update) {
+ r = p->callbacks.update(p->pakfire, p, p->callbacks.data, value);
+ if (r)
+ return r;
+ }
+
+ return r;
+}
+
+PAKFIRE_EXPORT unsigned long int pakfire_progress_get_value(struct pakfire_progress* p) {
+ return p->value;
+}
+
+PAKFIRE_EXPORT unsigned long int pakfire_progress_get_max_value(struct pakfire_progress* p) {
+ return p->max_value;
+}
+
+PAKFIRE_EXPORT const char* pakfire_progress_get_title(struct pakfire_progress* p) {
+ return p->title;
+}
+
+PAKFIRE_EXPORT double pakfire_progress_get_percentage(struct pakfire_progress* p) {
+ if (!p->max_value)
+ return 0;
+
+ return p->value * 100.0 / p->max_value;
+}
+
+PAKFIRE_EXPORT time_t pakfire_progress_get_elapsed_time(struct pakfire_progress* p) {
+ struct timespec now;
+ int r;
+
+ switch (p->status) {
+ case PAKFIRE_PROGRESS_INIT:
+ return 0;
+
+ case PAKFIRE_PROGRESS_RUNNING:
+ r = clock_gettime(CLOCK_REALTIME, &now);
+ if (r)
+ return -1;
+
+ return now.tv_sec - p->time_start.tv_sec;
+
+ case PAKFIRE_PROGRESS_FINISHED:
+ return p->time_finished.tv_sec - p->time_start.tv_sec;
+ }
+
+ return -1;
+}
+
+PAKFIRE_EXPORT time_t pakfire_progress_get_eta(struct pakfire_progress* p) {
+ const time_t t = pakfire_progress_get_elapsed_time(p);
+ if (t < 0)
+ return t;
+
+ return t * p->max_value / p->value - t;
+}
+
+PAKFIRE_EXPORT double pakfire_progress_get_transfer_speed(struct pakfire_progress* p) {
+ const time_t t = pakfire_progress_get_elapsed_time(p);
+ if (t <= 0)
+ return t;
+
+ return p->value / t;
+}