#include <stdio.h>
#include <string.h>
#include <stdarg.h>

#define nonempty(X) ((X) != NULL && (X)[0] != '\0')
#define endof(X) ((X)+(int)strlen(X))

void split(char *s, int num, ...)
{
  va_list vl ;
  char *t, **ptr ;
  int i, quoted ;

  t = s ;
  va_start(vl, num) ;

  for (i=0 ; i < num ; i++) {
    ptr = va_arg(vl, char **) ;
    quoted = va_arg(vl, int) ;
    if (quoted) t++ ; /* skip first quote */
    if (ptr != NULL) *ptr = t ;
    if (quoted) {
      while (*t != '"') t++ ;  /* skip to second quote */
      *t = '\0' ; /* null out the second quote */
      t++ ; /* skip to space which is either comma or null */
      if (*t == ',') t++ ; /* skip over comma */
    }
    else {
      while (*t != '\0' && *t != ',') t++ ;
      if (*t == ',') {
	*t = '\0' ;
	t++ ;
      }
    }
  }
}

void capitalize(char *s)
{
  int len, i ;
  len = strlen(s) ;
  if (len > 0) s[0] = toupper(s[0]) ;
  for (i=1 ; i < len ; i++) s[i] = tolower(s[i]) ;
}

void auth_convert(char *old, char *new) 
{
  unsigned char *s ;
  char *t ;

  for (s = (unsigned char *)old, t=new ; *s != '\0' ; s++) {
    switch(*s) {
    case '&':
      sprintf(t, "&amp;") ;
      for (;*t != '\0' ; t++) ;
      break ;
    case 0x81: 
      sprintf(t, "&uuml;") ;
      for (;*t != '\0' ; t++) ;
      break ;
    case 0x82: 
      sprintf(t, "&eacute;") ;
      for (;*t != '\0' ; t++) ;
      break ;
    case 0x84: 
      sprintf(t, "&auml;") ;
      for (;*t != '\0' ; t++) ;
      break ;
    case 0x89:
      sprintf(t, "&euml;") ;
      for (;*t != '\0' ; t++) ;
      break ;
    case 0x8A: 
      sprintf(t, "&egrave;") ;
      for (;*t != '\0' ; t++) ;
      break ;
    case 0x93: 
      sprintf(t, "&ocirc;") ;
      for (;*t != '\0' ; t++) ;
      break ;
    case 0x94: 
      sprintf(t, "&ouml;") ;
      for (;*t != '\0' ; t++) ;
      break ;
    case 0x98:
      sprintf(t, "&yuml;") ;
      for (;*t != '\0' ; t++) ;
      break ;
    case 0xA0: 
      sprintf(t, "&aacute;") ;
      for (;*t != '\0' ; t++) ;
      break ;
    case 0xA1: 
      sprintf(t, "&iacute;") ;
      for (;*t != '\0' ; t++) ;
      break ;
    case 0xA2:
      sprintf(t, "&oacute;") ;
      for (;*t != '\0' ; t++) ;
      break ;
    default:
      *t = *s ;
      t++ ;
    }
  }
  *t = '\0' ;
}

