/* 
 * $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.
 */

/*	Copyright (c) 1984 AT&T	*/
/*	  All Rights Reserved  	*/

/*	THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE OF AT&T	*/
/*	The copyright notice above does not evidence any   	*/
/*	actual or intended publication of such source code.	*/
#ident "$Header: gencat.c 1.6 90/05/23 $@(#)gencat.c  1.1 89/12/06 "
#include <stdio.h>
#include <ctype.h>
#include <nl_types.h>
#include "nl_type2.h"
#include <sys/param.h>
/*
 * gencat : take a message source file and produce a message catalog.
 * usage : gencat catfile [msgfile]
 * if msgfile is not specified, read from standard input.
 * Several message file can be specified
 */

#if NL_TEXTMAX > NBPG
char msg_buf[NL_TEXTMAX];
#else
char msg_buf[NBPG];
#endif

struct cat_set *sets = 0;
int curset = NL_SETD;
int no_output = 0;
int mkmsgs = FALSE;
int list = FALSE;
int malloc_format = TRUE;

extern char *gettxt();
extern int text_start;
extern int text_page;

FILE *tempfile;
FILE *fd_temp;

main(argc, argv)
  int argc;
  char **argv;
{
  char catname[128];
  FILE *fd;
  register int i;
  int cat_exists;
  char *progname = argv[0];
  
  argv++;
  argc--;

  /*
   * test for mkmsgs format
   */
  while (argc > 0 && **argv == '-') {
    switch (argv[0][1]) {

      case 's' :
	mkmsgs = TRUE;
	argv++;
	argc--;
	break;

      case 'l' :
	list = TRUE;
	argv++;
	argc--;
	break;

      case 'c' :
	malloc_format = FALSE;
	argv++;
	argc--;
	break;

      case 't' :
	argv++;
	argc--;
	text_start = atoi(argv[0]);
	argv++;
	argc--;
	break;

      case 'p' :
	argv++;
	argc--;
	text_page = atoi(argv[0]);
	argv++;
	argc--;
	break;

      default :
	usage(progname);
	exit(1);

    }
  }
      
  if (argc < 1) {
    usage(progname);
    exit(1);
  }

  /*
   * Create tempfile
   */
  if ((tempfile = tmpfile()) == NULL){
    perror("tempfile");
    exit(1);
  }
  
  /*
   * Check catfile name : if file exists, read it.
   */
  strcpy(catname,*argv);
  
  /*
   * Note that mkmsgs will not allow
   * you to use as input an shared library
   */
  if (!mkmsgs && malloc_format && cat_build(catname))
    cat_exists = TRUE;
  else
    cat_exists = FALSE;

  /*
   * add a default message set
   */
  add_set(curset);

  /*
   * Open msgfile(s) or use stdin and call handling proc
   */
  if (argc == 1)
    cat_msg_build("stdin", stdin);
  else {
    for (i = 1 ; i < argc ; i++){
      if ((fd = fopen(argv[i], "r")) == 0){
        /*
         * Cannot open file for reading
         */
        perror(argv[i]);
        continue;
      }
      if (argc > 2)
        fprintf(stdout, "%s:\n", argv[i]);
      cat_msg_build(argv[i], fd);
      fclose(fd);
    }
  }

  if (!no_output){
    /*
     * Work is done. Time now to open catfile (for writing) and to dump
     * the result
     */

    if (malloc_format) {
	if ((fd = fopen(catname, "w")) == NULL){
	  perror(catname);
	  fatal();
	}
	cat_dump(fd);
	fclose(fd);
    } else {
	fd_temp = tmpfile();
	if (fd_temp == NULL) {
	    fprintf(stderr,gettxt("gencat:28","Cannot open tempory file\n"));
	    fatal();
	}
	cat_lib_dump(fd_temp);
	genlib(catname,fd_temp);
    }
  } else {
    fprintf(stderr, gettxt("gencat:29","%s not %s\n"), catname, cat_exists ? "updated":"created");
    exit(1);
  }

  exit(0);
}

usage(name)
  char *name;
{
  fprintf(stderr, gettxt("gencat:30","Usage: %s [-c][-s][-l] catfile [msgfile ...]\n"), name);
  exit(1);
}

char linebuf[BUFSIZ];

/*
 * Scan message file and complete the tables
 */
