Logo Search packages:      
Sourcecode: hanzim version File versions  Download package


/* hanzim.c: main program file for Hanzi Master Chinese character learning
   aid program.  See .H file for more information. */

/* NOTE: All platform-dependent stuff is in this file
 * (hence the #ifdefs, and the ugliness :). */

/* 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"

#include <time.h>
#include <sys/timeb.h>
#include <errno.h>

/* Windows init stuff */
#ifdef _WINDOWS
#include <windows.h>
#include <malloc.h>
#include <locale.h>

extern int errno;

EXTERN int        Tk_CreateConsoleWindow(Tcl_Interp *interp);
EXTERN void       Tk_InitConsoleChannels(Tcl_Interp *interp);

static void       setargv _ANSI_ARGS_((int *argcPtr, char ***argvPtr));
static void       WishPanic _ANSI_ARGS_(TCL_VARARGS(char *,format));
/* End Windows init */

/* global variables */

char init_str_t[N_INITS][7];
char fin_str_t[N_FINALS][5][13];

/* arrays of character info structures, to be dynamically allocated */
pinyinst    *pinyintbl;
characterst *chartbl;
radicalst   *radtbl;
compoundst  *comptbl;
tripletst   *triplettbl;
int         *fnt2chr,   /* to convert given font to char index */
            nchars,           /* actual numbers we index */
            Nchar,            /* these 3 are maximum possible numbers */
            Ncomp,            /* determined in 1st pass through databases */

/* these flags are set based on command-line options */
int debug;
int verbose;
int quiet;

/* two bitmaps for use in interface window */
/* 1) left arrow */
#define left_width 30
#define left_height 28
static unsigned char left_bits[] = {
   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x00,
   0x00, 0x37, 0x00, 0x00, 0x80, 0x3b, 0x00, 0x00, 0xc0, 0x1d, 0x00, 0x00,
   0xe0, 0x0e, 0x00, 0x00, 0x70, 0x07, 0x00, 0x00, 0xb8, 0xff, 0xff, 0x1f,
   0x5c, 0xff, 0xff, 0x0f, 0xae, 0xaa, 0xaa, 0x06, 0x57, 0x55, 0x55, 0x03,
   0xae, 0xaa, 0xaa, 0x06, 0x5c, 0xff, 0xff, 0x0f, 0xb8, 0xff, 0xff, 0x1f,
   0x70, 0x07, 0x00, 0x00, 0xe0, 0x0e, 0x00, 0x00, 0xc0, 0x1d, 0x00, 0x00,
   0x80, 0x3b, 0x00, 0x00, 0x00, 0x37, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x00,
   0x00, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x00};
/* 2) right arrow */
#define right_width 30
#define right_height 28
static unsigned char right_bits[] = {
   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x07, 0x00, 0x00, 0x80, 0x0f, 0x00,
   0x00, 0x80, 0x1d, 0x00, 0x00, 0x80, 0x3b, 0x00, 0x00, 0x00, 0x77, 0x00,
   0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xdc, 0x01, 0xff, 0xff, 0xbf, 0x03,
   0xfe, 0xff, 0x5f, 0x07, 0xac, 0xaa, 0xaa, 0x0e, 0x58, 0x55, 0x55, 0x1d,
   0xac, 0xaa, 0xaa, 0x0e, 0xfe, 0xff, 0x5f, 0x07, 0xff, 0xff, 0xbf, 0x03,
   0x00, 0x00, 0xdc, 0x01, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0x77, 0x00,
   0x00, 0x80, 0x3b, 0x00, 0x00, 0x80, 0x1d, 0x00, 0x00, 0x80, 0x0f, 0x00,
   0x00, 0x80, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x00};

#ifdef UNIX
/* Tk AppInit stuff for unix, taken from Welch example and various other
   sources... */

/* Boilerplate Tk initialization functions that we define below. */
typedef int (Tcl_myAppInitProc) _ANSI_ARGS_((Tcl_Interp *interp ));
int Tcl_UnixAppInit(Tcl_Interp *interp);

/* Same functions w/different signatures useful for I forget... ;-| */
/*typedef int (Tcl_myAppInitProc) _ANSI_ARGS_((int argc, char **argv, Tcl_Interp *interp )); */
/*int Tcl_UnixAppInit(int argc, char **argv, Tcl_Interp *interp); */

