/*
 *  smoke16/include/smoke16/libObj.h - Header file for libObj.
 *  $Revision: 1.26 $
 *  Copyright 1997, 2000, Benjamin Sittler
 *
 *  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.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 */

/* This file defines useful structures and prototypes for the SMOKE-16
   tools. */

/* #define DEBUG_OBJ */
/* #define DEBUG_SYMS */
/* #define DEBUG_RELOCS */
/* #define DEBUG_SEARCH */
/* #define DEBUG_TMPFILE */

#ifndef SMOKE16__LIBOBJ_H_INCLUDED
#define SMOKE16__LIBOBJ_H_INCLUDED

#include <stdio.h>
#include <smoke16/types.h>
#include <smoke16/a_out.h>
#include <smoke16/hashpjw.h>

/*
 * functions to manipulate counted strings
 */

/* counted string */
typedef struct {
  char *buf; /* pointer to start of buffer */
  size_t len; /* portion of buffer which is used */
  size_t buflen; /* total size of buffer, or 0 if buffer is constant */
} Str;

/* create counted string */
extern Str *newStr(size_t buflen);

/* free counted string */
extern void freeStr(Str *s);

/* write counted string */
extern int fputStr(Str *s, FILE *f);

/* write counted string with escaping */
extern int debug_fputStr(Str *s, FILE *f);

/* append to a counted string */
extern Str *apStr(Str *s, Str *t);

/* copy counted string */
extern Str *dupStr(Str *s);

/* create a counted string with a formatted integer */
extern Str *itoStr(int a, unsigned base);

/* check for matching counted strings */
extern int matchStr(Str *s, Str *t);

/* append a char to a counted string */
extern Str *putchStr(Str *s, char c);

/* build a static counted string */
extern Str *staticStr(char *buf, size_t len);

/*
 * functions to (de-)serialize basic SMOKE-16 types
 */

/* convert a native character to an int without losing information */
#define CTOI(x) ((int) (unsigned char) (x))

/* portable characters */
extern int ntohc(int c); /* portable character -> native encoding */
extern int htonc(int c); /* native character -> portable encoding */
extern size_t fget_as(char *n, size_t nmemb, FILE *stream);
extern size_t fput_as(char *n, size_t nmemb, FILE *stream);

/* unsigned bytes */
extern size_t fget_u8(u8 *n, size_t nmemb, FILE *stream);
extern size_t fput_u8(u8 *n, size_t nmemb, FILE *stream);

/* signed bytes */
extern s8 u8_to_s8(u8 uc);
extern size_t fget_s8(s8 *n, size_t nmemb, FILE *stream);
extern size_t fput_s8(s8 *n, size_t nmemb, FILE *stream);

/* unsigned two-byte numbers */
extern size_t fget_u16(u16 *n, size_t nmemb, FILE *stream);
extern size_t fput_u16(u16 *n, size_t nmemb, FILE *stream);

/* signed two-byte numbers */
extern s16 u16_to_s16(u16 uc);
extern size_t fget_s16(s16 *n, size_t nmemb, FILE *stream);
extern size_t fput_s16(s16 *n, size_t nmemb, FILE *stream);

/* unsigned four-byte numbers */
extern size_t fget_u32(u32 *n, size_t nmemb, FILE *stream);
extern size_t fput_u32(u32 *n, size_t nmemb, FILE *stream);

/* signed four-byte numbers */
extern s32 u32_to_s32(u32 uc);
extern size_t fget_s32(s32 *n, size_t nmemb, FILE *stream);
extern size_t fput_s32(s32 *n, size_t nmemb, FILE *stream);

/*
 * functions to (de-)serialize a_out16_* structures
 */

/*
 * WARNING:
 * fput16exec() silently promotes toolversion from "v0" to "v1" in some cases!
 */

/* exec headers */
extern size_t fget16exec(struct a_out16_exec *exec, size_t nmemb, FILE *stream);
extern size_t fput16exec(struct a_out16_exec *exec, size_t nmemb, FILE *stream);

/* LMAGIC directory entries */
extern size_t fget16dirent(struct a_out16_dirent *dirent, size_t nmemb, FILE *stream);
extern size_t fput16dirent(struct a_out16_dirent *dirent, size_t nmemb, FILE *stream);

/* relocations */
extern size_t fget16reloc(struct a_out16_reloc *reloc, size_t nmemb, FILE *stream);
extern size_t fput16reloc(struct a_out16_reloc *reloc, size_t nmemb, FILE *stream);

/* symbols */
extern size_t fget16nlist(struct a_out16_nlist *nlist, size_t nmemb, FILE *stream);
extern size_t fput16nlist(struct a_out16_nlist *nlist, size_t nmemb, FILE *stream);