cat_msg_build(filename, fd)
  char *filename;
  FILE *fd;
{
  register int i, j;
  char quotechar = 0;
  char c;
  int msg_nr = 0;
  int msg_len;
  
  while (fgets(linebuf, BUFSIZ, fd) != 0){

    /*
     * If mkmsgs format then
     * just add in increasing
     * order to set number 1
     */
    if (mkmsgs) {
      if(msg_nr++ == 0) {
	curset=1;
	add_set(curset);
      }
      msg_len = strlen(linebuf);
      linebuf[msg_len] = '\0';
      if ((msg_len = msg_conv(filename, curset, msg_nr, fd, linebuf,
        BUFSIZ, msg_buf, NL_TEXTMAX, quotechar)) == -1)
        fprintf(stderr, gettxt("gencat:31","Message too long %d -- Ignored\n"),msg_nr);
      else
        add_msg(curset, msg_nr, msg_len + 1, msg_buf);
      continue;
    }

    if ((i = skip_blanks(linebuf, 0)) == -1)
      continue;

    if (linebuf[i] == '$'){
      i += 1;
      /*
       * Handle commands or comments
       */
      if (strncmp(&linebuf[i], CMD_SET, CMD_SET_LEN) == 0){
        i += CMD_SET_LEN;

        /*
         * Change current set
         */
        if ((i = skip_blanks(linebuf, i)) == -1){
          fprintf(stderr, gettxt("gencat:77","Incomplete set command -- Ignored\n"));
          continue;
        }
        add_set(atoi(&linebuf[i]));
        continue;
      }
      if (strncmp(&linebuf[i], CMD_DELSET, CMD_DELSET_LEN) == 0){
  i += CMD_DELSET_LEN;

        /*
         * Delete named set
         */
        if ((i = skip_blanks(linebuf, i)) == -1){
          fprintf(stderr, gettxt("gencat:32","Incomplete delset command -- Ignored\n"));
          continue;
        }
        del_set(atoi(&linebuf[i]));
        continue;
      }
      if (strncmp(&linebuf[i], CMD_QUOTE, CMD_QUOTE_LEN) == 0){
        i += CMD_QUOTE_LEN;

        /*
         * Change quote character
         */
        if ((i = skip_blanks(linebuf, i)) == -1)
          quotechar = 0;
        else
          quotechar = linebuf[i];
        continue;
      }
      /*
       * Everything else is a comment
       */
      continue;
    }
    if (isdigit(linebuf[i])){
      msg_nr = 0;
      /*
       * A message line
       */
      if (curset == 0){
        fprintf(stderr, gettxt("gencat:33","No current set -- Ignored\n"));
        continue;
      }
      while(isdigit(c = linebuf[i])){
        msg_nr *= 10;
        msg_nr += c - '0';
        i++;
      }
      j = i;
      if (!linebuf[i] || linebuf[i] == '\n')
          del_msg(curset, msg_nr);
      else if ((i = skip_blanks(linebuf, i)) == -1) {
          add_msg(curset, msg_nr, 1, "");
      }
      else {
        if ((msg_len = msg_conv(filename, curset, msg_nr, fd, linebuf + j,
        BUFSIZ, msg_buf, NL_TEXTMAX, quotechar)) == -1){
    no_output = 1;
          continue;
  }
        add_msg(curset, msg_nr, msg_len + 1, msg_buf);
      }
      continue;
    }
    
    /*
     * Everything else is unexpected
     */
    fprintf(stderr, gettxt("gencat:34","Unexpected line -- Skipped\n"));
  }
}

/*
 * Skip blanks in a line buffer
 */
skip_blanks(linebuf, i)
  char *linebuf;
  int i;
{
  while (linebuf[i] && isspace(linebuf[i]) && !iscntrl(linebuf[i]))
    i++;
  if (!linebuf[i] || linebuf[i] == '\n')
    return -1;
  return i;
}

/*
 * Dump the internal structure into the catfile.
 */
