/* $Copyright:	$
 * Copyright (c) 1984, 1985, 1986, 1987, 1988, 1989, 1990 
 * Sequent Computer Systems, Inc.   All rights reserved.
 *  
 * This software is furnished under a license and may be used
 * only in accordance with the terms of that license and with the
 * inclusion of the above copyright notice.   This software may not
 * be provided or otherwise made available to, or used by, any
 * other person.  No title to or ownership of the software is
 * hereby transferred.
 */

#ident "$Header: mkmsgs.c 1.11 90/10/23 $"

/*
 * mkmsgs - Transform a command message file into a message database
 *
 * Used to transform raw command messages into a format acceptable for
 * the gettxt(3) library routine to access them.
 *
 * DATA STRUCTURE:
 *
 *
 *		--------------------------------------
 *		|	    COFF Header		     |
 *              --------------------------------------
 *              |       Number of Messages           |
 *              --------------------------------------
 *              |                                    | 0
 *              |                                    |
 *              |          Pointer Table             |
 *              |                                    |
 *              |                                    |
 *              |                                    |
 *              --------------------------------------
 *              |                                    | M
 *              |                                    |
 *              |                                    |
 *              |                                    |
 *              |                                    |
 *              |            Text Table              |
 *              |                                    |
 *              |                                    |
 *              |                                    |
 *              |                                    |
 *              |                                    |
 *              --------------------------------------
 *		|	     Lib Section	     |
 *		--------------------------------------
 *
 *	The Text table consists of the command messages themselves, packed
 * together.  The pointer table is an array of position-independent
 * pointers into the text table.  This means that message number 1 will
 * be the first pointer in the table, which will point to the first
 * string in the text table.
 *
 *	"Position independence" in this context means that all of the
 * pointers assume that the entire data structure is loaded at location
 * 0.  (Thus, the first pointer in the above table will have a value of
 * M, the second will be the length of the first string plus M, and so on).
 * Using one of these pointers thus requires adding the starting location
 * of the pointer table after it has been loaded in memory.
 *
 * ALGORITHM:
 *
 *	A single pass is taken over the strings in the input file, loading
 * them into the text table, and the pointer table is populated with
 * the string addresses relative to the beginning of the text table.
 * Then a second pass over the pointer table is made fixing the addresses
 * to be relative to the beginning of the whole data structure.
 */

#ifdef SVAE
# include <stdio.h>
# include <sys/types.h>
# include <sys/stat.h>
# include <sys/param.h>
# include <machine/param.h>
# define PATH_MAX	MAXPATHLEN
# define uid_t		unsigned short
# define mode_t		unsigned short
#else
# include <stdio.h>
# include <limits.h>
# include <sys/types.h>
# include <sys/stat.h>
# include <sys/param.h>
# include <dirent.h>
#endif

#include <sys/filehdr.h>
#include <sys/aouthdr.h>
#include <sys/scnhdr.h>
#include <unistd.h>


#define MAX_MSGS	16 * 1024	/* allows at most 16K lines of msgs */
#define MSG_MAX_LEN	512		/* Max length of a message */
#define P_locale        "/usr/lib/locale/"
#define L_locale        (sizeof(P_locale))
#define MESSAGES        "/LC_MESSAGES/"
#define	USAGE	"\
Usage: mkmsgs [-o] inputfile createfile\n\
or:    mkmsgs -i [-o] inputfile createfile locale\n"

extern char *optarg;
extern int optind;
extern char * malloc();
extern void exit();
extern int close();
extern uid_t getuid();
extern char *strcpy();
extern char *strcat();
#ifdef CCS
char *strccpy();
#endif

int	instflg;			/* install messages */
int	wrtflg;				/* overwrite the output file */
int	errflg;				/* error in option processing */
char	path[PATH_MAX];			/* place to install database */
char	line[MSG_MAX_LEN];		/* input line from message file */
int	setrec[3] = { 1, 1, 0 };	/* set header record */
struct	stat	st;
char	*createfile;

