/*
*******************************************************************************
*
*   Copyright (C) 1999-2003, International Business Machines
*   Corporation and others.  All Rights Reserved.
*
*******************************************************************************
*   file name:  toolutil.c
*   encoding:   US-ASCII
*   tab size:   8 (not used)
*   indentation:4
*
*   created on: 1999nov19
*   created by: Markus W. Scherer
*
*   This file contains utility functions for ICU tools like genccode.
*/

#ifdef WIN32
#   define VC_EXTRALEAN
#   define WIN32_LEAN_AND_MEAN
#   define NOGDI
#   define NOUSER
#   define NOSERVICE
#   define NOIME
#   define NOMCX
#   include <windows.h>
#endif
#include <stdio.h>
#include "unicode/utypes.h"
#include "unicode/putil.h"
#include "cmemory.h"
#include "cstring.h"
#include "toolutil.h"

U_CAPI const char * U_EXPORT2
getLongPathname(const char *pathname) {
#ifdef WIN32
    /* anticipate problems with "short" pathnames */
    static WIN32_FIND_DATA info;
    HANDLE file=FindFirstFile(pathname, &info);
    if(file!=INVALID_HANDLE_VALUE) {
        if(info.cAlternateFileName[0]!=0) {
            /* this file has a short name, get and use the long one */
            const char *basename=findBasename(pathname);
            if(basename!=pathname) {
                /* prepend the long filename with the original path */
                uprv_memmove(info.cFileName+(basename-pathname), info.cFileName, uprv_strlen(info.cFileName)+1);
                uprv_memcpy(info.cFileName, pathname, basename-pathname);
            }
            pathname=info.cFileName;
        }
        FindClose(file);
    }
#endif
    return pathname;
}

U_CAPI const char * U_EXPORT2
findBasename(const char *filename) {
    const char *basename=uprv_strrchr(filename, U_FILE_SEP_CHAR);

#if U_FILE_ALT_SEP_CHAR!=U_FILE_SEP_CHAR
    if(basename==NULL) {
        /* Use lenient matching on Windows, which can accept either \ or /
           This is useful for environments like Win32+CygWin which have both.
        */
        basename=uprv_strrchr(filename, U_FILE_ALT_SEP_CHAR);
    }
#endif

    if(basename!=NULL) {
        return basename+1;
    } else {
        return filename;
    }
}

/* tool memory helper ------------------------------------------------------- */

struct UToolMemory {
    char name[64];
    int32_t capacity, maxCapacity, size, index;
    void *array;
    UAlignedMemory staticArray[1];
};

U_CAPI UToolMemory * U_EXPORT2
utm_open(const char *name, int32_t initialCapacity, int32_t maxCapacity, int32_t size) {
    UToolMemory *mem;

    if(maxCapacity<initialCapacity) {
        maxCapacity=initialCapacity;
    }

    mem=(UToolMemory *)uprv_malloc(sizeof(UToolMemory)+initialCapacity*size);
    if(mem==NULL) {
        fprintf(stderr, "error: %s - out of memory\n", name);
        exit(U_MEMORY_ALLOCATION_ERROR);
    }
    mem->array=mem->staticArray;

    uprv_strcpy(mem->name, name);
    mem->capacity=initialCapacity;
    mem->maxCapacity=maxCapacity;
    mem->size=size;
    mem->index=0;
    return mem;
}

U_CAPI void U_EXPORT2
utm_close(UToolMemory *mem) {
    if(mem!=NULL) {
        if(mem->array!=mem->staticArray) {
            uprv_free(mem->array);
        }
        uprv_free(mem);
    }
}


U_CAPI void * U_EXPORT2
utm_getStart(UToolMemory *mem) {
    return (char *)mem->array;
}

U_CAPI int32_t U_EXPORT2
utm_countItems(UToolMemory *mem) {
    return mem->index;
}


static UBool
utm_hasCapacity(UToolMemory *mem, int32_t capacity) {
    if(mem->capacity<capacity) {
        int32_t newCapacity;

        if(mem->maxCapacity<capacity) {
            fprintf(stderr, "error: %s - trying to use more than maxCapacity=%ld units\n",
                    mem->name, (long)mem->maxCapacity);
            exit(U_MEMORY_ALLOCATION_ERROR);
        }

        /* try to allocate a larger array */
        if(capacity>=2*mem->capacity) {
            newCapacity=capacity;
        } else if(mem->capacity<=mem->maxCapacity/3) {
            newCapacity=2*mem->capacity;
        } else {
            newCapacity=mem->maxCapacity;
        }

        if(mem->array==mem->staticArray) {
            mem->array=uprv_malloc(newCapacity*mem->size);
            if(mem->array!=NULL) {
                uprv_memcpy(mem->array, mem->staticArray, mem->index*mem->size);
            }
        } else {
            mem->array=uprv_realloc(mem->array, newCapacity*mem->size);
        }

        if(mem->array==NULL) {
            fprintf(stderr, "error: %s - out of memory\n", mem->name);
            exit(U_MEMORY_ALLOCATION_ERROR);
        }
    }

    return TRUE;
}

U_CAPI void * U_EXPORT2
utm_alloc(UToolMemory *mem) {
    char *p=(char *)mem->array+mem->index*mem->size;
    int32_t newIndex=mem->index+1;
    if(utm_hasCapacity(mem, newIndex)) {
        mem->index=newIndex;
        uprv_memset(p, 0, mem->size);
    }
    return p;
}

U_CAPI void * U_EXPORT2
utm_allocN(UToolMemory *mem, int32_t n) {
    char *p=(char *)mem->array+mem->index*mem->size;
    int32_t newIndex=mem->index+n;
    if(utm_hasCapacity(mem, newIndex)) {
        mem->index=newIndex;
        uprv_memset(p, 0, n*mem->size);
    }
    return p;
}
