wineshelllink: Fall back to $HOME if $HOME/Desktop does not exist.
[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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21  */
22
23 #include "config.h"
24 #include "wine/port.h"
25
26 #include <signal.h>
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <ctype.h>
30 #include <string.h>
31 #include <sys/stat.h>
32 #include <limits.h>
33 #ifdef HAVE_SYS_PARAM_H
34 # include <sys/param.h>
35 #endif
36
37 static const char *clean_file;
38
39 static const char* help =
40         "Usage: bin2res [OPTIONS] <rsrc.rc>\n"
41         "  -a archive binaries into the <rsrc.rc> file\n"
42         "  -x extract binaries from the <rsrc.rc> file\n"
43         "  -i <filename> archive the named file into the <rsrc.rc> file\n"
44         "  -o <filename> extract the named file from the <rsrc.rc> file\n"
45         "  -f force processing of older resources\n"
46         "  -v causes the command to be verbous during processing\n" 
47         "  -h print this help screen and exit\n"
48         "\n"
49         "This tool allows the insertion/extractions of embedded binary\n"
50         "resources to/from .rc files, for storage within the cvs tree.\n"
51         "This is accomplished by placing a magic marker in a comment\n"
52         "just above the resource. The marker consists of the BINRES\n"
53         "string followed by the file name. For example, to insert a\n"
54         "brand new binary resource in a .rc file, place the marker\n"
55         "above empty brackets:\n"
56         "    /* BINRES idb_std_small.bmp */\n"
57         "    IDB_STD_SMALL BITMAP idb_std_small.bmp\n"
58         "    /* {\n"
59         "    } */\n"
60         "To merge the binary resources into the .rc file, run:\n"
61         "   bin2res -a myrsrc.rc\n"
62         "Only resources that are newer than the .rc are processed.\n"
63         "To extract the binary resources from the .rc file, run:\n"
64         "  bin2res -x myrsrc.rc\n"
65         "Binary files newer than the .rc file are not overwritten.\n"
66         "\n"
67         "To force processing of all resources, use the -f flag.\n"
68         "To process a particular file, use the -i/-o options.\n";
69
70 static void usage(void)
71 {
72     printf(help);
73     exit(1);
74 }
75
76 static void cleanup_files(void)
77 {
78     if (clean_file) unlink( clean_file );
79 }
80
81 static void exit_on_signal( int sig )
82 {
83     exit(1);  /* this will call the atexit functions */
84 }
85
86 static int insert_hexdump (FILE* outfile, FILE* infile)
87 {
88     int i, c;
89
90     fprintf (outfile, "{\n '");
91     for (i = 0; (c = fgetc(infile)) != EOF; i++)
92     {
93         if (i && (i % 16) == 0) fprintf (outfile, "'\n '");
94         if (i % 16)  fprintf (outfile, " ");
95         fprintf(outfile, "%02X", c);
96     }
97     fprintf (outfile, "'\n}");
98
99     return 1;
100 }
101
102 static int hex2bin(char c)
103 {
104     if (!isxdigit(c)) return -1024;
105     if (isdigit(c)) return c - '0';
106     return toupper(c) - 'A' + 10;
107 }
108
109 static int extract_hexdump (FILE* outfile, FILE* infile)
110 {
111     int byte, c;
112
113     while ( (c = fgetc(infile)) != EOF && c != '}')
114     {
115         if (isspace(c) || c == '\'') continue;
116         byte = 16 * hex2bin(c);
117         c = fgetc(infile);
118         if (c == EOF) return 0;
119         byte += hex2bin(c);
120         if (byte < 0) return 0;
121         fputc(byte, outfile);
122     }
123     return 1;
124 }
125
126 static const char* parse_marker(const char *line, time_t* last_updated)
127 {
128     static char res_file_name[PATH_MAX], *rpos, *wpos;
129     struct stat st;
130
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++;
134     *wpos = 0;
135
136     *last_updated = (stat(res_file_name, &st) < 0) ? 0 : st.st_mtime;
137
138     return res_file_name;
139 }
140
141 static int process_resources(const char* input_file_name, const char* specific_file_name, 
142                       int inserting, int force_processing, int verbose)
143 {
144     char buffer[2048], tmp_file_name[PATH_MAX];
145     const char *res_file_name;
146     time_t rc_last_update, res_last_update;
147     FILE *fin, *fres, *ftmp = 0;
148     struct stat st;
149     int fd, c;
150
151     if (!(fin = fopen(input_file_name, "r"))) return 0;
152     if (fstat(fileno(fin), &st) < 0) {
153         fclose (fin);
154         return 0;
155     }
156     rc_last_update = st.st_mtime;
157
158     if (inserting)
159     {
160         strcpy(tmp_file_name, input_file_name);
161         strcat(tmp_file_name, "-XXXXXX.temp");
162         if ((fd = mkstemps(tmp_file_name, 5)) == -1)
163         {
164             strcpy(tmp_file_name, "/tmp/bin2res-XXXXXX.temp");
165             if ((fd = mkstemps(tmp_file_name, 5)) == -1) return 0;
166         }
167         clean_file = tmp_file_name;
168         if (!(ftmp = fdopen(fd, "w"))) return 0;
169     }
170
171     for (c = EOF; fgets(buffer, sizeof(buffer), fin); c = EOF)
172     {
173         if (inserting) fprintf(ftmp, "%s", buffer);
174         if (!(res_file_name = parse_marker(buffer, &res_last_update))) continue;
175         if ( (specific_file_name && strcmp(specific_file_name, res_file_name)) ||
176              (!force_processing && ((rc_last_update < res_last_update) == !inserting)) )
177         {
178             if (verbose) printf("skipping '%s'\n", res_file_name);
179             continue;
180         }
181
182         if (verbose) printf("processing '%s'\n", res_file_name);
183         while ( (c = fgetc(fin)) != EOF && c != '{')
184             if (inserting) fputc(c, ftmp);
185         if (c == EOF) break;
186
187         if (inserting)
188         {
189             if (!(fres = fopen(res_file_name, "rb"))) break;
190             if (!insert_hexdump(ftmp, fres)) break;
191             while ( (c = fgetc(fin)) != EOF && c != '}') /**/;
192             fclose(fres);
193         }
194         else
195         {
196             clean_file = res_file_name;
197             if (!(fres = fopen(res_file_name, "wb"))) break;
198             if (!extract_hexdump(fres, fin)) break;
199             fclose(fres);
200             clean_file = NULL;
201         }
202     }
203
204     fclose(fin);
205
206     if (inserting)
207     {
208         fclose(ftmp);
209         if (c == EOF)
210         {
211             if (rename(tmp_file_name, input_file_name) < 0)
212             {
213                 /* try unlinking first, Windows rename is brain-damaged */
214                 if (unlink(input_file_name) < 0 || rename(tmp_file_name, input_file_name) < 0)
215                     return 0;
216             }
217             clean_file = NULL;
218         }
219     }
220
221     return c == EOF;
222 }
223
224 int main(int argc, char **argv)
225 {
226     int convert_dir = 0, optc;
227     int force_overwrite = 0, verbose = 0;
228     const char* input_file_name = 0;
229     const char* specific_file_name = 0;
230
231     atexit( cleanup_files );
232     signal( SIGTERM, exit_on_signal );
233     signal( SIGINT, exit_on_signal );
234 #ifdef SIGHUP
235     signal( SIGHUP, exit_on_signal );
236 #endif
237
238     while((optc = getopt(argc, argv, "axi:o:fhv")) != EOF)
239     {
240         switch(optc)
241         {
242         case 'a':
243         case 'x':
244             if (convert_dir) usage();
245             convert_dir = optc;
246         break;
247         case 'i':
248         case 'o':
249             if (specific_file_name) usage();
250             specific_file_name = optarg;
251             optc = ((optc == 'i') ? 'a' : 'x');
252             if (convert_dir && convert_dir != optc) usage();
253             convert_dir = optc;
254         break;
255         case 'f':
256             force_overwrite = 1;
257         break;
258         case 'v':
259             verbose = 1;
260         break;
261         case 'h':
262             printf(help);
263             exit(0);
264         break;
265         default:
266             usage();
267         }
268     }
269
270     if (optind + 1 != argc) usage();
271     input_file_name = argv[optind];
272
273     if (!convert_dir) usage();
274
275     if (!process_resources(input_file_name, specific_file_name, 
276                            convert_dir == 'a', force_overwrite, verbose))
277     {
278         perror("Processing failed");
279         exit(1);
280     }
281
282     return 0;
283 }