1 /************************************************
3 * Converting binary resources from/to *.rc files
5 * Copyright 1999 Juergen Schmied
6 * Copyright 2003 Dimitrie O. Paun
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.
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.
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
31 #ifdef HAVE_SYS_PARAM_H
32 # include <sys/param.h>
39 #define DIR_SEPARATOR "\\"
43 #define DIR_SEPARATOR "/"
45 #define C_BAD_SEP '\\'
48 extern int mkstemps(char *template, int suffix_len
);
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
);
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"
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"
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"
81 "To force processing of all resources, use the -f flag.\n"
82 "To process a particular file, use the -i/-o options.\n";
90 int insert_hexdump (FILE* outfile
, FILE* infile
)
94 fprintf (outfile
, "{\n '");
95 for (i
= 0; (c
= fgetc(infile
)) != EOF
; i
++)
97 if (i
&& (i
% 16) == 0) fprintf (outfile
, "'\n '");
98 if (i
% 16) fprintf (outfile
, " ");
99 fprintf(outfile
, "%02X", c
);
101 fprintf (outfile
, "'\n}");
108 if (!isxdigit(c
)) return -1024;
109 if (isdigit(c
)) return c
- '0';
110 return toupper(c
) - 'A' + 10;
113 int extract_hexdump (FILE* outfile
, FILE* infile
)
117 while ( (c
= fgetc(infile
)) != EOF
&& c
!= '}')
119 if (isspace(c
) || c
== '\'') continue;
120 byte
= 16 * hex2bin(c
);
122 if (c
== EOF
) return 0;
124 if (byte
< 0) return 0;
125 fputc(byte
, outfile
);
130 const char* parse_marker(const char *line
, time_t* last_updated
)
132 static char res_file_name
[PATH_MAX
], *rpos
, *wpos
;
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
++;
140 *last_updated
= (stat(res_file_name
, &st
) < 0) ? 0 : st
.st_mtime
;
142 return res_file_name
;
145 const char* parse_include(const char *line
)
147 static char include_filename
[PATH_MAX
], *rpos
, *wpos
;
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;
158 return include_filename
;
161 char* get_filename_with_full_path(char* output
, const char* filename
, const char* relative_path
)
163 if (relative_path
!= NULL
&& relative_path
[0] != 0)
165 strcpy(output
, relative_path
);
166 strcat(output
, DIR_SEPARATOR
);
167 strcat(output
, filename
);
170 strcpy(output
, filename
);
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
)
178 char filename
[PATH_MAX
];
180 const char *include_file_name
;
184 if (!(fin
= fopen(input_file_name
, "r"))) return;
185 for (c
= EOF
; fgets(buffer
, sizeof(buffer
), fin
); c
= EOF
)
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
);
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
)
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;
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
;
214 strcpy(tmp_file_name
, input_file_name
);
215 strcat(tmp_file_name
, "-XXXXXX.temp");
216 if ((fd
= mkstemps(tmp_file_name
, 5)) == -1)
218 strcpy(tmp_file_name
, "/tmp/bin2res-XXXXXX.temp");
219 if ((fd
= mkstemps(tmp_file_name
, 5)) == -1) return 0;
221 if (!(ftmp
= fdopen(fd
, "w"))) return 0;
224 for (c
= EOF
; fgets(buffer
, sizeof(buffer
), fin
); c
= EOF
)
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
)) )
231 if (verbose
) printf("skipping '%s'\n", res_file_name
);
235 if (verbose
) printf("processing '%s'\n", res_file_name
);
236 while ( (c
= fgetc(fin
)) != EOF
&& c
!= '{')
237 if (inserting
) fputc(c
, ftmp
);
240 if (!(fres
= fopen(get_filename_with_full_path(filename
, res_file_name
, relative_path
),
241 inserting
? "rb" : "wb"))) break;
244 if (!insert_hexdump(ftmp
, fres
)) break;
245 while ( (c
= fgetc(fin
)) != EOF
&& c
!= '}') /**/;
249 if (!extract_hexdump(fres
, fin
)) break;
261 if (rename(tmp_file_name
, input_file_name
) < 0)
263 /* try unlinking first, Windows rename is brain-damaged */
264 if (unlink(input_file_name
) < 0 || rename(tmp_file_name
, input_file_name
) < 0)
266 unlink(tmp_file_name
);
271 else unlink(tmp_file_name
);
275 process_includes(input_file_name
, specific_file_name
, relative_path
,
276 inserting
, force_processing
, verbose
);
282 char* fix_path_sep(char* name
)
284 char *new_name
, *ptr
;
286 ptr
= new_name
= strdup(name
);
289 if (*ptr
== C_BAD_SEP
)
298 int main(int argc
, char **argv
)
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;
306 while((optc
= getopt(argc
, argv
, "axi:o:b:fhv")) != EOF
)
312 if (convert_dir
) usage();
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();
324 if (relative_path
) usage();
325 relative_path
= fix_path_sep(optarg
);
342 if (optind
+ 1 != argc
) usage();
343 input_file_name
= fix_path_sep(argv
[optind
]);
345 if (!convert_dir
) usage();
347 if (!process_resources(input_file_name
, specific_file_name
, relative_path
,
348 convert_dir
== 'a', force_overwrite
, verbose
))
350 perror("Processing failed");