/* function.c - functions installed in the parser by mirrordir
   This has nothing to do with cryptography.
   Copyright (C) 1998 Paul Sheer

   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 2 of the License, 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., 59 Temple Place, Suite 330, Boston, MA
   02111-1307, USA.  
 */

#include "mostincludes.h"
#ifdef HAVE_SYS_WAIT_H
#include <sys/wait.h>
#endif
#include "diffie/parse.h"
#include "mirrordir.h"
#include "vfs/util.h"		/* for S_IFBLK with -ansi -pedantic */
#include "diffie/compat.h"
#include "mad.h"


#define FUNC_FUNC(x,m)						\
    static int x (void *unused, ValueStack ** v)		\
    {								\
	Value value;						\
	value = parser_value_pop (v);				\
	if (!(value.type & VALUE_LONG)) {			\
	    fprintf (stderr, "mirrordir: bad arg type\n"); 	\
	    return 1;						\
	}							\
	parser_push_int (v, m(value.v.i));			\
	parser_value_free (value);					\
	return 0;						\
    }								\

FUNC_FUNC (op_islnk, S_ISLNK)
FUNC_FUNC (op_isreg, S_ISREG)
FUNC_FUNC (op_isdir, S_ISDIR)
FUNC_FUNC (op_ischr, S_ISCHR)
FUNC_FUNC (op_isblk, S_ISBLK)
FUNC_FUNC (op_isfifo, S_ISFIFO)
FUNC_FUNC (op_issock, S_ISSOCK)
char cwd[MAX_PATH_LEN + 1];

static int op_getcwd (struct stated_file *f, ValueStack ** v)
{
    parser_push_string (v, cwd, strlen (cwd));
    return 0;
}

static int op_getfile (struct stated_file *f, ValueStack ** v)
{
    char *filename;
    filename = strrchr (f->name, PATH_SEP) + 1;
    parser_push_string (v, filename, strlen (filename));
    return 0;
}

static int op_gettime (struct stated_file *f, ValueStack ** v)
{
    time_t t;
    time (&t);
    parser_push_int (v, t);
    return 0;
}

static int op_getname (struct stated_file *f, ValueStack ** v)
{
    char *filename, *p;
    filename = (char *) strdup (strrchr ((char *) f->name, PATH_SEP) + 1);
    p = strrchr (filename, '.');
    if (p)
	*p = '\0';
    parser_push_string_free (v, filename, strlen (filename));
    return 0;
}

static int op_getdir (struct stated_file *f, ValueStack ** v)
{
    char *filename, *p;
    p = (char *) strrchr (filename = (char *) strdup ((char *) f->name), PATH_SEP);
    *p = '\0';
    parser_push_string_free (v, filename, strlen (filename));
    return 0;
}

static int op_getpath (struct stated_file *f, ValueStack ** v)
{
    parser_push_string (v, f->name, strlen (f->name));
    return 0;
}

static int op_getext (struct stated_file *f, ValueStack ** v)
{
    char *filename, *p;
    filename = strrchr ((char *) f->name, PATH_SEP) + 1;
    p = strrchr (filename, '.');
    if (p)
	parser_push_string (v, p + 1, strlen (p + 1));
    else
	parser_push_string (v, "", 0);
    return 1;
}


#define FUNC_STAT(x,m)						\
    static int x (struct stated_file *f, ValueStack ** v)	\
    {								\
	parser_push_int (v, f->stat.m);				\
	return 0;						\
    }								\

long dev_to_long (dev_t dev);

static int op_stdev (struct stated_file *f, ValueStack ** v)
{
    parser_push_int (v, dev_to_long (f->stat.st_dev));
    return 0;
}

static int op_strdev (struct stated_file *f, ValueStack ** v)
{
    parser_push_int (v, dev_to_long (f->stat.st_rdev));
    return 0;
}

FUNC_STAT (op_stino, st_ino)
FUNC_STAT (op_stmode, st_mode)
FUNC_STAT (op_stnlink, st_nlink)
FUNC_STAT (op_stuid, st_uid)
FUNC_STAT (op_stgid, st_gid)
FUNC_STAT (op_strsize, st_size)
#ifdef HAVE_ST_BLKSIZE
FUNC_STAT (op_blksize, st_blksize)
#endif
#ifdef HAVE_ST_BLOCKS
FUNC_STAT (op_blocks, st_blocks)
#endif
FUNC_STAT (op_atime, st_atime)
FUNC_STAT (op_mtime, st_mtime)
FUNC_STAT (op_ctime, st_ctime)

