diff options
author | Kaleb Keithley <kaleb@freedesktop.org> | 2003-11-14 16:48:57 +0000 |
---|---|---|
committer | Kaleb Keithley <kaleb@freedesktop.org> | 2003-11-14 16:48:57 +0000 |
commit | 84208ce0135c0376208346d20a76add90d52aae4 (patch) | |
tree | dca4ed09085fca4e70c618e96caa84ec3b42b3cc |
Initial revisionXORG-STABLE
-rw-r--r-- | charset.c | 491 | ||||
-rw-r--r-- | charset.h | 71 | ||||
-rw-r--r-- | iso2022.c | 929 | ||||
-rw-r--r-- | iso2022.h | 94 | ||||
-rw-r--r-- | locale.c | 5 | ||||
-rw-r--r-- | luit.c | 593 | ||||
-rw-r--r-- | luit.h | 31 | ||||
-rw-r--r-- | luit.man | 221 | ||||
-rw-r--r-- | other.c | 246 | ||||
-rw-r--r-- | other.h | 63 | ||||
-rw-r--r-- | parser.c | 211 | ||||
-rw-r--r-- | parser.h | 35 | ||||
-rw-r--r-- | sys.c | 457 | ||||
-rw-r--r-- | sys.h | 33 |
14 files changed, 3480 insertions, 0 deletions
diff --git a/charset.c b/charset.c new file mode 100644 index 0000000..e262689 --- /dev/null +++ b/charset.c @@ -0,0 +1,491 @@ +/* +Copyright (c) 2001 by Juliusz Chroboczek + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ +/* $XFree86: xc/programs/luit/charset.c,v 1.7 2003/02/24 01:10:25 dawes Exp $ */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <ctype.h> +#include <X11/fonts/fontenc.h> +#include "other.h" +#include "charset.h" +#include "parser.h" + +#ifndef NULL +#define NULL 0 +#endif + +static unsigned int +IdentityRecode(unsigned int n, CharsetPtr self) +{ + return n; +} + +static int +IdentityReverse(unsigned int n, CharsetPtr self) +{ +#define IS_GL(n) ((n) >= 0x20 && (n) < 0x80) + switch(self->type) { + case T_94: + case T_96: + if (IS_GL(n)) return n; else return -1; + case T_128: + if (n < 0x80) return n; else return -1; + case T_9494: + case T_9696: + if(IS_GL(n>>8) && IS_GL(n&0xFF)) + return n; + else + return -1; + case T_94192: + if(IS_GL(n>>8) && IS_GL(n&0x7F)) + return n; + else + return -1; + default: + abort(); + } +#undef IS_GL +} + +static int +NullReverse(unsigned int n, CharsetPtr self) +{ + return -1; +} + +CharsetRec Unknown94Charset = +{ "Unknown (94)", T_94, 0, IdentityRecode, NullReverse, 0, 0}; +CharsetRec Unknown96Charset = +{ "Unknown (96)", T_96, 0, IdentityRecode, NullReverse, 0, 0}; +CharsetRec Unknown9494Charset = +{ "Unknown (94x94)", T_9494, 0, IdentityRecode, NullReverse, 0, 0}; +CharsetRec Unknown9696Charset = +{ "Unknown (96x96)", T_9696, 0, IdentityRecode, NullReverse, 0, 0}; + +typedef struct _FontencCharset { + char *name; + int type; + unsigned char final; + char *xlfd; + int shift; + FontMapPtr mapping; + FontMapReversePtr reverse; +} FontencCharsetRec, *FontencCharsetPtr; + +FontencCharsetRec fontencCharsets[] = { + {"ISO 646 (1973)", T_94, '@', "iso646.1973-0", 0x00, 0, 0}, + {"ASCII", T_94, 'B', "iso8859-1", 0x00, 0, 0}, + {"JIS X 0201:GL", T_94, 'J', "jisx0201.1976-0", 0x00, 0, 0}, + {"JIS X 0201:GR", T_94, 'I', "jisx0201.1976-0", 0x80, 0, 0}, + {"DEC Special", T_94, '0', "dec-special", 0x00, 0, 0}, + {"DEC Technical", T_94, '>', "dec-dectech", 0x00, 0, 0}, + + {"ISO 8859-1", T_96, 'A', "iso8859-1", 0x80, 0, 0}, + {"ISO 8859-2", T_96, 'B', "iso8859-2", 0x80, 0, 0}, + {"ISO 8859-3", T_96, 'C', "iso8859-3", 0x80, 0, 0}, + {"ISO 8859-4", T_96, 'D', "iso8859-4", 0x80, 0, 0}, + {"ISO 8859-5", T_96, 'L', "iso8859-5", 0x80, 0, 0}, + {"ISO 8859-6", T_96, 'G', "iso8859-6", 0x80, 0, 0}, + {"ISO 8859-7", T_96, 'F', "iso8859-7", 0x80, 0, 0}, + {"ISO 8859-8", T_96, 'H', "iso8859-8", 0x80, 0, 0}, + {"ISO 8859-9", T_96, 'M', "iso8859-9", 0x80, 0, 0}, + {"ISO 8859-10", T_96, 'V', "iso8859-10", 0x80, 0, 0}, + {"ISO 8859-11", T_96, 'T', "iso8859-11", 0x80, 0, 0}, + {"TIS 620", T_96, 'T', "iso8859-11", 0x80, 0, 0}, + {"ISO 8859-13", T_96, 'Y', "iso8859-13", 0x80, 0, 0}, + {"ISO 8859-14", T_96, '_', "iso8859-14", 0x80, 0, 0}, + {"ISO 8859-15", T_96, 'b', "iso8859-15", 0x80, 0, 0}, + {"ISO 8859-16", T_96, 'f', "iso8859-16", 0x80, 0, 0}, + {"KOI8-E", T_96, '@', "koi8-e", 0x80, 0, 0}, + {"TCVN", T_96, 'Z', "tcvn-0", 0x80, 0, 0}, + + {"GB 2312", T_9494, 'A', "gb2312.1980-0", 0x0000, 0, 0}, + {"JIS X 0208", T_9494, 'B', "jisx0208.1990-0", 0x0000, 0, 0}, + {"KSC 5601", T_9494, 'C', "ksc5601.1987-0", 0x0000, 0, 0}, + {"JIS X 0212", T_9494, 'D', "jisx0212.1990-0", 0x0000, 0, 0}, + + {"GB 2312", T_9696, 'A', "gb2312.1980-0", 0x0000, 0, 0}, + {"JIS X 0208", T_9696, 'B', "jisx0208.1990-0", 0x0000, 0, 0}, + {"KSC 5601", T_9696, 'C', "ksc5601.1987-0", 0x0000, 0, 0}, + {"JIS X 0212", T_9696, 'D', "jisx0212.1990-0", 0x0000, 0, 0}, + + {"KOI8-R", T_128, 0, "koi8-r", 0x80, 0, 0}, + {"KOI8-U", T_128, 0, "koi8-u", 0x80, 0, 0}, + {"KOI8-RU", T_128, 0, "koi8-ru", 0x80, 0, 0}, + {"CP 1252", T_128, 0, "microsoft-cp1252", 0x80, 0, 0}, + {"CP 1251", T_128, 0, "microsoft-cp1251", 0x80, 0, 0}, + {"CP 1250", T_128, 0, "microsoft-cp1250", 0x80, 0, 0}, + + {"Big 5", T_94192, 0, "big5.eten-0", 0x8000, 0, 0}, + {0, 0, 0, 0, 0, 0, 0} +}; + +typedef struct _OtherCharset { + char *name; + int (*init)(OtherStatePtr); + unsigned int (*mapping)(unsigned int, OtherStatePtr); + unsigned int (*reverse)(unsigned int, OtherStatePtr); + int (*stack)(unsigned char, OtherStatePtr); +} OtherCharsetRec, *OtherCharsetPtr; + +OtherCharsetRec otherCharsets[] = { + {"GBK", init_gbk, mapping_gbk, reverse_gbk, stack_gbk}, + {"UTF-8", init_utf8, mapping_utf8, reverse_utf8, stack_utf8}, + {"SJIS", init_sjis, mapping_sjis, reverse_sjis, stack_sjis}, + {0, 0, 0, 0, 0} +}; + +static int +compare(char *s, char *t) +{ + while(*s || *t) { + if(*s && (isspace(*s) || *s == '-' || *s == '_')) + s++; + else if(*t && (isspace(*t) || *t == '-' || *t == '_')) + t++; + else if(*s && *t && tolower(*s) == tolower(*t)) { + s++; + t++; + } else + return 1; + } + return 0; +} + +static unsigned int +FontencCharsetRecode(unsigned int n, CharsetPtr self) +{ + FontencCharsetPtr fc = (FontencCharsetPtr)(self->data); + + return FontEncRecode(n + fc->shift, fc->mapping); +} + +static int +FontencCharsetReverse(unsigned int i, CharsetPtr self) +{ + FontencCharsetPtr fc = (FontencCharsetPtr)(self->data); + int n; + + n = fc->reverse->reverse(i, fc->reverse->data); + if(n == 0 || n < fc->shift) + return -1; + else + n -= fc->shift; + +#define IS_GL(n) ((n) >= 0x20 && (n) < 0x80) + switch(self->type) { + case T_94: case T_96: + if (IS_GL(n)) return n; else return -1; + break; + case T_128: + if (n < 0x80) return n; else return -1; + case T_9494: case T_9696: + if(IS_GL(n>>8) && IS_GL(n&0xFF)) + return n; + else + return -1; + break; + case T_94192: + if(IS_GL(n>>8) && IS_GL(n&0x7F)) + return n; + else + return -1; + break; + default: + abort(); + } +#undef IS_GL +} + + +static CharsetPtr cachedCharsets = NULL; + +static CharsetPtr +getCachedCharset(unsigned char final, int type, char *name) +{ + CharsetPtr c; + for(c = cachedCharsets; c; c = c->next) { + if(((c->type == type && c->final == final) || + (name && !compare(c->name, name))) && + (c->type != T_FAILED)) + return c; + } + return NULL; +} + +static void +cacheCharset(CharsetPtr c) { + c->next = cachedCharsets; + cachedCharsets = c; +} + +static CharsetPtr +getFontencCharset(unsigned char final, int type, char *name) +{ + FontencCharsetPtr fc; + CharsetPtr c; + FontMapPtr mapping; + FontMapReversePtr reverse; + + fc = fontencCharsets; + while(fc->name) { + if(((fc->type == type && fc->final == final) || + (name && !compare(fc->name, name))) && + (fc->type != T_FAILED)) + break; + fc++; + } + + if(!fc->name) + return NULL; + + c = malloc(sizeof(CharsetRec)); + if(c == NULL) + return NULL; + + mapping = FontEncMapFind(fc->xlfd, FONT_ENCODING_UNICODE, -1, -1, NULL); + if(!mapping) { + fc->type = T_FAILED; + return NULL; + } + + reverse = FontMapReverse(mapping); + if(!reverse) { + fc->type = T_FAILED; + return NULL; + } + + fc->mapping = mapping; + fc->reverse = reverse; + + c->name = fc->name; + c->type = fc->type; + c->final = fc->final; + c->recode = FontencCharsetRecode; + c->reverse = FontencCharsetReverse; + c->data = fc; + + cacheCharset(c); + return c; +} + +static CharsetPtr +getOtherCharset(char *name) +{ + OtherCharsetPtr fc; + CharsetPtr c; + OtherStatePtr s; + + fc = otherCharsets; + while(fc->name) { + if(name && !compare(fc->name, name)) + break; + fc++; + } + + if(!fc->name) + return NULL; + + c = malloc(sizeof(CharsetRec)); + if(c == NULL) + return NULL; + + s = malloc(sizeof(OtherState)); + if(s == NULL) { + free(c); + return NULL; + } + + c->name = fc->name; + c->type = T_OTHER; + c->final = 0; + c->data = fc; + c->other_recode = fc->mapping; + c->other_reverse = fc->reverse; + c->other_stack = fc->stack; + c->other_aux = s; + + if(!fc->init(s)) { + c->type = T_FAILED; + return NULL; + } + + cacheCharset(c); + return c; +} + +CharsetPtr +getUnknownCharset(int type) +{ + switch(type) { + case T_94: return &Unknown94Charset; + case T_96: return &Unknown96Charset; + case T_9494: return &Unknown9494Charset; + case T_9696: return &Unknown9696Charset; + default: return &Unknown94Charset; + } +} + +CharsetPtr +getCharset(unsigned char final, int type) +{ + CharsetPtr c; + + c = getCachedCharset(final, type, NULL); + if(c) + return c; + + c = getFontencCharset(final, type, NULL); + if(c) + return c; + + return getUnknownCharset(type); +} + +CharsetPtr +getCharsetByName(char *name) +{ + CharsetPtr c; + + if(name == NULL) + return getUnknownCharset(T_94); + + c = getCachedCharset(0, 0, name); + if(c) + return c; + + c = getFontencCharset(0, 0, name); + if(c) + return c; + + c = getOtherCharset(name); + if(c) + return c; + + return getUnknownCharset(T_94); +} + +LocaleCharsetRec localeCharsets[] = { + { "C", 0, 2, "ASCII", NULL, "ISO 8859-1", NULL, NULL}, + { "POSIX", 0, 2, "ASCII", NULL, "ISO 8859-1", NULL, NULL}, + { "ISO8859-1", 0, 2, "ASCII", NULL, "ISO 8859-1", NULL, NULL}, + { "ISO8859-2", 0, 2, "ASCII", NULL, "ISO 8859-2", NULL, NULL}, + { "ISO8859-3", 0, 2, "ASCII", NULL, "ISO 8859-3", NULL, NULL}, + { "ISO8859-4", 0, 2, "ASCII", NULL, "ISO 8859-4", NULL, NULL}, + { "ISO8859-5", 0, 2, "ASCII", NULL, "ISO 8859-5", NULL, NULL}, + { "ISO8859-6", 0, 2, "ASCII", NULL, "ISO 8859-6", NULL, NULL}, + { "ISO8859-7", 0, 2, "ASCII", NULL, "ISO 8859-7", NULL, NULL}, + { "ISO8859-8", 0, 2, "ASCII", NULL, "ISO 8859-8", NULL, NULL}, + { "ISO8859-9", 0, 2, "ASCII", NULL, "ISO 8859-9", NULL, NULL}, + { "ISO8859-10", 0, 2, "ASCII", NULL, "ISO 8859-10", NULL, NULL}, + { "ISO8859-11", 0, 2, "ASCII", NULL, "ISO 8859-11", NULL, NULL}, + { "TIS620", 0, 2, "ASCII", NULL, "ISO 8859-11", NULL, NULL}, + { "ISO8859-13", 0, 2, "ASCII", NULL, "ISO 8859-13", NULL, NULL}, + { "ISO8859-14", 0, 2, "ASCII", NULL, "ISO 8859-14", NULL, NULL}, + { "ISO8859-15", 0, 2, "ASCII", NULL, "ISO 8859-15", NULL, NULL}, + { "ISO8859-16", 0, 2, "ASCII", NULL, "ISO 8859-16", NULL, NULL}, + { "KOI8-R", 0, 2, "ASCII", NULL, "KOI8-R", NULL, NULL}, + { "CP1251", 0, 2, "ASCII", NULL, "CP 1251", NULL, NULL}, + { "TCVN", 0, 2, "ASCII", NULL, "TCVN", NULL, NULL}, + { "eucCN", 0, 1, "ASCII", "GB 2312", NULL, NULL, NULL}, + { "GB2312", 0, 1, "ASCII", "GB 2312", NULL, NULL, NULL}, + { "eucJP", 0, 1, "ASCII", "JIS X 0208", "JIS X 0201:GR", "JIS X 0212", NULL}, + { "eucKR", 0, 1, "ASCII", "KSC 5601", NULL, NULL, NULL}, + { "eucCN", 0, 1, "ASCII", "GB 2312", NULL, NULL, NULL}, + { "Big5", 0, 1, "ASCII", "Big 5", NULL, NULL, NULL}, + { "gbk", 0, 1, NULL, NULL, NULL, NULL, "GBK"}, + { "UTF-8", 0, 1, NULL, NULL, NULL, NULL, "UTF-8"}, + { "SJIS", 0, 1, NULL, NULL, NULL, NULL, "SJIS"}, + { 0, 0, 0, 0, 0, 0, 0} +}; + +void +reportCharsets() +{ + LocaleCharsetPtr p; + FontencCharsetPtr q; + printf("Known locale encodings:\n\n"); + for(p = localeCharsets; p->name; p++) { + if(p->other) { + printf(" %s (non-ISO-2022 encoding)\n", p->other); + continue; + } + printf(" %s: GL -> G%d, GR -> G%d", p->name, p->gl, p->gr); + if(p->g0) printf(", G0: %s", p->g0); + if(p->g1) printf(", G1: %s", p->g1); + if(p->g2) printf(", G2: %s", p->g2); + if(p->g3) printf(", G3: %s", p->g3); + printf("\n"); + } + + printf("\n\nKnown charsets (not all may be available):\n\n"); + for(q = fontencCharsets; q->name; q++) + printf(" %s%s\n", + q->name, q->final?" (ISO 2022)":""); +} + +int +getLocaleState(char *locale, char *charset, + int *gl_return, int *gr_return, + CharsetPtr *g0_return, CharsetPtr *g1_return, + CharsetPtr *g2_return, CharsetPtr *g3_return, + CharsetPtr *other_return) +{ + char *resolved = 0; + LocaleCharsetPtr p; + + if(!charset) { + resolved = resolveLocale(locale); + if(!resolved) + return -1; + charset = strrchr(resolved, '.'); + if(charset) + charset++; + else + charset = resolved; + } + + for(p = localeCharsets; p->name; p++) { + if(compare(p->name, charset) == 0) + break; + } + + if(p->name == NULL) { + if (resolved != 0) + free(resolved); + return -1; + } + + *gl_return = p->gl; + *gr_return = p->gr; + *g0_return = getCharsetByName(p->g0); + *g1_return = getCharsetByName(p->g1); + *g2_return = getCharsetByName(p->g2); + *g3_return = getCharsetByName(p->g3); + if(p->other) + *other_return = getCharsetByName(p->other); + else + *other_return = NULL; + return 0; +} + diff --git a/charset.h b/charset.h new file mode 100644 index 0000000..bff6d3a --- /dev/null +++ b/charset.h @@ -0,0 +1,71 @@ +/* +Copyright (c) 2001 by Juliusz Chroboczek + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ +/* $XFree86: xc/programs/luit/charset.h,v 1.4 2002/10/17 01:06:09 dawes Exp $ */ + +#define T_FAILED 0 +#define T_94 1 +#define T_96 2 +#define T_128 3 +#define T_9494 4 +#define T_9696 5 +/* Big 5 */ +#define T_94192 6 +#define T_OTHER 7 + +/* True for charsets that pass control chars unchanged, at least in + the first byte */ +#define CHARSET_REGULAR(c) ((c)->type != T_128) + +typedef struct _Charset { + char *name; + int type; + unsigned char final; + unsigned int (*recode)(unsigned int, struct _Charset *self); + int (*reverse)(unsigned int, struct _Charset *self); + void *data; + int (*other_stack)(unsigned char c, OtherStatePtr aux); + OtherState *other_aux; + unsigned int (*other_recode)(unsigned int c, OtherStatePtr aux); + unsigned int (*other_reverse)(unsigned int c, OtherStatePtr aux); + struct _Charset *next; +} CharsetRec, *CharsetPtr; + +typedef struct _LocaleCharset { + char *name; + int gl; + int gr; + char *g0; + char *g1; + char *g2; + char *g3; + char *other; +} LocaleCharsetRec, *LocaleCharsetPtr; + +CharsetPtr getUnknownCharset(int); +CharsetPtr getCharset(unsigned char, int); +CharsetPtr getCharsetByName(char*); +void reportCharsets(void); +int getLocaleState(char *locale, char *charset, + int *gl_return, int *gr_return, + CharsetPtr *g0_return, CharsetPtr *g1_return, + CharsetPtr *g2_return, CharsetPtr *g3_return, + CharsetPtr *other_return); diff --git a/iso2022.c b/iso2022.c new file mode 100644 index 0000000..0b9a9e4 --- /dev/null +++ b/iso2022.c @@ -0,0 +1,929 @@ +/* +Copyright (c) 2001 by Juliusz Chroboczek + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ +/* $XFree86: xc/programs/luit/iso2022.c,v 1.9 2002/12/08 20:19:49 dickey Exp $ */ + +#include <stdlib.h> +#include <stdio.h> +#include <assert.h> +#include <stdarg.h> +#include <string.h> +#include <sys/types.h> +#include <unistd.h> +#include <errno.h> +#include <X11/fonts/fontenc.h> +#include "luit.h" +#include "sys.h" +#include "other.h" +#include "charset.h" +#include "iso2022.h" + +#define BUFFERED_INPUT_SIZE 4 +unsigned char buffered_input[BUFFERED_INPUT_SIZE]; +int buffered_input_count = 0; + +static void +FatalError(char *f, ...) +{ + va_list args; + va_start(args, f); + vfprintf(stderr, f, args); + va_end(args); + exit(1); +} + +static void +ErrorF(char *f, ...) +{ + va_list args; + va_start(args, f); + vfprintf(stderr, f, args); + va_end(args); +} + +#define OUTBUF_FREE(is, count) ((is)->outbuf_count + (count) <= BUFFER_SIZE) +#define OUTBUF_MAKE_FREE(is, fd, count) \ + if(!OUTBUF_FREE((is), (count))) outbuf_flush((is), (fd)) + + +static void +outbuf_flush(Iso2022Ptr is, int fd) +{ + int rc; + int i = 0; + + if(olog >= 0) + write(olog, is->outbuf, is->outbuf_count); + + while(i < is->outbuf_count) { + rc = write(fd, is->outbuf + i, is->outbuf_count - i); + if(rc > 0) { + i += rc; + } else { + if(rc < 0 && errno == EINTR) + continue; + else if((rc == 0) || ((rc < 0) && (errno == EAGAIN))) { + waitForOutput(fd); + continue; + } else + break; + } + } + is->outbuf_count = 0; +} + +static void +outbufOne(Iso2022Ptr is, int fd, unsigned c) +{ + OUTBUF_MAKE_FREE(is, fd, 1); + is->outbuf[is->outbuf_count++] = c; +} + +/* Discards null codepoints */ +static void +outbufUTF8(Iso2022Ptr is, int fd, unsigned c) +{ + if(c == 0) + return; + + if(c <= 0x7F) { + OUTBUF_MAKE_FREE(is, fd, 1); + is->outbuf[is->outbuf_count++] = c; + } else if(c <= 0x7FF) { + OUTBUF_MAKE_FREE(is, fd, 2); + is->outbuf[is->outbuf_count++] = 0xC0 | ((c >> 6) & 0x1F); + is->outbuf[is->outbuf_count++] = 0x80 | (c & 0x3F); + } else { + OUTBUF_MAKE_FREE(is, fd, 3); + is->outbuf[is->outbuf_count++] = 0xE0 | ((c >> 12) & 0x0F); + is->outbuf[is->outbuf_count++] = 0x80 | ((c >> 6) & 0x3F); + is->outbuf[is->outbuf_count++] = 0x80 | (c & 0x3F); + } +} + +static void +buffer(Iso2022Ptr is, char c) +{ + if(is->buffered == NULL) { + is->buffered = malloc(10); + if(is->buffered == NULL) + FatalError("Couldn't allocate buffered.\n"); + is->buffered_len = 10; + } + + if(is->buffered_count >= is->buffered_len) { + is->buffered = realloc(is->buffered, 2 * is->buffered_len + 1); + if(is->buffered == NULL) { + FatalError("Couldn't grow buffered.\n"); + } + is->buffered_len = 2 * is->buffered_len + 1; + } + + is->buffered[is->buffered_count++] = c; +} + +static void +outbuf_buffered_carefully(Iso2022Ptr is, int fd) +{ + /* This should never happen in practice */ + int i = 0; + + while(i < is->buffered_count) { + OUTBUF_MAKE_FREE(is, fd, 1); + is->outbuf[is->outbuf_count++] = is->buffered[i++]; + } + is->buffered_count = 0; +} + +static void +outbuf_buffered(Iso2022Ptr is, int fd) +{ + if(is->buffered_count > BUFFER_SIZE) + outbuf_buffered_carefully(is, fd); + + OUTBUF_MAKE_FREE(is, fd, is->buffered_count); + memcpy(is->outbuf + is->outbuf_count, is->buffered, is->buffered_count); + is->outbuf_count += is->buffered_count; + is->buffered_count = 0; +} + +static void +discard_buffered(Iso2022Ptr is) +{ + is->buffered_count = 0; +} + +Iso2022Ptr +allocIso2022(void) +{ + Iso2022Ptr is; + is = malloc(sizeof(Iso2022Rec)); + if(!is) + return NULL; + is->glp = is->grp = NULL; + G0(is) = G1(is) = G2(is) = G3(is) = OTHER(is) = NULL; + + is->parserState = P_NORMAL; + is->shiftState = S_NORMAL; + + is->inputFlags = IF_EIGHTBIT | IF_SS | IF_SSGR; + is->outputFlags = OF_SS | OF_LS | OF_SELECT; + + is->buffered = NULL; + is->buffered_len = 0; + is->buffered_count = 0; + + is->buffered_ku = -1; + + is->outbuf = malloc(BUFFER_SIZE); + if(!is->outbuf) { + free(is); + return NULL; + } + is->outbuf_count = 0; + + return is; +} + +void +destroyIso2022(Iso2022Ptr is) +{ + if(is->buffered) + free(is->buffered); + if(is->outbuf) + free(is->outbuf); + free(is); +} + +static int +identifyCharset(Iso2022Ptr i, CharsetPtr *p) +{ + if(p == &G0(i)) + return 0; + else if(p == &G1(i)) + return 1; + else if(p == &G2(i)) + return 2; + else if(p == &G3(i)) + return 3; + else + abort(); +} + +void +reportIso2022(Iso2022Ptr i) +{ + if(OTHER(i) != NULL) { + fprintf(stderr, "%s, non-ISO-2022 encoding.\n", OTHER(i)->name); + return; + } + fprintf(stderr, "G0 is %s, ", G0(i)->name); + fprintf(stderr, "G1 is %s, ", G1(i)->name); + fprintf(stderr, "G2 is %s, ", G2(i)->name); + fprintf(stderr, "G3 is %s.\n", G3(i)->name); + fprintf(stderr, "GL is G%d, ", identifyCharset(i, i->glp)); + fprintf(stderr, "GR is G%d.\n", identifyCharset(i, i->grp)); +} + +int +initIso2022(char *locale, char *charset, Iso2022Ptr i) +{ + int gl = 0, gr = 2; + CharsetPtr g0 = NULL, g1 = NULL, g2 = NULL, g3 = NULL, other = NULL; + int rc; + + rc = getLocaleState(locale, charset, &gl, &gr, &g0, &g1, &g2, &g3, &other); + if(rc < 0) { + if(charset) + ErrorF("Warning: couldn't find charset %s; " + "using ISO 8859-1.\n", charset); + else + ErrorF("Warning: couldn't find charset data for locale %s; " + "using ISO 8859-1.\n", locale); + } + + if(g0) + G0(i) = g0; + else + G0(i) = getCharsetByName("ASCII"); + + if(g1) + G1(i) = g1; + else + G1(i) = getUnknownCharset(T_94); + + if(g2) + G2(i) = g2; + else + G2(i) = getCharsetByName("ISO 8859-1"); + + if(g3) + G3(i) = g3; + else + G3(i) = getUnknownCharset(T_94); + + if(other) + OTHER(i) = other; + else + OTHER(i) = NULL; + + i->glp = &i->g[gl]; + i->grp = &i->g[gr]; + return 0; +} + +int +mergeIso2022(Iso2022Ptr d, Iso2022Ptr s) +{ + if(G0(d) == NULL) + G0(d) = G0(s); + if(G1(d) == NULL) + G1(d) = G1(s); + if(G2(d) == NULL) + G2(d) = G2(s); + if(G3(d) == NULL) + G3(d) = G3(s); + if(OTHER(d) == NULL) + OTHER(d) = OTHER(s); + if(d->glp == NULL) + d->glp = &(d->g[identifyCharset(s, s->glp)]); + if(d->grp == NULL) + d->grp = &(d->g[identifyCharset(s, s->grp)]); + return 0; +} + +static int +utf8Count(unsigned char c) +{ + /* All return values must be less than BUFFERED_INPUT_SIZE */ + if((c & 0x80) == 0) + return 1; + else if((c & 0x40) == 0) + return 1; /* incorrect UTF-8 */ + else if((c & 0x60) == 0x40) + return 2; + else if((c & 0x70) == 0x60) + return 3; + else if((c & 0x78) == 0x70) + return 4; + else + return 1; +} + +static int +fromUtf8(unsigned char *b) +{ + if((b[0] & 0x80) == 0) + return b[0]; + else if((b[0] & 0x40) == 0) + return -1; /* incorrect UTF-8 */ + else if((b[0] & 0x60) == 0x40) + return ((b[0] & 0x1F) << 6) | (b[1] & 0x3F); + else if((b[0] & 0x70) == 0x60) + return ((b[0] & 0x0F) << 12) + | ((b[1] & 0x3F) << 6) + | (b[2] & 0x3F); + else if((b[0] & 0x78) == 0x70) + return ((b[0] & 0x03) << 18) + | ((b[1] & 0x3F) << 12) + | ((b[2] & 0x3F) << 6) + | ((b[3] & 0x3F)); + else + return -1; +} + +void +copyIn(Iso2022Ptr is, int fd, unsigned char *buf, int count) +{ + unsigned char *c; + int codepoint, rem; + + c = buf; + rem = count; + +#define NEXT do {c++; rem--;} while(0) + + while(rem) { + codepoint = -1; + if(is->parserState == P_ESC) { + assert(buffered_input_count == 0); + codepoint = *c; + NEXT; + if(*c == CSI_7) + is->parserState = P_CSI; + else if(IS_FINAL_ESC(codepoint)) + is->parserState = P_NORMAL; + } else if(is->parserState == P_CSI) { + assert(buffered_input_count == 0); + codepoint = *c; + NEXT; + if(IS_FINAL_CSI(codepoint)) + is->parserState = P_NORMAL; + } else if(!(*c & 0x80)) { + if(buffered_input_count > 0) { + buffered_input_count = 0; + continue; + } else { + codepoint = *c; + NEXT; + if(codepoint == ESC) + is->parserState = P_ESC; + } + } else if((*c & 0x40)) { + if(buffered_input_count > 0) { + buffered_input_count = 0; + continue; + } else { + buffered_input[buffered_input_count] = *c; + buffered_input_count++; + NEXT; + } + } else { + if(buffered_input_count <= 0) { + buffered_input_count = 0; + NEXT; + continue; + } else { + buffered_input[buffered_input_count] = *c; + buffered_input_count++; + NEXT; + if(buffered_input_count >= utf8Count(buffered_input[0])) { + codepoint = fromUtf8(buffered_input); + buffered_input_count = 0; + if(codepoint == CSI) + is->parserState = P_CSI; + } + } + } +#undef NEXT + + if(codepoint >= 0) { + int i; + unsigned char obuf[4]; +#define WRITE_1(i) do {obuf[0]=(i); write(fd, obuf, 1);} while(0) +#define WRITE_2(i) do \ + {obuf[0]=((i)>>8)&0xFF; obuf[1]=(i)&0xFF; write(fd, obuf, 2);} \ + while(0) +#define WRITE_3(i) do \ + {obuf[0]=((i)>>16)&0xFF; obuf[1]=((i)>>8)&0xFF; obuf[2]=(i)&0xFF; \ + write(fd, obuf, 3);} \ + while(0) +#define WRITE_4(i) do \ + {obuf[0]=((i)>>24)&0xFF; obuf[1]=((i)>>16)&0xFF; obuf[2]=((i)>>8)&0xFF; \ + obuf[3]=(i)&0xFF; write(fd, obuf, 4);} \ + while(0) +#define WRITE_1_P_8bit(p, i) \ + {obuf[0]=(p); obuf[1]=(i); write(fd, obuf, 2);} +#define WRITE_1_P_7bit(p, i) \ + {obuf[0]=ESC; obuf[1]=(p)-0x40; obuf[2]=(i); write(fd, obuf, 3);} +#define WRITE_1_P(p,i) do \ + {if(is->inputFlags & IF_EIGHTBIT) \ + WRITE_1_P_8bit(p,i) else \ + WRITE_1_P_7bit(p,i) } \ + while(0) +#define WRITE_2_P_8bit(p, i) \ + {obuf[0]=(p); obuf[1]=((i)>>8)&0xFF; obuf[2]=(i)&0xFF; write(fd, obuf, 3);} +#define WRITE_2_P_7bit(p, i) \ + {obuf[0]=ESC; obuf[1]=(p)-0x40; obuf[2]=((i)>>8)&0xFF; obuf[3]=(i)&0xFF; \ + write(fd, obuf, 4);} +#define WRITE_2_P(p,i) do \ + {if(is->inputFlags & IF_EIGHTBIT) \ + WRITE_2_P_8bit(p,i) else \ + WRITE_2_P_7bit(p,i)} \ + while(0) +#define WRITE_1_P_S(p,i,s) do \ + {obuf[0]=(p); obuf[1]=(i)&0xFF; obuf[2]=(s); write(fd, obuf, 3);} \ + while(0) +#define WRITE_2_P_S(p,i,s) do \ + {obuf[0]=(p); obuf[1]=(((i)>>8)&0xFF); obuf[2]=(i)&0xFF; obuf[3]=(s); \ + write(fd, obuf, 4);} \ + while(0) + + if(codepoint < 0x20 || + (OTHER(is) == NULL && CHARSET_REGULAR(GR(is)) && + (codepoint >= 0x80 && codepoint < 0xA0))) { + WRITE_1(codepoint); + continue; + } + if(OTHER(is) != NULL) { + unsigned int c; + c = OTHER(is)->other_reverse(codepoint, OTHER(is)->other_aux); + if(c>>24) WRITE_4(c); + else if (c>>16) WRITE_3(c); + else if (c>>8) WRITE_2(c); + else if (c) WRITE_1(c); + continue; + } + i = (GL(is)->reverse)(codepoint, GL(is)); + if(i >= 0) { + switch(GL(is)->type) { + case T_94: case T_96: case T_128: + if(i >= 0x20) + WRITE_1(i); + break; + case T_9494: case T_9696: case T_94192: + if(i >= 0x2020) + WRITE_2(i); + break; + default: + abort(); + } + continue; + } + if(is->inputFlags & IF_EIGHTBIT) { + i = GR(is)->reverse(codepoint, GR(is)); + if(i >= 0) { + switch(GR(is)->type) { + case T_94: case T_96: case T_128: + /* we allow C1 characters if T_128 in GR */ + WRITE_1(i | 0x80); + break; + case T_9494: case T_9696: + WRITE_2(i | 0x8080); + break; + case T_94192: + WRITE_2(i | 0x8000); + break; + default: + abort(); + } + continue; + } + } + if(is->inputFlags & IF_SS) { + i = G2(is)->reverse(codepoint, G2(is)); + if(i >= 0) { + switch(GR(is)->type) { + case T_94: case T_96: case T_128: + if(i >= 0x20) { + if((is->inputFlags & IF_EIGHTBIT) && + (is->inputFlags & IF_SSGR)) + i |= 0x80; + WRITE_1_P(SS2, i); + } + break; + case T_9494: case T_9696: + if(i >= 0x2020) { + if((is->inputFlags & IF_EIGHTBIT) && + (is->inputFlags & IF_SSGR)) + i |= 0x8080; + WRITE_2_P(SS2, i); + } + break; + case T_94192: + if(i >= 0x2020) { + if((is->inputFlags & IF_EIGHTBIT) && + (is->inputFlags & IF_SSGR)) + i |= 0x8000; + WRITE_2_P(SS2, i); + } + break; + default: + abort(); + } + continue; + } + } + if(is->inputFlags & IF_SS) { + i = G3(is)->reverse(codepoint, G3(is)); + switch(GR(is)->type) { + case T_94: case T_96: case T_128: + if(i >= 0x20) { + if((is->inputFlags & IF_EIGHTBIT) && + (is->inputFlags & IF_SSGR)) + i |= 0x80; + WRITE_1_P(SS3, i); + } + break; + case T_9494: case T_9696: + if(i >= 0x2020) { + if((is->inputFlags & IF_EIGHTBIT) && + (is->inputFlags & IF_SSGR)) + i |= 0x8080; + WRITE_2_P(SS3, i); + } + break; + case T_94192: + if(i >= 0x2020) { + if((is->inputFlags & IF_EIGHTBIT) && + (is->inputFlags & IF_SSGR)) + i |= 0x8000; + WRITE_2_P(SS3, i); + } + break; + default: + abort(); + } + continue; + } + if(is->inputFlags & IF_LS) { + i = GR(is)->reverse(codepoint, GR(is)); + if(i >= 0) { + switch(GR(is)->type) { + case T_94: case T_96: case T_128: + WRITE_1_P_S(LS1, i, LS0); + break; + case T_9494: case T_9696: + WRITE_2_P_S(LS1, i, LS0); + break; + case T_94192: + WRITE_2_P_S(LS1, i, LS0); + break; + default: + abort(); + } + continue; + } + } +#undef WRITE_1 +#undef WRITE_2 +#undef WRITE_1_P +#undef WRITE_1_P_7bit +#undef WRITE_1_P_8bit +#undef WRITE_2_P +#undef WRITE_2_P_7bit +#undef WRITE_2_P_8bit + } + } +} + +void +copyOut(Iso2022Ptr is, int fd, unsigned char *buf, int count) +{ + unsigned char *s = buf; + + if(ilog >= 0) + write(ilog, buf, count); + + while(s < buf + count) { + switch(is->parserState) { + case P_NORMAL: + resynch: + if(is->buffered_ku < 0) { + if(*s == ESC) { + buffer(is, *s++); + is->parserState = P_ESC; + } else if(OTHER(is) != NULL) { + int c = OTHER(is)->other_stack(*s, OTHER(is)->other_aux); + if(c >= 0) { + outbufUTF8(is, fd, OTHER(is)->other_recode(c, OTHER(is)->other_aux)); + is->shiftState = S_NORMAL; + } + s++; + } else if(*s == CSI && CHARSET_REGULAR(GR(is))) { + buffer(is, *s++); + is->parserState = P_CSI; + } else if((*s == SS2 || *s == SS3 || *s == LS0 || *s == LS1) && + CHARSET_REGULAR(GR(is))) { + buffer(is, *s++); + terminate(is, fd); + is->parserState = P_NORMAL; + } else if (*s <= 0x20 && is->shiftState == S_NORMAL) { + /* Pass through C0 when GL is not regular */ + outbufOne(is, fd, *s); + s++; + } else { + CharsetPtr charset; + unsigned char code = 0; + if(*s <= 0x7F) { + switch(is->shiftState) { + case S_NORMAL: charset = GL(is); break; + case S_SS2: charset = G2(is); break; + case S_SS3: charset = G3(is); break; + default: abort(); + } + code = *s; + } else { + switch(is->shiftState) { + case S_NORMAL: charset = GR(is); break; + case S_SS2: charset = G2(is); break; + case S_SS3: charset = G3(is); break; + default: abort(); + } + code = *s - 0x80; + } + + switch(charset->type) { + case T_94: + if(code >= 0x21 && code <= 0x7E) + outbufUTF8(is, fd, charset->recode(code, charset)); + else + outbufUTF8(is, fd, *s); + s++; + is->shiftState = S_NORMAL; + break; + case T_96: + if(code >= 0x20) + outbufUTF8(is, fd, charset->recode(code, charset)); + else + outbufUTF8(is, fd, *s); + is->shiftState = S_NORMAL; + s++; + break; + case T_128: + outbufUTF8(is, fd, charset->recode(code, charset)); + is->shiftState = S_NORMAL; + s++; + break; + default: + /* First byte of a multibyte sequence */ + is->buffered_ku = *s; + s++; + } + } + } else { /* buffered_ku */ + CharsetPtr charset; + unsigned char ku_code; + unsigned code = 0; + if(is->buffered_ku <= 0x7F) { + switch(is->shiftState) { + case S_NORMAL: charset = GL(is); break; + case S_SS2: charset = G2(is); break; + case S_SS3: charset = G3(is); break; + default: abort(); + } + ku_code = is->buffered_ku; + if(*s < 0x80) + code = *s; + } else { + switch(is->shiftState) { + case S_NORMAL: charset = GR(is); break; + case S_SS2: charset = G2(is); break; + case S_SS3: charset = G3(is); break; + default: abort(); + } + ku_code = is->buffered_ku - 0x80; + if(*s >= 0x80) + code = *s - 0x80; + } + switch(charset->type) { + case T_94: + case T_96: + case T_128: + abort(); + break; + case T_9494: + if(code >= 0x21 && code <= 0x7E) { + outbufUTF8(is, fd, + charset->recode(ku_code << 8 | code, + charset)); + is->buffered_ku = -1; + is->shiftState = S_NORMAL; + } else { + is->buffered_ku = -1; + is->shiftState = S_NORMAL; + goto resynch; + } + s++; + break; + case T_9696: + if(code >= 0x20) { + outbufUTF8(is, fd, + charset->recode(ku_code << 8 | code, + charset)); + is->buffered_ku = -1; + is->shiftState = S_NORMAL; + } else { + is->buffered_ku = -1; + is->shiftState = S_NORMAL; + goto resynch; + } + s++; + break; + case T_94192: + /* Use *s, not code */ + if(((*s >= 0x21) && (*s <= 0x7E)) || + ((*s >= 0xA1) && (*s <= 0xFE))) { + outbufUTF8(is, fd, + charset->recode(ku_code << 8 | *s, + charset)); + is->buffered_ku = -1; + is->shiftState = S_NORMAL; + } else { + is->buffered_ku = -1; + is->shiftState = S_NORMAL; + goto resynch; + } + s++; + break; + default: + abort(); + } + } + break; + case P_ESC: + assert(is->buffered_ku == -1); + if(*s == CSI_7) { + buffer(is, *s++); + is->parserState = P_CSI; + } else if(IS_FINAL_ESC(*s)) { + buffer(is, *s++); + terminate(is, fd); + is->parserState = P_NORMAL; + } else { + buffer(is, *s++); + } + break; + case P_CSI: + if(IS_FINAL_CSI(*s)) { + buffer(is, *s++); + terminate(is, fd); + is->parserState = P_NORMAL; + } else { + buffer(is, *s++); + } + break; + default: + abort(); + } + } + outbuf_flush(is, fd); +} + +void terminate(Iso2022Ptr is, int fd) +{ + if(is->outputFlags & OF_PASSTHRU) { + outbuf_buffered(is, fd); + return; + } + + switch(is->buffered[0]) { + case SS2: + if(is->outputFlags & OF_SS) + is->shiftState = S_SS2; + discard_buffered(is); + return; + case SS3: + if(is->outputFlags & OF_SS) + is->shiftState = S_SS3; + discard_buffered(is); + return; + case LS0: + if(is->outputFlags & OF_LS) + is->glp = &G0(is); + discard_buffered(is); + return; + case LS1: + if(is->outputFlags & OF_LS) + is->glp = &G1(is); + discard_buffered(is); + return; + case ESC: + assert(is->buffered_count >= 2); + switch(is->buffered[1]) { + case SS2_7: + if(is->outputFlags & OF_SS) + is->shiftState = S_SS2; + discard_buffered(is); + return; + case SS3_7: + if(is->outputFlags & OF_SS) + is->shiftState = S_SS3; + discard_buffered(is); + return; + case LS2_7: + if(is->outputFlags & OF_SS) + is->glp = &G2(is); + discard_buffered(is); + return; + case LS3_7: + if(is->outputFlags & OF_LS) + is->glp = &G3(is); + discard_buffered(is); + return; + case LS1R_7: + if(is->outputFlags & OF_LS) + is->grp = &G1(is); + discard_buffered(is); + return; + case LS2R_7: + if(is->outputFlags & OF_LS) + is->grp = &G2(is); + discard_buffered(is); + return; + case LS3R_7: + if(is->outputFlags & OF_LS) + is->grp = &G3(is); + discard_buffered(is); + return; + default: + terminateEsc(is, fd, is->buffered + 1, is->buffered_count - 1); + } + return; + default: + outbuf_buffered(is, fd); + } +} + +void +terminateEsc(Iso2022Ptr is, int fd, unsigned char *s_start, int count) +{ + CharsetPtr charset; + + /* ISO 2022 doesn't allow 2C, but Emacs/MULE uses it in 7-bit + mode */ + + if((s_start[0] == 0x28 || s_start[0] == 0x29 || + s_start[0] == 0x2A || s_start[0] == 0x2B || + s_start[0] == 0x2C || s_start[0] == 0x2D || + s_start[0] == 0x2E || s_start[0] == 0x2F) && + count >= 2) { + if(is->outputFlags & OF_SELECT) { + if(s_start[0] <= 0x2B) + charset = getCharset(s_start[1], T_94); + else + charset = getCharset(s_start[1], T_96); + switch(s_start[0]) { + case 0x28: case 0x2C: G0(is) = charset; break; + case 0x29: case 0x2D: G1(is) = charset; break; + case 0x2A: case 0x2E: G2(is) = charset; break; + case 0x2B: case 0x2F: G3(is) = charset; break; + } + } + discard_buffered(is); + } else if(s_start[0] == 0x24 && count == 2) { + if(is->outputFlags & OF_SELECT) { + charset = getCharset(s_start[1], T_9494); + G0(is) = charset; + } + discard_buffered(is); + } else if(s_start[0] == 0x24 && count >=2 && + (s_start[1] == 0x28 || s_start[1] == 0x29 || + s_start[1] == 0x2A || s_start[1] == 0x2B || + s_start[1] == 0x2D || s_start[1] == 0x2E || + s_start[1] == 0x2F) && + count >= 3) { + if(is->outputFlags & OF_SELECT) { + if(s_start[1] <= 0x2B) + charset = getCharset(s_start[2], T_9494); + else + charset = getCharset(s_start[2], T_9696); + switch(s_start[1]) { + case 0x28: G0(is) = charset; break; + case 0x29: case 0x2D: G1(is) = charset; break; + case 0x2A: case 0x2E: G2(is) = charset; break; + case 0x2B: case 0x2F: G3(is) = charset; break; + } + } + discard_buffered(is); + } else + outbuf_buffered(is, fd); +} diff --git a/iso2022.h b/iso2022.h new file mode 100644 index 0000000..9b50117 --- /dev/null +++ b/iso2022.h @@ -0,0 +1,94 @@ +/* +Copyright (c) 2001 by Juliusz Chroboczek + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ +/* $XFree86: xc/programs/luit/iso2022.h,v 1.5 2002/10/17 01:06:09 dawes Exp $ */ + +#define ESC 0x1B +#define CSI 0x9B +#define CSI_7 '[' +#define SS2 0x8E +#define SS2_7 0x4E +#define SS3 0x8F +#define SS3_7 0x4F +#define LS0 0x0F +#define LS1 0x0E +#define LS2_7 0x6E +#define LS3_7 0x6F +#define LS1R_7 0x7E +#define LS2R_7 0x7D +#define LS3R_7 0x7C + +#define IS_FINAL_ESC(x) (((x) & 0xF0 ) != 0x20) +#define IS_FINAL_CSI(x) (((x) & 0xF0 ) != 0x20 && (((x) & 0xF0 ) != 0x30)) + +#define P_NORMAL 0 +#define P_ESC 1 +#define P_CSI 2 + +#define S_NORMAL 0 +#define S_SS2 1 +#define S_SS3 2 + +#define IF_SS 1 +#define IF_LS 2 +#define IF_EIGHTBIT 4 +#define IF_SSGR 8 + +#define OF_SS 1 +#define OF_LS 2 +#define OF_SELECT 4 +#define OF_PASSTHRU 8 + +typedef struct _Iso2022 { + CharsetPtr *glp, *grp; + CharsetPtr g[4]; + CharsetPtr other; + int parserState; + int shiftState; + int inputFlags; + int outputFlags; + unsigned char *buffered; + int buffered_len; + int buffered_count; + int buffered_ku; + unsigned char *outbuf; + int outbuf_count; +} Iso2022Rec, *Iso2022Ptr; + +#define GL(i) (*(i)->glp) +#define GR(i) (*(i)->grp) +#define G0(i) ((i)->g[0]) +#define G1(i) ((i)->g[1]) +#define G2(i) ((i)->g[2]) +#define G3(i) ((i)->g[3]) +#define OTHER(i) ((i)->other) + +#define BUFFER_SIZE 512 + +Iso2022Ptr allocIso2022(void); +void destroyIso2022(Iso2022Ptr); +int initIso2022(char *, char *, Iso2022Ptr); +int mergeIso2022(Iso2022Ptr, Iso2022Ptr); +void reportIso2022(Iso2022Ptr); +void terminate(Iso2022Ptr, int); +void terminateEsc(Iso2022Ptr, int, unsigned char*, int); +void copyIn(Iso2022Ptr, int, unsigned char*, int); +void copyOut(Iso2022Ptr, int, unsigned char*, int); diff --git a/locale.c b/locale.c new file mode 100644 index 0000000..fe070d8 --- /dev/null +++ b/locale.c @@ -0,0 +1,5 @@ +#include <stdlib.h> +#include <string.h> +#include <stdio.h> + +#include "locale.h" @@ -0,0 +1,593 @@ +/* +Copyright (c) 2001 by Juliusz Chroboczek + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ +/* $XFree86: xc/programs/luit/luit.c,v 1.10 2003/02/24 01:10:25 dawes Exp $ */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <locale.h> +#include <sys/types.h> +#include <fcntl.h> +#include <unistd.h> +#include <errno.h> +#include <assert.h> +#include <stdarg.h> +#include <sys/ioctl.h> +#include <signal.h> + +#ifdef SVR4 +#include <stropts.h> +#endif + +#include <X11/fonts/fontenc.h> +#include "luit.h" +#include "sys.h" +#include "other.h" +#include "charset.h" +#include "iso2022.h" + +static Iso2022Ptr inputState = NULL, outputState = NULL; + +static char *child_argv0 = NULL; +static char *locale_name = NULL; +int ilog = -1; +int olog = -1; +int verbose = 0; +int converter = 0; +int exitOnChild = 0; + +volatile int sigwinch_queued = 0; +volatile int sigchld_queued = 0; + +static int convert(int, int); +static int condom(int, char**); + +static void +ErrorF(char *f, ...) +{ + va_list args; + va_start(args, f); + vfprintf(stderr, f, args); + va_end(args); +} + +static void +FatalError(char *f, ...) +{ + va_list args; + va_start(args, f); + vfprintf(stderr, f, args); + va_end(args); + exit(1); +} + + +static void +help(void) +{ + fprintf(stderr, + "luit\n" + " [ -h ] [ -list ] [ -v ] [ -argv0 name ]\n" + " [ -gl gn ] [-gr gk] " + "[ -g0 set ] [ -g1 set ] " + "[ -g2 set ] [ -g3 set ]\n" + " [ -encoding encoding ] " + "[ +oss ] [ +ols ] [ +osl ] [ +ot ]\n" + " [ -kgl gn ] [-kgr gk] " + "[ -kg0 set ] [ -kg1 set ] " + "[ -kg2 set ] [ -kg3 set ]\n" + " [ -k7 ] [ +kss ] [ +kssgr ] [ -kls ]\n" + " [ -c ] [ -x ] [ -ilog filename ] [ -olog filename ] [ -- ]\n" + " [ program [ args ] ]\n"); + +} + + +static int +parseOptions(int argc, char **argv) +{ + int i = 1; + while(i < argc) { + if(argv[i][0] != '-' && argv[i][0] != '+') { + break; + } else if(!strcmp(argv[i], "--")) { + i++; + break; + } else if(!strcmp(argv[i], "-v")) { + verbose++; + i++; + } else if(!strcmp(argv[i], "-h")) { + help(); + exit(0); + } else if(!strcmp(argv[i], "-list")) { + reportCharsets(); + exit(0); + } else if(!strcmp(argv[i], "+oss")) { + outputState->outputFlags &= ~OF_SS; + i++; + } else if(!strcmp(argv[i], "+ols")) { + outputState->outputFlags &= ~OF_LS; + i++; + } else if(!strcmp(argv[i], "+osl")) { + outputState->outputFlags &= ~OF_SELECT; + i++; + } else if(!strcmp(argv[i], "+ot")) { + outputState->outputFlags = OF_PASSTHRU; + i++; + } else if(!strcmp(argv[i], "-k7")) { + inputState->inputFlags &= ~IF_EIGHTBIT; + i++; + } else if(!strcmp(argv[i], "+kss")) { + inputState->inputFlags &= ~IF_SS; + i++; + } else if(!strcmp(argv[1], "+kssgr")) { + inputState->inputFlags &= ~IF_SSGR; + i++; + } else if(!strcmp(argv[i], "-kls")) { + inputState->inputFlags |= IF_LS; + i++; + } else if(!strcmp(argv[i], "-g0")) { + if(i + 1 >= argc) + FatalError("-g0 requires an argument\n"); + G0(outputState) = getCharsetByName(argv[i + 1]); + i += 2; + } else if(!strcmp(argv[i], "-g1")) { + if(i + 1 >= argc) + FatalError("-g1 requires an argument\n"); + G1(outputState) = getCharsetByName(argv[i + 1]); + i += 2; + } else if(!strcmp(argv[i], "-g2")) { + if(i + 1 >= argc) + FatalError("-g2 requires an argument\n"); + G2(outputState) = getCharsetByName(argv[i + 1]); + i += 2; + } else if(!strcmp(argv[i], "-g3")) { + if(i + 1 >= argc) + FatalError("-g3 requires an argument\n"); + G3(outputState) = getCharsetByName(argv[i + 1]); + + i += 2; + } else if(!strcmp(argv[i], "-gl")) { + int j; + if(i + 1 >= argc) + FatalError("-gl requires an argument\n"); + if(strlen(argv[i + 1]) != 2 || + argv[i + 1][0] != 'g') + j = -1; + else + j = argv[i + 1][1] - '0'; + if(j < 0 || j > 3) + FatalError("The argument of -gl " + "should be one of g0 through g3,\n" + "not %s\n", argv[i + 1]); + else + outputState->glp = &outputState->g[j]; + i += 2; + } else if(!strcmp(argv[i], "-gr")) { + int j; + if(i + 1 >= argc) + FatalError("-gr requires an argument\n"); + if(strlen(argv[i + 1]) != 2 || + argv[i + 1][0] != 'g') + j = -1; + else + j = argv[i + 1][1] - '0'; + if(j < 0 || j > 3) + FatalError("The argument of -gl " + "should be one of g0 through g3,\n" + "not %s\n", argv[i + 1]); + else + outputState->grp = &outputState->g[j]; + i += 2; + } else if(!strcmp(argv[i], "-kg0")) { + if(i + 1 >= argc) + FatalError("-kg0 requires an argument\n"); + G0(inputState) = getCharsetByName(argv[i + 1]); + i += 2; + } else if(!strcmp(argv[i], "-kg1")) { + if(i + 1 >= argc) + FatalError("-kg1 requires an argument\n"); + G1(inputState) = getCharsetByName(argv[i + 1]); + i += 2; + } else if(!strcmp(argv[i], "-kg2")) { + if(i + 1 >= argc) + FatalError("-kg2 requires an argument\n"); + G2(inputState) = getCharsetByName(argv[i + 1]); + i += 2; + } else if(!strcmp(argv[i], "-kg3")) { + if(i + 1 >= argc) + FatalError("-kg3 requires an argument\n"); + G3(inputState) = getCharsetByName(argv[i + 1]); + + i += 2; + } else if(!strcmp(argv[i], "-kgl")) { + int j; + if(i + 1 >= argc) + FatalError("-kgl requires an argument\n"); + if(strlen(argv[i + 1]) != 2 || + argv[i + 1][0] != 'g') + j = -1; + else + j = argv[i + 1][1] - '0'; + if(j < 0 || j > 3) + FatalError("The argument of -kgl " + "should be one of g0 through g3,\n" + "not %s\n", argv[i + 1]); + else + inputState->glp = &inputState->g[j]; + i += 2; + } else if(!strcmp(argv[i], "-kgr")) { + int j; + if(i + 1 >= argc) + FatalError("-kgl requires an argument\n"); + if(strlen(argv[i + 1]) != 2 || + argv[i + 1][0] != 'g') + j = -1; + else + j = argv[i + 1][1] - '0'; + if(j < 0 || j > 3) + FatalError("The argument of -kgl " + "should be one of g0 through g3,\n" + "not %s\n", argv[i + 1]); + else + inputState->grp = &inputState->g[j]; + i += 2; + } else if(!strcmp(argv[i], "-argv0")) { + if(i + 1 >= argc) + FatalError("-argv0 requires an argument\n"); + child_argv0 = argv[i + 1]; + i += 2; + } else if(!strcmp(argv[i], "-x")) { + exitOnChild = 1; + i++; + } else if(!strcmp(argv[i], "-c")) { + converter = 1; + i++; + } else if(!strcmp(argv[i], "-ilog")) { + if(i + 1 >= argc) + FatalError("-ilog requires an argument\n"); + ilog = open(argv[i + 1], O_WRONLY | O_CREAT | O_TRUNC, 0777); + if(ilog < 0) { + perror("Couldn't open input log"); + exit(1); + } + i += 2; + } else if(!strcmp(argv[i], "-olog")) { + if(i + 1 >= argc) + FatalError("-olog requires an argument\n"); + olog = open(argv[i + 1], O_WRONLY | O_CREAT | O_TRUNC, 0777); + if(olog < 0) { + perror("Couldn't open output log"); + exit(1); + } + i += 2; + } else if(!strcmp(argv[i], "-encoding")) { + int rc; + if(i + 1 >= argc) + FatalError("-encoding requires an argument\n"); + rc = initIso2022(NULL, argv[i + 1], outputState); + if(rc < 0) + FatalError("Couldn't init output state\n"); + i += 2; + } else { + FatalError("Unknown option %s\n", argv[i]); + } + } + return i; +} + +static int +parseArgs(int argc, char **argv, char *argv0, + char **path_return, char ***argv_return) +{ + char *path = NULL; + char **child_argv = NULL; + + if(argc <= 0) { + char *shell; + shell = getenv("SHELL"); + if(shell) { + path = malloc(strlen(shell) + 1); + if(!path) + goto bail; + strcpy(path, shell); + } else { + path = malloc(strlen("/bin/sh") + 1); + if(!path) + goto bail; + strcpy(path, "/bin/sh"); + } + child_argv = malloc(2 * sizeof(char*)); + if(!child_argv) + goto bail; + if(argv0) + child_argv[0] = argv0; + else + child_argv[0] = my_basename(path); + child_argv[1] = NULL; + } else { + path = malloc(strlen(argv[0]) + 1); + if(!path) + goto bail; + strcpy(path, argv[0]); + child_argv = malloc((argc + 1) * sizeof(char*)); + if(!child_argv) { + goto bail; + } + if(child_argv0) + child_argv[0] = argv0; + else + child_argv[0] = my_basename(argv[0]); + memcpy(child_argv + 1, argv + 1, (argc - 1) * sizeof(char*)); + child_argv[argc] = NULL; + } + + *path_return = path; + *argv_return = child_argv; + return 0; + + bail: + if(path) + free(path); + if(argv) + free(argv); + return -1; +} + + +int +main(int argc, char **argv) +{ + int rc; + int i; + char *l; + + l = setlocale(LC_ALL, ""); + if(!l) + ErrorF("Warning: couldn't set locale.\n"); + + inputState = allocIso2022(); + if(!inputState) + FatalError("Couldn't create input state\n"); + + outputState = allocIso2022(); + if(!outputState) + FatalError("Couldn't create output state\n"); + + if(l) { + locale_name = setlocale(LC_CTYPE, NULL); + } else { + locale_name = getenv("LC_ALL"); + if(locale_name == NULL) { + locale_name = getenv("LC_CTYPE"); + if(locale_name == NULL) { + locale_name = getenv("LANG"); + } + } + } + + if(locale_name == NULL) { + ErrorF("Couldn't get locale name -- using C\n"); + locale_name = "C"; + } + + rc = initIso2022(locale_name, NULL, outputState); + if(rc < 0) + FatalError("Couldn't init output state\n"); + + i = parseOptions(argc, argv); + if(i < 0) + FatalError("Couldn't parse options\n"); + + rc = mergeIso2022(inputState, outputState); + if(rc < 0) + FatalError("Couldn't init input state\n"); + + if(converter) + return convert(0, 1); + else + return condom(argc - i, argv + i); +} + +static int +convert(int ifd, int ofd) +{ + int rc, i; + unsigned char buf[BUFFER_SIZE]; + + rc = droppriv(); + if(rc < 0) { + perror("Couldn't drop priviledges"); + exit(1); + } + + while(1) { + i = read(ifd, buf, BUFFER_SIZE); + if(i <= 0) { + if(i < 0) { + perror("Read error"); + exit(1); + } + break; + } + copyOut(outputState, ofd, buf, i); + } + return 0; +} + + +static int +condom(int argc, char **argv) +{ + int pty; + int pid; + char *line; + char *path; + char **child_argv; + int rc; + + rc = parseArgs(argc, argv, child_argv0, + &path, &child_argv); + if(rc < 0) + FatalError("Couldn't parse arguments\n"); + + rc = allocatePty(&pty, &line); + if(rc < 0) { + perror("Couldn't allocate pty"); + exit(1); + } + + rc = droppriv(); + if(rc < 0) { + perror("Couldn't drop priviledges"); + exit(1); + } + + pid = fork(); + if(pid < 0) { + perror("Couldn't fork"); + exit(1); + } + + if(pid == 0) { + close(pty); + child(line, path, child_argv); + } else { + free(child_argv); + free(path); + free(line); + parent(pid, pty); + } + + return 0; +} + +void +child(char *line, char *path, char **argv) +{ + int tty; + int pgrp; + + close(0); + close(1); + close(2); + pgrp = setsid(); + if(pgrp < 0) { + kill(getppid(), SIGABRT); + exit(1); + } + + tty = openTty(line); + if(tty < 0) { + kill(getppid(), SIGABRT); + exit(1); + } + + if(tty != 0) + dup2(tty, 0); + if(tty != 1) + dup2(tty, 1); + if(tty != 2) + dup2(tty, 2); + + if(tty > 2) + close(tty); + + execvp(path, argv); + perror("Couldn't exec"); + exit(1); +} + +static void +sigwinchHandler(int sig) { + sigwinch_queued = 1; +} + +static void +sigchldHandler(int sig) +{ + sigchld_queued = 1; +} + +void +parent(int pid, int pty) +{ + unsigned char buf[BUFFER_SIZE]; + int i; + int val; + int rc; + + if(verbose) { + reportIso2022(outputState); + } + +#ifdef SIGWINCH + installHandler(SIGWINCH, sigwinchHandler); +#endif + installHandler(SIGCHLD, sigchldHandler); + + rc = setRawTermios(); + if(rc < 0) + FatalError("Couldn't set terminal to raw\n"); + + val = fcntl(0, F_GETFL, 0); + if(val >= 0) { + fcntl(0, F_SETFL, val | O_NONBLOCK); + } + val = fcntl(pty, F_GETFL, 0); + if(val >= 0) { + fcntl(pty, F_SETFL, val | O_NONBLOCK); + } + + setWindowSize(0, pty); + + for(;;) { + rc = waitForInput(0, pty); + + if(sigwinch_queued) { + sigwinch_queued = 0; + setWindowSize(0, pty); + } + + if(sigchld_queued && exitOnChild) + break; + + if(rc > 0) { + if(rc & 2) { + i = read(pty, buf, BUFFER_SIZE); + if((i == 0) || ((i < 0) && (errno != EAGAIN))) + break; + if(i > 0) + copyOut(outputState, 0, buf, i); + } + if(rc & 1) { + i = read(0, buf, BUFFER_SIZE); + if((i == 0) || ((i < 0) && (errno != EAGAIN))) + break; + if(i > 0) + copyIn(inputState, pty, buf, i); + } + } + } + + restoreTermios(); +} @@ -0,0 +1,31 @@ +/* +Copyright (c) 2001 by Juliusz Chroboczek + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +extern int iso2022; +extern int verbose; +extern int sevenbit; +extern int ilog; +extern int olog; + +void child(char*, char*, char**); +void parent(int, int); + diff --git a/luit.man b/luit.man new file mode 100644 index 0000000..a97da6d --- /dev/null +++ b/luit.man @@ -0,0 +1,221 @@ +.\" $XFree86: xc/programs/luit/luit.man,v 1.7 2003/02/24 01:10:25 dawes Exp $ +.TH LUIT 1 __vendorversion__ +.SH NAME +luit \- Locale and ISO\ 2022 support for Unicode terminals +.SH SYNOPSIS +.B luit +[ +.I options +] [ +.B \-\- +] [ +.I program +[ +.I args +] ] +.SH DESCRIPTION +.B Luit +is a filter that can be run between an arbitrary application and a +UTF-8 terminal emulator. It will convert application output from the +locale's encoding into UTF-8, and convert terminal input from UTF-8 +into the locale's encoding. + +An application may also request switching to a different output +encoding using ISO\ 2022 and ISO\ 6429 escape sequences. Use of this +feature is discouraged: multilingual applications should be modified +to directly generate UTF-8 instead. + +.B Luit +is usually invoked transparently by the terminal emulator. For +information about running +.B luit +from the command line, see EXAMPLES below. +.SH OPTIONS +.TP +.B \-h +Display some summary help and quit. +.TP +.B \-list +List the supported charsets and encodings, then quit. +.TP +.B \-v +Be verbose. +.TP +.B \-c +Function as a simple converter from standard input to standard output. +.TP +.B \-x +Exit as soon as the child dies. This may cause +.B luit +to loose data at the end of the child's output. +.TP +.BI \-argv0 " name" +Set the child's name (as passed in argv[0]). +.TP +.BI \-encoding " encoding" +Set up +.B luit +to use +.I encoding +rather than the current locale's encoding. +.TP +.B +oss +Disable interpretation of single shifts in application output. +.TP +.B +ols +Disable interpretation of locking shifts in application output. +.TP +.B +osl +Disable interpretation of character set selection sequences in +application output. +.TP +.B +ot +Disable interpretation of all sequences and pass all sequences in +application output to the terminal unchanged. This may lead to +interesting results. +.TP +.B \-k7 +Generate seven-bit characters for keyboard input. +.TP +.B +kss +Disable generation of single-shifts for keyboard input. +.TP +.B +kssgr +Use GL codes after a single shift for keyboard input. By default, GR +codes are generated after a single shift when generating eight-bit +keyboard input. +.TP +.B \-kls +Generate locking shifts (SO/SI) for keyboard input. +.TP +.BI \-gl " gn" +Set the initial assignment of GL. The argument should be one of +.BR g0 , +.BR g1 , +.B g2 +or +.BR g3 . +The default depends on the locale, but is usually +.BR g0 . +.TP +.BI \-gr " gk" +Set the initial assignment of GR. The default depends on the locale, +and is usually +.B g2 +except for EUC locales, where it is +.BR g1 . +.TP +.BI \-g0 " charset" +Set the charset initially selected in G0. The default depends on +the locale, but is usually +.BR ASCII . +.TP +.BI \-g1 " charset" +Set the charset initially selected in G1. The default depends on the +locale. +.TP +.BI \-g2 " charset" +Set the charset initially selected in G2. The default depends on the +locale. +.TP +.BI \-g3 " charset" +Set the charset initially selected in G3. The default depends on the +locale. +.TP +.BI \-ilog " filename" +Log into +.I filename +all the bytes received from the child. +.TP +.BI \-olog " filename" +Log into +.I filename +all the bytes sent to the terminal emulator. +.TP +.B \-\- +End of options. +.SH EXAMPLES +The most typical use of +.B luit +is to adapt an instance of +.B XTerm +to the locale's encoding. Current versions of +.B XTerm +invoke +.B luit +automatically when it is needed. If you are using an older release of +.BR XTerm , +or a different terminal emulator, you may invoke +.B luit +manually: +.IP +$ xterm \-u8 \-e luit +.PP +If you are running in a UTF-8 locale but need to access a remote +machine that doesn't support UTF-8, +.B luit +can adapt the remote output to your terminal: +.IP +$ LC_ALL=fr_FR luit ssh legacy-machine +.PP +.B Luit +is also useful with applications that hard-wire an encoding that is +different from the one normally used on the system or want to use +legacy escape sequences for multilingual output. In particular, +versions of +.B Emacs +that do not speak UTF-8 well can use +.B luit +for multilingual output: +.IP +$ luit -encoding 'ISO 8859-1' emacs -nw +.PP +And then, in +.BR Emacs , +.IP +M-x set-terminal-coding-system RET iso-2022-8bit-ss2 RET +.PP +.SH FILES +.TP +.B __projectroot__/lib/X11/fonts/encodings/encodings.dir +The system-wide encodings directory. +.TP +.B __projectroot__/lib/X11/locale/locale.alias +The file mapping locales to locale encodings. +.SH SECURITY +On systems with SVR4 (``Unix-98'') ptys (Linux version 2.2 and later, +SVR4), +.B luit +should be run as the invoking user. + +On systems without SVR4 (``Unix-98'') ptys (notably BSD variants), +running +.B luit +as an ordinary user will leave the tty world-writable; this is a +security hole, and luit will generate a warning (but still accept to +run). A possible solution is to make +.B luit +suid root; +.B luit +should drop privileges sufficiently early to make this safe. However, +the startup code has not been exhaustively audited, and the author +takes no responsibility for any resulting security issues. + +.B Luit +will refuse to run if it is installed setuid and the underlying system +does not have POSIX saved ids. +.SH BUGS +None of this complexity should be necessary. Stateless UTF-8 +throughout the system is the way to go. + +Charsets with a non-trivial intermediary byte are not yet supported. + +Selecting alternate sets of control characters is not supported and +will never be. +.SH SEE ALSO +xterm(1), unicode(7), utf-8(7), charsets(7). +.I Character Code Structure and Extension Techniques (ISO\ 2022, ECMA-35). +.I Control Functions for Coded Character Sets (ISO\ 6429, ECMA-48). +.SH AUTHOR +Luit was written by Juliusz Chroboczek <jch@xfree86.org> for the +XFree86 project. @@ -0,0 +1,246 @@ +/* +Copyright (c) 2002 by Tomohiro KUBOTA + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ +/* $XFree86: xc/programs/luit/other.c,v 1.1 2002/10/17 01:06:09 dawes Exp $ */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <ctype.h> +#include <X11/fonts/fontenc.h> +#include "other.h" +#include "charset.h" + +#ifndef NULL +#define NULL 0 +#endif + +#define EURO_10646 0x20AC + +int +init_gbk(OtherStatePtr s) +{ + s->gbk.mapping = + FontEncMapFind("gbk-0", FONT_ENCODING_UNICODE, -1, -1, NULL); + if(!s->gbk.mapping) return 0; + + s->gbk.reverse = FontMapReverse(s->gbk.mapping); + if(!s->gbk.reverse) return 0; + + s->gbk.buf = -1; + return 1; +} + +unsigned int +mapping_gbk(unsigned int n, OtherStatePtr s) +{ + unsigned int r; + if(n < 128) return n; + if(n == 128) return EURO_10646; + r = FontEncRecode(n, s->gbk.mapping); + return r; +} + +unsigned int +reverse_gbk(unsigned int n, OtherStatePtr s) +{ + if(n < 128) return n; + if(n == EURO_10646) return 128; + return s->gbk.reverse->reverse(n, s->gbk.reverse->data); +} + +int +stack_gbk(unsigned char c, OtherStatePtr s) +{ + if(s->gbk.buf < 0) { + if(c < 129) return c; + s->gbk.buf = c; + return -1; + } else { + int b; + if(c < 0x40 || c == 0x7F) { + s->gbk.buf = -1; + return c; + } + if(s->gbk.buf < 0xFF && c < 0xFF) + b = (s->gbk.buf << 8) + c; + else + b = -1; + s->gbk.buf = -1; + return b; + } +} + +int +init_utf8(OtherStatePtr s) +{ + s->utf8.buf_ptr = 0; + return 1; +} + +unsigned int +mapping_utf8(unsigned int n, OtherStatePtr s) +{ + return n; +} + +unsigned int +reverse_utf8(unsigned int n, OtherStatePtr s) +{ + if(n < 0x80) + return n; + if(n < 0x800) + return 0xC080 + ((n&0x7C0)<<2) + (n&0x3F); + if(n < 0x10000) + return 0xE08080 + ((n&0xF000)<<4) + ((n&0xFC0)<<2) + (n&0x3F); + return 0xF0808080 + ((n&0x1C0000)<<6) + ((n&0x3F000)<<4) + + ((n&0xFC0)<<2) + (n&0x3F); +} + +int +stack_utf8(unsigned char c, OtherStatePtr s) +{ + int u; + + if(c < 0x80) { + s->utf8.buf_ptr = 0; + return c; + } + if(s->utf8.buf_ptr == 0) { + if((c & 0x40) == 0) return -1; + s->utf8.buf[s->utf8.buf_ptr++] = c; + if((c & 0x60) == 0x40) s->utf8.len = 2; + else if((c & 0x70) == 0x60) s->utf8.len = 3; + else if((c & 0x78) == 0x70) s->utf8.len = 4; + else s->utf8.buf_ptr = 0; + return -1; + } + if((c & 0x40) != 0) { + s->utf8.buf_ptr = 0; + return -1; + } + s->utf8.buf[s->utf8.buf_ptr++] = c; + if(s->utf8.buf_ptr < s->utf8.len) return -1; + switch(s->utf8.len) { + case 2: + u = ((s->utf8.buf[0] & 0x1F) << 6) | (s->utf8.buf[1] & 0x3F); + s->utf8.buf_ptr = 0; + if(u < 0x80) return -1; else return u; + case 3: + u = ((s->utf8.buf[0] & 0x0F) << 12) + | ((s->utf8.buf[1] & 0x3F) << 6) + | (s->utf8.buf[2] & 0x3F); + s->utf8.buf_ptr = 0; + if(u < 0x800) return -1; else return u; + case 4: + u = ((s->utf8.buf[0] & 0x03) << 18) + | ((s->utf8.buf[1] & 0x3F) << 12) + | ((s->utf8.buf[2] & 0x3F) << 6) + | ((s->utf8.buf[3] & 0x3F)); + s->utf8.buf_ptr = 0; + if(u < 0x10000) return -1; else return u; + } + s->utf8.buf_ptr = 0; + return -1; +} + + +#define HALFWIDTH_10646 0xFF61 +#define YEN_SJIS 0x5C +#define YEN_10646 0x00A5 +#define OVERLINE_SJIS 0x7E +#define OVERLINE_10646 0x203E + +int +init_sjis(OtherStatePtr s) +{ + s->sjis.x0208mapping = + FontEncMapFind("jisx0208.1990-0", FONT_ENCODING_UNICODE, -1, -1, NULL); + if(!s->sjis.x0208mapping) return 0; + + s->sjis.x0208reverse = FontMapReverse(s->sjis.x0208mapping); + if(!s->sjis.x0208reverse) return 0; + + s->sjis.x0201mapping = + FontEncMapFind("jisx0201.1976-0", FONT_ENCODING_UNICODE, -1, -1, NULL); + if(!s->sjis.x0201mapping) return 0; + + s->sjis.x0201reverse = FontMapReverse(s->sjis.x0201mapping); + if(!s->sjis.x0201reverse) return 0; + + s->sjis.buf = -1; + return 1; +} + +unsigned int +mapping_sjis(unsigned int n, OtherStatePtr s) +{ + unsigned int j1, j2, s1, s2; + if(n == YEN_SJIS) return YEN_10646; + if(n == OVERLINE_SJIS) return OVERLINE_10646; + if(n < 0x80) return n; + if(n >= 0xA0 && n <= 0xDF) return FontEncRecode(n, s->sjis.x0201mapping); + s1 = ((n>>8)&0xFF); + s2 = (n&0xFF); + j1 = (s1 << 1) - (s1 <= 0x9F ? 0xE0 : 0x160) - (s2 < 0x9F ? 1 : 0); + j2 = s2 - 0x1F - (s2 >= 0x7F ? 1 : 0) - (s2 >= 0x9F ? 0x5E : 0); + return FontEncRecode((j1<<8) + j2, s->sjis.x0208mapping); +} + +unsigned int +reverse_sjis(unsigned int n, OtherStatePtr s) +{ + unsigned int j, j1, j2, s1, s2; + if(n == YEN_10646) return YEN_SJIS; + if(n == OVERLINE_10646) return OVERLINE_SJIS; + if(n < 0x80) return n; + if(n >= HALFWIDTH_10646) + return s->sjis.x0201reverse->reverse(n, s->sjis.x0201reverse->data); + j = s->sjis.x0208reverse->reverse(n, s->sjis.x0208reverse->data); + j1 = ((j>>8)&0xFF); + j2 = (j&0xFF); + s1 = ((j1 - 1) >> 1) + ((j1 <= 0x5E) ? 0x71 : 0xB1); + s2 = j2 + ((j1 & 1) ? ((j2 < 0x60) ? 0x1F : 0x20) : 0x7E); + return (s1<<8) + s2; +} + +int +stack_sjis(unsigned char c, OtherStatePtr s) +{ + if(s->sjis.buf < 0) { + if(c < 128 || (c >= 0xA0 && c <= 0xDF)) return c; + s->sjis.buf = c; + return -1; + } else { + int b; + if(c < 0x40 || c == 0x7F) { + s->sjis.buf = -1; + return c; + } + if(s->sjis.buf < 0xFF && c < 0xFF) + b = (s->sjis.buf << 8) + c; + else + b = -1; + s->sjis.buf = -1; + return b; + } +} + @@ -0,0 +1,63 @@ +/* +Copyright (c) 2002 by Tomohiro KUBOTA + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ +/* $XFree86: xc/programs/luit/other.h,v 1.1 2002/10/17 01:06:09 dawes Exp $ */ + +typedef struct { + FontMapPtr mapping; + FontMapReversePtr reverse; + int buf; +} aux_gbk; + +typedef struct { + unsigned char buf[4]; + int buf_ptr, len; +} aux_utf8; + +typedef struct { + FontMapPtr x0208mapping; + FontMapPtr x0201mapping; + FontMapReversePtr x0208reverse; + FontMapReversePtr x0201reverse; + int buf; +} aux_sjis; + +typedef union { + aux_gbk gbk; + aux_utf8 utf8; + aux_sjis sjis; +} OtherState, *OtherStatePtr; + +int init_gbk(OtherStatePtr); +unsigned int mapping_gbk(unsigned int, OtherStatePtr); +unsigned int reverse_gbk(unsigned int, OtherStatePtr); +int stack_gbk(unsigned char, OtherStatePtr); + +int init_utf8(OtherStatePtr); +unsigned int mapping_utf8(unsigned int, OtherStatePtr); +unsigned int reverse_utf8(unsigned int, OtherStatePtr); +int stack_utf8(unsigned char, OtherStatePtr); + +int init_sjis(OtherStatePtr); +unsigned int mapping_sjis(unsigned int, OtherStatePtr); +unsigned int reverse_sjis(unsigned int, OtherStatePtr); +int stack_sjis(unsigned char, OtherStatePtr); + diff --git a/parser.c b/parser.c new file mode 100644 index 0000000..6734fb2 --- /dev/null +++ b/parser.c @@ -0,0 +1,211 @@ +/* +Copyright (c) 2001 by Juliusz Chroboczek + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ +/* $XFree86: xc/programs/luit/parser.c,v 1.2 2002/09/18 17:11:50 tsi Exp $ */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +#include "parser.h" + +static char keyword[MAX_KEYWORD_LENGTH]; + +static void +skipEndOfLine(FILE *f, int c) +{ + if(c == 0) + c = getc(f); + + for(;;) + if(c <= 0 || c == '\n') + return; + else + c = getc(f); +} + +static int +drainWhitespace(FILE *f, int c) +{ + if(c == 0) + c = getc(f); + + while (c == '#' || c == ' ' || c == '\t') { + if(c <= 0) + return 0; + if(c == '#') { + skipEndOfLine(f, c); + return '\n'; + } + c = getc(f); + } + + return c; +} + +static int +getString(FILE *f, int string_end, int *c_return) +{ + int i = 0; + int c; + + c = getc(f); + while(c > 0) { + if(c == string_end) + break; + if(c == '\\') { + c = getc(f); + if(c == '\n') + continue; + } + keyword[i++] = c; + if(i >= MAX_KEYWORD_LENGTH) + return TOK_ERROR; + c = getc(f); + } + + if(c <= 0) + return TOK_ERROR; + keyword[i] = '\0'; + *c_return = c; + return TOK_KEYWORD; +} + +static int +getToken(FILE *f, int c, int parse_assignments, int *c_return) +{ + int i; + c = drainWhitespace(f, c); + + if(c < 0) + return TOK_EOF; + if(c == '\n') { + *c_return = 0; + return TOK_EOL; + } + + if(parse_assignments && c == '=') { + *c_return = 0; + return TOK_EQUALS; + } + + if(c == '\'' || c == '"') + return getString(f, c, c_return); + + i = 0; + while(c > 0 && c != ' ' && c != '\t' && c != '\n') { + if(c == '\\') { + c = getc(f); + if(c == '\n') + continue; + } + keyword[i++] = c; + if(i >= MAX_KEYWORD_LENGTH) + return TOK_ERROR; + c = getc(f); + if(parse_assignments && c == '=') + break; + } + + *c_return = c<0?0:c; + keyword[i] = '\0'; + return TOK_KEYWORD; +} + + +/* Can parse both the old and new formats for locale.alias */ +static int +parseTwoTokenLine(FILE *f, char *first, char *second) +{ + int c = 0; + int tok; + + again: + + tok = getToken(f, c, 0, &c); + if(tok == TOK_EOF) + return -1; + else if(tok == TOK_EOL) + goto again; + else if(tok == TOK_KEYWORD) { + int len = strlen(keyword); + if(keyword[len - 1] == ':') + keyword[len - 1] = '\0'; + strcpy(first, keyword); + } else + return -2; + + tok = getToken(f, c, 0, &c); + if(tok == TOK_KEYWORD) { + strcpy(second, keyword); + } else + return -2; + + tok = getToken(f, c, 0, &c); + if(tok != TOK_EOL) + return -2; + + return 0; +} + +char * +resolveLocale(char *locale) +{ + FILE *f; + char first[MAX_KEYWORD_LENGTH], second[MAX_KEYWORD_LENGTH]; + char *resolved = NULL; + int rc; + + f = fopen(LOCALE_ALIAS_FILE, "r"); + if(f == NULL) + goto bail; + + do { + rc = parseTwoTokenLine(f, first, second); + if(rc < -1) + goto bail; + if(!strcmp(first, locale)) { + resolved = malloc(strlen(second) + 1); + if(resolved == NULL) + goto bail; + strcpy(resolved, second); + break; + } + } while(rc >= 0); + + if(resolved == NULL) { + resolved = malloc(strlen(locale) + 1); + if(resolved == NULL) + goto bail; + strcpy(resolved, locale); + } + + fclose(f); + + return resolved; + + bail: + if(f != NULL) + fclose(f); + if(resolved != NULL) + free(resolved); + return NULL; +} diff --git a/parser.h b/parser.h new file mode 100644 index 0000000..6fceb50 --- /dev/null +++ b/parser.h @@ -0,0 +1,35 @@ +/* +Copyright (c) 2001 by Juliusz Chroboczek + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +#ifndef LOCALE_ALIAS_FILE +#define LOCALE_ALIAS_FILE "/usr/X11R6/lib/X11/locale/locale.alias" +#endif + +#define MAX_KEYWORD_LENGTH 1024 + +#define TOK_ERROR (-2) +#define TOK_EOF (-1) +#define TOK_EOL 0 +#define TOK_EQUALS 1 +#define TOK_KEYWORD 2 + +char *resolveLocale(char *locale); @@ -0,0 +1,457 @@ +/* +Copyright (c) 2001 by Juliusz Chroboczek + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ +/* $XFree86: xc/programs/luit/sys.c,v 1.7 2002/01/07 20:38:30 dawes Exp $ */ + +#include <stdlib.h> +#include <string.h> +#include <stdio.h> +#include <sys/types.h> +#include <unistd.h> +#include <fcntl.h> +#include <sys/ioctl.h> +#include <sys/stat.h> +#include <sys/time.h> +#include <termios.h> +#include <signal.h> +#include <errno.h> + +#ifdef SVR4 +#define HAVE_POLL +#endif + +#ifndef HAVE_POLL +#ifndef _MINIX +#define HAVE_SELECT +#endif +#endif + +#ifdef HAVE_POLL +#include <sys/poll.h> +#undef HAVE_SELECT +#endif + +#ifdef __QNX__ +#include <sys/select.h> +#endif + + +#if (defined(__GLIBC__) && \ + (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 1))) || \ + defined(SVR4) +#define HAVE_GRANTPT +#endif + +#ifdef __GLIBC__ +#include <pty.h> +#endif + +#ifdef SVR4 +#include <stropts.h> +#endif + +#include "sys.h" + +static int saved_tio_valid = 0; +static struct termios saved_tio; + + +#ifdef HAVE_POLL +int +waitForOutput(int fd) +{ + struct pollfd pfd[1]; + int rc; + + pfd[0].fd = fd; + pfd[0].events = POLLOUT; + pfd[0].revents = 0; + + rc = poll(pfd, 1, -1); + if(rc < 0) + return -1; + + if(pfd[0].revents & POLLOUT) + return 1; + + return 0; +} + +int +waitForInput(int fd1, int fd2) +{ + struct pollfd pfd[2]; + int ret, rc; + + pfd[0].fd = fd1; + pfd[1].fd = fd2; + pfd[0].events = pfd[1].events = POLLIN; + pfd[0].revents = pfd[1].revents = 0; + + rc = poll(pfd, 2, -1); + if(rc < 0) + return -1; + + ret = 0; + if(pfd[0].revents & POLLIN) + ret |= 1; + if(pfd[1].revents & POLLIN) + ret |= 2; + return ret; +} +#endif + +#ifdef HAVE_SELECT +int +waitForOutput(int fd) +{ + fd_set fds; + int rc; + + FD_ZERO(&fds); + FD_SET(fd, &fds); + rc = select(FD_SETSIZE, NULL, &fds, NULL, NULL); + if(rc < 0) + return -1; + + if(FD_ISSET(fd, &fds)) + return 1; + + return 0; +} + +int +waitForInput(int fd1, int fd2) +{ + fd_set fds; + int ret, rc; + + FD_ZERO(&fds); + FD_SET(fd1, &fds); + FD_SET(fd2, &fds); + rc = select(FD_SETSIZE, &fds, NULL, NULL, NULL); + if(rc < 0) + return -1; + + ret = 0; + if(FD_ISSET(fd1, &fds)) + ret |= 1; + if(FD_ISSET(fd2, &fds)) + ret |= 2; + return ret; +} +#endif + +#ifndef HAVE_POLL +#ifndef HAVE_SELECT +/* Busy looping implementation */ +int +waitForOutput(int fd) +{ + return 1; +} + +int +waitForInput(int fd1, int fd2) +{ + return 1|2; +} +#endif +#endif + + +int +setWindowSize(int sfd, int dfd) +{ +#ifdef TIOCGWINSZ + int rc; + struct winsize ws; + rc = ioctl(sfd, TIOCGWINSZ, (char*)&ws); + if(rc < 0) + return -1; + rc = ioctl(dfd, TIOCSWINSZ, (char*)&ws); + if(rc < 0) + return -1; +#endif + return 0; +} + +int +installHandler(int signum, void (*handler)(int)) +{ + struct sigaction sa; + sigset_t ss; + int rc; + + sigemptyset(&ss); + + sa.sa_handler = handler; + sa.sa_mask = ss; + sa.sa_flags = 0; + rc = sigaction(signum, &sa, NULL); + return rc; +} + +int +saveTermios(void) +{ + int rc; + rc = tcgetattr(0, &saved_tio); + if(rc >= 0) + saved_tio_valid = 1; + return rc; +} + +int +restoreTermios(void) +{ + if(!saved_tio_valid) + return -1; + return tcsetattr(0, TCSAFLUSH, &saved_tio); +} + +int +setRawTermios(void) +{ + struct termios tio; + int rc; + + if(!saved_tio_valid) + saveTermios(); + rc = tcgetattr(0, &tio); + if(rc < 0) + return rc; + tio.c_lflag &= ~(ECHO|ICANON|ISIG); + tio.c_iflag &= ~(ICRNL|IXOFF|IXON|ISTRIP); +#ifdef ONLCR + tio.c_oflag &= ~ONLCR; +#endif +#ifdef OCRNL + tio.c_oflag &= ~OCRNL; +#endif +#ifdef ONOCR + tio.c_oflag &= ~ONOCR; +#endif + +#ifdef VMIN + tio.c_cc[VMIN] = 0; + tio.c_cc[VTIME] = 0; +#endif + rc = tcsetattr(0, TCSAFLUSH, &tio); + if(rc < 0) + return rc; + return 0; +} + + +char * +my_basename(char *path) +{ + char *p; + + p = strrchr(path, '/'); + if(!p) + p = path; + else + p++; + return p; +} + +static int +fix_pty_perms(char *line) +{ + int rc; + struct stat s; + int uid = getuid(), gid = getgid(); + + rc = stat(line, &s); + if(rc < 0) + return -1; + if(s.st_uid != uid || s.st_gid != gid) { + rc = chown(line, getuid(), getgid()); + if(rc < 0) { + fprintf(stderr, + "Warning: could not change ownership of tty -- " + "pty is insecure!\n"); + return 0; + } + } + if((s.st_mode & 0777) != (S_IRUSR | S_IWUSR | S_IWGRP)) { + rc = chmod(line, S_IRUSR | S_IWUSR | S_IWGRP); + if (rc < 0) { + fprintf(stderr, + "Warning: could not change permissions of tty -- " + "pty is insecure!\n"); + return 0; + } + } + return 1; +} + +int +allocatePty(int *pty_return, char **line_return) +{ + char name[12], *line = NULL; + int pty = -1; + char *name1 = "pqrstuvwxyzPQRST", *name2 = "0123456789abcdef"; + char *p1, *p2; + +#ifdef HAVE_GRANTPT + char *temp_line; + int rc; + + pty = open("/dev/ptmx", O_RDWR); + if(pty < 0) + goto bsd; + + rc = grantpt(pty); + if(rc < 0) { + close(pty); + goto bsd; + } + + rc = unlockpt(pty); + if(rc < 0) { + close(pty); + goto bsd; + } + + temp_line = ptsname(pty); + if(!temp_line) { + close(pty); + goto bsd; + } + line = malloc(strlen(temp_line) + 1); + if(!line) { + close(pty); + return -1; + } + strcpy(line, temp_line); + + fix_pty_perms(line); + + *pty_return = pty; + *line_return = line; + return 0; + + bsd: +#endif /* HAVE_GRANTPT */ + + strcpy(name, "/dev/pty??"); + for(p1 = name1; *p1; p1++) { + name[8] = *p1; + for(p2 = name2; *p2; p2++) { + name[9] = *p2; + pty = open(name, O_RDWR); + if(pty >= 0) + goto found; + if(errno == ENOENT) + goto bail; + else + continue; + } + } + + goto bail; + + found: + line = malloc(strlen(name)); + strcpy(line, name); + line[5] = 't'; + fix_pty_perms(line); + *pty_return = pty; + *line_return = line; + return 0; + + bail: + if(pty >= 0) + close(pty); + if(line) + free(line); + return -1; +} + +int +openTty(char *line) +{ + int rc; + int tty = -1; + + tty = open(line, O_RDWR | O_NOCTTY); + + if(tty < 0) + goto bail; + +#ifdef TIOCSCTTY + rc = ioctl(tty, TIOCSCTTY, (char *)0); + if(rc < 0) { + goto bail; + } +#endif + +#ifdef SVR4 + rc = ioctl(tty, I_PUSH, "ptem"); + if(rc < 0) + goto bail; + + rc = ioctl(tty, I_PUSH, "ldterm"); + if(rc < 0) + goto bail; + + rc = ioctl(tty, I_PUSH, "ttcompat"); + if(rc < 0) + goto bail; +#endif + + return tty; + + bail: + if(tty >= 0) + close(tty); + return -1; +} + +#ifdef _POSIX_SAVED_IDS +int +droppriv() +{ + int rc; + rc = setuid(getuid()); + if(rc < 0) + return rc; + return setgid(getgid()); +} +#else +int +droppriv() +{ + int uid = getuid(); + int euid = geteuid(); + int gid = getgid(); + int egid = getegid(); + + if(uid != euid || gid != egid) { + errno = ENOSYS; + return -1; + } + return 0; +} +#endif @@ -0,0 +1,33 @@ +/* +Copyright (c) 2001 by Juliusz Chroboczek + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +int waitForOutput(int fd); +int waitForInput(int fd1, int fd2); +int setWindowSize(int sfd, int dfd); +int installHandler(int signum, void (*handler)(int)); +int saveTermios(void); +int restoreTermios(void); +int setRawTermios(void); +char *my_basename(char *path); +int allocatePty(int *pty_return, char **line_return); +int openTty(char *line); +int droppriv(void); |