1 /****************************************************************************
3 FIGlet Copyright 1991, 1993, 1994 Glenn Chappell and Ian Chai
4 FIGlet Copyright 1996, 1997, 1998, 1999, 2000, 2001 John Cowan
5 FIGlet Copyright 2002 Christiaan Keet
6 FIGlet Copyright 2011, 2012 Claudio Matsuoka
7 Portions written by Paul Burton and Christiaan Keet
8 Internet: <info@figlet.org>
9 FIGlet, along with the various FIGlet fonts and documentation, is
10 copyrighted under the provisions of the New BSD License (3-clause)
11 (as listed in the file "LICENSE" which is included in this package)
12 ****************************************************************************/
14 #define DATE "31 May 2012"
15 #define VERSION "2.2.5"
16 #define VERSION_INT 20205
18 /* FIGlet (Frank, Ian & Glenn's Letters) */
19 /* by Glenn Chappell */
21 /* Automatic file addition by Ian Chai May 1991 */
22 /* Punctuation and numbers addition by Ian Chai Jan 1993 */
23 /* Full ASCII by Glenn Chappell Feb 1993 */
24 /* Line-breaking, general rewrite by Glenn Chappell Mar 1993 */
25 /* Hard blanks by Glenn Chappell Apr 1993 */
26 /* Release 2.0 5 Aug 1993 */
27 /* Right-to-left printing, extended char set by Glenn Chappell Dec 1993 */
28 /* Control files by Glenn Chappell Feb 1994 */
29 /* Release 2.1 12 Aug 1994 */
30 /* Release 2.1.1 25 Aug 1994 */
31 /* Release 2.1.2 by Gilbert (Mad Programmer) Healton: Add -A command line
32 option. Sept 8, 1996 */
33 /* Release 2.2 by John Cowan: multibyte inputs, compressed fonts,
34 mapping tables, kerning/smushing options. */
35 /* Release 2.2.1 by Christiaan Keet: minor updates including readmes
36 FAQs and comments. 13 July 2002. The new official FIGlet website is
37 http://www.figlet.org/ */
38 /* Release 2.2.2 by Christiaan Keet: License changed from "Artistic License"
39 to "Academic Free License" as agreed by FIGlet authors. 05 July 2005 */
40 /* Release 2.2.3 by Claudio Matsuoka, 12 Jan 2011: BSD license, fixes */
41 /* Release 2.2.4 by Claudio Matsuoka, 26 Jan 2011: tlf2 font support */
42 /* Release 2.2.5 by Claudio Matsuoka, 31 May 2012: flc licensing, minor fixes */
44 /*---------------------------------------------------------------------------
45 DEFAULTFONTDIR and DEFAULTFONTFILE should be defined in the Makefile.
46 DEFAULTFONTDIR is the full path name of the directory in which FIGlet
47 will search first for fonts (the ".flf" files).
48 DEFAULTFONTFILE is the filename of the font to be used if no other
49 is specified (standard.flf is recommended, but any other can be
50 used). This file should reside in the directory specified by
52 ---------------------------------------------------------------------------*/
53 #ifndef DEFAULTFONTDIR
54 #define DEFAULTFONTDIR "fonts"
56 #ifndef DEFAULTFONTFILE
57 #define DEFAULTFONTFILE "standard.flf"
67 #include <fcntl.h> /* Needed for get_columns */
69 #if defined(unix) || defined(__unix__) || defined(__APPLE__)
71 #include <sys/ioctl.h> /* Needed for get_columns */
80 #include "zipio.h" /* Package for reading compressed files */
82 #define MYSTRLEN(x) ((int)strlen(x)) /* Eliminate ANSI problem */
86 /* Leave alone for Unix and MS-DOS/Windows!
87 Note: '/' also used in filename in get_columns(). */
89 #define FONTFILESUFFIX ".flf"
90 #define FONTFILEMAGICNUMBER "flf2"
91 #define FSUFFIXLEN MYSTRLEN(FONTFILESUFFIX)
92 #define CONTROLFILESUFFIX ".flc"
93 #define CONTROLFILEMAGICNUMBER "flc2" /* no longer used in 2.2 */
94 #define CSUFFIXLEN MYSTRLEN(CONTROLFILESUFFIX)
95 #define DEFAULTCOLUMNS 80
96 #define MAXLEN 255 /* Maximum character width */
98 /* Add support for Sam Hocevar's TOIlet fonts */
100 #define TOILETFILESUFFIX ".tlf"
101 #define TOILETFILEMAGICNUMBER "tlf2"
102 #define TSUFFIXLEN MYSTRLEN(TOILETFILESUFFIX)
104 int toiletfont
; /* true if font is a TOIlet TLF font */
108 /****************************************************************************
110 Globals dealing with chars that are read
112 ****************************************************************************/
114 typedef long inchr
; /* "char" read from stdin */
116 inchr
*inchrline
; /* Alloc'd inchr inchrline[inchrlinelenlimit+1]; */
117 /* Note: not null-terminated. */
118 int inchrlinelen
,inchrlinelenlimit
;
119 inchr deutsch
[7] = {196, 214, 220, 228, 246, 252, 223};
120 /* Latin-1 codes for German letters, respectively:
121 LATIN CAPITAL LETTER A WITH DIAERESIS = A-umlaut
122 LATIN CAPITAL LETTER O WITH DIAERESIS = O-umlaut
123 LATIN CAPITAL LETTER U WITH DIAERESIS = U-umlaut
124 LATIN SMALL LETTER A WITH DIAERESIS = a-umlaut
125 LATIN SMALL LETTER O WITH DIAERESIS = o-umlaut
126 LATIN SMALL LETTER U WITH DIAERESIS = u-umlaut
127 LATIN SMALL LETTER SHARP S = ess-zed
130 int hzmode
; /* true if reading double-bytes in HZ mode */
131 int gndbl
[4]; /* gndbl[n] is true if Gn is double-byte */
132 inchr gn
[4]; /* Gn character sets: ASCII, Latin-1, none, none */
133 int gl
; /* 0-3 specifies left-half Gn character set */
134 int gr
; /* 0-3 specifies right-half Gn character set */
136 int Myargc
; /* to avoid passing around argc and argv */
139 /****************************************************************************
141 Globals dealing with chars that are written
143 ****************************************************************************/
146 typedef wchar_t outchr
; /* "char" written to stdout */
147 #define STRLEN(x) wcslen(x)
148 #define STRCPY(x,y) wcscpy((x),(y))
149 #define STRCAT(x,y) wcscat((x),(y))
150 #define ISSPACE(x) iswspace(x)
152 typedef char outchr
; /* "char" written to stdout */
153 #define STRLEN(x) MYSTRLEN(x)
154 #define STRCPY(x,y) strcpy((x),(y))
155 #define STRCAT(x,y) strcat((x),(y))
156 #define ISSPACE(x) isspace(x)
161 outchr
**thechar
; /* Alloc'd char thechar[charheight][]; */
165 fcharnode
*fcharlist
;
168 int previouscharwidth
;
169 outchr
**outputline
; /* Alloc'd char outputline[charheight][outlinelenlimit+1]; */
173 /****************************************************************************
175 Globals dealing with command file storage
177 ****************************************************************************/
184 cfnamenode
*cfilelist
,**cfilelistend
;
194 comnode
*commandlist
,**commandlistend
;
196 /****************************************************************************
198 Globals affected by command line options
200 ****************************************************************************/
202 int deutschflag
,justification
,paragraphflag
,right2left
,multibyte
;
209 #define SM_HIERARCHY 4
212 #define SM_HARDBLANK 32
216 #define SMO_NO 0 /* no command-line smushmode */
217 #define SMO_YES 1 /* use command-line smushmode, ignore font smushmode */
218 #define SMO_FORCE 2 /* logically OR command-line and font smushmodes */
224 char *fontdirname
,*fontname
;
227 /****************************************************************************
229 Globals read from font file
231 ****************************************************************************/
237 /****************************************************************************
239 Name of program, used in error messages
241 ****************************************************************************/
247 /****************************************************************************
251 Determines the number of columns of /dev/tty. Returns the number of
252 columns, or -1 if error. May return 0 if columns unknown.
253 Requires include files <fcntl.h> and <sys/ioctl.h>.
254 by Glenn Chappell & Ian Chai 14 Apr 1993
256 ****************************************************************************/
263 if ((fd
= open("/dev/tty",O_WRONLY
))<0) return -1;
264 result
= ioctl(fd
,TIOCGWINSZ
,&ws
);
266 return result
?-1:ws
.ws_col
;
268 #endif /* ifdef TIOCGWINSZ */
271 /****************************************************************************
275 Calls malloc. If malloc returns error, prints error message and
278 ****************************************************************************/
281 char *myalloc(size_t size
)
289 extern void *malloc();
292 if ((ptr
= (char*)malloc(size
))==NULL
) {
293 fprintf(stderr
,"%s: Out of memory\n",myname
);
302 /****************************************************************************
306 Returns true if s1 contains a DIRSEP or DIRSEP2 character.
308 ****************************************************************************/
313 if (strchr(s1
, DIRSEP
)) return 1;
314 else if (strchr(s1
, DIRSEP2
)) return 1;
318 /****************************************************************************
322 Returns true if s2 is a suffix of s1; uses case-blind comparison.
324 ****************************************************************************/
326 int suffixcmp(s1
, s2
)
334 if (len2
> len1
) return 0;
337 if (tolower(*s1
) != tolower(*s2
)) return 0;
344 /****************************************************************************
348 Skips to the end of a line, given a stream. Handles \r, \n, or \r\n.
350 ****************************************************************************/
357 while (dummy
=Zgetc(fp
),dummy
!=EOF
) {
358 if (dummy
== '\n') return;
361 if (dummy
!= EOF
&& dummy
!= '\n') Zungetc(dummy
,fp
);
368 /****************************************************************************
372 Local version of fgets. Handles \r, \n, and \r\n terminators.
374 ****************************************************************************/
376 char *myfgets(line
,maxlen
,fp
)
385 while((c
=Zgetc(fp
))!=EOF
&&maxlen
) {
391 if (c
!= EOF
&& c
!= '\n') Zungetc(c
,fp
);
397 return (c
==EOF
) ? NULL
: line
;
401 /****************************************************************************
405 Prints "Usage: ...." line to the given stream.
407 ****************************************************************************/
413 "Usage: %s [ -cklnoprstvxDELNRSWX ] [ -d fontdirectory ]\n",
416 " [ -f fontfile ] [ -m smushmode ] [ -w outputwidth ]\n");
418 " [ -C controlfile ] [ -I infocode ] [ message ]\n");
422 /****************************************************************************
426 Prints version and copyright message, or utility information.
428 ****************************************************************************/
430 void printinfo(infonum
)
434 case 0: /* Copyright message */
435 printf("FIGlet Copyright (C) 1991-2012 Glenn Chappell, Ian Chai, ");
436 printf("John Cowan,\nChristiaan Keet and Claudio Matsuoka\n");
437 printf("Internet: <info@figlet.org> ");
438 printf("Version: %s, date: %s\n\n",VERSION
,DATE
);
439 printf("FIGlet, along with the various FIGlet fonts");
440 printf(" and documentation, may be\n");
441 printf("freely copied and distributed.\n\n");
442 printf("If you use FIGlet, please send an");
443 printf(" e-mail message to <info@figlet.org>.\n\n");
444 printf("The latest version of FIGlet is available from the");
445 printf(" web site,\n\thttp://www.figlet.org/\n\n");
448 case 1: /* Version (integer) */
449 printf("%d\n",VERSION_INT
);
451 case 2: /* Font directory */
452 printf("%s\n",fontdirname
);
455 printf("%s\n",fontname
);
457 case 4: /* Outputwidth */
458 printf("%d\n",outputwidth
);
460 case 5: /* Font formats */
461 printf("%s", FONTFILEMAGICNUMBER
);
463 printf(" %s", TOILETFILEMAGICNUMBER
);
470 /****************************************************************************
474 Reads a four-character magic string from a stream.
476 ****************************************************************************/
477 void readmagic(fp
,magic
)
484 magic
[i
] = Zgetc(fp
);
489 /****************************************************************************
493 Skips whitespace characters from a stream.
495 ****************************************************************************/
500 while (c
=Zgetc(fp
),isascii(c
)&&isspace(c
)) ;
504 /****************************************************************************
508 Reads a number from a stream. Accepts "0" prefix for octal and
509 "0x" or "0X" for hexadecimal. Ignores leading whitespace.
511 ****************************************************************************/
512 void readnum(fp
,nump
)
521 char digits
[] = "0123456789ABCDEF";
534 if (c
=='x'||c
=='X') {
547 while((c
=Zgetc(fp
))!=EOF
) {
555 acc
= acc
*base
+(p
-digits
);
560 /****************************************************************************
564 Reads a control file "T" command character specification.
566 Character is a single byte, an escape sequence, or
569 ****************************************************************************/
578 if (thechar
=='\n' || thechar
=='\r') { /* Handle badly-formatted file */
582 if (thechar
!='\\') return thechar
;
602 if (next
=='-' || next
=='x' || (next
>='0' && next
<='9')) {
604 readnum(fp
,&thechar
);
611 /****************************************************************************
615 Get a Tchar representing a charset name, or 0 if none available.
616 Called in getcharset().
618 ****************************************************************************/
620 inchr
charsetname(fp
)
625 result
= readTchar(fp
);
626 if (result
== '\n' || result
== '\r') {
633 /****************************************************************************
637 Processes "g[0123]" character set specifier
638 Called in readcontrol().
640 ****************************************************************************/
642 void charset(n
, controlfile
)
649 if (Zgetc(controlfile
) != '9') {
650 skiptoeol(controlfile
);
653 ch
= Zgetc(controlfile
);
655 gn
[n
] = 65536L * charsetname(controlfile
) + 0x80;
657 skiptoeol(controlfile
);
661 skiptoeol(controlfile
);
664 ch
= Zgetc(controlfile
);
666 if (Zgetc(controlfile
) != '9') {
667 skiptoeol(controlfile
);
670 if (Zgetc(controlfile
) != '4') {
671 skiptoeol(controlfile
);
675 gn
[n
] = 65536L * charsetname(controlfile
);
677 skiptoeol(controlfile
);
680 Zungetc(ch
, controlfile
);
682 gn
[n
] = 65536L * charsetname(controlfile
);
687 /****************************************************************************
691 Given a FIGlet font or control file name and suffix, return the file
694 ****************************************************************************/
696 ZFILE
*FIGopen(name
,suffix
)
705 namelen
= MYSTRLEN(fontdirname
);
706 fontpath
= (char*)alloca(sizeof(char)*
707 (namelen
+MYSTRLEN(name
)+MYSTRLEN(suffix
)+2));
709 if (!hasdirsep(name
)) { /* not a full path name */
710 strcpy(fontpath
,fontdirname
);
711 fontpath
[namelen
] = DIRSEP
;
712 fontpath
[namelen
+1] = '\0';
713 strcat(fontpath
,name
);
714 strcat(fontpath
,suffix
);
715 if(stat(fontpath
,&st
)==0) goto ok
;
717 /* just append suffix */
718 strcpy(fontpath
,name
);
719 strcat(fontpath
,suffix
);
720 if(stat(fontpath
,&st
)==0) goto ok
;
725 fontfile
= Zopen(fontpath
,"rb");
729 /****************************************************************************
733 Allocates memory and reads in the given control file.
734 Called in readcontrolfiles().
736 ****************************************************************************/
738 void readcontrol(controlname
)
741 inchr firstch
,lastch
;
747 controlfile
= FIGopen(controlname
,CONTROLFILESUFFIX
);
749 if (controlfile
==NULL
) {
750 fprintf(stderr
,"%s: %s: Unable to open control file\n",myname
,
755 (*commandlistend
) = (comnode
*)myalloc(sizeof(comnode
));
756 (*commandlistend
)->thecommand
= 0; /* Begin with a freeze command */
757 commandlistend
= &(*commandlistend
)->next
;
758 (*commandlistend
) = NULL
;
760 while(command
=Zgetc(controlfile
),command
!=EOF
) {
762 case 't': /* Translate */
764 firstch
=readTchar(controlfile
);
765 if ((dashcheck
=Zgetc(controlfile
))=='-') {
766 lastch
=readTchar(controlfile
);
769 Zungetc(dashcheck
,controlfile
);
773 offset
=readTchar(controlfile
)-firstch
;
774 skiptoeol(controlfile
);
775 (*commandlistend
) = (comnode
*)myalloc(sizeof(comnode
));
776 (*commandlistend
)->thecommand
= 1;
777 (*commandlistend
)->rangelo
= firstch
;
778 (*commandlistend
)->rangehi
= lastch
;
779 (*commandlistend
)->offset
= offset
;
780 commandlistend
= &(*commandlistend
)->next
;
781 (*commandlistend
) = NULL
;
783 case '0': case '1': case '2': case '3': case '4':
784 case '5': case '6': case '7': case '8': case '9':
786 /* Mapping table entry */
787 Zungetc(command
,controlfile
);
788 readnum(controlfile
,&firstch
);
790 readnum(controlfile
,&lastch
);
791 offset
=lastch
-firstch
;
793 skiptoeol(controlfile
);
794 (*commandlistend
) = (comnode
*)myalloc(sizeof(comnode
));
795 (*commandlistend
)->thecommand
= 1;
796 (*commandlistend
)->rangelo
= firstch
;
797 (*commandlistend
)->rangehi
= lastch
;
798 (*commandlistend
)->offset
= offset
;
799 commandlistend
= &(*commandlistend
)->next
;
800 (*commandlistend
) = NULL
;
802 case 'f': /* freeze */
803 skiptoeol(controlfile
);
804 (*commandlistend
) = (comnode
*)myalloc(sizeof(comnode
));
805 (*commandlistend
)->thecommand
= 0;
806 commandlistend
= &(*commandlistend
)->next
;
807 (*commandlistend
) = NULL
;
809 case 'b': /* DBCS input mode */
812 case 'u': /* UTF-8 input mode */
815 case 'h': /* HZ input mode */
818 case 'j': /* Shift-JIS input mode */
821 case 'g': /* ISO 2022 character set choices */
824 command
=Zgetc(controlfile
);
826 case '0': /* define G0 charset */
827 charset(0, controlfile
);
829 case '1': /* set G1 charset */
830 charset(1, controlfile
);
832 case '2': /* set G2 charset */
833 charset(2, controlfile
);
835 case '3': /* set G3 charset */
836 charset(3, controlfile
);
838 case 'l': case 'L': /* define left half */
840 gl
= Zgetc(controlfile
) - '0';
841 skiptoeol(controlfile
);
843 case 'r': case 'R': /* define right half */
845 gr
= Zgetc(controlfile
) - '0';
846 skiptoeol(controlfile
);
848 default: /* meaningless "g" command */
849 skiptoeol(controlfile
);
851 case '\r': case '\n': /* blank line */
853 default: /* Includes '#' */
854 skiptoeol(controlfile
);
861 /****************************************************************************
865 Reads in the controlfiles names in cfilelist. Uses readcontrol.
868 ****************************************************************************/
870 void readcontrolfiles()
874 for (cfnptr
=cfilelist
;cfnptr
!=NULL
;cfnptr
=cfnptr
->next
) {
875 readcontrol(cfnptr
->thename
);
880 /****************************************************************************
884 Clears the control file list. Assumes thename does not need freeing.
886 ****************************************************************************/
888 void clearcfilelist()
890 cfnamenode
*cfnptr1
,*cfnptr2
;
893 while (cfnptr1
!= NULL
) {
894 cfnptr2
= cfnptr1
->next
;
899 cfilelistend
= &cfilelist
;
903 /****************************************************************************
907 Handles all command-line parameters. Puts all parameters within
910 ****************************************************************************/
916 int c
; /* "Should" be a char -- need int for "!= -1" test*/
917 int columns
,infoprint
;
918 char *controlname
,*env
;
920 if ((myname
= strrchr(Myargv
[0],DIRSEP
))!=NULL
) {
926 fontdirname
= DEFAULTFONTDIR
;
927 env
= getenv("FIGLET_FONTDIR");
931 fontname
= DEFAULTFONTFILE
;
933 cfilelistend
= &cfilelist
;
935 commandlistend
= &commandlist
;
936 smushoverride
= SMO_NO
;
943 outputwidth
= DEFAULTCOLUMNS
;
946 while ((c
= getopt(Myargc
,Myargv
,"ADEXLRI:xlcrpntvm:w:d:f:C:NFskSWo"))!= -1) {
947 /* Note: -F is not a legal option -- prints a special err message. */
986 smushoverride
= SMO_NO
;
990 smushoverride
= SMO_YES
;
993 smushmode
= SM_SMUSH
;
994 smushoverride
= SMO_FORCE
;
997 smushmode
= SM_SMUSH
;
998 smushoverride
= SMO_YES
;
1002 smushoverride
= SMO_YES
;
1006 columns
= get_columns();
1008 outputwidth
= columns
;
1010 #else /* ifdef TIOCGWINSZ */
1012 "%s: \"-t\" is disabled, since ioctl is not fully implemented.\n",
1014 #endif /* ifdef TIOCGWINSZ */
1020 infoprint
= atoi(optarg
);
1023 smushmode
= atoi(optarg
);
1024 if (smushmode
< -1) {
1025 smushoverride
= SMO_NO
;
1028 if (smushmode
== 0) smushmode
= SM_KERN
;
1029 else if (smushmode
== -1) smushmode
= 0;
1030 else smushmode
= (smushmode
& 63) | SM_SMUSH
;
1031 smushoverride
= SMO_YES
;
1034 columns
= atoi(optarg
);
1036 outputwidth
= columns
;
1040 fontdirname
= optarg
;
1044 if (suffixcmp(fontname
,FONTFILESUFFIX
)) {
1045 fontname
[MYSTRLEN(fontname
)-FSUFFIXLEN
] = '\0';
1048 else if (suffixcmp(fontname
,TOILETFILESUFFIX
)) {
1049 fontname
[MYSTRLEN(fontname
)-TSUFFIXLEN
] = '\0';
1054 controlname
= optarg
;
1055 if (suffixcmp(controlname
, CONTROLFILESUFFIX
)) {
1056 controlname
[MYSTRLEN(controlname
)-CSUFFIXLEN
] = '\0';
1058 (*cfilelistend
) = (cfnamenode
*)myalloc(sizeof(cfnamenode
));
1059 (*cfilelistend
)->thename
= controlname
;
1060 cfilelistend
= &(*cfilelistend
)->next
;
1061 (*cfilelistend
) = NULL
;
1069 gndbl
[0] = gndbl
[1] = gndbl
[2] = gndbl
[3] = 0;
1073 case 'F': /* Not a legal option */
1074 fprintf(stderr
,"%s: illegal option -- F\n",myname
);
1076 fprintf(stderr
,"\nBecause of numerous incompatibilities, the");
1077 fprintf(stderr
," \"-F\" option has been\n");
1078 fprintf(stderr
,"removed. It has been replaced by the \"figlist\"");
1079 fprintf(stderr
," program, which is now\n");
1080 fprintf(stderr
,"included in the basic FIGlet package. \"figlist\"");
1081 fprintf(stderr
," is also available\n");
1082 fprintf(stderr
,"from http://www.figlet.org/");
1083 fprintf(stderr
,"under UNIX utilities.\n");
1091 if (optind
!=Myargc
) cmdinput
= 1; /* force cmdinput if more arguments */
1092 outlinelenlimit
= outputwidth
-1;
1094 printinfo(infoprint
);
1100 /****************************************************************************
1104 Clears both the input (inchrline) and output (outputline) storage.
1106 ****************************************************************************/
1112 for (i
=0;i
<charheight
;i
++) {
1113 outputline
[i
][0] = '\0';
1120 /****************************************************************************
1124 Reads a font character from the font file, and places it in a
1125 newly-allocated entry in the list.
1127 ****************************************************************************/
1129 void readfontchar(file
,theord
)
1134 char templine
[MAXLEN
+1];
1135 outchr endchar
, outline
[MAXLEN
+1];
1138 fclsave
= fcharlist
;
1139 fcharlist
= (fcharnode
*)myalloc(sizeof(fcharnode
));
1140 fcharlist
->ord
= theord
;
1141 fcharlist
->thechar
= (outchr
**)myalloc(sizeof(outchr
*)*charheight
);
1142 fcharlist
->next
= fclsave
;
1146 for (row
=0;row
<charheight
;row
++) {
1147 if (myfgets(templine
,MAXLEN
,file
)==NULL
) {
1151 utf8_to_wchar(templine
,MAXLEN
,outline
,MAXLEN
,0);
1153 strcpy(outline
,templine
);
1155 k
= STRLEN(outline
)-1;
1156 while (k
>=0 && ISSPACE(outline
[k
])) { /* remove trailing spaces */
1160 endchar
= outline
[k
]; /* remove endmarks */
1161 while (k
>=0 && outline
[k
]==endchar
) {
1165 outline
[k
+1] = '\0';
1166 fcharlist
->thechar
[row
] = (outchr
*)myalloc(sizeof(outchr
)*(STRLEN(outline
)+1));
1167 STRCPY(fcharlist
->thechar
[row
],outline
);
1172 /****************************************************************************
1176 Allocates memory, initializes variables, and reads in the font.
1177 Called near beginning of main().
1179 ****************************************************************************/
1185 int maxlen
,cmtlines
,ffright2left
;
1187 char fileline
[MAXLEN
+1],magicnum
[5];
1190 fontfile
= FIGopen(fontname
,FONTFILESUFFIX
);
1192 if (fontfile
==NULL
) {
1193 fontfile
= FIGopen(fontname
,TOILETFILESUFFIX
);
1194 if(fontfile
) toiletfont
= 1;
1198 if (fontfile
==NULL
) {
1199 fprintf(stderr
,"%s: %s: Unable to open font file\n",myname
,fontname
);
1203 readmagic(fontfile
,magicnum
);
1204 if (myfgets(fileline
,MAXLEN
,fontfile
)==NULL
) {
1207 if (MYSTRLEN(fileline
)>0 ? fileline
[MYSTRLEN(fileline
)-1]!='\n' : 0) {
1208 skiptoeol(fontfile
);
1210 numsread
= sscanf(fileline
,"%*c%c %d %*d %d %d %d %d %d",
1211 &hardblank
,&charheight
,&maxlen
,&smush
,&cmtlines
,
1212 &ffright2left
,&smush2
);
1214 if (maxlen
> MAXLEN
) {
1215 fprintf(stderr
,"%s: %s: character is too wide\n",myname
,fontname
);
1219 if ((!toiletfont
&& strcmp(magicnum
,FONTFILEMAGICNUMBER
)) ||
1220 (toiletfont
&& strcmp(magicnum
,TOILETFILEMAGICNUMBER
)) || numsread
<5) {
1222 if (strcmp(magicnum
,FONTFILEMAGICNUMBER
) || numsread
<5) {
1224 fprintf(stderr
,"%s: %s: Not a FIGlet 2 font file\n",myname
,fontname
);
1227 for (i
=1;i
<=cmtlines
;i
++) {
1228 skiptoeol(fontfile
);
1235 if (numsread
<7) { /* if no smush2, decode smush into smush2 */
1236 if (smush
== 0) smush2
= SM_KERN
;
1237 else if (smush
< 0) smush2
= 0;
1238 else smush2
= (smush
& 31) | SM_SMUSH
;
1249 maxlen
+= 100; /* Give ourselves some extra room */
1251 if (smushoverride
== SMO_NO
)
1253 else if (smushoverride
== SMO_FORCE
)
1254 smushmode
|= smush2
;
1257 right2left
= ffright2left
;
1260 if (justification
<0) {
1261 justification
= 2*right2left
;
1264 /* Allocate "missing" character */
1265 fcharlist
= (fcharnode
*)myalloc(sizeof(fcharnode
));
1267 fcharlist
->thechar
= (outchr
**)myalloc(sizeof(outchr
*)*charheight
);
1268 fcharlist
->next
= NULL
;
1269 for (row
=0;row
<charheight
;row
++) {
1270 fcharlist
->thechar
[row
] = (outchr
*)myalloc(sizeof(outchr
));
1271 fcharlist
->thechar
[row
][0] = '\0';
1273 for (theord
=' ';theord
<='~';theord
++) {
1274 readfontchar(fontfile
,theord
);
1276 for (theord
=0;theord
<=6;theord
++) {
1277 readfontchar(fontfile
,deutsch
[theord
]);
1279 while (myfgets(fileline
,maxlen
+1,fontfile
)==NULL
?0:
1280 sscanf(fileline
,"%li",&theord
)==1) {
1281 readfontchar(fontfile
,theord
);
1287 /****************************************************************************
1291 Allocates & clears outputline, inchrline. Sets inchrlinelenlimit.
1292 Called near beginning of main().
1294 ****************************************************************************/
1300 outputline
= (outchr
**)myalloc(sizeof(outchr
*)*charheight
);
1301 for (row
=0;row
<charheight
;row
++) {
1302 outputline
[row
] = (outchr
*)myalloc(sizeof(outchr
)*(outlinelenlimit
+1));
1304 inchrlinelenlimit
= outputwidth
*4+100;
1305 inchrline
= (inchr
*)myalloc(sizeof(inchr
)*(inchrlinelenlimit
+1));
1310 /****************************************************************************
1314 Sets currchar to point to the font entry for the given character.
1315 Sets currcharwidth to the width of this character.
1317 ****************************************************************************/
1324 for (charptr
=fcharlist
;charptr
==NULL
?0:charptr
->ord
!=c
;
1325 charptr
=charptr
->next
) ;
1326 if (charptr
!=NULL
) {
1327 currchar
= charptr
->thechar
;
1330 for (charptr
=fcharlist
;charptr
==NULL
?0:charptr
->ord
!=0;
1331 charptr
=charptr
->next
) ;
1332 currchar
= charptr
->thechar
;
1334 previouscharwidth
= currcharwidth
;
1335 currcharwidth
= STRLEN(currchar
[0]);
1339 /****************************************************************************
1343 Given 2 characters, attempts to smush them into 1, according to
1344 smushmode. Returns smushed character or '\0' if no smushing can be
1347 smushmode values are sum of following (all values smush blanks):
1348 1: Smush equal chars (not hardblanks)
1349 2: Smush '_' with any char in hierarchy below
1350 4: hierarchy: "|", "/\", "[]", "{}", "()", "<>"
1351 Each class in hier. can be replaced by later class.
1352 8: [ + ] -> |, { + } -> |, ( + ) -> |
1353 16: / + \ -> X, > + < -> X (only in that order)
1354 32: hardblank + hardblank -> hardblank
1356 ****************************************************************************/
1358 outchr
smushem(lch
,rch
)
1361 if (lch
==' ') return rch
;
1362 if (rch
==' ') return lch
;
1364 if (previouscharwidth
<2 || currcharwidth
<2) return '\0';
1365 /* Disallows overlapping if the previous character */
1366 /* or the current character has a width of 1 or zero. */
1368 if ((smushmode
& SM_SMUSH
) == 0) return '\0'; /* kerning */
1370 if ((smushmode
& 63) == 0) {
1371 /* This is smushing by universal overlapping. */
1372 if (lch
==' ') return rch
;
1373 if (rch
==' ') return lch
;
1374 if (lch
==hardblank
) return rch
;
1375 if (rch
==hardblank
) return lch
;
1376 /* Above four lines ensure overlapping preference to */
1377 /* visible characters. */
1378 if (right2left
==1) return lch
;
1379 /* Above line ensures that the dominant (foreground) */
1380 /* fig-character for overlapping is the latter in the */
1381 /* user's text, not necessarily the rightmost character. */
1383 /* Occurs in the absence of above exceptions. */
1386 if (smushmode
& SM_HARDBLANK
) {
1387 if (lch
==hardblank
&& rch
==hardblank
) return lch
;
1390 if (lch
==hardblank
|| rch
==hardblank
) return '\0';
1392 if (smushmode
& SM_EQUAL
) {
1393 if (lch
==rch
) return lch
;
1396 if (smushmode
& SM_LOWLINE
) {
1397 if (lch
=='_' && strchr("|/\\[]{}()<>",rch
)) return rch
;
1398 if (rch
=='_' && strchr("|/\\[]{}()<>",lch
)) return lch
;
1401 if (smushmode
& SM_HIERARCHY
) {
1402 if (lch
=='|' && strchr("/\\[]{}()<>",rch
)) return rch
;
1403 if (rch
=='|' && strchr("/\\[]{}()<>",lch
)) return lch
;
1404 if (strchr("/\\",lch
) && strchr("[]{}()<>",rch
)) return rch
;
1405 if (strchr("/\\",rch
) && strchr("[]{}()<>",lch
)) return lch
;
1406 if (strchr("[]",lch
) && strchr("{}()<>",rch
)) return rch
;
1407 if (strchr("[]",rch
) && strchr("{}()<>",lch
)) return lch
;
1408 if (strchr("{}",lch
) && strchr("()<>",rch
)) return rch
;
1409 if (strchr("{}",rch
) && strchr("()<>",lch
)) return lch
;
1410 if (strchr("()",lch
) && strchr("<>",rch
)) return rch
;
1411 if (strchr("()",rch
) && strchr("<>",lch
)) return lch
;
1414 if (smushmode
& SM_PAIR
) {
1415 if (lch
=='[' && rch
==']') return '|';
1416 if (rch
=='[' && lch
==']') return '|';
1417 if (lch
=='{' && rch
=='}') return '|';
1418 if (rch
=='{' && lch
=='}') return '|';
1419 if (lch
=='(' && rch
==')') return '|';
1420 if (rch
=='(' && lch
==')') return '|';
1423 if (smushmode
& SM_BIGX
) {
1424 if (lch
=='/' && rch
=='\\') return '|';
1425 if (rch
=='/' && lch
=='\\') return 'Y';
1426 if (lch
=='>' && rch
=='<') return 'X';
1427 /* Don't want the reverse of above to give 'X'. */
1434 /****************************************************************************
1438 Returns the maximum amount that the current character can be smushed
1439 into the current line.
1441 ****************************************************************************/
1446 int row
,linebd
,charbd
;
1449 if ((smushmode
& (SM_SMUSH
| SM_KERN
)) == 0) {
1452 maxsmush
= currcharwidth
;
1453 for (row
=0;row
<charheight
;row
++) {
1455 if (maxsmush
>STRLEN(outputline
[row
])) {
1456 maxsmush
=STRLEN(outputline
[row
]);
1458 for (charbd
=STRLEN(currchar
[row
]);
1459 ch1
=currchar
[row
][charbd
],(charbd
>0&&(!ch1
||ch1
==' '));charbd
--) ;
1460 for (linebd
=0;ch2
=outputline
[row
][linebd
],ch2
==' ';linebd
++) ;
1461 amt
= linebd
+currcharwidth
-1-charbd
;
1464 for (linebd
=STRLEN(outputline
[row
]);
1465 ch1
= outputline
[row
][linebd
],(linebd
>0&&(!ch1
||ch1
==' '));linebd
--) ;
1466 for (charbd
=0;ch2
=currchar
[row
][charbd
],ch2
==' ';charbd
++) ;
1467 amt
= charbd
+outlinelen
-1-linebd
;
1469 if (!ch1
||ch1
==' ') {
1473 if (smushem(ch1
,ch2
)!='\0') {
1485 /****************************************************************************
1489 Attempts to add the given character onto the end of the current line.
1490 Returns 1 if this can be done, 0 otherwise.
1492 ****************************************************************************/
1497 int smushamount
,row
,k
,column
;
1501 smushamount
= smushamt();
1502 if (outlinelen
+currcharwidth
-smushamount
>outlinelenlimit
1503 ||inchrlinelen
+1>inchrlinelenlimit
) {
1507 templine
= (outchr
*)myalloc(sizeof(outchr
)*(outlinelenlimit
+1));
1508 for (row
=0;row
<charheight
;row
++) {
1510 STRCPY(templine
,currchar
[row
]);
1511 for (k
=0;k
<smushamount
;k
++) {
1512 templine
[currcharwidth
-smushamount
+k
] =
1513 smushem(templine
[currcharwidth
-smushamount
+k
],outputline
[row
][k
]);
1515 STRCAT(templine
,outputline
[row
]+smushamount
);
1516 STRCPY(outputline
[row
],templine
);
1519 for (k
=0;k
<smushamount
;k
++) {
1520 column
= outlinelen
-smushamount
+k
;
1524 outputline
[row
][column
] =
1525 smushem(outputline
[row
][column
],currchar
[row
][k
]);
1527 STRCAT(outputline
[row
],currchar
[row
]+smushamount
);
1531 outlinelen
= STRLEN(outputline
[0]);
1532 inchrline
[inchrlinelen
++] = c
;
1537 /****************************************************************************
1541 Prints out the given null-terminated string, substituting blanks
1542 for hardblanks. If outputwidth is 1, prints the entire string;
1543 otherwise prints at most outputwidth-1 characters. Prints a newline
1544 at the end of the string. The string is left-justified, centered or
1545 right-justified (taking outputwidth as the screen width) if
1546 justification is 0, 1 or 2, respectively.
1548 ****************************************************************************/
1550 void putstring(string
)
1560 len
= STRLEN(string
);
1561 if (outputwidth
>1) {
1562 if (len
>outputwidth
-1) {
1563 len
= outputwidth
-1;
1565 if (justification
>0) {
1566 for (i
=1;(3-justification
)*i
+len
+justification
-2<outputwidth
;i
++) {
1571 for (i
=0;i
<len
;i
++) {
1575 size
= wchar_to_utf8(wc
,1,c
,10,0);
1577 if(c
[0]==hardblank
) {
1584 putchar(string
[i
]==hardblank
?' ':string
[i
]);
1591 /****************************************************************************
1595 Prints outputline using putstring, then clears the current line.
1597 ****************************************************************************/
1603 for (i
=0;i
<charheight
;i
++) {
1604 putstring(outputline
[i
]);
1610 /****************************************************************************
1614 Splits inchrline at the last word break (bunch of consecutive blanks).
1615 Makes a new line out of the first part and prints it using
1616 printline. Makes a new line out of the second part and returns.
1618 ****************************************************************************/
1622 int i
,gotspace
,lastspace
,len1
,len2
;
1623 inchr
*part1
,*part2
;
1625 part1
= (inchr
*)myalloc(sizeof(inchr
)*(inchrlinelen
+1));
1626 part2
= (inchr
*)myalloc(sizeof(inchr
)*(inchrlinelen
+1));
1628 lastspace
= inchrlinelen
-1;
1629 for (i
=inchrlinelen
-1;i
>=0;i
--) {
1630 if (!gotspace
&& inchrline
[i
]==' ') {
1634 if (gotspace
&& inchrline
[i
]!=' ') {
1639 len2
= inchrlinelen
-lastspace
-1;
1640 for (i
=0;i
<len1
;i
++) {
1641 part1
[i
] = inchrline
[i
];
1643 for (i
=0;i
<len2
;i
++) {
1644 part2
[i
] = inchrline
[lastspace
+1+i
];
1647 for (i
=0;i
<len1
;i
++) {
1651 for (i
=0;i
<len2
;i
++) {
1659 /****************************************************************************
1663 Given an input character (type inchr), executes re-mapping commands
1664 read from control files. Returns re-mapped character (inchr).
1666 ****************************************************************************/
1668 inchr
handlemapping(c
)
1674 while (cmptr
!=NULL
) {
1675 if (cmptr
->thecommand
?
1676 (c
>= cmptr
->rangelo
&& c
<= cmptr
->rangehi
) : 0) {
1678 while(cmptr
!=NULL
? cmptr
->thecommand
: 0) {
1689 /****************************************************************************
1693 Replacement to getchar().
1694 Acts exactly like getchar if -A is NOT specified,
1695 else obtains input from All remaining command line words.
1697 ****************************************************************************/
1701 extern int optind
; /* current argv[] element under study */
1702 static int AgetMode
= 0; /* >= 0 for displacement into argv[n], <0 EOF */
1703 char *arg
; /* pointer to active character */
1704 int c
; /* current character */
1706 if ( ! cmdinput
) /* is -A active? */
1707 return( getchar() ); /* no: return stdin character */
1709 if ( AgetMode
< 0 || optind
>= Myargc
) /* EOF is sticky: */
1710 return( EOF
); /* **ensure it now and forever more */
1712 /* find next character */
1713 arg
= Myargv
[optind
]; /* pointer to active arg */
1714 c
= arg
[AgetMode
++]&0xFF; /* get appropriate char of arg */
1716 if ( ! c
) /* at '\0' that terminates word? */
1717 { /* at end of word: return ' ' if normal word, '\n' if empty */
1718 c
= ' '; /* suppose normal word and return blank */
1719 if ( AgetMode
== 1 ) /* if ran out in very 1st char, force \n */
1720 c
= '\n'; /* (allows "hello '' world" to do \n at '') */
1721 AgetMode
= 0; /* return to char 0 in NEXT word */
1722 if ( ++optind
>= Myargc
) /* run up word count and check if at "EOF" */
1723 { /* just ran out of arguments */
1724 c
= EOF
; /* return EOF */
1725 AgetMode
= -1; /* ensure all future returns return EOF */
1729 return( c
); /* return appropriate character */
1731 } /* end: Agetchar() */
1734 /****************************************************************************
1738 Called by getinchr. Interprets ISO 2022 sequences
1740 ******************************************************************************/
1750 if (ch
== EOF
) return ch
;
1751 if (ch
== 27) ch
= Agetchar() + 0x100; /* ESC x */
1752 if (ch
== 0x100 + '$') ch
= Agetchar() + 0x200; /* ESC $ x */
1754 case 14: /* invoke G1 into GL */
1757 case 15: /* invoke G0 into GL */
1760 case 142: case 'N' + 0x100: /* invoke G2 into GL for next char */
1761 save_gl
= gl
; save_gr
= gr
;
1764 gl
= save_gl
; gr
= save_gr
;
1766 case 143: case 'O' + 0x100: /* invoke G3 into GL for next char */
1767 save_gl
= gl
; save_gr
= gr
;
1770 gl
= save_gl
; gr
= save_gr
;
1772 case 'n' + 0x100: /* invoke G2 into GL */
1775 case 'o' + 0x100: /* invoke G3 into GL */
1778 case '~' + 0x100: /* invoke G1 into GR */
1781 case '}' + 0x100: /* invoke G2 into GR */
1784 case '|' + 0x100: /* invoke G3 into GR */
1787 case '(' + 0x100: /* set G0 to 94-char set */
1789 if (ch
== 'B') ch
= 0; /* ASCII */
1793 case ')' + 0x100: /* set G1 to 94-char set */
1795 if (ch
== 'B') ch
= 0;
1799 case '*' + 0x100: /* set G2 to 94-char set */
1801 if (ch
== 'B') ch
= 0;
1805 case '+' + 0x100: /* set G3 to 94-char set */
1807 if (ch
== 'B') ch
= 0;
1811 case '-' + 0x100: /* set G1 to 96-char set */
1813 if (ch
== 'A') ch
= 0; /* Latin-1 top half */
1814 gn
[1] = (ch
<< 16) | 0x80;
1817 case '.' + 0x100: /* set G2 to 96-char set */
1819 if (ch
== 'A') ch
= 0;
1820 gn
[2] = (ch
<< 16) | 0x80;
1823 case '/' + 0x100: /* set G3 to 96-char set */
1825 if (ch
== 'A') ch
= 0;
1826 gn
[3] = (ch
<< 16) | 0x80;
1829 case '(' + 0x200: /* set G0 to 94 x 94 char set */
1834 case ')' + 0x200: /* set G1 to 94 x 94 char set */
1839 case '*' + 0x200: /* set G2 to 94 x 94 char set */
1844 case '+' + 0x200: /* set G3 to 94 x 94 char set */
1850 if (ch
& 0x200) { /* set G0 to 94 x 94 char set (deprecated) */
1851 gn
[0] = (ch
& ~0x200) << 16;
1857 if (ch
>= 0x21 && ch
<= 0x7E) { /* process GL */
1860 return gn
[gl
] | (ch
<< 8) | ch2
;
1862 else return gn
[gl
] | ch
;
1864 else if (ch
>= 0xA0 && ch
<= 0xFF) { /* process GR */
1867 return gn
[gr
] | (ch
<< 8) | ch2
;
1869 else return gn
[gr
] | (ch
& ~0x80);
1874 /****************************************************************************
1878 Called by main. Pushes back an "inchr" to be read by getinchr
1881 ******************************************************************************/
1882 inchr getinchr_buffer
;
1888 getinchr_buffer
= c
;
1893 /*****************************************************************************
1897 Called by main. Processes multibyte characters. Invokes Agetchar.
1898 If multibyte = 0, ISO 2022 mode (see iso2022 routine).
1899 If multibyte = 1, double-byte mode (0x00-0x7f bytes are characters,
1900 0x80-0xFF bytes are first byte of a double-byte character).
1901 If multibyte = 2, Unicode UTF-8 mode (0x00-0x7F bytes are characters,
1902 0x80-0xBF bytes are nonfirst byte of a multibyte character,
1903 0xC0-0xFD bytes are first byte of a multibyte character,
1904 0xFE-0xFF bytes are errors (all errors return code 0x0080)).
1905 If multibyte = 3, HZ mode ("~{" starts double-byte mode, "}~" ends it,
1906 "~~" is a tilde, "~x" for all other x is ignored).
1907 If multibyte = 4, Shift-JIS mode (0x80-0x9F and 0xE0-0xEF are first byte
1908 of a double-byte character, all other bytes are characters).
1911 *****************************************************************************/
1915 int ch
, ch2
, ch3
, ch4
, ch5
, ch6
;
1917 if (getinchr_flag
) {
1919 return getinchr_buffer
;
1923 case 0: /* single-byte */
1927 if ((ch
>= 0x80 && ch
<= 0x9F) ||
1928 (ch
>= 0xE0 && ch
<= 0xEF)) {
1929 ch
= (ch
<< 8) + Agetchar();
1934 if (ch
< 0x80) return ch
; /* handles EOF, too */
1935 if (ch
< 0xC0 || ch
> 0xFD)
1936 return 0x0080; /* illegal first character */
1937 ch2
= Agetchar() & 0x3F;
1938 if (ch
< 0xE0) return ((ch
& 0x1F) << 6) + ch2
;
1939 ch3
= Agetchar() & 0x3F;
1941 return ((ch
& 0x0F) << 12) + (ch2
<< 6) + ch3
;
1942 ch4
= Agetchar() & 0x3F;
1944 return ((ch
& 0x07) << 18) + (ch2
<< 12) + (ch3
<< 6) + ch4
;
1945 ch5
= Agetchar() & 0x3F;
1947 return ((ch
& 0x03) << 24) + (ch2
<< 18) + (ch3
<< 12) +
1949 ch6
= Agetchar() & 0x3F;
1950 return ((ch
& 0x01) << 30) + (ch2
<< 24) + (ch3
<< 18) +
1951 (ch4
<< 12) + (ch5
<< 6) + ch6
;
1954 if (ch
== EOF
) return ch
;
1956 ch
= (ch
<< 8) + Agetchar();
1957 if (ch
== ('}' << 8) + '~') {
1963 else if (ch
== '~') {
1969 else if (ch
== '~') {
1977 case 4: /* Shift-JIS */
1979 if ((ch
>= 0x80 && ch
<= 0x9F) ||
1980 (ch
>= 0xE0 && ch
<= 0xEF)) {
1981 ch
= (ch
<< 8) + Agetchar();
1989 /****************************************************************************
1993 The main program, of course.
1994 Reads characters 1 by 1 from stdin, and makes lines out of them using
1995 addchar. Handles line breaking, (which accounts for most of the
1996 complexity in this function).
1998 ****************************************************************************/
2006 int last_was_eol_flag
;
2007 /*---------------------------------------------------------------------------
2009 -1: /^$/ and blanks are to be absorbed (when line break was forced
2010 by a blank or character larger than outlinelenlimit)
2011 0: /^ *$/ and blanks are not to be absorbed
2012 1: /[^ ]$/ no word break yet
2014 3: /[^ ]$/ had a word break
2015 ---------------------------------------------------------------------------*/
2027 last_was_eol_flag
= 0;
2033 while ((c
= getinchr())!=EOF
) {
2035 if (c
=='\n'&¶graphflag
&&!last_was_eol_flag
) {
2036 ungetinchr(c2
= getinchr());
2037 c
= ((isascii(c2
)&&isspace(c2
))?'\n':' ');
2039 last_was_eol_flag
= (isascii(c
)&&isspace(c
)&&c
!='\t'&&c
!=' ');
2042 if (c
>='[' && c
<=']') {
2045 else if (c
>='{' && c
<= '~') {
2046 c
= deutsch
[c
-'{'+3];
2050 c
= handlemapping(c
);
2052 if (isascii(c
)&&isspace(c
)) {
2053 c
= (c
=='\t'||c
==' ') ? ' ' : '\n';
2056 if ((c
>'\0' && c
<' ' && c
!='\n') || c
==127) continue;
2059 Note: The following code is complex and thoroughly tested.
2060 Be careful when modifying!
2066 if (wordbreakmode
== -1) {
2082 else if (addchar(c
)) {
2084 wordbreakmode
= (wordbreakmode
>=2)?3:1;
2087 wordbreakmode
= (wordbreakmode
>0)?2:0;
2091 else if (outlinelen
==0) {
2092 for (i
=0;i
<charheight
;i
++) {
2093 if (right2left
&& outputwidth
>1) {
2094 putstring(currchar
[i
]+STRLEN(currchar
[i
])-outlinelenlimit
);
2097 putstring(currchar
[i
]);
2104 if (wordbreakmode
==2) {
2114 if (wordbreakmode
>=2) {
2120 wordbreakmode
= (wordbreakmode
==3)?1:0;
2124 } while (char_not_added
);
2127 if (outlinelen
!=0) {