1 /**
2  * This file is part of the Detox package.
3  *
4  * Copyright (c) Doug Harple <detox.dharple@gmail.com>
5  *
6  * For the full copyright and license information, please view the LICENSE
7  * file that was distributed with this source code.
8  */
9 
10 #include "config.h"
11 
12 #include <sys/stat.h>
13 #include <stdio.h>
14 #include <stdlib.h>
15 #include <string.h>
16 #include <unistd.h>
17 #include <errno.h>
18 #include <libgen.h>
19 
20 #include "detox.h"
21 #include "config.h"
22 #include "parse_options.h"
23 
24 #ifdef HAVE_GETOPT_LONG
25 #include <getopt.h>
26 #endif
27 
28 enum {
29 	LONG_OPTION_DRY_RUN = 1,
30 	LONG_OPTION_INLINE,
31 	LONG_OPTION_REMOVE_TRAILING,
32 	LONG_OPTION_SPECIAL
33 };
34 
35 #define INLINE_DETOX_BIN "inline-detox"
36 
37 /* expect this to be overwritten! */
38 static int long_option = 0;
39 
40 #ifdef HAVE_GETOPT_LONG
41 static struct option longopts[] = {
42 
43 	/* long options with equivalents */
44 	{"help", no_argument, 0, 'h'},
45 	{"dry-run", no_argument, 0, 'n'},
46 
47 	/* long options without */
48 	{"inline", no_argument, &long_option, LONG_OPTION_INLINE},
49 	{"special", no_argument, &long_option, LONG_OPTION_SPECIAL},
50 
51 	/* deprecated long opts without */
52 	{"remove-trailing", no_argument, &long_option, LONG_OPTION_REMOVE_TRAILING},
53 
54 	/* done */
55 	{0, 0, 0, 0}
56 };
57 
58 #endif
59 
60 /* *INDENT-OFF* */
61 
62 char usage_message[] = {
63 	"usage: detox [-hLnrvV] [-f configfile] [-s sequence]"
64 #ifdef HAVE_GETOPT_LONG
65 	" [--dry-run] [--inline] [--special]"
66 	"\n\t "
67 #endif
68 	" file [file ...]\n"
69 };
70 
71 char help_message[] = {
72 	"	-f configfile	choose which config file to use\n"
73 #ifdef HAVE_GETOPT_LONG
74 	"	-h --help	this message\n"
75 #else
76 	"	-h 		this message\n"
77 #endif
78 #ifdef HAVE_GETOPT_LONG
79 	"	--inline	run inline mode\n"
80 #endif
81 	"	-L		list available sequences and exit\n"
82 	"			with -v ... dump sequence contents\n"
83 #ifdef HAVE_GETOPT_LONG
84 	"	-n --dry-run	do a dry run (don't actually do anything)\n"
85 #else
86 	"	-n 		do a dry run (don't actually do anything)\n"
87 #endif
88 	"	-r 		be recursive (descend into subdirectories)\n"
89 #ifdef HAVE_GETOPT_LONG
90 	"	--remove-trailing (deprecated)\n"
91 	"			remove trailing _ and - before a period\n"
92 #endif
93 	"	-s sequence	choose which sequence to detox with\n"
94 #ifdef HAVE_GETOPT_LONG
95 	"	--special	work on links and special files\n"
96 #endif
97 	"	-v 		be verbose\n"
98 	"	-V 		show the current version\n"
99 };
100 
101 char usage_message_inline[] = {
102 	"usage: inline-detox [-hLvV] [-f configfile] [-s sequence] [file]\n"
103 };
104 
105 char help_message_inline[] = {
106 	"	-f configfile	choose which config file to use\n"
107 #ifdef HAVE_GETOPT_LONG
108 	"	-h --help	this message\n"
109 #else
110 	"	-h 		this message\n"
111 #endif
112 	"	-L		list available sequences and exit\n"
113 	"			with -v ... dump sequence contents\n"
114 #ifdef HAVE_GETOPT_LONG
115 	"	--remove-trailing (deprecated)\n"
116 	"			remove trailing _ and - before a period\n"
117 #endif
118 	"	-s sequence	choose which sequence to detox with\n"
119 	"	-v 		be verbose\n"
120 	"	-V 		show the current version\n"
121 };
122 
123 /* *INDENT-ON* */
124 
initialize_main_options(void)125 struct detox_options *initialize_main_options(void)
126 {
127 	struct detox_options *main_options;
128 
129 	main_options = malloc(sizeof(struct detox_options));
130 	if (main_options == NULL) {
131 		fprintf(stderr, "out of memory: %s\n", strerror(errno));
132 		return NULL;
133 	}
134 
135 	memset(main_options, 0, sizeof(struct detox_options));
136 
137 	/*
138 	 * XXX - handle blank strings better
139 	 */
140 	main_options->sequence_name = getenv("DETOX_SEQUENCE");
141 
142 	return main_options;
143 }
144 
parse_options_getopt(int argc,char ** argv)145 struct detox_options *parse_options_getopt(int argc, char **argv)
146 {
147 	int optcode;
148 
149 	struct detox_options *main_options;
150 
151 	int i;
152 	int max = 10;
153 	char *binname;
154 
155 	main_options = initialize_main_options();
156 	if (main_options == NULL) {
157 		return NULL;
158 	}
159 
160 	binname = basename(argv[0]);
161 	main_options->is_inline_bin = main_options->is_inline_mode =
162 		(strcmp(binname, INLINE_DETOX_BIN) == 0);
163 
164 #ifdef HAVE_GETOPT_LONG
165 	while ((optcode = getopt_long(argc, argv, "hrvV?Ls:f:n", longopts, NULL)) != -1) {
166 #else
167 	while ((optcode = getopt(argc, argv, "hrvV?Ls:f:n")) != -1) {
168 #endif
169 		switch (optcode) {
170 			case 'h':
171 				printf("%s", !main_options->is_inline_bin ? usage_message :
172 					usage_message_inline);
173 				printf("\n");
174 				printf("%s", !main_options->is_inline_bin ? help_message :
175 					help_message_inline);
176 				exit(EXIT_SUCCESS);
177 
178 			case 'f':
179 				/*
180 				 * XXX - free multiple check_config_files
181 				 */
182 				main_options->check_config_file = strdup(optarg);
183 				break;
184 
185 			case 'L':
186 				main_options->list_sequences = 1;
187 				break;
188 
189 			case 'n':
190 				main_options->dry_run = 1;
191 				break;
192 
193 			case 'r':
194 				main_options->recurse = 1;
195 				break;
196 
197 			case 's':
198 				/*
199 				 * XXX - free multiple sequence name opts
200 				 */
201 				main_options->sequence_name = strdup(optarg);
202 				break;
203 
204 			case 'v':
205 				main_options->verbose++;
206 				break;
207 
208 			case 'V':
209 				printf("%s\n", PACKAGE_STRING);
210 				exit(EXIT_SUCCESS);
211 
212 			case '?':
213 				printf("%s", !main_options->is_inline_bin ? usage_message :
214 					usage_message_inline);
215 				exit(EXIT_SUCCESS);
216 
217 			case 0:
218 				switch (long_option) {
219 					case LONG_OPTION_INLINE:
220 						main_options->is_inline_mode = 1;
221 						break;
222 
223 					case LONG_OPTION_REMOVE_TRAILING:
224 						main_options->remove_trailing = 1;
225 						break;
226 
227 					case LONG_OPTION_SPECIAL:
228 						main_options->special = 1;
229 						break;
230 
231 					default:
232 						/*
233 						 * getopt_long shouldn't let us get here...
234 						 * verify?
235 						 */
236 						printf("unknown option: %s\n", optarg);
237 						break;
238 				}
239 				long_option = 0;	/* clean up! */
240 				break;
241 
242 			default:
243 				fprintf(stderr, "unknown option: %c\n", optcode);
244 				exit(EXIT_FAILURE);
245 		}
246 	}
247 
248 	if (main_options->list_sequences) {
249 		/*
250 		 * Early Retirement
251 		 */
252 		return main_options;
253 	}
254 
255 	main_options->files = malloc(sizeof(char *) * 10);
256 
257 	i = 0;
258 	max = 10;
259 
260 	if (optind < argc) {
261 		while (optind < argc) {
262                         /* not enough space for the next file and
263                            possible ending NULL -> realloc */
264 			if (i + 2 > max) {
265 				main_options->files = realloc(main_options->files, sizeof(char *) * (10 + max));
266 				max += 10;
267 			}
268 			main_options->files[i++] = strdup(argv[optind]);
269 
270 			optind++;
271 		}
272 
273 		main_options->files[i] = NULL;
274 	}
275 	else if (!main_options->is_inline_mode) {
276 		printf("%s", !main_options->is_inline_bin ? usage_message :
277 			usage_message_inline);
278 		exit(EXIT_FAILURE);
279 	}
280 
281 	return main_options;
282 }
283