/*
 * otpcode.c --
 *
 *      Implements and registers conversion from and to OTP's english words.
 *
 *
 * Copyright (c) 1999 Marshall Rose (mrose@dbc.mtview.ca.us)
 * All rights reserved.
 *
 * Permission is hereby granted, without written agreement and without
 * license or royalty fees, to use, copy, modify, and distribute this
 * software and its documentation for any purpose, provided that the
 * above copyright notice and the following two paragraphs appear in
 * all copies of this software.
 *
 * IN NO EVENT SHALL I LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, SPECIAL,
 * INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OF THIS
 * SOFTWARE AND ITS DOCUMENTATION, EVEN IF I HAVE BEEN ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 * I SPECIFICALLY DISCLAIM ANY WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND
 * I HAVE NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES,
 * ENHANCEMENTS, OR MODIFICATIONS.
 *
 * CVS: $Id: otpcode.c,v 1.7 2009/05/07 04:57:27 andreas_kupries Exp $
 */

#include <ctype.h>
#include "transformInt.h"

/*
 * Converter description
 * ---------------------
 *
 * Reference:
 *      RFC 2289
 *
 * Encoding:
 *      Each sequence of 64-bits is converted to OTP's 6 english words.
 *      For ease of readability, a newline is written after each 6 words.
 *
 * Decoding:
 *      Each 6 word sequence is converted to 64-bits. 
 */


/*
 * Declarations of internal procedures.
 */

static Trf_ControlBlock CreateEncoder  _ANSI_ARGS_ ((ClientData writeClientData,
                                                     Trf_WriteProc *fun,
                                                     Trf_Options optInfo,
                                                     Tcl_Interp*   interp,
                                                     ClientData clientData));
static void             DeleteEncoder  _ANSI_ARGS_ ((Trf_ControlBlock ctrlBlock,
                                                     ClientData clientData));
static int              Encode         _ANSI_ARGS_ ((Trf_ControlBlock ctrlBlock,
                                                     unsigned int character,
                                                     Tcl_Interp* interp,
                                                     ClientData clientData));
static int              EncodeBuffer   _ANSI_ARGS_ ((Trf_ControlBlock ctrlBlock,
                                                     unsigned char* buffer, int bufLen,
                                                     Tcl_Interp* interp,
                                                     ClientData clientData));
static int              FlushEncoder   _ANSI_ARGS_ ((Trf_ControlBlock ctrlBlock,
                                                     Tcl_Interp* interp,
                                                     ClientData clientData));
static void             ClearEncoder   _ANSI_ARGS_ ((Trf_ControlBlock ctrlBlock,
                                                     ClientData clientData));

static Trf_ControlBlock CreateDecoder  _ANSI_ARGS_ ((ClientData writeClientData,
                                                     Trf_WriteProc *fun,
                                                     Trf_Options optInfo,
                                                     Tcl_Interp*   interp,
                                                     ClientData clientData));
static void             DeleteDecoder  _ANSI_ARGS_ ((Trf_ControlBlock ctrlBlock,
                                                     ClientData clientData));
static int              Decode         _ANSI_ARGS_ ((Trf_ControlBlock ctrlBlock,
                                                     unsigned int character,
                                                     Tcl_Interp* interp,
                                                     ClientData clientData));
static int              DecodeBuffer   _ANSI_ARGS_ ((Trf_ControlBlock ctrlBlock,
                                                     unsigned char* buffer, int bufLen,
                                                     Tcl_Interp* interp,
                                                     ClientData clientData));
static int              FlushDecoder   _ANSI_ARGS_ ((Trf_ControlBlock ctrlBlock,
                                                     Tcl_Interp* interp,
                                                     ClientData clientData));
static void             ClearDecoder   _ANSI_ARGS_ ((Trf_ControlBlock ctrlBlock,
                                                     ClientData clientData));


/*
 * Converter definition.
 */

static Trf_TypeDefinition convDefinition =
{
  "otp_words",
  NULL, /* clientData not used by conversions. */
  NULL, /* set later by TrfInit_OTP_WORDS, THREAD: serialize initialization */
  {
    CreateEncoder,
    DeleteEncoder,
    Encode,
    EncodeBuffer,
    FlushEncoder,
    ClearEncoder,
    NULL /* no MaxRead */
  }, {
    CreateDecoder,
    DeleteDecoder,
    Decode,
    DecodeBuffer,
    FlushDecoder,
    ClearDecoder,
    NULL /* no MaxRead */
  },
  TRF_UNSEEKABLE
};

/*
 * Definition of the control blocks for en- and decoder.
 */

typedef struct _EncoderControl_ {
    Trf_WriteProc* write;
    ClientData     writeClientData;

    /* add conversion specific items here (otp_words encode) */

    int    charCount;
    unsigned char buf[8];

} EncoderControl;


typedef struct _DecoderControl_ {
    Trf_WriteProc* write;
    ClientData     writeClientData;

    /* add conversion specific items here (otp_words decode) */

    int    charCount;
    int    wordCount;
    char   words[6][5];

} DecoderControl;


/* taken from RFC 2289... */

static unsigned long    extract         _ANSI_ARGS_ ((char *s,
                                                      int start,
                                                      int length));
static void             insert          _ANSI_ARGS_ ((char *s,
                                                      int x,
                                                      int start,
                                                      int length));
static int              wsrch           _ANSI_ARGS_ ((char *w,
                                                      int low,
                                                      int high));

