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


#define  MAXLINES            10000
#define  MAXLEN               1024

#define  LEVE_OPEN               1

#define  INDEXNAME  "\\indexentry"   /* Zeilen mit diesem Kopf "ubergehen. */
#define  INDEXNAMELEN           11   /* L"ange von INDEXNAME.              */
                                     /* F"ur solche Zeilen ist MakeIndex   */
                                     /* zust"andig.                        */

#define  KILLNAME  "\\Ikillname{}"   /* -K: Damit wird ARG1 und ARG2 einer */
                                     /* Zeile ersetzt, falls diese in der  */
                                     /* vorausgehenden Zeile gleich sind.  */

char  *lineptr[MAXLINES];
FILE  *infile,  *outfile;


/*--------------------------------------------------------------------------*/



void info(){ char p[]="%";
printf(" %s%s +-----------------------------------------------------------------+\n", p, p);
printf(" %s%s |    GBIBSORT sorts the lines of a file following german rules.   |\n", p, p);
printf(" %s%s |            GBIBSORT is part of BIBARTS, Version 1.3.            |\n", p, p);
printf(" %s%s |                (c) Timo Baumann <28.Mar.1998>                   |\n", p, p);
printf(" %s%s |                                                                 |\n", p, p);
printf(" %s%s | BIBARTS.STY ist eine LaTeX-Dokumenten-Stil-Option; siehe dazu   |\n", p, p);
printf(" %s%s | BIBARTS.TEX. (LaTeX erzeugt mit BIBARTS.STY ein [File].BAR.)    |\n", p, p);
printf(" %s%s |                                                                 |\n", p, p);
printf(" %s%s | GBIBSORT sortiert zeilenweise: Es liest das angegebene File und |\n", p, p);
printf(" %s%s | sortiert Zeichen vor Buchstaben (a vor A ...), dann Zahlen.     |\n", p, p);
printf(" %s%s | ABER: Die LaTeX-Sonderzeichen \\\"a  und  \"a  werden als ae usw.  |\n", p, p);
printf(" %s%s | sortiert und \'\\\', \'{\', \'}\', \'^\', \'`\', \'-\', \' \' nicht gewichtet. |\n", p, p);
printf(" %s%s | {RELEVANT}@ wird wie RELEVANT sortiert, aber nicht ausgegeben.  |\n", p, p);
printf(" %s%s | Option -K ersetzt mehrfach genannte Autoren ab dem 2. Mal.      |\n", p, p);
printf(" %s%s |                                                                 |\n", p, p);
printf(" %s%s | BENUTZUNG:     gbibsort [File].BAR  [[Schreib-File].PHY]        |\n", p, p);
printf(" %s%s | Autor Killing: gbibsort [File].BAR  [[Schreib-File].PHY] -k     |\n", p, p);
printf(" %s%s |                                                                 |\n", p, p);
printf(" %s%s | Falls GBIB209.BAT oder GBIB2E.BAT fehlen --- sie enthalten:     |\n", p, p);
printf(" %s%s |   IF EXIST %s1.bar GBIBSORT %s1.bar %s1.phy                        |\n", p, p, p, p, p);
printf(" %s%s |   IF EXIST %s1.bar MAKEINDX %s1.bar                               |\n", p, p, p, p);
printf(" %s%s |   IF EXIST %s1.tex CALL [LaTeX] %s1                               |\n", p, p, p, p);
printf(" %s%s +-----------------------------------------------------------------+\n", p, p);
           }


/*--------------------------------------------------------------------------*/


/*         CopyLeft: a) Verantwortung  -  b) Benutzung  -  c) Distribuntion */

/*                   a) Keinerlei Haftung !                                 */ 
/*                   b) Keine Funktions-Garantie !                          */ 
/*                          Der Anwender erkennt die Punkte a) - c) an.     */ 
/*                   c) GBIBSORT.C und damit erstellte ausf"uhrbare         */
/*                          Dateien d"urfen nur weitergegeben werden, wenn  */
/*                              1. daf"ur keine Kosten erhoben werden,      */ 
/*                                    die "uber diejenigen f"ur ihren       */ 
/*                                    Transfer hinausgehen !                */ 
/*                              2. vorgenommene "Anderungen                 */ 
/*                                    kommentiert sind !                    */ 
                                   

