// C-Programmierung Beleg 5
// Copyright: Matthias Jauernig, 2004
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <stdbool.h>

// WICHTIG!: laut C9X-Standard ist fflush() nur auf Output-Streams anwendbar,
// fflush(stdin) z.B. undefiniert - dass dies bei Solaris funktioniert, ist reiner Zufall,
// bei Linux und anderen Systemen kann das schon ganz anders aussehen...
// => daher: eigenes Fflush() als Makro realisieren, welches stdin flush'ed
#define Fflush() while(getchar() != '\n')

/* --- Datenstruktur --------------------------------------------------------------------------	*/
typedef struct {
	char name[30], vorname[20];
	char adresse[50];
} datensatz;

/* --- Funktionsdeklarationen -----------------------------------------------------------------	*/
bool eingabe(char *filename);
bool ausgabe(char *filename);
bool aendern(char *filename);

/* --- main() ---------------------------------------------------------------------------------	*/
int main(int argc, char **argv){					//argv[1] ist evtl. filename
	char abfrage='j', *filename=(char*)malloc(100);
	FILE *fp;
	bool retval;
	
	do{
		if(argc==1 || abfrage!='j'){
			printf("\nEinzulesende Datei angeben: ");
			scanf("%s", filename); Fflush();
		}
		else strncpy(filename, argv[1], 100);			//filenamen lesen
	
		fp=fopen(filename,"rb");				//File existent?
		if(!fp){						//Datei existiert nicht
			perror("Fehler");
			printf("Datei neu anlegen (j/n, q=quit)?: ");
			abfrage=getchar(); Fflush();
			if(abfrage=='J' || abfrage=='j')
				fp=fopen(filename,"ab");		//File anlegen (Append-Modus)
			else if(abfrage=='q' || abfrage=='Q')
				return 1;				//Quit
		}
		else abfrage='j';					//Datei existiert, daher weiter
	}while(abfrage!='j' && abfrage!='J');
	fclose(fp);
	
	do{
		printf("\n-------------\n"
		       "| Hauptmenu |\n"
		       "-------------\n");
		printf("(1) Eingabe eines Datensatzes\n"
		       "(2) Ausgabe der Datensaetze im File\n"
		       "(3) Datensatz aendern\n"
		       "(4) Verlassen\n"
		       "> ");
		abfrage=getchar();  Fflush();
		
		retval=true;
		switch(abfrage){
			case '1':	retval=eingabe(filename);	break;
			case '2':	retval=ausgabe(filename);	break;
			case '3':	retval=aendern(filename);	break;
			case '4':	break;
			default:	printf("Ungueltige Nummer!\n");
		}
		if(!retval)
			perror("Fehler");
	}while(abfrage!='4');

	return 0;
}

/* --- Funktionsdefinitionen ------------------------------------------------------------------	*/
bool eingabe(char *filename){
	datensatz neu;
	FILE *fp=fopen(filename, "ab");					//Schreiben im Append-Modus
	if(!fp) return false;
	printf("Name eingeben:     ");
	fgets(neu.name,30,stdin); neu.name[strlen(neu.name)-1]='\0';
	printf("Vorname eingeben:  ");
	fgets(neu.vorname,20,stdin); neu.vorname[strlen(neu.vorname)-1]='\0';
	printf("Adresse eingeben:  ");
	fgets(neu.adresse,50,stdin); neu.adresse[strlen(neu.adresse)-1]='\0';
	if(!fwrite(&neu,sizeof(neu),1,fp)){
		fclose(fp);
		return false;
	}
	fclose(fp);
	return true;
}

bool ausgabe(char *filename){
	int i=1;
	datensatz neu;
	FILE *fp=fopen(filename, "rb");					//Lesen im Lese-Modus
	if(!fp) return false;
	while(fread(&neu,sizeof(neu),1,fp)){
		printf("\nDatensatz Nr. %d:\n",i++);
		printf("Name:     %s\n",neu.name);
		printf("Vorname:  %s\n",neu.vorname);
		printf("Adresse:  %s\n",neu.adresse);
	}
	if(ferror(fp)){
	 	fclose(fp);
		return false;
	}
	fclose(fp);
	return true;
}

bool aendern(char *filename){
	int i, nr;
	char abfrage, zk[50];
	datensatz neu;
	FILE *fp=fopen(filename, "rb+");				//R/W im Update-Modus
	if(!fp) return false;
	printf("\nNr. des zu aendernden Datensatzes?: ");
	scanf("%d",&nr); Fflush();
	if(fseek(fp,0,SEEK_END)==-1){					//Dateiende lesen
		fclose(fp);
		return false;
	}
	i=ftell(fp);							//Bytes bis zur akt. Filepos
	if(i/sizeof(datensatz)<nr){					//wenn nr über Fileende
		printf("Soviele Datensaetze sind im File nicht vorhanden!\n");
		fclose(fp);
		return true;
	}
	
	if(fseek(fp,(nr-1)*sizeof(neu),SEEK_SET)==-1){			//zu Datensatz nr spulen
		fclose(fp);
		return false;
	}
	if(!fread(&neu,sizeof(neu),1,fp)){				//Datensatz lesen
		fclose(fp);
		return false;
	}
	printf("\nDatensatz Nr. %d:\n",nr);
	printf("Name:     %s\n",neu.name);
	printf("Vorname:  %s\n",neu.vorname);
	printf("Adresse:  %s\n",neu.adresse);
	printf("=> Soll dieser Datensatz wirklich geaendert werden (j/n)?: ");
	abfrage=getchar(); Fflush();
	if(abfrage!='j' && abfrage!='J'){
		fclose(fp);
		return true;
	}
	
	printf("Name eingeben:     ");
	fgets(zk,30,stdin); zk[strlen(zk)-1]='\0';
	if(strcmp(zk,"")!=0) strncpy(neu.name,zk,30);
	printf("Vorname eingeben:  ");
	fgets(zk,20,stdin); zk[strlen(zk)-1]='\0';
	if(strcmp(zk,"")!=0) strncpy(neu.vorname,zk,20);
	printf("Adresse eingeben:  ");
	fgets(zk,50,stdin); zk[strlen(zk)-1]='\0';
	if(strcmp(zk,"")!=0) strncpy(neu.adresse,zk,50);
	
	if(fseek(fp,-sizeof(neu),SEEK_CUR)==-1){			//einen Datensatz zurück spulen
		fclose(fp);
		return false;
	}
	if(!fwrite(&neu,sizeof(neu),1,fp)){
		fclose(fp);
		return false;
	}
	fclose(fp);
	return true;
}

