/* ---------------------------------------------------- */
/* Source-file PHONE 1.2, (C) Wabnig Harald, 15.12.2002 */
/* ---------------------------------------------------- */

#include "basis.h"

#include "phonedb.h"

static DBHANDLE * pDB;

static int changed = FALSE;

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

#define MAXSTRG   10000

#ifdef WIN32
#define PHONE_FILE  "\\phonedb.grp"
/* 4.7.2002 */
#define PHONE_AFILE  "a:\\phonedb.grp"
#else

/* 16.12.2002 */
#include <termios.h>
#include <unistd.h>

#define PHONE_FILE  "phonedb.grp"
/* 4.7.2002 */
#define PHONE_AFILE  "aphonedb.grp"
#endif

#define UML_AE    142
#define UML_ae    132
#define UML_UE    154
#define UML_ue    129
#define UML_OE    153
#define UML_oe    148
#define UML_ss    225

static void echo_off()
{
#ifdef WIN32
  HANDLE Input;
  DWORD Mode;

  Input = GetStdHandle(STD_INPUT_HANDLE);
  if (! GetConsoleMode(Input, &Mode)) 
    return;
  Mode &= ~ENABLE_ECHO_INPUT;
  SetConsoleMode(Input, Mode);
#else
  /* 16.12.2002 */
  struct termios settings;
  int result;

  result = tcgetattr (STDOUT_FILENO, &settings);
  if (result < 0) {
    perror ("error in tcgetattr");
    return;
  }

  settings.c_lflag &= ~ECHO;

  result = tcsetattr (STDOUT_FILENO, TCSANOW, &settings);
  if (result < 0) {
    perror ("error in tcgetattr");
    return;
  }
#endif
}

static void echo_on()
{
#ifdef WIN32
  HANDLE Input;
  DWORD Mode;

  Input = GetStdHandle(STD_INPUT_HANDLE);
  GetConsoleMode(Input, &Mode);
  Mode |= ENABLE_ECHO_INPUT;
  SetConsoleMode(Input, Mode);
#else
  /* 16.12.2002 */
  struct termios settings;
  int result;

  result = tcgetattr (STDOUT_FILENO, &settings);
  if (result < 0) {
    perror ("error in tcgetattr");
    return;
  }

  settings.c_lflag |= ECHO;

  result = tcsetattr (STDOUT_FILENO, TCSANOW, &settings);
  if (result < 0) {
    perror ("error in tcgetattr");
    return;
  }
#endif
}

static int char_valid(char * fmt, unsigned char ch)
{
  int i = 0;

  if (fmt[0] == '-') {
    if (ch == '-')
      return TRUE;
    i++;
  }
  while (fmt[i] != '\0') {
    if (ch == fmt[i])
      return TRUE;
    if (fmt[i] == 'ä' && ch == UML_ae) 
      return TRUE;
    if (fmt[i] == 'Ä' && ch == UML_AE) 
      return TRUE;
    if (fmt[i] == 'ö' && ch == UML_oe) 
      return TRUE;
    if (fmt[i] == 'Ö' && ch == UML_OE) 
      return TRUE;
    if (fmt[i] == 'ü' && ch == UML_ue) 
      return TRUE;
    if (fmt[i] == 'Ü' && ch == UML_UE) 
      return TRUE;
    if (fmt[i] == 'ß' && ch == UML_ss) 
      return TRUE;
    if (fmt[i + 1] == '-' && fmt[i + 2] != '\0') {
      if (ch > fmt[i] && ch <= fmt[i + 2])
        return TRUE;
      i++; i++;
    }
    i++;
  }
  return FALSE;
}

static int scan_string(char * fmt, char * strg)
{
  unsigned char ch;
  int i = 0;

  while ((ch = getchar()) != '\n') {
    if (! char_valid(fmt, ch)) {
      fprintf(stderr, "character '%c' is not valid!\n", ch);
      while (getchar() != '\n');
      return -1;
    }
    strg[i++] = ch;
  }
  strg[i] = '\0';
  return i; 
}

