/********************************************************************* * bfs * * Copyright (C) 2015 Tavian Barnes * * * * This program is free software. It comes without any warranty, to * * the extent permitted by applicable law. You can redistribute it * * and/or modify it under the terms of the Do What The Fuck You Want * * To Public License, Version 2, as published by Sam Hocevar. See * * the COPYING file or http://www.wtfpl.net/ for more details. * *********************************************************************/ #include "color.h" #include "bftw.h" #include #include #include #include #include #include #include struct ext_color { const char *ext; size_t len; const char *color; struct ext_color *next; }; struct colors { const char *reset; const char *normal; const char *file; const char *dir; const char *link; const char *multi_hard; const char *pipe; const char *door; const char *block; const char *chardev; const char *orphan; const char *socket; const char *setuid; const char *setgid; const char *capable; const char *sticky_ow; const char *ow; const char *sticky; const char *exec; const char *warning; const char *error; struct ext_color *ext_list; char *data; }; struct colors *parse_colors(const char *ls_colors) { struct colors *colors = malloc(sizeof(struct colors)); if (!colors) { goto done; } // Defaults generated by dircolors --print-database colors->reset = "0"; colors->normal = NULL; colors->file = NULL; colors->dir = "01;34"; colors->link = "01;36"; colors->multi_hard = "00"; colors->pipe = "40;33"; colors->socket = "01;35"; colors->door = "01;35"; colors->block = "40;33;01"; colors->chardev = "40;33;01"; colors->orphan = "40;31;01"; colors->setuid = "37;41"; colors->setgid = "30;43"; colors->capable = "30;41"; colors->sticky_ow = "30;42"; colors->ow = "34;42"; colors->sticky = "37;44"; colors->exec = "01;32"; colors->warning = "40;33;01"; colors->error = "40;31;01"; colors->ext_list = NULL; colors->data = NULL; if (ls_colors) { colors->data = strdup(ls_colors); } if (!colors->data) { goto done; } char *start = colors->data; char *end; struct ext_color *ext; for (end = strchr(start, ':'); *start && end; start = end + 1, end = strchr(start, ':')) { char *equals = strchr(start, '='); if (!equals) { continue; } *equals = '\0'; *end = '\0'; const char *key = start; const char *value = equals + 1; switch (key[0]) { case 'b': if (strcmp(key, "bd") == 0) { colors->block = value; } break; case 'c': if (strcmp(key, "ca") == 0) { colors->capable = value; } else if (strcmp(key, "cd") == 0) { colors->chardev = value; } break; case 'd': if (strcmp(key, "di") == 0) { colors->dir = value; } else if (strcmp(key, "do") == 0) { colors->socket = value; } break; case 'e': if (strcmp(key, "ex") == 0) { colors->exec = value; } break; case 'f': if (strcmp(key, "fi") == 0) { colors->file = value; } break; case 'l': if (strcmp(key, "ln") == 0) { colors->link = value; } break; case 'n': if (strcmp(key, "no") == 0) { colors->normal = value; } break; case 'o': if (strcmp(key, "or") == 0) { colors->orphan = value; } else if (strcmp(key, "ow") == 0) { colors->ow = value; } break; case 'p': if (strcmp(key, "pi") == 0) { colors->pipe = value; } break; case 'r': if (strcmp(key, "rs") == 0) { colors->reset = value; } break; case 's': if (strcmp(key, "sg") == 0) { colors->setgid = value; } else if (strcmp(key, "so") == 0) { colors->socket = value; } else if (strcmp(key, "st") == 0) { colors->sticky = value; } else if (strcmp(key, "su") == 0) { colors->setuid = value; } break; case 't': if (strcmp(key, "tw") == 0) { colors->sticky_ow = value; } break; case '*': ext = malloc(sizeof(struct ext_color)); if (ext) { ext->ext = key + 1; ext->len = strlen(ext->ext); ext->color = value; ext->next = colors->ext_list; colors->ext_list = ext; } } } done: return colors; } static const char *file_color(const struct colors *colors, const char *filename, const struct BFTW *ftwbuf) { const struct stat *sb = ftwbuf->statbuf; if (!sb) { return colors->orphan; } const char *color = NULL; switch (sb->st_mode & S_IFMT) { case S_IFREG: if (sb->st_mode & S_ISUID) { color = colors->setuid; } else if (sb->st_mode & S_ISGID) { color = colors->setgid; } else if (sb->st_mode & 0111) { color = colors->exec; } if (!color) { size_t namelen = strlen(filename); for (struct ext_color *ext = colors->ext_list; ext; ext = ext->next) { if (namelen >= ext->len && memcmp(filename + namelen - ext->len, ext->ext, ext->len) == 0) { color = ext->color; break; } } } if (!color) { color = colors->file; } break; case S_IFDIR: if (sb->st_mode & S_ISVTX) { if (sb->st_mode & S_IWOTH) { color = colors->sticky_ow; } else { color = colors->sticky; } } else if (sb->st_mode & S_IWOTH) { color = colors->ow; } if (!color) { color = colors->dir; } break; case S_IFLNK: if (faccessat(ftwbuf->at_fd, ftwbuf->at_path, F_OK, 0) == 0) { color = colors->link; } else { color = colors->orphan; } break; case S_IFBLK: color = colors->block; break; case S_IFCHR: color = colors->chardev; break; case S_IFIFO: color = colors->pipe; break; case S_IFSOCK: color = colors->socket; break; } if (!color) { color = colors->normal; } return color; } static void print_esc(const char *esc, FILE *file) { fputs("\033[", file); fputs(esc, file); fputs("m", file); } void pretty_print(const struct colors *colors, const struct BFTW *ftwbuf) { const char *path = ftwbuf->path; if (!colors) { puts(path); return; } const char *filename = path + ftwbuf->nameoff; if (colors->dir) { print_esc(colors->dir, stdout); } fwrite(path, 1, ftwbuf->nameoff, stdout); if (colors->dir) { print_esc(colors->reset, stdout); } const char *color = file_color(colors, filename, ftwbuf); if (color) { print_esc(color, stdout); } fputs(filename, stdout); if (color) { print_esc(colors->reset, stdout); } fputs("\n", stdout); } static void pretty_format(const struct colors *colors, const char *color, const char *format, va_list args) { if (color) { print_esc(color, stderr); } vfprintf(stderr, format, args); if (color) { print_esc(colors->reset, stderr); } } void pretty_warning(const struct colors *colors, const char *format, ...) { va_list args; va_start(args, format); pretty_format(colors, colors ? colors->warning : NULL, format, args); va_end(args); } void pretty_error(const struct colors *colors, const char *format, ...) { va_list args; va_start(args, format); pretty_format(colors, colors ? colors->error : NULL, format, args); va_end(args); } void free_colors(struct colors *colors) { if (colors) { struct ext_color *ext = colors->ext_list; while (ext) { struct ext_color *saved = ext; ext = ext->next; free(saved); } free(colors->data); free(colors); } }