commit 83228d9bf7807a5fc538c5769f4f7311ac37d024
Author: typable <contact@typable.dev>
Date: Tue, 24 Sep 2024 10:52:15 +0200
feat: Initial commit
Diffstat:
A | Makefile | | | 21 | +++++++++++++++++++++ |
A | README.md | | | 5 | +++++ |
A | src/main.c | | | 223 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
3 files changed, 249 insertions(+), 0 deletions(-)
diff --git a/Makefile b/Makefile
@@ -0,0 +1,21 @@
+CC=cc
+CFLAGS=-Wall -Wextra -Werror -pedantic
+DEBUG=-g -rdynamic
+LIBS=-lstr -ltoml
+
+default: build
+
+build: src/*.c
+ $(CC) -o rc $(CFLAGS) $(LIBS) $^
+
+run: build
+ ./rc
+
+install: build
+ sudo cp ./rc /usr/local/bin/rc
+
+uninstall:
+ sudo rm /usr/local/bin/rc
+
+clean:
+ rm ./rc
diff --git a/README.md b/README.md
@@ -0,0 +1,5 @@
+# rc
+
+An easy-to-use terminal library
+
+rc is a tool which allows you to manage your config files without the hassle of remembering the location of all your files.
diff --git a/src/main.c b/src/main.c
@@ -0,0 +1,223 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <string.h>
+#include <ctype.h>
+#include <regex.h>
+
+#include <libstr.h>
+#include <toml.h>
+
+typedef struct {
+ char *name;
+ char *path;
+} program_t;
+
+typedef struct {
+ char *editor;
+ program_t *programs;
+ int programs_len;
+} config_t;
+
+/** Loads the config file. */
+config_t load_config(char *filename) {
+ FILE *fp = fopen(filename, "r");
+ char error[200];
+ toml_table_t *config = toml_parse_file(fp, error, sizeof(error));
+ fclose(fp);
+ toml_datum_t editor_prop = toml_string_in(config, "editor");
+ char *editor = malloc(sizeof(char) * strlen(editor_prop.u.s));
+ strcpy(editor, editor_prop.u.s);
+ toml_table_t *programs_prop = toml_table_in(config, "programs");
+ int programs_len = toml_table_nkval(programs_prop);
+ program_t *programs = malloc(sizeof(program_t) * programs_len);
+ for (int i = 0; i < programs_len; i++) {
+ const char *key = toml_key_in(programs_prop, i);
+ toml_datum_t value = toml_string_in(programs_prop, key);
+ programs[i].name = malloc(sizeof(char) * strlen(key));
+ programs[i].path = malloc(sizeof(char) * strlen(value.u.s));
+ strcpy(programs[i].name, key);
+ strcpy(programs[i].path, value.u.s);
+ }
+ toml_free(config);
+ return (config_t) {editor, programs, programs_len};
+}
+
+/** Frees the config file. */
+void free_config(config_t config) {
+ free(config.editor);
+ for (int i = 0; i < config.programs_len; i++) {
+ free(config.programs[i].name);
+ free(config.programs[i].path);
+ }
+ free(config.programs);
+}
+
+/** Finds a program in the config file. Returns `NULL` if nothing was found. */
+program_t *get_program(config_t config, char *name) {
+ for (int i = 0; i < config.programs_len; i++) {
+ if (strcmp(name, config.programs[i].name) == 0) {
+ return &config.programs[i];
+ }
+ }
+ return NULL;
+}
+
+/** Evaluates all environment variables contained in the path. */
+char *eval_path(char *path) {
+ char *str = NULL;
+ char *slice = path;
+ regex_t regex;
+ regmatch_t matches[1];
+ if (regcomp(®ex, "\\$[A-Z_]+", REG_EXTENDED) == 0) {
+ int i = 0;
+ while (1) {
+ if (regexec(®ex, slice, 1, matches, REG_EXTENDED)) {
+ break;
+ }
+ regoff_t len = matches[0].rm_eo - matches[0].rm_so;
+ char *ptr = slice + matches[0].rm_so;
+ if (matches[0].rm_so > 0) {
+ str_append_len(&str, slice, matches[0].rm_so);
+ }
+ char *var = malloc(sizeof(char) * (len - 1));
+ memcpy(var, ptr + 1, len - 1);
+ char *value = getenv(var);
+ if (value != NULL) {
+ str_append_len(&str, value, strlen(value));
+ }
+ else {
+ str_append_len(&str, ptr, len);
+ }
+ free(var);
+ slice += matches[0].rm_eo;
+ i++;
+ }
+ if (i == 0) {
+ str_append_len(&str, slice, strlen(slice));
+ }
+ else if (strlen(slice) < strlen(path)) {
+ str_append_len(&str, slice, strlen(slice));
+ }
+ }
+ return str;
+}
+
+/** Opens the program config file with the corresponding editor. */
+void open_program_config(config_t config, program_t *program) {
+ char *path = eval_path(program->path);
+ int command_len = strlen(config.editor) + strlen(path) + 2;
+ char command[command_len];
+ snprintf(command, command_len, "%s %s", config.editor, path);
+ system(command);
+ free(path);
+}
+
+/** Prints the program path. */
+void show_path(program_t *program) {
+ char *path = eval_path(program->path);
+ printf("%s", path);
+ free(path);
+}
+
+/** Prints a list of programs from the config file. */
+void show_list(config_t config) {
+ int width = 4;
+ for (int i = 0; i < config.programs_len; i++) {
+ int len = strlen(config.programs[i].name);
+ if (len > width) {
+ width = len;
+ }
+ }
+ printf("%-*s PATH\n", width, "NAME");
+ for (int i = 0; i < config.programs_len; i++) {
+ printf("%-*s %s\n", width, config.programs[i].name, config.programs[i].path);
+ }
+}
+
+/** Prints the help dialog. */
+void show_help(void) {
+ printf(
+ "rc - A config file management utility\n\n"
+ "rc is a tool which allows you to manage your config files without\n"
+ "the hassle of remembering the location of all your files.\n\n"
+ "Use \"rc rc\" to manage your configuration.\n\n"
+ "For more information visit: https://aest.me/git/rc\n\n"
+ "USAGE:\n"
+ " rc [FLAGS] [program]\n\n"
+ "ARGS:\n"
+ " <program> The program you want to edit.\n\n"
+ "FLAGS:\n"
+ " -p Get config path for a specific program.\n"
+ " -l List all config files.\n"
+ " -h Show help description.\n"
+ );
+}
+
+int main(int argc, char *argv[]) {
+ int c;
+ int path_flag = 0;
+ int list_flag = 0;
+ int help_flag = 0;
+ opterr = 0;
+ while ((c = getopt(argc, argv, "plh")) != -1) {
+ switch (c) {
+ case 'p':
+ path_flag = 1;
+ break;
+ case 'l':
+ list_flag = 1;
+ break;
+ case 'h':
+ help_flag = 1;
+ break;
+ case '?':
+ if (isprint(optopt)) {
+ fprintf(stderr, "ERROR: Unknown option '-%c'!\n", optopt);
+ }
+ else {
+ fprintf(stderr, "ERROR: Unknown option character '\\x%x'!\n", optopt);
+ }
+ exit(EXIT_FAILURE);
+ default:
+ exit(EXIT_FAILURE);
+ }
+ }
+ if (help_flag) {
+ show_help();
+ exit(EXIT_SUCCESS);
+ }
+ char *config_path = NULL;
+ str_append(&config_path, getenv("HOME"));
+ str_append(&config_path, "/.config/rc/config.toml");
+ config_t config = load_config(config_path);
+ free(config_path);
+ if (list_flag) {
+ show_list(config);
+ free_config(config);
+ exit(EXIT_SUCCESS);
+ }
+ if (optind == argc) {
+ if (path_flag) {
+ fprintf(stderr, "ERROR: No program was specified!\n");
+ exit(EXIT_FAILURE);
+ }
+ show_help();
+ exit(EXIT_SUCCESS);
+ }
+ char *name = argv[optind];
+ program_t *program = get_program(config, name);
+ if (program == NULL) {
+ fprintf(stderr, "ERROR: '%s' is cannot be found!\n", name);
+ exit(EXIT_FAILURE);
+ }
+ if (path_flag) {
+ show_path(program);
+ free_config(config);
+ exit(EXIT_SUCCESS);
+ }
+ open_program_config(config, program);
+ free_config(config);
+ return 0;
+}