/*---------------------------------------------------------------------------*/
/*
	File: manifestpp.c
*/
/*---------------------------------------------------------------------------*/

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

#define NAMESPACE
#define NAMESPACE_BEGIN
#define NAMESPACE_END
#define USING_NAMESPACE

#include "common/build/release.h"

#define _FILE "PLISTPP.C"


/*---------------------------------------------------------------------------*/

typedef char BOOL;
#ifndef TRUE
#define TRUE  (0==0)
#endif
#ifndef FALSE
#define FALSE (0==1)
#endif

#ifdef _O_RDWR

#define _lstat      _stat

#define _O_SYNC     0
#ifndef _S_IEXEC
#define _S_IEXEC    0
#endif

#else

#define _open       open
#define _read       read
#define _lseek      lseek
#define _write      write
#define _close      close
#define _chmod      chmod
#define _remove     remove
#define _rename     rename
#define _mkdir      mkdir
#define _rmdir      rmdir
#define _stat       stat
#define _lstat      lstat
#define _fstat      fstat
#define _opendir    opendir
#define _closedir   closedir
#define _readdir    readdir
#define _readlink   readlink
#define _symlink    symlink
#define _unlink     unlink

#define _O_APPEND   O_APPEND
#define _O_BINARY   0
#define _O_CREAT    O_CREAT
#define _O_EXCL     O_EXCL
#define _O_RDONLY   O_RDONLY
#define _O_RDWR     O_RDWR
#define _O_SYNC     O_SYNC
#define _O_TRUNC    O_TRUNC
#define _O_WRONLY   O_WRONLY
#define _S_IREAD    (S_IRUSR|S_IRGRP|S_IROTH)
#define _S_IWRITE   (S_IWUSR|S_IWGRP|S_IWOTH)
#define _S_IEXEC    (S_IXUSR|S_IXGRP|S_IXOTH)

#endif
/*----------------------------------------------------------------------------*/

#define FILE_BUFFER_SIZE 512
unsigned char FileBuffer[FILE_BUFFER_SIZE];


#define OCCURRENCE_TABLE_ENTRIES 2048

BOOL ReplaceDictionaryValue (int fdInput, int fdOutput,
	const unsigned char *Template, unsigned int TemplateSize,
	const unsigned char *Replacement, unsigned int ReplacementSize,
	unsigned long *pTotalOccurrences)
