1 | /* |
---|
2 | * postcodeine.c: |
---|
3 | * Given a postcode prefix, plot a map of all the postcodes matching that |
---|
4 | * prefix. |
---|
5 | * |
---|
6 | * Copyright (c) 2006 UK Citizens Online Democracy. All rights reserved. |
---|
7 | * Email: chris@mysociety.org; WWW: http://www.mysociety.org/ |
---|
8 | * |
---|
9 | * According to http://www.mysociety.org/?p=109 this is licenced under |
---|
10 | * the Affero GPL http://www.affero.org/oagpl.html |
---|
11 | * |
---|
12 | * Original code: http://bitter.ukcod.org.uk/~chris/postcodeine/code/ |
---|
13 | * |
---|
14 | * Changed 2006/11/01 David Sheldon to change the paths where it keeps data, and |
---|
15 | * to change the input format it requires. Also removed the osni |
---|
16 | * projection as we don't have NI postcodes and it was giving me |
---|
17 | * proj errors. Also actually parse the IF_MODIFIED_SINCE header, |
---|
18 | * and set the expires to 12 hours rather than 1 year. |
---|
19 | * (sorry about my C). |
---|
20 | */ |
---|
21 | |
---|
22 | static const char rcsid[] = "$Id: postcodeine.c,v 1.5 2006/01/19 19:03:23 chris Exp chris $"; |
---|
23 | #define _XOPEN_SOURCE |
---|
24 | |
---|
25 | |
---|
26 | #include <sys/types.h> |
---|
27 | |
---|
28 | #include <ctype.h> |
---|
29 | #include <dirent.h> |
---|
30 | #include <errno.h> |
---|
31 | #include <fcgi_stdio.h> |
---|
32 | #include <proj_api.h> |
---|
33 | #include <stdbool.h> |
---|
34 | #include <stdint.h> |
---|
35 | #include <stdio.h> |
---|
36 | #include <stdlib.h> |
---|
37 | #include <string.h> |
---|
38 | |
---|
39 | |
---|
40 | #include <time.h> |
---|
41 | #include <unistd.h> |
---|
42 | |
---|
43 | #include <sys/stat.h> |
---|
44 | |
---|
45 | #include "postcodeine.h" |
---|
46 | |
---|
47 | #define MIN_LON -11. |
---|
48 | #define MAX_LON 2. |
---|
49 | #define MIN_LAT 49.5 |
---|
50 | #define MAX_LAT 59.5 |
---|
51 | |
---|
52 | #define POSTCODE_CACHE "/tmp/postcodes.dat" |
---|
53 | #define POSTCODE_DIR "/tmp/postcodes/" |
---|
54 | #define IMAGE_CACHE_DIR "/tmp" |
---|
55 | |
---|
56 | #define die(...) do { fprintf(stderr, "postcodeine[%d]: ", (int)getpid()); fprintf(stderr, __VA_ARGS__); fprintf(stderr, "\n"); exit(1); } while (0) |
---|
57 | |
---|
58 | struct image images[] = { |
---|
59 | { 301, 400, NULL }, |
---|
60 | { 902, 1200, NULL }, |
---|
61 | }; |
---|
62 | |
---|
63 | #define NIMAGES 2 |
---|
64 | |
---|
65 | struct postcodepos { |
---|
66 | char postcode[10]; |
---|
67 | double x, y; /* float'd do */ |
---|
68 | }; |
---|
69 | |
---|
70 | /* clear_image I |
---|
71 | * Clear I. */ |
---|
72 | static void clear_image(struct image *I) { |
---|
73 | memset((char*)I->data + I->h * sizeof(pixel*), 0, I->h * I->w * sizeof(pixel)); |
---|
74 | } |
---|
75 | |
---|
76 | /* grid_to_map IRISH E N X Y |
---|
77 | * Convert the grid position (E, N) to an (X, Y) coordinate on the map. If |
---|
78 | * IRISH is true, the Irish grid is used; otherwise OSGB. */ |
---|
79 | static void grid_to_map(const bool is_irish_grid, const int e, const int n, double *x, double *y) { |
---|
80 | static const char *osgb_params[] = { "init=world:bng", NULL }, |
---|
81 | *merc_params[] = { "proj=merc", "ellps=WGS84", NULL }; |
---|
82 | static projPJ osgb, merc; |
---|
83 | projUV c1 = {0}, c2 = {0}; |
---|
84 | static double E0, N0, E1, N1; |
---|
85 | if (!osgb) { |
---|
86 | /* XXX these seem to abort on failure rather than returning an error */ |
---|
87 | if (!(osgb = pj_init(1, (char**)osgb_params))) |
---|
88 | die("%s: pj_init1: %s", osgb_params[0], pj_strerrno(pj_errno)); |
---|
89 | if (!(merc = pj_init(2, (char**)merc_params))) |
---|
90 | die("%s: pj_init3: %s %s", merc_params[0], merc_params[1], pj_strerrno(pj_errno)); |
---|
91 | |
---|
92 | /* Figure out bounds of the map. */ |
---|
93 | c1.u = MIN_LON * DEG_TO_RAD; |
---|
94 | c1.v = MAX_LAT * DEG_TO_RAD; |
---|
95 | c2 = pj_fwd(c1, merc); |
---|
96 | E0 = c2.u; |
---|
97 | N0 = c2.v; |
---|
98 | |
---|
99 | c1.u = MAX_LON * DEG_TO_RAD; |
---|
100 | c1.v = MIN_LAT * DEG_TO_RAD; |
---|
101 | c2 = pj_fwd(c1, merc); |
---|
102 | E1 = c2.u; |
---|
103 | N1 = c2.v; |
---|
104 | } |
---|
105 | |
---|
106 | /* grid to lat/lon. */ |
---|
107 | c1.u = e; |
---|
108 | c1.v = n; |
---|
109 | c2 = pj_inv(c1, osgb); |
---|
110 | |
---|
111 | /* XXX at this point we have a lat/lon registered to the Airy or modified |
---|
112 | * Airy datum. We should transform it to the WGS84 datum using a Helmert |
---|
113 | * transform or whatever, but for this scale it's not really worth the |
---|
114 | * effort. */ |
---|
115 | |
---|
116 | /* lat/lon to Mercator eastings/northings. */ |
---|
117 | c1 = pj_fwd(c2, merc); |
---|
118 | |
---|
119 | *x = (c1.u - E0) / (E1 - E0); |
---|
120 | *y = (c1.v - N0) / (N1 - N0); |
---|
121 | } |
---|
122 | |
---|
123 | struct pcindex { |
---|
124 | int first, last; |
---|
125 | }; |
---|
126 | |
---|
127 | static int tobase36(const char c) { |
---|
128 | if (tolower(c) >= 'a' && tolower(c) <= 'z') |
---|
129 | return tolower(c) - 'a'; |
---|
130 | else if (c >= '0' && c <= '9') |
---|
131 | return 26 + c - '0'; |
---|
132 | else |
---|
133 | die("bad character '%c'", c); |
---|
134 | } |
---|
135 | |
---|
136 | static bool fread_all(void *buf, const size_t n, FILE *fp) { |
---|
137 | return fread(buf, 1, n, fp) == n; |
---|
138 | } |
---|
139 | |
---|
140 | static bool fwrite_all(const void *buf, const size_t n, FILE *fp) { |
---|
141 | return fwrite(buf, 1, n, fp) == n; |
---|
142 | } |
---|
143 | |
---|
144 | static struct postcodepos *read_postcodes_cached(const char *dir, size_t *npcs, struct pcindex i1[36], struct pcindex i2[36][36], struct pcindex i3[36][36][36]) { |
---|
145 | FILE *fp; |
---|
146 | struct postcodepos *pp; |
---|
147 | int a, b, c; |
---|
148 | struct pcindex ind; |
---|
149 | |
---|
150 | if (!(fp = fopen(POSTCODE_CACHE, "r"))) |
---|
151 | return NULL; |
---|
152 | |
---|
153 | fprintf(stderr, "postcodeine[%d]: reading cached postcode data\n", (int)getpid()); |
---|
154 | |
---|
155 | if (!fread_all(npcs, sizeof *npcs, fp)) |
---|
156 | die("%s: read: %s", POSTCODE_CACHE, strerror(errno)); |
---|
157 | |
---|
158 | pp = malloc(*npcs * sizeof *pp); |
---|
159 | |
---|
160 | if (!fread_all(pp, *npcs * sizeof *pp, fp)) |
---|
161 | die("%s: read: %s", POSTCODE_CACHE, strerror(errno)); |
---|
162 | |
---|
163 | for (a = 0; a < 36; ++a) { |
---|
164 | if (!fread_all(&ind, sizeof(struct pcindex), fp)) |
---|
165 | die("%s: read: %s", POSTCODE_CACHE, strerror(errno)); |
---|
166 | i1[a] = ind; |
---|
167 | for (b = 0; b < 36; ++b) { |
---|
168 | if (!fread_all(&ind, sizeof(struct pcindex), fp)) |
---|
169 | die("%s: read: %s", POSTCODE_CACHE, strerror(errno)); |
---|
170 | i2[a][b] = ind; |
---|
171 | for (c = 0; c < 36; ++c) { |
---|
172 | if (!fread_all(&ind, sizeof(struct pcindex), fp)) |
---|
173 | die("%s: read: %s", POSTCODE_CACHE, strerror(errno)); |
---|
174 | i3[a][b][c] = ind; |
---|
175 | } |
---|
176 | } |
---|
177 | } |
---|
178 | |
---|
179 | fclose(fp); |
---|
180 | |
---|
181 | return pp; |
---|
182 | } |
---|
183 | |
---|
184 | /* read_postcodes DIRECTORY |
---|
185 | * Read CSV files containing postcodes and their positions from DIRECTORY. */ |
---|
186 | static struct postcodepos *read_postcodes(const char *dir, size_t *npcs, struct pcindex i1[36], struct pcindex i2[36][36], struct pcindex i3[36][36][36]) { |
---|
187 | DIR *D; |
---|
188 | struct postcodepos *pp; |
---|
189 | size_t n = 0, nalloc; |
---|
190 | struct dirent *e; |
---|
191 | char fn[256]; |
---|
192 | FILE *fp; |
---|
193 | int a, b, c; |
---|
194 | struct pcindex ind; |
---|
195 | |
---|
196 | if ((pp = read_postcodes_cached(dir, npcs, i1, i2, i3))) |
---|
197 | return pp; |
---|
198 | |
---|
199 | if (!(D = opendir(dir))) |
---|
200 | die("%s: opendir: %s", dir, strerror(errno)); |
---|
201 | pp = malloc((nalloc = 100000) * sizeof *pp); |
---|
202 | while ((e = readdir(D))) { |
---|
203 | char *q; |
---|
204 | char line[256]; |
---|
205 | int linenum; |
---|
206 | |
---|
207 | /* Check that the filename ends ".csv" or ".CSV". */ |
---|
208 | if ((!(q = strstr(e->d_name, ".csv")) |
---|
209 | && !(q = strstr(e->d_name, ".CSV"))) |
---|
210 | || q[4]) |
---|
211 | continue; |
---|
212 | |
---|
213 | sprintf(fn, "%s/%s", dir, e->d_name); |
---|
214 | if (!(fp = fopen(fn, "r"))) |
---|
215 | die("%s: open: %s", fn, strerror(errno)); |
---|
216 | |
---|
217 | linenum = 1; |
---|
218 | while (fgets(line, sizeof line, fp)) { |
---|
219 | char *pc, *p; |
---|
220 | int i, j, E, N; |
---|
221 | |
---|
222 | /* |
---|
223 | * Example line: |
---|
224 | * "AB119NB",395004,805097 |
---|
225 | */ |
---|
226 | |
---|
227 | pc = line + 1; |
---|
228 | p = strchr(pc, '\"'); |
---|
229 | if (!p) die("%s:%d: bad data (no quoted postcode)", fn, linenum); |
---|
230 | *(p++) = 0; |
---|
231 | |
---|
232 | for (i = 0; i < 1; ++i) { |
---|
233 | p = strchr(p, ','); |
---|
234 | if (!p) die("%s:%d: bad data (not enough fields)", fn, linenum); |
---|
235 | ++p; |
---|
236 | } |
---|
237 | |
---|
238 | if (2 != sscanf(p, "%d,%d,", &E, &N)) |
---|
239 | die("%s:%d: bad data (no coordinates)", fn, linenum); |
---|
240 | |
---|
241 | /* Some postcodes have no valid coordinate data. */ |
---|
242 | if (!E && !N) |
---|
243 | continue; |
---|
244 | |
---|
245 | if (n == nalloc) |
---|
246 | pp = realloc(pp, (nalloc += 1000000) * sizeof *pp); |
---|
247 | |
---|
248 | for (i = 0, j = 0; pc[i]; ++i) |
---|
249 | if (pc[i] != ' ') |
---|
250 | pp[n].postcode[j++] = pc[i]; |
---|
251 | pp[n].postcode[j] = 0; |
---|
252 | |
---|
253 | if (pp[n].postcode[strspn(pp[n].postcode, "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789")]) |
---|
254 | die("%s:%d: postcode \"%s\" contains invalid characters", fn, linenum, pp[n].postcode); |
---|
255 | |
---|
256 | grid_to_map(pp[n].postcode[0] == 'B' && pp[n].postcode[1] == 'T', E, N, &pp[n].x, &pp[n].y); |
---|
257 | |
---|
258 | if (n > 0 && strcmp(pp[n - 1].postcode, pp[n].postcode) >= 0) |
---|
259 | die("%s:%d: postcode \"%s\" does not follow previous postcode \"%s\" lexically", fn, linenum, pp[n].postcode, pp[n - 1].postcode); |
---|
260 | |
---|
261 | // puts(pp[n].postcode); |
---|
262 | a = tobase36(pp[n].postcode[0]); |
---|
263 | b = tobase36(pp[n].postcode[1]); |
---|
264 | c = tobase36(pp[n].postcode[2]); |
---|
265 | |
---|
266 | if (i1[a].first == -1) { |
---|
267 | i1[a].first = (int)n; |
---|
268 | } |
---|
269 | i1[a].last = (int)n; |
---|
270 | |
---|
271 | if (i2[a][b].first == -1) |
---|
272 | i2[a][b].first = (int)n; |
---|
273 | i2[a][b].last = (int)n; |
---|
274 | |
---|
275 | if (i3[a][b][c].first == -1) |
---|
276 | i3[a][b][c].first = (int)n; |
---|
277 | i3[a][b][c].last = (int)n; |
---|
278 | |
---|
279 | ++linenum; |
---|
280 | ++n; |
---|
281 | } |
---|
282 | |
---|
283 | if (ferror(fp)) |
---|
284 | die("%s: %s", fn, strerror(errno)); |
---|
285 | |
---|
286 | fclose(fp); |
---|
287 | } |
---|
288 | |
---|
289 | fprintf(stderr, "postcodeine[%d]: writing cached postcode data\n", (int)getpid()); |
---|
290 | sprintf(fn, "%s.%d", POSTCODE_CACHE, (int)getpid()); |
---|
291 | if (!(fp = fopen(fn, "w"))) |
---|
292 | die("%s: open: %s", fn, strerror(errno)); |
---|
293 | |
---|
294 | *npcs = n; |
---|
295 | if (!fwrite_all(npcs, sizeof *npcs, fp) |
---|
296 | || !fwrite_all(pp, n * sizeof *pp, fp)) |
---|
297 | die("%s: write: %s", fn, strerror(errno)); |
---|
298 | |
---|
299 | for (a = 0; a < 36; ++a) { |
---|
300 | ind = i1[a]; |
---|
301 | if (!fwrite_all(&ind, sizeof(struct pcindex), fp)) |
---|
302 | die("%s: write: %s", fn, strerror(errno)); |
---|
303 | for (b = 0; b < 36; ++b) { |
---|
304 | ind = i2[a][b]; |
---|
305 | if (!fwrite_all(&ind, sizeof(struct pcindex), fp)) |
---|
306 | die("%s: write: %s", fn, strerror(errno)); |
---|
307 | for (c = 0; c < 36; ++c) { |
---|
308 | ind = i3[a][b][c]; |
---|
309 | if (!fwrite_all(&ind, sizeof(struct pcindex), fp)) |
---|
310 | die("%s: write: %s", fn, strerror(errno)); |
---|
311 | } |
---|
312 | } |
---|
313 | } |
---|
314 | |
---|
315 | fclose(fp); |
---|
316 | |
---|
317 | if (-1 == rename(fn, POSTCODE_CACHE)) { |
---|
318 | unlink(fn); |
---|
319 | die("%s: rename: %s", fn, strerror(errno)); |
---|
320 | } |
---|
321 | |
---|
322 | return pp; |
---|
323 | } |
---|
324 | |
---|
325 | static void error(const int code, const char *str) { |
---|
326 | printf( |
---|
327 | "Status: %d %s\r\n" |
---|
328 | "Content-Type: text/plain\r\n" |
---|
329 | "Content-Length: %d\r\n" |
---|
330 | "\r\n" |
---|
331 | "%s\n", |
---|
332 | code, str, strlen(str) + 1, str); |
---|
333 | } |
---|
334 | |
---|
335 | int main(int argc, char *argv[]) { |
---|
336 | int i, j, k; |
---|
337 | struct postcodepos *pcs; |
---|
338 | size_t npcs; |
---|
339 | struct pcindex idx1[36], idx2[36][36], idx3[36][36][36]; |
---|
340 | extern bool writepng(const char *filename, const struct image *img); |
---|
341 | time_t modified; |
---|
342 | time_t e; |
---|
343 | struct stat file_stat; |
---|
344 | struct tm *E; |
---|
345 | char expirydate[32]; |
---|
346 | |
---|
347 | time(&e); |
---|
348 | |
---|
349 | e += 86400 / 2; |
---|
350 | E = gmtime(&e); |
---|
351 | strftime(expirydate, sizeof expirydate, "%a, %d %b %Y %H:%M:%S GMT", E); |
---|
352 | |
---|
353 | /* Must do this here so that error-handling works. */ |
---|
354 | if (FCGI_Accept() < 0) |
---|
355 | die("first FCGI_Accept() returned < 0"); |
---|
356 | |
---|
357 | fprintf(stderr, "postcodeine[%d]: starting up\n", (int)getpid()); |
---|
358 | |
---|
359 | /* Allocate space for the images. */ |
---|
360 | for (i = 0; i < NIMAGES; ++i) { |
---|
361 | int y; |
---|
362 | images[i].data = malloc(images[i].h * sizeof(pixel*) + images[i].h * images[i].w * sizeof(pixel)); |
---|
363 | for (y = 0; y < images[i].h; ++y) |
---|
364 | images[i].data[y] = (pixel*)((char*)images[i].data + images[i].h * sizeof(pixel*) + y * images[i].w * sizeof(pixel)); |
---|
365 | } |
---|
366 | |
---|
367 | /* Initialise the three indices. */ |
---|
368 | for (i = 0; i < 36; ++i) { |
---|
369 | idx1[i].first = idx1[i].last = -1; |
---|
370 | for (j = 0; j < 36; ++j) { |
---|
371 | idx2[i][j].first = idx2[i][j].last = -1; |
---|
372 | for (k = 0; k < 36; ++k) |
---|
373 | idx3[i][j][k].first = idx3[i][j][k].last = -1; |
---|
374 | } |
---|
375 | } |
---|
376 | |
---|
377 | pcs = read_postcodes(POSTCODE_DIR, &npcs, idx1, idx2, idx3); |
---|
378 | stat(POSTCODE_CACHE, &file_stat); |
---|
379 | modified = file_stat.st_mtime; |
---|
380 | |
---|
381 | do { |
---|
382 | char *query; |
---|
383 | int n0, n1; |
---|
384 | struct image *I; |
---|
385 | char fn1[256], fn2[256]; |
---|
386 | int c; |
---|
387 | FILE *fp; |
---|
388 | struct stat st; |
---|
389 | float X = 0, Y = 0; |
---|
390 | struct tm mod_since; |
---|
391 | time_t cached_time; |
---|
392 | |
---|
393 | if (getenv("HTTP_IF_MODIFIED_SINCE")) { |
---|
394 | strptime(getenv("HTTP_IF_MODIFIED_SINCE"),"%a, %d %b %Y %H:%M:%S %Z", &mod_since); |
---|
395 | cached_time = mktime(&mod_since); |
---|
396 | if (cached_time > modified) { |
---|
397 | printf( |
---|
398 | "Status: 304 Not Modified\r\n" |
---|
399 | "Content-Type: image/png\r\n" |
---|
400 | "\r\n"); |
---|
401 | continue; |
---|
402 | } |
---|
403 | } |
---|
404 | |
---|
405 | /* First character is "B" for big map or "S" for small map; remainder |
---|
406 | * is postcode prefix. */ |
---|
407 | query = getenv("QUERY_STRING"); |
---|
408 | |
---|
409 | if (!query || !*query || !strchr("BSXZ", *query)) { |
---|
410 | error(400, "Bad query"); |
---|
411 | continue; |
---|
412 | } |
---|
413 | |
---|
414 | if (*query == 'Z') { |
---|
415 | /* List of postcode zones matching. */ |
---|
416 | struct postcodezone *Z; |
---|
417 | size_t ql; |
---|
418 | char zone[3] = {0}; |
---|
419 | extern struct postcodezone zones[]; /* in zones.c */ |
---|
420 | printf( |
---|
421 | "Content-Type: text/html\r\n" /* sort-of */ |
---|
422 | "Expires: %s\r\n" |
---|
423 | "\r\n" |
---|
424 | "<table style=\"width: 100%%;\">" |
---|
425 | "<tr><th style=\"width: 4em;\"></th><th></th></tr>", |
---|
426 | expirydate); |
---|
427 | ql = 0; |
---|
428 | if (query[1] && isalpha(query[1])) { |
---|
429 | zone[0] = query[1]; |
---|
430 | ql = 1; |
---|
431 | if (query[2]) { |
---|
432 | zone[1] = isalpha(query[2]) ? query[2] : 0; |
---|
433 | ql = 2; |
---|
434 | } |
---|
435 | } |
---|
436 | for (Z = zones; Z->zone; ++Z) { |
---|
437 | if (!query[1] || memcmp(Z->zone, zone, ql) == 0) |
---|
438 | printf("<tr><td><strong>%.*s</strong>%s</td><td>%s</td></tr>", |
---|
439 | (int)strlen(zone), Z->zone, Z->zone + strlen(zone), Z->name); |
---|
440 | } |
---|
441 | printf("</table>"); |
---|
442 | continue; |
---|
443 | } |
---|
444 | |
---|
445 | sprintf(fn2, "%s/%s.png", IMAGE_CACHE_DIR, query); |
---|
446 | sprintf(fn1, "%s.%d", fn2, (int)getpid()); |
---|
447 | |
---|
448 | if (*query != 'X' && (fp = fopen(fn2, "r"))) |
---|
449 | goto showimage; |
---|
450 | |
---|
451 | if (*query == 'B') |
---|
452 | I = images + 1; |
---|
453 | else if (*query == 'S') |
---|
454 | I = images; |
---|
455 | else |
---|
456 | I = NULL; |
---|
457 | ++query; |
---|
458 | |
---|
459 | if (query[strspn(query, "ABCDEFGHIJKLMNOPQRSTUVWXYZ" |
---|
460 | "abcdefghijklmnopqrstuvwxyz" |
---|
461 | "0123456789")]) |
---|
462 | n0 = n1 = -1; |
---|
463 | else if (!*query) { |
---|
464 | n0 = 0; |
---|
465 | n1 = npcs; |
---|
466 | } else { |
---|
467 | int a = -1, b = -1, c = -1; |
---|
468 | a = tobase36(query[0]); |
---|
469 | if (query[1]) { |
---|
470 | b = tobase36(query[1]); |
---|
471 | if (query[2]) c = tobase36(query[2]); |
---|
472 | } |
---|
473 | |
---|
474 | if (c != -1) { |
---|
475 | size_t l; |
---|
476 | n0 = idx3[a][b][c].first; |
---|
477 | n1 = idx3[a][b][c].last + 1; |
---|
478 | l = strlen(query); |
---|
479 | |
---|
480 | /* test on I so that the "mean coordinates" mode isn't an |
---|
481 | * oracle for postcode-to-place lookups. */ |
---|
482 | if (l > 3 && I) { |
---|
483 | /* XXX should bisect */ |
---|
484 | while (strncmp(pcs[n0].postcode, query, l) && n0 < n1) ++n0; |
---|
485 | if (n0 == n1) |
---|
486 | n0 = n1 = -1; |
---|
487 | else { |
---|
488 | while (strncmp(pcs[n1].postcode, query, l) && n1 > n0) --n1; |
---|
489 | ++n1; |
---|
490 | } |
---|
491 | if (n0 == n1) |
---|
492 | n0 = n1 = -1; |
---|
493 | } |
---|
494 | |
---|
495 | } else if (b != -1) { |
---|
496 | n0 = idx2[a][b].first; |
---|
497 | n1 = idx2[a][b].last + 1; |
---|
498 | } else { |
---|
499 | n0 = idx1[a].first; |
---|
500 | n1 = idx1[a].last + 1; |
---|
501 | } |
---|
502 | } |
---|
503 | |
---|
504 | fprintf(stderr, "query = \"%s\"; from = %d; to = %d\n", query, n0, n1); |
---|
505 | |
---|
506 | if (I) clear_image(I); |
---|
507 | if (n0 != -1) { |
---|
508 | /* Draw a little square on each bit of the image. */ |
---|
509 | int n, d = 1, N; |
---|
510 | if (n1 - n0 > 100000) |
---|
511 | d = 10; |
---|
512 | else if (n1 - n0 > 10000) |
---|
513 | d = 2; |
---|
514 | for (n = n0, N = 0; n < n1; n += d) { |
---|
515 | int x, y, i, j; |
---|
516 | ++N; |
---|
517 | if (!I) { |
---|
518 | X += pcs[n].x; |
---|
519 | Y += pcs[n].y; |
---|
520 | continue; |
---|
521 | } |
---|
522 | x = pcs[n].x * I->w; |
---|
523 | y = pcs[n].y * I->h; |
---|
524 | for (j = y - 1; j <= y + 1; ++j) { |
---|
525 | if (j < 0 || j >= I->h) continue; |
---|
526 | for (i = x - 1; i <= x + 1; ++i) { |
---|
527 | if (i < 0 || i >= I->w) continue; |
---|
528 | I->data[j][i] = 1; |
---|
529 | } |
---|
530 | } |
---|
531 | } |
---|
532 | X /= N; |
---|
533 | Y /= N; |
---|
534 | } |
---|
535 | |
---|
536 | if (!I) { |
---|
537 | char buf[32]; |
---|
538 | sprintf(buf, "%.5f,%.5f", X, Y); |
---|
539 | printf( |
---|
540 | "Content-Type: text/plain\r\n" |
---|
541 | "Content-Length: %d\r\n" |
---|
542 | "Expires: %s\r\n" |
---|
543 | "\r\n" |
---|
544 | "%s", |
---|
545 | strlen(buf), expirydate, buf); |
---|
546 | continue; |
---|
547 | } |
---|
548 | |
---|
549 | /* Write a PNG file. */ |
---|
550 | --query; |
---|
551 | writepng(fn1, I); |
---|
552 | rename(fn1, fn2); |
---|
553 | |
---|
554 | if (!(fp = fopen(fn2, "r"))) { |
---|
555 | error(500, strerror(errno)); |
---|
556 | continue; |
---|
557 | } |
---|
558 | |
---|
559 | showimage: |
---|
560 | if (-1 == fstat(fileno(fp), &st)) { |
---|
561 | error(500, strerror(errno)); |
---|
562 | fclose(fp); |
---|
563 | continue; |
---|
564 | } |
---|
565 | |
---|
566 | printf( |
---|
567 | "Content-Type: image/png\r\n" |
---|
568 | "Content-Length: %u\r\n" |
---|
569 | "\r\n", |
---|
570 | (unsigned)st.st_size); |
---|
571 | |
---|
572 | while (EOF != (c = getc(fp))) |
---|
573 | putc(c, stdout); |
---|
574 | |
---|
575 | fclose(fp); |
---|
576 | } while (FCGI_Accept() >= 0); |
---|
577 | |
---|
578 | fprintf(stderr, "postcodeine[%d]: shutting down\n", (int)getpid()); |
---|
579 | |
---|
580 | return 0; |
---|
581 | } |
---|