2 * CDE - Common Desktop Environment
4 * Copyright (c) 1993-2012, The Open Group. All rights reserved.
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)
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
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
23 /* $TOG: FileListUtils.c /main/11 1999/10/14 13:17:49 mgreess $ */
24 /************************************<+>*************************************
25 ****************************************************************************
27 ** File: FileListUtils.c
29 ** Project: DtHelp library
31 ** Description: Locates and lists all files (volumes) accessible via the
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.
40 ****************************************************************************
41 ************************************<+>*************************************/
53 #include <sys/param.h> /* MAXPATHLEN */
55 #define X_INCLUDE_DIRENT_H
56 #define XOS_USE_XT_LOCKING
57 #include <X11/Xos_r.h>
63 #include "bufioI.h" /* for AccessI.h */
66 #include "StringFuncsI.h"
67 #include "FileUtilsI.h"
68 #include "FileListUtilsI.h"
71 /******** constants *********/
72 #define LANG_C_STR "C"
75 /******** types *********/
77 /******** public global variables *********/
79 /******** variables *********/
80 static const char * PeriodStr = ".";
81 static const char * DirSlashStr = "/";
82 static const char * PathSeparator = ":";
84 /******** functions *********/
87 /*****************************************************************************
88 * Function: GetExtension()
90 * locate the '.' of the filename extension, if present
92 *****************************************************************************/
93 static char * GetExtension(
97 if (_DtHelpCeStrrchr(filename,PeriodStr,MB_CUR_MAX,&ext) == 0 ) return ext;
98 else return ""; /* do NOT return a NULL*/
103 /*****************************************************************************
104 * Function: SpecialStrcmp()
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.
111 *****************************************************************************/
112 static int SpecialStrcmp(
118 if(NULL == str2) return 0; /* str1 == str2 */
119 return -1; /* str1 < str2 */
121 if(NULL == str2) return 1; /* str1 > str2 */
122 return(strcmp(str1,str2)); /* str1 ? str2 */
128 /*****************************************************************************
129 * Function: FileInfoMatchesP()
131 * Compares the info of an existing file entry with test file info
132 * and determines whether they match.
133 * Returns: True or False
135 *****************************************************************************/
136 static Boolean FileInfoMatchesP(
137 _DtHelpFileEntry file1,
138 _DtHelpFileEntry file2,
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) )
158 /*****************************************************************************
159 * Function: _DtHelpFileIsSameP()
161 * Compares the info of two files and determines whether they match.
162 * Returns: True or False
164 *****************************************************************************/
165 Boolean _DtHelpFileIsSameP(
168 _DtHelpFileInfoProc infoProc,
170 XtPointer pDisplayArea)
172 _DtHelpFileRec file1, file2;
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;
182 /* init the structs */
183 memset(&file1,0,sizeof(file1));
184 memset(&file2,0,sizeof(file2));
185 file1.fileName = fileName1;
186 file2.fileName = fileName2;
188 /* get info on the files */
189 (*infoProc)(pDisplayArea,fileName1,NULL,NULL,
190 &file1.docId,&file1.timeStamp,&file1.nameKey,
192 (*infoProc)(pDisplayArea,fileName2,NULL,NULL,
193 &file2.docId,&file2.timeStamp,&file2.nameKey,
196 ret = FileInfoMatchesP(&file1,&file2,compareFlags);
199 XtFree(file1.timeStamp);
201 XtFree(file2.timeStamp);
207 /*****************************************************************************
208 * Function: ScanDirForFiles
210 * scan a directory looking for files with a matching suffix
211 * returns number of files found in this directory
213 *****************************************************************************/
214 static int ScanDirForFiles(
216 const char * suffixList[],
217 _DtHelpFileList * in_out_list,
218 XmFontList * io_fontList,
221 _DtHelpFileInfoProc infoProc,
222 XtPointer pDisplayArea,
225 _DtHelpFileScanProcCB scanProc,
226 XtPointer clientData)
229 char fullName [MAXPATHLEN + 2];
232 struct dirent *result;
233 _Xreaddirparams dirEntryBuf;
235 /* open the directory */
236 pDir = opendir (dirpath);
237 if (pDir == NULL) return 0; /* RETURN */
239 /* build the pathname */
240 snprintf(fullName, sizeof(fullName), "%s%s", dirpath, DirSlashStr);
241 ptr = fullName + strlen (fullName);
244 * Scan through the files looking for matching suffixes
246 while ((result = _XReaddir(pDir, dirEntryBuf)) != NULL) {
247 const char * matchedSuffix;
250 /* Skip over any "." and ".." entries. */
251 if ((strcmp(result->d_name, ".") == 0) ||
252 (strcmp(result->d_name, "..") == 0))
255 /* get working values */
256 ext = GetExtension (result->d_name);
257 matchedSuffix = ""; /* default (==>no suffix to match) */
259 /* try to match with a suffix, if specified */
260 if (NULL != suffixList)
262 const char * * pSuffix;
263 matchedSuffix = NULL;
264 for (pSuffix = suffixList;
265 NULL != *pSuffix && NULL == matchedSuffix;
268 if (strcmp(ext,*pSuffix) == 0)
269 matchedSuffix = *pSuffix;
273 /* and a match is found */
274 if ( NULL != matchedSuffix )
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))
284 if (scanProc) (*scanProc)(count + foundFilesCnt,clientData);
286 *ret_mod |= mod; /* accumulate mods */
288 } /* while more entries */
290 /* close the directory stream */
298 /******************************************************************************
299 * Function: _DtHelpFileListGetMatch ()
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
307 * Returns: pointer to matching _DtHelpFileList entry
308 * or NULL if none is found
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
319 *****************************************************************************/
320 _DtHelpFileList _DtHelpFileListGetMatch (
321 _DtHelpFileList fileListHead,
323 _DtHelpFileInfoProc infoProc,
325 XtPointer pDisplayArea)
327 _DtHelpFileList next;
330 /* zero the record */
331 memset (&file,0,sizeof(file));
333 if (NULL == fileListHead || NULL == fullFilePath) return NULL; /* RETURN */
335 /* get filename without path */
336 file.fileName = fullFilePath;
337 if (_DtHelpCeStrrchr(fullFilePath,DirSlashStr,MB_CUR_MAX,&file.fileName) == 0)
340 /* if an info proc, use it, otherwise make due */
342 (*infoProc)(pDisplayArea,fullFilePath,
343 NULL,NULL,&file.docId,&file.timeStamp,&file.nameKey,
346 file.nameKey = _DtHelpCeStrHashToKey(file.fileName);
349 while ( (next = _DtHelpFileListGetNext(fileListHead, next)) != NULL )
351 if ( FileInfoMatchesP(next,&file,compareFlags) )
352 break; /* next is the matching entry */
355 /* fileName is part of fullFilePath */
357 XtFree(file.timeStamp);
365 /******************************************************************************
366 * Function: int _DtHelpFileListGetNext ()
369 * fileListHead head of the list
370 * curFile current entry in the list
372 * Returns: pointer to next _DtHelpFileList entry
373 * or NULL if end of list has been reached
375 * Purpose: Gets the next file in the list
377 *****************************************************************************/
378 _DtHelpFileList _DtHelpFileListGetNext (
379 _DtHelpFileList fileListHead,
380 _DtHelpFileList curFile)
382 /* could do a sanity test on curFile here */
383 if ( curFile ) return curFile->next;
384 else return fileListHead;
389 /******************************************************************************
390 * Function: int _DtHelpFileFreeEntry ()
394 * Purpose: free the contents of an entry
396 *****************************************************************************/
397 void _DtHelpFileFreeEntry (
398 _DtHelpFileEntry entry)
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);
413 /******************************************************************************
414 * Function: int _DtHelpFileListAddFile ()
418 * Returns: True if added a new entry
419 * False if entry not added because already present
420 * or an error occurred
425 * ENOMEM memory allocation error
427 * Purpose: adds a file to the file list if its not already present
429 *****************************************************************************/
430 Boolean _DtHelpFileListAddFile (
431 _DtHelpFileList * in_out_list,
432 XmFontList * io_fontList,
436 _DtHelpFileInfoProc infoProc,
439 XtPointer pDisplayArea)
441 _DtHelpFileList next;
442 _DtHelpFileList prev;
443 _DtHelpFileList newList;
444 _DtHelpFileRec addFile;
448 typedef int (*_CEStrcollProc)(const char *,const char *);
449 extern _CEStrcollProc _DtHelpCeGetStrcollProc();
450 _CEStrcollProc strcollfn = _DtHelpCeGetStrcollProc();
452 /* init the variable */
453 memset(&addFile, 0, sizeof(addFile));
455 /* if no full file path, go look for it */
456 if (NULL == fullFilePath || fullFilePath[0] == EOS)
457 return False; /* RETURN : incomplete file spec */
459 /* trace any links; _DtHelpFileTraceLinks may chg actualPath ptr */
460 actualPath = XtNewString(fullFilePath);
461 if ( _DtHelpFileTraceLinks(&actualPath) == False )
464 return False; /* RETURN: invalid file */
466 fullFilePath = actualPath;
468 /* if no filespec, get it */
469 if (NULL == fileName)
471 fileName = fullFilePath;
472 if(_DtHelpCeStrrchr(fullFilePath, DirSlashStr, MB_CUR_MAX,&fileName)==0)
475 addFile.fileName = fileName;
477 /* if an info proc, use it, otherwise make due */
480 (*infoProc)(pDisplayArea,fullFilePath,
481 &addFile.fileTitle,&addFile.fileTitleXmStr,
482 &addFile.docId,&addFile.timeStamp,&addFile.nameKey,
483 io_fontList,ret_mod);
487 nameKey = _DtHelpCeStrHashToKey(fileName);
490 /* look for prior existence and position */
492 while ( (next = _DtHelpFileListGetNext(*in_out_list, next)) != NULL )
494 if ( FileInfoMatchesP(next,&addFile,compareFlags) )
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 */
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 */
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) */
516 prev = next; /* save ref to prior entry */
518 /* if dup not found, fall thru, with prev valid */
520 /* no matching path, so create, initialize, and append to list */
521 newList = (_DtHelpFileList) XtCalloc(1, sizeof(_DtHelpFileRec));
524 XtFree(addFile.docId);
525 XtFree(addFile.timeStamp);
526 XtFree(addFile.fileTitle);
527 XmStringFree(addFile.fileTitleXmStr);
528 XtFree(fullFilePath);
529 return False; /* RETURN : error */
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;
544 return True; /* created new entry */
549 /******************************************************************************
550 * Function: int _DtHelpFileListScanPaths ()
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
558 * Returns: count of files added to the list or -1
563 * Purpose: Scans all paths of given type looking for files with the suffixes
565 *****************************************************************************/
566 int _DtHelpFileListScanPaths (
567 _DtHelpFileList * in_out_list,
568 XmFontList * io_fontList,
571 const char * suffixList[],
572 Boolean searchHomeDir,
573 _DtHelpFileInfoProc infoProc,
574 XtPointer pDisplayArea,
575 int sysPathCompareFlags,
576 int otherPathCompareFlags,
578 _DtHelpFileScanProcCB scanProc,
579 XtPointer clientData)
589 char * paths[_DtHELP_FILE_NUM_PATHS];
590 char ** scannedPaths = NULL;
591 int scannedPathsCnt = 0;
593 /* get the search paths */
594 _DtHelpFileGetSearchPaths( paths, searchHomeDir );
597 loc = _DtHelpGetLocale();
598 if (NULL == loc || EOS == loc[0]) loc = strdup(LANG_C_STR);
601 if (scanProc) (*scanProc)(0,clientData);
603 /* outer loop is once for each path */
605 for ( curPathIndex = 0;
606 curPathIndex < _DtHELP_FILE_NUM_PATHS;
609 curPath = paths[curPathIndex];
610 if (NULL == curPath) continue;
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
618 compareFlags = sysPathCompareFlags;
619 if (_DtHELP_FILE_SYS_PATH != curPathIndex)
620 compareFlags = otherPathCompareFlags;
622 /* find the files in that path */
626 /* look for next subpath separator and insert and EOS if found */
627 if (_DtHelpCeStrchr(curPath,PathSeparator,MB_CUR_MAX,&ptr)==0)
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);
635 && _DtHelpCeStrrchr(pathName,DirSlashStr,MB_CUR_MAX,&slashptr) == 0)
638 /* restore the subpath separator and advance past it */
639 if (ptr) *ptr++ = *PathSeparator;
641 /* if its a directory; scan for matching files in it */
642 if ( pathName != NULL
644 && access (pathName, R_OK | X_OK) == 0
645 && stat (pathName, &status) == 0
646 && S_ISDIR(status.st_mode)) /* a dir */
649 /* check that we haven't already scanned it */
650 for ( i=0; i<scannedPathsCnt; i++ )
651 if (strcmp(scannedPaths[i],pathName) == 0) break;
653 /* scan it if haven't already */
654 if ( i == scannedPathsCnt )
656 foundFilesCnt += ScanDirForFiles(
658 in_out_list,io_fontList,ret_mod,
659 foundFilesCnt,infoProc,pDisplayArea,
660 compareFlags,sortFlags,scanProc,clientData);
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 */
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),
680 if (pathName) free (pathName);
682 } while (curPath && *curPath); /* while more subpaths */
683 } /* for all paths */
685 /* free all scanned paths */
686 while ( scannedPathsCnt-- > 0 ) XtFree(scannedPaths[scannedPathsCnt]);
687 XtFree((char *)scannedPaths);
690 return foundFilesCnt;