/*--------------------------------------------------------------------------*/

      /* Neugewichtung der Zeichen 48 - 126 des ASCII-Codes. */
      /* 1-47 [und ggf. 127-254] bleiben gleich! -> default. */
      /* 127-254 kommen in FILE.bar aber auch niemals vor!   */

      /* Im Deutschen werden Zeichen vor Buchstaben und      */
      /* Buchstaben vor Zahlen einsortiert.                  */
      /* Im Konfliktfall stehen Kleinbuchstaben vor grossen. */

      /* Da der ASCII-Code eine andere Reihenfolge hat,      */
      /* muss den Zeichen innerhalb des angegebenen Bereichs */
      /* eine neue INTEGER-Zahl zugeordnet werden.           */

int bewerten(char zz)
{
  switch(zz)
  {
      case ':': return   48;  /*  ASCII  58 \                               */
      case ';': return   49;  /*  ASCII  59  |                              */
      case '<': return   50;  /*  ASCII  60  |                              */
      case '=': return   51;  /*  ASCII  61  |                              */
      case '>': return   52;  /*  ASCII  62  |                              */
      case '?': return   53;  /*  ASCII  63  |                              */
      case '@': return   54;  /*  ASCII  64  |                              */
                              /*             |                              */
      case '[': return   55;  /*  ASCII  91   \                             */
      case '\\':return   56;  /*  ASCII  92     > Zeichen vor Buchstaben    */
      case ']': return   57;  /*  ASCII  93   /                             */
      case '^': return   58;  /*  ASCII  94  |                              */
      case '_': return   59;  /*  ASCII  95  |                              */
      case '`': return   60;  /*  ASCII  96  |                              */
                              /*             |                              */
      case '{': return   61;  /*  ASCII 123  |                              */
      case '|': return   62;  /*  ASCII 124  |                              */
      case '}': return   63;  /*  ASCII 125  |                              */
      case '~': return   64;  /*  ASCII 126 /                               */
                                  
      case 'a': return   65;  /*  ASCII  97 \                               */
      case 'A': return   66;  /*  ASCII  65  |                              */
      case 'b': return   67;  /*  ASCII  98  |                              */
      case 'B': return   68;  /*  ASCII  66  |                              */
      case 'c': return   69;  /*  ASCII  99  |                              */
      case 'C': return   70;  /*  ASCII  67  |                              */
      case 'd': return   71;  /*  ASCII 100  |                              */
      case 'D': return   72;  /*  ASCII  68  |                              */
      case 'e': return   73;  /*  ASCII 101  |                              */
      case 'E': return   74;  /*  ASCII  69  |                              */
      case 'f': return   75;  /*  ASCII 102  |                              */
      case 'F': return   76;  /*  ASCII  70  |                              */
      case 'g': return   77;  /*  ASCII 103  |                              */
      case 'G': return   78;  /*  ASCII  71  |                              */
      case 'h': return   79;  /*  ASCII 104  |                              */
      case 'H': return   80;  /*  ASCII  72  |                              */
      case 'i': return   81;  /*  ASCII 105  |                              */
      case 'I': return   82;  /*  ASCII  73  |                              */
      case 'j': return   83;  /*  ASCII 106  |                              */
      case 'J': return   84;  /*  ASCII  74  |                              */
      case 'k': return   85;  /*  ASCII 107  |                              */
      case 'K': return   86;  /*  ASCII  75  |                              */
      case 'l': return   87;  /*  ASCII 108  |                              */
      case 'L': return   88;  /*  ASCII  76  |                              */
      case 'm': return   89;  /*  ASCII 109  |                              */
      case 'M': return   90;  /*  ASCII  77  |                              */
      case 'n': return   91;  /*  ASCII 110  |                              */
      case 'N': return   92;  /*  ASCII  78  |                              */
      case 'o': return   93;  /*  ASCII 111  |                              */
      case 'O': return   94;  /*  ASCII  79  |                              */
      case 'p': return   95;  /*  ASCII 112   \                             */
      case 'P': return   96;  /*  ASCII  80     > Buchstaben: a, A, ...     */
      case 'q': return   97;  /*  ASCII 113   /                             */
      case 'Q': return   98;  /*  ASCII  81  |                              */
      case 'r': return   99;  /*  ASCII 114  |                              */
      case 'R': return  100;  /*  ASCII  82  |                              */
      case 's': return  101;  /*  ASCII 115  |                              */
      case 'S': return  102;  /*  ASCII  83  |                              */
      case 't': return  103;  /*  ASCII 116  |                              */
      case 'T': return  104;  /*  ASCII  84  |                              */
      case 'u': return  105;  /*  ASCII 117  |                              */
      case 'U': return  106;  /*  ASCII  85  |                              */
      case 'v': return  107;  /*  ASCII 118  |                              */
      case 'V': return  108;  /*  ASCII  86  |                              */
      case 'w': return  109;  /*  ASCII 119  |                              */
      case 'W': return  110;  /*  ASCII  87  |                              */
      case 'x': return  111;  /*  ASCII 120  |                              */
      case 'X': return  112;  /*  ASCII  88  |                              */
      case 'y': return  113;  /*  ASCII 121  |                              */
      case 'Y': return  114;  /*  ASCII  89  |                              */
      case 'z': return  115;  /*  ASCII 122  |                              */
      case 'Z': return  116;  /*  ASCII  90 /                               */

      case '0': return  117;  /*  ASCII  48 \                               */
      case '1': return  118;  /*  ASCII  49  |                              */
      case '2': return  119;  /*  ASCII  50  |                              */
      case '3': return  120;  /*  ASCII  51  |                              */
      case '4': return  121;  /*  ASCII  52   \                             */
      case '5': return  122;  /*  ASCII  53     > Zahlen                    */
      case '6': return  123;  /*  ASCII  54   /                             */
      case '7': return  124;  /*  ASCII  55  |                              */
      case '8': return  125;  /*  ASCII  56  |                              */
      case '9': return  126;  /*  ASCII  57 /                               */

      default : return   zz;
  }
}


