1 /* 2 * Copyright (c) 2016-2020, Przemyslaw Skibinski, Yann Collet, Facebook, Inc. 3 * All rights reserved. 4 * 5 * This source code is licensed under both the BSD-style license (found in the 6 * LICENSE file in the root directory of this source tree) and the GPLv2 (found 7 * in the COPYING file in the root directory of this source tree). 8 * You may select, at your option, one of the above-listed licenses. 9 */ 10 11 #if defined (__cplusplus) 12 extern "C" { 13 #endif 14 15 16 /*-**************************************** 17 * Dependencies 18 ******************************************/ 19 #include "util.h" /* note : ensure that platform.h is included first ! */ 20 #include <stdlib.h> /* malloc, realloc, free */ 21 #include <stdio.h> /* fprintf */ 22 #include <time.h> /* clock_t, clock, CLOCKS_PER_SEC, nanosleep */ 23 #include <errno.h> 24 #include <assert.h> 25 26 #if defined(_WIN32) 27 # include <sys/utime.h> /* utime */ 28 # include <io.h> /* _chmod */ 29 #else 30 # include <unistd.h> /* chown, stat */ 31 # if PLATFORM_POSIX_VERSION < 200809L 32 # include <utime.h> /* utime */ 33 # else 34 # include <fcntl.h> /* AT_FDCWD */ 35 # include <sys/stat.h> /* utimensat */ 36 # endif 37 #endif 38 39 #if defined(_MSC_VER) || defined(__MINGW32__) || defined (__MSVCRT__) 40 #include <direct.h> /* needed for _mkdir in windows */ 41 #endif 42 43 #if defined(__linux__) || (PLATFORM_POSIX_VERSION >= 200112L) /* opendir, readdir require POSIX.1-2001 */ 44 # include <dirent.h> /* opendir, readdir */ 45 # include <string.h> /* strerror, memcpy */ 46 #endif /* #ifdef _WIN32 */ 47 48 49 /*-**************************************** 50 * Internal Macros 51 ******************************************/ 52 53 /* CONTROL is almost like an assert(), but is never disabled. 54 * It's designed for failures that may happen rarely, 55 * but we don't want to maintain a specific error code path for them, 56 * such as a malloc() returning NULL for example. 57 * Since it's always active, this macro can trigger side effects. 58 */ 59 #define CONTROL(c) { \ 60 if (!(c)) { \ 61 UTIL_DISPLAYLEVEL(1, "Error : %s, %i : %s", \ 62 __FILE__, __LINE__, #c); \ 63 exit(1); \ 64 } } 65 66 /* console log */ 67 #define UTIL_DISPLAY(...) fprintf(stderr, __VA_ARGS__) 68 #define UTIL_DISPLAYLEVEL(l, ...) { if (g_utilDisplayLevel>=l) { UTIL_DISPLAY(__VA_ARGS__); } } 69 70 /* A modified version of realloc(). 71 * If UTIL_realloc() fails the original block is freed. 72 */ 73 UTIL_STATIC void* UTIL_realloc(void *ptr, size_t size) 74 { 75 void *newptr = realloc(ptr, size); 76 if (newptr) return newptr; 77 free(ptr); 78 return NULL; 79 } 80 81 #if defined(_MSC_VER) 82 #define chmod _chmod 83 #endif 84 85 86 /*-**************************************** 87 * Console log 88 ******************************************/ 89 int g_utilDisplayLevel; 90 91 92 /*-************************************* 93 * Constants 94 ***************************************/ 95 #define LIST_SIZE_INCREASE (8*1024) 96 #define MAX_FILE_OF_FILE_NAMES_SIZE (1<<20)*50 97 98 99 /*-************************************* 100 * Functions 101 ***************************************/ 102 103 int UTIL_fileExist(const char* filename) 104 { 105 stat_t statbuf; 106 #if defined(_MSC_VER) 107 int const stat_error = _stat64(filename, &statbuf); 108 #else 109 int const stat_error = stat(filename, &statbuf); 110 #endif 111 return !stat_error; 112 } 113 114 int UTIL_isRegularFile(const char* infilename) 115 { 116 stat_t statbuf; 117 return UTIL_getFileStat(infilename, &statbuf); /* Only need to know whether it is a regular file */ 118 } 119 120 int UTIL_getFileStat(const char* infilename, stat_t *statbuf) 121 { 122 int r; 123 #if defined(_MSC_VER) 124 r = _stat64(infilename, statbuf); 125 if (r || !(statbuf->st_mode & S_IFREG)) return 0; /* No good... */ 126 #else 127 r = stat(infilename, statbuf); 128 if (r || !S_ISREG(statbuf->st_mode)) return 0; /* No good... */ 129 #endif 130 return 1; 131 } 132 133 /* like chmod, but avoid changing permission of /dev/null */ 134 int UTIL_chmod(char const* filename, mode_t permissions) 135 { 136 if (!strcmp(filename, "/dev/null")) return 0; /* pretend success, but don't change anything */ 137 return chmod(filename, permissions); 138 } 139 140 int UTIL_setFileStat(const char *filename, stat_t *statbuf) 141 { 142 int res = 0; 143 144 if (!UTIL_isRegularFile(filename)) 145 return -1; 146 147 /* set access and modification times */ 148 /* We check that st_mtime is a macro here in order to give us confidence 149 * that struct stat has a struct timespec st_mtim member. We need this 150 * check because there are some platforms that claim to be POSIX 2008 151 * compliant but which do not have st_mtim... */ 152 #if (PLATFORM_POSIX_VERSION >= 200809L) && defined(st_mtime) 153 { 154 /* (atime, mtime) */ 155 struct timespec timebuf[2] = { {0, UTIME_NOW} }; 156 timebuf[1] = statbuf->st_mtim; 157 res += utimensat(AT_FDCWD, filename, timebuf, 0); 158 } 159 #else 160 { 161 struct utimbuf timebuf; 162 timebuf.actime = time(NULL); 163 timebuf.modtime = statbuf->st_mtime; 164 res += utime(filename, &timebuf); 165 } 166 #endif 167 168 #if !defined(_WIN32) 169 res += chown(filename, statbuf->st_uid, statbuf->st_gid); /* Copy ownership */ 170 #endif 171 172 res += UTIL_chmod(filename, statbuf->st_mode & 07777); /* Copy file permissions */ 173 174 errno = 0; 175 return -res; /* number of errors is returned */ 176 } 177 178 int UTIL_isDirectory(const char* infilename) 179 { 180 stat_t statbuf; 181 #if defined(_MSC_VER) 182 int const r = _stat64(infilename, &statbuf); 183 if (!r && (statbuf.st_mode & _S_IFDIR)) return 1; 184 #else 185 int const r = stat(infilename, &statbuf); 186 if (!r && S_ISDIR(statbuf.st_mode)) return 1; 187 #endif 188 return 0; 189 } 190 191 int UTIL_compareStr(const void *p1, const void *p2) { 192 return strcmp(* (char * const *) p1, * (char * const *) p2); 193 } 194 195 int UTIL_isSameFile(const char* fName1, const char* fName2) 196 { 197 assert(fName1 != NULL); assert(fName2 != NULL); 198 #if defined(_MSC_VER) || defined(_WIN32) 199 /* note : Visual does not support file identification by inode. 200 * inode does not work on Windows, even with a posix layer, like msys2. 201 * The following work-around is limited to detecting exact name repetition only, 202 * aka `filename` is considered different from `subdir/../filename` */ 203 return !strcmp(fName1, fName2); 204 #else 205 { stat_t file1Stat; 206 stat_t file2Stat; 207 return UTIL_getFileStat(fName1, &file1Stat) 208 && UTIL_getFileStat(fName2, &file2Stat) 209 && (file1Stat.st_dev == file2Stat.st_dev) 210 && (file1Stat.st_ino == file2Stat.st_ino); 211 } 212 #endif 213 } 214 215 /* UTIL_isFIFO : distinguish named pipes */ 216 int UTIL_isFIFO(const char* infilename) 217 { 218 /* macro guards, as defined in : https://linux.die.net/man/2/lstat */ 219 #if PLATFORM_POSIX_VERSION >= 200112L 220 stat_t statbuf; 221 int const r = UTIL_getFileStat(infilename, &statbuf); 222 if (!r && S_ISFIFO(statbuf.st_mode)) return 1; 223 #endif 224 (void)infilename; 225 return 0; 226 } 227 228 int UTIL_isLink(const char* infilename) 229 { 230 /* macro guards, as defined in : https://linux.die.net/man/2/lstat */ 231 #if PLATFORM_POSIX_VERSION >= 200112L 232 stat_t statbuf; 233 int const r = lstat(infilename, &statbuf); 234 if (!r && S_ISLNK(statbuf.st_mode)) return 1; 235 #endif 236 (void)infilename; 237 return 0; 238 } 239 240 U64 UTIL_getFileSize(const char* infilename) 241 { 242 if (!UTIL_isRegularFile(infilename)) return UTIL_FILESIZE_UNKNOWN; 243 { int r; 244 #if defined(_MSC_VER) 245 struct __stat64 statbuf; 246 r = _stat64(infilename, &statbuf); 247 if (r || !(statbuf.st_mode & S_IFREG)) return UTIL_FILESIZE_UNKNOWN; 248 #elif defined(__MINGW32__) && defined (__MSVCRT__) 249 struct _stati64 statbuf; 250 r = _stati64(infilename, &statbuf); 251 if (r || !(statbuf.st_mode & S_IFREG)) return UTIL_FILESIZE_UNKNOWN; 252 #else 253 struct stat statbuf; 254 r = stat(infilename, &statbuf); 255 if (r || !S_ISREG(statbuf.st_mode)) return UTIL_FILESIZE_UNKNOWN; 256 #endif 257 return (U64)statbuf.st_size; 258 } 259 } 260 261 262 U64 UTIL_getTotalFileSize(const char* const * fileNamesTable, unsigned nbFiles) 263 { 264 U64 total = 0; 265 unsigned n; 266 for (n=0; n<nbFiles; n++) { 267 U64 const size = UTIL_getFileSize(fileNamesTable[n]); 268 if (size == UTIL_FILESIZE_UNKNOWN) return UTIL_FILESIZE_UNKNOWN; 269 total += size; 270 } 271 return total; 272 } 273 274 275 /* condition : @file must be valid, and not have reached its end. 276 * @return : length of line written into @buf, ended with `\0` instead of '\n', 277 * or 0, if there is no new line */ 278 static size_t readLineFromFile(char* buf, size_t len, FILE* file) 279 { 280 assert(!feof(file)); 281 /* Work around Cygwin problem when len == 1 it returns NULL. */ 282 if (len <= 1) return 0; 283 CONTROL( fgets(buf, (int) len, file) ); 284 { size_t linelen = strlen(buf); 285 if (strlen(buf)==0) return 0; 286 if (buf[linelen-1] == '\n') linelen--; 287 buf[linelen] = '\0'; 288 return linelen+1; 289 } 290 } 291 292 /* Conditions : 293 * size of @inputFileName file must be < @dstCapacity 294 * @dst must be initialized 295 * @return : nb of lines 296 * or -1 if there's an error 297 */ 298 static int 299 readLinesFromFile(void* dst, size_t dstCapacity, 300 const char* inputFileName) 301 { 302 int nbFiles = 0; 303 size_t pos = 0; 304 char* const buf = (char*)dst; 305 FILE* const inputFile = fopen(inputFileName, "r"); 306 307 assert(dst != NULL); 308 309 if(!inputFile) { 310 if (g_utilDisplayLevel >= 1) perror("zstd:util:readLinesFromFile"); 311 return -1; 312 } 313 314 while ( !feof(inputFile) ) { 315 size_t const lineLength = readLineFromFile(buf+pos, dstCapacity-pos, inputFile); 316 if (lineLength == 0) break; 317 assert(pos + lineLength < dstCapacity); 318 pos += lineLength; 319 ++nbFiles; 320 } 321 322 CONTROL( fclose(inputFile) == 0 ); 323 324 return nbFiles; 325 } 326 327 /*Note: buf is not freed in case function successfully created table because filesTable->fileNames[0] = buf*/ 328 FileNamesTable* 329 UTIL_createFileNamesTable_fromFileName(const char* inputFileName) 330 { 331 size_t nbFiles = 0; 332 char* buf; 333 size_t bufSize; 334 size_t pos = 0; 335 336 if (!UTIL_fileExist(inputFileName) || !UTIL_isRegularFile(inputFileName)) 337 return NULL; 338 339 { U64 const inputFileSize = UTIL_getFileSize(inputFileName); 340 if(inputFileSize > MAX_FILE_OF_FILE_NAMES_SIZE) 341 return NULL; 342 bufSize = (size_t)(inputFileSize + 1); /* (+1) to add '\0' at the end of last filename */ 343 } 344 345 buf = (char*) malloc(bufSize); 346 CONTROL( buf != NULL ); 347 348 { int const ret_nbFiles = readLinesFromFile(buf, bufSize, inputFileName); 349 350 if (ret_nbFiles <= 0) { 351 free(buf); 352 return NULL; 353 } 354 nbFiles = (size_t)ret_nbFiles; 355 } 356 357 { const char** filenamesTable = (const char**) malloc(nbFiles * sizeof(*filenamesTable)); 358 CONTROL(filenamesTable != NULL); 359 360 { size_t fnb; 361 for (fnb = 0, pos = 0; fnb < nbFiles; fnb++) { 362 filenamesTable[fnb] = buf+pos; 363 pos += strlen(buf+pos)+1; /* +1 for the finishing `\0` */ 364 } } 365 assert(pos <= bufSize); 366 367 return UTIL_assembleFileNamesTable(filenamesTable, nbFiles, buf); 368 } 369 } 370 371 static FileNamesTable* 372 UTIL_assembleFileNamesTable2(const char** filenames, size_t tableSize, size_t tableCapacity, char* buf) 373 { 374 FileNamesTable* const table = (FileNamesTable*) malloc(sizeof(*table)); 375 CONTROL(table != NULL); 376 table->fileNames = filenames; 377 table->buf = buf; 378 table->tableSize = tableSize; 379 table->tableCapacity = tableCapacity; 380 return table; 381 } 382 383 FileNamesTable* 384 UTIL_assembleFileNamesTable(const char** filenames, size_t tableSize, char* buf) 385 { 386 return UTIL_assembleFileNamesTable2(filenames, tableSize, tableSize, buf); 387 } 388 389 void UTIL_freeFileNamesTable(FileNamesTable* table) 390 { 391 if (table==NULL) return; 392 free((void*)table->fileNames); 393 free(table->buf); 394 free(table); 395 } 396 397 FileNamesTable* UTIL_allocateFileNamesTable(size_t tableSize) 398 { 399 const char** const fnTable = (const char**)malloc(tableSize * sizeof(*fnTable)); 400 FileNamesTable* fnt; 401 if (fnTable==NULL) return NULL; 402 fnt = UTIL_assembleFileNamesTable(fnTable, tableSize, NULL); 403 fnt->tableSize = 0; /* the table is empty */ 404 return fnt; 405 } 406 407 void UTIL_refFilename(FileNamesTable* fnt, const char* filename) 408 { 409 assert(fnt->tableSize < fnt->tableCapacity); 410 fnt->fileNames[fnt->tableSize] = filename; 411 fnt->tableSize++; 412 } 413 414 static size_t getTotalTableSize(FileNamesTable* table) 415 { 416 size_t fnb = 0, totalSize = 0; 417 for(fnb = 0 ; fnb < table->tableSize && table->fileNames[fnb] ; ++fnb) { 418 totalSize += strlen(table->fileNames[fnb]) + 1; /* +1 to add '\0' at the end of each fileName */ 419 } 420 return totalSize; 421 } 422 423 FileNamesTable* 424 UTIL_mergeFileNamesTable(FileNamesTable* table1, FileNamesTable* table2) 425 { 426 unsigned newTableIdx = 0; 427 size_t pos = 0; 428 size_t newTotalTableSize; 429 char* buf; 430 431 FileNamesTable* const newTable = UTIL_assembleFileNamesTable(NULL, 0, NULL); 432 CONTROL( newTable != NULL ); 433 434 newTotalTableSize = getTotalTableSize(table1) + getTotalTableSize(table2); 435 436 buf = (char*) calloc(newTotalTableSize, sizeof(*buf)); 437 CONTROL ( buf != NULL ); 438 439 newTable->buf = buf; 440 newTable->tableSize = table1->tableSize + table2->tableSize; 441 newTable->fileNames = (const char **) calloc(newTable->tableSize, sizeof(*(newTable->fileNames))); 442 CONTROL ( newTable->fileNames != NULL ); 443 444 { unsigned idx1; 445 for( idx1=0 ; (idx1 < table1->tableSize) && table1->fileNames[idx1] && (pos < newTotalTableSize); ++idx1, ++newTableIdx) { 446 size_t const curLen = strlen(table1->fileNames[idx1]); 447 memcpy(buf+pos, table1->fileNames[idx1], curLen); 448 assert(newTableIdx <= newTable->tableSize); 449 newTable->fileNames[newTableIdx] = buf+pos; 450 pos += curLen+1; 451 } } 452 453 { unsigned idx2; 454 for( idx2=0 ; (idx2 < table2->tableSize) && table2->fileNames[idx2] && (pos < newTotalTableSize) ; ++idx2, ++newTableIdx) { 455 size_t const curLen = strlen(table2->fileNames[idx2]); 456 memcpy(buf+pos, table2->fileNames[idx2], curLen); 457 assert(newTableIdx <= newTable->tableSize); 458 newTable->fileNames[newTableIdx] = buf+pos; 459 pos += curLen+1; 460 } } 461 assert(pos <= newTotalTableSize); 462 newTable->tableSize = newTableIdx; 463 464 UTIL_freeFileNamesTable(table1); 465 UTIL_freeFileNamesTable(table2); 466 467 return newTable; 468 } 469 470 #ifdef _WIN32 471 static int UTIL_prepareFileList(const char* dirName, 472 char** bufStart, size_t* pos, 473 char** bufEnd, int followLinks) 474 { 475 char* path; 476 size_t dirLength, pathLength; 477 int nbFiles = 0; 478 WIN32_FIND_DATAA cFile; 479 HANDLE hFile; 480 481 dirLength = strlen(dirName); 482 path = (char*) malloc(dirLength + 3); 483 if (!path) return 0; 484 485 memcpy(path, dirName, dirLength); 486 path[dirLength] = '\\'; 487 path[dirLength+1] = '*'; 488 path[dirLength+2] = 0; 489 490 hFile=FindFirstFileA(path, &cFile); 491 if (hFile == INVALID_HANDLE_VALUE) { 492 UTIL_DISPLAYLEVEL(1, "Cannot open directory '%s'\n", dirName); 493 return 0; 494 } 495 free(path); 496 497 do { 498 size_t const fnameLength = strlen(cFile.cFileName); 499 path = (char*) malloc(dirLength + fnameLength + 2); 500 if (!path) { FindClose(hFile); return 0; } 501 memcpy(path, dirName, dirLength); 502 path[dirLength] = '\\'; 503 memcpy(path+dirLength+1, cFile.cFileName, fnameLength); 504 pathLength = dirLength+1+fnameLength; 505 path[pathLength] = 0; 506 if (cFile.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { 507 if ( strcmp (cFile.cFileName, "..") == 0 508 || strcmp (cFile.cFileName, ".") == 0 ) 509 continue; 510 /* Recursively call "UTIL_prepareFileList" with the new path. */ 511 nbFiles += UTIL_prepareFileList(path, bufStart, pos, bufEnd, followLinks); 512 if (*bufStart == NULL) { free(path); FindClose(hFile); return 0; } 513 } else if ( (cFile.dwFileAttributes & FILE_ATTRIBUTE_NORMAL) 514 || (cFile.dwFileAttributes & FILE_ATTRIBUTE_ARCHIVE) 515 || (cFile.dwFileAttributes & FILE_ATTRIBUTE_COMPRESSED) ) { 516 if (*bufStart + *pos + pathLength >= *bufEnd) { 517 ptrdiff_t const newListSize = (*bufEnd - *bufStart) + LIST_SIZE_INCREASE; 518 *bufStart = (char*)UTIL_realloc(*bufStart, newListSize); 519 if (*bufStart == NULL) { free(path); FindClose(hFile); return 0; } 520 *bufEnd = *bufStart + newListSize; 521 } 522 if (*bufStart + *pos + pathLength < *bufEnd) { 523 memcpy(*bufStart + *pos, path, pathLength+1 /* include final \0 */); 524 *pos += pathLength + 1; 525 nbFiles++; 526 } } 527 free(path); 528 } while (FindNextFileA(hFile, &cFile)); 529 530 FindClose(hFile); 531 return nbFiles; 532 } 533 534 #elif defined(__linux__) || (PLATFORM_POSIX_VERSION >= 200112L) /* opendir, readdir require POSIX.1-2001 */ 535 536 static int UTIL_prepareFileList(const char *dirName, 537 char** bufStart, size_t* pos, 538 char** bufEnd, int followLinks) 539 { 540 DIR* dir; 541 struct dirent * entry; 542 size_t dirLength; 543 int nbFiles = 0; 544 545 if (!(dir = opendir(dirName))) { 546 UTIL_DISPLAYLEVEL(1, "Cannot open directory '%s': %s\n", dirName, strerror(errno)); 547 return 0; 548 } 549 550 dirLength = strlen(dirName); 551 errno = 0; 552 while ((entry = readdir(dir)) != NULL) { 553 char* path; 554 size_t fnameLength, pathLength; 555 if (strcmp (entry->d_name, "..") == 0 || 556 strcmp (entry->d_name, ".") == 0) continue; 557 fnameLength = strlen(entry->d_name); 558 path = (char*) malloc(dirLength + fnameLength + 2); 559 if (!path) { closedir(dir); return 0; } 560 memcpy(path, dirName, dirLength); 561 562 path[dirLength] = '/'; 563 memcpy(path+dirLength+1, entry->d_name, fnameLength); 564 pathLength = dirLength+1+fnameLength; 565 path[pathLength] = 0; 566 567 if (!followLinks && UTIL_isLink(path)) { 568 UTIL_DISPLAYLEVEL(2, "Warning : %s is a symbolic link, ignoring\n", path); 569 free(path); 570 continue; 571 } 572 573 if (UTIL_isDirectory(path)) { 574 nbFiles += UTIL_prepareFileList(path, bufStart, pos, bufEnd, followLinks); /* Recursively call "UTIL_prepareFileList" with the new path. */ 575 if (*bufStart == NULL) { free(path); closedir(dir); return 0; } 576 } else { 577 if (*bufStart + *pos + pathLength >= *bufEnd) { 578 ptrdiff_t newListSize = (*bufEnd - *bufStart) + LIST_SIZE_INCREASE; 579 assert(newListSize >= 0); 580 *bufStart = (char*)UTIL_realloc(*bufStart, (size_t)newListSize); 581 *bufEnd = *bufStart + newListSize; 582 if (*bufStart == NULL) { free(path); closedir(dir); return 0; } 583 } 584 if (*bufStart + *pos + pathLength < *bufEnd) { 585 memcpy(*bufStart + *pos, path, pathLength + 1); /* with final \0 */ 586 *pos += pathLength + 1; 587 nbFiles++; 588 } } 589 free(path); 590 errno = 0; /* clear errno after UTIL_isDirectory, UTIL_prepareFileList */ 591 } 592 593 if (errno != 0) { 594 UTIL_DISPLAYLEVEL(1, "readdir(%s) error: %s \n", dirName, strerror(errno)); 595 free(*bufStart); 596 *bufStart = NULL; 597 } 598 closedir(dir); 599 return nbFiles; 600 } 601 602 #else 603 604 static int UTIL_prepareFileList(const char *dirName, 605 char** bufStart, size_t* pos, 606 char** bufEnd, int followLinks) 607 { 608 (void)bufStart; (void)bufEnd; (void)pos; (void)followLinks; 609 UTIL_DISPLAYLEVEL(1, "Directory %s ignored (compiled without _WIN32 or _POSIX_C_SOURCE) \n", dirName); 610 return 0; 611 } 612 613 #endif /* #ifdef _WIN32 */ 614 615 int UTIL_isCompressedFile(const char *inputName, const char *extensionList[]) 616 { 617 const char* ext = UTIL_getFileExtension(inputName); 618 while(*extensionList!=NULL) 619 { 620 const int isCompressedExtension = strcmp(ext,*extensionList); 621 if(isCompressedExtension==0) 622 return 1; 623 ++extensionList; 624 } 625 return 0; 626 } 627 628 /*Utility function to get file extension from file */ 629 const char* UTIL_getFileExtension(const char* infilename) 630 { 631 const char* extension = strrchr(infilename, '.'); 632 if(!extension || extension==infilename) return ""; 633 return extension; 634 } 635 636 637 FileNamesTable* 638 UTIL_createExpandedFNT(const char** inputNames, size_t nbIfns, int followLinks) 639 { 640 unsigned nbFiles; 641 char* buf = (char*)malloc(LIST_SIZE_INCREASE); 642 char* bufend = buf + LIST_SIZE_INCREASE; 643 644 if (!buf) return NULL; 645 646 { size_t ifnNb, pos; 647 for (ifnNb=0, pos=0, nbFiles=0; ifnNb<nbIfns; ifnNb++) { 648 if (!UTIL_isDirectory(inputNames[ifnNb])) { 649 size_t const len = strlen(inputNames[ifnNb]); 650 if (buf + pos + len >= bufend) { 651 ptrdiff_t newListSize = (bufend - buf) + LIST_SIZE_INCREASE; 652 assert(newListSize >= 0); 653 buf = (char*)UTIL_realloc(buf, (size_t)newListSize); 654 if (!buf) return NULL; 655 bufend = buf + newListSize; 656 } 657 if (buf + pos + len < bufend) { 658 memcpy(buf+pos, inputNames[ifnNb], len+1); /* including final \0 */ 659 pos += len + 1; 660 nbFiles++; 661 } 662 } else { 663 nbFiles += (unsigned)UTIL_prepareFileList(inputNames[ifnNb], &buf, &pos, &bufend, followLinks); 664 if (buf == NULL) return NULL; 665 } } } 666 667 /* note : even if nbFiles==0, function returns a valid, though empty, FileNamesTable* object */ 668 669 { size_t ifnNb, pos; 670 size_t const fntCapacity = nbFiles + 1; /* minimum 1, allows adding one reference, typically stdin */ 671 const char** const fileNamesTable = (const char**)malloc(fntCapacity * sizeof(*fileNamesTable)); 672 if (!fileNamesTable) { free(buf); return NULL; } 673 674 for (ifnNb = 0, pos = 0; ifnNb < nbFiles; ifnNb++) { 675 fileNamesTable[ifnNb] = buf + pos; 676 if (buf + pos > bufend) { free(buf); free((void*)fileNamesTable); return NULL; } 677 pos += strlen(fileNamesTable[ifnNb]) + 1; 678 } 679 return UTIL_assembleFileNamesTable2(fileNamesTable, nbFiles, fntCapacity, buf); 680 } 681 } 682 683 684 void UTIL_expandFNT(FileNamesTable** fnt, int followLinks) 685 { 686 FileNamesTable* const newFNT = UTIL_createExpandedFNT((*fnt)->fileNames, (*fnt)->tableSize, followLinks); 687 CONTROL(newFNT != NULL); 688 UTIL_freeFileNamesTable(*fnt); 689 *fnt = newFNT; 690 } 691 692 FileNamesTable* UTIL_createFNT_fromROTable(const char** filenames, size_t nbFilenames) 693 { 694 size_t const sizeof_FNTable = nbFilenames * sizeof(*filenames); 695 const char** const newFNTable = (const char**)malloc(sizeof_FNTable); 696 if (newFNTable==NULL) return NULL; 697 memcpy((void*)newFNTable, filenames, sizeof_FNTable); /* void* : mitigate a Visual compiler bug or limitation */ 698 return UTIL_assembleFileNamesTable(newFNTable, nbFilenames, NULL); 699 } 700 701 702 /*-**************************************** 703 * count the number of physical cores 704 ******************************************/ 705 706 #if defined(_WIN32) || defined(WIN32) 707 708 #include <windows.h> 709 710 typedef BOOL(WINAPI* LPFN_GLPI)(PSYSTEM_LOGICAL_PROCESSOR_INFORMATION, PDWORD); 711 712 int UTIL_countPhysicalCores(void) 713 { 714 static int numPhysicalCores = 0; 715 if (numPhysicalCores != 0) return numPhysicalCores; 716 717 { LPFN_GLPI glpi; 718 BOOL done = FALSE; 719 PSYSTEM_LOGICAL_PROCESSOR_INFORMATION buffer = NULL; 720 PSYSTEM_LOGICAL_PROCESSOR_INFORMATION ptr = NULL; 721 DWORD returnLength = 0; 722 size_t byteOffset = 0; 723 724 #if defined(_MSC_VER) 725 /* Visual Studio does not like the following cast */ 726 # pragma warning( disable : 4054 ) /* conversion from function ptr to data ptr */ 727 # pragma warning( disable : 4055 ) /* conversion from data ptr to function ptr */ 728 #endif 729 glpi = (LPFN_GLPI)(void*)GetProcAddress(GetModuleHandle(TEXT("kernel32")), 730 "GetLogicalProcessorInformation"); 731 732 if (glpi == NULL) { 733 goto failed; 734 } 735 736 while(!done) { 737 DWORD rc = glpi(buffer, &returnLength); 738 if (FALSE == rc) { 739 if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) { 740 if (buffer) 741 free(buffer); 742 buffer = (PSYSTEM_LOGICAL_PROCESSOR_INFORMATION)malloc(returnLength); 743 744 if (buffer == NULL) { 745 perror("zstd"); 746 exit(1); 747 } 748 } else { 749 /* some other error */ 750 goto failed; 751 } 752 } else { 753 done = TRUE; 754 } } 755 756 ptr = buffer; 757 758 while (byteOffset + sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION) <= returnLength) { 759 760 if (ptr->Relationship == RelationProcessorCore) { 761 numPhysicalCores++; 762 } 763 764 ptr++; 765 byteOffset += sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION); 766 } 767 768 free(buffer); 769 770 return numPhysicalCores; 771 } 772 773 failed: 774 /* try to fall back on GetSystemInfo */ 775 { SYSTEM_INFO sysinfo; 776 GetSystemInfo(&sysinfo); 777 numPhysicalCores = sysinfo.dwNumberOfProcessors; 778 if (numPhysicalCores == 0) numPhysicalCores = 1; /* just in case */ 779 } 780 return numPhysicalCores; 781 } 782 783 #elif defined(__APPLE__) 784 785 #include <sys/sysctl.h> 786 787 /* Use apple-provided syscall 788 * see: man 3 sysctl */ 789 int UTIL_countPhysicalCores(void) 790 { 791 static S32 numPhysicalCores = 0; /* apple specifies int32_t */ 792 if (numPhysicalCores != 0) return numPhysicalCores; 793 794 { size_t size = sizeof(S32); 795 int const ret = sysctlbyname("hw.physicalcpu", &numPhysicalCores, &size, NULL, 0); 796 if (ret != 0) { 797 if (errno == ENOENT) { 798 /* entry not present, fall back on 1 */ 799 numPhysicalCores = 1; 800 } else { 801 perror("zstd: can't get number of physical cpus"); 802 exit(1); 803 } 804 } 805 806 return numPhysicalCores; 807 } 808 } 809 810 #elif defined(__linux__) 811 812 /* parse /proc/cpuinfo 813 * siblings / cpu cores should give hyperthreading ratio 814 * otherwise fall back on sysconf */ 815 int UTIL_countPhysicalCores(void) 816 { 817 static int numPhysicalCores = 0; 818 819 if (numPhysicalCores != 0) return numPhysicalCores; 820 821 numPhysicalCores = (int)sysconf(_SC_NPROCESSORS_ONLN); 822 if (numPhysicalCores == -1) { 823 /* value not queryable, fall back on 1 */ 824 return numPhysicalCores = 1; 825 } 826 827 /* try to determine if there's hyperthreading */ 828 { FILE* const cpuinfo = fopen("/proc/cpuinfo", "r"); 829 #define BUF_SIZE 80 830 char buff[BUF_SIZE]; 831 832 int siblings = 0; 833 int cpu_cores = 0; 834 int ratio = 1; 835 836 if (cpuinfo == NULL) { 837 /* fall back on the sysconf value */ 838 return numPhysicalCores; 839 } 840 841 /* assume the cpu cores/siblings values will be constant across all 842 * present processors */ 843 while (!feof(cpuinfo)) { 844 if (fgets(buff, BUF_SIZE, cpuinfo) != NULL) { 845 if (strncmp(buff, "siblings", 8) == 0) { 846 const char* const sep = strchr(buff, ':'); 847 if (sep == NULL || *sep == '\0') { 848 /* formatting was broken? */ 849 goto failed; 850 } 851 852 siblings = atoi(sep + 1); 853 } 854 if (strncmp(buff, "cpu cores", 9) == 0) { 855 const char* const sep = strchr(buff, ':'); 856 if (sep == NULL || *sep == '\0') { 857 /* formatting was broken? */ 858 goto failed; 859 } 860 861 cpu_cores = atoi(sep + 1); 862 } 863 } else if (ferror(cpuinfo)) { 864 /* fall back on the sysconf value */ 865 goto failed; 866 } } 867 if (siblings && cpu_cores) { 868 ratio = siblings / cpu_cores; 869 } 870 failed: 871 fclose(cpuinfo); 872 return numPhysicalCores = numPhysicalCores / ratio; 873 } 874 } 875 876 #elif defined(__FreeBSD__) 877 878 #include <sys/param.h> 879 #include <sys/sysctl.h> 880 881 /* Use physical core sysctl when available 882 * see: man 4 smp, man 3 sysctl */ 883 int UTIL_countPhysicalCores(void) 884 { 885 static int numPhysicalCores = 0; /* freebsd sysctl is native int sized */ 886 if (numPhysicalCores != 0) return numPhysicalCores; 887 888 #if __FreeBSD_version >= 1300008 889 { size_t size = sizeof(numPhysicalCores); 890 int ret = sysctlbyname("kern.smp.cores", &numPhysicalCores, &size, NULL, 0); 891 if (ret == 0) return numPhysicalCores; 892 if (errno != ENOENT) { 893 perror("zstd: can't get number of physical cpus"); 894 exit(1); 895 } 896 /* sysctl not present, fall through to older sysconf method */ 897 } 898 #endif 899 900 numPhysicalCores = (int)sysconf(_SC_NPROCESSORS_ONLN); 901 if (numPhysicalCores == -1) { 902 /* value not queryable, fall back on 1 */ 903 numPhysicalCores = 1; 904 } 905 return numPhysicalCores; 906 } 907 908 #elif defined(__NetBSD__) || defined(__OpenBSD__) || defined(__DragonFly__) || defined(__CYGWIN__) 909 910 /* Use POSIX sysconf 911 * see: man 3 sysconf */ 912 int UTIL_countPhysicalCores(void) 913 { 914 static int numPhysicalCores = 0; 915 916 if (numPhysicalCores != 0) return numPhysicalCores; 917 918 numPhysicalCores = (int)sysconf(_SC_NPROCESSORS_ONLN); 919 if (numPhysicalCores == -1) { 920 /* value not queryable, fall back on 1 */ 921 return numPhysicalCores = 1; 922 } 923 return numPhysicalCores; 924 } 925 926 #else 927 928 int UTIL_countPhysicalCores(void) 929 { 930 /* assume 1 */ 931 return 1; 932 } 933 934 #endif 935 936 #if defined (__cplusplus) 937 } 938 #endif 939