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 "\\"
41 #define DIR_SEPARATOR "/"
44 extern int mkstemps(char *template, int suffix_len
);
46 int process_resources(const char* input_file_name
, const char* specific_file_name
,
47 const char* relative_path
,
48 int inserting
, int force_processing
, int verbose
);
50 static const char* help
=
51 "Usage: bin2res [OPTIONS] <rsrc.rc>\n"
52 " -a archive binaries into the <rsrc.rc> file\n"
53 " -x extract binaries from the <rsrc.rc> file\n"
54 " -i <filename> archive the named file into the <rsrc.rc> file\n"
55 " -o <filename> extract the named file from the <rsrc.rc> file\n"
56 " -b <path> assume resources are relative to this base path\n"
57 " -f force processing of older resources\n"
58 " -v causes the command to be verbous during processing\n"
59 " -h print this help screen and exit\n"
61 "This tool allows the insertion/extractions of embedded binary\n"
62 "resources to/from .rc files, for storage within the cvs tree.\n"
63 "This is accomplished by placing a magic marker in a comment\n"
64 "just above the resource. The marker consists of the BINRES\n"
65 "string followed by the file name. For example, to insert a\n"
66 "brand new binary resource in a .rc file, place the marker\n"
67 "above empty brackets:\n"
68 " /* BINRES idb_std_small.bmp */\n"
70 "To merge the binary resources into the .rc file, run:\n"
71 " bin2res -a myrsrc.rc\n"
72 "Only resources that are newer than the .rc are processed.\n"
73 "To extract the binary resources from the .rc file, run:\n"
74 " bin2res -x myrsrc.rc\n"
75 "Binary files newer than the .rc file are not overwritten.\n"
77 "To force processing of all resources, use the -f flag.\n"
78 "To process a particular file, use the -i/-o options.\n";
86 int insert_hexdump (FILE* outfile
, FILE* infile
)
90 fprintf (outfile
, "{\n '");
91 for (i
= 0; (c
= fgetc(infile
)) != EOF
; i
++)
93 if (i
&& (i
% 16) == 0) fprintf (outfile
, "'\n '");
94 if (i
% 16) fprintf (outfile
, " ");
95 fprintf(outfile
, "%02X", c
);
97 fprintf (outfile
, "'\n}");
104 if (!isxdigit(c
)) return -1024;
105 if (isdigit(c
)) return c
- '0';
106 return toupper(c
) - 'A' + 10;
109 int extract_hexdump (FILE* outfile
, FILE* infile
)
113 while ( (c
= fgetc(infile
)) != EOF
&& c
!= '}')
115 if (isspace(c
) || c
== '\'') continue;
116 byte
= 16 * hex2bin(c
);
118 if (c
== EOF
) return 0;
120 if (byte
< 0) return 0;
121 fputc(byte
, outfile
);
126 const char* parse_marker(const char *line
, time_t* last_updated
)
128 static char res_file_name
[PATH_MAX
], *rpos
, *wpos
;
131 if (!(rpos
= strstr(line
, "BINRES"))) return 0;
132 for (rpos
+= 6; *rpos
&& isspace(*rpos
); rpos
++) /**/;
133 for (wpos
= res_file_name
; *rpos
&& !isspace(*rpos
); ) *wpos
++ = *rpos
++;
136 *last_updated
= (stat(res_file_name
, &st
) < 0) ? 0 : st
.st_mtime
;
138 return res_file_name
;
141 const char* parse_include(const char *line
)
143 static char include_filename
[PATH_MAX
], *rpos
, *wpos
;
145 if (!(rpos
= strstr(line
, "#"))) return 0;
146 for (rpos
+= 1; *rpos
&& isspace(*rpos
); rpos
++) /**/;
147 if (!(rpos
= strstr(line
, "include"))) return 0;
148 for (rpos
+= 7; *rpos
&& isspace(*rpos
); rpos
++) /**/;
149 for (; *rpos
&& (*rpos
== '"' || *rpos
== '<'); rpos
++) /**/;
150 for (wpos
= include_filename
; *rpos
&& !(*rpos
== '"' || *rpos
== '<'); ) *wpos
++ = *rpos
++;
151 if (!(rpos
= strstr(include_filename
, ".rc"))) return 0;
154 return include_filename
;
157 char* get_filename_with_full_path(char* output
, const char* filename
, const char* relative_path
)
159 if (relative_path
!= NULL
&& relative_path
[0] != 0)
161 strcpy(output
, relative_path
);
162 strcat(output
, DIR_SEPARATOR
);
163 strcat(output
, filename
);
166 strcpy(output
, filename
);
170 void process_includes(const char* input_file_name
, const char* specific_file_name
,
171 const char* relative_path
,
172 int inserting
, int force_processing
, int verbose
)
174 char filename
[PATH_MAX
];
176 const char *include_file_name
;
180 if (!(fin
= fopen(input_file_name
, "r"))) return;
181 for (c
= EOF
; fgets(buffer
, sizeof(buffer
), fin
); c
= EOF
)
183 if (!(include_file_name
= parse_include(buffer
))) continue;
184 if ( verbose
) printf ( "Processing included file %s\n", include_file_name
);
185 process_resources(get_filename_with_full_path(filename
, include_file_name
, relative_path
),
186 specific_file_name
, relative_path
,
187 inserting
, force_processing
, verbose
);
192 int process_resources(const char* input_file_name
, const char* specific_file_name
,
193 const char* relative_path
,
194 int inserting
, int force_processing
, int verbose
)
196 char filename
[PATH_MAX
];
197 char buffer
[2048], tmp_file_name
[PATH_MAX
];
198 const char *res_file_name
;
199 time_t rc_last_update
, res_last_update
;
200 FILE *fin
, *fres
, *ftmp
= 0;
204 if (!(fin
= fopen(input_file_name
, "r"))) return 0;
205 if (stat(input_file_name
, &st
) < 0) return 0;
206 rc_last_update
= st
.st_mtime
;
210 strcpy(tmp_file_name
, input_file_name
);
211 strcat(tmp_file_name
, "-XXXXXX.temp");
212 if ((fd
= mkstemps(tmp_file_name
, 5)) == -1)
214 strcpy(tmp_file_name
, "/tmp/bin2res-XXXXXX.temp");
215 if ((fd
= mkstemps(tmp_file_name
, 5)) == -1) return 0;
217 if (!(ftmp
= fdopen(fd
, "w"))) return 0;
220 for (c
= EOF
; fgets(buffer
, sizeof(buffer
), fin
); c
= EOF
)
222 if (inserting
) fprintf(ftmp
, "%s", buffer
);
223 if (!(res_file_name
= parse_marker(buffer
, &res_last_update
))) continue;
224 if ( (specific_file_name
&& strcmp(specific_file_name
, res_file_name
)) ||
225 (!force_processing
&& ((rc_last_update
< res_last_update
) == !inserting
)) )
227 if (verbose
) printf("skipping '%s'\n", res_file_name
);
231 if (verbose
) printf("processing '%s'\n", res_file_name
);
232 while ( (c
= fgetc(fin
)) != EOF
&& c
!= '{')
233 if (inserting
) fputc(c
, ftmp
);
236 if (!(fres
= fopen(get_filename_with_full_path(filename
, res_file_name
, relative_path
),
237 inserting
? "rb" : "wb"))) break;
240 if (!insert_hexdump(ftmp
, fres
)) break;
241 while ( (c
= fgetc(fin
)) != EOF
&& c
!= '}') /**/;
245 if (!extract_hexdump(fres
, fin
)) break;
257 if (rename(tmp_file_name
, input_file_name
) < 0)
259 /* try unlinking first, Windows rename is brain-damaged */
260 if (unlink(input_file_name
) < 0 || rename(tmp_file_name
, input_file_name
) < 0)
262 unlink(tmp_file_name
);
267 else unlink(tmp_file_name
);
271 process_includes(input_file_name
, specific_file_name
, relative_path
,
272 inserting
, force_processing
, verbose
);
278 int main(int argc
, char **argv
)
280 int convert_dir
= 0, optc
;
281 int force_overwrite
= 0, verbose
= 0;
282 const char* input_file_name
= 0;
283 const char* specific_file_name
= 0;
284 const char* relative_path
= 0;
286 while((optc
= getopt(argc
, argv
, "axi:o:b:fhv")) != EOF
)
292 if (convert_dir
) usage();
297 if (specific_file_name
) usage();
298 specific_file_name
= optarg
;
299 optc
= ((optc
== 'i') ? 'a' : 'x');
300 if (convert_dir
&& convert_dir
!= optc
) usage();
304 if (relative_path
) usage();
305 relative_path
= optarg
;
322 if (optind
+ 1 != argc
) usage();
323 input_file_name
= argv
[optind
];
325 if (!convert_dir
) usage();
327 if (!process_resources(input_file_name
, specific_file_name
, relative_path
,
328 convert_dir
== 'a', force_overwrite
, verbose
))
330 perror("Processing failed");