static int read_string(char * prompt, char * strg, int len, int min)
{
  do {
    if (prompt[0] != '\0')
      fprintf(stdout, "%s", prompt);
    else 
      fprintf(stdout, "# ");
    if (scan_string("-+a-zA-Z/ _0-9äöüÄÖÜß*@.(),;'%=<>?!", strg) <= 0) {
      if (min == 0) {
        strg[0] = '\0'; break;
      }
      continue;
    }
    /* 4.7.2002 */
    if (strcmp(strg, "q") == 0) {
      return FALSE;
    }
    if ((int)strlen(strg) > len) {
      fprintf(stdout, "string too long (max = %d)!\n", len);
      continue;
    }
    if ((int)strlen(strg) < min) {
      fprintf(stdout, "string too small (min = %d)!\n", min);
      continue;
    }
    break;
  } while (1 == 1);
  return TRUE;
}

static int change_string(char * prompt, char * ostrg, char * strg, int len)
{
  do {
    if (prompt[0] != '\0')
      fprintf(stdout, "%s", prompt);
    else 
      fprintf(stdout, "# ");
    if (ostrg[0] != '\0')
      fprintf(stdout, "(%s) ", ostrg);
    if (! scan_string("-+a-zA-Z/ _0-9äöüÄÖÜß*@.(),;'%=<>?!", strg) < 0) {
      continue;
    }
    if ((int)strlen(strg) > len) {
      fprintf(stdout, "string too long (max = %d)!\n", len);
      continue;
    }
    if (strcmp(strg, "q") == 0) {
      return FALSE;
    }
    if (strg[0] == '\0') {
      strcpy(strg, ostrg);
    }
    break;
  } while (1 == 1);
  return TRUE;
}

static int read_password(char * prompt, char * strg, int len, int min)
{
  do {
    if (prompt[0] != '\0')
      fprintf(stdout, "%s", prompt);
    else 
      fprintf(stdout, "# ");
    echo_off();
    if (scan_string("-+a-zA-Z/ _0-9äöüÄÖÜß*@.(),;'%=<>", strg) <= 0) {
      if (min == 0) {
        strg[0] = '\0'; break;
      }
      continue;
    }
    /* 4.7.2002 */
    if (strcmp(strg, "q") == 0) {
      fprintf(stdout, "\n");
      echo_on();
      return FALSE;
    }
    if ((int)strlen(strg) > len) {
      fprintf(stdout, "string too long (max = %d)!\n", len);
      continue;
    }
    if ((int)strlen(strg) < min) {
      fprintf(stdout, "string too small (min = %d)!\n", min);
      continue;
    }
    break;
  } while (1 == 1);
  fprintf(stdout, "\n");
  echo_on();
  return TRUE;
}

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

static void print_help()
{
  fprintf(stdout, "(a)dd,(c)hange,(d)elete,(i)mport,(e)xport\n" \
                  "(f)ind,(l)ist,(g)eneric search\n" \
                  "(s)ave,s(t)atus,(n)ew password,clea(r) password,(h)elp,(q)uit\n\n");
}

static void print_status()
{
  TELNR Telnr;
  int iCount;

  TELNR_Init(&Telnr);
  if (TELNR_Count(&Telnr, &iCount, pDB)) {
    fprintf(stdout, "%d entries\n", iCount);
  } else {
    fprintf(stdout, "number of entries could not be determined!\n");
  }
}

