#include "common.h"

/*
*  WARNING!  This turns all upper case names into lower case
*  local ones.
*/

static  String  *getdbfiles(void);
static  int     translate(char*, char**, String*, String*);
static  int     lookup(String**, String*, String*);
static  char    *mklower(char*);

static  int     debug;
static  int     from;
static  char    *namefiles = "namefiles";

#define dprint(...) if(debug)fprint(2, __VA_ARGS__); else {}

void
usage(void)
{
       fprint(2, "usage: aliasmail [-df] [-n namefile] [names ...]\n");
       exits("usage");
}

void
main(int argc, char *argv[])
{
       char *alias, **names, *p;               /* names of this system */
       int i, rv;
       String *s, *salias, *files;

       ARGBEGIN {
       case 'd':
               debug = 1;
               break;
       case 'f':
               from = 1;
               break;
       case 'n':
               namefiles = EARGF(usage());
               break;
       default:
               usage();
       } ARGEND
       if (chdir(UPASLIB) < 0)
               sysfatal("chdir: %r");

       names = sysnames_read();
       files = getdbfiles();
       salias = s_new();

       /* loop through the names to be translated (from standard input) */
       for(i=0; i<argc; i++) {
               s = unescapespecial(s_copy(mklower(argv[i])));
               if(strchr(s_to_c(s), '!') == 0)
                       rv = translate(s_to_c(s), names, files, salias);
               else
                       rv = -1;
               alias = s_to_c(salias);
               if(from){
                       if (rv >= 0 && *alias != '\0'){
                               if(p = strchr(alias, '\n'))
                                       *p = 0;
                               if(p = strchr(alias, '!')) {
                                       *p = 0;
                                       print("%s", alias);
                               } else {
                                       if(p = strchr(alias, '@'))
                                               print("%s", p+1);
                                       else
                                               print("%s", alias);
                               }
                       }
               } else {
                       if (rv < 0 || *alias == '\0')
                               print("local!%s\n", s_to_c(s));
                       else
                               /* this must be a write, not a print */
                               write(1, alias, strlen(alias));
               }
               s_free(s);
       }
       exits(0);
}

/* get the list of dbfiles to search */
static String *
getdbfiles(void)
{
       char *nf;
       Sinstack *sp;
       String *files;

       if(from)
               nf = "fromfiles";
       else
               nf = namefiles;

       /* system wide aliases */
       files = s_new();
       if ((sp = s_allocinstack(nf)) != 0){
               while(s_rdinstack(sp, files))
                       s_append(files, " ");
               s_freeinstack(sp);
       }

       dprint("files are %s\n", s_to_c(files));
       return files;
}

/* loop through the translation files */
static int
translate(char *name, char **namev,     String *files, String *alias)
{
       int n, rv;
       String *file, **fullnamev;

       rv = -1;
       file = s_new();

       dprint("translate(%s, %s, %s)\n", name,
               s_to_c(files), s_to_c(alias));

       /* create the full name to avoid loops (system!name) */
       for(n = 0; namev[n]; n++)
               ;

       fullnamev = (String**)malloc(sizeof(String*)*(n+2));
       n = 0;
       fullnamev[n++] = s_copy(name);
       for(; *namev; namev++){
               fullnamev[n] = s_copy(*namev);
               s_append(fullnamev[n], "!");
               s_append(fullnamev[n], name);
               n++;
       }
       fullnamev[n] = 0;

       /* look at system-wide names */
       s_restart(files);
       while (s_parse(files, s_restart(file)) != 0)
               if (lookup(fullnamev, file, alias)==0) {
                       rv = 0;
                       goto out;
               }

out:
       for(n = 0; fullnamev[n]; n++)
               s_free(fullnamev[n]);
       s_free(file);
       free(fullnamev);
       return rv;
}

/*
*  very dumb conversion to bang format
*/
static String*
attobang(String *token)
{
       char *p;
       String *tok;

       p = strchr(s_to_c(token), '@');
       if(p == 0)
               return token;

       p++;
       tok = s_copy(p);
       s_append(tok, "!");
       s_nappend(tok, s_to_c(token), p - s_to_c(token) - 1);

       return tok;
}

/*  Loop through the entries in a translation file looking for a match.
*  Return 0 if found, -1 otherwise.
*/
#define compare(a, b) cistrcmp(s_to_c(a), b)

static int
lookup(String **namev, String *file, String *alias)
{
       char *name;
       int i, rv;
       String *line, *token, *bangtoken;
       Sinstack *sp;

       dprint("lookup(%s, %s, %s, %s)\n", s_to_c(namev[0]), s_to_c(namev[1]),
               s_to_c(file), s_to_c(alias));

       rv = -1;
       name = s_to_c(namev[0]);
       line = s_new();
       token = s_new();
       s_reset(alias);
       if ((sp = s_allocinstack(s_to_c(file))) == 0)
               return -1;

       /* look for a match */
       while (s_rdinstack(sp, s_restart(line))!=0) {
               dprint("line is %s\n", s_to_c(line));
               s_restart(token);
               if (s_parse(s_restart(line), token)==0)
                       continue;
               if (compare(token, "#include")==0){
                       if(s_parse(line, s_restart(token))!=0) {
                               if(lookup(namev, line, alias) == 0)
                                       break;
                       }
                       continue;
               }
               if (compare(token, name)!=0)
                       continue;
               /* match found, get the alias */
               while(s_parse(line, s_restart(token))!=0) {
                       bangtoken = attobang(token);

                       /* avoid definition loops */
                       for(i = 0; namev[i]; i++)
                               if(compare(bangtoken, s_to_c(namev[i]))==0) {
                                       s_append(alias, "local");
                                       s_append(alias, "!");
                                       s_append(alias, name);
                                       break;
                               }

                       if(namev[i] == 0)
                               s_append(alias, s_to_c(token));
                       s_append(alias, "\n");

                       if(bangtoken != token)
                               s_free(bangtoken);
               }
               rv = 0;
               break;
       }
       s_free(line);
       s_free(token);
       s_freeinstack(sp);
       return rv;
}

static char*
mklower(char *name)
{
       char c, *p;

       for(p = name; c = *p; p++)
               if(c >= 'A' && c <= 'Z')
                       *p = c + 0x20;
       return name;
}