Merge branch 'master' of https://git.code.sf.net/p/cdesktopenv/code
[oweals/cde.git] / cde / programs / dtsession / SmSave.c
1 /*
2  * CDE - Common Desktop Environment
3  *
4  * Copyright (c) 1993-2012, The Open Group. All rights reserved.
5  *
6  * These libraries and programs are free software; you can
7  * redistribute them and/or modify them under the terms of the GNU
8  * Lesser General Public License as published by the Free Software
9  * Foundation; either version 2 of the License, or (at your option)
10  * any later version.
11  *
12  * These libraries and programs are distributed in the hope that
13  * they will be useful, but WITHOUT ANY WARRANTY; without even the
14  * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15  * PURPOSE. See the GNU Lesser General Public License for more
16  * details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with these librararies and programs; if not, write
20  * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
21  * Floor, Boston, MA 02110-1301 USA
22  */
23 /* $TOG: SmSave.c /main/27 1998/07/23 18:07:58 mgreess $ */
24 /*************************************<+>*************************************
25  *****************************************************************************
26  **
27  **  File:        SmSave.c
28  **
29  **  Project:     HP DT Session Manager (dtsession)
30  **
31  **  Description:
32  **  -----------
33  **  This file contains all routines that save off the session at any point
34  **  in time.  The session includes settings, resources, and clients.  How
35  **  the session manager behaves during a save depends on how the user has
36  **  configured it to behave.
37  **
38  **
39  *****************************************************************************
40  *************************************<+>*************************************/
41 /*
42  * (c) Copyright 1997 The Open Group
43  * (c) Copyright 1996 Digital Equipment Corporation.
44  * (c) Copyright 1990, 1993, 1994, 1996 Hewlett-Packard Company.
45  * (c) Copyright 1993, 1994, 1996 International Business Machines Corp.
46  * (c) Copyright 1993, 1994, 1996 Sun Microsystems, Inc.
47  * (c) Copyright 1993, 1994, 1996 Novell, Inc. 
48  * (c) Copyright 1996 FUJITSU LIMITED.
49  * (c) Copyright 1996 Hitachi.
50  */
51
52
53 #include <stdio.h>
54 #include <string.h>
55 #include <dirent.h>
56 #include <sys/types.h>
57 #include <sys/stat.h>
58 #include <sys/param.h>
59
60 #include <sys/socket.h>
61 #include <netinet/in.h>
62 #include <netdb.h>
63 #ifdef _SUN_OS
64 #include <arpa/inet.h>
65 #include <string.h>
66 #endif /* _SUN_OS */
67
68 #include <X11/Intrinsic.h>
69 #include <X11/Xutil.h>
70 #include <X11/Xatom.h>
71 #include <X11/StringDefs.h>
72 #include <X11/keysymdef.h>
73 #include <Dt/DtP.h>
74 #include <Dt/Connect.h>
75 #include <Dt/UserMsg.h>
76 #include <Dt/SessionM.h>
77 #include <Dt/MsgLog.h>
78 #include "Sm.h"
79 #include "SmSave.h"
80 #include "SmResource.h"
81 #include "SmError.h"
82 #include "SmGlobals.h"
83 #include "SmUI.h"
84 #include "SmWindow.h"
85 #include "SmProtocol.h"
86 #include "SmRestore.h"
87 #include "SmXSMP.h"
88 #include "SmDB.h"
89 #include "SmProperty.h"
90 #include "SmProp.h"
91
92 #ifdef   MULTIBYTE
93 #include <stdlib.h>
94 #include <limits.h>
95 #endif
96
97 #ifndef __osf__
98 #include <X11/Xlibint.h>
99 #endif /* __osf__ */
100
101 typedef struct _ProxyClientCache {
102         Window          window;
103         int             screen;
104 } ProxyClientCache, *ProxyClientCachePtr;
105
106 static ProxyClientCachePtr proxyList;
107 static int numProxyClients = 0;
108 static int numProxyAllocated = 0;
109 static int proxyCacheAllocIncrement = 10;
110
111 /*
112  * Local Function Declarations
113  */
114 static void CacheProxyClient (
115         Boolean                 firstClient, 
116         int                     screenNum, 
117         Window                  window);
118
119 static void PruneSessionDirectory ();
120
121 static Boolean WriteClientDatabase ();
122
123 static void OutputXSMPClients (
124         ClientDB                outputDB);
125
126 static void OutputProxyClients (
127         ClientDB                outputDB);
128
129 static Boolean CheckRequiredProperties (
130         ClientRecPtr            pClientRec);
131
132 static void PrintStartupInfo( 
133                         ClientDB outputDB,
134                         int screen,
135                         Window window);
136 static int QueryServerSettings( void ) ;
137 static int SaveCustomizeSettings( void ) ;
138 static int OutputResource( void ) ;
139 static void PrintPointerControl( 
140                         XrmDatabase *smBase) ;
141 static void PrintSessionInfo( 
142                         XrmDatabase *smBase) ;
143 static void PrintScreenSaver( 
144                         XrmDatabase *smBase) ;
145 static int PrintFontPaths( 
146                         XrmDatabase *smBase,
147                         char **fontPath,
148                         int numPaths) ;
149 static int PrintKeyboardControl( 
150                         XrmDatabase *smBase);
151 static int PrintPointerMapping( 
152                         XrmDatabase *smBase,
153                         char *buttonRet,
154                         int numButton) ;
155 static void PrintWorkHintString( 
156                         FILE *hintFile,
157                         char *hintString) ;
158 static void PrintCmdHintString( 
159                         FILE *hintFile,
160                         char *hintString) ;
161 static void PrintCmdString( 
162                         FILE *cmdFile,
163                         char *cmdString) ;
164 static void PrintRemoteCmdString( 
165                         FILE *cmdFile,
166                         char *cmdString) ;
167
168
169 /*
170  * Global variables that are exposed
171  */
172 SmScreenInfo            screenSaverVals;
173 SmAudioInfo             audioVals;
174 SmKeyboardInfo          keyboardVals;
175
176 /*
177  * Variables global to this module only
178  */
179
180 /*
181  * used as a buffer to write resources into
182  * before they are written out
183  */
184 static int      resSize = 10000;
185 static char     *resSpec;
186
187 /*
188  * Vars used to maintain state that is sent to the SaveState
189  * function.
190  */
191 static Boolean  saveHome;
192 int             saveMode;
193
194 \f
195
196
197 /*************************************<->*************************************
198  *
199  *  SetupSaveState
200  *
201  *  Description:
202  *  -----------
203  *  Do all of the preliminary work that must be done before the state
204  *  of a checkpoint can be saved.
205  *
206  *  Inputs:
207  *  ------
208  *  saveToHome:  This session is to be saved to the home session or current
209  *  mode = whether the session is to be reset or restarted
210  *
211  *************************************<->***********************************/
212 void
213 SetupSaveState(
214         Boolean         saveToHome,
215         int             mode)
216 {
217   unsigned char         *propData;
218
219   /*
220    * Cache saveToHome and mode - they will be needed after all of
221    * the XSMP clients are saved and the Proxy clients are saved.
222    */
223   saveHome = saveToHome;
224   saveMode = mode;
225
226   /*
227    * First set up the save paths so we know if we have to do anything here
228    */
229   if ((SetSavePath(saveToHome, mode)) == -1)
230       return;
231
232   /*
233    * Take care of the save mode property on the root window so that the
234    * clients will know how to save this file (if they save files)
235    */
236   if(saveToHome == False)
237   {
238       propData = (unsigned char *) SM_CURRENT_DIRECTORY;
239   }
240   else
241   {
242       propData = (unsigned char *) SM_HOME_DIRECTORY;
243   }
244
245   /*
246    * Change the save mode to give the subdirectory saved to
247    *
248    */
249   XChangeProperty(smGD.display, RootWindow(smGD.display, 0),
250                   XaSmSaveMode, XA_STRING, 8, PropModeReplace,
251                    propData, strlen((char *)propData));
252   XFlush(smGD.display);
253 }
254
255 /*************************************<->*************************************
256  *
257  *  SaveState
258  *
259  *  Description:
260  *  -----------
261  *  Tells the SM to notify the XSMP clients to save their state.
262  *  The function CompleteSave fill be invoked after the XSMP clients
263  *  have completed their save.
264  *
265  *  Inputs:
266  *  ------
267  *  saveToHome:  This session is to be saved to the home session or current
268  *  mode = whether the session is to be reset or restarted
269  *
270  *  Comments:
271  *  --------
272  *  As a result of this routine files will be saved in the users .dt directory
273  *  that will allow the session manager to restore the session at a later
274  *  time.
275  *
276  *  NOTE - this function is called as the result of the following
277  *      events:
278  *
279  *      1. User initiates a Save Home Session via the Style Manager
280  *      2. The Session Manager receives a SM_XSESSION_EXIT message
281  *      3. The Session Manager receives a SM_SAVE_SESSION message
282  * 
283  *************************************<->***********************************/
284 void
285 SaveState(
286         Boolean                 saveToHome,
287         int                     mode,
288         int                     saveType,
289         Bool                    shutdown,
290         int                     interactStyle,
291         Bool                    fast,
292         Bool                    global)
293 {
294   /*
295    * Notify XSMP clients of the save
296    */
297   SaveYourselfReqProc (NULL, NULL, saveType, shutdown, interactStyle, 
298                         fast, global);
299 }
300
301
302 /*************************************<->*************************************
303  *
304  *  CompleteSave 
305  *
306  *  Description:
307  *  -----------
308  *  Called after the XSMP clients have saved their state.  In charge of 
309  *  calling all routines that save session information for XSMP client
310  *  and Proxy clients as well as settings, resources, etc.
311  *
312  *  Outputs:
313  *  -------
314  *
315  *************************************<->***********************************/
316 int 
317 CompleteSave ( )
318 {
319
320   FILE                  *convertFP;
321   unsigned char         *propData;
322   char                  *tmpPath, tmpVersion[20];
323
324   Boolean               saveToHome = saveHome;
325   int                   mode = saveMode;
326
327   /*
328    * If this is a Home session and the user is logging out, return
329    * now - the XSMP clients have all responded to the save and the
330    * ICCC clients don't save their state when a Home session is
331    * exited.
332    */
333   if (smGD.sessionType == HOME_SESSION && 
334       smXSMP.saveState.shutdown == True && 
335       smSettings.startState == DtSM_HOME_STATE)
336       return (0);
337
338   /*
339    * Start a wait state - don't want anything to happen while
340    * ICCC clients are being saved
341    */
342   ShowWaitState(True);
343   
344   resSpec = (char *) SM_MALLOC(resSize * sizeof(char));
345   if (resSpec==NULL)
346   {
347       PrintErrnoError(DtError, smNLS.cantMallocErrorString);
348       return(-1);
349   }
350
351   if (!WriteClientDatabase ()) {
352       ShowWaitState(False);
353       SM_FREE(resSpec);
354       return (-1);
355   }
356
357   /*
358    * WARNING - THIS VARIABLE MUST BE SET BEFORE THE SETTING INFORMATION IS
359    * CALLED - SETTINGS MUST BE SAVED BEFORE RESOURCES BECAUSE SETTINGS
360    * GETS INFORMATION SAVED IN RESOURCES (smToSet.dClickBuf)
361    */
362   smToSet.dClickBuf[0] = NULL;
363
364   /*
365    * Do the actual output of the X settings information.  Output will
366    * go to the resource database at this point and be written to the
367    * resource file with the rest of the resources (below).
368    */
369   if(smRes.querySettings == True)
370   {
371       QueryServerSettings();
372   }
373   else
374   {
375       SaveCustomizeSettings();
376   }
377
378   /*
379    * Do the output of resource information.
380    */
381   if(OutputResource())
382   {
383       ShowWaitState(False);
384       SM_FREE(resSpec);
385       return(-1);
386   }
387
388   XDeleteProperty(smGD.display, DefaultRootWindow(smGD.display), XaSmSaveMode);
389  
390   /* 
391    * Go save the default palette at this time
392    */
393   if(saveToHome == True)
394      SaveDefaultPalette(smGD.display, smGD.savePath, DtSM_HOME_STATE);
395   else
396      SaveDefaultPalette(smGD.display, smGD.savePath, mode);
397  
398   PruneSessionDirectory ();
399
400   ShowWaitState(False);
401
402   return(0);
403 }
404
405
406 /*************************************<->*************************************
407  *
408  *  PruneSessionDirectory -
409  *
410  *  Description: Removes the oldest session direcotry if the number
411  *      of directories exceeds the smRes.numSessionsBackedup.
412  *
413  *  Inputs: void
414  * 
415  *************************************<->***********************************/
416 static void
417 PruneSessionDirectory ()
418 {
419         DIR                     * dirp;
420         struct dirent           * dp;
421         int                     numDirs = 0;
422         struct                  stat buf;
423         time_t                  oldestTime;
424         char                    *clientDB;
425         char                    *dirPrefix;
426         char                    *oldDir;        /* the .old dir */
427         char                    *oldestDir;
428         char                    *saveDir;
429         char                    *tmpDir;
430
431         if ((dirp = opendir (smGD.savePath)) == NULL)
432                 return;
433
434         clientDB = (char*) XtMalloc((2*MAXPATHLEN) + 1);
435         oldestDir = (char*) XtMalloc(MAXPATHLEN + 1);
436         saveDir = (char*) XtMalloc(MAXPATHLEN + 1);
437         tmpDir = (char*) XtMalloc(MAXPATHLEN + 1);
438         
439         dirPrefix = XtMalloc (strlen (smGD.restoreSession) + 2);
440         sprintf (dirPrefix, "%s.", smGD.restoreSession);
441         if (strlen (dirPrefix) > 5) {
442                 dirPrefix[4] = '.';
443                 dirPrefix[5] = '\000';
444         }
445
446         oldDir = XtMalloc (strlen (smGD.restoreSession) + 
447                            strlen (SM_OLD_EXTENSION) + 2);
448         sprintf (oldDir, "%s.%s", smGD.restoreSession, SM_OLD_EXTENSION);
449
450         (void) time (&oldestTime);
451
452         while ((dp = readdir (dirp)) != NULL) {
453                 /*
454                  * Do some sanity checks to limit the number of * stat() 
455                  * calls.  Note that for backward compatibility with CDE 
456                  * 1.0 if a ".old" directory exists, keep it.
457                  */
458                 if ((!strcmp (dp->d_name, ".")) || 
459                     (!strcmp (dp->d_name, "..")) ||
460                     (!strcmp (dp->d_name, oldDir)) ||
461                     (strncmp  (dp->d_name, dirPrefix, strlen (dirPrefix))))
462                         continue;
463
464                 sprintf (tmpDir, "%s/%s", smGD.savePath, dp->d_name);
465
466                 if (((stat (tmpDir, &buf)) == 0) && S_ISDIR (buf.st_mode)) {
467                         if (buf.st_mtime < oldestTime) {
468                                 /*
469                                  * Keep track of the oldest dir in case
470                                  * it needs to be removed
471                                  */
472                                 oldestTime = buf.st_mtime;
473                                 strcpy (oldestDir, tmpDir);
474                         }
475                         numDirs++;
476                 }
477         }
478         closedir(dirp);
479
480         if (numDirs > smRes.numSessionsBackedup) {
481                 /*
482                  * Need to remove the oldest directory.  The general
483                  * process (using "home" as an example) is:
484                  *
485                  *   mv home home.tmp
486                  *   mv oldestDir home
487                  *   Execute the DiscardCommand props in the home directory
488                  *   rm -rf home
489                  *   mv home.tmp home
490                  *
491                  * The reason this is done is because when the 
492                  * DiscardCommand were saved, the directory at the time of
493                  * the save contained "home".  For example, a DiscardCommand
494                  * may look like:
495                  *
496                  *    rm -f /u/somebody/.dt/sessions/home/app.state
497                  *
498                  * yet because of the session archiving, the app.state
499                  * file may actually be in a directory like:
500                  *
501                  *    /u/somebody/.dt/sessions/home.<tempnam>
502                  */ 
503
504                 char            * tmpName;
505                 char            * tmp;
506
507                 tmpName = (char *) XtMalloc (strlen (smGD.restoreSession) + 2);
508                 sprintf (tmpName, "%s.", smGD.restoreSession);
509                 if (strlen (tmpName) > 5) {
510                         tmpName[4] = '.';
511                         tmpName[5] = '\000';
512                 }
513                 tmp = (char *) tempnam (smGD.savePath, tmpName);
514
515                 sprintf (saveDir, "%s/%s", smGD.savePath, smGD.restoreSession);
516
517                 MoveDirectory (saveDir, tmp, False);
518                 MoveDirectory (oldestDir, saveDir, False);
519
520                 sprintf (clientDB, "%s/%s/%s", smGD.savePath, 
521                                 smGD.restoreSession, SM_CLIENT_FILE2);
522
523                 ExecuteDiscardCommands (clientDB);
524
525                 MoveDirectory (tmp, saveDir, True);
526
527                 free (tmp);
528                 XtFree (tmpName);
529         }
530
531         /*
532          * If exiting and the current session is a Current session
533          * but smSettings.startState has been set to to a 
534          * Home session (this would be true if a user started a 
535          * Current session but used the Style Manager to set the session
536          * upon next login to be a Home session), then if a home
537          * directory exists, mv current to current.old
538          */
539
540         if (smXSMP.saveState.shutdown && smGD.sessionType == CURRENT_SESSION &&
541             smSettings.startState == DtSM_HOME_STATE) {
542
543                 sprintf (tmpDir, "%s/%s", smGD.savePath, SM_HOME_DIRECTORY);
544
545                 if (((stat (tmpDir, &buf)) == 0) && S_ISDIR (buf.st_mode)) {
546
547                         sprintf (saveDir, "%s/%s", smGD.savePath, oldDir);
548
549                         if (((stat (saveDir, &buf)) == 0) && 
550                                         S_ISDIR (buf.st_mode)) {
551
552                                 sprintf (clientDB, "rm -rf %s", saveDir);
553                                 SystemCmd (clientDB);
554
555                                 sprintf (oldestDir, "%s/%s", smGD.savePath, 
556                                         SM_CURRENT_DIRECTORY);
557
558                                 MoveDirectory (oldestDir, saveDir, False);
559                         }
560                 }
561         }
562
563         XtFree(clientDB);
564         XtFree(dirPrefix);
565         XtFree(oldDir);
566         XtFree(oldestDir);
567         XtFree(saveDir);
568         XtFree(tmpDir);
569 }
570
571
572 /*************************************<->*************************************
573  *
574  *  WriteClientDatabase -
575  *
576  *  Description: Writes the client database (for XSMP and Proxy clients)
577  *
578  *  Inputs: None
579  * 
580  *  Returns True if the database is wrtten OK; False otherwise
581  *
582  *************************************<->***********************************/
583 static Boolean
584 WriteClientDatabase ()
585 {
586         char                    *db;
587         ClientDB                outputDB;
588
589         db = (char*) XtMalloc(MAXPATHLEN + 1);
590         (void) sprintf (db, "%s/%s/%s", smGD.savePath, smGD.restoreSession,
591                         SM_CLIENT_FILE2);
592
593         if ((outputDB = OpenOutputClientDB (db, smXSMP.dbVersion,
594                                             smXSMP.dbSessionId)) == NULL) {
595                 char            * str;
596
597                 str = strdup ((char *) GETMESSAGE (40, 18,
598                         "The following database file cannot be opened for writing:\n\n   %s\n\nThe session will not be saved."));
599
600                 if (str) {
601                         DtMsgLogMessage (smGD.programName, DtMsgLogError,
602                                          str, db);
603                         free (str);
604                 }
605                         
606                 XtFree(db);
607                 return (False);
608         }
609         XtFree(db);
610
611         OutputProxyClients (outputDB);
612         
613         OutputXSMPClients (outputDB);
614
615         CloseClientDB (outputDB, True);
616
617         return (True);
618 }
619
620
621 /*************************************<->*************************************
622  *
623  *  OutputProxyClients -
624  *
625  *  Description: Outputs the Proxy clients
626  *
627  *  Inputs: database pointer
628  * 
629  *  Returns: void
630  *
631  *************************************<->***********************************/
632 static void
633 OutputProxyClients (
634         ClientDB                outputDB)
635 {
636         int             i;
637         for (i = 0; i < numProxyClients; i++)
638                 PrintStartupInfo (outputDB, 
639                                   proxyList[i].screen, 
640                                   proxyList[i].window);
641 }
642
643
644 /*************************************<->*************************************
645  *
646  *  OutputXSMPClients -
647  *
648  *  Description: Outputs the XSMP clients
649  *
650  *  Inputs: database pointer
651  * 
652  *  Returns: void
653  *
654  *************************************<->***********************************/
655 static void
656 OutputXSMPClients (
657         ClientDB                outputDB)
658 {
659         XSMPClientDBRec         dbRec;
660         ClientRecPtr            pClientRec;
661
662         for (pClientRec = connectedList; pClientRec != NULL;
663                 pClientRec = pClientRec->next) {
664
665                 dbRec.restartHint = pClientRec->restartHint;
666                 (void) GetCardPropertyValue (pClientRec, SmRestartStyleHint, 
667                                              &dbRec.restartHint);
668
669                 if (dbRec.restartHint == SmRestartNever)
670                         continue;
671
672                 if (!pClientRec->active)
673                         continue;
674
675                 if (!CheckRequiredProperties (pClientRec))
676                         continue;
677
678                 dbRec.screenNum = 0;
679
680                 dbRec.clientId = pClientRec->clientId;
681                 dbRec.clientHost = pClientRec->clientHost;
682
683                 dbRec.program = GetArrayPropertyValue (pClientRec, 
684                                         SmProgram);
685                 dbRec.cwd = GetArrayPropertyValue (pClientRec, 
686                                         SmCurrentDirectory);
687
688                 dbRec.restartCommand = GetListOfArrayPropertyValue (pClientRec,
689                                         SmRestartCommand);
690                 dbRec.cloneCommand = GetListOfArrayPropertyValue (pClientRec,
691                                         SmCloneCommand);
692                 dbRec.discardCommand = GetListOfArrayPropertyValue (pClientRec,
693                                         SmDiscardCommand);
694                 dbRec.environment = GetListOfArrayPropertyValue (pClientRec,
695                                         SmEnvironment);
696
697                 if (!PutXSMPClientDBRec (outputDB, &dbRec)) {
698                         
699                         char            *str;
700
701                         str = strdup ((char *) GETMESSAGE (40, 19,
702                                 "Application '%s' cannot be saved."));
703
704                         if (str) {
705                                 DtMsgLogMessage (smGD.programName, 
706                                                  DtMsgLogError, str, 
707                                                  dbRec.program);
708                                 free (str);
709                         }
710                 }
711
712                 if (dbRec.restartCommand)
713                         XtFree ((char *) dbRec.restartCommand);
714                 if (dbRec.cloneCommand)
715                         XtFree ((char *) dbRec.cloneCommand);
716                 if (dbRec.discardCommand)
717                         XtFree ((char *) dbRec.discardCommand);
718                 if (dbRec.environment)
719                         XtFree ((char *) dbRec.environment);
720         }
721 }
722
723
724 /*************************************<->*************************************
725  *
726  *  CheckRequiredProperties -
727  *
728  *  Description: Check a client's required properties
729  *
730  *  Inputs: ClientRecPtr
731  * 
732  *  Returns: True if the client has all of the required properties;
733  *      otherwise returns False
734  *
735  *************************************<->***********************************/
736 static Boolean
737 CheckRequiredProperties (
738         ClientRecPtr            pClientRec)
739 {
740         PropertyRecPtr          pPropRec;
741         char                    * str;
742         char                    * program = NULL;
743
744         if ((program = GetArrayPropertyValue (pClientRec, SmProgram)) == NULL){
745
746                 str = strdup ((char *) GETMESSAGE (40, 23,
747                         "The following application did not set the required\nproperty '%s':\n\n   %s\n\nThis application will not be saved."));
748
749                 if (str) {
750                         DtMsgLogMessage (smGD.programName, DtMsgLogError, str, 
751                                          SmProgram, pClientRec->clientId);
752                         free (str);
753                 }
754                 return (False);
755         }
756
757         if ((pPropRec = GetPropertyRec (pClientRec, SmRestartCommand)) == NULL){
758
759                 str = strdup ((char *) GETMESSAGE (40, 24,
760                         "24 Application '%s' will not be saved because it did not set\nthe required property '%s'."));
761
762                 if (str) {
763                         DtMsgLogMessage (smGD.programName, DtMsgLogError,
764                                          str, program, SmRestartCommand);
765                         free (str);
766                 }
767                 return (False);
768         }
769
770         if ((pPropRec = GetPropertyRec (pClientRec, SmCloneCommand)) == NULL) {
771                 /*
772                  * This is a warning - still want to save the client
773                  */
774                 str = strdup ((char *) GETMESSAGE (40, 25,
775                         "Application '%s' did not set the required property '%s'."));
776
777                 if (str) {
778                         DtMsgLogMessage (smGD.programName, DtMsgLogWarning, str,
779                                          program, SmCloneCommand);
780                         free (str);
781                 }
782         }
783
784         return (True);
785 }
786
787
788 /*************************************<->*************************************
789  *
790  *  NotifyProxyClients -
791  *
792  *
793  *  Description:
794  *  -----------
795  *  Notify Proxy clients - calls routines to find top level windows, and 
796  *  notifies the Proxy clients about an impending save.
797  *
798  *  Inputs:
799  *  ------
800  *
801  *  Outputs:
802  *  -------
803  *
804  *************************************<->***********************************/
805 void 
806 NotifyProxyClients ( )
807 {
808   unsigned int          topListLength, containedListLength;
809   WindowInfo            * topList;      /* list of top-level windows */
810   WindowInfo            topLevelWindowInfo;
811   Atom                  actualType;
812   int                   actualFormat;
813   unsigned long         nitems;
814   unsigned long         leftover;
815   unsigned char         * data = NULL;
816   Boolean               containedClient;
817   int                   i, j;
818   int                   numClients = 0;
819
820   /*
821    * Save off the clients currently running.  This will be done
822    * one screen at a time
823    */
824   for(j = 0;j < smGD.numSavedScreens;j++)
825   {
826       /*
827        * Get a list of children of the root window. If there are none
828        * return with an error condition.
829        */
830       if(GetTopLevelWindows(j,&topList,&topListLength, &containedListLength))
831           return;
832   
833       /*
834        * Figure out how to restart the client associate with each window.
835        */
836       for (i=0 ; i<(topListLength + containedListLength); ++i)
837       {
838           topLevelWindowInfo=topList[i];
839
840           if(i >= topListLength)
841           {
842               containedClient = True;
843           }
844           else
845           {
846               containedClient = False;
847           }
848       
849           /*
850            * If this window is my window - skip it I don't want to
851            * save myself because I will be started automatically
852            */
853           if (XGetWindowProperty(smGD.display, topLevelWindowInfo.wid,
854                                  XA_WM_CLASS,0L,
855                                  (long)BUFSIZ,False,XA_STRING,&actualType,
856                                  &actualFormat,&nitems,&leftover,
857                                  &data)==Success)
858           {
859               if (actualType==XA_STRING &&
860                   !strcmp((char *) data, SM_RESOURCE_NAME))
861               {
862                   SM_FREE((char *)data);
863                   continue;
864               }
865               else
866               {
867                   SM_FREE((char *)data);
868               }
869           }
870       
871          /*
872           * If the client participates in the WM_SAVE_YOURSELF protocol, 
873           * send the appropriate messages and wait for the proper 
874           * responses.
875           */
876          (void) SaveYourself(topLevelWindowInfo);
877
878          CacheProxyClient ((numClients == 0), j, topLevelWindowInfo.wid);
879
880          numClients++;
881       }
882
883       /*
884        * Free space malloc'd by GetTopLevelWindowInfo
885        */
886       if(topListLength > 0)
887       {
888           SM_FREE((char *) topList);
889       }
890   }
891 }
892
893
894 /*************************************<->*************************************
895  *
896  *  CacheProxyClient -
897  *
898  *  Description:
899  *  -----------
900  *  Add a Proxy client to the proxy client cache.
901  *
902  *  Inputs:
903  *  ------
904  *  firstClient - if True, this is the first client in the cache
905  *  screenNum - the client's screen number
906  *  window - the client's window number
907  *
908  *  Outputs:
909  *  -------
910  *
911  *************************************<->***********************************/
912 static void CacheProxyClient (
913         Boolean                 firstClient, 
914         int                     screenNum, 
915         Window                  window)
916 {
917         if (firstClient)
918                 numProxyClients = 0;
919
920         if (numProxyClients >= numProxyAllocated) {
921                 numProxyAllocated += proxyCacheAllocIncrement;
922                 proxyList = (ProxyClientCachePtr) XtRealloc ((char *) proxyList,
923                         numProxyAllocated * sizeof (ProxyClientCache));
924         }
925
926         proxyList[numProxyClients].screen = screenNum;
927         proxyList[numProxyClients].window = window;
928
929         numProxyClients++;
930 }
931
932
933 \f
934 /*************************************<->*************************************
935  *
936  *  PrintStartupInfo -
937  *
938  *  Description:
939  *  -----------
940  *  Output startup information for one proxy client 
941  *
942  *  Inputs:
943  *  ------
944  *  outputDB = a pointer to the client database
945  *  screen = which screen the client is running on
946  *  window = the top level window of the lient
947  * 
948  *  Outputs:
949  *  -------
950  *
951  *  Comments:
952  *  --------
953  * 
954  *************************************<->***********************************/
955 static void
956 PrintStartupInfo(
957         ClientDB outputDB,
958         int screen,
959         Window window)
960 {
961   Boolean               writeStatus;
962   int                   wargc;
963   char                  ** wargv;
964   char                  *clientMachine;
965   XWindowAttributes     windowAttr;
966   Boolean               remoteHost;
967   Boolean               xsmpClient;
968   ProxyClientDBRec      dbRec;
969   char                  ** argv;
970   int                   i;
971   Status                status;
972
973 #ifdef _SUN_OS /* pull out quotes from OpenWindow tools commands */
974   char                  * ptr;
975 #endif
976
977   /*
978    * Get the attributes and properties on the window
979    */
980   if ((XGetWindowAttributes(smGD.display, window, &windowAttr)) == 0)
981     return;
982
983   if (GetStandardProperties(window, screen, &wargc, &wargv, 
984                             &clientMachine, &xsmpClient)==0)
985     return;
986   
987   /*
988    * If the client doesn't have a WM_COMMAND property, we can't restart it
989    */
990   if (!wargv||!wargv[0]) 
991     return;
992
993   /*
994    * If the client is an XSMP client, don't save it as a Proxy client.
995    */
996   if (xsmpClient)
997      return;
998
999 #ifdef _SUN_OS /* pull out quotes from OpenWindow tools commands */
1000   if (wargc == 1) {
1001      ptr = wargv[0];
1002      /* check for foreground */
1003      if ((ptr = strstr (ptr, "fg \"")) != NULL) {
1004         if (strlen (ptr) > 3)
1005            ptr[3] = ' ';
1006         if ((ptr = strchr (ptr, '"')) != NULL)
1007            ptr[0] = ' ';
1008      }
1009
1010      /* check for background */
1011      if ((ptr = strstr (wargv[0], "bg \"")) != NULL) {
1012         if (strlen (ptr) > 3)
1013            ptr[3] = ' ';
1014         if ((ptr = strchr (ptr, '"')) != NULL)
1015            ptr[0] = ' ';
1016      }
1017   }
1018 #endif
1019   
1020   /*
1021    * Find out if the client is running locally
1022    */
1023   dbRec.clientHost = NULL;
1024   if(clientMachine != NULL)
1025   {
1026       if(DtIsLocalHostP(clientMachine) == FALSE)
1027       {
1028           dbRec.clientHost = clientMachine;
1029       }
1030   }
1031
1032   /*
1033    * Must NULL-terminate the WM_COMMAND before writing it
1034    */
1035   argv = (char **) XtMalloc ((wargc + 1) * sizeof (char *));
1036   if (!argv)
1037       return;
1038
1039   for (i = 0; i < wargc; i++)
1040       argv[i] = wargv[i];
1041   argv[wargc] = NULL;
1042
1043   dbRec.command = argv;
1044   dbRec.screenNum = screen;
1045
1046   writeStatus = PutProxyClientDBRec (outputDB, &dbRec);
1047
1048   XtFree ((char *) argv);
1049
1050   if (writeStatus == False)
1051   {
1052     /*
1053      * There should be an error written here - however
1054      * since this means that there already is a write error to
1055      * the disk - no error can be written
1056      */
1057     (void) CloseClientDB (outputDB, True);
1058
1059     /*
1060      * On a write error - move the old session back to
1061      * be used and exit with an error condition
1062      */
1063     XFreeStringList(wargv);
1064
1065     strcat(smGD.savePath, "/");
1066     if(smGD.sessionType == HOME_SESSION)
1067     {
1068         strcat(smGD.savePath, SM_HOME_DIRECTORY);
1069     }
1070     else
1071     {
1072         strcat(smGD.savePath, SM_CURRENT_DIRECTORY);
1073     }
1074
1075     MoveDirectory(smGD.etcPath, smGD.savePath, False);
1076     SM_EXIT(-1);
1077   }
1078
1079   /*
1080    * Free up the argv property
1081    */
1082   XFreeStringList(wargv);
1083 }
1084
1085 \f
1086 /*************************************<->*************************************
1087  *
1088  *  QueryServerSettings ()
1089  *
1090  *
1091  *  Description:
1092  *  -----------
1093  *  Query the server for all it's current settings information, and then
1094  *  write that information to the session managers settings resource file.
1095  *
1096  *  Inputs:
1097  *  ------
1098  *  smGD.settingPath = (global) pointer to file where
1099  *                      setting info should be saved.
1100  * 
1101  *  Outputs:
1102  *  -------
1103  *
1104  *  Comments:
1105  *  --------
1106  *  This routine slows down the session manager's shutdown process
1107  *  considerably, and is therefore only called when the user specifies
1108  *  that all settings should be queried.  Otherwise, only settings set in
1109  *  the customizer should be saved.
1110  * 
1111  *************************************<->***********************************/
1112 static int 
1113 QueryServerSettings( void )
1114 {
1115     int                 i, numLoops;
1116     char                **fontPath;
1117     KeySym              *tmpSyms, tmpMod;
1118     XModifierKeymap     *modMap;
1119     char                *buttonRet;
1120     int                 numButton, numPaths;
1121     int                 buttonSize = 128;
1122     XrmDatabase         smBase = NULL;
1123     char                tmpChar[35];
1124
1125
1126     buttonRet = (char *) SM_MALLOC(buttonSize * sizeof(char));
1127     if (buttonRet==NULL)
1128     {
1129         PrintErrnoError(DtError, smNLS.cantMallocErrorString);
1130         return(-1);
1131     }
1132
1133     /*
1134      * First set up all pointer control parameters
1135      * These are simple integer values that get saved
1136      * here, then read into a structure and set at restore
1137      * time
1138      */
1139     XGetPointerControl(smGD.display, &smSettings.accelNum,
1140                        &smSettings.accelDenom,
1141                        &smSettings.threshold);
1142     PrintPointerControl(&smBase);
1143
1144     /*
1145      * Get all the screen saver values.  These are all simple integer
1146      * values that are read back into a structure and set at restore
1147      * time.
1148      */
1149     XGetScreenSaver(smGD.display, &screenSaverVals.smTimeout,
1150                     &screenSaverVals.smInterval,
1151                     &screenSaverVals.smPreferBlank,
1152                     &screenSaverVals.smAllowExp);
1153     PrintScreenSaver(&smBase);
1154
1155     /*
1156      *  Write out all font path information. 
1157      *  Write out all the paths one by one concatenated by commas.
1158      */
1159     fontPath = XGetFontPath(smGD.display, &numPaths);
1160     if(fontPath != NULL)
1161     {
1162         if(PrintFontPaths(&smBase, fontPath, numPaths) != 0)
1163         {
1164             XFreeFontPath(fontPath);
1165             SM_FREE(buttonRet);
1166             return(-1);
1167         }
1168     }
1169
1170     /*
1171      * Write out all the keyboard control information.
1172      */
1173     XGetKeyboardControl(smGD.display, &smSettings.kbdState);
1174     if(PrintKeyboardControl(&smBase) != 0)
1175     {
1176         if (fontPath)
1177             XFreeFontPath(fontPath);
1178         return(-1);
1179     }
1180     
1181     /*
1182      * Put all the button modifier information into the resource database.
1183      * Find out the number of buttons and then write out the character
1184      * string for each button.
1185      */
1186     numButton = XGetPointerMapping(smGD.display, (unsigned char *)buttonRet, 128);
1187     if(numButton > 128)
1188     {
1189         buttonRet = (char *) SM_REALLOC(buttonRet, numButton * sizeof(char));
1190         if (buttonRet==NULL)
1191         {
1192             if (fontPath)
1193                 XFreeFontPath(fontPath);
1194             PrintErrnoError(DtError, smNLS.cantMallocErrorString);
1195             return(-1);
1196         }
1197         numButton = XGetPointerMapping(smGD.display, (unsigned char *)buttonRet, numButton);
1198     }
1199     if(PrintPointerMapping(&smBase, buttonRet,
1200                            numButton)  != 0)
1201     {
1202         if (fontPath)
1203             XFreeFontPath(fontPath);
1204         SM_FREE(buttonRet);
1205         return(-1);
1206     }
1207
1208     /*
1209      * Put all keyboard mapping information in the resource database
1210      * Don't bother putting this in a separate print function because
1211      * the customizer doesn't do keymap savings this way.
1212      */
1213     smSettings.numKeyCode = smGD.display->max_keycode -
1214         smGD.display->min_keycode;
1215     tmpSyms = XGetKeyboardMapping(smGD.display,
1216                                   (KeyCode) smGD.display->min_keycode,
1217                                   smSettings.numKeyCode,
1218                                   &smSettings.keySymPerCode);
1219     if(tmpSyms == NULL)
1220     {
1221         if (fontPath)
1222             XFreeFontPath(fontPath);
1223         SM_FREE(buttonRet);
1224         PrintErrnoError(DtError, smNLS.cantMallocErrorString);
1225         return(-1);
1226     }
1227     
1228     strcpy(resSpec, SM_RESOURCE_CLASS);
1229     strcat(resSpec, "*");
1230     strcat(resSpec, SmCnumKeyCode);
1231     strcat(resSpec, ":");
1232     sprintf(tmpChar,"%d", smSettings.numKeyCode);
1233     strcat(resSpec, tmpChar);
1234     XrmPutLineResource( &smBase, resSpec);
1235     
1236     strcpy(resSpec, SM_RESOURCE_CLASS);
1237     strcat(resSpec, "*");
1238     strcat(resSpec, SmCkeySymsPerKey);
1239     strcat(resSpec, ":");
1240     sprintf(tmpChar,"%d", smSettings.keySymPerCode);
1241     strcat(resSpec, tmpChar);
1242     XrmPutLineResource( &smBase, resSpec);
1243
1244     strcpy(resSpec, SM_RESOURCE_CLASS);
1245     strcat(resSpec, "*");
1246     strcat(resSpec, SmCkeySyms);
1247     strcat(resSpec, ":");
1248     numLoops = smSettings.numKeyCode * smSettings.keySymPerCode;
1249     sprintf(tmpChar,"%ld", tmpSyms[0]);
1250     strcat(resSpec, tmpChar);
1251     for(i = 1;i < numLoops;i++)
1252     {
1253         if((strlen(resSpec) + 5) >= resSize)
1254         {
1255             resSize += 5000;
1256             resSpec = (char *) SM_REALLOC(resSpec, resSize * sizeof(char));
1257             if (resSpec==NULL)
1258             {
1259                 SM_FREE((char *)tmpSyms);
1260                 if (fontPath)
1261                     SM_FREE((char *)fontPath);
1262                 SM_FREE(buttonRet);
1263                 PrintErrnoError(DtError, smNLS.cantMallocErrorString);
1264                 return(-1);
1265             }
1266         }
1267         strcat(resSpec, ",");
1268         sprintf(tmpChar,"%ld", tmpSyms[i]);
1269         strcat(resSpec, tmpChar);
1270     }
1271     XrmPutLineResource( &smBase, resSpec);
1272         
1273
1274     /*
1275      * Write out all keyboard modifier info.  This will be a pretty slow
1276      * process considering that every keycode will have to be turned into
1277      * a keysym before writing.  Don't bother putting print stuff in a
1278      * separate function because it will not be handled in the customize
1279      * version.
1280      */
1281     modMap = XGetModifierMapping(smGD.display);
1282     if(modMap == NULL)
1283     {
1284         SM_FREE((char *)tmpSyms);
1285         if (fontPath)
1286             XFreeFontPath(fontPath);
1287         SM_FREE(buttonRet);
1288         PrintErrnoError(DtError, smNLS.cantMallocErrorString);
1289         return(-1);
1290     }
1291
1292     strcpy(resSpec, SM_RESOURCE_CLASS);
1293     strcat(resSpec, "*");
1294     strcat(resSpec, SmCmaxKeyPerMod);
1295     strcat(resSpec, ":");
1296     sprintf(tmpChar,"%d", modMap->max_keypermod);
1297     strcat(resSpec, tmpChar);
1298     XrmPutLineResource( &smBase, resSpec);
1299
1300     strcpy(resSpec, SM_RESOURCE_CLASS);
1301     strcat(resSpec, "*");
1302     strcat(resSpec, SmCmodMap);
1303     strcat(resSpec, ":");
1304     numLoops = (8 * modMap->max_keypermod) - 1;
1305     for(i = 0;i <= numLoops;i++)
1306     {
1307         if((strlen(resSpec) + 9) >= resSize)
1308         {
1309             resSize += 5000;
1310             resSpec = (char *) SM_REALLOC(resSpec, resSize * sizeof(char));
1311             if (resSpec==NULL)
1312             {
1313                 SM_FREE((char *)tmpSyms);
1314                 XFreeModifiermap(modMap);
1315                 if (fontPath)
1316                     XFreeFontPath(fontPath);
1317                 SM_FREE(buttonRet);
1318                 PrintErrnoError(DtError, smNLS.cantMallocErrorString);
1319                 return(-1);
1320             }
1321         }
1322         tmpMod = XKeycodeToKeysym(smGD.display, modMap->modifiermap[i], 0);
1323         sprintf(tmpChar,"%ld", tmpMod);
1324         strcat(resSpec, tmpChar);
1325         if(i != numLoops)
1326         {
1327             strcat(resSpec, ",");
1328         }
1329     }
1330     XrmPutLineResource( &smBase, resSpec);
1331     
1332
1333     /*
1334      * Print session manager settings
1335      */
1336     PrintSessionInfo(&smBase);
1337     
1338     /*
1339      * Write settings resources out to disk
1340      */
1341     XrmPutFileDatabase(smBase, smGD.settingPath);
1342
1343     /*
1344      * Free all allocated resources
1345      */
1346     if (fontPath)
1347         XFreeFontPath(fontPath);
1348     SM_FREE((char *)tmpSyms);
1349     XFreeModifiermap(modMap);
1350     SM_FREE(buttonRet);
1351     XrmDestroyDatabase(smBase);
1352     return(0);
1353 }
1354
1355 \f
1356 /*************************************<->*************************************
1357  *
1358  *  SaveCustomizeSettings ()
1359  *
1360  *
1361  *  Description:
1362  *  ------------
1363  *  Saves those settings that have been set in the customizer during the
1364  *  session to whatever values they were set to during the session.
1365  *
1366  *
1367  *  Inputs:
1368  *  ------
1369  *  smGD.resourcePath = (global) a pointer
1370  *                      to where the settings should be stored.
1371  *
1372  * 
1373  *  Outputs:
1374  *  -------
1375  *
1376  *
1377  *  Comments:
1378  *  --------
1379  *  This routine is the default saved for saving settings.  If the user
1380  *  wants all settings saved, they have to set a resource saying that that
1381  *  will be the case.
1382  * 
1383  *************************************<->***********************************/
1384 static int 
1385 SaveCustomizeSettings( void )
1386 {
1387     int                 i,numPaths;
1388     char                **fontPath;
1389     XrmDatabase         smBase = NULL;
1390     char                *tmpChar[20], *ptrRet = NULL, *tmpKey;
1391     Status              newStat;
1392         char            tmpMap[128];
1393
1394
1395     if(smCust.pointerChange)
1396     {
1397         newStat = _DtGetSmPointer(smGD.display, smGD.topLevelWindow,
1398                                  XaDtSmPointerInfo, &ptrRet);
1399         if(newStat != Success)
1400         {
1401             smToSet.pointerChange = False;
1402             smToSet.pointerMapChange = False;
1403         }
1404         else
1405         {
1406             sscanf(ptrRet, "%d %s %d %d %s",
1407                    &smToSet.numButton,
1408                    &tmpMap,
1409                    &smSettings.accelNum,
1410                    &smSettings.threshold,
1411                    &smToSet.dClickBuf);
1412             smSettings.accelDenom = 1;
1413
1414             tmpKey = strtok(tmpMap, "_");
1415             i = 0;
1416             while(tmpKey != NULL)
1417             {
1418                 smToSet.pointerMap[i] = (char) atoi(tmpKey);
1419                 i++;
1420                 tmpKey = strtok(NULL, "_");
1421             }
1422
1423
1424             /*
1425              * append a \n to dClickBuf because it was scanned out
1426              */
1427             strcat(smToSet.dClickBuf, "\n");
1428         }
1429         if (ptrRet) XFree(ptrRet);
1430     }
1431                                      
1432     if(smToSet.pointerChange == True)
1433     {
1434         PrintPointerControl(&smBase);
1435     }
1436
1437     if(smToSet.pointerMapChange == True)
1438     {
1439         if(PrintPointerMapping(&smBase, smToSet.pointerMap,
1440                             smToSet.numButton) != 0)
1441         {
1442             return(-1);
1443         }
1444     }
1445
1446     if(smToSet.screenSavChange == True)
1447     {
1448         PrintScreenSaver(&smBase);
1449     }
1450
1451     if(smCust.audioChange == True)
1452     {
1453       newStat = _DtGetSmAudio(smGD.display, smGD.topLevelWindow,
1454                                    XaDtSmAudioInfo, &audioVals);
1455      
1456       if(newStat == Success)
1457       {
1458         smSettings.kbdState.bell_percent = audioVals.smBellPercent;
1459         smSettings.kbdState.bell_pitch = audioVals.smBellPitch;
1460         smSettings.kbdState.bell_duration = audioVals.smBellDuration;
1461       }  
1462       else
1463       {
1464         smToSet.audioChange = False;
1465       }
1466     }
1467     
1468     if (smToSet.audioChange)
1469     {
1470       strcpy(resSpec, SM_RESOURCE_CLASS);
1471       strcat(resSpec, "*");
1472       strcat(resSpec, SmCbellPercent);
1473       strcat(resSpec, ":");
1474       sprintf((char *)tmpChar,"%d",smSettings.kbdState.bell_percent);
1475       strcat(resSpec, (char *)tmpChar);
1476       XrmPutLineResource(&smBase, resSpec);
1477
1478       strcpy(resSpec, SM_RESOURCE_CLASS);
1479       strcat(resSpec, "*");
1480       strcat(resSpec, SmCbellPitch);
1481       strcat(resSpec, ":");
1482       sprintf((char *)tmpChar,"%d",smSettings.kbdState.bell_pitch);
1483       strcat(resSpec, (char *)tmpChar);
1484       XrmPutLineResource(&smBase, resSpec);
1485
1486       strcpy(resSpec, SM_RESOURCE_CLASS);
1487       strcat(resSpec, "*");
1488       strcat(resSpec, SmCbellDuration);
1489       strcat(resSpec, ":");
1490       sprintf((char *)tmpChar,"%d",smSettings.kbdState.bell_duration);
1491       strcat(resSpec, (char *)tmpChar);
1492       XrmPutLineResource(&smBase, resSpec);
1493     }
1494
1495     if(smCust.keyboardChange == True)
1496     {
1497       newStat = _DtGetSmKeyboard(smGD.display, smGD.topLevelWindow,
1498                                       XaDtSmKeyboardInfo, &keyboardVals);
1499       if(newStat == Success)
1500       {
1501         smSettings.kbdState.key_click_percent = keyboardVals.smKeyClickPercent;
1502         smSettings.kbdState.global_auto_repeat = keyboardVals.smGlobalAutoRepeat;
1503       }
1504       else
1505       {
1506         smToSet.keyboardChange = False;
1507       }
1508     }
1509
1510     if(smToSet.keyboardChange == True)
1511     {
1512       strcpy(resSpec, SM_RESOURCE_CLASS);
1513       strcat(resSpec, "*");
1514       strcat(resSpec, SmCkeyClick);
1515       strcat(resSpec, ":");
1516       sprintf((char *)tmpChar,"%d", smSettings.kbdState.key_click_percent);
1517       strcat(resSpec, (char *)tmpChar);
1518       XrmPutLineResource(&smBase, resSpec);
1519
1520       strcpy(resSpec, SM_RESOURCE_CLASS);
1521       strcat(resSpec, "*");
1522       strcat(resSpec, SmCglobalRepeats);
1523       strcat(resSpec, ":");
1524       sprintf((char *)tmpChar,"%d", smSettings.kbdState.global_auto_repeat);
1525       strcat(resSpec, (char *)tmpChar);
1526       XrmPutLineResource(&smBase, resSpec);
1527     }
1528
1529
1530     /*
1531      *  Write out all font path information. 
1532      *  Write out all the paths one by one concatenated by commas.
1533      */
1534
1535     fontPath = XGetFontPath(smGD.display, &numPaths);
1536     if(fontPath != NULL) {
1537         if(PrintFontPaths(&smBase,  fontPath, numPaths) != 0) {
1538             XFreeFontPath(fontPath);
1539             return(-1);
1540         }
1541     }
1542
1543     /*
1544      * Save session manager settings
1545      */
1546     PrintSessionInfo(&smBase);
1547     
1548     if (fontPath)
1549         XFreeFontPath(fontPath);
1550
1551     /*
1552      * Write settings resources out to disk
1553      */
1554     XrmPutFileDatabase(smBase, smGD.settingPath);
1555     XrmDestroyDatabase(smBase);
1556
1557     return(0);
1558 }
1559
1560
1561 \f
1562 /*************************************<->*************************************
1563  *
1564  *  OutputResource ()
1565  *
1566  *
1567  *  Description:
1568  *  -----------
1569  *  Save session resources by getting the current RESOURCE_MANAGER property
1570  *  on the root window.
1571  *
1572  *  Inputs:
1573  *  ------
1574  *  smGD.resourcePath = (global) file name
1575  *                      where property contents should be saved.
1576  * 
1577  *  Outputs:
1578  *  -------
1579  *
1580  *
1581  *  Comments:
1582  *  --------
1583  *  This method is not a 100% accurate representation of currently active
1584  *  resources in the system.  It is however, the best representation (the
1585  *  values with the highest priority) without becoming overly invasive
1586  *  and slow.
1587  * 
1588  *************************************<->***********************************/
1589 static int 
1590 OutputResource( void )
1591 {
1592     Atom                actualType;
1593     int                 actualFormat;
1594     unsigned long       nitems, leftover; 
1595     char                *data = NULL;
1596     XrmDatabase         db;
1597     Status              newStat;
1598     char                *fontBuf = NULL, *langPtr, tmpChar[20], *sessionRes;
1599     float               fltYRes;
1600     int                 intYRes;
1601     char                *preeditBuf = NULL;
1602
1603     /*
1604      * Add anything to the Resource Manager property that needs to be added
1605      */
1606
1607     /*
1608      * Write out the LANG variable and the screen's Y resolution
1609      */
1610     *resSpec = NULL;
1611     langPtr = getenv("LANG");
1612     if((langPtr != NULL) && (*langPtr != NULL))
1613     {
1614         sprintf(resSpec, "%s*%s: %s\n", SM_RESOURCE_NAME, SmNsessionLang,
1615                 langPtr);
1616     }
1617     else
1618     {
1619         sprintf(resSpec, "%s*%s:   \n", SM_RESOURCE_NAME, SmNsessionLang);
1620     }
1621
1622     fltYRes = ((float) DisplayHeight(smGD.display, 0) /
1623         (float) DisplayHeightMM(smGD.display, 0)) * 1000;
1624     intYRes = fltYRes;
1625
1626     if(*resSpec == NULL)
1627     {
1628         strcpy(resSpec, SM_RESOURCE_NAME);
1629     }
1630     else
1631     {
1632         strcat(resSpec, SM_RESOURCE_NAME);
1633     }
1634     strcat(resSpec, "*");
1635     strcat(resSpec, SmNdisplayResolution);
1636     strcat(resSpec, ":");
1637     sprintf(tmpChar, "%d",intYRes);
1638     strcat(resSpec, tmpChar);
1639     strcat(resSpec, "\n");
1640     _DtAddToResource(smGD.display, resSpec);
1641     
1642     if(smCust.fontChange == True)
1643     {
1644         newStat = _DtGetSmFont(smGD.display, smGD.topLevelWindow,
1645                               XaDtSmFontInfo, &fontBuf);
1646         if(newStat == Success)
1647         {
1648             /*
1649              * Create the auxillary resource file
1650              */
1651             if(SetFontSavePath(langPtr) != -1)
1652             {
1653                 db = XrmGetStringDatabase(fontBuf);
1654             
1655                 if(intYRes < MED_RES_Y_RES)
1656                 {
1657                     sessionRes = SM_LOW_RES_EXT;
1658                 }
1659                 else
1660                 {
1661                     if(intYRes >= HIGH_RES_Y_RES)
1662                     {
1663                         sessionRes = SM_HIGH_RES_EXT;
1664                     }
1665                     else
1666                     {
1667                         sessionRes = SM_MED_RES_EXT;
1668                     }
1669                 }
1670
1671                 strcat(smGD.fontPath, "/");
1672                 strcat(smGD.fontPath, SM_FONT_FILE);
1673                 
1674                 strcat(smGD.fontPath, ".");
1675                 strcat(smGD.fontPath, sessionRes);
1676                     
1677                 XrmPutFileDatabase(db, smGD.fontPath);    
1678                 XrmDestroyDatabase(db);
1679             }
1680             
1681             /*
1682              * Now add this to the resource manager property to be saved
1683              */
1684             _DtAddToResource(smGD.display, fontBuf);
1685         }
1686         if (fontBuf) XFree(fontBuf);
1687     }
1688
1689     if(smCust.preeditChange == True)
1690     {
1691         newStat = _DtGetSmPreedit(smGD.display, smGD.topLevelWindow,
1692                                   XaDtSmPreeditInfo, &preeditBuf);
1693         if(newStat == Success)
1694         {
1695             _DtAddToResource(smGD.display, preeditBuf);
1696         }
1697         if (preeditBuf) XFree(preeditBuf);
1698     }
1699
1700     if((smCust.dClickChange == True) && (smToSet.dClickBuf[0] != NULL))
1701     {
1702         _DtAddToResource(smGD.display, (char *)smToSet.dClickBuf);
1703     }
1704
1705     /*
1706      * Get the contents of the _DT_SM_PREFERENCES property
1707      */
1708     data = _DtGetResString(smGD.display, _DT_ATR_PREFS);
1709
1710    /* 
1711     * Get _DT_SM_PREFERENCES database 'db'.
1712     */
1713     db  = XrmGetStringDatabase((char *)data);
1714
1715     XrmPutFileDatabase(db, smGD.resourcePath);
1716
1717     /*
1718      * Don't forget to free your data
1719      */
1720     SM_FREE((char *)data);
1721     XrmDestroyDatabase(db);
1722     
1723     return(0);
1724 }
1725
1726
1727 \f
1728 /*************************************<->*************************************
1729  *
1730  *  PrintPointerControl (smBase)
1731  *
1732  *
1733  *  Description:
1734  *  -----------
1735  *  A convenience function that is separated out instead of being included
1736  *  in both QueryServerSettings, and SaveCustomizeSettings.
1737  *  Saves pointer control information to the named resource file.
1738  *
1739  *  Inputs:
1740  *  ------
1741  *  smBase = pointer to newly opened resource db used to store setting info
1742  *  resSpec = a buffer to hold string resource information until it is
1743  *            printed.
1744  *  smSettings = place where settings are stored.
1745  * 
1746  *  Outputs:
1747  *  -------
1748  *
1749  *  Comments:
1750  *  --------
1751  * 
1752  *************************************<->***********************************/
1753 static void 
1754 PrintPointerControl(
1755         XrmDatabase *smBase)
1756 {
1757     char tmpChar[20];
1758     
1759     strcpy(resSpec, SM_RESOURCE_CLASS);
1760     strcat(resSpec, "*");
1761     strcat(resSpec, SmCaccelNum);
1762     strcat(resSpec, ":");
1763     sprintf(tmpChar, "%d", smSettings.accelNum);
1764     strcat(resSpec, tmpChar);
1765     XrmPutLineResource(smBase, resSpec);
1766     
1767     strcpy(resSpec, SM_RESOURCE_CLASS);
1768     strcat(resSpec, "*");
1769     strcat(resSpec, SmCaccelDenom);
1770     strcat(resSpec, ":");
1771     sprintf(tmpChar, "%d",smSettings.accelDenom);
1772     strcat(resSpec, tmpChar);
1773     XrmPutLineResource(smBase, resSpec);
1774     
1775     strcpy(resSpec, SM_RESOURCE_CLASS);
1776     strcat(resSpec, "*");
1777     strcat(resSpec, SmCthreshold);
1778     strcat(resSpec, ":");
1779     sprintf(tmpChar, "%d",smSettings.threshold);
1780     strcat(resSpec, tmpChar);
1781     XrmPutLineResource(smBase, resSpec);
1782
1783     return;
1784 }
1785
1786 \f
1787 /*************************************<->*************************************
1788  *
1789  *  PrintSessionInfo (smBase)
1790  *
1791  *
1792  *  Description:
1793  *  -----------
1794  *  A convenience function that is separated out instead of being included
1795  *  in both QueryServerSettings, and SaveCustomizeSettings.
1796  *  Saves session manager settings to the named resource file.
1797  *
1798  *  Inputs:
1799  *  ------
1800  *  smBase = pointer to newly opened resource db used to store setting info
1801  *  resSpec = a buffer to hold string resource information until it is
1802  *            printed.
1803  *  smSettings = place where settings are stored.
1804  * 
1805  *  Outputs:
1806  *  -------
1807  *
1808  *  Comments:
1809  *  --------
1810  * 
1811  *************************************<->***********************************/
1812 static void 
1813 PrintSessionInfo(
1814         XrmDatabase *smBase)
1815 {
1816     char tmpChar[20];
1817
1818     /*
1819      * Write out the settings for logout confirmation and
1820      * the correct session to return to
1821      */
1822     strcpy(resSpec, SM_RESOURCE_CLASS);
1823     strcat(resSpec, "*");
1824     strcat(resSpec, SmCshutDownState);
1825     strcat(resSpec, ":");
1826     sprintf(tmpChar, "%d",smSettings.confirmMode);
1827     strcat(resSpec, tmpChar);
1828     XrmPutLineResource(smBase, resSpec);
1829     
1830     strcpy(resSpec, SM_RESOURCE_CLASS);
1831     strcat(resSpec, "*");
1832     strcat(resSpec, SmCshutDownMode);
1833     strcat(resSpec, ":");
1834     sprintf(tmpChar, "%d",smSettings.startState);
1835     strcat(resSpec, tmpChar);
1836     XrmPutLineResource(smBase, resSpec);
1837
1838     return;
1839 }
1840
1841 \f
1842 /*************************************<->*************************************
1843  *
1844  *  PrintScreenSaver (smBase)
1845  *
1846  *
1847  *  Description:
1848  *  -----------
1849  *  A convenience function that is separated out instead of being included
1850  *  in both QueryServerSettings, and SaveCustomizeSettings.
1851  *  Saves screen saver information to the named resource file.
1852  *
1853  *  Inputs:
1854  *  ------
1855  *  smBase = pointer to newly opened resource db used to store setting info
1856  *  resSpec = a buffer to hold string resource information until it is
1857  *            printed.
1858  * 
1859  *  Outputs:
1860  *  -------
1861  *
1862  *  Comments:
1863  *  --------
1864  * 
1865  *************************************<->***********************************/
1866 static void 
1867 PrintScreenSaver(
1868         XrmDatabase *smBase)
1869 {
1870     Status              newScreenStat;
1871     char                tmpChar[20];
1872
1873     if(smCust.screenSavChange == True)
1874     {
1875         newScreenStat = _DtGetSmScreen(smGD.display, smGD.topLevelWindow,
1876                                       XaDtSmScreenInfo, &screenSaverVals);
1877         /*
1878          *  If the property has been deleted, it means that we return to
1879          *  the default
1880          */
1881         if(newScreenStat != Success)
1882         {
1883             return;
1884         }
1885     }
1886
1887     strcpy(resSpec, SM_RESOURCE_CLASS);
1888     strcat(resSpec, "*");
1889     strcat(resSpec, SmCtimeout);
1890     strcat(resSpec, ":");
1891     sprintf(tmpChar, "%d", screenSaverVals.smTimeout);
1892     strcat(resSpec, tmpChar);
1893     XrmPutLineResource(smBase, resSpec);
1894     
1895     strcpy(resSpec, SM_RESOURCE_CLASS);
1896     strcat(resSpec, "*");
1897     strcat(resSpec, SmCinterval);
1898     strcat(resSpec, ":");
1899     sprintf(tmpChar, "%d", screenSaverVals.smInterval);
1900     strcat(resSpec, tmpChar);
1901     XrmPutLineResource(smBase, resSpec);
1902     
1903     strcpy(resSpec, SM_RESOURCE_CLASS);
1904     strcat(resSpec, "*");
1905     strcat(resSpec, SmCpreferBlank);
1906     strcat(resSpec, ":");
1907     sprintf(tmpChar, "%d", screenSaverVals.smPreferBlank);
1908     strcat(resSpec, tmpChar);
1909     XrmPutLineResource(smBase, resSpec);
1910     
1911     strcpy(resSpec, SM_RESOURCE_CLASS);
1912     strcat(resSpec, "*");
1913     strcat(resSpec, SmCallowExp);
1914     strcat(resSpec, ":");
1915     sprintf(tmpChar, "%d", screenSaverVals.smAllowExp);
1916     strcat(resSpec, tmpChar);
1917     XrmPutLineResource(smBase, resSpec);
1918
1919     return;
1920 }
1921
1922 \f
1923 /*************************************<->*************************************
1924  *
1925  *  PrintFontPaths (smBase, fontPaths, numPaths)
1926  *
1927  *
1928  *  Description:
1929  *  -----------
1930  *  A convenience function that is separated out instead of being included
1931  *  in both QueryServerSettings, and SaveCustomizeSettings.
1932  *  Saves pointer control information to the named resource file.
1933  *
1934  *  Inputs:
1935  *  ------
1936  *  smBase = pointer to newly opened resource db used to store setting info
1937  *  resSpec = a buffer to hold string resource information until it is
1938  *            printed
1939  *  fontPaths = font paths to be saved
1940  *  numPaths = number of font paths to be saved
1941  * 
1942  *  Outputs:
1943  *  -------
1944  *  resSize = made bigger if current buffer is too small to hold all font info
1945  *
1946  *  Comments:
1947  *  --------
1948  * 
1949  *************************************<->***********************************/
1950 static int 
1951 PrintFontPaths(
1952         XrmDatabase *smBase,
1953         char **fontPath,
1954         int numPaths)
1955 {
1956     int i;
1957     
1958     /* If no fp save req'd, just pretend we did it */
1959     if (smRes.saveFontPath == False) return 0;
1960
1961     strcpy(resSpec, SM_RESOURCE_CLASS);
1962     strcat(resSpec, "*");
1963     strcat(resSpec, SmCfontPath);
1964     strcat(resSpec, ":");
1965     strcat(resSpec, *fontPath);
1966     for(i = 1;i < numPaths;i++)
1967     {
1968         if((strlen(resSpec) + strlen(fontPath[i]) + 1) >= resSize)
1969         {
1970             resSize += 5000;
1971             resSpec = (char *) SM_REALLOC(resSpec, resSize * sizeof(char));
1972             if (resSpec==NULL)
1973             {
1974                 PrintErrnoError(DtError, smNLS.cantMallocErrorString);
1975                 return(-1);
1976             }
1977         }
1978         strcat(resSpec, ",");
1979         strcat(resSpec, fontPath[i]);
1980     }
1981     XrmPutLineResource(smBase, resSpec);
1982
1983     return(0);
1984 }
1985
1986
1987 \f
1988 /*************************************<->*************************************
1989  *
1990  *  PrintKeyboardControl (smBase)
1991  *
1992  *
1993  *  Description:
1994  *  -----------
1995  *  A convenience function that is separated out instead of being included
1996  *  in both QueryServerSettings, and SaveCustomizeSettings.
1997  *  Saves pointer control information to the named resource file.
1998  *
1999  *  Inputs:
2000  *  ------
2001  *  smBase = pointer to newly opened resource db used to store setting info
2002  *  resSpec = a buffer to hold string resource information until it is
2003  *            printed
2004  *  resSize = size of current resource buffer
2005  * 
2006  *  Outputs:
2007  *  -------
2008  *  resSize = buffer is enlarged if more room is need to hold this info
2009  *
2010  *  Comments:
2011  *  --------
2012  * 
2013  *************************************<->***********************************/
2014 static int 
2015 PrintKeyboardControl(
2016         XrmDatabase *smBase)
2017 {
2018     int         i,j;
2019     char        bitTest;
2020     Bool        firstRepeat = True;
2021     char        tmpChar[20];
2022     
2023     strcpy(resSpec, SM_RESOURCE_CLASS);
2024     strcat(resSpec, "*");
2025     strcat(resSpec, SmCkeyClick);
2026     strcat(resSpec, ":");
2027     sprintf(tmpChar, "%d",smSettings.kbdState.key_click_percent);
2028     strcat(resSpec, tmpChar);
2029     XrmPutLineResource(smBase, resSpec);
2030
2031     strcpy(resSpec, SM_RESOURCE_CLASS);
2032     strcat(resSpec, "*");
2033     strcat(resSpec, SmCbellPercent);
2034     strcat(resSpec, ":");
2035     sprintf(tmpChar, "%d",smSettings.kbdState.bell_percent);
2036     strcat(resSpec, tmpChar);
2037     XrmPutLineResource(smBase, resSpec);
2038
2039     strcpy(resSpec, SM_RESOURCE_CLASS);
2040     strcat(resSpec, "*");
2041     strcat(resSpec, SmCbellPitch);
2042     strcat(resSpec, ":");
2043     sprintf(tmpChar, "%d",smSettings.kbdState.bell_pitch);
2044     strcat(resSpec, tmpChar);
2045     XrmPutLineResource(smBase, resSpec);
2046
2047     strcpy(resSpec, SM_RESOURCE_CLASS);
2048     strcat(resSpec, "*");
2049     strcat(resSpec, SmCbellDuration);
2050     strcat(resSpec, ":");
2051     sprintf(tmpChar, "%d",smSettings.kbdState.bell_duration);
2052     strcat(resSpec, tmpChar);
2053     XrmPutLineResource(smBase, resSpec);
2054     
2055     strcpy(resSpec, SM_RESOURCE_CLASS);
2056     strcat(resSpec, "*");
2057     strcat(resSpec, SmCledMask);
2058     strcat(resSpec, ":");
2059     sprintf(tmpChar, "%ld",smSettings.kbdState.led_mask);
2060     strcat(resSpec, tmpChar);
2061     XrmPutLineResource(smBase, resSpec);
2062
2063     strcpy(resSpec, SM_RESOURCE_CLASS);
2064     strcat(resSpec, "*");
2065     strcat(resSpec, SmCglobalRepeats);
2066     strcat(resSpec, ":");
2067     sprintf(tmpChar, "%d",smSettings.kbdState.global_auto_repeat);
2068     strcat(resSpec, tmpChar);
2069     XrmPutLineResource(smBase, resSpec);
2070
2071     strcpy(resSpec, SM_RESOURCE_CLASS);
2072     strcat(resSpec, "*");
2073     strcat(resSpec, SmCautoRepeats);
2074     strcat(resSpec, ":");
2075
2076     /*
2077      * Now write out which keys need to be auto repeated one at a time
2078      */
2079     if(smSettings.kbdState.global_auto_repeat != AutoRepeatModeOn)
2080     {
2081         for(i = 0;i < 32;i++)
2082         {
2083             bitTest = 0x01;
2084             for(j = 0;j < 8;j++)
2085             {
2086                 if((bitTest & smSettings.kbdState.auto_repeats[i]) != 0)
2087                 {
2088                     if(firstRepeat == True)
2089                     {
2090                         firstRepeat = False;
2091                     }
2092                     else
2093                     {
2094                         strcat(resSpec, ",");
2095                     }
2096                     
2097                     if((strlen(resSpec) + 5) >= resSize)
2098                     {
2099                         resSize += 5000;
2100                         resSpec = (char *) SM_REALLOC(resSpec,
2101                                                    resSize * sizeof(char));
2102                         if (resSpec==NULL)
2103                         {
2104                             PrintErrnoError(DtError,
2105                                             smNLS.cantMallocErrorString);
2106                             return(-1);
2107                         }
2108                     }
2109                         
2110                     sprintf(tmpChar, "%d", ((8 * i) + j));
2111                     strcat(resSpec, tmpChar);
2112                 }
2113                 bitTest <<= 1;
2114             }
2115         }
2116     }
2117     else
2118     {
2119         strcat(resSpec, "");
2120     }
2121     XrmPutLineResource(smBase, resSpec);
2122
2123     return(0);
2124 }
2125
2126
2127 \f
2128 /*************************************<->*************************************
2129  *
2130  *  PrintPointerMapping (smBase, buttonRet,  numButton)
2131  *
2132  *
2133  *  Description:
2134  *  -----------
2135  *  A convenience function that is separated out instead of being included
2136  *  in both QueryServerSettings, and SaveCustomizeSettings.
2137  *  Saves pointer mapping information to the named resource file.
2138  *
2139  *  Inputs:
2140  *  ------
2141  *  smBase = pointer to newly opened resource db used to store setting info
2142  *  resSpec = a buffer to hold string resource information until it is
2143  *            printed
2144  *  resSize = size of resource buffer
2145  *  buttonRet = button mapping to be saved
2146  *  numButton = number of buttons in button mapping
2147  * 
2148  *  Outputs:
2149  *  -------
2150  *  resSize = size of buffer is change if it needs to be enlarged during
2151  *            routine
2152  *
2153  *  Comments:
2154  *  --------
2155  * 
2156  *************************************<->***********************************/
2157 static int 
2158 PrintPointerMapping(
2159         XrmDatabase *smBase,
2160         char *buttonRet,
2161         int numButton )
2162 {
2163     int         i, numLoops;
2164     char        tmpChar[20];
2165     
2166     strcpy(resSpec, SM_RESOURCE_CLASS);
2167     strcat(resSpec, "*");
2168     strcat(resSpec, SmCbuttonMap);
2169     strcat(resSpec, ":");
2170
2171     numLoops = numButton - 1;
2172     for(i = 0;i <= numLoops;i++)
2173     {
2174         if((strlen(resSpec) + 5) >= resSize)
2175         {
2176             resSize += 5000;
2177             resSpec = (char *) SM_REALLOC(resSpec, resSize * sizeof(char));
2178             if (resSpec==NULL)
2179             {
2180                 PrintErrnoError(DtError, smNLS.cantMallocErrorString);
2181                 return(-1);
2182             }
2183         }
2184             
2185         sprintf(tmpChar, "%d",buttonRet[i]);
2186         strcat(resSpec, tmpChar);
2187         if(i != numLoops)
2188         {
2189             strcat(resSpec, ",");
2190         }
2191         XrmPutLineResource(smBase, resSpec);
2192     }
2193
2194     return(0);
2195 }