/*
** 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> --- August 1998
 */

/*
 * MacrosSetup.cpp --- hook the macros files
 */

#include "stdafx.h"

#ifndef WIN32
#	include <ctype.h>
#endif /* !WIN32 */

#ifdef HAVE_STRING_H
#	include <string.h>
#endif

#ifdef HAVE_STRINGS_H
#	include <strings.h>
#endif

#ifdef HAVE_ERRNO_H
#	include <errno.h>
#endif

#include "MacrosSetup.h"
#include "AppConsole.h"
#include "FileTraversal.h"
#include "CvsArgs.h"
#include "CvsCommands.h"

#ifdef TARGET_OS_MAC
#	include "MacMisc.h"
#	include <errno.h>
#endif

#ifdef qMacCvsPP
#	include "MacCvsApp.h"
#	include "getline.h"
#endif /* qMacCvsPP */

#ifdef __CYGWIN32__
#	include "getline.h"
#endif

#include "getopt.h"

#ifdef WIN32
#	include "wincvs.h"
#	include "getline.h"

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

CMacroSet gMacrosAdmin;
CMacroSet gMacrosSel;

static void getLoc(CStr & path, const char *subFolder)
{
	path = "";
#ifdef WIN32
	CWincvsApp* pApp = (CWincvsApp *)AfxGetApp();
	pApp->GetAppPath(path);

	CString appPath = path;
	// first the macro folder may be ./macro
	CString test = appPath;
	test.TrimRight('\\');
	test += "\\";
	test += subFolder;

	struct stat sb;
	if(stat(test, &sb) != -1 && S_ISDIR(sb.st_mode))
	{
		path = test;
		return;
	}

	CStr uppath, file;
	while(SplitPath(appPath, uppath, file) && !uppath.empty())
	{
		test = uppath;
		test.TrimRight('\\');
		test += "\\";
		test += subFolder;

		if(stat(test, &sb) != -1 && S_ISDIR(sb.st_mode))
		{
			path = test;
			return;
		}
		appPath = uppath;
	}
	path = test;
#elif defined(qUnix)
	struct stat sb;
	path = PACKAGE_DATA_DIR;
	if(stat(path, &sb) == -1 || !S_ISDIR(sb.st_mode))
	{
		path = PACKAGE_SOURCE_DIR;
		CStr uppath, folder;
		SplitPath(path, uppath, folder);
		path = uppath;
		if(!path.endsWith(kPathDelimiter))
			path << kPathDelimiter;
		path << subFolder;
	}
#elif qMacCvsPP
	UStr appPath = CMacCvsApp::GetAppPath();
	if(!appPath.endsWith(kPathDelimiter))
		appPath << kPathDelimiter;
	UStr test = appPath;
	test << subFolder;

	struct stat sb;
	if(stat(test, &sb) != -1 && S_ISDIR(sb.st_mode))
	{
		path = test;
		return;
	}

	UStr uppath, file;
	while(SplitPath(appPath, uppath, file) && !uppath.empty())
	{
		test = uppath;
		if(!test.endsWith(kPathDelimiter))
			test << kPathDelimiter;
		test << subFolder;

		if(stat(test, &sb) != -1 && S_ISDIR(sb.st_mode))
		{
			path = test;
			return;
		}
		appPath = uppath;
	}
	path = test;
#else
	if(!path.empty() && !path.endsWith(kPathDelimiter))
	{
		path << kPathDelimiter;
	}
	path << subFolder;
#endif
}

void MacrosGetLoc(CStr & path)
{
	getLoc(path, MACROS_FOLDER);
}

void PythonLibGetLoc(CStr & path)
{
	getLoc(path, PYTHONLIB_FOLDER);
}

static void MacrosFlushAll(void)
{
	gMacrosAdmin.Flush();
	gMacrosSel.Flush();
}

class MacroTraversal : public TraversalReport
{
public:
	virtual kTraversal OnError(const char *err, int errcode)
	{
		cvs_err(err);
		cvs_err("\n");
		return kTraversalError;
	}

	virtual kTraversal OnFile(const char *fullpath,
		const char *fullname,
		const char *name,
		const struct stat & dir, const UFSSpec * macspec);
};

kTraversal MacroTraversal::OnFile(const char *fullpath,
	const char *fullname,
	const char *name,
	const struct stat & dir, const UFSSpec * macspec)
{
	if(stricmp(name + strlen(name) - 4, ".tcl"))
		return kContinueTraversal;

	FILE *fpin = fopen(fullname, "r");
	if(fpin == 0L)
	{
		cvs_err("Impossible to open '%s' (error %d)\n", fullname, errno);
		return kContinueTraversal;
	}

	char *oldline = 0L;
	int line_length;
	size_t line_chars_allocated = 0;
	CvsArgs args(false);
	args.add("dummy");

	if((line_length = getline (&oldline, &line_chars_allocated, fpin)) > 0 &&
		strnicmp(oldline, "#!CVSGUI1.0", strlen("#!CVSGUI1.0")) == 0)
	{
		char *line = oldline + strlen("#!CVSGUI1.0");

		while(*line)
		{
			int hasQuote = 0;
			char *temp;

			// Search for the non space char
			while(*line && isspace(*line))
				++line;

			// handle the quote
			if((*line) == '"')
			{
				hasQuote = 1;
				line++;
			}
			for(temp=line;*line;++line)
			{
				if((*line) == '"')
				{
					if(hasQuote)
					{
						hasQuote = 0; /* ok doc */
						break;
					}
				}
				if(isspace(*line) && !hasQuote)
					break;
			}
			if(hasQuote) /* mismatch quote */
				temp = line;

			if(temp != line)
			{
				CStr arg;
				arg.set(temp, line - temp);
				args.add(arg);
			}
		}

		static struct option long_options[] =
		{
			{"admin", no_argument, 0L, 1},
			{"selection", no_argument, 0L, 2},
			{"name", required_argument, 0L, 3},
			{0, 0, 0, 0}
		};
		bool hasAdmin = false;
		bool hasSel = false;
		CStr dispname;

		optind = 0;
		int option_index = 0, c;
		while ((c = getopt_long
			(args.Argc(), args.Argv(), "+", long_options, &option_index)) != EOF)
		{
			switch (c)
			{
			case 1:
				hasAdmin = true;
				break;
			case 2:
				hasSel = true;
				break;
			case 3:
				dispname = optarg;
				break;
			default:
				cvs_err("Warning : unknown option in the file '%s'\n", fullname);
				break;
			}
		}

		if(dispname.empty())
		{
			dispname = name;
		}

		if(hasAdmin)
		{
			gMacrosAdmin.Add(fullname, dispname);
		}
		else if(hasSel)
		{
			gMacrosSel.Add(fullname, dispname);
		}
		else
		{
			cvs_err("Warning : no option <admin|selection> in the file '%s'\n", fullname);
		}
	}

	free (oldline);

	fclose(fpin);

	return kContinueTraversal;
}

void MacrosReloadAll(void)
{
	MacrosFlushAll();

	// get the macro folder
	CStr path;
	MacrosGetLoc(path);

	// traverse the macro folder
	MacroTraversal traverse;
	FileTraverse(path, traverse);
}
