/*
 * --------------------------------------------------------------------------
 *
 * module: buildinfo.cpp
 *
 * --------------------------------------------------------------------------
 * --------------------------------------------------------------------------
\if EMIT_LICENCE


    ========== licence begin  GPL
    Copyright (c) 2003-2005 SAP AG

    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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
    ========== licence end


\endif
 */

#ifndef WIN32
#define UNIX
#endif

#include "heo01.h"
#include "cn/buildinfo/buildinfo.h"
#include "SAPDB/SAPDBCommon/SAPDB_string.h"

#ifdef WIN32
#include <WINDOWS.h>
#include <tchar.h>
#include <io.h>
#include <fcntl.h>
#include <malloc.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#endif

#ifdef UNIX
#include <ctype.h>
#include <dirent.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#endif

#ifdef __osf__
#pragma message disable codeunreachable
#endif

#ifdef WIN32
#ifndef alloca
#define alloca(a) _alloca(a)
#endif
#ifndef open 
#define open(a,b) _open(a,b)
#endif
#ifndef read
#define read(a,b,c) _read(a,b,c)
#endif
#ifndef close
#define close(a) _close(a)
#endif
#ifndef snprintf
#define snprintf _snprintf
#endif
#ifndef strupr
#define strupr _strupr
#endif
#endif

#ifdef UNIX
#ifndef O_BINARY
#define O_BINARY 0
#endif
#endif

#if defined WIN32
#define NEED_TYPEDEF_UCHAR_T NEED_TYPEDEF_UCHAR_T
#define NEED_TYPEDEF_UNIT16_T NEED_TYPEDEF_UNIT16_T
#define NEED_TYPEDEF_UNIT32_T NEED_TYPEDEF_UNIT32_T
#define NEED_TYPEDEF_UNIT64_T NEED_TYPEDEF_UNIT64_T
#endif

#ifdef NEED_TYPEDEF_UCHAR_T
typedef unsigned char  uchar_t;
#endif
#ifdef NEED_TYPEDEF_UNIT16_T
typedef unsigned short uint16_t;
#endif
#ifdef NEED_TYPEDEF_UNIT32_T
typedef unsigned int   uint32_t;
#endif
#ifdef NEED_TYPEDEF_UNIT64_T
typedef unsigned long  uint64_t;
#endif

#if defined _WIN64
typedef __int64 ptr_t;
#else
typedef long ptr_t;
#endif

/*
 * common types and prototypes
 */
typedef struct buildinfo {
	char *key;
	char *val;
	struct buildinfo *next;
} buildinfo_t;

static buildinfo_t *
buildinfo_add (buildinfo_t *, char *, char *);
static void buildinfo_free (buildinfo_t *);

#define BUILD_TYPE_REL 1
#define BUILD_TYPE_APO 2

/*
 * windows part
 */
#ifdef WIN32
typedef struct {
	int value;
	char name[64];
} names_t;

static char *
get_name (int value, names_t *ptr) {
	while (ptr->value >= 0) {
		if (ptr->value == value)
			return (ptr->name);

		ptr++;
	} 
	return 0;
}

/*
 * structures from windows PE header
 */
typedef struct {
	uint16_t e_magic;    /* Magic number */
	uint16_t e_cblp;     /* Bytes on last page of file */
	uint16_t e_cp;       /* Pages in file */
	uint16_t e_crlc;     /* Relocations */
	uint16_t e_cparhdr;  /* Size of header in paragraphs */
	uint16_t e_minalloc; /* Minimum extra paragraphs needed */
	uint16_t e_maxalloc; /* Maximum extra paragraphs needed */
	uint16_t e_ss;       /* Initial (relative) SS value */
	uint16_t e_sp;       /* Initial SP value */
	uint16_t e_csum;     /* Checksum */
	uint16_t e_ip;       /* Initial IP value */
	uint16_t e_cs;       /* Initial (relative) CS value */
	uint16_t e_lfarlc;   /* File address of relocation table */
	uint16_t e_ovno;     /* Overlay number */
	uint16_t e_res[4];   /* Reserved words */
	uint16_t e_oemid;    /* OEM identifier (for e_oeminfo) */
	uint16_t e_oeminfo;  /* OEM information; e_oemid specific */
	uint16_t e_res2[10]; /* Reserved words */
	uint16_t e_lfanew;   /* File address of new exe header */
} win_doshdr_t;

typedef struct {
	uchar_t  Signatur[4];
	uint16_t Machine;
	uint16_t NumberOfSections;
	uint32_t TimeDateStamp;
	uint32_t PointerToSymbolTable;
	uint32_t NumberOfSymbols;
	uint16_t SizeOfOptionalHeaders;
	uint16_t Characteristics;
} win_imagefilehdr_t;

typedef struct {
	uint16_t Magic;
	uchar_t MajorLinkerVersion;
	uchar_t MinorLinkerVersion;
	uint32_t SizeOfCode;
	uint32_t SizeOfInitializedData;
	uint32_t SizeOfUninitializedData;
	uint32_t AddressOfEntryPoint;
	uint32_t BaseOfCode;
	uint32_t BaseOfData;
	uint32_t ImageBase;
	uint32_t SectionAlignment;
	uint32_t FileAlignment;
	uint16_t MajorOperatingSystemVersion;
	uint16_t MinorOperatingSystemVersion;
	uint16_t MajorImageVersion;
	uint16_t MinorImageVersion;
	uint16_t MajorSubsystemVersion;
	uint16_t MinorSubsystemVersion;
	uint32_t Win32VersionValue;
	uint32_t SizeOfImage;
	uint32_t SizeOfHeaders;
	uint32_t CheckSum;
	uint16_t Subsystem;
	uint16_t DllCharacteristics;
	uint32_t SizeOfStackReserve;
	uint32_t SizeOfStackCommit;
	uint32_t SizeOfHeapReserve;
	uint32_t SizeOfHeapCommit;
	uint32_t LoaderFlags;
	uint32_t NumberOfRvaAndSizes;
} win_optionalhdr32_t;

typedef struct {
	uint16_t Magic;
	uchar_t MajorLinkerVersion;
	uchar_t MinorLinkerVersion;
	uint32_t SizeOfCode;
	uint32_t SizeOfInitializedData;
	uint32_t SizeOfUninitializedData;
	uint32_t AddressOfEntryPoint;
	uint32_t BaseOfCode;
	uint32_t BaseOfData;
	uint32_t ImageBase;
	uint32_t SectionAlignment;
	uint32_t FileAlignment;
	uint16_t MajorOperatingSystemVersion;
	uint16_t MinorOperatingSystemVersion;
	uint16_t MajorImageVersion;
	uint16_t MinorImageVersion;
	uint16_t MajorSubsystemVersion;
	uint16_t MinorSubsystemVersion;
	uint32_t Win32VersionValue;
	uint32_t SizeOfImage;
	uint32_t SizeOfHeaders;
	uint32_t CheckSum;
	uint16_t Subsystem;
	uint16_t DllCharacteristics;
	uint32_t SizeOfStackReserve0;
	uint32_t SizeOfStackReserve1;
	uint32_t SizeOfStackCommit0;
	uint32_t SizeOfStackCommit1;
	uint32_t SizeOfHeapReserve0;
	uint32_t SizeOfHeapReserve1;
	uint32_t SizeOfHeapCommit0;
	uint32_t SizeOfHeapCommit1;
	uint32_t LoaderFlags;
	uint32_t NumberOfRvaAndSizes;
} win_optionalhdr64_t;

typedef struct {
	uint32_t VirtualAddress;
	uint32_t Size;
} win_datadirectory_t;

typedef struct {
	uchar_t Name[8];
	uint32_t PhysicalAddress;
	uint32_t VirtualAddress;
	uint32_t SizeOfRawData;
	uint32_t PointerToRawData;
	uint32_t PointerToRelocations;
	uint32_t PointerToLinenumbers;
	uint16_t NumberOfRelocations;
	uint16_t NumberOfLinenumbers;
	uint32_t Characteristics;
} win_sectionhdr_t;

typedef struct {
	uint32_t Characteristics;
	uint32_t TimeDateStamp;
	uint16_t MajorVersion;
	uint16_t MinorVersion;
	uint16_t NumberOfNamedEntries;
	uint16_t NumberOfIdEntries;
} win_resdirectory_t;

typedef struct {
	uint32_t Name;
	uint32_t OffsetToData;
} win_resdirentry_t;

typedef struct {
	uint32_t OffsetToData;
	uint32_t Size;
	uint32_t CodePage;
	uint32_t Reserved;
} win_resdataentry_t;

typedef struct {
	uchar_t Signatur[4];
} win_signatur_t;

static uchar_t win_dosmagic[2] = {'M', 'Z'};
static uchar_t win_ntmagic[4] = {'P', 'E', '\0', '\0'};

typedef struct {
	uint16_t Size;
	uint16_t ValueSize;
	uint16_t Type;
	uint16_t Magic[16];
} win_versioninfo_t;

