/*
 * $Header: /smsa/bsd:nls/nlsgen.c:bsd.riscix1_2  1.1  $
 * $Source: /smsa/bsd:nls/nlsgen.c: $
 *
 * Copyright (c) 1990 Acorn Computers Ltd., Cambridge, England
 *
 * $Log:	nlsgen.c,v $
 * Revision 1.1  90/11/15  16:31:54  kwelton
 * Initial revision
 * 
 *
 */

#if defined(TOOL_RCS) && !defined(lint)
static char RcsId[] = "$Header: /smsa/bsd:nls/nlsgen.c:bsd.riscix1_2  1.1  $";
#endif

#include <ctype.h>
#include <langinfo.h>
#include <limits.h>
#include <locale.h>
#include <stdio.h>

#include <sys/fcntl.h>
#include <sys/param.h>

#include "nls_support.h"

/*
 * structure used for defining
 * collation sequences
 */
struct _def_collate
{
    unsigned int character:8;
    unsigned int type:2;
    unsigned int collation:16;
    unsigned int character1;
};

char *source = DATAFILE;

#include DATAFILE

#define MAXCARD		(81)

static char datadir[] = "datafiles";
static char opfile[MAXPATHLEN];

extern void *malloc();

/**********************************************************************/

#ifdef NLS_CCOLLATE

/*
 * it is imperative that the smallest value for any primary collation
 * sequence is greater than the largest value for any secondary
 * collation sequence (otherwise strxfrm() will not work).
 *
 * find the amount by which to adjust the primary collation sequences
 * to acheive this
 */
static int findadjust(void)
{
    long pmin = ~(1 << 31), smax = 0;
    int i, doit = 0;
    struct _def_collate *defptr;

    for( i = 0, defptr = ccollate; i < ncollate; ++i, ++defptr )
	if( defptr->type == _COLT_SINGLE || defptr->type == _COLT_EXPAND )
	{
	    ++doit;
	    break;
	}

    if( !doit )
	return(0);

    for( i = 0, defptr = ccollate; i < ncollate; ++i, ++defptr )
    {
	if( defptr->type == _COLT_SINGLE )
	{
	    if( pmin > _COLLATE_PRIMARY(defptr->collation) )
		pmin = _COLLATE_PRIMARY(defptr->collation);

	    if( smax < _COLLATE_SECONDARY(defptr->collation) )
		smax = _COLLATE_SECONDARY(defptr->collation);
	}

	else if( defptr->type == _COLT_EXPAND )
	{
	    if( pmin > _COLLATE_PRIMARY(defptr->collation) )
		pmin = _COLLATE_PRIMARY(defptr->collation);

	    if( smax < _COLLATE_SECONDARY(defptr->collation) )
		smax = _COLLATE_SECONDARY(defptr->collation);

	    if( pmin > _COLLATE_PRIMARY(defptr->character1) )
		pmin = _COLLATE_PRIMARY(defptr->character1);

	    if( smax < _COLLATE_SECONDARY(defptr->character1) )
		smax = _COLLATE_SECONDARY(defptr->character1);
	}
    }

    /*
     * calcualte adjustment and shift into
     * to primary collation field
     */
    return((int)((smax - (pmin - 1)) << 8));
}

/**********************************************************************/

/*
 * generate & output collation data.
 *
 * NOTE: the pointer values only have any significance to
 * the current invocation of this program. They should
 * be stored as offsets from the start of the buffer,
 * allowing any process to calculate the correct pointer
 * values when it knows the base address of its own buffer.
 */