/* Dictionary for integer-word translations */
static char Wp[2048][4] = { /* THREADING: constant, read-only => safe */
"A",
"ABE",
"ACE",
"ACT",
"AD",
"ADA",
"ADD",
"AGO",
"AID",
"AIM",
"AIR",
"ALL",
"ALP",
"AM",
"AMY",
"AN",
"ANA",
"AND",
"ANN",
"ANT",
"ANY",
"APE",
"APS",
"APT",
"ARC",
"ARE",
"ARK",
"ARM",
"ART",
"AS",
"ASH",
"ASK",
"AT",
"ATE",
"AUG",
"AUK",
"AVE",
"AWE",
"AWK",
"AWL",
"AWN",
"AX",
"AYE",
"BAD",
"BAG",
"BAH",
"BAM",
"BAN",
"BAR",
"BAT",
"BAY",
"BE",
"BED",
"BEE",
"BEG",
"BEN",
"BET",
"BEY",
"BIB",
"BID",
"BIG",
"BIN",
"BIT",
"BOB",
"BOG",
"BON",
"BOO",
"BOP",
"BOW",
"BOY",
"BUB",
"BUD",
"BUG",
"BUM",
"BUN",
"BUS",
"BUT",
"BUY",
"BY",
"BYE",
"CAB",
"CAL",
"CAM",
"CAN",
"CAP",
"CAR",
"CAT",
"CAW",
"COD",
"COG",
"COL",
"CON",
"COO",
"COP",
"COT",
"COW",
"COY",
"CRY",
"CUB",
"CUE",
"CUP",
"CUR",
"CUT",
"DAB",
"DAD",
"DAM",
"DAN",
"DAR",
"DAY",
"DEE",
"DEL",
"DEN",
"DES",
"DEW",
"DID",
"DIE",
"DIG",
"DIN",
"DIP",
"DO",
"DOE",
"DOG",
"DON",
"DOT",
"DOW",
"DRY",
"DUB",
"DUD",
"DUE",
"DUG",
"DUN",
"EAR",
"EAT",
"ED",
"EEL",
"EGG",
"EGO",
"ELI",
"ELK",
"ELM",
"ELY",
"EM",
"END",
"EST",
"ETC",
"EVA",
"EVE",
"EWE",
"EYE",
"FAD",
"FAN",
"FAR",
"FAT",
"FAY",
"FED",
"FEE",
"FEW",
"FIB",
"FIG",
"FIN",
"FIR",
"FIT",
"FLO",
"FLY",
"FOE",
"FOG",
"FOR",
"FRY",
"FUM",
"FUN",
"FUR",
"GAB",
"GAD",
"GAG",
"GAL",
"GAM",
"GAP",
"GAS",
"GAY",
"GEE",
"GEL",
"GEM",
"GET",
"GIG",
"GIL",
"GIN",
"GO",
"GOT",
"GUM",
"GUN",
"GUS",
"GUT",
"GUY",
"GYM",
"GYP",
"HA",
"HAD",
"HAL",
"HAM",
"HAN",
"HAP",
"HAS",
"HAT",
"HAW",
"HAY",
"HE",
"HEM",
"HEN",
"HER",
"HEW",
"HEY",
"HI",
"HID",
"HIM",
"HIP",
"HIS",
"HIT",
"HO",
"HOB",
"HOC",
"HOE",
"HOG",
"HOP",
"HOT",
"HOW",
"HUB",
"HUE",
"HUG",
"HUH",
"HUM",
"HUT",
"I",
"ICY",
"IDA",
"IF",
"IKE",
"ILL",
"INK",
"INN",
"IO",
"ION",
"IQ",
"IRA",
"IRE",
"IRK",
"IS",
"IT",
"ITS",
"IVY",
"JAB",
"JAG",
"JAM",
"JAN",
"JAR",
"JAW",
"JAY",
"JET",
"JIG",
"JIM",
"JO",
"JOB",
"JOE",
"JOG",
"JOT",
"JOY",
"JUG",
"JUT",
"KAY",
"KEG",
"KEN",
"KEY",
"KID",
"KIM",
"KIN",
"KIT",
"LA",
"LAB",
"LAC",
"LAD",
"LAG",
"LAM",
"LAP",
"LAW",
"LAY",
"LEA",
"LED",
"LEE",
"LEG",
"LEN",
"LEO",
"LET",
"LEW",
"LID",
"LIE",
"LIN",
"LIP",
"LIT",
"LO",
"LOB",
"LOG",
"LOP",
"LOS",
"LOT",
"LOU",
"LOW",
"LOY",
"LUG",
"LYE",
"MA",
"MAC",
"MAD",
"MAE",
"MAN",
"MAO",
"MAP",
"MAT",
"MAW",
"MAY",
"ME",
"MEG",
"MEL",
"MEN",
"MET",
"MEW",
"MID",
"MIN",
"MIT",
"MOB",
"MOD",
"MOE",
"MOO",
"MOP",
"MOS",
"MOT",
"MOW",
"MUD",
"MUG",
"MUM",
"MY",
"NAB",
"NAG",
"NAN",
"NAP",
"NAT",
"NAY",
"NE",
"NED",
"NEE",
"NET",
"NEW",
"NIB",
"NIL",
"NIP",
"NIT",
"NO",
"NOB",
"NOD",
"NON",
"NOR",
"NOT",
"NOV",
"NOW",
"NU",
"NUN",
"NUT",
"O",
"OAF",
"OAK",
"OAR",
"OAT",
"ODD",
"ODE",
"OF",
"OFF",
"OFT",
"OH",
"OIL",
"OK",
"OLD",
"ON",
"ONE",
"OR",
"ORB",
"ORE",
"ORR",
"OS",
"OTT",
"OUR",
"OUT",
"OVA",
"OW",
"OWE",
"OWL",
"OWN",
"OX",
"PA",
"PAD",
"PAL",
"PAM",
"PAN",
"PAP",
"PAR",
"PAT",
"PAW",
"PAY",
"PEA",
"PEG",
"PEN",
"PEP",
"PER",
"PET",
"PEW",
"PHI",
"PI",
"PIE",
"PIN",
"PIT",
"PLY",
"PO",
"POD",
"POE",
"POP",
"POT",
"POW",
"PRO",
"PRY",
"PUB",
"PUG",
"PUN",
"PUP",
"PUT",
"QUO",
"RAG",
"RAM",
"RAN",
"RAP",
"RAT",
"RAW",
"RAY",
"REB",
"RED",
"REP",
"RET",
"RIB",
"RID",
"RIG",
"RIM",
"RIO",
"RIP",
"ROB",
"ROD",
"ROE",
"RON",
"ROT",
"ROW",
"ROY",
"RUB",
"RUE",
"RUG",
"RUM",
"RUN",
"RYE",
"SAC",
"SAD",
"SAG",
"SAL",
"SAM",
"SAN",
"SAP",
"SAT",
"SAW",
"SAY",
"SEA",
"SEC",
"SEE",
"SEN",
"SET",
"SEW",
"SHE",
"SHY",
"SIN",
"SIP",
"SIR",
"SIS",
"SIT",
"SKI",
"SKY",
"SLY",
"SO",
"SOB",
"SOD",
"SON",
"SOP",
"SOW",
"SOY",
"SPA",
"SPY",
"SUB",
"SUD",
"SUE",
"SUM",
"SUN",
"SUP",
"TAB",
"TAD",
"TAG",
"TAN",
"TAP",
"TAR",
"TEA",
"TED",
"TEE",
"TEN",
"THE",
"THY",
"TIC",
"TIE",
"TIM",
"TIN",
"TIP",
"TO",
"TOE",
"TOG",
"TOM",
"TON",
"TOO",
"TOP",
"TOW",
"TOY",
"TRY",
"TUB",
"TUG",
"TUM",
"TUN",
"TWO",
"UN",
"UP",
"US",
"USE",
"VAN",
"VAT",
"VET",
"VIE",
"WAD",
"WAG",
"WAR",
"WAS",
"WAY",
"WE",
"WEB",
"WED",
"WEE",
"WET",
"WHO",
"WHY",
"WIN",
"WIT",
"WOK",
"WON",
"WOO",
"WOW",
"WRY",
"WU",
"YAM",
"YAP",
"YAW",
"YE",
"YEA",
"YES",
"YET",
"YOU",
"ABED",
"ABEL",
"ABET",
"ABLE",
"ABUT",
"ACHE",
"ACID",
"ACME",
"ACRE",
"ACTA",
"ACTS",
"ADAM",
"ADDS",
"ADEN",
"AFAR",
"AFRO",
"AGEE",
"AHEM",
"AHOY",
"AIDA",
"AIDE",
"AIDS",
"AIRY",
"AJAR",
"AKIN",
"ALAN",
"ALEC",
"ALGA",
"ALIA",
"ALLY",
"ALMA",
"ALOE",
"ALSO",
"ALTO",
"ALUM",
"ALVA",
"AMEN",
"AMES",
"AMID",
"AMMO",
"AMOK",
"AMOS",
"AMRA",
"ANDY",
"ANEW",
"ANNA",
"ANNE",
"ANTE",
"ANTI",
"AQUA",
"ARAB",
"ARCH",
"AREA",
"ARGO",
"ARID",
"ARMY",
"ARTS",
"ARTY",
"ASIA",
"ASKS",
"ATOM",
"AUNT",
"AURA",
"AUTO",
"AVER",
"AVID",
"AVIS",
"AVON",
"AVOW",
"AWAY",
"AWRY",
"BABE",
"BABY",
"BACH",
"BACK",
"BADE",
"BAIL",
"BAIT",
"BAKE",
"BALD",
"BALE",
"BALI",
"BALK",
"BALL",
"BALM",
"BAND",
"BANE",
"BANG",
"BANK",
"BARB",
"BARD",
"BARE",
"BARK",
"BARN",
"BARR",
"BASE",
"BASH",
"BASK",
"BASS",
"BATE",
"BATH",
"BAWD",
"BAWL",
"BEAD",
"BEAK",
"BEAM",
"BEAN",
"BEAR",
"BEAT",
"BEAU",
"BECK",
"BEEF",
"BEEN",
"BEER",
"BEET",
"BELA",
"BELL",
"BELT",
"BEND",
"BENT",
"BERG",
"BERN",
"BERT",
"BESS",
"BEST",
"BETA",
"BETH",
"BHOY",
"BIAS",
"BIDE",
"BIEN",
"BILE",
"BILK",
"BILL",
"BIND",
"BING",
"BIRD",
"BITE",
"BITS",
"BLAB",
"BLAT",
"BLED",
"BLEW",
"BLOB",
"BLOC",
"BLOT",
"BLOW",
"BLUE",
"BLUM",
"BLUR",
"BOAR",
"BOAT",
"BOCA",
"BOCK",
"BODE",
"BODY",
"BOGY",
"BOHR",
"BOIL",
"BOLD",
"BOLO",
"BOLT",
"BOMB",
"BONA",
"BOND",
"BONE",
"BONG",
"BONN",
"BONY",
"BOOK",
"BOOM",
"BOON",
"BOOT",
"BORE",
"BORG",
"BORN",
"BOSE",
"BOSS",
"BOTH",
"BOUT",
"BOWL",
"BOYD",
"BRAD",
"BRAE",
"BRAG",
"BRAN",
"BRAY",
"BRED",
"BREW",
"BRIG",
"BRIM",
"BROW",
"BUCK",
"BUDD",
"BUFF",
"BULB",
"BULK",
"BULL",
"BUNK",
"BUNT",
"BUOY",
"BURG",
"BURL",
"BURN",
"BURR",
"BURT",
"BURY",
"BUSH",
"BUSS",
"BUST",
"BUSY",
"BYTE",
"CADY",
"CAFE",
"CAGE",
"CAIN",
"CAKE",
"CALF",
"CALL",
"CALM",
"CAME",
"CANE",
"CANT",
"CARD",
"CARE",
"CARL",
"CARR",
"CART",
"CASE",
"CASH",
"CASK",
"CAST",
"CAVE",
"CEIL",
"CELL",
"CENT",
"CERN",
"CHAD",
"CHAR",
"CHAT",
"CHAW",
"CHEF",
"CHEN",
"CHEW",
"CHIC",
"CHIN",
"CHOU",
"CHOW",
"CHUB",
"CHUG",
"CHUM",
"CITE",
"CITY",
"CLAD",
"CLAM",
"CLAN",
"CLAW",
"CLAY",
"CLOD",
"CLOG",
"CLOT",
"CLUB",
"CLUE",
"COAL",
"COAT",
"COCA",
"COCK",
"COCO",
"CODA",
"CODE",
"CODY",
"COED",
"COIL",
"COIN",
"COKE",
"COLA",
"COLD",
"COLT",
"COMA",
"COMB",
"COME",
"COOK",
"COOL",
"COON",
"COOT",
"CORD",
"CORE",
"CORK",
"CORN",
"COST",
"COVE",
"COWL",
"CRAB",
"CRAG",
"CRAM",
"CRAY",
"CREW",
"CRIB",
"CROW",
"CRUD",
"CUBA",
"CUBE",
"CUFF",
"CULL",
"CULT",
"CUNY",
"CURB",
"CURD",
"CURE",
"CURL",
"CURT",
"CUTS",
"DADE",
"DALE",
"DAME",
"DANA",
"DANE",
"DANG",
"DANK",
"DARE",
"DARK",
"DARN",
"DART",
"DASH",
"DATA",
"DATE",
"DAVE",
"DAVY",
"DAWN",
"DAYS",
"DEAD",
"DEAF",
"DEAL",
"DEAN",
"DEAR",
"DEBT",
"DECK",
"DEED",
"DEEM",
"DEER",
"DEFT",
"DEFY",
"DELL",
"DENT",
"DENY",
"DESK",
"DIAL",
"DICE",
"DIED",
"DIET",
"DIME",
"DINE",
"DING",
"DINT",
"DIRE",
"DIRT",
"DISC",
"DISH",
"DISK",
"DIVE",
"DOCK",
"DOES",
"DOLE",
"DOLL",
"DOLT",
"DOME",
"DONE",
"DOOM",
"DOOR",
"DORA",
"DOSE",
"DOTE",
"DOUG",
"DOUR",
"DOVE",
"DOWN",
"DRAB",
"DRAG",
"DRAM",
"DRAW",
"DREW",
"DRUB",
"DRUG",
"DRUM",
"DUAL",
"DUCK",
"DUCT",
"DUEL",
"DUET",
"DUKE",
"DULL",
"DUMB",
"DUNE",
"DUNK",
"DUSK",
"DUST",
"DUTY",
"EACH",
"EARL",
"EARN",
"EASE",
"EAST",
"EASY",
"EBEN",
"ECHO",
"EDDY",
"EDEN",
"EDGE",
"EDGY",
"EDIT",
"EDNA",
"EGAN",
"ELAN",
"ELBA",
"ELLA",
"ELSE",
"EMIL",
"EMIT",
"EMMA",
"ENDS",
"ERIC",
"EROS",
"EVEN",
"EVER",
"EVIL",
"EYED",
"FACE",
"FACT",
"FADE",
"FAIL",
"FAIN",
"FAIR",
"FAKE",
"FALL",
"FAME",
"FANG",
"FARM",
"FAST",
"FATE",
"FAWN",
"FEAR",
"FEAT",
"FEED",
"FEEL",
"FEET",
"FELL",
"FELT",
"FEND",
"FERN",
"FEST",
"FEUD",
"FIEF",
"FIGS",
"FILE",
"FILL",
"FILM",
"FIND",
"FINE",
"FINK",
"FIRE",
"FIRM",
"FISH",
"FISK",
"FIST",
"FITS",
"FIVE",
"FLAG",
"FLAK",
"FLAM",
"FLAT",
"FLAW",
"FLEA",
"FLED",
"FLEW",
"FLIT",
"FLOC",
"FLOG",
"FLOW",
"FLUB",
"FLUE",
"FOAL",
"FOAM",
"FOGY",
"FOIL",
"FOLD",
"FOLK",
"FOND",
"FONT",
"FOOD",
"FOOL",
"FOOT",
"FORD",
"FORE",
"FORK",
"FORM",
"FORT",
"FOSS",
"FOUL",
"FOUR",
"FOWL",
"FRAU",
"FRAY",
"FRED",
"FREE",
"FRET",
"FREY",
"FROG",
"FROM",
"FUEL",
"FULL",
"FUME",
"FUND",
"FUNK",
"FURY",
"FUSE",
"FUSS",
"GAFF",
"GAGE",
"GAIL",
"GAIN",
"GAIT",
"GALA",
"GALE",
"GALL",
"GALT",
"GAME",
"GANG",
"GARB",
"GARY",
"GASH",
"GATE",
"GAUL",
"GAUR",
"GAVE",
"GAWK",
"GEAR",
"GELD",
"GENE",
"GENT",
"GERM",
"GETS",
"GIBE",
"GIFT",
"GILD",
"GILL",
"GILT",
"GINA",
"GIRD",
"GIRL",
"GIST",
"GIVE",
"GLAD",
"GLEE",
"GLEN",
"GLIB",
"GLOB",
"GLOM",
"GLOW",
"GLUE",
"GLUM",
"GLUT",
"GOAD",
"GOAL",
"GOAT",
"GOER",
"GOES",
"GOLD",
"GOLF",
"GONE",
"GONG",
"GOOD",
"GOOF",
"GORE",
"GORY",
"GOSH",
"GOUT",
"GOWN",
"GRAB",
"GRAD",
"GRAY",
"GREG",
"GREW",
"GREY",
"GRID",
"GRIM",
"GRIN",
"GRIT",
"GROW",
"GRUB",
"GULF",
"GULL",
"GUNK",
"GURU",
"GUSH",
"GUST",
"GWEN",
"GWYN",
"HAAG",
"HAAS",
"HACK",
"HAIL",
"HAIR",
"HALE",
"HALF",
"HALL",
"HALO",
"HALT",
"HAND",
"HANG",
"HANK",
"HANS",
"HARD",
"HARK",
"HARM",
"HART",
"HASH",
"HAST",
"HATE",
"HATH",
"HAUL",
"HAVE",
"HAWK",
"HAYS",
"HEAD",
"HEAL",
"HEAR",
"HEAT",
"HEBE",
"HECK",
"HEED",
"HEEL",
"HEFT",
"HELD",
"HELL",
"HELM",
"HERB",
"HERD",
"HERE",
"HERO",
"HERS",
"HESS",
"HEWN",
"HICK",
"HIDE",
"HIGH",
"HIKE",
"HILL",
"HILT",
"HIND",
"HINT",
"HIRE",
"HISS",
"HIVE",
"HOBO",
"HOCK",
"HOFF",
"HOLD",
"HOLE",
"HOLM",
"HOLT",
"HOME",
"HONE",
"HONK",
"HOOD",
"HOOF",
"HOOK",
"HOOT",
"HORN",
"HOSE",
"HOST",
"HOUR",
"HOVE",
"HOWE",
"HOWL",
"HOYT",
"HUCK",
"HUED",
"HUFF",
"HUGE",
"HUGH",
"HUGO",
"HULK",
"HULL",
"HUNK",
"HUNT",
"HURD",
"HURL",
"HURT",
"HUSH",
"HYDE",
"HYMN",
"IBIS",
"ICON",
"IDEA",
"IDLE",
"IFFY",
"INCA",
"INCH",
"INTO",
"IONS",
"IOTA",
"IOWA",
"IRIS",
"IRMA",
"IRON",
"ISLE",
"ITCH",
"ITEM",
"IVAN",
"JACK",
"JADE",
"JAIL",
"JAKE",
"JANE",
"JAVA",
"JEAN",
"JEFF",
"JERK",
"JESS",
"JEST",
"JIBE",
"JILL",
"JILT",
"JIVE",
"JOAN",
"JOBS",
"JOCK",
"JOEL",
"JOEY",
"JOHN",
"JOIN",
"JOKE",
"JOLT",
"JOVE",
"JUDD",
"JUDE",
"JUDO",
"JUDY",
"JUJU",
"JUKE",
"JULY",
"JUNE",
"JUNK",
"JUNO",
"JURY",
"JUST",
"JUTE",
"KAHN",
"KALE",
"KANE",
"KANT",
"KARL",
"KATE",
"KEEL",
"KEEN",
"KENO",
"KENT",
"KERN",
"KERR",
"KEYS",
"KICK",
"KILL",
"KIND",
"KING",
"KIRK",
"KISS",
"KITE",
"KLAN",
"KNEE",
"KNEW",
"KNIT",
"KNOB",
"KNOT",
"KNOW",
"KOCH",
"KONG",
"KUDO",
"KURD",
"KURT",
"KYLE",
"LACE",
"LACK",
"LACY",
"LADY",
"LAID",
"LAIN",
"LAIR",
"LAKE",
"LAMB",
"LAME",
"LAND",
"LANE",
"LANG",
"LARD",
"LARK",
"LASS",
"LAST",
"LATE",
"LAUD",
"LAVA",
"LAWN",
"LAWS",
"LAYS",
"LEAD",
"LEAF",
"LEAK",
"LEAN",
"LEAR",
"LEEK",
"LEER",
"LEFT",
"LEND",
"LENS",
"LENT",
"LEON",
"LESK",
"LESS",
"LEST",
"LETS",
"LIAR",
"LICE",
"LICK",
"LIED",
"LIEN",
"LIES",
"LIEU",
"LIFE",
"LIFT",
"LIKE",
"LILA",
"LILT",
"LILY",
"LIMA",
"LIMB",
"LIME",
"LIND",
"LINE",
"LINK",
"LINT",
"LION",
"LISA",
"LIST",
"LIVE",
"LOAD",
"LOAF",
"LOAM",
"LOAN",
"LOCK",
"LOFT",
"LOGE",
"LOIS",
"LOLA",
"LONE",
"LONG",
"LOOK",
"LOON",
"LOOT",
"LORD",
"LORE",
"LOSE",
"LOSS",
"LOST",
"LOUD",
"LOVE",
"LOWE",
"LUCK",
"LUCY",
"LUGE",
"LUKE",
"LULU",
"LUND",
"LUNG",
"LURA",
"LURE",
"LURK",
"LUSH",
"LUST",
"LYLE",
"LYNN",
"LYON",
"LYRA",
"MACE",
"MADE",
"MAGI",
"MAID",
"MAIL",
"MAIN",
"MAKE",
"MALE",
"MALI",
"MALL",
"MALT",
"MANA",
"MANN",
"MANY",
"MARC",
"MARE",
"MARK",
"MARS",
"MART",
"MARY",
"MASH",
"MASK",
"MASS",
"MAST",
"MATE",
"MATH",
"MAUL",
"MAYO",
"MEAD",
"MEAL",
"MEAN",
"MEAT",
"MEEK",
"MEET",
"MELD",
"MELT",
"MEMO",
"MEND",
"MENU",
"MERT",
"MESH",
"MESS",
"MICE",
"MIKE",
"MILD",
"MILE",
"MILK",
"MILL",
"MILT",
"MIMI",
"MIND",
"MINE",
"MINI",
"MINK",
"MINT",
"MIRE",
"MISS",
"MIST",
"MITE",
"MITT",
"MOAN",
"MOAT",
"MOCK",
"MODE",
"MOLD",
"MOLE",
"MOLL",
"MOLT",
"MONA",
"MONK",
"MONT",
"MOOD",
"MOON",
"MOOR",
"MOOT",
"MORE",
"MORN",
"MORT",
"MOSS",
"MOST",
"MOTH",
"MOVE",
"MUCH",
"MUCK",
"MUDD",
"MUFF",
"MULE",
"MULL",
"MURK",
"MUSH",
"MUST",
"MUTE",
"MUTT",
"MYRA",
"MYTH",
"NAGY",
"NAIL",
"NAIR",
"NAME",
"NARY",
"NASH",
"NAVE",
"NAVY",
"NEAL",
"NEAR",
"NEAT",
"NECK",
"NEED",
"NEIL",
"NELL",
"NEON",
"NERO",
"NESS",
"NEST",
"NEWS",
"NEWT",
"NIBS",
"NICE",
"NICK",
"NILE",
"NINA",
"NINE",
"NOAH",
"NODE",
"NOEL",
"NOLL",
"NONE",
"NOOK",
"NOON",
"NORM",
"NOSE",
"NOTE",
"NOUN",
"NOVA",
"NUDE",
"NULL",
"NUMB",
"OATH",
"OBEY",
"OBOE",
"ODIN",
"OHIO",
"OILY",
"OINT",
"OKAY",
"OLAF",
"OLDY",
"OLGA",
"OLIN",
"OMAN",
"OMEN",
"OMIT",
"ONCE",
"ONES",
"ONLY",
"ONTO",
"ONUS",
"ORAL",
"ORGY",
"OSLO",
"OTIS",
"OTTO",
"OUCH",
"OUST",
"OUTS",
"OVAL",
"OVEN",
"OVER",
"OWLY",
"OWNS",
"QUAD",
"QUIT",
"QUOD",
"RACE",
"RACK",
"RACY",
"RAFT",
"RAGE",
"RAID",
"RAIL",
"RAIN",
"RAKE",
"RANK",
"RANT",
"RARE",
"RASH",
"RATE",
"RAVE",
"RAYS",
"READ",
"REAL",
"REAM",
"REAR",
"RECK",
"REED",
"REEF",
"REEK",
"REEL",
"REID",
"REIN",
"RENA",
"REND",
"RENT",
"REST",
"RICE",
"RICH",
"RICK",
"RIDE",
"RIFT",
"RILL",
"RIME",
"RING",
"RINK",
"RISE",
"RISK",
"RITE",
"ROAD",
"ROAM",
"ROAR",
"ROBE",
"ROCK",
"RODE",
"ROIL",
"ROLL",
"ROME",
"ROOD",
"ROOF",
"ROOK",
"ROOM",
"ROOT",
"ROSA",
"ROSE",
"ROSS",
"ROSY",
"ROTH",
"ROUT",
"ROVE",
"ROWE",
"ROWS",
"RUBE",
"RUBY",
"RUDE",
"RUDY",
"RUIN",
"RULE",
"RUNG",
"RUNS",
"RUNT",
"RUSE",
"RUSH",
"RUSK",
"RUSS",
"RUST",
"RUTH",
"SACK",
"SAFE",
"SAGE",
"SAID",
"SAIL",
"SALE",
"SALK",
"SALT",
"SAME",
"SAND",
"SANE",
"SANG",
"SANK",
"SARA",
"SAUL",
"SAVE",
"SAYS",
"SCAN",
"SCAR",
"SCAT",
"SCOT",
"SEAL",
"SEAM",
"SEAR",
"SEAT",
"SEED",
"SEEK",
"SEEM",
"SEEN",
"SEES",
"SELF",
"SELL",
"SEND",
"SENT",
"SETS",
"SEWN",
"SHAG",
"SHAM",
"SHAW",
"SHAY",
"SHED",
"SHIM",
"SHIN",
"SHOD",
"SHOE",
"SHOT",
"SHOW",
"SHUN",
"SHUT",
"SICK",
"SIDE",
"SIFT",
"SIGH",
"SIGN",
"SILK",
"SILL",
"SILO",
"SILT",
"SINE",
"SING",
"SINK",
"SIRE",
"SITE",
"SITS",
"SITU",
"SKAT",
"SKEW",
"SKID",
"SKIM",
"SKIN",
"SKIT",
"SLAB",
"SLAM",
"SLAT",
"SLAY",
"SLED",
"SLEW",
"SLID",
"SLIM",
"SLIT",
"SLOB",
"SLOG",
"SLOT",
"SLOW",
"SLUG",
"SLUM",
"SLUR",
"SMOG",
"SMUG",
"SNAG",
"SNOB",
"SNOW",
"SNUB",
"SNUG",
"SOAK",
"SOAR",
"SOCK",
"SODA",
"SOFA",
"SOFT",
"SOIL",
"SOLD",
"SOME",
"SONG",
"SOON",
"SOOT",
"SORE",
"SORT",
"SOUL",
"SOUR",
"SOWN",
"STAB",
"STAG",
"STAN",
"STAR",
"STAY",
"STEM",
"STEW",
"STIR",
"STOW",
"STUB",
"STUN",
"SUCH",
"SUDS",
"SUIT",
"SULK",
"SUMS",
"SUNG",
"SUNK",
"SURE",
"SURF",
"SWAB",
"SWAG",
"SWAM",
"SWAN",
"SWAT",
"SWAY",
"SWIM",
"SWUM",
"TACK",
"TACT",
"TAIL",
"TAKE",
"TALE",
"TALK",
"TALL",
"TANK",
"TASK",
"TATE",
"TAUT",
"TEAL",
"TEAM",
"TEAR",
"TECH",
"TEEM",
"TEEN",
"TEET",
"TELL",
"TEND",
"TENT",
"TERM",
"TERN",
"TESS",
"TEST",
"THAN",
"THAT",
"THEE",
"THEM",
"THEN",
"THEY",
"THIN",
"THIS",
"THUD",
"THUG",
"TICK",
"TIDE",
"TIDY",
"TIED",
"TIER",
"TILE",
"TILL",
"TILT",
"TIME",
"TINA",
"TINE",
"TINT",
"TINY",
"TIRE",
"TOAD",
"TOGO",
"TOIL",
"TOLD",
"TOLL",
"TONE",
"TONG",
"TONY",
"TOOK",
"TOOL",
"TOOT",
"TORE",
"TORN",
"TOTE",
"TOUR",
"TOUT",
"TOWN",
"TRAG",
"TRAM",
"TRAY",
"TREE",
"TREK",
"TRIG",
"TRIM",
"TRIO",
"TROD",
"TROT",
"TROY",
"TRUE",
"TUBA",
"TUBE",
"TUCK",
"TUFT",
"TUNA",
"TUNE",
"TUNG",
"TURF",
"TURN",
"TUSK",
"TWIG",
"TWIN",
"TWIT",
"ULAN",
"UNIT",
"URGE",
"USED",
"USER",
"USES",
"UTAH",
"VAIL",
"VAIN",
"VALE",
"VARY",
"VASE",
"VAST",
"VEAL",
"VEDA",
"VEIL",
"VEIN",
"VEND",
"VENT",
"VERB",
"VERY",
"VETO",
"VICE",
"VIEW",
"VINE",
"VISE",
"VOID",
"VOLT",
"VOTE",
"WACK",
"WADE",
"WAGE",
"WAIL",
"WAIT",
"WAKE",
"WALE",
"WALK",
"WALL",
"WALT",
"WAND",
"WANE",
"WANG",
"WANT",
"WARD",
"WARM",
"WARN",
"WART",
"WASH",
"WAST",
"WATS",
"WATT",
"WAVE",
"WAVY",
"WAYS",
"WEAK",
"WEAL",
"WEAN",
"WEAR",
"WEED",
"WEEK",
"WEIR",
"WELD",
"WELL",
"WELT",
"WENT",
"WERE",
"WERT",
"WEST",
"WHAM",
"WHAT",
"WHEE",
"WHEN",
"WHET",
"WHOA",
"WHOM",
"WICK",
"WIFE",
"WILD",
"WILL",
"WIND",
"WINE",
"WING",
"WINK",
"WINO",
"WIRE",
"WISE",
"WISH",
"WITH",
"WOLF",
"WONT",
"WOOD",
"WOOL",
"WORD",
"WORE",
"WORK",
"WORM",
"WORN",
"WOVE",
"WRIT",
"WYNN",
"YALE",
"YANG",
"YANK",
"YARD",
"YARN",
"YAWL",
"YAWN",
"YEAH",
"YEAR",
"YELL",
"YOGA",
"YOKE"
};




