// OSDP.CPP : OpenSource-DemoProgramm zur allegro-Klassenbibliothek
// 2011-03-14   Kann als Muster fuer eigene Anwendungen dienen
// 2012-06-19   Das Programm zeigt die Anwendung aller Basisklassen
//              Diesem allgem. Muster entsprechen auch acon und a99!

// Dieses Modell koennte z.B. Grundlage fuer einfache Webservices sein

// -----------------------------------------------------------------------
// Erweiterungen: Sinnvolle Stellen sind kommentiert mit  // ++++ hinweis
// -----------------------------------------------------------------------

char Version[24]="osdp V32.6";

// ********************
// Allgemeiner Vorspann
// ********************

// #include "allegro.hpp"
// enthaelt alle globalen Daten, ist aber in jedem der folgenden #includes 
// schon mit drin und wird daher sowieso geladen
// Darin sind Variablen wie Aerror und Alanguage definiert samt defaults


// Die 5 Basisklassen:
// Die Klassenfunktionen (Methoden) sind in  konfig.cpp  usw.

                       // Praefix der Methoden
#include "konfig.hpp"  // Ko... Konfiguration - wird immer gebraucht
                       //         d.h. Funktionsnamen beginnen mit Ko
#include "record.hpp"  // Datensatz  (kein Praefix)

#include "index.hpp"   // In... Indexdatei

#include "abase.hpp"   // Ab... DATENBANK
                       // Aw...   Schreibfunktionen (abasew.cpp)

#include "exet.hpp"    // Ex... Exportparameter  (deaktivieren, wenn nicht benoetigt)
                       // E2.. bzw. E3  falls Funktion in exet2.cpp / exet3.cpp

// ************************
// eigene globale Variablen hier, falls man welche braucht:
// ++++ nach Bedarf ergaenzen
// ************************
// Koennten auch in eine .h-Datei ausgelagert werden.
// Hier werden sie durch Aufruf-Optionen belegt, s.u. in main()

    char *arg;         // Hilfsadresse f. Argumentauswertung
    char konfig[32];   // Name der CFG-Datei (z.B. a)
    char dbDir[256];   // Db.Pfad (z.B. c:\allegro\demo2\ )
    char dbNam[32];    // Db Name (z.B. cat)
    char expFile[256]; // Name der Ausgabedatei f. Export
    char expPara[32];  // Name der Exportparameter
    char regNr[10];    // Registernummer f. find-Zugriff, z.B. "4"

// Objekte (Instanzen von Klassen aus ac15)
    KONFIG *oKfg;      // Konfiguration
    RECORD *oSatz;     // Datensatz
    EXET   *oExp;      // Export-Objekt (zu expPara)
                       // wird bei Bedarf angelegt
    ABASE  *oBank;     // Zeiger f.d. Datenbankobjekt
	                   // In einem Prog. kann es 2 oder mehr Datenbanken geben!

    FILE *outFile;     // Exportdatei

	char eingZeile[256];   // fuer je 1 "Zeile" input aus stdin (wird mit <dateiname eingespeist)

// **********************
// Funktionsdeklarationen und kleinere Hilfsfunktionen ++++
// **********************

void HauptFunktion();  // Deklaration der Fkt. HauptFunktion(), s.u.


void spacRem(char *k)  // whitespace am Ende von string k beseitigen 
{
	int l=strlen(k)-1;
	while(l>-1 && isspace(k[l])) k[l--]=0;
}

char *argFind(char *bef)  // In der Zeile bef den Anfang des Arguments finden
{                         // bef = befehlswort  argument
	char *a;
	a=bef;
    while(*a && *a!=' ') ++a;  // Zeiger auf erstes Spatium setzen
	while(*a==' ') ++a;        // dann auf erstes Zeichen hinter Spatien (u.U. 0)
	return a;  // Zeiger zeigt jetzt auf das erste Argumentzeichen (oder *a==0)
}

// Funktion zur Ausgabe eines Zeichens beim Export
// (evtl. macht man sich dafuer eine eigene Funktion!)
// d.h. hier ist unmittelbar vor dem Output das Zeichen noch pruefbar
// Wird genutzt vom Objekt oExp, s. weiter unten

