/*
 * Copyright (c) 2000-2001 Apple Computer, Inc. All Rights Reserved.
 * 
 * The contents of this file constitute Original Code as defined in and are
 * subject to the Apple Public Source License Version 1.2 (the 'License').
 * You may not use this file except in compliance with the License. Please obtain
 * a copy of the License at http://www.apple.com/publicsource and read it before
 * using this file.
 * 
 * This Original Code and all software distributed under the License are
 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS
 * OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, INCLUDING WITHOUT
 * LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
 * PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. Please see the License for the
 * specific language governing rights and limitations under the License.
 */


/*
 * compiler/back_ends/c_gen/str_util.c  - bunch of ASN.1/C string utilities
 *
 *
 * Mike Sample
 * 91/08/12
 * Copyright (C) 1991, 1992 Michael Sample
 *            and the University of British Columbia
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * $Header: /cvs/Darwin/src/live/Security/SecuritySNACCRuntime/compiler/back-ends/str-util.c,v 1.1.1.1 2001/05/18 23:14:08 mb Exp $
 * $Log: str-util.c,v $
 * Revision 1.1.1.1  2001/05/18 23:14:08  mb
 * Move from private repository to open source repository
 *
 * Revision 1.3  2001/05/05 00:59:27  rmurphy
 * Adding darwin license headers
 *
 * Revision 1.2  2000/05/10 21:36:43  rmurphy
 * changing the suffix for c++ output files to .cpp - requires -DMACOS on the compilation line
 *
 * Revision 1.1.1.1  1999/03/16 18:06:39  aram
 * Originals from SMIME Free Library.
 *
 * Revision 1.4  1995/07/25 18:13:31  rj
 * include string(s).h
 *
 * by default, snacc now derives output file names from the .asn1 input file name instead of the module name.
 * the global keepbaseG variable switches between the two behaviours.
 *
 * additional filename generator for idl backend.
 *
 * changed `_' to `-' in file names.
 *
 * Revision 1.3  1994/10/08  03:48:17  rj
 * since i was still irritated by cpp standing for c++ and not the C preprocessor, i renamed them to cxx (which is one known suffix for C++ source files). since the standard #define is __cplusplus, cplusplus would have been the more obvious choice, but it is a little too long.
 *
 * Revision 1.2  1994/09/01  00:25:31  rj
 * snacc_config.h removed; more portable .h file inclusion.
 *
 * Revision 1.1  1994/08/28  09:48:37  rj
 * first check-in. for a list of changes to the snacc-1.1 distribution please refer to the ChangeLog.
 *
 */

#include "asn-incl.h"

#include <ctype.h>
#if HAVE_UNISTD_H
#include <unistd.h>  /* for pathconf (..) */
#endif
#if STDC_HEADERS || HAVE_STRING_H
#include <string.h>
#else
#include <strings.h>
#endif
#include <stdio.h>

#include "asn1module.h"
#include "mem.h"
#include "define.h"
#include "c-gen/rules.h"
#include "c-gen/type-info.h"
#include "c-gen/kwd.h"
#include "c++-gen/kwd.h"
#include "str-util.h"


#define DIGIT_TO_ASCII( d)	(((d) % 10) + '0')

int	keepbaseG = TRUE;

/*
 * allocates new and returns a copy of the given
 * string with '-'s (dashes) replaced by  '_'s (underscores)
 */
char *
Asn1TypeName2CTypeName PARAMS ((aName),
    char *aName)
{
    char *retVal;
    if (aName == NULL)
        return NULL;

    retVal = Malloc (strlen (aName) + 1);
    strcpy (retVal, aName);
    Dash2Underscore (retVal, strlen (retVal));

    return retVal;
}  /* Asn1TypeName2CTypeName */


/*
 * allocates new str and returns a copy of the given
 * string with '-'s (dashes) replaced by  '_'s (underscores)
 */
