/* * Copyright (C) 2025 cryobs * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, this * list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include #include #include #include #include #include #include "modules.h" #include "config.h" #define COLORS 10 struct ascii { char *art; int width; int height; }; void free_ascii(struct ascii *s) { if (!s) return; free(s->art); s->art = NULL; } /* header */ typedef struct { char *name; char *sep; /* separator name[]hostname */ char hostname[256]; } header; typedef struct { char *label; char *value; } rendered_info; typedef struct { rendered_info *entries; size_t count; } info_list; void free_info_list(info_list *infos) { if (!infos) return; for (size_t i = 0; i < infos->count; i++) { free(infos->entries[i].label); free(infos->entries[i].value); } free(infos->entries); infos->count = 0; } /* * color to ansi code * function that takes color [0-15] and convert it to: * - [0-7] - [30-37] default colors * - [8-15] - [90-97] light colors * and writes to out buffer */ void c_to_ansi(const char *color, char *out, size_t out_size) { int n = atoi(color); int ansi_code = 39; if (n >= 0 && n <= 7) { ansi_code = n + 30; } else if (n > 7 && n <= 15) { ansi_code = n + 90 - 8; } snprintf(out, out_size, "%d", ansi_code); } /* * function that: * 1. if (align_info) add padding to label * 2. return info_list, with malloc! */ info_list render_info(info_item infos[], size_t info_size) { info_list res = {NULL, 0}; size_t maxlen = 0; if (info_align) { for(size_t i = 0; i < info_size; i++) { size_t len = strlen(infos[i].label); if (len > maxlen) { maxlen = len; } } } for (size_t i = 0; i < info_size; i++) { char *value = infos[i].func(); if (!value) value = "(null)"; /* count strings */ int split_count = 0; { char *tmp = strdup(value); char *p = strtok(tmp, "\n"); while (p) { split_count++; p = strtok(NULL, "\n"); } free(tmp); } /* split strings by \n */ char *line = strtok(value, "\n"); int number = 1; while (line) { res.entries = realloc(res.entries, sizeof(rendered_info) * (res.count + 1)); char tmp_label[128]; if (split_count > 1 && numerate_same) { snprintf(tmp_label, sizeof(tmp_label), "%s%d", infos[i].label, number); } else { snprintf(tmp_label, sizeof(tmp_label), "%s", infos[i].label); } if (info_align) { char padded[128]; snprintf(padded, sizeof(padded), "%-*s", (int)maxlen, tmp_label); res.entries[res.count].label = strdup(padded); } else { res.entries[res.count].label = strdup(tmp_label); } res.entries[res.count].value = strdup(line); res.count++; line = strtok(NULL, "\n"); number++; } free(value); } return res; } /* * function that returns dynamic sebstring from *start to *end */ char * substring(const char *start, const char *end) { if (!start || !end || end < start) return NULL; size_t len = end - start; char *out = malloc(len + 1); /* +1 for \0 */ if (!out) return NULL; memcpy(out, start, len); out[len] = '\0'; return out; } /* function that: * - deletes all spaces from start to first char * - deletes all spaces from end to first char */ char * trim(char *str) { char *end; /* skip start spaces */ while (isspace((unsigned char)*str)) str++; if (*str == 0) /* blank str */ return str; /* skip end spaces */ end = str + strlen(str) - 1; while (end > str && isspace((unsigned char)*end)) end--; /* ending string */ end[1] = '\0'; return str; } /* * function that prints header * returns: * > 0: header length * < 0: error */ int print_header() { char *name = getenv("USER"); char hostname[256]; if (gethostname(hostname, sizeof(hostname)) != 0) { perror("gethostname error"); return -1; } printf("\x1b[0m"); /* reset color */ printf("\x1b[%dm%s", colors[2], name); printf("\x1b[%dm%s", colors[7], header_sep); printf("\x1b[%dm%s", colors[2], hostname); return strlen(name) + strlen(header_sep) + strlen(hostname); } /* * function that prints boundary * returns: * boundary length */ int print_boundary(const char *c, int len) { printf("\x1b[%dm", colors[8]); for(int i = 0; i < len; i++) { printf("%s", c); } printf("\x1b[0m"); /* reset color */ return len * strlen(boundary_char); } /* * function that reads file to buf dynamicly * if returns < 0 here errors: * - (-1) file doesn't exist * - (-2) can't get size of the file * - (-3) problem with allocating memory for file content * - (-4) problem with reading file content * "adds '\0' to and" */ int read_file(const char *file_path, char **buf) { if (!file_path || !buf) { *buf = NULL; return -EINVAL; } FILE* fp = fopen(file_path, "r") ; if (!fp) { perror(file_path); return -1; } struct stat st; if (fstat(fileno(fp), &st) != 0 || !S_ISREG(st.st_mode)) { fclose(fp); return -2; } size_t sz = (size_t)st.st_size; *buf = malloc(sz + 1); if(!*buf) { fclose(fp); perror("malloc"); return -3; } size_t read = fread(*buf, 1, sz, fp); if (read < sz && ferror(fp)) { perror("fread"); free(*buf); fclose(fp); return -4; } (*buf)[read] = '\0'; fclose(fp); return 0; } /* * function that fill ascii.width and ascii.height fields, * by counting the most long line (width), * and counting lines (height); * "writes to res" */ void get_ascii_size(struct ascii *res) { if (!res || !res->art) { res->width = res->height = 0; return; } int maxw = 0; int curw = 0; int lines = 0; char *p = res->art; if (*p == '\0') { res->width = res->height = 0; return; } while (*p) { if (*p == '\n') { lines++; if (curw > maxw) maxw = curw; curw = 0; } else { curw++; } p++; } /* if last char is not '\n' */ if (curw == 0 || (res->art[0] != '\0' && res->art[0] == '\n')) { lines++; if (curw > maxw) maxw = curw; } res->width = maxw; res->height = lines; } /* * fill all struct ascii fields and return final structure */ struct ascii get_ascii() { struct ascii res = {0}; res.art = strdup(ascii_art); get_ascii_size(&res); return res; } /* * function that print: * - ascii art with colors from config, with processing like: * $1 - cfg.colors[1] * - information with print_info(), takes size of infos (size_t infos_size) * * if return < 0: * - */ int print_fetch(struct ascii *res) { char *p = res->art; int curw = 0; char cur_color = '7'; int cur_info = 0; int header_len = 0; info_list infos = render_info(config_items, config_items_len); while(*p || (size_t)cur_info < infos.count + ((color_palette_show == 1) ? 3 : 0) ) { if (*p) { /* check color */ if(*p == '$' && isdigit(*(p + 1))){ cur_color = *++p; printf("\x1b[%dm", colors[cur_color - '0']); p++; continue; } /* print char */ if(*p != '\n'){ putchar(*p); p++; curw++; continue; } p++; } /* add padding */ if (res->width > 0) { while(curw != res->width + ascii_pad) { putchar(' '); curw++; } } /* print header */ if (header_len == 0 && header_show != 0){ header_len = print_header(); if (header_len < 0) { fprintf(stderr, "Header Error: %d", header_len); exit(EXIT_FAILURE); } goto print_fetch_end; } else if (header_len > 0 && strlen(boundary_char) > 0) { print_boundary(boundary_char, header_len); header_len = -1; goto print_fetch_end; } /* print boundary for palette */ if ((size_t)cur_info == infos.count && color_palette_show) { print_boundary(" ", 1); cur_info++; goto print_fetch_end; } /* print normal palette */ if ((size_t)cur_info == infos.count + 1 && color_palette_show) { for (int i = 0; i < 8; i++) { printf("\x1b[4%dm ", i); } cur_info++; goto print_fetch_end; } /* print bright palette */ if ((size_t)cur_info == infos.count + 2 && color_palette_show) { for (int i = 0; i < 8; i++) { printf("\x1b[10%dm ", i); } cur_info++; goto print_fetch_end; } /* print info */ if ((size_t)cur_info < infos.count) { printf("\x1b[0m"); /* reset color */ printf("\x1b[%dm%s", colors[1], infos.entries[cur_info].label); printf("\x1b[%dm%s", colors[6], info_sep); printf("\x1b[%dm%s", colors[5], infos.entries[cur_info].value); cur_info++; } /* chill I know what I'm doing */ print_fetch_end: curw = 0; printf("\x1b[0m"); /* reset color */ printf("\n"); printf("\x1b[%dm", colors[cur_color - '0']); } printf("\x1b[0m"); /* reset color */ free_info_list(&infos); return 0; } int main(void) { struct ascii art = get_ascii(); print_fetch(&art); free_ascii(&art); return 0; }