decrypt_charstring(unsigned char *line, int len)
{
  int i;
  int32_t val;
  char buf[20];

  /* decrypt charstring */
  if (lenIV >= 0) {
    /* only decrypt if lenIV >= 0 -- negative lenIV means unencrypted
       charstring. Thanks to Tom Kacvinsky <tjk@ams.org> */
    uint16_t cr = cr_default;
    byte plain;
    for (i = 0; i < len; i++) {
      byte cipher = line[i];
      plain = (byte)(cipher ^ (cr >> 8));
      cr = (uint16_t)((cipher + cr) * c1 + c2);
      line[i] = plain;
    }
    line += lenIV;
    len -= lenIV;
  }

  /* handle each charstring command */
  for (i = 0; i < len; i++) {
    byte b = line[i];

    if (b >= 32) {
      if (b >= 32 && b <= 246)
	val = b - 139;
      else if (b >= 247 && b <= 250) {
	i++;
	val = (b - 247)*256 + 108 + line[i];
      } else if (b >= 251 && b <= 254) {
	i++;
	val = -(b - 251)*256 - 108 - line[i];
      } else {
	val =  (line[i+1] & 0xff) << 24;
	val |= (line[i+2] & 0xff) << 16;
	val |= (line[i+3] & 0xff) <<  8;
	val |= (line[i+4] & 0xff) <<  0;
	/* in case an int32 is larger than four bytes---sign extend */
#if INT_MAX > 0x7FFFFFFFUL
	if (val & 0x80000000)
	  val |= ~0x7FFFFFFF;
#endif
	i += 4;
      }
      sprintf(buf, "%d", val);
      output_token(buf);

    } else {
      switch (b) {
      case 0: output_token("error"); break;		/* special */
      case 1: output_token("hstem"); break;
      case 3: output_token("vstem"); break;
      case 4: output_token("vmoveto"); break;
      case 5: output_token("rlineto"); break;
      case 6: output_token("hlineto"); break;
      case 7: output_token("vlineto"); break;
      case 8: output_token("rrcurveto"); break;
      case 9: output_token("closepath"); break;		/* Type 1 ONLY */
      case 10: output_token("callsubr"); break;
      case 11: output_token("return"); break;
      case 13: output_token("hsbw"); break;		/* Type 1 ONLY */
      case 14: output_token("endchar"); break;
      case 16: output_token("blend"); break;		/* Type 2 */
      case 18: output_token("hstemhm"); break;		/* Type 2 */
      case 19: output_token("hintmask"); break;		/* Type 2 */
      case 20: output_token("cntrmask"); break;		/* Type 2 */
      case 21: output_token("rmoveto"); break;
      case 22: output_token("hmoveto"); break;
      case 23: output_token("vstemhm"); break;		/* Type 2 */
      case 24: output_token("rcurveline"); break;	/* Type 2 */
      case 25: output_token("rlinecurve"); break;	/* Type 2 */
      case 26: output_token("vvcurveto"); break;	/* Type 2 */
      case 27: output_token("hhcurveto"); break;	/* Type 2 */
      case 28: {		/* Type 2 */
	/* short integer */
	val =  (line[i+1] & 0xff) << 8;
	val |= (line[i+2] & 0xff);
	i += 2;
	if (val & 0x8000)
	  val |= ~0x7FFF;
	sprintf(buf, "%d", val);
	output_token(buf);
      }
      case 29: output_token("callgsubr"); break;	/* Type 2 */
      case 30: output_token("vhcurveto"); break;
      case 31: output_token("hvcurveto"); break;
      case 12:
	i++;
	b = line[i];
	switch (b) {
	case 0: output_token("dotsection"); break;	/* Type 1 ONLY */
	case 1: output_token("vstem3"); break;		/* Type 1 ONLY */
	case 2: output_token("hstem3"); break;		/* Type 1 ONLY */
	case 3: output_token("and"); break;		/* Type 2 */
	case 4: output_token("or"); break;		/* Type 2 */
	case 5: output_token("not"); break;		/* Type 2 */
	case 6: output_token("seac"); break;		/* Type 1 ONLY */
	case 7: output_token("sbw"); break;		/* Type 1 ONLY */
	case 8: output_token("store"); break;		/* Type 2 */
	case 9: output_token("abs"); break;		/* Type 2 */
	case 10: output_token("add"); break;		/* Type 2 */
	case 11: output_token("sub"); break;		/* Type 2 */
	case 12: output_token("div"); break;
	case 13: output_token("load"); break;		/* Type 2 */
	case 14: output_token("neg"); break;		/* Type 2 */
	case 15: output_token("eq"); break;		/* Type 2 */
	case 16: output_token("callothersubr"); break;	/* Type 1 ONLY */
	case 17: output_token("pop"); break;		/* Type 1 ONLY */
	case 18: output_token("drop"); break;		/* Type 2 */
	case 20: output_token("put"); break;		/* Type 2 */
	case 21: output_token("get"); break;		/* Type 2 */
	case 22: output_token("ifelse"); break;		/* Type 2 */
	case 23: output_token("random"); break;		/* Type 2 */
	case 24: output_token("mul"); break;		/* Type 2 */
	case 26: output_token("sqrt"); break;		/* Type 2 */
	case 27: output_token("dup"); break;		/* Type 2 */
	case 28: output_token("exch"); break;		/* Type 2 */
	case 29: output_token("index"); break;		/* Type 2 */
	case 30: output_token("roll"); break;		/* Type 2 */
	case 33: output_token("setcurrentpoint"); break;/* Type 1 ONLY */
	case 34: output_token("hflex"); break;		/* Type 2 */
	case 35: output_token("flex"); break;		/* Type 2 */
	case 36: output_token("hflex1"); break;		/* Type 2 */
	case 37: output_token("flex1"); break;		/* Type 2 */
	default:
	  sprintf(buf, "escape_%d", b);
	  unknown++;
	  output_token(buf);
	  break;
	}
	break;
      default:
       sprintf(buf, "UNKNOWN_%d", b);
       unknown++;
       output_token(buf);
       break;
      }
      output_token("\n");
    }
  }
  if (i > len) {
    output("\terror\n");
    error("disassembly error: charstring too short");
  }
}