aboutsummaryrefslogtreecommitdiff
path: root/ponysaytruncater.c
blob: 9c7191e1607d87dafcba911667c067653fb500f1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
/**
 * ponysaytruncater — Output truncater used by ponysay to stop large ponies from being printed badly.
 * 
 * DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
 * See COPYING for details
 */
#include <stdio.h>


#define  String   char*
#define  boolean  char

#define  true     1
#define  false    0



/**
 * Stdin file descriptor ID
 */
#define  STDIN  0



/**
 * The number of columns on the current line
 */
static int x =  0;

/**
 * Escape sequence state
 */
static int esc = 0;

/**
 * Last bytes as written
 */
static boolean ok = true;




void write(char b, int width);
int toInt(String string);



/**
 * <p>Mane method!</p>
 * <p>
 *   The only argument, in addition to the executed file,
 *   should be the width of the terminal which you get by
 *   adding <code>`tput cols || echo 0`</code> as and argument.
 * </p>
 * 
 * @param  argc  The number of startup arguments
 * @param  argv  The startup arguments, the first is the file itself
 * 
 * @author  Mattias Andrée, maandree@kth.se
 */
void main(int argc, String* argv)
{
    int width = 0;
    if (argc > 1)
        width = toInt(*(argv + 1));
    
    char b = 0;
    
    if (width > 15) //sanity
	while (read(STDIN, &b, 1))
	    write(b, width);
    else
	while (read(STDIN, &b, 1))
	    printf("%c", b);
}


/**
 * Writes a character to stdout, iff it fits within the terminal
 * 
 * @param  b      The character (byte) to write
 * @param  width  The width of the terminal
 */
void write(char b, int width)
{
    int i;
    char nx;
    
    if (esc == 0)
    {
        if (b == '\n')
	{
	    if (x >= width)
	    {
	        // Reset background colour
	        write('\e', width);
		write('[', width);
		write('4', width);
		write('9', width);
		write('m', width);
	    }
	    x = -1;
	}
	else if (b == '\t')
	{
	    // Tab to next pos ≡₈ 0
	    nx = 8 - (x & 7);
	    for (i = 0; i < nx; i++)
	        write(' ', width);
	    return; //(!)
	}
	else if (b == '\e')
	    esc = 1;
    }
    else if (esc == 1)
    {
        if      (b == '[')  esc = 2;  //CSI ends with a letter, m is for colour
	else if (b == ']')  esc = 3;  //OSI, OSI P is for palett editing in Linux VT
	else                esc = 10; //Nothing to see here, move along
    }
    else if (esc == 2)
    {
	if ((('a' <= b) && (b <= 'z')) || (('A' <= b) && (b <= 'Z')))
	    esc = 10;
    }
    else if ((esc == 3) && (b == 'P'))
    {
	esc = ~0;
    }
    else if (esc < 0)
    {
        esc--;
	if (esc == ~7)
	    esc = 10;
    }
    else
        esc = 10;
              
    if (                                // Can be printed:
	(x < width) ||                  //       within bounds   ∨ 
	(esc != 0) ||                   //     ∨ escape sequence ∨
	(ok && ((b & 0xC0) == 0x80)))   //     ∨ last with printed ∧ not first byte in character
    {
	printf("%c", b);
	if ((esc == 0) && ((b & 0xC0) != 0x80))  // Count up columns of not in escape sequnce and
	    x++;                                 // the byte is not the first byte in the character
	ok = true;
    }
    else
        ok = false;
	
    if (esc == 10)
        esc = 0;
}


/**
 * Converts a string to an integer
 * 
 * @param   string  The string to convert
 * @return          The integer represented by the string
 */
int toInt(String string)
{
    int rc = 0;
    String str = string;
    char c = 0;
    
    while ((c = *str++) != 0)
        rc = (rc << 1) + (rc << 3) - (c & 15);
    
    return -rc;
}