/* * 小学館ランダムハウス英語辞典検索ユーティリティー - csrd * * Written by Junn Ohta (ohta@src.ricoh.co.jp), Public Domain. */ char *progname = "csrd"; char *version = "1.0"; char *date = "1999/07/19"; #include #include #include #include #include #include #include #include #ifdef UNIX #include #endif #ifdef MSDOS #include #endif typedef unsigned char uchr; typedef unsigned int unt; #ifndef O_BINARY #define O_BINARY 0 #endif #define OK 0 #define ERR (-1) #define TRUE 1 #define FALSE 0 #define SJIS1(c) ((c)>=0x81 && (c)<=0x9f || (c)>=0xe0 && (c)<=0xef) #ifdef DEBUG int debug = FALSE; /* デバッグフラグ */ int debug2 = FALSE; /* デバッグフラグ */ #endif /* -------------------- ユーティリティー関数 -------------------- */ #define POOLSIZ 1024 /* 文字列プールの割り当て単位 */ uchr *pool = NULL; /* 文字列プール */ uchr *pfree = NULL; /* 文字列プールの空き領域先頭 */ int hexval(); uchr *escchar(); uchr *skipeq(); uchr *skipsp(); uchr *strpool(); void bsltosl(); int nspaces(); void lower(); /* * hexval - 2桁の16進数が示す値を返す */ int hexval(p) uchr *p; { int i, n; n = 0; for (i = 0; i < 2; i++) { n <<= 4; if (*p >= '0' && *p <= '9') n += *p - '0'; else if (*p >= 'a' && *p <= 'f') n += *p - 'a' + 10; else if (*p >= 'A' && *p <= 'F') n += *p - 'A' + 10; else n = 0; p++; } return n; } /* * escchar - '\'でエスケープされた文字を調べ、次の位置を返す */ uchr * escchar(str, cp) uchr *str; int *cp; { uchr *p; p = str; if (*p++ != '\\') return str; if (*p >= '0' && *p <= '7') { sscanf(p, "%3o", cp); return p + 3; } switch (*p) { case 'x': *cp = hexval(p + 1); p += 3; break; case 'n': *cp = '\n'; p++; break; case 't': *cp = '\t'; p++; break; default: *cp = *p++; break; } return p; } /* * skipeq - '='までを読み飛ばし、次の位置を返す */ uchr * skipeq(str) uchr *str; { while (*str && *str != '=') str++; if (*str == '=') str++; return str; } /* * skipsp - 空白を読み飛ばし、次の位置を返す */ uchr * skipsp(str) uchr *str; { while (*str == ' ' || *str == '\t') str++; return str; } /* * strpool - 文字列をプールに格納してそのアドレスを返す * (解放を考慮しないstrdup) */ uchr * strpool(str) uchr *str; { int len; uchr *p; if (str == NULL) return NULL; len = strlen(str) + 1; if (pool == NULL || pfree + len > pool + POOLSIZ) { pool = (uchr *)malloc(POOLSIZ); /* 以前のpoolの値は忘れる */ if (pool == NULL) return NULL; pfree = pool; } p = pfree; pfree += len; return strcpy(p, str); } /* * bsltosl - 文字列中のバックスラッシュ(\)をスラッシュ(/)に変換する */ void bsltosl(str) uchr *str; { if (str == NULL) return; while (*str) { if (*str == '\\') *str = '/'; if (SJIS1(*str)) str++; str++; } } /* * nspaces - 文字列の先頭にあるスペースの量を数える */ int nspaces(str) uchr *str; { int n; if (str == NULL) return 0; n = 0; while (*str && isspace(*str)) { switch (*str++) { case ' ': n++; break; case '\t': n = (n | 0x07) + 1; break; case '\n': case '\r': n = 0; break; case '\b': if (n > 0) n--; break; } } return n; } /* * lower - 英文字列を小文字化する */ void lower(str) uchr *str; { if (str == NULL) return; while (*str) { if (isascii(*str) && isupper(*str)) *str = tolower(*str); str++; } } /* -------------------- バッファード入力 -------------------- */ #define BBUFSIZ 4096 typedef struct _bfile { int fd; long size; long pos; uchr *p, *ep; uchr buf[BBUFSIZ]; } BFILE; BFILE *bopen(); long btell(); int bseek(); int bread(); int bgetc(); int bgetcbw(); int bclose(); /* * bopen - バッファード入力でファイルをオープンする * bseek()で位置づけをするまで読み込みはできない */ BFILE * bopen(file) char *file; { int fd; BFILE *bp; struct stat st; if ((fd = open(file, O_RDONLY|O_BINARY)) < 0) return NULL; if (fstat(fd, &st) < 0) { close(fd); return NULL; } bp = (BFILE *)malloc(sizeof(BFILE)); if (bp == NULL) { close(fd); return NULL; } bp->fd = fd; bp->size = st.st_size; bp->pos = 0L; bp->p = bp->ep = NULL; return bp; } /* * btell - バッファード入力のtell()もどき */ long btell(bp) BFILE *bp; { if (bp->p == NULL) return (long)ERR; return bp->pos + (bp->p - bp->buf); } /* * bseek - バッファード入力のlseek()もどき */ int bseek(bp, pos) BFILE *bp; long pos; { int n; if (bp->p && pos >= bp->pos && pos < bp->pos + (bp->ep - bp->buf)) { bp->p = bp->buf + (pos - bp->pos); return OK; } if (pos >= bp->size || lseek(bp->fd, pos, SEEK_SET) < 0 || (n = read(bp->fd, bp->buf, BBUFSIZ)) <= 0) { bp->pos = 0L; bp->p = bp->ep = NULL; return ERR; } bp->pos = pos; bp->p = bp->buf; bp->ep = bp->buf + n; return OK; } /* * bread - バッファード入力のread()もどき */ int bread(bp, buf, len) BFILE *bp; uchr *buf; int len; { int n; uchr *p; if (bp->p == NULL) return ERR; p = buf; while (len > 0) { n = bp->ep - bp->p; if (n >= len) { memcpy(p, bp->p, (size_t)len); p += len; bp->p += len; len = 0; } else if (n > 0) { memcpy(p, bp->p, (size_t)n); p += n; bp->p += n; len -= n; } else { n = read(bp->fd, bp->buf, BBUFSIZ); if (n <= 0) { bp->pos = 0L; bp->p = bp->ep = NULL; break; } bp->pos += bp->ep - bp->buf; bp->p = bp->buf; bp->ep = bp->buf + n; } } return p - buf; } /* * bgetc - バッファード入力のgetc()もどき */ int bgetc(bp) BFILE *bp; { int n; if (bp->p == NULL) return EOF; if (bp->p < bp->ep) return (int)*(bp->p++); n = read(bp->fd, bp->buf, BBUFSIZ); if (n <= 0) { bp->pos = 0L; bp->p = bp->ep = NULL; return EOF; } bp->pos += bp->ep - bp->buf; bp->p = bp->buf; bp->ep = bp->buf + n; return (int)*(bp->p++); } /* * bgetcbw - 現在の位置からファイル先頭方向にbgetc()する */ int bgetcbw(bp) BFILE *bp; { int n; long npos; if (bp->p == NULL) return EOF; if (bp->p > bp->buf) return (int)*(--bp->p); n = BBUFSIZ; npos = bp->pos - n; if (bp->pos < 0L) { npos = 0L; n = bp->pos - npos; if (n == 0) return EOF; } if (lseek(bp->fd, npos, SEEK_SET) < 0 || read(bp->fd, bp->buf, n) < n) { bp->pos = 0L; bp->p = bp->ep = NULL; return EOF; } bp->pos = npos; bp->ep = bp->buf + n; bp->p = bp->ep; return (int)*(--bp->p); } /* * bclose - バッファード入力のclose()もどき */ int bclose(bp) BFILE *bp; { int fd; fd = bp->fd; free((char *)bp); return close(fd); } /* -------------------- 日本語処理 -------------------- */ #define KC_JIS 0 #define KC_EUC 1 #define KC_SJIS 2 #ifdef UNIX #define KC_DEF KC_EUC #endif #ifdef MSDOS #define KC_DEF KC_SJIS #endif /* * 外字表 */ uchr *gaijitbl[8][256] = { { /* G0 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,"/", 0,0,0,0,0,0,0,0,0,0,":",0,0,"=",0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,"...",0,0,0,0,0,0,0,0,0,0, 0,0,0,"\"","\"",0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,"(C)",0,0,0,0,"(R)",0, 0,0,0,0,0,0,0,"/",0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }, { /* G1 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,"(タブー)",0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }, { /* G2 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,"▼",0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }, { /* G3 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,"(P)",0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,"←→",0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }, { /* G4 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,"→",0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,"《", "》","〔","〕",0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }, { /* G5 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }, { /* G6 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }, { /* G99 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,"#",0,0,0,0,0,0,0,0,0,0,0,"/", 0,0,0,0,0,0,0,0,0,0,0,0,"<",0,">",0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,"...",0,0,0,0,0,0,0,0,0,0, 0,0,0,"\"","\"",0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, "A","A",0,0,0,0,0,0,"E","E",0,0,"I","I",0,0, 0,0,"O","O",0,0,0,0,0,"U","U",0,0,0,0,0, "a","a",0,0,0,0,0,0,"e","e",0,0,"i","i",0,0, 0,0,"o","o",0,0,0,0,0,"u","u",0,0,"y",0,0 } }; /* * デフォルト外字 */ uchr *gdeftbl[8] = { 0, 0, 0, 0, 0, 0 ,"〓", 0 }; typedef struct _alttbl_t { uchr org[4]; /* 元表記 */ uchr *alt; /* 代替表記 */ } ALTTBL; #define ATBLINC 10 /* 代替表記表の増分 */ uchr have_alt[256]; /* その文字は代替表記を持つか? */ ALTTBL *alttbl = NULL; /* 代替表記表 */ int altnum = 0; /* 代替表記表要素数 */ int in_alt = FALSE; /* 代替表記中状態か? */ int curpos = 0; /* 現在の表示位置 */ int kcode = KC_DEF; /* 入出力漢字コード */ int jiskanji = 'B'; /* JIS漢字コードの漢字選択 */ int jisalpha = 'B'; /* JIS漢字コードの英字選択 */ int width = 0; /* 出力行幅 */ int indent = 0; /* インデント量 */ int kmode = FALSE; /* 端末がJIS漢字指示状態か? */ int rawmode = FALSE; /* 出力無加工モード */ int gfont = -1; /* 外字フォント番号 */ int hlink = FALSE; /* ハイパーリンク指定内 */ int add_alt(); int init_alt(); uchr *get_gaiji(); int load_gaiji(); uchr *tosjis(); void outchar(); void outstr(); void set_indent(); void newline(); /* * add_alt - 代替表記表に代替表記を追加する */ int add_alt(org, alt) uchr *org, *alt; { int i; uchr *p; static int atblsiz = 0; for (i = 0; i < altnum; i++) { if (!strcmp(alttbl[i].org, org)) { p = strpool(alt); if (p == NULL) return ERR; alttbl[i].alt = p; return OK; } } if (alttbl == NULL) { atblsiz = ATBLINC; alttbl = (ALTTBL *)malloc(sizeof(ALTTBL) * atblsiz); altnum = 0; } else if (altnum >= atblsiz) { atblsiz += ATBLINC; alttbl = (ALTTBL *)realloc(alttbl, sizeof(ALTTBL) * atblsiz); } if (alttbl == NULL) return ERR; p = strpool(alt); if (p == NULL) return ERR; strcpy(alttbl[altnum].org, org); alttbl[altnum].alt = p; have_alt[*org] = TRUE; altnum++; return OK; } /* * init_alt - 代替表記表にデフォルト代替表記を登録する */ int init_alt() { int err; err = 0; if (add_alt("\xa5", "/") == ERR) /* 1バイトカナの中点 */ err++; return err; } /* * get_gaiji - フォント番号とコードから外字文字列を返す */ uchr * get_gaiji(g, ch) int g, ch; { uchr *p; if (g == 9) g = 7; p = gaijitbl[g][ch]; if (p == NULL) p = gdeftbl[g]; return p; } /* * load_gaiji - 外字代替表記ファイルを読んで外字を登録する */ int load_gaiji(file) uchr *file; { int c, ch, g, isalt; int line, err; uchr *p, *q; FILE *fp; uchr org[4], buf[256], tmp[256]; if ((fp = fopen(file, "r")) == NULL) { outstr("ERR: 外字代替表記ファイルがオープンできません\n"); return ERR; } line = 0; err = 0; while (fgets(buf, 256, fp) != NULL) { line++; buf[strlen(buf) - 1] = '\0'; p = skipsp(buf); if (*p == '\0' || *p == ';') continue; if (*p == '"') { /* 代替表記 */ p++; if (*p == '\\') { p = escchar(p, &c); org[0] = c; org[1] = '\0'; } else if (SJIS1(*p)) { org[0] = *p++; org[1] = *p++; org[2] = '\0'; } else { org[0] = *p++; org[1] = '\0'; } if (*p != '"') { sprintf(tmp, "ERR: line %d: 代替元文字が正しくありません\n", line); outstr(tmp); err++; continue; } p++; isalt = TRUE; } else { /* 外字定義 */ if (*p != 'G' || !strchr("01234569", p[1]) || p[2] != '-' || !(isxdigit(p[3]) && isxdigit(p[4]) || p[3] == '*')) { sprintf(tmp, "ERR: line %d: 外字番号が正しくありません\n", line); outstr(tmp); err++; continue; } g = p[1] - '0'; if (g == 9) g = 7; if (p[3] == '*') { ch = -1; p += 4; } else { ch = hexval(p + 3); p += 5; } isalt = FALSE; } p = skipsp(p); if (*p == '=') p++; p = skipsp(p); if (*p != '"') { sprintf(tmp, "ERR: line %d: 代替表記文字列が正しくありません\n", line); outstr(tmp); err++; continue; } p++; q = tmp; while (*p && *p != '"') { if (*p == '\\') { p = escchar(p, &c); if (c == '\\' || c == '<' || c == '>') *q++ = '\\'; *q++ = c; continue; } if (SJIS1(*p)) { *q++ = *p++; *q++ = *p++; continue; } if (*p == '<' || *p == '>' || *p == '#') *q++ = '\\'; *q++ = *p++; } if (*p != '"') { sprintf(tmp, "ERR: line %d: 代替表記文字列が正しくありません\n", line); outstr(tmp); err++; continue; } *q = '\0'; if (isalt) { if (add_alt(org, tmp) == ERR) { outstr("ERR: メモリーが足りません\n"); err++; } continue; } if ((q = strpool(tmp)) == NULL) { outstr("ERR: メモリーが足りません\n"); err++; continue; } if (ch >= 0) gaijitbl[g][ch] = q; else gdeftbl[g] = q; } fclose(fp); if (err) return ERR; return OK; } #ifdef MSDOS uchr * tosjis(str) uchr *str; { return str; } void outchar(c) int c; { if (c == 0) return; putchar(c); } #endif #ifdef UNIX /* * tosjis - kcodeで表現された文字列をシフトJISに変換する * (コマンド行引数の変換に使う) */ uchr * tosjis(str) uchr *str; { int c, c1; int c2, km; uchr *p; static uchr buf[128]; if (kcode == KC_SJIS) { strcpy((char *)buf, str); return buf; } km = FALSE; p = buf; c1 = 0; while ((c = *str++) != '\0') { if (c1 != 0) { c1 &= 0x7f; c &= 0x7f; if ((c1 & 0x01) == 0) { c += 0x7e; } else { c += 0x1f; if (c > 0x7e) c++; } c1 = (c1 + 0xe1) >> 1; if (c1 > 0x9f) c1 += 0x40; *p++ = c1; *p++ = c; c1 = 0; continue; } if (c == '\033') { c1 = *str++; c2 = *str++; if (c1 == '$' && (c2 == '@' || c2 == 'B')) km = TRUE; else if (c1 == '(' && (c2 == 'J' || c2 == 'B')) km = FALSE; continue; } if ((c & 0x80) || km == 1) { c1 = c; continue; } *p++ = c; } *p = '\0'; return buf; } /* * outchar - シフトJISの文字を状態遷移を管理しながら * kcodeに変換して出力する * 引数が0なら出力側の状態を英字に戻す(JISのとき必要) */ void outchar(c) int c; { static int knj1 = 0; if (c == '\0') { if (kmode) { printf("\033(%c", jisalpha); kmode = FALSE; } return; } if (knj1 != 0) { if (kcode == KC_JIS && !kmode) { printf("\033$%c", jiskanji); kmode = TRUE; } if (kcode == KC_JIS || kcode == KC_EUC) { if (knj1 > 0x9f) knj1 -= 0x40; knj1 += knj1; if (c <= 0x9e) { knj1 -= 0xe1; if (c >= 0x80) c -= 1; c -= 0x1f; } else { knj1 -= 0xe0; c -= 0x7e; } if (kcode == KC_EUC) { knj1 += 0x80; c += 0x80; } } putchar(knj1); putchar(c); knj1 = 0; return; } if (c >= 0x81) { knj1 = c; return; } if (kmode) { printf("\033(%c", jisalpha); kmode = FALSE; } putchar(c); } #endif /* * outstr - 外字を変換しながらoutchar経由で文字を出力する */ void outstr(p) uchr *p; { int i, g; uchr *gstr; uchr tmp[16]; while (*p) { if (rawmode) { outchar(*p++); continue; } if (*p == '<') { if (p[1] == '/') { if (p[2] == 'G' && isdigit(p[3])) gfont = -1; else if (!strncmp(p+2, "RUBY>", 5)) outstr(")"); else if (!strncmp(p+2, "HL>", 3)) hlink = FALSE; } else { if (p[1] == 'G') gfont = p[2] - '0'; else if (!strncmp(p+1, "RUBY>", 5)) outstr("("); else if (!strncmp(p+1, "HL>", 3)) hlink = TRUE; } while (*p != '>') p++; p++; continue; } if (*p == ':' && hlink) { /* * ハイパーリンク指定内の「:」以降はすべて無視 */ p++; while (*p && (*p != '<' || strncmp(p, "", 5))) { if (SJIS1(*p)) p++; p++; } p += 5; hlink = FALSE; continue; } if (*p == '#' && p[3] == '#') { g = (gfont == -1)? 0: gfont; gstr = get_gaiji(g, hexval(p+1)); if (gstr) { outstr(gstr); } else { sprintf(tmp, "\\", g, p[1], p[2]); outstr(tmp); } p += 4; continue; } if (!in_alt && have_alt[*p]) { for (i = 0; i < altnum; i++) { if (*p == alttbl[i].org[0] && (!SJIS1(*p) || p[1] == alttbl[i].org[1])) break; } if (i < altnum) { in_alt = TRUE; outstr(alttbl[i].alt); in_alt = FALSE; if (SJIS1(*p)) p++; p++; continue; } } if (*p == '\\') p++; if (SJIS1(*p)) { if (width > 0 && curpos >= width - 1) newline(); outchar(*p++); outchar(*p++); curpos += 2; continue; } if (*p < ' ' || *p == 0x7f) { switch (*p) { case '\n': newline(); p++; break; case '\t': if (width > 0 && ((curpos | 0x07) + 1) > width) newline(); outchar(*p++); curpos = (curpos | 0x07) + 1; break; default: outchar(*p++); break; } continue; } if (width > 0 && curpos >= width && !strchr("!),.:;?]}", *p)) newline(); outchar(*p++); curpos++; } outchar(0); } /* * set_indent - インデント量を設定する */ void set_indent(n) int n; { indent = n; } /* * newline - 改行して設定量だけインデントする */ void newline() { outchar('\n'); for (curpos = 0; curpos < indent; curpos++) outchar(' '); } /* -------------------- 表示書式処理 -------------------- */ /* * 本文データ行頭のラベル */ #define LBL_ERROR (-1) #define LBL_CMT 0 /* 注釈(非表示) */ #define LBL_DR 1 /* 派生形 */ #define LBL_EN 2 /* 見出し語 */ #define LBL_ENN 3 /* 検索用異表記???(非表示) */ #define LBL_ENV 4 /* 異表記 */ #define LBL_ENVN 5 /* 別綴り異表記??? */ #define LBL_ET 6 /* 初出??? */ #define LBL_EV 7 /* 同義語 */ #define LBL_EX 8 /* 用例 */ #define LBL_EXKEY 9 /* 用例検索キー */ #define LBL_EXSUB 10 /* 用例サブ項目 */ #define LBL_F 11 /* IFの誤記???(tallith) */ #define LBL_ID 12 /* 成句 */ #define LBL_IDSUB 13 /* 成句サブ項目 */ #define LBL_IF 14 /* 不規則活用 */ #define LBL_IMG 15 /* 図版データ(非表示) */ #define LBL_J2E 16 /* 日本語から借用(adsuki bean) */ #define LBL_KEY 17 /* 検索キー(非表示) */ #define LBL_KEYEX 18 /* 用例検索キー(非表示) */ #define LBL_KEYID 19 /* 成句検索キー(非表示) */ #define LBL_LOC 20 /* 本文格納位置(非表示) */ #define LBL_MEN 21 /* MNの誤記??? */ #define LBL_MN 22 /* 意味 */ #define LBL_MNSUB 23 /* 意味サブ項目 */ #define LBL_NB 24 /* 註 */ #define LBL_NBP 25 /* 表現註 */ #define LBL_NBPSUB 26 /* 表現註サブ項目 */ #define LBL_NBSUB 27 /* 註サブ項目 */ #define LBL_NSBUS 28 /* NBSUBの誤記???(and) */ #define LBL_OT 29 /* 類語、発音、... */ #define LBL_OTSUB 30 /* 類語サブ項目 */ #define LBL_PR 31 /* 発音 */ #define LBL_PS 32 /* 品詞 */ #define LBL_RA 33 /* ???(非表示) */ #define LBL_RES 34 /* ???(非表示) */ #define LBL_RN 35 /* ???(非表示) */ #define LBL_SM 36 /* ???(非表示) */ #define LBL_SND 37 /* 音声データ(非表示) */ #define LBL_SRC 38 /* 語源 */ #define LBL_SRCSUB 39 /* 語源サブ項目 */ #define LBL_TL 40 /* 文献タイトル */ #define LBL_TLSUB 41 /* 文献タイトルサブ項目 */ #define LBL_TQ 42 /* 意味分類(getの「I 所有する」)*/ #define LBL_TR 43 /* 語意 */ #define LBL_TRSUB 44 /* 語意サブ項目 */ /* * 以下はcsrd内部処理用 */ #define LBL_ZEX 45 /* 成句・用例検索結果の用例 */ #define LBL_ZID 46 /* 成句・用例検索結果の成句 */ typedef struct _lbltbl_t { int id; /* ラベル識別ID */ int disp; /* 出力するか? */ uchr *label; /* ラベル文字列 */ uchr *leader; /* 先頭に出力する文字列 */ } LBLTBL; /* * ラベル表 */ LBLTBL labeltbl[] = { { LBL_CMT, FALSE, "CMT", "" }, { LBL_DR, TRUE, "DR", "" }, { LBL_EN, TRUE, "EN", "□ " }, { LBL_ENN, FALSE, "ENN", "" }, { LBL_ENV, TRUE, "ENV", "" }, { LBL_ENVN, TRUE, "ENVN", "" }, { LBL_ET, TRUE, "ET", "" }, { LBL_EV, TRUE, "EV", "" }, { LBL_EX, TRUE, "EX", " " }, { LBL_EXKEY, FALSE, "EXKEY", "" }, { LBL_EXSUB, TRUE, "EXSUB", "" }, { LBL_F, TRUE, "F", "" }, { LBL_ID, TRUE, "ID", "" }, { LBL_IDSUB, TRUE, "IDSUB", "" }, { LBL_IF, TRUE, "IF", "" }, { LBL_IMG, FALSE, "IMG", "" }, { LBL_J2E, TRUE, "J2E", "" }, { LBL_KEY, FALSE, "KEY", "" }, { LBL_KEYEX, FALSE, "KEYEX", "" }, { LBL_KEYID, FALSE, "KEYID", "" }, { LBL_LOC, FALSE, "LOC", "" }, { LBL_MEN, TRUE, "MEN", "" }, { LBL_MN, TRUE, "MN", "" }, { LBL_MNSUB, TRUE, "MNSUB", "" }, { LBL_NB, TRUE, "NB", "" }, { LBL_NBP, TRUE, "NBP", " " }, { LBL_NBPSUB, TRUE, "NBPSUB", "" }, { LBL_NBSUB, TRUE, "NBSUB", "" }, { LBL_NSBUS, TRUE, "NSBUS", "" }, { LBL_OT, TRUE, "OT", "" }, { LBL_OTSUB, TRUE, "OTSUB", "" }, { LBL_PR, TRUE, "PR", " " }, { LBL_PS, TRUE, "PS", "\n■" }, { LBL_RA, FALSE, "RA", "" }, { LBL_RES, FALSE, "RES", "" }, { LBL_RN, FALSE, "RN", "" }, { LBL_SM, TRUE, "SM", "" }, { LBL_SND, FALSE, "SND", "" }, { LBL_SRC, TRUE, "SRC", "" }, { LBL_SRCSUB, TRUE, "SRCSUB", "" }, { LBL_TL, TRUE, "TL", "" }, { LBL_TLSUB, TRUE, "TLSUB", "" }, { LBL_TQ, TRUE, "TQ", "\n" }, { LBL_TR, TRUE, "TR", "" }, { LBL_TRSUB, TRUE, "TRSUB", "" }, { LBL_ZEX, TRUE, "ZEX", "" }, { LBL_ZID, TRUE, "ZID", "" }, { LBL_ERROR, FALSE, NULL, NULL } }; LBLTBL *get_label(); int load_format(); int suppress(); /* * get_label - 行頭のラベルを識別する */ LBLTBL * get_label(str) uchr *str; { uchr *p, *q; LBLTBL *lp; uchr tmp[128]; p = str; q = tmp; while (*p && isalpha(*p) && isupper(*p)) *q++ = *p++; *q = '\0'; if (!strncmp(str, "J2E", 3)) strcpy(tmp, "J2E"); lp = labeltbl; while (lp->label != NULL) { if (!strncmp(tmp, lp->label, strlen(tmp))) break; lp++; } if (lp->id == LBL_OT) { if (isdigit(str[2]) && str[3] == 'S') lp++; /* OTの代わりにOTSUBを指すようにする */ } return lp; } /* * load_format - 書式ファイルを読んでラベル表を更新する */ int load_format(file) uchr *file; { int c, line, err, disp; uchr *p, *q; FILE *fp; LBLTBL *lp; uchr buf[256], tmp[256]; if ((fp = fopen(file, "r")) == NULL) { outstr("ERR: 表示書式ファイルがオープンできません\n"); return ERR; } line = 0; err = 0; while (fgets(buf, 256, fp) != NULL) { line++; buf[strlen(buf) - 1] = '\0'; disp = TRUE; p = skipsp(buf); if (*p == '\0' || *p == ';') continue; if (*p == '!') { disp = FALSE; p++; } p = skipsp(p); lp = get_label(p); if (lp->id == LBL_ERROR) { sprintf(tmp, "ERR: line %d: 書式項目名が正しくありません\n", line); outstr(tmp); err++; continue; } while (*p && (isupper(*p) || isdigit(*p))) p++; p = skipsp(p); if (*p == '=') p++; p = skipsp(p); if (*p != '"') { sprintf(tmp, "ERR: line %d: 書式文字列が正しくありません\n", line); outstr(tmp); err++; continue; } p++; q = tmp; while (*p && *p != '"') { if (*p == '\\') { p = escchar(p, &c); if (c == '\\' || c == '<' || c == '>') *q++ = '\\'; *q++ = c; continue; } if (SJIS1(*p)) { *q++ = *p++; *q++ = *p++; continue; } if (*p == '<' || *p == '>' || *p == '#') *q++ = '\\'; *q++ = *p++; } if (*p != '"') { sprintf(tmp, "ERR: line %d: 書式文字列が正しくありません\n", line); outstr(tmp); err++; continue; } *q = '\0'; if ((q = strpool(tmp)) == NULL) { outstr("ERR: メモリーが足りません\n"); err++; continue; } lp->leader = q; lp->disp = disp; } fclose(fp); if (err) return ERR; return OK; } /* * suppress - 文字列で指定された特定項目の表示を抑制する */ int suppress(str) uchr *str; { if (str == NULL) return OK; if (!strcmp(str, "a")) str = "teio"; while (*str) { switch (*str) { case 't': get_label("TR")->disp = FALSE; get_label("TRSUB")->disp = FALSE; break; case 'e': get_label("EX")->disp = FALSE; get_label("EXSUB")->disp = FALSE; get_label("ZEX")->disp = FALSE; break; case 'i': get_label("ID")->disp = FALSE; get_label("IDSUB")->disp = FALSE; get_label("ZID")->disp = FALSE; break; case 'o': get_label("OT")->disp = FALSE; get_label("OTSUB")->disp = FALSE; break; default: return ERR; } str++; } return OK; } /* -------------------- 本文表示 -------------------- */ #define MAIN_DAT "main.txt" /* 本文 */ #define REV_LF 0xf5 /* 0x0a(LF)のビット反転 */ #define LBUFSIZ 4096 /* 行バッファーのサイズ */ /* 現バージョンのmain.datの最大 */ /* 行長は2389だが余裕をみておく */ /* * 発音記号列は「{;」と「2}」で囲まれる(VEGA対応仕様) */ #define BP_LEADER '{' /* csrd側点字発音記号列開始 */ #define BP_START ';' /* 点字発音記号列開始 */ #define BP_END '2' /* 点字発音記号列終了 */ #define BP_TRAILER '}' /* csrd側点字発音記号列終了 */ /* * BPTBL.gcのマスク */ #define CHMASK 0x00ff /* 文字コード */ #define GRMASK 0x1f00 /* フォントグループ */ #define ITMASK 0x8000 /* イタリック */ #define GRNMASK 0x0f00 /* フォントグループ番号 */ #define GRUNDEF 0x1000 /* フォントグループ未定義 */ #define GNUM(g) (((g) == 0x0900)? 99: ((g) >> 8)) typedef struct _bptbl_t { unt gc; /* フォントグループ+文字コード */ uchr bps[4]; /* 点字発音記号列 */ } BPTBL; /* * 点字発音記号列表 */ BPTBL bptbl[] = { { 'a', "A" }, /* a */ { 'b', "B" }, /* b */ { 'd', "D" }, /* d */ { 'e', "E" }, /* e */ { 'f', "F" }, /* f */ { 'g', "G" }, /* g */ { 'h', "H" }, /* h */ { 'i', "I" }, /* i */ { 'j', "J" }, /* j */ { 'k', "K" }, /* k */ { 'l', "L" }, /* l */ { 'm', "M" }, /* m */ { 'n', "N" }, /* n */ { 'o', "O" }, /* o */ { 'p', "P" }, /* p */ { 'r', "R" }, /* r */ { 's', "S" }, /* s */ { 't', "T" }, /* t */ { 'u', "U" }, /* u */ { 'v', "V" }, /* v */ { 'w', "W" }, /* w */ { 'z', "Z" }, /* z */ { 0x00e0, "^A" }, /* a` */ { 0x00e1, "_A" }, /* a´ */ { 0x00e6, "%" }, /* aeの合字 */ { 0x00e8, "^E" }, /* e` */ { 0x00e9, "_E" }, /* e´ */ { 0x00ec, "^I" }, /* i` */ { 0x00ed, "_I" }, /* i´ */ { 0x00f0, "\\]" }, /* theのth */ { 0x00f2, "^O" }, /* o` */ { 0x00f3, "_O" }, /* o´ */ { 0x00f9, "^U" }, /* u` */ { 0x00fa, "_U" }, /* u´ */ { 0x0141, "*" }, /* とさかのないa */ { 0x0142, "\\<" }, /* 左の開いたo */ { 0x0143, "_+" }, /* 横棒のないAに´ */ { 0x014a, "_%" }, /* aeの合字に´ */ { 0x014b, "^%" }, /* aeの合字に` */ { 0x014c, "_5" }, /* あいまい母音に´ */ { 0x014d, "^5" }, /* あいまい母音に` */ { 0x014e, "_*" }, /* とさかのないaに´ */ { 0x014f, "^*" }, /* とさかのないaに` */ { 0x0150, ":" }, /* sheのsh */ { 0x0151, "_\\<" }, /* 左の開いたoに´ */ { 0x0152, "^\\<" }, /* 左の開いたoに` */ { 0x0153, "_\\>" }, /* 丸いEに´ */ { 0x0154, "^\\>" }, /* 丸いEに` */ { 0x016a, "?" }, /* throughのth */ { 0x0173, "!" }, /* 柔らかいg */ { 0x0176, "$" }, /* singのng */ { 0x02e1, "^+" }, /* 横棒のないAに` */ { 0x03b9, "5" }, /* あいまい母音 */ { 0x0477, "3" } /* 伸ばす音 */ }; #define NBPS (sizeof(bptbl) / sizeof(bptbl[0])) int do_braille = FALSE; /* 点字発音記号変換を行うか? */ int nobracket = FALSE; /* 元の発音囲み[]を削除するか? */ int firstidiom; /* その品詞の最初の成句 */ int have_en; /* 見出し単語表示直後(無改行時) */ uchr line[LBUFSIZ]; /* 1行データ表示用作業領域 */ uchr *bpstr(); uchr *bp_transfer(); BFILE *dic_open(); int dgetline(); int showline(); int show_data(); int show_idiom(); int show_header(); int show_version(); /* * bpstr - 文字に対応する点字発音記号列を返す */ uchr * bpstr(gc) unt gc; { int i; gc &= (GRNMASK|CHMASK); /* GRUNDEFはと同じ */ for (i = 0; i < NBPS; i++) { if (gc == bptbl[i].gc) return bptbl[i].bps; } return NULL; } /* * bp_transfer - 発音記号列の点訳を試みる * 結果は渡されたバッファーを上書きする形で書き込む * 変換結果文字列ではタグの開閉が狂っている可能性あり */ uchr * bp_transfer(line) uchr *line; { int level; unt c, it, nit, g, ng; uchr *p, *q; unt *u, *uu, *upto; static unt tmp[1024]; /* 変換元データは最大776バイト */ #ifdef DEBUG if (debug) printf("line in= \"%s\"\n", line); #endif /* * tmpにフォント/イタリック情報付き文字列を組み立てる */ g = GRUNDEF; it = 0; p = line; u = tmp; while (*p) { if (*p == '<') { if (p[1] == 'G') g = (p[2] - '0') << 8; else if (p[1] == '/' && p[2] == 'G') g = GRUNDEF; else if (p[1] == 'I' && p[2] == '>') it = ITMASK; else if (p[1] == '/' && p[2] == 'I' && p[3] == '>') it = 0; else { while (*p != '>') *u++ = it | g | *p++; *u++ = it | g | *p++; continue; } while (*p++ != '>') ; continue; } if (SJIS1(*p)) { *u++ = it | g | *p++; *u++ = it | g | *p++; continue; } if (*p == '#' && p[3] == '#') { *u++ = it | g | hexval(p + 1); p += 4; continue; } *u++ = it | g | *p++; } *u = '\0'; /* * 発音記号列を解釈しながらlineを作り直す */ g = GRUNDEF; it = 0; level = 0; p = line; u = tmp; upto = NULL; while (*u) { nit = *u & ITMASK; if (nit != it) { strcpy(p, nit? "": ""); while (*p++ != '>') ; it = nit; } ng = *u & GRMASK; if (ng != g) { if (g != GRUNDEF) { sprintf(p, "", GNUM(g)); while (*p++ != '>') ; } if (ng != GRUNDEF) { sprintf(p, "", GNUM(ng)); while (*p++ != '>') ; } g = ng; } if ((*u & GRUNDEF) != 0 && SJIS1(*u & CHMASK)) { *p++ = (*u++ & CHMASK); *p++ = (*u++ & CHMASK); continue; } if (upto && u < upto) goto notrans; switch (*u & (GRNMASK|CHMASK)) { case '[': if (!nobracket) *p++ = *u & CHMASK; level++; break; case ']': if (!nobracket) *p++ = *u & CHMASK; if (level > 0) level--; break; default: if (level > 0 && bpstr(*u) != NULL) { uu = u; while (*uu && bpstr(*uu)) { uu++; } c = *uu & (GRMASK|CHMASK); if (c == ' ' || c == ';' || c == ',' || c == '-' || c == ']' || c == '|') { if (g != GRUNDEF) { sprintf(p, "", GNUM(g)); while (*p++ != '>') ; g = GRUNDEF; } *p++ = BP_LEADER; *p++ = BP_START; while (u < uu) { q = bpstr(*u++); while (*q) *p++ = *q++; } *p++ = BP_END; *p++ = BP_TRAILER; u = uu; continue; } upto = uu; } notrans: ng = *u & GRMASK; c = *u & CHMASK; if (ng == GRUNDEF) { *p++ = c; } else if (ng != 0 || c >= 0x80 || c == '/' || c == ':' || c == '=') { sprintf(p, "#%02x#", c); p += 4; } else { *p++ = c; } } u++; } *p = '\0'; if (it) { strcpy(p, ""); p += 4; } if (g != GRUNDEF) sprintf(p, "", GNUM(g)); #ifdef DEBUG if (debug) printf("line out=\"%s\"\n", line); #endif return line; } /* * dic_open - 辞書データファイルをbopen()する */ BFILE * dic_open(dicdir, dicfile) uchr *dicdir, *dicfile; { BFILE *bfp; uchr buf[256]; sprintf(buf, "%s/%s", dicdir, dicfile); if ((bfp = bopen(buf)) == NULL) { outstr("ERR: 辞書ファイルがオープンできません("); outstr(buf); outstr(")\n"); return NULL; } return bfp; } /* * dgetline - 本文データを1行読み込み、改行を除いた長さを返す */ int dgetline(bfp, buf) BFILE *bfp; uchr *buf; { int c; uchr *p; p = buf; while ((c = bgetc(bfp)) != REV_LF) *p++ = ~c; *p = '\0'; return p - buf; } /* * showline - 1行の本文データを表示する */ int showline(line) uchr *line; { uchr *p, *q; LBLTBL *lp; if (rawmode) { outstr(line); outstr("\n"); return OK; } lp = get_label(line); if (lp->id == LBL_ERROR) { #ifdef DEBUG if (debug) { outstr("ERR> "); outstr(line); outstr("\n"); } #endif return ERR; } if (!lp->disp) { #ifdef DEBUG if (debug) { outstr("["); outstr(lp->label); outstr("*]\n"); } #endif return OK; } if (have_en && lp->id != LBL_PR) { set_indent(0); outstr("\n"); } have_en = FALSE; #ifdef DEBUG if (debug) { outstr("["); outstr(lp->label); outstr("] "); } #endif p = skipeq(line); if (*p == '\0') return OK; set_indent(nspaces(lp->leader)); outstr(lp->leader); switch (lp->id) { case LBL_DR: q = p; while (*q && *q != '|') q++; *q = 0; outstr(p); set_indent(0); outstr("\n"); break; case LBL_EN: q = p; while (*q && *q != '|') q++; *q = 0; outstr(p); have_en = TRUE; break; case LBL_ID: if (firstidiom) { outstr("\n【成句】\n"); firstidiom = FALSE; } goto normal; case LBL_PS: firstidiom = TRUE; goto normal; case LBL_TRSUB: if (!strncmp(p, "【", 2)) { p += 2; while (*p && strncmp(p, "】", 2)) { if (SJIS1(*p)) p++; p++; } if (*p) p += 2; } goto normal; case LBL_PR: case LBL_IF: if (do_braille) p = bp_transfer(p); goto normal; default: normal: outstr(p); set_indent(0); outstr("\n"); break; } return OK; } /* * show_data - 本文データを表示する */ int show_data(bfp, dpos, dlen) BFILE *bfp; long dpos, dlen; { int len; if (bseek(bfp, dpos) == ERR) return ERR; firstidiom = TRUE; while (dlen > 0L) { len = dgetline(bfp, line); showline(line); dlen -= (long)(len + 1); } return OK; } /* * show_idiom - 成句・用例の検索結果を表示する */ int show_idiom(bfp, dpos) BFILE *bfp; long dpos; { int idiom; long npos; uchr *p, *q; LBLTBL *lp; uchr ps[128], tr[32]; /* * あとで項目先頭まで遡る必要があるので * 成句・用例を読み出せるだけの余裕を残して * ファイル先頭寄りにいったんbseek()し * それから本来の位置にbseek()する * こうすることでread()の回数が減らせるはず */ npos = dpos - (BBUFSIZ - 512); if (npos < 0L) npos = 0; if (bseek(bfp, npos) == ERR) return ERR; if (bseek(bfp, dpos) == ERR) return ERR; dgetline(bfp, line); if (rawmode) { outstr(line); outstr("\n"); return OK; } if (*line == 'I' || *line == 'D') { lp = get_label("ZID"); idiom = TRUE; } else { lp = get_label("ZEX"); idiom = FALSE; } if (!lp->disp) return OK; set_indent(nspaces(lp->leader)); outstr(lp->leader); outstr(skipeq(line)); /* * ファイル先頭に遡ってEN=を探し、 * そこから現在位置まで必要な情報を読んで表示する */ if (bseek(bfp, dpos) == ERR) return ERR; for (;;) { if ((bgetcbw(bfp) ^ 0xff) == '=' && (bgetcbw(bfp) ^ 0xff) == 'N' && (bgetcbw(bfp) ^ 0xff) == 'E' && (bgetcbw(bfp) ^ 0xff) == '\n') break; } bgetc(bfp); outstr(" ("); dgetline(bfp, line); p = q = skipeq(line); while (*q && *q != '|') q++; *q = '\0'; outstr(p); *ps = *tr = '\0'; while (btell(bfp) < dpos) { dgetline(bfp, line); if (!strncmp(line, "PS=", 3)) { strcpy(ps, line + 3); *tr = '\0'; continue; } if (!idiom && !strncmp(line, "TR", 2)) { p = line + 2; if (*p == 'S') /* TRSUB */ p += 3; q = tr; while (*p && *p != '=') *q++ = *p++; *q = '\0'; } } if (*ps) { outstr(", "); outstr(ps); if (*tr) outstr(tr); if (idiom) outstr(" 成句"); } outstr(")"); set_indent(0); outstr("\n"); return OK; } /* * show_header - ENにある見出しデータを表示する */ int show_header(bfp, dpos) BFILE *bfp; long dpos; { uchr *p, *q; if (bseek(bfp, dpos) == ERR) return ERR; for (;;) { if (dgetline(bfp, line) <= 0) return ERR; if (get_label(line)->id != LBL_EN) continue; p = skipeq(line); q = p; while (*q && *q != '|') q++; *q = 0; outstr(p); break; } return OK; } /* * show_version - 辞書本文データのバージョンを表示する */ int show_version(dicdir) uchr *dicdir; { uchr *p; BFILE *bfp; if ((bfp = dic_open(dicdir, MAIN_DAT)) == NULL) return ERR; bseek(bfp, 0L); while (dgetline(bfp, line) > 0 && !strncmp(line, "CMT=", 4)) { if (rawmode) { outstr(line); outstr("\n"); continue; } p = line + 4; if (!strncmp(p, "***", 3) || !strncmp(p, "TITLE:", 6) || !strncmp(p, "Based", 5) || !strncmp(p, "DATE:", 5)) { outstr(p); outstr("\n"); } } bclose(bfp); return OK; } /* --------------- 成句・用例検索作業ファイル管理 --------------- */ #define TT0SIZ 1000 /* 検索結果格納配列の要素数 */ /* * データ格納先 */ #define TF_USE0 0 /* 配列0だけを使う */ #define TF_USE01 1 /* 配列0とファイル1を使う */ #define TF_USE1 2 /* ファイル1だけを使う */ #define TF_USE12 3 /* ファイル1とファイル2を使う */ typedef struct _tftbl { long val; /* データ値 */ long num; /* データ数 */ int n0; /* tt0のデータ数 */ int n0x; /* tt0の用例開始位置/現在位置 */ long n1; /* tf1のデータ数 */ long n1x; /* tf1の現在位置 */ long n2; /* tf2のデータ数 */ long n2x; /* tf2の現在位置 */ long *tt0; /* 作業配列0 */ uchr *tf1; /* 作業ファイル1 */ uchr *tf2; /* 作業ファイル2 */ FILE *fp1; /* 作業ファイル1 FP */ FILE *fp2; /* 作業ファイル2 FP */ long v1; /* tf1の先読みデータ */ long v2; /* tf2の先読みデータ */ int use; /* データ格納先 */ int open; /* オープンされているか? */ } TFTBL; TFTBL *tftbl; /* 作業ファイル管理表 */ int setuptmp(); void cleantmp(); int twopen(); int twrite(); int twclose(); int tropen(); long tread(); int trclose(); int tisopen(); void tswap(); void tfree(); int lcmp(); long getlval(); /* * setuptmp - 作業ファイル管理表を準備する */ int setuptmp() { int n, len; uchr *tmpdir; TFTBL *tp; uchr tmp[128]; tftbl = (TFTBL *)malloc(sizeof(TFTBL) * 3); if (tftbl == NULL) return ERR; len = 0; if ((tmpdir = getenv("TMP")) != NULL || (tmpdir = getenv("TEMP")) != NULL) { strcpy(tmp, tmpdir); bsltosl(tmp); len = strlen(tmp); if (tmp[len-1] != '/') tmp[len++] = '/'; } tp = tftbl; for (n = 0; n < 3; n++) { sprintf(tmp+len, "cs%d1XXXXXX", n); tp->tf1 = strpool(tmp); sprintf(tmp+len, "cs%d2XXXXXX", n); tp->tf2 = strpool(tmp); if (tp->tf1 == NULL || tp->tf2 == NULL) return ERR; mktemp(tp->tf1); mktemp(tp->tf2); tp->fp1 = NULL; tp->fp2 = NULL; tp->open = FALSE; tp++; } tp = tftbl; for (n = 0; n < 3; n++) { tp->tt0 = (long *)malloc(TT0SIZ * sizeof(long)); /* * メモリーが不足ならtt0は使えなくてもよい * エラーにはしない */ tp++; } #ifdef DEBUG if (debug2) { tp = tftbl; for (n = 0; n < 3; n++) { printf("tftbl[%d]:\n", n); printf(" tt0=%s\n", tp->tt0? "(in use)": "(not in use)"); printf(" tf1=%s\n", tp->tf1); printf(" tf2=%s\n", tp->tf2); tp++; } } #endif return OK; } /* * cleantmp - 残っているかもしれない作業ファイルを削除する */ void cleantmp() { int n; TFTBL *tp; if (tftbl == NULL) return; tp = tftbl; for (n = 0; n < 3; n++) { if (tp->fp1 != NULL) { fclose(tp->fp1); tp->fp1 = NULL; } if (tp->fp2 != NULL) { fclose(tp->fp2); tp->fp2 = NULL; } unlink(tp->tf1); unlink(tp->tf2); tp->open = FALSE; tp++; } } /* * twopen - 作業ファイルセットを書き出しオープンする */ int twopen(tn) int tn; { TFTBL *tp; tp = &tftbl[tn]; tp->val = 0L; tp->num = 0L; tp->n0 = tp->n0x = 0; tp->n1 = tp->n1x = 0L; tp->n2 = tp->n2x = 0L; tp->fp1 = tp->fp2 = NULL; tp->use = (tp->tt0 != NULL)? TF_USE0: TF_USE1; tp->open = TRUE; return OK; } /* * twrite - 作業ファイルセットに書き出す */ int twrite(tn, val) int tn; long val; { int n; TFTBL *tp; tp = &tftbl[tn]; switch (tp->use) { case TF_USE0: if (tp->n0 >= TT0SIZ) { /* * 配列に収まらなかったのでtf1を使う */ if ((tp->fp1 = fopen(tp->tf1, "w")) == NULL) goto openerr; #ifdef DEBUG if (debug2) { printf("twrite(%d): open tf1(%s), num=%ld, val=%07lx\n", tn, tp->tf1, tp->num, val); } #endif for (n = tp->n0x; n < TT0SIZ; n++) { if (fprintf(tp->fp1, "%07lx\n", tp->tt0[n]) == EOF) goto writeerr; } tp->n0 = tp->n0x; tp->n1 = (long)(TT0SIZ - tp->n0x); if (tp->n0x > 0) { /* * 配列中に成句と用例の境界があった * 成句は残して用例だけtf1に移動された * あとでtt0をソートする必要はない(n0x=0) */ tp->n0x = 0; tp->use = TF_USE01; goto use01; } /* * 全体がtf1に移動された */ tp->use = TF_USE1; goto use1; } if (val < tp->val) { /* * 成句と用例の境界が来た */ tp->n0x = tp->n0; } tp->tt0[tp->n0++] = val; break; case TF_USE01: use01: if (val < tp->val) goto fmterr; goto use1write; case TF_USE1: use1: if (val < tp->val) { /* * 成句と用例の境界が来た * 以後はtf2を使う */ if ((tp->fp2 = fopen(tp->tf2, "w")) == NULL) goto openerr; #ifdef DEBUG if (debug2) { printf("twrite(%d): open tf2(%s), num=%ld, val=%07lx\n", tn, tp->tf1, tp->num, val); } #endif tp->use = TF_USE12; goto use12; } use1write: if (fprintf(tp->fp1, "%07lx\n", val) == EOF) goto writeerr; tp->n1++; break; case TF_USE12: if (val < tp->val) goto fmterr; use12: if (fprintf(tp->fp2, "%07lx\n", val) == EOF) return ERR; tp->n2++; break; } tp->val = val; tp->num++; return OK; openerr: outstr("ERR: 作業用ファイルが新規作成できません\n"); return ERR; writeerr: outstr("ERR: ディスクがいっぱいです\n"); return ERR; fmterr: outstr("PANIC: 成句・用例インデックスが予期しない構造です\n"); #ifdef DEBUG if (debug2) { printf("num=%ld, cur=%lx, prev=%lx\n", tp->num, val, tp->val); } #endif return ERR; } /* * twclose - 作業ファイルセットをクローズする */ int twclose(tn) int tn; { TFTBL *tp; tp = &tftbl[tn]; #ifdef DEBUG if (debug2) { printf("twclose(%d): n0=%d, n1=%ld, n2=%ld\n", tn, tp->n0, tp->n1, tp->n2); } #endif switch (tp->use) { case TF_USE0: if (tp->n0x > 0) { /* * tt0の中に成句・用例の境界がある * tt0をソートする必要がある */ qsort((uchr *)tp->tt0, (size_t)tp->n0, sizeof(long), lcmp); } break; case TF_USE12: /* * tf1に成句、tf2に用例がある */ if (fclose(tp->fp2) == EOF) goto closeerr; tp->fp2 = NULL; /* fall thru ... */ case TF_USE01: /* * tt0に成句、tf1に用例がある */ /* fall thru ... */ case TF_USE1: /* * tf1の中に成句と用例のいずれかまたはその両方がある * 値は単調増加している */ if (fclose(tp->fp1) == EOF) goto closeerr; tp->fp1 = NULL; break; } tp->open = FALSE; return OK; closeerr: outstr("ERR: ディスクがいっぱいです\n"); return ERR; } /* * tropen - 作業ファイルセットを読み込みオープンする */ int tropen(tn) int tn; { TFTBL *tp; tp = &tftbl[tn]; switch (tp->use) { case TF_USE0: tp->n0x = 0; break; case TF_USE01: tp->n0x = 0; goto open1; case TF_USE12: if ((tp->fp2 = fopen(tp->tf2, "r")) == NULL) goto openerr; tp->v2 = getlval(tp->fp2); tp->n2x = 0L; /* fall thru ... */ case TF_USE1: open1: if ((tp->fp1 = fopen(tp->tf1, "r")) == NULL) goto openerr; tp->v1 = getlval(tp->fp1); tp->n1x = 0L; break; } tp->open = TRUE; return OK; openerr: outstr("ERR: 作業用ファイルがオープンできません\n"); return ERR; } /* * tread - 作業ファイルセットから読み込む */ long tread(tn) int tn; { long v; TFTBL *tp; tp = &tftbl[tn]; switch (tp->use) { case TF_USE0: if (tp->n0x >= tp->n0) return EOF; v = tp->tt0[tp->n0x++]; break; case TF_USE01: if (tp->n0x >= tp->n0) { if (tp->n1x >= tp->n1) { return EOF; } else { v = tp->v1; tp->v1 = getlval(tp->fp1); tp->n1x++; } } else { if (tp->n1x >= tp->n1) { v = tp->tt0[tp->n0x++]; } else { if (tp->tt0[tp->n0x] < tp->v1) { v = tp->tt0[tp->n0x++]; } else { v = tp->v1; tp->v1 = getlval(tp->fp1); tp->n1x++; } } } break; case TF_USE1: if (tp->n1x >= tp->n1) return EOF; v = tp->v1; tp->v1 = getlval(tp->fp1); tp->n1x++; break; case TF_USE12: if (tp->n1x >= tp->n1) { if (tp->n2x >= tp->n2) { return EOF; } else { v = tp->v2; tp->v2 = getlval(tp->fp2); tp->n2x++; } } else { if (tp->n2x >= tp->n2) { v = tp->v1; tp->v1 = getlval(tp->fp1); tp->n1x++; } else { if (tp->v1 < tp->v2) { v = tp->v1; tp->v1 = getlval(tp->fp1); tp->n1x++; } else { v = tp->v2; tp->v2 = getlval(tp->fp2); tp->n2x++; } } } break; } return v; } /* * trclose - 作業ファイルセットをクローズする */ int trclose(tn) int tn; { TFTBL *tp; tp = &tftbl[tn]; switch (tp->use) { case TF_USE0: break; case TF_USE12: fclose(tp->fp2); tp->fp2 = NULL; /* fall thru ... */ case TF_USE01: case TF_USE1: fclose(tp->fp1); tp->fp1 = NULL; break; } tp->open = FALSE; return OK; } /* * tisopen - 作業ファイルセットはオープンされているか? */ int tisopen(tn) int tn; { return tftbl[tn].open; } /* * tswap - TFTBLを交換する */ void tswap(tn0, tn1) int tn0, tn1; { TFTBL tftmp; tftmp = tftbl[tn0]; tftbl[tn0] = tftbl[tn1]; tftbl[tn1] = tftmp; } /* * tfree - tt0を解放する */ void tfree(tn) int tn; { TFTBL *tp; if (tftbl == NULL) return; tp = &tftbl[tn]; if (tp->tt0) { free((char *)tp->tt0); tp->tt0 = NULL; } } /* * lcmp - long値どうしの比較(qsort用) */ int lcmp(p1, p2) long *p1, *p2; { if (*p1 < *p2) return -1; if (*p1 > *p2) return 1; return 0; } /* * getlval - ファイルから1行の16進文字列を読んでlongとして返す */ long getlval(fp) FILE *fp; { uchr tmp[16]; long l; if (fgets(tmp, 16, fp) == NULL) return (long)EOF; sscanf(tmp, "%lx", &l); return l; } /* -------------------- 検索 -------------------- */ #define EN_SRCH 0 /* 英単語検索 */ #define TR_SRCH 1 /* 訳語検索 */ #define ID_SRCH 2 /* 成句・用例検索 */ #define X_MATCH 0 /* 完全一致検索 */ #define F_MATCH 1 /* 前方一致検索 */ #define R_MATCH 2 /* 後方一致検索 */ #define W_MATCH 3 /* ワイルドカード検索 */ #define EN_F_IDX "enf.idx" /* 英単語前方一致インデックス */ #define EN_R_IDX "enr.idx" /* 英単語後方一致インデックス */ #define TR_F_IDX "trf.idx" /* 訳語前方一致インデックス */ #define TR_R_IDX "trr.idx" /* 訳語後方一致インデックス */ #define EN_LIST "en.dat" /* 英単語リスト */ #define TR_LIST "tr.dat" /* 訳語リスト */ #define ID_IDX "id.idx" /* 成句・用例インデックス */ #define ID_LIST "id.dat" /* 成句・用例リスト */ #define EN_M_LIST "miden.txt" /* 英単語中間一致リスト */ #define TR_M_LIST "midtr.txt" /* 訳語中間一致リスト */ #define INODETOP 0x00000100L /* インデックスの先頭ノード位置 */ #define LENTTOP 0x00000100L /* リストエントリーの先頭位置 */ /* * インデックスエントリー */ typedef struct _ient { uchr lstpos[4]; /* リストファイル中の位置 */ uchr next[4]; /* 下位ノードのノード番号 */ } IENT; /* * インデックスノード */ typedef struct _inode { uchr pageno[4]; /* 不明 */ uchr last[4]; /* ノード中の最終エントリー番号 */ IENT ent[7]; /* ノード中のエントリー */ } INODE; /* * リストエントリー */ typedef struct _lent { long dpos; /* 本文データ位置 */ long dlen; /* 本文データサイズ */ uchr fkey[128]; /* 前方一致キー */ uchr rkey[128]; /* 後方一致キー */ uchr kstr[256]; /* 見出し文字列 */ } LENT; /* * 成句・用例リストエントリー */ typedef struct _ilent { unt entid; /* 順序番号 */ unt nent; /* 同一単語個数??? */ long dpos; /* 本文データ位置 */ uchr idkey[24]; /* 構成単語文字列(最大17文字) */ } ILENT; int stype = EN_SRCH; /* 検索種別(英単語/訳語/成句) */ int mtype = X_MATCH; /* マッチ(完全/前方/後方/中間) */ uchr *idxfile = NULL; /* 検索用インデックスファイル */ uchr *lstfile = NULL; /* 検索用リストファイル */ INODE inode; /* 作業用 */ LENT lent; /* 作業用 */ ILENT ilent; /* 作業用 */ long val(); uchr *fmtkstr(); void getlistkey(); int getfirstent(); void getlistent(); int index_search(); void getilistent(); int idiom_search(); uchr *mgetent(); int match(); int wild_search(); uchr *getword(); int search(); /* * val - 文字列中の4バイト値をlongに変換 */ long val(str) register uchr *str; { return ((long)str[3] << 24) + ((long)str[2] << 16) + ((long)str[1] << 8) + (long)str[0]; } /* * fmtkstr - 見出し文字列中の「@」を改行に変換する */ uchr * fmtkstr(kstr) uchr *kstr; { int in; uchr *p, *q; static uchr knew[256]; in = FALSE; p = kstr; q = knew; while (*p) { if (*p == '<') { if (!strncmp(p, "", 5)) in = TRUE; else if (!strncmp(p, "", 6)) in = FALSE; while (*p != '>') *q++ = *p++; *q++ = *p++; continue; } if (!in && *p == '@') { while (*p == '@') p++; *q++ = '\n'; continue; } *q++ = *p++; } if (q > knew && q[-1] == '\n') q--; *q = '\0'; return knew; } /* * getlistkey - リストファイルから見出し文字列を取り出す */ void getlistkey(lfp, buf, mtype) BFILE *lfp; uchr *buf; int mtype; { int len; uchr *p; bgetc(lfp); bgetc(lfp); if (mtype == R_MATCH) { len = bgetc(lfp); while (len-- > 0) bgetc(lfp); bgetc(lfp); bgetc(lfp); } len = bgetc(lfp); p = buf; while (len-- > 0) *p++ = bgetc(lfp); *p = '\0'; } /* * getfirstent - キーにマッチする最初のエントリーを探す */ int getfirstent(ifp, lfp, word, ip, np) BFILE *ifp, *lfp; uchr *word; INODE *ip; int *np; { int i, n, last, len, cmp; long ipos, lpos, nodeno; uchr key[128]; #ifdef DEBUG int first, level; #endif ipos = INODETOP; #ifdef DEBUG level = 0; #endif /* * トップ〜中間段の処理 */ for (;;) { if (bseek(ifp, ipos) == ERR || bread(ifp, (uchr *)ip, sizeof(INODE)) < sizeof(INODE)) { outstr("PANIC: インデックスファイルに不整合があります\n"); return ERR; } if (val(ip->ent[0].next) == 0L) { /* * 最終段→別の方法で処理する */ break; } #ifdef DEBUG if (debug) { printf("\n", ++level); printf("pageno=%08lx, last=%08lx\n", val(ip->pageno), val(ip->last)); for (i = 0; i <= (int)val(ip->last); i++) { lpos = val(ip->ent[i].lstpos); printf("e[%d].lstpos=%08lx, e[%d].next=%08lx", i, lpos, i, val(ip->ent[i].next)); if (lpos > 0L) { bseek(lfp, lpos); getlistkey(lfp, key, mtype); printf(", key=[%s]", key); } printf("\n"); } } #endif last = (int)val(ip->last); for (i = 0; i < last; i++) { lpos = val(ip->ent[i+1].lstpos); if (bseek(lfp, lpos) == ERR) { outstr("PANIC: リストファイルに不整合があります\n"); return ERR; } getlistkey(lfp, key, mtype); #ifdef DEBUG if (debug) printf("i=%d, word=[%s], key=[%s]\n", i, word, key); #endif if (strcmp(word, key) <= 0) break; } nodeno = val(ip->ent[i].next); ipos = (nodeno - 1L) * sizeof(INODE) + INODETOP; #ifdef DEBUG if (debug) printf("nodeno=%08lx, ipos=%08lx\n", nodeno, ipos); #endif } /* * 最終段の処理 */ len = strlen(word); last = (int)val(ip->last); n = 0; #ifdef DEBUG first = TRUE; #endif for (;;) { if (n > last) { if (bread(ifp, (uchr *)ip, sizeof(INODE)) < sizeof(INODE) || val(ip->pageno) == 0L) { /* * インデックスの終端に達した */ return ERR; } last = (int)val(ip->last); n = 0; } #ifdef DEBUG if (debug && n == 0) { if (first) { printf("\n"); first = FALSE; } else { printf("\n"); } printf("pageno=%08lx, last=%08lx\n", val(ip->pageno), val(ip->last)); for (i = 0; i <= (int)val(ip->last); i++) { lpos = val(ip->ent[i].lstpos); bseek(lfp, lpos); getlistkey(lfp, key, mtype); printf("e[%d].lstpos=%08lx, key=[%s]\n", i, lpos, key); } } #endif lpos = val(ip->ent[n].lstpos); if (bseek(lfp, lpos) == ERR) { outstr("PANIC: リストファイルに不整合があります\n"); return ERR; } getlistkey(lfp, key, mtype); cmp = strncmp(word, key, len); if (cmp < 0) return ERR; if (cmp == 0) { if (mtype == X_MATCH && strcmp(word, key) != 0) return ERR; *np = n; return OK; } n++; } /* NOTREACHED */ } /* * getlistent - リストファイルから見出しデータを取り出す */ void getlistent(lfp, lp) BFILE *lfp; LENT *lp; { int i, len; uchr *p; bgetc(lfp); bgetc(lfp); len = bgetc(lfp); p = lp->fkey; while (len-- > 0) *p++ = bgetc(lfp); *p = '\0'; bgetc(lfp); bgetc(lfp); len = bgetc(lfp); p = lp->rkey; while (len-- > 0) *p++ = bgetc(lfp); *p = '\0'; bgetc(lfp); bgetc(lfp); lp->dpos = 0L; for (i = 0; i < 32; i += 8) lp->dpos |= ((long)bgetc(lfp) << i); bgetc(lfp); bgetc(lfp); lp->dlen = 0L; for (i = 0; i < 32; i += 8) lp->dlen |= ((long)bgetc(lfp) << i); for (i = 0; i < 8; i++) bgetc(lfp); len = bgetc(lfp); p = lp->kstr; while (len-- > 0) *p++ = bgetc(lfp); *p = '\0'; } /* * index_search - 完全/前方/後方一致でインデックス検索を行う */ int index_search(dicdir, showall, word) uchr *dicdir, *word; int showall; { int n, last, len, cmp, ret; long lpos; uchr *keyp; BFILE *ifp, *lfp, *bfp; #ifdef DEBUG int i; uchr key[128]; #endif ret = OK; ifp = lfp = bfp = NULL; if ((ifp = dic_open(dicdir, idxfile)) == NULL || (lfp = dic_open(dicdir, lstfile)) == NULL) goto err; if (getfirstent(ifp, lfp, word, &inode, &n) == ERR) goto err; keyp = (mtype == R_MATCH)? lent.rkey: lent.fkey; len = strlen(word); last = (int)val(inode.last); for (;;) { if (n > last) { if (bread(ifp, (uchr *)&inode, sizeof(INODE)) < sizeof(INODE) || val(inode.pageno) == 0L) { /* * インデックスの終端に達した */ break; } last = (int)val(inode.last); n = 0; } #ifdef DEBUG if (debug && n == 0) { printf("\n"); printf("pageno=%08lx, last=%08lx\n", val(inode.pageno), val(inode.last)); for (i = 0; i <= (int)val(inode.last); i++) { lpos = val(inode.ent[i].lstpos); bseek(lfp, lpos); getlistkey(lfp, key, mtype); printf("e[%d].lstpos=%08lx, key=[%s]\n", i, lpos, key); } } #endif lpos = val(inode.ent[n].lstpos); if (bseek(lfp, lpos) == ERR) { outstr("PANIC: リストファイルに不整合があります\n"); goto err; } getlistent(lfp, &lent); cmp = strncmp(word, keyp, len); if (cmp < 0) break; if (cmp > 0) { n++; continue; } if (showall || mtype == X_MATCH) { if (mtype == X_MATCH && strcmp(word, keyp) != 0) break; if (bfp == NULL) { if ((bfp = dic_open(dicdir, MAIN_DAT)) == NULL) goto err; } else { outstr("\n"); } #ifdef DEBUG if (debug) printf("show_dat: key=[%s], dpos=%lx, dlen=%lx\n", lent.fkey, lent.dpos, lent.dlen); #endif if (show_data(bfp, lent.dpos, lent.dlen) == ERR) goto err; } else { outstr(fmtkstr(lent.kstr)); if (stype == TR_SRCH) { outstr(" ["); outstr(lent.fkey); outstr("]"); } outstr("\n"); } n++; } goto done; err: ret = ERR; done: if (ifp != NULL) bclose(ifp); if (lfp != NULL) bclose(lfp); if (bfp != NULL) bclose(bfp); return ret; } /* * getilistent - 成句・用例リストファイルから構成単語データを取り出す */ void getilistent(lfp, ip) BFILE *lfp; ILENT *ip; { int i, len; uchr *p; ip->entid = bgetc(lfp); ip->entid += (bgetc(lfp) << 8); len = bgetc(lfp); p = ip->idkey; while (len-- > 0) *p++ = bgetc(lfp); *p = '\0'; bgetc(lfp); bgetc(lfp); ip->dpos = 0L; for (i = 0; i < 32; i += 8) ip->dpos |= ((long)bgetc(lfp) << i); bgetc(lfp); bgetc(lfp); ip->nent = bgetc(lfp); ip->nent += (bgetc(lfp) << 8); for (i = 0; i < 4; i++) bgetc(lfp); } /* * idiom_search - 成句・用例検索を行う */ int idiom_search(dicdir, ac, av) uchr *dicdir; int ac; uchr **av; { int i, n, last, cmp, ret, nomore; long lpos, odpos; long l0, l1, num0, num1, num2, n0, n1; uchr *word; BFILE *ifp, *lfp, *bfp; #ifdef DEBUG uchr key[128]; #endif /* * 前準備 */ for (i = 0; i < ac; i++) { /* * 検索指定文字列が正常かどうか * 先にチェックだけしておく */ if (getword(tosjis(av[i])) == NULL) return ERR; } ret = OK; ifp = lfp = bfp = NULL; if ((ifp = dic_open(dicdir, idxfile)) == NULL || (lfp = dic_open(dicdir, lstfile)) == NULL || (bfp = dic_open(dicdir, MAIN_DAT)) == NULL) goto err; if (setuptmp() == ERR) { outstr("ERR: メモリーが足りません\n"); goto err; } /* * 最初の単語を検索する * 結果は作業セット0に書き出す * ただし最後の単語ならそのまま表示して終わり */ word = getword(tosjis(*av)); if (getfirstent(ifp, lfp, word, &inode, &n) == ERR) goto err; #ifdef DEBUG if (debug2) printf("search1 start: word=%s\n", word); #endif nomore = (ac == 1); num0 = 0L; odpos = 0L; last = (int)val(inode.last); for (;;) { if (n > last) { if (bread(ifp, (uchr *)&inode, sizeof(INODE)) < sizeof(INODE) || val(inode.pageno) == 0L) break; last = (int)val(inode.last); n = 0; } #ifdef DEBUG if (debug && n == 0) { printf("\n"); printf("pageno=%08lx, last=%08lx\n", val(inode.pageno), val(inode.last)); for (i = 0; i <= (int)val(inode.last); i++) { lpos = val(inode.ent[i].lstpos); bseek(lfp, lpos); getlistkey(lfp, key, X_MATCH); printf("e[%d].lstpos=%08lx, key=[%s]\n", i, lpos, key); } } #endif lpos = val(inode.ent[n].lstpos); if (bseek(lfp, lpos) == ERR) { outstr("PANIC: リストファイルに不整合があります\n"); goto err; } getilistent(lfp, &ilent); cmp = strcmp(word, ilent.idkey); if (cmp < 0) break; if (cmp > 0) { n++; continue; } #ifdef DEBUG if (debug) printf("idkey=[%s], entid=%04x, nent=%04x, dpos=%08lx\n", ilent.idkey, ilent.entid, ilent.nent, ilent.dpos); #endif if (ilent.dpos == odpos) { n++; continue; } odpos = ilent.dpos; if (nomore) { /* * 検索指定文字列が1つだけだった */ #ifdef DEBUG if (debug2) { printf("dpos=%08lx\n", ilent.dpos); num0++; n++; continue; } #endif if (show_idiom(bfp, ilent.dpos) == ERR) goto err; num0++; n++; continue; } /* * 検索結果を記録する */ if (!tisopen(0) && twopen(0) == ERR || twrite(0, ilent.dpos) == ERR) goto err; num0++; n++; } #ifdef DEBUG if (debug2) printf("num0=%ld\n", num0); #endif if (tisopen(0) && twclose(0) == ERR) goto err; if (nomore || num0 == 0L) { /* * 最後の単語か、結果が0件だった */ goto done; } ac--, av++; /* * 残りの単語を検索する * 結果は作業セット1に書き出す * 作業セット0と作業セット1を突き合わせて * 両方に存在するものだけを作業セット2に書き出す * あるいは最後の単語なら結果を表示する */ while (ac > 0) { word = getword(tosjis(*av)); if (getfirstent(ifp, lfp, word, &inode, &n) == ERR) goto err; #ifdef DEBUG if (debug2) printf("search2 start: word=%s\n", word); #endif nomore = (ac == 1); num1 = 0L; odpos = 0L; last = (int)val(inode.last); for (;;) { if (n > last) { if (bread(ifp, (uchr *)&inode, sizeof(INODE)) < sizeof(INODE) || val(inode.pageno) == 0L) break; last = (int)val(inode.last); n = 0; } #ifdef DEBUG if (debug && n == 0) { printf("\n"); printf("pageno=%08lx, last=%08lx\n", val(inode.pageno), val(inode.last)); for (i = 0; i <= (int)val(inode.last); i++) { lpos = val(inode.ent[i].lstpos); bseek(lfp, lpos); getlistkey(lfp, key, X_MATCH); printf("e[%d].lstpos=%08lx, key=[%s]\n", i, lpos, key); } } #endif lpos = val(inode.ent[n].lstpos); if (bseek(lfp, lpos) == ERR) { outstr("PANIC: リストファイルに不整合があります\n"); goto err; } getilistent(lfp, &ilent); cmp = strcmp(word, ilent.idkey); if (cmp < 0) break; if (cmp > 0) { n++; continue; } #ifdef DEBUG if (debug) printf("idkey=[%s], entid=%04x, nent=%04x, dpos=%08lx\n", ilent.idkey, ilent.entid, ilent.nent, ilent.dpos); #endif if (ilent.dpos == odpos) { n++; continue; } odpos = ilent.dpos; /* * 検索結果を記録する */ if (!tisopen(1) && twopen(1) == ERR || twrite(1, ilent.dpos) == ERR) goto err; num1++; n++; } #ifdef DEBUG if (debug2) printf("num1=%ld\n", num1); #endif if (tisopen(1) && twclose(1) == ERR) goto err; if (num1 == 0L) { /* * 結果が0件だった */ goto done; } /* * 作業セット0と作業セット1を突き合わせて * 両方に存在するものだけを作業セット2に書き出す * その結果を新たな作業セット0とする */ if (tropen(0) == ERR || tropen(1) == ERR) goto err; #ifdef DEBUG if (debug2) { printf("compare start\n"); } #endif n0 = n1 = num2 = 0L; l0 = tread(0); l1 = tread(1); for (;;) { if (l0 < l1) { if (++n0 >= num0) break; l0 = tread(0); continue; } if (l1 < l0) { if (++n1 >= num1) break; l1 = tread(1); continue; } if (nomore) { #ifdef DEBUG if (debug2) { printf("dpos=%08lx\n", l0); goto next; } #endif if (show_idiom(bfp, l0) == ERR) goto err; goto next; } /* * 検索結果を記録する */ if (!tisopen(2) && twopen(2) == ERR || twrite(2, l0) == ERR) goto err; next: if (++n0 >= num0) break; l0 = tread(0); if (++n1 >= num1) break; l1 = tread(1); num2++; } #ifdef DEBUG if (debug2) printf("num2=%ld\n", num2); #endif trclose(0); trclose(1); if (tisopen(2) && twclose(2) == ERR) goto err; if (num2 == 0L) { /* * 結果が0件だった */ goto done; } tswap(0, 2); ac--, av++; } goto done; err: ret = ERR; done: tfree(0); tfree(1); tfree(2); if (ifp != NULL) bclose(ifp); if (lfp != NULL) bclose(lfp); if (bfp != NULL) bclose(bfp); #ifdef DEBUG if (debug2) return ret; #endif cleantmp(); return ret; } /* * mgetent - 中間一致リストを1行読み込む */ uchr * mgetent(bfp, buf) BFILE *bfp; uchr *buf; { int c; uchr *p; if ((c = bgetc(bfp)) == EOF) return NULL; p = buf; do { *p++ = c; } while ((c = bgetc(bfp)) != '\r'); *p = '\0'; return buf; } /* * match - 文字列とパターンの照合を行う */ int match(str, pat) uchr *str, *pat; { unt c; uchr *s, *p; #ifdef DEBUG if (debug2) { printf("str=\"%s\", pat=\"%s\"\n", str, pat); } #endif if (stype == EN_SRCH) { /* * 1バイト文字の照合 */ while (*str) { switch (*pat) { case '?': /* * 任意の文字とマッチする */ break; case '*': /* * strの残りを1文字ずつ減らしながら * patの残りとマッチするかどうか調べる */ s = str; do { if (match(s, pat + 1)) return TRUE; } while (*s++); break; case '[': for (p = pat + 1; *p != ']'; p++) { if (*str == *p) break; if (p[1] == '-') { if (*str >= *p && *str <= p[2]) break; p += 2; } } if (*p == ']') { /* * マッチしなかった */ return FALSE; } while (*p != ']') p++; pat = p; break; case '\0': /* * パターンが先に尽きた */ return FALSE; default: if (*str != *pat) { /* * マッチしなかった */ return FALSE; } break; } str++; pat++; } if (*pat == '\0') { /* * ファイル名とパターンが同時に尽きた */ return TRUE; } return FALSE; } /* * 2バイト文字の照合 */ while (*str) { switch (*pat) { case '?': /* * 任意の文字とマッチする */ str += 2; pat++; break; case '*': /* * strの残りを1文字ずつ減らしながら * patの残りとマッチするかどうか調べる */ s = str; for (;;) { if (match(s, pat + 1)) return TRUE; if (*s == '\0') break; s += 2; } str += 2; pat++; break; case '[': p = pat + 1; while (*p != ']') { if (*str == *p && str[1] == p[1]) break; if (p[2] == '-') { c = (*str << 8 | str[1]); if (c >= (p[0] << 8 | p[1]) && c <= (p[3] << 8 | p[4])) break; p += 3; } p += 2; } if (*p == ']') { /* * マッチしなかった */ return FALSE; } while (*p != ']') { if (*p == '-') { p++; continue; } p += 2; } str += 2; pat = p + 1; break; case '\0': /* * パターンが先に尽きた */ return FALSE; default: if (*str != *pat || str[1] != pat[1]) { /* * マッチしなかった */ return FALSE; } str += 2; pat += 2; break; } } if (*pat == '\0') { /* * ファイル名とパターンが同時に尽きた */ return TRUE; } return FALSE; } /* * wild_search - ワイルドカード検索を行う */ int wild_search(dicdir, showall, pat) uchr *dicdir, *pat; int showall; { int ret, first; long dpos, dlen, odpos; uchr *p; BFILE *lfp, *bfp; uchr buf[256]; ret = OK; lfp = bfp = NULL; if ((lfp = dic_open(dicdir, lstfile)) == NULL || (bfp = dic_open(dicdir, MAIN_DAT)) == NULL) goto err; bseek(lfp, 0L); first = TRUE; odpos = 0L; while (mgetent(lfp, buf) != NULL) { p = buf; while (*p && *p != '0') p++; *p = '\0'; if (!match(buf, pat)) continue; sscanf(p+1, "%7lx%lx", &dpos, &dlen); #ifdef DEBUG if (debug) printf("show_dat: key=[%s], dpos=%lx, dlen=%lx\n", buf, dpos, dlen); #endif if (showall) { if (first) first = FALSE; else outstr("\n"); if (dpos == odpos) continue; if (show_data(bfp, dpos, dlen) == ERR) goto err; } else { if (show_header(bfp, dpos) == ERR) goto err; if (stype == EN_SRCH) lower(buf); outstr(" ["); outstr(buf); outstr("]\n"); } } goto done; err: ret = ERR; done: if (lfp != NULL) bclose(lfp); if (bfp != NULL) bclose(bfp); return ret; } /* * getword - 検索指定文字列を受け取って検索単語その他の設定を行う */ uchr * getword(str) uchr *str; { int n, len, err; int eng, knj, any, meta, bra; uchr *s, *p, *q, ch; static uchr buf[128]; len = strlen(str); eng = knj = any = meta = 0; err = 0; bra = FALSE; s = str; while (*s) { if (strchr("*?[]-", *s)) { if (*s == '*' && (bra || s[1] == '*') || *s == '?' && bra || *s == '[' && (bra || s[1] == '-' || s[1] == ']') || *s == '-' && (!bra || s[1] == ']') || *s == ']' && !bra) { err++; break; } if (*s == '[') bra = TRUE; else if (*s == ']') bra = FALSE; else if (*s == '?') any++; s++; meta++; continue; } if (SJIS1(*s)) { s += 2; knj++; continue; } s++; eng++; } if (err || bra) { outstr("ERR: ワイルドカード検索指定に誤りがあります\n"); return NULL; } if (knj > 0 && eng > 0) { outstr("ERR: 検索単語に英語と日本語が混在しています\n"); return NULL; } if (knj == 0 && eng == 0 && any == 0) { outstr("ERR: 検索単語が空です\n"); return NULL; } if (stype == ID_SRCH) { if (knj) { outstr("ERR: 成句・用例検索で日本語が指定されています\n"); return NULL; } if (mtype != X_MATCH) { outstr("ERR: 成句・用例検索で不完全一致検索が指定されています\n"); return NULL; } } if (meta == 0) mtype = X_MATCH; else if (meta == 1 && str[len-1] == '*') mtype = F_MATCH; else if (meta == 1 && *str == '*') mtype = R_MATCH; else mtype = W_MATCH; if (knj) stype = TR_SRCH; s = str; p = buf; if (mtype == F_MATCH || mtype == R_MATCH) { len--; if (mtype == R_MATCH) s++; } n = len; while (n > 0) { if (SJIS1(*s)) { *p++ = *s++; *p++ = *s++; n -= 2; continue; } if (islower(*s)) { *p++ = toupper(*s); s++; } else { *p++ = *s++; } n--; } *p = '\0'; if (mtype == R_MATCH) { /* * 検索文字列を逆順にする */ p = buf; while (*p) { if (SJIS1(*p)) { ch = *p; *p = p[1]; p[1] = ch; p++; } p++; } p = buf; q = buf + len - 1; while (p < q) { ch = *p; *p++ = *q; *q-- = ch; } } return buf; } /* * search - 検索のメインルーチン */ int search(dicdir, showall, ac, av) uchr *dicdir; int showall; int ac; uchr **av; { int ret; uchr *word; if ((word = getword(tosjis(*av))) == NULL) return ERR; switch (stype) { case EN_SRCH: switch (mtype) { case X_MATCH: case F_MATCH: idxfile = EN_F_IDX; lstfile = EN_LIST; ret = index_search(dicdir, showall, word); break; case R_MATCH: idxfile = EN_R_IDX; lstfile = EN_LIST; ret = index_search(dicdir, showall, word); break; case W_MATCH: lstfile = EN_M_LIST; ret = wild_search(dicdir, showall, word); break; } break; case TR_SRCH: switch (mtype) { case X_MATCH: case F_MATCH: idxfile = TR_F_IDX; lstfile = TR_LIST; ret = index_search(dicdir, showall, word); break; case R_MATCH: idxfile = TR_R_IDX; lstfile = TR_LIST; ret = index_search(dicdir, showall, word); break; case W_MATCH: lstfile = TR_M_LIST; ret = wild_search(dicdir, showall, word); break; } break; case ID_SRCH: idxfile = ID_IDX; lstfile = ID_LIST; ret = idiom_search(dicdir, ac, av); break; } return ret; } /* -------------------- メイン -------------------- */ #define CSRD_ENV "CSRD" /* オプションを設定する環境変数 */ #define MINWIDTH 20 /* 出力行幅の下限 */ int showall = FALSE; /* 不完全一致でも項目表示するか?*/ int showver = FALSE; /* 辞書データバージョン表示 */ uchr *dicdir = NULL; /* 辞書ディレクトリー */ uchr *fmtfile = NULL; /* 表示書式ファイル */ uchr *gaifile = NULL; /* 外字代替表記ファイル */ uchr *supstr = NULL; /* 表示抑制項目 */ void usage(); int parse_opt(); #if defined(UNIX) && defined(RC_PATH) int init_rcfile(); #endif int init_env(); void onintr(); int main(); /* * usage - 使用法を表示して終了する */ void usage() { outstr("小学館ランダムハウス英語辞典検索ユーティリティー Ver."); outstr(version); outstr(" ("); outstr(date); outstr(")\n Written by Junn Ohta (ohta@src.ricoh.co.jp),"); outstr(" Public Domain.\n\n使用法: "); outstr(progname); outstr(" [-d\\<辞書DIR\\>] [-f\\<書式ファイル\\>] [-g\\<外字ファイル\\>]\n"); outstr(" [-w\\<桁数\\>] [-s\\<非表示項目\\>] [-b[x]] [-a] [-v]"); #ifdef UNIX outstr("\n [-c\\<コード\\>]"); #endif outstr(" \\<検索指定\\>\n\n"); outstr("オプション: (環境変数"); outstr(CSRD_ENV); outstr("で指定することもできます)\n"); outstr(" -d: 辞書ディレクトリー\n"); outstr(" -f: 表示書式ファイル\n"); outstr(" -g: 外字代替表記ファイル\n"); outstr(" -w: 出力行幅 (-w0:改行なし(標準))\n"); outstr(" -s: 特定項目を非表示にする\n"); outstr(" (-st:意味, -se:用例, -si:成句, -so:類語, -sa:すべて)\n"); outstr(" -b: 発音記号列を点訳する (-bx:[]を取り除く)\n"); outstr(" -a: 不完全一致でも項目内容を表示する\n"); outstr(" -v: 辞書データのバージョンを表示する\n"); #ifdef UNIX outstr(" -c: 日本語コード (-cj:JIS, -ce:EUC(標準), -cs:シフトJIS)\n"); #endif outstr("\n検索指定: (成句・用例検索以外では訳語からも検索できます)\n"); outstr(" 完全一致検索: \\<文字列\\> ワイルドカード検索: *?[]-を含む文字列\n"); outstr(" 前方一致検索: \\<文字列\\>* 成句・用例検索: -i \\<文字列\\> ...\n"); outstr(" 後方一致検索: *\\<文字列\\>\n"); exit(1); } /* * parse_opt - 環境変数またはコマンド行引数で指定されたオプションを * 解析して必要な設定を行う */ int parse_opt(acp, avp) int *acp; uchr ***avp; { int n, ac; uchr *s, **av; ac = *acp; av = *avp; while (ac > 0 && **av == '-') { s = *av + 1; switch (*s++) { case 'D': case 'd': if (!*s) { ac--, av++; if (ac == 0 || **av == '-') return ERR; s = *av; } dicdir = strpool(s); bsltosl(dicdir); n = strlen(dicdir); if (dicdir[n-1] == '/') dicdir[n-1] = '\0'; break; case 'F': case 'f': if (!*s) { ac--, av++; if (ac == 0 || **av == '-') return ERR; s = *av; } fmtfile = strpool(s); bsltosl(fmtfile); break; case 'G': case 'g': if (!*s) { ac--, av++; if (ac == 0 || **av == '-') return ERR; s = *av; } gaifile = strpool(s); bsltosl(gaifile); break; case 'A': case 'a': showall = TRUE; break; case 'W': case 'w': if (!*s) { ac--, av++; if (ac == 0 || **av == '-') return ERR; s = *av; } if (!isdigit(*s)) return ERR; width = atoi(s); if (width != 0 && width < MINWIDTH) width = MINWIDTH; break; case 'B': case 'b': do_braille = TRUE; if (*s == 'x' || *s == 'X') nobracket = TRUE; else nobracket = FALSE; break; #ifdef UNIX case 'C': case 'c': if (!*s) { ac--, av++; if (ac == 0 || **av == '-') return ERR; s = *av; } switch (*s) { case 'J': case 'j': kcode = KC_JIS; if (s[1] && s[2]) { jiskanji = s[1]; jisalpha = s[2]; } break; case 'E': case 'e': kcode = KC_EUC; break; case 'S': case 's': kcode = KC_SJIS; break; default: return ERR; } break; #endif case 'S': case 's': if (!*s) { ac--, av++; if (ac == 0 || **av == '-') return ERR; s = *av; } supstr = strpool(s); break; case 'I': case 'i': stype = ID_SRCH; break; case 'V': case 'v': showver = TRUE; break; case 'R': case 'r': rawmode = TRUE; break; #ifdef DEBUG case 'Z': case 'z': if (*s == 'z' || *s == 'Z') debug2 = TRUE; else debug = TRUE; break; #endif default: return ERR; } ac--, av++; } *acp = ac; *avp = av; return OK; } #if defined(UNIX) && defined(RC_PATH) /* * init_rcfile - ファイルで指定されたオプションを解釈する * Contributed by 野首 貴嗣 (Nokubi Takatsugu) */ int init_rcfile() { int ec; uchr *env, *s, **ev, **pp; FILE *fp; struct stat st; if (stat(RC_PATH, &st) < 0) return OK; if ((fp = fopen(RC_PATH, "r")) == NULL) return ERR; env = (uchr *)malloc(st.st_size + 1); if (env == NULL) return ERR; if (fread(env, st.st_size, 1, fp) < 1) return ERR; *(env + st.st_size - 1) = '\0'; fclose(fp); ec = 1; for (s = env; *s; s++) { if (*s == ' ' || *s == '\t') ec++; } ev = (uchr **)malloc(sizeof(uchr *) * (ec + 1)); pp = ev; while ((s = strtok(env, " \t")) != NULL) { *pp++ = s; env = NULL; } *pp = NULL; pp = ev; if (parse_opt(&ec, &ev) != OK) { outstr("ERR: ファイル "); outstr(RC_PATH); outstr(" に誤りがあります\n"); return ERR; } free((char *)pp); free((char *)env); return OK; } #endif /* * init_env - 環境変数CSRDで指定されたオプションを解釈する */ int init_env() { int ec; uchr *env, *s, **ev, **pp; if ((env = getenv(CSRD_ENV)) == NULL) return OK; ec = 1; for (s = env; *s; s++) { if (*s == ' ' || *s == '\t') ec++; } ev = (uchr **)malloc(sizeof(uchr *) * (ec + 1)); pp = ev; while ((s = strtok(env, " \t")) != NULL) { *pp++ = s; env = NULL; } *pp = NULL; pp = ev; if (parse_opt(&ec, &ev) != OK) { outstr("ERR: 環境変数 "); outstr(CSRD_ENV); outstr(" に誤りがあります\n"); return ERR; } free((char *)pp); return OK; } /* * onintr - 割り込み時の終了処理 */ void onintr() { outchar(0); /* JIS漢字モードならASCIIに戻す(UNIX) */ cleantmp(); /* 成句・用例検索用の一時ファイルを削除する */ exit(0); } /* * main - メインルーチン */ int main(ac, av) int ac; uchr **av; { signal(SIGINT, onintr); #if defined(UNIX) && defined(RC_PATH) if (init_rcfile() != OK) exit(1); #endif if (init_env() != OK) exit(1); ac--, av++; if (parse_opt(&ac, &av) != OK) usage(); if (!showver && (ac == 0 || stype != ID_SRCH && ac > 1)) usage(); if (dicdir == NULL) { outstr("ERR: 辞書ディレクトリーが設定されていません\n"); exit(1); } if (showver) { show_version(dicdir); exit(0); } if (init_alt() == ERR) exit(1); if (gaifile != NULL) { if (load_gaiji(gaifile) == ERR) exit(1); } if (fmtfile != NULL) { if (load_format(fmtfile) == ERR) exit(1); } if (suppress(supstr) == ERR) { outstr("ERR: 表示抑制指定(-sオプション)に誤りがあります\n"); exit(1); } if (search(dicdir, showall, ac, av) == ERR) exit(1); exit(0); }