/* hanwin.c: Tcl (Tk) liaison functions for Hanzi Master.  These are
   called by Tk event handlers to generate various lists of characters. */

/* This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2 of the License, or
   (at your option) any later version.  Please see the file "COPYING"
   for details.  If you have not received it along with this program,
   please write to the Free Software Foundation, Inc., 59 Temple Place,
   Suite 330, Boston, MA 02111-1307, USA. */

#include "hanzim.h"

/* PENDING: get rid of these global variables */
char	simpFlag;
char	pinyinFlag;
char	miniFlag;
int	defnWidth;


/* sameRadList() returns a list of character,pronunciation,meaning triples
   (or actually character index, font representation, pinyin font representat-,
    ion, alphanumeric, and meaning quintuples) comprising all characters
   containing same radical as a character given as argument; currently nothing
   done about multiple pronunciations, although they are in the database */
int
sameRadList(ClientData clientData, Tcl_Interp *interp, int argc, char **argv)
{
  int		typ, i,
		givench,
		pchidx,
		flevel,
		gradidx;
  radicalst	*grad;
  characterst	*pch;
  Tcl_Obj	*resultPtr;

  if (clientData); /* shut compiler up */

  if (argc != 7)
    return TCL_ERROR;
  else {
    givench = atoi(argv[1]);
    flevel = atoi(argv[2]);
    simpFlag = strcmp(argv[3],"fanti");
    pinyinFlag = strcmp(argv[4],"bpmf");
    miniFlag = strcmp(argv[6], "false");
    defnWidth = miniFlag ? atoi(argv[5]) - 1 : atoi(argv[5]) - 9;
    }

  /* return all characters having this radical in any position */
  Tcl_ResetResult(interp);
  resultPtr = Tcl_GetObjResult(interp);
  gradidx = chartbl[givench].radidx;
  if (gradidx >= 0) {
    grad = &(radtbl[chartbl[givench].radidx]);
    for (typ=0;typ<4;typ++) {
      for (i=0;i<grad->chars[typ].n_entries;i++) {
	pch = &(chartbl[grad->chars[typ].ent[i]]);
	if ((pch->radtype == typ) && (pch->freq < flevel) &&
	    ((pchidx = pch - chartbl) != givench))
	  Tcl_ListObjAppendElement(interp, resultPtr,
                                   construct_char_entry(pchidx, interp));
	}
      }
    }
  return TCL_OK;
}


/* sameRemList() returns a list similar to above except for same remainder
   instead of same radical */
int
sameRemList(ClientData clientData, Tcl_Interp *interp,
		    int argc, char **argv)
{
    int		i,
		givench,
    		givenrem,
		flevel,
    		cidx;
  idxlist	*ro;
  Tcl_Obj	*resultPtr;

  if (clientData); /* shut compiler up */

  if (argc != 7)
    return TCL_ERROR;
  else {
    givench = atoi(argv[1]);
    flevel = atoi(argv[2]);
    simpFlag = strcmp(argv[3],"fanti");
    pinyinFlag = strcmp(argv[4],"bpmf");
    miniFlag = strcmp(argv[6], "false");
    defnWidth = miniFlag ? atoi(argv[5]) - 1 : atoi(argv[5]) - 9;
    }

  givenrem = chartbl[givench].remainder;

  /* clear last and return new result */
  Tcl_ResetResult(interp);
  resultPtr = Tcl_GetObjResult(interp);
  if (givenrem >= 0) {
    /* first add remainder itself, then list of others */
    Tcl_ListObjAppendElement(interp,resultPtr,
                             construct_char_entry(givenrem,interp));
    ro = &(chartbl[givenrem].remOf);
    for (i=0;i<ro->n_entries;i++) {
      cidx = ro->ent[i];
      if ((chartbl[cidx].freq < flevel) && (cidx != givench))
        Tcl_ListObjAppendElement(interp, resultPtr,
                                 construct_char_entry(cidx,interp));
      }
    }

  return TCL_OK;
}


