dtcm: Coverity 89287
[oweals/cde.git] / cde / programs / dtwm / WmXSMP.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 libraries 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 /*
24  * (c) Copyright 1996 Digital Equipment Corporation.
25  * (c) Copyright 1996 Hewlett-Packard Company.
26  * (c) Copyright 1996 International Business Machines Corp.
27  * (c) Copyright 1996 Sun Microsystems, Inc.
28  * (c) Copyright 1996 Novell, Inc. 
29  * (c) Copyright 1996 FUJITSU LIMITED.
30  * (c) Copyright 1996 Hitachi.
31  */
32
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <ctype.h>
36 #include <sys/param.h>
37 #include <X11/Intrinsic.h>
38 #include <X11/Shell.h>
39 #include <X11/Xatom.h>
40 #include <X11/SM/SM.h>
41 #include <Xm/XmP.h>
42 #include "WmGlobal.h"
43 #include "WmXSMP.h"
44 #include "WmWrkspace.h"
45 #include <Dt/Session.h>
46
47 typedef struct _ProxyClientInfo
48 {
49     int screen;
50     char *wmCommand;
51     char *wmClientMachine;
52     char *clientID;
53 } ProxyClientInfo;
54
55 #define RESTORE_RESOURCE(pCD, resFlag) \
56         ((pCD)->ignoreWMSaveHints || !((pCD)->wmSaveHintFlags & (resFlag)))
57 #define SAVE_RESOURCE(pCD, resFlag) RESTORE_RESOURCE(pCD, resFlag)
58
59 #define MAX_RESOURCE_LEN 1024
60
61 static char *dtwmFileName = "dtwm.db";
62
63 /* Fully-qualified resource names/classes. */
64 static char *xPositionStr = "%s.position.x";
65 static char *yPositionStr = "%s.position.y";
66 static char *widthSizeStr = "%s.size.width";
67 static char *heightSizeStr = "%s.size.height";
68 static char *initialStateStr = "%s.initialState";
69 static char *wmCommandStr = "%s.wmCommand";
70 static char *wmClientMachineStr = "%s.wmClientMachine";
71 static char *screenStr = "%s.screen";
72 static char *workspacesStr = "%s.workspaces";
73 static char *iconXPosStr = "%s.iconPos.x.%s";
74 static char *iconYPosStr = "%s.iconPos.y.%s";
75
76 /* Header for private database. */
77 static char *dbHeader = "\
78 ! %s\n\
79 !\n\
80 .version: %s\n\
81 .dtwmID: %s\n";
82
83 /* Format for client entries in database. */
84 static char *dbClientFormat = "\
85 !\n\
86 %s.%s: %s\n\
87 !\n";
88 static char *intArg = ": %d\n";
89 static char *strArg = ": %s\n";
90 static char *normalStateStr = "NormalState";
91 static char *iconicStateStr = "IconicState";
92
93 static char *XSMPClientStr = "Client";
94 static char *proxyClientStr = "ProxyClient";
95
96 /* Flag to tell us how to treat ProxyClient info. */
97 static Boolean smClientDBCheckpointed = False;
98
99 /*
100  *  Prototypes
101  */
102 /* Session mgmt callbacks. */
103 static void smSaveYourselfCallback(Widget, XtPointer, XtPointer);
104 static void smDieCallback(Widget, XtPointer, XtPointer);
105
106 /* Build client database file name. */
107 static void buildDBFileName(char [MAXPATHLEN], Boolean);
108
109 /* Get string of client's workspaces. */
110 static char *getClientWorkspaces(ClientData *);
111
112 /* List-of-clients utilities. */
113 static Boolean addClientToList(ClientData ***, int *, ClientData *);
114 static int clientWorkspaceCompare(const void *, const void *);
115
116 /* XSMP/Proxy functions to save/restore resources. */
117 static char *getClientResource(char *, char *);
118 static char *getXSMPResource(ClientData *, int, char *);
119 static void getClientGeometry(ClientData *, int *, int *,
120                               unsigned int *, unsigned int *);
121 static Boolean getProxyClientInfo(ClientData *, ProxyClientInfo *);
122 static Bool cmpProxyClientProc(XrmDatabase *, XrmBindingList,
123                                XrmQuarkList, XrmRepresentation *,
124                                XrmValue *, XPointer);
125 static char *findProxyClientID(ClientData *);
126 static Boolean findXSMPClientDBMatch(ClientData *, char **);
127 static Boolean findProxyClientDBMatch(ClientData *, char **);
128 static Boolean saveXSMPClient(FILE *, ClientData *);
129 static Boolean saveProxyClient(FILE *, ClientData *, int);
130 static void dbRemoveProxyClientEntry(char *);
131
132 static void
133 smSaveYourselfCallback(Widget w, XtPointer clientData, XtPointer callData)
134 {
135     XtCheckpointToken cpToken = (XtCheckpointToken)callData;
136     XrmDatabase newClientDB;
137     int scr;
138     static Boolean firstTime = True;
139
140     /*
141      *  This callback will be called on connection to the Session Manager.
142      *  At that time, we don't want to save any state, and we don't
143      *  want to request the second phase.
144      */
145     if (firstTime)
146     {
147         firstTime = False;
148         return;
149     }
150
151     /* Only respond to Local and Both save requests. */
152     if ((cpToken->save_type != SmSaveLocal) &&
153         (cpToken->save_type != SmSaveBoth))
154         return;
155
156     if (cpToken->shutdown &&
157         (cpToken->cancel_shutdown ||
158          cpToken->request_cancel ||
159          !cpToken->save_success))
160         return;  /* Return, maintaining current state */
161
162     /* If first phase, request notification when all other clients saved. */
163     if (cpToken->phase == 1)
164     {
165         cpToken->request_next_phase = True;
166         return;
167     }
168
169     /* Second phase: all other clients saved; now I can save myself. */
170     /* Copied from WmEvent.c. */
171     for (scr = 0; scr < wmGD.numScreens; scr++)
172     {
173         if (wmGD.Screens[scr].managed)
174         {
175             /*
176              * Write out current workspace, frontpanel 
177              * position and iconbox position and size.
178              */
179             SaveResources(&wmGD.Screens[scr]);
180         }
181     }
182
183     /*
184      *  NEW FOR SESSION MANAGEMENT: Write private client resource database.
185      *  Destroy old client database and save new one.
186      */
187     if ((newClientDB = SaveClientResourceDB())
188         != (XrmDatabase)NULL)
189     {
190         if (wmGD.clientResourceDB != (XrmDatabase)NULL)
191             XrmDestroyDatabase(wmGD.clientResourceDB);
192         wmGD.clientResourceDB = newClientDB;
193         smClientDBCheckpointed = True;
194
195     }
196 }
197
198 static void
199 smDieCallback(Widget w, XtPointer clientData, XtPointer callData)
200 {
201     /* We assume we've saved our state by the time this is called. */
202     ExitWM(0);
203 }
204
205 static void
206 buildDBFileName(char fileNameBuf[MAXPATHLEN], Boolean doingSave)
207 {
208     char *savePath = (char *)NULL;
209
210     fileNameBuf[0] = '\0';
211     if (doingSave)
212     {
213         char *saveFile = (char *)NULL;
214         char *ptr;
215
216         if (DtSessionSavePath(wmGD.topLevelW, &savePath, &saveFile))
217         {
218             XtFree(saveFile);
219
220             if ((ptr = strrchr(savePath, '/')) != (char *)NULL)
221                 *ptr = '\0';
222
223             if (strlen(savePath) + strlen(dtwmFileName) + 2 < MAXPATHLEN)
224                 sprintf(fileNameBuf, "%s/%s", savePath, dtwmFileName);
225
226             XtFree(savePath);
227         }
228     }
229     else
230     {
231         if (DtSessionRestorePath(wmGD.topLevelW, &savePath, dtwmFileName))
232         {
233             if ((int)strlen(savePath) < MAXPATHLEN)
234                 strcpy(fileNameBuf, savePath);
235
236             XtFree(savePath);
237         }
238     }
239
240     if (fileNameBuf[0] == '\0')
241         strcpy(fileNameBuf, dtwmFileName);
242
243 }
244
245 static char *
246 getClientWorkspaces(ClientData *pCD)
247 {
248     WmScreenData *pSD = pCD->pSD;
249     WmWorkspaceData *pWS;
250
251     /* Should we use _DtWmParseMakeQuotedString() when looking at */
252     /* the name of the workspace, as is done in WmWrkspace.c? */
253
254     /* Easy but slow way to do this would be to use XGetAtomName(). */
255     /* To avoid XServer round trips (and to weed out invalid WS names) */
256     /* we look through workspaces attached to this screen for ID matches. */
257     char *cwsP = NULL, *tmpP, *wsNameP;
258     int pLen = 0;
259     int i;
260
261     for (i = 0; i < pCD->numInhabited; i++)
262     {
263         if ((pWS = GetWorkspaceData(pSD, pCD->pWsList[i].wsID))
264             != (WmWorkspaceData *)NULL)
265         {
266             wsNameP = pWS->name;
267             if (pLen == 0)
268             {
269                 pLen = strlen(wsNameP) + 1;  /* 1 for null termination */
270                 if ((cwsP = (char *)XtMalloc(pLen * sizeof(char)))
271                     == (char *)NULL)
272                     return (char *)NULL;
273
274                 strcpy(cwsP, wsNameP);
275             }
276             else
277             {
278                 pLen += strlen(wsNameP) + 1;  /* 1 for space */
279                 if ((tmpP = (char *)XtRealloc(cwsP, pLen * sizeof(char)))
280                     == (char *)NULL)
281                 {
282                     XtFree((char *)cwsP);
283                     return (char *)NULL;
284                 }
285                 cwsP = tmpP;
286                 strcat(cwsP, " ");
287                 strcat(cwsP, wsNameP);
288             }
289         }
290     }
291
292     return cwsP;
293 }
294
295 static Boolean
296 addClientToList(ClientData ***cdList, int *nClients, ClientData *pCD)
297 {
298     ClientData **newPtr = (ClientData **)
299         XtRealloc((char *)*cdList, (*nClients + 1) * sizeof(ClientData *));
300
301     if (newPtr == (ClientData **)NULL)
302     {
303         if (*cdList != (ClientData **)NULL)
304             XtFree((char *)*cdList);
305         return False;
306     }
307
308     *cdList = newPtr;
309     newPtr[*nClients] = pCD;
310     (*nClients)++;
311
312     return True;
313 }
314
315 static int
316 clientWorkspaceCompare(const void *ppCD1, const void *ppCD2)
317 {
318     ClientData *pCD1 = *(ClientData **)ppCD1;
319     ClientData *pCD2 = *(ClientData **)ppCD2;
320     int screenDiff;
321
322     /* Sort first by screen. */
323     if ((screenDiff = pCD1->pSD->screen - pCD2->pSD->screen) != 0)
324         return screenDiff;
325
326     /* If same screen, sort by workspace id. */
327     /* How do we handle clients that live in more than one workspace? */
328     /* For now, pick the "current" one - if not in active workspace, */
329     /* this will simply be the first one in the client's list. */
330     return (int)(pCD1->pWsList[pCD1->currentWsc].wsID -
331                  pCD2->pWsList[pCD2->currentWsc].wsID);
332 }
333
334 /*
335  *  Assumes: wmGD.clientResourceDB is non-NULL
336  */
337 static char *
338 getClientResource(char *clientID, char *fmtStr)
339 {
340     char resourceBuf[MAX_RESOURCE_LEN];
341     char *resourceType;
342     XrmValue resourceValue;
343
344     sprintf(resourceBuf, fmtStr, clientID);
345     if (XrmGetResource(wmGD.clientResourceDB, resourceBuf, resourceBuf,
346                        &resourceType, &resourceValue))
347         return (char *)resourceValue.addr;
348
349     return (char *)NULL;
350 }
351
352 /*
353  *  Assumes: pCD has non-NULL smClientID;
354  *           wmGD.clientResourceDB is non-NULL
355  */
356 static char *
357 getXSMPResource(ClientData *pCD, int resourceFlag, char *fmtStr)
358 {
359     if (RESTORE_RESOURCE(pCD, resourceFlag))
360         return getClientResource(pCD->smClientID, fmtStr);
361
362     return (char *)NULL;
363 }
364
365 /*
366  *  Return True if client is XSMP, False otherwise.
367  */
368 static Boolean
369 findXSMPClientDBMatch(ClientData *pCD, char **workSpaceNamesP)
370 {
371     if (pCD->smClientID != (String)NULL)
372     {
373         if (wmGD.clientResourceDB != (XrmDatabase)NULL)
374         {
375             char *resourcePtr;
376
377             if ((resourcePtr = getXSMPResource(pCD, WMSAVE_X, xPositionStr))
378                 != (char *)NULL)
379             {
380                 pCD->clientX = atoi(resourcePtr);
381                 pCD->clientFlags |= SM_X;
382             }
383
384             if ((resourcePtr = getXSMPResource(pCD, WMSAVE_Y, yPositionStr))
385                 != (char *)NULL)
386             {
387                 pCD->clientY = atoi(resourcePtr);
388                 pCD->clientFlags |= SM_Y;
389             }
390
391             if ((resourcePtr = getXSMPResource(pCD, WMSAVE_WIDTH,
392                                                widthSizeStr))
393                 != (char *)NULL)
394             {
395                 pCD->clientWidth = atoi(resourcePtr);
396                 pCD->clientFlags |= SM_WIDTH;
397             }
398
399             if ((resourcePtr = getXSMPResource(pCD, WMSAVE_HEIGHT,
400                                                heightSizeStr))
401                 != (char *)NULL)
402             {
403                 pCD->clientHeight = atoi(resourcePtr);
404                 pCD->clientFlags |= SM_HEIGHT;
405             }
406
407             if ((resourcePtr = getXSMPResource(pCD, WMSAVE_STATE,
408                                                initialStateStr))
409                 != (char *)NULL)
410             {
411                 pCD->clientState =
412                     (strcmp(resourcePtr, normalStateStr) == 0) ?
413                         NORMAL_STATE : MINIMIZED_STATE;
414                 pCD->clientFlags |= SM_CLIENT_STATE;
415             }
416
417             if ((workSpaceNamesP != (char **)NULL) &&
418                 ((resourcePtr = getXSMPResource(pCD, WMSAVE_WORKSPACES,
419                                                 workspacesStr))
420                  != (char *)NULL))
421             {
422                 *workSpaceNamesP = XtNewString(resourcePtr);
423             }
424         }
425
426         /* Always return True for XSMP clients. */
427         return True;
428     }
429
430     return False;
431 }
432
433 static Boolean
434 getProxyClientInfo(ClientData *pCD, ProxyClientInfo *proxyClientInfo)
435 {
436     XTextProperty textProperty;
437     unsigned long i;
438
439     /* WM_COMMAND is required; WM_CLIENT_MACHINE is optional. */
440     if (!XGetTextProperty(wmGD.display, pCD->client, &textProperty,
441                           XA_WM_COMMAND))
442         return False;
443
444     if ((textProperty.encoding != XA_STRING) ||
445         (textProperty.format != 8) ||
446         (textProperty.value[0] == '\0'))
447     {
448         if (textProperty.value)
449             free((char *)textProperty.value);
450
451         return False;
452     }
453
454     /* Convert embedded NULL characters to space characters. */
455     /* (If last char is NULL, leave it alone) */
456     for (i = 0; i < textProperty.nitems - 1; i++)
457     {
458         if (textProperty.value[i] == '\0')
459             textProperty.value[i] = ' ';
460     }
461
462     proxyClientInfo->screen = pCD->pSD->screen;
463     proxyClientInfo->wmCommand = (char *)textProperty.value;
464
465     /* Since WM_CLIENT_MACHINE is optional, don't fail if not found. */
466     if (XGetWMClientMachine(wmGD.display, pCD->client, &textProperty))
467         proxyClientInfo->wmClientMachine = (char *)textProperty.value;
468     else proxyClientInfo->wmClientMachine = (char *)NULL;
469
470     proxyClientInfo->clientID = (char *)NULL;
471
472     return True;
473 }
474
475 /*
476  *  IMPORTANT: This function is called by XrmEnumerateDatabase().
477  *  It calls other Xrm*() functions - if dtwm is threaded, THIS
478  *  WILL HANG.  For now, dtwm is NOT threaded, so no problem.
479  */
480 static Bool
481 cmpProxyClientProc(XrmDatabase *clientDB, XrmBindingList bindingList,
482                    XrmQuarkList quarkList, XrmRepresentation *reps,
483                    XrmValue *value, XPointer uData)
484 {
485     char *clientScreen;
486     char *wmCommand;
487     char *wmClientMachine;
488     char *clientID = (char *)value->addr;
489     ProxyClientInfo *proxyClientInfo = (ProxyClientInfo *)uData;
490
491     if (((wmCommand =
492           getClientResource(clientID, wmCommandStr)) == (char *)NULL) ||
493         (strcmp(wmCommand, proxyClientInfo->wmCommand) != 0) ||
494         ((clientScreen =
495           getClientResource(clientID, screenStr)) == (char *)NULL) ||
496         (atoi(clientScreen) != proxyClientInfo->screen))
497         return FALSE;
498
499     /* So far so good.  If WM_CLIENT_MACHINE missing from either, */
500     /* or if it is set in both and it's the same, we've got a match! */
501     if (!proxyClientInfo->wmClientMachine ||
502         ((wmClientMachine =
503           getClientResource(clientID, wmClientMachineStr)) == (char *)NULL) ||
504         (strcmp(proxyClientInfo->wmClientMachine, wmClientMachine) == 0))
505     {
506         proxyClientInfo->clientID = clientID;
507         return TRUE;
508     }
509
510     return FALSE;
511 }
512
513 static char *
514 findProxyClientID(ClientData *pCD)
515 {
516     ProxyClientInfo proxyClientInfo;
517     char *clientID = (char *)NULL;
518     static XrmName proxyName[2] = {NULLQUARK, NULLQUARK};
519     static XrmClass proxyClass[2] = {NULLQUARK, NULLQUARK};
520
521     if (proxyName[0] == NULLQUARK)
522     {
523         proxyName[0] = XrmStringToName(proxyClientStr);
524         proxyClass[0] = XrmStringToClass(proxyClientStr);
525     }
526
527     /*
528      *  We need to match the screen and
529      *  the WM_COMMAND and WM_CLIENT_MACHINE properties.
530      */
531     if (!getProxyClientInfo(pCD, &proxyClientInfo))
532         return clientID;
533
534     if (XrmEnumerateDatabase(wmGD.clientResourceDB, proxyName, proxyClass,
535                              XrmEnumOneLevel, cmpProxyClientProc,
536                              (XPointer)&proxyClientInfo))
537         clientID = proxyClientInfo.clientID;
538
539     if (proxyClientInfo.wmCommand)
540         free(proxyClientInfo.wmCommand);
541     if (proxyClientInfo.wmClientMachine)
542         free(proxyClientInfo.wmClientMachine);
543
544     return clientID;
545 }
546
547 /*
548  *  Return True if client is *not* XSMP and is listed in the resource DB
549  *  and no checkpoint done yet.  Also remove entry from DB if found.
550  */
551 static Boolean
552 findProxyClientDBMatch(ClientData *pCD, char **workSpaceNamesP)
553 {
554     if ((pCD->smClientID == (String)NULL) &&
555         (wmGD.clientResourceDB != (XrmDatabase)NULL) &&
556         (!smClientDBCheckpointed))
557     {
558         char *proxyClientID;
559
560         if ((proxyClientID = findProxyClientID(pCD)) != (char *)NULL)
561         {
562             char *resourcePtr;
563
564             if ((resourcePtr =
565                  getClientResource(proxyClientID, xPositionStr))
566                 != (char *)NULL)
567             {
568                 pCD->clientX = atoi(resourcePtr);
569                 pCD->clientFlags |= SM_X;
570             }
571
572             if ((resourcePtr =
573                  getClientResource(proxyClientID, yPositionStr))
574                 != (char *)NULL)
575             {
576                 pCD->clientY = atoi(resourcePtr);
577                 pCD->clientFlags |= SM_Y;
578             }
579
580             if ((resourcePtr =
581                  getClientResource(proxyClientID, widthSizeStr))
582                 != (char *)NULL)
583             {
584                 pCD->clientWidth = atoi(resourcePtr);
585                 pCD->clientFlags |= SM_WIDTH;
586             }
587
588             if ((resourcePtr =
589                  getClientResource(proxyClientID, heightSizeStr))
590                 != (char *)NULL)
591             {
592                 pCD->clientHeight = atoi(resourcePtr);
593                 pCD->clientFlags |= SM_HEIGHT;
594             }
595
596             if ((resourcePtr =
597                  getClientResource(proxyClientID, initialStateStr))
598                 != (char *)NULL)
599             {
600                 pCD->clientState =
601                     (strcmp(resourcePtr, normalStateStr) == 0) ?
602                         NORMAL_STATE : MINIMIZED_STATE;
603                 pCD->clientFlags |= SM_CLIENT_STATE;
604             }
605
606             if ((workSpaceNamesP != (char **)NULL) &&
607                 ((resourcePtr =
608                   getClientResource(proxyClientID, workspacesStr))
609                  != (char *)NULL))
610             {
611                 *workSpaceNamesP = XtNewString(resourcePtr);
612             }
613
614             return True;
615         }
616     }
617
618     return False;
619 }
620
621 /*
622  *  Translate the client geometry into what's needed on restore.
623  */
624 static void
625 getClientGeometry(ClientData *pCD, int *clientX, int *clientY,
626                   unsigned int *clientWd, unsigned int *clientHt)
627 {
628     *clientX = pCD->clientX;
629     *clientY = pCD->clientY;
630     *clientWd = (pCD->widthInc != 0) ?
631         (pCD->clientWidth - pCD->baseWidth) / pCD->widthInc :
632             pCD->clientWidth;
633     *clientHt = (pCD->heightInc != 0) ?
634         (pCD->clientHeight - pCD->baseHeight) / pCD->heightInc :
635             pCD->clientHeight;
636 }
637
638 /*
639  *  Assumes: pCD->smClientID is not NULL
640  */
641 static Boolean
642 saveXSMPClient(FILE *fp, ClientData *pCD)
643 {
644     int clientX, clientY;
645     unsigned int clientWd, clientHt;
646     char *clientID = pCD->smClientID;
647
648     fprintf(fp, dbClientFormat, XSMPClientStr, clientID, clientID);
649
650     getClientGeometry(pCD, &clientX, &clientY, &clientWd, &clientHt);
651
652     if (SAVE_RESOURCE(pCD, WMSAVE_X))
653     {
654         fprintf(fp, xPositionStr, clientID);
655         fprintf(fp, intArg, clientX);
656     }
657
658     if (SAVE_RESOURCE(pCD, WMSAVE_Y))
659     {
660         fprintf(fp, yPositionStr, clientID);
661         fprintf(fp, intArg, clientY);
662     }
663
664     if (!pCD->pSD->useIconBox)
665     {
666         WmScreenData *pSD = pCD->pSD;
667         WmWorkspaceData *pWS;
668         int i;
669
670         for (i = 0; i < pCD->numInhabited; i++)
671         {
672             if ((pWS = GetWorkspaceData(pSD, pCD->pWsList[i].wsID))
673                 != (WmWorkspaceData *)NULL)
674             {
675                 if (SAVE_RESOURCE(pCD, WMSAVE_ICON_X))
676                 {
677                     fprintf(fp, iconXPosStr, clientID, pWS->name);
678                     fprintf(fp, intArg, pCD->pWsList[i].iconX);
679                 }
680
681                 if (SAVE_RESOURCE(pCD, WMSAVE_ICON_Y))
682                 {
683                     fprintf(fp, iconYPosStr, clientID, pWS->name);
684                     fprintf(fp, intArg, pCD->pWsList[i].iconY);
685                 }
686             }
687         }
688     }
689
690     if (SAVE_RESOURCE(pCD, WMSAVE_WIDTH))
691     {
692         fprintf(fp, widthSizeStr, clientID);
693         fprintf(fp, intArg, clientWd);
694     }
695
696     if (SAVE_RESOURCE(pCD, WMSAVE_HEIGHT))
697     {
698         fprintf(fp, heightSizeStr, clientID);
699         fprintf(fp, intArg, clientHt);
700     }
701
702     if (SAVE_RESOURCE(pCD, WMSAVE_STATE))
703     {
704         int clientState;
705
706         clientState = pCD->clientState & ~UNSEEN_STATE;
707
708         fprintf(fp, initialStateStr, clientID);
709         fprintf(fp, strArg, (clientState == NORMAL_STATE) ?
710                 normalStateStr : iconicStateStr);
711     }
712
713     if (SAVE_RESOURCE(pCD, WMSAVE_WORKSPACES))
714     {
715         char *clientWorkspaces = getClientWorkspaces(pCD);
716
717         if (clientWorkspaces != (char *)NULL)
718         {
719             fprintf(fp, workspacesStr, clientID);
720             fprintf(fp, strArg, clientWorkspaces);
721             XtFree(clientWorkspaces);
722         }
723     }
724
725     return True;
726 }
727
728 /*
729  *  Assumes: pCD->smClientID is NULL
730  */
731 static Boolean
732 saveProxyClient(FILE *fp, ClientData *pCD, int clientIDNum)
733 {
734     char clientID[50];
735     int clientState;
736     ProxyClientInfo proxyClientInfo;
737     int clientX, clientY;
738     unsigned int clientWd, clientHt;
739     char *clientWorkspaces;
740
741     if (!getProxyClientInfo(pCD, &proxyClientInfo))
742         return False;
743
744     sprintf(clientID, "%d", clientIDNum);
745     fprintf(fp, dbClientFormat, proxyClientStr, clientID, clientID);
746
747     fprintf(fp, screenStr, clientID);
748     fprintf(fp, intArg, proxyClientInfo.screen);
749
750     fprintf(fp, wmCommandStr, clientID);
751     fprintf(fp, strArg, proxyClientInfo.wmCommand);
752     free(proxyClientInfo.wmCommand);
753
754     if (proxyClientInfo.wmClientMachine != (char *)NULL)
755     {
756         fprintf(fp, wmClientMachineStr, clientID);
757         fprintf(fp, strArg, proxyClientInfo.wmClientMachine);
758         free(proxyClientInfo.wmClientMachine);
759     }
760
761     getClientGeometry(pCD, &clientX, &clientY, &clientWd, &clientHt);
762
763     fprintf(fp, xPositionStr, clientID);
764     fprintf(fp, intArg, clientX);
765
766     fprintf(fp, yPositionStr, clientID);
767     fprintf(fp, intArg, clientY);
768
769     if (!pCD->pSD->useIconBox)
770     {
771         WmScreenData *pSD = pCD->pSD;
772         WmWorkspaceData *pWS;
773         int i;
774
775         for (i = 0; i < pCD->numInhabited; i++)
776         {
777             if ((pWS = GetWorkspaceData(pSD, pCD->pWsList[i].wsID))
778                 != (WmWorkspaceData *)NULL)
779             {
780                 fprintf(fp, iconXPosStr, clientID, pWS->name);
781                 fprintf(fp, intArg, pCD->pWsList[i].iconX);
782
783                 fprintf(fp, iconYPosStr, clientID, pWS->name);
784                 fprintf(fp, intArg, pCD->pWsList[i].iconY);
785             }
786         }
787     }
788
789     fprintf(fp, widthSizeStr, clientID);
790     fprintf(fp, intArg, clientWd);
791
792     fprintf(fp, heightSizeStr, clientID);
793     fprintf(fp, intArg, clientHt);
794
795     clientState = pCD->clientState & ~UNSEEN_STATE;
796
797     fprintf(fp, initialStateStr, clientID);
798     fprintf(fp, strArg, (clientState == NORMAL_STATE) ?
799             normalStateStr : iconicStateStr);
800
801     clientWorkspaces = getClientWorkspaces(pCD);
802     if (clientWorkspaces != (char *)NULL)
803     {
804         fprintf(fp, workspacesStr, clientID);
805         fprintf(fp, strArg, clientWorkspaces);
806         XtFree(clientWorkspaces);
807     }
808
809     return True;
810 }
811
812 static void
813 dbRemoveProxyClientEntry(char *proxyClientID)
814 {
815     char resourceBuf[MAX_RESOURCE_LEN];
816
817     /* Remove entry from DB.  Since Xrm does not provide a means */
818     /* of removing something from the DB, we blank out key info. */
819     sprintf(resourceBuf, wmCommandStr, proxyClientID);
820     strcat(resourceBuf, ":");
821     XrmPutLineResource(&wmGD.clientResourceDB, resourceBuf);
822 }
823
824 /*
825  *  Add callbacks used in session management.
826  */
827 void
828 AddSMCallbacks(void)
829 {
830     XtAddCallback(wmGD.topLevelW, XtNsaveCallback,
831                   smSaveYourselfCallback, (XtPointer)NULL);
832     XtAddCallback(wmGD.topLevelW, XtNdieCallback,
833                   smDieCallback, (XtPointer)NULL);
834 }
835
836 /*
837  *  Resign from session management, closing any connections made.
838  */
839 void
840 ResignFromSM(void)
841 {
842     if (wmGD.topLevelW)
843     {
844         XtVaSetValues(wmGD.topLevelW,
845                       XtNjoinSession, False,
846                       NULL);
847     }
848 }
849
850 /*
851  *  Exit the WM, being polite by first resigning from session mgmt.
852  */
853 void
854 ExitWM(int exitCode)
855 {
856     ResignFromSM();
857     exit(exitCode);
858 }
859
860 /*
861  *  Read our private database of client resources.
862  */
863 XrmDatabase
864 LoadClientResourceDB(void)
865 {
866     char dbFileName[MAXPATHLEN];
867
868     buildDBFileName(dbFileName, False);
869
870     return XrmGetFileDatabase(dbFileName);
871 }
872
873 /*
874  *  Write our private database of client resources.
875  */
876 XrmDatabase
877 SaveClientResourceDB(void)
878 {
879     String mySessionID;
880     char dbFileName[MAXPATHLEN];
881     FILE *fp;
882     int scr;
883     WmScreenData *pSD;
884     ClientData *pCD;
885     int clientIDNum = 0;
886     ClientListEntry *pCL;
887
888     /* Iterate through client list, saving */
889     /* appropriate resources for each. */
890     buildDBFileName(dbFileName, True);
891     if ((fp = fopen(dbFileName, "w")) == (FILE *)NULL)
892         return (XrmDatabase)NULL;
893
894     XtVaGetValues(wmGD.topLevelW,
895                   XtNsessionID, &mySessionID,
896                   NULL);
897     fprintf(fp, dbHeader, dtwmFileName, "dtwm Version XSMP1.0",
898             (mySessionID != (String)NULL) ? mySessionID : "");
899
900     for (scr = 0; scr < wmGD.numScreens; scr++)
901     {
902         pSD = &(wmGD.Screens[scr]);
903
904         for (pCL = pSD->clientList;
905              pCL != (ClientListEntry *)NULL;
906              pCL = pCL->nextSibling)
907         {
908             /* Each client may be in list twice: normal & icon */
909             if (pCL->type != NORMAL_STATE)
910                 continue;
911
912             pCD = pCL->pCD;
913
914             if (pCD->smClientID != (String)NULL)
915             {
916                 saveXSMPClient(fp, pCD);
917             }
918             else
919             {
920                 if (saveProxyClient(fp, pCD, clientIDNum))
921                     clientIDNum++;
922             }
923         }
924     }
925
926     fclose(fp);
927
928     /* Retrieve database from file. */
929     return XrmGetFileDatabase(dbFileName);
930 }
931
932 /*
933  *  As with FindDtSessionMatch(), sets properties and then returns
934  *  an allocated string of workspace names.  This string must be
935  *  freed by the caller using XtFree().
936  */
937 Boolean
938 FindClientDBMatch(ClientData *pCD, char **workSpaceNamesP)
939 {
940     return (findXSMPClientDBMatch(pCD, workSpaceNamesP) ||
941             findProxyClientDBMatch(pCD, workSpaceNamesP));
942 }
943
944 Boolean
945 GetSmClientIdClientList(ClientData ***clients, int *nClients)
946 {
947     int scr;
948     WmScreenData *pSD;
949     ClientData *pCD;
950     ClientListEntry *pCL;
951
952     *nClients = 0;
953     *clients = (ClientData **)NULL;
954     for (scr = 0; scr < wmGD.numScreens; scr++)
955     {
956         pSD = &(wmGD.Screens[scr]);
957
958         for (pCL = pSD->clientList;
959              pCL != (ClientListEntry *)NULL;
960              pCL = pCL->nextSibling)
961         {
962             /* Each client may be in list twice: normal & icon */
963             if (pCL->type != NORMAL_STATE)
964                 continue;
965
966             pCD = pCL->pCD;
967
968             if (pCD->smClientID != (String)NULL)
969             {
970                 /* addClientToList() reclaims memory on failure. */
971                 if (!addClientToList(clients, nClients, pCD))
972                     return False;
973             }
974         }
975     }
976
977     return True;
978 }
979
980 void
981 SortClientListByWorkspace(ClientData **clients, int nClients)
982 {
983     if (nClients > 0)
984     {
985         qsort((void *)clients, nClients,
986               sizeof(ClientData *), clientWorkspaceCompare);
987     }
988 }
989
990 void
991 LoadClientIconPositions(ClientData *pCD)
992 {
993     char resourceBuf[MAX_RESOURCE_LEN];
994     WmScreenData *pSD = pCD->pSD;
995     WmWorkspaceData *pWS;
996     int i;
997     char *resourcePtr;
998
999     if (wmGD.clientResourceDB == (XrmDatabase)NULL)
1000         return;
1001
1002     if (pCD->smClientID != (String)NULL)
1003     {
1004         for (i = 0; i < pCD->numInhabited; i++)
1005         {
1006             if ((pWS = GetWorkspaceData(pSD, pCD->pWsList[i].wsID))
1007                 != (WmWorkspaceData *)NULL)
1008             {
1009                 sprintf(resourceBuf, iconXPosStr, "%s", pWS->name);
1010                 if ((resourcePtr =
1011                      getXSMPResource(pCD, WMSAVE_ICON_X, resourceBuf))
1012                     != (char *)NULL)
1013                 {
1014                     pCD->pWsList[i].iconX = atoi(resourcePtr);
1015                     pCD->clientFlags |= SM_ICON_X;
1016                 }
1017
1018                 sprintf(resourceBuf, iconYPosStr, "%s", pWS->name);
1019                 if ((resourcePtr =
1020                      getXSMPResource(pCD, WMSAVE_ICON_Y, resourceBuf))
1021                     != (char *)NULL)
1022                 {
1023                     pCD->pWsList[i].iconY = atoi(resourcePtr);
1024                     pCD->clientFlags |= SM_ICON_Y;
1025                 }
1026             }
1027         }
1028         return;
1029     }
1030
1031     /* Proxy client */
1032     if (!smClientDBCheckpointed)
1033     {
1034         char *proxyClientID;
1035
1036         if ((proxyClientID = findProxyClientID(pCD)) != (char *)NULL)
1037         {
1038             for (i = 0; i < pCD->numInhabited; i++)
1039             {
1040                 if ((pWS = GetWorkspaceData(pSD, pCD->pWsList[i].wsID))
1041                     != (WmWorkspaceData *)NULL)
1042                 {
1043                     sprintf(resourceBuf, iconXPosStr, "%s", pWS->name);
1044                     if ((resourcePtr =
1045                          getClientResource(proxyClientID, resourceBuf))
1046                         != (char *)NULL)
1047                     {
1048                         pCD->pWsList[i].iconX = atoi(resourcePtr);
1049                         pCD->clientFlags |= SM_ICON_X;
1050                     }
1051
1052                     sprintf(resourceBuf, iconYPosStr, "%s", pWS->name);
1053                     if ((resourcePtr =
1054                          getClientResource(proxyClientID, resourceBuf))
1055                         != (char *)NULL)
1056                     {
1057                         pCD->pWsList[i].iconY = atoi(resourcePtr);
1058                         pCD->clientFlags |= SM_ICON_Y;
1059                     }
1060                 }
1061             }
1062             dbRemoveProxyClientEntry(proxyClientID);
1063         }
1064     }
1065 }