static void prccollate(int adjust)
{
    char *buildbuffer;
    struct _collate **clist, *nextcol;
    struct _def_collate *defptr;
    int buildsize, i;
    int fd;

    /*
     * need to allocate enough space for
     *
     * a) struct _collate
     *    **clist[_NLS_CCOLLATE_LEN / sizeof(struct _collate *)]
     *
     * b) ncollate * sizeof(struct _collate)
     */
    buildsize = _NLS_CCOLLATE_LEN + (ncollate * sizeof(struct _collate));

    if( (buildbuffer = malloc(buildsize)) == NULL )
    {
	fprintf(stderr, "out of core\n");
	exit(1);
    }
    else
    {
	/* initialise collation pointers */
	clist = (struct _collate **)buildbuffer;
	nextcol = (struct _collate *)(buildbuffer + _NLS_CCOLLATE_LEN);
    }

    /*
     * set defaults for clist entries
     */
    for( i = 0; i < (_NLS_CCOLLATE_LEN / sizeof(struct _collate *)); ++i )
	*(clist + i) = NULL;

    /*
     * loop over all defined collation sequences
     */
    for( i = 0, defptr = ccollate; i < ncollate; ++i, ++defptr, ++nextcol )
    {
	nextcol->character = defptr->character;
	nextcol->type = defptr->type;
	nextcol->collation = defptr->collation + adjust;

	switch( defptr->type )
	{
	  case _COLT_SINGLE:
	  case _COLT_NULL:
	    nextcol->character2 = NULL;

	    if( defptr->character1 == 0x00 )
	    {
		/* this is a primary character */
		struct _collate **primary = clist + defptr->character;

		if( *primary != NULL )
		{
		    fprintf(stderr, "duplicate primary %#x\n",
			    defptr->character);
		    exit(1);
		}
		else
		    *primary = (struct _collate *)((char *)nextcol -
						   buildbuffer);
	    }
	    else
	    {
		/*
		 * this is a secondary character
		 */
		struct _collate *primary = (struct _collate *)
		    ((*(int *)(clist + defptr->character1)) +
		     (int)buildbuffer);

		if( primary == NULL )
		{
		    fprintf(stderr, "missing primary %#x for char %#x\n",
			    defptr->character1, defptr->character);
		    exit(1);
		}

		/*
		 * add this character to the list
		 * of secondaries
		 */
		while( primary->character2 != NULL )
		    primary = (struct _collate *)
			((int)primary->character2 + (int)buildbuffer);

		primary->character2 = (struct _collate *)((char *)nextcol - 
							  buildbuffer);
	    }
	    break;

	  case _COLT_EXPAND:
	    nextcol->collation2 = defptr->character1 + adjust;
	    *(clist + defptr->character) = (struct _collate *)
		((char *)nextcol - buildbuffer);
	    break;

	  case _COLT_IGNORE:
	    nextcol->character2 = NULL;
	    *(clist + defptr->character) = (struct _collate *)
		((char *)nextcol - buildbuffer);
	    break;

	  default:
	    fprintf(stderr, "unknown type `%d'\n",
		    defptr->type);
	    break;
	}
    }

    sprintf(opfile, "%s/%s/nls_ccollate.%s",
	    _NLS_ROOTPATH, datadir, LANGUAGE);


    if( (fd = open(opfile, O_WRONLY | O_CREAT | O_TRUNC, 0666)) == -1 )
    {
	perror(opfile);
	exit(1);
    }

    if( write(fd, buildbuffer, buildsize) != buildsize )
    {
	fprintf(stderr, "%s: write", opfile);
	perror("");
	exit(1);
    }

    close(fd);
}

#endif /* defined NLS_CCOLLATE */

/**********************************************************************/

#ifdef NLS_CTYPE

/*
 * output ctype & cshift data
 */