int outPut(int a)  // Ausgabefunktion f. ein einz. Zeichen
	{
	  return fputc(a,outFile);  // Zeichen in outFile schreiben
	}

void expOpen(char *expFile)
{
  if(outFile) fclose(outFile); // schliessen, falls schon offen

  if(*expFile) outFile=fopen(expFile,"w");   // Datei f, Export oeffnen 
  else outFile=stdout;  // stdout, wenn -E fehlt

  if(!outFile)
	{
           printf("\nProblem mit Ausgabedatei %s\n");    // Datei geht nicht
           exit(17);
	} 
}

void expCreate(char *expPara)
{
  if(oExp) delete oExp;
  if(*expPara) // eine Option -e... war gegeben
  {
    oExp = new EXET(expPara, oKfg, 0, dbDir);  // Exp.Param  expPara.apr  laden
    if(*Aerror)
	{
           printf("\nProblem mit Export-Objekt %s: %s\n",expPara,Aerror);    // ein Fehler ist dabei passiert
           exit(18);
	} 
    oExp->ExOutf(outPut);  // die Zeichen-Ausgabefunktion setzen
  }
      // zum Ausgeben soll outPut() dienen  (s.o.)
	  //    (default ist putchar, gesetzt in exet2.cpp)
  	  // d.i. Ausgabe eines Zeichens auf "stdout"  (oben definiert)
	  // Klasse EXET uebergibt jedes Zeichen an diese Funktion
}

void help()
  {
    printf("Programm osdp, Version %s\n",Version);
	printf("Aufruf: osdp -dDatenpfad -rRegisternr -bDbName -kKonfig -eExportParam -EAusgabedatei <steuerdatei\n");
    printf("z.B.:   osdp -dc:\\allegro\\demo2 -r1 -bcat -ka <xyz.bef\n");
    printf("Nur  <steuerdatei  ist notwendig, wenn die Optionen alle darin stehen\n");
    exit(10);
  }



// *********************
// ALLGEM. STARTFUNKTION
// *********************

// In jedem Fall muss es  main() geben, damit beginnt der Programmlauf
// Start erfolgt z.B. mit Aufruf:   osdp -dc:\allegro\demo2 -ka -bcat -s...