cat_dump(fd)
  FILE *fd;
{
  FILE *f_shdr, *f_mhdr, *f_msg;
  long o_shdr, o_mhdr, o_msg;
  struct cat_set *p_sets;
  struct cat_msg *p_msg;
  struct cat_hdr hdr;
  struct cat_set_hdr shdr;
  struct cat_msg_hdr mhdr;
  int msg_len;
  int nmsg = 0;
  
  if ((f_shdr = tmpfile()) == NULL || (f_mhdr = tmpfile()) == NULL ||
      (f_msg = tmpfile()) == NULL){
    fprintf(stderr, gettxt("gencat:35","cat_dump : Cannot create temp files\n"));
    fatal();
  }
  o_shdr = 0;
  o_mhdr = 0;
  o_msg = 0;
  
  p_sets = sets;
  hdr.hdr_set_nr = 0;
  hdr.hdr_magic = CAT_MAGIC;
  while (p_sets != 0){
    hdr.hdr_set_nr++;
    p_msg = p_sets->set_msg;
    
    /*
     * Keep offset in shdr temp file to mark the set's begin
     */
    shdr.shdr_msg = nmsg;
    shdr.shdr_msg_nr = 0;
    shdr.shdr_set_nr = p_sets->set_nr;
    while (p_msg != 0){
      shdr.shdr_msg_nr++;
      nmsg++;
      msg_len = p_msg->msg_len;
      /*
       * Get message from main temp file
       */
      if (fseek(tempfile, p_msg->msg_off, 0) != 0){
        fprintf(stderr, gettxt("gencat:36","cat_dump : Seek error in temp file\n"));
        fatal();
      }
      if (fread(msg_buf, 1, msg_len, tempfile) != msg_len){
        fprintf(stderr, gettxt("gencat:37","cat_dump : Read error in temp file\n"));
        fatal();
      }
      
      /*
       * Put it in the messages temp file and keep offset
       */
      mhdr.msg_ptr = (int)ftell(f_msg);
      mhdr.msg_len = msg_len;
      mhdr.msg_nr  = p_msg->msg_nr;
      if (fwrite(msg_buf, 1, msg_len, f_msg) != msg_len){
        fprintf(stderr, gettxt("gencat:38","cat_dump : Write error in msg temp file\n"));
        fatal();
      }

      /*
       * Put message header
       */
      if (fwrite((char *)&mhdr, sizeof(mhdr), 1, f_mhdr) != 1){
        fprintf(stderr, gettxt("gencat:39","cat_dump : Write error in mhdr temp file\n"));
        exit(1);
      }
      p_msg = p_msg->msg_next;
    }
    
    /*
     * Put set hdr
     */
    if (fwrite((char *)&shdr, sizeof(shdr), 1, f_shdr) != 1){
      fprintf(stderr, gettxt("gencat:40","cat_dump : write error in shdr temp file\n"));
      fatal();
    }
    p_sets = p_sets->set_next;
  }
  
  /*
   * Fill file header
   */
  hdr.hdr_mem = ftell(f_shdr) + ftell(f_mhdr) + ftell(f_msg);
  hdr.hdr_off_msg_hdr = ftell(f_shdr);
  hdr.hdr_off_msg = hdr.hdr_off_msg_hdr + ftell(f_mhdr);

  /*
   * Generate catfile
   */
  if (fwrite((char *)&hdr, sizeof (hdr), 1, fd) != 1){
    fprintf(stderr, gettxt("gencat:41","cat_dump : hdr write error in catfile\n"));
    fatal();
  }
  
  copy(fd, f_shdr);
  copy(fd, f_mhdr);
  copy(fd, f_msg);
  fclose(f_shdr);
  fclose(f_mhdr);
  fclose(f_msg);
}

static char copybuf[BUFSIZ];
static
copy(dst, src)
  FILE *dst, *src;
{
  int n;
  
  rewind(src);
  
  while ((n = fread(copybuf, 1, BUFSIZ, src)) > 0){
    if (fwrite(copybuf, 1, n, dst) != n){
      fprintf(stderr, gettxt("gencat:42","copy : Write error\n"));
      fatal();
    }
  }
}


/*
 * dump a shared library file
 */