static void prctype(void)
{
    int fd;

    if( sizeof(ctype) != _NLS_CTYPE_LEN )
    {
	fprintf(stderr, "sizeof(ctype) (%d) is wrong (should be %d)\n",
		sizeof(ctype), _NLS_CTYPE_LEN);
	exit(1);
    }

    if( sizeof(cshift) != _NLS_CSHIFT_LEN )
    {
	fprintf(stderr, "sizeof(cshift) (%d) is wrong (should be %d)\n",
		sizeof(cshift), _NLS_CSHIFT_LEN);
	exit(1);
    }

    sprintf(opfile, "%s/%s/nls_ctype.%s",
	    _NLS_ROOTPATH, datadir, LANGUAGE);

    if( (fd = open(opfile, O_WRONLY | O_CREAT | O_TRUNC, 0666)) == -1 )
    {
	perror(opfile);
	exit(1);
    }

    if( write(fd, ctype, _NLS_CTYPE_LEN) != _NLS_CTYPE_LEN )
    {
	fprintf(stderr, "%s: write", opfile);
	perror("");
	exit(1);
    }
	
    if( write(fd, cshift, _NLS_CSHIFT_LEN) != _NLS_CSHIFT_LEN )
    {
	fprintf(stderr, "%s: write", opfile);
	perror("");
	exit(1);
    }

    close(fd);
}

#endif /* defined NLS_CTYPE */

/**********************************************************************/

#ifdef NLS_LANGINFO

/*
 * output langinfo data
 */
static void prlanginfo(void)
{
    int i, fd;

    sprintf(opfile, "%s/%s/nls_langinfo.%s",
	    _NLS_ROOTPATH, datadir, LANGUAGE);

    if( (fd = open(opfile, O_WRONLY | O_CREAT | O_TRUNC, 0666)) == -1 )
    {
	perror(opfile);
	exit(1);
    }

    for( i = 0; i < _NL_NITEMS; ++i )
    {
	int nbytes = strlen(langinfo[i]) + 1;

	if( write(fd, langinfo[i], nbytes) != nbytes )
	{
	    fprintf(stderr, "%s: write", opfile);
	    perror("");
	    exit(1);
	}
    }

    close(fd);
}

#endif /* defined NLS_LANGINFO */

/**********************************************************************/

main()
{
    int fd;
    char ipbuffer[MAXCARD];

#ifdef NLS_CCOLLATE
    int adjust = findadjust();
#endif

    /*
     * make sure stdin / stdout is
     * not redirected
     */
    if( (fd = open("/dev/tty", O_RDWR)) == -1 )
    {
	perror("/dev/tty");
	exit(1);
    }
    else
    {
	close(fileno(stdin));
	close(fileno(stdout));
	close(fileno(stderr));

	if( dup(fd) == -1 )
	{
	    perror("dup(stdin)");
	    exit(1);
	}
	if( dup(fd) == -1 )
	{
	    perror("dup(stdout)");
	    exit(1);
	}
	if( dup(fd) == -1 )
	{
	    perror("dup(stderr)");
	    exit(1);
	}
	close(fd);
    }

    printf("Generating database entries for language `%s'\n",
	   LANGUAGE);
    printf("Files will be stored in directory %s/%s\n",
	   _NLS_ROOTPATH, datadir);
    printf("Is this ok (y/n)? ");
    fflush(stdout);

    gets(ipbuffer);
    if( *ipbuffer != 'y' && *ipbuffer != 'Y' )
    {
	printf("Abandoned\n");
	exit(0);
    }

#ifdef NLS_CTYPE
    fprintf(stderr, "Generating nls_ctype...");
    fflush(stderr);
    prctype();
    fprintf(stderr, "done\n");
#else
    fprintf(stderr, "nls_ctype not defined\n");
#endif

#ifdef NLS_CCOLLATE
    fprintf(stderr, "Generating nls_ccollate...");
    fflush(stderr);
    prccollate(adjust);
    fprintf(stderr, "done\n");
#else
    fprintf(stderr, "nls_ccollate not defined\n");
#endif

#ifdef NLS_LANGINFO
    fprintf(stderr, "Generating nls_langinfo...");
    fflush(stderr);
    prlanginfo();
    fprintf(stderr, "done\n");
#else
    fprintf(stderr, "nls_langinfo not defined\n");
#endif

    /* all OK */
    exit(0);
}

/**********************************************************************/

/* EOF nlsgen.c */
