/*
 * linkmap.c
 * linkmapファイルの関数アドレスをアドレス順に並べる。
 */


#include<unistd.h>
#include<stdio.h>
#include<stdlib.h>
#include<fcntl.h>
#include<sys/stat.h>
#include<string.h>


typedef struct ADDRES{
	char *str;
	uint addres;
	struct ADDRES *next;
	struct ADDRES *prev;
}ADDRES;


enum{
	TOP=16		/* 行頭からアドレスまでの文字数 */
};


static int atoh(const char*,int);


/* 改行までの文字数+1を返す。 */
extern inline int strlen_lf(const char *str)
{
	int i;
	
	
	for(i=0;str[i]!='\n';++i);
	return i+1;
}


/*
 * 引数 : ファイル名
 */
int main(int argc,char *argv[])
{
	enum{
		NUM_ADD_POINT=100		/* アドレス行のオフセット記憶する配列数 */
	};
	
	char *buf;
	char **add_point;
	int fd;
	int add_num;
	int max_add_point;
	ADDRES *addbuf;
	struct stat stat;
	char *p;
	int i;
	ADDRES *max_add,top_add,*q;


	if(argc<2){printf("Number of parameters is too few!\n");exit(0);}

	/* ファイルを読み込む */
	if((fd=open(argv[1],O_RDONLY))==-1){printf("File %s can't open!\n",argv[1]);exit(0);}
	fstat(fd,&stat);
	if((buf=(char*)malloc(stat.st_size+1))==NULL){printf("Fail memory alloc!\n");exit(0);}
	if(read(fd,buf,stat.st_size)!=stat.st_size){printf("Fail file read!\n");exit(0);}
	buf[stat.st_size]='\0';
	close(fd);

	/*
	 * linkmapの中の関数アドレス行を数える。
	 * "0x"をアドレスキー文字列にする。
	 */
	if((add_point=(char**)malloc(sizeof(char**)*NUM_ADD_POINT))==NULL)
		{printf("Fail memory alloc!\n");exit(0);}
	add_num=0;
	max_add_point=NUM_ADD_POINT;
	for(p=buf;*p!='\0';)
	{
		/* キー文字列発見 */
		if((*(p+TOP)=='0')&&(*(p+TOP+1)=='x'))
		{
			/* 関数アドレス行のオフセットを記憶する */
			if(add_num>=max_add_point)
			{
				max_add_point+=NUM_ADD_POINT;
				if((add_point=(char**)realloc(add_point,sizeof(char**)*max_add_point))==NULL)
					{printf("Fail memory alloc!\n");exit(0);}
			}
			add_point[add_num]=p+TOP;
			add_num+=1;			
		}

		/* 次の改行に進める */
		for(;*p!='\0';++p)
			if(*p=='\n')
			{
				++p;
				break;
			}
	}

	/* 関数アドレス行を配列に読み込む。 */
	if((addbuf=(ADDRES*)malloc(sizeof(ADDRES)*add_num))==NULL){printf("Fail memory alloc!\n");exit(0);}
	for(i=0;i<add_num;++i)
	{
		addbuf[i].str=add_point[i];
		addbuf[i].addres=atoh(add_point[i],10);
	}
	
	/* 関数アドレス行をアドレス順にソートする。 */
	addbuf[0].next=NULL;
	addbuf[0].prev=&top_add;
	top_add.next=&addbuf[0];
	for(i=1,max_add=&addbuf[0];i<add_num;++i)
	{
		if(addbuf[i].addres>=max_add->addres)
		{
			max_add->next=&addbuf[i];
			addbuf[i].prev=max_add;
			addbuf[i].next=NULL;
			max_add=&addbuf[i];
		}
		else
		{
			for(q=max_add->prev;q!=&top_add;q=q->prev)
				if(addbuf[i].addres>=q->addres)break;
			addbuf[i].next=q->next;
			addbuf[i].prev=q;
			q->next->prev=&addbuf[i];
			q->next=&addbuf[i];
		}
	}
	
	/* 関数アドレス行をソート順にlinkmapファイルに上書きする。 */
	if((fd=open(argv[1],O_RDWR|O_TRUNC))==-1){printf("File %s can't open!\n",argv[1]);exit(0);}
	for(q=top_add.next;q!=NULL;q=q->next)
		write(fd,q->str,strlen_lf(q->str));

	exit(0);
}


/*
 * 16進数文字列を数に変換する
 * parameters : string,number of characters
 * return : numelic,error -1
 */
int atoh(const char *str,int num)
{
	int rest=0;
	int i;


	/* 最初の文字列は"0x"と見なして除く */
	if(num<3)return -1;
	i=2;

	/* 先頭の'0'は除く */
	for(;i<num;++i)
		if(str[i]!='0')break;

	/* 文字を数に変換する */
	for(;i<num;++i)
	{
		rest*=16;

		if(str[i]>='0'&&str[i]<='9')rest+=str[i]-'0';
		else if(str[i]>='a'&&str[i]<='f')rest+=str[i]-'a'+10;
		else if(str[i]>='A'&&str[i]<='F')rest+=str[i]-'A'+10;
		else return -1;
	}

	return rest;
}
