]> www.wagner.pp.ru Git - oss/ck.git/blob - ckUtil.c
Ck console graphics toolkit
[oss/ck.git] / ckUtil.c
1 /* 
2  * ckUtil.c --
3  *
4  *      Miscellaneous utility functions.
5  *
6  * Copyright (c) 1995 Christian Werner.
7  *
8  * See the file "license.terms" for information on usage and redistribution
9  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
10  */
11
12 #include "ckPort.h"
13 #include "ck.h"
14
15 #define REPLACE 1
16 #define NORMAL  2
17 #define TAB     3
18 #define NEWLINE 4
19 #define GCHAR   5
20
21 struct charType {
22     char type;          /* Type of char, see definitions above. */
23     char width;         /* Width if replaced by backslash sequence. */
24 };
25
26 struct charEncoding {
27     char *name;         /* Name for this encoding table. */
28     struct charType ct[256];    /* Encoding table. */
29 };
30
31 /*
32  * For ISO8859, codes 0x81..0x99 are mapped to ACS characters
33  * according to this table:
34  */
35
36 static char *gcharTab[] = {
37     "ulcorner", "llcorner", "urcorner", "lrcorner",
38     "ltee", "rtee", "btee", "ttee",
39     "hline", "vline", "plus", "s1",
40     "s9", "diamond", "ckboard", "degree",
41     "plminus", "bullet", "larrow", "rarrow",
42     "darrow", "uarrow", "board", "lantern",
43     "block"
44 };
45
46 static struct charEncoding EncodingTable[] = {
47 \f
48 /*
49  *----------------------------------------------------------------------
50  *
51  * ISO 8859 encoding.
52  *
53  *----------------------------------------------------------------------
54  */
55
56 {
57     "ISO8859", {
58
59         { REPLACE, 4 }, /* \x00 */
60         { REPLACE, 4 }, /* \x01 */
61         { REPLACE, 4 }, /* \x02 */
62         { REPLACE, 4 }, /* \x03 */
63         { REPLACE, 4 }, /* \x04 */
64         { REPLACE, 4 }, /* \x05 */
65         { REPLACE, 4 }, /* \x06 */
66         { REPLACE, 4 }, /* \x07 */
67         { REPLACE, 2 }, /* \b */
68         { TAB, 2 },     /* \t */
69         { NEWLINE, 2 }, /* \n */
70         { REPLACE, 4 }, /* \x0b */
71         { REPLACE, 2 }, /* \f */
72         { REPLACE, 2 }, /* \r */
73         { REPLACE, 4 }, /* 0x0e */
74         { REPLACE, 4 }, /* 0x0f */
75
76         /* 0x10 .. 0x1f */
77         { REPLACE, 4 }, { REPLACE, 4 }, { REPLACE, 4 }, { REPLACE, 4 },
78         { REPLACE, 4 }, { REPLACE, 4 }, { REPLACE, 4 }, { REPLACE, 4 },
79         { REPLACE, 4 }, { REPLACE, 4 }, { REPLACE, 4 }, { REPLACE, 4 },
80         { REPLACE, 4 }, { REPLACE, 4 }, { REPLACE, 4 }, { REPLACE, 4 },
81
82         /* ' ' .. '/' */
83         { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
84         { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
85         { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
86         { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
87
88         /* '0' .. '?' */
89         { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
90         { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
91         { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
92         { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
93
94         /* '@' .. 'O' */
95         { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
96         { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
97         { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
98         { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
99
100         /* 'P' .. '_' */
101         { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
102         { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
103         { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
104         { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
105
106         /* '`' .. 'o' */
107         { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
108         { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
109         { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
110         { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
111
112         /* 'p' .. '~' */
113         { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
114         { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
115         { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
116         { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
117     
118         { REPLACE, 4 }, /* 0x7f */
119
120         /* 0x80 .. 0x8f */
121         { REPLACE, 4 }, { GCHAR, 1 }, { GCHAR, 1 }, { GCHAR, 1 },
122         { GCHAR, 1 }, { GCHAR, 1 }, { GCHAR, 1 }, { GCHAR, 1 },
123         { GCHAR, 1 }, { GCHAR, 1 }, { GCHAR, 1 }, { GCHAR, 1 },
124         { GCHAR, 1 }, { GCHAR, 1 }, { GCHAR, 1 }, { GCHAR, 1 },
125
126         /* 0x90 .. 0x9f */
127         { GCHAR, 1 }, { GCHAR, 1 }, { GCHAR, 1 }, { GCHAR, 1 },
128         { GCHAR, 1 }, { GCHAR, 1 }, { GCHAR, 1 }, { GCHAR, 1 },
129         { GCHAR, 1 }, { GCHAR, 1 }, { REPLACE, 4 }, { REPLACE, 4 },
130         { REPLACE, 4 }, { REPLACE, 4 }, { REPLACE, 4 }, { REPLACE, 4 },
131
132         /* 0xa0 .. 0xaf */
133         { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
134         { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
135         { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
136         { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
137
138         /* 0xb0 .. 0xbf */
139         { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
140         { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
141         { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
142         { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
143
144         /* 0xc0 .. 0xcf */
145         { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
146         { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
147         { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
148         { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
149
150         /* 0xd0 .. 0xdf */
151         { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
152         { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
153         { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
154         { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
155
156         /* 0xe0 .. 0xef */
157         { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
158         { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
159         { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
160         { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
161
162         /* 0xf0 .. 0xff */
163         { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
164         { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
165         { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
166         { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
167
168       }
169 },
170 \f
171 /*
172  *----------------------------------------------------------------------
173  *
174  * IBM code page 437 encoding.
175  *
176  *----------------------------------------------------------------------
177  */
178
179 {
180     "IBM437", {
181
182         { REPLACE, 4 }, /* \x00 */
183         { REPLACE, 4 }, /* \x01 */
184         { REPLACE, 4 }, /* \x02 */
185         { REPLACE, 4 }, /* \x03 */
186         { REPLACE, 4 }, /* \x04 */
187         { REPLACE, 4 }, /* \x05 */
188         { REPLACE, 4 }, /* \x06 */
189         { REPLACE, 4 }, /* \x07 */
190         { REPLACE, 2 }, /* \b */
191         { TAB, 2 },     /* \t */
192         { NEWLINE, 2 }, /* \n */
193         { REPLACE, 4 }, /* \x0b */
194         { REPLACE, 2 }, /* \f */
195         { REPLACE, 2 }, /* \r */
196         { REPLACE, 4 }, /* 0x0e */
197         { REPLACE, 4 }, /* 0x0f */
198
199         /* 0x10 .. 0x1f */
200         { REPLACE, 4 }, { REPLACE, 4 }, { REPLACE, 4 }, { REPLACE, 4 },
201         { REPLACE, 4 }, { REPLACE, 4 }, { REPLACE, 4 }, { REPLACE, 4 },
202         { REPLACE, 4 }, { REPLACE, 4 }, { REPLACE, 4 }, { REPLACE, 4 },
203         { REPLACE, 4 }, { REPLACE, 4 }, { REPLACE, 4 }, { REPLACE, 4 },
204
205         /* ' ' .. '/' */
206         { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
207         { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
208         { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
209         { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
210
211         /* '0' .. '?' */
212         { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
213         { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
214         { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
215         { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
216
217         /* '@' .. 'O' */
218         { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
219         { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
220         { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
221         { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
222
223         /* 'P' .. '_' */
224         { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
225         { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
226         { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
227         { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
228
229         /* '`' .. 'o' */
230         { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
231         { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
232         { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
233         { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
234
235         /* 'p' .. '~' */
236         { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
237         { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
238         { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
239         { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
240     
241         { REPLACE, 4 }, /* 0x7f */
242
243         /* 0x80 .. 0x8f */
244         { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
245         { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
246         { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
247         { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
248
249         /* 0x90 .. 0x9a */
250         { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
251         { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
252         { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
253
254         { NORMAL, 1 },  /* 0x9b */
255
256         /* 0x9c .. 0x9f */
257         { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
258
259         /* 0xa0 .. 0xaf */
260         { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
261         { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
262         { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
263         { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
264
265         /* 0xb0 .. 0xbf */
266         { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
267         { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
268         { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
269         { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
270
271         /* 0xc0 .. 0xcf */
272         { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
273         { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
274         { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
275         { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
276
277         /* 0xd0 .. 0xdf */
278         { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
279         { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
280         { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
281         { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
282
283         /* 0xe0 .. 0xef */
284         { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
285         { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
286         { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
287         { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
288
289         /* 0xf0 .. 0xfe */
290         { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
291         { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
292         { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
293         { NORMAL, 1 }, { NORMAL, 1 }, { NORMAL, 1 },
294
295         { NORMAL, 1 }   /* 0xff */
296
297       }
298 }
299
300 };
301
302 /*
303  * This is the switch for char encoding.
304  */
305
306 static int Encoding = 0;
307
308 #define CHARTYPE(x)    EncodingTable[Encoding].ct[(x)]
309
310 /*
311  * Characters used when displaying control sequences.
312  */
313
314 static char hexChars[] = "0123456789abcdefxtnvr\\";
315
316 /*
317  * The following table maps some control characters to sequences
318  * like '\n' rather than '\x10'.  A zero entry in the table means
319  * no such mapping exists, and the table only maps characters
320  * less than 0x10.
321  */
322
323 static char mapChars[] = {
324     0, 0, 0, 0, 0, 0, 0, 0,
325     'b', 't', 'n', 0, 'f', 'r', 0
326 };
327
328 \f
329 /*
330  *----------------------------------------------------------------------
331  *
332  * CkCopyAndGlobalEval --
333  *
334  *      This procedure makes a copy of a script then calls Tcl_GlobalEval
335  *      to evaluate it.  It's used in situations where the execution of
336  *      a command may cause the original command string to be reallocated.
337  *
338  * Results:
339  *      Returns the result of evaluating script, including both a standard
340  *      Tcl completion code and a string in interp->result.
341  *
342  * Side effects:
343  *      None.
344  *
345  *----------------------------------------------------------------------
346  */
347
348 int
349 CkCopyAndGlobalEval(interp, script)
350     Tcl_Interp *interp;                 /* Interpreter in which to evaluate
351                                          * script. */
352     char *script;                       /* Script to evaluate. */
353 {
354     Tcl_DString buffer;
355     int code;
356
357     Tcl_DStringInit(&buffer);
358     Tcl_DStringAppend(&buffer, script, -1);
359     code = Tcl_GlobalEval(interp, Tcl_DStringValue(&buffer));
360     Tcl_DStringFree(&buffer);
361     return code;
362 }
363 \f
364 /*
365  *----------------------------------------------------------------------
366  *
367  * Ck_GetScrollInfo --
368  *
369  *      This procedure is invoked to parse "xview" and "yview"
370  *      scrolling commands for widgets using the new scrolling
371  *      command syntax ("moveto" or "scroll" options).
372  *
373  * Results:
374  *      The return value is either CK_SCROLL_MOVETO, CK_SCROLL_PAGES,
375  *      CK_SCROLL_UNITS, or CK_SCROLL_ERROR.  This indicates whether
376  *      the command was successfully parsed and what form the command
377  *      took.  If CK_SCROLL_MOVETO, *dblPtr is filled in with the
378  *      desired position;  if CK_SCROLL_PAGES or CK_SCROLL_UNITS,
379  *      *intPtr is filled in with the number of lines to move (may be
380  *      negative);  if CK_SCROLL_ERROR, interp->result contains an
381  *      error message.
382  *
383  * Side effects:
384  *      None.
385  *
386  *----------------------------------------------------------------------
387  */
388
389 int
390 Ck_GetScrollInfo(interp, argc, argv, dblPtr, intPtr)
391     Tcl_Interp *interp;                 /* Used for error reporting. */
392     int argc;                           /* # arguments for command. */
393     char **argv;                        /* Arguments for command. */
394     double *dblPtr;                     /* Filled in with argument "moveto"
395                                          * option, if any. */
396     int *intPtr;                        /* Filled in with number of pages
397                                          * or lines to scroll, if any. */
398 {
399     int c;
400     size_t length;
401
402     length = strlen(argv[2]);
403     c = argv[2][0];
404     if ((c == 'm') && (strncmp(argv[2], "moveto", length) == 0)) {
405         if (argc != 4) {
406             Tcl_AppendResult(interp, "wrong # args: should be \"",
407                     argv[0], " ", argv[1], " moveto fraction\"",
408                     (char *) NULL);
409             return CK_SCROLL_ERROR;
410         }
411         if (Tcl_GetDouble(interp, argv[3], dblPtr) != TCL_OK) {
412             return CK_SCROLL_ERROR;
413         }
414         return CK_SCROLL_MOVETO;
415     } else if ((c == 's')
416             && (strncmp(argv[2], "scroll", length) == 0)) {
417         if (argc != 5) {
418             Tcl_AppendResult(interp, "wrong # args: should be \"",
419                     argv[0], " ", argv[1], " scroll number units|pages\"",
420                     (char *) NULL);
421             return CK_SCROLL_ERROR;
422         }
423         if (Tcl_GetInt(interp, argv[3], intPtr) != TCL_OK) {
424             return CK_SCROLL_ERROR;
425         }
426         length = strlen(argv[4]);
427         c = argv[4][0];
428         if ((c == 'p') && (strncmp(argv[4], "pages", length) == 0)) {
429             return CK_SCROLL_PAGES;
430         } else if ((c == 'u')
431                 && (strncmp(argv[4], "units", length) == 0)) {
432             return CK_SCROLL_UNITS;
433         } else {
434             Tcl_AppendResult(interp, "bad argument \"", argv[4],
435                     "\": must be units or pages", (char *) NULL);
436             return CK_SCROLL_ERROR;
437         }
438     }
439     Tcl_AppendResult(interp, "unknown option \"", argv[2],
440             "\": must be moveto or scroll", (char *) NULL);
441     return CK_SCROLL_ERROR;
442 }
443 \f
444 /*
445  *--------------------------------------------------------------
446  *
447  * Ck_SetEncoding --
448  *
449  *--------------------------------------------------------------
450  */
451
452 int
453 Ck_SetEncoding(interp, name)
454     Tcl_Interp *interp;
455     char *name;
456 {
457     int i;
458
459     for (i = 0; i < sizeof (EncodingTable) / sizeof (EncodingTable[0]); i++)
460         if (strcmp(name, EncodingTable[i].name) == 0) {
461             Encoding = i;
462             return TCL_OK;
463         }
464     Tcl_AppendResult(interp, "no encoding \"", name, "\"", (char *) NULL);
465     return TCL_ERROR;
466 }
467 \f
468 /*
469  *--------------------------------------------------------------
470  *
471  * Ck_GetEncoding --
472  *
473  *--------------------------------------------------------------
474  */
475
476 int
477 Ck_GetEncoding(interp)
478     Tcl_Interp *interp;
479 {
480     interp->result = EncodingTable[Encoding].name;
481     return TCL_OK;
482 }
483 \f
484 #if CK_USE_UTF
485 /*
486  *--------------------------------------------------------------
487  *
488  * MakeISO --
489  *
490  *      Procedure to convert UTF-8 representation to
491  *      ISO8859-1 for printing on screen.
492  *
493  *--------------------------------------------------------------
494  */
495
496 static char *
497 MakeISO(mainPtr, string, numChars, lenPtr)
498     CkMainInfo *mainPtr;
499     char *string;
500     int numChars;
501     int *lenPtr;
502 {
503     char *p;
504
505     Tcl_DStringFree(&mainPtr->isoBuffer);
506     p = Tcl_UtfToExternalDString(mainPtr->isoEncoding, string,
507             numChars, &mainPtr->isoBuffer);
508     if (lenPtr) {
509         *lenPtr = Tcl_DStringLength(&mainPtr->isoBuffer);
510     }
511     return p;
512 }
513 #endif
514 \f
515 /*
516  *--------------------------------------------------------------
517  *
518  * CkMeasureChars --
519  *
520  *      Measure the number of characters from a string that
521  *      will fit in a given horizontal span.  The measurement
522  *      is done under the assumption that CkDisplayChars will
523  *      be used to actually display the characters.
524  *
525  * Results:
526  *      The return value is the number of characters from source
527  *      that fit in the span given by startX and maxX.  *nextXPtr
528  *      is filled in with the x-coordinate at which the first
529  *      character that didn't fit would be drawn, if it were to
530  *      be drawn.
531  *      
532  *
533  * Side effects:
534  *      None.
535  *
536  *--------------------------------------------------------------
537  */
538
539 int
540 CkMeasureChars(mainPtr, source, maxChars, startX, maxX,
541         tabOrigin, flags, nextXPtr, nextCPtr)
542     CkMainInfo *mainPtr;        /* Needed for encoding. */
543     char *source;               /* Characters to be displayed.  Need not
544                                  * be NULL-terminated. */
545     int maxChars;               /* Maximum # of characters to consider from
546                                  * source. */
547     int startX;                 /* X-position at which first character will
548                                  * be drawn. */
549     int maxX;                   /* Don't consider any character that would
550                                  * cross this x-position. */
551     int tabOrigin;              /* X-location that serves as "origin" for
552                                  * tab stops. */
553     int flags;                  /* Various flag bits OR-ed together.
554                                  * CK_WHOLE_WORDS means stop on a word boundary
555                                  * (just before a space character) if
556                                  * possible.  CK_AT_LEAST_ONE means always
557                                  * return a value of at least one, even
558                                  * if the character doesn't fit. 
559                                  * CK_PARTIAL_OK means it's OK to display only
560                                  * a part of the last character in the line.
561                                  * CK_NEWLINES_NOT_SPECIAL means that newlines
562                                  * are treated just like other control chars:
563                                  * they don't terminate the line.
564                                  * CK_IGNORE_TABS means give all tabs zero
565                                  * width. */
566     int *nextXPtr;              /* Return x-position of terminating
567                                  * character here. */
568     int *nextCPtr;              /* Return byte position of terminating
569                                    character in source. */
570 {
571     register char *p;           /* Current character. */
572     register int c;
573     char *term;                 /* Pointer to most recent character that
574                                  * may legally be a terminating character. */
575     int termX;                  /* X-position just after term. */
576     int curX;                   /* X-position corresponding to p. */
577     int newX;                   /* X-position corresponding to p+1. */
578     int rem;
579 #if CK_USE_UTF
580     int n, m, srcRead, dstWrote, dstChars, nChars = 0;
581     Tcl_UniChar uch;
582     char buf[TCL_UTF_MAX], buf2[TCL_UTF_MAX];
583
584     /*
585      * Scan the input string one character at a time, until a character
586      * is found that crosses maxX.
587      */
588
589     newX = curX = startX;
590     termX = 0;
591     term = source;
592     for (p = source; *p != '\0' && maxChars > 0;) {
593         char *p2;
594
595         n = Tcl_UtfToUniChar(p, &uch);
596         p2 = p + n;
597         ++nChars;
598         maxChars -= n;
599         m = Tcl_UniCharToUtf(uch, buf);
600         Tcl_UtfToExternal(NULL, mainPtr->isoEncoding, buf, m,
601                           TCL_ENCODING_START | TCL_ENCODING_END,
602                           NULL, buf2, sizeof (buf2), &srcRead,
603                           &dstWrote, &dstChars);
604         if (buf2[0] == '\0') {
605             buf2[0] = '?';
606         }
607         c = buf2[0] & 0xFF;
608         if ((CHARTYPE(c).type == NORMAL) || (CHARTYPE(c).type == REPLACE) ||
609             (CHARTYPE(c).type == GCHAR)) {
610             newX += CHARTYPE(c).width;
611         } else if (CHARTYPE(c).type == TAB) {
612             if (!(flags & CK_IGNORE_TABS)) {
613                 newX += 8;
614                 rem = (newX - tabOrigin) % 8;
615                 if (rem < 0) {
616                     rem += 8;
617                 }
618                 newX -= rem;
619             }
620         } else if (CHARTYPE(c).type == NEWLINE) {
621             if (flags & CK_NEWLINES_NOT_SPECIAL) {
622                 newX += CHARTYPE(c).width;
623             } else {
624                 break;
625             }
626         }
627         if (newX > maxX) {
628             break;
629         }
630         p = p2;
631         if (maxChars > 1) {
632             n = Tcl_UtfToUniChar(p, &uch);
633             m = Tcl_UniCharToUtf(uch, buf);
634             Tcl_UtfToExternal(NULL, mainPtr->isoEncoding, buf, m,
635                               TCL_ENCODING_START | TCL_ENCODING_END,
636                               NULL, buf2, sizeof (buf2), &srcRead,
637                               &dstWrote, &dstChars);
638             if (buf2[0] == '\0') {
639                 buf2[0] = '?';
640             }
641             c = buf2[0] & 0xff;
642         } else {
643             c = 0;
644         }
645         if (isspace(c) || (c == 0)) {
646             term = p2;
647             termX = newX;
648         }
649         curX = newX;
650     }
651
652     /*
653      * P points to the first character that doesn't fit in the desired
654      * span. Use the flags to figure out what to return.
655      */
656
657     if ((flags & CK_PARTIAL_OK) && (curX < maxX)) {
658         curX = newX;
659         n = Tcl_UtfToUniChar(p, &uch);
660         p += n;
661         ++nChars;
662     }
663     if ((flags & CK_AT_LEAST_ONE) && (term == source) && (maxChars > 0)
664              && !isspace((unsigned char) *term)) {
665         term = p;
666         termX = curX;
667         if (term == source) {
668             n = Tcl_UtfToUniChar(term, &uch);
669             term += n;
670             ++nChars;
671         }
672     } else if ((maxChars == 0) || !(flags & CK_WHOLE_WORDS)) {
673         term = p;
674         termX = curX;
675     }
676     *nextXPtr = termX;
677     *nextCPtr = term - source;
678     return nChars;
679 #else
680     /*
681      * Scan the input string one character at a time, until a character
682      * is found that crosses maxX.
683      */
684
685     newX = curX = startX;
686     termX = 0;
687     term = source;
688     for (p = source, c = *p & 0xff; c != '\0' && maxChars > 0;
689         p++, maxChars--) {
690         if ((CHARTYPE(c).type == NORMAL) || (CHARTYPE(c).type == REPLACE) ||
691             (CHARTYPE(c).type == GCHAR)) {
692             newX += CHARTYPE(c).width;
693         } else if (CHARTYPE(c).type == TAB) {
694             if (!(flags & CK_IGNORE_TABS)) {
695                 newX += 8;
696                 rem = (newX - tabOrigin) % 8;
697                 if (rem < 0) {
698                     rem += 8;
699                 }
700                 newX -= rem;
701             }
702         } else if (CHARTYPE(c).type == NEWLINE) {
703             if (flags & CK_NEWLINES_NOT_SPECIAL) {
704                 newX += CHARTYPE(c).width;
705             } else {
706                 break;
707             }
708         }
709         if (newX > maxX) {
710             break;
711         }
712         if (maxChars > 1) {
713             c = p[1] & 0xff;
714         } else {
715             c = 0;
716         }
717         if (isspace(c) || (c == 0)) {
718             term = p+1;
719             termX = newX;
720         }
721         curX = newX;
722     }
723
724     /*
725      * P points to the first character that doesn't fit in the desired
726      * span. Use the flags to figure out what to return.
727      */
728
729     if ((flags & CK_PARTIAL_OK) && (curX < maxX)) {
730         curX = newX;
731         p++;
732     }
733     if ((flags & CK_AT_LEAST_ONE) && (term == source) && (maxChars > 0)
734              && !isspace((unsigned char) *term)) {
735         term = p;
736         termX = curX;
737         if (term == source) {
738             term++;
739             termX = newX;
740         }
741     } else if ((maxChars == 0) || !(flags & CK_WHOLE_WORDS)) {
742         term = p;
743         termX = curX;
744     }
745     *nextXPtr = termX;
746     *nextCPtr = termX;
747     return term - source;
748 #endif
749 }
750 \f
751 /*
752  *--------------------------------------------------------------
753  *
754  * CkDisplayChars --
755  *
756  *      Draw a string of characters on the screen, converting
757  *      tabs to the right number of spaces and control characters
758  *      to sequences of the form "\xhh" where hh are two hex
759  *      digits.
760  *
761  * Results:
762  *      None.
763  *
764  * Side effects:
765  *      Information gets drawn on the screen.
766  *
767  *--------------------------------------------------------------
768  */
769
770 void
771 CkDisplayChars(mainPtr, window, string, numChars, x, y, tabOrigin, flags)
772     CkMainInfo *mainPtr;        /* Needed for encoding. */
773     WINDOW *window;             /* Curses window. */
774     char *string;               /* Characters to be displayed. */
775     int numChars;               /* Number of characters to display from
776                                  * string. */
777     int x, y;                   /* Coordinates at which to draw string. */
778     int tabOrigin;              /* X-location that serves as "origin" for
779                                  * tab stops. */
780     int flags;                  /* Flags to control display.  Only
781                                  * CK_NEWLINES_NOT_SPECIAL, CK_IGNORE_TABS
782                                  * and CK_FILL_UNTIL_EOL are supported right
783                                  * now.  See CkMeasureChars for information
784                                  * about it. */
785 {
786     register char *p;           /* Current character being scanned. */
787     register int c;
788     int startX;                 /* X-coordinate corresponding to start. */
789     int curX;                   /* X-coordinate corresponding to p. */
790     char replace[10];
791     int rem, dummy, maxX;
792
793 #if CK_USE_UTF
794     string = MakeISO(mainPtr, string, numChars, &numChars);
795 #endif
796
797     /*
798      * Scan the string one character at a time and display the
799      * character.
800      */
801
802     getmaxyx(window, dummy, maxX);
803     maxX -= x;
804     if (numChars > maxX)
805         numChars = maxX;
806     p = string;
807     if (x < 0) {
808         numChars += x;
809         p -= x;
810         x = 0;
811     }
812     wmove(window, y, x);
813     startX = curX = x;
814     for (; numChars > 0; numChars--, p++) {
815         c = *p & 0xff;
816         if (c == '\0')
817             break;
818         if (CHARTYPE(c).type == NORMAL) {
819             waddch(window, c);
820             startX++;
821             continue;
822         }
823         if (CHARTYPE(c).type == TAB) {
824             if (!(flags & CK_IGNORE_TABS)) {
825                 curX += 8;
826                 rem = (curX - tabOrigin) % 8;
827                 if (rem < 0) {
828                     rem += 8;
829                 }
830                 curX -= rem;
831             }
832             while (startX < curX) {
833                 waddch(window, ' ');
834                 startX++;
835             }
836             continue;
837         } else if (CHARTYPE(c).type == GCHAR) {
838             int gchar;
839
840             if (Ck_GetGChar(NULL, gcharTab[c - 0x81], &gchar) != TCL_OK)
841                 goto replaceChar;
842             waddch(window, gchar);
843             startX++;
844             continue;
845         } else if (CHARTYPE(c).type == REPLACE || (CHARTYPE(c).type == NEWLINE
846             && (flags & CK_NEWLINES_NOT_SPECIAL))) {
847 replaceChar:
848             if ((c < sizeof(mapChars)) && (mapChars[c] != 0)) {
849                 replace[0] = '\\';
850                 replace[1] = mapChars[c];
851                 replace[2] = '\0';
852                 waddstr(window, replace);
853                 curX += 2;
854             } else {
855                 replace[0] = '\\';
856                 replace[1] = 'x';
857                 replace[2] = hexChars[(c >> 4) & 0xf];
858                 replace[3] = hexChars[c & 0xf];
859                 replace[4] = '\0';
860                 waddstr(window, replace);
861                 curX += 4;
862             }
863         } else if (CHARTYPE(c).type == NEWLINE) {
864             y++;
865             wmove(window, y, x);
866             curX = x;
867         }
868         startX = curX;
869     }
870     if (flags & CK_FILL_UNTIL_EOL) {
871         while (startX < maxX) {
872            waddch(window, ' ');
873            startX++;
874         }
875     }
876 }
877 \f
878 /*
879  *--------------------------------------------------------------
880  *
881  * CkUnderlineChars --
882  *
883  *      Draw a range of string of characters on the screen,
884  *      converting tabs to the right number of spaces and control
885  *      characters to sequences of the form "\xhh" where hh are two hex
886  *      digits.
887  *
888  * Results:
889  *      None.
890  *
891  * Side effects:
892  *      Information gets drawn on the screen.
893  *
894  *--------------------------------------------------------------
895  */
896
897 void
898 CkUnderlineChars(mainPtr, window, string, numChars, x, y, tabOrigin,
899         flags, first, last)
900     CkMainInfo *mainPtr;        /* Needed for encoding. */
901     WINDOW *window;             /* Curses window. */
902     char *string;               /* Characters to be displayed. */
903     int numChars;               /* Number of characters to display from
904                                  * string. */
905     int x, y;                   /* Coordinates at which to draw string. */
906     int tabOrigin;              /* X-location that serves as "origin" for
907                                  * tab stops. */
908     int flags;                  /* Flags to control display.  Only
909                                  * CK_NEWLINES_NOT_SPECIAL, CK_IGNORE_TABS
910                                  * and CK_FILL_UNTIL_EOL are supported right
911                                  * now.  See CkMeasureChars for information
912                                  * about it. */
913     int first, last;            /* Range: First and last characters to
914                                  * display. */
915 {
916     register char *p;           /* Current character being scanned. */
917     register int c, count;
918     int startX;                 /* X-coordinate corresponding to start. */
919     int curX;                   /* X-coordinate corresponding to p. */
920     char replace[10];
921     int rem, dummy, maxX;
922
923 #if CK_USE_UTF
924     string = MakeISO(mainPtr, string, numChars, &numChars);
925 #endif
926
927     /*
928      * Scan the string one character at a time and display the
929      * character.
930      */
931
932     count = 0;
933     getmaxyx(window, dummy, maxX);
934     maxX -= x;
935     if (numChars > maxX)
936         numChars = maxX;
937     p = string;
938     if (x < 0) {
939         numChars += x;
940         count += x;
941         p -= x;
942         x = 0;
943     }
944     wmove(window, y, x);
945     startX = curX = x;
946     for (; numChars > 0 && count <= last; numChars--, count++, p++) {
947         c = *p & 0xff;
948         if (c == '\0')
949             break;
950         if (CHARTYPE(c).type == NORMAL) {
951             startX++;
952             if (count >= first)
953                 waddch(window, c);
954             else
955                 wmove(window, y, startX);
956             continue;
957         }
958         if (CHARTYPE(c).type == TAB) {
959             if (!(flags & CK_IGNORE_TABS)) {
960                 curX += 8;
961                 rem = (curX - tabOrigin) % 8;
962                 if (rem < 0) {
963                     rem += 8;
964                 }
965                 curX -= rem;
966             }
967             while (startX < curX) {
968                 startX++;
969                 if (count >= first)
970                     waddch(window, ' ');
971                 else
972                     wmove(window, y, startX);
973             }
974             continue;
975         } else if (CHARTYPE(c).type == GCHAR) {
976             int gchar;
977
978             if (Ck_GetGChar(NULL, gcharTab[c - 0x81], &gchar) != TCL_OK)
979                 goto replaceChar;
980             startX++;
981             if (count >= first)
982                 waddch(window, gchar);
983             else
984                 wmove(window, y, startX);
985             continue;
986         } else if (CHARTYPE(c).type == REPLACE || (CHARTYPE(c).type == NEWLINE
987             && (flags & CK_NEWLINES_NOT_SPECIAL))) {
988 replaceChar:
989             if ((c < sizeof(mapChars)) && (mapChars[c] != 0)) {
990                 replace[0] = '\\';
991                 replace[1] = mapChars[c];
992                 replace[2] = '\0';
993                 curX += 2;
994                 if (count >= first)
995                     waddstr(window, replace);
996                 else
997                     wmove(window, y, curX);
998             } else {
999                 replace[0] = '\\';
1000                 replace[1] = 'x';
1001                 replace[2] = hexChars[(c >> 4) & 0xf];
1002                 replace[3] = hexChars[c & 0xf];
1003                 replace[4] = '\0';
1004                 curX += 4;
1005                 if (count >= first)
1006                     waddstr(window, replace);
1007                 else
1008                     wmove(window, y, curX);
1009             }
1010         } else if (CHARTYPE(c).type == NEWLINE) {
1011             y++;
1012             wmove(window, y, x);
1013             curX = x;
1014         }
1015         startX = curX;
1016     }
1017 }
1018