/* samePronList() same as previous two except for matching pronunciation */
int
samePronList(ClientData clientData, Tcl_Interp *interp,
		    int argc, char **argv)
{
  int		i,j,k,
		givench,
    		pchidx,
		flevel,
        	tone;
  pinyinst	*gpin, *tpin;
  characterst	*pch;
  Tcl_Obj	*resultPtr;

  if (clientData); /* shut compiler up */

  if (argc != 7)
    return TCL_ERROR;
  else {
    givench = atoi(argv[1]);
    flevel = atoi(argv[2]);
    simpFlag = strcmp(argv[3],"fanti");
    pinyinFlag = strcmp(argv[4],"bpmf");
    miniFlag = strcmp(argv[6], "false");
    defnWidth = miniFlag ? atoi(argv[5]) - 1 : atoi(argv[5]) - 9;
    }

  /* return all characters on the list of pinyin which is most frequent
     pronunciation of this character */
  Tcl_ResetResult(interp);
  resultPtr = Tcl_GetObjResult(interp);
  gpin = &(pinyintbl[chartbl[givench].pinyins.ent[0]]);
  for (i=0;i<gpin->chars.n_entries;i++) {
    pch = &(chartbl[gpin->chars.ent[i]]);
    if ((pch->freq < flevel) && ((pchidx = pch - chartbl) != givench))
      Tcl_ListObjAppendElement(interp, resultPtr,
                               construct_char_entry(pchidx, interp));
    }

  /* tack on a blank entry to separate the other tones */
  Tcl_ListObjAppendElement(interp, resultPtr,
                           construct_char_entry(-givench, interp));
  

  /* also, return all characters on the list of the other 4 pinyins with
     same initial and final but different tone */
  tone = gpin->tone;
  for (i=0; i<5; i++) {
    if (i != tone) {
      for (j=0; j<NPINYIN; j++) {
        tpin = &(pinyintbl[j]);
        if ((tpin->tone == i)
            && (tpin->init == gpin->init) && (tpin->finl == gpin->finl)) {
          for (k=0; k<tpin->chars.n_entries; k++) {
            pch = &(chartbl[tpin->chars.ent[k]]);
            if ((pch->freq<flevel) && ((pchidx = pch-chartbl) != givench))
              Tcl_ListObjAppendElement(interp, resultPtr,
                                       construct_char_entry(pchidx, interp));
            }
          }
        }
      }
    }

  return TCL_OK;
}


/* LcompList returns list of compounds having the same right character as
   the character given as argument */
int
LcompList(ClientData clientData, Tcl_Interp *interp,
	  int argc, char **argv)
{
  int		i,
		givench,
		flevel;
  Tcl_Obj	*resultPtr;

  if (clientData); /* shut compiler up */

  if (argc != 6)
    return TCL_ERROR;
  else {
    givench = atoi(argv[1]);
    flevel = atoi(argv[2]);
    simpFlag = strcmp(argv[3],"fanti");
    pinyinFlag = strcmp(argv[4],"bpmf");
    defnWidth = atoi(argv[5]) - 1;
    }

  /* we just return everything on the character's Lpart (left partner) list */
  Tcl_ResetResult(interp);
  resultPtr = Tcl_GetObjResult(interp);
  for (i=0;i<chartbl[givench].Lpart.n_entries;i++) {
    if (comptbl[chartbl[givench].Lpart.ent[i]].freq < flevel)
      Tcl_ListObjAppendElement(interp, resultPtr,
       construct_comp_entry(&(comptbl[chartbl[givench].Lpart.ent[i]]),interp));
    }
  return TCL_OK;
}


/* Rcomplist returns list of compounds with same left character as given. */
int
RcompList(ClientData clientData, Tcl_Interp *interp,
		    int argc, char **argv)
{
  int		i,
		givench,
		flevel;
  Tcl_Obj	*resultPtr;

  if (clientData); /* shut compiler up */

  if (argc != 6)
    return TCL_ERROR;
  else {
    givench = atoi(argv[1]);
    flevel = atoi(argv[2]);
    simpFlag = strcmp(argv[3],"fanti");
    pinyinFlag = strcmp(argv[4],"bpmf");
    defnWidth = atoi(argv[5]) - 1;
    }

  /* we just return everything on the characters Rpart (right partner) list */
  Tcl_ResetResult(interp);
  resultPtr = Tcl_GetObjResult(interp);
  for (i=0;i<chartbl[givench].Rpart.n_entries;i++) {
    if (comptbl[chartbl[givench].Rpart.ent[i]].freq < flevel)
      Tcl_ListObjAppendElement(interp, resultPtr,
       construct_comp_entry(&(comptbl[chartbl[givench].Rpart.ent[i]]),interp));
    }
  return TCL_OK;
}