char *
Asn1FieldName2CFieldName PARAMS ((aName),
    char *aName)
{
    char *retVal;
    if (aName == NULL)
        return NULL;

    retVal = Malloc (strlen (aName) + 1);
    strcpy (retVal, aName);
    Dash2Underscore (retVal, strlen (retVal));

    return retVal;
}  /* Asn1FieldName2CFieldName */


/*
 * allocates new str and returns a copy of the given
 * string with '-'s (dashes) replaced by  '_'s (underscores)
 */
char *
Asn1ValueName2CValueName PARAMS ((aName),
    char *aName)
{
    char *retVal;
    if (aName == NULL)
        return NULL;

    retVal = Malloc (strlen (aName) + 1);
    strcpy (retVal, aName);
    Dash2Underscore (retVal, strlen (retVal));

    return retVal;
}  /* Asn1FieldName2CFieldName */


/*
 * allocates and returns a string with all of
 * the caps from the given string
 */
char *
GetCaps PARAMS ((str),
    char *str)
{
    int i, j;
    char *retVal;

    if (str == NULL)
        return NULL;

    retVal = Malloc (strlen (str) + 1);

    for (j = 0, i = 0; i < strlen (str); i++)
    {
        if (isupper (str[i]))
            retVal[j++] = str[i];
    }

    retVal[j] = '\0';  /* null terminate */

    return retVal;

}  /* GetCaps */


/*
 * allocates and returns a string with all of
 * the caps and digits from the given string
 */
char *
GetCapsAndDigits PARAMS ((str),
    char *str)
{
    int i, j;
    char *retVal;

    if (str == NULL)
        return NULL;

    retVal = Malloc (strlen (str) + 1);

    for (j = 0, i = 0; i < strlen (str); i++)
    {
        if ((isupper (str[i])) ||  (isdigit (str[i])))
            retVal[j++] = str[i];
    }

    retVal[j] = '\0';  /* null terminate */

    return retVal;

}  /* GetCapsAndDigits */


/*
 * replaces lowercase chars in given str
 * with upper case version
 * NOTE: modifies given str
 */
void
Str2UCase PARAMS ((str, len),
    char *str _AND_
    int len)
{
    int i;
    for (i=0; i < len; i++)
    {
        if (islower (str[i]))
            str[i] = toupper (str[i]);
    }
} /* Str2UCase */


/*
 * replaces uppercase chars in given str
 * with lower case version
 * NOTE: modifies given str
 */
void
Str2LCase PARAMS ((str, len),
    char *str _AND_
    int len)
{
    int i;
    for (i=0; i < len; i++)
    {
        if (isupper (str[i]))
            str[i] = tolower (str[i]);
    }
} /* Str2LCase */


/*
 * replace dash chars in given str
 * with underscores
 * NOTE: modifies given str
 */
void
Dash2Underscore PARAMS ((str, len),
    char *str _AND_
    int len)
{
    int i;
    for (i=0; i < len; i++)
    {
        if (str[i] == '-')
            str[i] = '_';
    }
} /* Dash2Underscore */


/*
 * tacks on the ascii version of the given digit
 * at the end of the given str.
 * NOTE: make sure the str you give has enough space
 * for the digits
 */
void
AppendDigit PARAMS ((str, digit),
    char *str _AND_
    int digit)
{
    int high = 1000000000;
    int currDigit;
    int value;
    char digitStr[20]; /* arbitrary length > max */

    if (digit < 0)
        digit *= -1;

    currDigit = 0;
    while (high > 0)
    {
        value = digit / high;
        if (value != 0)
            digitStr[currDigit++]= DIGIT_TO_ASCII (value);

        digit = digit % high;
        high  = high/10;
    }

    if (currDigit == 0)
        strcat (str, "0");
    else
    {
        digitStr[currDigit] = '\0';  /* null terminate */
        strcat (str, digitStr);
    }
} /* AppendDigit */