typedef struct {
	uint16_t Size;
	uint16_t ValueSize;
	uint16_t Type;
	uint16_t Magic[15];
} win_stringfileinfo_t;

typedef struct {
	uint16_t Size;
	uint16_t ValueSize;
	uint16_t Type;
	uint16_t Magic[12];
} win_varfileinfo_t;

typedef struct {
	uint16_t Size;
	uint16_t ValueSize;
	uint16_t Type;
	uint16_t Key[9];
} win_stringtable_t;

typedef struct {
	uint16_t Size;
	uint16_t ValueSize;
	uint16_t Type;
} win_string_t;

static uint16_t win_versioninfo_magic[16] =
	{'V', 'S', '_', 'V', 'E', 'R', 'S', 'I', 'O', 'N', '_',
	 'I', 'N', 'F', 'O', 0};

static uint16_t win_stringfileinfo_magic[15] =
	{'S', 't', 'r', 'i', 'n', 'g', 'F', 'i', 'l', 'e',
	 'I', 'n', 'f', 'o', 0};

static uint16_t win_varfileinfo_magic[12] =
	{'V', 'a', 'r', 'F', 'i', 'l', 'e',
	 'I', 'n', 'f', 'o', 0};

static names_t win_flag_names[] = {
	{0x0002, "executable"},
	{0x1000, "system file"},
	{0x2000, "dll"},
	{-1, ""}
};

static names_t win_machine_names[] = {
	{0x0000, "unknown machine"},
	{0x014c, "i386"},
	{0x0160, "MIPS R3000 big-endian"},
	{0x0166, "MIPS R4000 little-endian"},
	{0x0168, "MIPS R10000 little-endian"},
	{0x0169, "MIPS WCE v2 little-endian"},
	{0x0184, "ALPHA 32-bit"},
	{0x01f0, "PowerPC"},
	{0x0200, "IA64"},
	{0x0284, "ALPHA 64-bit"},
	{-1, ""}
};

static int
is_windowsdos (char *buff, char *sz_text, int cb_text) {
	win_doshdr_t *doshdr;

	doshdr = (win_doshdr_t *) buff;

	if (memcmp 
	(&doshdr->e_magic, win_dosmagic, sizeof (win_dosmagic)) != 0) {
		return -1;
	}

	snprintf (sz_text, cb_text - 1, "MS DOS");
	return 0;
}

static int
is_windowspe (char *buff, char *sz_text, int cb_text) {
	win_imagefilehdr_t *winnthdr;
	win_signatur_t *signature;
	char *machine;
	names_t *ptr;

	signature = (win_signatur_t *) buff;
	winnthdr = (win_imagefilehdr_t *) buff;

	if (memcmp 
	(&signature->Signatur, win_ntmagic, sizeof (win_ntmagic)) != 0) {
		return -1;
	}

	machine = get_name (winnthdr->Machine, win_machine_names);
	if (machine == 0)
		return -1;

	snprintf (sz_text, cb_text - 1, "MS Windows");
	switch (winnthdr->Machine) {
	case 0x014c:
		/* i386 */
		snprintf (sz_text + strlen (sz_text), cb_text - 1, " 32-bit");
		break;
	case 0x0200:
		/* ia64 */
		snprintf (sz_text + strlen (sz_text), cb_text - 1, " 64-bit");
		break;
	}

	snprintf (sz_text + strlen (sz_text), cb_text - 1, " %s", machine);

	ptr = win_flag_names;
	while (ptr->value != -1) {
		if (ptr->value & winnthdr->Characteristics) {
			snprintf (sz_text + strlen (sz_text),
			          cb_text - 1, " %s", ptr->name);
		}
		ptr++;
	}
	return 0;
}

static int
get_magic (char *buff, char *sz_text, int cb_text) {
	if (is_windowsdos (buff, sz_text, cb_text) == 0)
		return 0;

	if (is_windowspe (buff, sz_text, cb_text) == 0)
		return 0;

	strncpy (sz_text, "unknown magic", cb_text - 1);	
	return 0;
}

typedef struct {
	int peoffset;
	struct {
		int offset;
		int size;
	} res;
	win_doshdr_t        * doshdr;
	win_imagefilehdr_t  * filehdr; 

	win_optionalhdr32_t * opthdr32; 
	win_optionalhdr64_t * opthdr64; 
	win_datadirectory_t * datadir;
	win_sectionhdr_t    * sechdr;
} win_image_t;

typedef struct session {
	struct session *next;
	int fd;
	char *buff;
	int offset;
	int size;
} session_t;

static void calc_peoffset (win_image_t *);
static void calc_resoffset (win_image_t *);
static int virtual2real (win_image_t *, int);
static int get_versionresource (char *, int, int, int *);

static session_t * sess_root = 0;

static session_t *
get_session (int fd) {
	session_t *curr, *prev;

	if (sess_root == 0) {
		curr = (session_t *) malloc (sizeof (session_t));
		if (curr == 0)
			return 0;

		memset (curr, 0, sizeof (session_t));
		curr->fd = fd;

		sess_root = curr;
		return (curr);
	}

	for (curr = sess_root; curr != 0;) {
		if (curr->fd == fd)
			return (curr);

		prev = curr;
		curr = curr->next;
	}

	curr = (session_t *) malloc (sizeof (session_t));
	if (curr == 0)
		return 0;

	memset (curr, 0, sizeof (session_t));
	curr->fd = fd;

	prev->next = curr;
	return (curr);
}

static void
del_session (int fd) {
	session_t *curr, *prev;

	if (sess_root == 0)
		return;

	for (curr = sess_root; curr != 0;) {
		if (curr->fd == fd) {
			if (curr == sess_root) 
				sess_root = curr->next;
			else
				prev->next = curr->next;

			free (curr);
			return;
		}

		prev = curr;
		curr = curr->next;
	}

	return;
}

