<commit_before>#include "system.h"

#include <rpmlib.h>
#include "fprint.h"

fingerPrintCache fpCacheCreate(int sizeHint)
{
    fingerPrintCache fpc;

    fpc = xmalloc(sizeof(*fpc));
    fpc->ht = htCreate(sizeHint * 2, 0, 1, hashFunctionString,
		       hashEqualityString);
    return fpc;
}

void fpCacheFree(fingerPrintCache cache)
{
    htFree(cache->ht);
    free(cache);
}

static const struct fprintCacheEntry_s * cacheContainsDirectory(
			    fingerPrintCache cache,
			    const char * dirName)
{
    const void ** data;

    if (htGetEntry(cache->ht, dirName, &data, NULL, NULL))
	return NULL;
    return data[0];
}

static fingerPrint doLookup(fingerPrintCache cache,
	const char * dirName, const char * baseName, int scareMemory)
{
    char dir[PATH_MAX];
    char * end;
    fingerPrint fp;
    struct stat sb;
    char * buf;
    const struct fprintCacheEntry_s * cacheHit;

    /* assert(*dirName == '/' || !scareMemory); */

    if (*dirName != '/') {
	scareMemory = 0;

	/* Using realpath on the arg isn't correct if the arg is a symlink,
	 * especially if the symlink is a dangling link.  What we 
	 * do instaed is use realpath() on `.' and then append arg to
	 * the result.
	 */

	/* if the current directory doesn't exist, we might fail. 
	   oh well. likewise if it's too long.  */
	dir[0] = '\0';
	if ( /*@-unrecog@*/ realpath(".", dir) /*@=unrecog@*/ != NULL) {
	    char *s = alloca(strlen(dir) + strlen(dirName) + 2);
	    sprintf(s, "%s/%s", dir, dirName);
	    dirName = s;
	}
    }

    /* FIXME: perhaps we should collapse //, /./, and /../ stuff if
       !scareMemory?? */

    buf = alloca(strlen(dirName) + 1);
    strcpy(buf, dirName);
    end = buf + strlen(buf);
    fp.entry = NULL;
    while (*buf) {

	/* as we're stating paths here, we want to follow symlinks */

	if ((cacheHit = cacheContainsDirectory(cache, *buf ? buf : "/"))) {
	    fp.entry = cacheHit;
	} else if (!stat(*buf ? buf : "/", &sb)) {
	    size_t nb = sizeof(*fp.entry) + (*buf ? strlen(buf) : 1) + 1;
	    char * dn = xmalloc(nb);
	    struct fprintCacheEntry_s * newEntry = (void *)dn;
	    dn += sizeof(*newEntry);
	    strcpy(dn, (*buf ? buf : "/"));
	    newEntry->ino = sb.st_ino;
	    newEntry->dev = sb.st_dev;
	    newEntry->isFake = 0;
	    newEntry->dirName = dn;
	    fp.entry = newEntry;

	    htAddEntry(cache->ht, dn, fp.entry);
	}

        if (fp.entry) {
	    fp.subDir = dirName + (end - buf);
	    if (fp.subDir[0] == '/' && fp.subDir[1] != '\0')
		fp.subDir++;
	    else
		fp.subDir = NULL;
	    fp.baseName = baseName;
	    if (!scareMemory && fp.subDir != NULL)
		fp.subDir = xstrdup(fp.subDir);
	    return fp;
	}

	end--;
	while ((end > buf) && *end != '/') end--;
	*end = '\0';
    }

    /* This can't happen, or stat('/') just failed! */
    abort();
    /*@notreached@*/

    return fp;
}

fingerPrint fpLookup(fingerPrintCache cache, const char * dirName, 
			const char * baseName, int scareMemory)
{
    return doLookup(cache, dirName, baseName, scareMemory);
}

unsigned int fpHashFunction(const void * key)
{
    const fingerPrint * fp = key;
    unsigned int hash = 0;
    char ch;
    const char * chptr;

    ch = 0;
    chptr = fp->baseName;
    while (*chptr) ch ^= *chptr++;

    hash |= ((unsigned)ch) << 24;
    hash |= (((((unsigned)fp->entry->dev) >> 8) ^ fp->entry->dev) & 0xFF) << 16;
    hash |= fp->entry->ino & 0xFFFF;
    
    return hash;
}

