X-Git-Url: http://git.refcnt.org/?p=colorize.git;a=blobdiff_plain;f=colorize.c;h=8a1cb5236adc3edd3e268afe70acef24eab4a658;hp=f80bdced0626687af9494834053e7991c89576b2;hb=048936d2b0358ba184b5380751744b8516fd74d1;hpb=e33f1445fc79d6c3d43d22808f09e85f0947530b diff --git a/colorize.c b/colorize.c index f80bdce..8a1cb52 100644 --- a/colorize.c +++ b/colorize.c @@ -2,7 +2,7 @@ * colorize - Read text from standard input stream or file and print * it colorized through use of ANSI escape sequences * - * Copyright (c) 2011-2018 Steven Schubiger + * Copyright (c) 2011-2019 Steven Schubiger * * 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 @@ -37,6 +37,7 @@ #include #include #include +#include #ifndef DEBUG # define DEBUG 0 @@ -129,7 +130,7 @@ #define PROGRAM_NAME "colorize" -#define VERSION "0.64" +#define VERSION "0.65" typedef enum { false, true } bool; @@ -216,10 +217,22 @@ static const struct { { bg_colors, COUNT_OF (bg_colors, struct color), "background" }, }; +static unsigned int opts_set; +enum opt_set { + OPT_ATTR_SET = 0x01, + OPT_EXCLUDE_RANDOM_SET = 0x02, + OPT_OMIT_COLOR_EMPTY_SET = 0x04 +}; +static struct { + char *attr; + char *exclude_random; +} opts_arg = { NULL, NULL }; + enum { OPT_ATTR = 1, OPT_CLEAN, OPT_CLEAN_ALL, + OPT_CONFIG, OPT_EXCLUDE_RANDOM, OPT_OMIT_COLOR_EMPTY, OPT_HELP, @@ -230,6 +243,7 @@ static const struct option long_opts[] = { { "attr", required_argument, &opt_type, OPT_ATTR }, { "clean", no_argument, &opt_type, OPT_CLEAN }, { "clean-all", no_argument, &opt_type, OPT_CLEAN_ALL }, + { "config", required_argument, &opt_type, OPT_CONFIG }, { "exclude-random", required_argument, &opt_type, OPT_EXCLUDE_RANDOM }, { "omit-color-empty", no_argument, &opt_type, OPT_OMIT_COLOR_EMPTY }, { "help", no_argument, &opt_type, OPT_HELP }, @@ -270,13 +284,15 @@ static const char *program_name; #if DEBUG static void print_tstamp (FILE *); #endif -static void process_opts (int, char **); +static void process_opts (int, char **, char **); +static void conf_file_path (char **); static void process_opt_attr (const char *, const bool); static void write_attr (const struct attr *, unsigned int *, const bool); static void process_opt_exclude_random (const char *, const bool); static void parse_conf (const char *, struct conf *); static void assign_conf (const char *, struct conf *, const char *, char *); static void init_conf_vars (const struct conf *); +static void init_opts_vars (void); static void print_hint (void); static void print_help (void); static void print_version (void); @@ -319,6 +335,7 @@ static void *realloc_wrap_debug (void *, size_t, const char *, unsigned int); static void free_wrap (void **); static char *strdup_wrap (const char *, const char *, unsigned int); static char *str_concat_wrap (const char *, const char *, const char *, unsigned int); +static char *expand_string (const char *); static bool get_bytes_size (unsigned long, struct bytes_size *); static char *get_file_type (mode_t); static bool has_color_name (const char *, const char *); @@ -342,11 +359,7 @@ main (int argc, char **argv) const char *file = NULL; - char *conf_file; - uid_t uid; - struct passwd *passwd; - size_t size; - + char *conf_file = NULL; struct conf config = { NULL, NULL, NULL, NULL }; program_name = argv[0]; @@ -361,22 +374,25 @@ main (int argc, char **argv) attr[0] = '\0'; + process_opts (argc, argv, &conf_file); + #ifdef CONF_FILE_TEST conf_file = to_str (CONF_FILE_TEST); #elif !defined(TEST) - uid = getuid (); - errno = 0; - if ((passwd = getpwuid (uid)) == NULL) + if (conf_file == NULL) + conf_file_path (&conf_file); + else { - if (errno == 0) - vfprintf_diag ("password file entry for uid %lu not found", (unsigned long)uid); - else - perror ("getpwuid"); - exit (EXIT_FAILURE); + char *s; + if ((s = expand_string (conf_file))) + { + free (conf_file); + conf_file = s; + } + errno = 0; + if (access (conf_file, F_OK) == -1) + vfprintf_fail (formats[FMT_FILE], conf_file, strerror (errno)); } - size = strlen (passwd->pw_dir) + 1 + strlen (CONF_FILE) + 1; - conf_file = xmalloc (size); - snprintf (conf_file, size, "%s/%s", passwd->pw_dir, CONF_FILE); #endif #if defined(CONF_FILE_TEST) || !defined(TEST) if (access (conf_file, F_OK) != -1) @@ -387,7 +403,7 @@ main (int argc, char **argv) #endif init_conf_vars (&config); - process_opts (argc, argv); + init_opts_vars (); arg_cnt = argc - optind; @@ -396,14 +412,21 @@ main (int argc, char **argv) if (clean && clean_all) vfprintf_fail (formats[FMT_GENERIC], "--clean and --clean-all switch are mutually exclusive"); if (arg_cnt > 1) - { - const char *const format = "%s %s"; - const char *const message = "switch cannot be used with more than one file"; - if (clean) - vfprintf_fail (format, "--clean", message); - else if (clean_all) - vfprintf_fail (format, "--clean-all", message); - } + vfprintf_fail ("--clean%s switch cannot be used with more than one file", clean_all ? "-all" : ""); + { + unsigned int i; + const struct option_set { + const char *option; + enum opt_set set; + } options[] = { + { "attr", OPT_ATTR_SET }, + { "exclude-random", OPT_EXCLUDE_RANDOM_SET }, + { "omit-color-empty", OPT_OMIT_COLOR_EMPTY_SET }, + }; + for (i = 0; i < COUNT_OF (options, struct option_set); i++) + if (opts_set & options[i].set) + vfprintf_diag ("--%s switch has no meaning with --clean%s", options[i].option, clean_all ? "-all" : ""); + } } else { @@ -455,6 +478,10 @@ print_tstamp (FILE *log) } #endif +#define DUP_CONFIG() \ + *conf_file = xstrdup (optarg); \ + break; + #define PRINT_HELP_EXIT() \ print_help (); \ exit (EXIT_SUCCESS); @@ -466,10 +493,10 @@ print_tstamp (FILE *log) extern char *optarg; static void -process_opts (int argc, char **argv) +process_opts (int argc, char **argv, char **conf_file) { int opt; - while ((opt = getopt_long (argc, argv, "hV", long_opts, NULL)) != -1) + while ((opt = getopt_long (argc, argv, "c:hV", long_opts, NULL)) != -1) { switch (opt) { @@ -477,8 +504,8 @@ process_opts (int argc, char **argv) switch (opt_type) { case OPT_ATTR: - attr[0] = '\0'; /* Clear attr string to discard values from the config file. */ - process_opt_attr (optarg, true); + opts_set |= OPT_ATTR_SET; + opts_arg.attr = xstrdup (optarg); break; case OPT_CLEAN: clean = true; @@ -486,11 +513,14 @@ process_opts (int argc, char **argv) case OPT_CLEAN_ALL: clean_all = true; break; + case OPT_CONFIG: + DUP_CONFIG (); case OPT_EXCLUDE_RANDOM: - process_opt_exclude_random (optarg, true); + opts_set |= OPT_EXCLUDE_RANDOM_SET; + opts_arg.exclude_random = xstrdup (optarg); break; case OPT_OMIT_COLOR_EMPTY: - omit_color_empty = true; + opts_set |= OPT_OMIT_COLOR_EMPTY_SET; break; case OPT_HELP: PRINT_HELP_EXIT (); @@ -500,6 +530,8 @@ process_opts (int argc, char **argv) ABORT_TRACE (); } break; + case 'c': + DUP_CONFIG (); case 'h': PRINT_HELP_EXIT (); case 'V': @@ -513,6 +545,31 @@ process_opts (int argc, char **argv) } } +static void +conf_file_path (char **conf_file) +{ + char *path; + uid_t uid; + struct passwd *passwd; + size_t size; + + uid = getuid (); + errno = 0; + if ((passwd = getpwuid (uid)) == NULL) + { + if (errno == 0) + vfprintf_diag ("password file entry for uid %lu not found", (unsigned long)uid); + else + perror ("getpwuid"); + exit (EXIT_FAILURE); + } + size = strlen (passwd->pw_dir) + 1 + strlen (CONF_FILE) + 1; + path = xmalloc (size); + snprintf (path, size, "%s/%s", passwd->pw_dir, CONF_FILE); + + *conf_file = path; +} + static void process_opt_attr (const char *p, const bool is_opt) { @@ -526,7 +583,7 @@ process_opt_attr (const char *p, const bool is_opt) }; unsigned int attr_types = 0; const char *desc_type[2] = { "--attr switch", "attr conf option" }; - unsigned int DESC_TYPE = is_opt ? DESC_OPTION : DESC_CONF; + const unsigned int DESC_TYPE = is_opt ? DESC_OPTION : DESC_CONF; while (*p) { @@ -586,8 +643,7 @@ process_opt_exclude_random (const char *s, const bool is_opt) { bool valid = false; unsigned int i; - if (exclude) - RELEASE_VAR (exclude); + RELEASE_VAR (exclude); exclude = xstrdup (s); STACK_VAR (exclude); for (i = 1; i < tables[GENERIC].count - 1; i++) /* skip color none and default */ @@ -601,7 +657,24 @@ process_opt_exclude_random (const char *s, const bool is_opt) } if (!valid) vfprintf_fail ("%s must be provided a plain color", - is_opt ? "--exlude-random switch" : "exclude-random conf option"); + is_opt ? "--exclude-random switch" : "exclude-random conf option"); +} + +static void +init_opts_vars (void) +{ + if (opts_set & OPT_ATTR_SET) + { + attr[0] = '\0'; /* Clear attr string to discard values from the config file. */ + process_opt_attr (opts_arg.attr, true); + } + if (opts_set & OPT_EXCLUDE_RANDOM_SET) + process_opt_exclude_random (opts_arg.exclude_random, true); + if (opts_set & OPT_OMIT_COLOR_EMPTY_SET) + omit_color_empty = true; + + free (opts_arg.attr); + free (opts_arg.exclude_random); } #define IS_SPACE(c) ((c) == ' ' || (c) == '\t') @@ -609,6 +682,7 @@ process_opt_exclude_random (const char *s, const bool is_opt) static void parse_conf (const char *conf_file, struct conf *config) { + unsigned int cnt = 0; char line[256 + 1]; FILE *conf; @@ -620,9 +694,12 @@ parse_conf (const char *conf_file, struct conf *config) char *assign, *comment, *opt, *value; char *p; + cnt++; + if ((p = strchr (line, '\r')) && *(p + 1) != '\n') + vfprintf_fail ("%s: CR ending of line %u is not supported, switch to CRLF/LF instead", conf_file, cnt); if (strlen (line) > (sizeof (line) - 2)) - vfprintf_fail ("%s: line exceeds maximum of %u characters", conf_file, (unsigned int)(sizeof (line) - 2)); - if ((p = strrchr (line, '\n'))) + vfprintf_fail ("%s: line %u exceeds maximum of %u characters", conf_file, cnt, (unsigned int)(sizeof (line) - 2)); + if ((p = strpbrk (line, "\n\r"))) *p = '\0'; /* NAME PARSING (start) */ p = line; @@ -635,9 +712,9 @@ parse_conf (const char *conf_file, struct conf *config) opt = p; if (!(assign = strchr (opt, '='))) /* check for = */ { - char *space; - if ((space = strchr (opt, ' '))) - *space = '\0'; + char *s; + if ((s = strpbrk (opt, "# "))) + *s = '\0'; vfprintf_fail (formats[FMT_CONF], conf_file, opt, "not followed by ="); } p = assign; @@ -682,29 +759,22 @@ parse_conf (const char *conf_file, struct conf *config) fclose (conf); } +#define ASSIGN_CONF(str,val) do { \ + free (str); \ + str = val; \ +} while (false) + static void assign_conf (const char *conf_file, struct conf *config, const char *cfg, char *val) { if (streq (cfg, "attr")) - { - free (config->attr); - config->attr = val; - } + ASSIGN_CONF (config->attr, val); else if (streq (cfg, "color")) - { - free (config->color); - config->color = val; - } + ASSIGN_CONF (config->color, val); else if (streq (cfg, "exclude-random")) - { - free (config->exclude_random); - config->exclude_random = val; - } + ASSIGN_CONF (config->exclude_random, val); else if (streq (cfg, "omit-color-empty")) - { - free (config->omit_color_empty); - config->omit_color_empty = val; - } + ASSIGN_CONF (config->omit_color_empty, val); else vfprintf_fail (formats[FMT_CONF], conf_file, cfg, "not recognized"); } @@ -723,7 +793,7 @@ init_conf_vars (const struct conf *config) else if (streq (config->omit_color_empty, "no")) omit_color_empty = false; else - vfprintf_fail ("omit-color-empty conf option is not valid"); + vfprintf_fail (formats[FMT_GENERIC], "omit-color-empty conf option is not valid"); } } @@ -743,6 +813,7 @@ print_help (void) }; const struct opt_data opts_data[] = { { "attr", NULL, "=ATTR1,ATTR2,..." }, + { "config", "c", "=PATH" }, { "exclude-random", NULL, "=COLOR" }, { "help", "h", NULL }, { "version", "V", NULL }, @@ -782,9 +853,12 @@ print_help (void) if (opt_data) { if (opt_data->short_opt) - printf ("\t\t-%s, --%s\n", opt_data->short_opt, opt->name); + printf ("\t\t-%s, --%s", opt_data->short_opt, opt->name); else - printf ("\t\t --%s%s\n", opt->name, opt_data->arg); + printf ("\t\t --%s", opt->name); + if (opt_data->arg) + printf ("%s", opt_data->arg); + printf ("\n"); } else printf ("\t\t --%s\n", opt->name); @@ -889,7 +963,7 @@ free_conf (struct conf *config) static void process_args (unsigned int arg_cnt, char **arg_strings, char *attr, const struct color **colors, const char **file, FILE **stream, struct conf *config) { - bool use_conf_color; + bool has_hyphen, use_conf_color; int ret; char *p; struct stat sb; @@ -904,23 +978,27 @@ process_args (unsigned int arg_cnt, char **arg_strings, char *attr, const struct assert (color_string != NULL); - if (streq (color_string, "-")) + has_hyphen = streq (color_string, "-"); + + if (has_hyphen) { if (file_string) vfprintf_fail (formats[FMT_GENERIC], "hyphen cannot be used as color string"); - else + else if (!config->color) vfprintf_fail (formats[FMT_GENERIC], "hyphen must be preceded by color string"); } - if ((ret = lstat (color_string, &sb)) == 0) /* exists */ + if (!has_hyphen && (ret = lstat (color_string, &sb)) == 0) /* exists */ /* Ensure that we don't fail if there's a file with one or more color names in its path. */ use_conf_color = skip_path_colors (color_string, file_string, &sb, !!config->color); + else if (has_hyphen) + use_conf_color = true; + else + use_conf_color = false; /* Use color from config file. */ - if (arg_cnt == 1 - && (access (color_string, F_OK) != -1) - && use_conf_color) + if (arg_cnt == 1 && use_conf_color) { file_string = color_string; color_string = config->color; @@ -1103,6 +1181,7 @@ gather_color_names (const char *color_string, char *attr, struct color_name **co break; case BACKGROUND: vfprintf_fail (formats[FMT_COLOR], tables[BACKGROUND].desc, color, "cannot be bold"); + break; default: /* never reached */ ABORT_TRACE (); } @@ -1664,6 +1743,20 @@ str_concat_wrap (const char *str1, const char *str2, const char *file, unsigned return str; } +static char * +expand_string (const char *str) +{ + char *s = NULL; + wordexp_t p; + + wordexp (str, &p, 0); + if (p.we_wordc >= 1) + s = xstrdup (p.we_wordv[0]); + wordfree (&p); + + return s; +} + static bool get_bytes_size (unsigned long bytes, struct bytes_size *bytes_size) {