char	*getbasename();
void	write_coff();
void	fix_libs();

#define	DATA_START	(2*NBPG)
#define	SIZE		(sizeof(int*) * ptrsize + (t - text))

main(argc, argv)

	int	argc;
	char	**argv;
{
	int	c;
	char	*inputfile;
	char	*basename;
	FILE	*infp;
	int	ofp;
	char	*locale;
	int	**ptr;			/* pointer table */
	char	*text;			/* text table */
	int	**p;			/* pointer into the pointer table */
	char	*t;			/* pointer into the text table */
	int	ptrsize;		/* number of ptrs in pointer table */
	int	cnt = 0;		/* number of messages */

	while ((c = getopt(argc, argv, "io")) != -1)
		switch (c) {
		case 'i':
			instflg++;
			break;
		case 'o':
			wrtflg++;
			break;
		case '?':
			errflg++;
		}
	if (errflg)
		usage();

	if (instflg) {
		if (argc - optind != 3)
			usage();
	} else {
		if (argc - optind != 2)
			usage();
	}

	inputfile = argv[optind++];
	createfile = argv[optind++];
	basename = getbasename(createfile);
	if (instflg)
		locale = argv[optind];
	if ((infp = fopen(inputfile, "r")) == NULL) {
		(void)fprintf(stderr,
			"ERROR: cannot open message source file \"%s\"\n",
			inputfile);
		exit(1);
	}
	if (instflg && (int)getuid() != 0) {
		overwrt_error();
	}
	if (!wrtflg) {
		if (instflg) {
#ifdef CCS
			char *context, *getenv();

			if (context = getenv("CONTEXT")) {
				(void) strcpy(path,context);
				(void) strcat(path,"/tmpos");
				(void) strcat(path,P_locale);
			}
			else
#endif
				(void)strcpy(path, P_locale);
			(void)strcat(path, locale);
			(void)strcat(path, MESSAGES);
			(void)(strcat(path, createfile));

			if (access(path, 0) == 0)
				overwrt_error();
		} else {
			if (access(createfile, 0) == 0)
				overwrt_error();
		}
	}

	ptr = (int **)malloc((unsigned)(MAX_MSGS * sizeof(char *)));
	if (ptr == NULL) {
		(void)fprintf(stderr, "Out of memory\n");
		exit(1);
	}
	if (fstat(fileno(infp), &st) < 0) {
		(void)fprintf(stderr, "mkmsgs: ");
		(void)perror(inputfile);
		exit(1);
	}
	text = malloc((unsigned)st.st_size);
	if (text == NULL) {
		(void)fprintf(stderr, "Out of memory\n");
		exit(1);
	}

	t = text;
	p = ptr + 1;

	/*
	 * First pass: collect message strings into text table, with
	 * pointers to them relative to the beginning of the text table
	 * stored in the ptr table.
	 */
	while (fgets(line, sizeof(line), infp) != NULL) {
		line[strlen(line) - 1] = '\0';	/* delete trailing \n */
		(void)strccpy(t, line);
		*(p++) = (int *)(t - text);
		cnt++;
		if (p > ptr + MAX_MSGS * sizeof(char *)) {
			(void)fprintf(stderr, "Too many messages in \"%s\"\n",
				inputfile);
			(void)fprintf(stderr,
				"Split up message file to reduce size.\n");
			exit(1);
		}
		t += strlen(t) + 1;		/* add null at end of string */
	}

	ptrsize = (int)(p - ptr);
	setrec[2] = ptrsize - 1;

	/*
	 * Second pass: run back through the ptr table, correcting for
	 * the size of the ptr table.
	 */
	while (p > ptr)
		*(p--) += ptrsize + sizeof(setrec) / sizeof(int);

	/*
	 * finally, set the number of messages in the database as the
	 * first thing in the structure.  Used as a bounds-check in gettxt().
	 */
	*ptr = (int *)cnt;

	/*
	 * If -i was specified, do the installation.  Spec for this
	 * command says it will create the directory path if it
	 * doesn't exist, so we do that first.
	 */

	if (instflg) {
		int save;
#ifdef CCS
		char *context, *getenv();

		if (context = getenv("CONTEXT")) {
			(void) strcpy(path,context);
			(void) strcat(path,"/tmpos");
			(void) strcat(path,P_locale);
		}
		else
#endif
		    (void)strcpy(path, P_locale);
		if (opendir(path) == NULL) {
			if (mkdir(path, (mode_t)0755) < 0) {
				(void)fprintf(stderr,
					"unable to make directory %s -",
					path);
				perror("mkdir");
				exit(1);
			}
		}
		(void)strcat(path,locale);
		if (opendir(path) == NULL) {
			if (mkdir(path, (mode_t)0755) < 0) {
				(void)fprintf(stderr,
					"unable to make directory %s -",
					path);
				perror("mkdir");
				exit(1);
			}
		}
		save=strlen(path);
		(void)strcat(path,MESSAGES);
		if (opendir(path) == NULL) {
			if (mkdir(path, (mode_t)0755) < 0) {
				(void)fprintf(stderr,
					"unable to make directory %s -",
					path);
				perror("mkdir");
				exit(1);
			}
		}
		(void)strcat(path,createfile);
		if ((ofp = creat(path, 0555)) < 0) {
			(void)fprintf(stderr, "Can't create \"%s\" - ", path);
			perror("creat");
			exit(1);
		}
		write_coff(ofp,SIZE + sizeof(setrec),basename);
		if (write(ofp, (char *)ptr,
				(unsigned)(sizeof(int *) * ptrsize)) < 0 ||
		    write(ofp, (char *)setrec, sizeof(setrec)) < 0 ||
		    write(ofp, text, (unsigned)(t - text)) < 0) {
			(void)fprintf(stderr, "Failure to write \"%s\" - ",
				path);
			perror("write");
			exit(1);
		}
		fix_libs(ofp,DATA_START + 4 + (SIZE & ~(NBPG - 1)), basename);
		(void)close(ofp);
		if (chmod(path, 0555) < 0) {
			(void)fprintf(stderr, "Unable to set mode on \"%s\" - ");
			perror("chmod");
			exit(1);
		}
		/*
		 * owner root, group root
		 */
		if (chown(path, 0, 0) < 0) {
			(void)fprintf(stderr, "Unable to set mode on \"%s\" - ");
			perror("chmod");
			exit(1);
		}
		path[save] = '\0';
		(void)printf("Message file \"%s\" installed in %s\n",
			createfile, path);
	} else {
		if ((ofp = creat(createfile, 0755)) < 0) {
			(void)fprintf(stderr, "Can't create \"%s\" - ",
				createfile);
			perror("creat");
			exit(1);
		}
		write_coff(ofp,SIZE + sizeof(setrec),basename);
		if (write(ofp, (char *)ptr,
				(unsigned)(sizeof(int *) * ptrsize)) < 0 ||
		    write(ofp, (char *)setrec, sizeof(setrec)) < 0 ||
		    write(ofp, text, (unsigned)(t - text)) < 0) {
			(void)fprintf(stderr, "Failure to write \"%s\" - ",
				createfile);
			perror("write");
			exit(1);
		}
		fix_libs(ofp,DATA_START + 4 + (SIZE & ~(NBPG - 1)),basename);
		(void)close(ofp);
	}
	exit(0);
	/*NOTREACHED*/
}


