pct_expando(const char *string,struct expando_args *args)
{
  const char *ch=string;
  int idx=0,maxlen=0,done=0;
  u32 pk_keyid[2]={0,0},sk_keyid[2]={0,0};
  char *ret=NULL;

  if(args->pk)
    keyid_from_pk(args->pk,pk_keyid);

  if(args->pksk)
    keyid_from_pk (args->pksk, sk_keyid);

  /* This is used so that %k works in photoid command strings in
     --list-secret-keys (which of course has a sk, but no pk). */
  if(!args->pk && args->pksk)
    keyid_from_pk (args->pksk, pk_keyid);

  while(*ch!='\0')
    {
      if(!done)
	{
	  /* 8192 is way bigger than we'll need here */
	  if(maxlen>=8192)
	    goto fail;

	  maxlen+=1024;
	  ret=xrealloc(ret,maxlen);
	}

      done=0;

      if(*ch=='%')
	{
	  switch(*(ch+1))
	    {
	    case 's': /* short key id */
	      if(idx+8<maxlen)
		{
		  sprintf(&ret[idx],"%08lX",(ulong)sk_keyid[1]);
		  idx+=8;
		  done=1;
		}
	      break;

	    case 'S': /* long key id */
	      if(idx+16<maxlen)
		{
		  sprintf(&ret[idx],"%08lX%08lX",
			  (ulong)sk_keyid[0],(ulong)sk_keyid[1]);
		  idx+=16;
		  done=1;
		}
	      break;

	    case 'k': /* short key id */
	      if(idx+8<maxlen)
		{
		  sprintf(&ret[idx],"%08lX",(ulong)pk_keyid[1]);
		  idx+=8;
		  done=1;
		}
	      break;

	    case 'K': /* long key id */
	      if(idx+16<maxlen)
		{
		  sprintf(&ret[idx],"%08lX%08lX",
			  (ulong)pk_keyid[0],(ulong)pk_keyid[1]);
		  idx+=16;
		  done=1;
		}
	      break;

	    case 'U': /* z-base-32 encoded user id hash. */
              if (args->namehash)
                {
                  char *tmp = zb32_encode (args->namehash, 8*20);
                  if (tmp)
                    {
                      if (idx + strlen (tmp) < maxlen)
                        {
                          strcpy (ret+idx, tmp);
                          idx += strlen (tmp);
                        }
                      xfree (tmp);
                      done = 1;
                    }
                }
	      break;

	    case 'c': /* signature count from card, if any. */
	      if(idx+10<maxlen)
		{
		  sprintf (&ret[idx],"%lu", get_signature_count (args->pksk));
		  idx+=strlen(&ret[idx]);
		  done=1;
		}
	      break;

	    case 'f': /* Fingerprint of key being signed */
	    case 'p': /* Fingerprint of the primary key making the signature. */
	    case 'g': /* Fingerprint of thge key making the signature.  */
	      {
		byte array[MAX_FINGERPRINT_LEN];
		size_t len;
		int i;

		if ((*(ch+1))=='f' && args->pk)
		  fingerprint_from_pk (args->pk, array, &len);
		else if ((*(ch+1))=='p' && args->pksk)
		  {
		    if(args->pksk->flags.primary)
		      fingerprint_from_pk (args->pksk, array, &len);
		    else if (args->pksk->main_keyid[0]
                             || args->pksk->main_keyid[1])
		      {
                        /* Not the primary key: Find the fingerprint
                           of the primary key.  */
			PKT_public_key *pk=
			  xmalloc_clear(sizeof(PKT_public_key));

			if (!get_pubkey_fast (pk,args->pksk->main_keyid))
			  fingerprint_from_pk (pk, array, &len);
			else
			  memset (array, 0, (len=MAX_FINGERPRINT_LEN));
			free_public_key (pk);
		      }
		    else /* Oops: info about the primary key missing.  */
		      memset(array,0,(len=MAX_FINGERPRINT_LEN));
		  }
		else if((*(ch+1))=='g' && args->pksk)
		  fingerprint_from_pk (args->pksk, array, &len);
		else
		  memset(array,0,(len=MAX_FINGERPRINT_LEN));

		if(idx+(len*2)<maxlen)
		  {
		    for(i=0;i<len;i++)
		      {
			sprintf(&ret[idx],"%02X",array[i]);
			idx+=2;
		      }
		    done=1;
		  }
	      }
	      break;

	    case 'v': /* validity letters */
	      if(args->validity_info && idx+1<maxlen)
		{
		  ret[idx++]=args->validity_info;
		  ret[idx]='\0';
		  done=1;
		}
	      break;

	      /* The text string types */
	    case 't':
	    case 'T':
	    case 'V':
	      {
		const char *str=NULL;

		switch(*(ch+1))
		  {
		  case 't': /* e.g. "jpg" */
		    str=image_type_to_string(args->imagetype,0);
		    break;

		  case 'T': /* e.g. "image/jpeg" */
		    str=image_type_to_string(args->imagetype,2);
		    break;

		  case 'V': /* e.g. "full", "expired", etc. */
		    str=args->validity_string;
		    break;
		  }

		if(str && idx+strlen(str)<maxlen)
		  {
		    strcpy(&ret[idx],str);
		    idx+=strlen(str);
		    done=1;
		  }
	      }
	      break;

	    case '%':
	      if(idx+1<maxlen)
		{
		  ret[idx++]='%';
		  ret[idx]='\0';
		  done=1;
		}
	      break;

	      /* Any unknown %-keys (like %i, %o, %I, and %O) are
		 passed through for later expansion.  Note this also
		 handles the case where the last character in the
		 string is a '%' - the terminating \0 will end up here
		 and properly terminate the string. */
	    default:
	      if(idx+2<maxlen)
		{
		  ret[idx++]='%';
		  ret[idx++]=*(ch+1);
		  ret[idx]='\0';
		  done=1;
		}
	      break;
	      }

	  if(done)
	    ch++;
	}
      else
	{
	  if(idx+1<maxlen)
	    {
	      ret[idx++]=*ch;
	      ret[idx]='\0';
	      done=1;
	    }
	}

      if(done)
	ch++;
    }

  return ret;

 fail:
  xfree(ret);
  return NULL;
}