/*
 *------------------------------------------------------*
 *
 *      TrfInit_OTP_WORDS --
 *
 *      ------------------------------------------------*
 *      Register the conversion implemented in this file.
 *      ------------------------------------------------*
 *
 *      Sideeffects:
 *              As of 'Trf_Register'.
 *
 *      Result:
 *              A standard Tcl error code.
 *
 *------------------------------------------------------*
 */

int
TrfInit_OTP_WORDS (interp)
Tcl_Interp* interp;
{
  TrfLock; /* THREADING: serialize initialization */
  convDefinition.options = Trf_ConverterOptions ();
  TrfUnlock;

  return Trf_Register (interp, &convDefinition);
}

/*
 *------------------------------------------------------*
 *
 *      CreateEncoder --
 *
 *      ------------------------------------------------*
 *      Allocate and initialize the control block of a
 *      data encoder.
 *      ------------------------------------------------*
 *
 *      Sideeffects:
 *              Allocates memory.
 *
 *      Result:
 *              An opaque reference to the control block.
 *
 *------------------------------------------------------*
 */

static Trf_ControlBlock
CreateEncoder (writeClientData, fun, optInfo, interp, clientData)
ClientData    writeClientData;
Trf_WriteProc *fun;
Trf_Options   optInfo;
Tcl_Interp*   interp;
ClientData clientData;
{
  EncoderControl* c;

  c = (EncoderControl*) ckalloc (sizeof (EncoderControl));
  c->write           = fun;
  c->writeClientData = writeClientData;

  /* initialize conversion specific items here (otp_words encode) */

  ClearEncoder ((Trf_ControlBlock) c, clientData);

  return ((ClientData) c);
}