int fpEqual(const void * key1, const void * key2)
{
    const fingerPrint *k1 = key1;
    const fingerPrint *k2 = key2;
    /* XXX negated to preserve strcmp return behavior in ht->eq */
    return (FP_EQUAL(*k1, *k2) ? 0 : 1);
}

void fpLookupList(fingerPrintCache cache, const char ** dirNames, 
		  const char ** baseNames, const int * dirIndexes, 
		  int fileCount, fingerPrint * fpList)
{
    int i;

    for (i = 0; i < fileCount; i++) {
	/* If this is in the same directory as the last file, don't bother
	   redoing all of this work */
	if (i > 0 && dirIndexes[i - 1] == dirIndexes[i]) {
	    fpList[i].entry = fpList[i - 1].entry;
	    fpList[i].subDir = fpList[i - 1].subDir;
	    fpList[i].baseName = baseNames[i];
	} else {
	    fpList[i] = doLookup(cache, dirNames[dirIndexes[i]], baseNames[i],
				 1);
	}
    }
}

void fpLookupHeader(fingerPrintCache cache, Header h, fingerPrint * fpList)
{
    int fileCount;
    const char ** baseNames, ** dirNames;
    int_32 * dirIndexes;

    if (!headerGetEntryMinMemory(h, RPMTAG_BASENAMES, NULL, 
			(void **) &baseNames, &fileCount)) return;
    headerGetEntryMinMemory(h, RPMTAG_DIRNAMES, NULL, (void **) &dirNames, 
			NULL);
    headerGetEntry(h, RPMTAG_DIRINDEXES, NULL, (void **) &dirIndexes, NULL);
			
    fpLookupList(cache, dirNames, baseNames, dirIndexes, fileCount, fpList);

    free(dirNames);
    free(baseNames);
}
<commit_msg>doLookup() wasn't stat'ing / properly<commit_after>#include "system.h"

#include <rpmlib.h>
#include "fprint.h"

fingerPrintCache fpCacheCreate(int sizeHint)
{
    fingerPrintCache fpc;

    fpc = xmalloc(sizeof(*fpc));
    fpc->ht = htCreate(sizeHint * 2, 0, 1, hashFunctionString,
		       hashEqualityString);
    return fpc;
}

void fpCacheFree(fingerPrintCache cache)
{
    htFree(cache->ht);
    free(cache);
}

static const struct fprintCacheEntry_s * cacheContainsDirectory(
			    fingerPrintCache cache,
			    const char * dirName)
{
    const void ** data;

    if (htGetEntry(cache->ht, dirName, &data, NULL, NULL))
	return NULL;
    return data[0];
}

static fingerPrint doLookup(fingerPrintCache cache,
	const char * dirName, const char * baseName, int scareMemory)
{
    char dir[PATH_MAX];
    char * end;		    /* points to the '\0' at the end of "buf" */
    fingerPrint fp;
    struct stat sb;
    char * buf;
    const struct fprintCacheEntry_s * cacheHit;

    /* assert(*dirName == '/' || !scareMemory); */

    if (*dirName != '/') {
	scareMemory = 0;

	/* Using realpath on the arg isn't correct if the arg is a symlink,
	 * especially if the symlink is a dangling link.  What we 
	 * do instaed is use realpath() on `.' and then append arg to
	 * the result.
	 */

	/* if the current directory doesn't exist, we might fail. 
	   oh well. likewise if it's too long.  */
	dir[0] = '\0';
	if ( /*@-unrecog@*/ realpath(".", dir) /*@=unrecog@*/ != NULL) {
	    char *s = alloca(strlen(dir) + strlen(dirName) + 2);
	    sprintf(s, "%s/%s", dir, dirName);
	    dirName = s;
	}
    }

    /* FIXME: perhaps we should collapse //, /./, and /../ stuff if
       !scareMemory?? */

    buf = alloca(strlen(dirName) + 1);
    strcpy(buf, dirName);
    end = buf + strlen(buf);

    /* no need to pay attention to that extra little / at the end of dirName */
    if (*(end - 1) == '/' && buf[1]) {
	end--;
	*end = '\0';
    }

    fp.entry = NULL;
    while (1) {

	/* as we're stating paths here, we want to follow symlinks */

	if ((cacheHit = cacheContainsDirectory(cache, *buf ? buf : "/"))) {
	    fp.entry = cacheHit;
	} else if (!stat(*buf ? buf : "/", &sb)) {
	    size_t nb = sizeof(*fp.entry) + (*buf ? strlen(buf) : 1) + 1;
	    char * dn = xmalloc(nb);
	    struct fprintCacheEntry_s * newEntry = (void *)dn;
	    dn += sizeof(*newEntry);
	    strcpy(dn, (*buf ? buf : "/"));
	    newEntry->ino = sb.st_ino;
	    newEntry->dev = sb.st_dev;
	    newEntry->isFake = 0;
	    newEntry->dirName = dn;
	    fp.entry = newEntry;

	    htAddEntry(cache->ht, dn, fp.entry);
	}

        if (fp.entry) {
	    fp.subDir = dirName + (end - buf);
	    if (fp.subDir[0] == '/' && fp.subDir[1] != '\0')
		fp.subDir++;
	    else
		fp.subDir = NULL;
	    fp.baseName = baseName;
	    if (!scareMemory && fp.subDir != NULL)
		fp.subDir = xstrdup(fp.subDir);
	    return fp;
	}

        /* stat of '/' just failed! */
	if (end == buf + 1)
	    abort();

	end--;
	while ((end > buf) && *end != '/') end--;
	if (end == buf)	    /* back to stat'ing just '/' */
	    end++;

	*end = '\0';
    }

    /*@notreached@*/

    return fp;
}

