home *** CD-ROM | disk | FTP | other *** search
/ Power-Programmierung / CD1.mdf / rexx / library2 / gbmrexx / gbm / gbmbpp.c < prev    next >
C/C++ Source or Header  |  1993-09-14  |  15KB  |  604 lines

  1. /*
  2.  
  3. GBMBPP.C  Change bits per pixel in a General Bitmap
  4.  
  5. */
  6.  
  7. /*...sincludes:0:*/
  8. #include <stdio.h>
  9. #include <ctype.h>
  10. #include <string.h>
  11. #include <stddef.h>
  12. #include <stdlib.h>
  13. #include <stdarg.h>
  14. #include <memory.h>
  15. #include <malloc.h>
  16. #ifdef AIX
  17. #include <unistd.h>
  18. #else
  19. #include <io.h>
  20. #endif
  21. #include <fcntl.h>
  22. #include <sys/types.h>
  23. #include <sys/stat.h>
  24. #ifndef O_BINARY
  25. #define    O_BINARY    0
  26. #endif
  27. #include "standard.h"
  28. #include "gbm.h"
  29. #include "gbmerr.h"
  30. #include "gbmtrunc.h"
  31. #include "gbmht.h"
  32. #include "gbmhist.h"
  33.  
  34. /*...vgbm\46\h:0:*/
  35. /*...vgbmerr\46\h:0:*/
  36. /*...vgbmtrunc\46\h:0:*/
  37. /*...vgbmht\46\h:0:*/
  38. /*...vgbmhist\46\h:0:*/
  39. /*...e*/
  40.  
  41. static char progname [] = "gbmbpp";
  42. static char sccs_id [] = "@(#)Change bits per pixel in a General Bitmap  1/3/93";
  43.  
  44. /*...sfatal:0:*/
  45. static void fatal(const char *fmt, ...)
  46.     {
  47.     va_list    vars;
  48.     char    s [256+1];
  49.  
  50.     va_start(vars, fmt);
  51.     vsprintf(s, fmt, vars);
  52.     va_end(vars);
  53.     fprintf(stderr, "%s: %s\n", progname, s);
  54.     exit(1);
  55.     }
  56. /*...e*/
  57. /*...susage:0:*/
  58. static void usage(void)
  59.     {
  60.     int    ft, n_ft;
  61.  
  62.     fprintf(stderr, "usage: %s [-m map] [-e] [-h] fn1.ext{,opt} [fn2.ext{,opt}]\n", progname);
  63.     fprintf(stderr, "flags: -m map         mapping to perform (default 7x8x4)\n");
  64.     fprintf(stderr, "                      bw           black and white\n");
  65.     fprintf(stderr, "                      vga          16 colour VGA\n");
  66.     fprintf(stderr, "                      8            8 colour (in 4 bit file)\n");
  67.     fprintf(stderr, "                      4g           4 bit greyscale\n");
  68.     fprintf(stderr, "                      7x8x4        7 levels red, 8 green, 4 blue 8514/A\n");
  69.     fprintf(stderr, "                      6x6x6        6 levels red, 6 green, 6 blue\n");
  70.     fprintf(stderr, "                      8g           8 bit greyscale\n");
  71.     fprintf(stderr, "                      tripel       64 reds, 64 greens, 64 blues tripel\n");
  72.     fprintf(stderr, "                      freqR:G:B:N  keep R red, G green, b blue bits, and map to\n");
  73.     fprintf(stderr, "                                   N most used colours in 8 bit palette\n");
  74.     fprintf(stderr, "                      R:G:B        keep R red, G green, B blue bits (eg: 8:8:8)\n");
  75.     fprintf(stderr, "       -e             enable error-diffusion (default is to truncate)\n");
  76.     fprintf(stderr, "                      -e not with -m 8g or tripel or freq or -h\n");
  77.     fprintf(stderr, "       -h             enable halftoning (default is to truncate)\n");
  78.     fprintf(stderr, "                      -h only with -m 7x8x4, 6x6x6, 8, vga or R:G:B, with no -e\n");
  79.     fprintf(stderr, "       fn1.ext{,opt}  input filename (with any format specific options)\n");
  80.     fprintf(stderr, "       fn2.ext{,opt}  optional output filename (or will use fn1 if not present)\n");
  81.     fprintf(stderr, "                      ext's are used to deduce desired bitmap file formats\n");
  82.  
  83.     gbm_init();
  84.     gbm_query_n_filetypes(&n_ft);
  85.     for ( ft = 0; ft < n_ft; ft++ )
  86.         {
  87.         GBMFT gbmft;
  88.  
  89.         gbm_query_filetype(ft, &gbmft);
  90.         fprintf(stderr, "                      %s when ext in [%s]\n",
  91.             gbmft.short_name, gbmft.extensions);
  92.         }
  93.     gbm_deinit();
  94.  
  95.     fprintf(stderr, "       opt's          bitmap format specific options\n");
  96.  
  97.     exit(1);
  98.     }
  99. /*...e*/
  100. /*...ssame:0:*/
  101. static BOOLEAN same(char *s1, char *s2, int n)
  102.     {
  103.     for ( ; n--; s1++, s2++ )
  104.         if ( tolower(*s1) != tolower(*s2) )
  105.             return ( FALSE );
  106.     return ( TRUE );
  107.     }
  108. /*...e*/
  109. /*...smain:0:*/
  110. /*...smapinfos:0:*/
  111. #define    CVT_BW        0
  112. #define    CVT_VGA        1
  113. #define    CVT_8        2
  114. #define    CVT_4G        3
  115. #define    CVT_784        4
  116. #define    CVT_666        5
  117. #define    CVT_8G        6
  118. #define    CVT_TRIPEL    7
  119. #define    CVT_RGB        8
  120. #define    CVT_FREQ    9
  121. #define    CVT_ERRDIFF    0x4000
  122. #define    CVT_HALFTONE    0x2000
  123.  
  124. typedef struct { char *name; int m; int dest_bpp; } MAPINFO;
  125.  
  126. static MAPINFO mapinfos [] =
  127.     {
  128.     "bw",        CVT_BW,        1,
  129.     "vga",        CVT_VGA,    4,
  130.     "8",        CVT_8,        4,
  131.     "4g",        CVT_4G,        4,
  132.     "7x8x4",    CVT_784,    8,
  133.     "6x6x6",    CVT_666,    8,
  134.     "8g",        CVT_8G,        8,
  135.     "tripel",    CVT_TRIPEL,    8,
  136.     };
  137.  
  138. #define    N_MAPINFOS    (sizeof(mapinfos)/sizeof(mapinfos [0]))
  139. /*...e*/
  140. /*...sget_masks:0:*/
  141. /*
  142. Returns TRUE if a set of masks given at map.
  143. Also sets *rm, *gm, *bm from these.
  144. Else returns FALSE.
  145. */
  146.  
  147. static byte mask [] = { 0x00, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff };
  148.  
  149. static BOOLEAN get_masks(char *map, byte *rm, byte *gm, byte *bm)
  150.     {
  151.     if ( map [0] <  '0' || map [0] > '8' ||
  152.          map [1] != ':' ||
  153.          map [2] <  '0' || map [2] > '8' ||
  154.          map [3] != ':' ||
  155.          map [4] <  '0' || map [4] > '8' )
  156.         return ( FALSE );
  157.  
  158.     *rm = mask [map [0] - '0'];
  159.     *gm = mask [map [2] - '0'];
  160.     *bm = mask [map [4] - '0'];
  161.     return ( TRUE );
  162.     }
  163. /*...e*/
  164. /*...sexpand_to_24bit:0:*/
  165. static void expand_to_24bit(GBM *gbm, GBMRGB *gbmrgb, byte **data)
  166.     {
  167.     int    stride = ((gbm -> w * gbm -> bpp + 31)/32) * 4;
  168.     int    new_stride = ((gbm -> w * 3 + 3) & ~3);
  169.     int    bytes, y;
  170.     byte    *new_data;
  171.  
  172.     if ( gbm -> bpp == 24 )
  173.         return;
  174.  
  175.     bytes = new_stride * gbm -> h;
  176.     if ( (new_data = malloc(bytes)) == NULL )
  177.         fatal("out of memory allocating %d bytes", bytes);
  178.  
  179.     for ( y = 0; y < gbm -> h; y++ )
  180.         {
  181.         byte    *src = *data + y * stride;
  182.         byte    *dest = new_data + y * new_stride;
  183.         int    x;
  184.  
  185.         switch ( gbm -> bpp )
  186.             {
  187. /*...s1:24:*/
  188. case 1:
  189.     {
  190.     byte    c;
  191.  
  192.     for ( x = 0; x < gbm -> w; x++ )
  193.         {
  194.         if ( (x & 7) == 0 )
  195.             c = *src++;
  196.         else
  197.             c <<= 1;
  198.  
  199.         *dest++ = gbmrgb [c >> 7].b;
  200.         *dest++ = gbmrgb [c >> 7].g;
  201.         *dest++ = gbmrgb [c >> 7].r;
  202.         }
  203.     }
  204.     break;
  205. /*...e*/
  206. /*...s4:24:*/
  207. case 4:
  208.     for ( x = 0; x + 1 < gbm -> w; x += 2 )
  209.         {
  210.         byte    c = *src++;
  211.  
  212.         *dest++ = gbmrgb [c >> 4].b;
  213.         *dest++ = gbmrgb [c >> 4].g;
  214.         *dest++ = gbmrgb [c >> 4].r;
  215.         *dest++ = gbmrgb [c & 15].b;
  216.         *dest++ = gbmrgb [c & 15].g;
  217.         *dest++ = gbmrgb [c & 15].r;
  218.         }
  219.  
  220.     if ( x < gbm -> w )
  221.         {
  222.         byte    c = *src;
  223.  
  224.         *dest++ = gbmrgb [c >> 4].b;
  225.         *dest++ = gbmrgb [c >> 4].g;
  226.         *dest++ = gbmrgb [c >> 4].r;
  227.         }
  228.     break;
  229. /*...e*/
  230. /*...s8:24:*/
  231. case 8:
  232.     for ( x = 0; x < gbm -> w; x++ )
  233.         {
  234.         byte    c = *src++;
  235.  
  236.         *dest++ = gbmrgb [c].b;
  237.         *dest++ = gbmrgb [c].g;
  238.         *dest++ = gbmrgb [c].r;
  239.         }
  240.     break;
  241. /*...e*/
  242.             }
  243.         }
  244.     free(*data);
  245.     *data = new_data;
  246.     gbm -> bpp = 24;
  247.     }
  248. /*...e*/
  249. /*...sto_grey_pal:0:*/
  250. static void to_grey_pal(GBMRGB *gbmrgb)
  251.     {
  252.     int    i;
  253.  
  254.     for ( i = 0; i < 0x100; i++ )
  255.         gbmrgb [i].r =
  256.         gbmrgb [i].g =
  257.         gbmrgb [i].b = (byte) i;
  258.     }
  259. /*...e*/
  260. /*...sto_grey:0:*/
  261. static void to_grey(GBM *gbm, byte *src_data, byte *dest_data)
  262.     {
  263.     int    src_stride  = ((gbm -> w * 3 + 3) & ~3);
  264.     int    dest_stride = ((gbm -> w     + 3) & ~3);
  265.     int    y;
  266.  
  267.     for ( y = 0; y < gbm -> h; y++ )
  268.         {
  269.         byte    *src  = src_data;
  270.         byte    *dest = dest_data;
  271.         int    x;
  272.  
  273.         for ( x = 0; x < gbm -> w; x++ )
  274.             {
  275.             byte    b = *src++;
  276.             byte    g = *src++;
  277.             byte    r = *src++;
  278.  
  279.             *dest++ = (byte) (((word) r * 77 + (word) g * 151 + (word) b * 28) >> 8);
  280.             }
  281.  
  282.         src_data  += src_stride;
  283.         dest_data += dest_stride;
  284.         }
  285.     gbm -> bpp = 8;
  286.     }
  287. /*...e*/
  288. /*...stripel_pal:0:*/
  289. static void tripel_pal(GBMRGB *gbmrgb)
  290.     {
  291.     int    i;
  292.  
  293.     memset(gbmrgb, 0, 0x100 * sizeof(GBMRGB));
  294.  
  295.     for ( i = 0; i < 0x40; i++ )
  296.         {
  297.         gbmrgb [i       ].r = (byte) (i << 2);
  298.         gbmrgb [i + 0x40].g = (byte) (i << 2);
  299.         gbmrgb [i + 0x80].b = (byte) (i << 2);
  300.         }
  301.     }
  302. /*...e*/
  303. /*...stripel:0:*/
  304. static void tripel(GBM *gbm, byte *src_data, byte *dest_data)
  305.     {
  306.     int    src_stride  = ((gbm -> w * 3 + 3) & ~3);
  307.     int    dest_stride = ((gbm -> w     + 3) & ~3);
  308.     int    y;
  309.  
  310.     for ( y = 0; y < gbm -> h; y++ )
  311.         {
  312.         byte    *src  = src_data;
  313.         byte    *dest = dest_data;
  314.         int    x;
  315.  
  316.         for ( x = 0; x < gbm -> w; x++ )
  317.             {
  318.             byte    b = *src++;
  319.             byte    g = *src++;
  320.             byte    r = *src++;
  321.  
  322.             switch ( (x+y)%3 )
  323.                 {
  324.                 case 0:    *dest++ = (byte)         (r >> 2) ;    break;
  325.                 case 1:    *dest++ = (byte) (0x40 + (g >> 2));    break;
  326.                 case 2:    *dest++ = (byte) (0x80 + (b >> 2));    break;
  327.                 }
  328.             }
  329.  
  330.         src_data  += src_stride;
  331.         dest_data += dest_stride;
  332.         }
  333.     gbm -> bpp = 8;
  334.     }
  335. /*...e*/
  336.  
  337. int    main(int argc, char *argv [])
  338.     {
  339.     BOOLEAN    errdiff = FALSE, halftone = FALSE, ok = TRUE;
  340.     char    *map = "7x8x4";
  341.     char    fn_src [500+1], fn_dst [500+1], *opt_src, *opt_dst;
  342.     int    fd, ft_src, ft_dst, i, stride, bytes, flag, m, dest_bpp;
  343.     byte    rm, gm, bm;
  344.     int    ncols;
  345.     GBM_ERR    rc;
  346.     GBMFT    gbmft;
  347.     GBM    gbm;
  348.     GBMRGB    gbmrgb [0x100];
  349.     byte    *data;
  350.  
  351. /*...sprocess command line options:8:*/
  352. for ( i = 1; i < argc; i++ )
  353.     {
  354.     if ( argv [i][0] != '-' )
  355.         break;
  356.     switch ( argv [i][1] )
  357.         {
  358.         case 'e':    errdiff = TRUE;
  359.                 break;
  360.         case 'h':    halftone = TRUE;
  361.                 break;
  362.         case 'm':    if ( ++i == argc )
  363.                     fatal("expected map argument");
  364.                 map = argv [i];
  365.                 break;
  366.         default:    usage();
  367.                 break;
  368.         }
  369.     }
  370.  
  371. if ( errdiff && halftone )
  372.     fatal("error-diffusion and halftoning can't both be done at once");
  373. /*...e*/
  374.  
  375. /*...sdeduce mapping and bits per pixel etc\46\:8:*/
  376. if ( get_masks(map, &rm, &gm, &bm) && map [5] == '\0' )
  377.     {
  378.     m = CVT_RGB;
  379.     dest_bpp = 24;
  380.     }
  381. else if ( same(map, "freq", 4) )
  382.     {
  383.     m = CVT_FREQ;
  384.     dest_bpp = 8;
  385.     if ( !get_masks(map + 4, &rm, &gm, &bm) )
  386.         fatal("freqR:G:B:N has bad/missing R:G:B");
  387.     if ( map [9] != ':' )
  388.         fatal("freqR:G:B:N has bad/missing :N");
  389.     sscanf(map + 10, "%i", &ncols);
  390.     if ( ncols < 1 || ncols > 256 )
  391.         fatal("freqR:G:B:N N number between 1 and 256 required");
  392.     }
  393. else
  394.     {
  395.     int    j;
  396.  
  397.     for ( j = 0; j < N_MAPINFOS; j++ )
  398.         if ( same(map, mapinfos [j].name, strlen(map) + 1) )
  399.             break;
  400.     if ( j == N_MAPINFOS )
  401.         fatal("unrecognised mapping %s", map);
  402.     m        = mapinfos [j].m;
  403.     dest_bpp = mapinfos [j].dest_bpp;
  404.     }
  405. /*...e*/
  406.  
  407.     if ( i == argc )
  408.         usage();
  409.     strcpy(fn_src, argv [i++]);
  410.     strcpy(fn_dst, ( i == argc ) ? fn_src : argv [i++]);
  411.     if ( i < argc )
  412.         usage();
  413.  
  414.     if ( (opt_src = strchr(fn_src, ',')) != NULL )
  415.         *opt_src++ = '\0';
  416.     else
  417.         opt_src = "";
  418.  
  419.     if ( (opt_dst = strchr(fn_dst, ',')) != NULL )
  420.         *opt_dst++ = '\0';
  421.     else
  422.         opt_dst = "";
  423.  
  424.     gbm_init();
  425.  
  426.     if ( gbm_guess_filetype(fn_src, &ft_src) != GBM_ERR_OK )
  427.         fatal("can't guess bitmap file format for %s", fn_src);
  428.  
  429.     if ( gbm_guess_filetype(fn_dst, &ft_dst) != GBM_ERR_OK )
  430.         fatal("can't guess bitmap file format for %s", fn_dst);
  431.  
  432.     if ( (fd = open(fn_src, O_RDONLY | O_BINARY)) == -1 )
  433.         fatal("can't open %s", fn_src);
  434.  
  435.     if ( (rc = gbm_read_header(fn_src, fd, ft_src, &gbm, opt_src)) != GBM_ERR_OK )
  436.         {
  437.         close(fd);
  438.         fatal("can't read header of %s: %s", fn_src, gbm_err(rc));
  439.         }
  440.  
  441.     gbm_query_filetype(ft_dst, &gbmft);
  442.     switch ( dest_bpp )
  443.         {
  444.         case 24:    flag = GBM_FT_W24;    break;
  445.         case 8:        flag = GBM_FT_W8;    break;
  446.         case 4:        flag = GBM_FT_W4;    break;
  447.         case 1:        flag = GBM_FT_W1;    break;
  448.         }
  449.  
  450.     if ( (gbmft.flags & flag) == 0 )
  451.         {
  452.         close(fd);
  453.         fatal("output bitmap format %s does not support writing %d bpp data",
  454.             gbmft.short_name, dest_bpp);
  455.         }
  456.  
  457.     if ( (rc = gbm_read_palette(fd, ft_src, &gbm, gbmrgb)) != GBM_ERR_OK )
  458.         {
  459.         close(fd);
  460.         fatal("can't read palette of %s: %s", fn_src, gbm_err(rc));
  461.         }
  462.  
  463.     stride = ( ((gbm.w * gbm.bpp + 31)/32) * 4 );
  464.     bytes = stride * gbm.h;
  465.     if ( (data = malloc(bytes)) == NULL )
  466.         {
  467.         close(fd);
  468.         fatal("out of memory allocating %d bytes", bytes);
  469.         }
  470.  
  471.     if ( (rc = gbm_read_data(fd, ft_src, &gbm, data)) != GBM_ERR_OK )
  472.         {
  473.         free(data);
  474.         close(fd);
  475.         fatal("can't read bitmap data of %s: %s", fn_src, gbm_err(rc));
  476.         }
  477.  
  478.     close(fd);
  479.  
  480.     /* Now expand bits per pixel if necessary */
  481.  
  482.     expand_to_24bit(&gbm, gbmrgb, &data);
  483.  
  484.     if ( errdiff )
  485.         m |= CVT_ERRDIFF;
  486.  
  487.     if ( halftone )
  488.         m |= CVT_HALFTONE;
  489.  
  490.     switch ( m )
  491.         {
  492.         case CVT_BW:
  493.             gbm_trunc_pal_BW(gbmrgb);
  494.             gbm_trunc_BW(&gbm, data, data);
  495.             break;
  496.         case CVT_4G:
  497.             gbm_trunc_pal_4G(gbmrgb);
  498.             gbm_trunc_4G(&gbm, data, data);
  499.             break;
  500.         case CVT_8:
  501.             gbm_trunc_pal_8(gbmrgb);
  502.             gbm_trunc_8(&gbm, data, data);
  503.             break;
  504.         case CVT_VGA:
  505.             gbm_trunc_pal_VGA(gbmrgb);
  506.             gbm_trunc_VGA(&gbm, data, data);
  507.             break;
  508.         case CVT_784:
  509.             gbm_trunc_pal_7R8G4B(gbmrgb);
  510.             gbm_trunc_7R8G4B(&gbm, data, data);
  511.             break;
  512.         case CVT_666:
  513.             gbm_trunc_pal_6R6G6B(gbmrgb);
  514.             gbm_trunc_6R6G6B(&gbm, data, data);
  515.             break;
  516.         case CVT_8G:
  517.             to_grey_pal(gbmrgb);
  518.             to_grey(&gbm, data, data);
  519.             break;
  520.         case CVT_TRIPEL:
  521.             tripel_pal(gbmrgb);
  522.             tripel(&gbm, data, data);
  523.             break;
  524.         case CVT_FREQ:
  525.             memset(gbmrgb, 0, sizeof(gbmrgb));
  526.             gbm_hist(&gbm, data, gbmrgb, data, ncols, rm, gm, bm);
  527.             break;
  528.         case CVT_RGB:
  529.             gbm_trunc_24(&gbm, data, data, rm, gm, bm);
  530.             break;
  531.         case CVT_BW | CVT_ERRDIFF:
  532.             gbm_errdiff_pal_BW(gbmrgb);
  533.             ok = gbm_errdiff_BW(&gbm, data, data);
  534.             break;
  535.         case CVT_4G | CVT_ERRDIFF:
  536.             gbm_errdiff_pal_4G(gbmrgb);
  537.             ok = gbm_errdiff_4G(&gbm, data, data);
  538.             break;
  539.         case CVT_8 | CVT_ERRDIFF:
  540.             gbm_errdiff_pal_8(gbmrgb);
  541.             ok = gbm_errdiff_8(&gbm, data, data);
  542.             break;
  543.         case CVT_VGA | CVT_ERRDIFF:
  544.             gbm_errdiff_pal_VGA(gbmrgb);
  545.             ok = gbm_errdiff_VGA(&gbm, data, data);
  546.             break;
  547.         case CVT_784 | CVT_ERRDIFF:
  548.             gbm_errdiff_pal_7R8G4B(gbmrgb);
  549.             ok = gbm_errdiff_7R8G4B(&gbm, data, data);
  550.             break;
  551.         case CVT_666 | CVT_ERRDIFF:
  552.             gbm_errdiff_pal_6R6G6B(gbmrgb);
  553.             ok = gbm_errdiff_6R6G6B(&gbm, data, data);
  554.             break;
  555.         case CVT_RGB | CVT_ERRDIFF:
  556.             ok = gbm_errdiff_24(&gbm, data, data, rm, gm, bm);
  557.             break;
  558.         case CVT_784 | CVT_HALFTONE:
  559.             gbm_ht_pal_7R8G4B(gbmrgb);
  560.             gbm_ht_7R8G4B_2x2(&gbm, data, data);
  561.             break;
  562.         case CVT_666 | CVT_HALFTONE:
  563.             gbm_ht_pal_6R6G6B(gbmrgb);
  564.             gbm_ht_6R6G6B_2x2(&gbm, data, data);
  565.             break;
  566.         case CVT_8 | CVT_HALFTONE:
  567.             gbm_ht_pal_8(gbmrgb);
  568.             gbm_ht_8_3x3(&gbm, data, data);
  569.             break;
  570.         case CVT_VGA | CVT_HALFTONE:
  571.             gbm_ht_pal_VGA(gbmrgb);
  572.             gbm_ht_VGA_3x3(&gbm, data, data);
  573.             break;
  574.         case CVT_RGB | CVT_HALFTONE:
  575.             gbm_ht_24_2x2(&gbm, data, data, rm, gm, bm);
  576.             break;
  577.         default:
  578.             fatal("bad mapping/error-diffusion/halftone combination");
  579.         }
  580.  
  581.     if ( !ok )
  582.         fatal("unable to perform mapping");
  583.  
  584.     gbm.bpp = dest_bpp;
  585.  
  586.     if ( (fd = open(fn_dst, O_CREAT | O_TRUNC | O_WRONLY | O_BINARY, S_IREAD | S_IWRITE)) == -1 )
  587.         fatal("can't create %s", fn_dst);
  588.  
  589.     if ( (rc = gbm_write(fn_dst, fd, ft_dst, &gbm, gbmrgb, data, opt_dst)) != GBM_ERR_OK )
  590.         {
  591.         close(fd);
  592.         fatal("can't write %s: %s", fn_dst, gbm_err(rc));
  593.         }
  594.  
  595.     close(fd);
  596.  
  597.     free(data);
  598.  
  599.     gbm_deinit();
  600.  
  601.     return ( 0 );
  602.     }
  603. /*...e*/
  604.