/* radList() takes no arguments and returns a list of lists of, for each
   number of strokes, a space-separated pair for each radical of a character
   index for the first character containing the radical and the font string
   for the radical; (used for generating a radical->character look-up table) */
int
radList(ClientData clientData, Tcl_Interp *interp,
		    int argc, char **argv)
{
#define MRS 11
  int	i,j, found;
  char	pair[22],
    	*tpos;
  char	*strokeList[MRS][50];	/* assume won't have >50 radicals per stroke */
  int	n_entries[MRS], ns;
  static char strokeTitles[MRS][5] = {
    "1","2","3","4","5","6","7","8","9","10",">10" };
  Tcl_Obj *resultPtr, *tclList;

  if (clientData); /* shut compiler up */
  if (argc != 2)
    return TCL_ERROR;
  else
    simpFlag = strcmp(argv[1],"fanti");
  for (i=0;i<MRS;i++) n_entries[i] = 0;

  for (i=0;i<NRAD;i++) {
    /* find a character entry if there is one, or leave as 0 */
    for (found=0,j=3;j>-1;j--)
      if (radtbl[i].chars[j].n_entries > 0)
	found = radtbl[i].chars[j].ent[0];
    strcpy(pair,itoa(found));
    strcat(pair," ");
    tpos = pair + strlen(pair);

    /* copy in font string -- always simplified */
    strcpy(tpos,chartbl[radtbl[i].charidx].utf_jh);
    tpos += 3;
    *tpos = '\0';

    /* append entry to proper strokelist */
    ns = radtbl[i].strokes > 10 ? 10 : radtbl[i].strokes - 1;
    if (!(strokeList[ns][n_entries[ns]] = (char *) malloc(22 * sizeof(char))))
      memfail("stroke list");
    strcpy(strokeList[ns][n_entries[ns]],pair);
    n_entries[ns]++;
    }
  /* now build up list structure to return */
  Tcl_ResetResult(interp);  /* clear from previous */
  resultPtr = Tcl_GetObjResult(interp);
  for (ns=0;ns<MRS;ns++) {
    tclList = Tcl_NewListObj(0, NULL);
    Tcl_ListObjAppendElement(interp, tclList,
                             Tcl_NewStringObj(strokeTitles[ns],-1));
    for (i=0;i<n_entries[ns];i++)
      Tcl_ListObjAppendElement(interp, tclList,
                               Tcl_NewStringObj(strokeList[ns][i],-1));
    Tcl_ListObjAppendElement(interp, resultPtr, tclList);
    for (i=0;i<n_entries[ns];i++)
      free(strokeList[ns][i]);
    }

  return(TCL_OK);
}


/* pyChar() returns the first character having the given pronunciation */
int
pyChar(ClientData clientData, Tcl_Interp *interp,
		    int argc, char **argv)
{
  pinyinst	*pyt;
  int		flevel, pyidx, i, nEnt, res;
  Tcl_Obj	*resultPtr;

  if (clientData); /* shut compiler up */

  if (argc != 3)
    return TCL_ERROR;
  else {	/* parse pinyin string */
    pyidx = pin_index(argv[1]);
    flevel = atoi(argv[2]);
    if (pyidx < 0)
      return TCL_ERROR;
    else
      pyt = &(pinyintbl[pyidx]);
  }
  
  /* clear last and return new result */
  Tcl_ResetResult(interp);
  resultPtr = Tcl_GetObjResult(interp);
  nEnt = pyt->chars.n_entries;
  if (nEnt > 0) {
    /* find first character satisfying our frequency criterion or use first */
    res = pyt->chars.ent[0];
    for (i=0; i < nEnt; i++) {
      if (chartbl[pyt->chars.ent[i]].freq < flevel) {
        res = pyt->chars.ent[i];
        break;
      }
    }
    Tcl_ListObjAppendElement(interp,resultPtr, Tcl_NewStringObj(itoa(res),-1));
    return TCL_OK;
  }
  return TCL_ERROR;  /* didn't find anything */
}