/*
 *------------------------------------------------------*
 *
 *      DeleteEncoder --
 *
 *      ------------------------------------------------*
 *      Destroy the control block of an encoder.
 *      ------------------------------------------------*
 *
 *      Sideeffects:
 *              Releases the memory allocated by 'CreateEncoder'
 *
 *      Result:
 *              None.
 *
 *------------------------------------------------------*
 */

static void
DeleteEncoder (ctrlBlock, clientData)
Trf_ControlBlock ctrlBlock;
ClientData clientData;
{
  EncoderControl* c = (EncoderControl*) ctrlBlock;

  /* release conversion specific items here (otp_words encode) */

  ckfree ((char*) c);
}

/*
 *------------------------------------------------------*
 *
 *      Encode --
 *
 *      ------------------------------------------------*
 *      Encode the given character and write the result.
 *      ------------------------------------------------*
 *
 *      Sideeffects:
 *              As of the called WriteFun.
 *
 *      Result:
 *              Generated bytes implicitly via WriteFun.
 *              A standard Tcl error code.
 *
 *------------------------------------------------------*
 */

static int
Encode (ctrlBlock, character, interp, clientData)
Trf_ControlBlock ctrlBlock;
unsigned int character;
Tcl_Interp* interp;
ClientData clientData;
{
  EncoderControl* c = (EncoderControl*) ctrlBlock;

  /* execute conversion specific code here (otp_words encode) */

  if (c -> charCount == 8) {
    int    i = FlushEncoder (ctrlBlock, interp, clientData);

    if (i == TCL_OK)
      i = c -> write (c -> writeClientData, (unsigned char*) "\n", 1, interp);

    if (i != TCL_OK)
      return i;
  }
  
  c -> buf[c -> charCount++] = character;

  return TCL_OK;
}