cat_lib_dump(fd)
  FILE *fd;
{
  FILE *f_shdr, *f_mhdr, *f_msg;
  struct cat_set *p_sets;
  struct cat_msg *p_msg;
  struct cat_lib_set set;
  int msg_len;
  int nmsg = 1;
  int nsets = 0;
  int moffset;
  
  if ((f_shdr = tmpfile()) == NULL || (f_mhdr = tmpfile()) == NULL ||
      (f_msg = tmpfile()) == NULL){
    fprintf(stderr, gettxt("gencat:43","cat_lib_dump : Cannot create temp files\n"));
    fatal();
  }
  
  init_message(f_mhdr);

  p_sets = sets;
  while (p_sets != 0){
    nsets++;
    /*
     * if set holes then
     * fill them
     */
    set.first_in_set = 0;
    set.largest_number = 0;
    while (nsets != p_sets->set_nr) {
      if (fwrite((char *)&set, sizeof (struct cat_lib_set) , 1, f_shdr) != 1){
        fprintf(stderr, gettxt("gencat:44","cat_lib_dump : write error in shdr temp file\n"));
        fatal();
      }
      nsets++;
    }
    p_msg = p_sets->set_msg;
    
    /*
     * Keep offset in shdr temp file to mark the set's begin
     */
    set.largest_number = 0;
    if (p_msg)
	set0(p_msg->msg_nr);
    set.first_in_set = nmsg;

    while (p_msg != 0){
      msg_len = p_msg->msg_len;
      /*
       * Get message from main temp file
       */
      if (fseek(tempfile, p_msg->msg_off, 0) != 0){
        fprintf(stderr, gettxt("gencat:45","cat_lib_dump : Seek error in temp file\n"));
        fatal();
      }
      if (fread(msg_buf, 1, msg_len, tempfile) != msg_len){
        fprintf(stderr, gettxt("gencat:46","cat_lib_dump : Read error in temp file\n"));
        fatal();
      }
      
      /*
       * Put it in the messages temp file and keep offset
       */
      moffset = ftell(f_msg);
      set.largest_number  = p_msg->msg_nr;
      if (fwrite(msg_buf, 1, msg_len, f_msg) != msg_len){
        fprintf(stderr, gettxt("gencat:47","cat_lib_dump : Write error in msg temp file\n"));
        fatal();
      }
      nmsg = put_message(p_msg->msg_nr,moffset);
      p_msg = p_msg->msg_next;
    }
    
    /*
     * Put set hdr into set temp file
     */
    if (fwrite((char *)&set, sizeof (struct cat_lib_set) , 1, f_shdr) != 1){
      fprintf(stderr, gettxt("gencat:48","cat_lib_dump : write error in shdr temp file\n"));
      fatal();
    }
    p_sets = p_sets->set_next;
  }
  
  mflush(nmsg-1);
  /*
   * seek to begining of output file
   * and then write data
   */
  if (fseek(fd, p_msg->msg_off, 0) != 0){
    fprintf(stderr, gettxt("gencat:49","cat_lib_dump : Seek error in temp file\n"));
    fatal();
  }

  mwrite(fd,nmsg-1,f_mhdr,nsets,f_shdr,f_msg);

  fclose(f_shdr);
  fclose(f_mhdr);
  fclose(f_msg);
}


/*
 * the following routines build 
 * the message array to be
 * put at the begining of
 * the shared library.
 * The address will be relative 
 * and is fixed up in mwrite()
 */

static FILE * f_msghdr;
static int current = 0;           /* Current message number */
static int current_in_set = 0;    /* Current message number */
				  /* in the set. Used to    */
				  /* calculate gaps.        */
static int mesg_addresses[MSIZE]; /* A portion of the array */
				  /* is kept to improve I/O */


init_message( fp )
FILE *fp;
{
int i;
	
  f_msghdr = fp;
  for (i=0;i<MSIZE;i++)
	mesg_addresses[i] = 0;
}

/*
 * first in set 
 * for put message
 */
static
set0(x)
{
int i;

  for (current_in_set=1; current_in_set < x ;) {

    current_in_set++;
    i = current++ % MSIZE;
    mesg_addresses[i] = 0;
    if ( (current % MSIZE) == 0)
	mflush(MSIZE);
  }
}

/*
 * Put message remembers
 * what message is put where 
 * by building the shared library
 * messages array
 * If there are holes we set them to
 * zero
 */

static int
put_message ( number_in_set , address )
{
int i;

  for (current_in_set; current_in_set < number_in_set ;) {

    current_in_set++;
    i = current++ % MSIZE;
    mesg_addresses[i] = 0;
    if ( (current % MSIZE) == 0)
	mflush(MSIZE);

  }
	
  current_in_set++;
  i = current++ % MSIZE;
  mesg_addresses[i] = address;
  if ( (current % MSIZE) == 0)
    mflush(MSIZE);

  /*
   * return current message
   * number which is always
   * +1 as real messages begin at 1 
   * not 0 as in mesg_addresses
   */
  return current + 1;
}

