5b522780d96c223d745acf7b946bf993ca12cfc7
[oweals/cde.git] / cde / lib / DtHelp / FileListUtils.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 /* $TOG: FileListUtils.c /main/11 1999/10/14 13:17:49 mgreess $ */
24 /************************************<+>*************************************
25  ****************************************************************************
26  **
27  **   File:     FileListUtils.c
28  **
29  **   Project:    DtHelp library
30  **
31  **   Description: Locates and lists all files (volumes) accessible via the 
32  **             known paths
33  **
34  **  (c) Copyright 1993, 1994 Hewlett-Packard Company
35  **  (c) Copyright 1993, 1994 International Business Machines Corp.
36  **  (c) Copyright 1993, 1994 Sun Microsystems, Inc.
37  **  (c) Copyright 1993, 1994 Novell, Inc.
38  **
39  **
40  ****************************************************************************
41  ************************************<+>*************************************/
42
43 /*
44  * system includes
45  */
46 #include <errno.h>
47 #include <fcntl.h>
48 #include <stdlib.h>
49 #include <stdio.h>
50 #include <string.h>
51 #include <unistd.h>
52 #include <sys/stat.h>
53 #include <sys/param.h> /* MAXPATHLEN */
54 #include <limits.h>
55 #define X_INCLUDE_DIRENT_H
56 #define XOS_USE_XT_LOCKING
57 #include <X11/Xos_r.h>
58 #include <Dt/Help.h>
59
60 /*
61  * private includes
62  */
63 #include "bufioI.h"  /* for AccessI.h */
64 #include "Access.h"
65 #include "AccessI.h"
66 #include "StringFuncsI.h"
67 #include "FileUtilsI.h"
68 #include "FileListUtilsI.h"
69 #include "HelposI.h"
70
71 /******** constants *********/
72 #define LANG_C_STR     "C"
73 #define EOS            '\0'
74
75 /******** types *********/
76
77 /******** public global variables *********/
78
79 /******** variables *********/
80 static const char * PeriodStr = ".";
81 static const char * DirSlashStr = "/";
82 static const char * PathSeparator = ":";
83
84 /******** functions *********/
85
86 \f
87 /*****************************************************************************
88  * Function:   GetExtension()
89  *
90  *  locate the '.' of the filename extension, if present
91  *
92  *****************************************************************************/
93 static char * GetExtension(
94             char *         filename)
95 {
96     char * ext;
97     if (_DtHelpCeStrrchr(filename,PeriodStr,MB_CUR_MAX,&ext) == 0 ) return ext;
98     else return "";          /* do NOT return a NULL*/
99 }
100
101
102
103 /*****************************************************************************
104  * Function:   SpecialStrcmp()
105  *
106  * Tests the args for NULL pointers. If both are NULL or if
107  * both aren't NULL and are the same string, then returns 0.
108  * If one arg is NULL and other isn't, or if strings are
109  * different, returns -1 or +1.
110  *
111  *****************************************************************************/
112 static int SpecialStrcmp(
113     const char *    str1,
114     const char *    str2)
115 {
116            if(NULL == str1)
117            {
118               if(NULL == str2) return 0;   /* str1 == str2 */
119               return -1;                   /* str1 < str2 */
120            }
121            if(NULL == str2) return 1;      /* str1 > str2 */
122            return(strcmp(str1,str2));      /* str1 ? str2 */
123 }
124
125
126
127 \f
128 /*****************************************************************************
129  * Function:   FileInfoMatchesP()
130  *
131  *  Compares the info of an existing file entry with test file info
132  *  and determines whether they match.
133  *  Returns: True or False
134  *
135  *****************************************************************************/
136 static Boolean FileInfoMatchesP(
137    _DtHelpFileEntry file1,
138    _DtHelpFileEntry file2,
139    int              compareFlags)
140 {
141       /* no duplicate files allowed: compare doc stamps */
142       /* note: currently am not comparing nameKey nor returning
143           a placement (-1,+1) val from the compare. */
144       if (    (   (compareFlags & _DtHELP_FILE_NAME) == 0
145                || SpecialStrcmp(file1->fileName,file2->fileName) == 0)
146            && (   (compareFlags & _DtHELP_FILE_TITLE) == 0
147                || SpecialStrcmp(file1->fileTitle,file2->fileTitle) == 0)
148            && (   (compareFlags & _DtHELP_FILE_IDSTR) == 0
149                || SpecialStrcmp(file1->docId,file2->docId) == 0)
150            && (   (compareFlags & _DtHELP_FILE_TIME) == 0
151                || SpecialStrcmp(file1->timeStamp,file2->timeStamp) == 0) )
152           return True;
153        return False;
154 }
155
156
157
158 /*****************************************************************************
159  * Function:   _DtHelpFileIsSameP()
160  *
161  *  Compares the info of two files and determines whether they match.
162  *  Returns: True or False
163  *
164  *****************************************************************************/
165 Boolean _DtHelpFileIsSameP(
166     char *              fileName1,
167     char *              fileName2,
168     _DtHelpFileInfoProc infoProc,
169     int                 compareFlags,
170     XtPointer           pDisplayArea)
171 {
172     _DtHelpFileRec file1, file2;
173     char *    fileName;
174     Boolean   ret;
175
176    /* get filenames without path */
177    if (_DtHelpCeStrrchr(fileName1, DirSlashStr, MB_CUR_MAX, &fileName) == 0) 
178        fileName1 = fileName + 1;
179    if (_DtHelpCeStrrchr(fileName2, DirSlashStr, MB_CUR_MAX, &fileName) == 0) 
180        fileName2 = fileName + 1;
181
182     /* init the structs */
183     memset(&file1,0,sizeof(file1));
184     memset(&file2,0,sizeof(file2));
185     file1.fileName = fileName1;
186     file2.fileName = fileName2;
187
188     /* get info on the files */
189     (*infoProc)(pDisplayArea,fileName1,NULL,NULL,
190                           &file1.docId,&file1.timeStamp,&file1.nameKey,
191                           NULL,NULL);
192     (*infoProc)(pDisplayArea,fileName2,NULL,NULL,
193                           &file2.docId,&file2.timeStamp,&file2.nameKey,
194                           NULL,NULL);
195
196     ret = FileInfoMatchesP(&file1,&file2,compareFlags);
197
198     XtFree(file1.docId);
199     XtFree(file1.timeStamp);
200     XtFree(file2.docId);
201     XtFree(file2.timeStamp);
202     return ret;
203 }
204
205
206 \f
207 /*****************************************************************************
208  * Function:   ScanDirForFiles
209  *
210  *  scan a directory looking for files with a matching suffix
211  *  returns number of files found in this directory
212  *
213  *****************************************************************************/
214 static int ScanDirForFiles(
215             char *         dirpath,
216             const char *   suffixList[],
217             _DtHelpFileList * in_out_list,
218             XmFontList *   io_fontList,
219             Boolean *      ret_mod,
220             int            foundFilesCnt,
221             _DtHelpFileInfoProc infoProc,
222             XtPointer      pDisplayArea,
223             int            compareFlags,
224             int            sortFlags,
225             _DtHelpFileScanProcCB scanProc,
226             XtPointer      clientData)
227 {
228     int     count = 0;
229     char    fullName [MAXPATHLEN + 2];
230     char   *ptr;
231     DIR    *pDir;
232     struct dirent *result;
233     _Xreaddirparams dirEntryBuf;
234
235     /* open the directory */
236     pDir = opendir (dirpath);
237     if (pDir == NULL) return 0;                        /* RETURN */
238
239     /* build the pathname */
240     strcpy (fullName, dirpath);
241     strcat (fullName, DirSlashStr);
242     ptr = fullName + strlen (fullName);
243
244     /*
245      * Scan through the files looking for matching suffixes
246      */
247     while ((result = _XReaddir(pDir, dirEntryBuf)) != NULL) {
248         const char * matchedSuffix;
249         char * ext;
250
251         /* Skip over any "." and ".." entries. */
252         if ((strcmp(result->d_name, ".") == 0) ||
253             (strcmp(result->d_name, "..") == 0))
254           continue;
255
256         /* get working values */
257         ext = GetExtension (result->d_name);
258         matchedSuffix = "";      /* default (==>no suffix to match) */
259
260         /* try to match with a suffix, if specified */
261         if (NULL != suffixList)
262         {
263           const char * * pSuffix;
264           matchedSuffix = NULL;
265           for (pSuffix = suffixList; 
266                NULL != *pSuffix && NULL == matchedSuffix; 
267                pSuffix++ )
268           {
269             if (strcmp(ext,*pSuffix) == 0)
270               matchedSuffix = *pSuffix;
271           }
272         }
273
274         /* and a match is found */
275         if ( NULL != matchedSuffix ) 
276         {
277            Boolean mod;
278
279            /* then generate the full path and add to the list */
280            strcpy (ptr, result->d_name);
281            if (_DtHelpFileListAddFile(in_out_list,io_fontList,&mod,
282                   fullName,ptr,infoProc,compareFlags,sortFlags,pDisplayArea))
283            {
284               count++;
285               if (scanProc) (*scanProc)(count + foundFilesCnt,clientData);
286            }
287            *ret_mod |= mod;        /* accumulate mods */
288         }
289     } /* while more entries */
290
291     /* close the directory stream */
292     closedir(pDir);
293
294     return count;
295 }
296
297
298 \f
299 /******************************************************************************
300  * Function:  _DtHelpFileListGetMatch ()
301  *
302  * Parameters:
303  *              fileListHead     head of the list
304  *              fullFilePath     fullFilePath to file to find
305  *              infoProc         will deliver info on the file
306  *              pDisplayArea     used to interpret vol info
307  *
308  * Returns:     pointer to matching _DtHelpFileList entry
309  *              or NULL if none is found
310  *
311  * Purpose:     Looks for a file list entry for the file
312  *              identified by fullFilePath.
313  *              The infoProc routine gets info about the file
314  *              to be matched, and uses that when comparing
315  *              with the file list.  This allows more
316  *              than one file with the same name to be in
317  *              the list and be discriminated on the basis
318  *              of the info.
319  * 
320  *****************************************************************************/
321 _DtHelpFileList _DtHelpFileListGetMatch ( 
322         _DtHelpFileList     fileListHead,
323         char *              fullFilePath,
324         _DtHelpFileInfoProc infoProc,
325         int                 compareFlags,
326         XtPointer           pDisplayArea)
327 {
328    _DtHelpFileList next;
329    _DtHelpFileRec  file;
330
331    /* zero the record */
332    memset (&file,0,sizeof(file));
333
334    if (NULL == fileListHead || NULL == fullFilePath) return NULL; /* RETURN */
335
336    /* get filename without path */
337    file.fileName = fullFilePath;
338    if (_DtHelpCeStrrchr(fullFilePath,DirSlashStr,MB_CUR_MAX,&file.fileName) == 0) 
339        file.fileName++;
340
341    /* if an info proc, use it, otherwise make due */
342    if (infoProc) 
343       (*infoProc)(pDisplayArea,fullFilePath,
344                      NULL,NULL,&file.docId,&file.timeStamp,&file.nameKey,
345                      NULL,NULL);
346    else
347       file.nameKey = _DtHelpCeStrHashToKey(file.fileName);
348
349    next =  NULL;
350    while ( (next = _DtHelpFileListGetNext(fileListHead, next)) != NULL )
351    {
352       if ( FileInfoMatchesP(next,&file,compareFlags) )
353          break;                   /* next is the matching entry */
354    }
355
356    /* fileName is part of fullFilePath */
357    XtFree(file.docId);
358    XtFree(file.timeStamp);
359    return next;
360 }
361
362
363
364
365 \f
366 /******************************************************************************
367  * Function: int _DtHelpFileListGetNext ()
368  *
369  * Parameters:
370  *              fileListHead     head of the list
371  *              curFile          current entry in the list
372  *
373  * Returns:     pointer to next _DtHelpFileList entry
374  *              or NULL if end of list has been reached
375  *
376  * Purpose:     Gets the next file in the list
377  * 
378  *****************************************************************************/
379 _DtHelpFileList _DtHelpFileListGetNext ( 
380         _DtHelpFileList fileListHead,
381         _DtHelpFileList curFile)
382 {
383    /* could do a sanity test on curFile here */
384    if ( curFile ) return curFile->next;
385    else return fileListHead;
386 }
387
388
389 \f
390 /******************************************************************************
391  * Function: int _DtHelpFileFreeEntry ()
392  *
393  * Parameters:
394  *
395  * Purpose:     free the contents of an entry
396  * 
397  *****************************************************************************/
398 void _DtHelpFileFreeEntry (
399         _DtHelpFileEntry entry)
400 {
401     if(NULL == entry) return;
402     XmStringFree(entry->fileTitleXmStr);
403     XtFree(entry->docId);
404     XtFree(entry->timeStamp);
405     XtFree(entry->fileTitle);
406     XtFree((char *) entry->fileName);
407     XtFree((char *) entry->fullFilePath);
408     XtFree((char *) entry->clientData);
409     XtFree((char *) entry);
410 }
411
412
413 \f
414 /******************************************************************************
415  * Function: int _DtHelpFileListAddFile ()
416  *
417  * Parameters:
418  *
419  * Returns:     True if added a new entry
420  *              False if entry not added because already present
421  *              or an error occurred
422  *
423  *
424  * errno Values:
425  *              0        no error
426  *              ENOMEM   memory allocation error
427  *
428  * Purpose:     adds a file to the file list if its not already present
429  * 
430  *****************************************************************************/
431 Boolean _DtHelpFileListAddFile (
432         _DtHelpFileList * in_out_list,
433         XmFontList *    io_fontList,
434         Boolean *       ret_mod,
435         char *          fullFilePath,
436         char *          fileName,
437         _DtHelpFileInfoProc infoProc,
438         int             compareFlags,
439         int             sortFlags,
440         XtPointer       pDisplayArea)
441 {
442    _DtHelpFileList next;
443    _DtHelpFileList prev;
444    _DtHelpFileList newList;
445    _DtHelpFileRec  addFile;
446    char *       actualPath;
447    char         empty = 0;
448    int          nameKey = 0;
449    typedef int (*_CEStrcollProc)(const char *,const char *);
450    extern _CEStrcollProc _DtHelpCeGetStrcollProc();
451    _CEStrcollProc strcollfn = _DtHelpCeGetStrcollProc();
452
453    /* init the variable */
454    memset(&addFile, 0, sizeof(addFile));
455
456    /* if no full file path, go look for it */
457    if (NULL == fullFilePath || fullFilePath[0] == EOS) 
458        return False;       /* RETURN : incomplete file spec */
459
460    /* trace any links; _DtHelpFileTraceLinks may chg actualPath ptr */
461    actualPath = XtNewString(fullFilePath);
462    if ( _DtHelpFileTraceLinks(&actualPath) == False )
463    {
464       XtFree(actualPath);
465       return False;       /* RETURN: invalid file */
466    }
467    fullFilePath = actualPath;
468
469    /* if no filespec, get it */
470    if (NULL == fileName) 
471    {
472       fileName = fullFilePath;
473       if(_DtHelpCeStrrchr(fullFilePath, DirSlashStr, MB_CUR_MAX,&fileName)==0)
474           fileName++;
475    }
476    addFile.fileName = fileName;
477
478    /* if an info proc, use it, otherwise make due */
479    if (infoProc) 
480    {
481       (*infoProc)(pDisplayArea,fullFilePath,
482                        &addFile.fileTitle,&addFile.fileTitleXmStr,
483                        &addFile.docId,&addFile.timeStamp,&addFile.nameKey,
484                        io_fontList,ret_mod);
485    }
486    else
487    { 
488       nameKey = _DtHelpCeStrHashToKey(fileName);
489    }
490
491    /* look for prior existence and position */
492    next = prev = NULL;
493    while ( (next = _DtHelpFileListGetNext(*in_out_list, next)) != NULL )
494    {
495       if ( FileInfoMatchesP(next,&addFile,compareFlags) )
496       {
497          XtFree(addFile.docId);
498          XtFree(addFile.timeStamp);
499          XtFree(addFile.fileTitle);
500          XmStringFree(addFile.fileTitleXmStr);
501          XtFree(fullFilePath);
502          return False;                        /* RETURN : repeat entry */
503       }
504
505       /* insert lexically according to title */
506       /* use case insensitive NLS collating for ordering */
507       if (   (sortFlags & _DtHELP_FILE_TITLE) 
508           && (*strcollfn) (addFile.fileTitle, next->fileTitle) <= 0 )
509          break;          /* BREAK: insert after prev, before next */
510
511       /* FIX: add support for other sorting here */
512       /* if (   (sortFlags & _DtHELP_FILE_DIR)  */
513       /* if (   (sortFlags & _DtHELP_FILE_TIME)  */
514       /* if (   (sortFlags & _DtHELP_FILE_IDSTR)  */
515       /* if (   (sortFlags & _DtHELP_FILE_LOCALE)  */
516
517       prev = next;                           /* save ref to prior entry */
518    }
519    /* if dup not found, fall thru, with prev valid */
520
521    /* no matching path, so create, initialize, and append to list */
522    newList = (_DtHelpFileList) XtCalloc(1, sizeof(_DtHelpFileRec));
523    if (NULL == newList) 
524    {
525       XtFree(addFile.docId);
526       XtFree(addFile.timeStamp);
527       XtFree(addFile.fileTitle);
528       XmStringFree(addFile.fileTitleXmStr);
529       XtFree(fullFilePath);
530       return False;                            /* RETURN : error */
531    }
532
533    /* init the contents */
534    newList->fullFilePath = fullFilePath; /* copy made earlier */
535    newList->fileName = XtNewString(addFile.fileName);
536    newList->nameKey = addFile.nameKey;
537    newList->docId = (addFile.docId == &empty ? NULL : addFile.docId);
538    newList->timeStamp = (addFile.timeStamp == &empty ? NULL : addFile.timeStamp);
539    newList->fileTitle = addFile.fileTitle;
540    newList->fileTitleXmStr = addFile.fileTitleXmStr;
541    if (next) newList->next = next;
542    if (prev) prev->next = newList;
543    else *in_out_list = newList;
544
545    return True;                                 /* created new entry */
546 }
547
548
549 \f
550 /******************************************************************************
551  * Function: int _DtHelpFileListScanPaths ()
552  *
553  * Parameters:
554  *        type:           subdirectories to search (%T)
555  *        suffix:         extension of the files to find (%S)
556  *        searchHomeDir:  boolean flag
557  *        in_out_list:    manages list of files generated
558  *
559  * Returns:     count of files added to the list or -1
560  *
561  * errno Values:
562  *              EINVAL
563  *
564  * Purpose:     Scans all paths of given type looking for files with the suffixes
565  * 
566  *****************************************************************************/
567 int _DtHelpFileListScanPaths (
568         _DtHelpFileList * in_out_list,
569         XmFontList *   io_fontList,
570         Boolean *      ret_mod,
571         char *         type,
572         const char *   suffixList[],
573         Boolean        searchHomeDir,
574         _DtHelpFileInfoProc infoProc,
575         XtPointer      pDisplayArea,
576         int            sysPathCompareFlags,
577         int            otherPathCompareFlags,
578         int            sortFlags,
579         _DtHelpFileScanProcCB scanProc,
580         XtPointer      clientData)
581 {
582     char *loc;
583     char *ptr;
584     char *pathName;
585     struct stat status;
586     char *curPath;
587     int   curPathIndex;
588     int   foundFilesCnt;
589     int   compareFlags;
590     char * paths[_DtHELP_FILE_NUM_PATHS];
591     char ** scannedPaths = NULL;
592     int     scannedPathsCnt = 0;
593
594     /* get the search paths */
595     _DtHelpFileGetSearchPaths( paths, searchHomeDir );
596
597     /* get the locale */
598     loc = _DtHelpGetLocale();
599     if (NULL == loc || EOS == loc[0]) loc = strdup(LANG_C_STR);
600
601     /* zero found */
602     if (scanProc) (*scanProc)(0,clientData);
603
604     /* outer loop is once for each path */
605     foundFilesCnt = 0;
606     for ( curPathIndex = 0;
607           curPathIndex < _DtHELP_FILE_NUM_PATHS; 
608           curPathIndex++ )
609     {
610        curPath = paths[curPathIndex];
611        if (NULL == curPath) continue;
612
613        /* set the comparison flags */
614        /* This enables implementation of different policies for
615           listing files that are found, e.g. only list the first file
616           of a given name found in the SYS search path, while listing
617           same named files that have different times & ids in the
618           USER PATH. */
619        compareFlags = sysPathCompareFlags;
620        if (_DtHELP_FILE_SYS_PATH != curPathIndex) 
621           compareFlags = otherPathCompareFlags;
622     
623        /* find the files in that path */
624        do
625        {
626           char * slashptr;
627           /* look for next subpath separator and insert and EOS if found */
628           if (_DtHelpCeStrchr(curPath,PathSeparator,MB_CUR_MAX,&ptr)==0)
629               *ptr = EOS;
630
631           /* generate the (directory) path using all the variables 
632              and fix it up to remove the unwanted stuff involving the filename*/
633           pathName = _DtHelpCeExpandPathname(curPath, 
634                                      NULL, type, NULL, loc, NULL, 0);
635           if(   pathName 
636              && _DtHelpCeStrrchr(pathName,DirSlashStr,MB_CUR_MAX,&slashptr) == 0)
637              slashptr[0] = EOS; 
638
639           /* restore the subpath separator and advance past it */
640           if (ptr) *ptr++ = *PathSeparator;
641
642           /* if its a directory; scan for matching files in it */
643           if (     pathName != NULL 
644                 && *pathName != EOS 
645                 && access (pathName, R_OK | X_OK) == 0 
646                 && stat (pathName, &status) == 0
647                 && S_ISDIR(status.st_mode))        /* a dir */
648           {
649               int i;
650               /* check that we haven't already scanned it */
651               for ( i=0; i<scannedPathsCnt; i++ )
652                  if (strcmp(scannedPaths[i],pathName) == 0) break;
653
654               /* scan it if haven't already */
655               if ( i == scannedPathsCnt )
656               {
657                  foundFilesCnt += ScanDirForFiles(
658                                     pathName,suffixList,
659                                     in_out_list,io_fontList,ret_mod,
660                                     foundFilesCnt,infoProc,pDisplayArea,
661                                     compareFlags,sortFlags,scanProc,clientData);
662
663                  /* add to list of scanned */
664                  scannedPaths = (char **) XtRealloc((char *)scannedPaths,
665                                         (scannedPathsCnt+1) * sizeof(char *) );
666                  scannedPaths[scannedPathsCnt++] = strdup(pathName);
667               }  /* if haven' scanned already */
668           }  /* if a directory */
669 #if 0
670           else
671           {
672              printf("Unknown dir: %s\n", pathName);
673              printf("Access: %d, stat: %d, IS_DIR: %d, mode: %x\n",
674                 access (pathName, R_OK | X_OK),
675                 stat (pathName, &status),
676                 S_ISDIR(status.st_mode),
677                 status.st_mode);
678           }
679 #endif
680
681           if (pathName) free (pathName);
682           curPath = ptr;
683        } while (curPath && *curPath); /* while more subpaths */
684    }  /* for all paths */
685
686    /* free all scanned paths */
687    while ( scannedPathsCnt-- > 0 ) XtFree(scannedPaths[scannedPathsCnt]);
688    XtFree((char *)scannedPaths);
689
690    XtFree(loc);
691    return foundFilesCnt;
692 }
693