static void process_add()
{
  TELNR Telnr, Telnr1;
  char name[MAXSTRG], nr[MAXSTRG], email[MAXSTRG];

  TELNR_Init(&Telnr); TELNR_Init(&Telnr1);
  if (! read_string("# Name: ", name, 35, 1)) {
    return;
  }
  COPY_FChar_35(Telnr.fszName, name);
  if (! read_string("# Private: ", nr, 25, 0)) {
    return;
  }
  COPY_FChar_25(Telnr.fszPrivate, nr);
  if (! read_string("# Office: ", nr, 25, 0)) {
    return;
  }
  COPY_FChar_25(Telnr.fszOffice, nr);
  if (! read_string("# Handy: ", nr, 25, 0)) {
    return;
  }
  COPY_FChar_25(Telnr.fszHandy, nr);
  if (! read_string("# Email: ", email, 80, 0)) {
    return;
  }
  COPY_FChar_80(Telnr.fszEmail, email);
  if (! read_string("# Comment: ", email, 80, 0)) {
    return;
  }
  COPY_FChar_80(Telnr.fszComment, email);

  TELNR_Assign(&Telnr1, &Telnr);
  while (TELNR_SelectLk(&Telnr1, pDB)) {
    if (! change_string("# Name: ", Make_Reduce_String(Telnr.fszName), name, 35)) {
      Free_ValueStrings();
      return;
    }
    if (name[0] != '\0') {
      COPY_FChar_35(Telnr.fszName, name);
      COPY_FChar_35(Telnr1.fszName, name);
    }
    Free_ValueStrings();
  }
  if (! TELNR_Insert(&Telnr, pDB)) {
    DB_Print_Error_String();
    fprintf(stderr, "db-error in TELNR_Insert!\n");
    return;
  } 
  fprintf(stdout, "item added\n");
  changed = TRUE;
}

static void print_telnr(TELNR * pTelnr)
{
  fprintf(stdout, "%s:\n", Make_Reduce_String(pTelnr->fszName));
  if (! CMP_FChar_25(pTelnr->fszPrivate, DEF_FChar_25)) {
    fprintf(stdout, "  Private: %s\n", Make_Reduce_String(pTelnr->fszPrivate));
  }
  if (! CMP_FChar_25(pTelnr->fszOffice, DEF_FChar_25)) {
    fprintf(stdout, "  Office:  %s\n", Make_Reduce_String(pTelnr->fszOffice));
  }
  if (! CMP_FChar_25(pTelnr->fszHandy, DEF_FChar_25)) {
    fprintf(stdout, "  Handy:   %s\n", Make_Reduce_String(pTelnr->fszHandy));
  }
  if (! CMP_FChar_80(pTelnr->fszEmail, DEF_FChar_80)) {
    fprintf(stdout, "  Email:   %s\n", Make_Reduce_String(pTelnr->fszEmail));
  }
  if (! CMP_FChar_80(pTelnr->fszComment, DEF_FChar_80)) {
    fprintf(stdout, "  Comment: %s\n", Make_Reduce_String(pTelnr->fszComment));
  }
  Free_ValueStrings();
}

static void print_telnr_name(TELNR * pTelnr)
{
  fprintf(stdout, "[%s]\t", Make_Reduce_String(pTelnr->fszName));
  Free_ValueStrings();
}

static void process_find_list(int kz_list)
{
  TELNR Telnr;
  char name[MAXSTRG];
  int iCount;

  TELNR_Init(&Telnr);
  if (! read_string("# Name? ", name, 35, 1)) {
    return;
  }
  Make_SearchString_Size(name, 35);
  COPY_FChar_35(Telnr.fszName, name);
  iCount = 0;
  if (! TELNR_Select_Name(&Telnr, pDB)) {
    DB_Print_Error_String();
    fprintf(stderr, "db-error in TELNR_Select_Name!\n");
  } else {
    char ch = ' ';

    while (TELNR_Fetch_Name(&Telnr, pDB)) {
      if (ch != ' ' && ch != Telnr.fszName[0])
        fprintf(stdout, "\n");
      if (kz_list) 
        print_telnr_name(&Telnr);
      else
        print_telnr(&Telnr);
      ch = Telnr.fszName[0];
      iCount++;
    }
    if (iCount > 0) 
      fprintf(stdout, "\n");
  }
  fprintf(stdout, "found %d items\n", iCount);
}