static void *
get_versioninfo (int fd, void *resinfo, int *p_size) {
	int got = 0;
	int total = 0;
	int offset = 0;

	char *buff = 0;
	int versioninfo = 0;

	session_t *sess;
	win_image_t *image;

	sess = get_session (fd);

	if (sess->buff != 0) {
		*p_size = 0;

		if (sess->buff != 0) {
			if (resinfo != 0) {
				memcpy (resinfo, sess->buff, sess->size);
				*p_size = sess->size;
			}

			free (sess->buff);
			sess->buff = 0;
			sess->offset = 0;
			sess->size = 0;
		}

		del_session (fd);
		return (resinfo);
	}

	image = (win_image_t *) malloc (sizeof (win_image_t));
	if (image == 0)
		return 0;

	memset (image, 0, sizeof (win_image_t));

	for (;;) {
		buff = (char *) realloc (buff, total - offset + 512);

		got =
		read (fd, buff + (total - offset), 512);

		if (got <= 0) break;

		total += got;

		/*
		 * looking for pe-offset from dos-header
		 */
		if (image->peoffset == 0 && image->doshdr == 0) {
			if (offset == 0) {
				char sz_magic[1024];
				int from;
				int to;

				from = 0;
				if (total <= from) {
					offset = total;
					continue;
				}

				to = from + (int) sizeof (win_doshdr_t); 
				if (total < to) continue;

				image->doshdr = (win_doshdr_t *) malloc (sizeof (win_doshdr_t));
				if (image->doshdr == 0) break;

				memcpy (image->doshdr,
				buff + from - offset,
				sizeof (win_doshdr_t));

				calc_peoffset (image);

				sz_magic[0] = '\0';
				get_magic (buff + from - offset, sz_magic, 1024);

				if (strlen (sz_magic) != 0) {
					/*
					 * if this is not a least an MS DOS header,
					 * it makes no sense to look for any resources
					 */
					if (memcmp (sz_magic, "MS DOS", 6) != 0) {
						if (image != 0) {
							if (image->doshdr != 0)
								free (image->doshdr);

							if (image->filehdr != 0)
								free (image->filehdr);

							if (image->opthdr32 != 0)
								free (image->opthdr32);

							if (image->opthdr64 != 0)
								free (image->opthdr64);

							if (image->sechdr != 0)
								free (image->sechdr);

							if (image->datadir != 0)
								free (image->datadir);

							free (image);
						}

						if (buff != 0)
							free (buff);

						return 0;
					}
				}
			}
		}

		/*
		 * looking for pe-image-fileheader
		 */
		if (image->filehdr == 0) {
			int from;
			int to;
			char sz_magic[1024];

			from = image->peoffset;
			if (total <= from) {
				offset = total;
				continue;
			}

			to =
			image->peoffset + (int) sizeof (win_imagefilehdr_t); 

			if (total < to) continue;

			image->filehdr = (win_imagefilehdr_t *) malloc (sizeof (win_imagefilehdr_t));
			if (image->filehdr == 0) break;

			memcpy (image->filehdr,
			buff + from - offset,
			sizeof (win_imagefilehdr_t));

			sz_magic[0] = '\0';
			get_magic (buff + from - offset, sz_magic, 1024);
		}

		/*
		 * looking for optional-header
		 */
		if (image->opthdr32 == 0 && image->opthdr64 == 0) {
			int from;
			int to;

			from =
			image->peoffset + (int) sizeof (win_imagefilehdr_t);

			if (total <= from) {
				offset = total;
				continue;
			}

			if (image->filehdr->SizeOfOptionalHeaders == 224) {
				to = image->peoffset +
				(int) sizeof (win_imagefilehdr_t) +
				(int) sizeof (win_optionalhdr32_t);
				if (total < to) continue;

				image->opthdr32 =
				(win_optionalhdr32_t *) malloc (sizeof (win_optionalhdr32_t));

				if (image->opthdr32 == 0) break;

				memcpy (image->opthdr32,
				buff + from - offset,
				sizeof (win_optionalhdr32_t));
			} else {
				to = image->peoffset +
				(int) sizeof (win_imagefilehdr_t) +
				(int) sizeof (win_optionalhdr64_t);
				if (total < to) continue;

				image->opthdr64 =
				(win_optionalhdr64_t *) malloc (sizeof (win_optionalhdr64_t));

				if (image->opthdr64 == 0) break;

				memcpy (image->opthdr64,
				buff + from - offset,
				sizeof (win_optionalhdr64_t));
			}
		}

		/*
		 * looking for datadirectory
		 */
		if (image->datadir == 0) {
			int from;
			int to;
			int num_of_rva;

			if (image->opthdr32 != 0) {
				num_of_rva =
				image->opthdr32->NumberOfRvaAndSizes;

				from = image->peoffset +
				(int) sizeof (win_imagefilehdr_t) +
				sizeof (win_optionalhdr32_t);
			} else {
				num_of_rva =
				image->opthdr64->NumberOfRvaAndSizes;

				from = image->peoffset +
				(int) sizeof (win_imagefilehdr_t) +
				sizeof (win_optionalhdr64_t);
			}

			if (total <= from) {
				offset = total;
				continue;
			}

			to = from +
			num_of_rva * (int) sizeof (win_datadirectory_t);

			if (total < to) continue;

			image->datadir =
			(win_datadirectory_t *) malloc (num_of_rva * sizeof (win_datadirectory_t));

			if (image->datadir == 0) break;

			memcpy (image->datadir,
			buff + from - offset,
			num_of_rva  * sizeof (win_datadirectory_t));
		}

		/*
		 * looking for section headers
		 */
		if (image->sechdr == 0) {
			int from;
			int to;
			int entries;
			int num_of_rva;

			entries = image->filehdr->NumberOfSections;
			if (image->opthdr32 != 0) {
				num_of_rva =
				image->opthdr32->NumberOfRvaAndSizes;

				from = image->peoffset +
				(int) sizeof (win_imagefilehdr_t) +
				sizeof (win_optionalhdr32_t) +
				num_of_rva * (int) sizeof (win_datadirectory_t);
			} else {
				num_of_rva =
				image->opthdr64->NumberOfRvaAndSizes;

				from = image->peoffset +
				(int) sizeof (win_imagefilehdr_t) +
				sizeof (win_optionalhdr64_t) +
				num_of_rva * (int) sizeof (win_datadirectory_t);
			}

			if (total <= from) {
				offset = total;
				continue;
			}

			to = from +
			entries * (int) sizeof (win_sectionhdr_t);

			if (total < to) continue;

			image->sechdr =
			(win_sectionhdr_t *) malloc (entries * sizeof (win_sectionhdr_t));

			if (image->sechdr == 0) break;

			memcpy (image->sechdr,
			buff + from - offset,
			entries  * sizeof (win_sectionhdr_t));

			calc_resoffset (image);
		}

		/*
		 * go to resource section
		 * and get versioninfo
		 */
		if (versioninfo == 0) {
			int from;
			int to;
			int ressize;

			from = image->res.offset;
			if (total <= from) {
				offset = total;
				continue;
			}

			if (image->res.size <= 0) {
				if (image != 0) {
					if (image->doshdr != 0)
						free (image->doshdr);

					if (image->filehdr != 0)
						free (image->filehdr);

					if (image->opthdr32 != 0)
						free (image->opthdr32);

					if (image->opthdr64 != 0)
						free (image->opthdr64);

					if (image->sechdr != 0)
						free (image->sechdr);

					if (image->datadir != 0)
						free (image->datadir);

					free (image);
				}

				if (buff != 0)
					free (buff);

				return 0;
			}

			to = image->res.offset + image->res.size;
			if (total < to) continue;

			ressize = 0;

			versioninfo = get_versionresource (
			buff + image->res.offset - offset,
			image->res.offset, 0, &ressize);

			sess->size = ressize;
			sess->buff = (char *) malloc (sess->size);
			sess->offset =
			virtual2real (image, versioninfo) -
			              (image->res.offset - offset);

			*p_size = sess->size;

			memcpy (sess->buff, buff + sess->offset, sess->size);

			free (buff);
			free (image->doshdr);
			free (image->filehdr);
			free (image->opthdr32);
			free (image->opthdr64);
			free (image->sechdr);
			free (image->datadir);
			free (image);

			return 0;
		}

		offset = total;
	}

	free (image->doshdr);
	free (image->filehdr);
	free (image->opthdr32);
	free (image->opthdr64);
	free (image->sechdr);
	free (image->datadir);
	free (image);

	free (buff);
	return 0;
}

static int
get_versionresource (char *resource,
                     int resoffset, int offset, int *p_ressize) {
	int i;
	int num_of_entries;

	win_resdirectory_t *resdir;
	win_resdirentry_t  *resentry;
	int infodata = 0;

	resdir = (win_resdirectory_t *) (resource + offset);

	num_of_entries =
	resdir->NumberOfIdEntries + resdir->NumberOfNamedEntries;

	resentry = (win_resdirentry_t *)
	(resource + offset + sizeof (win_resdirectory_t)); 

	for (i = 0; i < num_of_entries; i++) {
		if (offset == 0)
			if (!(resentry[i].Name & 0x80000000L))
				/* next if it is it a versioninfo */
				if ((resentry[i].Name & ~0x80000000L)  != 16)
					continue;

		if (resentry[i].OffsetToData & 0x80000000L) {
			infodata = get_versionresource (resource, resoffset,
			resentry[i].OffsetToData & ~0x80000000L, p_ressize);
		} else {
			win_resdataentry_t *dataentry;

			dataentry = (win_resdataentry_t *)
			            (resource + resentry[i].OffsetToData);

			dataentry->OffsetToData;

			*p_ressize = dataentry->Size;
			return (dataentry->OffsetToData);
		}
	}

	return (infodata);
}

void
calc_peoffset (win_image_t *image) {
	if (image->doshdr == 0)
		return;

	if (memcmp (&image->doshdr->e_magic, win_dosmagic,
	sizeof (win_dosmagic)) != 0)
		return;

	image->peoffset = image->doshdr->e_lfanew;
	return;
}

void
calc_resoffset (win_image_t *image) {
	int i;
	int va_image_dir;
	int image_dir = 2;
	int num_of_rva;

	if (image->filehdr->SizeOfOptionalHeaders == 224)
		num_of_rva = image->opthdr32->NumberOfRvaAndSizes;
	else
		num_of_rva = image->opthdr64->NumberOfRvaAndSizes;

	if (image_dir >= (int) (num_of_rva))
		return;

	va_image_dir = image->datadir[image_dir].VirtualAddress;

	for (i = 0; i < (int) (image->filehdr->NumberOfSections); i++) {
		int from;
		int to;

		from = (int) (image->sechdr[i].VirtualAddress);
		to = from + (int) (image->sechdr[i].SizeOfRawData);

		if (from <= va_image_dir && to > va_image_dir) break;
	}

    if (i > image->filehdr->NumberOfSections)
        return;

    image->res.offset =
	(va_image_dir - image->sechdr[i].VirtualAddress +
	image->sechdr[i].PointerToRawData);

	image->res.size = image->sechdr[i].SizeOfRawData;

	return;
}

static int
virtual2real (win_image_t *image, int va) {
	int i;
	
	for (i = 0; i < (int) (image->filehdr->NumberOfSections); i++) {
		int from;
		int to;

		from = (int) (image->sechdr[i].VirtualAddress);
		to = from + (int) (image->sechdr[i].SizeOfRawData);

		if (from <= va && to > va)
			return (va - from);
	}

	return 0;
}

static int
equals (char *ascii, char *ucs2) {
	int i;

	for (i = 0; i < (int) strlen (ascii); i++)
		 if (ascii[i] != ((uint16_t *)ucs2)[i])
			return -1;

	return 0;
}

static int
ascii_toupper (int c) {
	if (c >= 'a' && c <= 'z')
		return (c - ('a' - 'A'));

	return (c);
}

