1    | /*****
2    |  ** ** Module Header ******************************************************* **
3    |  ** 									     **
4    |  **   Modules Revision 3.0						     **
5    |  **   Providing a flexible user environment				     **
6    |  ** 									     **
7    |  **   File:		cmdXResource.c					     **
8    |  **   First Edition:	91/10/23					     **
9    |  ** 									     **
10   |  **   Authors:	John Furlan, jlf@behere.com				     **
11   |  **		Leif Hedstrom<hedstrom"@boot.org>			     **
12   |  **		Jens Hamisch, jens@Strawberry.COM			     **
13   |  ** 									     **
14   |  **   Description:	Module command to merge/remove resources from the X11**
15   |  **			resource manager. The database is update internally, **
16   |  **			ie. its not done at evaluation of string modulecmd   **
17   |  **			returns. It will do something like "xrdb -merge"     **
18   |  **			using the default display ($DISPLAY).		     **
19   |  ** 									     **
20   |  **   Exports:		xresourceFinish					     **
21   |  **			cmdXResource					     **
22   |  ** 									     **
23   |  **   Notes:		Fragments of this code are from the original xrdb    **
24   |  **			source, Copyright 1987 & 1991 by DIGITAL EQUIPMENT   **
25   |  **			CORPORATION. Xrdb was written and modified by:	     **
26   |  ** 									     **
27   |  **				Jim Gettys, August 28, 1987		     **
28   |  **				Phil Karlton, January 5, 1987		     **
29   |  **				Bob Scheifler, February, 1991		     **
30   |  ** 									     **
31   |  **   ToDo/Bugs:	+ The command only handles screen independant re-    **
32   |  **                       sources.                                           **
33   |  ** ************************************************************************ **
34   |  ****/
35   | 
36   | /** ** Copyright *********************************************************** **
37   |  ** 									     **
38   |  ** Copyright 1991-1994 by John L. Furlan.                      	     **
39   |  ** see LICENSE.GPL, which must be provided, for details		     **
40   |  ** 									     ** 
41   |  ** ************************************************************************ **/
42   | 
43   | static char Id[] = "@(#)$Id: cmdXResource.c.src.html,v 1.2 2005/11/25 20:09:37 rkowen Exp $";
44   | static void *UseId[] = { &UseId, Id };
45   | 
46   | /** ************************************************************************ **/
47   | /** 				      HEADERS				     **/
48   | /** ************************************************************************ **/
49   | 
50   | #include "modules_def.h"
51   | 
52   | #ifdef HAS_X11LIBS
53   | #include <X11/Xlib.h>
54   | #include <X11/Xatom.h>
55   | // #include <X11/Xmu/SysUtil.h>
56   | #endif
57   | 
58   | /** ************************************************************************ **/
59   | /** 				  LOCAL DATATYPES			     **/
60   | /** ************************************************************************ **/
61   | 
62   | #ifdef HAS_X11LIBS
63   | typedef struct _ResourceDB {
64   |     Tcl_HashTable *data;
65   |     Window root;
66   |     Atom prop;
67   | } ResourceDB;
68   | #endif
69   | 
70   | /** ************************************************************************ **/
71   | /** 				     CONSTANTS				     **/
72   | /** ************************************************************************ **/
73   | 
74   | #ifndef R_OK
75   | #define F_OK		0		/** does file exist		     **/
76   | #define X_OK		1		/** is it executable by caller	     **/
77   | #define W_OK		2		/** is it writable by caller	     **/
78   | #define R_OK		4		/** is it readable by caller	     **/
79   | #endif
80   | 
81   | /** ************************************************************************ **/
82   | /**				      MACROS				     **/
83   | /** ************************************************************************ **/
84   | 
85   | #ifdef HAS_X11LIBS
86   | #define MAXHOSTNAME	 255
87   | #define Resolution( pixels, mm)	(((pixels * 100000 / mm) + 50) / 100)
88   | #endif
89   | 
90   | /** ************************************************************************ **/
91   | /** 				    LOCAL DATA				     **/
92   | /** ************************************************************************ **/
93   | 
94   | static	char	module_name[] = "cmdXResource.c";	/** File name of this module **/
95   | 
96   | #if WITH_DEBUGGING_UTIL_2
97   | static	char	_proc_addDef[] = "addDef";
98   | static	char	_proc_addNum[] = "addNum";
99   | #endif
100  | #if WITH_DEBUGGING_UTIL_1
101  | static	char	_proc_doDisplayDefines[] = "doDisplayDefines";
102  | static	char	_proc_doScreenDefines[] = "doScreenDefines";
103  | static	char	_proc_readFile[] = "readFile";
104  | static	char	_proc_getEntries[] = "getEntries";
105  | static	char	_proc_storeResProp[] = "storeResProp";
106  | static	char	_proc_getOld[] = "getOld";
107  | static	char	_proc_initBuffers[] = "initBuffers";
108  | static	char	_proc_xresourceFinish[] = "xresourceFinish";
109  | #endif
110  | #if WITH_DEBUGGING_CALLBACK
111  | static	char	_proc_cmdXResource[] = "cmdXResource";
112  | #endif
113  | 
114  | #ifdef HAS_X11LIBS
115  | static Display		*dpy		= (Display *) NULL;
116  | static char		*defines	= (char *) NULL;
117  | static int		def_base	= 0;
118  | static Tcl_DString	*buffer		= (Tcl_DString *) NULL;
119  | static ResourceDB	resDB		= { NULL, (Window) 0, (Atom) 0 };
120  | #endif
121  | 
122  | /** ************************************************************************ **/
123  | /**				    PROTOTYPES				     **/
124  | /** ************************************************************************ **/
125  | 
126  | static	void	addDef(	char*, char*);
127  | static	void	addNum(	char*, int);
128  | static	void	doDisplayDefines(void);
129  | static	void	doScreenDefines( int);
130  | static	int	readFile( register FILE	*, int);
131  | static	ErrType getEntries(Tcl_Interp*,  Tcl_HashTable*, register char*, int);
132  | #ifdef HAS_X11LIBS
133  | static	void	storeResProp( register ResourceDB* );
134  | #endif
135  | static	ErrType getOld( register char**);
136  | static	ErrType	initBuffers(Tcl_Interp*, register int );
137  | 
138  | #ifdef HAS_X11LIBS
139  | 
140  | /*++++
141  |  ** ** Function-Header ***************************************************** **
142  |  ** 									     **
143  |  **   Function:		addDef, addNum					     **
144  |  ** 									     **
145  |  **   Description:	Adds DEFINES to the define buffer. This code is main-**
146  |  **			ly the same as in the original xrdb.c		     **
147  |  ** 									     **
148  |  **   First Edition:	91/10/23					     **
149  |  ** 									     **
150  |  **   Parameters:	char	*title		Name of the resource	     **
151  |  **			char	*value		and its value		     **
152  |  ** 									     **
153  |  **   Result:		-						     **
154  |  ** 									     **
155  |  **   Attached Globals:	defines		Buffer for all DEFINES which will be **
156  |  **					written here in command lien syntax: **
157  |  **					   -D <titel>=<value>		     **
158  |  ** 									     **
159  |  ** ************************************************************************ **
160  |  ++++*/
161  | 
162  | static	void	addDef(	char	*title,
163  | 			char	*value)
164  | {
165  |     register int quote;
166  | 
167  | #if WITH_DEBUGGING_UTIL_2
168  |     ErrorLogger( NO_ERR_START, LOC, _proc_addDef, NULL);
169  | #endif
170  | 
171  |     /**
172  |      **  Add '-D title' at first
173  |      **/
174  | 
175  |     if( title && *title) {
176  | 
177  | 	strcat( defines, " -D");
178  | 	strcat( defines, title);
179  | 
180  | 	/**
181  | 	 **  Add the value if there is one
182  | 	 **/
183  | 
184  | 	if( value && *value) {
185  | 
186  | 	    quote = (value && strchr( value, ' '));
187  | 	    strcat( defines, (quote ? "=\"" : "="));
188  | 	    strcat( defines, value);
189  | 	    if( quote)
190  | 		strcat( defines,"\"");
191  | 
192  | 	} /** if( value) **/
193  |     } /** if( title) **/
194  | 
195  | } /** End of 'addDef' **/
196  | 
197  | static	void	addNum(	char	*title,
198  | 			int	 value)
199  | {
200  |     char num[ 20];
201  | 
202  | #if WITH_DEBUGGING_UTIL_2
203  |     ErrorLogger( NO_ERR_START, LOC, _proc_addNum, NULL);
204  | #endif
205  | 
206  |     sprintf( num, "%d", value);
207  |     addDef( title, num);
208  | 
209  | } /** End of 'addNum' **/
210  | 
211  | /*++++
212  |  ** ** Function-Header ***************************************************** **
213  |  ** 									     **
214  |  **   Function:		doDisplayDefines				     **
215  |  ** 									     **
216  |  **   Description:	Put the client and server specific defines on the    **
217  |  **			define buffer					     **
218  |  ** 									     **
219  |  **   First Edition:	91/10/23					     **
220  |  ** 									     **
221  |  **   Parameters:	-						     **
222  |  ** 									     **
223  |  **   Result:		-						     **
224  |  ** 									     **
225  |  **   Attached Globals:	dpy		For seeking the name of the display  **
226  |  **			defines		(via addDef and addNum)	  	     **
227  |  ** 									     **
228  |  ** ************************************************************************ **
229  |  ++++*/
230  | 
231  | static	void	doDisplayDefines()
232  | {
233  |     char	 client[ MAXHOSTNAME],		/** X client name buffer     **/
234  | 		 server[ MAXHOSTNAME],		/** X server name buffer     **/
235  | 		*colon;				/** Pointer for seeking the  **/
236  | 						/** colon in the server name **/
237  | #if WITH_DEBUGGING_UTIL_1
238  |     ErrorLogger( NO_ERR_START, LOC, _proc_doDisplayDefines, NULL);
239  | #endif
240  | 
241  |     /**
242  |      **  Get client and server hostname. Remove everything after the ':' from
243  |      **  the server name. If there's no server name available, the server de-
244  |      **  faults to the client.
245  |      **/
246  | 
247  |     //    XmuGetHostname( client, MAXHOSTNAME);
248  |     gethostname(client,MAXHOSTNAME);
249  |     strcpy( server, XDisplayName( NULL));
250  | 
251  |     if( colon = strchr( server, ':'))
252  | 	*colon = '\0';
253  |     if( !*server)
254  | 	strcpy( server, client);
255  | 
256  |     /**
257  |      **  Add the standard defines now ...
258  |      **/
259  | 
260  |     addDef( "HOST", server);
261  |     addDef( "SERVERHOST", server);
262  |     addDef( "CLIENTHOST", client);
263  |     addNum( "VERSION", ProtocolVersion( dpy));
264  |     addNum( "REVISION", ProtocolRevision( dpy));
265  |     addDef( "VENDOR", ServerVendor( dpy));
266  |     addNum( "RELEASE", VendorRelease( dpy));
267  | 
268  | } /** End of 'doDisplayDefines' **/
269  | 
270  | /*++++
271  |  ** ** Function-Header ***************************************************** **
272  |  ** 									     **
273  |  **   Function:		doScreenDefines					     **
274  |  ** 									     **
275  |  **   Description:	Put the screen specific defines on the define buffer **
276  |  ** 									     **
277  |  **   First Edition:	91/10/23					     **
278  |  ** 									     **
279  |  **   Parameters:	int	scrno	Screen number			     **
280  |  ** 									     **
281  |  **   Result:		-						     **
282  |  ** 									     **
283  |  **   Attached Globals:	dpy		For seeking the name of the display  **
284  |  **			defines		(via addDef and addNum)	  	     **
285  |  ** 									     **
286  |  ** ************************************************************************ **
287  |  ++++*/
288  | 
289  | static	void	 doScreenDefines( int	scrno)
290  | {
291  |     register Screen	*screen;
292  |     register Visual	*visual;
293  |     
294  | #if WITH_DEBUGGING_UTIL_1
295  |     ErrorLogger( NO_ERR_START, LOC, _proc_doScreenDefines, NULL);
296  | #endif
297  | 
298  |     /**
299  |      **  Get screen data at first
300  |      **/
301  | 
302  |     screen = ScreenOfDisplay( dpy, scrno);
303  |     visual = DefaultVisualOfScreen( screen);
304  | 
305  |     /**
306  |      **  Put screen data on the defines buffer
307  |      **/
308  | 
309  |     addNum( "WIDTH", screen->width);
310  |     addNum( "HEIGHT", screen->height);
311  |     addNum( "X_RESOLUTION", Resolution( screen->width, screen->mwidth));
312  |     addNum( "Y_RESOLUTION", Resolution( screen->height, screen->mheight));
313  |     addNum( "PLANES", DisplayPlanes( dpy, scrno));
314  |     addNum( "BITS_PER_RGB", visual->bits_per_rgb);
315  | 
316  |     /**
317  |      **  The CLASS and COLOR do depend on the screen class
318  |      **/
319  | 
320  |     switch(visual->class) {
321  | 
322  | 	case StaticGray:	addDef( "CLASS", "StaticGray");
323  | 				break;
324  | 
325  | 	case GrayScale:		addDef( "CLASS", "GrayScale");
326  | 				break;
327  | 
328  | 	case StaticColor:	addDef( "CLASS", "StaticColor");
329  | 				addDef( "COLOR", NULL);
330  | 				break;
331  | 
332  | 	case PseudoColor:	addDef( "CLASS", "PseudoColor");
333  | 				addDef( "COLOR", NULL);
334  | 				break;
335  | 
336  | 	case TrueColor:		addDef( "CLASS", "TrueColor");
337  | 				addDef( "COLOR", NULL);
338  | 				break;
339  | 
340  | 	case DirectColor:	addDef( "CLASS", "DirectColor");
341  | 				addDef( "COLOR", NULL);
342  | 				break;
343  |     } /** switch **/
344  | 
345  | } /** End of 'doScreenDefines' **/
346  | 
347  | /*++++
348  |  ** ** Function-Header ***************************************************** **
349  |  ** 									     **
350  |  **   Function:		readFile					     **
351  |  ** 									     **
352  |  **   Description:	Read resource from a file, which normally is a pipe  **
353  |  **			opened with popen.				     **
354  |  **			The file will be closed, when reading is finished    **
355  |  **									     **
356  |  **   Note:		This routine uses the global variable 'line', declar-**
357  |  **			ed in another file!!!				     **
358  |  ** 									     **
359  |  **   First Edition:	91/10/23					     **
360  |  ** 									     **
361  |  **   Parameters:	register FILE	*input	The stream to be read from   **
362  |  **			int		 do_cpp	Differs betweem a pipe or a  **
363  |  **						file being assigned to input **
364  |  ** 									     **
365  |  **   Result:		-						     **
366  |  ** 									     **
367  |  **   Attached Globals:	line		Buffer for a line to be read	     **
368  |  **			buffer		Buffer for the whole resource file  **
369  |  **					image				     **
370  |  ** 									     **
371  |  ** ************************************************************************ **
372  |  ++++*/
373  | 
374  | static	int	readFile(	register FILE	*input,
375  | 				int		 do_cpp)
376  | {
377  |     register int bytes;
378  | 
379  | #if WITH_DEBUGGING_UTIL_1
380  |     ErrorLogger( NO_ERR_START, LOC, _proc_readFile, NULL);
381  | #endif
382  | 
383  |     while( !feof( input) && (bytes = fread( line, 1, LINELENGTH, input)))
384  | 	Tcl_DStringAppend( buffer, line, bytes);
385  | 
386  |     if( do_cpp) {
387  | 	if( -1 == pclose( input))
388  | 	    if( OK != ErrorLogger( ERR_PCLOSE, LOC, NULL))
389  | 		return( TCL_ERROR);	/** -------- EXIT (FAILURE) -------> **/
390  |     } else {
391  | 	if( EOF == fclose( input))
392  | 	    if( OK != ErrorLogger( ERR_CLOSE, LOC, NULL))
393  | 		return( TCL_ERROR);	/** -------- EXIT (FAILURE) -------> **/
394  |     }
395  | 
396  |     return( TCL_OK);
397  | 
398  | } /**  End of 'readFile' **/
399  | 
400  | /*++++
401  |  ** ** Function-Header ***************************************************** **
402  |  ** 									     **
403  |  **   Function:		getEntries					     **
404  |  ** 									     **
405  |  **   Description:	Updates the resources database (which is a Tcl hash **
406  |  **			table) with the resources passed in the buffer. The **
407  |  **			buffer contains a X resource lookalike text image.  **
408  |  ** 									     **
409  |  **   First Edition:	91/10/23					     **
410  |  ** 									     **
411  |  **   Parameters:	Tcl_Interp	*interp		According Tcl interp.**
412  |  **			Tcl_HashTable	*data	The hash tables holding the  **
413  |  **						resource data		     **
414  |  **			register char	*buf	The buffer containing the    **
415  |  **						resources to be modified in **
416  |  **						X resource syntax	     **
417  |  **			int		 remove	Remove or add resources     **
418  |  ** 									     **
419  |  **   Result:		ErrType	NO_ERR		Success			     **
420  |  **				ERR_PARSE	Parse error		     **
421  |  ** 									     **
422  |  **   Attached Globals:	-						     **
423  |  ** 									     **
424  |  ** ************************************************************************ **
425  |  ++++*/
426  | 
427  | static	ErrType getEntries(	Tcl_Interp	*interp,
428  | 				Tcl_HashTable	*data,
429  | 				register char	*buf,
430  | 				int		 remove)
431  | {
432  |     Tcl_RegExp		res_exp = (Tcl_RegExp) NULL;
433  |     register Tcl_HashEntry	*entry;
434  |     char			*end;
435  |     int				 new_res;
436  | 
437  | #if WITH_DEBUGGING_UTIL_1
438  |     ErrorLogger( NO_ERR_START, LOC, _proc_getEntries, NULL);
439  | #endif
440  | 
441  |     /**
442  |      **  The following regular expression matches pattern like
443  |      **
444  |      **       <resource>:	<value>
445  |      **
446  |      **  The resource will be returned as \1 and the value as \2
447  |      **  Set the regexp pointer only, if it hasn't already been set. This 
448  |      **  is a constant regexp!
449  |      **/
450  | 
451  |     if( !res_exp)
452  | 	res_exp  = Tcl_RegExpCompile(interp,
453  | 		 "^[ \t]*([^ \t]*)[ \t]*:[ \t]*(.*)[ \t]*$");
454  | 
455  |     /**
456  |      **  Seek for the lines (buffers) end. Put a terminator there. Take care of
457  |      **  escaped newlines!
458  |      **/
459  | 
460  |     for( end = buf; *end; end++)
461  | 	if( *end == '\\' && *(end+1) == '\n')
462  | 	    end++;
463  | 	else if( *end == '\n')
464  | 	    *end = '\0';
465  | 
466  |     /**
467  |      **  Now read the whole buffer.
468  |      **  At first, we have to ignore comments
469  |      **/
470  | 
471  |     while( buf <= end) {
472  | 
473  | 	if( *buf == '#' || *buf == '!' || !*buf) {
474  | 	    while( *buf++) ;
475  | 
476  | 	/**
477  | 	 **  Otherwise we're seeking for a syntacticl correct X resource entry
478  | 	 **/
479  | 
480  | 	} else if( !Tcl_RegExpExec(interp, res_exp, buf, buf)) {
481  | 	    if( OK != ErrorLogger( ERR_PARSE, LOC, NULL)) {
482  | 		return( ERR_PARSE);	/** ------ EXIT (PARSE ERROR) -----> **/
483  | 	    }
484  | 
485  | 	} else {
486  | 
487  | 	    /**
488  | 	     **  Valid entry found. Set up buf pointing behind the pattern
489  | 	     **  that has matched and put a terminator at the end of either the
490  | 	     **  resource name and its value.
491  | 	     **/
492  | 
493  | 	    char *startp, *endp;
494  | 	    Tcl_RegExpRange(res_exp, 0, &startp, &endp);
495  | 	    buf = endp + 1;
496  | 	    Tcl_RegExpRange(res_exp, 1, &startp, &endp);
497  | 	    *endp = '\0';
498  | 	    Tcl_RegExpRange(res_exp, 2, &startp, &endp);
499  | 	    *endp = '\0';
500  | 
501  | 	    /**
502  | 	     **  Now create (or remove) a hash entry for the parsed resource
503  | 	     **/
504  | 
505  | 	    if( remove) {
506  | 	        Tcl_RegExpRange(res_exp, 1, &startp, &endp);
507  | 		if( entry = Tcl_FindHashEntry( data, startp)) {
508  | 		    null_free((void *) &( Tcl_GetHashValue( entry)));
509  | 		    Tcl_DeleteHashEntry( entry);
510  | 		}
511  | 	    } else {
512  | 	        Tcl_RegExpRange(res_exp, 1, &startp, &endp);
513  | 		entry = Tcl_CreateHashEntry( data, startp, &new_res);
514  | 		if( !new_res)
515  | 		    null_free((void *) &( Tcl_GetHashValue( entry)));
516  | 	        Tcl_RegExpRange(res_exp, 2, &startp, &endp);
517  | 		Tcl_SetHashValue( entry, strdup( startp));
518  | 	    }
519  | 
520  | 	} /** if( reg exp matched) **/
521  |     } /** while **/
522  | 
523  |     /**
524  |      **  Return on success
525  |      **/
526  | 
527  |     return( NO_ERR);
528  | 
529  | } /** end of 'getEntries' **/
530  | 
531  | /*++++
532  |  ** ** Function-Header ***************************************************** **
533  |  ** 									     **
534  |  **   Function:		storeResProp					     **
535  |  ** 									     **
536  |  **   Description:	Update the X11 resource property, adding new resour- **
537  |  **			ces.						     **
538  |  ** 									     **
539  |  **   First Edition:	91/10/23					     **
540  |  ** 									     **
541  |  **   Parameters:	register ResourceDB *rdb	Resource database   **
542  |  ** 									     **
543  |  **   Result:		-						     **
544  |  ** 									     **
545  |  **   Attached Globals:	-						     **
546  |  ** 									     **
547  |  ** ************************************************************************ **
548  |  ++++*/
549  | 
550  | #ifdef HAS_X11LIBS
551  | static	void	storeResProp(	register ResourceDB *rdb)
552  | {
553  |     Tcl_HashSearch	search;
554  |     register int	mode = PropModeReplace;
555  |     register int	max = (XMaxRequestSize( dpy) << 2) - 28;
556  |     register int	left;
557  |     register Tcl_HashEntry *entry = Tcl_FirstHashEntry( rdb->data, &search);
558  |     unsigned char	*buf;
559  | 
560  | #if WITH_DEBUGGING_UTIL_1
561  |     ErrorLogger( NO_ERR_START, LOC, _proc_storeResProp, NULL);
562  | #endif
563  | 
564  |     /**
565  |      **	 Write all attached resources into the buffer. Follow the X 
566  |      **  resource syntax:
567  |      **
568  |      **        <resource>:	<value>
569  |      **/
570  | 
571  |     Tcl_DStringTrunc( buffer, 0);
572  |     while( entry) {
573  | 	Tcl_DStringAppend( buffer, Tcl_GetHashKey(rdb->data, entry), -1);
574  | 	Tcl_DStringAppend( buffer, ":\t", 2);
575  | 	Tcl_DStringAppend( buffer, (char *)Tcl_GetHashValue( entry), -1);
576  | 	Tcl_DStringAppend( buffer, "\n", 1);
577  | 	entry = Tcl_NextHashEntry( &search);
578  |     }
579  | 
580  |     /**
581  |      **  In case of the request being larger than the largest request the X
582  |      **  server may handle, spool it block by block until the final one.
583  |      **/
584  | 
585  |     buf = (unsigned char *) Tcl_DStringValue( buffer);
586  |     if( max < (left = Tcl_DStringLength( buffer))) {
587  | 	XGrabServer( dpy);
588  | 	mode = PropModeAppend;
589  | 	do {
590  | 	    XChangeProperty( dpy, rdb->root, rdb->prop, XA_STRING, 8, mode, buf,
591  | 		max);
592  | 	    buf += max;
593  | 	} while( max < (left -= max));
594  |     }
595  | 
596  |     /**
597  |      **  Put the final request block (which may be the only one, if the if-
598  |      **  statement above doesn't match) to the X server
599  |      **/
600  | 
601  |     XChangeProperty( dpy, rdb->root, rdb->prop, XA_STRING, 8, mode, buf, left);
602  | 
603  |     if( mode != PropModeReplace)
604  | 	XUngrabServer( dpy);
605  | 
606  | } /** End of 'storeResProp' **/
607  | #endif
608  | 
609  | /*++++
610  |  ** ** Function-Header ***************************************************** **
611  |  ** 									     **
612  |  **   Function:		getOld						     **
613  |  ** 									     **
614  |  **   Description:	First, we have to find the resources already loaded  **
615  |  **			into the X11 resource property. This routine current-**
616  |  **			ly only handles one screen, the default screen for   **
617  |  **			the DISPLAY. This routine should only be called if   **
618  |  **			resDB.data is NULL.				     **
619  |  ** 									     **
620  |  **   First Edition:	91/10/23					     **
621  |  ** 									     **
622  |  **   Parameters:	register char	**buf	Buffer for the old resource **
623  |  **						database		     **
624  |  ** 									     **
625  |  **   Result:		ErrType	ERR_PARAM	resDB.data != NULL	     **
626  |  **				ERR_ALLOC	out of memory		     **
627  |  **				NO_ERR		Success			     **
628  |  ** 									     **
629  |  **   Attached Globals:	resDB		The data area will be installed as a **
630  |  **					Tcl hash table			     **
631  |  **			dpy		The current display		     **
632  |  ** 									     **
633  |  ** ************************************************************************ **
634  |  ++++*/
635  | 
636  | static	ErrType getOld( register char **buf)
637  | {
638  | 
639  | #if WITH_DEBUGGING_UTIL_1
640  |     ErrorLogger( NO_ERR_START, LOC, _proc_getOld, NULL);
641  | #endif
642  | 
643  |     /**
644  |      **  Allocate memory for the hash table
645  |      **/
646  | 
647  |     if( resDB.data)
648  | 	if( OK != ErrorLogger( ERR_PARAM, LOC, "Resource database", NULL))
649  | 	    return( ERR_PARAM);		/** ------- EXIT (PARAMETER) -----> **/
650  | 
651  |     if( !(resDB.data = (Tcl_HashTable *) malloc( sizeof( Tcl_HashTable))))
652  | 	if( OK != ErrorLogger( ERR_ALLOC, LOC, NULL))
653  | 	    return( ERR_ALLOC);		/** ----- EXIT (OUT OF MEMORY) ----> **/
654  | 
655  |     /**
656  |      **  Initialize the hash table and read in the old resources
657  |      **/
658  | 
659  |     Tcl_InitHashTable( resDB.data, TCL_STRING_KEYS);
660  |     resDB.root = RootWindow( dpy, 0);
661  |     resDB.prop = XA_RESOURCE_MANAGER;
662  |     *buf = XResourceManagerString( dpy);
663  | 
664  |     /**
665  |      **  Success
666  |      **/
667  | 
668  |     return( NO_ERR);
669  | 
670  | } /** End of 'getOld' **/
671  | 
672  | /*++++
673  |  ** ** Function-Header ***************************************************** **
674  |  ** 									     **
675  |  **   Function:		initBuffers					     **
676  |  ** 									     **
677  |  **   Description:	Initilize buffers if not already done, or reinitia-  **
678  |  **			lize some variables if buffers already exists.	     **
679  |  ** 									     **
680  |  **   First Edition:	91/10/23					     **
681  |  ** 									     **
682  |  **   Parameters:	Tcl_Interp	*interp		According Tcl interp.**
683  |  **   			register int is_file	Differs between a single X   **
684  |  **						resource to be modified or  **
685  |  **						a resource file to be merged**
686  |  ** 									     **
687  |  **   Result:		ErrType	ERR_DISPLAY	Cannot open DISPLAY	     **
688  |  **				ERR_ALLOC	ALLOC failure		     **
689  |  **				ERR_EXTRACT				     **
690  |  **				NO_ERR		Success			     **
691  |  ** 									     **
692  |  **   Attached Globals:	dpy		Display will be openend		     **
693  |  **			resDB		Resource database will be filled up **
694  |  **					with the current setup		     **
695  |  **			defines						     **
696  |  ** 									     **
697  |  ** ************************************************************************ **
698  |  ++++*/
699  | 
700  | static	ErrType	initBuffers(	Tcl_Interp *interp,
701  | 				register int is_file)
702  | {
703  |     char *tmpbuf;
704  | 
705  | #if WITH_DEBUGGING_UTIL_1
706  |     ErrorLogger( NO_ERR_START, LOC, _proc_initBuffers, NULL);
707  | #endif
708  | 
709  |     /**
710  |      **  Open the display
711  |      **/
712  | 
713  |     if( !dpy && !(dpy = XOpenDisplay( NULL)))
714  | 	if( OK != ErrorLogger( ERR_DISPLAY, LOC, NULL))
715  | 	    return( ERR_DISPLAY);	/** -------- EXIT (FAILURE) -------> **/
716  | 
717  |     /**
718  |      **  Read in the old setup at first and put it into the resource database
719  |      **/
720  | 
721  |     if( !resDB.data) {
722  | 
723  | 	if( getOld( &tmpbuf) != NO_ERR)	/** NULL if no resources exists      **/
724  | 	    if( OK != ErrorLogger( ERR_ALLOC, LOC, NULL))
725  | 		return( ERR_ALLOC);	/** ----- EXIT (OUT OF MEMORY) ----> **/
726  | 
727  | 	if( tmpbuf && (getEntries(interp, resDB.data, tmpbuf, 0) != NO_ERR))
728  | 	    if( OK != ErrorLogger( ERR_EXTRACT, LOC, NULL))
729  | 		return( ERR_EXTRACT);
730  |     }
731  | 
732  |     /**
733  |      **  Conditionally allocate a buffer and initialize this guy
734  |      **/
735  | 
736  |     if( !buffer) {
737  | 	if( !(buffer = (Tcl_DString *) malloc( sizeof( Tcl_DString)))) {
738  | 	    if( OK != ErrorLogger( ERR_ALLOC, LOC, NULL))
739  | 		return( ERR_ALLOC);	/** ----- EXIT (OUT OF MEMORY) ----> **/
740  | 	} else 
741  | 	    Tcl_DStringInit( buffer);
742  |     } else
743  | 	Tcl_DStringTrunc( buffer, 0);
744  | 
745  |     /**
746  |      **  Set up all the defines according display and screen
747  |      **/
748  | 
749  |     if( defines)
750  | 	defines[ def_base] = '\0';
751  |     else if( is_file) {
752  | 
753  | 	if( !(defines = (char *) malloc( BUFSIZ * sizeof( char))))
754  | 	    if( OK != ErrorLogger( ERR_ALLOC, LOC, NULL))
755  | 		return( ERR_ALLOC);	/** ----- EXIT (OUT OF MEMORY) ----> **/
756  | 
757  | 	/* sprintf( defines, "%s %s ", CPPSTDIN, CPPMINUS); */
758  | 	strcpy( defines, CPPSTDIN);
759  | 	strcat( defines, " ");
760  | 	strcat( defines, CPPMINUS);
761  | 	strcat( defines, " ");
762  | 	doDisplayDefines();
763  | 	doScreenDefines( DefaultScreen( dpy));
764  | 	def_base = strlen( strcat( defines, " "));
765  |     }
766  | 
767  |     /**
768  |      **  Return on success
769  |      **/
770  | 
771  |     return( NO_ERR);
772  | 
773  | } /** End of 'initBuffers' **/
774  | #endif
775  | 
776  | /*++++
777  |  ** ** Function-Header ***************************************************** **
778  |  ** 									     **
779  |  **   Function:		xresourceFinish					     **
780  |  ** 									     **
781  |  **   Description:	Update the resource property if everything is ok.    **
782  |  **			This routine should be called when all properies have**
783  |  **			been defines or updated. Remember that this routine  **
784  |  **			always will be called, even if there was no 	     **
785  |  **			"x-resource" command in the module!		     **
786  |  ** 									     **
787  |  **   First Edition:    91/10/23                                             **
788  |  **                                                                          **
789  |  **   Parameters:                                                            **
790  |  **                                                                          **
791  |  **   Result:                                                                **
792  |  **                                                                          **
793  |  **   Attached Globals: -                                                    **
794  |  **                                                                          **
795  |  ** ************************************************************************ **
796  |  ++++*/
797  | 
798  | void xresourceFinish(register int no_errors)
799  | {
800  | 
801  | #if WITH_DEBUGGING_UTIL_1
802  |     ErrorLogger( NO_ERR_START, LOC, _proc_xresourceFinish, NULL);
803  | #endif
804  | 
805  | #ifdef HAS_X11LIBS
806  | 
807  |     /**
808  |      **  If there is data stored in the resource database, spool it to the
809  |      **  according X server
810  |      **/
811  | 
812  |     if( resDB.data && no_errors)
813  | 	storeResProp( &resDB);
814  | 
815  |     /**
816  |      **  Close the display and free what has been used
817  |      **/
818  | 
819  |     if( dpy)
820  | 	XCloseDisplay( dpy);
821  |     if( buffer)
822  | 	Tcl_DStringFree( buffer);
823  | 
824  | #endif
825  | 
826  | } /** End of 'xresourceFinish' **/
827  | 
828  | /*++++
829  |  ** ** Function-Header ***************************************************** **
830  |  ** 									     **
831  |  **   Function:	 	cmdXResource					     **
832  |  ** 									     **
833  |  **   Description:	Callback function for 'x-resource'. The function    **
834  |  **			sets up a hash table containing all resources to be **
835  |  **			passed to the X server. This hash table will be	     **
836  |  **			flushed whenever the function xresourceFinish is cal-**
837  |  **			led.						     **
838  |  ** 									     **
839  |  **   First Edition:	91/10/23					     **
840  |  ** 									     **
841  |  **   Parameters:	ClientData	 client_data			     **
842  |  **			Tcl_Interp	*interp		According Tcl interp.**
843  |  **			int		 argc		Number of arguments  **
844  |  **			char		*argv[]		Argument array	     **
845  |  ** 									     **
846  |  **   Result:		int	TCL_OK		Successfull completion	     **
847  |  **				TCL_ERROR	Any error		     **
848  |  ** 									     **
849  |  **   Attached Globals:	g_flags		These are set up accordingly before  **
850  |  **					this function is called in order to  **
851  |  **					control everything		     **
852  |  ** 									     **
853  |  ** ************************************************************************ **
854  |  ++++*/
855  | 
856  | int	cmdXResource(	ClientData	 client_data,
857  | 			Tcl_Interp	*interp,
858  | 			int		 argc,
859  | 			char		*argv[])
860  | {
861  |     register FILE	*inp;
862  |     int			 is_file, i,
863  | 			 do_cpp = 1,
864  |     			 opt_ind = 1;
865  | 
866  | #if WITH_DEBUGGING_CALLBACK
867  |     ErrorLogger( NO_ERR_START, LOC, _proc_cmdXResource, NULL);
868  | #endif
869  | 
870  |     /**
871  |      **  Whatis mode?
872  |      **/
873  | 
874  |     if( g_flags & (M_WHATIS | M_HELP))
875  |         return( TCL_OK);		/** ------- EXIT PROCEDURE -------> **/
876  | 
877  |     if( !getenv("DISPLAY")) {
878  |       /* don't bother trying to set display variables, if there is no display */
879  |       return(TCL_OK);
880  |     }
881  |     
882  | 	
883  |     /**
884  |      **  Parameter check
885  |      **/
886  | 
887  |     if( argc > 1 && !strcmp( argv[1], "-nocpp")) {
888  | 	do_cpp = 0;
889  | 	opt_ind++;
890  |     }
891  | 
892  |     if( argc <= opt_ind) {
893  | 	if( OK != ErrorLogger( ERR_USAGE, LOC, argv[0], "[ -nocpp ] strings", NULL))
894  | 	    return( TCL_ERROR);		/** -------- EXIT (FAILURE) -------> **/
895  |     }
896  | 
897  |     /**
898  |      **  Ok, now let's treat all remaining arguments as X resources or
899  |      **  X resource files. At first let's check if it is a file ...
900  |      **/
901  | 
902  |     while( opt_ind < argc) {
903  | 	is_file = (access( argv[ opt_ind], R_OK & F_OK) == 0);
904  | 
905  | #ifdef HAS_X11LIBS
906  | 
907  | 	if( g_flags & M_DISPLAY) {
908  | 	    fprintf( stderr, "xrdb -merge\t ");
909  | 	    for( i=1; i<argc; i++)
910  | 		fprintf( stderr, "%s ", argv[ i]);
911  | 	    fprintf( stderr, "\n");
912  | 
913  | 	} else {
914  | 
915  | 	    /**
916  | 	     **  Initialize read buffers
917  | 	     **/
918  | 
919  | 	    if( NO_ERR != initBuffers(interp, is_file)) 
920  | 		return( TCL_ERROR);	/** -------- EXIT (FAILURE) -------> **/
921  | 
922  | 	    /**
923  | 	     **  This puts all required resources into a text image buffer ...
924  | 	     **/
925  | 
926  | 	    if( !is_file) {
927  | 		Tcl_DStringAppend( buffer, argv[ opt_ind], -1);
928  | 	    } else {
929  | 
930  | 		if( NULL == (inp = (do_cpp ?
931  | 		    popen( strcat( defines, argv[ opt_ind]), "r") : 
932  | 		    fopen( argv[ opt_ind], "r")) ) )
933  | 		    if( OK != ErrorLogger( (do_cpp ? ERR_POPEN : ERR_OPEN), LOC,
934  | 			"argv[ opt_ind]", "reading" ))
935  | 			return( TCL_ERROR);     /** ---- EXIT (FAILURE) ---> **/
936  | 
937  | 		if( TCL_ERROR == readFile( inp, do_cpp))
938  | 		    return( TCL_ERROR);	/** -------- EXIT (FAILURE) -------> **/
939  | 	    }
940  | 
941  | 	    /**
942  | 	     **  ... and this transforms the text image buffer into a Tcl hash
943  | 	     **  table
944  | 	     **/
945  | 
946  | 	    if( NO_ERR != getEntries(interp, resDB.data,
947  | 		Tcl_DStringValue( buffer), g_flags & M_REMOVE)) {
948  | 		return( TCL_ERROR);	/** -------- EXIT (FAILURE) -------> **/
949  | 	    }
950  | 	}
951  | #else
952  | 	if( g_flags & M_DISPLAY) {
953  | 	    fprintf( stderr, "xrdb -merge\t ");
954  | 	    for( i=1; i<argc; i++)
955  | 		fprintf( stderr, "%s ", argv[ i]);
956  | 	    fprintf( stderr, "*** ignored ***\n");
957  | 	}
958  | #endif
959  | 
960  | 	opt_ind++; 
961  | 
962  |     } /** while **/
963  | 
964  | #if WITH_DEBUGGING_CALLBACK
965  |     ErrorLogger( NO_ERR_END, LOC, _proc_cmdXResource, NULL);
966  | #endif
967  | 
968  |     return( TCL_OK);
969  | 
970  | } /** End of 'cmdXResource' **/