#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>

#define MAX_STRINGS	300	/* Die Maximalzahl verschiedener Woerter  */
#define MAX_WORDLENGTH	31	/* Die maximale Wortlaenge (+1 fuer '\0') */

void usage(char *name);
int parse_ops(int argc, char *argv[], int *flag_c, int *flag_w, int *flag_l, int *flag_s);

void read_and_sort_words(void);
void read_and_count(int flag_c, int flag_w, int flag_l);
void swap(char **a, char **b);
void string_sort(int cnt, char *strings[]);

int string_cmp(char *s1, char *s2);
char *next_word(char *word);
int insert_word(char *string, char *all_words[], int no_of_words);

/*
 * Die Main-Funktion...
 */
int main(int argc, char *argv[])
{
  int flag_c, flag_w, flag_l, flag_s;	/* flags fuer die Optionen */

  if(parse_ops(argc, argv, &flag_c, &flag_w, &flag_l, &flag_s))
    usage(argv[0]);

  if(flag_s)
    read_and_sort_words();
  else
    read_and_count(flag_c, flag_w, flag_l);

  return 0;
}

/*
 * Usage-Meldung ausgeben und Programm beenden.
 */
void usage(char *name)
{
  fprintf(stderr, "usage: %s [-s] OR\n", name);
  fprintf(stderr, "       %s [-c] [-w] [-l]\n", name);
  exit(-1);
}

/*
 * Parsen der Kommandozeilenoptionen.
 * Liefert 0, wenn alles ok,
 *	  -1 im Fehlerfall
 */
int parse_ops(int argc, char *argv[], int *flag_c, int *flag_w, int *flag_l, int *flag_s)
{
  int i;

  *flag_c = *flag_w = *flag_l = *flag_s = 0;

  for(i=1; i<argc; i++)
    {
      if(argv[i][0]!='-')
	{
	  fprintf(stderr, "Option %s beginnt nicht mit '-'\n", argv[i]);
	  return -1;
	}
      switch(argv[i][1])
	{
	case 'c' : *flag_c = 1; break;
	case 'w' : *flag_w = 1; break;
	case 'l' : *flag_l = 1; break;
	case 's' : *flag_s = 1; break;
	default  : fprintf(stderr,"Unbekannte Option: %s\n", argv[i]); return -1;
	}
      if(argv[i][2]!='\0')
	{
	  fprintf(stderr, "Option %s besteht aus >2 Buchstaben\n", argv[i]);
	  return -1;
	}
    }

  /* Wenn -s gesetzt ist darf keine andere Option gesetzt sein */
  if(*flag_s && (*flag_c || *flag_w || *flag_l))
    {
      fprintf(stderr,"Unzulaessige Optionenkombination\n");
      return -1;
    }

  /* Wenn keine Option vorhanden ist sollen Woerter gezaehlt werden */
  if(!(*flag_s || *flag_c || *flag_w || *flag_l))
    *flag_w = 1;

  /* Alles ok. */
  return 0;
}

/*
 * Zeichenweise von stdin lesen und dabei, je nach gesetzten
 * Optionen, Zeichen, Woerter und Zeilen zaehlen.
 */
void read_and_count(int flag_c, int flag_w, int flag_l)
{
  int flag = 1;
  int chars = 0;
  int words = 0;
  int lines = 0;
  int ch;

  /* Text zeichenweise lesen und dabei Zeichen, etc. zaehlen */
  while(((ch = getchar()) != EOF))
    {
      chars++;			/* Jedes Zeichen wird gezaehlt.  */
      if(!isspace(ch) && flag)	/* Wenn "Whitespace" gefunden    */
	{			/* wird, ist ein Wort zuende;    */
	  words++;		/* 'flag' sorgt dafuer, dass auf-*/
	  flag = 0;		/* einanderfolgende Spaces nicht */
	}			/* als mehrere Woerter zaehlen.  */
      if(isspace(ch))
	flag = 1;
      if(ch=='\n')		/* Zeilenzaehler erhoehen bei '\n' */
	lines++;
    }

  if(flag_c)
    printf("Anzahl der eingelesenen Zeichen: %d\n", chars);
  if(flag_w)
    printf("Anzahl der eingelesenen Worte:   %d\n", words);
  if(flag_l)
    printf("Anzahl der eingelesenen Zeilen:  %d\n", lines);
}

/*
 * Vom Benutzer Woerter einlesen und sortieren.
 */
