Correct Aphlict websocket URI construction after PHP8 compatibility changes
[phabricator.git] / externals / figlet / figlet.c
blobd34b19a8b8f9ea98d348909f7bb78d4bb38a10f7
1 /****************************************************************************
3 FIGlet Copyright 1991, 1993, 1994 Glenn Chappell and Ian Chai
4 FIGlet Copyright 1996, 1997, 1998, 1999, 2000, 2001 John Cowan
5 FIGlet Copyright 2002 Christiaan Keet
6 FIGlet Copyright 2011, 2012 Claudio Matsuoka
7 Portions written by Paul Burton and Christiaan Keet
8 Internet: <info@figlet.org>
9 FIGlet, along with the various FIGlet fonts and documentation, is
10 copyrighted under the provisions of the New BSD License (3-clause)
11 (as listed in the file "LICENSE" which is included in this package)
12 ****************************************************************************/
14 #define DATE "31 May 2012"
15 #define VERSION "2.2.5"
16 #define VERSION_INT 20205
18 /* FIGlet (Frank, Ian & Glenn's Letters) */
19 /* by Glenn Chappell */
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 */
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
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 */
69 #if defined(unix) || defined(__unix__) || defined(__APPLE__)
70 #include <unistd.h>
71 #include <sys/ioctl.h> /* Needed for get_columns */
72 #endif
74 #ifdef TLF_FONTS
75 #include <wchar.h>
76 #include <wctype.h>
77 #include "utf8.h"
78 #endif
80 #include "zipio.h" /* Package for reading compressed files */
82 #define MYSTRLEN(x) ((int)strlen(x)) /* Eliminate ANSI problem */
84 #define DIRSEP '/'
85 #define DIRSEP2 '\\'
86 /* Leave alone for Unix and MS-DOS/Windows!
87 Note: '/' also used in filename in get_columns(). */
89 #define FONTFILESUFFIX ".flf"
90 #define FONTFILEMAGICNUMBER "flf2"
91 #define FSUFFIXLEN MYSTRLEN(FONTFILESUFFIX)
92 #define CONTROLFILESUFFIX ".flc"
93 #define CONTROLFILEMAGICNUMBER "flc2" /* no longer used in 2.2 */
94 #define CSUFFIXLEN MYSTRLEN(CONTROLFILESUFFIX)
95 #define DEFAULTCOLUMNS 80
96 #define MAXLEN 255 /* Maximum character width */
98 /* Add support for Sam Hocevar's TOIlet fonts */
99 #ifdef TLF_FONTS
100 #define TOILETFILESUFFIX ".tlf"
101 #define TOILETFILEMAGICNUMBER "tlf2"
102 #define TSUFFIXLEN MYSTRLEN(TOILETFILESUFFIX)
104 int toiletfont; /* true if font is a TOIlet TLF font */
105 #endif
108 /****************************************************************************
110 Globals dealing with chars that are read
112 ****************************************************************************/
114 typedef long inchr; /* "char" read from stdin */
116 inchr *inchrline; /* Alloc'd inchr inchrline[inchrlinelenlimit+1]; */
117 /* Note: not null-terminated. */
118 int inchrlinelen,inchrlinelenlimit;
119 inchr deutsch[7] = {196, 214, 220, 228, 246, 252, 223};
120 /* Latin-1 codes for German letters, respectively:
121 LATIN CAPITAL LETTER A WITH DIAERESIS = A-umlaut
122 LATIN CAPITAL LETTER O WITH DIAERESIS = O-umlaut
123 LATIN CAPITAL LETTER U WITH DIAERESIS = U-umlaut
124 LATIN SMALL LETTER A WITH DIAERESIS = a-umlaut
125 LATIN SMALL LETTER O WITH DIAERESIS = o-umlaut
126 LATIN SMALL LETTER U WITH DIAERESIS = u-umlaut
127 LATIN SMALL LETTER SHARP S = ess-zed
130 int hzmode; /* true if reading double-bytes in HZ mode */
131 int gndbl[4]; /* gndbl[n] is true if Gn is double-byte */
132 inchr gn[4]; /* Gn character sets: ASCII, Latin-1, none, none */
133 int gl; /* 0-3 specifies left-half Gn character set */
134 int gr; /* 0-3 specifies right-half Gn character set */
136 int Myargc; /* to avoid passing around argc and argv */
137 char **Myargv;
139 /****************************************************************************
141 Globals dealing with chars that are written
143 ****************************************************************************/
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
159 typedef struct fc {
160 inchr ord;
161 outchr **thechar; /* Alloc'd char thechar[charheight][]; */
162 struct fc *next;
163 } fcharnode;
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;
173 /****************************************************************************
175 Globals dealing with command file storage
177 ****************************************************************************/
179 typedef struct cfn {
180 char *thename;
181 struct cfn *next;
182 } cfnamenode;
184 cfnamenode *cfilelist,**cfilelistend;
186 typedef struct cm {
187 int thecommand;
188 inchr rangelo;
189 inchr rangehi;
190 inchr offset;
191 struct cm *next;
192 } comnode;
194 comnode *commandlist,**commandlistend;
196 /****************************************************************************
198 Globals affected by command line options
200 ****************************************************************************/
202 int deutschflag,justification,paragraphflag,right2left,multibyte;
203 int cmdinput;
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
214 int smushmode;
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 */
220 int smushoverride;
222 int outputwidth;
223 int outlinelenlimit;
224 char *fontdirname,*fontname;
227 /****************************************************************************
229 Globals read from font file
231 ****************************************************************************/
233 char hardblank;
234 int charheight;
237 /****************************************************************************
239 Name of program, used in error messages
241 ****************************************************************************/
243 char *myname;
246 #ifdef TIOCGWINSZ
247 /****************************************************************************
249 get_columns
251 Determines the number of columns of /dev/tty. Returns the number of
252 columns, or -1 if error. May return 0 if columns unknown.
253 Requires include files <fcntl.h> and <sys/ioctl.h>.
254 by Glenn Chappell & Ian Chai 14 Apr 1993
256 ****************************************************************************/
258 int get_columns()
260 struct winsize ws;
261 int fd,result;
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;
268 #endif /* ifdef TIOCGWINSZ */
271 /****************************************************************************
273 myalloc
275 Calls malloc. If malloc returns error, prints error message and
276 quits.
278 ****************************************************************************/
280 #ifdef __STDC__
281 char *myalloc(size_t size)
282 #else
283 char *myalloc(size)
284 int size;
285 #endif
287 char *ptr;
288 #ifndef __STDC__
289 extern void *malloc();
290 #endif
292 if ((ptr = (char*)malloc(size))==NULL) {
293 fprintf(stderr,"%s: Out of memory\n",myname);
294 exit(1);
296 else {
297 return ptr;
302 /****************************************************************************
304 hasdirsep
306 Returns true if s1 contains a DIRSEP or DIRSEP2 character.
308 ****************************************************************************/
310 int hasdirsep(s1)
311 char *s1;
313 if (strchr(s1, DIRSEP)) return 1;
314 else if (strchr(s1, DIRSEP2)) return 1;
315 else return 0;
318 /****************************************************************************
320 suffixcmp
322 Returns true if s2 is a suffix of s1; uses case-blind comparison.
324 ****************************************************************************/
326 int suffixcmp(s1, s2)
327 char *s1;
328 char *s2;
330 int len1, len2;
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++;
341 return 1;
344 /****************************************************************************
346 skiptoeol
348 Skips to the end of a line, given a stream. Handles \r, \n, or \r\n.
350 ****************************************************************************/
352 void skiptoeol(fp)
353 ZFILE *fp;
355 int dummy;
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;
368 /****************************************************************************
370 myfgets
372 Local version of fgets. Handles \r, \n, and \r\n terminators.
374 ****************************************************************************/
376 char *myfgets(line,maxlen,fp)
377 char *line;
378 int maxlen;
379 ZFILE *fp;
381 int c = 0;
382 char *p;
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;
396 *p = 0;
397 return (c==EOF) ? NULL : line;
401 /****************************************************************************
403 usageerr
405 Prints "Usage: ...." line to the given stream.
407 ****************************************************************************/
409 void printusage(out)
410 FILE *out;
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");
422 /****************************************************************************
424 printinfo
426 Prints version and copyright message, or utility information.
428 ****************************************************************************/
430 void printinfo(infonum)
431 int infonum;
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");
470 /****************************************************************************
472 readmagic
474 Reads a four-character magic string from a stream.
476 ****************************************************************************/
477 void readmagic(fp,magic)
478 ZFILE *fp;
479 char *magic;
481 int i;
483 for (i=0;i<4;i++) {
484 magic[i] = Zgetc(fp);
486 magic[4] = 0;
489 /****************************************************************************
491 skipws
493 Skips whitespace characters from a stream.
495 ****************************************************************************/
496 void skipws(fp)
497 ZFILE *fp;
499 int c;
500 while (c=Zgetc(fp),isascii(c)&&isspace(c)) ;
501 Zungetc(c,fp);
504 /****************************************************************************
506 readnum
508 Reads a number from a stream. Accepts "0" prefix for octal and
509 "0x" or "0X" for hexadecimal. Ignores leading whitespace.
511 ****************************************************************************/
512 void readnum(fp,nump)
513 ZFILE *fp;
514 inchr *nump;
516 int acc = 0;
517 char *p;
518 int c;
519 int base;
520 int sign = 1;
521 char digits[] = "0123456789ABCDEF";
523 skipws(fp);
524 c = Zgetc(fp);
525 if (c=='-') {
526 sign = -1;
528 else {
529 Zungetc(c,fp);
531 c = Zgetc(fp);
532 if (c=='0') {
533 c = Zgetc(fp);
534 if (c=='x'||c=='X') {
535 base = 16;
537 else {
538 base = 8;
539 Zungetc(c,fp);
542 else {
543 base = 10;
544 Zungetc(c,fp);
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;
555 acc = acc*base+(p-digits);
557 *nump = acc * sign;
560 /****************************************************************************
562 readTchar
564 Reads a control file "T" command character specification.
566 Character is a single byte, an escape sequence, or
567 an escaped numeric.
569 ****************************************************************************/
571 inchr readTchar(fp)
572 ZFILE *fp;
574 inchr thechar;
575 char next;
577 thechar=Zgetc(fp);
578 if (thechar=='\n' || thechar=='\r') { /* Handle badly-formatted file */
579 Zungetc(thechar,fp);
580 return '\0';
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;
607 return next;
611 /****************************************************************************
613 charsetname
615 Get a Tchar representing a charset name, or 0 if none available.
616 Called in getcharset().
618 ****************************************************************************/
620 inchr charsetname(fp)
621 ZFILE *fp;
623 inchr result;
625 result = readTchar(fp);
626 if (result == '\n' || result == '\r') {
627 result = 0;
628 Zungetc(result,fp);
630 return result;
633 /****************************************************************************
635 charset
637 Processes "g[0123]" character set specifier
638 Called in readcontrol().
640 ****************************************************************************/
642 void charset(n, controlfile)
643 int n;
644 ZFILE *controlfile;
646 int ch;
648 skipws(controlfile);
649 if (Zgetc(controlfile) != '9') {
650 skiptoeol(controlfile);
651 return;
653 ch = Zgetc(controlfile);
654 if (ch == '6') {
655 gn[n] = 65536L * charsetname(controlfile) + 0x80;
656 gndbl[n] = 0;
657 skiptoeol(controlfile);
658 return;
660 if (ch != '4') {
661 skiptoeol(controlfile);
662 return;
664 ch = Zgetc(controlfile);
665 if (ch == 'x') {
666 if (Zgetc(controlfile) != '9') {
667 skiptoeol(controlfile);
668 return;
670 if (Zgetc(controlfile) != '4') {
671 skiptoeol(controlfile);
672 return;
674 skipws(controlfile);
675 gn[n] = 65536L * charsetname(controlfile);
676 gndbl[n] = 1;
677 skiptoeol(controlfile);
678 return;
680 Zungetc(ch, controlfile);
681 skipws(controlfile);
682 gn[n] = 65536L * charsetname(controlfile);
683 gndbl[n] = 0;
684 return;
687 /****************************************************************************
689 FIGopen
691 Given a FIGlet font or control file name and suffix, return the file
692 or NULL if not found
694 ****************************************************************************/
696 ZFILE *FIGopen(name,suffix)
697 char *name;
698 char *suffix;
700 char *fontpath;
701 ZFILE *fontfile;
702 struct stat st;
703 int namelen;
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;
717 /* just append suffix */
718 strcpy(fontpath,name);
719 strcat(fontpath,suffix);
720 if(stat(fontpath,&st)==0) goto ok;
722 return NULL;
725 fontfile = Zopen(fontpath,"rb");
726 return fontfile;
729 /****************************************************************************
731 readcontrol
733 Allocates memory and reads in the given control file.
734 Called in readcontrolfiles().
736 ****************************************************************************/
738 void readcontrol(controlname)
739 char *controlname;
741 inchr firstch,lastch;
742 char dashcheck;
743 inchr offset;
744 int command;
745 ZFILE *controlfile;
747 controlfile = FIGopen(controlname,CONTROLFILESUFFIX);
749 if (controlfile==NULL) {
750 fprintf(stderr,"%s: %s: Unable to open control file\n",myname,
751 controlname);
752 exit(1);
755 (*commandlistend) = (comnode*)myalloc(sizeof(comnode));
756 (*commandlistend)->thecommand = 0; /* Begin with a freeze command */
757 commandlistend = &(*commandlistend)->next;
758 (*commandlistend) = NULL;
760 while(command=Zgetc(controlfile),command!=EOF) {
761 switch (command) {
762 case 't': /* Translate */
763 skipws(controlfile);
764 firstch=readTchar(controlfile);
765 if ((dashcheck=Zgetc(controlfile))=='-') {
766 lastch=readTchar(controlfile);
768 else {
769 Zungetc(dashcheck,controlfile);
770 lastch=firstch;
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);
851 case '\r': case '\n': /* blank line */
852 break;
853 default: /* Includes '#' */
854 skiptoeol(controlfile);
857 Zclose(controlfile);
861 /****************************************************************************
863 readcontrolfiles
865 Reads in the controlfiles names in cfilelist. Uses readcontrol.
866 Called in main().
868 ****************************************************************************/
870 void readcontrolfiles()
872 cfnamenode *cfnptr;
874 for (cfnptr=cfilelist;cfnptr!=NULL;cfnptr=cfnptr->next) {
875 readcontrol(cfnptr->thename);
880 /****************************************************************************
882 clearcfilelist
884 Clears the control file list. Assumes thename does not need freeing.
886 ****************************************************************************/
888 void clearcfilelist()
890 cfnamenode *cfnptr1,*cfnptr2;
892 cfnptr1 = cfilelist;
893 while (cfnptr1 != NULL) {
894 cfnptr2 = cfnptr1->next;
895 free(cfnptr1);
896 cfnptr1 = cfnptr2;
898 cfilelist = NULL;
899 cfilelistend = &cfilelist;
903 /****************************************************************************
905 getparams
907 Handles all command-line parameters. Puts all parameters within
908 bounds.
910 ****************************************************************************/
912 void getparams()
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;
920 if ((myname = strrchr(Myargv[0],DIRSEP))!=NULL) {
921 myname++;
923 else {
924 myname = Myargv[0];
926 fontdirname = DEFAULTFONTDIR;
927 env = getenv("FIGLET_FONTDIR");
928 if (env!=NULL) {
929 fontdirname = env;
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;
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;
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;
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';
1047 #ifdef TLF_FONTS
1048 else if (suffixcmp(fontname,TOILETFILESUFFIX)) {
1049 fontname[MYSTRLEN(fontname)-TSUFFIXLEN] = '\0';
1051 #endif
1052 break;
1053 case 'C':
1054 controlname = optarg;
1055 if (suffixcmp(controlname, CONTROLFILESUFFIX)) {
1056 controlname[MYSTRLEN(controlname)-CSUFFIXLEN] = '\0';
1058 (*cfilelistend) = (cfnamenode*)myalloc(sizeof(cfnamenode));
1059 (*cfilelistend)->thename = controlname;
1060 cfilelistend = &(*cfilelistend)->next;
1061 (*cfilelistend) = NULL;
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);
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);
1100 /****************************************************************************
1102 clearline
1104 Clears both the input (inchrline) and output (outputline) storage.
1106 ****************************************************************************/
1108 void clearline()
1110 int i;
1112 for (i=0;i<charheight;i++) {
1113 outputline[i][0] = '\0';
1115 outlinelen = 0;
1116 inchrlinelen = 0;
1120 /****************************************************************************
1122 readfontchar
1124 Reads a font character from the font file, and places it in a
1125 newly-allocated entry in the list.
1127 ****************************************************************************/
1129 void readfontchar(file,theord)
1130 ZFILE *file;
1131 inchr theord;
1133 int row,k;
1134 char templine[MAXLEN+1];
1135 outchr endchar, outline[MAXLEN+1];
1136 fcharnode *fclsave;
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;
1144 outline[0] = 0;
1146 for (row=0;row<charheight;row++) {
1147 if (myfgets(templine,MAXLEN,file)==NULL) {
1148 templine[0] = '\0';
1150 #ifdef TLF_FONTS
1151 utf8_to_wchar(templine,MAXLEN,outline,MAXLEN,0);
1152 #else
1153 strcpy(outline,templine);
1154 #endif
1155 k = STRLEN(outline)-1;
1156 while (k>=0 && ISSPACE(outline[k])) { /* remove trailing spaces */
1157 k--;
1159 if (k>=0) {
1160 endchar = outline[k]; /* remove endmarks */
1161 while (k>=0 && outline[k]==endchar) {
1162 k--;
1165 outline[k+1] = '\0';
1166 fcharlist->thechar[row] = (outchr*)myalloc(sizeof(outchr)*(STRLEN(outline)+1));
1167 STRCPY(fcharlist->thechar[row],outline);
1172 /****************************************************************************
1174 readfont
1176 Allocates memory, initializes variables, and reads in the font.
1177 Called near beginning of main().
1179 ****************************************************************************/
1181 void readfont()
1183 int i,row,numsread;
1184 inchr theord;
1185 int maxlen,cmtlines,ffright2left;
1186 int smush,smush2;
1187 char fileline[MAXLEN+1],magicnum[5];
1188 ZFILE *fontfile;
1190 fontfile = FIGopen(fontname,FONTFILESUFFIX);
1191 #ifdef TLF_FONTS
1192 if (fontfile==NULL) {
1193 fontfile = FIGopen(fontname,TOILETFILESUFFIX);
1194 if(fontfile) toiletfont = 1;
1196 #endif
1198 if (fontfile==NULL) {
1199 fprintf(stderr,"%s: %s: Unable to open font file\n",myname,fontname);
1200 exit(1);
1203 readmagic(fontfile,magicnum);
1204 if (myfgets(fileline,MAXLEN,fontfile)==NULL) {
1205 fileline[0] = '\0';
1207 if (MYSTRLEN(fileline)>0 ? fileline[MYSTRLEN(fileline)-1]!='\n' : 0) {
1208 skiptoeol(fontfile);
1210 numsread = sscanf(fileline,"%*c%c %d %*d %d %d %d %d %d",
1211 &hardblank,&charheight,&maxlen,&smush,&cmtlines,
1212 &ffright2left,&smush2);
1214 if (maxlen > MAXLEN) {
1215 fprintf(stderr,"%s: %s: character is too wide\n",myname,fontname);
1216 exit(1);
1218 #ifdef TLF_FONTS
1219 if ((!toiletfont && strcmp(magicnum,FONTFILEMAGICNUMBER)) ||
1220 (toiletfont && strcmp(magicnum,TOILETFILEMAGICNUMBER)) || numsread<5) {
1221 #else
1222 if (strcmp(magicnum,FONTFILEMAGICNUMBER) || numsread<5) {
1223 #endif
1224 fprintf(stderr,"%s: %s: Not a FIGlet 2 font file\n",myname,fontname);
1225 exit(1);
1227 for (i=1;i<=cmtlines;i++) {
1228 skiptoeol(fontfile);
1231 if (numsread<6) {
1232 ffright2left = 0;
1235 if (numsread<7) { /* if no smush2, decode smush into smush2 */
1236 if (smush == 0) smush2 = SM_KERN;
1237 else if (smush < 0) smush2 = 0;
1238 else smush2 = (smush & 31) | SM_SMUSH;
1241 if (charheight<1) {
1242 charheight = 1;
1245 if (maxlen<1) {
1246 maxlen = 1;
1249 maxlen += 100; /* Give ourselves some extra room */
1251 if (smushoverride == SMO_NO)
1252 smushmode = smush2;
1253 else if (smushoverride == SMO_FORCE)
1254 smushmode |= smush2;
1256 if (right2left<0) {
1257 right2left = ffright2left;
1260 if (justification<0) {
1261 justification = 2*right2left;
1264 /* Allocate "missing" character */
1265 fcharlist = (fcharnode*)myalloc(sizeof(fcharnode));
1266 fcharlist->ord = 0;
1267 fcharlist->thechar = (outchr**)myalloc(sizeof(outchr*)*charheight);
1268 fcharlist->next = NULL;
1269 for (row=0;row<charheight;row++) {
1270 fcharlist->thechar[row] = (outchr*)myalloc(sizeof(outchr));
1271 fcharlist->thechar[row][0] = '\0';
1273 for (theord=' ';theord<='~';theord++) {
1274 readfontchar(fontfile,theord);
1276 for (theord=0;theord<=6;theord++) {
1277 readfontchar(fontfile,deutsch[theord]);
1279 while (myfgets(fileline,maxlen+1,fontfile)==NULL?0:
1280 sscanf(fileline,"%li",&theord)==1) {
1281 readfontchar(fontfile,theord);
1283 Zclose(fontfile);
1287 /****************************************************************************
1289 linealloc
1291 Allocates & clears outputline, inchrline. Sets inchrlinelenlimit.
1292 Called near beginning of main().
1294 ****************************************************************************/
1296 void linealloc()
1298 int row;
1300 outputline = (outchr**)myalloc(sizeof(outchr*)*charheight);
1301 for (row=0;row<charheight;row++) {
1302 outputline[row] = (outchr*)myalloc(sizeof(outchr)*(outlinelenlimit+1));
1304 inchrlinelenlimit = outputwidth*4+100;
1305 inchrline = (inchr*)myalloc(sizeof(inchr)*(inchrlinelenlimit+1));
1306 clearline();
1310 /****************************************************************************
1312 getletter
1314 Sets currchar to point to the font entry for the given character.
1315 Sets currcharwidth to the width of this character.
1317 ****************************************************************************/
1319 void getletter(c)
1320 inchr c;
1322 fcharnode *charptr;
1324 for (charptr=fcharlist;charptr==NULL?0:charptr->ord!=c;
1325 charptr=charptr->next) ;
1326 if (charptr!=NULL) {
1327 currchar = charptr->thechar;
1329 else {
1330 for (charptr=fcharlist;charptr==NULL?0:charptr->ord!=0;
1331 charptr=charptr->next) ;
1332 currchar = charptr->thechar;
1334 previouscharwidth = currcharwidth;
1335 currcharwidth = STRLEN(currchar[0]);
1339 /****************************************************************************
1341 smushem
1343 Given 2 characters, attempts to smush them into 1, according to
1344 smushmode. Returns smushed character or '\0' if no smushing can be
1345 done.
1347 smushmode values are sum of following (all values smush blanks):
1348 1: Smush equal chars (not hardblanks)
1349 2: Smush '_' with any char in hierarchy below
1350 4: hierarchy: "|", "/\", "[]", "{}", "()", "<>"
1351 Each class in hier. can be replaced by later class.
1352 8: [ + ] -> |, { + } -> |, ( + ) -> |
1353 16: / + \ -> X, > + < -> X (only in that order)
1354 32: hardblank + hardblank -> hardblank
1356 ****************************************************************************/
1358 outchr smushem(lch,rch)
1359 outchr lch,rch;
1361 if (lch==' ') return rch;
1362 if (rch==' ') return lch;
1364 if (previouscharwidth<2 || currcharwidth<2) return '\0';
1365 /* Disallows overlapping if the previous character */
1366 /* or the current character has a width of 1 or zero. */
1368 if ((smushmode & SM_SMUSH) == 0) return '\0'; /* kerning */
1370 if ((smushmode & 63) == 0) {
1371 /* This is smushing by universal overlapping. */
1372 if (lch==' ') return rch;
1373 if (rch==' ') return lch;
1374 if (lch==hardblank) return rch;
1375 if (rch==hardblank) return lch;
1376 /* Above four lines ensure overlapping preference to */
1377 /* visible characters. */
1378 if (right2left==1) return lch;
1379 /* Above line ensures that the dominant (foreground) */
1380 /* fig-character for overlapping is the latter in the */
1381 /* user's text, not necessarily the rightmost character. */
1382 return rch;
1383 /* Occurs in the absence of above exceptions. */
1386 if (smushmode & SM_HARDBLANK) {
1387 if (lch==hardblank && rch==hardblank) return lch;
1390 if (lch==hardblank || rch==hardblank) return '\0';
1392 if (smushmode & SM_EQUAL) {
1393 if (lch==rch) return lch;
1396 if (smushmode & SM_LOWLINE) {
1397 if (lch=='_' && strchr("|/\\[]{}()<>",rch)) return rch;
1398 if (rch=='_' && strchr("|/\\[]{}()<>",lch)) return lch;
1401 if (smushmode & SM_HIERARCHY) {
1402 if (lch=='|' && strchr("/\\[]{}()<>",rch)) return rch;
1403 if (rch=='|' && strchr("/\\[]{}()<>",lch)) return lch;
1404 if (strchr("/\\",lch) && strchr("[]{}()<>",rch)) return rch;
1405 if (strchr("/\\",rch) && strchr("[]{}()<>",lch)) return lch;
1406 if (strchr("[]",lch) && strchr("{}()<>",rch)) return rch;
1407 if (strchr("[]",rch) && strchr("{}()<>",lch)) return lch;
1408 if (strchr("{}",lch) && strchr("()<>",rch)) return rch;
1409 if (strchr("{}",rch) && strchr("()<>",lch)) return lch;
1410 if (strchr("()",lch) && strchr("<>",rch)) return rch;
1411 if (strchr("()",rch) && strchr("<>",lch)) return lch;
1414 if (smushmode & SM_PAIR) {
1415 if (lch=='[' && rch==']') return '|';
1416 if (rch=='[' && lch==']') return '|';
1417 if (lch=='{' && rch=='}') return '|';
1418 if (rch=='{' && lch=='}') return '|';
1419 if (lch=='(' && rch==')') return '|';
1420 if (rch=='(' && lch==')') return '|';
1423 if (smushmode & SM_BIGX) {
1424 if (lch=='/' && rch=='\\') return '|';
1425 if (rch=='/' && lch=='\\') return 'Y';
1426 if (lch=='>' && rch=='<') return 'X';
1427 /* Don't want the reverse of above to give 'X'. */
1430 return '\0';
1434 /****************************************************************************
1436 smushamt
1438 Returns the maximum amount that the current character can be smushed
1439 into the current line.
1441 ****************************************************************************/
1443 int smushamt()
1445 int maxsmush,amt;
1446 int row,linebd,charbd;
1447 outchr ch1,ch2;
1449 if ((smushmode & (SM_SMUSH | SM_KERN)) == 0) {
1450 return 0;
1452 maxsmush = currcharwidth;
1453 for (row=0;row<charheight;row++) {
1454 if (right2left) {
1455 if (maxsmush>STRLEN(outputline[row])) {
1456 maxsmush=STRLEN(outputline[row]);
1458 for (charbd=STRLEN(currchar[row]);
1459 ch1=currchar[row][charbd],(charbd>0&&(!ch1||ch1==' '));charbd--) ;
1460 for (linebd=0;ch2=outputline[row][linebd],ch2==' ';linebd++) ;
1461 amt = linebd+currcharwidth-1-charbd;
1463 else {
1464 for (linebd=STRLEN(outputline[row]);
1465 ch1 = outputline[row][linebd],(linebd>0&&(!ch1||ch1==' '));linebd--) ;
1466 for (charbd=0;ch2=currchar[row][charbd],ch2==' ';charbd++) ;
1467 amt = charbd+outlinelen-1-linebd;
1469 if (!ch1||ch1==' ') {
1470 amt++;
1472 else if (ch2) {
1473 if (smushem(ch1,ch2)!='\0') {
1474 amt++;
1477 if (amt<maxsmush) {
1478 maxsmush = amt;
1481 return maxsmush;
1485 /****************************************************************************
1487 addchar
1489 Attempts to add the given character onto the end of the current line.
1490 Returns 1 if this can be done, 0 otherwise.
1492 ****************************************************************************/
1494 int addchar(c)
1495 inchr c;
1497 int smushamount,row,k,column;
1498 outchr *templine;
1500 getletter(c);
1501 smushamount = smushamt();
1502 if (outlinelen+currcharwidth-smushamount>outlinelenlimit
1503 ||inchrlinelen+1>inchrlinelenlimit) {
1504 return 0;
1507 templine = (outchr*)myalloc(sizeof(outchr)*(outlinelenlimit+1));
1508 for (row=0;row<charheight;row++) {
1509 if (right2left) {
1510 STRCPY(templine,currchar[row]);
1511 for (k=0;k<smushamount;k++) {
1512 templine[currcharwidth-smushamount+k] =
1513 smushem(templine[currcharwidth-smushamount+k],outputline[row][k]);
1515 STRCAT(templine,outputline[row]+smushamount);
1516 STRCPY(outputline[row],templine);
1518 else {
1519 for (k=0;k<smushamount;k++) {
1520 column = outlinelen-smushamount+k;
1521 if (column < 0) {
1522 column = 0;
1524 outputline[row][column] =
1525 smushem(outputline[row][column],currchar[row][k]);
1527 STRCAT(outputline[row],currchar[row]+smushamount);
1530 free(templine);
1531 outlinelen = STRLEN(outputline[0]);
1532 inchrline[inchrlinelen++] = c;
1533 return 1;
1537 /****************************************************************************
1539 putstring
1541 Prints out the given null-terminated string, substituting blanks
1542 for hardblanks. If outputwidth is 1, prints the entire string;
1543 otherwise prints at most outputwidth-1 characters. Prints a newline
1544 at the end of the string. The string is left-justified, centered or
1545 right-justified (taking outputwidth as the screen width) if
1546 justification is 0, 1 or 2, respectively.
1548 ****************************************************************************/
1550 void putstring(string)
1551 outchr *string;
1553 int i,len;
1554 char c[10];
1555 #ifdef TLF_FONTS
1556 size_t size;
1557 wchar_t wc[2];
1558 #endif
1560 len = STRLEN(string);
1561 if (outputwidth>1) {
1562 if (len>outputwidth-1) {
1563 len = outputwidth-1;
1565 if (justification>0) {
1566 for (i=1;(3-justification)*i+len+justification-2<outputwidth;i++) {
1567 putchar(' ');
1571 for (i=0;i<len;i++) {
1572 #ifdef TLF_FONTS
1573 wc[0] = string[i];
1574 wc[1] = 0;
1575 size = wchar_to_utf8(wc,1,c,10,0);
1576 if(size==1) {
1577 if(c[0]==hardblank) {
1578 c[0] = ' ';
1581 c[size] = 0;
1582 printf("%s",c);
1583 #else
1584 putchar(string[i]==hardblank?' ':string[i]);
1585 #endif
1587 putchar('\n');
1591 /****************************************************************************
1593 printline
1595 Prints outputline using putstring, then clears the current line.
1597 ****************************************************************************/
1599 void printline()
1601 int i;
1603 for (i=0;i<charheight;i++) {
1604 putstring(outputline[i]);
1606 clearline();
1610 /****************************************************************************
1612 splitline
1614 Splits inchrline at the last word break (bunch of consecutive blanks).
1615 Makes a new line out of the first part and prints it using
1616 printline. Makes a new line out of the second part and returns.
1618 ****************************************************************************/
1620 void splitline()
1622 int i,gotspace,lastspace,len1,len2;
1623 inchr *part1,*part2;
1625 part1 = (inchr*)myalloc(sizeof(inchr)*(inchrlinelen+1));
1626 part2 = (inchr*)myalloc(sizeof(inchr)*(inchrlinelen+1));
1627 gotspace = 0;
1628 lastspace = inchrlinelen-1;
1629 for (i=inchrlinelen-1;i>=0;i--) {
1630 if (!gotspace && inchrline[i]==' ') {
1631 gotspace = 1;
1632 lastspace = i;
1634 if (gotspace && inchrline[i]!=' ') {
1635 break;
1638 len1 = i+1;
1639 len2 = inchrlinelen-lastspace-1;
1640 for (i=0;i<len1;i++) {
1641 part1[i] = inchrline[i];
1643 for (i=0;i<len2;i++) {
1644 part2[i] = inchrline[lastspace+1+i];
1646 clearline();
1647 for (i=0;i<len1;i++) {
1648 addchar(part1[i]);
1650 printline();
1651 for (i=0;i<len2;i++) {
1652 addchar(part2[i]);
1654 free(part1);
1655 free(part2);
1659 /****************************************************************************
1661 handlemapping
1663 Given an input character (type inchr), executes re-mapping commands
1664 read from control files. Returns re-mapped character (inchr).
1666 ****************************************************************************/
1668 inchr handlemapping(c)
1669 inchr c;
1671 comnode *cmptr;
1673 cmptr=commandlist;
1674 while (cmptr!=NULL) {
1675 if (cmptr->thecommand ?
1676 (c >= cmptr->rangelo && c <= cmptr->rangehi) : 0) {
1677 c += cmptr->offset;
1678 while(cmptr!=NULL ? cmptr->thecommand : 0) {
1679 cmptr=cmptr->next;
1682 else {
1683 cmptr=cmptr->next;
1686 return c;
1689 /****************************************************************************
1691 Agetchar
1693 Replacement to getchar().
1694 Acts exactly like getchar if -A is NOT specified,
1695 else obtains input from All remaining command line words.
1697 ****************************************************************************/
1699 int Agetchar()
1701 extern int optind; /* current argv[] element under study */
1702 static int AgetMode = 0; /* >= 0 for displacement into argv[n], <0 EOF */
1703 char *arg; /* pointer to active character */
1704 int c; /* current character */
1706 if ( ! cmdinput ) /* is -A active? */
1707 return( getchar() ); /* no: return stdin character */
1709 if ( AgetMode < 0 || optind >= Myargc ) /* EOF is sticky: */
1710 return( EOF ); /* **ensure it now and forever more */
1712 /* find next character */
1713 arg = Myargv[optind]; /* pointer to active arg */
1714 c = arg[AgetMode++]&0xFF; /* get appropriate char of arg */
1716 if ( ! c ) /* at '\0' that terminates word? */
1717 { /* at end of word: return ' ' if normal word, '\n' if empty */
1718 c = ' '; /* suppose normal word and return blank */
1719 if ( AgetMode == 1 ) /* if ran out in very 1st char, force \n */
1720 c = '\n'; /* (allows "hello '' world" to do \n at '') */
1721 AgetMode = 0; /* return to char 0 in NEXT word */
1722 if ( ++optind >= Myargc ) /* run up word count and check if at "EOF" */
1723 { /* just ran out of arguments */
1724 c = EOF; /* return EOF */
1725 AgetMode = -1; /* ensure all future returns return EOF */
1729 return( c ); /* return appropriate character */
1731 } /* end: Agetchar() */
1734 /****************************************************************************
1736 iso2022
1738 Called by getinchr. Interprets ISO 2022 sequences
1740 ******************************************************************************/
1742 inchr iso2022()
1744 inchr ch;
1745 inchr ch2;
1746 int save_gl;
1747 int save_gr;
1749 ch = Agetchar();
1750 if (ch == EOF) return ch;
1751 if (ch == 27) ch = Agetchar() + 0x100; /* ESC x */
1752 if (ch == 0x100 + '$') ch = Agetchar() + 0x200; /* ESC $ x */
1753 switch (ch) {
1754 case 14: /* invoke G1 into GL */
1755 gl = 1;
1756 return iso2022();
1757 case 15: /* invoke G0 into GL */
1758 gl = 0;
1759 return iso2022();
1760 case 142: case 'N' + 0x100: /* invoke G2 into GL for next char */
1761 save_gl = gl; save_gr = gr;
1762 gl = gr = 2;
1763 ch = iso2022();
1764 gl = save_gl; gr = save_gr;
1765 return ch;
1766 case 143: case 'O' + 0x100: /* invoke G3 into GL for next char */
1767 save_gl = gl; save_gr = gr;
1768 gl = gr = 3;
1769 ch = iso2022();
1770 gl = save_gl; gr = save_gr;
1771 return ch;
1772 case 'n' + 0x100: /* invoke G2 into GL */
1773 gl = 2;
1774 return iso2022();
1775 case 'o' + 0x100: /* invoke G3 into GL */
1776 gl = 3;
1777 return iso2022();
1778 case '~' + 0x100: /* invoke G1 into GR */
1779 gr = 1;
1780 return iso2022();
1781 case '}' + 0x100: /* invoke G2 into GR */
1782 gr = 2;
1783 return iso2022();
1784 case '|' + 0x100: /* invoke G3 into GR */
1785 gr = 3;
1786 return iso2022();
1787 case '(' + 0x100: /* set G0 to 94-char set */
1788 ch = Agetchar();
1789 if (ch == 'B') ch = 0; /* ASCII */
1790 gn[0] = ch << 16;
1791 gndbl[0] = 0;
1792 return iso2022();
1793 case ')' + 0x100: /* set G1 to 94-char set */
1794 ch = Agetchar();
1795 if (ch == 'B') ch = 0;
1796 gn[1] = ch << 16;
1797 gndbl[1] = 0;
1798 return iso2022();
1799 case '*' + 0x100: /* set G2 to 94-char set */
1800 ch = Agetchar();
1801 if (ch == 'B') ch = 0;
1802 gn[2] = ch << 16;
1803 gndbl[2] = 0;
1804 return iso2022();
1805 case '+' + 0x100: /* set G3 to 94-char set */
1806 ch = Agetchar();
1807 if (ch == 'B') ch = 0;
1808 gn[3] = ch << 16;
1809 gndbl[3] = 0;
1810 return iso2022();
1811 case '-' + 0x100: /* set G1 to 96-char set */
1812 ch = Agetchar();
1813 if (ch == 'A') ch = 0; /* Latin-1 top half */
1814 gn[1] = (ch << 16) | 0x80;
1815 gndbl[1] = 0;
1816 return iso2022();
1817 case '.' + 0x100: /* set G2 to 96-char set */
1818 ch = Agetchar();
1819 if (ch == 'A') ch = 0;
1820 gn[2] = (ch << 16) | 0x80;
1821 gndbl[2] = 0;
1822 return iso2022();
1823 case '/' + 0x100: /* set G3 to 96-char set */
1824 ch = Agetchar();
1825 if (ch == 'A') ch = 0;
1826 gn[3] = (ch << 16) | 0x80;
1827 gndbl[3] = 0;
1828 return iso2022();
1829 case '(' + 0x200: /* set G0 to 94 x 94 char set */
1830 ch = Agetchar();
1831 gn[0] = ch << 16;
1832 gndbl[0] = 1;
1833 return iso2022();
1834 case ')' + 0x200: /* set G1 to 94 x 94 char set */
1835 ch = Agetchar();
1836 gn[1] = ch << 16;
1837 gndbl[1] = 1;
1838 return iso2022();
1839 case '*' + 0x200: /* set G2 to 94 x 94 char set */
1840 ch = Agetchar();
1841 gn[2] = ch << 16;
1842 gndbl[2] = 1;
1843 return iso2022();
1844 case '+' + 0x200: /* set G3 to 94 x 94 char set */
1845 ch = Agetchar();
1846 gn[3] = ch << 16;
1847 gndbl[3] = 1;
1848 return iso2022();
1849 default:
1850 if (ch & 0x200) { /* set G0 to 94 x 94 char set (deprecated) */
1851 gn[0] = (ch & ~0x200) << 16;
1852 gndbl[0] = 1;
1853 return iso2022();
1857 if (ch >= 0x21 && ch <= 0x7E) { /* process GL */
1858 if (gndbl[gl]) {
1859 ch2 = Agetchar();
1860 return gn[gl] | (ch << 8) | ch2;
1862 else return gn[gl] | ch;
1864 else if (ch >= 0xA0 && ch <= 0xFF) { /* process GR */
1865 if (gndbl[gr]) {
1866 ch2 = Agetchar();
1867 return gn[gr] | (ch << 8) | ch2;
1869 else return gn[gr] | (ch & ~0x80);
1871 else return ch;
1874 /****************************************************************************
1876 ungetinchr
1878 Called by main. Pushes back an "inchr" to be read by getinchr
1879 on the next call.
1881 ******************************************************************************/
1882 inchr getinchr_buffer;
1883 int getinchr_flag;
1885 inchr ungetinchr(c)
1886 inchr c;
1888 getinchr_buffer = c;
1889 getinchr_flag = 1;
1890 return c;
1893 /*****************************************************************************
1895 getinchr
1897 Called by main. Processes multibyte characters. Invokes Agetchar.
1898 If multibyte = 0, ISO 2022 mode (see iso2022 routine).
1899 If multibyte = 1, double-byte mode (0x00-0x7f bytes are characters,
1900 0x80-0xFF bytes are first byte of a double-byte character).
1901 If multibyte = 2, Unicode UTF-8 mode (0x00-0x7F bytes are characters,
1902 0x80-0xBF bytes are nonfirst byte of a multibyte character,
1903 0xC0-0xFD bytes are first byte of a multibyte character,
1904 0xFE-0xFF bytes are errors (all errors return code 0x0080)).
1905 If multibyte = 3, HZ mode ("~{" starts double-byte mode, "}~" ends it,
1906 "~~" is a tilde, "~x" for all other x is ignored).
1907 If multibyte = 4, Shift-JIS mode (0x80-0x9F and 0xE0-0xEF are first byte
1908 of a double-byte character, all other bytes are characters).
1911 *****************************************************************************/
1913 inchr getinchr()
1915 int ch, ch2, ch3, ch4, ch5, ch6;
1917 if (getinchr_flag) {
1918 getinchr_flag = 0;
1919 return getinchr_buffer;
1922 switch(multibyte) {
1923 case 0: /* single-byte */
1924 return iso2022();
1925 case 1: /* DBCS */
1926 ch = Agetchar();
1927 if ((ch >= 0x80 && ch <= 0x9F) ||
1928 (ch >= 0xE0 && ch <= 0xEF)) {
1929 ch = (ch << 8) + Agetchar();
1931 return ch;
1932 case 2: /* UTF-8 */
1933 ch = Agetchar();
1934 if (ch < 0x80) return ch; /* handles EOF, too */
1935 if (ch < 0xC0 || ch > 0xFD)
1936 return 0x0080; /* illegal first character */
1937 ch2 = Agetchar() & 0x3F;
1938 if (ch < 0xE0) return ((ch & 0x1F) << 6) + ch2;
1939 ch3 = Agetchar() & 0x3F;
1940 if (ch < 0xF0)
1941 return ((ch & 0x0F) << 12) + (ch2 << 6) + ch3;
1942 ch4 = Agetchar() & 0x3F;
1943 if (ch < 0xF8)
1944 return ((ch & 0x07) << 18) + (ch2 << 12) + (ch3 << 6) + ch4;
1945 ch5 = Agetchar() & 0x3F;
1946 if (ch < 0xFC)
1947 return ((ch & 0x03) << 24) + (ch2 << 18) + (ch3 << 12) +
1948 (ch4 << 6) + ch5;
1949 ch6 = Agetchar() & 0x3F;
1950 return ((ch & 0x01) << 30) + (ch2 << 24) + (ch3 << 18) +
1951 (ch4 << 12) + (ch5 << 6) + ch6;
1952 case 3: /* HZ */
1953 ch = Agetchar();
1954 if (ch == EOF) return ch;
1955 if (hzmode) {
1956 ch = (ch << 8) + Agetchar();
1957 if (ch == ('}' << 8) + '~') {
1958 hzmode = 0;
1959 return getinchr();
1961 return ch;
1963 else if (ch == '~') {
1964 ch = Agetchar();
1965 if (ch == '{') {
1966 hzmode = 1;
1967 return getinchr();
1969 else if (ch == '~') {
1970 return ch;
1972 else {
1973 return getinchr();
1976 else return ch;
1977 case 4: /* Shift-JIS */
1978 ch = Agetchar();
1979 if ((ch >= 0x80 && ch <= 0x9F) ||
1980 (ch >= 0xE0 && ch <= 0xEF)) {
1981 ch = (ch << 8) + Agetchar();
1983 return ch;
1984 default:
1985 return 0x80;
1989 /****************************************************************************
1991 main
1993 The main program, of course.
1994 Reads characters 1 by 1 from stdin, and makes lines out of them using
1995 addchar. Handles line breaking, (which accounts for most of the
1996 complexity in this function).
1998 ****************************************************************************/
2000 int main(argc,argv)
2001 int argc;
2002 char *argv[];
2004 inchr c,c2;
2005 int i;
2006 int last_was_eol_flag;
2007 /*---------------------------------------------------------------------------
2008 wordbreakmode:
2009 -1: /^$/ and blanks are to be absorbed (when line break was forced
2010 by a blank or character larger than outlinelenlimit)
2011 0: /^ *$/ and blanks are not to be absorbed
2012 1: /[^ ]$/ no word break yet
2013 2: /[^ ] *$/
2014 3: /[^ ]$/ had a word break
2015 ---------------------------------------------------------------------------*/
2016 int wordbreakmode;
2017 int char_not_added;
2019 Myargc = argc;
2020 Myargv = argv;
2021 getparams();
2022 readcontrolfiles();
2023 readfont();
2024 linealloc();
2026 wordbreakmode = 0;
2027 last_was_eol_flag = 0;
2029 #ifdef TLF_FONTS
2030 toiletfont = 0;
2031 #endif
2033 while ((c = getinchr())!=EOF) {
2035 if (c=='\n'&&paragraphflag&&!last_was_eol_flag) {
2036 ungetinchr(c2 = getinchr());
2037 c = ((isascii(c2)&&isspace(c2))?'\n':' ');
2039 last_was_eol_flag = (isascii(c)&&isspace(c)&&c!='\t'&&c!=' ');
2041 if (deutschflag) {
2042 if (c>='[' && c<=']') {
2043 c = deutsch[c-'['];
2045 else if (c >='{' && c <= '~') {
2046 c = deutsch[c-'{'+3];
2050 c = handlemapping(c);
2052 if (isascii(c)&&isspace(c)) {
2053 c = (c=='\t'||c==' ') ? ' ' : '\n';
2056 if ((c>'\0' && c<' ' && c!='\n') || c==127) continue;
2059 Note: The following code is complex and thoroughly tested.
2060 Be careful when modifying!
2063 do {
2064 char_not_added = 0;
2066 if (wordbreakmode== -1) {
2067 if (c==' ') {
2068 break;
2070 else if (c=='\n') {
2071 wordbreakmode = 0;
2072 break;
2074 wordbreakmode = 0;
2077 if (c=='\n') {
2078 printline();
2079 wordbreakmode = 0;
2082 else if (addchar(c)) {
2083 if (c!=' ') {
2084 wordbreakmode = (wordbreakmode>=2)?3:1;
2086 else {
2087 wordbreakmode = (wordbreakmode>0)?2:0;
2091 else if (outlinelen==0) {
2092 for (i=0;i<charheight;i++) {
2093 if (right2left && outputwidth>1) {
2094 putstring(currchar[i]+STRLEN(currchar[i])-outlinelenlimit);
2096 else {
2097 putstring(currchar[i]);
2100 wordbreakmode = -1;
2103 else if (c==' ') {
2104 if (wordbreakmode==2) {
2105 splitline();
2107 else {
2108 printline();
2110 wordbreakmode = -1;
2113 else {
2114 if (wordbreakmode>=2) {
2115 splitline();
2117 else {
2118 printline();
2120 wordbreakmode = (wordbreakmode==3)?1:0;
2121 char_not_added = 1;
2124 } while (char_not_added);
2127 if (outlinelen!=0) {
2128 printline();
2130 return 0;