int main( int argc, char *argv[] )
{

  ARGC = argc;  // in allegro.hpp global definiert
  ARGV = argv;	// zur Auswertung durch SyGetArg()
 
  SyInit();  // system.cpp in ac15, einige systemische Variablen und Grundfunktionen
             // z.B.  SyGetArg(), SyOpen() / UIF-Speicherbereich anlegen
             // Deklarationen in  allegro.hpp

// Evtl. hier eine eigene INI-Datei o.ae. auswerten


// Dann Aufruf- oder INI-Argumente vorbearbeiten

  if(argc==2 && argv[1][1]=='h')  // Aufruf erfolgte ohne Argumente
     help();

// Defaults setzen   ++++ eigene Werte
  strcpy(konfig,"a");
  strcpy(dbNam,"cat");
  strcpy(dbDir,"c:\\allegro\\demo2");
  strcpy(expPara,"");  // Kein Export, wenn kein -e gegeben!
  strcpy(expFile,"osdp.adt");
  strcpy(regNr,"9");


// Bereich OPTIONS aus stdin lesen

  char *I; I="x";   // Wird 0, wenn stdin zu Ende


  *eingZeile=0;
// jeweils eine Zeile in  eingZeile  einlesen, bis eine gueltige Zeile kommt
  while(I && (!*eingZeile || *eingZeile==' ')) { I=fgets(eingZeile,255,stdin); spacRem(eingZeile); }
  if(!I) help();
// Wenn OPTIONS, dann muss das die erste gueltige Zeile sein
  if(*eingZeile=='O')  // Es gibt OPTIONS
  {
	while(*eingZeile!='C')  // Lesen, bis  eingZeile  mit C beginnt
    {
      if(*eingZeile=='-')
      {
        switch(eingZeile[1])
        {
// ++++ Eigene Werte ergaenzen
          case 'k':
            strcpy(konfig,arg);
            break;
          case 'b':
			arg[4]=0; // mehr als 4 soll nicht sein
            strcpy(dbNam,arg);
            break;
          case 'd':
            strcpy(dbDir,arg);
            break;
          case 'E':
            strcpy(expFile,arg);
            break;
          case 'e':
			arg[31]=0; // kuerzen, zur Sicerheit
            strcpy(expPara,arg);
            break;
          case 'r':
			arg[1]=0;  // auf 1 Byte kuerzen
            strcpy(regNr,arg);
			break;
        }
      }
      I=fgets(eingZeile,255,stdin);
      if(!I) break; // nichts mehr im stdin
      arg=argFind(eingZeile+1); spacRem(arg);
    }
  }
// OPTONS-Abschnitt ist beendet
// gibt es COMMANDS ?
  if(*eingZeile!='C')  // Neinm - dann passiert nix
  {
	  printf("%s, Keine Befehle - Abbruch\n(Abschnitt 'Commands' fehlt'\n",eingZeile);
      exit(30);
  }

// **********************************
// Aber zuerst:
// AufrufOptionen  -k, -d, -b, -e, -E, -r   auswerten.
//  Werte überschreiben diejenigen aus der Datei eingZeile
//  SyGetArg() [in system.cpp] liefert 0, wenn Arg. nicht vorh.)
// z.B. so aufrufen:
//    osdp -dc:\allegro\demo2 -r1 -staube -bcat -ka -ee-w -Eoutput.adt <inputfile
//    bedeutet: im Reg. 1 nach "taube?" suchen, Datenbank c:\allegro\demo2\cat

// ++++ Sonstige, eigene Optionen in gleicher Weise einfuegen
  
  arg = SyGetArg('k',1);  // -k Konfiguration (default: "a")
  if(arg) strcpy(konfig,arg);

  arg = SyGetArg('d',1);  // -d Datenordner (def.: demo2
  if(arg) strcpy(dbDir,arg); 

// slash an dbDir anhaengen, wenn er hinten fehlt
  if(dbDir[strlen(dbDir)-1]!=*fsep) strcat(dbDir,fsep); // fsep in allegro.hpp

  arg = SyGetArg('b',1);  // -b Datenbank-Name (default: "cat")
  if(arg) { arg[4]=0; strcpy(dbNam,arg); }

  arg = SyGetArg('e',1);  // -e Exportparameter (z.B. "e-w")
  if(arg) strcpy(expPara,arg);
// Wenn -e fehlt, dann kein Export!

  arg = SyGetArg('E',1);  // -E Exportdatei (z.B. "output.dat")
  if(arg) strcpy(expFile,arg);

  arg = SyGetArg('r',1);  // -r Registernummer, def. "1"
  if(arg) { arg[1]=0; strcpy(regNr,arg); }

// ********************
// UIF-Dateien  uif0ger  einlesen

  if (!uifRead('0',dbDir))  // 's', falls man die uif von acon nutzen will
                            // z.B. Fehlermeld. bei Einlesen der CFG oder Param
  {
	  printf("Datei uif0ger nicht gefunden\n");
	  exit(12);  // allgemeine uif-Texte: uif0ger,
  }

//	if (!uifRead('z',dbDir)) exit(13);  // eigene programmspezifische uif-Texte, z.B. uifzger
//  (in allegro.hpp ist Alanguage auf "ger" gesetzt)

  HauptFunktion();  // Hauptfunktion

  return 1;

}  // Ende  main()


// *************************************
// HAUPTFUNKTION des Anwendungsprogramms  (Deklaration siehe oben)
// *************************************
// Liest die COMMANDS aus stdin ein und fuehrt sie aus
// d.h. stdin ist weiterhin geöffnet

