@@ -53,6 +53,40 @@ static bool is_absolute_path(const char *path) {
5353 return path && path [0 ] == '/' ;
5454}
5555
56+ static int resolve_path (const char * input , void * output ) {
57+ char * * out_path = output ;
58+ if (!input ) {
59+ return - EINVAL ;
60+ }
61+
62+ if (input [0 ] == '~' ) {
63+ if (input [1 ] != '\0' && input [1 ] != '/' ) {
64+ return - EINVAL ;
65+ }
66+ const char * home = getenv ("HOME" );
67+ if (!home ) {
68+ return - EINVAL ;
69+ }
70+ size_t len = strlen (home ) + strlen (input );
71+ char * expanded = malloc (len );
72+ expect (expanded );
73+ snprintf_safe (expanded , len , "%s%s" , home , input + 1 );
74+ * out_path = expanded ;
75+ } else {
76+ char * dup = strdup (input );
77+ expect (dup );
78+ * out_path = dup ;
79+ }
80+
81+ if (!is_absolute_path (* out_path )) {
82+ free (* out_path );
83+ * out_path = NULL ;
84+ return - EINVAL ;
85+ }
86+
87+ return 0 ;
88+ }
89+
5690/**
5791 * This whole section consists of conversion functions to go from a string in
5892 * the config file to the type we expect for `struct Config`.
@@ -105,13 +139,7 @@ static int convert_cm_dir(const char *str, void *output) {
105139 if (!str ) {
106140 str = get_runtime_directory ();
107141 }
108- if (!is_absolute_path (str )) {
109- return - EINVAL ;
110- }
111- char * rtd = strdup (str );
112- expect (rtd );
113- * (char * * )output = rtd ;
114- return 0 ;
142+ return resolve_path (str , output );
115143}
116144
117145static int _nonnull_ convert_launcher (const char * str , void * output ) {
@@ -175,29 +203,39 @@ static int get_config_file(char *config_path, size_t config_path_len) {
175203 const char * cm_config = getenv ("CM_CONFIG" );
176204 const char * xdg_config_home = getenv ("XDG_CONFIG_HOME" );
177205 const char * home = getenv ("HOME" );
206+ _drop_ (free ) char * resolved = NULL ;
178207
179208 if (cm_config ) {
180- if (!is_absolute_path (cm_config )) {
181- fprintf (stderr , "Error parsing config file path\n" );
182- return - EINVAL ;
209+ int ret = resolve_path (cm_config , & resolved );
210+ if (ret != 0 ) {
211+ fprintf (stderr ,
212+ "Invalid config path from $CM_CONFIG: expected absolute "
213+ "path or leading ~\n" );
214+ return ret ;
183215 }
184- snprintf_safe (config_path , config_path_len , "%s" , cm_config );
216+ snprintf_safe (config_path , config_path_len , "%s" , resolved );
185217 } else if (xdg_config_home ) {
186- if (!is_absolute_path (xdg_config_home )) {
187- fprintf (stderr , "Error parsing config file path\n" );
188- return - EINVAL ;
218+ int ret = resolve_path (xdg_config_home , & resolved );
219+ if (ret != 0 ) {
220+ fprintf (stderr ,
221+ "Invalid config path from $XDG_CONFIG_HOME: expected "
222+ "absolute path or leading ~\n" );
223+ return ret ;
189224 }
190225 snprintf_safe (config_path , config_path_len , "%s/clipmenu/clipmenu.conf" ,
191- xdg_config_home );
226+ resolved );
192227 } else {
193228 die_on (!home ,
194229 "None of $CM_CONFIG, $XDG_CONFIG_HOME, or $HOME is set\n" );
195- if (!is_absolute_path (home )) {
196- fprintf (stderr , "Error parsing config file path\n" );
197- return - EINVAL ;
230+ int ret = resolve_path (home , & resolved );
231+ if (ret != 0 ) {
232+ fprintf (stderr ,
233+ "Invalid config path from $HOME: expected absolute path or "
234+ "leading ~\n" );
235+ return ret ;
198236 }
199237 snprintf_safe (config_path , config_path_len ,
200- "%s/.config/clipmenu/clipmenu.conf" , home );
238+ "%s/.config/clipmenu/clipmenu.conf" , resolved );
201239 }
202240
203241 return 0 ;
0 commit comments