/* 
 * $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: cat_build.c 1.5 90/05/21 $@(#)cat_build.c	1.1 89/12/01"

#include <stdio.h>
#include <nl_types.h>
#include <nl_type2.h>
#include <sys/scnhdr.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/param.h>
#include <sys/filehdr.h>
#include <sys/aouthdr.h>
#include <sys/mman.h>
#include <sys/fcntl.h>

static	int	msgattach();

extern	char	*_align_malloc();
extern	void	_align_free();
extern	int	_mmap();


/*
 * Read a catalog and build the internal structure
 */
cat_build(name, res)
  char *name;
  nl_catd res;
{
  struct cat_hdr hdr;
  char *mem;
  int fd, ret;

  /*
   * Read file header
   */
  res->catd_set = 0;
  if((fd=open(name,0)) < 0) {
    /*
     * Might be library with execute
     * permission only
     */
    return cat_lib_build(name,res);
	;
  } else 
  if (read(fd, (char *)&hdr, sizeof(struct cat_hdr)) == sizeof(struct cat_hdr)){
    if (hdr.hdr_magic == CAT_MAGIC) {
      ret = cat_malloc_build(fd,&hdr,res);
      close(fd);
      return ret;
    }
    else {
      close(fd);
      return cat_lib_build(name,res);
    }
  }
  return 0;
}

/*
 * Read a malloc catalog and build the internal structure
 */
cat_malloc_build(fd, hdr, res)
  int fd;
  struct cat_hdr *hdr;
  nl_catd res;
{
  char *mem;
  extern void *malloc();

  if ((mem = malloc(hdr->hdr_mem)) != (char*)0){

    if (read(fd, mem, hdr->hdr_mem) == hdr->hdr_mem){
      res->catd_set = mem;
      res->catd_msgs = mem + hdr->hdr_off_msg_hdr;
      res->catd_data = mem + hdr->hdr_off_msg;
      res->catd_set_nr = hdr->hdr_set_nr;
      res->catd_type = MALLOC;
      return 1;
    } else{
      free(mem);
    }
  }
  return 0;
}

/*
 * Attach the shared library
 * and build the structures.
 */
static
cat_lib_build (catname,res)
  char  *catname;
  nl_catd res;
{
  int addr;
  int msg_num;
  int set_num;
  int *msg_ptr;
  int *set_ptr;
  char *msgs;

  /*
   * attach library to get at data
   * dont need read headers then
   */
  if ((msg_ptr = (int*)msgattach(catname)) == (int*)-1){
    return 0;
  }

  /*
   * get message number 
   * and start of messages
   */
  msg_num  = msg_ptr[0];
  set_num  = msg_ptr[msg_num+1];
  set_ptr  = msg_ptr;
  set_ptr += msg_num+2;
  res->catd_set = (char *)set_ptr;
  res->catd_msgs =(char *)msg_ptr;
  res->catd_set_nr = set_num;
  res->catd_type = SHARED_LIBRARY;
  /*
   * adjust message ptr to
   * the actual data 
   */
  msgs = (char*)msg_ptr;
  msgs  += msg_ptr[1];
  if (msgs <= (char*)set_ptr) {
    /*
     * is summit input
     * so there is no set info;
     */
    res->catd_set = malloc(sizeof (struct cat_lib_set));
    if (res->catd_set) {
      res->catd_set_nr = 1;
      ((struct cat_lib_set*)(res->catd_set))->first_in_set = 1;
      ((struct cat_lib_set*)(res->catd_set))->largest_number = msg_num;
    }
  }
  return 1;
}

/*
 * msgattach - Make a message database available in the address space
 *
 * _align_malloc() is used to provide room for the database, then mmap()
 * makes the file accessable at that address.  The mapping is
 * arranged to minimize impact on commands which link with this routine.
 */

#define	COFF_FHDR(coff) \
	((struct filehdr *)coff)
#define	COFF_AHDR(coff) \
	((struct aouthdr *)((coff)+sizeof(struct filehdr)))
#define	COFF_SHDR0(coff) \
	((struct scnhdr *)((coff) + sizeof(struct filehdr) + \
				    sizeof(struct aouthdr)))

static int
msgattach(path)
char *path;
{
	int		addr;
	int		fd;
	struct	stat	st;
	struct	scnhdr	*shdr;
	int		sindex;
	int		txt_ptr;

	if ((fd = open(path, O_RDONLY)) < 0) {
		return(-1);
	}
	
	if (fstat(fd, &st) < 0) {
		close(fd);
		return(-1);
	}

	if ((addr = (int)_align_malloc(st.st_size, NBPG)) == NULL) {
		close(fd);		/* cannot malloc */
		return(-1);
	}

	/*
	 * Making it executable will mean that the page will remain in
	 * memory a while even after the application goes away.
	 * Making it "shared" means that other processes can use it.
	 * Should mean that the pages will be multiplexed amongst
	 * all processes which need the message database.
	 */

	if (_mmap(addr, st.st_size, PROT_READ|PROT_EXEC, MAP_SHARED, fd, 0) < 0)
	{
		_align_free(addr);	/* cannot map */
		close(fd);
		return(-1);
	}

	if ( COFF_FHDR(addr)->f_magic != I386MAGIC ||	/* magic no. wrong */
	     COFF_AHDR(addr)->magic != 0443 ) {
		_mmap(addr, st.st_size, PROT_RDWR, MAP_ZEROFILL, 0, 0);
		_align_free(addr);
		close(fd);
		return(-1);
	}

	shdr = COFF_SHDR0(addr);		/* look for .text section */
	for (sindex = 0; sindex < COFF_FHDR(addr)->f_nscns; sindex++, shdr++ ) {
		if (!strcmp( shdr->s_name, ".text" ))
			break;
	}

	if (sindex == COFF_FHDR(addr)->f_nscns) {	/* no .text */
		_mmap(addr, st.st_size, PROT_RDWR, MAP_ZEROFILL, 0, 0);
		_align_free(addr);
		close(fd);
		return(-1);
	}

	txt_ptr = shdr->s_scnptr;		/* get .text section start */

	if (_mmap(addr, txt_ptr, PROT_RDWR, MAP_ZEROFILL, 0, 0) < 0)
	{					/* unmap COFF header */
		_mmap(addr, st.st_size, PROT_RDWR, MAP_ZEROFILL, 0, 0);
		_align_free(addr);
		close(fd);
		return(-1);
	}

	close(fd);			/* space should still be mapped */
	addr += txt_ptr;
	((int*)addr)[-1] = st.st_size - txt_ptr;	/* size for unmap */
	((int*)addr)[-2] = addr - txt_ptr;		/* addr for free */
	return(addr);
}
