Skip to content

Commit cc073fa

Browse files
committed
Big improvements
- Pass in C file instead of directory - Implement a "dry run" mode - Improve error handling
1 parent 89d70ef commit cc073fa

File tree

3 files changed

+121
-48
lines changed

3 files changed

+121
-48
lines changed

Bakefile.sh

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,10 @@ init() {
66
}
77

88
task.build() {
9-
bear -- gcc $CFLAGS -DCONFIG_DIR=$1 d.c -o ./d
9+
local dir=$1
10+
bake.assert_not_empty dir
11+
12+
bear -- gcc $CFLAGS -DCONFIG_DIR="\"$dir\"" d.c -o ./d
1013
}
1114

1215
task.run() {

README.md

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -18,21 +18,15 @@ A dotfile manager.
1818
## Summary
1919

2020
On a more serious note, `d` is your standard dotfile manager, with the twist
21-
that it can be configured using C, hopefully leveraging the ~~cursed~~ C
22-
preprocessor. It's meant to be small and only does what it says it does.
23-
24-
It approaches reconciliation using symlinks. It doesn't support templates or any
25-
of that nonsense.
26-
27-
A disclaimer for those curious to use `d`: currently, the code is dogshit. But
28-
I'm sure that many of you wouldn't mind one bit.
21+
that it can be configured using C, hopefully leveraging the ~~cursed~~ amazing C
22+
preprocessor.
2923

3024
### Usage
3125

3226
```bash
33-
git clone [email protected]:fox-incubating/d
27+
git clone [email protected]:hyperupcall-experiments-incubating/d
3428
cd ./d
35-
gcc -DCONFIG_DIR=\"$HOME/.dotfiles/config\" ./d.c -o ./d
29+
./bake build "$HOME/.dotfiles/config"
3630
ln -s "$PWD/d" "$HOME/.local/bin/d"
3731
```
3832

d.c

Lines changed: 113 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
#define _GNU_SOURCE
2+
23
#include <dlfcn.h>
34
#include <errno.h>
45
#include <libgen.h>
@@ -23,14 +24,13 @@ struct Failure {
2324
char *reason;
2425
char *info;
2526
};
26-
void fail(struct Failure failure) {
27+
void error(struct Failure failure) {
2728
#define RED "\033[0;31m"
2829
#define BLUE "\033[0;34m"
2930
#define CYAN "\033[0;36m"
3031
#define YELLOW "\033[0;33m"
3132
#define MAGENTA "\033[0;35m"
3233
#define RESET "\033[0m"
33-
3434
fprintf(
3535
stderr,
3636
"Application reached a " RED "terminating failure" RESET "...\n" BLUE "Operation: " RESET "Failed to %s\n" CYAN
@@ -40,19 +40,30 @@ void fail(struct Failure failure) {
4040
if (failure.info != NULL) {
4141
fprintf(stderr, YELLOW "Extra Information: " RESET "%s\n", failure.info);
4242
}
43-
43+
}
44+
void fail(struct Failure failure) {
45+
error(failure);
4446
exit(EXIT_FAILURE);
4547
}
46-
void deploy(char *, char *, bool);
48+
void deploy(char *, char *, bool, bool);
4749

4850
int main(int argc, char *argv[]) {
49-
static bool debug = false;
51+
static bool is_debug = false;
52+
static bool is_dry_run = false;
53+
54+
char *config_dir = NULL;
55+
char *config_file_copy = NULL;
56+
char *lib_path = NULL;
57+
char *cmd = NULL;
58+
void *handle = NULL;
59+
5060
if (getenv("DEBUG") != NULL) {
51-
debug = true;
61+
is_debug = true;
5262
}
5363

5464
char *help_menu = "d: A dotfile manager.\n"
55-
"Commands: <deploy | undeploy | print | compile>";
65+
"Commands: <deploy | undeploy | print | compile>\n"
66+
"Flags: --dry (for deploy/undeploy)";
5667

5768
enum Command {
5869
CommandNone,
@@ -68,6 +79,7 @@ int main(int argc, char *argv[]) {
6879
.reason = "Must pass a subcommand",
6980
});
7081
}
82+
7183
if (strcmp(argv[1], "deploy") == 0) {
7284
command = CommandDeploy;
7385
} else if (strcmp(argv[1], "undeploy") == 0) {
@@ -91,30 +103,60 @@ int main(int argc, char *argv[]) {
91103
});
92104
}
93105

106+
if ((command == CommandDeploy || command == CommandUndeploy) && argc > 2) {
107+
if (strcmp(argv[2], "--dry") == 0) {
108+
is_dry_run = true;
109+
}
110+
}
111+
112+
config_dir = strdup(CONFIG_DIR);
113+
if (!config_dir) {
114+
error((struct Failure){.operation = "duplicate config dir", .reason = strerror(errno)});
115+
goto error;
116+
}
117+
char *dir = dirname(config_dir);
118+
119+
config_file_copy = strdup(CONFIG_DIR);
120+
if (!config_file_copy) {
121+
error((struct Failure){.operation = "duplicate config file", .reason = strerror(errno)});
122+
goto error;
123+
}
124+
125+
if (asprintf(&lib_path, "%s/libdotfiles.so", dir) == -1) {
126+
error((struct Failure){.operation = "format library path with asprintf", .reason = strerror(errno)});
127+
goto error;
128+
}
129+
130+
char *filename = basename(config_file_copy);
131+
94132
if (command == CommandCompile) {
95-
char *cmd = "gcc -Werror=unused-variable -g -fPIC -c " CONFIG_DIR "/dotfiles.c -o " CONFIG_DIR
96-
"/dotfiles.o && gcc -shared -o " CONFIG_DIR "/libdotfiles.so " CONFIG_DIR "/dotfiles.o";
133+
if (asprintf(&cmd, "gcc -g -fPIC -c %s -o %s/%s.o && gcc -shared -o %s/libdotfiles.so %s/%s.o",
134+
CONFIG_DIR, dir, filename, dir, dir, filename) == -1) {
135+
error((struct Failure){.operation = "format command with asprintf", .reason = strerror(errno)});
136+
goto error;
137+
}
138+
97139
if (system(cmd) == -1) {
98-
fail((struct Failure){.operation = "system", .reason = strerror(errno)});
140+
error((struct Failure){.operation = "system", .reason = strerror(errno)});
141+
goto error;
99142
};
100143

101144
printf("Compiled configuration file\n");
102-
103-
return EXIT_SUCCESS;
145+
goto cleanup;
104146
}
105147

106-
void *handle = dlopen(CONFIG_DIR "/libdotfiles.so", RTLD_LAZY);
148+
handle = dlopen(lib_path, RTLD_LAZY);
107149
if (!handle) {
108150
fprintf(stderr, "%s\n", dlerror());
109-
exit(EXIT_FAILURE);
151+
goto error;
110152
}
111153
dlerror();
112154

113155
struct Entry(**configuration) = (struct Entry **)dlsym(handle, "configuration");
114-
char *error = dlerror();
115-
if (error != NULL) {
116-
fprintf(stderr, "%s\n", error);
117-
exit(1);
156+
char *dl_error = dlerror();
157+
if (dl_error != NULL) {
158+
fprintf(stderr, "%s\n", dl_error);
159+
goto error;
118160
}
119161

120162
if (command == CommandPrint)
@@ -147,14 +189,14 @@ int main(int argc, char *argv[]) {
147189
char *home = getenv("HOME");
148190
if (home == NULL) {
149191
perror("getenv");
150-
exit(1);
192+
goto error;
151193
}
152194
if (command == CommandDeploy) {
153195
char source_path[PATH_MAX];
154196
char destination_path[PATH_MAX];
155197
snprintf(source_path, PATH_MAX, "%s", entry.source);
156198
snprintf(destination_path, PATH_MAX, "%s", entry.destination);
157-
deploy(source_path, destination_path, debug);
199+
deploy(source_path, destination_path, is_debug, is_dry_run);
158200
} else if (command == CommandUndeploy) {
159201
char source_path[PATH_MAX];
160202
char destination_path[PATH_MAX];
@@ -163,22 +205,41 @@ int main(int argc, char *argv[]) {
163205
if (destination_path[strlen(destination_path) - 1] == '/') {
164206
destination_path[strlen(destination_path) - 1] = '\0';
165207
}
166-
if (unlink(destination_path) == -1) {
167-
if (errno != ENOENT) {
168-
fprintf(stderr, "Failed to unlink \"%s\"\n", destination_path);
169-
perror("unlink");
170-
exit(1);
208+
if (is_dry_run) {
209+
printf("[DRY RUN] Would unlink: %s\n", destination_path);
210+
} else {
211+
if (unlink(destination_path) == -1) {
212+
if (errno != ENOENT) {
213+
fprintf(stderr, "Failed to unlink \"%s\"\n", destination_path);
214+
perror("unlink");
215+
goto error;
216+
}
171217
}
172218
}
173219
}
174220
}
175221
}
176222
if (command == CommandPrint)
177223
printf("]\n");
178-
dlclose(handle);
224+
225+
cleanup:
226+
if (handle) dlclose(handle);
227+
if (lib_path) free(lib_path);
228+
if (config_file_copy) free(config_file_copy);
229+
if (config_dir) free(config_dir);
230+
if (cmd) free(cmd);
231+
return 1;
232+
233+
error:
234+
if (handle) dlclose(handle);
235+
if (lib_path) free(lib_path);
236+
if (config_file_copy) free(config_file_copy);
237+
if (config_dir) free(config_dir);
238+
if (cmd) free(cmd);
239+
exit(1);
179240
}
180241

181-
void deploy(char *source_path, char *destination_path, bool debug) {
242+
void deploy(char *source_path, char *destination_path, bool debug, bool dry_run) {
182243
// Check trailing slash.
183244
{
184245
if (destination_path[strlen(destination_path) - 1] == '/') {
@@ -246,10 +307,15 @@ void deploy(char *source_path, char *destination_path, bool debug) {
246307
perror("stat");
247308
exit(1);
248309
}
249-
printf("Creating directory for: %s\n", dir);
250-
if (mkdir(dir, 0755) == -1) {
251-
perror("mkdir");
252-
exit(1);
310+
311+
if (dry_run) {
312+
printf("[DRY RUN] Would create directory: %s\n", dir);
313+
} else {
314+
printf("Creating directory for: %s\n", dir);
315+
if (mkdir(dir, 0755) == -1) {
316+
perror("mkdir");
317+
exit(1);
318+
}
253319
}
254320
}
255321
free(dir);
@@ -259,6 +325,7 @@ void deploy(char *source_path, char *destination_path, bool debug) {
259325
free(dir);
260326
exit(1);
261327
end:
328+
;
262329
}
263330

264331
struct stat st = {0};
@@ -274,15 +341,24 @@ void deploy(char *source_path, char *destination_path, bool debug) {
274341

275342
if (!exists || S_ISLNK(st.st_mode)) {
276343
if (S_ISLNK(st.st_mode)) {
277-
if (unlink(destination_path) == -1) {
278-
perror("unlink");
344+
if (dry_run) {
345+
printf("[DRY RUN] Would unlink existing symlink: %s\n", destination_path);
346+
} else {
347+
if (unlink(destination_path) == -1) {
348+
perror("unlink");
349+
}
279350
}
280351
}
281-
if (symlink(source_path, destination_path) == -1) {
282-
perror("symlink");
283-
exit(1);
352+
353+
if (dry_run) {
354+
printf("[DRY RUN] Would symlink %s to %s\n", source_path, destination_path);
355+
} else {
356+
if (symlink(source_path, destination_path) == -1) {
357+
perror("symlink");
358+
exit(1);
359+
}
360+
printf("Symlinked %s to %s\n", source_path, destination_path);
284361
}
285-
printf("Symlinked %s to %s\n", source_path, destination_path);
286362
} else {
287363
printf("SKIPPING: %s\n", destination_path);
288364
}

0 commit comments

Comments
 (0)