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'&¶graphflag&&!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