2630fbe280990a3afedeb80571a7e3275f7e57c5
[rrq/maintain_lilo.git] / src / edit.c
1 /* edit.c -- bitmap file manipulation and editing */
2 /*
3 Copyright 2002-2004 John Coffman.
4 All rights reserved.
5
6 Licensed under the terms contained in the file 'COPYING' in the 
7 source directory.
8
9 */
10
11 #define _GNU_SOURCE
12 #include <unistd.h>
13 #include <sys/types.h>
14 #include <sys/statfs.h>
15 #include <sys/stat.h>
16 #include <stdio.h>
17 #include <stdlib.h>
18 #include <fcntl.h>
19 #include <errno.h>
20 #include <string.h>
21 #include <ctype.h>
22 #include <math.h>
23
24 #ifdef  _SYS_STATFS_H
25 #define _I386_STATFS_H  /* two versions of statfs is not good ... */
26 #endif
27
28 #include "config.h"
29 #include "lilo.h"
30 #include "common.h"
31 #include "cfg.h"
32 #include "temp.h"
33 #include "bsect.h"
34 #include "bitmap.h"
35 #include "edit.h"
36
37
38 #define USE_BSECT_PW_INPUT 0            /* use another's input routine */
39 #define BMP_CONF ".dat"
40 #define BMP_BMP  ".bmp"
41 #define NPALETTE 256
42
43 /* Luminance of a color 0..255 -- YIQ color model */
44 #define Y(c) (((c).red*0.30+(c).green*0.59+(c).blue*0.11)/255)
45 #define NORM(x) ((float)(x)/255.0)
46
47 static BITMAPFILEHEADER fh;
48 static BITMAPHEADER bmh;
49 static RGB palette[NPALETTE];
50 static int filepos, npal;
51
52 static BITMAPLILOHEADER lh0 = {
53         {sizeof(BITMAPLILOHEADER), 0}, "LILO",
54         5*16, 12*8, 1,                  /* row, col, ncol */
55         25, (MAX_IMAGE_NAME+1)*8,       /* maxcol, xpitch */
56         7, 7, 7,                        /* normal text fg, bg, shadow */
57         15, 15, 15,                     /* highlight  fg, bg, shadow  */
58         7, 0, 7,                        /* timer text  fg, bg, shadow */
59         2*16, 64*8,                     /* timer row, col */
60         4, {0, 0, 0}                    /* mincol, reserved[3] */
61                 };
62
63
64
65 #if USE_BSECT_PW_INPUT
66 #define getLine pw_input
67 #else
68 static char *getLine(void)
69 {
70     char *pass;
71     char buf[MAX_TOKEN+1];
72     int i, ch;
73     
74     i = 0;
75     fflush(stdout);
76     while((ch=getchar())!='\n') if (i<MAX_TOKEN) buf[i++]=ch;
77     buf[i]=0;
78     pass = stralloc(buf);
79 /*    while (i) buf[--i]=0;     */
80     return pass;
81 }
82 #endif
83
84
85 int get_std_headers(int fd,
86         BITMAPFILEHEADER *fh,
87         BITMAPHEADER *bmh,
88         BITMAPLILOHEADER *lh)
89 {
90     short size;
91     BITMAPHEADER2 bmh2;
92     int n, i;
93     
94     lseek(fd, 0, SEEK_SET);
95     if (read(fd, (void*)fh, sizeof(BITMAPFILEHEADER)) !=
96                         sizeof(BITMAPFILEHEADER) )  return -1;
97     if (fh->magic != 0x4D42 /* "BM" */)  return 1;
98     if (read(fd, &size, sizeof(size)) != sizeof(size))   return -1;
99     if (size==sizeof(BITMAPHEADER2)) { /* an OS/2 bitmap */
100         if (read(fd, (char*)&bmh2+sizeof(size), sizeof(BITMAPHEADER2)-sizeof(size))
101                 != sizeof(BITMAPHEADER2)-sizeof(size) )   return -1;
102         memset(bmh, 0, sizeof(BITMAPHEADER));
103         bmh->width = bmh2.width;
104         bmh->height = bmh2.height;
105         n = bmh->numBitPlanes = bmh2.numBitPlanes;
106         n *= bmh->numBitsPerPlane = bmh2.numBitsPerPlane;
107         bmh->numColorsUsed = bmh->numImportantColors = 1 << n;
108         bmh->sizeImageData = *(int*)(fh->size) - *(int*)(fh->offsetToBits);
109         bmh->size = sizeof(*bmh);    /* new size!! */
110         n = sizeof(RGB2);
111     }
112     else if (size==sizeof(BITMAPHEADER)) {
113         if (read(fd, (char*)bmh+sizeof(size), sizeof(BITMAPHEADER)-sizeof(size))
114                 != sizeof(BITMAPHEADER)-sizeof(size) )   return -1;
115         bmh->size = size;
116         n = sizeof(RGB);
117     }
118     else  return 2;
119
120     *lh = lh0;
121     npal = 1 << (bmh->numBitPlanes * bmh->numBitsPerPlane);
122     colormax = npal - 1;
123     if (npal > nelem(palette) )   return 3;
124     for (i=0; i<npal; i++) {
125         if (read(fd, &palette[i], n) != n)  return -1;
126         if (n==sizeof(RGB2)) palette[i].null = 0;
127     }
128     if (*(int*)(fh->offsetToBits) == sizeof(BITMAPFILEHEADER) +
129                 sizeof(BITMAPHEADER) + sizeof(BITMAPLILOHEADER) +
130                 npal*sizeof(RGB) )  /* test will fail for OS/2 bitmaps */ {
131       /* get probable BITMAPLILOHEADER */
132         if (read(fd, &size, sizeof(size)) != sizeof(size))  return -1;
133         if (size != sizeof(BITMAPLILOHEADER))   return 4;
134         if (read(fd, (char*)lh+sizeof(size), sizeof(*lh)-sizeof(size)) !=
135                 sizeof(*lh)-sizeof(size))  return -1;
136         *(int*)(lh->size) = size;
137         if (strncmp(lh->magic, "LILO", 4) != 0)   return 5;
138     } else { /* there is no BITMAPLILOHEADER present */
139 #ifdef STANDALONE
140         printf("No BITMAPLILOHEADER\n");
141 #endif
142     }
143
144 /* file is left positioned at the start of the bitmap data */
145     filepos = lseek(fd, 0, SEEK_CUR);
146     return 0;
147 }
148
149
150 int put_std_bmpfile(int fd, int ifd,
151         BITMAPFILEHEADER *fh,
152         BITMAPHEADER *bmh,
153         BITMAPLILOHEADER *lh)
154 {
155     int n, total, npalette;
156     char buf[1024];
157     
158     npalette = 1 << (bmh->numBitPlanes * bmh->numBitsPerPlane);
159     write(fd, fh, sizeof(*fh));
160     write(fd, bmh, sizeof(*bmh));
161     write(fd, palette, npalette*sizeof(palette[0]));
162     write(fd, lh, sizeof(*lh));
163     total=0;
164     lseek(ifd, filepos, SEEK_SET);
165     do {
166         n = read(ifd, buf, sizeof(buf));
167         if (n>0) {
168             if (write(fd, buf, n) != n)  return -1;
169             total += n;
170         }
171         else if (n<0)  printf("Error reading input\n");
172     } while (n>0);
173     bmh->sizeImageData = total;
174     *(int*)(fh->offsetToBits) = n = sizeof(BITMAPFILEHEADER) +
175                 sizeof(BITMAPHEADER) + sizeof(BITMAPLILOHEADER) +
176                 npalette*sizeof(RGB);
177     *(int*)(fh->size) = total + n;
178     lseek(fd, 0, SEEK_SET);
179     write(fd, fh, sizeof(*fh));
180     write(fd, bmh, sizeof(*bmh));
181
182     return 0;
183 }
184
185
186 #ifndef STANDALONE
187 static  char *temp_file, *bitmap_file;
188 static  int ifd, ofd;
189 static  union {
190            unsigned char buffer[256];
191            MENUTABLE mt;
192            BITMAPLILOHEADER bmlh;
193         } tm;
194 static  MENUTABLE *menu = &tm.mt;
195 static  BITMAPLILOHEADER *lh = (void*)(tm.buffer +
196                         ((long)&tm.mt.row - (long)&tm.bmlh.row));
197
198 /* a convenience definition */
199 #define mn tm.mt
200
201 /* timer = 1 if timer is enabled, 0 if timer is disabled */
202 #define timer (mn.t_row>=0)
203
204 static int yesno(char *query, int def)
205 {
206     char *yn;
207     int ans = 2;
208
209     while (ans>1) {    
210         printf("%s (yes or no) [%c]:  ", query, def?'Y':'N');
211         yn = getLine();
212         if (!*yn) ans = def;
213         else if (toupper(*yn) == 'Y') ans = 1;
214         else if (toupper(*yn) == 'N') ans = 0;
215         free(yn);
216     }
217     return ans;
218 }
219
220
221 static void dat_file_creat(char *bmp)
222 {
223     char *datfile;
224     FILE *fdat;
225     
226     datfile = stralloc(bmp);
227     *strrchr(datfile,*(BMP_BMP)) = 0;
228     strcat(datfile, BMP_CONF);
229     if (!(fdat = fopen(datfile, "w"))) pdie("Open .dat file");
230
231     fprintf(fdat,"#\n# generated companion file to:\n#\n");
232     fprintf(fdat,"bitmap = %s\n", bmp);
233     
234     fprintf(fdat,"bmp-table = %d%s,%d%s;%d,%d,%d%s,%d\n",
235         mn.col%8 ? mn.col : mn.col/8+1,
236         mn.col%8 ? "p" : "",
237         mn.row%16 ? mn.row : mn.row/16+1,
238         mn.row%16 ? "p" : "",
239         mn.ncol,
240         mn.maxcol,
241         mn.xpitch%8 ? mn.xpitch : mn.xpitch/8,
242         mn.xpitch%8 ? "p" : "",
243         mn.mincol );
244     
245     fprintf(fdat,"bmp-colors = %d,", mn.fg);
246     if (mn.bg != mn.fg) fprintf(fdat,"%d",mn.bg);
247     putc(',',fdat);
248     if (mn.sh != mn.fg) fprintf(fdat,"%d",mn.sh);
249     putc(';',fdat);
250     fprintf(fdat,"%d,", mn.h_fg);
251     if (mn.h_bg != mn.h_fg) fprintf(fdat,"%d",mn.h_bg);
252     putc(',',fdat);
253     if (mn.h_sh != mn.h_fg) fprintf(fdat,"%d",mn.h_sh);
254     putc('\n',fdat);
255
256     fprintf(fdat,"bmp-timer = ");
257     if (mn.t_row < 0) fprintf(fdat,"none\n");
258     else {
259         fprintf(fdat,"%d%s,%d%s;%d,",
260             mn.t_col%8 ? mn.t_col : mn.t_col/8+1,
261             mn.t_col%8 ? "p" : "",
262             mn.t_row%16 ? mn.t_row : mn.t_row/16+1,
263             mn.t_row%16 ? "p" : "",
264             mn.t_fg );
265         if (mn.t_bg != mn.t_fg) fprintf(fdat,"%d", mn.t_bg);
266         putc(',',fdat);
267         if (mn.t_sh != mn.t_fg) fprintf(fdat,"%d", mn.t_sh);
268         putc('\n',fdat);
269     }
270     fclose(fdat);
271 }
272
273 static void bmp_file_open(char *bmp)
274 {
275     int n;
276
277     bitmap_file = bmp;
278     temp_file = strcat(strcpy(alloc(strlen(bitmap_file)+strlen(MAP_TMP_APP)+1),
279                               bitmap_file),
280                        MAP_TMP_APP);
281     ifd = open(bitmap_file, O_RDONLY);
282     if (ifd<0) pdie("Cannot open bitmap file");
283     ofd = open(temp_file, O_CREAT|O_WRONLY, 0644);
284     if (ofd<0) pdie("Cannot open temporary file");
285     temp_register(temp_file);
286     
287     n = get_std_headers(ifd, &fh, &bmh, lh);
288     if (verbose >= 3) printf("get_std_headers:  returns %d\n", n);
289     
290     if (n<0) die("read file '%s': %s", bitmap_file, strerror(errno));
291     switch (n) {
292         case 1:
293         case 2:
294             die("Not a bitmap file '%s'", bitmap_file);
295         case 3:
296             die("Unsupported bitmap file '%s' (%d bit color)", bitmap_file,
297                 bmh.numBitPlanes*bmh.numBitsPerPlane);
298         case 4:
299         case 5:
300             die("Unrecognized auxiliary header in file '%s'", bitmap_file);
301         default:
302             ;
303     }
304 }
305
306
307 static void bmp_file_close(int update)
308 {
309     int n;
310     
311     if (update) n = put_std_bmpfile(ofd, ifd, &fh, &bmh, lh);
312     
313     close(ifd);
314     close(ofd);
315     temp_unregister(temp_file);
316     if (!update || test) {
317         if (verbose < 9) remove(temp_file);
318     } else {
319         n = rename(temp_file, bitmap_file);
320     }
321 }
322
323
324 static void location(char *what, short x, short y)
325 {
326     printf("%sColumn(X): %d%s (chars) or %hdp (pixels)", what, x/8+1, x%8?"+":"", x);
327     printf("   Row(Y): %d%s (chars) or %hdp (pixels)\n", y/16+1, y%16?"+":"", y);
328 }
329
330
331 static void color(char *what, short fg, short bg, short sh)
332 {
333 static char sp[] = "   ";
334
335     printf("%sForeground: %hd%sBackground: ", what, fg, sp);
336     if (bg==fg) printf("transparent%s",sp);
337     else printf("%hd%s", bg, sp);
338     printf("Shadow: ");
339     if (sh==fg) printf("none\n");
340     else printf("%hd\n", sh);
341 }
342
343
344 static void get3colors(char *what, short *color)
345 {
346 static char *co[] = { "fg", "bg", "sh" };
347 static char *op[] = { "", ",transparent", ",none" };
348     int i;
349     int tr, no;
350     unsigned int c;
351     char n[4], *line, *end;
352     int dcol[3];
353
354     for (i=0; i<3; i++) dcol[i] = color[i];     /* save inputs */
355     tr = (dcol[0] == dcol[1]);
356     no = (dcol[0] == dcol[2]);
357     
358     printf("\n");
359     for (i=0; i<3; i++) {
360         sprintf(n, "%hd", dcol[i]);
361         printf("%s text %s color (0..%d%s) [%s]: ", what, co[i], npal-1, op[i],
362                 i==1 && tr ? "transparent" :
363                 i==2 && no ? "none" : n);
364         line = getLine();
365         if (!*line) c = dcol[i];
366         else if (toupper(*line)=='T' && i==1) c = color[0];
367         else if (toupper(*line)=='N' && i==2) c = color[0];
368         else {
369             c = strtol(line, &end, 0);
370             if (line==end || c>=npal || *end) {
371                 c = dcol[i];
372                 printf("???\n");
373             }
374         }
375         color[i] = c;
376         free(line);
377         if (i==0) {
378             if (tr) dcol[1]=c;
379             if (no) dcol[2]=c;
380         }
381     }
382 }
383
384
385 static void number(char *what, short *num, int min, int max)
386 {
387     char *line, *end;
388     int val;
389     
390     printf("%s (%d..%d) [%hd]:  ", what, min, max, *num);
391     line = getLine();
392     if (!*line) val = *num;
393     else {
394         val = strtol(line, &end, 0);
395         if (val < min || val > max || *end) {
396             printf("???");
397             val = *num;
398         }
399     }
400     free(line);
401     *num = val;
402 }
403
404
405 static void getXY(char *what, short *locp, int scale, int abs)
406 {
407     char *line, *end;
408     int val;
409     int min = abs ? 1 : MAX_IMAGE_NAME;
410     int minp = min*scale;
411     int max = scale==8 ? 80 : 30;
412     int maxp = (max-abs)*scale;
413     int loc = *locp/scale + abs;
414     char *plus = *locp%scale ? "+" : "";
415     
416     printf("%s (%d..%d) or (%dp..%dp) [%d%s or %dp]: ", what,
417                         min, max, minp, maxp, loc, plus, (int)*locp);
418     
419     line = getLine();
420     if (!*line) val = *locp;
421     else {
422         val = strtol(line, &end, 0);
423         if (line==end || (*end && toupper(*end)!='P')) {
424             val = *locp;
425             printf("???1\n");
426         }
427         if (toupper(*end)!='P') val = (val-1)*scale;
428         if (val > maxp) {
429             val = *locp;
430             printf("???2\n");
431         }
432     }
433     *locp = val;
434     free(line);
435 }
436
437
438 static void show_timer(void)
439 {
440     if (timer) {
441         color("    Timer:  ", mn.t_fg, mn.t_bg, mn.t_sh);
442         location("Timer position:\n  ", mn.t_col, mn.t_row);
443     }
444     else
445     {
446         printf("\n\tThe timer is DISABLED.\n");
447     }
448 }
449
450
451 static void show_colors(int timopt)
452 {
453     color("   Normal:  ", mn.fg, mn.bg, mn.sh);
454     color("Highlight:  ", mn.h_fg, mn.h_bg, mn.h_sh);
455     if (timopt && timer)
456         color("    Timer:  ", mn.t_fg, mn.t_bg, mn.t_sh);
457 }
458
459 static void show_layout(void)
460 {
461     printf("\nTable dimensions:\n");
462     printf("  Number of columns:  %hd\n",  mn.ncol);
463     printf("  Entries per column (number of rows):  %hd\n", mn.maxcol);
464     if (mn.ncol > 1) {
465         printf( "  Column pitch (X-spacing from character 1 of one column to character 1\n"
466                 "      of the next column):  %d%s (chars)  %hdp (pixels)\n", mn.xpitch/8,
467                 mn.xpitch%8 ? "+" : "", mn.xpitch);
468         printf( "  Spill threshold (number of entries filled-in in the first column\n"
469                 "      before entries are made in the second column):  %hd\n", mn.mincol);
470     }
471     location("Table upper left corner:\n  ", mn.col, mn.row);
472 }
473
474
475 static void edit_timer(void)
476 {
477     char *cmd;
478     int editing = 1;
479     
480     do {
481         if (timer) printf("\nTimer colors:\n");
482         show_timer();
483         printf("\nTimer setup:  ");
484         
485         if (timer) printf("C)olors, P)osition, D)isable");
486         else printf("E)nable");
487         
488         printf(", B)ack:  ");
489         
490         cmd = getLine();
491         
492         if (timer) switch(toupper(*cmd)) {
493             case 'C':
494                 get3colors("Timer", &mn.t_fg);
495                 break;
496             case 'D':
497                 while (timer) {
498                     mn.t_row -= 480;
499                 }
500                 break;
501             case 'P':
502                 getXY("\nTimer col", &mn.t_col, 8, 1);
503                 getXY("Timer row", &mn.t_row, 16, 1);
504                 break;
505             case 'B':
506                 editing = 0;
507                 break;
508             default:
509                 printf("???");
510         }
511         else switch(toupper(*cmd)) {
512             case 'E':
513                 while (!timer) {
514                     mn.t_row += 480;
515                 }
516                 break;
517             case 'B':
518                 editing = 0;
519                 break;
520             default:
521                 printf("???");
522         }
523         free(cmd);
524         printf("\n");
525     } while (editing);
526 }
527
528
529 static void edit_layout(void)
530 {
531     char *cmd;
532     int editing = 1;
533     
534     do {
535         show_layout();
536         
537         printf("\nLayout options:  D)imensions, P)osition, B)ack:  ");
538
539         cmd = getLine();
540         switch(toupper(*cmd)) {
541             case 'D':
542                 number("\nNumber of columns", &mn.ncol, 1, 80/MAX_IMAGE_NAME);
543                 number("Entries per column", &mn.maxcol, 1, 30);
544                 if (mn.ncol > 1) {
545                     getXY("Column pitch", &mn.xpitch, 8, 0);
546                     number("Spill threshold", &mn.mincol, 1, mn.maxcol);
547                 }
548                 break;
549             case 'P':
550                 getXY("\nTable UL column", &mn.col, 8, 1);
551                 getXY("Table UL row", &mn.row, 16, 1);
552                 break;
553             case 'B':
554                 editing = 0;
555                 break;
556             default:
557                 printf("???");
558         }
559         free(cmd);
560         printf("\n");
561     } while (editing);
562 }
563
564
565 static void edit_colors(void)
566 {
567     char *cmd;
568     int editing = 1;
569     
570     do {
571         printf("\n");
572         show_colors(1);
573         
574         printf("\nText color options:  N)ormal, H)ighlight, ");
575         if (timer) printf("T)imer, ");
576         printf("B)ack:  ");
577
578         cmd = getLine();
579         switch(toupper(*cmd)) {
580             case 'N':
581                 get3colors("Normal text", &mn.fg);
582                 break;
583             case 'H':
584                 get3colors("Highlight text", &mn.h_fg);
585                 break;
586             case 'T':
587                 if (timer) get3colors("Timer text", &mn.t_fg);
588                 else goto bad;
589                 break;
590             case 'B':
591                 editing = 0;
592                 break;
593             default:
594             bad:
595                 printf("???");
596         }
597         free(cmd);
598         printf("\n");
599     } while (editing);
600 }
601
602
603 static void edit_bitmap(char *bitmap_file)
604 {
605     char *cmd;
606     int editing = 1;
607     
608     printf("Editing contents of bitmap file:  %s\n", bitmap_file);
609     
610     bmp_file_open(bitmap_file);
611
612     do {
613         show_layout();
614         printf("\nText colors:\n");
615         show_colors(0);
616         show_timer();
617         
618         printf("\nCommands are:  L)ayout, C)olors, T)imer, Q)uit, W)rite:  ");
619         cmd = getLine();
620         switch(toupper(*cmd)) {
621             case 'C':
622                 edit_colors();
623                 break;
624             case 'L':
625                 edit_layout();
626                 break;
627             case 'T':
628                 edit_timer();
629                 break;
630             case 'W':
631                 if (yesno("Save companion configuration file?", 0))
632                                                 dat_file_creat(bitmap_file);
633                 editing = !yesno("Save changes to bitmap file?", 0);
634                 if (!editing) {
635                     printf("Writing output file:  %s\n", bitmap_file);
636                     bmp_file_close(!test);  /* update */
637                     if (test) printf("***The bitmap file has not been changed***\n");
638                 }
639                 break;
640             case 'Q':
641                 editing = !yesno("Abandon changes?", 0);
642                 if (!editing) bmp_file_close(0);  /* no update */
643                 break;
644             default:
645                 printf("???");
646         }
647         free(cmd);
648         printf("\n");
649     } while (editing);
650     exit(0);
651 }
652
653
654 static void transfer_params(char *config_file)
655 {
656     int n;
657     char *bitmap_file, *opt;
658     char *cp;
659     int cfd;
660
661     cfg_bitmap_only();          /* disable everything but cf_bitmap */
662     
663     cfd = cfg_open(config_file);
664     if (verbose >= 3) printf("cfg_open returns: %d\n", cfd);
665     n = cfg_parse(cf_bitmap);
666     if (verbose >= 3) printf("cfg_parse returns: %d\n", n);
667     if (n != 0) {
668         die("Illegal token in '%s'", config_file);
669     }
670     if ((bitmap_file = cfg_get_strg(cf_bitmap, "bitmap")) != NULL) {
671         opt = "Using";
672         cp = strrchr(config_file, '/');
673         if (cp && bitmap_file[0] != '/') {
674             *++cp = 0;
675             bitmap_file = strcat(strcpy(alloc(strlen(config_file) + strlen(bitmap_file) + 1),
676                                         config_file),
677                                  bitmap_file);
678             *cp = '/';
679         }
680     } else {
681         opt = "Assuming";
682         cp = strrchr(config_file, '.');
683         if (cp) *cp = 0;
684         bitmap_file = alloc(strlen(config_file) + strlen(BMP_BMP) + 1);
685         strcpy(bitmap_file, config_file);
686         strcat(bitmap_file, BMP_BMP);
687         if (cp) *cp = '.';
688     }
689
690     printf("Transfer parameters from '%s' to '%s'", config_file, bitmap_file);
691     if (yesno("?", 0)==0) exit(0);
692
693     if (verbose > 0) printf("%s bitmap file:  %s\n", opt, bitmap_file);
694     
695     bmp_file_open(bitmap_file);
696     
697     bmp_do_table(cfg_get_strg(cf_bitmap, "bmp-table"), menu);
698     bmp_do_colors(cfg_get_strg(cf_bitmap, "bmp-colors"), menu);
699     bmp_do_timer(cfg_get_strg(cf_bitmap, "bmp-timer"), menu);
700     
701     bmp_file_close(1);  /* update */
702     
703     exit(0);
704 }
705
706
707 void do_bitmap_edit(char *filename)
708 {
709     char *bmp = BMP_BMP;
710     char *fn = strrchr(filename, *bmp);
711
712     if (!fn)
713         die ("'%s'/'%s' filename extension required:  %s", BMP_BMP, BMP_CONF, filename);
714
715     if (strcmp(fn, BMP_CONF)==0) transfer_params(filename);
716     if (strcmp(fn, BMP_BMP)==0) edit_bitmap(filename);
717     
718     die("Unknown filename extension:  %s", filename);
719 }
720
721 #undef mn
722 #else   /* STANDALONE */
723
724 static RGB vga_palette[16] = {
725 /*               B       G       R              */
726         {       000,    000,    000,    0 },    /*  k -- black  */
727         {       170,    000,    000,    0 },    /*  b -- blue   */
728         {       000,    170,    000,    0 },    /*  g -- green  */
729         {       170,    170,    000,    0 },    /*  c -- cyan   */
730
731 /*               B       G       R              */
732         {       000,    000,    170,    0 },    /*  r -- red    */
733         {       170,    000,    170,    0 },    /*  m -- magenta */
734         {       000,     85,    170,    0 },    /*  y -- yellow (amber) */
735         {       170,    170,    170,    0 },    /*  w -- white  */
736
737 /*               B       G       R              */
738         {        85,     85,     85,    0 },    /*  K -- BLACK (dark grey) */
739         {       255,    000,    000,    0 },    /*  B -- BLUE   */
740         {       000,    255,    000,    0 },    /*  G -- GREEN  */
741         {       255,    255,    000,    0 },    /*  C -- CYAN   */
742
743 /*               B       G       R              */
744         {       000,    000,    255,    0 },    /*  R -- RED    */
745         {       255,    000,    255,    0 },    /*  M -- MAGENTA */
746         {       000,    255,    255,    0 },    /*  Y -- YELLOW */
747         {       255,    255,    255,    0 }     /*  W -- WHITE  */
748                         };
749
750 FILE* errstd;
751 static BITMAPLILOHEADER lh;
752 static int idx[16];
753
754 static float hue[NPALETTE], y_yiq[NPALETTE], s_hsv[NPALETTE], 
755                         s_hls[NPALETTE], v_hsv[NPALETTE], l_hls[NPALETTE];
756
757 void gsort(float array[])
758 {
759     int i, j;
760     int n=16;
761     
762     for (j=n-1; j>0; j--) {
763         for (i=0; i<j; i++) {
764             if (array[idx[i]] > array[idx[i+1]]) {
765                 int t = idx[i];
766                 idx[i] = idx[i+1];
767                 idx[i+1] = t;
768             }
769         }
770     }
771 }
772
773 #define MAX(a,b) (a>b?a:b)
774 #define MIN(a,b) (a<b?a:b)
775
776 static void compute_arrays(RGB pal[], int n)
777 {
778     int i;
779     float r, g, b, max, min, delta;
780     float l, h, mm, hsv, hls;
781     
782     for (i=0; i<n; i++) {
783         idx[i] = i;
784         r = NORM(pal[i].red);
785         g = NORM(pal[i].green);
786         b = NORM(pal[i].blue);
787         
788         max = MAX(r,g);
789         max = MAX(max,b);
790         min = MIN(r,g);
791         min = MIN(min,b);
792         mm = max+min;
793         l = mm * 0.5;
794         delta = max-min;
795         hsv = (max!=0.0 ? delta/max : 0.0);
796         if (delta==0.0) {
797             hls = 0.0;
798             h = -1.0;
799         } else {
800             hls = delta / ( (mm <= 1.0) ? mm: (2.0 - mm) );
801             h = r==max ? (g - b)/delta :
802                 g==max ? (b - r)/delta + 2 :
803                          (r - g)/delta + 4;
804             h *= 60;
805             if (h < 0) h += 360;
806         }
807 /* compute the YIQ luminance [0..1] */
808         y_yiq[i] = r*0.3 + g*0.59 + b*0.11;
809         
810         l_hls[i] = l;
811         s_hls[i] = hls;
812         s_hsv[i] = hsv;
813         v_hsv[i] = max;
814         hue[i] = h;
815         
816     }  /* for ... */
817 }
818
819
820 char *Hue(int idx)
821 {
822     static char val[8];
823     static const char name[] = "RYGCBM";
824     int i;
825     float h;
826     
827     h = hue[idx];
828     if (h<0) return "";
829     h += 30;
830     i = h/60.0;
831     h -= i*60.0;
832     i %= 6;
833     h -= 30;
834     if (fabs(h)<0.1) { val[0]=name[i]; val[1]=0; }
835     else sprintf(val,"%c%+3.1f", name[i], h);
836     
837     return val;
838 }
839
840
841 void printline(RGB pal[], int i)
842 {
843 /*            R   G   B   i     Y      V    S(hsv)   S(hls)   L      H       */
844     printf("(%3d,%3d,%3d)%3d  %6.3f  %6.3f  %6.3f    %6.3f  %6.3f  %+6.1f  %s\n",
845         pal[i].red, pal[i].green, pal[i].blue, i,
846         y_yiq[i], v_hsv[i], s_hsv[i], s_hls[i], l_hls[i], hue[i], Hue(i) );
847 }
848
849
850 void printpalette(RGB pal[], int n)
851 {
852     int i;
853 /*            R   G   B   i     Y      V    S(hsv)   S(hls)   L      H       */
854 printf("   R   G   B   i     Y       V     S-hsv     S-hls     L       H\n");
855     for (i=0; i<n; i++)  printline(pal, idx[i]);
856     printf("\n");
857 }
858
859
860 int main(int argc, char *argv[])
861 {
862     int ifd;
863     char *outname;
864     int i;
865 /*    char *cc = "kbgcrmywKBGCRMYW";  */
866     
867     errstd = stderr;
868     if (argc < 2) {
869         printf("Input file not specified\n");
870         exit(1);
871     }
872     ifd = open(argv[1], O_RDONLY);
873     if (ifd<0) pdie("opening input file");
874     if (argc > 2) die("Too many arguments");
875     if (argc < 3) outname = "out.bmp";
876     else outname = argv[2];
877
878     compute_arrays(vga_palette, 16);
879     printf("\nVGA palette:\n\n\n");
880     printpalette(vga_palette, 16);
881     gsort(y_yiq);
882     printf("\n\nVGA pallette by luminance\n\n\n");
883     printpalette(vga_palette, 16);
884     
885     i = get_std_headers(ifd, &fh, &bmh, &lh);
886     if (i) {
887         printf("Error exit on GET:  %d\n", i);
888         exit(i);
889     }
890     printf("\n\n\nContained palette:\n\n");
891
892     compute_arrays(palette, 16);
893     
894     gsort(y_yiq);
895     printpalette(palette, 16);
896     
897     close(ifd);
898
899     return 0;
900 }
901
902 #endif  /* STANDALONE */