/*
 *------------------------------------------------------*
 *
 *      EncodeBuffer --
 *
 *      ------------------------------------------------*
 *      Encode the given buffer and write the result.
 *      ------------------------------------------------*
 *
 *      Sideeffects:
 *              As of the called WriteFun.
 *
 *      Result:
 *              Generated bytes implicitly via WriteFun.
 *              A standard Tcl error code.
 *
 *------------------------------------------------------*
 */

static int
EncodeBuffer (ctrlBlock, buffer, bufLen, interp, clientData)
Trf_ControlBlock ctrlBlock;
unsigned char* buffer;
int bufLen;
Tcl_Interp* interp;
ClientData clientData;
{
  /* EncoderControl* c = (EncoderControl*) ctrlBlock; unused */
  /* execute conversion specific code here (otp_words encode) */

  int    i = TCL_OK;

  while (bufLen-- > 0) {
    if ((i = Encode (ctrlBlock, *buffer++ & 0xff, interp, clientData))
	!= TCL_OK)
      break;
  }

  return i;
}

/*
 *------------------------------------------------------*
 *
 *      FlushEncoder --
 *
 *      ------------------------------------------------*
 *      Encode an incomplete character sequence (if possible).
 *      ------------------------------------------------*
 *
 *      Sideeffects:
 *              As of the called WriteFun.
 *
 *      Result:
 *              Generated bytes implicitly via WriteFun.
 *              A standard Tcl error code.
 *
 *------------------------------------------------------*
 */

