1 /* -*-c-*- */
2 /* Copyright (C) 1999  Dominik Vogt */
3 /* This program is free software; you can redistribute it and/or modify
4  * it under the terms of the GNU General Public License as published by
5  * the Free Software Foundation; either version 2 of the License, or
6  * (at your option) any later version.
7  *
8  * This program is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11  * GNU General Public License for more details.
12  *
13  * You should have received a copy of the GNU General Public License
14  * along with this program; if not, see: <http://www.gnu.org/licenses/>
15  */
16 
17 /*
18 ** XResource.c:
19 ** These routines provide modules with an interface to parse all kinds of
20 ** configuration options (X resources, command line options and configuration
21 ** file lines) in the same way (Xrm database).
22 */
23 
24 #include "config.h"
25 
26 #include <X11/Xlib.h>
27 #include <X11/Xresource.h>
28 
29 #include "fvwmlib.h"
30 #include "XResource.h"
31 
32 
33 
34 /*
35  * If you have a module MyModule and want to parse X resources as well as
36  * command line options and a config file:
37  *
38  *** EXAMPLE */
39 #if 0
40     #include <fvwmlib.h>
41 
42     void main(int argc, char **argv)
43     {
44       const char *MyName = "MyModule";
45       XrmDatabase db = NULL;
46       XrmValue *rm_value;
47       char *line;
48 
49       /* our private options */
50       const XrmOptionDescRec my_opts[] = {
51 	{ "-iconic",   ".Iconic", XrmoptionNoArg,  "any_string" },
52 	{ "-foo",      "*bar",    XrmoptionSepArg, NULL }
53       };
54       int opt_argc = argc - 6; /* options start at 6th argument for modules */
55       char **opt_argv = argv + 6;
56 
57       /* ... (open config file, etc.) */
58 
59       /* Get global X resources */
60       MergeXResources(NULL, &db, False);
61 
62       /* config file descriptor in fd; config file takes precedence over X
63        * resources (this may not be what you want). */
64       for (GetConfigLine(fd, &line); line != NULL; GetConfigLine(fd, &line))
65       {
66 	if (!MergeConfigLineResource(&db, line, MyName, '*'))
67 	{
68 	  /* Parse other lines here (e.g. "ImagePath") */
69 	}
70 	else
71 	{
72 	  /* You may still have to parse the line here yourself (e.g.
73 	   * FvwmButtons may have multiple lines for the same resource). */
74 	}
75       }
76 
77       /* command line takes precedence over all */
78       MergeCmdLineResources(&db, (XrmOptionDescList)my_opts, 2, MyName,
79 			    &opt_argc, opt_argv, True /*no default options*/);
80 
81       /* Now parse the database values: */
82       if (GetResourceString(db, "iconic", MyName, &rm_value))
83       {
84 	/* Just see if there is *any* string and don't mind it's value. */
85 	/* flags |= ICONIC */
86       }
87       if (GetResourceString(db, "bar", MyName, &rm_value))
88       {
89 	/* ... */
90       }
91 
92       /* ... */
93       XrmDestroyDatabase(db);
94     }
95 #endif
96 
97 /*** END OF EXAMPLE ***/
98 
99 
100 
101 
102 /* Default option table */
103 static XrmOptionDescRec default_opts[] =
104 {
105   { "-fg",       "*Foreground", XrmoptionSepArg, NULL },
106   { "-bg",       "*Background", XrmoptionSepArg, NULL },
107   { "-fn",       "*Font",       XrmoptionSepArg, NULL },
108   { "-geometry", "*Geometry",   XrmoptionSepArg, NULL },
109   { "-title",    "*Title",      XrmoptionSepArg, NULL }
110   /* Remember to update NUM_DEFAULT_OPTIONS if you change this list! */
111 };
112 #define NUM_DEFAULT_OPTS 5
113 
114 
115 
116 /* internal function */
DoMergeString(char * resource,XrmDatabase * ptarget,Bool override)117 static void DoMergeString(char *resource, XrmDatabase *ptarget, Bool override)
118 {
119   XrmDatabase db;
120 
121   if (!resource)
122     return;
123   db = XrmGetStringDatabase(resource);
124   XrmCombineDatabase(db, ptarget, override);
125 }
126 
127 /*
128  *
129  * Merges all X resources for the display/screen into a Xrm database.
130  * If the database does not exist (*pdb == NULL), a new database is created.
131  * If override is True, existing entries of the same name are overwritten.
132  *
133  * Please remember to destroy the database with XrmDestroyDatabase(*pdb)
134  * if you do not need it amymore.
135  *
136  */
MergeXResources(Display * dpy,XrmDatabase * pdb,Bool override)137 void MergeXResources(Display *dpy, XrmDatabase *pdb, Bool override)
138 {
139   if (!*pdb)
140     /* create new database */
141     XrmPutStringResource(pdb, "", "");
142   DoMergeString(XResourceManagerString(dpy), pdb, override);
143   DoMergeString(XScreenResourceString(DefaultScreenOfDisplay(dpy)), pdb,
144 		override);
145 }
146 
147 /*
148  *
149  * Parses the command line given through pargc/argv and puts recognized
150  * entries into the Xrm database *pdb (if *pdb is NULL a new database is
151  * created). The caller may provide an option list in XrmOptionDescList
152  * format (see XrmParseCommand manpage) and/or parse only standard options
153  * (fg, bg, geometry, fn, title). User given options have precedence over
154  * standard options which are disabled if fNoDefaults is True. Existing
155  * values are overwritten.
156  *
157  * All recognised options are removed from the command line (*pargc and
158  * argv are updated accordingly).
159  *
160  * Please remember to destroy the database with XrmDestroyDatabase(*pdb)
161  * if you do not need it amymore.
162  *
163  */
MergeCmdLineResources(XrmDatabase * pdb,XrmOptionDescList opts,int num_opts,char * name,int * pargc,char ** argv,Bool fNoDefaults)164 void MergeCmdLineResources(XrmDatabase *pdb, XrmOptionDescList opts,
165 			   int num_opts, char *name, int *pargc, char **argv,
166 			   Bool fNoDefaults)
167 {
168   if (!name)
169     return;
170   if (opts && num_opts > 0)
171     XrmParseCommand(pdb, opts, num_opts, name, pargc, argv);
172   if (!fNoDefaults)
173     XrmParseCommand(pdb, default_opts, NUM_DEFAULT_OPTS,
174 		    name, pargc, argv);
175 }
176 
177 /*
178  *
179  * Takes a line from a config file and puts a corresponding value into the
180  * Xrm database *pdb (will be created if *pdb is NULL). 'prefix' is the
181  * name of the module. A specific type of binding in the database must be
182  * provided in bindstr (either "*" or "."). Leading unquoted whitespace are
183  * stripped from value. Existing values in the database are overwritten.
184  * True is returned if the line was indeed merged into the database (i.e. it
185  * had the correct format) or False if not.
186  *
187  * Example: If prefix = "MyModule" and bindstr = "*", the line
188  *
189  *   *MyModuleGeometry   80x25+0+0
190  *
191  * will be put into the database as if you had this line in your .Xdefaults:
192  *
193  *   MyModule*Geometry:  80x25+0+0
194  *
195  * Please remember to destroy the database with XrmDestroyDatabase(*pdb)
196  * if you do not need it amymore.
197  *
198  */
MergeConfigLineResource(XrmDatabase * pdb,char * line,char * prefix,char * bindstr)199 Bool MergeConfigLineResource(XrmDatabase *pdb, char *line, char *prefix,
200 			     char *bindstr)
201 {
202   int len;
203   char *end;
204   char *value;
205   char *myvalue;
206   char *resource;
207 
208   /* translate "*(prefix)(suffix)" to "(prefix)(binding)(suffix)",
209    * e.g. "*FvwmPagerGeometry" to "FvwmPager.Geometry" */
210   if (!line || *line != '*')
211     return False;
212 
213   line++;
214   len = (prefix) ? strlen(prefix) : 0;
215   if (!prefix || strncasecmp(line, prefix, len))
216     return False;
217 
218   line += len;
219   end = line;
220   while (*end && !isspace((unsigned char)*end))
221     end++;
222   if (line == end)
223     return False;
224   value = end;
225   while (*value && isspace((unsigned char)*value))
226     value++;
227 
228   /* prefix*suffix: value */
229   resource = (char *)safemalloc(len + (end - line) + 2);
230   strcpy(resource, prefix);
231   strcat(resource, bindstr);
232   strncat(resource, line, end - line);
233 
234   len = strlen(value);
235   myvalue = (char *)safemalloc(len + 1);
236   strcpy(myvalue, value);
237   for (len--; len >= 0 && isspace((unsigned char)myvalue[len]); len--)
238     myvalue[len] = 0;
239 
240   /* merge string into database */
241   XrmPutStringResource(pdb, resource, myvalue);
242 
243   free(resource);
244   free(myvalue);
245   return True;
246 }
247 
248 /*
249  *
250  * Reads the string-value for the pair prefix/resource from the Xrm database
251  * db and returns a pointer to it. The string may only be read and must not
252  * be freed by the caller. 'prefix' is the class name (usually the name of
253  * the module). If no value is found in the database, *val will be NULL.
254  * True is returned if a value was found, False if not. If you are only
255  * interested if there is a string, but not it's value, you can set val to
256  * NULL.
257  *
258  * Example:
259  *
260  *   GetResourceString(db, "Geometry", "MyModule", &r)
261  *
262  * returns the resource value of the "Geometry" resource for MyModule in r.
263  *
264  */
GetResourceString(XrmDatabase db,const char * resource,const char * prefix,XrmValue * xval)265 Bool GetResourceString(
266   XrmDatabase db, const char *resource, const char *prefix, XrmValue *xval)
267 {
268   char *str_type;
269   char *name;
270   char *Name;
271   int i;
272 
273   name = (char *)safemalloc(strlen(resource) + strlen(prefix) + 2);
274   Name = (char *)safemalloc(strlen(resource) + strlen(prefix) + 2);
275   strcpy(name, prefix);
276   strcat(name, ".");
277   strcat(name, resource);
278   strcpy(Name, name);
279   if (isupper(name[0]))
280     name[0] = tolower(name[0]);
281   if (islower(Name[0]))
282     Name[0] = toupper(Name[0]);
283   i = strlen(prefix) + 1;
284   if (isupper(name[i]))
285     name[i] = tolower(name[i]);
286   if (islower(Name[i]))
287     Name[i] = toupper(Name[i]);
288   if (!XrmGetResource(db, name, Name, &str_type, xval) ||
289       xval->addr == NULL || xval->size == 0)
290   {
291     free(name);
292     free(Name);
293     xval->size = 0;
294     xval->addr = NULL;
295 
296     return False;
297   }
298   free(name);
299   free(Name);
300 
301   return True;
302 }
303