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