int __fastcall KeyGen(TConsole * Console, TProgramParams * Params)
{
  int Result = RESULT_SUCCESS;
  UnicodeString Passphrase;
  UnicodeString NewPassphrase;
  try
  {
    UnicodeString InputFileName;
    std::unique_ptr<TStrings> Args(new TStringList());
    if (!Params->FindSwitch(KEYGEN_SWITCH, Args.get(), 1) ||
        (Args->Count < 1) ||
        Args->Strings[0].IsEmpty())
    {
      throw Exception(LoadStr(KEYGEN_NO_INPUT));
    }
    InputFileName = Args->Strings[0];

    bool ValueSet;
    UnicodeString OutputFileName;
    FindPuttygenCompatibleSwitch(Params, KEYGEN_OUTPUT_SWITCH, L"o", OutputFileName, ValueSet);

    UnicodeString NewComment;
    FindPuttygenCompatibleSwitch(Params, KEYGEN_COMMENT_SWITCH, L"C", NewComment, ValueSet);

    bool NewPassphraseSet;
    bool ChangePassphrase =
      FindPuttygenCompatibleSwitch(Params, KEYGEN_CHANGE_PASSPHRASE_SWITCH, L"P", NewPassphrase, NewPassphraseSet);

    TKeyType Type = KeyType(InputFileName);
    int Error = errno;
    switch (Type)
    {
      case ktSSH1:
        throw Exception(LoadStr(KEYGEN_SSH1));

      case ktSSH2:
        if (NewComment.IsEmpty() && !ChangePassphrase)
        {
          throw Exception(LoadStr(KEYGEN_NO_ACTION));
        }
        break;

      case ktOpenSSHPEM:
      case ktOpenSSHNew:
      case ktSSHCom:
        if (OutputFileName.IsEmpty())
        {
          throw Exception(LoadStr(KEYGEN_NO_OUTPUT));
        }
        break;

      case ktSSH1Public:
      case ktSSH2PublicRFC4716:
      case ktSSH2PublicOpenSSH:
        throw Exception(LoadStr(KEYGEN_PUBLIC));

      case ktUnopenable:
        throw EOSExtException(FMTLOAD(KEY_TYPE_UNOPENABLE, (InputFileName)), Error);

      case ktOpenSSHAuto:
      default:
        DebugFail();
        // fallthru
      case ktUnknown:
        throw Exception(FMTLOAD(KEY_TYPE_UNKNOWN2, (InputFileName)));
    }

    UnicodeString Comment;
    if (IsKeyEncrypted(Type, InputFileName, Comment))
    {
      Passphrase = Params->SwitchValue(PassphraseOption);
      if (Passphrase.IsEmpty())
      {
        Console->Print(StripHotkey(FMTLOAD(PROMPT_KEY_PASSPHRASE, (Comment))) + L" ");
        if (!Console->Input(Passphrase, false, 0) ||
            Passphrase.IsEmpty())
        {
          Abort();
        }
      }
    }

    TPrivateKey * PrivateKey = LoadKey(Type, InputFileName, Passphrase);

    try
    {
      if (!NewComment.IsEmpty())
      {
        ChangeKeyComment(PrivateKey, NewComment);
      }

      if (ChangePassphrase)
      {
        if (!NewPassphraseSet)
        {
          Console->Print(LoadStr(KEYGEN_PASSPHRASE) + L" ");
          if (!Console->Input(NewPassphrase, false, 0))
          {
            Abort();
          }

          Console->Print(LoadStr(KEYGEN_PASSPHRASE2) + L" ");
          UnicodeString NewPassphrase2;
          if (!Console->Input(NewPassphrase2, false, 0))
          {
            Abort();
          }

          if (NewPassphrase != NewPassphrase2)
          {
            Shred(NewPassphrase2);
            throw Exception(LoadStr(KEYGEN_PASSPHRASES_MISMATCH));
          }
        }
      }
      else
      {
        NewPassphrase = Passphrase;
      }

      if (OutputFileName.IsEmpty())
      {
        OutputFileName = InputFileName;
      }

      SaveKey(ktSSH2, OutputFileName, NewPassphrase, PrivateKey);

      Console->PrintLine(FMTLOAD(KEYGEN_SAVED, (OutputFileName)));
    }
    __finally
    {
      FreeKey(PrivateKey);
    }
  }
  catch (Exception & E)
  {
    Result = HandleException(Console, E);
  }

  Shred(Passphrase);
  Shred(NewPassphrase);

  Console->WaitBeforeExit();
  return Result;
}