generate_keypair (ctrl_t ctrl, int full, const char *fname,
                  const char *card_serialno, int card_backup_key)
{
  unsigned int nbits;
  char *uid = NULL;
  int algo;
  unsigned int use;
  int both = 0;
  u32 expire;
  struct para_data_s *para = NULL;
  struct para_data_s *r;
  struct output_control_s outctrl;

#ifndef ENABLE_CARD_SUPPORT
  (void)card_backup_key;
#endif

  memset( &outctrl, 0, sizeof( outctrl ) );

  if (opt.batch && card_serialno)
    {
      /* We don't yet support unattended key generation. */
      log_error (_("can't do this in batch mode\n"));
      return;
    }

  if (opt.batch)
    {
      read_parameter_file( fname );
      return;
    }

  if (card_serialno)
    {
#ifdef ENABLE_CARD_SUPPORT
      r = xcalloc (1, sizeof *r + strlen (card_serialno) );
      r->key = pSERIALNO;
      strcpy( r->u.value, card_serialno);
      r->next = para;
      para = r;

      algo = PUBKEY_ALGO_RSA;

      r = xcalloc (1, sizeof *r + 20 );
      r->key = pKEYTYPE;
      sprintf( r->u.value, "%d", algo );
      r->next = para;
      para = r;
      r = xcalloc (1, sizeof *r + 20 );
      r->key = pKEYUSAGE;
      strcpy (r->u.value, "sign");
      r->next = para;
      para = r;

      r = xcalloc (1, sizeof *r + 20 );
      r->key = pSUBKEYTYPE;
      sprintf( r->u.value, "%d", algo );
      r->next = para;
      para = r;
      r = xcalloc (1, sizeof *r + 20 );
      r->key = pSUBKEYUSAGE;
      strcpy (r->u.value, "encrypt");
      r->next = para;
      para = r;

      r = xcalloc (1, sizeof *r + 20 );
      r->key = pAUTHKEYTYPE;
      sprintf( r->u.value, "%d", algo );
      r->next = para;
      para = r;

      if (card_backup_key)
        {
          r = xcalloc (1, sizeof *r + 1);
          r->key = pCARDBACKUPKEY;
          strcpy (r->u.value, "1");
          r->next = para;
          para = r;
        }
#endif /*ENABLE_CARD_SUPPORT*/
    }
  else if (full)  /* Full featured key generation.  */
    {
      int subkey_algo;
      char *curve = NULL;

      /* Fixme: To support creating a primary key by keygrip we better
         also define the keyword for the parameter file.  Note that
         the subkey case will never be asserted if a keygrip has been
         given.  */
      algo = ask_algo (ctrl, 0, &subkey_algo, &use, NULL);
      if (subkey_algo)
        {
          /* Create primary and subkey at once.  */
          both = 1;
          if (algo == PUBKEY_ALGO_ECDSA
              || algo == PUBKEY_ALGO_EDDSA
              || algo == PUBKEY_ALGO_ECDH)
            {
              curve = ask_curve (&algo, both);
              r = xmalloc_clear( sizeof *r + 20 );
              r->key = pKEYTYPE;
              sprintf( r->u.value, "%d", algo);
              r->next = para;
              para = r;
              nbits = 0;
              r = xmalloc_clear (sizeof *r + strlen (curve));
              r->key = pKEYCURVE;
              strcpy (r->u.value, curve);
              r->next = para;
              para = r;
            }
          else
            {
              r = xmalloc_clear( sizeof *r + 20 );
              r->key = pKEYTYPE;
              sprintf( r->u.value, "%d", algo);
              r->next = para;
              para = r;
              nbits = ask_keysize (algo, 0);
              r = xmalloc_clear( sizeof *r + 20 );
              r->key = pKEYLENGTH;
              sprintf( r->u.value, "%u", nbits);
              r->next = para;
              para = r;
            }
          r = xmalloc_clear( sizeof *r + 20 );
          r->key = pKEYUSAGE;
          strcpy( r->u.value, "sign" );
          r->next = para;
          para = r;

          r = xmalloc_clear( sizeof *r + 20 );
          r->key = pSUBKEYTYPE;
          sprintf( r->u.value, "%d", subkey_algo);
          r->next = para;
          para = r;
          r = xmalloc_clear( sizeof *r + 20 );
          r->key = pSUBKEYUSAGE;
          strcpy( r->u.value, "encrypt" );
          r->next = para;
          para = r;

          if (algo == PUBKEY_ALGO_ECDSA
              || algo == PUBKEY_ALGO_EDDSA
              || algo == PUBKEY_ALGO_ECDH)
            {
              if (algo == PUBKEY_ALGO_EDDSA
                  && subkey_algo == PUBKEY_ALGO_ECDH)
                {
                  /* Need to switch to a different curve for the
                     encryption key.  */
                  xfree (curve);
                  curve = xstrdup ("Curve25519");
                }
              r = xmalloc_clear (sizeof *r + strlen (curve));
              r->key = pSUBKEYCURVE;
              strcpy (r->u.value, curve);
              r->next = para;
              para = r;
            }
        }
      else /* Create only a single key.  */
        {
          /* For ECC we need to ask for the curve before storing the
             algo because ask_curve may change the algo.  */
          if (algo == PUBKEY_ALGO_ECDSA
              || algo == PUBKEY_ALGO_EDDSA
              || algo == PUBKEY_ALGO_ECDH)
            {
              curve = ask_curve (&algo, 0);
              nbits = 0;
              r = xmalloc_clear (sizeof *r + strlen (curve));
              r->key = pKEYCURVE;
              strcpy (r->u.value, curve);
              r->next = para;
              para = r;
            }

          r = xmalloc_clear( sizeof *r + 20 );
          r->key = pKEYTYPE;
          sprintf( r->u.value, "%d", algo );
          r->next = para;
          para = r;

          if (use)
            {
              r = xmalloc_clear( sizeof *r + 25 );
              r->key = pKEYUSAGE;
              sprintf( r->u.value, "%s%s%s",
                       (use & PUBKEY_USAGE_SIG)? "sign ":"",
                       (use & PUBKEY_USAGE_ENC)? "encrypt ":"",
                       (use & PUBKEY_USAGE_AUTH)? "auth":"" );
              r->next = para;
              para = r;
            }
          nbits = 0;
        }

      if (algo == PUBKEY_ALGO_ECDSA
          || algo == PUBKEY_ALGO_EDDSA
          || algo == PUBKEY_ALGO_ECDH)
        {
          /* The curve has already been set.  */
        }
      else
        {
          nbits = ask_keysize (both? subkey_algo : algo, nbits);
          r = xmalloc_clear( sizeof *r + 20 );
          r->key = both? pSUBKEYLENGTH : pKEYLENGTH;
          sprintf( r->u.value, "%u", nbits);
          r->next = para;
          para = r;
        }

      xfree (curve);
    }
  else /* Default key generation.  */
    {
      tty_printf ( _("Note: Use \"%s %s\""
                     " for a full featured key generation dialog.\n"),
                   NAME_OF_INSTALLED_GPG, "--full-gen-key" );
      para = quickgen_set_para (para, 0,
                                DEFAULT_STD_ALGO, DEFAULT_STD_KEYSIZE,
                                DEFAULT_STD_CURVE);
      para = quickgen_set_para (para, 1,
                                DEFAULT_STD_SUBALGO, DEFAULT_STD_SUBKEYSIZE,
                                DEFAULT_STD_SUBCURVE);
    }


  expire = full? ask_expire_interval (0, NULL) : 0;
  r = xcalloc (1, sizeof *r + 20);
  r->key = pKEYEXPIRE;
  r->u.expire = expire;
  r->next = para;
  para = r;
  r = xcalloc (1, sizeof *r + 20);
  r->key = pSUBKEYEXPIRE;
  r->u.expire = expire;
  r->next = para;
  para = r;

  uid = ask_user_id (0, full, NULL);
  if (!uid)
    {
      log_error(_("Key generation canceled.\n"));
      release_parameter_list( para );
      return;
    }
  r = xcalloc (1, sizeof *r + strlen (uid));
  r->key = pUSERID;
  strcpy (r->u.value, uid);
  r->next = para;
  para = r;

  proc_parameter_file (para, "[internal]", &outctrl, !!card_serialno);
  release_parameter_list (para);
}