/*
	© Mathias Kende, 2007
	mathias@kende.fr
	
	Ce programme doit etre lance par un utilisateur ayant les droits d'ecriture sur son fichier.
	Et le programme doit etre lance avec un chemin absolu ou relatif au dossier de travail, mais
	ne doit pas se trouver dans le PATH.
	
	C'est un programme qui se modifie lui meme et vous affichera bonjour la premiere fois qu'il
	sera lance et au revoir la deuxieme fois.
*/

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

char marque[5] = "axF5";
char data[1008] = "axF5axF5Bonjour a tous, comment aller vous ?";
char ndata[1000] = "Au revoir tout le monde et a bientot !";

off_t lsize(int fd)
{
	off_t cur = lseek(fd,0,SEEK_CUR);
	if (cur == (off_t)-1)
		return (off_t)-1;
	
	off_t end = lseek(fd,0,SEEK_END);
	
	if(lseek(fd,cur,SEEK_SET) == (off_t)-1)
		return (off_t)-1;

	return end;
}

int strnfind(const char* meule_de_foin, int meule_size, const char* aiguille, int aiguille_size, int compte)
{
	int i,j=0;
	for (i = 0; i < meule_size; i++)
	{
		if (meule_de_foin[i] == aiguille[j % aiguille_size])
		{
			j++;
			if (j == aiguille_size*compte)
				return (i+1);
		}
		else
		{
			i -= j;
			j = 0;
		}
	}
	return -1;
}

int main(int argc, char** argv, char** env)
{
	printf("Demarrage du programme.\nListe des arguments du programme :\n");
	
	int i;
	for (i=0; i<argc; i++)
		printf("%i : %s\n",i,argv[i]);
	
	int fd = open(argv[0],O_RDONLY);
	if (fd == -1)
	{
		fprintf(stderr,"Impossible d'ouvrir le fichier du programme, erreur %i :\n\t%s\n",errno,strerror(errno));
		return errno;
	}
	
	off_t size = lsize(fd);
	if (size == (off_t)-1)
	{
		fprintf(stderr,"Impossible de lire la taille du fichier du programme, erreur %i :\n\t%s\n",errno,strerror(errno));
		return errno;		
	}
	
	printf("Le fichier du programme fait %d octets.\n",size);
	
	char* buf = (char*)malloc(size);
	if (buf == NULL)
	{
		fprintf(stderr,"Impossible d'allouer la memoire pour charger le fichier du programme.\n");
		return 1;				
	}
	
	int done = 0;
	while (done < size)
	{
		ssize_t t = read(fd,buf+done,(size_t)(size-done));
		if (t == -1 && errno != EINTR)
		{
			fprintf(stderr,"Lors de la lecture du fichier du programme, erreur %i :\n\t%s\n",errno,strerror(errno));
			return errno;		
		}
		else if (t == 0)
		{
			fprintf(stderr,"Erreur lors de la lecture du fichier du programme.\n");
			return 1;		
		}
		else
			done += t;
	}
	
	printf("Le programme a ete charge correctement en memoire.\n");
	
	struct stat st;
	if (fstat(fd,&st) == -1)
	{
		fprintf(stderr,"Impossible de lire les stats du fichier du programme, erreur %i :\n\t%s\n",errno,strerror(errno));
		return errno;		
	}
	
	if (close(fd) == -1)
	{
		fprintf(stderr,"Impossible de fermer le fichier du programme, erreur %i :\n\t%s\n",errno,strerror(errno));
		return errno;		
	}
	
	int off_data = strnfind(buf,size,marque,strlen(marque),2);
	if (off_data == -1)
	{
		fprintf(stderr,"Impossible de trouver les donnees dans le programme.\n");
		return 1;				
	}
	
	printf("Les donnees ont ete trouvees a l'offset : 0x%X.\n",off_data);
	printf("%s\n",buf + off_data);
	
	if (unlink(argv[0]) == -1)
	{
		fprintf(stderr,"Impossible de supprimer le fichier du programme, erreur %i :\n\t%s\n",errno,strerror(errno));
		return errno;		
	}
	
	fd = creat(argv[0],st.st_mode);
	if (fd == -1)
	{
		fprintf(stderr,"Impossible de recreer le fichier du programme, erreur %i :\n\t%s\n",errno,strerror(errno));
		return errno;		
	}
	
	strncpy(buf+off_data,ndata,1000);
	
	done = 0;
	while (done < size)
	{
		ssize_t t = write(fd,buf+done,(size_t)(size-done));
		if (t == -1 && errno != EINTR)
		{
			fprintf(stderr,"Lors de l'ecriture du fichier du programme, erreur %i :\n\t%s\n",errno,strerror(errno));
			return errno;		
		}
		else if (t == 0)
		{
			fprintf(stderr,"Erreur lors de l'ecriture du fichier du programme.\n");
			return 1;		
		}
		else
			done += t;
	}
			
	
	free(buf);
	
	printf("Programme termine avec succes.\n");
	
	return 0;
}


