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