static int
get_string (char *raw, int rawsize, int offset,
char *desc, char **info, int *infosize) {
	win_string_t *s;
	char *key;
	char *val;

	if (offset >= rawsize)
		return 0;

	s = (win_string_t *) (raw + offset);

	key = ((char *) s) + sizeof (win_string_t);  

	if (strlen (desc) > 0 && equals (desc, key) == 0) {
		val = key + 2 * (strlen (desc) + 1);
		if ((ptr_t) val % 4 != 0)
			val += 4 - ((ptr_t) val % 4);

		*infosize = s->ValueSize;
		*info = val;
		return 0;
	}

	offset += s->Size;
	if (offset % 4 != 0)
		offset += 4 - offset % 4;

	return (get_string (raw, rawsize, offset, desc, info, infosize));
}

static int
get_stringtable (char *raw, int rawsize, int offset,
char *desc, char **info, int *infosize) {
	int string;
	int size;
	int rc;
	win_stringtable_t *st;

	if (offset >= rawsize)
		return 0;

	st = (win_stringtable_t *) (raw + offset);

	if (offset + st->Size > rawsize)
		return 0;

	string = offset + sizeof (win_stringtable_t);
	if (string % 4 != 0)
		string += 4 - string % 4;

	size = st->Size - (string - offset);

	if (strlen (desc) > 9 &&
	desc[0] == ascii_toupper (st->Key[0]) &&
	desc[1] == ascii_toupper (st->Key[1]) &&
	desc[2] == ascii_toupper (st->Key[2]) &&
	desc[3] == ascii_toupper (st->Key[3]) &&
	desc[4] == ascii_toupper (st->Key[4]) &&
	desc[5] == ascii_toupper (st->Key[5]) &&
	desc[6] == ascii_toupper (st->Key[6]) &&
	desc[7] == ascii_toupper (st->Key[7]) &&
	desc[8] == '\\') {
		desc += 9;
		rc = get_string (raw + string, size, 0, desc, info, infosize);
	}

	rc = get_stringtable
	(raw, rawsize, offset + st->Size, desc, info, infosize);

	return (rc);
}

static int
get_fileinfo (void *raw, int rawsize, int offset, char *desc,
char **info, int *infosize) {
	int rc;
	win_versioninfo_t *vi;
	win_varfileinfo_t *vfi;
	win_stringfileinfo_t *sfi;

	if (offset >= rawsize)
		return 0;

	/*
	 * VS_VERSIONINFO structure
	 */
	vi = (win_versioninfo_t *) ((char *) raw + offset);
	if (memcmp (vi->Magic, win_versioninfo_magic,
	sizeof (win_versioninfo_magic)) == 0) {
		if (offset != 0)
			return 0;

		if (vi->Size != rawsize)
			return 0;

		offset = sizeof (win_versioninfo_t);
		if (offset % 4 != 0)
			offset += 4 - offset % 4;

		offset += vi->ValueSize;
		if (offset % 4 != 0)
			offset += 4 - offset % 4;

		rc = get_fileinfo (raw, rawsize, offset, desc, info, infosize);
		return (rc);
	}

	/*
	 * VarFileInfo structure
	 */
	vfi = (win_varfileinfo_t *) ((char *) raw + offset);
	if (memcmp (vfi->Magic, win_varfileinfo_magic,
	sizeof (win_varfileinfo_magic)) == 0) {
		if (offset + vfi->Size > rawsize)
			return 0;

		offset += vfi->Size;

		rc = get_fileinfo (raw, rawsize, offset, desc, info, infosize);
		return (rc);
	}

	/*
	 * StringFileInfo structure 
	 */
	sfi = (win_stringfileinfo_t *) ((char *) raw + offset);
	if (memcmp (sfi->Magic, win_stringfileinfo_magic,
		sizeof (win_stringfileinfo_magic)) == 0) {
		char *inforoot = "\\StringFileInfo";
		int stringtable;
		int size;

		/*
		 * we are interested in StringFileInfo
		 */
		if (memcmp (desc, inforoot, strlen (inforoot)) == 0) {
			stringtable = offset + sizeof (win_stringfileinfo_t);
			if (stringtable % 4 != 0)
				stringtable += 4 - stringtable % 4;

			size = sfi->Size - (stringtable - offset);
			desc += strlen (inforoot) + 1;
			rc = get_stringtable
			((char *) raw + stringtable, size, 0, desc, info, infosize);

			if (*info != 0 && *infosize != 0)
				return (rc);
		}

		if (offset + sfi->Size > rawsize)
			return 0;

		offset += sfi->Size;

		rc = get_fileinfo (raw, rawsize, offset, desc, info, infosize);
		return (rc);
	}

	return 0;
}

static int
version_query_value (void *raw, char *desc, void **info, int *infosize) {
	int rc;
	uint16_t rawsize;

	*info = 0;
	*infosize = 0;

	rawsize = * (uint16_t *) raw;
	rc = get_fileinfo (raw, rawsize, 0, desc, (char **) info, infosize);

	return (rc);
}

static char *
convert_slashes (char *filename) {
	int i;

	for (i = 0; i <= (int) strlen (filename); i++)
		if (filename[i] == '/')
			filename[i] ='\\';

	return (filename);
}

static buildinfo_t *
get_buildinfo_using_res (int fd) {
	int i;
	void *raw;
	int raw_size;
	void *untrans;
	int untrans_size;
	char *ascii;

	int type;
	int major;
	int minor;
	int corr;
	int build;
	char *next;
	char *str;
	buildinfo_t *buildinfo;
	char buildnumber_key[] = "BUILDNUMBER"; 

	ascii = 0;
	buildinfo = 0;

	/*
	 * look for version information
	 */
	get_versioninfo (fd, 0, &raw_size);

	raw = malloc (raw_size);
	if (raw == 0)
		return (buildinfo);

	get_versioninfo (fd, raw, &raw_size);

	/*
	 * first try us-english ascii
	 */
	untrans = 0;
	version_query_value
	(raw, "\\StringFileInfo\\040904E4\\FileVersion",
	&untrans, &untrans_size);

	/*
	 * try us-english unicode
	 */
	if (untrans_size == 0) {
		version_query_value
		(raw, "\\StringFileInfo\\040904B0\\FileVersion",
		&untrans, &untrans_size);
	}

	/*
	 * or give up
	 */
	if (untrans_size == 0) {
		free (raw);
		return (buildinfo);
	}

	ascii = (char *) malloc (untrans_size);
	if (ascii == 0) {
		free (raw);
		return (buildinfo);
	}

	for (i = 0; i < untrans_size; i++)
		ascii[i] = (char)(((uint16_t *)untrans)[i]);

	type = 0;
	if (strchr (ascii, '.') == 0) {
		/*
		 * no dots, this is an apo version
		 * apo version looks like
		 */
		type = BUILD_TYPE_APO;

		version_query_value
		(raw, "\\StringFileInfo\\040904E4\\Build", &untrans, &untrans_size);

		if (untrans_size == 0) {
			version_query_value
			(raw, "\\StringFileInfo\\040904B0\\Build", &untrans, &untrans_size);
		}

		if (untrans_size == 0) {
			free (ascii);
			ascii = 0;
		} else {
			ascii = (char *) realloc (ascii, untrans_size);
			if (ascii == 0) {
				free (raw);
				free (ascii);
				return (buildinfo);
			}

			for (i = 0; i < untrans_size; i++)
				ascii[i] = (char)(((uint16_t *)untrans)[i]);
		}

		if (ascii == 0) {
				free (raw);
				return (buildinfo);
		}
		
		if (memcmp (ascii, "APO", 3) != 0) {
			/*
			 * apo old style,
			 * no buildnumber
			 * 3029 => APO 3.0A Build 29
			 * or
			 * 3103 => APO 3.10 Build 03
			 */
			major = ascii[0] - '0';
			minor = ascii[1] - '0';
			build = strtol (ascii + 2, &next, 10);

			/*
			 * '3.00 Build: xxx-000-000-000'
			 */
			str = (char *) malloc (33);
			if (str == 0) {
				free (raw);
				free (ascii);
				return (buildinfo);
			}

			sprintf (str, "APO %01d.%02d   Build %03d-000-000-000", major, minor, build);
			free (ascii);
			ascii = 0;

			buildinfo = buildinfo_add (buildinfo, buildnumber_key, str);
			free (str);
		} else {
			/*
			 * apo new style
			 * using buildnumber
			 */
			ascii = (char *) realloc (ascii, untrans_size);
			if (ascii == 0) {
				free (raw);
				free (ascii);
				return (buildinfo);
			}

			for (i = 0; i < untrans_size; i++)
				ascii[i] = (char)(((uint16_t *)untrans)[i]);

			major = ascii[4] - '0';
			minor = 10 * (ascii[6] - '0') + ascii[7] - '0';

			str = (char *) malloc (33);
			if (str == 0) {
				free (raw);
				free (ascii);
				return (buildinfo);
			}

			sprintf (str, "APO %01d.%02d   Build %15s", major, minor, ascii + 10);
			free (ascii);
			ascii = 0;

			buildinfo = buildinfo_add (buildinfo, buildnumber_key, str);
			free (str);
		}
	} else {
		/*
		 * sapdb version
		 */
		type = BUILD_TYPE_REL;
		major = strtol (ascii, &next, 10);
		minor = strtol (next + 1, &next, 10);
		corr = strtol (next + 1, &next, 10);

		version_query_value
		(raw, "\\StringFileInfo\\040904E4\\Build", &untrans, &untrans_size);

		if (untrans_size == 0) {
			version_query_value
			(raw, "\\StringFileInfo\\040904B0\\Build", &untrans, &untrans_size);
		}

		if (untrans_size == 0) {
			free (raw);
			free (ascii);
			return (buildinfo);
		}

		ascii = (char *) realloc (ascii, untrans_size);
		if (ascii == 0) {
			free (raw);
			free (ascii);
			return (buildinfo);
		}

		for (i = 0; i < untrans_size; i++)
			ascii[i] = (char)(((uint16_t *)untrans)[i]);

		/*
		 * '7.4.03 Build xxx-000-000-000'
		 */
		str = (char *) malloc (33);
			if (str == 0) {
			free (raw);
			free (ascii);
			return (buildinfo);
		}

		sprintf (str, "Rel %01d.%01d.%02d Build %15s", major, minor, corr, ascii);
		free (ascii);
		ascii = 0;

		buildinfo = buildinfo_add (buildinfo, buildnumber_key, str);
		free (str);
	}

	if (type == BUILD_TYPE_REL) {
		version_query_value
		(raw, "\\StringFileInfo\\040904E4\\Date", &untrans, &untrans_size);

		if (untrans_size == 0) {
			free (raw);
			free (ascii);
			return (buildinfo);
		}

		ascii = (char *) realloc (ascii, untrans_size);
		if (ascii == 0) {
			free (raw);
			free (ascii);
			return (buildinfo);
		}

		for (i = 0; i < untrans_size; i++)
			ascii[i] = (char)(((uint16_t *)untrans)[i]);

		buildinfo = buildinfo_add (buildinfo, "DATE", ascii);
		free (ascii);
		ascii = 0;
	}

	if (type == BUILD_TYPE_APO) {
		/* ChangeList */
		version_query_value
		(raw, "\\StringFileInfo\\040904E4\\ChangeList", &untrans, &untrans_size);

		if (untrans_size == 0) {
			free (raw);
			free (ascii);
			return (buildinfo);
		}

		ascii = (char *) realloc (ascii, untrans_size);
		if (ascii == 0) {
			free (raw);
			free (ascii);
			return (buildinfo);
		}

		for (i = 0; i < untrans_size; i++)
			ascii[i] = (char)(((uint16_t *)untrans)[i]);

		buildinfo = buildinfo_add (buildinfo, "CHANGELIST", ascii);
		free (ascii);
		ascii = 0;

		/* PatchLevel */
		version_query_value
		(raw, "\\StringFileInfo\\040904E4\\PatchLevel", &untrans, &untrans_size);

		if (untrans_size == 0) {
			free (raw);
			free (ascii);
			return (buildinfo);
		}

		ascii = (char *) realloc (ascii, untrans_size);
		if (ascii == 0) {
			free (raw);
			free (ascii);
			return (buildinfo);
		}

		for (i = 0; i < untrans_size; i++)
			ascii[i] = (char)(((uint16_t *)untrans)[i]);

		buildinfo = buildinfo_add (buildinfo, "PATCHLEVEL", ascii);
		free (ascii);
		ascii = 0;

	}

	free (raw);
	return (buildinfo);
}
#endif

