Add GNU LGPL headers to all .c .C and .h files
[oweals/cde.git] / cde / programs / dtstyle / I18nEnv.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: I18nEnv.c /main/4 1997/08/11 12:31:10 samborn $ */
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 /************************************<+>*************************************
34  ****************************************************************************
35  **
36  **   File:        I18nEnv.c
37  **
38  **   Description: Controls the Dtstyle I18N component interaction with 
39  **                the environment.
40  **
41  **
42  ****************************************************************************
43  ************************************<+>*************************************/
44
45 /*+++++++++++++++++++++++++++++++++++++++*/
46 /* include files                         */
47 /*+++++++++++++++++++++++++++++++++++++++*/
48
49 #include <pwd.h>
50 #include <X11/Xlib.h>
51 #include <X11/Intrinsic.h>
52
53 #include <DtHelp/LocaleXlate.h>  /* for locale equivalence between platforms */
54
55 #include "Main.h"
56 #include "I18nEnv.h"
57
58 /*+++++++++++++++++++++++++++++++++++++++*/
59 /* include extern functions              */
60 /*+++++++++++++++++++++++++++++++++++++++*/
61
62 /*+++++++++++++++++++++++++++++++++++++++*/
63 /* Local #defines                        */
64 /*+++++++++++++++++++++++++++++++++++++++*/
65
66
67 /*+++++++++++++++++++++++++++++++++++++++*/
68 /* Internal Functions                    */
69 /*+++++++++++++++++++++++++++++++++++++++*/
70
71 static int GetUserEnv(I18nEnv *env);
72 static int FindCDELocaleName(UserEnv *uenv);
73 static int GetUserIMSelectionFile(I18nEnv *env);
74 static int GetUserFileName(I18nEnv *env);
75 static int ReadImSelectionFile(FileSel *fsel, FILE *fp);
76 static int GetImsList(I18nEnv *env, char *hostname);
77 static void TimeOutProc(XtPointer client_data, XtIntervalId* timer);
78 static void ReadPipe(XtPointer client_data, int *fd, XtInputId *id);
79 static int ProcessBuf(char *savebuf, I18nEnv *env);
80 static int CheckHostname(I18nEnv *env, char *hostname);
81 static void PutSelectMode(FILE *fp, int start_mode);
82 static void PutImsName(FILE *fp, char *im_name);
83 static void PutHostname(FILE *fp, char *hostname);
84 static void PutSelectionEntry(FILE *fp, char *tag, char *value);
85
86
87 /*+++++++++++++++++++++++++++++++++++++++*/
88 /* Internal Variables                    */
89 /*+++++++++++++++++++++++++++++++++++++++*/
90
91 /*+++++++++++++++++++++++++++++++++++++++*/
92 /* Global Variables                      */
93 /*+++++++++++++++++++++++++++++++++++++++*/
94
95 I18nEnv i18n_env;
96
97
98 /*+++++++++++++++++++++++++++++++++++++++*/
99 /* strcasecmp                            */
100 /*+++++++++++++++++++++++++++++++++++++++*/
101
102 #ifdef NEED_STRCASECMP
103 /*
104  * In case strcasecmp is not provided by the system here is one
105  * which does the trick.
106  */
107 static int
108 strcasecmp(register const char *s1,
109            register const char *s2)
110 {
111     register int c1, c2;
112
113     while (*s1 && *s2) {
114         c1 = isupper(*s1) ? tolower(*s1) : *s1;
115         c2 = isupper(*s2) ? tolower(*s2) : *s2;
116         if (c1 != c2)
117             return (c1 - c2);
118         s1++;
119         s2++;
120     }
121     return (int) (*s1 - *s2);
122 }
123 #endif
124
125
126 /*+++++++++++++++++++++++++++++++++++++++*/
127 /* _DtI18nGetEnvValues                   */
128 /*+++++++++++++++++++++++++++++++++++++++*/
129
130 int
131 _DtI18nGetEnvValues(
132     I18nEnv *env
133 )
134 {
135     int ret = NoError;
136
137     /* Get the user environment */
138     env->user_env = (UserEnv *) XtMalloc(sizeof(UserEnv));    
139     ret = GetUserEnv(env);
140
141     /* Get the user selection stored in the IM Selection File */
142     if (ret == NoError) {
143         env->file_sel = (FileSel *) XtMalloc(sizeof(FileSel));    
144         ret = GetUserIMSelectionFile(env);
145     }
146
147     /* Alloc the user selection */
148     env->ims_sel = (ImsSel *) XtMalloc(sizeof(ImsSel)); 
149     env->ims_sel->ims_list = NULL;
150     env->ims_sel->ims_list_size = 0 ;
151     env->ims_sel->host_name = NULL ;
152
153     /* Get the Input Method available on the selected host.
154      * The selected host is the one stored in the IMS Selection File.
155      */
156     if (ret == NoError)
157         ret = _DtI18nGetImList(env, env->file_sel->hostname);
158
159     return ret;
160 }
161
162 /*+++++++++++++++++++++++++++++++++++++++*/
163 /* GetUserEnv                            */
164 /*+++++++++++++++++++++++++++++++++++++++*/
165 static int
166 GetUserEnv(
167      I18nEnv *env
168 )
169 {
170     UserEnv *uenv = env->user_env ;
171     int ret = NoError;
172     char buf[BUFSIZ], *p;
173
174     /* get the host name */
175     gethostname(buf, BUFSIZ);
176     uenv->localhostname = XtNewString(buf);
177
178     /* get the user name */
179     if ((p = getlogin()) == NULL) {
180       struct passwd *pw;
181
182       pw = getpwuid(getuid());
183       p = pw->pw_name;
184     }
185     
186     uenv->username = XtNewString(p);
187
188     /* get the display name */
189     uenv->displayname = XtNewString(XDisplayString(XtDisplay(env->shell)));
190     
191     /* get the locale */
192     if ((p = getenv("LANG")) && *p)
193         uenv->locale = XtNewString(p);
194     else
195         return ErrNoLocale;
196     
197     /* find the CDE generic locale name */
198     if (FindCDELocaleName(uenv) != NoError)
199         return ErrNoCDELocale;
200     
201     /* get the home directory */
202     if ((p = getenv("HOME")) && *p)
203         uenv->homedir = XtNewString(p);
204     else 
205         ret = ErrNoHome;
206
207     return ret;
208 }
209
210 static int
211 FindCDELocaleName(
212     UserEnv *uenv
213 )
214 {
215    _DtXlateDb db = NULL;
216    int  ret = NoError;
217    char plat[_DtPLATFORM_MAX_LEN];
218    int  execver;
219    int  compver;
220
221    ret = _DtLcxOpenAllDbs(&db);
222
223    if (ret == NoError)
224        ret = _DtXlateGetXlateEnv(db, plat, &execver, &compver);
225
226    if (ret == NoError)
227        ret = _DtLcxXlateOpToStd(db, plat, compver, DtLCX_OPER_SETLOCALE,
228                                 uenv->locale, &uenv->CDE_locale,
229                                 NULL, NULL, NULL);
230    if (ret == NoError)
231        ret = _DtLcxCloseDb(&db);
232
233    return ret;
234 }
235
236 static int
237 GetUserIMSelectionFile(
238     I18nEnv *env
239 )
240 {
241     int         ret = NoError;
242     FILE        *fp;
243
244     ret = GetUserFileName(env);
245     
246     if (ret == NoError) {
247         /* Look if this file is readable */
248         if ((fp = fopen(env->file_sel->fname, "r")) == NULL)
249             env->file_sel->start_mode = -1;
250             return ErrNoSelectionFile;
251     }
252
253     start_tag_line(env->file_sel->fname);
254     ret = ReadImSelectionFile(env->file_sel, fp);
255     
256     return ret;
257 }
258
259 static int 
260 GetUserFileName(
261     I18nEnv *env
262 )
263 {
264     int                status, ret = NoError;
265     char               *path, *tmp_path;
266     int                len = 0;
267     struct stat        buf;
268
269     /* The user IMS Selection File should be of the following form:
270      * $HOME/.dt/ims/[display-name]/CDE-locale
271      */
272
273     path = (char *) XtMalloc((MAXPATHLEN + 1) * sizeof(char));
274
275     strcpy(path, env->user_env->homedir);
276     strcat(path, DtUSER_IMSFS_DIR);
277
278     /* Look if there is a display specific directory */
279
280     tmp_path = (char *) XtMalloc((MAXPATHLEN + 1) * sizeof(char));
281     
282     strcpy(tmp_path, path);
283     strcat(tmp_path, "/");
284     strcat(tmp_path, env->user_env->displayname);
285
286     if ((status = stat (tmp_path, &buf)) == 0) {
287         strcat(path, "/");
288         strcat(path, env->user_env->displayname);
289     }
290
291     XtFree(tmp_path);
292
293     /* Now add the CDE-specific locale name */
294
295     strcat(path, "/");
296     strcat(path, env->user_env->CDE_locale);
297
298     env->file_sel->fname = path;
299
300     return ret;
301 }           
302
303 static int
304 ReadImSelectionFile(
305     FileSel     *fsel,
306     FILE        *fp
307 )
308 {
309     int         ret = NoError;
310     char        *lp, *valp, *vp, *p;
311     int         select_mode = 0;
312     char        *imsname, *hostname;
313     int         line_num, i;
314
315     imsname = hostname = NULL;
316
317     while ((line_num = read_tag_line(fp, &lp, &valp)) > 0) {
318         if (!valp) {
319             continue;
320         }
321         if (lp[0] != STR_PREFIX_CHAR) {
322             continue;
323         }
324         if (strncmp(lp + 1, STR_SELECTMODE, 3) == 0) {
325             if (str_to_int(valp, &i) && i >= 0)
326                 select_mode = i;
327         } else if (strncmp(lp + 1, STR_IMSNAME, 4) == 0) {
328             vp = valp; cut_field(valp);
329             if (*vp) {
330                 XtFree(imsname);
331                 imsname = XtNewString(vp);
332             }
333         } else if (strncmp(lp + 1, STR_HOSTNAME, 4) == 0) {
334             vp = valp; cut_field(valp);
335             if (*vp) {
336                 XtFree(hostname);
337                 if (strcmp(vp, NAME_LOCAL))
338                     hostname = XtNewString(vp);
339             }
340         }
341     }
342
343     fsel->im_name = imsname;
344     fsel->hostname = hostname;
345     fsel->start_mode = select_mode;
346
347     return ret;
348
349 }
350
351 int 
352 _DtI18nGetImList(
353     I18nEnv *env,
354     char    *hostname )
355 {
356     int         ret = NoError;
357     int         host_type = HOST_LOCAL;
358
359     if (hostname)
360         host_type = CheckHostname(env, hostname);
361
362     switch (host_type) {
363         case HOST_UNKNOWN:
364             ret = ErrUnknownHost;
365             break;
366
367         case HOST_REMOTE:
368             /* Put the host name in the ImsSel structure */
369             env->ims_sel->host_name = hostname;
370             ret = GetImsList(env, hostname);
371             break;
372
373         case HOST_LOCAL:
374             if (hostname && strcasecmp(hostname, "local") != 0)
375                 env->ims_sel->host_name = hostname;             
376             ret = GetImsList(env, env->user_env->localhostname);
377             break;
378     }
379
380     return ret;
381 }
382
383
384 static int 
385 GetImsList(
386     I18nEnv     *env,
387     char        *hostname )
388 {
389     int         ret = NoError;
390     char        pipe_command[255];
391     FILE        *fp;
392     struct stat  buf;
393     unsigned long timeout;
394
395     /* First check if dtimsstart is installed correctly */
396
397     if ((ret = stat ("/usr/dt/bin/dtimsstart", &buf)) != NoError) {
398         return ErrNoDtimsstart;
399     }
400     
401     sprintf(pipe_command, "/usr/dt/bin/dtimsstart -listname -hostname %s", 
402             hostname);
403
404     if (fp = popen(pipe_command, "r")) {
405
406         /* Set the sensitivity of the InputMethod Title Box to False until we
407          * are done reading the new information. */
408         _DtI18nSetSensitiveImTB(env, False);
409
410         /* Initialize the pipe record. */
411         if (!(env->pipe_info))
412             env->pipe_info = (PipeRec *) XtMalloc(sizeof(PipeRec));
413         
414         env->pipe_info->pipe = fp;  /* to close it */
415         env->pipe_info->input_id =  /* to remove it */
416             XtAppAddInput (XtWidgetToApplicationContext(env->shell), 
417                            fileno(fp), (XtPointer) XtInputReadMask, 
418                            ReadPipe, (XtPointer) env); 
419
420         /* Also add a timeout in case the pipe ain't talk */
421         timeout = (unsigned long) (style.xrdb.pipeTimeOut) * 1000;
422         env->pipe_info->timer_id = 
423             XtAppAddTimeOut(XtWidgetToApplicationContext(env->shell), 
424                             timeout, TimeOutProc, (XtPointer)env);
425     } else {
426         ret = ErrNoPopen;
427     }
428
429     return ret;
430 }
431     
432 static void TimeOutProc (XtPointer client_data, 
433                          XtIntervalId* timer)
434 {
435     I18nEnv *env = (I18nEnv *) client_data;
436
437     /* the command is not fast enough,  but calling pclose blocks
438        and then print "Broken Pipe". I need to kill the child somehow 
439     pclose(env->pipe_info->pipe);*/
440
441     XtRemoveInput(env->pipe_info->input_id);
442
443     /* Set the sensitivity of the InputMethod Title Box back to True. */
444     _DtI18nSetSensitiveImTB(env, True);
445
446     _DtI18nErrorDialog(ErrTimeOut) ;
447 }
448
449 static void
450 ReadPipe (
451     XtPointer    client_data,
452     int          *fd,
453     XtInputId    *id )
454
455 {
456     char      buf[512];
457     int       i, nbytes;
458     int       status = NoError;
459     static    char * savebuf = NULL; 
460     static    int savebuf_bytes = 0 ;
461     I18nEnv   *env = (I18nEnv *) client_data;
462         
463     nbytes = read (*fd, buf, 512);
464
465     if (nbytes) {
466         savebuf = XtRealloc(savebuf, savebuf_bytes + nbytes);
467         memcpy(savebuf+savebuf_bytes, buf, nbytes);
468         savebuf_bytes += nbytes ;
469     } else {
470         if (savebuf)
471             *(savebuf + savebuf_bytes)  = '\0';
472         status = ProcessBuf(savebuf, client_data);
473         savebuf_bytes = 0;
474         if (savebuf) {
475             XtFree(savebuf);
476             savebuf = NULL;
477         }
478
479         pclose(env->pipe_info->pipe);
480         XtRemoveInput(*id);
481
482         /* Set the sensitivity of the InputMethod Title Box back to True. */
483         _DtI18nSetSensitiveImTB(env, True);
484
485         /* Remove timer too */
486         XtRemoveTimeOut(env->pipe_info->timer_id);
487
488         if (status != NoError)
489             _DtI18nErrorDialog(status) ;
490     }
491
492 }
493
494 static int
495 ProcessBuf(
496      char    *savebuf, 
497      I18nEnv *env )
498 {
499     int    i, n = 0;
500     int    ret = NoError;
501     ImsEnt *ims_ent;
502     char * filename, * label ;
503
504     /* The dtimsstart execution gave back an empty buffer */
505     if (!savebuf)
506         return(ErrRemoteFailed);
507         
508     /* parse savebuf: The lines have the following syntax: 
509      * [#](im_filename) im_label  or
510      * [#]im_filename im_label
511      * where "#" if present means the default IM,
512      *       im_filename is the file name where all the IM info is stored,
513      *       im_label is the label to present to the user.
514      */
515     while (*savebuf) {
516         /* We're at the beginning of a new line */
517
518         /* grow the array */
519         env->ims_sel->ims_list = 
520             (ImsEnt *) XtRealloc((char *) env->ims_sel->ims_list,
521                                  sizeof(ImsEnt)*(n+1));
522
523         ims_ent = &(env->ims_sel->ims_list[n]);
524
525         /* Look to see if this is the default one. */
526         if (*savebuf == '#') {
527             ims_ent->im_default = 1;
528             savebuf++;
529         } else
530             ims_ent->im_default = 0;
531
532         /* Look to see if it is not reachable. */
533         if (*savebuf == '(') {
534             ims_ent->inactive = 1;
535             savebuf++ ;
536         } else 
537              ims_ent->inactive = 0 ;
538
539         /* Mark begin of im filename */
540         filename = savebuf ;
541         /* Go to the end of it */
542         while (!isspace(*savebuf++)) ;
543         /* We're on the first space, mark the end of the im filename,
544          * don't forget to count the ')' if inactive. */
545         *(savebuf - ims_ent->inactive - 1) = '\0';
546
547         /* Mark the beginning of the im name */
548         label = savebuf ;
549         while (*savebuf != '\n') savebuf++ ;
550         /* mark the end */
551         *savebuf = '\0';
552         savebuf++;
553
554         /* copy the string data here. it's gonna be freed after that */
555         ims_ent->im_name = XtNewString(filename) ;
556         ims_ent->im_label = XtNewString(label) ;
557         n++;
558     }      
559
560     env->ims_sel->ims_list_size = n;
561
562     return(ret);
563 }
564
565
566 static int      
567 CheckHostname(
568     I18nEnv     *env,
569     char        *hostname
570 )
571 {
572     int         host_type = HOST_UNKNOWN;
573     char        *local = env->user_env->localhostname;
574     struct hostent      *hp;
575     unsigned long       addr = 0L;
576     static unsigned long  local_addr = 0L;
577
578     if (!hostname || !*hostname || strcasecmp(hostname, "local") == 0
579                         || strcasecmp(hostname, local) == 0) {
580         host_type =  HOST_LOCAL;
581     } else {            /* compare inet address */
582         if (!local_addr) {
583             if ((hp = gethostbyname(local)) && hp->h_addrtype == AF_INET) {
584                 local_addr = *((unsigned long *) hp->h_addr_list[0]);
585             } else {
586                 host_type = HOST_REMOTE;
587             }
588         }
589         if (host_type == HOST_UNKNOWN) {
590             if ((hp = gethostbyname(hostname)) && hp->h_addrtype == AF_INET) {
591                 addr = *((unsigned long *) hp->h_addr_list[0]);
592                 if (addr == local_addr)
593                     host_type = HOST_LOCAL;
594                 else
595                     host_type = HOST_REMOTE;
596             } else {
597                 host_type = HOST_UNKNOWN;
598             }
599         }
600     }
601
602     return host_type;
603 }
604
605
606 /*+++++++++++++++++++++++++++++++++++++++*/
607 /* _DtI18nWriteImSelectionFile - writes  */
608 /* the values saved in the FileSel       */
609 /* structure to the IMS Selection File.  */
610 /*+++++++++++++++++++++++++++++++++++++++*/
611 int
612 _DtI18nWriteImSelectionFile(
613      I18nEnv *env
614 )
615 {
616     FILE        *fp;
617
618     
619     /* Look if this file is writable */
620     if ((fp = fopen(env->file_sel->fname, "w")) == NULL)
621         return ErrFileCreate;
622
623     /* Write the select mode */
624     PutSelectMode(fp, env->file_sel->start_mode);
625
626     /* Write the IM selected if not null */
627     PutImsName(fp, env->file_sel->im_name);
628
629     /* Write the hostname if not null */
630     PutHostname(fp, env->file_sel->hostname);
631     
632     /* Close the file */
633     fclose(fp);
634 }
635
636 static void
637 PutSelectMode(
638      FILE *fp,
639      int  start_mode
640 )
641 {
642     char val[20];
643     
644     sprintf(val, "%ld", (long)start_mode);
645     PutSelectionEntry(fp, STR_SELECTMODE, val);
646 }
647
648 static void
649 PutImsName(
650      FILE *fp,
651      char *im_name
652 )
653 {
654     char *valp;
655
656     if ((valp = im_name) && *valp)
657         PutSelectionEntry(fp, STR_IMSNAME, valp);
658 }
659
660 static void
661 PutHostname(
662      FILE *fp,
663      char *hostname
664 )
665 {
666     char *valp;
667
668     if ((valp = hostname) && *valp)
669         PutSelectionEntry(fp, STR_HOSTNAME, valp);
670 }
671
672
673 static void 
674 PutSelectionEntry(
675      FILE *fp,
676      char *tag,
677      char *value
678 )
679 {
680     fprintf(fp, "%c%s%c\t%s\n", STR_PREFIX_CHAR, tag, TAG_END_CHAR, value);
681 }