dtinfo subtree clients
[oweals/cde.git] / cde / programs / dtinfo / clients / dtinfo_start / dtinfo_start.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: dtinfo_start.c /main/9 1999/09/20 13:26:59 mgreess $
24  *
25  * (c) Copyright 1996 Digital Equipment Corporation.
26  * (c) Copyright 1996 Hewlett-Packard Company.
27  * (c) Copyright 1996 International Business Machines Corp.
28  * (c) Copyright 1996 Sun Microsystems, Inc.
29  * (c) Copyright 1996 Novell, Inc. 
30  * (c) Copyright 1996 FUJITSU LIMITED.
31  * (c) Copyright 1996 Hitachi.
32  *
33  * This file contains the main program for: dtinfo_start
34  */
35
36 #include <stdio.h>
37 #include <stdlib.h>
38
39 #include <X11/Intrinsic.h>
40
41 #include <Xm/XmP.h>
42 #include <Xm/XmAll.h>
43
44 #include <Dt/EnvControlP.h>
45 #include <Dt/Action.h>
46 #include <Dt/DtGetMessageP.h>
47 #include <Dt/MsgLog.h>
48
49 #include <Tt/tt_c.h>
50
51 #include "dtinfo_start.h"
52 #include "dtinfo_start.opnums"
53
54 /*
55  * Operation names, number of arguments and default action names
56  */
57 static const char       * LOAD_INFO_LIB_STRING = "DtInfo_LoadInfoLib";
58 static const int        LOAD_INFO_LIB_ARGS = 3;
59 static const char       * LOAD_INFO_LIB_ACTION = "DtInfoStart";
60
61 static const char       * SHOW_INFO_AT_LOC_STRING = "DtInfo_ShowInfoAtLoc";
62 static const int        SHOW_INFO_AT_LOC_ARGS = 4;
63 static const char       * SHOW_INFO_LIB_SECTION_ACTION = "DtInfoStartAtLoc";
64
65 /*
66  * Static global vars
67  */
68 static Widget           top_level;
69
70 static XtAppContext     app_context;
71
72 static const int        time_out = 1000;        /* milliseconds */
73
74 static const int        SET_NUM = 1;            /* message cat set number */
75
76 static const char       * name = "DtInfo"; /* ptype name and Xt name */
77
78 /*
79  * Forward declarations for static functions
80  */
81 static int InvokeAction (
82         char                    * prog_name, 
83         int                     op_num,
84         const char              * action_name, 
85         char                    * exec_host, 
86         char                    * info_lib, 
87         char                    * topic);
88
89 static void ActionDoneCallback (
90         DtActionInvocationID    id,
91         XtPointer               client_data,
92         DtActionArg             * args,
93         int                     num_args,
94         DtActionStatus          status);
95
96 static void Exit (
97         XtPointer               client_data,
98         XtIntervalId            id);
99
100 static Tt_status ConnectToMessageServer (
101         char                    * prog_name);
102
103 static void ReceiveMessage (
104         XtPointer               client_data,
105         int                     * fd,
106         XtInputId               * id);
107
108 static void LogToolTalkError (
109         DtMsgLogType            msg_type,
110         char                    * prog_name,
111         char                    * function_name,
112         Tt_status               error_num);
113 static void DieFromToolTalkError (
114         Widget                  parent,
115         char                    * errfmt,
116         Tt_status               status);
117
118
119 int
120 main ( 
121         int                     argc,
122         char                    ** argv) 
123 {
124         Display                 * display;
125         Arg                     args[10];
126         int                     i = 0;
127         Tt_status               status;
128   
129         XtSetLanguageProc (NULL, NULL, NULL);
130
131         _DtEnvControl (DT_ENV_SET);
132
133         XtToolkitInitialize ();
134
135         app_context = XtCreateApplicationContext ();
136
137         if (!(display = XtOpenDisplay (app_context, NULL, NULL, name,
138                                        NULL, 0, &argc, argv))) {
139                 DtMsgLogMessage (argv[0], DtMsgLogError, 
140                         (char *) GETMESSAGE (SET_NUM, 7, "XtOpenDisplay() failed.  Perhaps the DISPLAY environment\nvariable is not set or is invalid."));
141                 exit (1);
142         }
143   
144         /*
145          * Create a minimalist application shell - needed by
146          * DtActionInvoke.
147          */
148         XtSetArg (args[i], XtNallowShellResize,         True);  i++;
149         XtSetArg (args[i], XtNmappedWhenManaged,        False); i++;
150         XtSetArg (args[i], XtNheight,                   1);     i++;
151         XtSetArg (args[i], XtNwidth,                    1);     i++;
152
153         top_level = XtAppCreateShell (argv[0], 
154                                       name, 
155                                       topLevelShellWidgetClass, 
156                                       display, 
157                                       args, 
158                                       i);
159
160         XtRealizeWidget (top_level);
161
162         /*  
163          * Initialize the desktop
164          */
165         if (DtAppInitialize (app_context, display, top_level, argv[0], name) 
166                         == False) {
167                 DtMsgLogMessage (argv[0], DtMsgLogError, 
168                         (char *) GETMESSAGE (SET_NUM, 8, "DtAppInitialize() failed.  Perhaps the desktop environment\nis not properly installed."));
169                 exit (1);
170         }
171
172         /*
173          * Connect to the message server
174          */
175         status = ConnectToMessageServer (argv[0]);
176         if (TT_OK != status) {
177                 char *errfmt;
178
179                 /*
180                  * An error Message has already been logged.
181                  */
182                 errfmt = GETMESSAGE (2, 2,
183                           "Could not connect to ToolTalk:\n%s\nExiting ...");
184                 DieFromToolTalkError (top_level, errfmt, status);
185         }
186
187         /* 
188          * Load the datatypes and actions 
189          */
190         DtDbLoad();
191
192         XtAppMainLoop (app_context);
193 }
194
195
196 static int
197 InvokeAction (
198         char                    * prog_name, 
199         int                     op_num,
200         const char              * action_name, 
201         char                    * exec_host, 
202         char                    * info_lib, 
203         char                    * topic)
204 {
205         DtActionArg             * args;
206         int                     num_args = 1;
207         DtActionInvocationID    action_id;
208
209         if (op_num == SHOW_INFO_AT_LOC)
210                 num_args = 2;
211
212         args = (DtActionArg *) XtCalloc (num_args, sizeof (DtActionArg));
213         if (!args) {
214                 DtMsgLogMessage (prog_name, DtMsgLogError, 
215                         (char *) GETMESSAGE (SET_NUM, 6,
216                         "Cannot invoke an action because malloc(1) failed."));
217                 return (0);
218         }
219
220         args[0].argClass = DtACTION_FILE;
221         args[0].u.file.name = info_lib;
222
223         if (op_num == SHOW_INFO_AT_LOC) {
224                 args[1].argClass = DtACTION_BUFFER;
225                 args[1].u.buffer.name = topic;
226                 args[1].u.buffer.size = strlen (topic) + 1;
227                 args[1].u.buffer.writable = False;
228         }
229
230         action_id = DtActionInvoke (top_level,
231                                     (char *) action_name,
232                                     args,
233                                     num_args,
234                                     NULL,
235                                     exec_host,
236                                     NULL,
237                                     True,
238                                     (DtActionCallbackProc) ActionDoneCallback,
239                                     prog_name);
240
241 #if defined(DEBUG)
242         DtMsgLogMessage (prog_name, DtMsgLogError, "Invoked %s\n", action_name);
243 #endif
244
245         /*
246          * The following test, comment and code are from:
247          *   $TOP/dtaction/Main.c
248          *
249          * "Set up a timer if we didn't get a valid procId --
250          *  since there will be no invocation update in that case."
251          */
252         if (!action_id) {
253                 XtAppAddTimeOut (app_context,
254                                  10, 
255                                  (XtTimerCallbackProc) Exit,
256                                  NULL);
257         }
258
259         return (1);
260 }
261
262
263 /* ARGSUSED */
264 static void 
265 ActionDoneCallback (
266         DtActionInvocationID    id,
267         XtPointer               client_data,
268         DtActionArg             * args,
269         int                     num_args,
270         DtActionStatus          status)
271 {
272 #if defined(DEBUG)
273         char *status_name;
274
275         switch (status) {
276                 case DtACTION_DONE:
277                     status_name = "DtACTION_DONE";
278                     break;
279                 case DtACTION_OK:
280                     status_name = "DtACTION_DONE";
281                     break;
282                 case DtACTION_INVOKED:
283                     status_name = "DtACTION_DONE";
284                     break;
285                 case DtACTION_FAILED:
286                     status_name = "DtACTION_DONE";
287                     break;
288                 case DtACTION_CANCELED:
289                     status_name = "DtACTION_DONE";
290                     break;
291                 case DtACTION_INVALID_ID:
292                     status_name = "DtACTION_DONE";
293                     break;
294                 case DtACTION_STATUS_UPDATE:
295                     status_name = "DtACTION_DONE";
296                     break;
297                 default:
298                     status_name = "UNKNOWN";
299                     break;
300         }
301 #endif
302         switch (status) {
303                 case DtACTION_DONE:
304                 case DtACTION_OK:
305 #if defined(DEBUG)
306                         DtMsgLogMessage ("dtinfo_start", DtMsgLogError,
307                                          "Action returned %s\n", status_name);
308 #endif
309                         XtAppAddTimeOut(app_context, 10 * time_out, 
310                                         (XtTimerCallbackProc) Exit,
311                                         NULL);
312
313                 case DtACTION_INVOKED:
314                 case DtACTION_FAILED:
315                 case DtACTION_CANCELED:
316                 case DtACTION_INVALID_ID:
317                 case DtACTION_STATUS_UPDATE:
318                 default:
319 #if defined(DEBUG)
320                         DtMsgLogMessage ("dtinfo_start", DtMsgLogError,
321                                          "Action returned %s\n", status_name);
322 #endif
323                         XtAppAddTimeOut(app_context, 10 * time_out, 
324                                         (XtTimerCallbackProc) Exit,
325                                         NULL);
326         }
327 }
328
329
330 static Tt_status
331 ConnectToMessageServer (
332         char                    * prog_name)
333 {
334         int                     mark;
335         char                    * procid;
336         int                     ttfd;
337         Tt_status               status;
338   
339         mark = tt_mark();
340
341         procid = tt_open();
342         status = tt_pointer_error (procid);
343         if (status != TT_OK) {
344                 LogToolTalkError (DtMsgLogError, prog_name,
345                                     "tt_open", status);
346                 tt_release (mark);
347                 return (status);
348         }
349   
350         status = tt_ptype_declare (name);
351         if (status != TT_OK) {
352                 LogToolTalkError (DtMsgLogError, prog_name, 
353                                     "tt_ptype_declare", status);
354                 tt_release (mark);
355                 return (status);
356         }
357
358         ttfd = tt_fd ();
359         if ((tt_int_error (ttfd)) != TT_OK) {
360                 LogToolTalkError (DtMsgLogError, prog_name, 
361                                     "tt_fd", status);
362                 tt_release (mark);
363                 return (status);
364         }
365
366         XtAppAddInput (app_context, ttfd, (XtPointer) XtInputReadMask,
367                        ReceiveMessage, (char *) prog_name);
368
369         status = tt_session_join (tt_default_session());
370         if ((tt_int_error (ttfd)) != TT_OK) {
371                 LogToolTalkError (DtMsgLogError, prog_name, 
372                                     "tt_session_join", status);
373                 tt_release (mark);
374                 return (status);
375         }
376   
377         tt_release(mark);
378
379         return (TT_OK);
380 }
381
382
383 /* ARGSUSED */
384 static void
385 ReceiveMessage (
386         XtPointer               client_data,
387         int                     * fd,
388         XtInputId               * id)
389 {
390         static int              last_op_num = 0;
391         int                     op_num;
392         int                     opstatus;
393         int                     num_args;
394         Tt_message              message;
395         const char              * action_name;
396         char                    * exec_host;
397         char                    * locale;
398         char                    * topic = NULL;
399         char                    * prog_name = (char *) client_data;
400         static                  char lang[200];
401         const char              * op_name;
402         char                    * info_lib = NULL;
403
404         message = tt_message_receive();
405
406         /* from tt_message_receive(3), and after examining other TT clients */
407         if (message == 0) return;
408
409         if (tt_pointer_error (message) != TT_OK) {
410                 LogToolTalkError (DtMsgLogError, prog_name, 
411                                     "tt_message_receive", 
412                                     tt_pointer_error (message));
413                 exit (1);
414         }
415
416         op_num = tt_message_opnum (message);
417         switch (op_num) {
418
419                 case LOAD_INFO_LIB:
420                         op_name = LOAD_INFO_LIB_STRING;
421                         break;
422
423                 case SHOW_INFO_AT_LOC:
424                         op_name = SHOW_INFO_AT_LOC_STRING;
425                         break;
426
427                 default: {
428                         /*
429                          * Don't know how I got this message
430                          */
431                         DtMsgLogMessage (prog_name, DtMsgLogError, 
432                                 (char *) GETMESSAGE (SET_NUM, 11, "The message operation '%d' is not supported."),
433                                 op_num);
434
435                         tt_message_reject (message);
436                         tt_message_destroy(message);
437                         exit (1);
438                 }
439         }
440
441         /*
442          * Check the number of arguments
443          */
444         num_args =  tt_message_args_count (message);
445         if ((op_num == LOAD_INFO_LIB &&    num_args != LOAD_INFO_LIB_ARGS) ||
446             (op_num == SHOW_INFO_AT_LOC && num_args != SHOW_INFO_AT_LOC_ARGS)) {
447
448                 DtMsgLogMessage (prog_name, DtMsgLogError, 
449                         (char *) GETMESSAGE (SET_NUM, 12, "Message '%s' does not have the required\nnumber of arguments '%d'."),
450                         op_name, 
451                         (op_num == LOAD_INFO_LIB) ?
452                                 LOAD_INFO_LIB_ARGS : SHOW_INFO_AT_LOC_ARGS);
453                 tt_message_reject (message);
454                 tt_message_destroy(message);
455                 exit (1);
456         }
457
458         /*
459          * Check for an info lib
460          */
461         info_lib = tt_message_file (message);
462         if ((tt_ptr_error (info_lib) != TT_OK) || info_lib == NULL) {
463                 /*
464                  * Use DTINFOLIBDEFAULT if it is defined
465                  */
466                 if ((info_lib = getenv ("DTINFOLIBDEFAULT")) == NULL) {
467                         DtMsgLogMessage (prog_name, DtMsgLogError, 
468                                 (char *) GETMESSAGE (SET_NUM, 10, "An InfoLib was not provided in the '%s' message\nand the environment variable DTINFOLIBDEFAULT was not defined."),
469                                 op_name);
470                         exit (1);
471                 }
472         }
473
474         if ((action_name = tt_message_arg_val (message, 0)) == NULL) {
475
476                 action_name = (op_num == LOAD_INFO_LIB) ? 
477                         LOAD_INFO_LIB_ACTION : SHOW_INFO_LIB_SECTION_ACTION;
478
479                 DtMsgLogMessage (prog_name, DtMsgLogWarning,
480                         (char *) GETMESSAGE (SET_NUM, 13, "Message '%s' specified a NULL action name.\nAction '%s' will be invoked."),
481                         op_name,
482                         action_name);
483         }
484
485         if ((exec_host = tt_message_arg_val (message, 1)) == NULL) {
486
487                 DtMsgLogMessage (prog_name, DtMsgLogWarning, 
488                         (char *) GETMESSAGE (SET_NUM, 14, "Message '%s' specified a NULL execution host."),
489                         op_name);
490         }
491
492         if ((locale = tt_message_arg_val (message, 2)) == NULL) {
493
494                 DtMsgLogMessage (prog_name, DtMsgLogWarning,
495                         (char *) GETMESSAGE (SET_NUM, 15, "Message '%s' specified a NULL locale."),
496                         op_name);
497         }
498         else {
499                 /*
500                  * Put locale into the environment so that DtActionInvoke
501                  * will propagate it to the dtinfo process.
502                  */
503                 (void) sprintf (lang, "LANG=%s", locale);
504                 (void) putenv (lang);
505         }
506
507         if (op_num == SHOW_INFO_AT_LOC) {
508                 if ((topic = tt_message_arg_val (message, 3)) == NULL) {
509
510                         DtMsgLogMessage (prog_name, DtMsgLogError, 
511                                 (char *) GETMESSAGE (SET_NUM, 16, "Message '%s' specified a NULL topic."),
512                                 op_name);
513                         exit (1);
514                 }
515         }
516
517         if (op_num == last_op_num)
518         {
519 #if defined(DEBUG)
520             DtMsgLogMessage("dtinfo_start", DtMsgLogError,
521                             "Received a duplicate tooltalk message:  %s",
522                             (op_num == LOAD_INFO_LIB) ? 
523                               "LOAD_INFO_LIB" : "SHOW_INFO_AT_LOC" );
524 #endif
525         }
526         else
527         {
528 #if defined(DEBUG)
529             DtMsgLogMessage(
530                 "dtinfo_start", DtMsgLogError,
531                 "Received a tooltalk message:  %s\nInvoking action %s",
532                 (op_num==LOAD_INFO_LIB) ? "LOAD_INFO_LIB" : "SHOW_INFO_AT_LOC",
533                 action_name);
534 #endif
535             last_op_num = op_num;
536             opstatus = InvokeAction (prog_name, 
537                                      op_num, 
538                                      action_name, 
539                                      exec_host, 
540                                      info_lib,
541                                      topic);
542             if (!opstatus) {
543                     tt_message_fail (message);
544                     exit (1);
545             }
546         }
547
548         tt_message_reply (message);
549
550         tt_message_destroy(message);
551 }
552
553
554 static void
555 LogToolTalkError (
556         DtMsgLogType            msg_type,
557         char                    * prog_name,
558         char                    * function_name,
559         Tt_status               error_num)
560 {
561         char                    *message;
562         message = tt_status_message(error_num);
563         DtMsgLogMessage (prog_name, msg_type, "%s: %s", function_name, message);
564 }
565
566 #define GETXMSTRING(s, m, d)    XmStringCreateLocalized(GETMESSAGE(s,m,d))
567
568 static void
569 ExitCB (Widget dialog, XtPointer client_data, XtPointer call_data)
570 {
571     exit((size_t) client_data);
572 }
573
574 static void
575 DieFromToolTalkError(Widget parent, char *errfmt, Tt_status status)
576 {
577     Arg          args[10];
578     Widget       dialog, dialogShell;
579     char        *errmsg, *statmsg, *title;
580     XmString     xms_errmsg, xms_ok, xms_title;
581     int          n;
582
583     if (! tt_is_err(status)) return;
584
585     statmsg = tt_status_message(status);
586     errmsg = XtMalloc(strlen(errfmt) + strlen(statmsg) + 2);
587     sprintf(errmsg, errfmt, statmsg);
588
589     xms_ok = GETXMSTRING(2, 3, "OK");
590     xms_errmsg = XmStringCreateLocalized(errmsg);
591     xms_title = GETXMSTRING(2, 1, "Dtinfo: Error");
592
593     n = 0;
594     XtSetArg(args[n], XmNautoUnmanage, False); n++;
595     XtSetArg(args[n], XmNokLabelString, xms_ok); n++;
596     XtSetArg(args[n], XmNdialogTitle, xms_title); n++;
597     XtSetArg(args[n], XmNmessageString, xms_errmsg); n++;
598     XtSetArg(args[n], XmNdialogStyle, XmDIALOG_FULL_APPLICATION_MODAL); n++;
599
600     dialog = XmCreateErrorDialog(parent, "DtinfoStartError", args, n);
601     XtAddCallback(dialog, XmNokCallback, ExitCB, (XtPointer) status);
602     XtUnmanageChild(XmMessageBoxGetChild(dialog, XmDIALOG_CANCEL_BUTTON));
603     XtUnmanageChild(XmMessageBoxGetChild(dialog, XmDIALOG_HELP_BUTTON));
604
605     /*
606      * Disable the frame menu from dialog since we don't want the user
607      * to be able to close dialogs with the frame menu
608      */
609     dialogShell = XtParent(dialog);
610     n = 0;
611     XtSetArg(args[n], XmNmwmDecorations, MWM_DECOR_ALL | MWM_DECOR_MENU); n++;
612     XtSetValues(dialogShell, args, n);
613     XtManageChild(dialog);
614     XtRealizeWidget(dialogShell);
615
616     XtFree(errmsg);
617     XmStringFree(xms_ok);
618     XmStringFree(xms_errmsg);
619     XmStringFree(xms_title);
620
621     while (TRUE)
622       XtAppProcessEvent(XtWidgetToApplicationContext(dialog), XtIMAll);
623 }
624
625 /* ARGSUSED */
626 static void
627 Exit (
628         XtPointer               client_data,
629         XtIntervalId            id)
630 {
631         /*
632          * The following code to determine if DtActionInvoke
633          * resulted in the posting a dialog is from:
634          *   $TOP/dtaction/Main.c
635          */
636         if (top_level->core.num_popups ==  0)
637                 exit(0);
638
639         XtAppAddTimeOut (app_context, 
640                          time_out, 
641                          (XtTimerCallbackProc) Exit, 
642                          NULL);
643
644 }