/* randChar() returns a random character satisfying the frequency criterion */
int
randChar(ClientData clientData, Tcl_Interp *interp,
		    int argc, char **argv)
{
  int		z, flevel;
  static char	chrIdx[10];

  if (clientData); /* shut compiler up */

  if (argc != 2)
    return TCL_ERROR;
  else
    flevel = atoi(argv[1]);

  do {
    z = (int) floor((float) nchars * dr());
    }
  while (chartbl[z].freq >= flevel);
  strcpy(chrIdx, itoa(z));
  Tcl_ResetResult(interp);
  Tcl_SetObjResult(interp, Tcl_NewStringObj(chrIdx, -1));
  return TCL_OK;
}


/* convertSelection() attempts to find a Chinese character as the first part
   of the passed-in string and convert the font index to a character index;
   if this fails, the passed-in index is returned as default */
int
convertSelection(ClientData clientData, Tcl_Interp *interp,
                 int objc, Tcl_Obj **objv)
{
  int		defaultIdx, attemptIdx, fontIdx;
  int		selChar1, selChar2;
  int		strLen;
  int		i;
  Tcl_Obj	*resultPtr;


  if (clientData); /* shut compiler up */

  if (objc != 5)
    return TCL_ERROR;
  else {

    Tcl_GetIntFromObj(interp, objv[1], &selChar1);
    Tcl_GetIntFromObj(interp, objv[2], &selChar2);
    simpFlag = strcmp(Tcl_GetStringFromObj(objv[3],&strLen), "fanti");
    Tcl_GetIntFromObj(interp, objv[4], &defaultIdx);
  }

  fontIdx = f_char2int(selChar1,selChar2);

  /* If it's GB, just look it up directly; otherwise, we do a linear search
     through the array of 7000+ characters, which actually is on the whole
     preferable to the alternative of adding 64K memory and slowing the
     startup time just for this rarely-used function.. */
  if (simpFlag) {
    attemptIdx = fnt2chr[fontIdx];
  } else {
    attemptIdx = -1;
    for (i=0;i<nchars;i++) {
      if (chartbl[i].big5idx == fontIdx) {
        attemptIdx = i;
        break;
      }
    }
  }

  /* if didn't find it, go with passed-in default. */
  if (attemptIdx == -1) {
    attemptIdx = defaultIdx;
    fprintf(stderr,"");
  }

  /* clear last and return new result */
  Tcl_ResetResult(interp);
  resultPtr = Tcl_GetObjResult(interp);
  Tcl_ListObjAppendElement(interp, resultPtr,
                           Tcl_NewStringObj(itoa(attemptIdx), -1));
  return TCL_OK;

}


/* charinfo() returns a list of a given character's character string, pinyin,
   and definition */