/*
 * what part
 */
typedef struct {
	int type;
	int major;
	int minor;
	int correction;
	int build;
} build_t;

static buildinfo_t *check_vars (buildinfo_t *, char *);
static buildinfo_t *check_sysinfo (buildinfo_t *, char *);
static int  check_buildstr (char *, char *);
static char *get_what (char *, char *, char *, int);
static int  split_build (char *, build_t *);
static int  gt_build (build_t *, build_t *);
static void remove_colon (char *);
static void remove_dot (char *);

#define BUFFER_SIZE         (512 * 1024)
#define LINE_SIZE            (64 * 1024)

static buildinfo_t *
get_buildinfo_using_what (int fd) {
	int got;
	int got_buildstr;
	int done;
	char *end;
	char *at;
	static char buff[BUFFER_SIZE + 3];
	char line[LINE_SIZE];
	char *buildstr, *apo_buildstr;
	buildinfo_t *buildinfo;
	char buildnumber_key[] = "BUILDNUMBER"; 

	buildinfo = 0;

	buildstr = (char *) malloc (LINE_SIZE);
	if (buildstr == 0)
		return 0;

	memset (buildstr, 0, LINE_SIZE);
	buff[0] = buff[1] = buff[2] = '\0';
	for (;;) {
		got = read (fd, buff + 3, BUFFER_SIZE);
		if (got <= 0)
			break;

		got_buildstr = 0;
		done = 0;
		at = buff;  
		end = buff + got;
		for (;;) {
			at = (char *) memchr (at, '@', end - at);
			if (at == 0)
				break;

			if (at[1] == '(' && at[2] == '#' && at[3] == ')') {
				at = get_what (at + 4, end + 3, line, fd);
				if (at == 0) {
					done = 1;
					break;
				}

				check_buildstr (line, buildstr);
				buildinfo = check_sysinfo (buildinfo, line);
				buildinfo = check_vars (buildinfo, line);
			}

			at++;
			if (at >= end)
				break;
		}

		if (done != 0)
			break;

		buff[0] = end[0]; 
		buff[1] = end[1];
		buff[2] = end[2];
	}

	if (strlen (buildstr) == 0)
		return 0;

	remove_colon (buildstr);
	remove_dot (buildstr);

	if (memcmp (buildstr, "APO", 3) != 0) {
		buildinfo = buildinfo_add (buildinfo, buildnumber_key, buildstr);
		free (buildstr);
		return (buildinfo);
	}

	apo_buildstr = (char *) malloc (35);
	free (buildstr);

	if (apo_buildstr == 0)
		return (buildinfo);

	memset (apo_buildstr, '\0', 35);
	memcpy (apo_buildstr, buildstr, 8);
	strcat (apo_buildstr, "   Build ");
	memcpy (apo_buildstr + strlen (apo_buildstr), buildstr + 15, 15);

	buildinfo = buildinfo_add (buildinfo, buildnumber_key, apo_buildstr);
	free (apo_buildstr);
	return (buildinfo);
}

static char *
get_what (char *begin, char *end, char *line, int fd) {
	char *ptr = line;

	while (begin < end) {
		*ptr = *begin;

		if (*begin == '\0')
			return (begin);

		ptr++;
		begin++;
	}
	
	for (;;) {
		if (read (fd, ptr, 1) <= 0) {
			*ptr = '\0';
			return 0;
		}

		if (*ptr == '\0')
			return (end);

		ptr++;
	}

	return 0;
}

