/
githubmirror
/
libcap-ng
Обзор
Документация
Войти
/
githubmirror
/
libcap-ng
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
utils/proc-output.c
277 строк
6 KB
Steve Grubb
Refactor shared proc utility output helpers
17 май 2026, 20:52
17 май 2026, 20:52
6f65367
Код
Авторство
О чём код?
/* * proc-output.c - Shared terminal output helpers for proc utilities * Copyright (c) 2026 Red Hat Inc. * All Rights Reserved. * * This software may be freely redistributed and/or modified under the * terms of the GNU General Public License as published by the Free * Software Foundation; either version 2, or (at your option) any * later version. */ #include "config.h" #include <errno.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/ioctl.h> #include <unistd.h> #include "proc-output.h" #define PROC_COLOR_RESET "\033[0m" /* * proc_output_width - choose the output width for wrapped terminal text. * @none: function takes no parameters. * * Returns terminal columns from ioctl(), then $COLUMNS, else 80. */ int proc_output_width(void) { struct winsize ws; const int fds[] = { STDOUT_FILENO, STDERR_FILENO, STDIN_FILENO }; const char *env; char *end = NULL; unsigned long v; size_t i; for (i = 0; i < sizeof(fds) / sizeof(fds[0]); i++) { if (ioctl(fds[i], TIOCGWINSZ, &ws) == 0 && ws.ws_col) return ws.ws_col; } env = getenv("COLUMNS"); if (env) { errno = 0; v = strtoul(env, &end, 10); if (errno == 0 && end != env && *end == '\0' && v > 0 && v < 400) return (int)v; } return 80; } /* * proc_wrap_plain - choose a wrap index for plain text. * @text: source string to wrap. * @max: maximum number of bytes to include before wrapping. * * Returns the byte offset where output should wrap, preferring commas and * spaces when possible. */ size_t proc_wrap_plain(const char *text, size_t max) { size_t len = strlen(text); size_t i; if (len <= max) return len; for (i = max; i > 0; i--) { if (text[i - 1] == ',') { if (i < len && text[i] == ' ') return i + 1; return i; } if (text[i - 1] == ' ') return i; } return max; } /* * skip_ansi_sgr - skip one ANSI SGR escape sequence. * @text: source string that may contain an SGR sequence at @i. * @i: byte offset to inspect. * * Returns the first byte after the SGR sequence, or @i when @text[@i] does * not start one. */ static int skip_ansi_sgr(const char *text, int i) { if (text[i] != '\033' || text[i + 1] != '[') return i; i += 2; while (text[i] && text[i] != 'm') i++; if (text[i] == 'm') i++; return i; } /* * copy_sgr - copy the active ANSI SGR sequence into @dst. * @dst: destination buffer for the active SGR sequence. * @dst_sz: size of @dst in bytes. * @text: source string containing an SGR sequence at @i. * @i: byte offset to inspect. * * Returns the first byte after the SGR sequence, or @i when no sequence was * present. A reset sequence clears @dst. */ static int copy_sgr(char *dst, size_t dst_sz, const char *text, int i) { int start = i; int end; size_t len; end = skip_ansi_sgr(text, i); if (end == start) return i; len = end - start; if (strncmp(text + start, PROC_COLOR_RESET, strlen(PROC_COLOR_RESET)) == 0) { dst[0] = '\0'; return end; } if (len < dst_sz) { memcpy(dst, text + start, len); dst[len] = '\0'; } return end; } /* * scan_color_state - update active color while scanning a wrapped segment. * @text: source string containing the rendered segment. * @from: first byte offset in the segment. * @to: byte offset just past the segment. * @active: destination buffer holding the active SGR sequence. * @active_sz: size of @active in bytes. * * Returns no value. @active is updated in place for continuation lines. */ static void scan_color_state(const char *text, int from, int to, char *active, size_t active_sz) { int i; for (i = from; i < to && text[i]; ) { if (text[i] == '\033' && text[i + 1] == '[') { i = copy_sgr(active, active_sz, text, i); continue; } i++; } } /* * wrap_ansi - choose a wrap index for text containing ANSI SGR escapes. * @text: source string to wrap. * @from: byte offset where this line starts. * @limit: maximum visible columns to include. * * Returns the byte offset where output should continue; SGR bytes do not * count toward @limit. */ static int wrap_ansi(const char *text, int from, int limit) { int i; int vis = 0; int break_at = -1; for (i = from; text[i] && vis < limit; ) { if (text[i] == '\033' && text[i + 1] == '[') { i = skip_ansi_sgr(text, i); continue; } if (text[i] == ' ' || text[i] == ',') break_at = i + 1; i++; vis++; } if (!text[i]) return i; if (break_at > from) return break_at; if (i == from) return from + 1; return i; } /* * proc_print_wrapped - print wrapped text with separate prefixes. * @head: prefix used for the first output line. * @cont: prefix used for continuation lines. * @text: text to render. * @width: target terminal width. * * Returns no value. ANSI SGR color is preserved across wrapped lines. */ void proc_print_wrapped(const char *head, const char *cont, const char *text, int width) { char active[32] = ""; int pos = 0; int first = 1; while (1) { const char *lead = first ? head : cont; int lead_len = strlen(lead); int avail = width - lead_len; int to; if (avail < 10) avail = 10; if (!text[pos]) { printf("%s\n", lead); return; } to = wrap_ansi(text, pos, avail); printf("%s", lead); if (!first && active[0]) fputs(active, stdout); printf("%.*s", to - pos, text + pos); scan_color_state(text, pos, to, active, sizeof(active)); if (active[0]) fputs(PROC_COLOR_RESET, stdout); putchar('\n'); while (text[to] == ' ') to++; pos = to; first = 0; if (!text[pos]) return; } } /* * proc_tree_print_node - print one wrapped tree node. * @prefix: existing tree prefix before this node. * @is_last: non-zero when this node is the last sibling. * @txt: node text to render. * @width: target terminal width. * * Returns no value. */ void proc_tree_print_node(const char *prefix, int is_last, const char *txt, int width) { char head[512]; char cont[512]; snprintf(head, sizeof(head), "%s%s", prefix, is_last ? "└─ " : "├─ "); snprintf(cont, sizeof(cont), "%s%s", prefix, is_last ? " " : "│ "); proc_print_wrapped(head, cont, txt, width); } /* * proc_tree_build_child_prefix - extend a tree prefix for child nodes. * @dst: destination buffer receiving the child prefix. * @dst_sz: size of @dst in bytes. * @prefix: parent prefix to extend. * @parent_is_last: non-zero when the parent is the last sibling. * * Returns no value. */ void proc_tree_build_child_prefix(char *dst, size_t dst_sz, const char *prefix, int parent_is_last) { snprintf(dst, dst_sz, "%s%s", prefix, parent_is_last ? " " : "│ "); }