void HauptFunktion() 
{

// Es folgen einige Demo-Beispiele fuer den Umgang mit der Klassenbibliothek

// ****************
//	VORBEREITUNGEN
// ****************
//  Objekte anlegen; Namen beginnen mit o (nur zur besseren Erkennbarkeit, ansonsten beliebig)

// eine Konfig wird immer gebraucht, gesucht soll sie zuerst auf dem dbDir werden (dann progDir)

	outFile=0;
    oExp=0;
    
	oKfg = new KONFIG(konfig,dbDir);
    if (*Aerror)
    {
        printf("\nFehler bei Konfig: %s\n",Aerror);    // Fehler in A.CFG
        exit(15);
    }
// die Konstruktoren der Klassen liefern Fehlermeldungen immer in Aerror ab


// Ein Datensatzobjekt wird angelegt
    oSatz = new RECORD(oKfg);
    if (*Aerror)
    {
        printf("\nProblem mit Datensatz-Objekt: %s\n",Aerror);    // ein Fehler ist dabei passiert
        exit(16);
    }


// ******************
// Export vorbereiten

   expCreate(expPara);  // Objekt  oExp  der Klasse EXET anlegen

   expOpen(expFile);    // Datei  outfile  oeffnen


// *******************************
// Datenbankobjekt  oBank  anlegen (d.h. gewuenschte Datenbank oeffnen)

    oBank = new ABASE(dbDir,dbNam, oKfg,3);
    if (*Aerror)
    {
        printf("Problem mit Datenbank %s auf %s: %s\n",dbNam,dbDir,Aerror);
        exit(19);
    }

// oBank verfuegt ueber alle Methoden von
//     abase.cpp (Ab...), abasew.cpp (Aw...), index.cpp (In...)  und exet?.cpp


// **********************
// VORBEREITUNGEN BEENDET
// **********************

// *************************************************************
// ********************   AKTIONSTEIL **************************
// *************************************************************

// VERARBEITUNG BEGINNT
// ********************
//   Beispiele fuer den Umgang mit Datensaetzen
	
// Datensatz suchen und laden

    char found[256];  // Hilfsvar. f. gefundene Indexzeile
    RECNR recn;       //   interne Satznummer (RECNR = long int)



// ***********************  AENDERUNGEN AM DATENSATZ

// Unser Satz-Objekt ist  oSatz.
// Die Datenfelder liegen in dem String-Array  oSatz->ga[i], i=0 .. oSatz->gri-1
// Manipulation der Datenfelder geht mit
// den Methoden Ins(), Del(), SrRp() und FSrRp() in record.cpp 

	while(fgets(eingZeile,255,stdin)!=0)  // stdin zeilenweise lesen (Datei mit <name uebergeben!)
	{
		spacRem(eingZeile);
          // Umcodierung der eingelesenen Zeile von ANSI nach ASCII mit der o-Tabelle
          // (sonst Umlaute falsch)
        oBank->E3Coding((CHAR *)eingZeile, 0, 1);  // 0: gesamte Laenge, 1: ANSI->ASCII

// Zeiger arg auf das Argument hinter dem Bef.Wort setzen, mit dem eingZeile beginnt:
		arg=argFind(eingZeile);
        switch(*eingZeile)   // Auswertung des Befehlsworts und Arguments
		{
// ++++ Eigene commands (neue Befehlswoerter) hier auswerten

        case 'f':   // find arg : Satz nur laden
        case 'F':   // Find arg : Satz laden UND sperren (incl. Schluesselberechnung)
		{
		  int k=0;
		  oSatz->Empty();
		  // suchbegriff  arg  im Reg. regNr suchen, Eintrag kann laenger sein (Modus 1)
  	      recn = oBank->InFindEnt(atoi(regNr), arg, found, 1);
          if (recn==0)
		  {
            printf("Eine mit >%s< beginnende Zeile gibt es nicht, sondern %s\n",arg,found);
// Es geht dann weiter, aber der Satz wird als neu behandelt wg. recn==0 und ist leer
		  }
          else
		  {
		  // Jetzt sind auch die Schlüssel errechnet

		   k=oBank->AbGet(recn,oSatz);  // Satz mit Nr. recn nach oSatz einlesen
// bei Erfolg ist k die Anzahl Felder des Satzes, sonst k==0 - Satz leer

		   if(k<1) { printf("Satz kann nicht geladen werden, %s\n",Aerror); break; }
// Schluessel berechnen
           if(*eingZeile=='F')
		      if(oSatz->Reserve()!=0) { printf("Satz kann nicht gesperrt werden, %s\n",Aerror); break; }
		  }
          if(k>0)
		  {
			 printf("Satznr / Dateinr / OffsetPos: %ld / %d / %ld\n",oSatz->rNr,oSatz->rFi,oSatz->rOf);
		  }
		   break;
		}

		case '#':   // Feld einordnen
		  oSatz->Ins((CHAR *)eingZeile);
		  break;

		case '_':   // _abc_xyz_ : Ersetzung im Satz
		case ',':   // ,abc,xyz, : Mit Komma, falls _ in abc vorkommt
          oSatz->SrRp((CHAR *)eingZeile,oSatz->ga[0],1,0,1);
		  break;
		case '*':   // *#nnn_ybc_xyz_ : Lokale Ersetzung in einem Feld
          oSatz->FSrRp((CHAR *)eingZeile,1);
		  break;

		case 'p':   // put
          // Wegschreiben des Satzes, dabei werden u.a. die Indexeintraege erledigt
          int k;
          if(*arg=='n') recn=oSatz->rNr=0;  // put new: Satz als neuen speichern
		  k=oBank->AwPut(oSatz);   // Satz speichern
          if(k==-1) printf("Speichern gescheitert - Kein Schreibrecht?\n");
          if(k==0)  printf("Speichern gescheitert - .TBL gesperrt?\n");
          if(*Aerror) printf("irgendwas hat nicht geklappt: %s\n",Aerror);
          if(k>0)
			  if(recn) printf("ok, Satz #%ld ist wieder gespeichert\n",oSatz->rNr); // Erfolgsmeldung
			  else printf("ok, Satz #%ld ist als Neusatz gespeichert\n",oSatz->rNr);
		  
		  break;

		case 'e':   // export: Satz ausgeben
          //   Geht nur, wenn Parameter geladen. Dann ist *expPara!=0
          if(*expPara)
		  {
			  //  oExp->Exp(oSatz);   // besser, wenn keine V14-Ersetzungen noetig!
			  oBank->AbExpV14(oSatz,oExp);
		  }
		  break;

		case 's':  // sleep, z.B. sleep 2 (2 Sek. pausieren)
		  SyPause(atoi(arg)*1000);
		  break;

        case 'n':  // new (Neuer Satz)
          oSatz->Empty(); // incl. Satznr. auf 0 setzen
		  recn=0;
          break;

        case '-':  // Eine Option -x  wird geaendert; nur -e, -E, -r
			{
              char o=eingZeile[1];
              arg=argFind(eingZeile+1);

			  switch(o)
			  {
			    case 'e':  // -e exportparamtetername
					{
					 strcpy(expPara,arg);
					 expCreate(expPara);
					}
					break;
			    case 'E':  // -E ausgabedateiname
                    strcpy(expFile,arg);
                    expOpen(expFile);   // outfile schliessen und wieder oeffnen mit neuem Namen
					break;
			    case 'r': // -r registernummer
 			        arg[1]=0;  // auf 1 Zeichen kuerzen = 1...9,:,;
                    strcpy(regNr,arg);
					break;
			    default: break;
			  }
			  break;
			  }

        case 'k':  // keys : Schluessel des Satzes berechnen und ausgeben

			printf("Schluessel des Satzes:\n");
			oBank->AbKeys(oSatz,0); // Schluessel berechnen, landen in oSatz->rKy[]
			k=0;
			while (k<oSatz->rKx)   //  und ausgeben, rKx = Anzahl
			{
				printf("%d: %s\n",k,oSatz->rKy[k]);
				++k;
			}

        default:   // sonstige Zeilen: ignorieren
		  break;
		}
	}




return;
}  // Ende HauptFunktion()


// **** //
// ENDE //
// **** //