fingerPrint fpLookup(fingerPrintCache cache, const char * dirName, 
			const char * baseName, int scareMemory)
{
    return doLookup(cache, dirName, baseName, scareMemory);
}

unsigned int fpHashFunction(const void * key)
{
    const fingerPrint * fp = key;
    unsigned int hash = 0;
    char ch;
    const char * chptr;

    ch = 0;
    chptr = fp->baseName;
    while (*chptr) ch ^= *chptr++;

    hash |= ((unsigned)ch) << 24;
    hash |= (((((unsigned)fp->entry->dev) >> 8) ^ fp->entry->dev) & 0xFF) << 16;
    hash |= fp->entry->ino & 0xFFFF;
    
    return hash;
}

int fpEqual(const void * key1, const void * key2)
{
    const fingerPrint *k1 = key1;
    const fingerPrint *k2 = key2;
    /* XXX negated to preserve strcmp return behavior in ht->eq */
    return (FP_EQUAL(*k1, *k2) ? 0 : 1);
}

void fpLookupList(fingerPrintCache cache, const char ** dirNames, 
		  const char ** baseNames, const int * dirIndexes, 
		  int fileCount, fingerPrint * fpList)
{
    int i;

    for (i = 0; i < fileCount; i++) {
	/* If this is in the same directory as the last file, don't bother
	   redoing all of this work */
	if (i > 0 && dirIndexes[i - 1] == dirIndexes[i]) {
	    fpList[i].entry = fpList[i - 1].entry;
	    fpList[i].subDir = fpList[i - 1].subDir;
	    fpList[i].baseName = baseNames[i];
	} else {
	    fpList[i] = doLookup(cache, dirNames[dirIndexes[i]], baseNames[i],
				 1);
	}
    }
}

void fpLookupHeader(fingerPrintCache cache, Header h, fingerPrint * fpList)
{
    int fileCount;
    const char ** baseNames, ** dirNames;
    int_32 * dirIndexes;

    if (!headerGetEntryMinMemory(h, RPMTAG_BASENAMES, NULL, 
			(void **) &baseNames, &fileCount)) return;
    headerGetEntryMinMemory(h, RPMTAG_DIRNAMES, NULL, (void **) &dirNames, 
			NULL);
    headerGetEntry(h, RPMTAG_DIRINDEXES, NULL, (void **) &dirIndexes, NULL);
			
    fpLookupList(cache, dirNames, baseNames, dirIndexes, fileCount, fpList);

    free(dirNames);
    free(baseNames);
}

{'fraction_non_alphanumeric': 0.15584939941455536, 'fraction_numerical': 0.009286363177551226, 'mean_word_length': 2.800537015726889, 'pattern_counts': {'":': 0, '<': 15, '<?xml version=': 0, '>': 45, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2426661', 'n_tokens_mistral': 3762, 'n_tokens_neox': 3502, 'n_words': 968}