usage()
{
	(void)fprintf(stderr, USAGE);
	exit(1);
}

overwrt_error()
{
	(void)fprintf(stderr,
		"Cannot write message file \"%s\"\n", createfile);
	exit(1);
}


static char *
getbasename(pathname)
char *pathname;
{
	char *last = pathname;
	while (*pathname) {
		if (*pathname == '/')
			last = ++pathname;
		else
			pathname++;
	}
	return last;
}

#ifdef CCS
/*
 * this is a copy of lib/libgen/strccpy.c since strccpy() does not otherwise
 * exist in a cross build.

/*
	strccpy(output, input)
	strccpy copys the input string to the output string compressing
	any C-like escape sequences to the real character.
	Esacpe sequences recognized are those defined in "The C Programming
	Language" pages 180-181.
*/

char *
strccpy(pout, pin)
register char *pout;
register char *pin;
{
	register char c;
	register char *output;
	int count;
	int wd;

	output = pout;
	while (c = *pin++) {
		if (c == '\\')
			switch (c = *pin++) {
			case 'n':
				*pout++ = '\n';
				continue;
			case 't':
				*pout++ = '\t';
				continue;
			case 'b':
				*pout++ = '\b';
				continue;
			case 'r':
				*pout++ = '\r';
				continue;
			case 'f':
				*pout++ = '\f';
				continue;
			case 'v':
				*pout++ = '\v';
				continue;
			case '\\':
				*pout++ = '\\';
				continue;
			case '0': case '1': case '2': case '3':
			case '4': case '5': case '6': case '7':
				wd = c - '0';
				count = 0;
				while ((c = *pin++) >= '0' && c <= '7') {
					wd <<= 3;
					wd |= (c - '0');
					if (++count > 1) {   /* 3 digits max */
						pin++;
						break;
					}
				}
				*pout++ = wd;
				--pin;
				continue;
			default:
				*pout++ = c;
				continue;
		}
		*pout++ = c;
	}
	*pout = '\0';
	return (output);
}

