NEWS: Welcome to my new homepage! <3

main.c - rc - A config file management utility

rc

A config file management utility
git clone git://192.168.2.2/rc
Log | Files | Refs | README

main.c (6108B)


      1 #include <stdio.h>
      2 #include <stdlib.h>
      3 #include <unistd.h>
      4 #include <getopt.h>
      5 #include <string.h>
      6 #include <ctype.h>
      7 #include <regex.h>
      8 
      9 #include <libstr.h>
     10 #include <toml.h>
     11 
     12 typedef struct {
     13   char *name;
     14   char *path;
     15 } program_t;
     16 
     17 typedef struct  {
     18   char *editor;
     19   program_t *programs;
     20   int programs_len;
     21 } config_t;
     22 
     23 /** Loads the config file. */
     24 config_t load_config(char *filename) {
     25   FILE *fp = fopen(filename, "r");
     26   char error[200];
     27   toml_table_t *config = toml_parse_file(fp, error, sizeof(error));
     28   fclose(fp);
     29   toml_datum_t editor_prop = toml_string_in(config, "editor");
     30   char *editor = malloc(sizeof(char) * strlen(editor_prop.u.s));
     31   strcpy(editor, editor_prop.u.s);
     32   toml_table_t *programs_prop = toml_table_in(config, "programs");
     33   int programs_len = toml_table_nkval(programs_prop);
     34   program_t *programs = malloc(sizeof(program_t) * programs_len);
     35   for (int i = 0; i < programs_len; i++) {
     36     const char *key = toml_key_in(programs_prop, i);
     37     toml_datum_t value = toml_string_in(programs_prop, key);
     38     programs[i].name = malloc(sizeof(char) * strlen(key));
     39     programs[i].path = malloc(sizeof(char) * strlen(value.u.s));
     40     strcpy(programs[i].name, key);
     41     strcpy(programs[i].path, value.u.s);
     42   }
     43   toml_free(config);
     44   return (config_t) {editor, programs, programs_len};
     45 }
     46 
     47 /** Frees the config file. */
     48 void free_config(config_t config) {
     49   free(config.editor);
     50   for (int i = 0; i < config.programs_len; i++) {
     51     free(config.programs[i].name);
     52     free(config.programs[i].path);
     53   }
     54   free(config.programs);
     55 }
     56 
     57 /** Finds a program in the config file. Returns `NULL` if nothing was found. */
     58 program_t *get_program(config_t config, char *name) {
     59   for (int i = 0; i < config.programs_len; i++) {
     60     if (strcmp(name, config.programs[i].name) == 0) {
     61       return &config.programs[i];
     62     }
     63   }
     64   return NULL;
     65 }
     66 
     67 /** Evaluates all environment variables contained in the path. */
     68 char *eval_path(char *path) {
     69   char *str = NULL;
     70   char *slice = path;
     71   regex_t regex;
     72   regmatch_t matches[1];
     73   if (regcomp(&regex, "\\$[A-Z_]+", REG_EXTENDED) == 0) {
     74     int i = 0;
     75     while (1) {
     76       if (regexec(&regex, slice, 1, matches, REG_EXTENDED)) {
     77         break;
     78       }
     79       regoff_t len = matches[0].rm_eo - matches[0].rm_so;
     80       char *ptr = slice + matches[0].rm_so;
     81       if (matches[0].rm_so > 0) {
     82         str_append_len(&str, slice, matches[0].rm_so);
     83       }
     84       char *var = malloc(sizeof(char) * (len - 1));
     85       memcpy(var, ptr + 1, len - 1);
     86       char *value = getenv(var);
     87       if (value != NULL) {
     88         str_append_len(&str, value, strlen(value));
     89       }
     90       else {
     91         str_append_len(&str, ptr, len);
     92       }
     93       free(var);
     94       slice += matches[0].rm_eo;
     95       i++;
     96     }
     97     if (i == 0) {
     98       str_append_len(&str, slice, strlen(slice));
     99     }
    100     else if (strlen(slice) < strlen(path)) {
    101       str_append_len(&str, slice, strlen(slice));
    102     }
    103   }
    104   return str;
    105 }
    106 
    107 /** Opens the program config file with the corresponding editor. */
    108 void open_program_config(config_t config, program_t *program) {
    109   char *path = eval_path(program->path);
    110   int command_len = strlen(config.editor) + strlen(path) + 2;
    111   char command[command_len];
    112   snprintf(command, command_len, "%s %s", config.editor, path);
    113   system(command);
    114   free(path);
    115 }
    116 
    117 /** Prints the program path. */
    118 void show_path(program_t *program) {
    119   char *path = eval_path(program->path);
    120   printf("%s", path);
    121   free(path);
    122 }
    123 
    124 /** Prints a list of programs from the config file. */
    125 void show_list(config_t config) {
    126   int width = 4;
    127   for (int i = 0; i < config.programs_len; i++) {
    128     int len = strlen(config.programs[i].name);
    129     if (len > width) {
    130       width = len;
    131     }
    132   }
    133   printf("%-*s  PATH\n", width, "NAME");
    134   for (int i = 0; i < config.programs_len; i++) {
    135     printf("%-*s  %s\n", width, config.programs[i].name, config.programs[i].path);
    136   }
    137 }
    138 
    139 /** Prints the help dialog. */
    140 void show_help(void) {
    141   printf(
    142     "rc - A config file management utility\n\n"
    143     "rc is a tool which allows you to manage your config files without\n"
    144     "the hassle of remembering the location of all your files.\n\n"
    145     "Use \"rc rc\" to manage your configuration.\n\n"
    146     "For more information visit: https://aest.me/git/rc\n\n"
    147     "USAGE:\n"
    148     "  rc [FLAGS] [program]\n\n"
    149     "ARGS:\n"
    150     "  <program>     The program you want to edit.\n\n"
    151     "FLAGS:\n"
    152     "  -p    Get config path for a specific program.\n"
    153     "  -l    List all config files.\n"
    154     "  -h    Show help description.\n"
    155   );
    156 }
    157 
    158 int main(int argc, char *argv[]) {
    159   int c;
    160   int path_flag = 0;
    161   int list_flag = 0;
    162   int help_flag = 0;
    163   opterr = 0;
    164   while ((c = getopt(argc, argv, "plh")) != -1) {
    165     switch (c) {
    166       case 'p':
    167         path_flag = 1;
    168         break;
    169       case 'l':
    170         list_flag = 1;
    171         break;
    172       case 'h':
    173         help_flag = 1;
    174         break;
    175       case '?':
    176         if (isprint(optopt)) {
    177           fprintf(stderr, "ERROR: Unknown option '-%c'!\n", optopt);
    178         }
    179         else {
    180           fprintf(stderr, "ERROR: Unknown option character '\\x%x'!\n", optopt);
    181         }
    182         exit(EXIT_FAILURE);
    183       default:
    184         exit(EXIT_FAILURE);
    185     }
    186   }
    187   if (help_flag) {
    188     show_help();
    189     exit(EXIT_SUCCESS);
    190   }
    191   char *config_path = NULL;
    192   str_append(&config_path, getenv("HOME"));
    193   str_append(&config_path, "/.config/rc/config.toml");
    194   config_t config = load_config(config_path);
    195   free(config_path);
    196   if (list_flag) {
    197     show_list(config);
    198     free_config(config);
    199     exit(EXIT_SUCCESS);
    200   }
    201   if (optind == argc) {
    202     if (path_flag) {
    203       fprintf(stderr, "ERROR: No program was specified!\n");
    204       exit(EXIT_FAILURE);
    205     }
    206     show_help();
    207     exit(EXIT_SUCCESS);
    208   }
    209   char *name = argv[optind];
    210   program_t *program = get_program(config, name);
    211   if (program == NULL) {
    212     fprintf(stderr, "ERROR: '%s' is cannot be found!\n", name);
    213     exit(EXIT_FAILURE);
    214   }
    215   if (path_flag) {
    216     show_path(program);
    217     free_config(config);
    218     exit(EXIT_SUCCESS);
    219   }
    220   open_program_config(config, program);
    221   free_config(config);
    222   return 0;
    223 }