text: #include "common.h"
  
  /*
   *  WARNING!  This turns all upper case names into lower case
   *  local ones.
   */
  
  /* predeclared */
  static String	*getdbfiles(void);
  static int	translate(char*, char**, String*, String*);
  static int	lookup(String**, String*, String*);
  static int	compare(String*, char*);
  static char*	mklower(char*);
  
  static int debug;
  static int from;
  static char *namefiles = "namefiles";
  #define DEBUG if(debug)
  
  /* loop through the names to be translated */
  void
  main(int argc, char *argv[])
  {
  	String *s;
  	String *alias;		/* the alias for the name */
  	char **names;		/* names of this system */
  	String *files;		/* list of files to search */
  	int i, rv;
  	char *p;
  
  	ARGBEGIN {
  	case 'd':
  		debug = 1;
  		break;
  	case 'f':
  		from = 1;
  		break;
  	case 'n':
  		namefiles = ARGF();
  		break;
  	} ARGEND
  
  	if (chdir(UPASLIB) < 0)
  		sysfatal("aliasmail chdir %s: %r", UPASLIB);
  
  	/* get environmental info */
  	names = sysnames_read();
  	files = getdbfiles();
  	alias = 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, alias);
  		else
  			rv = -1;
  		if(from){
  			if (rv >= 0 && *s_to_c(alias) != '\0'){
  				p = strchr(s_to_c(alias), '\n');
  				if(p)
  					*p = 0;
  				p = strchr(s_to_c(alias), '!');
  				if(p) {
  					*p = 0;
  					print("%s", s_to_c(alias));
  				} else {
  					p = strchr(s_to_c(alias), '@');
  					if(p)
  						print("%s", p+1);
  					else
  						print("%s", s_to_c(alias));
  				}
  			}
  		} else {
  			if (rv < 0 || *s_to_c(alias) == '\0')
  				print("local!%s\n", s_to_c(s));
  			else {
  				/* this must be a write, not a print */
  				write(1, s_to_c(alias), strlen(s_to_c(alias)));
  			}
  		}
  		s_free(s);
  	}
  	exits(0);
  }
  
  /* get the list of dbfiles to search */
  static String *
  getdbfiles(void)
  {
  	Sinstack *sp;
  	String *files = s_new();
  	char *nf;
  
  	if(from)
  		nf = "fromfiles";
  	else
  		nf = namefiles;
  
  	/* system wide aliases */
  	if ((sp = s_allocinstack(nf)) != 0){
  		while(s_rdinstack(sp, files))
  			s_append(files, " ");
  		s_freeinstack(sp);
  	}
  
  
  	DEBUG print("files are %s\n", s_to_c(files));
  
  	return files;
  }
  
  /* loop through the translation files */
  static int
  translate(char *name,		/* name to translate */
  	char **namev,		/* names of this system */
  	String *files,		/* names of system alias files */
  	String *alias)		/* where to put the alias */
  {
  	String *file = s_new();
  	String **fullnamev;
  	int n, rv;
  
  	rv = -1;
  
  	DEBUG print("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.
   */
  static int
  lookup(
  	String **namev,
  	String *file,
  	String *alias)	/* returned String */
  {
  	String *line = s_new();
  	String *token = s_new();
  	String *bangtoken;
  	int i, rv = -1;
  	char *name =  s_to_c(namev[0]);
  	Sinstack *sp;
  
  	DEBUG print("lookup(%s, %s, %s, %s)\n", s_to_c(namev[0]), s_to_c(namev[1]),
  		s_to_c(file), s_to_c(alias));
  
  	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) {
  		DEBUG print("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;
  }
  
  #define lower(c) ((c)>='A' && (c)<='Z' ? (c)-('A'-'a'):(c))
  
  /* compare two Strings (case insensitive) */
  static int
  compare(String *s1,
  	char *p2)
  {
  	char *p1 = s_to_c(s1);
  	int rv;
  
  	DEBUG print("comparing %s to %s\n", p1, p2);
  	while((rv = lower(*p1) - lower(*p2)) == 0) {
  		if (*p1 == '\0')
  			break;
  		p1++;
  		p2++;
  	}
  	return rv;
  }
  
  static char*
  mklower(char *name)
  {
  	char *p;
  	char c;
  
  	for(p = name; *p; p++){
  		c = *p;
  		*p = lower(c);
  	}
  	return name;
  }
  