static void process_change()
{
  TELNR Telnr, Telnr1;
  char name[MAXSTRG], nr[MAXSTRG], email[MAXSTRG];
  int iCount;

  TELNR_Init(&Telnr); TELNR_Init(&Telnr1);
  do {
    if (! read_string("# Name? ", name, 35, 1)) {
      return;
    }
    COPY_FChar_35(Telnr.fszName, name);
    if (TELNR_SelectLk(&Telnr, pDB)) {
      iCount = 1;
      print_telnr_name(&Telnr);
      fprintf(stdout, "\n");
    } else {
      Make_SearchString_Size(name,35);
      COPY_FChar_35(Telnr.fszName, name);
      iCount = 0;
      if (! TELNR_Select_Name(&Telnr, pDB)) {
        DB_Print_Error_String();
        fprintf(stderr, "db-error in TELNR_Select_Name!\n");
      } else {
        while (TELNR_Fetch_Name(&Telnr, pDB)) {
          print_telnr_name(&Telnr);
          iCount++;
        }
        if (iCount > 0) 
          fprintf(stdout, "\n");
      }
    }
  } while (iCount != 1);

  do {
    if (! change_string("# Name: ", Make_Reduce_String(Telnr.fszName), name, 35)) {
      Free_ValueStrings();
      return;
    }
    if (name[0] != '\0') {
      COPY_FChar_35(Telnr.fszName, name);
    }
    Free_ValueStrings();
  } while (CMP_FChar_35(Telnr.fszName, DEF_FChar_35));

  if (! change_string("# Private: ", Make_Reduce_String(Telnr.fszPrivate), nr, 25)) {
    Free_ValueStrings();
    return;
  }
  if (nr[0] != '\0') {
    COPY_FChar_25(Telnr.fszPrivate, nr);
  }
  Free_ValueStrings();
  if (! change_string("# Office: ", Make_Reduce_String(Telnr.fszOffice), nr, 25)) {
    Free_ValueStrings();
    return;
  }
  if (nr[0] != '\0') {
    COPY_FChar_25(Telnr.fszOffice, nr);
  }
  Free_ValueStrings();
  if (! change_string("# Handy: ", Make_Reduce_String(Telnr.fszHandy), nr, 25)) {
    Free_ValueStrings();
    return;
  }
  if (nr[0] != '\0') {
    COPY_FChar_25(Telnr.fszHandy, nr);
  }
  Free_ValueStrings();
  if (! change_string("# Email: ", Make_Reduce_String(Telnr.fszEmail), email, 80)) {
    Free_ValueStrings();
    return;
  }
  if (email[0] != '\0') {
    COPY_FChar_80(Telnr.fszEmail, email);
  }
  Free_ValueStrings();
  if (! change_string("# Comment: ", Make_Reduce_String(Telnr.fszComment), email, 80)) {
    Free_ValueStrings();
    return;
  }
  if (email[0] != '\0') {
    COPY_FChar_80(Telnr.fszComment, email);
  }
  Free_ValueStrings();

  TELNR_Assign(&Telnr1, &Telnr);
  while (TELNR_SelectLk(&Telnr1, pDB)) {
    if (CMP_FSurrogat(Telnr1.fszTELNR_Id, Telnr.fszTELNR_Id)) 
      break;
    if (! change_string("# Name: ", Make_Reduce_String(Telnr.fszName), name, 35)) {
      Free_ValueStrings();
      return;
    }
    if (name[0] != '\0') {
      COPY_FChar_35(Telnr.fszName, name);
      COPY_FChar_35(Telnr1.fszName, name);
    }
    Free_ValueStrings();
  } 
  if (! TELNR_Update(&Telnr, pDB)) {
    DB_Print_Error_String();
    fprintf(stderr, "db-error in TELNR_Update!\n");
    return;
  }
  fprintf(stdout, "item changed\n");
  changed = TRUE;
}

static void process_delete()
{
  TELNR Telnr;
  char name[MAXSTRG];
  char ch;
  int iCount;

  TELNR_Init(&Telnr);
  do {
    if (! read_string("# Name? ", name, 35, 1)) {
      return;
    }
    COPY_FChar_35(Telnr.fszName, name);
    if (TELNR_SelectLk(&Telnr, pDB)) {
      iCount = 1;
      print_telnr_name(&Telnr);
      fprintf(stdout, "\n");
    } else {
      Make_SearchString_Size(name, 35);
      COPY_FChar_35(Telnr.fszName, name);
      iCount = 0;
      if (! TELNR_Select_Name(&Telnr, pDB)) {
        DB_Print_Error_String();
        fprintf(stderr, "db-error in TELNR_Select_Name!\n");
      } else {
        while (TELNR_Fetch_Name(&Telnr, pDB)) {
          print_telnr_name(&Telnr);
          iCount++;
        }
        if (iCount > 0) 
          fprintf(stdout, "\n");
      }
    }
  } while (iCount != 1);

  fprintf(stdout, "Are you sure? (y/n) ");
  while (ch = getchar()) {
    while (getchar() != '\n');
    if (ch == 'y') break;
    if (ch == 'n') return;
  }

  if (! TELNR_Delete(&Telnr, pDB)) {
    DB_Print_Error_String();
    fprintf(stderr, "db-error in TELNR_Delete!\n");
    return;
  }
  fprintf(stdout, "item deleted\n");
  changed = TRUE;
}