static int
FlushEncoder (ctrlBlock, interp, clientData)
Trf_ControlBlock ctrlBlock;
Tcl_Interp* interp;
ClientData clientData;
{
  EncoderControl* c = (EncoderControl*) ctrlBlock;

  /* execute conversion specific code here (otp_words encode) */

  int    i, p;
  char   cp[10];

  switch (c -> charCount) {
  case 8:
    break;

  case 0:
    return TCL_OK;

  default:
    if (interp) {
      Tcl_ResetResult (interp);
      Tcl_AppendResult (interp,
			"input string must be a multiple of 64-bits",
			(char *) NULL);
    }
    return TCL_ERROR;
  }
    
  memset (cp, '\0', sizeof cp);
  memcpy (cp, c -> buf, sizeof c -> buf);

  for (p = 0, i = 0; i < 64; i += 2)
    p += extract (cp, i, 2);

  cp[sizeof c -> buf] = (char) (p << 6);

  for (p = 0; p <= 55; p += 11) {
    char    *w = &Wp[extract (cp, p, 11)][0];
    char     buf[5];

    memset (buf, '\0', sizeof buf);

    for (i = 0; i < 4; i++) {
      buf[i] = *w++;
    }

    i = c -> write (c -> writeClientData, (unsigned char*) buf, strlen (buf),
		    interp);
    if ((i == TCL_OK) && (p != 55))
      i = c -> write (c -> writeClientData, (unsigned char*) " ", 1, interp);
    if (i != TCL_OK)
      return i;
  }

  ClearEncoder (ctrlBlock, clientData);

  return TCL_OK;
}

