1 /*
2  * npc.c -- animation counter main routine
3  *
4  * Copyright (c) 1995-1997 by nir@mxa.meshnet.or.jp
5  *
6  * This file is part of npc.cgi source tree.
7  * npc.cgi is free software; you can redistribute it and/or modify it
8  * for any purpose.
9  *
10  * @(#)$Id: npc.c,v 2.9 1997/12/11 14:07:34 nir Rel $
11  */
12 
13 /*
14  * $Log: npc.c,v $
15  * Revision 2.9  1997/12/11 14:07:34  nir
16  * Add left, top and disposal_method elements to struct IMAGE.
17  * In the GIF animation mode, only the rewriting digit(s) is output.
18  *
19  * Revision 2.8  1997/11/10 16:19:05  nir
20  * Modify error_out() message.
21  *
22  * Revision 2.7  1997/11/06 19:51:52  nir
23  * Add MODE initialization.
24  *
25  * Revision 2.6  1997/11/05 12:09:15  nir
26  * Add Copyright, Id and Log.
27  * Change animation mode default from server push to 89aGIF animation.
28  *
29  */
30 
31 #include "npc.h"
32 
33 INTERN void initialize(void);
34 INTERN void select_num(void);
35 INTERN void set_num(void);
36 INTERN void arg2num(char *str);
37 INTERN void number_out(DIGITS *digits, int increment);
38 INTERN void gif_animation_out(DIGITS *digits, int width);
39 INTERN void server_push_out(DIGITS *digits, int width);
40 INTERN void static_set(DIGITS *digits, int width, int add_lines);
41 INTERN void msleep(int msec);
42 INTERN void set_image(int row, int top);
43 INTERN void global_save(void);
44 INTERN void local_save(void);
45 INTERN void header_save(void);
46 INTERN void logical_screen_save(void);
47 INTERN void global_palette_save(void);
48 INTERN void graphic_control_extension_save(void);
49 INTERN void image_descriptor_save(void);
50 INTERN void image_data_save(void);
51 INTERN void scale_out(unsigned int line);
52 INTERN void trailer_save(void);
53 
54 GLOBAL IMAGE	image;
55 GLOBAL IMAGE	numbers;
56 GLOBAL GCONTROL	gcontrol;
57 GLOBAL ENV	env;
58 
59 
main(int argc,char ** argv)60 int main(int argc, char **argv) {
61 	FILE *index_file;
62 
63 	initialize();
64 	get_environ(argc, argv);
65 	index_file = open_index();
66 	get_config(index_file);
67 	if (gcontrol.restriction > 1)
68 		error_out("No Permission To Use This Counter");
69 	get_param();
70 	update_count(index_file);
71 	fclose(index_file);
72 	select_num();
73 	set_num();
74 	return(0);
75 }
76 
77 
initialize(void)78 INTERN void initialize(void) {
79 	env.index_dir = INDEX_DIR;
80 	env.index = INDEX_FILE;
81 	gcontrol.random = NO;
82 	gcontrol.progress = NO;
83 	gcontrol.gif_animation = YES;
84 	gcontrol.width = DEFAULT_WIDTH;
85 	gcontrol.unit = 0;
86 	gcontrol.offset = 0;
87 	gcontrol.number = BAD;
88 	gcontrol.increment = 1;
89 	gcontrol.restriction = 0;
90 	gcontrol.mode = 0;
91 	gcontrol.digits = 0;
92 	gcontrol.initial = 1;
93 	gcontrol.transparent = BAD;
94 	gcontrol.delay[0] = gcontrol.delay[1] = 0;
95 	gcontrol.color[0] = gcontrol.color[1] = (long)BAD;
96 	gcontrol.location = NULL;
97 	srand(time(NULL));
98 #if defined(_WIN32)
99 	_setmode(fileno(stdout), _O_BINARY);
100 #endif
101 }
102 
103 
select_num(void)104 INTERN void select_num(void) {
105 	unsigned char color[2][3];
106 	unsigned char *p;
107 	int i, j, n;
108 	long l;
109 
110 	memcpy(&numbers, get_digits(gcontrol.digits), sizeof(IMAGE));
111 	if ((gcontrol.transparent >= 0)
112 	 && (! numbers.transparent_color_flag)) {
113 		numbers.version = GIF89A;
114 		numbers.transparent_color_flag = YES;
115 		numbers.transparent_color = gcontrol.transparent;
116 	}
117 	if (gcontrol.color[0] < 0L)
118 		return;
119 	for (i = 0; i < 2; i++) {
120 		l = gcontrol.color[i];
121 		for (j = 2; j >= 0; j--) {
122 			color[i][j] = (unsigned char)(l & 0xFF);
123 			l >>= 8;
124 		}
125 	}
126 	for (n = 0; n < (2 << numbers.p_size); n++) {
127 		p = numbers.color_table + 3 * n;
128 		i = ((int)p[0] * 77
129 		   + (int)p[1] * 151
130 		   + (int)p[2] * 29) / 0x100;	/* i = 0 .. 255 */
131 		for (j = 0; j < 3; j++) {
132 			p[j] = (color[0][j] * i + color[1][j] * (255 - i)) / 255;
133 		}
134 	}
135 }
136 
137 
set_num(void)138 INTERN void set_num(void) {
139 	DIGITS digits[MAX_WIDTH];
140 	int c, n, number, previous, increment;
141 
142 	if (gcontrol.random) {
143 		number = rand();
144 	} else if (gcontrol.number < 0) {
145 		error_out("INTERNAL ERROR: set_num: No Number Setting");
146 	} else {
147 		number = gcontrol.number;
148 	}
149 	increment = (gcontrol.progress) ? gcontrol.increment : 0;
150 	previous = number - increment;
151 	for (n = 0; n < MAX_WIDTH; n++) {
152 		digits[n].number = c = previous % 10;
153 		digits[n].increment = (c == number % 10) ? 0 : increment;
154 		number /= 10;
155 		previous /= 10;
156 	}
157 	number_out(digits, increment);
158 }
159 
160 
number_out(DIGITS * digits,int increment)161 INTERN void number_out(DIGITS *digits, int increment) {
162 	int width;
163 
164 	width = gcontrol.width;
165 	if (width <= 0)
166 		width = 1;
167 	if (width > MAX_WIDTH)
168 		width = MAX_WIDTH;
169 	memcpy(&image, &numbers, sizeof(IMAGE));
170 	image.left = image.top = 0;
171 	image.width = numbers.width * width;
172 	image.height = (gcontrol.unit > 0) ? gcontrol.unit : numbers.height / 10;
173 	image.interlace_flag = NO;
174 	image.disposal_method = 2;	/* Restore to background color */
175 	if ((image.pixel = (unsigned char *)malloc(image.width * image.height)) == NULL)
176 		error_out("number_out: Cannot Allocate Memory");
177 	memset(image.pixel, image.background, image.width * image.height);
178 	if (increment == 0) {
179 		static_set(digits, width, 0);
180 		save_main();
181 	} else if (gcontrol.gif_animation) {
182 		gif_animation_out(digits, width);
183 	} else {
184 		server_push_out(digits, width);
185 	}
186 	free(image.pixel);
187 }
188 
189 
gif_animation_out(DIGITS * digits,int width)190 INTERN void gif_animation_out(DIGITS *digits, int width) {
191 	unsigned int line;
192 	int n;
193 
194 	image.version = GIF89A;
195 	header_save();
196 	global_save();
197 	static_set(digits, width, 0);
198 	image.delay = 0;
199 	image.disposal_method = 0;
200 	local_save();
201 	for (n = 1; n < width; n++) {
202 		if (digits[n].increment == 0)
203 			break;
204 	}
205 	image.left = numbers.width * (width - n);
206 	image.width = numbers.width * n;
207 	image.disposal_method = 2;
208 	for (line = 0; line <= numbers.height / 10; line++) {
209 		static_set(digits, n, line);
210 		image.delay = gcontrol.delay[(line == 0) ? 0 : 1] / 10;
211 		local_save();
212 	}
213 	trailer_save();
214 }
215 
216 
server_push_out(DIGITS * digits,int width)217 INTERN void server_push_out(DIGITS *digits, int width) {
218 	unsigned int line, msec;
219 
220 	if (env.nph)
221 		fputs("HTTP/1.0 200 OK\n", stdout);
222 	printf("Content-type: multipart/x-mixed-replace;boundary=%s\n\n", BOUNDARY);
223 	for (line = 0; line <= numbers.height / 10; line++) {
224 		printf("--%s\n", BOUNDARY);
225 		static_set(digits, width, line);
226 		save_main();
227 		msec = gcontrol.delay[(line == 0) ? 0 : 1];
228 		fputs("\n", stdout);
229 		if (msec > 0) {
230 			fflush(stdout);
231 			msleep(msec);
232 		}
233 	}
234 	printf("--%s--\n", BOUNDARY);
235 }
236 
237 
msleep(int msec)238 INTERN void msleep(int msec) {
239 #if defined(_WIN32)
240 	Sleep(msec);
241 #else
242 	struct timeval timeout;
243 
244 	timeout.tv_sec = msec / 1000;
245 	timeout.tv_usec = (msec % 1000) * 1000;
246 	select(0, 0, 0, 0, &timeout);
247 #endif
248 }
249 
250 
static_set(DIGITS * digits,int width,int add_lines)251 INTERN void static_set(DIGITS *digits, int width, int add_lines) {
252 	int n;
253 
254 	for (n = 0; n < width; n++) {
255 		set_image(width - n - 1,
256 			digits[n].number * numbers.height / 10
257 			+ digits[n].increment * add_lines
258 			+ gcontrol.offset);
259 	}
260 }
261 
262 
set_image(int row,int top)263 INTERN void set_image(int row, int top) {
264 	unsigned int y;
265 
266 	for (y = 0; y < image.height; y++) {
267 		memcpy(image.pixel
268 			+ numbers.width * row
269 			+ image.width * y,
270 			numbers.pixel
271 			+ numbers.width
272 			 * ((top + y + numbers.height) % numbers.height),
273 			numbers.width);
274 	}
275 }
276 
277 
save_main(void)278 void save_main(void) {
279 	header_save();
280 	global_save();
281 	local_save();
282 	trailer_save();
283 }
284 
285 
global_save(void)286 INTERN void global_save(void) {
287 	logical_screen_save();
288 	global_palette_save();
289 }
290 
291 
local_save(void)292 INTERN void local_save(void) {
293 	graphic_control_extension_save();
294 	image_descriptor_save();
295 	image_data_save();
296 }
297 
298 
header_save(void)299 INTERN void header_save(void) {
300 	if (env.nph)
301 		fputs("HTTP/1.0 200 OK\n", stdout);
302 	fputs("Content-type: image/gif\n\n", stdout);
303 
304 	gwrite((unsigned char *)((image.version == GIF87A) ? "GIF87a" : "GIF89a"), 6);
305 }
306 
307 
logical_screen_save(void)308 INTERN void logical_screen_save(void) {
309 	int bits;
310 
311 	gputw(image.width);
312 	gputw(image.height);
313 	bits = 0x00;
314 	if (image.color_table_flag)
315 		bits |= 0x80;
316 	bits |= (image.s_size << 4);
317 	if (image.sort_flag)
318 		bits |= 0x08;
319 	bits |= image.p_size;
320 	gputc(bits);
321 	gputc((image.transparent_color_flag) ? image.transparent_color : image.background);
322 	gputc(image.aspect);
323 }
324 
325 
global_palette_save(void)326 INTERN void global_palette_save(void) {
327 	gwrite(image.color_table, 3 * (2 << image.p_size));
328 }
329 
330 
graphic_control_extension_save(void)331 INTERN void graphic_control_extension_save(void) {
332 	if ((image.version == GIF87A)
333 	 || ((image.transparent_color_flag == NO) && (image.delay == 0)))
334 		return;
335 	gputc(CODE_EXT);
336 	gputc(CODE_CTRL_EXT);
337 	gputc(4);			/* block size after this field */
338 	gputc((image.disposal_method << 2) + image.transparent_color_flag);
339 					/* packed field */
340 	gputw(image.delay);		/* delay time */
341 	gputc(image.transparent_color);	/* transparent color index */
342 	gputc(0);			/* block terminator */
343 }
344 
345 
image_descriptor_save(void)346 INTERN void image_descriptor_save(void) {
347 	gputc(CODE_IMAGE);
348 	gputw(image.left);
349 	gputw(image.top);
350 	gputw(image.width);
351 	gputw(image.height);
352 	gputc(image.interlace_flag << 6);
353 }
354 
355 
image_data_save(void)356 INTERN void image_data_save(void) {
357 	gputc(image.s_size + 1);
358 	convert(image.s_size + 1, image.pixel, image.width * image.height);
359 }
360 
361 
trailer_save(void)362 INTERN void trailer_save(void) {
363 	gputc(CODE_TRAILER);
364 }
365 
366