1 /****************************************************************************
2 
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 ****************************************************************************/
13 
14 #define DATE "31 May 2012"
15 #define VERSION "2.2.5"
16 #define VERSION_INT 20205
17 
18 /* FIGlet (Frank, Ian & Glenn's Letters) */
19 /* by Glenn Chappell */
20 /* Apr 1991 */
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 */
43 
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
51     DEFAULTFONTDIR.
52 ---------------------------------------------------------------------------*/
53 #ifndef DEFAULTFONTDIR
54 #define DEFAULTFONTDIR "fonts"
55 #endif
56 #ifndef DEFAULTFONTFILE
57 #define DEFAULTFONTFILE "standard.flf"
58 #endif
59 
60 #include <stdio.h>
61 #ifdef __STDC__
62 #include <stdlib.h>
63 #endif
64 #include <string.h>
65 #include <ctype.h>
66 #include <sys/stat.h>
67 #include <fcntl.h>     /* Needed for get_columns */
68 
69 #ifdef unix
70 #include <unistd.h>
71 #include <sys/ioctl.h> /* Needed for get_columns */
72 #endif
73 
74 #ifdef TLF_FONTS
75 #include <wchar.h>
76 #include <wctype.h>
77 #include "utf8.h"
78 #endif
79 
80 #include "zipio.h"     /* Package for reading compressed files */
81 
82 #define MYSTRLEN(x) ((int)strlen(x)) /* Eliminate ANSI problem */
83 
84 #define DIRSEP '/'
85 #define DIRSEP2 '\\'
86 /* Leave alone for Unix and MS-DOS/Windows!
87 Note: '/' also used in filename in get_columns(). */
88 
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 */
97 
98 /* Add support for Sam Hocevar's TOIlet fonts */
99 #ifdef TLF_FONTS
100 #define TOILETFILESUFFIX ".tlf"
101 #define TOILETFILEMAGICNUMBER "tlf2"
102 #define TSUFFIXLEN MYSTRLEN(TOILETFILESUFFIX)
103 
104 int toiletfont;	/* true if font is a TOIlet TLF font */
105 #endif
106 
107 
108 /****************************************************************************
109 
110   Globals dealing with chars that are read
111 
112 ****************************************************************************/
113 
114 typedef long inchr; /* "char" read from stdin */
115 
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
128   */
129 
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 */
135 
136 int Myargc;  /* to avoid passing around argc and argv */
137 char **Myargv;
138 
139 /****************************************************************************
140 
141   Globals dealing with chars that are written
142 
143 ****************************************************************************/
144 
145 #ifdef TLF_FONTS
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)
151 #else
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)
157 #endif
158 
159 typedef struct fc {
160   inchr ord;
161   outchr **thechar;  /* Alloc'd char thechar[charheight][]; */
162   struct fc *next;
163   } fcharnode;
164 
165 fcharnode *fcharlist;
166 outchr **currchar;
167 int currcharwidth;
168 int previouscharwidth;
169 outchr **outputline;   /* Alloc'd char outputline[charheight][outlinelenlimit+1]; */
170 int outlinelen;
171 
172 
173 /****************************************************************************
174 
175   Globals dealing with command file storage
176 
177 ****************************************************************************/
178 
179 typedef struct cfn {
180   char *thename;
181   struct cfn *next;
182   } cfnamenode;
183 
184 cfnamenode *cfilelist,**cfilelistend;
185 
186 typedef struct cm {
187   int thecommand;
188   inchr rangelo;
189   inchr rangehi;
190   inchr offset;
191   struct cm *next;
192   } comnode;
193 
194 comnode *commandlist,**commandlistend;
195 
196 /****************************************************************************
197 
198   Globals affected by command line options
199 
200 ****************************************************************************/
201 
202 int deutschflag,justification,paragraphflag,right2left,multibyte;
203 int cmdinput;
204 
205 #define SM_SMUSH 128
206 #define SM_KERN 64
207 #define SM_EQUAL 1
208 #define SM_LOWLINE 2
209 #define SM_HIERARCHY 4
210 #define SM_PAIR 8
211 #define SM_BIGX 16
212 #define SM_HARDBLANK 32
213 
214 int smushmode;
215 
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 */
219 
220 int smushoverride;
221 
222 int outputwidth;
223 int outlinelenlimit;
224 char *fontdirname,*fontname;
225 
226 
227 /****************************************************************************
228 
229   Globals read from font file
230 
231 ****************************************************************************/
232 
233 char hardblank;
234 int charheight;
235 
236 
237 /****************************************************************************
238 
239   Name of program, used in error messages
240 
241 ****************************************************************************/
242 
243 char *myname;
244 
245 
246 #ifdef TIOCGWINSZ
247 /****************************************************************************
248 
249   get_columns
250 
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
255 
256 ****************************************************************************/
257 
get_columns()258 int get_columns()
259 {
260   struct winsize ws;
261   int fd,result;
262 
263   if ((fd = open("/dev/tty",O_WRONLY))<0) return -1;
264   result = ioctl(fd,TIOCGWINSZ,&ws);
265   close(fd);
266   return result?-1:ws.ws_col;
267 }
268 #endif /* ifdef TIOCGWINSZ */
269 
270 
271 /****************************************************************************
272 
273   myalloc
274 
275   Calls malloc.  If malloc returns error, prints error message and
276   quits.
277 
278 ****************************************************************************/
279 
280 #ifdef __STDC__
myalloc(size_t size)281 char *myalloc(size_t size)
282 #else
283 char *myalloc(size)
284 int size;
285 #endif
286 {
287   char *ptr;
288 #ifndef __STDC__
289   extern void *malloc();
290 #endif
291 
292   if ((ptr = (char*)malloc(size))==NULL) {
293     fprintf(stderr,"%s: Out of memory\n",myname);
294     exit(1);
295     }
296   else {
297     return ptr;
298     }
299 }
300 
301 
302 /****************************************************************************
303 
304   hasdirsep
305 
306   Returns true if s1 contains a DIRSEP or DIRSEP2 character.
307 
308 ****************************************************************************/
309 
hasdirsep(s1)310 int hasdirsep(s1)
311 char *s1;
312 {
313   if (strchr(s1, DIRSEP)) return 1;
314   else if (strchr(s1, DIRSEP2)) return 1;
315   else return 0;
316 }
317 
318 /****************************************************************************
319 
320   suffixcmp
321 
322   Returns true if s2 is a suffix of s1; uses case-blind comparison.
323 
324 ****************************************************************************/
325 
suffixcmp(s1,s2)326 int suffixcmp(s1, s2)
327 char *s1;
328 char *s2;
329 {
330   int len1, len2;
331 
332   len1 = MYSTRLEN(s1);
333   len2 = MYSTRLEN(s2);
334   if (len2 > len1) return 0;
335   s1 += len1 - len2;
336   while (*s1) {
337     if (tolower(*s1) != tolower(*s2)) return 0;
338     s1++;
339     s2++;
340     }
341   return 1;
342 }
343 
344 /****************************************************************************
345 
346   skiptoeol
347 
348   Skips to the end of a line, given a stream.  Handles \r, \n, or \r\n.
349 
350 ****************************************************************************/
351 
skiptoeol(fp)352 void skiptoeol(fp)
353 ZFILE *fp;
354 {
355   int dummy;
356 
357   while (dummy=Zgetc(fp),dummy!=EOF) {
358     if (dummy == '\n') return;
359     if (dummy == '\r') {
360       dummy = Zgetc(fp);
361       if (dummy != EOF && dummy != '\n') Zungetc(dummy,fp);
362       return;
363       }
364   }
365 }
366 
367 
368 /****************************************************************************
369 
370   myfgets
371 
372   Local version of fgets.  Handles \r, \n, and \r\n terminators.
373 
374 ****************************************************************************/
375 
myfgets(line,maxlen,fp)376 char *myfgets(line,maxlen,fp)
377 char *line;
378 int maxlen;
379 ZFILE *fp;
380 {
381   int c = 0;
382   char *p;
383 
384   p = line;
385   while((c=Zgetc(fp))!=EOF&&maxlen) {
386     *p++ = c;
387     maxlen--;
388     if (c=='\n') break;
389     if (c=='\r') {
390       c = Zgetc(fp);
391       if (c != EOF && c != '\n') Zungetc(c,fp);
392       *(p-1) = '\n';
393       break;
394       }
395     }
396   *p = 0;
397   return (c==EOF) ? NULL : line;
398 }
399 
400 
401 /****************************************************************************
402 
403   usageerr
404 
405   Prints "Usage: ...." line to the given stream.
406 
407 ****************************************************************************/
408 
printusage(out)409 void printusage(out)
410 FILE *out;
411 {
412   fprintf(out,
413     "Usage: %s [ -cklnoprstvxDELNRSWX ] [ -d fontdirectory ]\n",
414     myname);
415   fprintf(out,
416     "              [ -f fontfile ] [ -m smushmode ] [ -w outputwidth ]\n");
417   fprintf(out,
418     "              [ -C controlfile ] [ -I infocode ] [ message ]\n");
419 }
420 
421 
422 /****************************************************************************
423 
424   printinfo
425 
426   Prints version and copyright message, or utility information.
427 
428 ****************************************************************************/
429 
printinfo(infonum)430 void printinfo(infonum)
431 int infonum;
432 {
433   switch (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");
446       printusage(stdout);
447       break;
448     case 1: /* Version (integer) */
449       printf("%d\n",VERSION_INT);
450       break;
451     case 2: /* Font directory */
452       printf("%s\n",fontdirname);
453       break;
454     case 3: /* Font */
455       printf("%s\n",fontname);
456       break;
457     case 4: /* Outputwidth */
458       printf("%d\n",outputwidth);
459       break;
460     case 5: /* Font formats */
461       printf("%s", FONTFILEMAGICNUMBER);
462 #ifdef TLF_FONTS
463       printf(" %s", TOILETFILEMAGICNUMBER);
464 #endif
465       printf("\n");
466     }
467 }
468 
469 
470 /****************************************************************************
471 
472   readmagic
473 
474   Reads a four-character magic string from a stream.
475 
476 ****************************************************************************/
readmagic(fp,magic)477 void readmagic(fp,magic)
478 ZFILE *fp;
479 char *magic;
480 {
481   int i;
482 
483   for (i=0;i<4;i++) {
484     magic[i] = Zgetc(fp);
485     }
486   magic[4] = 0;
487   }
488 
489 /****************************************************************************
490 
491   skipws
492 
493   Skips whitespace characters from a stream.
494 
495 ****************************************************************************/
skipws(fp)496 void skipws(fp)
497 ZFILE *fp;
498 {
499   int c;
500   while (c=Zgetc(fp),isascii(c)&&isspace(c)) ;
501   Zungetc(c,fp);
502   }
503 
504 /****************************************************************************
505 
506   readnum
507 
508   Reads a number from a stream.  Accepts "0" prefix for octal and
509   "0x" or "0X" for hexadecimal.  Ignores leading whitespace.
510 
511 ****************************************************************************/
readnum(fp,nump)512 void readnum(fp,nump)
513 ZFILE *fp;
514 inchr *nump;
515 {
516   int acc = 0;
517   char *p;
518   int c;
519   int base;
520   int sign = 1;
521   char digits[] = "0123456789ABCDEF";
522 
523   skipws(fp);
524   c = Zgetc(fp);
525   if (c=='-') {
526     sign = -1;
527     }
528   else {
529     Zungetc(c,fp);
530     }
531   c = Zgetc(fp);
532   if (c=='0') {
533      c = Zgetc(fp);
534      if (c=='x'||c=='X') {
535        base = 16;
536        }
537      else {
538        base = 8;
539        Zungetc(c,fp);
540        }
541     }
542   else {
543     base = 10;
544     Zungetc(c,fp);
545     }
546 
547   while((c=Zgetc(fp))!=EOF) {
548     c=toupper(c);
549     p=strchr(digits,c);
550     if (!p) {
551       Zungetc(c,fp);
552       *nump = acc * sign;
553       return;
554       }
555     acc = acc*base+(p-digits);
556     }
557   *nump = acc * sign;
558   }
559 
560 /****************************************************************************
561 
562   readTchar
563 
564   Reads a control file "T" command character specification.
565 
566   Character is a single byte, an escape sequence, or
567   an escaped numeric.
568 
569 ****************************************************************************/
570 
readTchar(fp)571 inchr readTchar(fp)
572 ZFILE *fp;
573 {
574   inchr thechar;
575   char next;
576 
577   thechar=Zgetc(fp);
578   if (thechar=='\n' || thechar=='\r') { /* Handle badly-formatted file */
579     Zungetc(thechar,fp);
580     return '\0';
581     }
582   if (thechar!='\\') return thechar;
583   next=Zgetc(fp);
584   switch(next) {
585     case 'a':
586       return 7;
587     case 'b':
588       return 8;
589     case 'e':
590       return 27;
591     case 'f':
592       return 12;
593     case 'n':
594       return 10;
595     case 'r':
596       return 13;
597     case 't':
598       return 9;
599     case 'v':
600       return 11;
601     default:
602       if (next=='-' || next=='x' || (next>='0' && next<='9')) {
603         Zungetc(next,fp);
604         readnum(fp,&thechar);
605         return thechar;
606         }
607       return next;
608     }
609 }
610 
611 /****************************************************************************
612 
613   charsetname
614 
615   Get a Tchar representing a charset name, or 0 if none available.
616   Called in getcharset().
617 
618 ****************************************************************************/
619 
charsetname(fp)620 inchr charsetname(fp)
621 ZFILE *fp;
622 {
623   inchr result;
624 
625   result = readTchar(fp);
626   if (result == '\n' || result == '\r') {
627     result = 0;
628     Zungetc(result,fp);
629     }
630   return result;
631   }
632 
633 /****************************************************************************
634 
635   charset
636 
637   Processes "g[0123]" character set specifier
638   Called in readcontrol().
639 
640 ****************************************************************************/
641 
charset(n,controlfile)642 void charset(n, controlfile)
643 int n;
644 ZFILE *controlfile;
645 {
646   int ch;
647 
648   skipws(controlfile);
649   if (Zgetc(controlfile) != '9') {
650     skiptoeol(controlfile);
651     return;
652     }
653   ch = Zgetc(controlfile);
654   if (ch == '6') {
655      gn[n] = 65536L * charsetname(controlfile) + 0x80;
656      gndbl[n] = 0;
657      skiptoeol(controlfile);
658      return;
659      }
660   if (ch != '4') {
661     skiptoeol(controlfile);
662     return;
663     }
664   ch = Zgetc(controlfile);
665   if (ch == 'x') {
666      if (Zgetc(controlfile) != '9') {
667        skiptoeol(controlfile);
668        return;
669        }
670      if (Zgetc(controlfile) != '4') {
671        skiptoeol(controlfile);
672        return;
673        }
674      skipws(controlfile);
675      gn[n] = 65536L * charsetname(controlfile);
676      gndbl[n] = 1;
677      skiptoeol(controlfile);
678      return;
679      }
680   Zungetc(ch, controlfile);
681   skipws(controlfile);
682   gn[n] = 65536L * charsetname(controlfile);
683   gndbl[n] = 0;
684   return;
685   }
686 
687 /****************************************************************************
688 
689   FIGopen
690 
691   Given a FIGlet font or control file name and suffix, return the file
692   or NULL if not found
693 
694 ****************************************************************************/
695 
FIGopen(name,suffix)696 ZFILE *FIGopen(name,suffix)
697 char *name;
698 char *suffix;
699 {
700   char *fontpath;
701   ZFILE *fontfile;
702   struct stat st;
703   int namelen;
704 
705   namelen = MYSTRLEN(fontdirname);
706   fontpath = (char*)alloca(sizeof(char)*
707     (namelen+MYSTRLEN(name)+MYSTRLEN(suffix)+2));
708   fontfile = NULL;
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;
716     }
717   /* just append suffix */
718   strcpy(fontpath,name);
719   strcat(fontpath,suffix);
720   if(stat(fontpath,&st)==0) goto ok;
721 
722   return NULL;
723 
724 ok:
725   fontfile = Zopen(fontpath,"rb");
726   return fontfile;
727 }
728 
729 /****************************************************************************
730 
731   readcontrol
732 
733   Allocates memory and reads in the given control file.
734   Called in readcontrolfiles().
735 
736 ****************************************************************************/
737 
readcontrol(controlname)738 void readcontrol(controlname)
739 char *controlname;
740 {
741   inchr firstch,lastch;
742   char dashcheck;
743   inchr offset;
744   int command;
745   ZFILE *controlfile;
746 
747   controlfile = FIGopen(controlname,CONTROLFILESUFFIX);
748 
749   if (controlfile==NULL) {
750     fprintf(stderr,"%s: %s: Unable to open control file\n",myname,
751       controlname);
752     exit(1);
753     }
754 
755   (*commandlistend) = (comnode*)myalloc(sizeof(comnode));
756   (*commandlistend)->thecommand = 0; /* Begin with a freeze command */
757   commandlistend = &(*commandlistend)->next;
758   (*commandlistend) = NULL;
759 
760   while(command=Zgetc(controlfile),command!=EOF) {
761     switch (command) {
762       case 't': /* Translate */
763         skipws(controlfile);
764         firstch=readTchar(controlfile);
765         if ((dashcheck=Zgetc(controlfile))=='-') {
766           lastch=readTchar(controlfile);
767           }
768         else {
769           Zungetc(dashcheck,controlfile);
770           lastch=firstch;
771           }
772         skipws(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;
782         break;
783       case '0': case '1': case '2': case '3': case '4':
784       case '5': case '6': case '7': case '8': case '9':
785       case '-':
786                 /* Mapping table entry */
787         Zungetc(command,controlfile);
788         readnum(controlfile,&firstch);
789         skipws(controlfile);
790 	readnum(controlfile,&lastch);
791 	offset=lastch-firstch;
792         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;
801         break;
802       case 'f': /* freeze */
803         skiptoeol(controlfile);
804         (*commandlistend) = (comnode*)myalloc(sizeof(comnode));
805         (*commandlistend)->thecommand = 0;
806         commandlistend = &(*commandlistend)->next;
807         (*commandlistend) = NULL;
808         break;
809       case 'b': /* DBCS input mode */
810         multibyte = 1;
811         break;
812       case 'u': /* UTF-8 input mode */
813         multibyte = 2;
814         break;
815       case 'h': /* HZ input mode */
816         multibyte = 3;
817         break;
818       case 'j': /* Shift-JIS input mode */
819         multibyte = 4;
820         break;
821       case 'g': /* ISO 2022 character set choices */
822         multibyte = 0;
823         skipws(controlfile);
824         command=Zgetc(controlfile);
825         switch (command) {
826           case '0': /* define G0 charset */
827             charset(0, controlfile);
828             break;
829           case '1': /* set G1 charset */
830             charset(1, controlfile);
831             break;
832           case '2': /* set G2 charset */
833             charset(2, controlfile);
834             break;
835           case '3': /* set G3 charset */
836             charset(3, controlfile);
837             break;
838           case 'l': case 'L': /* define left half */
839             skipws(controlfile);
840             gl = Zgetc(controlfile) - '0';
841             skiptoeol(controlfile);
842             break;
843           case 'r': case 'R': /* define right half */
844             skipws(controlfile);
845             gr = Zgetc(controlfile) - '0';
846             skiptoeol(controlfile);
847             break;
848           default: /* meaningless "g" command */
849             skiptoeol(controlfile);
850           }
851       case '\r': case '\n': /* blank line */
852         break;
853       default: /* Includes '#' */
854         skiptoeol(controlfile);
855       }
856     }
857   Zclose(controlfile);
858 }
859 
860 
861 /****************************************************************************
862 
863   readcontrolfiles
864 
865   Reads in the controlfiles names in cfilelist.  Uses readcontrol.
866   Called in main().
867 
868 ****************************************************************************/
869 
readcontrolfiles()870 void readcontrolfiles()
871 {
872   cfnamenode *cfnptr;
873 
874   for (cfnptr=cfilelist;cfnptr!=NULL;cfnptr=cfnptr->next) {
875     readcontrol(cfnptr->thename);
876     }
877 }
878 
879 
880 /****************************************************************************
881 
882   clearcfilelist
883 
884   Clears the control file list.  Assumes thename does not need freeing.
885 
886 ****************************************************************************/
887 
clearcfilelist()888 void clearcfilelist()
889 {
890   cfnamenode *cfnptr1,*cfnptr2;
891 
892   cfnptr1 = cfilelist;
893   while (cfnptr1 != NULL) {
894     cfnptr2 = cfnptr1->next;
895     free(cfnptr1);
896     cfnptr1 = cfnptr2;
897     }
898   cfilelist = NULL;
899   cfilelistend = &cfilelist;
900 }
901 
902 
903 /****************************************************************************
904 
905   getparams
906 
907   Handles all command-line parameters.  Puts all parameters within
908   bounds.
909 
910 ****************************************************************************/
911 
getparams()912 void getparams()
913 {
914   extern char *optarg;
915   extern int optind;
916   int c; /* "Should" be a char -- need int for "!= -1" test*/
917   int columns,infoprint;
918   char *controlname,*env;
919 
920   if ((myname = strrchr(Myargv[0],DIRSEP))!=NULL) {
921     myname++;
922     }
923   else {
924     myname = Myargv[0];
925     }
926   fontdirname = DEFAULTFONTDIR;
927   env = getenv("FIGLET_FONTDIR");
928   if (env!=NULL) {
929     fontdirname = env;
930     }
931   fontname = DEFAULTFONTFILE;
932   cfilelist = NULL;
933   cfilelistend = &cfilelist;
934   commandlist = NULL;
935   commandlistend = &commandlist;
936   smushoverride = SMO_NO;
937   deutschflag = 0;
938   justification = -1;
939   right2left = -1;
940   paragraphflag = 0;
941   infoprint = -1;
942   cmdinput = 0;
943   outputwidth = DEFAULTCOLUMNS;
944   gn[1] = 0x80;
945   gr = 1;
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.  */
948     switch (c) {
949       case 'A':
950         cmdinput = 1;
951         break;
952       case 'D':
953         deutschflag = 1;
954         break;
955       case 'E':
956         deutschflag = 0;
957         break;
958       case 'X':
959         right2left = -1;
960         break;
961       case 'L':
962         right2left = 0;
963         break;
964       case 'R':
965         right2left = 1;
966         break;
967       case 'x':
968         justification = -1;
969         break;
970       case 'l':
971         justification = 0;
972         break;
973       case 'c':
974         justification = 1;
975         break;
976       case 'r':
977         justification = 2;
978         break;
979       case 'p':
980         paragraphflag = 1;
981         break;
982       case 'n':
983         paragraphflag = 0;
984         break;
985       case 's':
986         smushoverride = SMO_NO;
987         break;
988       case 'k':
989         smushmode = SM_KERN;
990         smushoverride = SMO_YES;
991         break;
992       case 'S':
993         smushmode = SM_SMUSH;
994 	smushoverride = SMO_FORCE;
995         break;
996       case 'o':
997         smushmode = SM_SMUSH;
998 	smushoverride = SMO_YES;
999         break;
1000       case 'W':
1001         smushmode = 0;
1002 	smushoverride = SMO_YES;
1003         break;
1004       case 't':
1005 #ifdef TIOCGWINSZ
1006         columns = get_columns();
1007         if (columns>0) {
1008           outputwidth = columns;
1009           }
1010 #else /* ifdef TIOCGWINSZ */
1011         fprintf(stderr,
1012           "%s: \"-t\" is disabled, since ioctl is not fully implemented.\n",
1013           myname);
1014 #endif /* ifdef TIOCGWINSZ */
1015         break;
1016       case 'v':
1017         infoprint = 0;
1018         break;
1019       case 'I':
1020         infoprint = atoi(optarg);
1021         break;
1022       case 'm':
1023         smushmode = atoi(optarg);
1024         if (smushmode < -1) {
1025           smushoverride = SMO_NO;
1026           break;
1027           }
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;
1032         break;
1033       case 'w':
1034         columns = atoi(optarg);
1035         if (columns>0) {
1036           outputwidth = columns;
1037           }
1038         break;
1039       case 'd':
1040         fontdirname = optarg;
1041         break;
1042       case 'f':
1043         fontname = optarg;
1044         if (suffixcmp(fontname,FONTFILESUFFIX)) {
1045           fontname[MYSTRLEN(fontname)-FSUFFIXLEN] = '\0';
1046           }
1047 #ifdef TLF_FONTS
1048         else if (suffixcmp(fontname,TOILETFILESUFFIX)) {
1049           fontname[MYSTRLEN(fontname)-TSUFFIXLEN] = '\0';
1050           }
1051 #endif
1052         break;
1053       case 'C':
1054         controlname = optarg;
1055         if (suffixcmp(controlname, CONTROLFILESUFFIX)) {
1056           controlname[MYSTRLEN(controlname)-CSUFFIXLEN] = '\0';
1057           }
1058         (*cfilelistend) = (cfnamenode*)myalloc(sizeof(cfnamenode));
1059         (*cfilelistend)->thename = controlname;
1060         cfilelistend = &(*cfilelistend)->next;
1061         (*cfilelistend) = NULL;
1062         break;
1063       case 'N':
1064         clearcfilelist();
1065         multibyte = 0;
1066         gn[0] = 0;
1067         gn[1] = 0x80;
1068         gn[2] = gn[3] = 0;
1069         gndbl[0] = gndbl[1] = gndbl[2] = gndbl[3] = 0;
1070         gl = 0;
1071         gr = 1;
1072         break;
1073       case 'F': /* Not a legal option */
1074         fprintf(stderr,"%s: illegal option -- F\n",myname);
1075         printusage(stderr);
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");
1084         exit(1);
1085         break;
1086       default:
1087         printusage(stderr);
1088         exit(1);
1089       }
1090     }
1091   if (optind!=Myargc) cmdinput = 1; /* force cmdinput if more arguments */
1092   outlinelenlimit = outputwidth-1;
1093   if (infoprint>=0) {
1094     printinfo(infoprint);
1095     exit(0);
1096     }
1097 }
1098 
1099 
1100 /****************************************************************************
1101 
1102   clearline
1103 
1104   Clears both the input (inchrline) and output (outputline) storage.
1105 
1106 ****************************************************************************/
1107 
clearline()1108 void clearline()
1109 {
1110   int i;
1111 
1112   for (i=0;i<charheight;i++) {
1113     outputline[i][0] = '\0';
1114     }
1115   outlinelen = 0;
1116   inchrlinelen = 0;
1117 }
1118 
1119 
1120 /****************************************************************************
1121 
1122   readfontchar
1123 
1124   Reads a font character from the font file, and places it in a
1125   newly-allocated entry in the list.
1126 
1127 ****************************************************************************/
1128 
readfontchar(file,theord)1129 void readfontchar(file,theord)
1130 ZFILE *file;
1131 inchr theord;
1132 {
1133   int row,k;
1134   char templine[MAXLEN+1];
1135   outchr endchar, outline[MAXLEN+1];
1136   fcharnode *fclsave;
1137 
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;
1143 
1144   for (row=0;row<charheight;row++) {
1145     if (myfgets(templine,MAXLEN,file)==NULL) {
1146       templine[0] = '\0';
1147       }
1148 #ifdef TLF_FONTS
1149     utf8_to_wchar(templine,MAXLEN,outline,MAXLEN,0);
1150 #else
1151     strcpy(outline,templine);
1152 #endif
1153     k = STRLEN(outline)-1;
1154     while (k>=0 && ISSPACE(outline[k])) {  /* remove trailing spaces */
1155       k--;
1156       }
1157     if (k>=0) {
1158       endchar = outline[k];  /* remove endmarks */
1159       while (k>=0 && outline[k]==endchar) {
1160         k--;
1161         }
1162       }
1163     outline[k+1] = '\0';
1164     fcharlist->thechar[row] = (outchr*)myalloc(sizeof(outchr)*(STRLEN(outline)+1));
1165     STRCPY(fcharlist->thechar[row],outline);
1166     }
1167 }
1168 
1169 
1170 /****************************************************************************
1171 
1172   readfont
1173 
1174   Allocates memory, initializes variables, and reads in the font.
1175   Called near beginning of main().
1176 
1177 ****************************************************************************/
1178 
readfont()1179 void readfont()
1180 {
1181   int i,row,numsread;
1182   inchr theord;
1183   int maxlen,cmtlines,ffright2left;
1184   int smush,smush2;
1185   char fileline[MAXLEN+1],magicnum[5];
1186   ZFILE *fontfile;
1187 
1188   fontfile = FIGopen(fontname,FONTFILESUFFIX);
1189 #ifdef TLF_FONTS
1190   if (fontfile==NULL) {
1191     fontfile = FIGopen(fontname,TOILETFILESUFFIX);
1192     if(fontfile) toiletfont = 1;
1193     }
1194 #endif
1195 
1196   if (fontfile==NULL) {
1197     fprintf(stderr,"%s: %s: Unable to open font file\n",myname,fontname);
1198     exit(1);
1199     }
1200 
1201   readmagic(fontfile,magicnum);
1202   if (myfgets(fileline,MAXLEN,fontfile)==NULL) {
1203     fileline[0] = '\0';
1204     }
1205   if (MYSTRLEN(fileline)>0 ? fileline[MYSTRLEN(fileline)-1]!='\n' : 0) {
1206     skiptoeol(fontfile);
1207     }
1208   numsread = sscanf(fileline,"%*c%c %d %*d %d %d %d %d %d",
1209     &hardblank,&charheight,&maxlen,&smush,&cmtlines,
1210     &ffright2left,&smush2);
1211 
1212   if (maxlen > MAXLEN) {
1213     fprintf(stderr,"%s: %s: character is too wide\n",myname,fontname);
1214     exit(1);
1215     }
1216 #ifdef TLF_FONTS
1217   if ((!toiletfont && strcmp(magicnum,FONTFILEMAGICNUMBER)) ||
1218       (toiletfont && strcmp(magicnum,TOILETFILEMAGICNUMBER)) || numsread<5) {
1219 #else
1220   if (strcmp(magicnum,FONTFILEMAGICNUMBER) || numsread<5) {
1221 #endif
1222     fprintf(stderr,"%s: %s: Not a FIGlet 2 font file\n",myname,fontname);
1223     exit(1);
1224     }
1225   for (i=1;i<=cmtlines;i++) {
1226     skiptoeol(fontfile);
1227     }
1228 
1229   if (numsread<6) {
1230     ffright2left = 0;
1231     }
1232 
1233   if (numsread<7) { /* if no smush2, decode smush into smush2 */
1234     if (smush == 0) smush2 = SM_KERN;
1235     else if (smush < 0) smush2 = 0;
1236     else smush2 = (smush & 31) | SM_SMUSH;
1237     }
1238 
1239   if (charheight<1) {
1240     charheight = 1;
1241     }
1242 
1243   if (maxlen<1) {
1244     maxlen = 1;
1245     }
1246 
1247   maxlen += 100; /* Give ourselves some extra room */
1248 
1249   if (smushoverride == SMO_NO)
1250      smushmode = smush2;
1251   else if (smushoverride == SMO_FORCE)
1252      smushmode |= smush2;
1253 
1254   if (right2left<0) {
1255     right2left = ffright2left;
1256     }
1257 
1258   if (justification<0) {
1259     justification = 2*right2left;
1260     }
1261 
1262   /* Allocate "missing" character */
1263   fcharlist = (fcharnode*)myalloc(sizeof(fcharnode));
1264   fcharlist->ord = 0;
1265   fcharlist->thechar = (outchr**)myalloc(sizeof(outchr*)*charheight);
1266   fcharlist->next = NULL;
1267   for (row=0;row<charheight;row++) {
1268     fcharlist->thechar[row] = (outchr*)myalloc(sizeof(outchr));
1269     fcharlist->thechar[row][0] = '\0';
1270     }
1271   for (theord=' ';theord<='~';theord++) {
1272     readfontchar(fontfile,theord);
1273     }
1274   for (theord=0;theord<=6;theord++) {
1275     readfontchar(fontfile,deutsch[theord]);
1276     }
1277   while (myfgets(fileline,maxlen+1,fontfile)==NULL?0:
1278     sscanf(fileline,"%li",&theord)==1) {
1279     readfontchar(fontfile,theord);
1280     }
1281   Zclose(fontfile);
1282 }
1283 
1284 
1285 /****************************************************************************
1286 
1287   linealloc
1288 
1289   Allocates & clears outputline, inchrline. Sets inchrlinelenlimit.
1290   Called near beginning of main().
1291 
1292 ****************************************************************************/
1293 
1294 void linealloc()
1295 {
1296   int row;
1297 
1298   outputline = (outchr**)myalloc(sizeof(outchr*)*charheight);
1299   for (row=0;row<charheight;row++) {
1300     outputline[row] = (outchr*)myalloc(sizeof(outchr)*(outlinelenlimit+1));
1301     }
1302   inchrlinelenlimit = outputwidth*4+100;
1303   inchrline = (inchr*)myalloc(sizeof(inchr)*(inchrlinelenlimit+1));
1304   clearline();
1305 }
1306 
1307 
1308 /****************************************************************************
1309 
1310   getletter
1311 
1312   Sets currchar to point to the font entry for the given character.
1313   Sets currcharwidth to the width of this character.
1314 
1315 ****************************************************************************/
1316 
1317 void getletter(c)
1318 inchr c;
1319 {
1320   fcharnode *charptr;
1321 
1322   for (charptr=fcharlist;charptr==NULL?0:charptr->ord!=c;
1323     charptr=charptr->next) ;
1324   if (charptr!=NULL) {
1325     currchar = charptr->thechar;
1326     }
1327   else {
1328     for (charptr=fcharlist;charptr==NULL?0:charptr->ord!=0;
1329       charptr=charptr->next) ;
1330     currchar = charptr->thechar;
1331     }
1332   previouscharwidth = currcharwidth;
1333   currcharwidth = STRLEN(currchar[0]);
1334 }
1335 
1336 
1337 /****************************************************************************
1338 
1339   smushem
1340 
1341   Given 2 characters, attempts to smush them into 1, according to
1342   smushmode.  Returns smushed character or '\0' if no smushing can be
1343   done.
1344 
1345   smushmode values are sum of following (all values smush blanks):
1346     1: Smush equal chars (not hardblanks)
1347     2: Smush '_' with any char in hierarchy below
1348     4: hierarchy: "|", "/\", "[]", "{}", "()", "<>"
1349        Each class in hier. can be replaced by later class.
1350     8: [ + ] -> |, { + } -> |, ( + ) -> |
1351    16: / + \ -> X, > + < -> X (only in that order)
1352    32: hardblank + hardblank -> hardblank
1353 
1354 ****************************************************************************/
1355 
1356 outchr smushem(lch,rch)
1357 outchr lch,rch;
1358 {
1359   if (lch==' ') return rch;
1360   if (rch==' ') return lch;
1361 
1362   if (previouscharwidth<2 || currcharwidth<2) return '\0';
1363     /* Disallows overlapping if the previous character */
1364     /* or the current character has a width of 1 or zero. */
1365 
1366   if ((smushmode & SM_SMUSH) == 0) return '\0';  /* kerning */
1367 
1368   if ((smushmode & 63) == 0) {
1369     /* This is smushing by universal overlapping. */
1370     if (lch==' ') return rch;
1371     if (rch==' ') return lch;
1372     if (lch==hardblank) return rch;
1373     if (rch==hardblank) return lch;
1374       /* Above four lines ensure overlapping preference to */
1375       /* visible characters. */
1376     if (right2left==1) return lch;
1377       /* Above line ensures that the dominant (foreground) */
1378       /* fig-character for overlapping is the latter in the */
1379       /* user's text, not necessarily the rightmost character. */
1380     return rch;
1381       /* Occurs in the absence of above exceptions. */
1382     }
1383 
1384   if (smushmode & SM_HARDBLANK) {
1385     if (lch==hardblank && rch==hardblank) return lch;
1386     }
1387 
1388   if (lch==hardblank || rch==hardblank) return '\0';
1389 
1390   if (smushmode & SM_EQUAL) {
1391     if (lch==rch) return lch;
1392     }
1393 
1394   if (smushmode & SM_LOWLINE) {
1395     if (lch=='_' && strchr("|/\\[]{}()<>",rch)) return rch;
1396     if (rch=='_' && strchr("|/\\[]{}()<>",lch)) return lch;
1397     }
1398 
1399   if (smushmode & SM_HIERARCHY) {
1400     if (lch=='|' && strchr("/\\[]{}()<>",rch)) return rch;
1401     if (rch=='|' && strchr("/\\[]{}()<>",lch)) return lch;
1402     if (strchr("/\\",lch) && strchr("[]{}()<>",rch)) return rch;
1403     if (strchr("/\\",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     }
1411 
1412   if (smushmode & SM_PAIR) {
1413     if (lch=='[' && rch==']') return '|';
1414     if (rch=='[' && lch==']') return '|';
1415     if (lch=='{' && rch=='}') return '|';
1416     if (rch=='{' && lch=='}') return '|';
1417     if (lch=='(' && rch==')') return '|';
1418     if (rch=='(' && lch==')') return '|';
1419     }
1420 
1421   if (smushmode & SM_BIGX) {
1422     if (lch=='/' && rch=='\\') return '|';
1423     if (rch=='/' && lch=='\\') return 'Y';
1424     if (lch=='>' && rch=='<') return 'X';
1425       /* Don't want the reverse of above to give 'X'. */
1426     }
1427 
1428   return '\0';
1429 }
1430 
1431 
1432 /****************************************************************************
1433 
1434   smushamt
1435 
1436   Returns the maximum amount that the current character can be smushed
1437   into the current line.
1438 
1439 ****************************************************************************/
1440 
1441 int smushamt()
1442 {
1443   int maxsmush,amt;
1444   int row,linebd,charbd;
1445   outchr ch1,ch2;
1446 
1447   if ((smushmode & (SM_SMUSH | SM_KERN)) == 0) {
1448     return 0;
1449     }
1450   maxsmush = currcharwidth;
1451   for (row=0;row<charheight;row++) {
1452     if (right2left) {
1453       for (charbd=STRLEN(currchar[row]);
1454         ch1=currchar[row][charbd],(charbd>0&&(!ch1||ch1==' '));charbd--) ;
1455       for (linebd=0;ch2=outputline[row][linebd],ch2==' ';linebd++) ;
1456       amt = linebd+currcharwidth-1-charbd;
1457       }
1458     else {
1459       for (linebd=STRLEN(outputline[row]);
1460         ch1 = outputline[row][linebd],(linebd>0&&(!ch1||ch1==' '));linebd--) ;
1461       for (charbd=0;ch2=currchar[row][charbd],ch2==' ';charbd++) ;
1462       amt = charbd+outlinelen-1-linebd;
1463       }
1464     if (!ch1||ch1==' ') {
1465       amt++;
1466       }
1467     else if (ch2) {
1468       if (smushem(ch1,ch2)!='\0') {
1469         amt++;
1470         }
1471       }
1472     if (amt<maxsmush) {
1473       maxsmush = amt;
1474       }
1475     }
1476   return maxsmush;
1477 }
1478 
1479 
1480 /****************************************************************************
1481 
1482   addchar
1483 
1484   Attempts to add the given character onto the end of the current line.
1485   Returns 1 if this can be done, 0 otherwise.
1486 
1487 ****************************************************************************/
1488 
1489 int addchar(c)
1490 inchr c;
1491 {
1492   int smushamount,row,k,column;
1493   outchr *templine;
1494 
1495   getletter(c);
1496   smushamount = smushamt();
1497   if (outlinelen+currcharwidth-smushamount>outlinelenlimit
1498       ||inchrlinelen+1>inchrlinelenlimit) {
1499     return 0;
1500     }
1501 
1502   templine = (outchr*)myalloc(sizeof(outchr)*(outlinelenlimit+1));
1503   for (row=0;row<charheight;row++) {
1504     if (right2left) {
1505       STRCPY(templine,currchar[row]);
1506       for (k=0;k<smushamount;k++) {
1507         templine[currcharwidth-smushamount+k] =
1508           smushem(templine[currcharwidth-smushamount+k],outputline[row][k]);
1509         }
1510       STRCAT(templine,outputline[row]+smushamount);
1511       STRCPY(outputline[row],templine);
1512       }
1513     else {
1514       for (k=0;k<smushamount;k++) {
1515 	column = outlinelen-smushamount+k;
1516 	if (column < 0) {
1517 	  column = 0;
1518 	  }
1519         outputline[row][column] =
1520           smushem(outputline[row][column],currchar[row][k]);
1521         }
1522       STRCAT(outputline[row],currchar[row]+smushamount);
1523       }
1524     }
1525   free(templine);
1526   outlinelen = STRLEN(outputline[0]);
1527   inchrline[inchrlinelen++] = c;
1528   return 1;
1529 }
1530 
1531 
1532 /****************************************************************************
1533 
1534   putstring
1535 
1536   Prints out the given null-terminated string, substituting blanks
1537   for hardblanks.  If outputwidth is 1, prints the entire string;
1538   otherwise prints at most outputwidth-1 characters.  Prints a newline
1539   at the end of the string.  The string is left-justified, centered or
1540   right-justified (taking outputwidth as the screen width) if
1541   justification is 0, 1 or 2, respectively.
1542 
1543 ****************************************************************************/
1544 
1545 void putstring(string)
1546 outchr *string;
1547 {
1548   int i,len;
1549   char c[10];
1550 #ifdef TLF_FONTS
1551   size_t size;
1552   wchar_t wc[2];
1553 #endif
1554 
1555   len = STRLEN(string);
1556   if (outputwidth>1) {
1557     if (len>outputwidth-1) {
1558       len = outputwidth-1;
1559       }
1560     if (justification>0) {
1561       for (i=1;(3-justification)*i+len+justification-2<outputwidth;i++) {
1562         putchar(' ');
1563         }
1564       }
1565     }
1566   for (i=0;i<len;i++) {
1567 #ifdef TLF_FONTS
1568     wc[0] = string[i];
1569     wc[1] = 0;
1570     size = wchar_to_utf8(wc,1,c,10,0);
1571     if(size==1) {
1572       if(c[0]==hardblank) {
1573         c[0] = ' ';
1574         }
1575       }
1576     c[size] = 0;
1577     printf("%s",c);
1578 #else
1579     putchar(string[i]==hardblank?' ':string[i]);
1580 #endif
1581     }
1582   putchar('\n');
1583 }
1584 
1585 
1586 /****************************************************************************
1587 
1588   printline
1589 
1590   Prints outputline using putstring, then clears the current line.
1591 
1592 ****************************************************************************/
1593 
1594 void printline()
1595 {
1596   int i;
1597 
1598   for (i=0;i<charheight;i++) {
1599     putstring(outputline[i]);
1600     }
1601   clearline();
1602 }
1603 
1604 
1605 /****************************************************************************
1606 
1607   splitline
1608 
1609   Splits inchrline at the last word break (bunch of consecutive blanks).
1610   Makes a new line out of the first part and prints it using
1611   printline.  Makes a new line out of the second part and returns.
1612 
1613 ****************************************************************************/
1614 
1615 void splitline()
1616 {
1617   int i,gotspace,lastspace,len1,len2;
1618   inchr *part1,*part2;
1619 
1620   part1 = (inchr*)myalloc(sizeof(inchr)*(inchrlinelen+1));
1621   part2 = (inchr*)myalloc(sizeof(inchr)*(inchrlinelen+1));
1622   gotspace = 0;
1623   lastspace = inchrlinelen-1;
1624   for (i=inchrlinelen-1;i>=0;i--) {
1625     if (!gotspace && inchrline[i]==' ') {
1626       gotspace = 1;
1627       lastspace = i;
1628       }
1629     if (gotspace && inchrline[i]!=' ') {
1630       break;
1631       }
1632     }
1633   len1 = i+1;
1634   len2 = inchrlinelen-lastspace-1;
1635   for (i=0;i<len1;i++) {
1636     part1[i] = inchrline[i];
1637     }
1638   for (i=0;i<len2;i++) {
1639     part2[i] = inchrline[lastspace+1+i];
1640     }
1641   clearline();
1642   for (i=0;i<len1;i++) {
1643     addchar(part1[i]);
1644     }
1645   printline();
1646   for (i=0;i<len2;i++) {
1647     addchar(part2[i]);
1648     }
1649   free(part1);
1650   free(part2);
1651 }
1652 
1653 
1654 /****************************************************************************
1655 
1656   handlemapping
1657 
1658   Given an input character (type inchr), executes re-mapping commands
1659   read from control files.  Returns re-mapped character (inchr).
1660 
1661 ****************************************************************************/
1662 
1663 inchr handlemapping(c)
1664 inchr c;
1665 {
1666   comnode *cmptr;
1667 
1668   cmptr=commandlist;
1669   while (cmptr!=NULL) {
1670     if (cmptr->thecommand ?
1671       (c >= cmptr->rangelo && c <= cmptr->rangehi) : 0) {
1672       c += cmptr->offset;
1673       while(cmptr!=NULL ? cmptr->thecommand : 0) {
1674         cmptr=cmptr->next;
1675         }
1676       }
1677     else {
1678       cmptr=cmptr->next;
1679       }
1680     }
1681   return c;
1682 }
1683 
1684 /****************************************************************************
1685 
1686   Agetchar
1687 
1688   Replacement to getchar().
1689   Acts exactly like getchar if -A is NOT specified,
1690   else obtains input from All remaining command line words.
1691 
1692 ****************************************************************************/
1693 
1694 int Agetchar()
1695 {
1696     extern int optind;		/* current argv[] element under study */
1697     static int AgetMode = 0;	/* >= 0 for displacement into argv[n], <0 EOF */
1698     char   *arg;		/* pointer to active character */
1699     int    c;			/* current character */
1700 
1701     if ( ! cmdinput )		/* is -A active? */
1702 	return( getchar() );	/* no: return stdin character */
1703 
1704     if ( AgetMode < 0 || optind >= Myargc )		/* EOF is sticky: */
1705 	return( EOF );		/* **ensure it now and forever more */
1706 
1707     /* find next character */
1708     arg = Myargv[optind];		/* pointer to active arg */
1709     c = arg[AgetMode++]&0xFF;	/* get appropriate char of arg */
1710 
1711     if ( ! c )			/* at '\0' that terminates word? */
1712     {   /* at end of word: return ' ' if normal word, '\n' if empty */
1713 	c = ' ';		/* suppose normal word and return blank */
1714 	if ( AgetMode == 1 )	/* if ran out in very 1st char, force \n */
1715 	    c = '\n';		/* (allows "hello '' world" to do \n at '') */
1716 	AgetMode = 0;		/* return to char 0 in NEXT word */
1717 	if ( ++optind >= Myargc )	/* run up word count and check if at "EOF" */
1718 	{   /* just ran out of arguments */
1719 	    c = EOF;		/* return EOF */
1720 	    AgetMode = -1;	/* ensure all future returns return EOF */
1721 	}
1722     }
1723 
1724     return( c );		/* return appropriate character */
1725 
1726 }	/* end: Agetchar() */
1727 
1728 
1729 /****************************************************************************
1730 
1731   iso2022
1732 
1733   Called by getinchr.  Interprets ISO 2022 sequences
1734 
1735 ******************************************************************************/
1736 
1737 inchr iso2022()
1738 {
1739   inchr ch;
1740   inchr ch2;
1741   int save_gl;
1742   int save_gr;
1743 
1744   ch = Agetchar();
1745   if (ch == EOF) return ch;
1746   if (ch == 27) ch = Agetchar() + 0x100; /* ESC x */
1747   if (ch == 0x100 + '$') ch = Agetchar() + 0x200; /* ESC $ x */
1748   switch (ch) {
1749     case 14: /* invoke G1 into GL */
1750       gl = 1;
1751       return iso2022();
1752     case 15: /* invoke G0 into GL */
1753       gl = 0;
1754       return iso2022();
1755     case 142: case 'N' + 0x100: /* invoke G2 into GL for next char */
1756       save_gl = gl; save_gr = gr;
1757       gl = gr = 2;
1758       ch = iso2022();
1759       gl = save_gl; gr = save_gr;
1760       return ch;
1761     case 143: case 'O' + 0x100: /* invoke G3 into GL for next char */
1762       save_gl = gl; save_gr = gr;
1763       gl = gr = 3;
1764       ch = iso2022();
1765       gl = save_gl; gr = save_gr;
1766       return ch;
1767     case 'n' + 0x100: /* invoke G2 into GL */
1768       gl = 2;
1769       return iso2022();
1770     case 'o' + 0x100: /* invoke G3 into GL */
1771       gl = 3;
1772       return iso2022();
1773     case '~' + 0x100: /* invoke G1 into GR */
1774       gr = 1;
1775       return iso2022();
1776     case '}' + 0x100: /* invoke G2 into GR */
1777       gr = 2;
1778       return iso2022();
1779     case '|' + 0x100: /* invoke G3 into GR */
1780       gr = 3;
1781       return iso2022();
1782     case '(' + 0x100: /* set G0 to 94-char set */
1783       ch = Agetchar();
1784       if (ch == 'B') ch = 0; /* ASCII */
1785       gn[0] = ch << 16;
1786       gndbl[0] = 0;
1787       return iso2022();
1788     case ')' + 0x100: /* set G1 to 94-char set */
1789       ch = Agetchar();
1790       if (ch == 'B') ch = 0;
1791       gn[1] = ch << 16;
1792       gndbl[1] = 0;
1793       return iso2022();
1794     case '*' + 0x100: /* set G2 to 94-char set */
1795       ch = Agetchar();
1796       if (ch == 'B') ch = 0;
1797       gn[2] = ch << 16;
1798       gndbl[2] = 0;
1799       return iso2022();
1800     case '+' + 0x100: /* set G3 to 94-char set */
1801       ch = Agetchar();
1802       if (ch == 'B') ch = 0;
1803       gn[3] = ch << 16;
1804       gndbl[3] = 0;
1805       return iso2022();
1806     case '-' + 0x100: /* set G1 to 96-char set */
1807       ch = Agetchar();
1808       if (ch == 'A') ch = 0; /* Latin-1 top half */
1809       gn[1] = (ch << 16) | 0x80;
1810       gndbl[1] = 0;
1811       return iso2022();
1812     case '.' + 0x100: /* set G2 to 96-char set */
1813       ch = Agetchar();
1814       if (ch == 'A') ch = 0;
1815       gn[2] = (ch << 16) | 0x80;
1816       gndbl[2] = 0;
1817       return iso2022();
1818     case '/' + 0x100: /* set G3 to 96-char set */
1819       ch = Agetchar();
1820       if (ch == 'A') ch = 0;
1821       gn[3] = (ch << 16) | 0x80;
1822       gndbl[3] = 0;
1823       return iso2022();
1824     case '(' + 0x200: /* set G0 to 94 x 94 char set */
1825       ch = Agetchar();
1826       gn[0] = ch << 16;
1827       gndbl[0] = 1;
1828       return iso2022();
1829     case ')' + 0x200: /* set G1 to 94 x 94 char set */
1830       ch = Agetchar();
1831       gn[1] = ch << 16;
1832       gndbl[1] = 1;
1833       return iso2022();
1834     case '*' + 0x200: /* set G2 to 94 x 94 char set */
1835       ch = Agetchar();
1836       gn[2] = ch << 16;
1837       gndbl[2] = 1;
1838       return iso2022();
1839     case '+' + 0x200: /* set G3 to 94 x 94 char set */
1840       ch = Agetchar();
1841       gn[3] = ch << 16;
1842       gndbl[3] = 1;
1843       return iso2022();
1844     default:
1845       if (ch & 0x200) { /* set G0 to 94 x 94 char set (deprecated) */
1846         gn[0] = (ch & ~0x200) << 16;
1847         gndbl[0] = 1;
1848         return iso2022();
1849         }
1850       }
1851 
1852   if (ch >= 0x21 && ch <= 0x7E) { /* process GL */
1853     if (gndbl[gl]) {
1854       ch2 = Agetchar();
1855       return gn[gl] | (ch << 8) | ch2;
1856       }
1857     else return gn[gl] | ch;
1858     }
1859   else if (ch >= 0xA0 && ch <= 0xFF) { /* process GR */
1860     if (gndbl[gr]) {
1861       ch2 = Agetchar();
1862       return gn[gr] | (ch << 8) | ch2;
1863       }
1864     else return gn[gr] | (ch & ~0x80);
1865     }
1866   else return ch;
1867   }
1868 
1869 /****************************************************************************
1870 
1871   ungetinchr
1872 
1873   Called by main.  Pushes back an "inchr" to be read by getinchr
1874   on the next call.
1875 
1876 ******************************************************************************/
1877 inchr getinchr_buffer;
1878 int getinchr_flag;
1879 
1880 inchr ungetinchr(c)
1881 inchr c;
1882 {
1883   getinchr_buffer = c;
1884   getinchr_flag = 1;
1885   return c;
1886 }
1887 
1888 /*****************************************************************************
1889 
1890   getinchr
1891 
1892   Called by main.  Processes multibyte characters.  Invokes Agetchar.
1893   If multibyte = 0, ISO 2022 mode (see iso2022 routine).
1894   If multibyte = 1,  double-byte mode (0x00-0x7f bytes are characters,
1895     0x80-0xFF bytes are first byte of a double-byte character).
1896   If multibyte = 2, Unicode UTF-8 mode (0x00-0x7F bytes are characters,
1897     0x80-0xBF bytes are nonfirst byte of a multibyte character,
1898     0xC0-0xFD bytes are first byte of a multibyte character,
1899     0xFE-0xFF bytes are errors (all errors return code 0x0080)).
1900   If multibyte = 3, HZ mode ("~{" starts double-byte mode, "}~" ends it,
1901     "~~" is a tilde, "~x" for all other x is ignored).
1902   If multibyte = 4, Shift-JIS mode (0x80-0x9F and 0xE0-0xEF are first byte
1903     of a double-byte character, all other bytes are characters).
1904 
1905 
1906 *****************************************************************************/
1907 
1908 inchr getinchr()
1909 {
1910   int ch, ch2, ch3, ch4, ch5, ch6;
1911 
1912   if (getinchr_flag) {
1913     getinchr_flag = 0;
1914     return getinchr_buffer;
1915     }
1916 
1917   switch(multibyte) {
1918     case 0: /* single-byte */
1919       return iso2022();
1920    case 1: /* DBCS */
1921      ch = Agetchar();
1922      if ((ch >= 0x80 && ch <= 0x9F) ||
1923          (ch >= 0xE0 && ch <= 0xEF)) {
1924        ch = (ch << 8) + Agetchar();
1925        }
1926      return ch;
1927    case 2: /* UTF-8 */
1928       ch = Agetchar();
1929       if (ch < 0x80) return ch;  /* handles EOF, too */
1930       if (ch < 0xC0 || ch > 0xFD)
1931         return 0x0080;  /* illegal first character */
1932       ch2 = Agetchar() & 0x3F;
1933       if (ch < 0xE0) return ((ch & 0x1F) << 6) + ch2;
1934       ch3 = Agetchar() & 0x3F;
1935       if (ch < 0xF0)
1936         return ((ch & 0x0F) << 12) + (ch2 << 6) + ch3;
1937       ch4 = Agetchar() & 0x3F;
1938       if (ch < 0xF8)
1939         return ((ch & 0x07) << 18) + (ch2 << 12) + (ch3 << 6) + ch4;
1940       ch5 = Agetchar() & 0x3F;
1941       if (ch < 0xFC)
1942         return ((ch & 0x03) << 24) + (ch2 << 18) + (ch3 << 12) +
1943           (ch4 << 6) + ch5;
1944       ch6 = Agetchar() & 0x3F;
1945       return ((ch & 0x01) << 30) + (ch2 << 24) + (ch3 << 18) +
1946         (ch4 << 12) + (ch5 << 6) + ch6;
1947    case 3: /* HZ */
1948      ch = Agetchar();
1949      if (ch == EOF) return ch;
1950      if (hzmode) {
1951        ch = (ch << 8) + Agetchar();
1952        if (ch == ('}' << 8) + '~') {
1953          hzmode = 0;
1954          return getinchr();
1955          }
1956        return ch;
1957        }
1958      else if (ch == '~') {
1959        ch = Agetchar();
1960        if (ch == '{') {
1961           hzmode = 1;
1962           return getinchr();
1963           }
1964       else if (ch == '~') {
1965         return ch;
1966         }
1967       else {
1968         return getinchr();
1969         }
1970       }
1971      else return ch;
1972    case 4: /* Shift-JIS */
1973      ch = Agetchar();
1974      if ((ch >= 0x80 && ch <= 0x9F) ||
1975          (ch >= 0xE0 && ch <= 0xEF)) {
1976        ch = (ch << 8) + Agetchar();
1977        }
1978      return ch;
1979    default:
1980      return 0x80;
1981     }
1982   }
1983 
1984 /****************************************************************************
1985 
1986   main
1987 
1988   The main program, of course.
1989   Reads characters 1 by 1 from stdin, and makes lines out of them using
1990   addchar. Handles line breaking, (which accounts for most of the
1991   complexity in this function).
1992 
1993 ****************************************************************************/
1994 
1995 int main(argc,argv)
1996 int argc;
1997 char *argv[];
1998 {
1999   inchr c,c2;
2000   int i;
2001   int last_was_eol_flag;
2002 /*---------------------------------------------------------------------------
2003   wordbreakmode:
2004     -1: /^$/ and blanks are to be absorbed (when line break was forced
2005       by a blank or character larger than outlinelenlimit)
2006     0: /^ *$/ and blanks are not to be absorbed
2007     1: /[^ ]$/ no word break yet
2008     2: /[^ ]  *$/
2009     3: /[^ ]$/ had a word break
2010 ---------------------------------------------------------------------------*/
2011   int wordbreakmode;
2012   int char_not_added;
2013 
2014   Myargc = argc;
2015   Myargv = argv;
2016   getparams();
2017   readcontrolfiles();
2018   readfont();
2019   linealloc();
2020 
2021   wordbreakmode = 0;
2022   last_was_eol_flag = 0;
2023 
2024 #ifdef TLF_FONTS
2025   toiletfont = 0;
2026 #endif
2027 
2028   while ((c = getinchr())!=EOF) {
2029 
2030     if (c=='\n'&&paragraphflag&&!last_was_eol_flag) {
2031       ungetinchr(c2 = getinchr());
2032       c = ((isascii(c2)&&isspace(c2))?'\n':' ');
2033       }
2034     last_was_eol_flag = (isascii(c)&&isspace(c)&&c!='\t'&&c!=' ');
2035 
2036     if (deutschflag) {
2037       if (c>='[' && c<=']') {
2038         c = deutsch[c-'['];
2039         }
2040       else if (c >='{' && c <= '~') {
2041         c = deutsch[c-'{'+3];
2042         }
2043       }
2044 
2045     c = handlemapping(c);
2046 
2047     if (isascii(c)&&isspace(c)) {
2048       c = (c=='\t'||c==' ') ? ' ' : '\n';
2049       }
2050 
2051     if ((c>'\0' && c<' ' && c!='\n') || c==127) continue;
2052 
2053 /*
2054   Note: The following code is complex and thoroughly tested.
2055   Be careful when modifying!
2056 */
2057 
2058     do {
2059       char_not_added = 0;
2060 
2061       if (wordbreakmode== -1) {
2062         if (c==' ') {
2063           break;
2064           }
2065         else if (c=='\n') {
2066           wordbreakmode = 0;
2067           break;
2068           }
2069         wordbreakmode = 0;
2070         }
2071 
2072       if (c=='\n') {
2073         printline();
2074         wordbreakmode = 0;
2075         }
2076 
2077       else if (addchar(c)) {
2078         if (c!=' ') {
2079           wordbreakmode = (wordbreakmode>=2)?3:1;
2080           }
2081         else {
2082           wordbreakmode = (wordbreakmode>0)?2:0;
2083           }
2084         }
2085 
2086       else if (outlinelen==0) {
2087         for (i=0;i<charheight;i++) {
2088           if (right2left && outputwidth>1) {
2089             putstring(currchar[i]+STRLEN(currchar[i])-outlinelenlimit);
2090             }
2091           else {
2092             putstring(currchar[i]);
2093             }
2094           }
2095         wordbreakmode = -1;
2096         }
2097 
2098       else if (c==' ') {
2099         if (wordbreakmode==2) {
2100           splitline();
2101           }
2102         else {
2103           printline();
2104           }
2105         wordbreakmode = -1;
2106         }
2107 
2108       else {
2109         if (wordbreakmode>=2) {
2110           splitline();
2111           }
2112         else {
2113           printline();
2114           }
2115         wordbreakmode = (wordbreakmode==3)?1:0;
2116         char_not_added = 1;
2117         }
2118 
2119       } while (char_not_added);
2120     }
2121 
2122   if (outlinelen!=0) {
2123     printline();
2124     }
2125   return 0;
2126 }
2127