int
charinfo(ClientData clientData, Tcl_Interp *interp, int argc, char **argv)
{
  int		cr;
  characterst	*cht;
  char		chr[4],
		pinyin[20],
		defn[80];
  Tcl_Obj	*resultPtr;

  if (clientData); /* shut compiler up */

  if ((argc!=5) || ((cr=atoi(argv[1]))>=nchars) || (chartbl[cr].fontidx==-1)) {
    return TCL_ERROR;
  } else {
    cht = &(chartbl[cr]);
    simpFlag = strcmp(argv[2],"fanti");
    pinyinFlag = strcmp(argv[3],"bpmf");
    defnWidth = atoi(argv[4]) - 1;
  }

  if (simpFlag) strcpy(chr,cht->utf_jh);
  else strcpy(chr,cht->utf_ft);

  /* clear last and return new result */
  Tcl_ResetResult(interp);
  resultPtr = Tcl_GetObjResult(interp);
  /* character, pinyin, meaning */
  Tcl_ListObjAppendElement(interp, resultPtr, Tcl_NewStringObj(chr, -1));
  strcpy(pinyin,init_str[pinyintbl[cht->pinyins.ent[0]].init]);
  strcat(pinyin,fin_str[pinyintbl[cht->pinyins.ent[0]].finl]);
  strcat(pinyin,itoa(pinyintbl[cht->pinyins.ent[0]].tone + 1));
  Tcl_ListObjAppendElement(interp, resultPtr, Tcl_NewStringObj(pinyin, -1));
  defn[0] = '\0';
  catdef(defn, cht->meaning, defnWidth, 2, 0, 0);
  Tcl_ListObjAppendElement(interp, resultPtr, Tcl_NewStringObj(defn, -1));
  /* radical, radical meaning */
  if ((cr = cht->radidx) >= 0) {
    strcpy(chr,chartbl[radtbl[cr].charidx].utf_jh);
    strncpy(defn,radtbl[cr].meaning,79);
    }
  else strcpy(chr,""), strcpy(defn,"");
  Tcl_ListObjAppendElement(interp, resultPtr, Tcl_NewStringObj(chr, -1));
  Tcl_ListObjAppendElement(interp, resultPtr, Tcl_NewStringObj(defn, -1));
  /* remainder, remainder pronunciation */
  if ((cr = cht->remainder) >= 0) {
    if (simpFlag) strcpy(chr,chartbl[cr].utf_jh);
    else strcpy(chr,chartbl[cr].utf_ft);
    copyPron(pinyin, &(chartbl[cr]));
    }
  else strcpy(chr,""), strcpy(pinyin,"");
  Tcl_ListObjAppendElement(interp, resultPtr, Tcl_NewStringObj(chr, -1));
  Tcl_ListObjAppendElement(interp, resultPtr, Tcl_NewStringObj(pinyin, -1));
  /* main pronunciation but encoded in utf with diacriticals */
  copyPron(pinyin, cht);
  Tcl_ListObjAppendElement(interp, resultPtr, Tcl_NewStringObj(pinyin, -1));
  return TCL_OK;
}


/* construct_char_entry() builds a tcl list of:
   character index, font code (in chars), pinyin string in gb font
   representation, pinyin string in alphanumeric, and meaning
   for a character given as argument, and returns it as a string in s */
Tcl_Obj *
construct_char_entry(int cidx, Tcl_Interp *interp)
{
  characterst	*cht;
  char		tidx[10], tchar[10], tpyaln[20], tpyfnt[20], tmeaning[DEFNL],
		*ptrs[5];
  int		i;
  Tcl_Obj	*tclList;

  if (cidx < 0) {

    /* this is a hack to indicate we should make a blank entry w/same idx */
    strcpy(tidx, itoa(-cidx));
    strcpy(tchar, " ");
    strcpy(tpyaln, " ");
    if (miniFlag) strcpy(tmeaning, "\n     (OTHER TONES)");
    else strcpy(tmeaning, "\n       (OTHER TONES)");
    strcpy(tpyfnt, " ");

  } else {

    cht = &(chartbl[cidx]);
    strcpy(tidx,itoa(cidx));
    if (simpFlag) strcpy(tchar,cht->utf_jh);
    else strcpy(tchar,cht->utf_ft);
    strcpy(tpyaln,init_str[pinyintbl[cht->pinyins.ent[0]].init]);
    strcat(tpyaln,fin_str[pinyintbl[cht->pinyins.ent[0]].finl]);
    strcat(tpyaln,itoa(pinyintbl[cht->pinyins.ent[0]].tone + 1));
    tmeaning[0] = '\0';
    if (miniFlag) catdef(tmeaning, cht->meaning, defnWidth, 2, 0, 1);
    else catdef(tmeaning, cht->meaning, defnWidth, 2, 1, 1);
    copyPron(tpyfnt, cht);
  }

  tclList = Tcl_NewListObj(0, NULL);
  ptrs[0] = tidx, ptrs[1] = tchar, ptrs[2] = tpyfnt, ptrs[3] = tpyaln,
    ptrs[4] = tmeaning;
  for (i=0; i<5; i++)
    Tcl_ListObjAppendElement(interp, tclList, Tcl_NewStringObj(ptrs[i],-1));
  return tclList;
}


/* construct_comp_entry() composes a tcl list of:
   character indices, font strings, pinyin strings, and
   compound meaning for a compound given as argument, and returns it as
   a string in s */
