1 /* Copyright (C) 2010-2011 G.P. Halkes
2 This program is free software: you can redistribute it and/or modify
3 it under the terms of the GNU General Public License version 3, as
4 published by the Free Software Foundation.
5
6 This program is distributed in the hope that it will be useful,
7 but WITHOUT ANY WARRANTY; without even the implied warranty of
8 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
9 GNU General Public License for more details.
10
11 You should have received a copy of the GNU General Public License
12 along with this program. If not, see <http://www.gnu.org/licenses/>.
13 */
14
15 #include <stdio.h>
16 #include <string.h>
17 #include <sys/types.h>
18 #include <unistd.h>
19 #include <sys/stat.h>
20 #include <sys/wait.h>
21 #include <errno.h>
22
23 #include "definitions.h"
24 #include "util.h"
25 #include "optionMacros.h"
26 #define DWFILTER_COMPILE
27 #include "optionDescriptions.h"
28
29 #define TEMPLATE "/tmp/dwdiffXXXXXXXX"
30 #define TEMPLATE_LENGTH sizeof(TEMPLATE)
31
32 #ifndef DWDIFF
33 # define DWDIFF "dwdiff"
34 #endif
35
36
37 struct {
38 int oldFile, newFile;
39 bool reverse;
40 int postProcessorStart;
41 } option;
42
43 static PARSE_FUNCTION(parseCmdLine)
44 int noOptionCount = 0;
45 bool discard;
46
47 /* Initialise options to correct values */
48 memset(&option, 0, sizeof(option));
49
50 OPTIONS
51 OPTION('d', "delimiters", REQUIRED_ARG)
52 END_OPTION
53 OPTION('P', "punctuation", NO_ARG)
54 END_OPTION
55 OPTION('W', "white-space", REQUIRED_ARG)
56 END_OPTION
57 OPTION('h', "help", NO_ARG)
58 int i = 0;
59 printf(_("Usage: dwfilter [OPTIONS] <OLD FILE> <NEW FILE> <POST PROCESSOR> [POST PROCESSOR OPTIONS]\n"));
60 while (descriptions[i] != NULL)
61 fputs(_(descriptions[i++]), stdout);
62 exit(EXIT_SUCCESS);
63 END_OPTION
64 OPTION('v', "version", NO_ARG)
65 fputs("dwfilter " VERSION_STRING "\n", stdout);
66 fputs(
67 /* TRANSLATORS:
68 - If the (C) symbol (that is the c in a circle) is not available,
69 leave as it as is. (Unicode code point 0x00A9)
70 - G.P. Halkes is name and should be left as is. */
71 _("Copyright (C) 2006-2011 G.P. Halkes\nLicensed under the GNU General Public License version 3\n"), stdout);
72 exit(EXIT_SUCCESS);
73 END_OPTION
74 OPTION('i', "ignore-case", NO_ARG)
75 END_OPTION
76 OPTION('I', "ignore-formatting", NO_ARG)
77 END_OPTION
78 SINGLE_DASH
79 switch (noOptionCount++) {
80 case 0:
81 option.oldFile = optargind;
82 break;
83 case 1:
84 option.newFile = optargind;
85 break;
86 default:
87 option.postProcessorStart = optargind;
88 STOP_OPTION_PROCESSING;
89 break;
90 }
91 END_OPTION
92 DOUBLE_DASH
93 NO_MORE_OPTIONS;
94 END_OPTION
95 OPTION('C', "context", REQUIRED_ARG)
96 END_OPTION
97 OPTION('m', "match-context", REQUIRED_ARG)
98 END_OPTION
99 BOOLEAN_LONG_OPTION("aggregate-changes", discard)
100 OPTION('A', "algorithm", REQUIRED_ARG)
101 END_OPTION
102 BOOLEAN_LONG_OPTION("wdiff-output", discard)
103 /* FIXME: make this work again, after fixing dwdiff */
104 /* OPTION('S', "paragraph-separator", OPTIONAL_ARG)
105 END_OPTION */
106
107 BOOLEAN_OPTION('r', "reverse", option.reverse)
108 LONG_OPTION("profile", REQUIRED_ARG)
109 END_OPTION
110 LONG_OPTION("no-profile", NO_ARG)
111 END_OPTION
112
113 fatal(_("Option %.*s does not exist\n"), OPTPRARG);
114 NO_OPTION
115 switch (noOptionCount++) {
116 case 0:
117 option.oldFile = optargind;
118 break;
119 case 1:
120 option.newFile = optargind;
121 break;
122 default:
123 option.postProcessorStart = optargind;
124 STOP_OPTION_PROCESSING;
125 break;
126 }
127 END_OPTIONS
128 /* Check that we have something to work with. */
129 if (noOptionCount < 2)
130 fatal(_("Need two files to compare\n"));
131 if (noOptionCount < 3)
132 fatal(_("No post processor specified\n"));
133
134 END_FUNCTION
135
136 static char tempfile[TEMPLATE_LENGTH];
137
cleanup(void)138 static void cleanup(void) {
139 unlink(tempfile);
140 }
141
createTempfile(void)142 static void createTempfile(void) {
143 int fd;
144 /* Create temporary file. */
145 /* Make sure the umask is set so that we don't introduce a security risk. */
146 umask(~S_IRWXU);
147 /* Make sure we will remove temporary files on exit. */
148 atexit(cleanup);
149
150 strcpy(tempfile, TEMPLATE);
151 if ((fd = mkstemp(tempfile)) < 0)
152 fatal(_("Could not create temporary file: %s\n"), strerror(errno));
153 close(fd);
154 }
155
execute(char * const args[])156 static int execute(char * const args[]) {
157 pid_t pid;
158 int status;
159
160 if ((pid = fork()) == 0) {
161 execvp(args[0], args);
162 exit(127);
163 } else if (pid < 0) {
164 fatal(_("Could not execute %s: %s\n"), args[0], strerror(errno));
165 }
166 pid = wait(&status);
167 if (pid < 0)
168 fatal(_("Error waiting for child process to terminate: %s\n"), strerror(errno));
169 return WEXITSTATUS(status);
170 }
171
172 #define OUTPUT_OPTION "--dwfilter="
173
main(int argc,char * argv[])174 int main(int argc, char *argv[]) {
175 char **cmdArgs;
176 char *outputArg;
177 int i, j;
178
179 #if defined(USE_GETTEXT) || defined(USE_UNICODE)
180 setlocale(LC_ALL, "");
181 #endif
182
183 #ifdef USE_GETTEXT
184 bindtextdomain("dwdiff", LOCALEDIR);
185 textdomain("dwdiff");
186 #endif
187
188 parseCmdLine(argc, argv);
189 createTempfile();
190 cmdArgs = safe_malloc(sizeof(char *) * (option.postProcessorStart + 3));
191 outputArg = safe_malloc(strlen(tempfile) + sizeof(OUTPUT_OPTION) + 1);
192
193 cmdArgs[0] = safe_strdup(DWDIFF);
194 /* argument has been allocated to correct size above, so we can safely use sprintf */
195 sprintf(outputArg, OUTPUT_OPTION "%s", tempfile);
196 cmdArgs[1] = outputArg;
197
198 for (i = 1, j = 2; i < option.postProcessorStart; i++)
199 if (i != option.oldFile && i != option.newFile)
200 cmdArgs[j++] = argv[i];
201
202 cmdArgs[j++] = argv[option.reverse ? option.newFile : option.oldFile];
203 cmdArgs[j++] = argv[option.reverse ? option.oldFile : option.newFile];
204 cmdArgs[j++] = NULL;
205
206 if (execute(cmdArgs) > 2)
207 fatal(_("dwdiff returned an error\n"));
208
209 free(outputArg);
210 free(cmdArgs);
211
212 cmdArgs = safe_malloc(sizeof(char *) * (argc - option.postProcessorStart + 3));
213
214 for (i = option.postProcessorStart, j = 0; i < argc; i++, j++)
215 cmdArgs[j] = argv[i];
216
217 cmdArgs[j++] = option.reverse ? argv[option.oldFile] : tempfile;
218 cmdArgs[j++] = option.reverse ? tempfile : argv[option.newFile];
219 cmdArgs[j++] = NULL;
220
221 return execute(cmdArgs);
222 }
223
224