static int op_regexp (void *dummy, ValueStack ** v)
{
/* FIXME: not thread safe */
    static regex_t regexp;
    Value value1, value2;
    char *p = 0;
    static char *s = 0;
    value2 = parser_value_pop (v);
    value1 = parser_value_pop (v);
    if (!((value2.type & value1.type) & VALUE_STRING)) {
	fprintf (stderr, "mirrordir: bad arg type to regexp()\n");
	return 1;
    }
    if (!s) {
	goto new_comp;
	memset (&regexp, 0, sizeof (regexp));	/* probably don't need this */
    }
    if (strcmp (s, value1.v.s)) {
      new_comp:
	p = value1.v.s;
	if (s) {
	    free (s);
	    regfree (&regexp);
	}
	s = (char *) strdup (value1.v.s);
	if (regcomp (&regexp, p, REG_EXTENDED | REG_NOSUB)) {
	    fprintf (stderr, "mirrordir: regexp(): error compiling regular expression");
	    return 1;
	}
    }
    parser_push_int (v, regexec (&regexp, value2.v.s, 0, 0, 0));
    parser_value_free (value1);
    parser_value_free (value2);
    return 0;
}

char *glob_translate (char *s);

static int op_glob (void *dummy, ValueStack ** v)
{
/* FIXME: not thread safe */
    static regex_t regexp;
    Value value1, value2;
    char *p = 0;
    static char *s = 0;
    value2 = parser_value_pop (v);
    value1 = parser_value_pop (v);
    if (!((value2.type & value1.type) & VALUE_STRING)) {
	fprintf (stderr, "mirrordir: bad arg type to regexp()\n");
	return 1;
    }
    if (!s)
	goto new_comp;
    if (strcmp (s, value1.v.s)) {
      new_comp:
	p = glob_translate (value1.v.s);
	if (s)
	    free (s);
	s = (char *) strdup (value1.v.s);
	if (regcomp (&regexp, p, REG_EXTENDED | REG_NOSUB)) {
	    fprintf (stderr, "mirrordir: glob(): error compiling glob expression\n");
	    return 1;
	}
	free (p);
    }
    parser_push_int (v, regexec (&regexp, value2.v.s, 0, 0, 0));
    parser_value_free (value1);
    parser_value_free (value2);
    return 0;
}