/*
	Returns TRUE on success.
	The string find algorithm according to Knuth, Morris, Pratt (see algorithm
	of program 1.2 in Niklaus Wirth "Algorithmen und Datenstrukturen", 4. Aufl.
	S.66 ff) is applied.
*/
{
	unsigned int *MismatchIndexTable;
	unsigned long SearchPos, ReadPos, WritePos, EndPos, FileSize;
	int Occ, Occurrences, BytesRead, BytesWritten, n;
	unsigned int i, j, TagCount;
	unsigned long ReplaceStart;

	*pTotalOccurrences = 0;

	MismatchIndexTable = (unsigned int *)(malloc (TemplateSize * sizeof(unsigned int)));
	if (MismatchIndexTable == NULL)
	{
		fprintf (stderr, "(%s, %d) can't allocate mismatch index table\n", _FILE, __LINE__);
		exit (1);
	}
	MismatchIndexTable[0] = ((unsigned int) -1);
	j = 0;
	for (i = 1; i < TemplateSize; i++)
	{
		MismatchIndexTable[i] = (Template[i] == Template[j]) ? MismatchIndexTable[j] : j;
		while ((j != ((unsigned int) -1)) && (Template[i] != Template[j]))
			j = MismatchIndexTable[j];
		j++;
	}

	FileSize = _lseek (fdInput, 0, SEEK_END);
	if (FileSize == -1L)
	{
		free (MismatchIndexTable);
		fprintf (stderr, "(%s, %d) can't get file size (%d)\n", _FILE, __LINE__, errno);
		return (FALSE);
	}
	TagCount = 0;
    ReplaceStart = 0;
	SearchPos = 0;
	do
	{
		_lseek (fdInput, SearchPos, SEEK_SET);
		i = 0;
		do
		{
			BytesRead = _read (fdInput, FileBuffer, FILE_BUFFER_SIZE);
			if (BytesRead > 0)
			{
				j = 0;
                if (i < TemplateSize)
                {
				    do
				    {
					    while ((j < ((unsigned int) BytesRead)) && (i < TemplateSize) && (FileBuffer[j] == Template[i]))
					    {
						    i++;
						    j++;
					    }
					    if ((j < ((unsigned int) BytesRead)) && (i < TemplateSize))
					    {
						    i = MismatchIndexTable[i];
						    if (i == ((unsigned int) -1))
						    {
							    i = 0;
							    j++;
						    }
					    }
				    } while ((j < ((unsigned int) BytesRead)) && (i < TemplateSize));
                }
                if (i == TemplateSize)
                {
					while ((j < ((unsigned int) BytesRead)) && ((TagCount < 2) || (FileBuffer[j] != '<')))
					{
						if (FileBuffer[j] == '>')
						{
							ReplaceStart = SearchPos + j + 1;
							TagCount++;
						}
                        j++;
					}
                }
				SearchPos += j;
			}
		} while ((BytesRead > 0) && ((i < TemplateSize) || (j == ((unsigned int) BytesRead))));
		if (BytesRead < 0)
		{
			free (MismatchIndexTable);
			fprintf (stderr, "(%s, %d) can't read input (%d)\n", _FILE, __LINE__, errno);
			return (FALSE);
		}
		if ((BytesRead == 0) && (SearchPos != FileSize))
		{
			free (MismatchIndexTable);
			fprintf (stderr, "(%s, %d) FileEndPos (%ld) != FileSize (%ld)\n", _FILE, __LINE__, SearchPos, FileSize);
			return (FALSE);
		}
        if (i == TemplateSize)
        {
            Occurrences = 1;
    		(*pTotalOccurrences)++;
			if (ReplacementSize <= SearchPos - ReplaceStart)
			{
				ReadPos = 0;
				WritePos = 0;
				Occ = 0;
				do
				{
					EndPos = (Occ < Occurrences) ? ReplaceStart : FileSize;
					while (ReadPos < EndPos)
					{
						if (_lseek (fdInput, ReadPos, SEEK_SET) == -1L)
						{
							free (MismatchIndexTable);
							fprintf (stderr, "(%s, %d) can't seek input (%d)\n", _FILE, __LINE__, errno);
							return (FALSE);
						}
						n = (EndPos - ReadPos < FILE_BUFFER_SIZE) ? (int)(EndPos - ReadPos) : FILE_BUFFER_SIZE;
						BytesRead = _read (fdInput, FileBuffer, n);
						if (BytesRead <= 0)
						{
							free (MismatchIndexTable);
							fprintf (stderr, "(%s, %d) can't read input (%d)\n", _FILE, __LINE__, errno);
							return (FALSE);
						}
						if (_lseek (fdOutput, WritePos, SEEK_SET) == -1L)
						{
							free (MismatchIndexTable);
							fprintf (stderr, "(%s, %d) can't seek output (%d)\n", _FILE, __LINE__, errno);
							return (FALSE);
						}
						BytesWritten = _write (fdOutput, FileBuffer, BytesRead);
						if (BytesWritten != BytesRead)
						{
							free (MismatchIndexTable);
							fprintf (stderr, "(%s, %d) can't write output (%d)\n", _FILE, __LINE__, errno);
							return (FALSE);
						}
						ReadPos += BytesRead;
						WritePos += BytesWritten;
					}
					if (Occ < Occurrences)
					{
						if (_lseek (fdOutput, WritePos, SEEK_SET) == -1L)
						{
							free (MismatchIndexTable);
							fprintf (stderr, "(%s, %d) can't seek output (%d)\n", _FILE, __LINE__, errno);
							return (FALSE);
						}
						if (ReplacementSize != 0)
						{
							BytesWritten = _write (fdOutput, Replacement, ReplacementSize);
							if (((unsigned int) BytesWritten) != ReplacementSize)
							{
								free (MismatchIndexTable);
								fprintf (stderr, "(%s, %d) can't write output (%d)\n", _FILE, __LINE__, errno);
								return (FALSE);
							}
						}
						ReadPos += SearchPos - ReplaceStart;
						WritePos += ReplacementSize;
					}
					Occ++;
				} while (Occ <= Occurrences);
                if (_lseek (fdInput, 0L, SEEK_SET) == -1L)
				{
					free (MismatchIndexTable);
					fprintf (stderr, "(%s, %d) can't seek input (%d)\n", _FILE, __LINE__, errno);
					return (FALSE);
				}
				if (_chsize (fdOutput, WritePos) == -1)
				{
					free (MismatchIndexTable);
					fprintf (stderr, "(%s, %d) can't change size of output (%d)\n", _FILE, __LINE__, errno);
					return (FALSE);
				}
			}
			else
			{
				ReadPos = FileSize;
				WritePos = FileSize + (ReplacementSize - (SearchPos - ReplaceStart));
				if (_chsize (fdOutput, WritePos) == -1)
				{
					free (MismatchIndexTable);
					fprintf (stderr, "(%s, %d) can't change size of output (%d)\n", _FILE, __LINE__, errno);
					return (FALSE);
				}
				Occ = Occurrences + 1;
				do
				{
					Occ--;
					if (Occ < Occurrences)
					{
						ReadPos -= SearchPos - ReplaceStart;
						WritePos -= ReplacementSize;
						if (_lseek (fdOutput, WritePos, SEEK_SET) == -1L)
						{
							free (MismatchIndexTable);
							fprintf (stderr, "(%s, %d) can't seek output (%d)\n", _FILE, __LINE__, errno);
							return (FALSE);
						}
						if (ReplacementSize != 0)
						{
							BytesWritten = _write (fdOutput, Replacement, ReplacementSize);
							if (((unsigned int) BytesWritten) != ReplacementSize)
							{
								free (MismatchIndexTable);
								fprintf (stderr, "(%s, %d) can't write output (%d)\n", _FILE, __LINE__, errno);
								return (FALSE);
							}
						}
					}
					EndPos = (Occ != 0) ? SearchPos : 0;
					while (ReadPos > EndPos)
					{
						n = (ReadPos - EndPos < FILE_BUFFER_SIZE) ? (int)(ReadPos - EndPos) : FILE_BUFFER_SIZE;
						ReadPos -= n;
						WritePos -= n;
						if (_lseek (fdInput, ReadPos, SEEK_SET) == -1L)
						{
							free (MismatchIndexTable);
							fprintf (stderr, "(%s, %d) can't seek input (%d)\n", _FILE, __LINE__, errno);
							return (FALSE);
						}
						BytesRead = _read (fdInput, FileBuffer, n);
						if (BytesRead != n)
						{
							free (MismatchIndexTable);
							fprintf (stderr, "(%s, %d) can't read input (%d)\n", _FILE, __LINE__, errno);
							return (FALSE);
						}
						if (_lseek (fdOutput, WritePos, SEEK_SET) == -1L)
						{
							free (MismatchIndexTable);
							fprintf (stderr, "(%s, %d) can't seek output (%d)\n", _FILE, __LINE__, errno);
							return (FALSE);
						}
						BytesWritten = _write (fdOutput, FileBuffer, BytesRead);
						if (BytesWritten != BytesRead)
						{
							free (MismatchIndexTable);
							fprintf (stderr, "(%s, %d) can't write output (%d)\n", _FILE, __LINE__, errno);
							return (FALSE);
						}
					}
				} while (Occ > 0);
			}
		}
		FileSize = FileSize + (ReplacementSize - (SearchPos - ReplaceStart));
		SearchPos = SearchPos + (ReplacementSize - (SearchPos - ReplaceStart));
	} while (SearchPos < FileSize);
	free (MismatchIndexTable);
	return (TRUE);
}


