- The separator (slash or back slash), exepostfix and exeprefix are initialized from...
[reactos.git] / reactos / tools / bin2res / 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
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <ctype.h>
28 #include <string.h>
29 #include <sys/stat.h>
30 #include <limits.h>
31 #ifdef HAVE_SYS_PARAM_H
32 # include <sys/param.h>
33 #endif
34 #ifdef HAVE_UNISTD_H
35 # include <unistd.h>
36 #endif
37
38 #if defined(WIN32)
39 #define DIR_SEPARATOR "\\"
40 #define C_SEP '\\'
41 #define C_BAD_SEP '/'
42 #else
43 #define DIR_SEPARATOR "/"
44 #define C_SEP '/'
45 #define C_BAD_SEP '\\'
46 #endif
47
48 extern int mkstemps(char *template, int suffix_len);
49
50 int process_resources(const char* input_file_name, const char* specific_file_name,
51 const char* relative_path,
52 int inserting, int force_processing, int verbose);
53
54 static const char* help =
55 "Usage: bin2res [OPTIONS] <rsrc.rc>\n"
56 " -a archive binaries into the <rsrc.rc> file\n"
57 " -x extract binaries from the <rsrc.rc> file\n"
58 " -i <filename> archive the named file into the <rsrc.rc> file\n"
59 " -o <filename> extract the named file from the <rsrc.rc> file\n"
60 " -b <path> assume resources are relative to this base path\n"
61 " -f force processing of older resources\n"
62 " -v causes the command to be verbous during processing\n"
63 " -h print this help screen and exit\n"
64 "\n"
65 "This tool allows the insertion/extractions of embedded binary\n"
66 "resources to/from .rc files, for storage within the cvs tree.\n"
67 "This is accomplished by placing a magic marker in a comment\n"
68 "just above the resource. The marker consists of the BINRES\n"
69 "string followed by the file name. For example, to insert a\n"
70 "brand new binary resource in a .rc file, place the marker\n"
71 "above empty brackets:\n"
72 " /* BINRES idb_std_small.bmp */\n"
73 " {}\n"
74 "To merge the binary resources into the .rc file, run:\n"
75 " bin2res -a myrsrc.rc\n"
76 "Only resources that are newer than the .rc are processed.\n"
77 "To extract the binary resources from the .rc file, run:\n"
78 " bin2res -x myrsrc.rc\n"
79 "Binary files newer than the .rc file are not overwritten.\n"
80 "\n"
81 "To force processing of all resources, use the -f flag.\n"
82 "To process a particular file, use the -i/-o options.\n";
83
84 void usage(void)
85 {
86 printf(help);
87 exit(1);
88 }
89
90 int insert_hexdump (FILE* outfile, FILE* infile)
91 {
92 int i, c;
93
94 fprintf (outfile, "{\n '");
95 for (i = 0; (c = fgetc(infile)) != EOF; i++)
96 {
97 if (i && (i % 16) == 0) fprintf (outfile, "'\n '");
98 if (i % 16) fprintf (outfile, " ");
99 fprintf(outfile, "%02X", c);
100 }
101 fprintf (outfile, "'\n}");
102
103 return 1;
104 }
105
106 int hex2bin(char c)
107 {
108 if (!isxdigit(c)) return -1024;
109 if (isdigit(c)) return c - '0';
110 return toupper(c) - 'A' + 10;
111 }
112
113 int extract_hexdump (FILE* outfile, FILE* infile)
114 {
115 int byte, c;
116
117 while ( (c = fgetc(infile)) != EOF && c != '}')
118 {
119 if (isspace(c) || c == '\'') continue;
120 byte = 16 * hex2bin(c);
121 c = fgetc(infile);
122 if (c == EOF) return 0;
123 byte += hex2bin(c);
124 if (byte < 0) return 0;
125 fputc(byte, outfile);
126 }
127 return 1;
128 }
129
130 const char* parse_marker(const char *line, time_t* last_updated)
131 {
132 static char res_file_name[PATH_MAX], *rpos, *wpos;
133 struct stat st;
134
135 if (!(rpos = strstr(line, "BINRES"))) return 0;
136 for (rpos += 6; *rpos && isspace(*rpos); rpos++) /**/;
137 for (wpos = res_file_name; *rpos && !isspace(*rpos); ) *wpos++ = *rpos++;
138 *wpos = 0;
139
140 *last_updated = (stat(res_file_name, &st) < 0) ? 0 : st.st_mtime;
141
142 return res_file_name;
143 }
144
145 const char* parse_include(const char *line)
146 {
147 static char include_filename[PATH_MAX], *rpos, *wpos;
148
149 if (!(rpos = strstr(line, "#"))) return 0;
150 for (rpos += 1; *rpos && isspace(*rpos); rpos++) /**/;
151 if (!(rpos = strstr(line, "include"))) return 0;
152 for (rpos += 7; *rpos && isspace(*rpos); rpos++) /**/;
153 for (; *rpos && (*rpos == '"' || *rpos == '<'); rpos++) /**/;
154 for (wpos = include_filename; *rpos && !(*rpos == '"' || *rpos == '<'); ) *wpos++ = *rpos++;
155 if (!(rpos = strstr(include_filename, ".rc"))) return 0;
156 *wpos = 0;
157
158 return include_filename;
159 }
160
161 char* get_filename_with_full_path(char* output, const char* filename, const char* relative_path)
162 {
163 if (relative_path != NULL && relative_path[0] != 0)
164 {
165 strcpy(output, relative_path);
166 strcat(output, DIR_SEPARATOR);
167 strcat(output, filename);
168 }
169 else
170 strcpy(output, filename);
171 return output;
172 }
173
174 void process_includes(const char* input_file_name, const char* specific_file_name,
175 const char* relative_path,
176 int inserting, int force_processing, int verbose)
177 {
178 char filename[PATH_MAX];
179 char buffer[2048];
180 const char *include_file_name;
181 FILE *fin;
182 int c;
183
184 if (!(fin = fopen(input_file_name, "r"))) return;
185 for (c = EOF; fgets(buffer, sizeof(buffer), fin); c = EOF)
186 {
187 if (!(include_file_name = parse_include(buffer))) continue;
188 if ( verbose ) printf ( "Processing included file %s\n", include_file_name);
189 process_resources(get_filename_with_full_path(filename, include_file_name, relative_path),
190 specific_file_name, relative_path,
191 inserting, force_processing, verbose);
192 }
193 fclose(fin);
194 }
195
196 int process_resources(const char* input_file_name, const char* specific_file_name,
197 const char* relative_path,
198 int inserting, int force_processing, int verbose)
199 {
200 char filename[PATH_MAX];
201 char buffer[2048], tmp_file_name[PATH_MAX];
202 const char *res_file_name;
203 time_t rc_last_update, res_last_update;
204 FILE *fin, *fres, *ftmp = 0;
205 struct stat st;
206 int fd, c;
207
208 if (!(fin = fopen(input_file_name, "r"))) return 0;
209 if (stat(input_file_name, &st) < 0) return 0;
210 rc_last_update = st.st_mtime;
211
212 if (inserting)
213 {
214 strcpy(tmp_file_name, input_file_name);
215 strcat(tmp_file_name, "-XXXXXX.temp");
216 if ((fd = mkstemps(tmp_file_name, 5)) == -1)
217 {
218 strcpy(tmp_file_name, "/tmp/bin2res-XXXXXX.temp");
219 if ((fd = mkstemps(tmp_file_name, 5)) == -1) return 0;
220 }
221 if (!(ftmp = fdopen(fd, "w"))) return 0;
222 }
223
224 for (c = EOF; fgets(buffer, sizeof(buffer), fin); c = EOF)
225 {
226 if (inserting) fprintf(ftmp, "%s", buffer);
227 if (!(res_file_name = parse_marker(buffer, &res_last_update))) continue;
228 if ( (specific_file_name && strcmp(specific_file_name, res_file_name)) ||
229 (!force_processing && ((rc_last_update < res_last_update) == !inserting)) )
230 {
231 if (verbose) printf("skipping '%s'\n", res_file_name);
232 continue;
233 }
234
235 if (verbose) printf("processing '%s'\n", res_file_name);
236 while ( (c = fgetc(fin)) != EOF && c != '{')
237 if (inserting) fputc(c, ftmp);
238 if (c == EOF) break;
239
240 if (!(fres = fopen(get_filename_with_full_path(filename, res_file_name, relative_path),
241 inserting ? "rb" : "wb"))) break;
242 if (inserting)
243 {
244 if (!insert_hexdump(ftmp, fres)) break;
245 while ( (c = fgetc(fin)) != EOF && c != '}') /**/;
246 }
247 else
248 {
249 if (!extract_hexdump(fres, fin)) break;
250 }
251 fclose(fres);
252 }
253
254 fclose(fin);
255
256 if (inserting)
257 {
258 fclose(ftmp);
259 if (c == EOF)
260 {
261 if (rename(tmp_file_name, input_file_name) < 0)
262 {
263 /* try unlinking first, Windows rename is brain-damaged */
264 if (unlink(input_file_name) < 0 || rename(tmp_file_name, input_file_name) < 0)
265 {
266 unlink(tmp_file_name);
267 return 0;
268 }
269 }
270 }
271 else unlink(tmp_file_name);
272 }
273 else
274 {
275 process_includes(input_file_name, specific_file_name, relative_path,
276 inserting, force_processing, verbose);
277 }
278
279 return c == EOF;
280 }
281
282 char* fix_path_sep(char* name)
283 {
284 char *new_name, *ptr;
285
286 ptr = new_name = strdup(name);
287 while(*ptr)
288 {
289 if (*ptr == C_BAD_SEP)
290 {
291 *ptr = C_SEP;
292 }
293 ptr++;
294 }
295 return new_name;
296 }
297
298 int main(int argc, char **argv)
299 {
300 int convert_dir = 0, optc;
301 int force_overwrite = 0, verbose = 0;
302 const char* input_file_name = 0;
303 const char* specific_file_name = 0;
304 const char* relative_path = 0;
305
306 while((optc = getopt(argc, argv, "axi:o:b:fhv")) != EOF)
307 {
308 switch(optc)
309 {
310 case 'a':
311 case 'x':
312 if (convert_dir) usage();
313 convert_dir = optc;
314 break;
315 case 'i':
316 case 'o':
317 if (specific_file_name) usage();
318 specific_file_name = fix_path_sep(optarg);
319 optc = ((optc == 'i') ? 'a' : 'x');
320 if (convert_dir && convert_dir != optc) usage();
321 convert_dir = optc;
322 break;
323 case 'b':
324 if (relative_path) usage();
325 relative_path = fix_path_sep(optarg);
326 break;
327 case 'f':
328 force_overwrite = 1;
329 break;
330 case 'v':
331 verbose = 1;
332 break;
333 case 'h':
334 printf(help);
335 exit(0);
336 break;
337 default:
338 usage();
339 }
340 }
341
342 if (optind + 1 != argc) usage();
343 input_file_name = fix_path_sep(argv[optind]);
344
345 if (!convert_dir) usage();
346
347 if (!process_resources(input_file_name, specific_file_name, relative_path,
348 convert_dir == 'a', force_overwrite, verbose))
349 {
350 perror("Processing failed");
351 exit(1);
352 }
353
354 return 0;
355 }