]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/jpg/ico.c
readtif, writetif: prevent buffer overflows in some corner cases
[plan9front.git] / sys / src / cmd / jpg / ico.c
1 #include <u.h>
2 #include <libc.h>
3 #include <bio.h>
4 #include <draw.h>
5 #include <memdraw.h>
6 #include <event.h>
7 #include <cursor.h>
8
9 typedef struct Icon Icon;
10 struct Icon
11 {
12         Icon    *next;
13
14         uchar   w;              /* icon width */
15         uchar   h;              /* icon height */
16         ushort  ncolor;         /* number of colors */
17         ushort  nplane;         /* number of bit planes */
18         ushort  bits;           /* bits per pixel */
19         ulong   len;            /* length of data */
20         ulong   offset;         /* file offset to data */
21
22         Memimage        *img;
23         Memimage        *mask;
24
25         Rectangle r;            /* relative */
26         Rectangle sr;           /* abs */
27 };
28
29 typedef struct Header Header;
30 struct Header
31 {
32         uint    n;
33         Icon    *first;
34         Icon    *last;
35 };
36
37 int debug;
38 int cflag;
39 Mouse mouse;
40 Header h;
41 Image *background;
42
43 ushort
44 gets(uchar *p)
45 {
46         return p[0] | (p[1]<<8);
47 }
48
49 ulong
50 getl(uchar *p)
51 {
52         return p[0] | (p[1]<<8) | (p[2]<<16) | (p[3]<<24);
53 }
54
55 int
56 Bgetheader(Biobuf *b, Header *h)
57 {
58         uchar buf[40];
59         Icon *icon;
60         int i;
61
62         memset(h, 0, sizeof(*h));
63         if(Bread(b, buf, 6) != 6)
64                 goto eof;
65         if(gets(&buf[0]) != 0)
66                 goto header;
67         if(gets(&buf[2]) != 1)
68                 goto header;
69         h->n = gets(&buf[4]);
70         for(i = 0; i < h->n; i++){
71                 icon = mallocz(sizeof(*icon), 1);
72                 if(icon == nil)
73                         sysfatal("malloc: %r");
74                 if(Bread(b, buf, 16) != 16)
75                         goto eof;
76                 icon->w = buf[0] == 0 ? 256 : buf[0];
77                 icon->h = buf[1] == 0 ? 256 : buf[1];
78                 icon->ncolor = buf[2] == 0 ? 256 : buf[2];
79                 icon->nplane = gets(&buf[4]);
80                 icon->bits = gets(&buf[6]);
81                 icon->len = getl(&buf[8]);
82                 icon->offset = getl(&buf[12]);
83                 if(i == 0)
84                         h->first = icon;
85                 else
86                         h->last->next = icon;
87                 h->last = icon;
88         }
89         return 0;
90
91 eof:
92         werrstr("unexpected EOF");
93         return -1;
94 header:
95         werrstr("unknown header format");
96         return -1;
97 }
98
99 uchar*
100 transcmap(Icon *icon, int ncolor, uchar *map)
101 {
102         uchar *m, *p;
103         int i;
104
105         p = m = mallocz(sizeof(int)*(1<<icon->bits), 1);
106         if(m == nil)
107                 sysfatal("malloc: %r");
108         for(i = 0; i < ncolor; i++){
109                 *p++ = rgb2cmap(map[2], map[1], map[0]);
110                 map += 4;
111         }
112         return m;
113 }
114
115 Memimage*
116 xor2img(Icon *icon, long chan, uchar *xor, uchar *map)
117 {
118         uchar *data;
119         Memimage *img;
120         int inxlen;
121         uchar *from, *to;
122         int s, byte, mask;
123         int x, y;
124
125         inxlen = 4*((icon->bits*icon->w+31)/32);
126         img = allocmemimage(Rect(0,0,icon->w,icon->h), chan);
127         if(img == nil)
128                 return nil;
129
130         if(chan != CMAP8){
131                 from = xor + icon->h*inxlen;
132                 for(y = 0; y < icon->h; y++){
133                         from -= inxlen;
134                         loadmemimage(img, Rect(0,y,icon->w,y+1), from, inxlen);
135                 }
136                 return img;
137         }
138
139         to = data = malloc(icon->w*icon->h);
140         if(data == nil){
141                 freememimage(img);
142                 return nil;
143         }
144
145         /* rotate around the y axis, go to 8 bits, and convert color */
146         mask = (1<<icon->bits)-1;
147         for(y = 0; y < icon->h; y++){
148                 s = -1;
149                 byte = 0;
150                 from = xor + (icon->h - 1 - y)*inxlen;
151                 for(x = 0; x < icon->w; x++){
152                         if(s < 0){
153                                 byte = *from++;
154                                 s = 8-icon->bits;
155                         }
156                         *to++ = map[(byte>>s) & mask];
157                         s -= icon->bits;
158                 }
159         }
160         /* stick in an image */
161         loadmemimage(img, Rect(0,0,icon->w,icon->h), data, icon->h*icon->w);
162         free(data);
163         return img;
164 }
165
166 Memimage*
167 and2img(Icon *icon, uchar *and)
168 {
169         uchar *data;
170         Memimage *img;
171         int inxlen;
172         int outxlen;
173         uchar *from, *to;
174         int x, y;
175
176         inxlen = 4*((icon->w+31)/32);
177         to = data = malloc(inxlen*icon->h);
178         if(data == nil)
179                 return nil;
180
181         /* rotate around the y axis and invert bits */
182         outxlen = (icon->w+7)/8;
183         for(y = 0; y < icon->h; y++){
184                 from = and + (icon->h - 1 - y)*inxlen;
185                 for(x = 0; x < outxlen; x++)
186                         *to++ = ~(*from++);
187         }
188
189         /* stick in an image */
190         if(img = allocmemimage(Rect(0,0,icon->w,icon->h), GREY1))
191                 loadmemimage(img, Rect(0,0,icon->w,icon->h), data, icon->h*outxlen);
192
193         free(data);
194         return img;
195 }
196
197 int
198 Bgeticon(Biobuf *b, Icon *icon)
199 {
200         uchar *end;
201         uchar *xor;
202         uchar *and;
203         uchar *cm;
204         uchar *buf;
205         uchar *map2map;
206         Memimage *img;
207         int ncolor;
208         long chan;
209
210         if(icon->len < 40){
211                 werrstr("bad icon header length");
212                 return -1;
213         }
214         Bseek(b, icon->offset, 0);
215         buf = malloc(icon->len);
216         if(buf == nil)
217                 return -1;
218         if(Bread(b, buf, icon->len) != icon->len){
219                 werrstr("unexpected EOF");
220                 return -1;
221         }
222         /* this header's info takes precedence over previous one */
223         if(getl(buf) != 40){
224                 werrstr("bad icon header");
225                 return -1;
226         }
227
228         ncolor = 0;
229         icon->w = getl(buf+4);
230         icon->h = getl(buf+8)>>1;
231         icon->nplane = gets(buf+12);
232         icon->bits = gets(buf+14);
233
234         /* limit what we handle */
235         switch(icon->bits){
236         case 1:
237         case 2:
238         case 4:
239         case 8:
240                 ncolor = icon->ncolor;
241                 if(ncolor > (1<<icon->bits))
242                         ncolor = 1<<icon->bits;
243                 chan = CMAP8;
244                 break;
245         case 15:
246         case 16:
247                 chan = RGB16;
248                 break;
249         case 24:
250                 chan = RGB24;
251                 break;
252         case 32:
253                 chan = ARGB32;
254                 break;
255         default:
256                 werrstr("don't support %d bit pixels", icon->bits);
257                 return -1;
258         }
259         if(icon->nplane != 1){
260                 werrstr("don't support %d planes", icon->nplane);
261                 return -1;
262         }
263
264         xor = cm = buf + 40;
265         if(chan == CMAP8)
266                 xor += 4*ncolor;
267         end = xor + icon->h*4*((icon->bits*icon->w+31)/32);
268         if(end < buf || end > buf+icon->len){
269                 werrstr("bad icon length %lux != %lux", end - buf, icon->len);
270                 return -1;
271         }
272
273         /* translate the color map to a plan 9 one */
274         map2map = nil;
275         if(chan == CMAP8)
276                 map2map = transcmap(icon, ncolor, cm);
277
278         /* convert the images */
279         icon->img = xor2img(icon, chan, xor, map2map);
280         if(icon->img == nil){
281                 werrstr("xor2img: %r");
282                 return -1;
283         }
284         icon->mask = nil;
285
286         /* check for and mask */
287         and = end;
288         end += icon->h*4*((icon->w+31)/32);
289         if(end <= buf+icon->len)
290                 icon->mask = and2img(icon, and);
291
292         /* so that we save an image with a white background */
293         if(img = allocmemimage(icon->img->r, icon->img->chan)){
294                 memfillcolor(img, DWhite);
295                 memimagedraw(img, icon->img->r, icon->img, ZP, icon->mask, ZP, SoverD);
296                 freememimage(icon->img);
297                 icon->img = img;
298         }
299
300         free(buf);
301         free(map2map);
302         return 0;
303 }
304
305 void
306 usage(void)
307 {
308         fprint(2, "usage: %s [ -c ] [ file ]\n", argv0);
309         exits("usage");
310 }
311
312 enum
313 {
314         Mimage,
315         Mmask,
316         Mexit,
317
318         Up= 1,
319         Down= 0,
320 };
321
322 char    *menu3str[] = {
323         [Mimage]        "write image",
324         [Mmask]         "write mask",
325         [Mexit]         "exit",
326         0,
327 };
328
329 Menu    menu3 = {
330         menu3str
331 };
332
333 Cursor sight = {
334         {-7, -7},
335         {0x1F, 0xF8, 0x3F, 0xFC, 0x7F, 0xFE, 0xFB, 0xDF,
336          0xF3, 0xCF, 0xE3, 0xC7, 0xFF, 0xFF, 0xFF, 0xFF,
337          0xFF, 0xFF, 0xFF, 0xFF, 0xE3, 0xC7, 0xF3, 0xCF,
338          0x7B, 0xDF, 0x7F, 0xFE, 0x3F, 0xFC, 0x1F, 0xF8,},
339         {0x00, 0x00, 0x0F, 0xF0, 0x31, 0x8C, 0x21, 0x84,
340          0x41, 0x82, 0x41, 0x82, 0x41, 0x82, 0x7F, 0xFE,
341          0x7F, 0xFE, 0x41, 0x82, 0x41, 0x82, 0x41, 0x82,
342          0x21, 0x84, 0x31, 0x8C, 0x0F, 0xF0, 0x00, 0x00,}
343 };
344
345 void
346 buttons(int ud)
347 {
348         while((mouse.buttons==0) != ud)
349                 mouse = emouse();
350 }
351
352 void
353 mesg(char *fmt, ...)
354 {
355         va_list arg;
356         char buf[1024];
357         static char obuf[1024];
358
359         va_start(arg, fmt);
360         vseprint(buf, buf+sizeof(buf), fmt, arg);
361         va_end(arg);
362         string(screen, screen->r.min, background, ZP, font, obuf);
363         string(screen, screen->r.min, display->white, ZP, font, buf);
364         strcpy(obuf, buf);
365 }
366
367 void
368 doimage(Icon *icon)
369 {
370         int rv;
371         char file[256];
372         int fd;
373
374         rv = -1;
375         snprint(file, sizeof(file), "%dx%d.img", icon->w, icon->h);
376         fd = create(file, OWRITE, 0664);
377         if(fd >= 0){
378                 rv = writememimage(fd, icon->img);
379                 close(fd);
380         }
381         if(rv < 0)
382                 mesg("error writing %s: %r", file);
383         else
384                 mesg("created %s", file);
385 }
386
387 void
388 domask(Icon *icon)
389 {
390         int rv;
391         char file[64];
392         int fd;
393
394         rv = -1;
395         snprint(file, sizeof(file), "%dx%d.mask", icon->w, icon->h);
396         fd = create(file, OWRITE, 0664);
397         if(fd >= 0){
398                 rv = writememimage(fd, icon->mask);
399                 close(fd);
400         }
401         if(rv < 0)
402                 mesg("error writing %s: %r", file);
403         else
404                 mesg("created %s", file);
405 }
406
407 void
408 apply(void (*f)(Icon*))
409 {
410         Icon *icon;
411
412         esetcursor(&sight);
413         buttons(Down);
414         if(mouse.buttons == 4)
415                 for(icon = h.first; icon; icon = icon->next)
416                         if(ptinrect(mouse.xy, icon->sr)){
417                                 buttons(Up);
418                                 f(icon);
419                                 break;
420                         }
421         buttons(Up);
422         esetcursor(0);
423 }
424
425 void
426 menu(void)
427 {
428         int sel;
429
430         sel = emenuhit(3, &mouse, &menu3);
431         switch(sel){
432         case Mimage:
433                 apply(doimage);
434                 break;
435         case Mmask:
436                 apply(domask);
437                 break;
438         case Mexit:
439                 exits(0);
440                 break;
441         }
442 }
443
444 void
445 mousemoved(void)
446 {
447         Icon *icon;
448
449         for(icon = h.first; icon; icon = icon->next)
450                 if(ptinrect(mouse.xy, icon->sr)){
451                         mesg("%dx%d", icon->w, icon->h);
452                         return;
453                 }
454         mesg("");
455 }
456
457 enum
458 {
459         BORDER= 1,
460 };
461
462 Image*
463 screenimage(Memimage *m)
464 {
465         Rectangle r;
466         Image *i;
467
468         if(i = allocimage(display, m->r, m->chan, 0, DNofill)){
469                 r = m->r;
470                 while(r.min.y < m->r.max.y){
471                         r.max.y = r.min.y+1;
472                         loadimage(i, r, byteaddr(m, r.min), bytesperline(r, m->depth));
473                         r.min.y++;
474                 }
475         }
476         return i;
477 }
478
479 void
480 eresized(int new)
481 {
482         Icon *icon;
483         Image *i;
484         Rectangle r;
485
486         if(new && getwindow(display, Refnone) < 0)
487                 sysfatal("can't reattach to window");
488         draw(screen, screen->clipr, background, nil, ZP);
489         r.max.x = screen->r.min.x;
490         r.min.y = screen->r.min.y + font->height + 2*BORDER;
491         for(icon = h.first; icon != nil; icon = icon->next){
492                 r.min.x = r.max.x + BORDER;
493                 r.max.x = r.min.x + Dx(icon->img->r);
494                 r.max.y = r.min.y + Dy(icon->img->r);
495                 if(i = screenimage(icon->img)){
496                         draw(screen, r, i, nil, ZP);
497                         freeimage(i);
498                 }
499                 border(screen, r, -BORDER, display->black, ZP);
500                 icon->sr = r;
501         }
502         flushimage(display, 1);
503 }
504
505 void
506 main(int argc, char **argv)
507 {
508         Biobuf in;
509         Icon *icon;
510         int num, fd;
511         Rectangle r;
512         Event e;
513
514         ARGBEGIN{
515         case 'd':
516                 debug = 1;
517                 break;
518         case 'c':
519                 cflag = 1;
520                 break;
521         default:
522                 usage();
523         }ARGEND;
524
525         fd = -1;
526         switch(argc){
527         case 0:
528                 fd = 0;
529                 break;
530         case 1:
531                 fd = open(argv[0], OREAD);
532                 if(fd < 0)
533                         sysfatal("opening: %r");
534                 break;
535         default:
536                 usage();
537                 break;
538         }
539
540         memimageinit();
541         Binit(&in, fd, OREAD);
542
543         if(Bgetheader(&in, &h) < 0)
544                 sysfatal("reading header: %r");
545
546         num = 0;
547         r.min = Pt(4, 4);
548         for(icon = h.first; icon != nil; icon = icon->next){
549                 if(Bgeticon(&in, icon) < 0){
550                         fprint(2, "%s: read fail: %r\n", argv0);
551                         continue;
552                 }
553                 if(debug)
554                         fprint(2, "w %ud h %ud ncolor %ud bits %ud len %lud offset %lud\n",
555                            icon->w, icon->h, icon->ncolor, icon->bits, icon->len, icon->offset);
556                 r.max = addpt(r.min, Pt(icon->w, icon->h));
557                 icon->r = r;
558                 if(cflag){
559                         writememimage(1, icon->img);
560                         exits(0);
561                 }
562                 r.min.x += r.max.x;
563                 num++;
564         }
565
566         if(num == 0 || cflag)
567                 sysfatal("no images");
568
569         initdraw(nil, nil, "ico");
570         background = allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, 0x808080FF);
571         eresized(0);
572         einit(Emouse|Ekeyboard);
573         for(;;)
574                 switch(event(&e)){
575                 case Ekeyboard:
576                         break;
577                 case Emouse:
578                         mouse = e.mouse;
579                         if(mouse.buttons & 4)
580                                 menu();
581                         else
582                                 mousemoved();
583                         break;
584                 }
585         /* not reached */
586 }