#define ENTRY_SYM "__entry"

struct Obj;

typedef struct Sym {
  struct Sym *next;
  struct Obj *obj;
  char *string;
  u8 type; /* SMOKE16_N_* */
  s8 other; /* stab "other" field */
  s16 desc; /* stab "desc" field */
  u16 value; /* value or offset */
  u16 index; /* in object file */
  int refcnt; /* reference count */
} Sym;

extern int defSym(Sym *sym, u8 type, s8 other, s16 desc, u16 value, int refcnt);
extern Sym *newSym(char *string, u8 type, s8 other, s16 desc, u16 value);
extern Sym *cpSym(Sym *sym);
extern void freeSym(Sym *sym);

typedef struct SymTab {
  Sym *tab[HASHSIZE];
  int syms;
} SymTab;

extern SymTab *newSymTab(void);
extern void freeSymTab(SymTab *tab);
extern Sym *lookinSymTab(SymTab *tab, char *string);
extern void addtoSymTab(SymTab *tab, Sym *sym);
extern Sym *enterinSymTab(SymTab *tab, Sym *sym);
extern int removefromSymTab(SymTab *tab, Sym *sym);

typedef struct SymSearch {
  SymTab *tab;
  int bucket;
  Sym *node;
  int (*criterion)(Sym *);  
  int syms;
} SymSearch;

extern SymSearch *newSymSearch(SymTab *tab, int (*criterion)(Sym *));
extern void freeSymSearch(SymSearch *search);
extern Sym *nextSym(SymSearch *search);
extern void backupSymSearch(SymSearch *search);

/* search criteria */
extern int anySym(Sym *sym);
extern int externSym(Sym *sym);
extern int externorundfSym(Sym *sym);
extern int commonSym(Sym *sym);
extern int usedSym(Sym *sym);
extern int debugSym(Sym *sym);
extern int alignmentSym(Sym *sym);
extern int nondebugSym(Sym *sym);
extern int usedorexternSym(Sym *sym);
extern int unusedinternSym(Sym *sym);

typedef struct Reloc {
  struct Reloc *next;
  u16 r_extern:1,
        high:1, /* addr_high has meaning */
        low:1, /* addr_low has meaning */
        unused:11,
        type:2; /* SMOKE16_RELOC_* */
  u16 addr_high;
  u16 addr_low;
  u16 value;
  union {
    u8 n_type; /* !r_extern -- section-relative SMOKE16_N_* */
    Sym *sym; /* r_extern -- sym-relative */
  } rel;
} Reloc;

extern Reloc *newSymReloc(u16 type, char high, char low, u16 addr_high, u16 addr_low, u16 value, Sym *sym);
extern Reloc *newSectionReloc(u16 type, char high, char low, u16 addr_high, u16 addr_low, u16 value, u8 n_type);
extern void freeReloc(Reloc *reloc);

typedef struct TmpFile {
  FILE *file;
  char *name;
} TmpFile;

extern TmpFile *newTmpFile(void);
extern void freeTmpFile(TmpFile *tmp);

typedef struct Member {
  struct Member *next; /* next member in archive */
  char *name; /* member name */
  u16 magic; /* copy of member's magic number; used as a sanity check */
  u32 value; /* offset to member in data section */
  u32 size; /* size of member in data section */
  s32 mtime; /* member's modification time */
} Member;

typedef struct Obj {
  Member *first; /* if this object is an archive */
  u16 magic;
  u16 toolversion;
  u16 entry;
  u32 text_size, data_size;
  u16 bss_size;
  u16 text_align, data_align, bss_align;
  TmpFile *text, *data;
  Reloc *text_relocs, *data_relocs;
  SymTab *tab;
  char *name;
  s32 mtime; /* modification time */
} Obj;

extern Obj *newObj(char *name);
extern void freeObj(Obj *obj);
extern Obj *readObj(char *name, FILE *objfile);
extern Obj *extractObj(Obj *archive, Member *member);
extern int writeObj(Obj *obj, FILE *objfile);
extern int linkObj(Obj *obj, Obj *linkin);
extern int simplifyObj(Obj *obj);
extern int loadObj(Obj *obj,
		   int Entry_p, u16 Entry,
		   int Ttext_p, u16 Ttext,
		   int Tdata_p, u16 Tdata,
		   int Tbss_p, u16 Tbss,
		   char *entry);
extern int stripObj(Obj *obj, int stripall);

extern size_t fcopy(FILE *src, FILE *dest, size_t max_bytes);

#endif /* SMOKE16__LIBOBJ_H_INCLUDED */
