Merge branch 'master' into cde-next
[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     snprintf(fullName, sizeof(fullName), "%s%s", dirpath, DirSlashStr);
241     ptr = fullName + strlen (fullName);
242
243     /*
244      * Scan through the files looking for matching suffixes
245      */
246     while ((result = _XReaddir(pDir, dirEntryBuf)) != NULL) {
247         const char * matchedSuffix;
248         char * ext;
249
250         /* Skip over any "." and ".." entries. */
251         if ((strcmp(result->d_name, ".") == 0) ||
252             (strcmp(result->d_name, "..") == 0))
253           continue;
254
255         /* get working values */
256         ext = GetExtension (result->d_name);
257         matchedSuffix = "";      /* default (==>no suffix to match) */
258
259         /* try to match with a suffix, if specified */
260         if (NULL != suffixList)
261         {
262           const char * * pSuffix;
263           matchedSuffix = NULL;
264           for (pSuffix = suffixList; 
265                NULL != *pSuffix && NULL == matchedSuffix; 
266                pSuffix++ )
267           {
268             if (strcmp(ext,*pSuffix) == 0)
269               matchedSuffix = *pSuffix;
270           }
271         }
272
273         /* and a match is found */
274         if ( NULL != matchedSuffix ) 
275         {
276            Boolean mod;
277
278            /* then generate the full path and add to the list */
279            strcpy (ptr, result->d_name);
280            if (_DtHelpFileListAddFile(in_out_list,io_fontList,&mod,
281                   fullName,ptr,infoProc,compareFlags,sortFlags,pDisplayArea))
282            {
283               count++;
284               if (scanProc) (*scanProc)(count + foundFilesCnt,clientData);
285            }
286            *ret_mod |= mod;        /* accumulate mods */
287         }
288     } /* while more entries */
289
290     /* close the directory stream */
291     closedir(pDir);
292
293     return count;
294 }
295
296
297 \f
298 /******************************************************************************
299  * Function:  _DtHelpFileListGetMatch ()
300  *
301  * Parameters:
302  *              fileListHead     head of the list
303  *              fullFilePath     fullFilePath to file to find
304  *              infoProc         will deliver info on the file
305  *              pDisplayArea     used to interpret vol info
306  *
307  * Returns:     pointer to matching _DtHelpFileList entry
308  *              or NULL if none is found
309  *
310  * Purpose:     Looks for a file list entry for the file
311  *              identified by fullFilePath.
312  *              The infoProc routine gets info about the file
313  *              to be matched, and uses that when comparing
314  *              with the file list.  This allows more
315  *              than one file with the same name to be in
316  *              the list and be discriminated on the basis
317  *              of the info.
318  * 
319  *****************************************************************************/
320 _DtHelpFileList _DtHelpFileListGetMatch ( 
321         _DtHelpFileList     fileListHead,
322         char *              fullFilePath,
323         _DtHelpFileInfoProc infoProc,
324         int                 compareFlags,
325         XtPointer           pDisplayArea)
326 {
327    _DtHelpFileList next;
328    _DtHelpFileRec  file;
329
330    /* zero the record */
331    memset (&file,0,sizeof(file));
332
333    if (NULL == fileListHead || NULL == fullFilePath) return NULL; /* RETURN */
334
335    /* get filename without path */
336    file.fileName = fullFilePath;
337    if (_DtHelpCeStrrchr(fullFilePath,DirSlashStr,MB_CUR_MAX,&file.fileName) == 0) 
338        file.fileName++;
339
340    /* if an info proc, use it, otherwise make due */
341    if (infoProc) 
342       (*infoProc)(pDisplayArea,fullFilePath,
343                      NULL,NULL,&file.docId,&file.timeStamp,&file.nameKey,
344                      NULL,NULL);
345    else
346       file.nameKey = _DtHelpCeStrHashToKey(file.fileName);
347
348    next =  NULL;
349    while ( (next = _DtHelpFileListGetNext(fileListHead, next)) != NULL )
350    {
351       if ( FileInfoMatchesP(next,&file,compareFlags) )
352          break;                   /* next is the matching entry */
353    }
354
355    /* fileName is part of fullFilePath */
356    XtFree(file.docId);
357    XtFree(file.timeStamp);
358    return next;
359 }
360
361
362
363
364 \f
365 /******************************************************************************
366  * Function: int _DtHelpFileListGetNext ()
367  *
368  * Parameters:
369  *              fileListHead     head of the list
370  *              curFile          current entry in the list
371  *
372  * Returns:     pointer to next _DtHelpFileList entry
373  *              or NULL if end of list has been reached
374  *
375  * Purpose:     Gets the next file in the list
376  * 
377  *****************************************************************************/
378 _DtHelpFileList _DtHelpFileListGetNext ( 
379         _DtHelpFileList fileListHead,
380         _DtHelpFileList curFile)
381 {
382    /* could do a sanity test on curFile here */
383    if ( curFile ) return curFile->next;
384    else return fileListHead;
385 }
386
387
388 \f
389 /******************************************************************************
390  * Function: int _DtHelpFileFreeEntry ()
391  *
392  * Parameters:
393  *
394  * Purpose:     free the contents of an entry
395  * 
396  *****************************************************************************/
397 void _DtHelpFileFreeEntry (
398         _DtHelpFileEntry entry)
399 {
400     if(NULL == entry) return;
401     XmStringFree(entry->fileTitleXmStr);
402     XtFree(entry->docId);
403     XtFree(entry->timeStamp);
404     XtFree(entry->fileTitle);
405     XtFree((char *) entry->fileName);
406     XtFree((char *) entry->fullFilePath);
407     XtFree((char *) entry->clientData);
408     XtFree((char *) entry);
409 }
410
411
412 \f
413 /******************************************************************************
414  * Function: int _DtHelpFileListAddFile ()
415  *
416  * Parameters:
417  *
418  * Returns:     True if added a new entry
419  *              False if entry not added because already present
420  *              or an error occurred
421  *
422  *
423  * errno Values:
424  *              0        no error
425  *              ENOMEM   memory allocation error
426  *
427  * Purpose:     adds a file to the file list if its not already present
428  * 
429  *****************************************************************************/
430 Boolean _DtHelpFileListAddFile (
431         _DtHelpFileList * in_out_list,
432         XmFontList *    io_fontList,
433         Boolean *       ret_mod,
434         char *          fullFilePath,
435         char *          fileName,
436         _DtHelpFileInfoProc infoProc,
437         int             compareFlags,
438         int             sortFlags,
439         XtPointer       pDisplayArea)
440 {
441    _DtHelpFileList next;
442    _DtHelpFileList prev;
443    _DtHelpFileList newList;
444    _DtHelpFileRec  addFile;
445    char *       actualPath;
446    char         empty = 0;
447    int          nameKey = 0;
448    typedef int (*_CEStrcollProc)(const char *,const char *);
449    extern _CEStrcollProc _DtHelpCeGetStrcollProc();
450    _CEStrcollProc strcollfn = _DtHelpCeGetStrcollProc();
451
452    /* init the variable */
453    memset(&addFile, 0, sizeof(addFile));
454
455    /* if no full file path, go look for it */
456    if (NULL == fullFilePath || fullFilePath[0] == EOS) 
457        return False;       /* RETURN : incomplete file spec */
458
459    /* trace any links; _DtHelpFileTraceLinks may chg actualPath ptr */
460    actualPath = XtNewString(fullFilePath);
461    if ( _DtHelpFileTraceLinks(&actualPath) == False )
462    {
463       XtFree(actualPath);
464       return False;       /* RETURN: invalid file */
465    }
466    fullFilePath = actualPath;
467
468    /* if no filespec, get it */
469    if (NULL == fileName) 
470    {
471       fileName = fullFilePath;
472       if(_DtHelpCeStrrchr(fullFilePath, DirSlashStr, MB_CUR_MAX,&fileName)==0)
473           fileName++;
474    }
475    addFile.fileName = fileName;
476
477    /* if an info proc, use it, otherwise make due */
478    if (infoProc) 
479    {
480       (*infoProc)(pDisplayArea,fullFilePath,
481                        &addFile.fileTitle,&addFile.fileTitleXmStr,
482                        &addFile.docId,&addFile.timeStamp,&addFile.nameKey,
483                        io_fontList,ret_mod);
484    }
485    else
486    { 
487       nameKey = _DtHelpCeStrHashToKey(fileName);
488    }
489
490    /* look for prior existence and position */
491    next = prev = NULL;
492    while ( (next = _DtHelpFileListGetNext(*in_out_list, next)) != NULL )
493    {
494       if ( FileInfoMatchesP(next,&addFile,compareFlags) )
495       {
496          XtFree(addFile.docId);
497          XtFree(addFile.timeStamp);
498          XtFree(addFile.fileTitle);
499          XmStringFree(addFile.fileTitleXmStr);
500          XtFree(fullFilePath);
501          return False;                        /* RETURN : repeat entry */
502       }
503
504       /* insert lexically according to title */
505       /* use case insensitive NLS collating for ordering */
506       if (   (sortFlags & _DtHELP_FILE_TITLE) 
507           && (*strcollfn) (addFile.fileTitle, next->fileTitle) <= 0 )
508          break;          /* BREAK: insert after prev, before next */
509
510       /* FIX: add support for other sorting here */
511       /* if (   (sortFlags & _DtHELP_FILE_DIR)  */
512       /* if (   (sortFlags & _DtHELP_FILE_TIME)  */
513       /* if (   (sortFlags & _DtHELP_FILE_IDSTR)  */
514       /* if (   (sortFlags & _DtHELP_FILE_LOCALE)  */
515
516       prev = next;                           /* save ref to prior entry */
517    }
518    /* if dup not found, fall thru, with prev valid */
519
520    /* no matching path, so create, initialize, and append to list */
521    newList = (_DtHelpFileList) XtCalloc(1, sizeof(_DtHelpFileRec));
522    if (NULL == newList) 
523    {
524       XtFree(addFile.docId);
525       XtFree(addFile.timeStamp);
526       XtFree(addFile.fileTitle);
527       XmStringFree(addFile.fileTitleXmStr);
528       XtFree(fullFilePath);
529       return False;                            /* RETURN : error */
530    }
531
532    /* init the contents */
533    newList->fullFilePath = fullFilePath; /* copy made earlier */
534    newList->fileName = XtNewString(addFile.fileName);
535    newList->nameKey = addFile.nameKey;
536    newList->docId = (addFile.docId == &empty ? NULL : addFile.docId);
537    newList->timeStamp = (addFile.timeStamp == &empty ? NULL : addFile.timeStamp);
538    newList->fileTitle = addFile.fileTitle;
539    newList->fileTitleXmStr = addFile.fileTitleXmStr;
540    if (next) newList->next = next;
541    if (prev) prev->next = newList;
542    else *in_out_list = newList;
543
544    return True;                                 /* created new entry */
545 }
546
547
548 \f
549 /******************************************************************************
550  * Function: int _DtHelpFileListScanPaths ()
551  *
552  * Parameters:
553  *        type:           subdirectories to search (%T)
554  *        suffix:         extension of the files to find (%S)
555  *        searchHomeDir:  boolean flag
556  *        in_out_list:    manages list of files generated
557  *
558  * Returns:     count of files added to the list or -1
559  *
560  * errno Values:
561  *              EINVAL
562  *
563  * Purpose:     Scans all paths of given type looking for files with the suffixes
564  * 
565  *****************************************************************************/
566 int _DtHelpFileListScanPaths (
567         _DtHelpFileList * in_out_list,
568         XmFontList *   io_fontList,
569         Boolean *      ret_mod,
570         char *         type,
571         const char *   suffixList[],
572         Boolean        searchHomeDir,
573         _DtHelpFileInfoProc infoProc,
574         XtPointer      pDisplayArea,
575         int            sysPathCompareFlags,
576         int            otherPathCompareFlags,
577         int            sortFlags,
578         _DtHelpFileScanProcCB scanProc,
579         XtPointer      clientData)
580 {
581     char *loc;
582     char *ptr;
583     char *pathName;
584     struct stat status;
585     char *curPath;
586     int   curPathIndex;
587     int   foundFilesCnt;
588     int   compareFlags;
589     char * paths[_DtHELP_FILE_NUM_PATHS];
590     char ** scannedPaths = NULL;
591     int     scannedPathsCnt = 0;
592
593     /* get the search paths */
594     _DtHelpFileGetSearchPaths( paths, searchHomeDir );
595
596     /* get the locale */
597     loc = _DtHelpGetLocale();
598     if (NULL == loc || EOS == loc[0]) loc = strdup(LANG_C_STR);
599
600     /* zero found */
601     if (scanProc) (*scanProc)(0,clientData);
602
603     /* outer loop is once for each path */
604     foundFilesCnt = 0;
605     for ( curPathIndex = 0;
606           curPathIndex < _DtHELP_FILE_NUM_PATHS; 
607           curPathIndex++ )
608     {
609        curPath = paths[curPathIndex];
610        if (NULL == curPath) continue;
611
612        /* set the comparison flags */
613        /* This enables implementation of different policies for
614           listing files that are found, e.g. only list the first file
615           of a given name found in the SYS search path, while listing
616           same named files that have different times & ids in the
617           USER PATH. */
618        compareFlags = sysPathCompareFlags;
619        if (_DtHELP_FILE_SYS_PATH != curPathIndex) 
620           compareFlags = otherPathCompareFlags;
621     
622        /* find the files in that path */
623        do
624        {
625           char * slashptr;
626           /* look for next subpath separator and insert and EOS if found */
627           if (_DtHelpCeStrchr(curPath,PathSeparator,MB_CUR_MAX,&ptr)==0)
628               *ptr = EOS;
629
630           /* generate the (directory) path using all the variables 
631              and fix it up to remove the unwanted stuff involving the filename*/
632           pathName = _DtHelpCeExpandPathname(curPath, 
633                                      NULL, type, NULL, loc, NULL, 0);
634           if(   pathName 
635              && _DtHelpCeStrrchr(pathName,DirSlashStr,MB_CUR_MAX,&slashptr) == 0)
636              slashptr[0] = EOS; 
637
638           /* restore the subpath separator and advance past it */
639           if (ptr) *ptr++ = *PathSeparator;
640
641           /* if its a directory; scan for matching files in it */
642           if (     pathName != NULL 
643                 && *pathName != EOS 
644                 && access (pathName, R_OK | X_OK) == 0 
645                 && stat (pathName, &status) == 0
646                 && S_ISDIR(status.st_mode))        /* a dir */
647           {
648               int i;
649               /* check that we haven't already scanned it */
650               for ( i=0; i<scannedPathsCnt; i++ )
651                  if (strcmp(scannedPaths[i],pathName) == 0) break;
652
653               /* scan it if haven't already */
654               if ( i == scannedPathsCnt )
655               {
656                  foundFilesCnt += ScanDirForFiles(
657                                     pathName,suffixList,
658                                     in_out_list,io_fontList,ret_mod,
659                                     foundFilesCnt,infoProc,pDisplayArea,
660                                     compareFlags,sortFlags,scanProc,clientData);
661
662                  /* add to list of scanned */
663                  scannedPaths = (char **) XtRealloc((char *)scannedPaths,
664                                         (scannedPathsCnt+1) * sizeof(char *) );
665                  scannedPaths[scannedPathsCnt++] = strdup(pathName);
666               }  /* if haven' scanned already */
667           }  /* if a directory */
668 #if 0
669           else
670           {
671              printf("Unknown dir: %s\n", pathName);
672              printf("Access: %d, stat: %d, IS_DIR: %d, mode: %x\n",
673                 access (pathName, R_OK | X_OK),
674                 stat (pathName, &status),
675                 S_ISDIR(status.st_mode),
676                 status.st_mode);
677           }
678 #endif
679
680           if (pathName) free (pathName);
681           curPath = ptr;
682        } while (curPath && *curPath); /* while more subpaths */
683    }  /* for all paths */
684
685    /* free all scanned paths */
686    while ( scannedPathsCnt-- > 0 ) XtFree(scannedPaths[scannedPathsCnt]);
687    XtFree((char *)scannedPaths);
688
689    XtFree(loc);
690    return foundFilesCnt;
691 }
692