/*
** 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 1, 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.
*/

/*
 * Author : Alexandre Parenteau <aubonbeurre@hotmail.com> --- December 1997
 */

/*
 * CvsArgs.cpp --- class to handle cvs arguments
 */

#include "stdafx.h"

#include <stdlib.h>
#include <string.h>

#include "CvsPrefs.h"
#include "CvsArgs.h"
#include "AppConsole.h"
#include "Authen.h"

#ifdef WIN32
#	ifdef _DEBUG
#	define new DEBUG_NEW
#	undef THIS_FILE
	static char THIS_FILE[] = __FILE__;
#	endif
#endif /* WIN32 */

static char *mystrdup(const char *string)
{
	int size = strlen(string);
	char *result = (char *)malloc((size + 1) * sizeof(char));
	if(result == NULL)
	{
		cvs_err("Impossible to allocate %d bytes !\n", size + 1);
		exit(1);
	}
	strcpy(result, string);
	return result;
}

CvsArgs::CvsArgs(bool defargs)
{
	argv = NULL;
	argc = 0;
	
	reset(defargs);
}

CvsArgs::CvsArgs(char * const *fromargv, int fromargc)
{
	argv = NULL;
	argc = 0;
	
	reset(false);

	for(int i = 0; i < fromargc; i++)
	{
		add(fromargv[i]);
	}
}

CvsArgs::~CvsArgs()
{
	reset(false);
}

void CvsArgs::reset(bool defargs)
{
	if(argv != 0L)
	{
		for(int i = 0; i < argc; i++)
		{
			if(argv[i] != 0L)
				free(argv[i]);
		}
		free(argv);
	}
		
	argc = 0;
	argv = NULL;
	
	if(defargs)
	{
		int numargs = 1;
		
		if(gCvsPrefs.Z9Option())
			numargs++;
		if(gCvsPrefs.QuietOption())
			numargs++;
		if(gCvsPrefs.AlwaysUseCvsroot())
			numargs += 2;

		argv = (char **)malloc(numargs * sizeof(char *));
		argv[argc++] = mystrdup("cvs");
		if(gCvsPrefs.AlwaysUseCvsroot())
		{
			argv[argc++] = mystrdup("-d");
			CStr root;
			const char *ccvsroot = gCvsPrefs;
			ccvsroot = Authen::skiptoken(ccvsroot);
			root = gAuthen.token();
			root << ccvsroot;
			argv[argc++] = mystrdup(root);
		}
		if(gCvsPrefs.Z9Option())
		{
			CStr zlev("-z");
			zlev << gCvsPrefs.ZLevel();
			argv[argc++] = mystrdup(zlev);
		}
		if(gCvsPrefs.QuietOption())
			argv[argc++] = mystrdup("-q");
	}
}

// reduce a long string for printing, add "..." if the length
// is up the limit
#define MAX_PRINT_ARG 80
static char *reduceString(const char *str, CStaticAllocT<char> & buf)
{
	buf.AdjustSize(MAX_PRINT_ARG + 4); // "..." + '\0'
	size_t len = strlen(str);
	if(len < MAX_PRINT_ARG)
	{
		strcpy(buf, str);
	}
	else
	{
		memcpy((char *)buf, str, MAX_PRINT_ARG * sizeof(char));
		char *tmp = (char *)buf + MAX_PRINT_ARG;
		*tmp++ = '.';
		*tmp++ = '.';
		*tmp++ = '.';
		*tmp = '\0';
	}

	return buf;
}

// replace \n by "\n" so it's readable
static char *expandLF(const char *str, CStaticAllocT<char> & buf)
{
	int numLFs = 0;
	const char *runner = str;
	while((runner = strchr(runner, '\n')) != 0L)
	{
		numLFs++;
		runner++;
	}

	size_t len = strlen(str);
	buf.AdjustSize(len + numLFs + 1); // numLFs * "\n" + '\0'

	char *tmp = buf;
	char c;
	runner = str;
	while((c = *runner++) != '\0')
	{
		if(c == '\n')
		{
			*tmp++ = '\\';
			*tmp++ = 'n';
		}
		else
			*tmp++ = c;
	}
	*tmp++ = '\0';

	return buf;
}

static CStaticAllocT<char> buf;

void CvsArgs::print(const char *indirectory)
{
	static CStaticAllocT<char> buf;
	
	for(int i = 0; i < argc; i++)
	{
		CStr newarg;
		newarg = argv[i];
		bool hasLF = strchr(newarg, '\n') != 0L;
		size_t len = newarg.length();

		if(len > MAX_PRINT_ARG)
			newarg = reduceString(newarg, buf);

		if(hasLF)
			newarg = expandLF(newarg, buf);

		bool hasSpace = strchr(newarg, ' ') != 0L;
		if(hasSpace)
			cvs_out("\"");
		cvs_outstr(newarg, newarg.length());
		if(hasSpace)
			cvs_out("\"");
		cvs_out(" ");
	}
	if(indirectory != NULL)
		cvs_out("(in directory %s)", indirectory);
	cvs_out("\n");
}

void CvsArgs::add(const char *arg)
{
	if(argv == 0L)
		argv = (char **)malloc(2 * sizeof(char *));
	else
		argv = (char **)realloc(argv, (argc + 2) * sizeof(char *));
	argv[argc++] = arg != 0L ? mystrdup(arg) : 0L;
	argv[argc] = 0L;
}

void CvsArgs::addfile(const char *arg, const char * /*dir*/, const FSSpec * /*spec*/)
{
	add(arg);
}