static void process_save()
{
  if (! GRP_Data_Save (pDB, PHONE_FILE, "TELNRDAT")) {
    fprintf(stderr, "error saving phonedb to file '%s'!\n", PHONE_FILE);
  } else {
    fprintf(stdout, "phonedb saved to file '%s'!\n", PHONE_FILE);
    changed = FALSE;
  }
}

static void process_generic()
{
  char sql[MAXSTRG];
  int cnt;

  do {
    if (! read_string("# SQL-command? ", sql, 160, 1)) {
      if (strcmp(sql, "q") == 0) 
        return;
      continue;
    }
    if (! DB_Exec_SQL(pDB, sql, &cnt, TRUE)) {
      DB_Print_Error_String();
    } else if (cnt < 0) {
      fprintf(stdout, "not all rows evaluated!\n");
    }
  } while (1 == 1);
}

static void process_export()
{
  char * data;
  int size;

  if (! GRP_Data_Save (pDB, PHONE_FILE, "TELNRDAT")) {
    fprintf(stderr, "error exporting file '%s'!\n", PHONE_FILE);
    return;
  } else {
    changed = FALSE;
  }
  if ((data = read_binfile(PHONE_FILE, &size)) == NULL) {
    fprintf(stderr, "error exporting file '%s'!\n", PHONE_FILE);
    return;
  }
  if (! write_binfile(PHONE_AFILE, data, size)) {
    fprintf(stderr, "error exporting file '%s'!\n", PHONE_AFILE);
  } else {
    fprintf(stderr, "file '%s' exported successfully!\n", PHONE_AFILE);
  }
  free(data);
}