void read_and_sort_words(void)
{
  int  i, no_of_words=0;	/* Die Anzahl bisher erfasster Worte       */
  char *all_words[MAX_STRINGS];	/* Enthaelt Zeiger auf die erfassten Worte */
  char words[MAX_STRINGS][MAX_WORDLENGTH]; /* Platz zum Abspeichern der Woerter */
  char *wp;			/* Hilfszeiger */

  /* Woerter einlesen; dabei verhindern, dass Woerter doppelt auftreten; */
  /* Zeiger in all_words auf die eingelesenen Woerter setzen; */
  while(no_of_words<MAX_STRINGS && (wp = next_word(words[no_of_words])) != NULL)
    no_of_words += insert_word(wp, all_words, no_of_words);

  /* Woerter sortieren (ueber Zeigerarray all_words) */
  string_sort(no_of_words, all_words);
   
  /* und alle sortiert ausgeben */
  printf("\nVorkommende Woerter, sortiert:\n");
  for(i=0;i<no_of_words;i++)
    printf("%s\n",all_words[i]);
}

/*
 * Vertauschen zweier Zeiger auf Zeichenketten 
 */
void swap(char **a, char **b)
{
  char *tmp = *a;
  *a = *b;
  *b = tmp;
}
 
/*
 * Einfacher Sortieralgorithmus: Bubblesort...
 */
void string_sort(int cnt, char *strings[])
{
  int i, j, changed;
  
  if(cnt==1)
    return;

  /* Sortieren kann abgebrochen werden, wenn in einem
     Durchlauf keine Vertauschungen erfolgt sind. */
  for(j=1; changed; j++)
    {
      changed = 0;
      for(i=0;i<cnt-j;i++)
        if(string_cmp(strings[i],strings[i+1])>0)
	  {
	    swap(&strings[i], &strings[i+1]);
	    changed=1;
	  }
    }
}



/***************************************************************/
/* Hier folgen die in den Tafeluebungen vorgestellte Funktionen */
/***************************************************************/



/*
 * Vergleich zweier Zeichenketten (gleiche Funktionalitaet wie
 * die Bibliotheksfunktion strcmp(); siehe "man strcmp")
 * liefert: 0, wenn die Zeichenketten identisch sind
 *          1, wenn die erst Zeichenkette lexikographisch groesser
 *             ist als die zweite
 *         -1, wenn die erst Zeichenkette lexikographisch kleiner
 *             ist als die zweite
 */
int string_cmp(char *s1, char *s2)
{
  while(*s1 && *s2 && (*s1==*s2))
    {
      s1++;
      s2++;
    }
  if(*s1 > *s2)
    return 1;
  if(*s1 < *s2)
    return -1;
  return 0;
}

/*
 * Funktion zum Einlesen des naechsten Wortes.
 * Woerter laenger als MAX_WORDLENGTH werden abgeschnitten!
 * liefert: Zeiger auf eingelesenes Wort 
 *          NULL, falls Eingabeende erreicht wurde
 */
char *next_word(char *word)
{
  char *wp;
  int ch, count;

  /* Alles ueberlesen bis zum naechsten alphanumerischen Zeichen */
  while(((ch = getchar()) != EOF) && !isalnum(ch));

  /* Hoppla, Eingabe ist leer */
  if(ch == EOF) return NULL;
   
  /* alles einlesen bis zum naechsten nicht alphanumerischen Zeichen
     (Bindestriche stellen eine Ausnahme dar) */
  wp    = word;
  count = 0;
  do
    {
      *wp++ = ch;

      /* Testen, ob die maximale Wortlaenge nicht ueberschritten wird */
      count++;
      if(count == MAX_WORDLENGTH-1)
	break;

      ch    = getchar();
    }while(isalnum(ch) || (ch=='-'));

  *wp= '\0';
  return word;
}

/*
 * Einfuegen eines Wortes in die Liste erfasster Worte
 * liefert:  0 falls Wort bereits bekannt war
 *           1 falls es sich um ein neues Wort handelt
 */
int insert_word(char *string, char *all_words[], int no_of_words)
{
  int i;

  /* Testen, ob das Wort bereits erfasst wurde */
  for(i=0;i<no_of_words;i++)
    if(string_cmp(all_words[i], string)==0)
      return 0;

  /* Test, ob noch fuer ein neues Wort Platz ist */
  if(no_of_words==MAX_STRINGS)
    {
      fprintf(stderr,"Fehler: Text enthaelt zu viele verschiedene Woerter\n");
      exit(-1);
    }

  /* Neues Wort erfassen */
  all_words[no_of_words] = string;
  return 1;
}