Tcl_Obj *
construct_comp_entry(compoundst *comp, Tcl_Interp *interp)
{
  characterst	*cht1, *cht2;
  char		tidx[20], tchar[20], pystrL[20], pystrR[20], tmeaning[DEFNL],
		*ptrs[5];
  int		i;
  Tcl_Obj	*tclList;

  cht1 = &(chartbl[comp->Lcharidx]), cht2 = &(chartbl[comp->Rcharidx]);

  strcpy(tidx,itoa(comp->Lcharidx)), strcat(tidx," ");
  strcat(tidx,itoa(comp->Rcharidx)), strcat(tidx," ");
  if (simpFlag) strcpy(tchar,cht1->utf_jh), strcat(tchar,cht2->utf_jh);
  else strcpy(tchar,cht1->utf_ft), strcat(tchar,cht2->utf_ft);

  /* pronunciations encoded in utf with diacriticals;
     each pinyin character consumes 2 chars, for a max of 12 */
  copyPron(pystrL, cht1);
  copyPron(pystrR, cht2);

  /* definition */
  tmeaning[0] = '\0';
  catdef(tmeaning, comp->meaning, defnWidth, 2, 0, 1);

  tclList = Tcl_NewListObj(0, NULL);
  ptrs[0]=tidx, ptrs[1]=tchar, ptrs[2]=pystrL,ptrs[3]=pystrR, ptrs[4]=tmeaning;
  for (i=0; i<5; i++)
    Tcl_ListObjAppendElement(interp, tclList, Tcl_NewStringObj(ptrs[i],-1));
  return tclList;
}


/* construct_triplet_entry() acts as construct_comp_entry but for triplets;
   at some point these two methods should be consolidated since they share
   90% of their code */
Tcl_Obj *
construct_triplet_entry(tripletst *triplet, Tcl_Interp *interp)
{
  characterst	*cht1, *cht2, *cht3;
  char		tidx[20], tchar[20], pystr1[20], pystr2[20], pystr3[20],
		tmeaning[DEFNL],
		*ptrs[6];
  int		i;
  Tcl_Obj	*tclList;

  cht1 = &(chartbl[triplet->char1idx]), cht2 = &(chartbl[triplet->char2idx]),
    cht3 = &(chartbl[triplet->char3idx]);

  strcpy(tidx,itoa(triplet->char1idx)), strcat(tidx," ");
  strcat(tidx,itoa(triplet->char2idx)), strcat(tidx," ");
  strcat(tidx,itoa(triplet->char3idx)), strcat(tidx," ");
  if (simpFlag)
    strcpy(tchar,cht1->utf_jh), strcat(tchar,cht2->utf_jh),
      strcat(tchar,cht3->utf_jh);
  else
    strcpy(tchar,cht1->utf_ft), strcat(tchar,cht2->utf_ft),
      strcat(tchar,cht3->utf_ft);

  /* pronunciations encoded in utf with diacriticals;
     each pinyin character consumes 2 chars, for a max of 12 */
  copyPron(pystr1,cht1);
  copyPron(pystr2,cht2);
  copyPron(pystr3,cht3);

  /* definition */
  strcpy(tmeaning,"");
  catdef(tmeaning, triplet->meaning, defnWidth, 2, 0, 1);

  tclList = Tcl_NewListObj(0, NULL);
  ptrs[0]=tidx, ptrs[1]=tchar, ptrs[2]=pystr1, ptrs[3]=pystr2, ptrs[4]=pystr3,
    ptrs[5]=tmeaning;
  for (i=0; i<6; i++)
    Tcl_ListObjAppendElement(interp, tclList, Tcl_NewStringObj(ptrs[i],-1));
  return tclList;
}


/* yingHan returns a chinese string for use as a button title -- don't know
   how to represent it straight in tcl */
int
yingHanCh(ClientData clientData, Tcl_Interp *interp,int argc, char **argv)
{
  static char s[20];

  if (clientData); /* shut compiler up */
  if (argc); /* shut compiler up */
  if (argv); /* shut compiler up */

  sprintf(s,"%s%s",chartbl[fnt2chr[54178]].utf_jh,
	  chartbl[fnt2chr[47802]].utf_jh);
  Tcl_ResetResult(interp);
  Tcl_SetObjResult(interp,Tcl_NewStringObj(s,-1));
  return TCL_OK;
}


/* searchDefn searches the compounds database for any with definitions
   containing the *entire string* given and returns them in the same fashion
   as [LR]compList */