/* 
 * not a perfect emulation, (doesn't set errno) but good enough, I suspect.
 */
mkdir(file,mode)
char *file;
mode_t mode;
{
    int result, wpid = 0, pid;

    if ((pid = fork()) == -1) {
	perror("mkdir");
	return(-1);
	}
    if (pid == 0) {
	/* child */
	execl("/bin/mkdir","mkdir",file,0);
	_exit(127);
	}
    else
	while (wpid != pid)
	    if ((wpid = wait(&result)) < 0) {
		perror("mkdir (wait)");
		return(-1);
		}
    if (result)
	return(-1);
    return(chmod(file,mode));
}
#endif


/*
 * Generate a coff format
 * shared library for this 
 * message catalog
 */

/*
 * default addreses for library
 * This is the start offfset , in the file, of the text segment
 * and the size of a text page in the library.
 * For Release 3.2 you need to specify 
 * that things begin in VM page 
 * boundaraies ie 2K
 */
static	char buf[NBPG];

void
write_coff(fd,size,name)
int	fd;
int	size;
{
struct filehdr *fhdr = (struct filehdr *)buf;
struct aouthdr *ahdr = (struct aouthdr *)(buf + sizeof(struct filehdr));
struct scnhdr  *shdr = (struct scnhdr *)(buf + sizeof(struct filehdr) +
						sizeof(struct aouthdr));

	memset(buf,'\0',NBPG);
	/*
	 * Set header
	 */
#if u3b2
	fhdr->f_magic = WE32MAGIC;
#endif
#if i386
	fhdr->f_magic = I386MAGIC;
#endif
	fhdr->f_nscns = 4;
	fhdr->f_timdat = time(0L);
#if u3b2
	fhdr->f_symptr = 16432;
	fhdr->f_nsyms = 18;
#endif
#if i386
	fhdr->f_symptr = 8420;
	fhdr->f_nsyms = 24;
#endif
	fhdr->f_opthdr = sizeof(ahdr);
#if u3b2
	fhdr->f_flags = F_BM32RST | F_RELFLG | F_EXEC | F_AR32W ;
#endif
#if i386
	fhdr->f_flags =  F_RELFLG | F_EXEC | F_AR32WR ;
#endif
	/*
	 * now do aouthdr
	 */
	ahdr->magic = 0443;
	ahdr->vstamp = 0;
	ahdr->tsize = size;
	ahdr->dsize = 4;
	ahdr->bsize = 0;
	ahdr->entry = 0;
	ahdr->text_start = 0x0;
	ahdr->data_start = 0x400000;
	/*
	 * Now text header
	 */
	strcpy(shdr[0].s_name,".text");
	shdr[0].s_paddr = 0x0;
	shdr[0].s_vaddr = 0x0;
	shdr[0].s_size = size;
	shdr[0].s_scnptr = NBPG;
	shdr[0].s_relptr = 0;
	shdr[0].s_lnnoptr = 0;
	shdr[0].s_nreloc= 0;
	shdr[0].s_nlnno= 0;
	shdr[0].s_flags = STYP_TEXT ;
	/*
	 * now data
	 */
	strcpy(shdr[1].s_name,".data");
	shdr[1].s_paddr = ahdr->data_start;
	shdr[1].s_vaddr = ahdr->data_start;
	shdr[1].s_size = 4;
	shdr[1].s_scnptr = DATA_START + size & ~(NBPG - 1);
	shdr[1].s_relptr = 0;
	shdr[1].s_lnnoptr = 0;
	shdr[1].s_nreloc= 0;
	shdr[1].s_nlnno= 0;
	shdr[1].s_flags = STYP_DATA ;
	/* 
	 * now BSS
	 */
	strcpy(shdr[2].s_name,".bss");
	shdr[2].s_paddr = shdr[1].s_paddr + 4;
	shdr[2].s_vaddr = shdr[1].s_vaddr + 4;
	shdr[2].s_size = 0;
	shdr[2].s_scnptr = 0;
	shdr[2].s_relptr = 0;
	shdr[2].s_lnnoptr = 0;
	shdr[2].s_nreloc= 0;
	shdr[2].s_nlnno= 0;
	shdr[2].s_flags = STYP_BSS ;
	/* 
	 * now lib
	 */
	strcpy(shdr[3].s_name,".lib");
	shdr[3].s_paddr = 0x1;
	shdr[3].s_vaddr = 0x0;
	/*
	 * size of section
	 * is two ints + name
	 * alignment
	 */
	shdr[3].s_size = ( (strlen(name) / sizeof(int)) + 3) * sizeof (int);
	shdr[3].s_scnptr = DATA_START + 4 + size & ~(NBPG - 1);
	shdr[3].s_relptr = 0;
	shdr[3].s_lnnoptr = 0;
	shdr[3].s_nreloc= 0;
	shdr[3].s_nlnno= 0;
	shdr[3].s_flags = STYP_LIB ;
	/*
	 * write whole coff header
	 */
	if (write(fd, buf, NBPG) < 0) {
		(void)fprintf(stderr, "Failure to write \"%s\" - ",
			name);
		perror("write");
		exit(1);
	}
}

void
fix_libs(fd,offset,name)
FILE	*fd;
long	offset;
char	*name;
{
	int	val;
	/*
	 * lets fix the  .lib section
	 */
	lseek(fd,offset,0);
	/*
	 * total number of words
	 */
	val = strlen(name) / sizeof(int) + 3;
	write(fd,&val,sizeof(int));
	/*
	 * onlt two words before name
	 */
	val = 2;
	write(fd,&val,sizeof(int));
	/*
	 * the name
	 */
	val = strlen(name);
	write(fd,name,val);
	/*
	 * fill to page boundary
	 */
	if (fstat(fd, &st) < 0) {
		fprintf(stderr,"Cannot stat message file \"%s\"\n",createfile);
		exit(1);
	}
	val = NBPG - st.st_size % NBPG;
	if (val) {
	    memset(buf,'\0',val);
	    write(fd,buf,val);
	}
}
