Avoid excessive heap memory reallocation when generating EMF
[wine] / tools / bin2res.c
1 /************************************************
2  *
3  * Converting binary resources from/to *.rc files
4  *
5  * Copyright 1999 Juergen Schmied
6  * Copyright 2003 Dimitrie O. Paun
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2.1 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to the Free Software
20  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21  */
22
23 #include "config.h"
24 #include "wine/port.h"
25
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <ctype.h>
29 #include <string.h>
30 #include <sys/stat.h>
31 #ifdef HAVE_SYS_PARAM_H
32 # include <sys/param.h>
33 #endif
34
35 static const char* help =
36         "Usage: bin2res [OPTIONS] <rsrc.rc>\n"
37         "  -a archive binaries into the <rsrc.rc> file\n"
38         "  -x extract binaries from the <rsrc.rc> file\n"
39         "  -i <filename> archive the named file into the <rsrc.rc> file\n"
40         "  -o <filename> extract the named file from the <rsrc.rc> file\n"
41         "  -f force processing of older resources\n"
42         "  -v causes the command to be verbous during processing\n" 
43         "  -h print this help screen and exit\n"
44         "\n"
45         "This tool allows the insertion/extractions of embedded binary\n"
46         "resources to/from .rc files, for storage within the cvs tree.\n"
47         "This is accomplished by placing a magic marker in a comment\n"
48         "just above the resource. The marker consists of the BINRES\n"
49         "string followed by the file name. For example, to insert a\n"
50         "brand new binary resource in a .rc file, place the marker\n"
51         "above empty brackets:\n"
52         "    /* BINRES idb_std_small.bmp */\n"
53         "   {}\n"
54         "To merge the binary resources into the .rc file, run:\n"
55         "   bin2res -a myrsrc.rc\n"
56         "Only resources that are newer than the .rc are processed.\n"
57         "To extract the binary resources from the .rc file, run:\n"
58         "  bin2res -x myrsrc.rc\n"
59         "Binary files newer than the .rc file are not overwritten.\n"
60         "\n"
61         "To force processing of all resources, use the -f flag.\n"
62         "To process a particular file, use the -i/-o options.\n";
63
64 void usage(void)
65 {
66     printf(help);
67     exit(1);
68 }
69
70 int insert_hexdump (FILE* outfile, FILE* infile)
71 {
72     int i, c;
73
74     fprintf (outfile, "{\n '");
75     for (i = 0; (c = fgetc(infile)) != EOF; i++)
76     {
77         if (i && (i % 16) == 0) fprintf (outfile, "'\n '");
78         if (i % 16)  fprintf (outfile, " ");
79         fprintf(outfile, "%02X", c);
80     }
81     fprintf (outfile, "'\n}");
82
83     return 1;
84 }
85
86 int hex2bin(char c)
87 {
88     if (!isxdigit(c)) return -1024;
89     if (isdigit(c)) return c - '0';
90     return toupper(c) - 'A' + 10;
91 }
92
93 int extract_hexdump (FILE* outfile, FILE* infile)
94 {
95     int byte, c;
96
97     while ( (c = fgetc(infile)) != EOF && c != '}')
98     {
99         if (isspace(c) || c == '\'') continue;
100         byte = 16 * hex2bin(c);
101         c = fgetc(infile);
102         if (c == EOF) return 0;
103         byte += hex2bin(c);
104         if (byte < 0) return 0;
105         fputc(byte, outfile);
106     }
107     return 1;
108 }
109
110 const char* parse_marker(const char *line, time_t* last_updated)
111 {
112     static char res_file_name[PATH_MAX], *rpos, *wpos;
113     struct stat st;
114
115     if (!(rpos = strstr(line, "BINRES"))) return 0;
116     for (rpos += 6; *rpos && isspace(*rpos); rpos++) /**/;
117     for (wpos = res_file_name; *rpos && !isspace(*rpos); ) *wpos++ = *rpos++;
118     *wpos = 0;
119
120     *last_updated = (stat(res_file_name, &st) < 0) ? 0 : st.st_mtime;
121
122     return res_file_name;
123 }
124
125 int process_resources(const char* input_file_name, const char* specific_file_name, 
126                       int inserting, int force_processing, int verbose)
127 {
128     char buffer[2048], tmp_file_name[PATH_MAX];
129     const char *res_file_name;
130     time_t rc_last_update, res_last_update;
131     FILE *fin, *fres, *ftmp = 0;
132     struct stat st;
133     int fd, c;
134
135     if (!(fin = fopen(input_file_name, "r"))) return 0;
136     if (stat(input_file_name, &st) < 0) return 0;
137     rc_last_update = st.st_mtime;
138
139     if (inserting)
140     {
141         strcpy(tmp_file_name, input_file_name);
142         strcat(tmp_file_name, "-XXXXXX.temp");
143         if ((fd = mkstemps(tmp_file_name, 5)) == -1)
144         {
145             strcpy(tmp_file_name, "/tmp/bin2res-XXXXXX.temp");
146             if ((fd = mkstemps(tmp_file_name, 5)) == -1) return 0;
147         }
148         if (!(ftmp = fdopen(fd, "w"))) return 0;
149     }
150
151     for (c = EOF; fgets(buffer, sizeof(buffer), fin); c = EOF)
152     {
153         if (inserting) fprintf(ftmp, "%s", buffer);
154         if (!(res_file_name = parse_marker(buffer, &res_last_update))) continue;
155         if ( (specific_file_name && strcmp(specific_file_name, res_file_name)) ||
156              (!force_processing && ((rc_last_update < res_last_update) == !inserting)) )
157         {
158             if (verbose) printf("skipping '%s'\n", res_file_name);
159             continue;
160         }
161
162         if (verbose) printf("processing '%s'\n", res_file_name);
163         while ( (c = fgetc(fin)) != EOF && c != '{')
164             if (inserting) fputc(c, ftmp);
165         if (c == EOF) break;
166
167         if (!(fres = fopen(res_file_name, inserting ? "rb" : "wb"))) break;
168         if (inserting)
169         {
170             if (!insert_hexdump(ftmp, fres)) break;
171             while ( (c = fgetc(fin)) != EOF && c != '}') /**/;
172         }
173         else
174         {
175             if (!extract_hexdump(fres, fin)) break;
176         }
177         fclose(fres);
178     }
179
180     fclose(fin);
181
182     if (inserting)
183     {
184         fclose(ftmp);
185         if (c == EOF && rename(tmp_file_name, input_file_name) < 0)
186             c = '.'; /* force an error */
187         else unlink(tmp_file_name);
188     }
189
190     return c == EOF;
191 }
192
193 int main(int argc, char **argv)
194 {
195     int convert_dir = 0, optc;
196     int force_overwrite = 0, verbose = 0;
197     const char* input_file_name = 0;
198     const char* specific_file_name = 0;
199
200     while((optc = getopt(argc, argv, "axi:o:fh")) != EOF)
201     {
202         switch(optc)
203         {
204         case 'a':
205         case 'x':
206             if (convert_dir) usage();
207             convert_dir = optc;
208         break;
209         case 'i':
210         case 'o':
211             if (specific_file_name) usage();
212             specific_file_name = optarg;
213             optc = ((optc == 'i') ? 'a' : 'x');
214             if (convert_dir && convert_dir != optc) usage();
215             convert_dir = optc;
216         break;
217         case 'f':
218             force_overwrite = 1;
219         break;
220         case 'v':
221             verbose = 1;
222         break;
223         case 'h':
224             printf(help);
225             exit(0);
226         break;
227         default:
228             usage();
229         }
230     }
231
232     if (optind + 1 != argc) usage();
233     input_file_name = argv[optind];
234
235     if (!convert_dir) usage();
236
237     if (!process_resources(input_file_name, specific_file_name, 
238                            convert_dir == 'a', force_overwrite, verbose))
239     {
240         perror("Processing failed");
241         exit(1);
242     }
243
244     return 0;
245 }