/* An intermediary between main() and the above function. */
void Tk_myMain(int argc, char **argv, Tcl_myAppInitProc *myappInitProc);

/* This function is called if the user specifies the '-buildDB' option.
   It simply runs the database initialization routines (which may result
   in the building of the file "hcompound.dat" if it doesn't exist) and
   exits. */
int buildOnly(char *dst, char *key, char *nextArg);

/* A table for command line arguments.
   This array describes the arguments that can be used and their help
   messages, and any special handling that Tcl should accord them. */
char *buildOpt = NULL;
static Tk_ArgvInfo argTable[] = {
      {(char *) NULL, TK_ARGV_HELP, (char *)NULL, (char *)NULL,
         "\n -db <directory>: Specify the location of data files, overriding both the\n\t\t  compiled default and the environment variable HANZIM_LIB."},
      {"\n -buildDB", TK_ARGV_FUNC, (char *) &buildOnly,
      (char *) &buildOpt, "  (Only run first time, by root) Build binary compound database."},
      {"-verbose", TK_ARGV_CONSTANT, (char *) 1, (char *) &verbose,
            "Print some additional diagnostic information on startup."},
      {"-quiet", TK_ARGV_CONSTANT, (char *) 1, (char *) &quiet,
            "Don't print any startup information."},
      {"-debug", TK_ARGV_CONSTANT, (char *) 1, (char *) &debug,
            "Enable debugging/interactive mode (implies -verbose)."},
      {"-help", TK_ARGV_HELP, (char *)NULL, (char *)NULL,
            "Print command-line help information (this).\n"},

      {(char *) NULL, TK_ARGV_HELP, (char *)NULL, (char *)NULL,
            " -simplified:  use simplified characters on startup (default)"},
      {(char *) NULL, TK_ARGV_HELP, (char *)NULL, (char *)NULL,
            " -traditional: use traditional characters on startup"},

      {(char *) NULL, TK_ARGV_HELP, (char *)NULL, (char *)NULL,
            "\n -pinyin:   force use of pinyin even in traditional mode"},
      {(char *) NULL, TK_ARGV_HELP, (char *)NULL, (char *)NULL,
            " -bopomofo: force use of zhuyin fuhao even in simplified mode"},

      {(char *) NULL, TK_ARGV_HELP, (char *)NULL, (char *)NULL,
            "\n -large:  large size (1024x768) display (default)"},
      {(char *) NULL, TK_ARGV_HELP, (char *)NULL, (char *)NULL,
            " -medium: medium size (800x600) display"},
      {(char *) NULL, TK_ARGV_HELP, (char *)NULL, (char *)NULL,
            " -small:  small size  (640x480) display"},

      {(char *) NULL, TK_ARGV_HELP, (char *)NULL, (char *)NULL,
            "\n -hidepron: do not display Roman pinyin in lower panels\n"},
      {"", TK_ARGV_END, },

/* buildOnly() called (called in unix only) performs database initialization
   only and then exits.  It is meant to be run by installer programs as root,
   for purposes of building the binary compund database 'hcompound.dat'.  If
   the "cidian" file is ever updated, this must be redone.
buildOnly(char *dst, char *key, char *nextArg)
  char fname[100];
  char datadir[80];

  fprintf(stderr,"Hanzim: (re)building database...\n");

  /* set datadir to LIBDIR; in future should allow simultaneous use of
     '-db' and '-buildDB' */

  /* This code a subset of what occurs in db_init() below. */
  if (!init_vars(datadir)) {
    printf("\n\n *** Could not allocate memory for variables.  Sorry!\n");

  /* remove old hcompound.dat, if exists, from the data directory */
  sprintf(fname,"%s/%s",datadir, "hcompound.dat");
  if ((remove(fname) == -1) && (errno == EACCES)) {
    fprintf(stderr,"**Error: You don't have write permission for the file:\n");
    fprintf(stderr,"         \"%s\".\n\n", fname);

  /* build the new one */
  kanzi(datadir); /* character data; needs to be done before compounds */
  kanhe(datadir); /* compound data; will build database "hcompound.dat"*/

  /* We exit, but for niceness' sake put a return at the end. */
  return 0;

/* main(), called in Unix only, just calls a special Tk initialization
   function, which in turn calls Tcl_UnixAppInit below, which sets up the C
   commands that will be linked to Tk events and also calls the hanzi
   database initialization function. */
main(int argc, char *argv[]) {
  Tcl_Interp *interp;

  /* do an initial parse of the command-line options so that help message */
  /* can be printed and we can set some global flags */
  interp = Tcl_CreateInterp();
  if (Tk_ParseArgv(interp, (Tk_Window) NULL, &argc, (const char **) argv,
               argTable, TK_ARGV_NO_DEFAULTS) != TCL_OK) {
    fprintf(stderr, "\nHanzim: %s\n", interp->result);
  if (quiet) verbose = 0;
  if (debug) verbose = 1;

  if (!debug)
    Tk_myMain(argc, argv, Tcl_UnixAppInit); /* slicker Tk_Main */
  else  Tk_Main(argc, argv, Tcl_UnixAppInit); /* system-supplied Tk_Main */

/* Tk_myMain() replaces a Tk_Main() procedure that comes in the Tcl/Tk
   library.  Modified from the "mgsimple" example on the Neosoft archive. */
Tk_myMain(int argc, char **argv, Tcl_myAppInitProc *myappInitProc) {
  Tcl_Interp *interp;
  char buf[20], *args;
  Tcl_DString argString;

  /* create an interpreter after doing the necessary preliminary */
  interp = Tcl_CreateInterp();

  /* get the arguments into the interpreter */
  /* (taken from tk8.3.1/generic/tkMain.c) */
  args = Tcl_Merge(argc-1, (const char **) argv+1);
  Tcl_ExternalToUtfDString(NULL, args, -1, &argString);
  Tcl_SetVar(interp, "argv", Tcl_DStringValue(&argString), TCL_GLOBAL_ONLY);
  sprintf(buf, "%d", argc-1);
  Tcl_ExternalToUtfDString(NULL, argv[0], -1, &argString);
  Tcl_SetVar(interp, "argc", buf, TCL_GLOBAL_ONLY);
  Tcl_SetVar(interp, "argv0", Tcl_DStringValue(&argString), TCL_GLOBAL_ONLY);

  /* call the so-called "application-specific initialization" */
  if ((*myappInitProc)(interp) != TCL_OK) {
    fprintf(stderr,"\nHanzim:\tstart-up error!\n");
    fprintf(stderr,"\tWas the program installed properly and are all the\n");
    fprintf(stderr,"\tfiles present in the library/data directory?\n");

  /* loop until main window closed */


/* Tcl_UnixAppInit() registers C functions as Tk-callable commands and calls
   the database initialization function db_init().
   This header is called in Unix only, but the bottom half below is common
   to it and Tcl_WinAppInit(). */
Tcl_UnixAppInit(Tcl_Interp *interp)
  int       argcl, argc,
  char            **argv, *eptr,

  if (Tcl_Init(interp) == TCL_ERROR) {
      return TCL_ERROR;
  if (Tk_Init(interp) == TCL_ERROR) {
      return TCL_ERROR;

/**** Cut off function in mid-flight, to be returned to below... *****/

#else /* Windows */

/* Windows app init stuff, taken from tk source distribution windows subdir
   winMain.c */
int DLLEXPORT Tcl_WinAppInit(Tcl_Interp *interp);

/* Dummy */
int buildOnly(char *dst, char *key, char *nextArg) { return 0; }

/* WinMain returns false if initialization fails. */
int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
                              LPSTR lpszCmdLine, int nCmdShow)
    char **argv;
    int argc;
    /* Tcl_Interp *interp; */

    Tcl_SetPanicProc(WishPanic);    /* very important for Windows :) */

    /* Set up the default locale to be standard "C" locale so parsing
       is performed correctly. */
    setlocale(LC_ALL, "C");
    setargv(&argc, &argv);

    /* Increase the application queue size from default value of 8.
     * At the default value, cross application SendMessage of WM_KILLFOCUS
     * will fail because the handler will not be able to do a PostMessage!
     * This is only needed for Windows 3.x, since NT dynamically expands
     * the queue. */
  /* Not working yet... probably need to do console init first (see below). */
    /* interp = Tcl_CreateInterp();
    if (Tk_ParseArgv(interp, (Tk_Window) NULL, &argc, argv,
               argTable, TK_ARGV_NO_DEFAULTS) != TCL_OK) {
    fprintf(stderr, "\nHanzim: %s\n", interp->result);
    if (debug) verbose = 1;  */

    Tk_Main(argc, argv, Tcl_WinAppInit);
    return 1;

/* This is the corresponding Windows header to Tcl_UnixAppInit() above.
   It shares the same bottom half with that function. */
int DLLEXPORT Tcl_WinAppInit(Tcl_Interp *interp)
  int argcl, argc,
  char      **argv, *eptr,
  static char     fontFile[90];

    if (Tcl_Init(interp) == TCL_ERROR) {
      return TCL_ERROR;
    if (Tk_Init(interp) == TCL_ERROR) {
      return TCL_ERROR;

    Tcl_StaticPackage(interp, "Tk", Tk_Init, Tk_SafeInit);

    /* Initialize the console only if we are running as an interactive
     * application. */
    /* Create the console channels and install them as the standard
     * channels.  All I/O will be discarded until TkConsoleInit is
     * called to attach the console to a text widget. */

      /* The following will create a console window for debugging purposes.*/
      if (debug) {
            if (Tk_CreateConsoleWindow(interp) == TCL_ERROR) {
                  return TCL_ERROR;

  /* Not usually needed... */
    /* Tcl_SetVar(interp, "tcl_rcFileName","~/wishrc.tcl",TCL_GLOBAL_ONLY); */


 * this segment is in Tcl_UnixAppInit or Tcl_WinAppInit depending on platform

    /* Call Tcl_CreateCommand for application-specific commands. */
    Tcl_CreateCommand(interp, "sameRadList", sameRadList,
                  (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
    Tcl_CreateCommand(interp, "sameRemList", sameRemList,
                  (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
    Tcl_CreateCommand(interp, "samePronList", samePronList,
                  (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
    Tcl_CreateCommand(interp, "LcompList", LcompList,
                  (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
    Tcl_CreateCommand(interp, "RcompList", RcompList,
                  (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
    Tcl_CreateCommand(interp, "radList", radList,
                  (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
    Tcl_CreateCommand(interp, "pyChar", pyChar,
                  (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
    Tcl_CreateCommand(interp, "randChar", randChar,
                  (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
    Tcl_CreateCommand(interp, "charinfo", charinfo,
                  (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
    Tcl_CreateCommand(interp, "yingHanCh", yingHanCh,
                  (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
    Tcl_CreateCommand(interp, "searchDefn", searchDefn,
                  (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
    Tcl_CreateObjCommand(interp, "convertSelection",
                         (Tcl_ObjCmdProc *) convertSelection,
                         (ClientData) NULL, (void (*)()) NULL);

    /* See if user specified a database directory in the command line args
       or in an environment variable then call database initialization
       function. */
    eptr = getenv("HANZIM_LIB");  /* check in environment */
    if (eptr == NULL) strcpy(dbase_dir,LIBDIR); /* default */
    else strcpy(dbase_dir, eptr);

    /* check for command line */
    argcl = atoi(Tcl_GetVar(interp, "argc", 0));

    if (argcl > 0)      /* tcl inanity */
      Tcl_SplitList(interp,Tcl_GetVar(interp, "argv", 0),
                    &argc, (const char***) &argv);
    for (i=0;i<argcl;i++) {
      if ((!strcmp(argv[i],"-db")) && (argcl > i+1)) {
      if (dbase_dir[strlen(dbase_dir)-1] == '/')
        dbase_dir[strlen(dbase_dir)-1] = '\0';  /* chop slash */

    /* set fonts file depending on whether in windows or unix */
#ifdef UNIX
  if (argcl > 0) Tcl_Free((char *) argv);       /* (unrelated) */

    /* evaluate a tcl script to get things going (sets up initial window
       and event handlers) */
    Tk_DefineBitmap(interp, Tk_GetUid("leftArrow"), (char *) left_bits,
                left_width, left_height);
    Tk_DefineBitmap(interp, Tk_GetUid("rightArrow"), (char *) right_bits,
                right_width, right_height);
    Tcl_SetVar(interp, "fontPropsFile", fontFile, TCL_GLOBAL_ONLY);
    sprintf(tclfname,"%s/hanwin.tcl", dbase_dir);
    if (Tcl_EvalFile(interp,tclfname) == TCL_ERROR)
      return TCL_ERROR;

    return TCL_OK;

/* db_init() calls all of the various database reading functions in hinit.C
   which get everything into the structures in hanzi.H */
db_init(char *datadir, Tcl_Interp *interp)
  /* initialize random number generator from system clock */
#ifdef _WINDOWS
  struct _timeb tp;
  struct timeb    tp;

  if (!quiet) {
    printf("\n\nWelcome to Hanzi Master, where YOU will master the hanzi.\n");
    printf("\nPlease use \"Ctrl-h\" or \"Alt-h\" for help.\n");
    printf("Invoke 'hanzim -help' for description of command-line options.\n");
    printf("\n(Software released under GPL, carries no warranty.)\n\n");

  if (!init_vars(datadir)) {
    printf("\n\n *** Could not allocate memory for variables.  Sorry!\n");
  if (verbose) printf("Variables initialized.\n");

  kanzi(datadir); /* character data */
  kanhe(datadir); /* compound data */
  kanbu(datadir); /* radical data */
  kansanzi(datadir);    /* triplet data; no indexing performed */

  if (verbose) printf("Converting to UTF encoding...\n");

  if (verbose) printf("Starting the rest of the stuff...\n");
  if (!quiet) printf("\n");

 * Some various windows-specific functions making up for deficiences on
 * that platform...

#ifdef _WINDOWS
/* WishPanic -- Display a message and exit. */
WishPanic TCL_VARARGS_DEF(char *,arg1)
    va_list argList;
    char buf[1024];
    char *format;
    format = TCL_VARARGS_START(char *,arg1,argList);
    vsprintf(buf, format, argList);

    MessageBox(NULL, buf, "Fatal Error in Wish",
#ifdef _MSC_VER

/* setargv --
 *    Parse the Windows command line string into argc/argv.  Done here
 *    because we don't trust the builtin argument parser in crt0.  
 *    Windows applications are responsible for breaking their command
 *    line into arguments.
 *    2N backslashes + quote -> N backslashes + begin quoted string
 *    2N + 1 backslashes + quote -> literal
 *    N backslashes + non-quote -> literal
 *    quote + quote in a quoted string -> single quote
 *    quote + quote not in quoted string -> empty string
 *    quote -> begin quoted string
 * Results:
 *    Fills argcPtr with the number of arguments and argvPtr with the
 *    array of arguments.
 * Side effects:
 *    Memory allocated.
static void
setargv(int *argcPtr, char ***argvPtr)
    char *cmdLine, *p, *arg, *argSpace;
    char **argv;
    int argc, size, inquote, copy, slashes;
    cmdLine = GetCommandLine();     /* INTL: BUG */

    /* Precompute an overly pessimistic guess at the number of arguments
     * in the command line by counting non-space spans. */

    size = 2;
    for (p = cmdLine; *p != '\0'; p++) {
      if ((*p == ' ') || (*p == '\t')) {  /* INTL: ISO space. */
          while ((*p == ' ') || (*p == '\t')) { /* INTL: ISO space. */
          if (*p == '\0') {
    argSpace = (char *) Tcl_Alloc(
          (unsigned) (size * sizeof(char *) + strlen(cmdLine) + 1));
    argv = (char **) argSpace;
    argSpace += size * sizeof(char *);

    p = cmdLine;
    for (argc = 0; argc < size; argc++) {
      argv[argc] = arg = argSpace;
      while ((*p == ' ') || (*p == '\t')) {     /* INTL: ISO space. */
      if (*p == '\0') {

      inquote = 0;
      slashes = 0;
      while (1) {
          copy = 1;
          while (*p == '\\') {
          if (*p == '"') {
            if ((slashes & 1) == 0) {
                copy = 0;
                if ((inquote) && (p[1] == '"')) {
                  copy = 1;
                } else {
                  inquote = !inquote;
                slashes >>= 1;

            while (slashes) {
            *arg = '\\';

          if ((*p == '\0')
             || (!inquote && ((*p == ' ') || (*p == '\t')))) { /* INTL: ISO space. */
          if (copy != 0) {
            *arg = *p;
      *arg = '\0';
      argSpace = arg + 1;
    argv[argc] = NULL;

    *argcPtr = argc;
    *argvPtr = argv;
#endif /* _WINDOWS */

Generated by  Doxygen 1.6.0   Back to index