/*--------------------------------------------------------------------------*/

      /* Dies ist die eigentliche Sortier-Funktion.          */
      /* Sie zerteilt das Sortierproblem in viele triviale:  */
      /* Wie sind  z w e i  Eintr"age zu sortieren?          */

void QQsort(void *v[], int left, int right, int (*comp)(void *, void *))
{
  int i, last;
  void swap (void *v[], int, int);
  if (left >= right) return;
  swap(v, left, (left + right)/2);
  last = left;
  for (i = left + 1; i <= right; i++)
       if ((*comp)(v[i], v[left]) < 0)
           swap(v, ++last, i);
  swap(v, left, last);
  QQsort(v, left, last - 1, comp);
  QQsort(v, last + 1, right, comp);
}


/*--------------------------------------------------------------------------*/

      /* Wenn zwei Eintr"age in der falschen Reihenfolge     */
      /* sortiert sind, werden die Zeiger, die auf sie       */
      /* weisen, ausgetauscht.                               */

void swap(void *v[], int i, int j)
{
   void *temp;
   temp = v[i];
   v[i] = v[j];
   v[j] = temp;
}


/*--------------------------------------------------------------------------*/

      /* Hier werden Leerzeichen, Backslash, geschweifte     */
      /* Klammern, Tilden und Minus-Zeichen gleich "uber-    */
      /* gangen, um \"a und "a, \ss und \ss{}, a~b und a b,  */
      /* Info und In\-fo gleich gewichtet zu bekommen.       */
      /* \ss und "s werden als ss, "[x] und \"[x] als [x]e   */
      /* sortiert, aber original ausgedruckt.                */
      /* `[x], ^[x] und '[x] werden als [x] gewichtet.       */

      /* \newumlaut muss nicht beachtet werden, denn mit     */
      /* dem GBIBARTS.STY-Makro \@sIcHerUnG konnte es unter- */
      /* dr"uckt werden, dass dies in [File].BAR erscheint.  */

      /* Da in [File].BAR die Umlaute und "s immer expan-    */
      /* diert sind, muss auf die Ein-Zeichen-Umlaute, wie   */
      /* sie im erweiterten ASCII-Code definiert sind,       */
      /* keine R"ucksicht genommen werden.                   */

