#include <ctype.h>
#include <stdio.h>
#include <string.h>
#ifndef NOMALLOC_H
#include <malloc.h>
#endif
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif

#include "fudgit.h"
#include "setshow.h"
#include "macro.h"
#include "head.h"

static struct {
	char	 *name;
	char	 *command;
	int		nargs;
} com[] = {
	{"gnu!plot", "\tsave vector $1 $2 $Tmp\n\tpmode plot '$Tmp'\n", 2},
/* add built-in macros here: e.g.,
 * "sgi!plot",
 *	"\tpmode read -\n\t_dumplot $1 $2\n\tpmode\n\tdone\n\tplot\n\tfmode\n", 2,
 */
	{0,	  0,		0}
};

static struct {
	char *name;
	char *alias;
} aliases[] = {
	{"rm",  "!rm -i\n"},
	{"cp", "!cp -i\n"},
	{"mv", "!mv -i\n"},
	{"da!te", "!date\n"},
	{0, 0}
};



extern int Ft_almost (register char *str1, register char *str2);
extern FILE *popen(const char *, const char *);

void Ft_initmacros(void)
{
	int i;
	Macro *m;

	for (i = 0;aliases[i].name; i++) {
		m = Ft_macinstall(aliases[i].name, aliases[i].alias, ANALIAS);
	}
	for (i = 0;com[i].name; i++) {
		m = Ft_macinstall(com[i].name, com[i].command, AMACRO);
		m->nargs = com[i].nargs;
	}
}

static Macro *maclist = 0;

Macro *Ft_mplisp(void)
{
	return(maclist);
}

Macro *Ft_maclook(char *m, int type)
{
	Macro *mp;

	if (!m)
		return(NULL);
	for (mp = maclist; mp != (Macro *)0; mp = mp->next) {
		if (Ft_almost(m, mp->name) && mp->type == type)
			return(mp);
	}
	return(NULL);
}

int Ft_save_macros(int type, char *name, char *mode)
{
	Macro *mp;
	FILE *fp;

	if ((fp = fopen(name, mode)) == NULL) {
		fprintf(stderr, "%s: Permission denied.\n", name);
		return(ERRR);
	}
	for (mp = maclist; mp != (Macro *)0; mp = mp->next) {
		if (mp->type != type)
			continue;
		fprintf(fp, "%c\n", Ft_Comchar);
		switch (mp->type) {
			case AMACRO:
			fprintf(fp, "define %s %s %d\n", "macro", mp->name, mp->nargs);
			fprintf(fp, "%sstop\n", mp->line);
			break;
			default:
			fprintf(stderr, "Impossible macro type in save_macro().\n");
			return(ERRR);
		}
	}
	fclose(fp);
	return(0);
}

Macro *Ft_macinstall(char *name, char *line, int type)
{
	Macro *mp;
	unsigned int size = TOKENSIZE;

	if ((mp = (Macro *)malloc(sizeof(Macro))) == (Macro *)0) {
		fprintf(stderr, "Allocation error in macro install.\n");
		Ft_catcher(ERRR);
	}
	if ((mp->name = (char *)malloc(strlen(name)+1)) == (char *)0) {
		fprintf(stderr, "Allocation error in macro install.\n");
		Ft_catcher(ERRR);
	}
	if (type == AMACRO || type == ANALIAS) {
		char *fp, *np;

		if ((mp->fname = (char *)malloc(strlen(name)+1)) == (char *)0) {
			fprintf(stderr, "Allocation error in macro install.\n");
			Ft_catcher(ERRR);
		}
		for (np=name, fp=mp->fname; *np != '\0'; np++) {
			if (*np != '!')
				*fp++ = *np;
		}
		*fp = '\0';
		size = strlen(line) + 1;
	}
	else {
		mp->fname = 0;
	}
	if ((mp->line = (char *)malloc(size)) == (char *)0) {
		fprintf(stderr, "Allocation error in macro install.\n");
		Ft_catcher(ERRR);
	}

	mp->type = type;
	mp->nargs = 0;
	if (*name)
		sscanf(name, "%s", mp->name);
	if (*line)
		sprintf(mp->line, "%s", line);
	mp->next = maclist;
	maclist = mp;
	return(mp);
}

	/* remove macros on request */
int Ft_macremove(char *name, int type)
{
	Macro *mp, *mprev=(Macro *)0;
	Macro *mpres;
	int found;
	int all;

	found = all = (strcmp(name, "@all") == 0);
	mp = maclist;
	while (mp != (Macro *)0) {
		if ((Ft_almost(name, mp->name) || all) && mp->type == type) {
			found = 1;
			mpres = mp;
			if (mp == maclist) {  /* first one ? */
				maclist = mp->next;
				mp = maclist;
			}
			else {
				mp = mprev->next = mp->next;
			}
			free(mpres->name);
			free(mpres->line);
			free((char *)mpres);
		}
		else {
			mprev = mp;
			mp = mp->next;
		}
	}

	if (!found) {
		if (type == AMACRO)
			fprintf(stderr, "Warning: %s: No such macro.\n", name);
		else
			fprintf(stderr, "Warning: %s: No such alias.\n", name);
		return(ERRR);
	}
	return(0);
}

int Ft_showmac(int argc, char **argv, int type)
{
	FILE *fp;
	Macro *mp;
	int found, some;
	extern int Ft_Interact;

	fp = stdout;
	some = (argc == 3);
	found = 0;
	if (!some && Ft_Interact && *Ft_Pager && type == AMACRO) {
		if ((fp = popen(Ft_Pager, "w")) == (FILE *)NULL)  {
			fprintf(stderr, "Could not open pager %s.\n", Ft_Pager);
			fp = stdout;
		}
	}

	for (mp = maclist; mp != (Macro *)0; mp = mp->next) {
		if (mp->type != type)
			continue;
		if (some) {
			if (found) {
				if (fp != stdout) {
					pclose(fp);
				}
				return(0);
			}
			else if (Ft_almost(argv[2], mp->name)) {
				if ((fp = popen(Ft_Pager, "w")) == (FILE *)NULL)  {
					fprintf(stderr, "Could not open pager %s.\n", Ft_Pager);
					fp = stdout;
				}
				found = 1;
			}
			else {
				continue;
			}
		}
		switch (mp->type) {
			case ANALIAS:
				fprintf(fp, "%s\t%s",
				mp->name, mp->line);
				break;
			case AMACRO:
				fprintf(fp, "macro %s: (%d args)\n{\n%s}\n",
				mp->name, mp->nargs, mp->line);
				break;
			default:
				break;
		}
	}
	if (some && !found)
		fprintf(stderr, "show macro: %s: No such macro.\n", argv[2]);
	if (fp != stdout)
		pclose(fp);
	return(0);
}