static buildinfo_t *
check_vars (buildinfo_t *buildinfo, char *line) {
	char *pkey, *pval;

	pkey = pval = 0;
	if (pkey = strstr (line, "Architecture")) {
		char keyname[] = "ARCHITECTURE";

		pval = strchr (pkey, ':');
		if (pval == 0)
			return (buildinfo);

		pval += 2;
		buildinfo = buildinfo_add (buildinfo, keyname, pval);
		return (buildinfo);
	}

	if (pkey = strstr (line, "Operating System")) {
		char keyname[] = "OS-VERSION";

		pval = strchr (pkey, ':');
		if (pval == 0)
			return (buildinfo);

		pval += 2;
		buildinfo = buildinfo_add (buildinfo, keyname, pval);
		return (buildinfo);
	}

	if (pkey = strstr (line, "C Compiler")) {
		char keyname[] = "C-COMPILER";

		pval = strchr (pkey, ':');
		if (pval == 0)
			return (buildinfo);

		pval += 2;
		buildinfo = buildinfo_add (buildinfo, keyname, pval);
		return (buildinfo);
	}

	if (pkey = strstr (line, "C++ Compiler")) {
		char keyname[] = "C++-COMPILER";

		pval = strchr (pkey, ':');
		if (pval == 0)
			return (buildinfo);

		pval += 2;
		buildinfo = buildinfo_add (buildinfo, keyname, pval);
		return (buildinfo);
	}

	if (pkey = strstr (line, "changelist")) {
		char keyname[] = "CHANGELIST";

		pval = strchr (pkey, ':');
		if (pval == 0)
			return (buildinfo);

		pval += 2;
		buildinfo = buildinfo_add (buildinfo, keyname, pval);
		return (buildinfo);
	}

	if (pkey = strstr (line, "makedate")) {
		char keyname[] = "MAKEDATE";

		pval = strchr (pkey, ':');
		if (pval == 0)
			return (buildinfo);

		pval += 2;
		buildinfo = buildinfo_add (buildinfo, keyname, pval);
		return (buildinfo);
	}

	if (pkey = strstr (line, "makeuser")) {
		char keyname[] = "MAKEUSER";

		pval = strchr (pkey, ':');
		if (pval == 0)
			return (buildinfo);

		pval += 2;
		buildinfo = buildinfo_add (buildinfo, keyname, pval);
		return (buildinfo);
	}

	if (pkey = strstr (line, "makeid")) {
		char keyname[] = "MAKEID";

		pval = strchr (pkey, ':');
		if (pval == 0)
			return (buildinfo);

		pval += 2;
		buildinfo = buildinfo_add (buildinfo, keyname, pval);
		return (buildinfo);
	}

	if (pkey = strstr (line, "makestate")) {
		char keyname[] = "MAKESTATE";

		pval = strchr (pkey, ':');
		if (pval == 0)
			return (buildinfo);

		pval += 2;
		buildinfo = buildinfo_add (buildinfo, keyname, pval);
		return (buildinfo);
	}

	if (pkey = strstr (line, "makepath")) {
		char keyname[] = "MAKEPATH";

		pval = strchr (pkey, ':');
		if (pval == 0)
			return (buildinfo);

		pval += 2;
		buildinfo = buildinfo_add (buildinfo, keyname, pval);
		return (buildinfo);
	}

	if (pkey = strstr (line, "toolchangelist")) {
		char keyname[] = "TOOLCHANGELIST";

		pval = strchr (pkey, ':');
		if (pval == 0)
			return (buildinfo);

		pval += 2;
		buildinfo = buildinfo_add (buildinfo, keyname, pval);
		return (buildinfo);
	}

	if (pkey = strstr (line, "purpose")) {
		char keyname[] = "PURPOSE";

		pval = strchr (pkey, ':');
		if (pval == 0)
			return (buildinfo);

		pval += 2;
		buildinfo = buildinfo_add (buildinfo, keyname, pval);
		return (buildinfo);
	}

	return (buildinfo);
}

static buildinfo_t *
check_sysinfo (buildinfo_t *buildinfo, char *line) {
	char sz_sysinfo[] = "sysinfo.";
	char *pkey, *key;
	char *pval, *val;
	int len;

	pkey = strstr (line, sz_sysinfo);
	if (pkey == 0)
		return (buildinfo);
	
	pkey += strlen (sz_sysinfo);

	pval = strchr (pkey, ':');
	if (pval == 0)
		return (buildinfo);

	len = (pval - pkey);
	if (len <= 0)
		return (buildinfo);

	pval += 2;

	val = (char *) malloc (strlen (pval) + 1);
	if (val == 0)
		return (buildinfo);

	strcpy (val, pval);

	key = (char *) malloc (len + 1);
	if (key == 0)
		return (buildinfo);

	memset (key, 0, len + 1);
	memcpy (key, pkey, len);

#if 0
	if (strcmp (key, "architecture") == 0) {
		char keyname[] = "ARCHITECTURE";
		buildinfo = buildinfo_add (buildinfo, keyname, val);
	}

	if (strcmp (key, "system") == 0) {
		char keyname[] = "OSNAME";
		buildinfo = buildinfo_add (buildinfo, keyname, val);
	}

	if (strcmp (key, "version") == 0) {
		char keyname[] = "OSVERSION";
		buildinfo = buildinfo_add (buildinfo, keyname, val);
	}
#endif

	if (strcmp (key, "subversion") == 0) {
		char keyname[] = "OS-SUBVERSION";
		buildinfo = buildinfo_add (buildinfo, keyname, val);
	}

	if (strcmp (key, "c_runtime") == 0) {
		char keyname[] = "C-RUNTIME";
		buildinfo = buildinfo_add (buildinfo, keyname, val);
	}

	if (strcmp (key, "cpp_runtime") == 0) {
		char keyname[] = "C++-RUNTIME";
		buildinfo = buildinfo_add (buildinfo, keyname, val);
	}

	if (strcmp (key, "cpp_runtime64") == 0) {
		char keyname[] = "C++-RUNTIME-64";
		buildinfo = buildinfo_add (buildinfo, keyname, val);
	}

	free (key);
	free (val);

	return (buildinfo);
}

static int
check_buildstr (char *line, char *buildstr) {
	build_t my[1], their[1];

	if (split_build (line, my) != 0)
		return 0;

	/*
	 * it is the first buildstring we found
	 */
	if (strlen (buildstr) == 0) {
		strcpy (buildstr, line);
		return 1;
	}

	if (split_build (buildstr, their) != 0)
		return 0;

	if (gt_build (my, their) != 0)
		return 0;

	/*
	 * my buildsting this greater than theirs
	 */

	strcpy (buildstr, line);
	return 1;
}

static int
split_build (char *line, build_t *my) {
	char sz_apo[]   = "APO ";
	char sz_rel[]   = "Rel. ";
	char sz_build[] = "Build: ";
	char *rel;
	char *build;
	char *next;
	char *tmp;

	rel = strstr (line, sz_rel);
	my->type = BUILD_TYPE_REL;

	if (rel == 0) {
		rel = strstr (line, sz_apo);
		my->type = BUILD_TYPE_APO;
	}

	if (rel == 0)
		return -1;

	build = strstr (line, sz_build);
	if (build == 0)
		return -1;

	switch (my->type) {
	case BUILD_TYPE_REL:
		tmp = (char *) alloca (strlen (build) + 1);
		strcpy (tmp, build);

		my->major = strtol (rel + strlen (sz_rel), &next, 10);
		my->minor = strtol (next + 1, &next, 10);
		my->correction = strtol (next + 1, &next, 10);
		my->build = strtol (build + strlen (sz_build), &next, 10);

		sprintf (line, "Rel. %1d.%1d.%02d %s",
		my->major, my->minor, my->correction, tmp);
		break;
	case BUILD_TYPE_APO:
		my->major = strtol (rel + strlen (sz_apo), &next, 10);
		my->minor = next[1] - '0';
		my->correction = next[2];
		if (my->correction >= 'A' && my->correction <= 'Z') {
			my->correction -= 'A';
		} else if (my->correction >= '0' && my->correction <= '9') {
			my->correction -= '0';
		} else if (my->correction == ' ' || my->correction == '\t') {
			my->correction = 0;
		}
		my->build = strtol (build + strlen (sz_build), &next, 10);
		break;
	default:
		return -1;
	}

	return 0;
}

static int
gt_build (build_t *my, build_t *their) {

	if (my->type < their->type) {
		return -1;
	}

	if ((my->type == their->type) && (my->major < their->major)) {
		return -1;
	}

	if ((my->type == their->type) && (my->major == their->major) &&
	    (my->minor < their->minor)) {
		return -1;
	}

	if ((my->type == their->type) && (my->major == their->major) &&
	(my->minor == their->minor) && (my->correction < their->correction)) {
		return -1;
	}

	if ((my->major == their->major) && (my->minor == their->minor) &&
	(my->correction == their->correction) && (my->build < their->build)) {
		return -1;
	}

	return 0;
}

static void
remove_colon (char *line) {
	char sz_build[] = "Build: ";
	char *ptr;

	ptr = strstr (line, sz_build);
	if (ptr == 0)
		return;

	strcpy (ptr + 5, ptr + 6);
	return;
}

static void
remove_dot (char *line) {
	char sz_rel[] = "Rel. ";
	char *ptr;

	ptr = strstr (line, sz_rel);
	if (ptr == 0)
		return;

	strcpy (ptr + 3, ptr + 4);
	return;
}

/*
 * common part
 */
static buildinfo_t *
buildinfo_add (buildinfo_t *buildinfo, char *key, char *val) {
	buildinfo_t *next, *curr;

	next = (buildinfo_t *) malloc (sizeof (buildinfo_t));
	if (next == 0) {
		buildinfo_free (buildinfo);
		return 0;
	}

	memset (next, 0, sizeof (buildinfo_t));

	next->key = (char *) malloc (strlen (key) + 1);
	if (next->key == 0) {
		buildinfo_free (buildinfo);
		free (next);
		return 0;
	}
		
	next->val = (char *) malloc (strlen (val) + 1);
	if (next->val == 0) {
		buildinfo_free (buildinfo);
		free (next->key);
		free (next);
		return 0;
	}

	strcpy (next->key, key);
	strcpy (next->val, val);

	if (buildinfo == 0)
		return (next);

	for (curr = buildinfo; curr->next != 0; curr = curr->next) {
		if (strcmp (curr->key, key) == 0) {
			/* update existing key */
			free (curr->val);
			curr->val = next->val;
			free (next->key);
			free (next);

			return (buildinfo);
		}
	}

	curr->next = next;
	return (buildinfo);
}