/*---------------------------------------------------------------------------*/

static char UsageMsg[] =
	"plistpp replaces dictionary values of key <Key> with <Value>.\n"
	"The known limitation is that the file size must not be greater than 2GB.\n"
	"\n"
	"Usage: plistpp [options] <File> <Key> <Value>\n"
	"options:\n"
	"  -x      Ignore if Key not found\n"
	"  -e<num> Expected number of matches\n"
	"  -b      Binary, expects hex strings xx xx xx\n";


void main (int argc, char *(argv[]))
{
	int fd, arg;
	unsigned long Occurrences;
	unsigned long ExpectedOccurrences;
	BOOL IgnoreTemplateNotFound, BinaryMode;
	unsigned char *BinaryTemplate, *BinaryReplacement;
	unsigned int TemplateSize, ReplacementSize;
	char ReplacementBuffer[1024] = VERSION_STR "." BUILD_STRING;
	char *p;

	ExpectedOccurrences = 0;
	IgnoreTemplateNotFound = FALSE;
	BinaryMode = FALSE;
	arg = 1;
	while ((arg < argc) && ((argv[arg][0] == '-') || (argv[arg][0] == '/')))
	{
		switch (argv[arg][1])
		{
		case 'x':
		case 'X':
			IgnoreTemplateNotFound = TRUE;
			break;

		case 'e':
		case 'E':
			ExpectedOccurrences = atol (&(argv[arg][2]));
			break;

		case 'b':
		case 'B':
			BinaryMode = TRUE;
			break;

		default:
			fprintf (stderr, "Unknown option '%s'\n%s", argv[arg], UsageMsg);
			exit (1);
		}
		arg++;
	}
/*
    if (argc - arg < 3)
	{
		fprintf (stderr, "Missing arguments\n%s", UsageMsg);
		exit (1);
	}
	if (argv[arg+1][0] == '\0')
	{
		fprintf (stderr, "Template must not be empty\n");
		exit (1);
	}
*/
#pragma warning(disable : 4996)
	fd = _open (argv[arg], _O_RDWR | _O_BINARY);
	if (fd == -1)
	{
		fprintf (stderr, "Cannot open file '%s' (%d)\n", argv[arg], errno);
		exit (1);
	}
    if (argc - arg < 3)
    {
		for (ReplacementSize = 0; ReplacementBuffer[ReplacementSize] != '\0'; ReplacementSize++) {
			if (!isdigit(ReplacementBuffer[ReplacementSize])) {
				ReplacementBuffer[ReplacementSize] = '.';
			}
		}
		if (!ReplaceDictionaryValue (fd, fd,
			"CFBundleVersion", strlen ("CFBundleVersion"),
            ReplacementBuffer, ReplacementSize,
			&Occurrences))
		{
			_close (fd);
			exit (1);
		}
	    if ((Occurrences == 0) && !IgnoreTemplateNotFound)
	    {
		    fprintf (stderr, "No occurrence of '%s'\n", argv[arg+1]);
		    _close (fd);
		    exit (1);
	    }
	    if ((Occurrences != ExpectedOccurrences) && (ExpectedOccurrences != 0))
	    {
		    fprintf (stderr, "Not expected number of occurrences (%ld)\n", ExpectedOccurrences);
		    _close (fd);
		    exit (1);
	    }
    	fprintf (stderr, "%ld occurrences replaced\n", Occurrences);
		if (!ReplaceDictionaryValue (fd, fd,
			"CFBundleShortVersionString", strlen ("CFBundleShortVersionString"),
            ReplacementBuffer, ReplacementSize,
			&Occurrences))
		{
			_close (fd);
			exit (1);
		}
    }
	else if (BinaryMode)
	{
		BinaryTemplate = (unsigned char *)(malloc ((strlen (argv[arg+1]) + 1) >> 1));
		if (BinaryTemplate == NULL)
		{
			fprintf (stderr, "(%s, %d) can't allocate binary template\n", _FILE, __LINE__);
			_close (fd);
			exit (1);
		}
		BinaryReplacement = (unsigned char *)(malloc ((strlen (argv[arg+2]) + 1) >> 1));
		if (BinaryReplacement == NULL)
		{
			fprintf (stderr, "(%s, %d) can't allocate binary replacement\n", _FILE, __LINE__);
			free (BinaryTemplate);
			_close (fd);
			exit (1);
		}
		TemplateSize = 0;
		p = argv[arg+1];
		do
		{
			while ((*p != '\0') && !isxdigit (*p))
				p++;
			if (*p != '\0')
			{
				BinaryTemplate[TemplateSize] =
					(isdigit (*p) ? *p - '0' : tolower (*p) - 'a' + 10) << 4;
				p++;
				if (!isxdigit (*p))
				{
					fprintf (stderr, "Odd number of digits in template\n");
					free (BinaryReplacement);
					free (BinaryTemplate);
					_close (fd);
					exit (1);
				}
				BinaryTemplate[TemplateSize] |=
					isdigit (*p) ? *p - '0' : tolower (*p) - 'a' + 10;
				p++;
				TemplateSize++;
			}
		} while (*p != '\0');
		if (TemplateSize == 0)
		{
			fprintf (stderr, "Template must not be empty\n");
			free (BinaryReplacement);
			free (BinaryTemplate);
			_close (fd);
			exit (1);
		}
		ReplacementSize = 0;
		p = argv[arg+2];
		do
		{
			while ((*p != '\0') && !isxdigit (*p))
				p++;
			if (*p != '\0')
			{
				BinaryReplacement[ReplacementSize] =
					(isdigit (*p) ? *p - '0' : tolower (*p) - 'a' + 10) << 4;
				p++;
				if (!isxdigit (*p))
				{
					fprintf (stderr, "Odd number of digits in replacement\n");
					free (BinaryReplacement);
					free (BinaryTemplate);
					_close (fd);
					exit (1);
				}
				BinaryReplacement[ReplacementSize] |=
					isdigit (*p) ? *p - '0' : tolower (*p) - 'a' + 10;
				p++;
				ReplacementSize++;
			}
		} while (*p != '\0');
		if (!ReplaceDictionaryValue (fd, fd,
			BinaryTemplate, TemplateSize, BinaryReplacement, ReplacementSize,
			&Occurrences))
		{
			free (BinaryReplacement);
			free (BinaryTemplate);
			_close (fd);
			exit (1);
		}
		free (BinaryReplacement);
		free (BinaryTemplate);
	}
	else
	{
		if (!ReplaceDictionaryValue (fd, fd,
			argv[arg+1], strlen (argv[arg+1]), argv[arg+2], strlen (argv[arg+2]),
			&Occurrences))
		{
			_close (fd);
			exit (1);
		}
	}
	if ((Occurrences == 0) && !IgnoreTemplateNotFound)
	{
		fprintf (stderr, "No occurrence of '%s'\n", argv[arg+1]);
		_close (fd);
		exit (1);
	}
	if ((Occurrences != ExpectedOccurrences) && (ExpectedOccurrences != 0))
	{
		fprintf (stderr, "Not expected number of occurrences (%ld)\n", ExpectedOccurrences);
		_close (fd);
		exit (1);
	}
	fprintf (stderr, "%ld occurrences replaced\n", Occurrences);
	_close (fd);
}


/*---------------------------------------------------------------------------*/
