1 /* Lepton EDA library
2  * Copyright (C) 1998-2010 Ales Hvezda
3  * Copyright (C) 1998-2016 gEDA Contributors
4  * Copyright (C) 2016 Peter Brett <peter@peter-b.co.uk>
5  * Copyright (C) 2017-2021 Lepton EDA Contributors
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20  */
21 /*! \file g_rc.c
22  *  \brief Execute Scheme initialisation files.
23  *
24  * Contains functions to open, parse and manage Scheme initialisation
25  * (RC) files.
26  */
27 
28 #include <config.h>
29 
30 #include <errno.h>
31 #include <stdio.h>
32 #include <sys/stat.h>
33 #include <ctype.h>
34 #ifdef HAVE_STRING_H
35 #include <string.h>
36 #endif
37 #ifdef HAVE_STDLIB_H
38 #include <stdlib.h>
39 #endif
40 #ifdef HAVE_UNISTD_H
41 #include <unistd.h>
42 #endif
43 
44 #include "liblepton_priv.h"
45 #include "libleptonguile_priv.h"
46 
47 
48 /*! \brief Mark an RC file as loaded.
49  * \par Function Description
50  * If the Scheme initialisation file \a filename has not already been
51  * loaded, mark it as loaded and return TRUE, storing \a filename in
52  * \a toplevel (\a filename should not subsequently be freed).
53  * Otherwise, return FALSE, and set \a err appropriately.
54  *
55  * \note Should only be called by g_rc_parse_file().
56  *
57  * \param toplevel  The current #LeptonToplevel structure.
58  * \param filename  The RC file name to test.
59  * \param err       Return location for errors, or NULL.
60  * \return TRUE if \a filename not already loaded, FALSE otherwise.
61  */
62 static gboolean
g_rc_try_mark_read(LeptonToplevel * toplevel,gchar * filename,GError ** err)63 g_rc_try_mark_read (LeptonToplevel *toplevel,
64                     gchar *filename,
65                     GError **err)
66 {
67   GList *found = NULL;
68   g_return_val_if_fail ((toplevel != NULL), FALSE);
69   g_return_val_if_fail ((filename != NULL), FALSE);
70 
71   /* Test if marked read already */
72   found = g_list_find_custom (toplevel->RC_list, filename,
73                               (GCompareFunc) strcmp);
74   if (found != NULL) {
75     g_set_error (err, EDA_ERROR, EDA_ERROR_RC_TWICE,
76                  _("RC file already loaded"));
77     return FALSE;
78   }
79 
80   toplevel->RC_list = g_list_append (toplevel->RC_list, filename);
81   /* N.b. don't free name_norm here; it's stored in the LeptonToplevel. */
82   return TRUE;
83 }
84 
85 /*! \brief Load an RC file.
86  * \par Function Description
87  * Load and run the Scheme initialisation file \a rcfile, reporting
88  * errors via \a err.
89  *
90  * \param toplevel  The current #LeptonToplevel structure.
91  * \param rcfile    The filename of the RC file to load.
92  * \param cfg       The configuration context to use while loading.
93  * \param err       Return location for errors, or NULL;
94  * \return TRUE on success, FALSE on failure.
95  */
96 static gboolean
g_rc_parse_file(LeptonToplevel * toplevel,const gchar * rcfile,EdaConfig * cfg,GError ** err)97 g_rc_parse_file (LeptonToplevel *toplevel,
98                  const gchar *rcfile,
99                  EdaConfig *cfg,
100                  GError **err)
101 {
102   gchar *name_norm = NULL;
103   GError *tmp_err = NULL;
104   gboolean status = FALSE;
105   g_return_val_if_fail ((toplevel != NULL), FALSE);
106   g_return_val_if_fail ((rcfile != NULL), FALSE);
107 
108   /* If no configuration file was specified, get the default
109    * configuration file for the rc file. */
110   if (cfg == NULL) {
111     cfg = eda_config_get_context_for_path (rcfile);
112   }
113   /* If the configuration wasn't loaded yet, attempt to load
114    * it. Config loading is on a best-effort basis; if we fail, just
115    * print a warning. */
116   if (!eda_config_is_loaded (cfg)) {
117     eda_config_load (cfg, &tmp_err);
118     if (tmp_err != NULL &&
119         !g_error_matches (tmp_err, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
120       g_warning (_("Failed to load config from '%1$s': %2$s\n"),
121                  eda_config_get_filename (cfg), tmp_err->message);
122     g_clear_error (&tmp_err);
123   }
124 
125   /* Normalise filename */
126   name_norm = f_normalize_filename (rcfile, err);
127   if (name_norm == NULL) return FALSE;
128 
129   /* Attempt to load the RC file, if it hasn't been loaded already.
130    * If g_rc_try_mark_read() succeeds, it stores name_norm in
131    * toplevel, so we *don't* free it. */
132   status = (g_rc_try_mark_read (toplevel, name_norm, &tmp_err)
133             && g_read_file (toplevel, name_norm, &tmp_err));
134 
135   if (status) {
136     g_message (_("Loaded RC file [%1$s]"), name_norm);
137   } else {
138     /* Copy tmp_err into err, with a prefixed message. */
139     g_propagate_prefixed_error (err, tmp_err,
140                                 _("Failed to load RC file [%1$s]: "),
141                                 name_norm);
142     g_free (name_norm);
143   }
144   return status;
145 }
146 
147 /*! \brief Load a system RC file.
148  * \par Function Description
149  * Attempts to load and run the system Scheme initialisation file with
150  * basename \a rcname.  The string "system-" is prefixed to \a rcname.
151  * If \a rcname is NULL, the default value of "gafrc" is used.
152  *
153  * \param toplevel  The current #LeptonToplevel structure.
154  * \param rcname    The basename of the RC file to load, or NULL.
155  * \param err       Return location for errors, or NULL.
156  * \return TRUE on success, FALSE on failure.
157  */
158 gboolean
g_rc_parse_system(LeptonToplevel * toplevel,const gchar * rcname,GError ** err)159 g_rc_parse_system (LeptonToplevel *toplevel,
160                    const gchar *rcname,
161                    GError **err)
162 {
163   gchar *sysname = NULL;
164   gchar *rcfile = NULL;
165   gboolean status = TRUE;
166   const gchar * const * sys_dirs = eda_get_system_config_dirs();
167   EdaConfig *cfg = eda_config_get_system_context();
168 
169   /* Default to gafrc */
170   rcname = (rcname != NULL) ? rcname : "gafrc";
171 
172   sysname = g_strdup_printf ("system-%s", rcname);
173   for (gint i = 0; sys_dirs[i]; ++i)
174   {
175     rcfile = g_build_filename (sys_dirs[i], sysname, NULL);
176     if (g_file_test(rcfile, G_FILE_TEST_IS_REGULAR))
177     {
178       break;
179     }
180     g_free(rcfile);
181     rcfile = NULL;
182   }
183 
184   if (rcfile)
185   {
186     status = g_rc_parse_file (toplevel, rcfile, cfg, err);
187   }
188 
189   g_free (rcfile);
190   g_free (sysname);
191   return status;
192 }
193 
194 /*! \brief Load a user RC file.
195  * \par Function Description
196  * Attempts to load the user Scheme initialisation file with basename
197  * \a rcname.  If \a rcname is NULL, the default value of "gafrc" is
198  * used.
199  *
200  * \param toplevel  The current #LeptonToplevel structure.
201  * \param rcname    The basename of the RC file to load, or NULL.
202  * \param err       Return location for errors, or NULL.
203  * \return TRUE on success, FALSE on failure.
204  */
205 gboolean
g_rc_parse_user(LeptonToplevel * toplevel,const gchar * rcname,GError ** err)206 g_rc_parse_user (LeptonToplevel *toplevel,
207                  const gchar *rcname,
208                  GError **err)
209 {
210   gchar *rcfile = NULL;
211   gboolean status;
212 
213   /* Default to gafrc */
214   rcname = (rcname != NULL) ? rcname : "gafrc";
215 
216   rcfile = g_build_filename (eda_get_user_config_dir (), rcname, NULL);
217   status = g_rc_parse_file (toplevel, rcfile,
218                             eda_config_get_user_context (), err);
219   g_free (rcfile);
220   return status;
221 }
222 
223 /*! \brief Load a local RC file.
224  * \par Function Description
225  * Attempts to load the Scheme initialisation file with basename \a
226  * rcname corresponding to \a path, reporting errors via \a err.  If
227  * \a path is a directory, looks for a file named \a rcname in that
228  * directory. Otherwise, looks for a file named \a rcname in the same
229  * directory as \a path. If \a path is NULL, looks in the current
230  * directory. If \a rcname is NULL, the default value of "gafrc" is
231  * used.
232  *
233  * \param toplevel  The current #LeptonToplevel structure.
234  * \param rcname    The basename of the RC file to load, or NULL.
235  * \param path      The path to load a RC file for, or NULL.
236  * \param err       Return location for errors, or NULL.
237  * \return TRUE on success, FALSE on failure.
238  */
239 gboolean
g_rc_parse_local(LeptonToplevel * toplevel,const gchar * rcname,const gchar * path,GError ** err)240 g_rc_parse_local (LeptonToplevel *toplevel,
241                   const gchar *rcname,
242                   const gchar *path,
243                   GError **err)
244 {
245   gchar *dir = NULL;
246   gchar *rcfile = NULL;
247   gboolean status;
248   g_return_val_if_fail ((toplevel != NULL), FALSE);
249 
250   /* Default to gafrc */
251   rcname = (rcname != NULL) ? rcname : "gafrc";
252   /* Default to cwd */
253   path = (path != NULL) ? path : ".";
254 
255   /* If path isn't a directory, get the dirname. */
256   if (g_file_test (path, G_FILE_TEST_IS_DIR)) {
257     dir = g_strdup (path);
258   } else {
259     dir = g_path_get_dirname (path);
260   }
261 
262   rcfile = g_build_filename (dir, rcname, NULL);
263   status = g_rc_parse_file (toplevel, rcfile, NULL, err);
264 
265   g_free (dir);
266   g_free (rcfile);
267   return status;
268 }
269 
270 static void
g_rc_parse__process_error(GError ** err,const gchar * pname)271 g_rc_parse__process_error (GError **err, const gchar *pname)
272 {
273   char *pbase;
274 
275   /* Take no chances; if err was not set for some reason, bail out. */
276   if (*err == NULL) {
277     const gchar *msgl =
278       _("ERROR: An unknown error occurred while parsing configuration files.");
279     g_message ("%1$s", msgl);
280     fprintf(stderr, "%1$s\n", msgl);
281 
282   } else {
283     /* RC files are allowed to be missing or skipped; check for
284      * this. */
285     if (g_error_matches (*err, G_FILE_ERROR, G_FILE_ERROR_NOENT) ||
286         g_error_matches (*err, EDA_ERROR, EDA_ERROR_RC_TWICE)) {
287       return;
288     }
289 
290     g_message (_("ERROR: %1$s"), (*err)->message);
291     fprintf (stderr, _("ERROR: %1$s\n"), (*err)->message);
292   }
293 
294   /* g_path_get_basename() allocates memory, but we don't care
295    * because we're about to exit. */
296   pbase = g_path_get_basename (pname);
297   fprintf (stderr, _("ERROR: The %1$s log may contain more information.\n"),
298            pbase);
299   exit (1);
300 }
301 
302 /*! \brief General RC file parsing function.
303  * \par Function Description
304  * Calls g_rc_parse_handler() with the default error handler. If any
305  * error other than ENOENT occurs while loading or running a Scheme
306  * initialisation file, prints an informative message and calls
307  * exit(1).
308  *
309  * \bug libgeda shouldn't call exit() - this function calls
310  *      g_rc_parse__process_error(), which does.
311  *
312  * \warning Since this function may not return, it should only be used
313  * on application startup or when there is no chance of data loss from
314  * an unexpected exit().
315  *
316  * \param [in] pname     The name of the application (usually argv[0]).
317  * \param [in] rcname    RC file basename, or NULL.
318  * \param [in] rcfile    Specific RC file path, or NULL.
319  */
320 void
g_rc_parse(const gchar * pname,const gchar * rcname,const gchar * rcfile)321 g_rc_parse (const gchar *pname,
322             const gchar *rcname,
323             const gchar *rcfile)
324 {
325   LeptonToplevel *toplevel = edascm_c_current_toplevel ();
326   g_rc_parse_handler (toplevel, rcname, rcfile,
327                       (ConfigParseErrorFunc) g_rc_parse__process_error,
328                       (void *) pname);
329 }
330 
331 
332 /*! \brief General RC file parsing function.
333  * \par Function Description
334  * Attempt to load and run system, user and local (current working directory)
335  * Scheme initialisation files, first with the default "gafrc"
336  * basename and then with the basename \a rcname, if \a rcname is not
337  * NULL.  Additionally, attempt to load and run \a rcfile
338  * if \a rcfile is not NULL.
339  *
340  * If an error occurs, calls \a handler with the provided \a user_data
341  * and a GError.
342  *
343  * \see g_rc_parse().
344  *
345  * \param toplevel  The current #LeptonToplevel structure.
346  * \param rcname    RC file basename, or NULL.
347  * \param rcfile    Specific RC file path, or NULL.
348  * \param handler   Handler function for RC errors.
349  * \param user_data Data to be passed to \a handler.
350  */
351 void
g_rc_parse_handler(LeptonToplevel * toplevel,const gchar * rcname,const gchar * rcfile,ConfigParseErrorFunc handler,void * user_data)352 g_rc_parse_handler (LeptonToplevel *toplevel,
353                     const gchar *rcname,
354                     const gchar *rcfile,
355                     ConfigParseErrorFunc handler,
356                     void *user_data)
357 {
358   GError *err = NULL;
359 
360 #ifdef HANDLER_DISPATCH
361 #  error HANDLER_DISPATCH already defined
362 #endif
363 #define HANDLER_DISPATCH \
364   do { if (err == NULL) break;  handler (&err, user_data);        \
365        g_clear_error (&err); } while (0)
366 
367   /* Load cache configuration: */
368   g_rc_load_cache_config (toplevel, &err); HANDLER_DISPATCH;
369 
370   /* Load RC files in order. */
371   /* First gafrc files. */
372   g_rc_parse_system (toplevel, NULL, &err); HANDLER_DISPATCH;
373   g_rc_parse_user (toplevel, NULL, &err); HANDLER_DISPATCH;
374   g_rc_parse_local (toplevel, NULL, NULL, &err); HANDLER_DISPATCH;
375   /* Next application-specific rcname. */
376   if (rcname != NULL) {
377     g_rc_parse_system (toplevel, rcname, &err); HANDLER_DISPATCH;
378     g_rc_parse_user (toplevel, rcname, &err); HANDLER_DISPATCH;
379     g_rc_parse_local (toplevel, rcname, NULL, &err); HANDLER_DISPATCH;
380   }
381   /* Finally, optional additional RC file.  Specifically use the
382    * current working directory's configuration context here, no matter
383    * where the rc file is located on disk. */
384   if (rcfile != NULL) {
385     EdaConfig *cwd_cfg = eda_config_get_context_for_path (".");
386     g_rc_parse_file (toplevel, rcfile, cwd_cfg, &err); HANDLER_DISPATCH;
387   }
388 
389 #undef HANDLER_DISPATCH
390 }
391 
392 
393 /*! \brief Load cache configuration data.
394  *
395  * \param toplevel  The current #LeptonToplevel structure.
396  * \param err       Return location for errors, or NULL.
397  * \return TRUE on success, FALSE on failure.
398  */
399 gboolean
g_rc_load_cache_config(LeptonToplevel * toplevel,GError ** err)400 g_rc_load_cache_config (LeptonToplevel* toplevel,
401                         GError** err)
402 {
403   g_return_val_if_fail (toplevel != NULL, FALSE);
404 
405   EdaConfig* cfg = eda_config_get_cache_context();
406 
407   gboolean status = FALSE;
408   if (cfg != NULL)
409   {
410     GError* tmp_err = NULL;
411     status = eda_config_load (cfg, &tmp_err);
412 
413     /* It's OK if file is not found (e.g. on first program run): */
414     if (g_error_matches (tmp_err, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
415     {
416       g_clear_error (&tmp_err);
417       status = TRUE;
418     }
419   }
420 
421   return status;
422 }
423