static buildinfo_t *
buildinfo_del (buildinfo_t *buildinfo, buildinfo_t *curr) {
	buildinfo_t *last;

	if (buildinfo == 0)
		return 0;

	if (curr == 0)
		return (buildinfo);
	
	for (last = buildinfo; last->next != 0 && last->next != curr; last = last->next);

	if (curr == buildinfo)
		buildinfo = curr->next;
	else
		last->next = curr->next;

	if (curr->key)
		free (curr->key);

	if (curr->val)
		free (curr->val);

	free (curr);
	return (buildinfo);
}

static void
buildinfo_free (buildinfo_t *buildinfo) {
	buildinfo_t *curr, *last;

	curr = buildinfo;
	for (;;) {
		if (curr == 0)
			break;

		if (curr->key != 0)
				free (curr->key);

		if (curr->val != 0)
				free (curr->val);

		last = curr;
		curr = curr->next;

		free (last);
	}

	return;
}

static buildinfo_t *
buildinfo_merge (buildinfo_t *to, buildinfo_t *from) {
	buildinfo_t *curr;

	if (to == 0 && from == 0)
		return 0;

	if (to != 0 && from == 0)
		return (to);

	if (to == 0 && from != 0)
		return (from);

	for (curr = from; curr != 0; curr = curr->next) {
		to = buildinfo_add (to, curr->key, curr->val);
	}

	buildinfo_free (from);
	return (to);
}

static buildinfo_t *
get_buildinfo (int fd) {
#ifdef WIN32
	buildinfo_t *buildinfo;

	if (lseek (fd, 0, SEEK_SET) != 0)
		return 0;

	buildinfo = get_buildinfo_using_res (fd);

	if (lseek (fd, 0, SEEK_SET) != 0)
		return (buildinfo);

	return (buildinfo_merge (buildinfo, get_buildinfo_using_what (fd)));
#endif
#ifdef UNIX
	return (get_buildinfo_using_what (fd));
#endif
}

#ifdef WIN32
static	char exeext[] = ".exe";
static char dllext[] = ".dll";
#endif
#if defined UNIX && defined __hpux && !defined HP_IA64
static char exeext[] = "";
static char dllext[] = ".sl";
#endif
#if defined UNIX && defined __hpux && defined HP_IA64
static char exeext[] = "";
static char dllext[] = ".so";
#endif
#if defined UNIX && !defined __hpux
static char exeext[] = "";
static char dllext[] = ".so";
#endif

static int
isa_pathname (const char *filename) {
	if (filename == 0)
		return 0;

#ifdef WIN32
	if (strlen (filename) < 2)
		return 0;

	if (filename[0] == '\\' && filename[1] == '\\')
		return 1;

	if (strlen (filename )< 4)
		return 0;

	if (
	filename[0] >= 'A' && filename[0] <= 'Z' &&
	filename [1] == ':' && filename[2] == '\\')
		return 1;

	if (
	filename[0] >= 'a' && filename[0] <= 'z' &&
	filename [1] == ':' && filename[2] == '\\')
		return 1;
#endif
#ifdef UNIX
	if (strlen (filename) < 1)
		return 0;

	if (filename[0] =='/')
		return 1;
#endif

	return 0;
}

static char *
get_filename (const char *logical) {
	tsp01_RteError err[1];
	char *buff;

	buff = (char *) malloc (PATH_LEN_EO01);
	if (buff == 0)
		return 0;

	if (isa_pathname (logical) != 0) {
		SAPDB_strncpy (buff, logical, PATH_LEN_EO01-1);
        buff[PATH_LEN_EO01-1]='\0';
		return (buff);
	}
	
	if (strcmp (logical, "KERNEL") == 0) {
		sqlGetDbrootPgmPath ((tsp00_Pathc &) *buff, 1, err);
		strcat (buff, "kernel");
		strcat (buff, exeext);
		return (buff);
	}

	if (strcmp (logical, "LIBOMS") == 0) {
#if defined WIN32
		sqlGetDbrootPgmPath ((tsp00_Pathc &) *buff, 1, err);
#else
		sqlGetDbrootLibPath ((tsp00_Pathc &) *buff, 1, err);
#endif
		strcat (buff, "liboms");
		strcat (buff, dllext);
		return (buff);
	}

	if (strcmp (logical, "LIBDBPINSTALL") == 0) {
#if defined WIN32
		sqlGetDbrootPgmPath ((tsp00_Pathc &) *buff, 1, err);
#else
		sqlGetDbrootLibPath ((tsp00_Pathc &) *buff, 1, err);
#endif
		strcat (buff, "dbpinstall");
		strcat (buff, dllext);
		return (buff);
	}

#ifdef WIN32
	if (memcmp (logical, "LIB", 3) == 0) {
		HANDLE hdir;
		WIN32_FIND_DATA	dirent[1];
		char *normalized;

		sqlGetDbrootSapPath ((tsp00_Pathc &) *buff, 1, err);
		strcat (buff, "*");
		strcat (buff, dllext);

		hdir = FindFirstFile (buff, dirent);
		if (hdir == INVALID_HANDLE_VALUE) {
			free (buff);
			return 0;
		}

		normalized = 0;
		for (;;) {
			char *ptr;

			normalized = (char *) realloc (normalized, strlen (dirent->cFileName) + 1);
			if (normalized == 0) {
				free (buff);
				FindClose (hdir);
				return 0;
			}

			strcpy (normalized, dirent->cFileName);
			ptr = strstr (normalized, dllext);
			if (ptr != 0)
				*ptr = '\0';

			strupr (normalized);
			if ((strcmp (normalized, logical) == 0) ||
			(strcmp (normalized, logical + 3) == 0)) {
				sqlGetDbrootSapPath ((tsp00_Pathc &) *buff, 1, err);
				strcat (buff, dirent->cFileName);
				free (normalized);
				FindClose (hdir);
				return (buff);
			}
			
			if (FindNextFile (hdir, dirent) == 0)
				break;
		}

		free (normalized);
		FindClose (hdir);
	}
#endif
#ifdef UNIX
	if (memcmp (logical, "LIB", 3) == 0) {
		int i;
		DIR *dp;
		struct dirent *dirent;
		char *normalized;
		char *ptr;

		sqlGetDbrootSapPath ((tsp00_Pathc &) *buff, 0, err);
		dp = opendir (buff);
		if (dp == 0) {
			free (buff);
			return 0;
		}

		normalized = 0;
		for (;;) {
			dirent = readdir (dp);
			if (dirent == 0) {
				free (normalized);
				free (buff);
				closedir (dp);
				return 0;
			}

			normalized =
			(char *) realloc (normalized, strlen (dirent->d_name) + 1);
			if (normalized == 0) {
				free (normalized);
				free (buff);
				closedir (dp);
				return 0;
			}

			strcpy (normalized, dirent->d_name);
			ptr = strstr (normalized, dllext);
			if (ptr != 0)
				*ptr = '\0';

			for (i = 0; i < strlen (normalized); i++)
				normalized[i] = toupper (normalized[i]);

			if ((strcmp (normalized, logical) == 0) ||
			(strcmp (normalized, logical + 3) == 0)) {
				sqlGetDbrootSapPath ((tsp00_Pathc &) *buff, 1, err);
				strcat (buff, dirent->d_name);
				free (normalized);
				closedir (dp);
				return (buff);
			}
		}

		free (normalized);
		closedir (dp);
	}
#endif

	free (buff);
	return 0;
}

static char *
read_sapdir (void *pdir, int *pidx) {
#ifdef WIN32
	HANDLE dir;
	WIN32_FIND_DATA	dirent[1];
	tsp01_RteError err[1];
	char path[sizeof(tsp00_Pathc)];
	char *out;
	char *ptr;

	if (pdir == 0)
		return 0;

	if (*(void **) pdir == 0) {
		*pidx = 0;

		sqlGetDbrootSapPath ((tsp00_Pathc &) path, 1, err);
		strcat (path, "*");
		strcat (path, dllext);

		dir =  FindFirstFile (path, dirent);
		if (dir == INVALID_HANDLE_VALUE)
			return 0;

		*(void **) pdir = (void *) dir;
	} else {
		dir = *(HANDLE*)pdir;
		if (FindNextFile (dir, dirent) == 0) {
			FindClose (dir);
			return 0;
		}

		*pidx++;
	}

	out = (char *) malloc (strlen (dirent->cFileName) + 4);
	if (out == 0) {
		FindClose (dir);
		return 0;
	}
	
	strcpy (out, dirent->cFileName);
	ptr = strstr (out, dllext);
	if (ptr != 0)
		*ptr = '\0';

	strupr (out);
	if (memcmp (out, "LIB", 3) == 0)
		return (out);

	strcpy (out, "LIB");
	strcat (out, dirent->cFileName);
	ptr = strstr (out, dllext);
	if (ptr != 0)
		*ptr = '\0';

	strupr (out);
	return (out);
#endif
#ifdef UNIX
	tsp01_RteError err[1];
	tsp00_Pathc path;
	DIR *dir;
	struct dirent *dirent;
	int i;
	char *out;
	char *ptr;

	if (pdir == 0)
		return 0;

	if (*(void **) pdir == 0) {
		*pidx = 0;

		sqlGetDbrootSapPath (path, 0, err);
		dir = opendir ((char *) path);
		if (dir == 0)
			return 0;

		*(void **) pdir = (void *) dir;
		for (;;) {
			char *ptr;
			dirent = readdir (dir);
			if (dirent == 0) {
				closedir (dir);
				return 0;
			}

			ptr = strstr (dirent->d_name, dllext);
			if (ptr != 0 && strcmp (ptr, dllext) == 0) 
				break;
		}
	} else {
		dir = *(DIR **)pdir;
		for (;;) {
			char *ptr;
			dirent = readdir (dir);
			if (dirent == 0) {
				closedir (dir);
				return 0;
			}

			ptr = strstr (dirent->d_name, dllext);
			if (ptr != 0 && strcmp (ptr, dllext) == 0) 
				break;
		}

		*pidx++;
	}

	out = (char *) malloc (strlen (dirent->d_name) + 4);
	if (out == 0) {
		closedir (dir);
		return 0;
	}

	strcpy (out, dirent->d_name);
	ptr = strstr (out, dllext);
	if (ptr != 0)
		*ptr = '\0';

	for (i = 0; i < strlen (out); i++)
		out[i] = toupper (out[i]);

	if (memcmp (out, "LIB", 3) == 0)
		return (out);

	strcpy (out, "LIB");
	strcat (out, dirent->d_name);
	ptr = strstr (out, dllext);
	if (ptr != 0)
		*ptr = '\0';

	for (i = 0; i < strlen (out); i++)
		out[i] = toupper (out[i]);

	return (out);
#endif
}