static void process_import()
{
  MTELNR MTelnr;
  TELNR Telnr, * pTelnr;
  MPASSWD MPasswd;
  PASSWD Passwd, * pPasswd;
  int i;

  if (! exists_file(PHONE_AFILE)) {
    fprintf(stderr, "error importing file '%s'!\n", PHONE_AFILE);
    return;
  }
  /* store tables */
  MTELNR_Init(&MTelnr);
  TELNR_Init(&Telnr);
  if (TELNR_Select(&Telnr, pDB)) 
  while (TELNR_Fetch(&Telnr, pDB)) {
    MTELNR_Add(&MTelnr, &Telnr);
  }
  MPASSWD_Init(&MPasswd);
  PASSWD_Init(&Passwd);
  if (PASSWD_Select(&Passwd, pDB)) 
  while (PASSWD_Fetch(&Passwd, pDB)) {
    MPASSWD_Add(&MPasswd, &Passwd);
  }
  changed = TRUE;
  /* open planer-file */
  if (! GRP_Data_Load (pDB, PHONE_AFILE, "TELNRDAT")) {
    MTELNR_Free(&MTelnr);
    fprintf(stderr, "error loading file '%s'!\n", PHONE_AFILE);
    return;
  }
  if (TELNR_Select(&Telnr, pDB)) 
  while (TELNR_Fetch(&Telnr, pDB)) {
    for (i = 0; i < MTelnr.iCount; i++) {
      pTelnr = MTELNR_GetPtr(&MTelnr, i);
      if (TELNR_CmpLk(pTelnr, &Telnr))
        break;
    }
    if (i == MTelnr.iCount)
      fprintf(stdout, "'%s' loaded\n", Make_Reduce_String(Telnr.fszName));
  }
  /* reload PASSWD-table */
  PASSWD_DeleteAll(pDB);
  for (i = 0; i < MPasswd.iCount; i++) {
    pPasswd = MPASSWD_GetPtr(&MPasswd, i);
    PASSWD_Insert(pPasswd, pDB);
  }
  /* add stored tables */
  for (i = 0; i < MTelnr.iCount; i++) {
    pTelnr = MTELNR_GetPtr(&MTelnr, i);
    TELNR_Assign(&Telnr, pTelnr);
    if (! TELNR_SelectLk(&Telnr, pDB)) {
      TELNR_Insert(pTelnr, pDB);
    } else {
      COPY_FSurrogat(pTelnr->fszTELNR_Id, Telnr.fszTELNR_Id);
      if (CMP_FChar_25(pTelnr->fszPrivate, DEF_FChar_25)) {
        COPY_FChar_25(pTelnr->fszPrivate, Telnr.fszPrivate);
      }
      if (CMP_FChar_25(pTelnr->fszOffice, DEF_FChar_25)) {
        COPY_FChar_25(pTelnr->fszOffice, Telnr.fszOffice);
      }
      if (CMP_FChar_25(pTelnr->fszHandy, DEF_FChar_25)) {
        COPY_FChar_25(pTelnr->fszHandy, Telnr.fszHandy);
      }
      if (CMP_FChar_80(pTelnr->fszEmail, DEF_FChar_80)) {
        COPY_FChar_80(pTelnr->fszEmail, Telnr.fszEmail);
      }
      if (CMP_FChar_80(pTelnr->fszComment, DEF_FChar_80)) {
        COPY_FChar_80(pTelnr->fszComment, Telnr.fszComment);
      }
      TELNR_Update(pTelnr, pDB);
    }
  }
  MTELNR_Free(&MTelnr);
  MPASSWD_Free(&MPasswd);
  fprintf(stdout, "file '%s' loaded successfully!\n", PHONE_AFILE);
}

static void process_new_password()
{
  PASSWD Passwd, Passwd1;
  char password[MAXSTRG], strg[MAXSTRG];
  int iCount;

  PASSWD_Init(&Passwd);
  PASSWD_Init(&Passwd1);
  strcpy(password, "");
  if (PASSWD_Select(&Passwd, pDB))
  while (PASSWD_Fetch(&Passwd, pDB)) {
    if (Passwd.fsSubnummer == 1) {
      strcpy(password, Passwd.fszPassword);
      PASSWD_Assign(&Passwd1, &Passwd);
    }
  }
  if (! read_password("# New Password: ", strg, 35, 4)) {
    return;
  }
  if (strcmp(password, "") == 0) {
    PASSWD_Init(&Passwd);
    COPY_FChar_35(Passwd.fszPassword, strg);
    COPY_FShort(Passwd.fsSubnummer, 1);
    if (! PASSWD_Insert(&Passwd, pDB)) {
      fprintf(stdout, "error inserting password - password not stored!\n");
    } else {
      changed = TRUE;
    }
  } else {
    COPY_FChar_35(Passwd1.fszPassword, strg);
    if (! PASSWD_Update_Password(&Passwd1, &iCount, pDB) || iCount == 0) {
      fprintf(stdout, "error changing password - password not changed!\n");
      return;
    } else {
      changed = TRUE;
    }
  }
  fprintf(stdout, "password changed\n");
}

static void process_clear_password()
{
  PASSWD_DeleteAll(pDB);
  changed = TRUE;
  fprintf(stdout, "password cleared\n");
}