/*
 *------------------------------------------------------*
 *
 *      ClearEncoder --
 *
 *      ------------------------------------------------*
 *      Discard an incomplete character sequence.
 *      ------------------------------------------------*
 *
 *      Sideeffects:
 *              See above.
 *
 *      Result:
 *              None.
 *
 *------------------------------------------------------*
 */

static void
ClearEncoder (ctrlBlock, clientData)
Trf_ControlBlock ctrlBlock;
ClientData clientData;
{
  EncoderControl* c = (EncoderControl*) ctrlBlock;

  /* execute conversion specific code here (otp_words encode) */

  c -> charCount = 0;
  memset (c -> buf, '\0', sizeof c -> buf);
}

/*
 *------------------------------------------------------*
 *
 *      CreateDecoder --
 *
 *      ------------------------------------------------*
 *      Allocate and initialize the control block of a
 *      data decoder.
 *      ------------------------------------------------*
 *
 *      Sideeffects:
 *              Allocates memory.
 *
 *      Result:
 *              An opaque reference to the control block.
 *
 *------------------------------------------------------*
 */

static Trf_ControlBlock
CreateDecoder (writeClientData, fun, optInfo, interp, clientData)
ClientData    writeClientData;
Trf_WriteProc *fun;
Trf_Options   optInfo;
Tcl_Interp*   interp;
ClientData clientData;
{
  DecoderControl* c;
  
  c = (DecoderControl*) ckalloc (sizeof (DecoderControl));
  c->write           = fun;
  c->writeClientData = writeClientData;

  /* initialize conversion specific items here (otp_words decode) */

  ClearDecoder ((Trf_ControlBlock) c, clientData);

  return ((ClientData) c);
}

/*
 *------------------------------------------------------*
 *
 *      DeleteDecoder --
 *
 *      ------------------------------------------------*
 *      Destroy the control block of an decoder.
 *      ------------------------------------------------*
 *
 *      Sideeffects:
 *              Releases the memory allocated by 'CreateDecoder'
 *
 *      Result:
 *              None.
 *
 *------------------------------------------------------*
 */

static void
DeleteDecoder (ctrlBlock, clientData)
Trf_ControlBlock ctrlBlock;
ClientData clientData;
{
  DecoderControl* c = (DecoderControl*) ctrlBlock;

  /* release conversion specific items here (otp_words decode) */

  ckfree ((char*) c);
}

/*
 *------------------------------------------------------*
 *
 *      Decode --
 *
 *      ------------------------------------------------*
 *      Decode the given character and write the result.
 *      ------------------------------------------------*
 *
 *      Sideeffects:
 *              As of the called WriteFun.
 *
 *      Result:
 *              Generated bytes implicitly via WriteFun.
 *              A standard Tcl error code.
 *
 *------------------------------------------------------*
 */

static int
Decode (ctrlBlock, character, interp, clientData)
Trf_ControlBlock ctrlBlock;
unsigned int character;
Tcl_Interp* interp;
ClientData clientData;
{
  DecoderControl* c = (DecoderControl*) ctrlBlock;

  /* execute conversion specific code here (otp_words decode) */

  char x = character & 0xff;
  char *cp;

  if (c -> wordCount == 6) {
    int    i = FlushDecoder (ctrlBlock, interp, clientData);

    if (i != TCL_OK)
      return i;
  }

  if (!isascii (x)) {
    if (interp) {
      Tcl_ResetResult (interp);
      Tcl_AppendResult (interp, "invalid character", (char *) NULL);
    }
    return TCL_ERROR;
  }

  switch (x) {
  case ' ':  /* fall through */
  case '\t': /* fall through */
  case '\n': /* fall through */
  case ',':
    if (c -> charCount == 0) {
      if (interp) {
	Tcl_ResetResult (interp);
	Tcl_AppendResult (interp, "empty word", (char *) NULL);
      }
      return TCL_ERROR;
    }
    c -> charCount = 0;
    c -> wordCount++;
    return TCL_OK;
      
  default:
    if (c -> charCount == 4) {
      if (interp) {
	Tcl_ResetResult (interp);
	Tcl_AppendResult (interp, "word too long", (char *) NULL);
      }
      return TCL_ERROR;
    }
    if (islower (x)) {
      x = toupper (x);
    }
    break;
  }

  switch (x) {
  case '5':
    x = 'S';
    break;
      
  case '1':
    x = 'L';
    break;
      
  case '0':
    x = 'O';
    break;
  }

  cp = &(c -> words[c -> wordCount][0]);
  cp[c -> charCount++] = x;
  cp[c -> charCount] = '\0';
    
  return TCL_OK;
}

