1 /*-
2  ***********************************************************************
3  *
4  * $Id: support.c,v 1.64 2014/07/18 06:40:44 mavrik Exp $
5  *
6  ***********************************************************************
7  *
8  * Copyright 2000-2014 The FTimes Project, All Rights Reserved.
9  *
10  ***********************************************************************
11  */
12 #include "all-includes.h"
13 
14 #ifdef WIN32
15 static int (*CompareFunction)  () = strcasecmp;
16 static int (*NCompareFunction) () = strncasecmp;
17 #else
18 static int (*CompareFunction)  () = strcmp;
19 static int (*NCompareFunction) () = strncmp;
20 #endif
21 
22 /*-
23  ***********************************************************************
24  *
25  * SupportAddListItem
26  *
27  ***********************************************************************
28  */
29 FILE_LIST *
SupportAddListItem(FILE_LIST * psItem,FILE_LIST * psHead,char * pcError)30 SupportAddListItem(FILE_LIST *psItem, FILE_LIST *psHead, char *pcError)
31 {
32   const char          acRoutine[] = "SupportAddListItem()";
33   FILE_LIST          *psCurrent = NULL;
34   FILE_LIST          *psTail = NULL;
35 
36   /*-
37    *********************************************************************
38    *
39    * Check that the item is not NULL.
40    *
41    *********************************************************************
42    */
43   if (psItem == NULL)
44   {
45     snprintf(pcError, MESSAGE_SIZE, "%s: NULL input. That shouldn't happen.", acRoutine);
46     return NULL;
47   }
48 
49   /*-
50    *********************************************************************
51    *
52    * If the head is NULL, return the new item as the head.
53    *
54    *********************************************************************
55    */
56   if (psHead == NULL)
57   {
58     return psItem;
59   }
60 
61   /*-
62    *********************************************************************
63    *
64    * Otherwise, find the tail, and append the new item to it.
65    *
66    *********************************************************************
67    */
68   for (psCurrent = psTail = psHead; psCurrent != NULL; psCurrent = psCurrent->psNext)
69   {
70     if (psCurrent->psNext == NULL)
71     {
72       psTail = psCurrent;
73     }
74   }
75   psTail->psNext = psItem;
76 
77   return psHead;
78 }
79 
80 
81 /*-
82  ***********************************************************************
83  *
84  * SupportAddToList
85  *
86  ***********************************************************************
87  */
88 int
SupportAddToList(char * pcPath,FILE_LIST ** ppsList,char * pcListName,char * pcError)89 SupportAddToList(char *pcPath, FILE_LIST **ppsList, char *pcListName, char *pcError)
90 {
91   const char          acRoutine[] = "SupportAddToList()";
92   char                acLocalError[MESSAGE_SIZE] = "";
93   char                acLocalPath[FTIMES_MAX_PATH];
94   int                 iIndex = 0;
95   int                 iLocalIndex = 0;
96   int                 iLength = 0;
97   int                 iType = FILE_LIST_REGULAR;
98   FILE_LIST          *psHead = NULL;
99   FILE_LIST          *psItem = NULL;
100 
101   /*-
102    *********************************************************************
103    *
104    * Make sure that the path has a valid length.
105    *
106    *********************************************************************
107    */
108   iLength = strlen(pcPath);
109   if (iLength < 1 || iLength > FTIMES_MAX_PATH - 1)
110   {
111     snprintf(pcError, MESSAGE_SIZE, "%s: List = [%s], Length = [%d]: Path must be at least 1 byte and less than %d bytes long.", acRoutine, pcListName, iLength, FTIMES_MAX_PATH);
112     return ER;
113   }
114 
115   /*-
116    *********************************************************************
117    *
118    * Check to see if this is a URL-encoded path. If it is, advance the
119    * index to skip over the "file://" prefix, and set the path type.
120    *
121    *********************************************************************
122    */
123   if (strncmp(pcPath, "file://", 7) == 0)
124   {
125     iIndex = 7;
126     iType = FILE_LIST_ENCODED;
127   }
128 
129   /*-
130    *********************************************************************
131    *
132    * Make sure that we have the start of a full path.
133    *
134    *********************************************************************
135    */
136 #ifdef WIN32
137 //THIS CHANGE IS PART OF EXTENDED PREFIX SUPPORT (\\?\)
138 //    if (!(isalpha((int) pcPath[iIndex]) && pcPath[iIndex + 1] == ':'))
139     if (!(isalpha((int) pcPath[iIndex]) && pcPath[iIndex + 1] == ':' && pcPath[iIndex + 2] == FTIMES_SLASHCHAR))
140 //END (\\?\)
141 #else
142     if (pcPath[iIndex] != FTIMES_SLASHCHAR)
143 #endif
144     {
145       snprintf(pcError, MESSAGE_SIZE, "%s: List = [%s], Item = [%s]: A full path is required.", acRoutine, pcListName, pcPath);
146       return ER;
147     }
148 
149   /*-
150    *********************************************************************
151    *
152    * Copy pcPath into acLocalPath removing extra slashes along the way.
153    *
154    *********************************************************************
155    */
156   for (iLocalIndex = 0; iIndex < iLength; iIndex++)
157   {
158     if
159     (
160       (
161         (iType == FILE_LIST_REGULAR && iIndex > 0) ||
162         (iType == FILE_LIST_ENCODED && iIndex > 7)
163       ) &&
164       pcPath[iIndex]     == FTIMES_SLASHCHAR &&
165       pcPath[iIndex - 1] == FTIMES_SLASHCHAR
166     )
167     {
168       continue;
169     }
170     acLocalPath[iLocalIndex++] = pcPath[iIndex];
171   }
172   acLocalPath[iLocalIndex] = 0;
173   iLength = iLocalIndex;
174 
175   /*-
176    *********************************************************************
177    *
178    * If this is not the root directory chop off any trailing slashes.
179    *
180    *********************************************************************
181    */
182 //THIS CHANGE IS PART OF EXTENDED PREFIX SUPPORT (\\?\)
183 #ifdef WIN32
184   if (strcmp(&acLocalPath[2], FTIMES_SLASH) != 0)
185 #else
186   if (strcmp(acLocalPath, FTIMES_SLASH) != 0)
187 #endif
188 //END (\\?\)
189   {
190     while (acLocalPath[iLength - 1] == FTIMES_SLASHCHAR && iLength > 1)
191     {
192       acLocalPath[--iLength] = 0;
193     }
194   }
195 
196   /*-
197    *********************************************************************
198    *
199    * Allocate and initialize a new item.
200    *
201    *********************************************************************
202    */
203   psItem = SupportNewListItem(acLocalPath, iType, acLocalError);
204   if (psItem == NULL)
205   {
206     snprintf(pcError, MESSAGE_SIZE, "%s: List = [%s], Item = [%s]: %s", acRoutine, pcListName, pcPath, acLocalError);
207     return ER;
208   }
209 
210   /*-
211    *********************************************************************
212    *
213    * If this is not a duplicate item, add it to the list.
214    *
215    *********************************************************************
216    */
217   if (SupportMatchExclude(*ppsList, psItem->pcRegularPath) == NULL)
218   {
219     psHead = SupportAddListItem(psItem, *ppsList, acLocalError);
220     if (psHead == NULL)
221     {
222       snprintf(pcError, MESSAGE_SIZE, "%s: List = [%s], Item = [%s]: %s", acRoutine, pcListName, pcPath, acLocalError);
223       SupportFreeListItem(psItem);
224       return ER;
225     }
226     if (*ppsList == NULL)
227     {
228       *ppsList = psHead;
229     }
230   }
231   else
232   {
233     snprintf(acLocalError, MESSAGE_SIZE, "List = [%s], Item = [%s]: Ignoring duplicate item.", pcListName, pcPath);
234     ErrorHandler(ER_Warning, acLocalError, ERROR_WARNING);
235   }
236 
237   return ER_OK;
238 }
239 
240 
241 #ifdef WINNT
242 /*-
243  ***********************************************************************
244  *
245  * SupportAdjustPrivileges
246  *
247  ***********************************************************************
248  */
249 BOOL
SupportAdjustPrivileges(LPCTSTR lpcPrivilege)250 SupportAdjustPrivileges(LPCTSTR lpcPrivilege)
251 {
252   HANDLE              hToken;
253   TOKEN_PRIVILEGES    sTokenPrivileges;
254   BOOL                bResult;
255 
256   /*-
257    *********************************************************************
258    *
259    * Open the access token associated with this process.
260    *
261    *********************************************************************
262    */
263   bResult = OpenProcessToken
264               (
265                 GetCurrentProcess(),
266                 TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
267                 &hToken
268               );
269   if (bResult == FALSE)
270   {
271     return FALSE;
272   }
273 
274   /*-
275    *********************************************************************
276    *
277    * Retrieve the locally unique identifier (LUID) used on this system
278    * that represents the specified privilege.
279    *
280    *********************************************************************
281    */
282   bResult = LookupPrivilegeValue(
283                                 NULL,
284                                 lpcPrivilege,
285                                 &sTokenPrivileges.Privileges[0].Luid
286     );
287 
288   if (bResult == FALSE)
289   {
290     return FALSE;
291   }
292 
293   sTokenPrivileges.PrivilegeCount = 1;
294 
295   /*-
296    *********************************************************************
297    *
298    * One privilege to set.
299    *
300    *********************************************************************
301    */
302   sTokenPrivileges.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
303 
304   /*-
305    *********************************************************************
306    *
307    * Enable the specified privilege. Note: Enabling and/or disabling
308    * privileges requires TOKEN_ADJUST_PRIVILEGES access.
309    *
310    *********************************************************************
311    */
312   bResult = AdjustTokenPrivileges(
313                                  hToken,
314                                  FALSE,
315                                  &sTokenPrivileges,
316                                  0,
317                                  (PTOKEN_PRIVILEGES) NULL,
318                                  0
319     );
320 
321   if (bResult == FALSE || GetLastError() != ERROR_SUCCESS)
322   {
323     return FALSE;
324   }
325 
326   return TRUE;
327 }
328 #endif
329 
330 
331 /*-
332  ***********************************************************************
333  *
334  * SupportCheckList
335  *
336  ***********************************************************************
337  */
338 int
SupportCheckList(FILE_LIST * psHead,char * pcListName,char * pcError)339 SupportCheckList(FILE_LIST *psHead, char *pcListName, char *pcError)
340 {
341   const char          acRoutine[] = "SupportCheckList()";
342   char                acLocalError[MESSAGE_SIZE] = "";
343   FILE_LIST          *psList;
344 
345   for (psList = psHead; psList != NULL; psList = psList->psNext)
346   {
347     if (SupportGetFileType(psList->pcRegularPath, acLocalError) == FTIMES_FILETYPE_ERROR)
348     {
349       snprintf(pcError, MESSAGE_SIZE, "%s: List = [%s], NeuteredItem = [%s]: %s", acRoutine, pcListName, psList->pcEncodedPath, acLocalError);
350       return ER;
351     }
352   }
353   return ER_OK;
354 }
355 
356 
357 /*-
358  ***********************************************************************
359  *
360  * SupportChopEOLs
361  *
362  ***********************************************************************
363  */
364 int
SupportChopEOLs(char * pcLine,int iStrict,char * pcError)365 SupportChopEOLs(char *pcLine, int iStrict, char *pcError)
366 {
367   const char          acRoutine[] = "SupportChopEOLs()";
368   int                 iLineLength;
369   int                 iSaveLength;
370 
371   /*-
372    *********************************************************************
373    *
374    * Calculate line length.
375    *
376    *********************************************************************
377    */
378   iLineLength = iSaveLength = strlen(pcLine);
379 
380   /*-
381    *********************************************************************
382    *
383    * Scan backwards over EOL characters.
384    *
385    *********************************************************************
386    */
387   while (iLineLength > 0 && ((pcLine[iLineLength - 1] == '\r') || (pcLine[iLineLength - 1] == '\n')))
388   {
389     iLineLength--;
390   }
391 
392   /*-
393    *********************************************************************
394    *
395    * If strict checking is on and EOL was not found, it's an error.
396    *
397    *********************************************************************
398    */
399   if (iStrict && iLineLength == iSaveLength)
400   {
401     snprintf(pcError, MESSAGE_SIZE, "%s: EOL required but not found.", acRoutine);
402     return ER;
403   }
404 
405   /*-
406    *********************************************************************
407    *
408    * Terminate line excluding any EOL characters.
409    *
410    *********************************************************************
411    */
412   pcLine[iLineLength] = 0;
413 
414   return ER_OK;
415 }
416 
417 
418 /*-
419  ***********************************************************************
420  *
421  * SupportDisplayRunStatistics
422  *
423  ***********************************************************************
424  */
425 void
SupportDisplayRunStatistics(FTIMES_PROPERTIES * psProperties)426 SupportDisplayRunStatistics(FTIMES_PROPERTIES *psProperties)
427 {
428   char                acMessage[MESSAGE_SIZE];
429   double              dStopTime = 0;
430 
431   /*-
432    *********************************************************************
433    *
434    * Stop the RunTime clock. Report Warnings, Failures, and RunTime.
435    *
436    *********************************************************************
437    */
438   dStopTime = TimeGetTimeValueAsDouble();
439 
440   snprintf(acMessage, MESSAGE_SIZE, "Warnings=%d", ErrorGetWarnings());
441   MessageHandler(MESSAGE_QUEUE_IT, MESSAGE_INFORMATION, MESSAGE_PROPERTY_STRING, acMessage);
442 
443   snprintf(acMessage, MESSAGE_SIZE, "Failures=%d", ErrorGetFailures());
444   MessageHandler(MESSAGE_QUEUE_IT, MESSAGE_INFORMATION, MESSAGE_PROPERTY_STRING, acMessage);
445 
446   snprintf(acMessage, MESSAGE_SIZE, "Duration=%.6f (s)", (double) (dStopTime - psProperties->dStartTime));
447   MessageHandler(MESSAGE_QUEUE_IT, MESSAGE_INFORMATION, MESSAGE_PROPERTY_STRING, acMessage);
448 
449   switch (psProperties->iRunMode)
450   {
451     case FTIMES_DIGAUTO:
452     case FTIMES_DIGMODE:
453     case FTIMES_MAPAUTO:
454     case FTIMES_MAPMODE:
455     case FTIMES_MADMODE:
456       snprintf(acMessage, MESSAGE_SIZE, "AnalysisTime=%.6f (s)", AnalyzeGetAnalysisTime());
457       MessageHandler(MESSAGE_QUEUE_IT, MESSAGE_INFORMATION, MESSAGE_PROPERTY_STRING, acMessage);
458       snprintf(acMessage, MESSAGE_SIZE, "AverageDps=%.2f (KB/s)", AnalyzeGetDps());
459       MessageHandler(MESSAGE_QUEUE_IT, MESSAGE_INFORMATION, MESSAGE_PROPERTY_STRING, acMessage);
460       break;
461   }
462 }
463 
464 
465 /*-
466  ***********************************************************************
467  *
468  * SupportDropListItem
469  *
470  ***********************************************************************
471  */
472 FILE_LIST *
SupportDropListItem(FILE_LIST * psHead,FILE_LIST * psDrop)473 SupportDropListItem(FILE_LIST *psHead, FILE_LIST *psDrop)
474 {
475   FILE_LIST          *psList;
476   FILE_LIST          *psNewHead;
477 
478   psNewHead = psHead;
479 
480   if (psDrop == psHead)
481   {
482     psNewHead = psDrop->psNext;
483     free(psDrop);
484   }
485   else
486   {
487     for (psList = psHead; psList != NULL; psList = psList->psNext)
488     {
489       if (psDrop == psList->psNext)
490       {
491         psList->psNext = psDrop->psNext;
492         free(psDrop);
493       }
494     }
495   }
496   return psNewHead;
497 }
498 
499 
500 /*-
501  ***********************************************************************
502  *
503  * SupportEraseFile
504  *
505  ***********************************************************************
506  */
507 int
SupportEraseFile(char * pcName,char * pcError)508 SupportEraseFile(char *pcName, char *pcError)
509 {
510   const char          acRoutine[] = "SupportEraseFile()";
511 #define WIPE_BUFSIZE 0x8000
512   char               *apcWipe[WIPE_BUFSIZE];
513   long                lFileLength;
514   long                lNWiped;
515   FILE               *pFile;
516 
517   if ((pFile = fopen(pcName, "rb+")) != NULL)
518   {
519     fseek(pFile, 0, SEEK_END);
520     lFileLength = ftell(pFile);
521     rewind(pFile);
522     lNWiped = 0;
523     memset(apcWipe, 0xa5, WIPE_BUFSIZE);
524     while (((lNWiped += fwrite(apcWipe, 1, WIPE_BUFSIZE, pFile)) <= lFileLength) && !ferror(pFile));
525     fclose(pFile);
526   }
527 
528   if (unlink(pcName) != ER_OK)
529   {
530     snprintf(pcError, MESSAGE_SIZE, "%s: %s", acRoutine, strerror(errno));
531     return ER;
532   }
533 
534   return ER_OK;
535 }
536 
537 
538 /*-
539  ***********************************************************************
540  *
541  * SupportExpandDirectoryPath
542  *
543  ***********************************************************************
544  */
545 int
SupportExpandDirectoryPath(char * pcPath,char * pcFullPath,int iFullPathSize,char * pcError)546 SupportExpandDirectoryPath(char *pcPath, char *pcFullPath, int iFullPathSize, char *pcError)
547 {
548   const char          acRoutine[] = "SupportExpandDirectoryPath()";
549   char               *pcCwdDir;
550   char               *pcNewDir;
551   char               *pcTempPath;
552   int                 iError;
553   int                 iLength;
554 
555   iLength = strlen(pcPath);
556   if (iLength < 1)
557   {
558     snprintf(pcError, MESSAGE_SIZE, "%s: Directory = [%s], Length = [%d]: Length less than 1 byte.", acRoutine, pcPath, iLength);
559     return ER_Length;
560   }
561 
562   if (iLength > iFullPathSize - 1)
563   {
564     snprintf(pcError, MESSAGE_SIZE, "%s: Directory = [%s]: Length (%d) exceeds %d bytes.", acRoutine, pcPath, iLength, iFullPathSize - 1);
565     return ER_Length;
566   }
567 
568   pcTempPath = malloc(iLength + 2); /* +1 for a FTIMES_SLASHCHAR, +1 for a NULL */
569   if (pcTempPath == NULL)
570   {
571     snprintf(pcError, MESSAGE_SIZE, "%s: Directory = [%s]: %s", acRoutine, pcPath, strerror(errno));
572     return ER_BadHandle;
573   }
574 
575   strcpy(pcTempPath, pcPath);
576 
577   /*-
578    *********************************************************************
579    *
580    * We need to tack on a FTIMES_SLASHCHAR so that NT can figure out what we
581    * want to do. If you are in c:\xyz and type "cd c:", NT will simply
582    * print out the name of the directory that you are in (i.e. c:\xyz).
583    * To avoid this problem, you need to type "cd c:\". The same seems
584    * to be true for getwcd().
585    *
586    *********************************************************************
587    */
588   if (pcPath[iLength - 1] != FTIMES_SLASHCHAR)
589   {
590     pcTempPath[iLength] = FTIMES_SLASHCHAR;
591     pcTempPath[iLength + 1] = 0;
592   }
593 
594   /*-
595    *********************************************************************
596    *
597    * Save the current directory entry after getting its full path.
598    *
599    *********************************************************************
600    */
601   pcCwdDir = getcwd(NULL, FTIMES_MAX_PATH);
602   if (pcCwdDir == NULL)
603   {
604     snprintf(pcError, MESSAGE_SIZE, "%s: Directory = [%s]: %s", acRoutine, pcPath, strerror(errno));
605     free(pcTempPath);
606     return ER;
607   }
608 
609   /*-
610    *********************************************************************
611    *
612    * Change to the specified directory, and expand its path.
613    *
614    *********************************************************************
615    */
616   iError = chdir((const char *) pcTempPath);
617   if (iError != ER_OK)
618   {
619     snprintf(pcError, MESSAGE_SIZE, "%s: Directory = [%s]: %s", acRoutine, pcPath, strerror(errno));
620     free(pcTempPath);
621     free(pcCwdDir); /* Created by getcwd() */
622     return ER;
623   }
624 
625   pcNewDir = getcwd(pcFullPath, iFullPathSize);
626   if (pcNewDir == NULL)
627   {
628     snprintf(pcError, MESSAGE_SIZE, "%s: Directory = [%s]: %s", acRoutine, pcPath, strerror(errno));
629     free(pcTempPath);
630     free(pcCwdDir); /* Created by getcwd() */
631     return ER;
632   }
633 
634   /*-
635    *********************************************************************
636    *
637    * Chop off any trailing slashes.
638    *
639    *********************************************************************
640    */
641   iLength = strlen(pcFullPath);
642   while (pcFullPath[iLength - 1] == FTIMES_SLASHCHAR && iLength > 1)
643   {
644     pcFullPath[--iLength] = 0;
645   }
646 
647   /*-
648    *********************************************************************
649    *
650    * Change back to the starting directory.
651    *
652    *********************************************************************
653    */
654   iError = chdir((const char *) pcCwdDir);
655   if (iError != ER_OK)
656   {
657     snprintf(pcError, MESSAGE_SIZE, "%s: Directory = [%s]: %s", acRoutine, pcPath, strerror(errno));
658     free(pcTempPath);
659     free(pcCwdDir); /* Created by getcwd() */
660     return ER;
661   }
662 
663   free(pcTempPath);
664   free(pcCwdDir); /* Created by getcwd() */
665 
666   return ER_OK;
667 }
668 
669 
670 /*-
671  ***********************************************************************
672  *
673  * SupportExpandPath
674  *
675  ***********************************************************************
676  */
677 int
SupportExpandPath(char * pcPath,char * pcFullPath,int iFullPathSize,int iForceExpansion,char * pcError)678 SupportExpandPath(char *pcPath, char *pcFullPath, int iFullPathSize, int iForceExpansion, char *pcError)
679 {
680   const char          acRoutine[] = "SupportExpandPath()";
681   char                acLocalError[MESSAGE_SIZE] = "";
682   char               *pcTempFile;
683   char               *pcTempPath;
684   int                 iError;
685   int                 iLength;
686   int                 iTempLength;
687 
688   iLength = iTempLength = strlen(pcPath);
689   if (iLength < 1)
690   {
691     snprintf(pcError, MESSAGE_SIZE, "%s: File = [%s], Length = [%d]: Length less than 1 byte.", acRoutine, pcPath, iLength);
692     return ER_Length;
693   }
694 
695   if (iLength > iFullPathSize - 1)
696   {
697     snprintf(pcError, MESSAGE_SIZE, "%s: File = [%s]: Length (%d) exceeds %d bytes.", acRoutine, pcPath, iLength, iFullPathSize - 1);
698     return ER_Length;
699   }
700 
701   /*-
702    *********************************************************************
703    *
704    * If the path is URL-encoded, no expansion is performed.
705    *
706    *********************************************************************
707    */
708   if (strncmp(pcPath, "file://", 7) == 0)
709   {
710     strncpy(pcFullPath, pcPath, iFullPathSize);
711     return ER_OK;
712   }
713 
714   /*-
715    *********************************************************************
716    *
717    * If forced expansion is disabled, fully qualified paths are copied
718    * directly into the output buffer. However, relative paths must be
719    * expanded in any case.
720    *
721    *********************************************************************
722    */
723   if (!iForceExpansion)
724   {
725 #ifdef WIN32
726     if (
727          (iLength == 2 && isalpha((int) pcPath[0]) && pcPath[1] == ':') ||
728          (iLength >= 3 && isalpha((int) pcPath[0]) && pcPath[1] == ':' && pcPath[2] == FTIMES_SLASHCHAR)
729        )
730     {
731       strncpy(pcFullPath, pcPath, iFullPathSize);
732       return ER_OK;
733     }
734 #endif
735 #ifdef UNIX
736     if (pcPath[0] == FTIMES_SLASHCHAR)
737     {
738       strncpy(pcFullPath, pcPath, iFullPathSize);
739       return ER_OK;
740     }
741 #endif
742   }
743 
744   switch (SupportGetFileType(pcPath, acLocalError))
745   {
746   case FTIMES_FILETYPE_ERROR:
747     snprintf(pcError, MESSAGE_SIZE, "%s: File = [%s]: %s", acRoutine, pcPath, acLocalError);
748     return ER_BadValue;
749     break;
750 
751   case FTIMES_FILETYPE_DIRECTORY:
752     iError = SupportExpandDirectoryPath(pcPath, pcFullPath, iFullPathSize, acLocalError);
753     if (iError != ER_OK)
754     {
755       snprintf(pcError, MESSAGE_SIZE, "%s: Path = [%s]: %s", acRoutine, pcPath, acLocalError);
756       return iError;
757     }
758     break;
759 
760   default:
761     /*-
762      *******************************************************************
763      *
764      * Create a working copy of the input path.
765      *
766      *******************************************************************
767      */
768     pcTempFile = malloc(iLength + 1); /* +1 for a NULL */
769     if (pcTempFile == NULL)
770     {
771       snprintf(pcError, MESSAGE_SIZE, "%s: File = [%s]: %s", acRoutine, pcPath, strerror(errno));
772       return ER_BadHandle;
773     }
774 
775     pcTempPath = malloc(iLength + 1); /* +1 for a NULL */
776     if (pcTempPath == NULL)
777     {
778       snprintf(pcError, MESSAGE_SIZE, "%s: File = [%s]: %s", acRoutine, pcPath, strerror(errno));
779       free(pcTempFile);
780       return ER_BadHandle;
781     }
782 
783     strcpy(pcTempPath, pcPath);
784 
785     /*-
786      *******************************************************************
787      *
788      * Scan backwards looking for a directory separator. If found, note
789      * the location.
790      *
791      *******************************************************************
792      */
793     while (iTempLength > 0)
794     {
795       if (pcTempPath[iTempLength - 1] == FTIMES_SLASHCHAR)
796       {
797         break;
798       }
799       iTempLength--;
800     }
801 
802     /*-
803      *******************************************************************
804      *
805      * Copy off the filename portion of the path. It will be referenced
806      * during construction of the full path. Insert a null after the
807      * last directory separator to terminate the directory portion of
808      * the path.
809      *
810      *******************************************************************
811      */
812     strcpy(pcTempFile, &pcTempPath[iTempLength]);
813     pcTempPath[iTempLength] = 0;
814 
815     /*-
816      *******************************************************************
817      *
818      * Expand the directory path. If length is zero, a valid directory
819      * separator was not found. In that case, expand cwd (i.e. FTIMES_DOT).
820      *
821      *******************************************************************
822      */
823     if (iTempLength == 0)
824     {
825       iError = SupportExpandDirectoryPath(FTIMES_DOT, pcFullPath, iFullPathSize, acLocalError);
826     }
827     else
828     {
829       iError = SupportExpandDirectoryPath(pcTempPath, pcFullPath, iFullPathSize, acLocalError);
830     }
831     if (iError != ER_OK)
832     {
833       snprintf(pcError, MESSAGE_SIZE, "%s: File = [%s]: %s", acRoutine, pcPath, acLocalError);
834       free(pcTempPath);
835       free(pcTempFile);
836       return iError;
837     }
838 
839     /*-
840      *******************************************************************
841      *
842      * Construct the full path. Abort, if the size limit is exceeded.
843      *
844      *******************************************************************
845      */
846     iLength = strlen(pcFullPath) + strlen(FTIMES_SLASH) + strlen(pcTempFile);
847     if (iLength > iFullPathSize - 1)
848     {
849       snprintf(pcError, MESSAGE_SIZE, "%s: File = [%s]: Length (%d) exceeds %d bytes.", acRoutine, pcPath, iLength, iFullPathSize - 1);
850       free(pcTempPath);
851       free(pcTempFile);
852       return ER_Length;
853     }
854     strcat(pcFullPath, FTIMES_SLASH);
855     strcat(pcFullPath, pcTempFile);
856 
857     free(pcTempPath);
858     free(pcTempFile);
859 
860     break;
861   }
862 
863   return ER_OK;
864 }
865 
866 
867 /*-
868  ***********************************************************************
869  *
870  * SupportFreeData
871  *
872  ***********************************************************************
873  */
874 void
SupportFreeData(void * pcData)875 SupportFreeData(void *pcData)
876 {
877   if (pcData != NULL)
878   {
879     free(pcData);
880   }
881 }
882 
883 
884 /*-
885  ***********************************************************************
886  *
887  * SupportFreeListItem
888  *
889  ***********************************************************************
890  */
891 void
SupportFreeListItem(FILE_LIST * psItem)892 SupportFreeListItem(FILE_LIST *psItem)
893 {
894   if (psItem != NULL)
895   {
896     if (psItem->pcRegularPath != NULL)
897     {
898       free(psItem->pcRegularPath);
899     }
900     if (psItem->pcEncodedPath != NULL)
901     {
902       free(psItem->pcEncodedPath);
903     }
904     free(psItem);
905   }
906 }
907 
908 
909 /*-
910  ***********************************************************************
911  *
912  * SupportGetFileHandle
913  *
914  ***********************************************************************
915  */
916 FILE *
SupportGetFileHandle(char * pcFile,char * pcError)917 SupportGetFileHandle(char *pcFile, char *pcError)
918 {
919   const char          acRoutine[] = "SupportGetFileHandle()";
920   static int          iStdinTaken = 0;
921   FILE               *pFile = NULL;
922 
923   /*-
924    *********************************************************************
925    *
926    * Open the specified file. If "-" was specified, bind the handle to
927    * stdin, but do not do this more than once per invocation.
928    *
929    *********************************************************************
930    */
931   if (strcmp(pcFile, "-") == 0 && iStdinTaken == 0)
932   {
933     pFile = stdin;
934     iStdinTaken = 1;
935   }
936   else
937   {
938     pFile = fopen(pcFile, "rb");
939     if (pFile == NULL)
940     {
941       snprintf(pcError, MESSAGE_SIZE, "%s: fopen(): %s", acRoutine, strerror(errno));
942       return NULL;
943     }
944   }
945 
946   return pFile;
947 }
948 
949 
950 /*-
951  ***********************************************************************
952  *
953  * SupportGetFileType
954  *
955  ***********************************************************************
956  */
957 int
SupportGetFileType(char * pcPath,char * pcError)958 SupportGetFileType(char *pcPath, char *pcError)
959 {
960   const char          acRoutine[] = "SupportGetFileType()";
961   struct stat         sStatEntry;
962 
963 #ifdef UNIX
964   if (lstat(pcPath, &sStatEntry) == ER)
965   {
966     snprintf(pcError, MESSAGE_SIZE, "%s: lstat(): %s", acRoutine, strerror(errno));
967     return FTIMES_FILETYPE_ERROR;
968   }
969 
970   switch (sStatEntry.st_mode & S_IFMT)
971   {
972   case S_IFBLK:
973     return FTIMES_FILETYPE_BLOCK;
974     break;
975   case S_IFCHR:
976     return FTIMES_FILETYPE_CHARACTER;
977     break;
978   case S_IFDIR:
979     return FTIMES_FILETYPE_DIRECTORY;
980     break;
981 #ifdef S_IFDOOR
982   case S_IFDOOR:
983     return FTIMES_FILETYPE_DOOR;
984     break;
985 #endif
986   case S_IFIFO:
987     return FTIMES_FILETYPE_FIFO;
988     break;
989   case S_IFLNK:
990     return FTIMES_FILETYPE_LINK;
991     break;
992   case S_IFREG:
993     return FTIMES_FILETYPE_REGULAR;
994     break;
995   case S_IFSOCK:
996     return FTIMES_FILETYPE_SOCKET;
997     break;
998 #ifdef S_IFWHT
999   case S_IFWHT:
1000     return FTIMES_FILETYPE_WHITEOUT;
1001     break;
1002 #endif
1003   default:
1004     return FTIMES_FILETYPE_UNKNOWN;
1005     break;
1006   }
1007 #endif
1008 
1009 #ifdef WIN32
1010   char                acWorkingPath[4];
1011 
1012   if ((isalpha((int) pcPath[0]) && pcPath[1] == ':' && pcPath[2] == 0))
1013   {
1014     acWorkingPath[0] = pcPath[0];
1015     acWorkingPath[1] = pcPath[1];
1016     acWorkingPath[2] = FTIMES_SLASHCHAR;
1017     acWorkingPath[3] = 0;
1018 
1019     if (stat(acWorkingPath, &sStatEntry) == ER)
1020     {
1021       snprintf(pcError, MESSAGE_SIZE, "%s: stat(): %s", acRoutine, strerror(errno));
1022       return FTIMES_FILETYPE_ERROR;
1023     }
1024   }
1025   else
1026   {
1027     if (stat(pcPath, &sStatEntry) == ER)
1028     {
1029       snprintf(pcError, MESSAGE_SIZE, "%s: stat(): %s", acRoutine, strerror(errno));
1030       return FTIMES_FILETYPE_ERROR;
1031     }
1032   }
1033 
1034   switch (sStatEntry.st_mode & _S_IFMT)
1035   {
1036   case _S_IFCHR:
1037     return FTIMES_FILETYPE_CHARACTER;
1038     break;
1039   case _S_IFDIR:
1040     return FTIMES_FILETYPE_DIRECTORY;
1041     break;
1042   case _S_IFIFO:
1043     return FTIMES_FILETYPE_FIFO;
1044     break;
1045   case _S_IFREG:
1046     return FTIMES_FILETYPE_REGULAR;
1047     break;
1048   default:
1049     return FTIMES_FILETYPE_UNKNOWN;
1050     break;
1051   }
1052 #endif
1053 }
1054 
1055 
1056 /*-
1057  ***********************************************************************
1058  *
1059  * SupportGetHostname
1060  *
1061  ***********************************************************************
1062  */
1063 char *
SupportGetHostname(void)1064 SupportGetHostname(void)
1065 {
1066 #define MAX_HOSTNAME_LENGTH 256
1067   static char         acHostname[MAX_HOSTNAME_LENGTH] = "NA";
1068 #ifdef UNIX
1069   struct utsname      sUTSName;
1070 
1071   memset(&sUTSName, 0, sizeof(struct utsname));
1072   if (uname(&sUTSName) != -1)
1073   {
1074     snprintf(acHostname, MAX_HOSTNAME_LENGTH, "%s", (sUTSName.nodename[0]) ? sUTSName.nodename : "NA");
1075   }
1076 #endif
1077 #ifdef WIN32
1078   char                acTempname[MAX_HOSTNAME_LENGTH];
1079   DWORD               dwTempNameLength = sizeof(acTempname);
1080 
1081   if (GetComputerName(acTempname, &dwTempNameLength) == TRUE)
1082   {
1083     snprintf(acHostname, MAX_HOSTNAME_LENGTH, "%s", acTempname);
1084   }
1085 #endif
1086   return acHostname;
1087 }
1088 
1089 
1090 /*-
1091  ***********************************************************************
1092  *
1093  * SupportGetSystemOS
1094  *
1095  ***********************************************************************
1096  */
1097 char *
SupportGetSystemOS(void)1098 SupportGetSystemOS(void)
1099 {
1100 #define MAX_SYSTEMOS_LENGTH 256
1101   static char         acSystemOS[MAX_SYSTEMOS_LENGTH] = "NA";
1102 #ifdef UNIX
1103   struct utsname      sUTSName;
1104 
1105   memset(&sUTSName, 0, sizeof(struct utsname));
1106   if (uname(&sUTSName) != -1)
1107   {
1108 #ifdef FTimes_AIX
1109     snprintf(acSystemOS, MAX_SYSTEMOS_LENGTH, "%s %s %s.%s", sUTSName.machine, sUTSName.sysname, sUTSName.version, sUTSName.release);
1110 #else
1111     snprintf(acSystemOS, MAX_SYSTEMOS_LENGTH, "%s %s %s", sUTSName.machine, sUTSName.sysname, sUTSName.release);
1112 #endif
1113   }
1114 #endif
1115 #ifdef WIN32
1116   char                acOS[16];
1117   char                acPlatform[16];
1118   OSVERSIONINFO       sOSVersionInfo;
1119   SYSTEM_INFO         sSystemInfo;
1120 
1121   memset(&sSystemInfo, 0, sizeof(SYSTEM_INFO));
1122   GetSystemInfo(&sSystemInfo);
1123   switch (sSystemInfo.wProcessorArchitecture)
1124   {
1125   case PROCESSOR_ARCHITECTURE_INTEL:
1126     strncpy(acPlatform, "INTEL", sizeof(acPlatform));
1127     break;
1128   case PROCESSOR_ARCHITECTURE_MIPS:
1129     strncpy(acPlatform, "MIPS", sizeof(acPlatform));
1130     break;
1131   case PROCESSOR_ARCHITECTURE_ALPHA:
1132     strncpy(acPlatform, "ALPHA", sizeof(acPlatform));
1133     break;
1134   case PROCESSOR_ARCHITECTURE_PPC:
1135     strncpy(acPlatform, "PPC", sizeof(acPlatform));
1136     break;
1137   default:
1138     strncpy(acPlatform, "NA", sizeof(acPlatform));
1139     break;
1140   }
1141 
1142   memset(&sOSVersionInfo, 0, sizeof(OSVERSIONINFO));
1143   sOSVersionInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
1144   if (GetVersionEx(&sOSVersionInfo) == TRUE)
1145   {
1146     switch (sOSVersionInfo.dwPlatformId)
1147     {
1148     case VER_PLATFORM_WIN32s:
1149       strncpy(acOS, "Windows 3.1", sizeof(acOS));
1150       break;
1151     case VER_PLATFORM_WIN32_WINDOWS:
1152       strncpy(acOS, "Windows 98", sizeof(acOS));
1153       break;
1154     case VER_PLATFORM_WIN32_NT:
1155       strncpy(acOS, "Windows NT", sizeof(acOS));
1156       break;
1157     default:
1158       strncpy(acOS, "NA", sizeof(acOS));
1159       break;
1160     }
1161 
1162     snprintf(
1163               acSystemOS, MAX_SYSTEMOS_LENGTH, "%s %s %u.%u Build %u %s",
1164               acPlatform,
1165               acOS,
1166               sOSVersionInfo.dwMajorVersion,
1167               sOSVersionInfo.dwMinorVersion,
1168               sOSVersionInfo.dwBuildNumber,
1169               sOSVersionInfo.szCSDVersion
1170             );
1171   }
1172 #endif
1173   return acSystemOS;
1174 }
1175 
1176 
1177 /*-
1178  ***********************************************************************
1179  *
1180  * SupportIncludeEverything
1181  *
1182  ***********************************************************************
1183  */
1184 FILE_LIST *
SupportIncludeEverything(char * pcError)1185 SupportIncludeEverything(char *pcError)
1186 {
1187   const char          acRoutine[] = "SupportIncludeEverything()";
1188   char                acLocalError[MESSAGE_SIZE] = "";
1189   FILE_LIST          *psHead = NULL;
1190   FILE_LIST          *psItem = NULL;
1191 #ifdef WIN32
1192   char                acDriveList[26 * 4 + 2];
1193   char               *pcDrive;
1194   int                 iLength;
1195   int                 iTempLength;
1196 #endif
1197 
1198 #ifdef WIN32
1199   if (GetLogicalDriveStrings(26 * 4 + 2, acDriveList) == 0)
1200   {
1201     snprintf(pcError, MESSAGE_SIZE, "%s: GetLogicalDriveStrings: %u", acRoutine, GetLastError());
1202     return NULL;
1203   }
1204 
1205   /*-
1206    *******************************************************************
1207    *
1208    * Strip off backslash characters, and add the drive to the Include
1209    * list. Remember the total length of the original drive as it is
1210    * needed to update pcDrive next time through the loop.
1211    *
1212    *******************************************************************
1213    */
1214   for (pcDrive = acDriveList; *pcDrive; pcDrive += iLength + 1)
1215   {
1216     iLength = iTempLength = strlen(pcDrive);
1217 //THIS CHANGE IS PART OF EXTENDED PREFIX SUPPORT (\\?\)
1218 //    while (pcDrive[iTempLength - 1] == FTIMES_SLASHCHAR)
1219 //    {
1220 //      pcDrive[--iTempLength] = 0;
1221 //    }
1222 //END (\\?\)
1223 
1224     /*-
1225      *******************************************************************
1226      *
1227      * Allocate and initialize a new list item.
1228      *
1229      *******************************************************************
1230      */
1231     psItem = SupportNewListItem(pcDrive, FILE_LIST_REGULAR, acLocalError);
1232     if (psItem == NULL)
1233     {
1234       snprintf(pcError, MESSAGE_SIZE, "%s: Include = [%s]: %s", acRoutine, FTIMES_ROOT_PATH, acLocalError);
1235       return NULL;
1236     }
1237 
1238     /*-
1239      *******************************************************************
1240      *
1241      * Now, add it to the list.
1242      *
1243      *******************************************************************
1244      */
1245     psHead = SupportAddListItem(psItem, psHead, acLocalError);
1246     if (psHead == NULL)
1247     {
1248       snprintf(pcError, MESSAGE_SIZE, "%s: Include = [%s]: %s", acRoutine, pcDrive, acLocalError);
1249       SupportFreeListItem(psItem);
1250       return NULL;
1251     }
1252   }
1253 
1254   if (psHead == NULL)
1255   {
1256     snprintf(pcError, MESSAGE_SIZE, "%s: No supported drives found.", acRoutine);
1257   }
1258 #else
1259   /*-
1260    *********************************************************************
1261    *
1262    * Allocate and initialize a new list item.
1263    *
1264    *********************************************************************
1265    */
1266   psItem = SupportNewListItem(FTIMES_ROOT_PATH, FILE_LIST_REGULAR, acLocalError);
1267   if (psItem == NULL)
1268   {
1269     snprintf(pcError, MESSAGE_SIZE, "%s: Include = [%s]: %s", acRoutine, FTIMES_ROOT_PATH, acLocalError);
1270     return NULL;
1271   }
1272 
1273   /*-
1274    *********************************************************************
1275    *
1276    * Now, add it to the list.
1277    *
1278    *********************************************************************
1279    */
1280   psHead = SupportAddListItem(psItem, psHead, acLocalError);
1281   if (psHead == NULL)
1282   {
1283     snprintf(pcError, MESSAGE_SIZE, "%s: Include = [%s]: %s", acRoutine, FTIMES_ROOT_PATH, acLocalError);
1284     SupportFreeListItem(psItem);
1285     return NULL;
1286   }
1287 #endif
1288 
1289   return psHead;
1290 }
1291 
1292 
1293 /*-
1294  ***********************************************************************
1295  *
1296  * SupportMakeName
1297  *
1298  ***********************************************************************
1299  */
1300 int
SupportMakeName(char * pcDir,char * pcBaseName,char * pcBaseNameSuffix,char * pcExtension,char * pcFilename,char * pcError)1301 SupportMakeName(char *pcDir, char *pcBaseName, char *pcBaseNameSuffix, char *pcExtension, char *pcFilename, char *pcError)
1302 {
1303   const char          acRoutine[] = "SupportMakeName()";
1304   int                 iLength;
1305 
1306   iLength  = strlen(pcDir);
1307   iLength += 1; /* FTIMES_SLASH */
1308   iLength += strlen(pcBaseName);
1309   iLength += (pcBaseNameSuffix[0] != 0) ? 1 : 0; /* "_" */
1310   iLength += strlen(pcBaseNameSuffix);
1311   iLength += strlen(pcExtension);
1312 
1313   if (iLength > FTIMES_MAX_PATH - 1)
1314   {
1315     snprintf(pcError, MESSAGE_SIZE, "%s: Length (%d) exceeds %d bytes", acRoutine, iLength, (FTIMES_MAX_PATH - 1));
1316     return ER_Length;
1317   }
1318   snprintf(pcFilename, FTIMES_MAX_PATH, "%s%s%s%s%s%s",
1319     pcDir,
1320     FTIMES_SLASH,
1321     pcBaseName,
1322     (pcBaseNameSuffix[0] != 0) ? "_" : "",
1323     pcBaseNameSuffix,
1324     pcExtension
1325     );
1326 
1327   return ER_OK;
1328 }
1329 
1330 
1331 /*-
1332  ***********************************************************************
1333  *
1334  * SupportMatchExclude
1335  *
1336  ***********************************************************************
1337  */
1338 FILE_LIST *
SupportMatchExclude(FILE_LIST * psHead,char * pcPath)1339 SupportMatchExclude(FILE_LIST *psHead, char *pcPath)
1340 {
1341   FILE_LIST          *psList;
1342 
1343   for (psList = psHead; psList != NULL; psList = psList->psNext)
1344   {
1345     if (CompareFunction(psList->pcRegularPath, pcPath) == 0)
1346     {
1347       return psList;
1348     }
1349   }
1350   return NULL;
1351 }
1352 
1353 
1354 /*-
1355  ***********************************************************************
1356  *
1357  * SupportMatchSubTree
1358  *
1359  ***********************************************************************
1360  */
1361 FILE_LIST *
SupportMatchSubTree(FILE_LIST * psHead,FILE_LIST * psTarget)1362 SupportMatchSubTree(FILE_LIST *psHead, FILE_LIST *psTarget)
1363 {
1364   int                 x;
1365   int                 y;
1366   FILE_LIST          *psList;
1367 
1368   x = psTarget->iLength;
1369   for (psList = psHead; psList != NULL; psList = psList->psNext)
1370   {
1371     y = psList->iLength;
1372     if (NCompareFunction(psTarget->pcRegularPath, psList->pcRegularPath, MIN(x, y)) == 0)
1373     {
1374       if (x <= y)
1375       {
1376         if ((psList->pcRegularPath[x - 1] == FTIMES_SLASHCHAR && psList->pcRegularPath[x] != FTIMES_SLASHCHAR) ||
1377             (psList->pcRegularPath[x - 1] != FTIMES_SLASHCHAR && psList->pcRegularPath[x] == FTIMES_SLASHCHAR) ||
1378             (psList->pcRegularPath[x - 1] == FTIMES_SLASHCHAR && psList->pcRegularPath[x] == FTIMES_SLASHCHAR))
1379         {
1380           return psList;
1381         }
1382       }
1383       else
1384       {
1385         if ((psTarget->pcRegularPath[y - 1] == FTIMES_SLASHCHAR && psTarget->pcRegularPath[y] != FTIMES_SLASHCHAR) ||
1386             (psTarget->pcRegularPath[y - 1] != FTIMES_SLASHCHAR && psTarget->pcRegularPath[y] == FTIMES_SLASHCHAR) ||
1387             (psTarget->pcRegularPath[y - 1] == FTIMES_SLASHCHAR && psTarget->pcRegularPath[y] == FTIMES_SLASHCHAR))
1388         {
1389           return psTarget;
1390         }
1391       }
1392     }
1393   }
1394   return NULL;
1395 }
1396 
1397 
1398 /*-
1399  ***********************************************************************
1400  *
1401  * SupportNeuterString
1402  *
1403  ***********************************************************************
1404  */
1405 char *
SupportNeuterString(char * pcData,int iLength,char * pcError)1406 SupportNeuterString(char *pcData, int iLength, char *pcError)
1407 {
1408   const char          acRoutine[] = "SupportNeuterString()";
1409   char               *pcNeutered;
1410   int                 i;
1411   int                 n;
1412 
1413   /*-
1414    *********************************************************************
1415    *
1416    * The caller is expected to free this memory.
1417    *
1418    *********************************************************************
1419    */
1420   pcNeutered = malloc((3 * iLength) + 1);
1421   if (pcNeutered == NULL)
1422   {
1423     snprintf(pcError, MESSAGE_SIZE, "%s: %s", acRoutine, strerror(errno));
1424     return NULL;
1425   }
1426   pcNeutered[0] = 0;
1427 
1428   /*-
1429    *********************************************************************
1430    *
1431    * Neuter non-printables and [|"'`%+#]. Convert spaces to '+'. Avoid
1432    * isprint() here because it has led to unexpected results on Windows
1433    * platforms. In the past, isprint() on certain Windows systems has
1434    * decided that several characters in the range 0x7f - 0xff are
1435    * printable.
1436    *
1437    *********************************************************************
1438    */
1439   for (i = n = 0; i < iLength; i++)
1440   {
1441     if (pcData[i] > '~' || pcData[i] < ' ')
1442     {
1443       n += sprintf(&pcNeutered[n], "%%%02x", (unsigned char) pcData[i]);
1444     }
1445     else
1446     {
1447       switch (pcData[i])
1448       {
1449       case '|':
1450       case '"':
1451       case '\'':
1452       case '`':
1453       case '%':
1454       case '+':
1455       case '#':
1456         n += sprintf(&pcNeutered[n], "%%%02x", (unsigned char) pcData[i]);
1457         break;
1458       case ' ':
1459         pcNeutered[n++] = '+';
1460         break;
1461       default:
1462         pcNeutered[n++] = pcData[i];
1463         break;
1464       }
1465     }
1466   }
1467   pcNeutered[n] = 0;
1468 
1469   return pcNeutered;
1470 }
1471 
1472 
1473 /*-
1474  ***********************************************************************
1475  *
1476  * SupportPruneList
1477  *
1478  ***********************************************************************
1479  */
1480 FILE_LIST *
SupportPruneList(FILE_LIST * psList,char * pcListName)1481 SupportPruneList(FILE_LIST *psList, char *pcListName)
1482 {
1483   char                acLocalError[MESSAGE_SIZE] = "";
1484   FILE_LIST          *psListHead;
1485   FILE_LIST          *psListTree;
1486   FILE_LIST          *psListKill;
1487 
1488   /*-
1489    *********************************************************************
1490    *
1491    * If there's nothing to prune, just return.
1492    *
1493    *********************************************************************
1494    */
1495   if (psList == NULL)
1496   {
1497     return psList;
1498   }
1499 
1500   /*-
1501    *********************************************************************
1502    *
1503    * Eliminate any subtree components from the tree list. For example,
1504    * /usr/local would be eliminated from /usr. We also eliminate any
1505    * duplicate entries in the process. However, there should not be any
1506    * duplicates because they should have been automatically pruned as
1507    * the list was being created.
1508    *
1509    *********************************************************************
1510    */
1511   for (psListTree = psListHead = psList; psListTree->psNext != NULL;)
1512   {
1513     psListKill = SupportMatchSubTree(psListTree->psNext, psListTree);
1514 
1515     /*-
1516      *******************************************************************
1517      *
1518      * When a match is found, another search is performed to ensure
1519      * that there are no more matches further down in the list. This is
1520      * done implicitly by setting psListTree to psListHead after the
1521      * drop.
1522      *
1523      *******************************************************************
1524      */
1525     if (psListKill != NULL)
1526     {
1527       snprintf(acLocalError, MESSAGE_SIZE, "List = [%s], NeuteredItem = [%s]: Pruning item because it is part of a larger branch.", pcListName, psListKill->pcEncodedPath);
1528       ErrorHandler(ER_Warning, acLocalError, ERROR_WARNING);
1529       psListHead = SupportDropListItem(psListHead, psListKill);
1530       psListTree = psListHead;
1531     }
1532     else
1533     {
1534       psListTree = psListTree->psNext;
1535     }
1536   }
1537   return psListHead;
1538 }
1539 
1540 
1541 /*-
1542  ***********************************************************************
1543  *
1544  * SupportRequirePrivilege
1545  *
1546  ***********************************************************************
1547  */
1548 int
SupportRequirePrivilege(char * pcError)1549 SupportRequirePrivilege(char *pcError)
1550 {
1551   const char          acRoutine[] = "SupportRequirePrivilege()";
1552 
1553 #ifdef WINNT
1554   char                acLocalError[MESSAGE_SIZE] = "";
1555 
1556   if (SupportSetPrivileges(acLocalError) != ER_OK)
1557   {
1558     snprintf(pcError, MESSAGE_SIZE, "%s: %s", acRoutine, acLocalError);
1559     return ER;
1560   }
1561 #endif
1562 
1563 #ifdef UNIX
1564   if (getuid() != 0)
1565   {
1566     snprintf(pcError, MESSAGE_SIZE, "%s: Need root privilege to continue.", acRoutine);
1567     return ER;
1568   }
1569 #endif
1570 
1571   return ER_OK;
1572 }
1573 
1574 
1575 /*-
1576  ***********************************************************************
1577  *
1578  * SupportSetLogLevel
1579  *
1580  ***********************************************************************
1581  */
1582 int
SupportSetLogLevel(char * pcLevel,int * piLevel,char * pcError)1583 SupportSetLogLevel(char *pcLevel, int *piLevel, char *pcError)
1584 {
1585   if ((int) strlen(pcLevel) != 1)
1586   {
1587     snprintf(pcError, MESSAGE_SIZE, "Level must be %d-%d.", MESSAGE_DEBUGGER, MESSAGE_CRITICAL);
1588     return ER_Length;
1589   }
1590   switch (pcLevel[0])
1591   {
1592   case '0':
1593   case '1':
1594   case '2':
1595   case '3':
1596   case '4':
1597   case '5':
1598   case '6':
1599     *piLevel = pcLevel[0] - '0';
1600     MessageSetLogLevel(*piLevel);
1601     break;
1602   default:
1603     snprintf(pcError, MESSAGE_SIZE, "Level must be %d-%d.", MESSAGE_DEBUGGER, MESSAGE_CRITICAL);
1604     return ER_Length;
1605     break;
1606   }
1607 
1608   return ER_OK;
1609 }
1610 
1611 
1612 /*-
1613  ***********************************************************************
1614  *
1615  * SupportSetPriority
1616  *
1617  ***********************************************************************
1618  */
1619 int
SupportSetPriority(FTIMES_PROPERTIES * psProperties,char * pcError)1620 SupportSetPriority(FTIMES_PROPERTIES *psProperties, char *pcError)
1621 {
1622   const char          acRoutine[] = "SupportSetPriority()";
1623   char               *pcPriority = NULL;
1624   int                 i = 0;
1625 #ifndef WIN32
1626   int                 iError = 0;
1627 #endif
1628 
1629   /*-
1630    *********************************************************************
1631    *
1632    * A priority specified in the environment trumps one specified in a
1633    * config file.
1634    *
1635    *********************************************************************
1636    */
1637   pcPriority = FTimesGetEnvValue("FTIMES_PRIORITY");
1638   if (pcPriority && strlen(pcPriority) < FTIMES_MAX_PRIORITY_LENGTH)
1639   {
1640     strncpy(psProperties->acPriority, pcPriority, FTIMES_MAX_PRIORITY_LENGTH);
1641   }
1642 
1643   /*-
1644    *********************************************************************
1645    *
1646    * If a priority was specified, convert it into a platform-specific
1647    * value and set it. Otherwise, do nothing.
1648    *
1649    *********************************************************************
1650    */
1651   if (psProperties->acPriority[0])
1652   {
1653     if (strcasecmp(psProperties->acPriority, "LOW") == 0)
1654     {
1655       psProperties->iPriority = FTIMES_PRIORITY_LOW;
1656     }
1657     else if (strcasecmp(psProperties->acPriority, "BELOW_NORMAL") == 0)
1658     {
1659       psProperties->iPriority = FTIMES_PRIORITY_BELOW_NORMAL;
1660     }
1661     else if (strcasecmp(psProperties->acPriority, "NORMAL") == 0)
1662     {
1663       psProperties->iPriority = FTIMES_PRIORITY_NORMAL;
1664     }
1665     else if (strcasecmp(psProperties->acPriority, "ABOVE_NORMAL") == 0)
1666     {
1667       psProperties->iPriority = FTIMES_PRIORITY_ABOVE_NORMAL;
1668     }
1669     else if (strcasecmp(psProperties->acPriority, "HIGH") == 0)
1670     {
1671       psProperties->iPriority = FTIMES_PRIORITY_HIGH;
1672     }
1673     else
1674     {
1675       snprintf(pcError, MESSAGE_SIZE, "%s: Priority (%s) must be one of [low|below_normal|normal|above_normal|high].", acRoutine, psProperties->acPriority);
1676       return ER;
1677     }
1678     for (i = 0; i < (int) strlen(psProperties->acPriority); i++)
1679     {
1680       psProperties->acPriority[i] = tolower(psProperties->acPriority[i]);
1681     }
1682 #ifdef WIN32
1683     if (!SetPriorityClass(GetCurrentProcess(), (DWORD) psProperties->iPriority))
1684     {
1685       char *pcMessage = NULL;
1686       ErrorFormatWinxError(GetLastError(), &pcMessage);
1687       snprintf(pcError, MESSAGE_SIZE, "%s: SetPriorityClass(): %s", acRoutine, pcMessage);
1688       return ER;
1689     }
1690 #else
1691     iError = setpriority(PRIO_PROCESS, 0, psProperties->iPriority);
1692     if (iError == -1)
1693     {
1694       snprintf(pcError, MESSAGE_SIZE, "%s: setpriority(): %s", acRoutine, strerror(errno));
1695       return ER;
1696     }
1697 #endif
1698   }
1699 
1700   return ER_OK;
1701 }
1702 
1703 
1704 #ifdef WINNT
1705 /*-
1706  ***********************************************************************
1707  *
1708  * SupportSetPrivileges
1709  *
1710  ***********************************************************************
1711  */
1712 int
SupportSetPrivileges(char * pcError)1713 SupportSetPrivileges(char *pcError)
1714 {
1715   const char          acRoutine[] = "SupportSetPrivileges()";
1716 
1717   /*-
1718    *********************************************************************
1719    *
1720    * Attempt to obtain backup user rights.
1721    *
1722    *********************************************************************
1723    */
1724   if (SupportAdjustPrivileges(SE_BACKUP_NAME) == FALSE)
1725   {
1726     snprintf(pcError, MESSAGE_SIZE, "%s: Can't set SE_BACKUP_NAME privilege.", acRoutine);
1727     return ER;
1728   }
1729 
1730   /*-
1731    *********************************************************************
1732    *
1733    * Attempt to obtain restore user rights.
1734    *
1735    *********************************************************************
1736    */
1737   if (SupportAdjustPrivileges(SE_RESTORE_NAME) == FALSE)
1738   {
1739     snprintf(pcError, MESSAGE_SIZE, "%s: Can't set SE_RESTORE_NAME privilege.", acRoutine);
1740     return ER;
1741   }
1742 
1743   return ER_OK;
1744 }
1745 #endif
1746 
1747 
1748 /*-
1749  ***********************************************************************
1750  *
1751  * SupportStringToUInt64
1752  *
1753  ***********************************************************************
1754  */
1755 int
SupportStringToUInt64(char * pcData,APP_UI64 * pui64Value,char * pcError)1756 SupportStringToUInt64(char *pcData, APP_UI64 *pui64Value, char *pcError)
1757 {
1758   const char          acRoutine[] = "SupportStringTo64BitDecimal()";
1759   int                 i = 0;
1760   int                 iLength = 0;
1761   APP_UI64            ui64Value = 0;
1762   APP_UI64            ui64Multiplier = 1;
1763 
1764   iLength = strlen(pcData);
1765 
1766 #define SUPPORT_MAX_64BIT_NUMBER_SIZE 20 /* strlen("18446744073709551615") */
1767   if (iLength < 1 || iLength > SUPPORT_MAX_64BIT_NUMBER_SIZE)
1768   {
1769     snprintf(pcError, MESSAGE_SIZE, "%s: The specified number (%s) is either too small or too large to be a valid 64-bit value.", acRoutine, pcData);
1770     return ER;
1771   }
1772 
1773   for (i = iLength - 1; i >= 0; i--)
1774   {
1775     switch ((int) pcData[i])
1776     {
1777     case '0': case '1': case '2': case '3': case '4':
1778     case '5': case '6': case '7': case '8': case '9':
1779       ui64Value += ((int) pcData[i] - 0x30) * ui64Multiplier;
1780       ui64Multiplier *= 10;
1781       break;
1782     default:
1783       snprintf(pcError, MESSAGE_SIZE, "%s: The specified number (%s) contains one or more invalid digits.", acRoutine, pcData);
1784       return ER;
1785       break;
1786     }
1787   }
1788   *pui64Value = ui64Value;
1789 
1790   return ER_OK;
1791 }
1792 
1793 
1794 /*-
1795  ***********************************************************************
1796  *
1797  * SupportWriteData
1798  *
1799  ***********************************************************************
1800  */
1801 int
SupportWriteData(FILE * pFile,char * pcData,int iLength,char * pcError)1802 SupportWriteData(FILE *pFile, char *pcData, int iLength, char *pcError)
1803 {
1804   const char          acRoutine[] = "SupportWriteData()";
1805   int                 iNWritten;
1806 
1807   iNWritten = fwrite(pcData, 1, iLength, pFile);
1808   if (ferror(pFile))
1809   {
1810     if (iNWritten != iLength)
1811     {
1812       snprintf(pcError, MESSAGE_SIZE, "%s: fwrite(): NWritten = [%d] != [%d]: WriteLength mismatch!: %s",
1813         acRoutine,
1814         iNWritten,
1815         iLength,
1816         (errno == 0) ? "unexpected error -- check device for sufficient space" : strerror(errno)
1817         );
1818     }
1819     else
1820     {
1821       snprintf(pcError, MESSAGE_SIZE, "%s: fwrite(): %s",
1822         acRoutine,
1823         (errno == 0) ? "unexpected error -- check device for sufficient space" : strerror(errno)
1824         );
1825     }
1826     return ER;
1827   }
1828   if (fflush(pFile) != 0)
1829   {
1830     snprintf(pcError, MESSAGE_SIZE, "%s: fflush(): %s", acRoutine, strerror(errno));
1831     return ER;
1832   }
1833 
1834   return ER_OK;
1835 }
1836 
1837 
1838 #ifdef USE_PCRE
1839 /*-
1840  ***********************************************************************
1841  *
1842  * SupportAddFilter
1843  *
1844  ***********************************************************************
1845  */
1846 int
SupportAddFilter(char * pcFilter,FILTER_LIST ** psHead,char * pcError)1847 SupportAddFilter(char *pcFilter, FILTER_LIST **psHead, char *pcError)
1848 {
1849   const char          acRoutine[] = "SupportAddFilter()";
1850   char                acLocalError[MESSAGE_SIZE] = "";
1851   FILTER_LIST        *psCurrent = NULL;
1852   FILTER_LIST        *psFilter = NULL;
1853 
1854   /*-
1855    *********************************************************************
1856    *
1857    * Allocate and initialize a new Filter.
1858    *
1859    *********************************************************************
1860    */
1861   psFilter = SupportNewFilter(pcFilter, acLocalError);
1862   if (psFilter == NULL)
1863   {
1864     snprintf(pcError, MESSAGE_SIZE, "%s: %s", acRoutine, acLocalError);
1865     return ER;
1866   }
1867 
1868   /*-
1869    *********************************************************************
1870    *
1871    * If the head is NULL, insert the new filter and return. Otherwise,
1872    * append the new filter to the end of the list.
1873    *
1874    *********************************************************************
1875    */
1876   if (*psHead == NULL)
1877   {
1878     *psHead = psFilter;
1879   }
1880   else
1881   {
1882     psCurrent = *psHead;
1883     while (psCurrent != NULL)
1884     {
1885       if (psCurrent->psNext == NULL)
1886       {
1887         psCurrent->psNext = psFilter;
1888         break;
1889       }
1890       psCurrent = psCurrent->psNext;
1891     }
1892   }
1893 
1894   return ER_OK;
1895 }
1896 
1897 
1898 /*-
1899  ***********************************************************************
1900  *
1901  * SupportFreeFilter
1902  *
1903  ***********************************************************************
1904  */
1905 void
SupportFreeFilter(FILTER_LIST * psFilter)1906 SupportFreeFilter(FILTER_LIST *psFilter)
1907 {
1908   if (psFilter != NULL)
1909   {
1910     if (psFilter->pcFilter != NULL)
1911     {
1912       free(psFilter->pcFilter);
1913     }
1914     if (psFilter->psPcre != NULL)
1915     {
1916       pcre_free(psFilter->psPcre);
1917     }
1918     if (psFilter->psPcreExtra != NULL)
1919     {
1920       pcre_free(psFilter->psPcreExtra);
1921     }
1922     free(psFilter);
1923   }
1924 }
1925 
1926 
1927 /*-
1928  ***********************************************************************
1929  *
1930  * SupportMatchFilter
1931  *
1932  ***********************************************************************
1933  */
1934 FILTER_LIST *
SupportMatchFilter(FILTER_LIST * psFilterList,char * pcPath)1935 SupportMatchFilter(FILTER_LIST *psFilterList, char *pcPath)
1936 {
1937   FILTER_LIST        *psFilter;
1938 #ifndef PCRE_OVECTOR_ARRAY_SIZE
1939 #define PCRE_OVECTOR_ARRAY_SIZE 30
1940 #endif
1941   int                 aiPcreOVector[PCRE_OVECTOR_ARRAY_SIZE];
1942   int                 iError = 0;
1943 
1944   for (psFilter = psFilterList; psFilter != NULL; psFilter = psFilter->psNext)
1945   {
1946     /*-
1947      *******************************************************************
1948      *
1949      * PCRE_NOTEMPTY is used here to squash any attempts to match
1950      * empty strings. Even though a value of zero would mean that that
1951      * there was an overflow in ovector, accept those matches as
1952      * valid. This should be OK since the elements in ovector are
1953      * ignored anyway. Note that any value less than zero is currently
1954      * being treated as if it were not a match. If that turns out to
1955      * be an issue, this routine will need to be modified such that it
1956      * can throw an error, and the upstream code will need to be
1957      * adjusted accordingly.
1958      *
1959      *******************************************************************
1960      */
1961     iError = pcre_exec(psFilter->psPcre, psFilter->psPcreExtra, pcPath, strlen(pcPath), 0, PCRE_NOTEMPTY, aiPcreOVector, PCRE_OVECTOR_ARRAY_SIZE);
1962     if (iError >= 0)
1963     {
1964       return psFilter;
1965     }
1966   }
1967 
1968   return NULL;
1969 }
1970 
1971 
1972 /*-
1973  ***********************************************************************
1974  *
1975  * SupportNewFilter
1976  *
1977  ***********************************************************************
1978  */
1979 FILTER_LIST *
SupportNewFilter(char * pcFilter,char * pcError)1980 SupportNewFilter(char *pcFilter, char *pcError)
1981 {
1982   const char          acRoutine[] = "SupportNewFilter()";
1983   const char         *pcPcreError = NULL;
1984   FILTER_LIST        *psFilter = NULL;
1985   int                 iError = 0;
1986   int                 iCaptureCount = 0;
1987   int                 iLength = 0;
1988   int                 iPcreErrorOffset = 0;
1989 
1990   /*-
1991    *********************************************************************
1992    *
1993    * Check that the input filter is not NULL and that it has length.
1994    *
1995    *********************************************************************
1996    */
1997   if (pcFilter == NULL)
1998   {
1999     snprintf(pcError, MESSAGE_SIZE, "%s: NULL input. That shouldn't happen.", acRoutine);
2000     return NULL;
2001   }
2002 
2003   iLength = strlen(pcFilter);
2004   if (iLength < 1)
2005   {
2006     snprintf(pcError, MESSAGE_SIZE, "%s: Length = [%d]: Length must be greater than zero.", acRoutine, iLength);
2007     return NULL;
2008   }
2009 
2010   /*-
2011    *********************************************************************
2012    *
2013    * Allocate memory for a new Filter. The caller should free this
2014    * memory with SupportFreeFilter().
2015    *
2016    *********************************************************************
2017    */
2018   psFilter = calloc(sizeof(FILTER_LIST), 1);
2019   if (psFilter == NULL)
2020   {
2021     snprintf(pcError, MESSAGE_SIZE, "%s: calloc(): %s", acRoutine, strerror(errno));
2022     return NULL;
2023   }
2024 
2025   psFilter->pcFilter = calloc(iLength + 1, 1);
2026   if (psFilter->pcFilter == NULL)
2027   {
2028     snprintf(pcError, MESSAGE_SIZE, "%s: calloc(): %s", acRoutine, strerror(errno));
2029     return NULL;
2030   }
2031   strncpy(psFilter->pcFilter, pcFilter, iLength);
2032 
2033   /*-
2034    *********************************************************************
2035    *
2036    * Compile and study the regular expression. Compile-time options
2037    * (?imsx) are not set here because the user can specify them as
2038    * needed in the filter.
2039    *
2040    *********************************************************************
2041    */
2042   psFilter->psPcre = pcre_compile(pcFilter, 0, &pcPcreError, &iPcreErrorOffset, NULL);
2043   if (psFilter->psPcre == NULL)
2044   {
2045     snprintf(pcError, MESSAGE_SIZE, "%s: pcre_compile(): %s", acRoutine, pcPcreError);
2046     SupportFreeFilter(psFilter);
2047     return NULL;
2048   }
2049   psFilter->psPcreExtra = pcre_study(psFilter->psPcre, 0, &pcPcreError);
2050   if (pcPcreError != NULL)
2051   {
2052     snprintf(pcError, MESSAGE_SIZE, "%s: pcre_study(): %s", acRoutine, pcPcreError);
2053     SupportFreeFilter(psFilter);
2054     return NULL;
2055   }
2056   iError = pcre_fullinfo(psFilter->psPcre, psFilter->psPcreExtra, PCRE_INFO_CAPTURECOUNT, (void *) &iCaptureCount);
2057   if (iError == ER_OK)
2058   {
2059     if (iCaptureCount > PCRE_MAX_CAPTURE_COUNT)
2060     {
2061       snprintf(pcError, MESSAGE_SIZE, "%s: Invalid capture count [%d]. The maximum number of capturing '()' subpatterns allowed is %d. Use '(?:)' if grouping is required.", acRoutine, iCaptureCount, PCRE_MAX_CAPTURE_COUNT);
2062       SupportFreeFilter(psFilter);
2063       return NULL;
2064     }
2065   }
2066   else
2067   {
2068     snprintf(pcError, MESSAGE_SIZE, "%s: pcre_fullinfo(): Unexpected return value [%d]. That shouldn't happen.", acRoutine, iError);
2069     SupportFreeFilter(psFilter);
2070     return NULL;
2071   }
2072   psFilter->psNext = NULL;
2073 
2074   return psFilter;
2075 }
2076 #endif
2077 
2078 
2079 /*-
2080  ***********************************************************************
2081  *
2082  * SupportNewListItem
2083  *
2084  ***********************************************************************
2085  */
2086 FILE_LIST *
SupportNewListItem(char * pcPath,int iType,char * pcError)2087 SupportNewListItem(char *pcPath, int iType, char *pcError)
2088 {
2089   const char          acRoutine[] = "SupportNewListItem()";
2090   char                acLocalError[MESSAGE_SIZE] = "";
2091   int                 iIndex = 0;
2092   int                 iLength = 0;
2093   FILE_LIST          *psItem = NULL;
2094 
2095   /*-
2096    *********************************************************************
2097    *
2098    * Check that the input string is not NULL and that it has length.
2099    *
2100    *********************************************************************
2101    */
2102   if (pcPath == NULL)
2103   {
2104     snprintf(pcError, MESSAGE_SIZE, "%s: NULL input. That shouldn't happen.", acRoutine);
2105     return NULL;
2106   }
2107 
2108   iLength = strlen(pcPath);
2109   if (iLength < 1)
2110   {
2111     snprintf(pcError, MESSAGE_SIZE, "%s: Length = [%d]: Length must be greater than zero.", acRoutine, iLength);
2112     return NULL;
2113   }
2114 
2115   /*-
2116    *********************************************************************
2117    *
2118    * Allocate memory for a new list item, and initialize its members.
2119    * If the path is encoded (i.e., it was specified using the
2120    * "file://" prefix), decode it. Otherwise, just copy it into place.
2121    * Once the regular path has been set, neuter it and carry it around
2122    * in the structure -- the neutered form is used in various print
2123    * statements.
2124    *
2125    *********************************************************************
2126    */
2127   psItem = calloc(sizeof(FILE_LIST), 1);
2128   if (psItem == NULL)
2129   {
2130     snprintf(pcError, MESSAGE_SIZE, "%s: calloc(): %s", acRoutine, strerror(errno));
2131     return NULL;
2132   }
2133 
2134   if (iType == FILE_LIST_ENCODED)
2135   {
2136     psItem->pcRegularPath = HttpUnEscape(pcPath, &iLength, acLocalError);
2137     if (psItem->pcRegularPath == NULL)
2138     {
2139       snprintf(pcError, MESSAGE_SIZE, "%s: %s", acRoutine, acLocalError);
2140       SupportFreeListItem(psItem);
2141       return NULL;
2142     }
2143 /* NOTE: The realloc() below is needed so we can append a NULL byte to the string. HttpUnEscape() does not do that. */
2144     psItem->pcRegularPath = realloc(psItem->pcRegularPath, iLength + 1);
2145     if (psItem->pcRegularPath == NULL)
2146     {
2147       snprintf(pcError, MESSAGE_SIZE, "%s: realloc(): %s", acRoutine, strerror(errno));
2148       SupportFreeListItem(psItem);
2149       return NULL;
2150     }
2151     psItem->pcRegularPath[iLength] = 0;
2152     psItem->iLength = iLength;
2153     for (iIndex = 0; iIndex < iLength; iIndex++)
2154     {
2155       if (psItem->pcRegularPath[iIndex] == 0)
2156       {
2157         snprintf(pcError, MESSAGE_SIZE, "%s: Path contains a NULL byte, which is not allowed.", acRoutine);
2158         SupportFreeListItem(psItem);
2159         return NULL;
2160       }
2161     }
2162   }
2163   else
2164   {
2165     psItem->pcRegularPath = calloc(iLength + 1, 1);
2166     if (psItem->pcRegularPath == NULL)
2167     {
2168       snprintf(pcError, MESSAGE_SIZE, "%s: calloc(): %s", acRoutine, strerror(errno));
2169       SupportFreeListItem(psItem);
2170       return NULL;
2171     }
2172     strncpy(psItem->pcRegularPath, pcPath, iLength + 1);
2173     psItem->iLength = iLength;
2174   }
2175 
2176   psItem->pcEncodedPath = SupportNeuterString(psItem->pcRegularPath, psItem->iLength, acLocalError);
2177   if (psItem->pcEncodedPath == NULL)
2178   {
2179     snprintf(pcError, MESSAGE_SIZE, "%s: %s", acRoutine, acLocalError);
2180     SupportFreeListItem(psItem);
2181     return NULL;
2182   }
2183 
2184   psItem->iType = iType;
2185   psItem->psNext = NULL;
2186 
2187   return psItem;
2188 }
2189