static void process()
{
  char strg[MAXSTRG];
  int i;

  do {
    fprintf(stdout, "> ");
    if (scan_string("qhtacdflsgienr ", strg) <= 0) {
      continue;
    }
    /* skip spaces at end of command */
    i = strlen(strg) - 1;
    while (i >= 0 && isspace(strg[i])) i--;
    strg[i + 1] = '\0';
    if (strcmp(strg, "q") == 0) {
      break;
    } else if (strcmp(strg, "h") == 0) {
      print_help();
    } else if (strcmp(strg, "t") == 0) {
      print_status();
    } else if (strcmp(strg, "a") == 0) {
      process_add();
    } else if (strcmp(strg, "c") == 0) {
      process_change();
    } else if (strcmp(strg, "d") == 0) {
      process_delete();
    } else if (strcmp(strg, "f") == 0) {
      process_find_list(FALSE);
    } else if (strcmp(strg, "l") == 0) {
      process_find_list(TRUE);
    } else if (strcmp(strg, "s") == 0) {
      process_save();
    } else if (strcmp(strg, "g") == 0) {
      process_generic();
    /* 4.7.2002 */
    } else if (strcmp(strg, "i") == 0) {
      process_import();
    } else if (strcmp(strg, "e") == 0) {
      process_export();
    } else if (strcmp(strg, "n") == 0) {
      process_new_password();
    } else if (strcmp(strg, "r") == 0) {
      process_clear_password();
    } else {
      fprintf(stdout, "unknown command!\n");
    }
  } while (1 == 1);
}

static void process_password()
{
  MPASSWD MPasswd;
  PASSWD Passwd, * pPasswd;
  char password[MAXSTRG], strg[MAXSTRG];
  int i, iCount;

  MPASSWD_Init(&MPasswd);
  PASSWD_Init(&Passwd);
  strcpy(password, "");
  if (PASSWD_Select(&Passwd, pDB))
  while (PASSWD_Fetch(&Passwd, pDB)) {
    if (Passwd.fsSubnummer == 1) {
      strcpy(password, Passwd.fszPassword);
    } else {
      MPASSWD_Add(&MPasswd, &Passwd);
    }
  }
  if (strcmp(password, "") == 0) {
    return;
    /*
    if (! read_password("# New Password: ", strg, 35, 4)) {
      process_save();
      exit(1);
    }
    PASSWD_Init(&Passwd);
    COPY_FChar_35(Passwd.fszPassword, strg);
    COPY_FShort(Passwd.fsSubnummer, 1);
    if (! PASSWD_Insert(&Passwd, pDB)) {
      fprintf(stdout, "error inserting password - password not stored!\n");
    }
    changed = TRUE;
    */
  } else {
    if (MPasswd.iCount > 0) {
      fprintf(stdout, "%d wrong passwords tried:\n", MPasswd.iCount);
      for (i = 0; i < MPasswd.iCount; i++) {
        pPasswd = MPASSWD_GetPtr(&MPasswd, i);
        fprintf(stdout, "  at %s\n", Make_DatiString(&pPasswd->fdtAt));
      }
    }

    iCount = 0;
    do {
      if (iCount > 0) {
        fprintf(stdout, "invalid password!\n");
        COPY_FChar_35(Passwd.fszPassword, strg);
        COPY_FShort(Passwd.fsSubnummer, iCount + 1);
        Get_Timestamp(&Passwd.fdtAt);
        PASSWD_Insert(&Passwd, pDB);
        changed = TRUE;
      }
      if (! read_password("# Password: ", strg, 35, 4)) {
        process_save();
        exit(1);
      }
      /*
      if (iCount == 1 && MPasswd.iCount > 0) {
        PASSWD_Delete_Wrong(&Passwd, &i, pDB);
        changed = TRUE;
      }
      */
      iCount++;
    } while (! CMP_FChar_35(password, strg));

    if (MPasswd.iCount > 0/*iCount > 1*/) {
      fprintf(stdout, "%d wrong passwords tried:\n", MPasswd.iCount);
      for (i = 0; i < MPasswd.iCount; i++) {
        pPasswd = MPASSWD_GetPtr(&MPasswd, i);
        fprintf(stdout, "  %s at %s\n",
                Make_Reduce_String(pPasswd->fszPassword),
                Make_DatiString(&pPasswd->fdtAt));
      }
      PASSWD_Delete_Wrong(&Passwd, &i, pDB);
      changed = TRUE;
    }
  }
  fprintf(stdout, "password ok\n");
}

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

#define TABLE_TELNR     "CREATE TABLE TELNR ("\
                        "  TELNR_ID CHARACTER(8),"\
                        "  NAME CHARACTER(35),"\
                        "  PRIVATE CHARACTER(25),"\
                        "  OFFICE CHARACTER(25),"\
                        "  HANDY CHARACTER(25),"\
                        "  EMAIL CHARACTER(80),"\
                        "  COMMENT CHARACTER(80)"\
                        ");"