/*
 * top level function
 */
bool
cn46BuildInfo (const char *logical, char *buff, int size) {
	char *filename;
	int fd;
	buildinfo_t *buildinfo, *curr;

	char fmt[] = "%-15s = %-61s\n";

	if (buff == 0 || size == 0)
		return 0;

	buff[0] = '\0';

	filename = get_filename (logical);
	if (filename == 0)
		return 0;

	fd = open (filename, O_RDONLY | O_BINARY);
	free (filename);

	if (fd < 0) {
		snprintf (buff, size, (char *) "cannot open %s", logical);
		return 0;
	}

	buildinfo = get_buildinfo (fd);
	close (fd);

	if (buildinfo == 0) {
		snprintf (buff, size, (char *) "cannot find info in %s", logical);
		return 0;
	}

	for (curr = buildinfo; curr != 0; curr = curr->next) {
		if (strcmp (curr->key, "BUILDNUMBER") == 0) {
			snprintf (buff + strlen (buff), size - strlen (buff), fmt,
			curr->key, curr->val);
			break;
		}
	}

	buildinfo_free (buildinfo);
	return 1;
}

bool
cn46BuildInfoFull (const char *logical, char *buff, int size) {
	int fd;
	char *filename;
	buildinfo_t *buildinfo, *curr;

	char fmt[] = "%-15s = %-61s\n";

	if (buff == 0 || size == 0)
		return 0;

	buff[0] = '\0';

	filename = get_filename (logical);
	if (filename == 0)
		return 0;

	fd = open (filename, O_RDONLY | O_BINARY);

	if (fd < 0) {
		snprintf (buff, size, (char *) "cannot open %s", logical);
		free (filename);
		return 0;
	}

	buildinfo = get_buildinfo (fd);
	close (fd);

	if (buildinfo == 0) {
		snprintf (buff, size, (char *) "cannot find info in %s", logical);
		free (filename);
		return 0;
	}

	snprintf (buff + strlen (buff), size - strlen (buff), fmt,
			"FILENAME", filename);

	for (curr = buildinfo; curr != 0; curr = curr->next) {
		if (strcmp (curr->key, "BUILDNUMBER") == 0) {
			snprintf (buff + strlen (buff), size - strlen (buff), fmt,
			curr->key, curr->val);
			buildinfo = buildinfo_del (buildinfo, curr);
		}
	}

	for (curr = buildinfo; curr != 0; curr = curr->next) {
		if (strcmp (curr->key, "OS-VERSION") == 0) {
			snprintf (buff + strlen (buff), size - strlen (buff), fmt,
			curr->key, curr->val);
			buildinfo = buildinfo_del (buildinfo, curr);
		}
	}

	for (curr = buildinfo; curr != 0; curr = curr->next) {
		if (strcmp (curr->key, "ARCHITECTURE") == 0) {
			snprintf (buff + strlen (buff), size - strlen (buff), fmt,
			curr->key, curr->val);
			buildinfo = buildinfo_del (buildinfo, curr);
		}
	}

	for (curr = buildinfo; curr != 0; curr = curr->next) {
		if (strcmp (curr->key, "C-COMPILER") == 0) {
			snprintf (buff + strlen (buff), size - strlen (buff), fmt,
			curr->key, curr->val);
			buildinfo = buildinfo_del (buildinfo, curr);
		}
	}

	for (curr = buildinfo; curr != 0; curr = curr->next) {
		if (strcmp (curr->key, "C++-COMPILER") == 0) {
			snprintf (buff + strlen (buff), size - strlen (buff), fmt,
			curr->key, curr->val);
			buildinfo = buildinfo_del (buildinfo, curr);
		}
	}

	for (curr = buildinfo; curr != 0; curr = curr->next) {
		if (strcmp (curr->key, "C-RUNTIME") == 0) {
			snprintf (buff + strlen (buff), size - strlen (buff), fmt,
			curr->key, curr->val);
			buildinfo = buildinfo_del (buildinfo, curr);
		}
	}

	for (curr = buildinfo; curr != 0; curr = curr->next) {
		if (strcmp (curr->key, "C++-RUNTIME") == 0) {
			snprintf (buff + strlen (buff), size - strlen (buff), fmt,
			curr->key, curr->val);
			buildinfo = buildinfo_del (buildinfo, curr);
		}
	}

	for (curr = buildinfo; curr != 0; curr = curr->next) {
		if (strcmp (curr->key, "C++-RUNTIME-64") == 0) {
			snprintf (buff + strlen (buff), size - strlen (buff), fmt,
			curr->key, curr->val);
			buildinfo = buildinfo_del (buildinfo, curr);
		}
	}

	for (curr = buildinfo; curr != 0; curr = curr->next) {
		if (strcmp (curr->key, "MAKEPATH") == 0) {
			char *from, *to;

			from = curr->val;
			for (;;) {
				to = strchr (from, ',');
				if (to != 0)
					*to = '\0';
	
				snprintf (buff + strlen (buff), size - strlen (buff), fmt,
				curr->key, from);

				if (to == 0)
					break;

				from = to + 1;
			}

			buildinfo = buildinfo_del (buildinfo, curr);
		}
	}

	for (curr = buildinfo; curr != 0; curr = curr->next) {
		if (curr->key != 0) {
			snprintf (buff + strlen (buff), size - strlen (buff), fmt,
			curr->key, curr->val);
		}
	}

	buildinfo_free (buildinfo);
	free (filename);
	return 1;
}

bool
cn46BuildInfoList (char *buff, int size) {
	char *logical;
	char *filename;
	void *dp;
	int idx;
	int fd;
	buildinfo_t *buildinfo, *curr;

	char fmt[] = "%-31s = %-45s\n";

	char szkernel[]        = "KERNEL";
	char szliboms[]        = "LIBOMS";
	char szlibdbpinstall[] = "LIBDBPINSTALL";

	if (buff == 0 || size == 0)
		return 0;

	buff[0] = '\0';

	logical = 0;
	for (;;) {
		if (logical == 0) {
			logical = (char *) malloc (strlen (szkernel) + 1);
			strcpy (logical, szkernel);
		} else if (strcmp (logical, szkernel) == 0) {
			logical = (char *) realloc (logical, strlen (szliboms) + 1);
			strcpy (logical, szliboms);
		} else if (strcmp (logical, szliboms) == 0) {
			logical = (char *) realloc (logical, strlen (szlibdbpinstall) + 1);
			strcpy (logical, szlibdbpinstall);
		} else {
			if (strcmp (logical, szlibdbpinstall) == 0)
				dp = 0; idx = 0;

			free (logical);
			logical = read_sapdir (&dp, &idx);
			if (logical == 0)
				return 1;
		}

		filename = get_filename (logical);
		if (filename == 0)
			continue;

		fd = open (filename, O_RDONLY | O_BINARY);
		free (filename);

		if (fd < 0)
			continue;
	
		buildinfo = get_buildinfo (fd);
		close (fd);

		if (buildinfo == 0)
			continue;

		for (curr = buildinfo; curr != 0; curr = curr->next) {
			if (strcmp (curr->key, "BUILDNUMBER") == 0) {
				snprintf (buff + strlen (buff), size - strlen (buff), fmt,
				logical, curr->val);
				break;
			}
		}

		buildinfo_free (buildinfo);
	}

	free (logical);
	return 1;
}
