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(®ex, "\\$[A-Z_]+", REG_EXTENDED) == 0) { 74 int i = 0; 75 while (1) { 76 if (regexec(®ex, 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 }