int vergleich(const void *s1,const void *s2)
{
        int a1=1, b1=1, a2=1, b2=1, a3=1, b3=1, a=1, b=1;
        
        while (a == b) 
              {
                while (overjump(*((char *)s1)) > 0) s1 = (char *)s1 + 1;
                while (overjump(*((char *)s2)) > 0) s2 = (char *)s2 + 1;

                              a3 = a2;                b3 = b2;
                              a2 = a1;                b2 = b1;
                a1 = bewerten(*((char *)s1));   b1 = bewerten(*((char *)s2));
                                s1 = (char *)s1 + 1;                   s2 = (char *)s2 + 1;

                              a  = a2 ;               b  = b2 ;
                
                if (                               /* Abpr"ufen, ob ein Um- */
                        ( a2 == bewerten('\"') )   /*    laut vorhanden ist */
                   ) 
                   {       a  = a1;            }
                
                if (
                        ( b2 == bewerten('\"') )
                   )
                   {       b  = b1;            }
                
                if (   
                        ( a3 == bewerten('\"') )
                     && ( a2 != bewerten('s')  )   /* "s ist kein Umlaut */
                   ) 
                   {       a  = bewerten('e'); }   /* "a  als  ae  etc.  */
                
                if (    
                        ( b3 == bewerten('\"') )
                     && ( b2 != bewerten('s')  )
                   ) 
                   {       b  = bewerten('e'); }
              
              }
        
        if (a   >   b ) {return  1;}
        else            {return -1;}
}


/*--------------------------------------------------------------------------*/


int getline(char s[], int lim) 
{
        int c,i;
        i=0;
        while(--lim > 0 && (c=fgetc(infile)) != EOF && c != '\n')
                s[i++] = c;
        if (c == '\n') s[i++] = c;
        s[i] = '\0';
        return i;
}


/*--------------------------------------------------------------------------*/

      /* Definiert f"ur vergleich, welche Zeichen "uber- */
      /* gangen werden sollen in der Frage, ob zwei      */
      /* Zeilen gleich sind.                             */

int overjump(int a)
{
                   if (    a == ' '       /* Leerzeichen */
                        || a == '\\'      /* Backslash   */
                        || a == '{'       /* {           */
                        || a == '}'       /* }           */
                        || a == '~'       /* Tilde       */
                        || a == '-'       /* \-          */
                        || a == '\''      /* \'          */
                        || a == '`'       /* \`          */
                        || a == '^'       /* \^          */
                        || a == '@'       /* {...}@      */
                      )
                    return  1;
               else return -1;
}


/*--------------------------------------------------------------------------*/

      /* Definiert f"ur TeXvergleich, welche Zeichen     */
      /* nicht gewichtet werden sollen in der Frage, ob  */
      /* zwei Zeilen gleich sind.                        */

int vergljump(int a)
{
                   if (    a == ' '       /* Leerzeichen */
                        || a == '\\'      /* Backslash   */
                        || a == '~'       /* Tilde       */
                        || a == '-'       /* \-          */
                        || a == '\''      /* \'          */
                        || a == '`'       /* \`          */
                        || a == '^'       /* \^          */
                        || a == '@'       /* {...}@      */
                      )
                    return  1;
               else return -1;
}


/*--------------------------------------------------------------------------*/

      /* {...}@ wird aus der Zeile gel"oscht.            */