#define INDEX_XTELNR01  "CREATE UNIQUE INDEX XTELNR01 ON TELNR ("\
                        "  TELNR_ID"\
                        ");"
#define INDEX_XTELNR02  "CREATE UNIQUE INDEX XTELNR02 ON TELNR ("\
                        "  NAME"\
                        ");"

#define TABLE_PASSWD    "CREATE TABLE PASSWD ("\
                        "  PASSWD_ID CHARACTER(8),"\
                        "  PASSWORD CHARACTER(35),"\
                        "  SUBNUMMER SMALLINT,"\
                        "  AT TIMESTAMP"\
                        ");"
#define INDEX_XPASSWD01 "CREATE UNIQUE INDEX XPASSWD01 ON PASSWD ("\
                        "  PASSWD_ID"\
                        ");"
#define INDEX_XPASSWD02 "CREATE UNIQUE INDEX XPASSWD02 ON PASSWD ("\
                        "  SUBNUMMER"\
                        ");"

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

int main (int argc, char *argv[])
{
  fprintf(stdout, "Phone directory 1.3, (C) Wabnig Harald, 12.07.2003\n");

#ifdef SPEEDY1
  if (! Speedy1_Init("", "PHONEDB")) {
    fprintf(stderr, "Speedy1_Init error!\n");
    exit(1);
  }
#endif
  /* Connect to database "PHONEDB" */
  DB_Set_Error_String();
  if (! DBHANDLE_Init(&pDB, "PHONEDB")) {
    DB_Print_Error_String();
    fprintf(stderr, "DBHANDLE_Init error!\n");
    exit(1);
  }

  /* Create database-tables/indices if necessary */

  if (! DB_Exists_Table(pDB, "TELNR")) {
    if (! DB_Create_Table(pDB, TABLE_TELNR)) {
      DB_Print_Error_String();
      fprintf(stderr, "DB_Create_Table 'TELNR' error!\n");
      exit(1);
    }
  }
  if (! DB_Exists_Index(pDB, "XTELNR01")) {
    if (! DB_Create_Index(pDB, INDEX_XTELNR01)) {
      DB_Print_Error_String();
      fprintf(stderr, "DB_Create_Index 'XTELNR01' error!\n");
      exit(1);
    }
  }
  if (! DB_Exists_Index(pDB, "XTELNR02")) {
    if (! DB_Create_Index(pDB, INDEX_XTELNR02)) {
      DB_Print_Error_String();
      fprintf(stderr, "DB_Create_Index 'XTELNR02' error!\n");
      exit(1);
    }
  }

  if (! DB_Exists_Table(pDB, "PASSWD")) {
    if (! DB_Create_Table(pDB, TABLE_PASSWD)) {
      DB_Print_Error_String();
      fprintf(stderr, "DB_Create_Table 'PASSWD' error!\n");
      exit(1);
    }
  }
  if (! DB_Exists_Index(pDB, "XPASSWD01")) {
    if (! DB_Create_Index(pDB, INDEX_XPASSWD01)) {
      DB_Print_Error_String();
      fprintf(stderr, "DB_Create_Index 'XPASSWD01' error!\n");
      exit(1);
    }
  }
  if (! DB_Exists_Index(pDB, "XPASSWD02")) {
    if (! DB_Create_Index(pDB, INDEX_XPASSWD02)) {
      DB_Print_Error_String();
      fprintf(stderr, "DB_Create_Index 'XPASSWD02' error!\n");
      exit(1);
    }
  }

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

  /* read database */

  changed = FALSE;
  if (exists_file(PHONE_FILE)) {
    if (! GRP_Data_Load (pDB, PHONE_FILE, "TELNRDAT")) {
      fprintf(stdout, "error reading phone-db from file '%s'!\n", PHONE_FILE);
    }
  }

  process_password();

  print_status();
  print_help();

  process();

  if (changed)
    process_save();

  exit(0);
}