static Operator operators[] =
{
    {0, "TIME", 0, OP_PREDEF, 1, (Func) op_gettime, 0, 0},
    {0, "FILE", 0, OP_PREDEF, 1, (Func) op_getfile, 0, 0},
    {0, "CWD", 0, OP_PREDEF, 1, (Func) op_getcwd, 0, 0},
    {0, "NAME", 0, OP_PREDEF, 1, (Func) op_getname, 0, 0},
    {0, "EXTENSION", 0, OP_PREDEF, 1, (Func) op_getext, 0, 0},
    {0, "DIR", 0, OP_PREDEF, 1, (Func) op_getdir, 0, 0},
    {0, "PATH", 0, OP_PREDEF, 1, (Func) op_getpath, 0, 0},
    {0, "stat.st_dev", 0, OP_PREDEF, 1, (Func) op_stdev, 0, 0},
    {0, "stat.st_ino", 0, OP_PREDEF, 1, (Func) op_stino, 0, 0},
    {0, "stat.st_mode", 0, OP_PREDEF, 1, (Func) op_stmode, 0, 0},
    {0, "stat.st_nlink", 0, OP_PREDEF, 1, (Func) op_stnlink, 0, 0},
    {0, "stat.st_uid", 0, OP_PREDEF, 1, (Func) op_stuid, 0, 0},
    {0, "stat.st_gid", 0, OP_PREDEF, 1, (Func) op_stgid, 0, 0},
    {0, "stat.st_rdev", 0, OP_PREDEF, 1, (Func) op_strdev, 0, 0},
    {0, "stat.st_size", 0, OP_PREDEF, 1, (Func) op_strsize, 0, 0},
#ifdef HAVE_ST_BLKSIZE
    {0, "stat.st_blksize", 0, OP_PREDEF, 1, (Func) op_blksize, 0, 0},
#endif
#ifdef HAVE_ST_BLOCKS
    {0, "stat.st_blocks", 0, OP_PREDEF, 1, (Func) op_blocks, 0, 0},
#endif
    {0, "stat.st_atime", 0, OP_PREDEF, 1, (Func) op_atime, 0, 0},
    {0, "stat.st_mtime", 0, OP_PREDEF, 1, (Func) op_mtime, 0, 0},
    {0, "stat.st_ctime", 0, OP_PREDEF, 1, (Func) op_ctime, 0, 0},
    {0, "glob", 0, OP_FUNCTION, 1, (Func) op_glob, 0, 2},
    {0, "regexp", 0, OP_FUNCTION, 1, (Func) op_regexp, 0, 2},
    {0, "S_ISLNK", 0, OP_FUNCTION, 1, (Func) op_islnk, 0, 1},
    {0, "S_ISREG", 0, OP_FUNCTION, 1, (Func) op_isreg, 0, 1},
    {0, "S_ISDIR", 0, OP_FUNCTION, 1, (Func) op_isdir, 0, 1},
    {0, "S_ISCHR", 0, OP_FUNCTION, 1, (Func) op_ischr, 0, 1},
    {0, "S_ISBLK", 0, OP_FUNCTION, 1, (Func) op_isblk, 0, 1},
    {0, "S_ISFIFO", 0, OP_FUNCTION, 1, (Func) op_isfifo, 0, 1},
    {0, "S_ISSOCK", 0, OP_FUNCTION, 1, (Func) op_issock, 0, 1},
    {0, "S_IFMT", 0, OP_CONSTANT, 1, (Func) S_IFMT, 0, 0},
    {0, "S_IFSOCK", 0, OP_CONSTANT, 1, (Func) S_IFSOCK, 0, 0},
    {0, "S_IFLNK", 0, OP_CONSTANT, 1, (Func) S_IFLNK, 0, 0},
    {0, "S_IFREG", 0, OP_CONSTANT, 1, (Func) S_IFREG, 0, 0},
    {0, "S_IFBLK", 0, OP_CONSTANT, 1, (Func) S_IFBLK, 0, 0},
    {0, "S_IFDIR", 0, OP_CONSTANT, 1, (Func) S_IFDIR, 0, 0},
    {0, "S_IFCHR", 0, OP_CONSTANT, 1, (Func) S_IFCHR, 0, 0},
    {0, "S_IFIFO", 0, OP_CONSTANT, 1, (Func) S_IFIFO, 0, 0},
    {0, "S_ISUID", 0, OP_CONSTANT, 1, (Func) S_ISUID, 0, 0},
    {0, "S_ISGID", 0, OP_CONSTANT, 1, (Func) S_ISGID, 0, 0},
    {0, "S_ISVTX", 0, OP_CONSTANT, 1, (Func) S_ISVTX, 0, 0},
    {0, "S_IRWXU", 0, OP_CONSTANT, 1, (Func) S_IRWXU, 0, 0},
    {0, "S_IRUSR", 0, OP_CONSTANT, 1, (Func) S_IRUSR, 0, 0},
    {0, "S_IWUSR", 0, OP_CONSTANT, 1, (Func) S_IWUSR, 0, 0},
    {0, "S_IXUSR", 0, OP_CONSTANT, 1, (Func) S_IXUSR, 0, 0},
    {0, "S_IRWXG", 0, OP_CONSTANT, 1, (Func) S_IRWXG, 0, 0},
    {0, "S_IRGRP", 0, OP_CONSTANT, 1, (Func) S_IRGRP, 0, 0},
    {0, "S_IWGRP", 0, OP_CONSTANT, 1, (Func) S_IWGRP, 0, 0},
    {0, "S_IXGRP", 0, OP_CONSTANT, 1, (Func) S_IXGRP, 0, 0},
    {0, "S_IRWXO", 0, OP_CONSTANT, 1, (Func) S_IRWXO, 0, 0},
    {0, "S_IROTH", 0, OP_CONSTANT, 1, (Func) S_IROTH, 0, 0},
    {0, "S_IWOTH", 0, OP_CONSTANT, 1, (Func) S_IWOTH, 0, 0},
    {0, "S_IXOTH", 0, OP_CONSTANT, 1, (Func) S_IXOTH, 0, 0},
    {0, "UNKNOWN", 0, OP_CONSTANT, 1, (Func) PATH_UNKNOWN, 0, 0},
    {0, "INCLUDE", 0, OP_CONSTANT, 1, (Func) PATH_INCLUDED, 0, 0},
    {0, "IGNORE", 0, OP_CONSTANT, 1, (Func) PATH_IGNORED, 0, 0},
    {0, "EXCLUDE", 0, OP_CONSTANT, 1, (Func) PATH_EXCLUDED, 0, 0}
};



static int n_operator = sizeof (operators) / sizeof (Operator);

void add_functions (void)
{
    int i;
    for (i = 0; i < n_operator; i++)
	parser_add_operator (operators + i);
}