void killsort(char *lineptr[], int nlines)
{
        char a[MAXLEN], b[MAXLEN];
        int i, j, m;

        while(nlines-- > 0)
        { strcpy(a, *lineptr);
          i = 0; j = 0; m = 0;
          while(a[i]!='\0')
          {
            if (a[i] == '{') m = j;
            if (a[i] == '}' && a[i+1] == '@' && a[i+2] != '\0')
               { j = m; ++i; ++i; }
            b[j] = a[i];
            ++i; ++j;
          }
          b[j++] = '\0';
          free(*lineptr);
          *lineptr = (char *)malloc(j*sizeof(char));
          strcpy(*lineptr++, b);
        }
}


/*--------------------------------------------------------------------------*/


void writelines(char *lineptr[], int nlines)
{
        while(nlines-- > 0)
          fprintf(outfile,"%s\n", *lineptr++);
}


/*--------------------------------------------------------------------------*/

      /* Hier wird f"ur doubleauthor bestimmt, ob Name   */
      /* und Vorname zweier Zeilen gleich sind.          */

int TeXvergleich(const void *s1,const void *s2)
{    int i = 0, j = 0, k = 0, l = 0;

   while(1)
   {
                while (vergljump(*((char *)s1)) > 0) ++s1;
                while (vergljump(*((char *)s2)) > 0) ++s2;

          if (*(char *)s1 == '{') ++i;
     else if (*(char *)s1 == '}') { --i; if (i==LEVE_OPEN) ++j; }
          if (*(char *)s1 == '{') ++k;
     else if (*(char *)s1 == '}') { --k; if (k==LEVE_OPEN) ++l; }

     if (*(char *)s1 == '\0' && *(char *)s2 == '\0') return 0;
     else if (j == 2 && l == 2) return 2;

          if (*(char *)s1 == '\0') return 1;
     else if (*(char *)s2 == '\0') return -1;

     if (*(char *)s1 == *(char *)s2) { ++s1; ++s2; }
     else if (*(char *)s1 > *(char *)s2) return 1;
     else if (*(char *)s1 < *(char *)s2) return -1;

   }
}


/*--------------------------------------------------------------------------*/

      /* Autoren, die mehrfach vorkommen, werden einmal  */
      /* genannt, und dann durch KILLNAME ersetzt.       */

void doubleauthor(char *lineptr[], int nlines)
{
        int j, i, h;
        char a[MAXLEN];

        while(--nlines > 0)
        { if (TeXvergleich(lineptr[nlines], lineptr[nlines-1]) == 2)
          {
                 strcpy(a, lineptr[nlines]);
            j = 0; h = 0; i = 0;
                   while(1)
                   {      if(a[j]=='{') ++i;
                     else if(a[j]=='}') { --i; if (i==LEVE_OPEN) ++h; }
                     ++j;
                     if (h==2 || a[j]=='\0') break;
                   }
            if (h==2)
            { i = 0;
              while(a[i]!='\0' && a[i]!='{') ++i;
              a[i+1]='\0';           /* '{' soll bleiben! */
              strcat(a, KILLNAME);
              i = i + strlen(KILLNAME);
              while(a[j]!='\0') a[++i] = a[j++];
              a[++i] = '\0';
                  free(lineptr[nlines]);
                       lineptr[nlines] = (char *)malloc((++i)*sizeof(char));
                strcpy(lineptr[nlines], a);
            }
          }
        }
}


/*--------------------------------------------------------------------------*/

      /* Zeilen, die mit INDEXNAME beginnen, werden      */
      /* nicht aufgenommen.                              */

int readlines(char *Lineptr[], int maxlines)
{
        int len, nlines;
        char *p, line[MAXLEN];
        nlines = 0;
        while ((len = getline(line,MAXLEN)) > 0) 
           if (strncmp(line, INDEXNAME, INDEXNAMELEN)==0) continue;
           else if (nlines >= maxlines ||
                         (p = (char *)malloc(len*sizeof(char))) == NULL)
                        return -1;
                else {
                        line[len-1] = '\0';
                        strcpy(p, line);
                        Lineptr[nlines++] = p;
                }
        return nlines;
}