int
searchDefn(ClientData clientData, Tcl_Interp *interp,
	  int argc, char **argv)
{
  int		i;
  char		*defn;
  Tcl_Obj	*resultPtr;

  if (clientData); /* shut compiler up */

  if (argc != 4) return TCL_ERROR;
  else {
    defn = argv[1];
    pinyinFlag = strcmp(argv[2],"bpmf");
    defnWidth = atoi(argv[3]);
    }

  /* clear last and return new result */
  Tcl_ResetResult(interp);
  resultPtr = Tcl_GetObjResult(interp);
  /* need to search through entire compound database.. note, using strstr()
     might lead to compatibility problems :( */
  /* PENDING: try to match only whole words? */
  for (i=0;i<ncomps;i++) {
    if (strstr(comptbl[i].meaning,defn)) {
      Tcl_ListObjAppendElement(interp, resultPtr,
                               construct_comp_entry(&(comptbl[i]), interp));
    }
  }

  for (i=0;i<ntriplets;i++) {
    if (strstr(triplettbl[i].meaning,defn)) {
      Tcl_ListObjAppendElement(interp, resultPtr,
                             construct_triplet_entry(&(triplettbl[i]),interp));
    }
  }

  return TCL_OK;
}


/* copyPron() copies the pinyin or bopomofo string for character ch's most
   common pronunciation into string dest; uses global variable pinyinFlag,
   which must be set before calling */
void
copyPron(char *dest, characterst *ch)
{
  if (!pinyinFlag) {
    strcpy(dest,ch->utf_bpmf);
  } else {
    strcpy(dest,init_str_t[pinyintbl[ch->pinyins.ent[0]].init]);
    strcat(dest, fin_str_t[pinyintbl[ch->pinyins.ent[0]].finl][pinyintbl[ch->pinyins.ent[0]].tone]);
  }
}


/* catdef() copies nl lines of length llen from definition string src to a
   destination string dest, wrapping words and padding all lines with
   spaces if necessary */
void
catdef(char *dest, char *src, int llen, int nl, int ntabs, char uscoreflag)
{
  int 	l, c;				/* indices */
  char	*spos, *dpos, *tspos, *last;	/* character pointers */
  
  last = src + strlen(src) - 1;
  spos = src, dpos = dest + strlen(dest);
  for (l=0; l<nl;l++) {	/* for each line */
    /* case 1: defn ended already - fully pad with spaces */
    if (spos > last) {
      dpos += spacepad(dpos,llen);
      }
    /* case 2: defn will end this line - copy and pad with spaces */
    else if ((spos + llen) > last) {
      for (c=0; spos <= last; c++)
	*dpos++ = *spos++;
      dpos += spacepad(dpos, llen - c);
      spos = last + 1;
      }
    /* case 3: line ends on a word in definition - copy up to word and pad */
    /* PENDING: fails where single word > line length */
    else if (!isspace(*(spos+llen-1)) && !isspace(*(spos+llen))) {
      for (tspos = spos + llen; !isspace(*tspos); tspos--);
      for (c=0; spos <= tspos; c++)
	*dpos++ = *spos++;
      dpos += spacepad(dpos, llen - c); /* - 1); */
      }
    /* case 4: line ends on a space or end of a word - copy and don't pad */
    else {
      for (c=0;c<llen;c++)
	*dpos++ = *spos++;
      if (*spos == ' ') spos++;	/* swallow space at beginning of next line */
      }

    if (l < (nl-1)) {
      *dpos++ = '\n';
      if (uscoreflag) *dpos++ = '_';
      for (c=0;c<ntabs;c++) *dpos++ = '\t';
      *dpos++ = ' '; /* indent lines after first */
      if (l == 0) llen--;
      }

    }

  /* add line marker and null terminate */
  if (uscoreflag && isspace(*(dpos-1))) *(dpos-1) = '_';
  *dpos = '\0';

}


/* issep() returns nonzero if space, / or - line-ending OK */
int
issep(char c)
{
  return isspace(c) || (c == '/') || (c == '-');
}


/* spacepad() adds n spaces to string dest */
int
spacepad(char *dest, int n)
{
  int	i;
  char	*tpos;

  tpos = dest;
  for (i=0;i<n;i++)
    *tpos++ = ' ';

  return n;
}
