[393] | 1 | /* $Id: kDep.c 3315 2020-03-31 01:12:19Z bird $ */
|
---|
| 2 | /** @file
|
---|
[785] | 3 | * kDep - Common Dependency Managemnt Code.
|
---|
[2019] | 4 | */
|
---|
| 5 |
|
---|
| 6 | /*
|
---|
[2851] | 7 | * Copyright (c) 2004-2013 knut st. osmundsen <bird-kBuild-spamx@anduin.net>
|
---|
[393] | 8 | *
|
---|
[2851] | 9 | * Permission is hereby granted, free of charge, to any person obtaining a
|
---|
| 10 | * copy of this software and associated documentation files (the "Software"),
|
---|
| 11 | * to deal in the Software without restriction, including without limitation
|
---|
| 12 | * the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
---|
| 13 | * and/or sell copies of the Software, and to permit persons to whom the
|
---|
| 14 | * Software is furnished to do so, subject to the following conditions:
|
---|
[393] | 15 | *
|
---|
[2851] | 16 | * The above copyright notice and this permission notice shall be included
|
---|
| 17 | * in all copies or substantial portions of the Software.
|
---|
[393] | 18 | *
|
---|
[2851] | 19 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
---|
| 20 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
---|
| 21 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
---|
| 22 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
---|
| 23 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
---|
| 24 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
---|
| 25 | * IN THE SOFTWARE.
|
---|
[393] | 26 | *
|
---|
[2851] | 27 | * Alternatively, the content of this file may be used under the terms of the
|
---|
| 28 | * GPL version 2 or later, or LGPL version 2.1 or later.
|
---|
[393] | 29 | */
|
---|
| 30 |
|
---|
[2851] | 31 |
|
---|
[393] | 32 | /*******************************************************************************
|
---|
| 33 | * Header Files *
|
---|
| 34 | *******************************************************************************/
|
---|
[2886] | 35 | #ifdef KMK /* For when it gets compiled and linked into kmk. */
|
---|
[3140] | 36 | # include "makeint.h"
|
---|
[2886] | 37 | #endif
|
---|
[393] | 38 | #include <stdio.h>
|
---|
| 39 | #include <stdlib.h>
|
---|
| 40 | #include <string.h>
|
---|
| 41 | #include <errno.h>
|
---|
| 42 | #include <ctype.h>
|
---|
| 43 | #include <limits.h>
|
---|
| 44 | #include <sys/stat.h>
|
---|
[1284] | 45 | #include "k/kDefs.h"
|
---|
[2263] | 46 | #include "k/kTypes.h"
|
---|
[1283] | 47 | #if K_OS == K_OS_WINDOWS
|
---|
[2263] | 48 | # define USE_WIN_MMAP
|
---|
| 49 | # include <io.h>
|
---|
| 50 | # include <Windows.h>
|
---|
[2886] | 51 | # include "nt_fullpath.h"
|
---|
| 52 | # include "nt/ntstat.h"
|
---|
[1283] | 53 | #else
|
---|
[393] | 54 | # include <dirent.h>
|
---|
| 55 | # include <unistd.h>
|
---|
| 56 | # include <stdint.h>
|
---|
| 57 | #endif
|
---|
| 58 |
|
---|
| 59 | #include "kDep.h"
|
---|
| 60 |
|
---|
[2948] | 61 | #ifdef KWORKER
|
---|
| 62 | extern int kwFsPathExists(const char *pszPath);
|
---|
| 63 | #endif
|
---|
[393] | 64 |
|
---|
[2948] | 65 |
|
---|
[3105] | 66 | /*********************************************************************************************************************************
|
---|
| 67 | * Defined Constants And Macros *
|
---|
| 68 | *********************************************************************************************************************************/
|
---|
| 69 | /* For the GNU/hurd weirdo. */
|
---|
| 70 | #if !defined(PATH_MAX) && !defined(_MAX_PATH)
|
---|
[3114] | 71 | # define PATH_MAX 4096
|
---|
[3105] | 72 | #endif
|
---|
| 73 |
|
---|
| 74 |
|
---|
[3167] | 75 | /**
|
---|
| 76 | * Initializes the dep instance.
|
---|
| 77 | *
|
---|
| 78 | * @param pThis The dep instance to init.
|
---|
| 79 | */
|
---|
| 80 | void depInit(PDEPGLOBALS pThis)
|
---|
| 81 | {
|
---|
| 82 | pThis->pDeps = NULL;
|
---|
| 83 | }
|
---|
[393] | 84 |
|
---|
| 85 |
|
---|
[407] | 86 | /**
|
---|
[3167] | 87 | * Cleans up the dep instance (frees resources).
|
---|
| 88 | *
|
---|
| 89 | * @param pThis The dep instance to cleanup.
|
---|
| 90 | */
|
---|
| 91 | void depCleanup(PDEPGLOBALS pThis)
|
---|
| 92 | {
|
---|
| 93 | PDEP pDep = pThis->pDeps;
|
---|
| 94 | pThis->pDeps = NULL;
|
---|
| 95 | while (pDep)
|
---|
| 96 | {
|
---|
| 97 | PDEP pFree = pDep;
|
---|
| 98 | pDep = pDep->pNext;
|
---|
| 99 | free(pFree);
|
---|
| 100 | }
|
---|
| 101 | }
|
---|
| 102 |
|
---|
| 103 |
|
---|
| 104 | /**
|
---|
[407] | 105 | * Corrects all slashes to unix slashes.
|
---|
| 106 | *
|
---|
| 107 | * @returns pszFilename.
|
---|
| 108 | * @param pszFilename The filename to correct.
|
---|
| 109 | */
|
---|
| 110 | static char *fixslash(char *pszFilename)
|
---|
| 111 | {
|
---|
| 112 | char *psz = pszFilename;
|
---|
| 113 | while ((psz = strchr(psz, '\\')) != NULL)
|
---|
| 114 | *psz++ = '/';
|
---|
| 115 | return pszFilename;
|
---|
| 116 | }
|
---|
| 117 |
|
---|
| 118 |
|
---|
[1283] | 119 | #if K_OS == K_OS_OS2
|
---|
[393] | 120 |
|
---|
| 121 | /**
|
---|
| 122 | * Corrects the case of a path.
|
---|
| 123 | *
|
---|
| 124 | * @param pszPath Pointer to the path, both input and output.
|
---|
| 125 | * The buffer must be able to hold one more byte than the string length.
|
---|
| 126 | */
|
---|
| 127 | static void fixcase(char *pszFilename)
|
---|
| 128 | {
|
---|
| 129 | return;
|
---|
| 130 | }
|
---|
| 131 |
|
---|
[1283] | 132 | #elif K_OS != K_OS_WINDOWS
|
---|
[393] | 133 |
|
---|
| 134 | /**
|
---|
| 135 | * Corrects the case of a path.
|
---|
| 136 | *
|
---|
| 137 | * @param pszPath Pointer to the path, both input and output.
|
---|
| 138 | */
|
---|
| 139 | static void fixcase(char *pszFilename)
|
---|
| 140 | {
|
---|
| 141 | char *psz;
|
---|
| 142 |
|
---|
| 143 | /*
|
---|
| 144 | * Skip the root.
|
---|
| 145 | */
|
---|
| 146 | psz = pszFilename;
|
---|
| 147 | while (*psz == '/')
|
---|
| 148 | psz++;
|
---|
| 149 |
|
---|
| 150 | /*
|
---|
| 151 | * Iterate all the components.
|
---|
| 152 | */
|
---|
| 153 | while (*psz)
|
---|
| 154 | {
|
---|
| 155 | char chSlash;
|
---|
| 156 | struct stat s;
|
---|
| 157 | char *pszStart = psz;
|
---|
| 158 |
|
---|
| 159 | /*
|
---|
| 160 | * Find the next slash (or end of string) and terminate the string there.
|
---|
| 161 | */
|
---|
| 162 | while (*psz != '/' && *psz)
|
---|
[3063] | 163 | psz++;
|
---|
[393] | 164 | chSlash = *psz;
|
---|
| 165 | *psz = '\0';
|
---|
| 166 |
|
---|
| 167 | /*
|
---|
| 168 | * Does this part exist?
|
---|
| 169 | * If not we'll enumerate the directory and search for an case-insensitive match.
|
---|
| 170 | */
|
---|
| 171 | if (stat(pszFilename, &s))
|
---|
| 172 | {
|
---|
| 173 | struct dirent *pEntry;
|
---|
| 174 | DIR *pDir;
|
---|
| 175 | if (pszStart == pszFilename)
|
---|
| 176 | pDir = opendir(*pszFilename ? pszFilename : ".");
|
---|
| 177 | else
|
---|
| 178 | {
|
---|
| 179 | pszStart[-1] = '\0';
|
---|
| 180 | pDir = opendir(pszFilename);
|
---|
| 181 | pszStart[-1] = '/';
|
---|
| 182 | }
|
---|
| 183 | if (!pDir)
|
---|
| 184 | {
|
---|
| 185 | *psz = chSlash;
|
---|
| 186 | break; /* giving up, if we fail to open the directory. */
|
---|
| 187 | }
|
---|
| 188 |
|
---|
| 189 | while ((pEntry = readdir(pDir)) != NULL)
|
---|
| 190 | {
|
---|
| 191 | if (!strcasecmp(pEntry->d_name, pszStart))
|
---|
| 192 | {
|
---|
| 193 | strcpy(pszStart, pEntry->d_name);
|
---|
| 194 | break;
|
---|
| 195 | }
|
---|
| 196 | }
|
---|
| 197 | closedir(pDir);
|
---|
| 198 | if (!pEntry)
|
---|
| 199 | {
|
---|
| 200 | *psz = chSlash;
|
---|
| 201 | break; /* giving up if not found. */
|
---|
| 202 | }
|
---|
| 203 | }
|
---|
| 204 |
|
---|
| 205 | /* restore the slash and press on. */
|
---|
| 206 | *psz = chSlash;
|
---|
| 207 | while (*psz == '/')
|
---|
| 208 | psz++;
|
---|
| 209 | }
|
---|
| 210 |
|
---|
| 211 | return;
|
---|
| 212 | }
|
---|
| 213 |
|
---|
[1283] | 214 | #endif /* !OS/2 && !Windows */
|
---|
[393] | 215 |
|
---|
| 216 |
|
---|
| 217 | /**
|
---|
| 218 | * 'Optimizes' and corrects the dependencies.
|
---|
| 219 | */
|
---|
[3167] | 220 | void depOptimize(PDEPGLOBALS pThis, int fFixCase, int fQuiet, const char *pszIgnoredExt)
|
---|
[393] | 221 | {
|
---|
| 222 | /*
|
---|
| 223 | * Walk the list correct the names and re-insert them.
|
---|
| 224 | */
|
---|
[2955] | 225 | size_t cchIgnoredExt = pszIgnoredExt ? strlen(pszIgnoredExt) : 0;
|
---|
[3167] | 226 | PDEP pDepOrg = pThis->pDeps;
|
---|
| 227 | PDEP pDep = pThis->pDeps;
|
---|
| 228 | pThis->pDeps = NULL;
|
---|
[393] | 229 | for (; pDep; pDep = pDep->pNext)
|
---|
| 230 | {
|
---|
[1283] | 231 | #ifndef PATH_MAX
|
---|
[393] | 232 | char szFilename[_MAX_PATH + 1];
|
---|
| 233 | #else
|
---|
| 234 | char szFilename[PATH_MAX + 1];
|
---|
| 235 | #endif
|
---|
| 236 | char *pszFilename;
|
---|
[2950] | 237 | #if !defined(KWORKER) && !defined(KMK)
|
---|
[393] | 238 | struct stat s;
|
---|
[2886] | 239 | #endif
|
---|
[393] | 240 |
|
---|
| 241 | /*
|
---|
| 242 | * Skip some fictive names like <built-in> and <command line>.
|
---|
| 243 | */
|
---|
| 244 | if ( pDep->szFilename[0] == '<'
|
---|
| 245 | && pDep->szFilename[pDep->cchFilename - 1] == '>')
|
---|
| 246 | continue;
|
---|
| 247 | pszFilename = pDep->szFilename;
|
---|
| 248 |
|
---|
[2955] | 249 | /*
|
---|
| 250 | * Skip pszIgnoredExt if given.
|
---|
| 251 | */
|
---|
| 252 | if ( pszIgnoredExt
|
---|
| 253 | && pDep->cchFilename > cchIgnoredExt
|
---|
| 254 | && memcmp(&pDep->szFilename[pDep->cchFilename - cchIgnoredExt], pszIgnoredExt, cchIgnoredExt) == 0)
|
---|
| 255 | continue;
|
---|
| 256 |
|
---|
[1283] | 257 | #if K_OS != K_OS_OS2 && K_OS != K_OS_WINDOWS
|
---|
[393] | 258 | /*
|
---|
| 259 | * Skip any drive letters from compilers running in wine.
|
---|
| 260 | */
|
---|
| 261 | if (pszFilename[1] == ':')
|
---|
| 262 | pszFilename += 2;
|
---|
| 263 | #endif
|
---|
| 264 |
|
---|
| 265 | /*
|
---|
| 266 | * The microsoft compilers are notoriously screwing up the casing.
|
---|
| 267 | * This will screw up kmk (/ GNU Make).
|
---|
| 268 | */
|
---|
| 269 | if (fFixCase)
|
---|
| 270 | {
|
---|
[1283] | 271 | #if K_OS == K_OS_WINDOWS
|
---|
[2886] | 272 | nt_fullpath_cached(pszFilename, szFilename, sizeof(szFilename));
|
---|
[1285] | 273 | fixslash(szFilename);
|
---|
[1165] | 274 | #else
|
---|
[1177] | 275 | strcpy(szFilename, pszFilename);
|
---|
[404] | 276 | fixslash(szFilename);
|
---|
[393] | 277 | fixcase(szFilename);
|
---|
[1165] | 278 | #endif
|
---|
[393] | 279 | pszFilename = szFilename;
|
---|
| 280 | }
|
---|
| 281 |
|
---|
| 282 | /*
|
---|
| 283 | * Check that the file exists before we start depending on it.
|
---|
| 284 | */
|
---|
[3174] | 285 | errno = 0;
|
---|
[2948] | 286 | #ifdef KWORKER
|
---|
[2950] | 287 | if (!kwFsPathExists(pszFilename))
|
---|
[2948] | 288 | #elif defined(KMK)
|
---|
[2886] | 289 | if (!file_exists_p(pszFilename))
|
---|
| 290 | #elif K_OS == K_OS_WINDOWS
|
---|
| 291 | if (birdStatModTimeOnly(pszFilename, &s.st_mtim, 1 /*fFollowLink*/) != 0)
|
---|
| 292 | #else
|
---|
| 293 | if (stat(pszFilename, &s) != 0)
|
---|
| 294 | #endif
|
---|
[393] | 295 | {
|
---|
[2104] | 296 | if ( !fQuiet
|
---|
| 297 | || errno != ENOENT
|
---|
| 298 | || ( pszFilename[0] != '/'
|
---|
| 299 | && pszFilename[0] != '\\'
|
---|
| 300 | && ( !isalpha(pszFilename[0])
|
---|
| 301 | || pszFilename[1] != ':'
|
---|
| 302 | || ( pszFilename[2] != '/'
|
---|
| 303 | && pszFilename[2] != '\\')))
|
---|
| 304 | )
|
---|
| 305 | fprintf(stderr, "kDep: Skipping '%s' - %s!\n", pszFilename, strerror(errno));
|
---|
[393] | 306 | continue;
|
---|
| 307 | }
|
---|
| 308 |
|
---|
| 309 | /*
|
---|
| 310 | * Insert the corrected dependency.
|
---|
| 311 | */
|
---|
[3167] | 312 | depAdd(pThis, pszFilename, strlen(pszFilename));
|
---|
[393] | 313 | }
|
---|
| 314 |
|
---|
| 315 | /*
|
---|
| 316 | * Free the old ones.
|
---|
| 317 | */
|
---|
| 318 | while (pDepOrg)
|
---|
| 319 | {
|
---|
| 320 | pDep = pDepOrg;
|
---|
| 321 | pDepOrg = pDepOrg->pNext;
|
---|
| 322 | free(pDep);
|
---|
| 323 | }
|
---|
| 324 | }
|
---|
| 325 |
|
---|
| 326 |
|
---|
| 327 | /**
|
---|
[3315] | 328 | * Write a filename that contains characters that needs escaping.
|
---|
| 329 | *
|
---|
| 330 | * @param pOutput The output stream.
|
---|
| 331 | * @param pszFile The filename.
|
---|
| 332 | * @param cchFile The length of the filename.
|
---|
| 333 | * @param fDep Whether this is for a dependency file or a target file.
|
---|
| 334 | */
|
---|
| 335 | int depNeedsEscaping(const char *pszFile, size_t cchFile, int fDependency)
|
---|
| 336 | {
|
---|
| 337 | return memchr(pszFile, ' ', cchFile) != NULL
|
---|
| 338 | || memchr(pszFile, '\t', cchFile) != NULL
|
---|
| 339 | || memchr(pszFile, '#', cchFile) != NULL
|
---|
| 340 | || memchr(pszFile, '=', cchFile) != NULL
|
---|
| 341 | || memchr(pszFile, ';', cchFile) != NULL
|
---|
| 342 | || memchr(pszFile, '$', cchFile) != NULL
|
---|
| 343 | || memchr(pszFile, fDependency ? '|' : '%', cchFile) != NULL;
|
---|
| 344 | }
|
---|
| 345 |
|
---|
| 346 |
|
---|
| 347 | /**
|
---|
| 348 | * Write a filename that contains characters that needs escaping.
|
---|
| 349 | *
|
---|
| 350 | * @param pOutput The output stream.
|
---|
| 351 | * @param pszFile The filename.
|
---|
| 352 | * @param cchFile The length of the filename.
|
---|
| 353 | * @param fDep Whether this is for a dependency file or a target file.
|
---|
| 354 | */
|
---|
| 355 | void depEscapedWrite(FILE *pOutput, const char *pszFile, size_t cchFile, int fDepenency)
|
---|
| 356 | {
|
---|
| 357 | size_t cchWritten = 0;
|
---|
| 358 | size_t off = 0;
|
---|
| 359 | while (off < cchFile)
|
---|
| 360 | {
|
---|
| 361 | char const ch = pszFile[off];
|
---|
| 362 | switch (ch)
|
---|
| 363 | {
|
---|
| 364 | default:
|
---|
| 365 | off++;
|
---|
| 366 | break;
|
---|
| 367 |
|
---|
| 368 | /*
|
---|
| 369 | * Escaped by slash, but any preceeding slashes must be escaped too.
|
---|
| 370 | * A couple of characters are only escaped on one side of the ':'.
|
---|
| 371 | */
|
---|
| 372 | case '%': /* target side only */
|
---|
| 373 | case '|': /* dependency side only */
|
---|
| 374 | if (ch != (fDepenency ? '|' : '%'))
|
---|
| 375 | {
|
---|
| 376 | off++;
|
---|
| 377 | break;
|
---|
| 378 | }
|
---|
| 379 | /* fall thru */
|
---|
| 380 | case ' ':
|
---|
| 381 | case '\t':
|
---|
| 382 | case '#':
|
---|
| 383 | case '=': /** @todo buggy GNU make handling */
|
---|
| 384 | case ';': /** @todo buggy GNU make handling */
|
---|
| 385 | if (cchWritten < off)
|
---|
| 386 | fwrite(&pszFile[cchWritten], off - cchWritten, 1, pOutput);
|
---|
| 387 | if (off == 0 || pszFile[off - 1] != '\\')
|
---|
| 388 | {
|
---|
| 389 | fputc('\\', pOutput);
|
---|
| 390 | cchWritten = off; /* We write the escaped character with the next bunch. */
|
---|
| 391 | }
|
---|
| 392 | else
|
---|
| 393 | {
|
---|
| 394 | size_t cchSlashes = 1;
|
---|
| 395 | while (cchSlashes < off && pszFile[off - cchSlashes - 1] == '\\')
|
---|
| 396 | cchSlashes++;
|
---|
| 397 | fwrite(&pszFile[off - cchSlashes], cchSlashes, 1, pOutput);
|
---|
| 398 | cchWritten = off - 1; /* Write a preceeding slash and the escaped character with the next bunch. */
|
---|
| 399 | }
|
---|
| 400 | off += 1;
|
---|
| 401 | break;
|
---|
| 402 |
|
---|
| 403 | /*
|
---|
| 404 | * Escaped by doubling it.
|
---|
| 405 | * Implemented by including in the pending writeout job as well as in the next one.
|
---|
| 406 | */
|
---|
| 407 | case '$':
|
---|
| 408 | fwrite(&pszFile[cchWritten], off - cchWritten + 1, 1, pOutput);
|
---|
| 409 | cchWritten = off++; /* write it again the next time */
|
---|
| 410 | break;
|
---|
| 411 | }
|
---|
| 412 | }
|
---|
| 413 |
|
---|
| 414 | /* Remainder: */
|
---|
| 415 | if (cchWritten < cchFile)
|
---|
| 416 | fwrite(&pszFile[cchWritten], cchFile - cchWritten, 1, pOutput);
|
---|
| 417 | }
|
---|
| 418 |
|
---|
| 419 |
|
---|
| 420 | /**
|
---|
| 421 | * Escapes all trailing trailing slashes in a filename that ends with such.
|
---|
| 422 | */
|
---|
| 423 | static void depPrintTrailngSlashEscape(FILE *pOutput, const char *pszFilename, size_t cchFilename)
|
---|
| 424 | {
|
---|
| 425 | size_t cchSlashes = 1;
|
---|
| 426 | while (cchSlashes < cchFilename && pszFilename[cchFilename - cchSlashes - 1] == '\\')
|
---|
| 427 | cchSlashes++;
|
---|
| 428 | fwrite(&pszFilename[cchFilename - cchSlashes], cchSlashes, 1, pOutput);
|
---|
| 429 | }
|
---|
| 430 |
|
---|
| 431 |
|
---|
| 432 | /**
|
---|
[393] | 433 | * Prints the dependency chain.
|
---|
| 434 | *
|
---|
[3167] | 435 | * @param pThis The 'dep' instance.
|
---|
[393] | 436 | * @param pOutput Output stream.
|
---|
| 437 | */
|
---|
[3315] | 438 | void depPrintChain(PDEPGLOBALS pThis, FILE *pOutput)
|
---|
[393] | 439 | {
|
---|
[3315] | 440 | static char const g_szEntryText[] = " \\\n\t";
|
---|
| 441 | static char const g_szTailText[] = "\n\n";
|
---|
| 442 | static char const g_szTailSlashText[] = " \\\n\n";
|
---|
| 443 | PDEP pDep;
|
---|
[3167] | 444 | for (pDep = pThis->pDeps; pDep; pDep = pDep->pNext)
|
---|
[3315] | 445 | {
|
---|
| 446 | fwrite(g_szEntryText, sizeof(g_szEntryText) - 1, 1, pOutput);
|
---|
| 447 | if (!pDep->fNeedsEscaping)
|
---|
| 448 | fwrite(pDep->szFilename, pDep->cchFilename, 1, pOutput);
|
---|
| 449 | else
|
---|
| 450 | depEscapedWrite(pOutput, pDep->szFilename, pDep->cchFilename, 1 /*fDependency*/);
|
---|
| 451 | if (pDep->fTrailingSlash)
|
---|
| 452 | { /* Escape only if more dependencies. If last, we must add a line continuation or it won't work. */
|
---|
| 453 | if (pDep->pNext)
|
---|
| 454 | depPrintTrailngSlashEscape(pOutput, pDep->szFilename, pDep->cchFilename);
|
---|
| 455 | else
|
---|
| 456 | {
|
---|
| 457 | fwrite(g_szTailSlashText, sizeof(g_szTailSlashText), 1, pOutput);
|
---|
| 458 | return;
|
---|
| 459 | }
|
---|
| 460 | }
|
---|
| 461 | }
|
---|
| 462 |
|
---|
| 463 | fwrite(g_szTailText, sizeof(g_szTailText) - 1, 1, pOutput);
|
---|
[393] | 464 | }
|
---|
| 465 |
|
---|
| 466 |
|
---|
| 467 | /**
|
---|
[3315] | 468 | * Prints the dependency chain with a preceeding target.
|
---|
| 469 | *
|
---|
| 470 | * @param pThis The 'dep' instance.
|
---|
| 471 | * @param pOutput Output stream.
|
---|
| 472 | * @param pszTarget The target filename.
|
---|
| 473 | * @param fEscapeTarget Whether to consider escaping the target.
|
---|
| 474 | */
|
---|
| 475 | void depPrintTargetWithDeps(PDEPGLOBALS pThis, FILE *pOutput, const char *pszTarget, int fEscapeTarget)
|
---|
| 476 | {
|
---|
| 477 | static char const g_szSeparator[] = ":";
|
---|
| 478 | size_t const cchTarget = strlen(pszTarget);
|
---|
| 479 | if (!fEscapeTarget || !depNeedsEscaping(pszTarget, cchTarget, 0 /*fDependency*/))
|
---|
| 480 | fwrite(pszTarget, cchTarget, 1, pOutput);
|
---|
| 481 | else
|
---|
| 482 | depEscapedWrite(pOutput, pszTarget, cchTarget, 0 /*fDependency*/);
|
---|
| 483 |
|
---|
| 484 | if (cchTarget == 0 || pszTarget[cchTarget - 1] != '\\')
|
---|
| 485 | { /* likely */ }
|
---|
| 486 | else
|
---|
| 487 | depPrintTrailngSlashEscape(pOutput, pszTarget, cchTarget);
|
---|
| 488 | fwrite(g_szSeparator, sizeof(g_szSeparator) - 1, 1, pOutput);
|
---|
| 489 |
|
---|
| 490 | depPrintChain(pThis, pOutput);
|
---|
| 491 | }
|
---|
| 492 |
|
---|
| 493 |
|
---|
| 494 | /**
|
---|
[393] | 495 | * Prints empty dependency stubs for all dependencies.
|
---|
[3167] | 496 | *
|
---|
| 497 | * @param pThis The 'dep' instance.
|
---|
| 498 | * @param pOutput Output stream.
|
---|
[393] | 499 | */
|
---|
[3167] | 500 | void depPrintStubs(PDEPGLOBALS pThis, FILE *pOutput)
|
---|
[393] | 501 | {
|
---|
[3315] | 502 | static char g_szTailText[] = ":\n\n";
|
---|
[393] | 503 | PDEP pDep;
|
---|
[3167] | 504 | for (pDep = pThis->pDeps; pDep; pDep = pDep->pNext)
|
---|
[3315] | 505 | {
|
---|
| 506 | if (!pDep->fNeedsEscaping && memchr(pDep->szFilename, '%', pDep->cchFilename) == 0)
|
---|
| 507 | fwrite(pDep->szFilename, pDep->cchFilename, 1, pOutput);
|
---|
| 508 | else
|
---|
| 509 | depEscapedWrite(pOutput, pDep->szFilename, pDep->cchFilename, 0 /*fDependency*/);
|
---|
| 510 |
|
---|
| 511 | if (pDep->cchFilename == 0 || !pDep->fTrailingSlash)
|
---|
| 512 | { /* likely */ }
|
---|
| 513 | else
|
---|
| 514 | depPrintTrailngSlashEscape(pOutput, pDep->szFilename, pDep->cchFilename);
|
---|
| 515 | fwrite(g_szTailText, sizeof(g_szTailText) - 1, 1, pOutput);
|
---|
| 516 | }
|
---|
[393] | 517 | }
|
---|
| 518 |
|
---|
| 519 |
|
---|
| 520 | /* sdbm:
|
---|
| 521 | This algorithm was created for sdbm (a public-domain reimplementation of
|
---|
| 522 | ndbm) database library. it was found to do well in scrambling bits,
|
---|
| 523 | causing better distribution of the keys and fewer splits. it also happens
|
---|
| 524 | to be a good general hashing function with good distribution. the actual
|
---|
| 525 | function is hash(i) = hash(i - 1) * 65599 + str[i]; what is included below
|
---|
| 526 | is the faster version used in gawk. [there is even a faster, duff-device
|
---|
| 527 | version] the magic constant 65599 was picked out of thin air while
|
---|
| 528 | experimenting with different constants, and turns out to be a prime.
|
---|
| 529 | this is one of the algorithms used in berkeley db (see sleepycat) and
|
---|
| 530 | elsewhere. */
|
---|
[2263] | 531 | static unsigned sdbm(const char *str, size_t size)
|
---|
[393] | 532 | {
|
---|
| 533 | unsigned hash = 0;
|
---|
| 534 | int c;
|
---|
| 535 |
|
---|
[2263] | 536 | while (size-- > 0 && (c = *(unsigned const char *)str++))
|
---|
[393] | 537 | hash = c + (hash << 6) + (hash << 16) - hash;
|
---|
| 538 |
|
---|
| 539 | return hash;
|
---|
| 540 | }
|
---|
| 541 |
|
---|
| 542 |
|
---|
| 543 | /**
|
---|
| 544 | * Adds a dependency.
|
---|
| 545 | *
|
---|
| 546 | * @returns Pointer to the allocated dependency.
|
---|
[3167] | 547 | * @param pThis The 'dep' instance.
|
---|
[2263] | 548 | * @param pszFilename The filename. Does not need to be terminated.
|
---|
[393] | 549 | * @param cchFilename The length of the filename.
|
---|
| 550 | */
|
---|
[3167] | 551 | PDEP depAdd(PDEPGLOBALS pThis, const char *pszFilename, size_t cchFilename)
|
---|
[393] | 552 | {
|
---|
[2263] | 553 | unsigned uHash = sdbm(pszFilename, cchFilename);
|
---|
| 554 | PDEP pDep;
|
---|
| 555 | PDEP pDepPrev;
|
---|
[393] | 556 |
|
---|
| 557 | /*
|
---|
| 558 | * Check if we've already got this one.
|
---|
| 559 | */
|
---|
| 560 | pDepPrev = NULL;
|
---|
[3167] | 561 | for (pDep = pThis->pDeps; pDep; pDepPrev = pDep, pDep = pDep->pNext)
|
---|
[393] | 562 | if ( pDep->uHash == uHash
|
---|
| 563 | && pDep->cchFilename == cchFilename
|
---|
| 564 | && !memcmp(pDep->szFilename, pszFilename, cchFilename))
|
---|
| 565 | return pDep;
|
---|
| 566 |
|
---|
| 567 | /*
|
---|
| 568 | * Add it.
|
---|
| 569 | */
|
---|
| 570 | pDep = (PDEP)malloc(sizeof(*pDep) + cchFilename);
|
---|
| 571 | if (!pDep)
|
---|
| 572 | {
|
---|
[2019] | 573 | fprintf(stderr, "\nOut of memory! (requested %lx bytes)\n\n",
|
---|
[1733] | 574 | (unsigned long)(sizeof(*pDep) + cchFilename));
|
---|
[393] | 575 | exit(1);
|
---|
| 576 | }
|
---|
| 577 |
|
---|
| 578 | pDep->cchFilename = cchFilename;
|
---|
[2263] | 579 | memcpy(pDep->szFilename, pszFilename, cchFilename);
|
---|
| 580 | pDep->szFilename[cchFilename] = '\0';
|
---|
[3315] | 581 | pDep->fNeedsEscaping = depNeedsEscaping(pszFilename, cchFilename, 1 /*fDependency*/);
|
---|
| 582 | pDep->fTrailingSlash = cchFilename > 0 && pszFilename[cchFilename - 1] == '\\';
|
---|
[393] | 583 | pDep->uHash = uHash;
|
---|
| 584 |
|
---|
| 585 | if (pDepPrev)
|
---|
| 586 | {
|
---|
| 587 | pDep->pNext = pDepPrev->pNext;
|
---|
| 588 | pDepPrev->pNext = pDep;
|
---|
| 589 | }
|
---|
| 590 | else
|
---|
| 591 | {
|
---|
[3167] | 592 | pDep->pNext = pThis->pDeps;
|
---|
| 593 | pThis->pDeps = pDep;
|
---|
[393] | 594 | }
|
---|
| 595 | return pDep;
|
---|
| 596 | }
|
---|
[1165] | 597 |
|
---|
| 598 |
|
---|
| 599 | /**
|
---|
[2263] | 600 | * Performs a hexdump.
|
---|
| 601 | */
|
---|
| 602 | void depHexDump(const KU8 *pb, size_t cb, size_t offBase)
|
---|
| 603 | {
|
---|
| 604 | const unsigned cchWidth = 16;
|
---|
| 605 | size_t off = 0;
|
---|
| 606 | while (off < cb)
|
---|
| 607 | {
|
---|
| 608 | unsigned i;
|
---|
[2378] | 609 | printf("%s%0*lx %04lx:", off ? "\n" : "", (int)sizeof(pb) * 2,
|
---|
| 610 | (unsigned long)offBase + (unsigned long)off, (unsigned long)off);
|
---|
[2263] | 611 | for (i = 0; i < cchWidth && off + i < cb ; i++)
|
---|
| 612 | printf(off + i < cb ? !(i & 7) && i ? "-%02x" : " %02x" : " ", pb[i]);
|
---|
| 613 |
|
---|
| 614 | while (i++ < cchWidth)
|
---|
| 615 | printf(" ");
|
---|
| 616 | printf(" ");
|
---|
| 617 |
|
---|
| 618 | for (i = 0; i < cchWidth && off + i < cb; i++)
|
---|
| 619 | {
|
---|
| 620 | const KU8 u8 = pb[i];
|
---|
[2270] | 621 | printf("%c", u8 < 127 && u8 >= 32 ? u8 : '.');
|
---|
[2263] | 622 | }
|
---|
| 623 | off += cchWidth;
|
---|
| 624 | pb += cchWidth;
|
---|
| 625 | }
|
---|
| 626 | printf("\n");
|
---|
| 627 | }
|
---|
| 628 |
|
---|
| 629 |
|
---|
| 630 | /**
|
---|
| 631 | * Reads the file specified by the pInput file stream into memory.
|
---|
| 632 | *
|
---|
| 633 | * @returns The address of the memory mapping on success. This must be
|
---|
| 634 | * freed by calling depFreeFileMemory.
|
---|
| 635 | *
|
---|
| 636 | * @param pInput The file stream to load or map into memory.
|
---|
| 637 | * @param pcbFile Where to return the mapping (file) size.
|
---|
| 638 | * @param ppvOpaque Opaque data when mapping, otherwise NULL.
|
---|
| 639 | */
|
---|
| 640 | void *depReadFileIntoMemory(FILE *pInput, size_t *pcbFile, void **ppvOpaque)
|
---|
| 641 | {
|
---|
| 642 | void *pvFile;
|
---|
| 643 | long cbFile;
|
---|
| 644 |
|
---|
| 645 | /*
|
---|
| 646 | * Figure out file size.
|
---|
| 647 | */
|
---|
| 648 | #if defined(_MSC_VER)
|
---|
| 649 | cbFile = _filelength(fileno(pInput));
|
---|
| 650 | if (cbFile < 0)
|
---|
| 651 | #else
|
---|
| 652 | if ( fseek(pInput, 0, SEEK_END) < 0
|
---|
| 653 | || (cbFile = ftell(pInput)) < 0
|
---|
| 654 | || fseek(pInput, 0, SEEK_SET))
|
---|
| 655 | #endif
|
---|
| 656 | {
|
---|
| 657 | fprintf(stderr, "kDep: error: Failed to determin file size.\n");
|
---|
| 658 | return NULL;
|
---|
| 659 | }
|
---|
| 660 | if (pcbFile)
|
---|
| 661 | *pcbFile = cbFile;
|
---|
| 662 |
|
---|
| 663 | /*
|
---|
| 664 | * Try mmap first.
|
---|
| 665 | */
|
---|
| 666 | #ifdef USE_WIN_MMAP
|
---|
| 667 | {
|
---|
| 668 | HANDLE hMapObj = CreateFileMapping((HANDLE)_get_osfhandle(fileno(pInput)),
|
---|
| 669 | NULL, PAGE_READONLY, 0, cbFile, NULL);
|
---|
| 670 | if (hMapObj != NULL)
|
---|
| 671 | {
|
---|
| 672 | pvFile = MapViewOfFile(hMapObj, FILE_MAP_READ, 0, 0, cbFile);
|
---|
| 673 | if (pvFile)
|
---|
| 674 | {
|
---|
| 675 | *ppvOpaque = hMapObj;
|
---|
| 676 | return pvFile;
|
---|
| 677 | }
|
---|
| 678 | fprintf(stderr, "kDep: warning: MapViewOfFile failed, %d.\n", GetLastError());
|
---|
| 679 | CloseHandle(hMapObj);
|
---|
| 680 | }
|
---|
| 681 | else
|
---|
| 682 | fprintf(stderr, "kDep: warning: CreateFileMapping failed, %d.\n", GetLastError());
|
---|
| 683 | }
|
---|
| 684 |
|
---|
| 685 | #endif
|
---|
| 686 |
|
---|
| 687 | /*
|
---|
| 688 | * Allocate memory and read the file.
|
---|
| 689 | */
|
---|
| 690 | pvFile = malloc(cbFile + 1);
|
---|
| 691 | if (pvFile)
|
---|
| 692 | {
|
---|
| 693 | if (fread(pvFile, cbFile, 1, pInput))
|
---|
| 694 | {
|
---|
| 695 | ((KU8 *)pvFile)[cbFile] = '\0';
|
---|
| 696 | *ppvOpaque = NULL;
|
---|
| 697 | return pvFile;
|
---|
| 698 | }
|
---|
| 699 | fprintf(stderr, "kDep: error: Failed to read %ld bytes.\n", cbFile);
|
---|
| 700 | free(pvFile);
|
---|
| 701 | }
|
---|
| 702 | else
|
---|
| 703 | fprintf(stderr, "kDep: error: Failed to allocate %ld bytes (file mapping).\n", cbFile);
|
---|
| 704 | return NULL;
|
---|
| 705 | }
|
---|
| 706 |
|
---|
| 707 |
|
---|
| 708 | /**
|
---|
| 709 | * Free resources allocated by depReadFileIntoMemory.
|
---|
| 710 | *
|
---|
| 711 | * @param pvFile The address of the memory mapping.
|
---|
| 712 | * @param pvOpaque The opaque value returned together with the mapping.
|
---|
| 713 | */
|
---|
| 714 | void depFreeFileMemory(void *pvFile, void *pvOpaque)
|
---|
| 715 | {
|
---|
| 716 | #if defined(USE_WIN_MMAP)
|
---|
| 717 | if (pvOpaque)
|
---|
| 718 | {
|
---|
| 719 | UnmapViewOfFile(pvFile);
|
---|
| 720 | CloseHandle(pvOpaque);
|
---|
| 721 | return;
|
---|
| 722 | }
|
---|
| 723 | #endif
|
---|
| 724 | free(pvFile);
|
---|
| 725 | }
|
---|
| 726 |
|
---|