/*
 * given a defined object list containing null termintated strs,
 * a str to be made unique wrt to the list by adding digits to the
 * end, the max number of digits to add and the digit to start
 * at, str is modified to be unique.  It is not added to the
 * defined object list.  The given str must have enough spare,
 * allocated chars after it's null terminator to hold maxDigits
 * more characters.
 * Only appends digits if the string is not unique or is a C keyword.
 *
 * Eg  MakeCStrUnique ({ "Foo", "Bar" }, "Foo\0   ", 3, 1)
 *         modifies the the Str "Foo" to "Foo1"
 */
void
MakeCStrUnique PARAMS ((nameList, str, maxDigits, startingDigit),
    DefinedObj *nameList _AND_
    char *str _AND_
    int maxDigits _AND_
    int startingDigit)
{
    int digit, len, maxDigitVal;

    if (ObjIsDefined (nameList, str, StrObjCmp) || IsCKeyWord (str))
    {
        for (maxDigitVal = 1; maxDigits > 0; maxDigits--)
            maxDigitVal *= 10;

        len = strlen (str);
        digit = startingDigit;
        do
        {
            str[len] = '\0';
            AppendDigit (str, digit++);
        } while (ObjIsDefined (nameList, str, StrObjCmp) &&  (digit < maxDigitVal));
    }
}  /* MakeCStrUnique */


/*
 * same as MakeCStrUnique except checks against C++ keywords
 */
void
MakeCxxStrUnique PARAMS ((nameList, str, maxDigits, startingDigit),
    DefinedObj *nameList _AND_
    char *str _AND_
    int maxDigits _AND_
    int startingDigit)
{
    int digit, len, maxDigitVal;

    if (ObjIsDefined (nameList, str, StrObjCmp) || IsCxxKeyWord (str))
    {
        for (maxDigitVal = 1; maxDigits > 0; maxDigits--)
            maxDigitVal *= 10;

        len = strlen (str);
        digit = startingDigit;
        do
        {
            str[len] = '\0';
            AppendDigit (str, digit++);
        } while (ObjIsDefined (nameList, str, StrObjCmp) &&  (digit < maxDigitVal));
    }
}  /* MakeCxxStrUnique */


/*
 * if (keepbaseG)
 * {
 *   strip leading path and trailing suffix
 * }
 * else
 * {
 *   allocates and returns a base file name generated from
 *   the module's name.  May shorten the name if the
 *   expected length exceed the systems max path component length
 *   (eg to support SYS V 14 char filename len limit)
 * }
 * Base file name is used as the base name for the generated C source files.
 */
char *
MakeBaseFileName PARAMS ((refName),
    const char *refName)
{
  if (keepbaseG)
  {
    char	*base, *dot;
    int		stublen;
    char	*stub;

    if (base = strrchr (refName, '/'))
      base++;
    else
      base = refName;

    if (dot = strrchr (base, '.'))
      stublen = dot - base;
    else
      stublen = strlen (base);

    stub = Malloc (stublen+1);
    memcpy (stub, base, stublen);
    stub[stublen] = '\0';

    return stub;
  }
  else
  {
    int fNameLen;
    int cpyLen;
    char *retVal;
    int maxPathComponentLen;
    char pathName[1024];
#   define MAX_SUFFIX_LEN 2 /* .c, .h, .C */
    extern int maxFileNameLenG; /* declared in snacc.c */

    /*
     * if the user has not given the max file name len
     * via the -mf option,
     * find the max filename len (ala POSIX method)
     * if possible.  Otherwise hardwire it to 14
     * to support underpowered OSes
     */
    if (maxFileNameLenG > 2)
        maxPathComponentLen = maxFileNameLenG;
    else
#ifdef _PC_NAME_MAX
        maxPathComponentLen = pathconf (getcwd (pathName, 1024), _PC_NAME_MAX);
#else
        maxPathComponentLen = 14;
#endif

    retVal = (char *)Malloc (strlen (refName) +1);
    fNameLen = strlen (refName) + MAX_SUFFIX_LEN;
    if ((fNameLen > maxPathComponentLen) && (maxPathComponentLen != -1))
    {
        cpyLen = maxPathComponentLen - MAX_SUFFIX_LEN;

        /* don't allow trailing dash */
        if (refName[cpyLen-1] == '-')
            cpyLen--;

        strncpy (retVal, refName, cpyLen);
        retVal[cpyLen] = '\0';
    }
    else
        strcpy (retVal, refName);

    return retVal;
  }
} /* MakeBaseFileName */