main(int argc, char *argv[])
{
  FILE *fp ;
  int i ;
  char *ncode, *tcode, *acsy, *rank_str, *fam, *genhyb, *gen ;
  char *spechyb, *spec, *trirank, *tri, *quadrank, *quad, *auth ;
  char new_auth[256] ;
  int rank ;
  char cur_name[256], cur_spec[256], cur_tri[256], cur_quad[256] ;
  char pp_gen[256], pp_spec[256], pp_auth[256] ;
  char **sy_gens, **sy_specs, **sy_auths ;
  int num_sy_names, max_sy_names ;
  char ac_gen[256], ac_spec[256], ac_spec_auth[256] ;
  char ac_tri[256], ac_tri_auth[256] ;
  char ac_fam_code[10], ac_gen_code[10], ac_spec_code[10], ac_tri_code[10] ;
  char qstr[1024] ;

  char *start_format = "%-8s\t%-1s\t%-8s\t%d\t" ;
  char *codes_format = "%-8s\t%-8s\t%-8s\t%-8s\t" ;
  char *names_format = "%-20s\t%-1s\t%-20s\t%-1s\t%-30s\t%-4s\t%-30s\t%-4s\t%-30s\t" ;

  fp = fopen(argv[1], "r") ;

  max_sy_names = 0 ;

  do {
    char s[256] ;

    fgets(s, 256, fp) ;
    if (!feof(fp)) {
      int len ;
      len = strlen(s) ;
      if (s[len-1] == '\n') s[len-1] = '\0' ;
      split(s, 14, &ncode, 1,
	    &tcode, 1,
	    &acsy, 1, 
	    &rank_str, 0,
	    &fam, 1, 
	    &genhyb, 1,
	    &gen, 1,
	    &spechyb, 1,
	    &spec, 1,
	    &trirank, 1,
	    &tri, 1,
	    &quadrank, 1,
	    &quad, 1,
	    &auth, 1) ;

      /* for (i=1 ; ncode[i] != '\0' ; i++) ncode[i] += 'A' - '0' ;
	 for (i=1 ; tcode[i] != '\0' ; i++) tcode[i] += 'A' - '0' ; */

      rank = atoi(rank_str) ;

      auth_convert(auth, new_auth) ;
      auth = new_auth ;

      capitalize(fam) ;

      if (rank == 1 && strcmp(acsy, "PP")) 
	num_sy_names = 0 ;

      else {
	if (nonempty(spechyb))
	  sprintf(cur_spec, "%s<i>%s</i> X <i>%s</i>",
		  nonempty(genhyb) ? "X " : "", gen, spec) ;
	else
	  sprintf(cur_spec, "%s<i>%s %s</i>",
		  nonempty(genhyb) ? "X " : "", gen, spec) ;
	if (rank >= 3)
	  sprintf(cur_tri, "%s. <i>%s</i>", trirank, tri) ;
	if (rank == 4)
	  sprintf(cur_quad, "%s. <i>%s</i>", quadrank, quad) ;
      }

      if (!strcmp(acsy, "PP")) {
	strcpy(pp_gen, gen) ;
	strcpy(pp_spec, spec) ;
	strcpy(pp_auth, auth) ;
      }
      else if (!strcmp(acsy, "SY") && rank == 2) {
	if (num_sy_names == max_sy_names) {
	  sy_gens = (char **)
	    (max_sy_names == 0 ? 
	     malloc(sizeof(char *)) :
	     realloc(sy_gens, sizeof(char *) * (max_sy_names+1))) ;
	  sy_gens[max_sy_names] = (char *)malloc(sizeof(char) * 256) ;

	  sy_specs = (char **)
	    (max_sy_names == 0 ? 
	     malloc(sizeof(char *)) :
	     realloc(sy_specs, sizeof(char *) * (max_sy_names+1))) ;
	  sy_specs[max_sy_names] = (char *)malloc(sizeof(char) * 256) ;

	  sy_auths = (char **)
	    (max_sy_names == 0 ? 
	     malloc(sizeof(char *)) :
	     realloc(sy_auths, sizeof(char *) * (max_sy_names+1))) ;
	  sy_auths[max_sy_names] = (char *)malloc(sizeof(char) * 256) ;
	  max_sy_names++ ;
	}
	strcpy(sy_gens[num_sy_names], gen) ;
	strcpy(sy_specs[num_sy_names], spec) ;
	strcpy(sy_auths[num_sy_names], auth) ;
	num_sy_names++ ;
      }
      else if (!strcmp(acsy, "AC"))
	if (rank == 0) {
	  strcpy(ac_fam_code, ncode) ;
	}
	else if (rank == 1) {
	  strcpy(ac_gen_code, ncode) ;
	}
	else if (rank == 2) {
	  strcpy(ac_spec_code, ncode) ;
	  strcpy(ac_gen, gen) ;
	  strcpy(ac_spec, spec) ;
	  strcpy(ac_spec_auth, auth) ;
	}
	else if (rank == 3) {
	  strcpy(ac_tri_code, ncode) ;
	  strcpy(ac_tri, tri) ;
	  strcpy(ac_tri_auth, auth) ;
	}

      
      if (strcmp(acsy, "PP")) {
	char *ghstr = nonempty(genhyb) ? "X" : "" ;
	char *shstr = nonempty(spechyb) ? "X" : "" ;

	printf(start_format,
	       ncode+1, !strcmp(acsy, "AC") ? "t" : "f",
	       tcode+1, rank) ;


	cur_name[0] = '\0' ;

	switch (rank) {
	case 0:
	  printf(codes_format,
		  "", "", "", "") ;
	  printf(names_format,
		  fam, "", "", "", "", "", "", "", "") ;
	  printf("%s\t%s", fam, "") ;
	  break ;
	case 1:
	  printf(codes_format,
		  ac_fam_code+1, "", "", "", "") ;
	  printf(names_format,
		  fam, ghstr, gen, "", "", "", "", "", "") ;
	  printf("<i>%s</i>\t<i>%s</i> %s", gen, gen, auth) ;

	  break ;
	case 2:
	  printf(codes_format, ac_fam_code+1, ac_gen_code+1, "", "") ;
	  printf(names_format, fam, ghstr, gen, shstr, spec, "", "", "", "") ;
	  printf("%s\t%s %s", cur_spec, cur_spec, auth) ;
	  break ;
	case 3:
	  printf(codes_format, ac_fam_code+1, ac_gen_code+1, 
		 ac_spec_code+1, "");
	  printf(names_format, fam, ghstr, gen, shstr, spec, trirank, tri,
		 "", "") ;
	  printf("%s %s\t", cur_spec, cur_tri) ;

	  if (!strcmp(acsy, "AC")) 
	    printf("%s %s %s %s",
		   cur_spec, ac_spec_auth, cur_tri, auth) ;
	  else {
	    if (!strcmp(gen, pp_gen) &&
		!strcmp(spec, pp_spec))
	      printf("%s %s %s %s",
		     cur_spec, pp_auth, cur_tri, auth) ;

	    else if (!strcmp(gen, ac_gen) &&
		     !strcmp(spec, ac_spec))
	      printf("%s %s %s %s",
		     cur_spec, ac_spec_auth, cur_tri, auth) ;
	    else {
	      int i ;
	      for (i=0 ; i < num_sy_names ; i++) 
		if (!strcmp(gen, sy_gens[i]) &&
		    !strcmp(spec, sy_specs[i])) {
		  printf("%s %s %s %s", 
			 cur_spec, sy_auths[i], cur_tri, auth) ;
		  break ;
		}
	      if (i == num_sy_names) {
		printf("%s %s %s %s", 
		       cur_spec, "???", cur_tri, auth) ;
	      }
	    }
	  }
	  break ;
	case 4:
	  printf(codes_format, ac_fam_code+1, ac_gen_code+1,
		 ac_spec_code+1, ac_tri_code+1) ;
	  printf(names_format, fam, ghstr, gen, shstr, spec,
		 trirank, tri, quadrank, quad) ;
	  printf("%s %s %s\t", cur_spec, cur_tri, cur_quad) ;
	  printf("%s %s %s %s %s %s", cur_spec, ac_spec_auth,
		 cur_tri, ac_tri_auth, cur_quad, auth) ;
	  break ;

	}
	putchar('\n') ;
      }
    }
  } while (!feof(fp)) ;
  
  fclose(fp) ; 
}


