/
niceSOFT
/
wget2
Обзор
Документация
Войти
/
niceSOFT
/
wget2
Код
Задачи
Вики
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
src/bar.c
389 строк
9 KB
Darshit Shah
all: Update Copyright Year
03 фев 2026, 23:40
03 фев 2026, 23:40
84b9aa0
Код
Авторство
О чём код?
/* * Copyright (c) 2014 Tim Ruehsen * Copyright (c) 2015-2026 Free Software Foundation, Inc. * * This file is part of Wget. * * Wget 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. * * Wget 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 Wget. If not, see <https://www.gnu.org/licenses/>. * * * Progress bar routines * * Changelog * 11.09.2014 Tim Ruehsen created * */ #include <config.h> #include <stdio.h> #include <stdbool.h> #include <stdarg.h> #include <string.h> #include <wget.h> #include "wget_main.h" #include "wget_options.h" #include "wget_bar.h" // Rate at which progress thread it updated. This is the amount of time (in ms) // for which the thread will sleep before waking up and redrawing the progress enum { BAR_THREAD_SLEEP_DURATION = 1000, BAR_THREAD_WINDOWS_CONSOLE_SIZE_CHECK_INTERVAL = 1000 }; static wget_bar *bar; static wget_thread progress_thread; static volatile bool terminate_thread; // Dot progress implementation #define RING_SIZE 64 typedef struct { long long total_length; long long downloaded; long long accumulated; long long row_start_bytes; long long row_start_time; long long time_ring[RING_SIZE]; long long bytes_ring[RING_SIZE]; int ring_pos; int dots_printed; int rows_printed; bool ring_wrapped; char *filename; } dot_slot_t; static dot_slot_t *dot_slots; static int n_dot_slots; static wget_thread_mutex dot_mutex; static void dot_init(void) { wget_thread_mutex_init(&dot_mutex); } static void dot_exit(void) { if (dot_slots) { for (int i = 0; i < n_dot_slots; i++) xfree(dot_slots[i].filename); xfree(dot_slots); } wget_thread_mutex_destroy(&dot_mutex); } static void dot_slot_begin(int slot, const char *filename, ssize_t filesize) { wget_thread_mutex_lock(dot_mutex); if (slot >= n_dot_slots) { int new_n = slot + 1; dot_slots = wget_realloc(dot_slots, new_n * sizeof(dot_slot_t)); memset(dot_slots + n_dot_slots, 0, (new_n - n_dot_slots) * sizeof(dot_slot_t)); n_dot_slots = new_n; } dot_slot_t *dp = &dot_slots[slot]; xfree(dp->filename); dp->filename = wget_strdup(filename); dp->total_length = filesize; dp->downloaded = 0; dp->accumulated = 0; dp->row_start_bytes = 0; dp->row_start_time = wget_get_timemillis(); dp->dots_printed = 0; dp->rows_printed = 0; // init ring buffer dp->ring_pos = 0; dp->ring_wrapped = false; dp->time_ring[0] = 0; dp->bytes_ring[0] = 0; wget_thread_mutex_unlock(dot_mutex); } static long long get_speed(dot_slot_t *dp) { long long cur_time = wget_get_timemillis(); // update ring buffer int next_pos = dp->ring_pos; dp->time_ring[next_pos] = cur_time; dp->bytes_ring[next_pos] = dp->downloaded; long long time_span; long long bytes_span; if (dp->ring_wrapped) { int oldest = next_pos + 1; if (oldest >= RING_SIZE) oldest = 0; time_span = cur_time - dp->time_ring[oldest]; bytes_span = dp->downloaded - dp->bytes_ring[oldest]; } else { time_span = cur_time - dp->row_start_time; bytes_span = dp->downloaded; } if (++dp->ring_pos >= RING_SIZE) { dp->ring_pos = 0; dp->ring_wrapped = true; } if (time_span <= 0) time_span = 1; return (bytes_span * 1000 + time_span / 2) / time_span; } static void print_row_stats(dot_slot_t *dp) { long long speed = get_speed(dp); int percentage = 0; if (dp->total_length > 0) percentage = (int)(100 * dp->downloaded / dp->total_length); char speed_buf[16]; wget_human_readable(speed_buf, sizeof(speed_buf), speed); char eta_buf[32] = ""; if (dp->total_length > 0 && speed > 0) { long long eta = (dp->total_length - dp->downloaded + speed / 2) / speed; if (eta < 60) wget_snprintf(eta_buf, sizeof(eta_buf), "%llds", eta); else if (eta < 3600) wget_snprintf(eta_buf, sizeof(eta_buf), "%lldm%llds", eta / 60, eta % 60); else wget_snprintf(eta_buf, sizeof(eta_buf), "%lldh%lldm", eta / 3600, (eta % 3600) / 60); } wget_fprintf(stderr, " %3d%% %s %s", percentage, speed_buf, eta_buf); } static void dot_update(int slot, size_t nbytes) { wget_thread_mutex_lock(dot_mutex); if (slot < n_dot_slots) { dot_slot_t *dp = &dot_slots[slot]; dp->downloaded += nbytes; dp->accumulated += nbytes; const long long DOT_BYTES = config.dot_bytes ? config.dot_bytes : 1024; const int DOTS_PER_LINE = config.dots_in_line ? config.dots_in_line : 50; const int DOT_SPACING = config.dot_spacing ? config.dot_spacing : 10; while (dp->accumulated >= DOT_BYTES) { dp->accumulated -= DOT_BYTES; if (dp->dots_printed == 0) { char buf[16]; wget_fprintf(stderr, "\n%6s", wget_human_readable(buf, sizeof(buf), dp->rows_printed * DOTS_PER_LINE * DOT_BYTES)); } if (dp->dots_printed % DOT_SPACING == 0) wget_fprintf(stderr, " "); wget_fprintf(stderr, "."); dp->dots_printed++; if (dp->dots_printed >= DOTS_PER_LINE) { print_row_stats(dp); dp->dots_printed = 0; dp->rows_printed++; } } fflush(stderr); } wget_thread_mutex_unlock(dot_mutex); } static void dot_finish_slot(int slot) { wget_thread_mutex_lock(dot_mutex); if (slot < n_dot_slots) { dot_slot_t *dp = &dot_slots[slot]; const int DOTS_PER_LINE = config.dots_in_line ? config.dots_in_line : 50; const int DOT_SPACING = config.dot_spacing ? config.dot_spacing : 10; // Fill remaining dots with spaces to align stats if (dp->dots_printed > 0) { int spaces = DOTS_PER_LINE - dp->dots_printed; for (int i = dp->dots_printed; i < DOTS_PER_LINE; i++) { if (i % DOT_SPACING == 0) spaces++; } wget_fprintf(stderr, "%*.*s", spaces, spaces, ""); print_row_stats(dp); wget_fprintf(stderr, "\n"); } else if (dp->total_length == dp->downloaded) { // Let's just output a newline to be safe if we are not mid-line. wget_fprintf(stderr, "\n"); } fflush(stderr); } wget_thread_mutex_unlock(dot_mutex); } #ifdef _WIN32 static void *bar_update_thread(void *p WGET_GCC_UNUSED) { static int elapsed = 0; int lastWidth = 0; int curWidth = 0; while (!terminate_thread) { wget_bar_update(bar); wget_millisleep(BAR_THREAD_SLEEP_DURATION); elapsed += BAR_THREAD_SLEEP_DURATION; if (elapsed >= BAR_THREAD_WINDOWS_CONSOLE_SIZE_CHECK_INTERVAL) { if (! wget_get_screen_size(&curWidth, NULL) && curWidth != lastWidth) wget_bar_screen_resized(); elapsed = 0; } } return NULL; } #else // _WIN32 static void *bar_update_thread(void *p WGET_GCC_UNUSED) { while (!terminate_thread) { wget_bar_update(bar); wget_millisleep(BAR_THREAD_SLEEP_DURATION); } return NULL; } #endif // _WIN32 static void error_write(const char *buf, size_t len) { // write 'above' the progress bar area, scrolls screen one line up, red text color // CSI 31m: Red text color // CSI m: reset text color wget_bar_write_line_ext(bar, buf, len, "\033[31m", "\033[m"); } static void info_write(const char *buf, size_t len) { // write 'above' the progress bar area, scrolls screen one line up wget_bar_write_line(bar, buf, len); } bool bar_init(void) { if (config.progress == PROGRESS_TYPE_DOT) { dot_init(); return true; } if (wget_thread_support()) { if (!(bar = wget_bar_init(NULL, 1))) goto nobar; wget_bar_set_speed_type(config.report_speed); // set custom write function for wget_error_printf() wget_logger_set_func(wget_get_logger(WGET_LOGGER_ERROR), error_write); wget_logger_set_func(wget_get_logger(WGET_LOGGER_INFO), info_write); terminate_thread = 0; if (wget_thread_start(&progress_thread, bar_update_thread, NULL, 0)) { wget_bar_free(&bar); goto nobar; } return true; } nobar: wget_error_printf(_("Cannot create progress bar thread. Disabling progress bar.\n")); config.progress = PROGRESS_TYPE_NONE; return false; } void bar_deinit(void) { if (config.progress == PROGRESS_TYPE_DOT) { dot_exit(); return; } if (bar) { terminate_thread = 1; wget_thread_join(&progress_thread); wget_bar_free(&bar); } } void bar_print(int slot, const char *s) { if (config.progress == PROGRESS_TYPE_DOT) return; wget_bar_print(bar, slot, s); } void bar_vprintf(int slot, const char *fmt, va_list args) { if (config.progress == PROGRESS_TYPE_DOT) return; wget_bar_vprintf(bar, slot, fmt, args); } void bar_printf(int slot, const char *fmt, ...) { if (config.progress == PROGRESS_TYPE_DOT) return; va_list args; va_start(args, fmt); bar_vprintf(slot, fmt, args); va_end(args); } void bar_slot_begin(int slot, const char *filename, int new_file, ssize_t filesize) { if (config.progress == PROGRESS_TYPE_DOT) { dot_slot_begin(slot, filename, filesize); return; } wget_bar_slot_begin(bar, slot, filename, new_file, filesize); } void bar_set_downloaded(int slot, size_t nbytes) { if (config.progress == PROGRESS_TYPE_DOT) { dot_update(slot, nbytes); return; } wget_bar_slot_downloaded(bar, slot, nbytes); } void bar_slot_deregister(int slot) { if (config.progress == PROGRESS_TYPE_DOT) { dot_finish_slot(slot); return; } wget_bar_slot_deregister(bar, slot); } void bar_update_slots(int nslots) { if (config.progress == PROGRESS_TYPE_DOT) return; wget_bar_set_slots(bar, nslots); }