1 /*
2  * Copyright (c) 1999, 2021 Tanuki Software, Ltd.
3  * http://www.tanukisoftware.com
4  * All rights reserved.
5  *
6  * This software is the proprietary information of Tanuki Software.
7  * You shall use it only in accordance with the terms of the
8  * license agreement you entered into with Tanuki Software.
9  * http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html
10  */
11 
12 /**
13  * Author:
14  *   Tanuki Software Development Team <support@tanukisoftware.com>
15  */
16 
17 #ifdef WIN32
18  #include <errno.h>
19  #include <io.h>
20 #else
21  #include <glob.h>
22  #include <string.h>
23  #include <limits.h>
24 #endif
25 
26 #ifdef WIN32
27  #include <winsock.h>
28 #endif
29 
30 #include "logger.h"
31 #include "logger_file.h"
32 #include "wrapper_i18n.h"
33 
34 #define FILES_CHUNK 5
35 
36 #ifndef TRUE
37 #define TRUE -1
38 #endif
39 
40 #ifndef FALSE
41 #define FALSE 0
42 #endif
43 
44 /**
45  * Returns a valid sort mode given a name: "TIMES", "NAMES_ASC", "NAMES_DEC", "NAMES_SMART".
46  *  In the event of an invalid value, TIMES will be returned.
47  */
loggerFileGetSortMode(const TCHAR * modeName)48 int loggerFileGetSortMode(const TCHAR *modeName) {
49     if (strcmpIgnoreCase(modeName, TEXT("NAMES_ASC")) == 0) {
50         return LOGGER_FILE_SORT_MODE_NAMES_ASC;
51     } else if (strcmpIgnoreCase(modeName, TEXT("NAMES_DEC")) == 0) {
52         return LOGGER_FILE_SORT_MODE_NAMES_DEC;
53     } else if (strcmpIgnoreCase(modeName, TEXT("NAMES_SMART")) == 0) {
54         return LOGGER_FILE_SORT_MODE_NAMES_SMART;
55     } else {
56         return LOGGER_FILE_SORT_MODE_TIMES;
57     }
58 }
59 
60 #ifdef WIN32
sortFilesTimes(TCHAR ** files,__time64_t * fileTimes,int cnt)61 int sortFilesTimes(TCHAR **files, __time64_t *fileTimes, int cnt) {
62 #else
63 int sortFilesTimes(TCHAR **files, time_t *fileTimes, int cnt) {
64 #endif
65     int i, j;
66     TCHAR *temp;
67 #ifdef WIN32
68     __time64_t tempTime;
69 #else
70     time_t tempTime;
71 #endif
72 
73     for (i = 0; i < cnt; i++) {
74         for (j = 0; j < cnt - 1; j++) {
75             if (fileTimes[j] < fileTimes[j + 1]) {
76                 temp = files[j + 1];
77                 tempTime = fileTimes[j + 1];
78 
79                 files[j + 1] = files[j];
80                 fileTimes[j + 1] = fileTimes[j];
81 
82                 files[j] = temp;
83                 fileTimes[j] = tempTime;
84             }
85         }
86     }
87 
88     return TRUE;
89 }
90 
91 /**
92  * Compares two strings.  Returns 0 if they are equal, -1 if file1 is bigger, 1 if file2 is bigger.
93  */
94 int compareFileNames(const TCHAR *file1, const TCHAR *file2) {
95     int pos1, pos2;
96     TCHAR c1, c2;
97     int numeric1, numeric2;
98     long int num1, num2;
99     int afterNumber = FALSE;
100 
101     pos1 = 0;
102     pos2 = 0;
103 
104     while (TRUE) {
105         c1 = file1[pos1];
106         c2 = file2[pos2];
107         /*printf("     file1[%d]=%d, file2[%d]=%d\n", pos1, c1, pos2, c2);*/
108 
109         /* Did we find the null. */
110         if (c1 == 0) {
111             if (c2 == 0) {
112                 return 0;
113             } else {
114                 return 1;
115             }
116         } else {
117             if (c2 == 0) {
118                 return -1;
119             } else {
120                 /* Continue. */
121             }
122         }
123 
124         /* We have two characters. */
125         numeric1 = ((c1 >= TEXT('0')) && (c1 <= TEXT('9')));
126         numeric2 = ((c2 >= TEXT('0')) && (c2 <= TEXT('9')));
127 
128         /* See if one or both of the strings is numeric. */
129         if (numeric1) {
130             if (numeric2) {
131                 /* Both are numeric, we need to start comparing the two file names as integer values. */
132                 num1 = c1 - TEXT('0');
133                 c1 = file1[pos1 + 1];
134                 while ((c1 >= TEXT('0')) && (c1 <= TEXT('9'))) {
135                     num1 = num1 * 10 + (c1 - TEXT('0'));
136                     pos1++;
137                     c1 = file1[pos1 + 1];
138                 }
139 
140                 num2 = c2 - TEXT('0');
141                 c2 = file2[pos2 + 1];
142                 while ((c2 >= TEXT('0')) && (c2 <= TEXT('9'))) {
143                     num2 = num2 * 10 + (c2 - TEXT('0'));
144                     pos2++;
145                     c2 = file2[pos2 + 1];
146                 }
147 
148                 /*printf("     num1=%ld, num2=%ld\n", num1, num2);*/
149                 if (num1 > num2) {
150                     return -1;
151                 } else if (num2 > num1) {
152                     return 1;
153                 } else {
154                     /* Equal, continue. */
155                 }
156                 afterNumber = TRUE;
157             } else {
158                 /* 1 is numeric, 2 is not. */
159                 if (afterNumber) {
160                     return -1;
161                 } else {
162                     return 1;
163                 }
164             }
165         } else {
166             if (numeric2) {
167                 /* 1 is not, 2 is numeric. */
168                 if (afterNumber) {
169                     return 1;
170                 } else {
171                     return -1;
172                 }
173             } else {
174                 /* Neither is numeric. */
175             }
176         }
177 
178         /* Compare the characters as is. */
179         if (c1 > c2) {
180             return -1;
181         } else if (c2 > c1) {
182             return 1;
183         } else {
184             /* Equal, continue. */
185             if ((c1 == TEXT('.')) || (c1 == TEXT('-')) || (c1 == TEXT('_'))) {
186             } else {
187                 afterNumber = FALSE;
188             }
189         }
190 
191         pos1++;
192         pos2++;
193     }
194 }
195 
196 int compareFileNamesIndex(const TCHAR *file1, const TCHAR *file2, int startIndex, int stopIndex, int startCountFromEnd, int stopCountFromEnd) {
197     TCHAR *file1_;
198     TCHAR *file2_;
199     int start;
200     int len;
201     int result;
202 
203     start = (startCountFromEnd ? (int)_tcslen(file1) - startIndex : startIndex);
204     len   = (stopCountFromEnd  ? (int)_tcslen(file1) - stopIndex  : stopIndex) - start;
205     file1_ = malloc(sizeof(TCHAR) * (len + 1));
206     if (!file1_) {
207         outOfMemoryQueued(TEXT("CFNI"), 1);
208         return 0;
209     }
210     _tcsncpy(file1_, file1 + start, len);
211     file1_[len] = 0;
212 
213     start = (startCountFromEnd ? (int)_tcslen(file2) - startIndex : startIndex);
214     len   = (stopCountFromEnd  ? (int)_tcslen(file2) - stopIndex  : stopIndex) - start;
215     file2_ = malloc(sizeof(TCHAR) * (len + 1));
216     if (!file2_) {
217         free(file1_);
218         outOfMemoryQueued(TEXT("CFNI"), 2);
219         return 0;
220     }
221     _tcsncpy(file2_, file2 + start, len);
222     file2_[len] = 0;
223 
224     result = compareFileNames(file1_, file2_);
225 
226     free(file1_);
227     free(file2_);
228 
229     return result;
230 }
231 
232 int sortFilesNamesAsc(TCHAR **files, int cnt) {
233     int i, j;
234     TCHAR *temp;
235     int cmp;
236 
237     for (i = 0; i < cnt; i++) {
238         for (j = 0; j < cnt - 1; j++) {
239             cmp = compareFileNames(files[j], files[j+1]);
240             if (cmp < 0) {
241                 temp = files[j + 1];
242                 files[j + 1] = files[j];
243                 files[j] = temp;
244             }
245         }
246     }
247 
248     return TRUE;
249 }
250 
251 int sortFilesNamesDec(TCHAR **files, int cnt) {
252     int i, j;
253     TCHAR *temp;
254     int cmp;
255 
256     for (i = 0; i < cnt; i++) {
257         for (j = 0; j < cnt - 1; j++) {
258             cmp = compareFileNames(files[j], files[j+1]);
259             if (cmp > 0) {
260                 temp = files[j + 1];
261                 files[j + 1] = files[j];
262                 files[j] = temp;
263             }
264         }
265     }
266 
267     return TRUE;
268 }
269 
270 int sortFilesNamesDecIndex(TCHAR **files, int cnt, int startIndex, int stopIndex, int startCountFromEnd, int stopCountFromEnd) {
271     int i, j;
272     TCHAR *temp;
273     int cmp;
274 
275     for (i = 0; i < cnt; i++) {
276         for (j = 0; j < cnt - 1; j++) {
277             cmp = compareFileNamesIndex(files[j], files[j+1], startIndex, stopIndex, startCountFromEnd, stopCountFromEnd);
278             if (cmp > 0) {
279                 temp = files[j + 1];
280                 files[j + 1] = files[j];
281                 files[j] = temp;
282             }
283         }
284     }
285 
286     return TRUE;
287 }
288 
289 /**
290  * This function allows to sort filenames with the following logic:
291  *  - if the given pattern contains a ???????? (date) token, the files are first sorted by date descending.
292  *  - if the given pattern contains a * (num) token, the files are sorted by ascending numbers (for equivalent dates).
293  *     The first file being the one without number.
294  *  NOTE: The function assumes that there is at most one token of each!
295  *
296  * @param pattern pattern used to figure out how to sort the files.
297  * @param files list of files to sort.
298  * @param cnt number of files to sort.
299  */
300 int sortFilesNamesSmart(const TCHAR* pattern, TCHAR **files, int cnt) {
301     int i, j;
302     TCHAR *temp;
303     int cmp;
304     TCHAR* numToken;
305     TCHAR* dateToken;
306     int numStartIndex, numStopIndex;
307     int dateStartIndex = 0;
308     int dateStopIndex = 0;
309     int startCountFromEnd = 0;
310     int stopCountFromEnd = 0;
311 
312     /* First sort by date. */
313     dateToken = _tcsstr(pattern, TEXT("?"));
314     numToken = _tcsstr(pattern, TEXT("*"));
315 
316     if (dateToken) {
317         if (!numToken || (dateToken < numToken)) {
318             dateStartIndex = (int)(dateToken - pattern);
319             dateStopIndex = dateStartIndex + 8;
320             startCountFromEnd = FALSE;
321             stopCountFromEnd = FALSE;
322         } else {
323             /* There is a num token before the date. So the length before the date is not fixed. Calculate the index from the end. */
324             dateStartIndex = (int)_tcslen(pattern) - (int)(dateToken - pattern);
325             dateStopIndex = dateStartIndex - 8;
326             startCountFromEnd = TRUE;
327             stopCountFromEnd = TRUE;
328         }
329         sortFilesNamesDecIndex(files, cnt, dateStartIndex, dateStopIndex, startCountFromEnd, stopCountFromEnd);
330     }
331 
332     if (numToken) {
333         numStartIndex = (int)(numToken - pattern);
334         numStopIndex = (int)_tcslen(pattern) - (numStartIndex + 1);
335         for (i = 0; i < cnt; i++) {
336             for (j = 0; j < cnt - 1; j++) {
337                 if (dateToken) {
338                     /* Make sure that the dates are equals. */
339                     cmp = compareFileNamesIndex(files[j], files[j+1], dateStartIndex, dateStopIndex, startCountFromEnd, stopCountFromEnd);
340                     if (cmp != 0) {
341                         continue;
342                     }
343                 }
344                 /* Sort by ascending name. */
345                 cmp = compareFileNamesIndex(files[j], files[j+1], numStartIndex, numStopIndex, FALSE, TRUE);
346                 if (cmp < 0) {
347                     temp = files[j + 1];
348                     files[j + 1] = files[j];
349                     files[j] = temp;
350                 }
351             }
352         }
353     }
354 
355     return TRUE;
356 }
357 
358 /**
359  * Returns a NULL terminated list of file names within the specified pattern.
360  *  The files will be sorted new to old for TIMES.  Then incremental ordering
361  *  for NAMES.  The numeric components of the names will be treated as
362  *  numbers and sorted accordingly.
363  */
364 TCHAR** loggerFileGetFiles(const TCHAR* pattern, int sortMode) {
365     int cnt;
366     int filesSize;
367     TCHAR **files;
368 #ifdef WIN32
369     int i;
370     size_t dirLen;
371     TCHAR *c;
372     TCHAR *dirPart;
373     intptr_t handle;
374     struct _tfinddata64_t fblock;
375     size_t fileLen;
376     TCHAR **newFiles;
377     __time64_t *fileTimes;
378     __time64_t *newFileTimes;
379 #else
380 #ifdef WRAPPER_FILE_DEBUG
381     int i;
382 #endif
383     int result;
384     glob_t g;
385     int findex;
386     time_t *fileTimes;
387     struct stat fileStat;
388 #endif
389 
390 #ifdef WRAPPER_FILE_DEBUG
391     _tprintf(TEXT("loggerFileGetFiles(%s, %d)\n"), pattern, sortMode);
392 #endif
393 
394 #ifdef WIN32
395     cnt = 0;
396     /* Initialize the files array. */
397     filesSize = FILES_CHUNK;
398     files = malloc(sizeof(TCHAR *) * filesSize);
399     if (!files) {
400         outOfMemoryQueued(TEXT("WFGF"), 1);
401         return NULL;
402     }
403     memset(files, 0, sizeof(TCHAR *) * filesSize);
404 
405     fileTimes = malloc(sizeof(__time64_t) * filesSize);
406     if (!fileTimes) {
407         outOfMemoryQueued(TEXT("WFGF"), 2);
408         free(files);
409         return NULL;
410     }
411     memset(fileTimes, 0, sizeof(__time64_t) * filesSize);
412 
413     /* Extract any path information from the beginning of the file */
414     c = max(_tcsrchr(pattern, TEXT('\\')), _tcsrchr(pattern, TEXT('/')));
415     if (c == NULL) {
416         /* No directory component */
417         dirPart = malloc(sizeof(TCHAR) * 1);
418         if (!dirPart) {
419             outOfMemoryQueued(TEXT("WFGF"), 3);
420             return NULL;
421         }
422         dirPart[0] = TEXT('\0');
423         dirLen = 0;
424     } else {
425         /* extract the directory. */
426         dirLen = c - pattern + 1;
427         dirPart = malloc(sizeof(TCHAR) * (dirLen + 1));
428         if (!dirPart) {
429             outOfMemoryQueued(TEXT("WFGF"), 4);
430             return NULL;
431         }
432         _tcsncpy(dirPart, pattern, dirLen);
433         dirPart[dirLen] = TEXT('\0');
434     }
435 
436 #ifdef WRAPPER_FILE_DEBUG
437     _tprintf(TEXT("  dirPart=[%s]\n"), dirPart);
438 #endif
439 
440     /* Get the first file. */
441 #ifdef _IA64_
442     /* On Itanium, the first parameter is not a "const". If you don't cast it, then you have a warning */
443     if ((handle = _tfindfirst64((TCHAR *)pattern, &fblock)) > 0) {
444 #else
445     if ((handle = _tfindfirst64(pattern, &fblock)) > 0) {
446 #endif
447         if ((_tcscmp(fblock.name, TEXT(".")) != 0) && (_tcscmp(fblock.name, TEXT("..")) != 0)) {
448             fileLen = _tcslen(fblock.name);
449             files[cnt] = malloc((_tcslen(dirPart) + _tcslen(fblock.name) + 1) * sizeof(TCHAR));
450             if (!files[cnt]) {
451                 outOfMemoryQueued(TEXT("WFGF"), 5);
452                 free(fileTimes);
453                 loggerFileFreeFiles(files);
454                 free(dirPart);
455                 return NULL;
456             }
457             _sntprintf(files[cnt], _tcslen(dirPart) + _tcslen(fblock.name) + 1, TEXT("%s%s"), dirPart, fblock.name);
458             fileTimes[cnt] = fblock.time_write;
459 #ifdef WRAPPER_FILE_DEBUG
460             _tprintf(TEXT("  files[%d]=%s, %ld\n"), cnt, files[cnt], fileTimes[cnt]);
461 #endif
462 
463             cnt++;
464         }
465 
466         /* Look for additional files. */
467         while (_tfindnext64(handle, &fblock) == 0) {
468             if ((_tcscmp(fblock.name, TEXT(".")) != 0) && (_tcscmp(fblock.name, TEXT("..")) != 0)) {
469                 /* Make sure we have enough room in the files array. */
470                 if (cnt >= filesSize - 1) {
471                     newFiles = malloc(sizeof(TCHAR *) * (filesSize + FILES_CHUNK));
472                     if (!newFiles) {
473                         outOfMemoryQueued(TEXT("WFGF"), 6);
474                         free(fileTimes);
475                         loggerFileFreeFiles(files);
476                         free(dirPart);
477                         return NULL;
478                     }
479                     memset(newFiles, 0, sizeof(TCHAR *) * (filesSize + FILES_CHUNK));
480                     newFileTimes = malloc(sizeof(__time64_t) * (filesSize + FILES_CHUNK));
481                     if (!newFileTimes) {
482                         outOfMemoryQueued(TEXT("WFGF"), 7);
483                         free(newFiles);
484                         free(fileTimes);
485                         loggerFileFreeFiles(files);
486                         free(dirPart);
487                         return NULL;
488                     }
489                     memset(newFileTimes, 0, sizeof(__time64_t) * (filesSize + FILES_CHUNK));
490 
491                     for (i = 0; i < filesSize; i++) {
492                         newFiles[i] = files[i];
493                         newFileTimes[i] = fileTimes[i];
494                     }
495                     free(files);
496                     free(fileTimes);
497                     files = newFiles;
498                     fileTimes = newFileTimes;
499                     filesSize += FILES_CHUNK;
500 #ifdef WRAPPER_FILE_DEBUG
501                     _tprintf(TEXT("  increased files to %d\n"), filesSize);
502 #endif
503                 }
504 
505                 fileLen = _tcslen(fblock.name);
506                 files[cnt] = malloc((_tcslen(dirPart) + _tcslen(fblock.name) + 1) * sizeof(TCHAR));
507                 if (!files[cnt]) {
508                     outOfMemoryQueued(TEXT("WFGF"), 8);
509                     free(fileTimes);
510                     loggerFileFreeFiles(files);
511                     free(dirPart);
512                     return NULL;
513                 }
514                 _sntprintf(files[cnt], _tcslen(dirPart) + _tcslen(fblock.name) + 1, TEXT("%s%s"), dirPart, fblock.name);
515                 fileTimes[cnt] = fblock.time_write;
516 
517 #ifdef WRAPPER_FILE_DEBUG
518                 _tprintf(TEXT("  files[%d]=%s, %ld\n"), cnt, files[cnt], fileTimes[cnt]);
519 #endif
520                 cnt++;
521             }
522         }
523 
524         /* Close the file search */
525         _findclose(handle);
526     }
527 
528     if (cnt <= 0) {
529         if (errno == ENOENT) {
530             /* No files matched. */
531 #ifdef WRAPPER_FILE_DEBUG
532             _tprintf(TEXT("  No files matched.\n"));
533 #endif
534         } else {
535             /* Encountered an error of some kind. */
536             log_printf_queue(TRUE, WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT("Error listing files, %s: %s"), pattern, getLastErrorText());
537             free(fileTimes);
538             loggerFileFreeFiles(files);
539             return NULL;
540         }
541     }
542 #else /* Unix */
543 
544 #ifdef UNICODE
545     char* cPattern;
546     size_t req;
547 
548     req = wcstombs(NULL, pattern, 0) + 1;
549     cPattern = malloc(req);
550     if(!cPattern) {
551         outOfMemoryQueued(TEXT("WFGF"), 8);
552         return NULL;
553     }
554     wcstombs(cPattern, pattern, req);
555 
556     result = glob(cPattern, GLOB_MARK | GLOB_NOSORT, NULL, &g);
557     free(cPattern);
558 #else
559     result = glob(pattern, GLOB_MARK | GLOB_NOSORT, NULL, &g);
560 #endif
561     cnt = 0;
562     if (!result) {
563         if (g.gl_pathc > 0) {
564             filesSize = g.gl_pathc + 1;
565             files = malloc(sizeof(TCHAR *) * filesSize);
566             if (!files) {
567                 outOfMemoryQueued(TEXT("WFGF"), 9);
568                 return NULL;
569             }
570             memset(files, 0, sizeof(TCHAR *) * filesSize);
571 
572             fileTimes = malloc(sizeof(time_t) * filesSize);
573             if (!fileTimes) {
574                 outOfMemoryQueued(TEXT("WFGF"), 10);
575                 loggerFileFreeFiles(files);
576                 return NULL;
577             }
578             memset(fileTimes, 0, sizeof(time_t) * filesSize);
579 
580             for (findex = 0; findex < g.gl_pathc; findex++) {
581 #ifdef UNICODE
582                 req = mbstowcs(NULL, g.gl_pathv[findex], 0);
583                 if (req == (size_t)-1) {
584                     invalidMultiByteSequence(TEXT("GLET"), 1);
585                 }
586                 files[cnt] = malloc((req + 1) * sizeof(TCHAR));
587                 if (!files[cnt]) {
588                     outOfMemoryQueued(TEXT("WFGF"), 11);
589                     free(fileTimes);
590                     loggerFileFreeFiles(files);
591                     return NULL;
592                 }
593                 mbstowcs(files[cnt], g.gl_pathv[findex], req + 1);
594 
595 #else
596                 files[cnt] = malloc((strlen(g.gl_pathv[findex]) + 1));
597                 if (!files[cnt]) {
598                     outOfMemoryQueued(TEXT("WFGF"), 11);
599                     free(fileTimes);
600                     loggerFileFreeFiles(files);
601                     return NULL;
602                 }
603 
604                 strncpy(files[cnt], g.gl_pathv[findex], strlen(g.gl_pathv[findex]) + 1);
605 #endif
606 
607                 /* Only try to get the modified time if it is really necessary. */
608                 if (sortMode == LOGGER_FILE_SORT_MODE_TIMES) {
609                     if (!_tstat(files[cnt], &fileStat)) {
610                         fileTimes[cnt] = fileStat.st_mtime;
611                     } else {
612                         log_printf_queue(TRUE, WRAPPER_SOURCE_WRAPPER, LEVEL_WARN, TEXT("Failed to stat %s: %s"), files[cnt], getLastErrorText());
613                     }
614                 }
615 #ifdef WRAPPER_FILE_DEBUG
616                 _tprintf(TEXT("  files[%d]=%s, %ld\n"), cnt, files[cnt], fileTimes[cnt]);
617 #endif
618                 cnt++;
619             }
620         } else {
621 #ifdef WRAPPER_FILE_DEBUG
622             printf("  No files matched.\n");
623 #endif
624             /* No files, but we still need the array. */
625             filesSize = 1;
626             files = malloc(sizeof(TCHAR *) * filesSize);
627             if (!files) {
628                 outOfMemoryQueued(TEXT("WFGF"), 12);
629                 return NULL;
630             }
631             memset(files, 0, sizeof(TCHAR *) * filesSize);
632 
633             fileTimes = malloc(sizeof(time_t) * filesSize);
634             if (!fileTimes) {
635                 free(files);
636                 outOfMemoryQueued(TEXT("WFGF"), 13);
637                 return NULL;
638             }
639             memset(fileTimes, 0, sizeof(time_t) * filesSize);
640         }
641 
642         globfree(&g);
643     } else if (result == GLOB_NOMATCH) {
644 #ifdef WRAPPER_FILE_DEBUG
645         _tprintf(TEXT("  No files matched.\n"));
646 #endif
647         /* No files, but we still need the array. */
648         filesSize = 1;
649         files = malloc(sizeof(TCHAR *) * filesSize);
650         if (!files) {
651             outOfMemoryQueued(TEXT("WFGF"), 14);
652             return NULL;
653         }
654         memset(files, 0, sizeof(TCHAR *) * filesSize);
655 
656         fileTimes = malloc(sizeof(time_t) * filesSize);
657         if (!fileTimes) {
658             free(files);
659             outOfMemoryQueued(TEXT("WFGF"), 15);
660             return NULL;
661         }
662         memset(fileTimes, 0, sizeof(time_t) * filesSize);
663     } else {
664         /* Encountered an error of some kind. */
665         log_printf_queue(TRUE, WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT("Error listing files, %s: %s"), pattern, getLastErrorText());
666         return NULL;
667     }
668 #endif
669 
670     if (sortMode == LOGGER_FILE_SORT_MODE_TIMES) {
671         if (!sortFilesTimes(files, fileTimes, cnt)) {
672             /* Failed. Reported. */
673             free(fileTimes);
674             loggerFileFreeFiles(files);
675             return NULL;
676         }
677     } else if (sortMode == LOGGER_FILE_SORT_MODE_NAMES_SMART) {
678         if (!sortFilesNamesSmart(pattern, files, cnt)) {
679             /* Failed. Reported. */
680             free(fileTimes);
681             loggerFileFreeFiles(files);
682             return NULL;
683         }
684     } else if (sortMode == LOGGER_FILE_SORT_MODE_NAMES_DEC) {
685         if (!sortFilesNamesDec(files, cnt)) {
686             /* Failed. Reported. */
687             free(fileTimes);
688             loggerFileFreeFiles(files);
689             return NULL;
690         }
691     } else { /* LOGGER_FILE_SORT_MODE_NAMES_ASC */
692         if (!sortFilesNamesAsc(files, cnt)) {
693             /* Failed. Reported. */
694             free(fileTimes);
695             loggerFileFreeFiles(files);
696             return NULL;
697         }
698     }
699 
700 #ifdef WRAPPER_FILE_DEBUG
701     _tprintf(TEXT("  Sorted:\n"));
702     for (i = 0; i < cnt; i++) {
703         _tprintf(TEXT("  files[%d]=%s, %ld\n"), i, files[i], fileTimes[i]);
704     }
705     _tprintf(TEXT("loggerFileGetFiles(%s, %d) END\n"), pattern, sortMode);
706 #endif
707 
708     free(fileTimes);
709 
710     return files;
711 }
712 
713 /**
714  * Frees the array of file names returned by loggerFileGetFiles()
715  */
716 void loggerFileFreeFiles(TCHAR** files) {
717     int i;
718 
719     i = 0;
720     while (files[i]) {
721         free(files[i]);
722         i++;
723     }
724     free(files);
725 }
726 
727 /**
728  * Combines two paths and take care to add only one separator between them.
729  *
730  * The returned string must be freed by the caller.
731  *
732  * @param path1 base path
733  * @param path2 relative path.
734  *
735  * @return The resulting path, or NULL if there were any problems.
736  */
737 TCHAR *combinePath(const TCHAR *path1, const TCHAR *path2) {
738     TCHAR* result;
739     TCHAR* tempPath1 = NULL;
740     TCHAR* tempPath2 = NULL;
741     TCHAR* tempPath2Ptr;
742     TCHAR c1;
743     TCHAR c2;
744     size_t len1 = _tcslen(path1);
745     size_t len2 = _tcslen(path2);
746     size_t len = len1 + len2;
747     int i = 0;
748 #ifdef WIN32
749     const TCHAR bad_sep  = TEXT('/');
750     const TCHAR good_sep = TEXT('\\');
751 #else
752     const TCHAR bad_sep  = TEXT('\\');
753     const TCHAR good_sep = TEXT('/');
754 #endif
755 
756     if (len1 > 0) {
757         tempPath1 = malloc(sizeof(TCHAR) * (len1 + 1));
758         if (!tempPath1) {
759             outOfMemoryQueued(TEXT("CP"), 1);
760             return NULL;
761         }
762         _tcsncpy(tempPath1, path1, len1 + 1);
763     }
764     if (len2 > 0) {
765         tempPath2 = malloc(sizeof(TCHAR) * (len2 + 1));
766         if (!tempPath2) {
767             outOfMemoryQueued(TEXT("CP"), 2);
768             free(tempPath1);
769             return NULL;
770         }
771         _tcsncpy(tempPath2, path2, len2 + 1);
772     }
773     if (!tempPath1 && !tempPath2) {
774         result = NULL;
775     } else if (tempPath1 && !tempPath2) {
776         result = tempPath1;
777     } else if (!tempPath1 && tempPath2) {
778         result = tempPath2;
779     } else {
780         tempPath2Ptr = tempPath2;
781 
782         /* first replace all directory separators by their standard according to the platform.
783          *  we want to avoid that the two paths use different separators. */
784         while (tempPath1[i] != TEXT('\0')) {
785             if (tempPath1[i] == bad_sep) {
786                 tempPath1[i] = good_sep;
787             }
788             i++;
789         }
790         i = 0;
791         while (tempPath2[i] != TEXT('\0')) {
792             if (tempPath2[i] == bad_sep) {
793                 tempPath2[i] = good_sep;
794             }
795             i++;
796         }
797 
798         c1 = tempPath1[len1 - 1];
799         c2 = tempPath2[0];
800 
801         if (c1 == good_sep) {
802             if (c2 == good_sep) {
803                 tempPath2Ptr++;
804             } else {
805                 len += 1;
806             }
807 
808             result = malloc(sizeof(TCHAR) * len);
809             if (!result) {
810                 outOfMemoryQueued(TEXT("CP"), 3);
811                 free(tempPath1);
812                 free(tempPath2);
813                 return NULL;
814             }
815             _sntprintf(result, len, TEXT("%s%s"), tempPath1, tempPath2Ptr);
816         } else {
817             if (c2 == good_sep) {
818                 tempPath2Ptr++;
819                 len += 1;
820             } else {
821                 len += 2;
822             }
823 
824             result = malloc(sizeof(TCHAR) * len);
825             if (!result) {
826                 outOfMemoryQueued(TEXT("CP"), 4);
827                 free(tempPath1);
828                 free(tempPath2);
829                 return NULL;
830             }
831             _sntprintf(result, len, TEXT("%s%c%s"), tempPath1, good_sep, tempPath2Ptr);
832         }
833         free(tempPath1);
834         free(tempPath2);
835     }
836     return result;
837 }
838 
839 /**
840  * Given a path, resolve a real absolute path which has resolved all relative and symbolic links.
841  *
842  * The returned string must be freed by the caller.
843  *
844  * @param path The source path.
845  * @param pathDesc A description of the path used for error messages.
846  * @param errorLevel Level to log errors at.
847  *
848  * @return The absolute path, or NULL if there were any problems.
849  */
850 TCHAR *getRealPath(const TCHAR *path, const TCHAR *pathDesc, int errorLevel, int useQueue) {
851     TCHAR *realPath;
852 #ifdef WIN32
853     DWORD len;
854 #else
855     size_t len;
856     TCHAR *tempPath;
857 #endif
858 
859 #ifdef WIN32
860     len = GetFullPathName(path, 0, NULL, NULL);
861     if (!len) {
862         if (errorLevel != LEVEL_NONE) {
863             log_printf_queue(useQueue, WRAPPER_SOURCE_WRAPPER, errorLevel, TEXT("Unable to resolve the %s '%s': %s"), pathDesc, path, getLastErrorText());
864         }
865         return NULL;
866     }
867     realPath = malloc(sizeof(TCHAR) * len);
868     if (!realPath) {
869         if (useQueue) {
870             outOfMemoryQueued(TEXT("GRP"), 1);
871         } else {
872             outOfMemory(TEXT("GRP"), 1);
873         }
874         return NULL;
875     }
876     if (!GetFullPathName(path, len, realPath, NULL)) {
877         if (errorLevel != LEVEL_NONE) {
878             log_printf_queue(useQueue, WRAPPER_SOURCE_WRAPPER, errorLevel, TEXT("Unable to resolve the %s '%s': %s"), pathDesc, path, getLastErrorText());
879         }
880         free(realPath);
881         return NULL;
882     }
883 #else
884     /* The solaris implementation of realpath will return a relative path if a relative
885      *  path is provided.  We always need an absolute path here.  So build up one and
886      *  then use realpath to remove any .. or other relative references. */
887     tempPath = malloc(sizeof(TCHAR) * (PATH_MAX + 1));
888     if (!tempPath) {
889         if (useQueue) {
890             outOfMemoryQueued(TEXT("GRP"), 2);
891         } else {
892             outOfMemory(TEXT("GRP"), 2);
893         }
894         return NULL;
895     }
896     if (_trealpathN(path, tempPath, PATH_MAX + 1) == NULL) {
897         if (errorLevel != LEVEL_NONE) {
898             log_printf_queue(useQueue, WRAPPER_SOURCE_WRAPPER, errorLevel, TEXT("Unable to resolve the %s '%s': %s"), pathDesc, path, getLastErrorText());
899         }
900         free(tempPath);
901         return NULL;
902     }
903 
904     /* Now that we know how big the resulting string is, put it into a buffer of the correct size to avoid waste. */
905     len = _tcslen(tempPath) + 1;
906     realPath = malloc(sizeof(TCHAR) * len);
907     if (!realPath) {
908         if (useQueue) {
909             outOfMemoryQueued(TEXT("GRP"), 3);
910         } else {
911             outOfMemory(TEXT("GRP"), 3);
912         }
913         free(tempPath);
914         return NULL;
915     }
916     _tcsncpy(realPath, tempPath, len);
917     free(tempPath);
918 #endif
919 
920     return realPath;
921 }
922 
923 /**
924  * Returns the absolute path of a file even if the file is not yet created.
925  *  The folder containing the file must exist.
926  *
927  * The returned string must be freed by the caller.
928  *
929  * @param path The source path.
930  * @param pathDesc A description of the path used for error messages.
931  * @param errorLevel Level to log errors at.
932  *
933  * @return The absolute path, or NULL if there were any problems.
934  */
935 TCHAR* getAbsolutePathOfFile(const TCHAR* path, const TCHAR *pathDesc, int errorLevel, int useQueue) {
936     TCHAR* ptr;
937     TCHAR* dir;
938     const TCHAR* file;
939     TCHAR* result = NULL;
940     TCHAR* pathCpy;
941 
942     pathCpy = malloc(sizeof(TCHAR) * (_tcslen(path) + 1));
943     if (!pathCpy) {
944         outOfMemoryQueued(TEXT("GAPOF"), 1);
945     } else {
946         _tcsncpy(pathCpy, path, _tcslen(path) + 1);
947         ptr = __max(_tcsrchr(pathCpy, TEXT('\\')), _tcsrchr(pathCpy, TEXT('/')));
948         if (ptr) {
949             *ptr = 0;
950             ptr++;
951             dir = getRealPath(pathCpy, pathDesc, errorLevel, useQueue);
952             file = ptr;
953         } else {
954             dir = getRealPath(TEXT("."), pathDesc, errorLevel, useQueue);
955             file = pathCpy;
956         }
957 
958         if (dir) {
959             result = combinePath(dir, file);
960             free(dir);
961         }
962         free(pathCpy);
963     }
964 
965     return result;
966 }
967 
968 #ifdef LOGGER_FILE_DEBUG
969 void loggerFileTests() {
970     TCHAR** files;
971 
972     printf("Start loggerFileTests\n");
973     files = loggerFileGetFiles((TEXT("../logs/*.log*"), LOGGER_FILE_SORT_MODE_TIMES);
974     if (files) {
975         loggerFileFreeFiles(files);
976     }
977 
978     files = loggerFileGetFiles(TEXT("../logs/*.log*"), LOGGER_FILE_SORT_MODE_NAMES_ASC);
979     if (files) {
980         loggerFileFreeFiles(files);
981     }
982 
983     files = loggerFileGetFiles(TEXT("../logs/*.log*"), LOGGER_FILE_SORT_MODE_NAMES_DEC);
984     if (files) {
985         loggerFileFreeFiles(files);
986     }
987     printf("End loggerFileTests\n");
988 }
989 #endif
990