/*
 * flush save message array
 */

static
mflush ( x )
{

  if (x <= 0)
    return;

  if (x != MSIZE)
    x %= MSIZE;
  if (fwrite(mesg_addresses,sizeof (int) ,x,f_msghdr) != x) {
    fprintf(stderr,gettxt("gencat:50","cat_lib_dump : write error in mesg temp file\n"));
    fatal();
  }
}

/*
 * perform fixups on tempory
 * message array. Then write to real
 * thing
 */

static
mwrite(fd, nmsg, f_mhdr, nsets, f_shdr, f_msg)
int    fd, nmsg,         nsets;
FILE            *f_mhdr,       *f_shdr,*f_msg;
{
int offset;
int i;
int x;
char array[512];
struct cat_lib_set set;
int first_time;

  /*
   * calculate the offset
   * of the data
   */

  /*
   * write number of messages
   */
  if (fseek(fd, 0L , 0) != 0){
    fprintf(stderr, gettxt("gencat:51","cat_lib_dump : Seek error in catalog file\n"));
    fatal();
  }
  if (fwrite(&nmsg,sizeof (int),1,fd) != 1){
    fprintf(stderr,gettxt("gencat:52","cat_lib_dump : write error in catalog file\n"));
    fatal();
  }

  /*
   * write messages adreses with fixup
   */

  offset = (nmsg + 2) * sizeof(int) + nsets * sizeof(struct cat_lib_set);

  if (fseek(f_mhdr, 0L , 0) != 0){
    fprintf(stderr, gettxt("gencat:53","cat_lib_dump : Seek error in mesg header temp file\n"));
    fatal();
  }
  first_time = 1;
  while ((x=fread(mesg_addresses,sizeof (int),MSIZE,f_mhdr)) > 0 ) {
    for(i=0;i<x;i++)
      if(first_time || mesg_addresses[i]) {
        first_time = 0;
        mesg_addresses[i] += offset;
    }
    if (fwrite(mesg_addresses,sizeof (int),x,fd) != x){
      fprintf(stderr,gettxt("gencat:54","cat_lib_dump : write error in catalog file\n"));
      fatal();
    }
  }
  if (x < 0) {
    fprintf(stderr,gettxt("gencat:55","cat_lib_dump : read error in mesg header temp file\n"));
    fatal();
  }

  /*
   * write no of sets
   */
  if (fwrite(&nsets,sizeof (int),1,fd) != 1){
    fprintf(stderr,gettxt("gencat:56","cat_lib_dump : write error in catalog file\n"));
    fatal();
  }

  /*
   * write the set info
   */
  if (fseek(f_shdr, 0L , 0) != 0){
    fprintf(stderr, gettxt("gencat:57","cat_lib_dump : Seek error in set header temp file\n"));
    fatal();
  }
  while ((x=fread(&set,sizeof (struct cat_lib_set),1,f_shdr)) == 1 ) {
    if (fwrite(&set,sizeof (struct cat_lib_set),1,fd) != 1){
      fprintf(stderr,gettxt("gencat:58","cat_lib_dump : write error in catalog file\n"));
      fatal();
    }
  }
  if (x < 0) {
    fprintf(stderr,gettxt("gencat:59","cat_lib_dump : read error in set header temp file\n"));
    fatal();
  }

  /*
   * get the messages
   */
  if (fseek(f_msg, 0L , 0) != 0){
    fprintf(stderr, gettxt("gencat:60","cat_lib_dump : Seek error in message temp file\n"));
    fatal();
  }
  while ((x=fread(array,1,sizeof (array),f_msg)) > 0 ) {
    if (fwrite(array,1 ,x,fd) != x){
      fprintf(stderr,gettxt("gencat:61","cat_lib_dump : write error in catalog file\n"));
      fatal();
    }
  }
  if (x < 0) {
    fprintf(stderr,gettxt("gencat:62","cat_lib_dump : read error in message temp file\n"));
    fatal();
  }

}

fatal()
{
  exit(1);
}
