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
4850int 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