#include <ctype.h>
#include <stdio.h>
#include <string.h>
#ifndef NOMALLOC_H
#include <malloc.h>
#endif
#ifndef NOUNISTD_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 *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;
#ifndef AMIGA
    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;
        }
    }
#endif

    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)) {
#ifndef AMIGA
        		if ((fp = popen(Ft_Pager, "w")) == (FILE *)NULL)  {
            		fprintf(stderr, "Could not open pager %s.\n", Ft_Pager);
            		fp = stdout;
        		}
#endif
				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);
}