/*
 *------------------------------------------------------*
 *
 *      DecodeBuffer --
 *
 *      ------------------------------------------------*
 *      Decode the given buffer and write the result.
 *      ------------------------------------------------*
 *
 *      Sideeffects:
 *              As of the called WriteFun.
 *
 *      Result:
 *              Generated bytes implicitly via WriteFun.
 *              A standard Tcl error code.
 *
 *------------------------------------------------------*
 */

static int
DecodeBuffer (ctrlBlock, buffer, bufLen, interp, clientData)
Trf_ControlBlock ctrlBlock;
unsigned char* buffer;
int bufLen;
Tcl_Interp* interp;
ClientData clientData;
{
  /* DecoderControl* c = (DecoderControl*) ctrlBlock; unused */
  /* execute conversion specific code here (otp_words decode) */

  int    i = TCL_OK;

  while (bufLen-- > 0) {
    if ((i = Decode (ctrlBlock, *buffer++ & 0xff, interp, clientData))
	!= TCL_OK)
      break;
  }
  
  return i;
}

/*
 *------------------------------------------------------*
 *
 *      FlushDecoder --
 *
 *      ------------------------------------------------*
 *      Decode an incomplete character sequence (if possible).
 *      ------------------------------------------------*
 *
 *      Sideeffects:
 *              As of the called WriteFun.
 *
 *      Result:
 *              Generated bytes implicitly via WriteFun.
 *              A standard Tcl error code.
 *
 *------------------------------------------------------*
 */

static int
FlushDecoder (ctrlBlock, interp, clientData)
Trf_ControlBlock ctrlBlock;
Tcl_Interp* interp;
ClientData clientData;
{
  DecoderControl* c = (DecoderControl*) ctrlBlock;

  /* execute conversion specific code here (otp_words decode) */

  int    i;
  unsigned long p;
  char   b[9];

  if ((c -> wordCount == 5) && (c -> charCount > 0))
    c -> wordCount = 6;

  switch (c -> wordCount) {
  case 6:
    break;

  case 0:
    if (c -> charCount == 0)
      return TCL_OK;
    /* else fall through... */
  default:
    if (interp) {
      Tcl_ResetResult (interp);
      Tcl_AppendResult (interp, "too few words", (char *) NULL);
    }
    return TCL_ERROR;
  }

  memset (b, '\0', sizeof b);
  for (c -> wordCount = 0, p = 0;
       c -> wordCount < 6;
       c -> wordCount++, p += 11) {
    int    lo, hi;
    int    v;
    char  *w = &c -> words[c -> wordCount][0];

    if (strlen (w) < 4)
      lo = 0, hi = 570;
    else
      lo = 571, hi = 2047;
    if ((v = wsrch (w, lo, hi)) < 0) {
      if (interp) {
	Tcl_ResetResult (interp);
	Tcl_AppendResult (interp, "unknown word \"", w, "\"",
			  (char *) NULL);
      }
      return TCL_ERROR;
    }
    insert (b, v, p, 11);
  }

  for (p = 0, i = 0; i < 64; i += 2)
    p += extract (b, i, 2);

  if ((p & 0x03) != extract (b, 64, 2)) {
    if (interp) {
      Tcl_ResetResult (interp);
      Tcl_AppendResult (interp, "parity error", (char *) NULL);
    }
    return TCL_ERROR;
  }

  i = c -> write (c -> writeClientData, (unsigned char*) b, 8, interp);
  if (i != TCL_OK)
    return i;

  ClearDecoder (ctrlBlock, clientData);

  return TCL_OK;
}

/*
 *------------------------------------------------------*
 *
 *      ClearDecoder --
 *
 *      ------------------------------------------------*
 *      Discard an incomplete character sequence.
 *      ------------------------------------------------*
 *
 *      Sideeffects:
 *              See above.
 *
 *      Result:
 *              None.
 *
 *------------------------------------------------------*
 */

static void
ClearDecoder (ctrlBlock, clientData)
Trf_ControlBlock ctrlBlock;
ClientData clientData;
{
  DecoderControl* c = (DecoderControl*) ctrlBlock;

  /* execute conversion specific code here (otp_words decode) */

  c -> charCount = 0;
  c -> wordCount = 0;
  memset (c -> words, '\0', sizeof c -> words);
}


/* subroutines taken from the module put.c in the original Bellcore reference
   distribution */


/* Dictionary binary search */
static int
wsrch(w,low,high)
char *w;
int low, high;
{
  int i,j;

  for(;;) {
    i = (low + high)/2;
    if((j = strncmp(w,Wp[i],4)) == 0)
      return i;       /* Found it */
    if(high == low+1){
      /* Avoid effects of integer truncation in /2 */
      if(strncmp(w,Wp[high],4) == 0)
	return high;
      else
	return -1;
    }
    if(low >= high)
      return -1;      /* I don't *think* this can happen...*/
    if(j < 0)
      high = i;       /* Search lower half */
    else
      low = i;        /* Search upper half */
  }
}

static void
insert(s, x, start, length)
char *s;
int x;
int  start, length;
{
  unsigned char cl;
  unsigned char cc;
  unsigned char cr;
  unsigned long y;
  int shift;

  assert(length <= 11);
  assert(start >= 0);
  assert(length >= 0);
  assert(start +length <= 66);

  shift = ((8  -(( start + length) % 8))%8);
  y = (long) (x << shift);
  cl = (unsigned char) ((y >> 16) & 0xff);
  cc = (unsigned char) ((y >> 8) & 0xff);
  cr = (unsigned char) (y & 0xff);
  if(shift + length > 16) {
    s[start /8] |= cl;
    s[start/8 +1] |= cc;
    s[start/8 +2] |= cr;
  } else if(shift +length > 8) {
    s[start/8] |= cc;
    s[start/8 + 1] |= cr;
  } else {
    s[start/8] |= cr;
  }
}

/* Extract 'length' bits from the char array 's' starting with bit 'start' */
static unsigned long
extract(s, start, length)
char *s;
int start, length;
{
  unsigned char cl;
  unsigned char cc;
  unsigned char cr;
  unsigned long x;

  assert(length <= 11);
  assert(start >= 0);
  assert(length >= 0);
  assert(start +length <= 66);
  
  cl = s[start/8];
  cc = s[start/8 +1];
  cr = s[start/8 +2];
  x = ((long)(cl<<8 | cc) <<8  | cr) ;
  x = x >> (24 - (length + (start %8)));
  x =( x & (0xffff >> (16-length) )   );
  return(x);
}

