1 /*******************************************************************************
2 * *
3 * prefFile.c -- Nirvana utilities for providing application preferences files *
4 * *
5 * Copyright (C) 1999 Mark Edel *
6 * *
7 * This is free software; you can redistribute it and/or modify it under the *
8 * terms of the GNU General Public License as published by the Free Software *
9 * Foundation; either version 2 of the License, or (at your option) any later *
10 * version. In addition, you may distribute version of this program linked to *
11 * Motif or Open Motif. See README for details. *
12 * *
13 * This software is distributed in the hope that it will be useful, but WITHOUT *
14 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or *
15 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License *
16 * for more details. *
17 * *
18 * You should have received a copy of the GNU General Public License along with *
19 * software; if not, write to the Free Software Foundation, Inc., 59 Temple *
20 * Place, Suite 330, Boston, MA 02111-1307 USA *
21 * *
22 * Nirvana Text Editor *
23 * June 3, 1993 *
24 * *
25 * Written by Mark Edel *
26 * *
27 *******************************************************************************/
28
29 #ifdef HAVE_CONFIG_H
30 #include "../config.h"
31 #endif
32
33 #include "prefFile.h"
34 #include "fileUtils.h"
35 #include "utils.h"
36 #include "nedit_malloc.h"
37
38 #include <stdlib.h>
39 #include <stdio.h>
40 #include <string.h>
41 #ifdef VMS
42 #include "VMSparam.h"
43 #else
44 #ifndef __MVS__
45 #include <sys/param.h>
46 #endif
47 #endif
48 #include <Xm/Xm.h>
49
50 #ifdef HAVE_DEBUG_H
51 #include "../debug.h"
52 #endif
53
54 #include <inttypes.h>
55
56 #define N_BOOLEAN_STRINGS 13
57 static const char *TrueStrings[N_BOOLEAN_STRINGS] = {"True", "true", "TRUE", "T", "t",
58 "Yes", "yes", "YES", "y", "Y", "on", "On", "ON"};
59 static const char *FalseStrings[N_BOOLEAN_STRINGS] = {"False", "false", "FALSE", "F", "f",
60 "No", "no", "NO", "n", "N", "off", "Off", "OFF"};
61
62 static void readPrefs(XrmDatabase prefDB, XrmDatabase appDB,
63 const char *appName, const char *appClass,
64 PrefDescripRec *rsrcDescrip, int nRsrc, int overlay);
65 static int stringToPref(const char *string, PrefDescripRec *rsrcDescrip);
66 static char *removeWhiteSpace(const char *string);
67
68 /*
69 ** Preferences File
70 **
71 ** An application maintains a preferences file so that users can
72 ** quickly save and restore program options from within a program,
73 ** without being forced to learn the X resource mechanism.
74 **
75 ** Preference files are the same format as X resource files, and
76 ** are read using the X resource file reader. X-savvy users are allowed
77 ** to move resources out of a preferences file to their X resource
78 ** files. They would do so if they wanted to attach server-specific
79 ** preferences (such as fonts and colors) to different X servers, or to
80 ** combine additional preferences served only by X resources with those
81 ** provided by the program's menus.
82 */
83
84 /*
85 ** Preference description table
86 **
87 ** A preference description table contains the information necessary
88 ** to read preference resources and store their values in a data
89 ** structure. The table can, so far, describe four types
90 ** of values (this will probably be expanded in the future to include
91 ** more types): ints, booleans, enumerations, and strings. Each entry
92 ** includes the name and class for saving and restoring the parameter
93 ** in X database format, the data type, a default value in the form of
94 ** a character string, and the address where the parameter value is
95 ** be stored. Strings and enumerations take an additional argument.
96 ** For strings, it is the maximum length string that can safely be
97 ** stored or NULL to indicate that new space should be allocated and a
98 ** pointer to it stored in the value address. For enums, it is an array
99 ** of string pointers to the names of each of its possible values. The
100 ** last value in a preference record is a flag for determining whether
101 ** the value should be written to the save file by SavePreferences.
102 */
103
104 /*
105 ** CreatePreferencesDatabase
106 **
107 ** Process a preferences file and the command line options pertaining to
108 ** the X resources used to set those preferences. Create an X database
109 ** of the results. The reason for this odd set of functionality is
110 ** to process command line options before XtDisplayInitialize reads them
111 ** into the application database that the toolkit attaches to the display.
112 ** This allows command line arguments to properly override values specified
113 ** in the preferences file.
114 **
115 ** fileName Name only of the preferences file to be found
116 ** in the user's home directory
117 ** appName Application name to use in reading the preference
118 ** resources
119 ** opTable Xrm command line option table for the resources
120 ** used in the preferences file ONLY. Command line
121 ** options for other X resources should be processed
122 ** by XtDisplayInitialize.
123 ** nOptions Number of items in opTable
124 ** argcInOut Address of argument count. This will be altered
125 ** to remove the command line options that are
126 ** recognized in the option table.
127 ** argvInOut Argument vector. Will be altered as argcInOut.
128 */
CreatePreferencesDatabase(const char * fullName,const char * appName,XrmOptionDescList opTable,int nOptions,unsigned int * argcInOut,char ** argvInOut)129 XrmDatabase CreatePreferencesDatabase(const char *fullName, const char *appName,
130 XrmOptionDescList opTable, int nOptions, unsigned int *argcInOut,
131 char **argvInOut)
132 {
133 XrmDatabase db;
134 int argcCopy;
135 char **argvCopy;
136 char *fileString;
137 static XrmOptionDescRec xrmOnlyTable[] =
138 {{"-xrm", NULL, XrmoptionResArg, (caddr_t)NULL}};
139
140 /* read the preferences file into an X database.
141 On failure prefDB will be NULL. */
142 if (NULL == fullName)
143 {
144 db = NULL;
145 } else
146 {
147 fileString = ReadAnyTextFile(fullName, False);
148 if (NULL == fileString)
149 {
150 db = NULL;
151 } else
152 {
153 char* rsrcName;
154 db = XrmGetStringDatabase(fileString);
155 NEditFree(fileString);
156
157 /* Add a resource to the database which remembers that
158 the file is read, so that NEdit will know it. */
159 rsrcName = (char*) NEditMalloc(strlen(appName) + 14);
160 sprintf(rsrcName, "%s.prefFileRead", appName);
161 XrmPutStringResource(&db, rsrcName, "True");
162 NEditFree(rsrcName);
163 }
164 }
165
166 /* parse the command line, storing results in the preferences database */
167 XrmParseCommand(&db, opTable, nOptions, appName, (int *)argcInOut,
168 argvInOut);
169
170 /* process -xrm (resource setting by resource name) arguments so those
171 pertaining to preference resources will be included in the database.
172 Don't remove -xrm arguments from the argument vector, however, so
173 XtDisplayInitialize can still read the non-preference resources */
174 argvCopy = (char**)NEditMalloc(sizeof(char *) * *argcInOut);
175 memcpy(argvCopy, argvInOut, sizeof(char *) * *argcInOut);
176 argcCopy = *argcInOut;
177 XrmParseCommand(&db, xrmOnlyTable, XtNumber(xrmOnlyTable), appName,
178 &argcCopy, argvCopy);
179 NEditFree((char *)argvCopy);
180 return db;
181 }
182
183 /*
184 ** RestorePreferences
185 **
186 ** Fill in preferences data from two X databases, values in prefDB taking
187 ** precidence over those in appDB.
188 */
RestorePreferences(XrmDatabase prefDB,XrmDatabase appDB,const char * appName,const char * appClass,PrefDescripRec * rsrcDescrip,int nRsrc)189 void RestorePreferences(XrmDatabase prefDB, XrmDatabase appDB,
190 const char *appName, const char *appClass, PrefDescripRec *rsrcDescrip, int nRsrc)
191 {
192 readPrefs(prefDB, appDB, appName, appClass, rsrcDescrip, nRsrc, False);
193 }
194
195 /*
196 ** OverlayPreferences
197 **
198 ** Incorporate preference specified in database "prefDB", preserving (not
199 ** restoring to default) existing preferences, not mentioned in "prefDB"
200 */
OverlayPreferences(XrmDatabase prefDB,const char * appName,const char * appClass,PrefDescripRec * rsrcDescrip,int nRsrc)201 void OverlayPreferences(XrmDatabase prefDB, const char *appName,
202 const char *appClass, PrefDescripRec *rsrcDescrip, int nRsrc)
203 {
204 readPrefs(NULL, prefDB, appName, appClass, rsrcDescrip, nRsrc, True);
205 }
206
readPrefs(XrmDatabase prefDB,XrmDatabase appDB,const char * appName,const char * appClass,PrefDescripRec * rsrcDescrip,int nRsrc,int overlay)207 static void readPrefs(XrmDatabase prefDB, XrmDatabase appDB,
208 const char *appName, const char *appClass, PrefDescripRec *rsrcDescrip,
209 int nRsrc, int overlay)
210 {
211 char rsrcName[256], rsrcClass[256], *valueString, *type;
212 XrmValue rsrcValue;
213 int i;
214
215 /* read each resource, trying first the preferences file database, then
216 the application database, then the default value if neither are found */
217 for (i=0; i<nRsrc; i++) {
218 sprintf(rsrcName,"%s.%s", appName, rsrcDescrip[i].name);
219 sprintf(rsrcClass, "%s.%s", appClass, rsrcDescrip[i].class);
220 if (prefDB!=NULL &&
221 XrmGetResource(prefDB, rsrcName, rsrcClass, &type, &rsrcValue)) {
222 if (strcmp(type, XmRString)) {
223 fprintf(stderr,"nedit: Internal Error: Unexpected resource type, %s\n",
224 type);
225 return;
226 }
227 valueString = rsrcValue.addr;
228 } else if (XrmGetResource(appDB,rsrcName,rsrcClass,&type,&rsrcValue)) {
229 if (strcmp(type, XmRString)) {
230 fprintf(stderr,"nedit: Internal Error: Unexpected resource type, %s\n",
231 type);
232 return;
233 }
234 valueString = rsrcValue.addr;
235 } else
236 valueString = rsrcDescrip[i].defaultString;
237 if (overlay && valueString == rsrcDescrip[i].defaultString)
238 continue;
239 if (!stringToPref(valueString, &rsrcDescrip[i]))
240 fprintf(stderr, "nedit: Could not read value of resource %s\n", rsrcName);
241 }
242 }
243
244 /*
245 ** RestoreDefaultPreferences
246 **
247 ** Restore preferences to their default values as stored in rsrcDesrcip
248 */
RestoreDefaultPreferences(PrefDescripRec * rsrcDescrip,int nRsrc)249 void RestoreDefaultPreferences(PrefDescripRec *rsrcDescrip, int nRsrc)
250 {
251 int i;
252
253 for (i=0; i<nRsrc; i++)
254 stringToPref(rsrcDescrip[i].defaultString, &rsrcDescrip[i]);
255 }
256
257 /*
258 ** SavePreferences
259 **
260 ** Create or replace an application preference file according to
261 ** the resource descriptions in rsrcDesrcip.
262 */
SavePreferences(Display * display,const char * fullName,const char * fileHeader,PrefDescripRec * rsrcDescrip,int nRsrc)263 int SavePreferences(Display *display, const char *fullName,
264 const char *fileHeader, PrefDescripRec *rsrcDescrip, int nRsrc)
265 {
266 char *appName, *appClass, **enumStrings;
267 FILE *fp;
268 int type;
269 int i;
270
271 /* open the file */
272 if ((fp = fopen(fullName, "w")) == NULL)
273 return False;
274
275 /* write the file header text out to the file */
276 fprintf(fp, "%s\n", fileHeader);
277
278 /* write out the resources so they can be read by XrmGetFileDatabase */
279 XtGetApplicationNameAndClass(display, &appName, &appClass);
280 for (i=0; i<nRsrc; i++) {
281 if (rsrcDescrip[i].save) {
282 type = rsrcDescrip[i].dataType;
283 fprintf(fp, "%s.%s: ", appName, rsrcDescrip[i].name);
284 if (type == PREF_STRING)
285 fprintf(fp, "%s", (char *)rsrcDescrip[i].valueAddr);
286 if (type == PREF_ALLOC_STRING)
287 fprintf(fp, "%s", *(char **)rsrcDescrip[i].valueAddr);
288 else if (type == PREF_ENUM) {
289 enumStrings = (char **)rsrcDescrip[i].arg;
290 fprintf(fp,"%s", enumStrings[*(int *)rsrcDescrip[i].valueAddr]);
291 } else if (type == PREF_INT)
292 fprintf(fp, "%d", *(int *)rsrcDescrip[i].valueAddr);
293 else if (type == PREF_BOOLEAN) {
294 if (*(int *)rsrcDescrip[i].valueAddr)
295 fprintf(fp, "True");
296 else
297 fprintf(fp, "False");
298 }
299 fprintf(fp, "\n");
300 }
301 }
302 fclose(fp);
303 return True;
304 }
305
stringToPref(const char * string,PrefDescripRec * rsrcDescrip)306 static int stringToPref(const char *string, PrefDescripRec *rsrcDescrip)
307 {
308 int i;
309 char *cleanStr, *endPtr, **enumStrings;
310
311 switch (rsrcDescrip->dataType) {
312 case PREF_INT:
313 cleanStr = removeWhiteSpace(string);
314 *(int *)rsrcDescrip->valueAddr =
315 strtol(cleanStr, &endPtr, 10);
316 if (strlen(cleanStr) == 0) { /* String is empty */
317 *(int *)rsrcDescrip->valueAddr = 0;
318 NEditFree(cleanStr);
319 return False;
320 } else if (*endPtr != '\0') { /* Whole string not parsed */
321 *(int *)rsrcDescrip->valueAddr = 0;
322 NEditFree(cleanStr);
323 return False;
324 }
325 NEditFree(cleanStr);
326 return True;
327 case PREF_BOOLEAN:
328 cleanStr = removeWhiteSpace(string);
329 for (i=0; i<N_BOOLEAN_STRINGS; i++) {
330 if (!strcmp(TrueStrings[i], cleanStr)) {
331 *(int *)rsrcDescrip->valueAddr = True;
332 NEditFree(cleanStr);
333 return True;
334 }
335 if (!strcmp(FalseStrings[i], cleanStr)) {
336 *(int *)rsrcDescrip->valueAddr = False;
337 NEditFree(cleanStr);
338 return True;
339 }
340 }
341 NEditFree(cleanStr);
342 *(int *)rsrcDescrip->valueAddr = False;
343 return False;
344 case PREF_ENUM:
345 cleanStr = removeWhiteSpace(string);
346 enumStrings = (char **)rsrcDescrip->arg;
347 for (i=0; enumStrings[i]!=NULL; i++) {
348 if (!strcmp(enumStrings[i], cleanStr)) {
349 *(int *)rsrcDescrip->valueAddr = i;
350 NEditFree(cleanStr);
351 return True;
352 }
353 }
354 NEditFree(cleanStr);
355 *(int *)rsrcDescrip->valueAddr = 0;
356 return False;
357 case PREF_STRING:
358 if (strlen(string) >= (size_t)(intptr_t)rsrcDescrip->arg)
359 return False;
360 strncpy(rsrcDescrip->valueAddr, string, (size_t)(intptr_t)rsrcDescrip->arg);
361 return True;
362 case PREF_ALLOC_STRING:
363 *(char **)rsrcDescrip->valueAddr = NEditStrdup(string);
364 return True;
365 }
366 return False;
367 }
368
369 /*
370 ** Remove the white space (blanks and tabs) from a string and return
371 ** the result in a newly allocated string as the function value
372 */
removeWhiteSpace(const char * string)373 static char *removeWhiteSpace(const char *string)
374 {
375 char *outPtr, *outString;
376
377 outPtr = outString = (char*)NEditMalloc(strlen(string)+1);
378 while (TRUE) {
379 if (*string != ' ' && *string != '\t')
380 *(outPtr++) = *(string++);
381 else
382 string++;
383 if (*string == 0) {
384 *outPtr = 0;
385 return outString;
386 }
387 }
388 }
389
390 /*******************
391 Implementation Note:
392 Q: Why aren't you using the Xt type conversion services?
393 A: 1) To create a save file, you also need to convert values back to text form,
394 and there are no converters for that direction. 2) XtGetApplicationResources
395 can only be used on the resource database created by the X toolkit at
396 initialization time, and there is no way to intervene in the creation of
397 that database or store new resources in it reliably after it is created.
398 3) The alternative, XtConvertAndStore is not adequately documented. The
399 toolkit mauual does not explain why it overwrites its input value structure.
400 4) XtGetApplicationResources and XtConvertAndStore do not work well together
401 because they use different storage strategies for certain data types.
402 *******************/
403