/*
 * given a module name and a suffix, the
 * suffix is appended to the module name
 * and the whole string is put into lower case
 * and underscores are inserted in likely places
 * (ie MTSAbstractSvc.h -> mts_abstract_svc.h)
 */
char *
MakeFileName PARAMS ((refName, suffix),
    const char *refName _AND_
    const char *suffix)
{
  if (keepbaseG)
  {
    size_t	baselen = strlen (refName),
		sufflen = strlen (suffix);
    char *filename = Malloc (baselen + sufflen + 1);

    memcpy (filename, refName, baselen);
    memcpy (filename+baselen, suffix, sufflen);
    filename[baselen+sufflen] = '\0';

    return filename;
  }
  else
  {
    int i, cpyIndex, len;
    char *hdrCpy;
    int fNameLen;
    char *fName;
#define MAX_UNDERSCORE 10

    fName = Malloc (strlen (refName) + strlen (suffix) + 1);
    strcpy (fName, refName);
    strcat (fName, suffix);


    fNameLen = strlen (fName);

    /*
     * convert dashes to underscores, add spaces
     */
    Dash2Underscore (fName, fNameLen);


    /*
     * remove the next two lines if you uncomment the
     * following underscore inserter
     */
    Str2LCase (fName, fNameLen - strlen (suffix));
    return fName;

    /*
     *  NO LONGER DONE - LET THE USER MODIFY THE ASN.1 IF DESIRED
     *  add underscore between Lcase/Ucase of UCase/UcaseLcasce
     *  eg MTSAbstractSvc -> MTS_Abstract_Svc
     *  (if enough space)
    len = strlen (fName) + MAX_UNDERSCORE + 1;
    hdrCpy = (char *) Malloc (len);

    hdrCpy[0] = fName[0];
    for (i = 1, cpyIndex = 1; (cpyIndex < len) && (i < fNameLen); i++)
    {
        if (((islower (fName[i-1])) && (isupper (fName[i]))) ||
             ((isupper (fName[i-1])) && (isupper (fName[i])) &&
                            ((i < (fNameLen-1)) && (islower (fName[i+1])))))
        {
            hdrCpy[cpyIndex++] = '_';
            hdrCpy[cpyIndex++] = fName[i];
        }
        else
            hdrCpy[cpyIndex++] = fName[i];
    }
    hdrCpy[cpyIndex++] = '\0';

    Str2LCase (hdrCpy, cpyIndex - strlen (suffix));

    Free (fName);
    return hdrCpy;
    */
  }
}  /* MakeFileName */


char *
MakeCHdrFileName PARAMS ((refName),
    const char *refName)
{
    return MakeFileName (refName, ".h");
}

char *
MakeCSrcFileName PARAMS ((refName),
    const char *refName)
{
    return MakeFileName (refName, ".c");
}

char *
MakeCxxHdrFileName PARAMS ((refName),
    const char *refName)
{
    return MakeFileName (refName, ".h");
}

char *
MakeCxxSrcFileName PARAMS ((refName),
    const char *refName)
{
#ifndef MACOS
    return MakeFileName (refName, ".C");
#else
    return MakeFileName (refName, ".cpp");      /* ignore cpp rant */
#endif
}

#ifdef _IBM_ENC_
char *                                           /* 19.8.93 IBM-ENC */
MakedbHdrFileName PARAMS ((refName),
    const char *refName)
{
    return MakeFileName (refName, "db.h");
}

char *                                           /* 19.8.93 IBM-ENC */
MakedbSrcFileName PARAMS ((refName),
    const char *refName)
{
    return MakeFileName (refName, "db.C");
}
#endif /* _IBM_ENC_ */

#if IDL
char *
MakeIDLFileName PARAMS ((refName),
    const char *refName)
{
    return MakeFileName (refName, ".idl");
}
#endif
