libDtHelp: Fix another regression caused by Coverity fix, clicking 'Help Manager...
[oweals/cde.git] / cde / programs / dtfile / fsDialog.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 /* $XConsortium: fsDialog.c /main/6 1996/10/08 11:58:08 mustafa $ */
24 /************************************<+>*************************************
25  ****************************************************************************
26  *
27  *   FILE:           fsDialog.c
28  *
29  *   COMPONENT_NAME: Desktop File Manager (dtfile)
30  *
31  *   Description:    Containes routines to handle contents of dtfile.config
32  *
33  *   FUNCTIONS: configFileName
34  *              configFileOK
35  *              fsDialogAvailable
36  *              getFSType
37  *              readConfigFile
38  *              readDialogData
39  *              readFSID
40  *              readLine
41  *              stricmp
42  *              strip
43  *
44  *   (c) Copyright 1993, 1994, 1995 Hewlett-Packard Company
45  *   (c) Copyright 1993, 1994, 1995 International Business Machines Corp.
46  *   (c) Copyright 1993, 1994, 1995 Sun Microsystems, Inc.
47  *   (c) Copyright 1993, 1994, 1995 Novell, Inc.
48  *
49  ****************************************************************************
50  ************************************<+>*************************************/
51
52 /*---------------------------------------------------------------------------------
53  *
54  *   fsDialogAvailable (String path, dtFSData *fsDialogData)
55  *
56  *   For the file specified by path, determine if there is a properties
57  *      dialog specific to the file system that the file is on.
58  *      For example, if the file is in AFS there might be a dialog to edit
59  *      the Access Control List. fsDialogData is filled if there is a dialog.
60  *   path must not include host:
61  *
62  *   If any errors are encountered, FALSE is returned and no other action is taken
63  *      or message displayed.
64  *
65  *--------------------------------------------------------------------------------*/
66
67 #include <string.h>
68 #include <stdlib.h>
69 #include <stdio.h>
70 #include <ctype.h>
71 #include <errno.h>
72 #include <sys/stat.h>
73
74 #include <Dt/UserMsg.h>   /* for DtSimpleError */
75
76 #include <Xm/Xm.h>
77 #include "Encaps.h"
78 #include "ModAttr.h"
79 #include "FileMgr.h"
80 #include "Desktop.h"
81 #include "Main.h"
82
83 /*-----------------------------------------------------
84  * Configuration file search order is:
85  *
86  *       $DTFSCONFIG   (meant to be used for debugging)
87  *       $HOME/CONFIGFILENAME
88  *       SYSCONFIGFILEDIR/CONFIGFILENAME
89  *       DEFCONFIGFILEDIR/$LANG/CONFIGFILENAME
90  *       DEFCONFIGFILEDIR/C/CONFIGFILENAME
91  *
92  * The function configFileName() determines the correct
93  *    name. It will apply $LANG where appropriate.
94  *
95  *-----------------------------------------------------*/
96
97 #define SYSCONFIGFILEDIR  CDE_CONFIGURATION_TOP "/config"
98 #define DEFCONFIGFILEDIR  CDE_INSTALLATION_TOP  "/config"
99 #define CONFIGFILENAME "dtfile.config"
100 #define DEFCONFIGFILENAME  CDE_INSTALLATION_TOP  "/config/C/dtfile.config"
101
102 /*--------------------------------------------------------
103      Platform-specific includes required for getFSType()
104  *--------------------------------------------------------*/
105
106 #ifdef __aix
107 #endif
108
109 #if defined(sun)
110 #include <sys/types.h>
111 #include <sys/statvfs.h>
112 #endif
113
114 #ifdef __hpux
115 #include <sys/vfs.h>
116 #endif
117
118 /*----------------------
119  *   global variables
120  *----------------------*/
121
122 #define EMSGMAX 1024
123 char g_errorMessage[EMSGMAX];
124
125
126 /*--------------------
127      Local functions
128  *--------------------*/
129
130 static void    getFSType      (const String      path,
131                                      String      fsType,
132                                      String      platform);
133 static void    readConfigFile (const String      fsType,
134                                const String      platform,
135                                      dtFSData  * fsDialogData,
136                                      Boolean   * dialogAvailable);
137 static String  configFileName (void);
138 static void    readFSID       (FILE          * fptr,
139                                const String    fsType,
140                                const String    platform,
141                                String          fsID);
142 static void    readDialogData (FILE          * fptr,
143                                const String    fsID,
144                                Boolean       * dialogAvailable,
145                                dtFSData      * fsDialogData);
146 static int     readLine        (FILE  * fptr, String line);
147 static int     stricmp         (const String  s1,  const String  s2);
148 static String  strip           (String  s);
149 static Boolean configFileOK(void);
150
151
152 /*----------------------
153  *   fsDialogAvailable
154  *----------------------*/
155
156 Boolean
157 fsDialogAvailable (const String path, dtFSData *fsDialogData)
158
159 {
160    char     fsType[MAXLINELENGTH], platform[MAXLINELENGTH];
161    Boolean  dialogAvailable;
162
163    DPRINTF(("fsDialogAvailble: checking path \"%s\"\n",path));
164    if ( ! configFileOK())
165       return FALSE;
166
167    getFSType(path, fsType, platform);
168    DPRINTF(("   fsType=\"%s\"\n   platform=\"%s\"\n",fsType,platform));
169
170    if (strlen(fsType) == 0)
171       return FALSE;
172    else
173    {
174       readConfigFile(fsType, platform, fsDialogData, &dialogAvailable);
175       if (dialogAvailable)
176       {
177          strcpy (fsDialogData->path,path);
178       }
179       return dialogAvailable;
180    }
181
182 }
183
184
185 /*--------------------------------------------------------------------------------
186  *
187  *   configFileOK
188  *
189  *   is the configuration file accessible?
190  *
191  *------------------------------------------------------------------------------*/
192
193 static Boolean
194 configFileOK(void)
195
196 {
197    struct stat  buf;
198    int          err;
199    String       fname;
200    String       msg1, msg2;
201
202    fname = configFileName();
203    err   = stat(fname, &buf);
204    DPRINTF(("   config file \"%s\"  stat ret=%i\n",fname,err));
205
206    if (err == 0)
207       return TRUE;
208    else
209    {
210       msg1 = GETMESSAGE(21, 22, "Cannot open file manager configuration file: ");
211       msg2 = strerror(errno);
212       sprintf(g_errorMessage,"%s%s\n   %s\n",msg1,fname,msg2);
213       _DtSimpleError (application_name, DtError, NULL, g_errorMessage, NULL);
214       return FALSE;
215    }
216
217 }  /* end configFileOK */
218
219
220 /*--------------------------------------------------------------------------------
221  *
222  *   getFSType
223  *
224  *   Do the platform-specific work required to determine what type of file system
225  *   the file defined by path is on. Return strings defining the file system type
226  *   and the platform. Any errors result in a fsType set to the null string.
227  *
228  *   Both fsType and platform are allocated by the caller
229  *
230  *   The strings used in platform and fsType must match those used in
231  *      the configuration file.
232  *
233  *--------------------------------------------------------------------------------*/
234
235 static void getFSType(const String  path,
236                             String  fsType,
237                             String  platform)
238
239 {
240 #ifdef __aix
241 #define GETFSTYPE
242    struct stat  buf;
243
244    strncpy(platform,"aix",MAXLINELENGTH);
245    if (lstat(path, &buf) == 0)
246       sprintf(fsType,"%i",buf.st_vfstype);
247    else
248       strncpy(fsType,"",MAXLINELENGTH);
249 #endif /* __aix */
250
251 #ifdef sun
252 #define GETFSTYPE
253    struct statvfs buf;
254
255    strncpy(platform,"sunos",MAXLINELENGTH);
256    if (statvfs(path, &buf) == 0)
257       strncpy(fsType,buf.f_basetype,MAXLINELENGTH);
258    else
259       strncpy(fsType,"",MAXLINELENGTH);
260 #endif /* sun */
261
262 #ifdef __hpux
263 #define GETFSTYPE
264    struct statfs buf;
265
266    strncpy(platform,"hpux",MAXLINELENGTH);
267    if (statfs(path, &buf) == 0)
268       sprintf(fsType,"%li",buf.f_fsid[1]);
269    else
270       strncpy(fsType,"",MAXLINELENGTH);
271 #endif /* __hpux */
272
273 #ifndef GETFSTYPE
274    strncpy(platform,"unknown",MAXLINELENGTH);
275    strncpy(fsType,  "",MAXLINELENGTH);
276 #endif  /* unknown platform */
277
278    return;
279
280 }    /* end getFSType */
281
282
283 /*--------------------------------------------------------------------------------
284  *
285  *   readConfigFile
286  *
287  *   Given the platform and type of file system, read the configuration file to
288  *   determine if there is a file-system specific dialog. If there is, fill
289  *   fsDialogData. If there is no file or if the file has no entry for
290  *   platform:fsType, dialogAvailable is set to FALSE.
291  *
292  *   The configuration file consists of two parts. The first is a list of entries
293  *   (ending with "end") of the form
294  *
295  *   platform:file-system-type=file-system-id
296  *
297  *   where platform is one of the strings from getFSType(), file-system-type is
298  *   a (platform-dependent) string identifying the type of file system, and
299  *   file-system-id is a string used in the configuration file to identify the
300  *   file system. The second part of the file consists of lists of attributes
301  *   (in the form name = value, one per line) for each of the file system id's.
302  *   Blank lines and lines beginning with * are ignored as is case. A example
303  *   is given below.
304  *
305  *   -------------------------------------------------
306  *   * sample dtfs configuration file
307  *
308  *   aix:4=afs
309  *   hpux:8=afs
310  *   sunos:nfs=nfs
311  *   end
312  *
313  *   afs:     buttonlabel = Change AFS ACL ...
314  *            fsDialog    = modAttrAFS
315  *            warning     = File system access may be further restriced by AFS ACLs
316  *
317  *   nfs:     buttonLabel = Change NFS attributes ...
318  *            fsDialog    = <configuration-location>/bin/modNFSAttr
319  *   -------------------------------------------------
320  *
321  *--------------------------------------------------------------------------------*/
322
323 static void
324 readConfigFile(const String      fsType,
325                const String      platform,
326                      dtFSData  * fsDialogData,
327                      Boolean   * dialogAvailable)
328 {
329    char      fsID[MAXLINELENGTH];
330    FILE    * fptr;
331    String    fname, msg1, msg2;
332
333
334    *dialogAvailable = FALSE;
335
336    fname = configFileName();
337    fptr = fopen(fname,"r");
338    if (fptr == NULL)
339    {
340       msg1 = GETMESSAGE(21, 22, "Cannot open file manager configuration file: ");
341       msg2 = strerror(errno);
342       sprintf(g_errorMessage,"%s%s\n   %s\n",msg1,fname,msg2);
343       _DtSimpleError (application_name, DtError, NULL, g_errorMessage, NULL);
344       return;
345    }
346
347    readFSID(fptr, fsType, platform, fsID);
348    DPRINTF(("   fsID=\"%s\"\n",fsID));
349
350    readDialogData(fptr, fsID, dialogAvailable, fsDialogData);
351
352    /* make sure that a dialog program has been specified or execl will do unfortunate things */
353    if (strlen(fsDialogData->fsDialogProgram) == 0)
354    {
355       *dialogAvailable = FALSE;
356       if (strlen(fsDialogData->buttonLabel) != 0)
357       {
358          msg1 = XtNewString(GETMESSAGE(21, 29, "No value was provided for the fsDialog field in dtfile's configuration file"));
359          _DtSimpleError (application_name, DtError, NULL, msg1, NULL);
360          XtFree(msg1);
361       }
362    }
363
364    fclose(fptr);
365    return;
366
367 }  /* end readConfigFile */
368
369
370 /*--------------------------------------------------------------------------------
371  *
372  *   configFileName
373  *
374  *   return the name of the configuration file which defines file-system
375  *   specific dialogs ... a hierarchy of names is searched until a file
376  *   is found ... no checking is done to see if it can be read
377  *
378  *------------------------------------------------------------------------------*/
379
380 static String
381 configFileName(void)
382
383 {
384           int          i, err;
385           struct stat  buf;
386           String       s;
387    static char         fn[MAX_PATH]="";   /* remember name once its been determined */
388
389
390    if (strlen(fn) != 0) return fn;   /* has filename been determined already? */
391
392    for (i=0; i < 4; i++)
393    {
394       switch (i)
395       {
396          case 0: /* file name $DTFSCONFIG */
397             if ( (s=getenv("DTFSCONFIG")) != NULL)        /* a convenience for debugging */
398                strncpy(fn,s,MAX_PATH);
399             else  /* DTFSCONFIG not defined */
400                continue;
401             break;
402
403          case 1: /* $HOME/dtfile.config */
404             if ( (s=getenv("HOME")) != NULL)
405             {
406                strncpy(fn,s,MAX_PATH);
407                strcat (fn,"/");
408                strncat(fn,CONFIGFILENAME,MAX_PATH-strlen(fn)-1);
409             }
410             else  /* $HOME not defined */
411                continue;
412             break;
413
414          case 2: /* SYSCONFIGFILEDIR, e.g. /etc/dt/config/dtfile.config */
415             strncpy(fn,SYSCONFIGFILEDIR,MAX_PATH);
416             strcat (fn,"/");
417             strncat(fn,CONFIGFILENAME,MAX_PATH-strlen(fn)-1);
418             break;
419
420          case 3: /* DEFCONFIGFILEDIR, e.g. /usr/dt/config/C/dtfile.config */
421             strncpy(fn,DEFCONFIGFILEDIR,MAX_PATH);
422             strcat (fn,"/");
423             if ( (s=getenv("LANG")) != NULL)
424             {
425                strncat(fn,s,MAX_PATH-strlen(fn)-1);
426                strcat (fn,"/");
427             }
428             else
429             {
430                strncat(fn,"C",MAX_PATH-strlen(fn)-1);
431                strcat (fn,"/");
432             }
433             strncat(fn,CONFIGFILENAME,MAX_PATH-strlen(fn)-1);
434             break;
435       }  /* end switch */
436
437       err   = stat(fn, &buf);
438       DPRINTF(("   config file \"%s\"  stat ret=%i\n",fn,err));
439
440       if (err == 0)  /* file is found */
441          return fn;
442
443    }  /* end loop over possible config files */
444
445    /* didn't find any of the files (this should never, ever happen) */
446    /* return the name of the factory default (case 3 above) which should always be there */
447
448    strncpy(fn,DEFCONFIGFILENAME,MAX_PATH);
449    return fn;
450
451
452 }  /* end configFileName */
453
454
455 /*--------------------------------------------------------------------------------
456  *
457  *   readFSID
458  *
459  *   get the file-system identifier from the configuration file
460  *
461  *   fptr refers to an open configuration file
462  *   fsType and platform identify the platfrom (aix, hpux, sunos, etc)
463  *      and file-system type (afs, nfs, dfs, etc.)
464  *   fsID is filled by matching platform:fsType with with information in the
465  *      file and reading the id; the string comparisons are case insensitive
466  *      if there is more than one match, the last fsID found will be returned;
467  *      if no match is found, fsID is returned as ""
468  *   upon conclusion, the file is positioned past the "end" record
469  *
470  *------------------------------------------------------------------------------*/
471
472 static void
473 readFSID (      FILE    * fptr,
474           const String    fsType,
475           const String    platform,
476                 String    fsID)
477
478 {
479    char    line[MAXLINELENGTH];
480    String  pform, type, id;
481    int     lineLength;
482    String  lineIn, lineOut;
483
484
485    fsID[0] = '\0';
486
487    while ( (lineLength=readLine(fptr,line)) != EOF &&
488             stricmp(line,"end")             != 0 )
489    {
490       /* readLine has changed all white space to blanks ... now remove
491          the blanks as they are not significant here */
492       for (lineIn = lineOut = line; *lineIn != '\0'; lineIn++)
493          if (*lineIn != ' ')
494          {
495             *lineOut = *lineIn;
496             lineOut++;
497          }
498       *lineOut = '\0';
499
500       pform = strtok(line,":");
501       type  = strtok(NULL,"=");
502       id    = strtok(NULL," ");
503       if (stricmp(pform,platform)==0 && stricmp(type,fsType)==0)
504          strncpy(fsID,id,MAXLINELENGTH);
505    }
506
507    return;
508
509 }  /* end readFSID */
510
511
512 /*--------------------------------------------------------------------------------
513  *
514  *   readDialogData
515  *
516  *   fill dtFSData with information from the configuration file; set
517  *      dialogAvailable
518  *
519  *   fsID is a file-system identifier defined, in the configuration file,
520  *      by a platform:file-system-type pair. The configuration file is
521  *      searched from its current position until a definition section
522  *      for fsID is found. The string comparison is case insensitive; if more
523  *      than one match is found, the last in the file is used. The fields in
524  *      dtFSData are initialized (e.g. string fields are set to "") if no
525  *      matches are found or if no match is found for a particular field.
526  *   Upon conclusion, the file is positioned at EOF.
527  *
528  *------------------------------------------------------------------------------*/
529
530 static void
531 readDialogData(      FILE      * fptr,
532                const String      fsID,
533                      Boolean   * dialogAvailable,
534                      dtFSData  * fsDialogData)
535
536
537 {
538    int     lineLength;
539    String  s, id, token1, token2;
540    String  msg1;
541    char    line[MAXLINELENGTH];
542
543
544    *dialogAvailable = FALSE;
545
546    /* initialize fields in fsDialogData */
547    strcpy(fsDialogData->buttonLabel,"");
548    strcpy(fsDialogData->fsDialogProgram,"");
549    strcpy(fsDialogData->warningMessage,"");
550    fsDialogData->dismissStdPermissionDialog = FALSE;
551
552    if (strlen(fsID) == 0)
553       return;
554
555    /* loop over lines in the file; note that readLine is called
556       from within the loop except for the first pass */
557    for (lineLength=readLine(fptr,line); lineLength != EOF; )
558    {
559       id = strtok(line,":");
560       if (stricmp(id,fsID) == 0)
561       {
562          /* a section matching the input fsID has been found */
563          *dialogAvailable = TRUE;
564          token1 = strtok(NULL," =");
565          s = strchr(token1,'\0') + 1;   /* first character after token1 */
566          token2 = &s[strspn(s," =")];   /* first non-blank, non-= after token1 */
567          /* loop to get data for fields in fsDialogData */
568          while(strchr(token1,':') == NULL  && lineLength != EOF)
569          {
570             if (stricmp(token1,"buttonlabel") == 0)
571                strncpy(fsDialogData->buttonLabel,token2,MAXLINELENGTH);
572             else if (stricmp(token1,"warning") == 0)
573             {
574                strncpy(fsDialogData->warningMessage,token2,MAXLINELENGTH);
575             }
576             else if (stricmp(token1,"fsdialog") == 0)
577                strncpy(fsDialogData->fsDialogProgram,token2,MAXLINELENGTH);
578             else if (stricmp(token1,"dismiss") == 0)
579                fsDialogData->dismissStdPermissionDialog = (stricmp(token2,"yes") == 0);
580             else
581             {
582                msg1 = GETMESSAGE(21, 24, "Unknown field label in file manager configuration file: ");
583                sprintf(g_errorMessage,"%s\"%s\"\n",msg1,token1);
584                _DtSimpleError (application_name, DtWarning, NULL, g_errorMessage, NULL);
585             }
586             lineLength = readLine(fptr,line);
587             if (lineLength != EOF)
588             {
589                token1 = strtok(line," =");
590                s = strchr(token1,'\0') + 1;
591                token2 = &s[strspn(s," =")];
592             }
593          } /* end while */
594       }  /* end if */
595       else
596       {
597          /* the current line does not match fsID ... get next line */
598          lineLength = readLine(fptr,line);
599       }
600    }  /* end for */
601
602    if ( ! *dialogAvailable )
603    {
604       msg1 = GETMESSAGE(21, 25, "No information found in file manager configuration file for file-system identifier");
605       sprintf(g_errorMessage,"%s \"%s\"\n",msg1,fsID);
606       _DtSimpleError (application_name, DtError, NULL, g_errorMessage, NULL);
607    }
608
609 }  /* end readDialogData */
610
611
612 /*--------------------------------------------------------------------------------
613  *
614  *   readLine
615  *
616  *   read a line from the stream pointed to by fptr and return it in line
617  *   line is allocated by the caller
618  *
619  *   blank lines and lines starting with * are skipped
620  *   leading and trailing white space is removed
621  *   imbedded white space characters are changed to blanks
622  *   imbedded "\n" is changed to space, newline
623  *   the length of the line or EOF (on end-of-file or error) is returned
624  *
625  *------------------------------------------------------------------------------*/
626
627 static int
628 readLine(FILE  * fptr, String  line)
629
630 {
631    const char    commentChar = '#';
632          char    myLine[MAXLINELENGTH];
633          String  s, t;
634          int     i, len;
635
636    while (TRUE)
637    {
638       if ( fgets(myLine,MAXLINELENGTH,fptr) == NULL)
639          return EOF;
640       else
641       {
642          s = strip(myLine);  /* remove leading & trailing whitespace */
643          if ((len=strlen(s)) != 0  &&  s[0] != commentChar)
644          {
645             /* change imbedded white space characters to spaces */
646             for (i=0; i<len; i++)
647                if (isspace(s[i]))
648                   s[i] = ' ';
649
650             /* change any imbedded "\n" to space followed by newline */
651             while ( (t=strstr(s,"\\n")) != NULL )
652             {
653                *t     = ' ';      /* space */
654                *(t+1) = '\n';     /* new line */
655             }
656
657             strncpy(line,s,MAXLINELENGTH);
658             return len;
659          }
660       }
661    }
662
663 }  /* end readLine */
664
665
666 /*--------------------------------------------------------------------------------
667  *
668  *  stricmp
669  *
670  *  compare strings ignoring case, return value as in strcmp
671  *
672  *------------------------------------------------------------------------------*/
673
674 static int
675 stricmp(const String  s1, const String  s2)
676
677 {
678    int i;
679
680    for (i=0; tolower(s1[i]) == tolower(s2[i]); i++)
681       if (s1[i] == '\0')
682          return 0;
683
684    return (tolower(s1[i]) > tolower(s2[i])) ? +1 : -1;
685
686 }  /* end stricmp */
687
688
689 /*--------------------------------------------------------------------------------
690  *
691  *   strip
692  *
693  *   remove trailing white space by inserting \0
694  *   return a pointer to the first non-white space character
695  *------------------------------------------------------------------------------*/
696
697 static String
698 strip(String  s)
699
700 {
701    int i;
702
703    for (i=(strlen(s)-1); isspace(s[i]) && i>=0; i--)
704       ;
705    s[i+1] = '\0';
706
707    for (i=0; isspace(s[i]); i++)
708       ;
709
710    return &s[i];
711
712 }  /* end strip */
713
714
715 /*----------------
716  *   for testing
717  *----------------*/
718
719  /*
720 main(int argc, char **argv)
721 {
722     char        pwd[100];
723     int         err = 0;
724     Boolean     test;
725     dtFSData    data;
726
727     if(argc > 1)
728     {
729         strcpy(pwd, argv[1]);
730     }
731     else
732     {
733         getcwd(pwd,sizeof(pwd));
734
735     }
736     printf("testing file \"%s\"\n",pwd);
737     test = fsDialogAvailable(pwd, &data);
738     if (test)
739     {
740         printf("dialog is available\n");
741         printf("   buttonLabel=\"%s\"\n",data.buttonLabel);
742         printf("   fsDialogProgram=\"%s\"\n",data.fsDialogProgram);
743     }
744     else
745     {
746         printf("dialog not available\n");
747     }
748 }
749 */
750