/*--------------------------------------------------------------------------*/

      /* Kehrt mit -argc+1 zur"uck, wenn Option da ist.  */

int single_option(int argc, char *argv[], char a[])
{
  
  int l = 1, c;
  int _found = 0;

  while (l < argc)
  {
    if (strcmp(argv[l], a) == 0)
    { _found = 1;
         /* free(argv[l]); */
         --argc;
         c = l;
         while (c < argc)
         { argv[c] = argv[c+1];
           ++c;
         }
    --l;
    }
    ++l;
  }
  if (_found == 1) return -argc;
  else             return  argc;
}


/*--------------------------------------------------------------------------*/

      /* Erkennt unbekannte Optionen.                    */

int surp_option(int argc, char *argv[])
{
  
        char p[]=" %% ";
  int l = 1, c;
  char a[2];

  while (l < argc)
  {
    strncpy(a, argv[l], 1);
    if (a[0] == '-')
    { printf("\n%s *** BREAK: Get unknown option \"%s\"!\n", p, argv[l]);
         /* free(argv[l]); */
         --argc;
         c = l;
         while (c < argc)
         { argv[c] = argv[c+1];
           ++c;
         }
    --l;
    }
    ++l;
  }
  return argc;
}


/*--------------------------------------------------------------------------*/


int main(int argc, char *argv[])
{
        char p[]=" %% ";
        int nlines=0;
        int i;
        int kill = 0;

   argc = single_option(argc, argv, "-K");
   if (argc < 0)
   { argc = -argc;
     kill = 1;
   }

   argc = single_option(argc, argv, "-k");
   if (argc < 0)
   { argc = -argc;
     kill = 1;
   }

         if (argc == 1) {
                          info();
                          exit(1);
                        }

         if (argc != surp_option(argc, argv)) exit(1);

         if (argc == 2)
         {
                infile = fopen(argv[1],"r");
                outfile = stdout;
         }
         else if (argc == 3)
         {
                if (strcmp(argv[1], argv[2]) == 0)
                {
                  printf("\n%s Quelle und Ziel d\"urfen nicht identisch sein.\n", p);
                  exit(1);
                }
                infile = fopen(argv[1],"r");
                outfile = fopen(argv[2],"w");
        }
        else {
                printf("\n%s Zu viele Argumente.\n", p);
                exit(1);
        }

        if (infile == NULL)
        {
                printf("\n%s \"%s\" kann nicht gelesen werden.\n", p, argv[1]);
                exit(1);
        }
        if (outfile == NULL)
        {
                printf("\n%s Betriebsystem verhindert Ausgabe in \"%s\".\n", p, argv[2]);
                printf("%s Gegebenenfalls Schutz gegen \"Uberschreiben entfernen.\n", p);
                exit(1);
        }


        if ((nlines = readlines(lineptr, MAXLINES)) >= 0) 
        {
                printf("\n%s  GBIBSORT sortiert \"%s\" alphabetisch", p, argv[1]);
                QQsort((void **) lineptr, 0, nlines-1,
                        (int (*)(void*,void*))(vergleich));
                                   killsort(lineptr, nlines);
                if (kill == 1)
                { printf("\n%s  --- wobei mehrfach genannte Autoren bei wiederholter Nennung\n", p, KILLNAME);
                  printf("%s      durch %s ersetzt werden", p, KILLNAME);
                               doubleauthor(lineptr, nlines);
                }
                  printf(".\n");
                              printf("%s   Ausgabe ", p);
                if (argc == 2) printf("auf die Standard-Ausgabe.\n");
                           else printf("in \"%s\".\n", argv[2]);
                                 writelines(lineptr, nlines);
                return 0;
        }
        else 
        {
                printf("Fehler: Datei zu gross.\n");
                return 1;
        }

}


/*--------------------------------------------------------------------------*/
/*--------------------------------------------------------------------------*/
