1 /*
2  * dvdbackup - tool to rip DVDs from the command line
3  *
4  * Copyright (C) 2002  Olaf Beck <olaf_sc@yahoo.com>
5  * Copyright (C) 2008-2012  Benjamin Drung <benjamin.drung@gmail.com>
6  *
7  * This program is free software: you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation, either version 3 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
19  */
20 
21 #include <config.h>
22 #include "dvdbackup.h"
23 
24 /* internationalisation */
25 #include "gettext.h"
26 #define _(String) gettext(String)
27 
28 /* C standard libraries */
29 #include <limits.h>
30 #include <locale.h>
31 #include <stdbool.h>
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <string.h>
35 
36 /* C POSIX libraries */
37 #include <sys/stat.h>
38 #include <unistd.h>
39 
40 /* libdvdread */
41 #include <dvdread/dvd_reader.h>
42 
43 /* other libraries */
44 #include <getopt.h>
45 
46 
47 /* String containing name the program is called with. */
48 const char* program_name;
49 
50 
print_version()51 static void print_version() {
52 	printf("%s\n", PACKAGE_STRING);
53 
54 	/* It is important to separate the year from the rest of the message,
55 	 * as done here, to avoid having to retranslate the message when a new
56 	 * year comes around.
57 	 */
58 	printf(_("Copyright (C) 2002 Olaf Beck <olaf_sc@yahoo.com>\n\
59 Copyright (C) %s Benjamin Drung <benjamin.drung@gmail.com>\n\n\
60 License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>\n\
61 This is free software: you are free to change and redistribute it.\n\
62 There is NO WARRANTY, to the extent permitted by law.\n\
63 Homepage: %s\n"), "2008-2012", "http://dvdbackup.sourceforge.net/");
64 }
65 
66 
print_help()67 static void print_help() {
68 	/* TRANSLATORS: --help output 1 (synopsis) */
69 	printf(_("Usage: %s [OPTION]...\n"), program_name);
70 
71 	/* TRANSLATORS: --help output 2 (brief description)
72 	   no-wrap */
73 /*	fputs (_("\
74 Print a friendly, customizable greeting.\n"), stdout); */
75 	printf("\n");
76 
77 	/* TRANSLATORS: --help output 3: options 1/2
78 	   no-wrap */
79 	printf(_("\
80   -h, --help         display this help and exit\n\
81   -V, --version      display version information and exit\n\n"));
82 
83 	/* TRANSLATORS: --help output 4: options 2/2
84 	   no-wrap */
85 	printf(_("\
86   -I, --info         prints information about the DVD\n\
87   -M, --mirror       backup the whole DVD\n\
88   -F, --feature      backup the main feature of the DVD\n\
89   -T, --titleset=X   backup title set X\n\
90   -t, --title=X      backup title X\n\
91   -s, --start=X      backup from chapter X\n\
92   -e, --end=X        backup to chapter X\n\n"));
93 
94 	printf(_("\
95   -i, --input=DEVICE       where DEVICE is your DVD device\n\
96                            if not given /dev/cd0 is used\n\
97   -o, --output=DIRECTORY   where directory is your backup target\n\
98                            if not given the current directory is used\n"));
99 	printf(_("\
100   -v, --verbose            print more information about progress\n\
101   -n, --name=NAME          set the title (useful if autodetection fails)\n\
102   -a, --aspect=0           to get aspect ratio 4:3 instead of 16:9 if both are\n\
103                            present\n\
104   -r, --error={a,b,m}      select read error handling: a=abort, b=skip block,\n\
105                            m=skip multiple blocks (default)\n\
106   -p, --progress           print progress information while copying VOBs\n\n"));
107 
108 	printf(_("\
109   -a is option to the -F switch and has no effect on other options\n\
110   -s and -e should preferably be used together with -t\n"));
111 
112 	printf("\n");
113 	/* TRANSLATORS: --help output 5 (end)
114 	   TRANSLATORS: the placeholder indicates the bug-reporting address
115 	   for this application.  Please add _another line_ with the
116 	   address for translation bugs. */
117 	printf (_("Report bugs on Launchpad: %s\n"),
118 		"https://bugs.launchpad.net/dvdbackup");
119 }
120 
121 
init_i18n()122 void init_i18n() {
123 	setlocale(LC_ALL, "");
124 	bindtextdomain(PACKAGE, LOCALEDIR);
125 	textdomain(PACKAGE);
126 }
127 
128 
main(int argc,char * argv[])129 int main(int argc, char* argv[]) {
130 
131 	/* Args */
132 	int flags;
133 	bool lose = false;
134 
135 	/* Switches */
136 	int title_set = 0;
137 	int titles = 0;
138 	int start_chapter = 0;
139 	int end_chapter = 0;
140 
141 	int do_mirror = 0;
142 	int do_title_set = 0;
143 	int do_chapter = 0;
144 	int do_titles = 0;
145 	int do_feature = 0;
146 	int do_info = 0;
147 
148 	/* Because of copy protection you normally want to skip
149 	 * the defect sectors. To speed things up we skip multiblocks.
150 	 */
151 	read_error_strategy_t errorstrat = STRATEGY_SKIP_MULTIBLOCK;
152 
153 	int return_code = 0;
154 
155 	/* DVD Video device */
156 	char* dvd = "/dev/cd0";
157 
158 	/* Temp switch helpers */
159 	char* aspect_temp = NULL;
160 	char* start_chapter_temp = NULL;
161 	char* end_chapter_temp = NULL;
162 	char* titles_temp = NULL;
163 	char* title_set_temp = NULL;
164 	char* errorstrat_temp = NULL;
165 
166 
167 	/* Title of the DVD */
168 	char title_name[33] = "";
169 	char* provided_title_name = NULL;
170 
171 	/* Targer dir */
172 	char* targetdir = ".";
173 
174 	/* Temp filename,dirname */
175 	char targetname[PATH_MAX];
176 	struct stat fileinfo;
177 
178 	/* The DVD main structure */
179 	dvd_reader_t* _dvd = NULL;
180 
181 	/* the long and the short options */
182 	static const struct option longopts[] = {
183 		{"help", no_argument, NULL, 'h'},
184 		{"version", no_argument, NULL, 'V'},
185 
186 		{"info", no_argument, NULL, 'I'},
187 		{"mirror", no_argument, NULL, 'M'},
188 		{"feature", no_argument, NULL, 'F'},
189 		{"titleset", required_argument, NULL, 'T'},
190 		{"title", required_argument, NULL, 't'},
191 		{"start", required_argument, NULL, 's'},
192 		{"end", required_argument, NULL, 'e'},
193 
194 		{"input", required_argument, NULL, 'i'},
195 		{"output", required_argument, NULL, 'o'},
196 		{"verbose", no_argument, NULL, 'v'},
197 		{"name", required_argument, NULL, 'n'},
198 		{"aspect", required_argument, NULL, 'a'},
199 		{"error", required_argument, NULL, 'r'},
200 		{"progress", no_argument, NULL, 'p'},
201 		{NULL, 0, NULL, 0}
202 	};
203 	const char* shortopts = "hVIMFT:t:s:e:i:o:vn:a:r:p";
204 
205 	init_i18n();
206 	program_name = argv[0];
207 
208 	/* TODO: do isdigit check */
209 
210 	while((flags = getopt_long(argc, argv, shortopts, longopts, NULL)) != -1) {
211 		switch(flags) {
212 		case 'h':
213 			print_help();
214 			exit(EXIT_SUCCESS);
215 			break;
216 		case 'V':
217 			print_version();
218 			exit(EXIT_SUCCESS);
219 			break;
220 
221 		case 'I':
222 			do_info = 1;
223 			break;
224 		case 'M':
225 			do_mirror = 1;
226 			break;
227 		case 'F':
228 			do_feature = 1;
229 			break;
230 		case 'T':
231 			title_set_temp = optarg;
232 			break;
233 		case 't':
234 			titles_temp = optarg;
235 			break;
236 		case 's':
237 			start_chapter_temp = optarg;
238 			break;
239 		case 'e':
240 			end_chapter_temp = optarg;
241 			break;
242 
243 		case 'i':
244 			dvd = optarg;
245 			break;
246 		case 'o':
247 			targetdir = optarg;
248 			break;
249 		case 'v':
250 			verbose = 10;
251 			break;
252 		case 'n':
253 			provided_title_name = optarg;
254 			break;
255 		case 'a':
256 			aspect_temp = optarg;
257 			break;
258 		case 'r':
259 			errorstrat_temp=optarg;
260 			break;
261 		case 'p':
262 			progress = 1;
263 			break;
264 
265 		default:
266 			lose = true;
267 			break;
268 		}
269 	}
270 
271 	if(lose || optind < argc) {
272 		/* Print error message and exit. */
273 		if (optind < argc) {
274 			fprintf(stderr, _("%s: extra operand: %s\n"), program_name, argv[optind]);
275 		}
276 		fprintf(stderr, _("Try `%s --help' for more information.\n"), program_name);
277 		exit (EXIT_FAILURE);
278 	}
279 
280 	if(errorstrat_temp != NULL) {
281 		if(errorstrat_temp[0]=='a') {
282 			errorstrat=STRATEGY_ABORT;
283 		} else if(errorstrat_temp[0]=='b') {
284 			errorstrat=STRATEGY_SKIP_BLOCK;
285 		} else if(errorstrat_temp[0]=='m') {
286 			errorstrat=STRATEGY_SKIP_MULTIBLOCK;
287 		} else {
288 			print_help();
289 			exit(1);
290 		}
291 	}
292 
293 	if (aspect_temp == NULL) {
294 		/* Default to 16:9 aspect ratio */
295 		aspect = 3;
296 	} else {
297 		aspect = atoi(aspect_temp);
298 	}
299 
300 	if((aspect != 0) && (aspect != 3) && (do_info == 0)){
301 		print_help();
302 		exit(1);
303 	}
304 
305 	if ( titles_temp != NULL) {
306 		titles = atoi(titles_temp);
307 		if ( titles < 1 ) {
308 			print_help();
309 			exit(1);
310 		}
311 	}
312 
313 	if ( start_chapter_temp !=NULL) {
314 		start_chapter = atoi(start_chapter_temp);
315 		if ( start_chapter < 1 || start_chapter > 99 ) {
316 			print_help();
317 			exit(1);
318 		}
319 	}
320 
321 	if (end_chapter_temp != NULL) {
322 		end_chapter = atoi(end_chapter_temp);
323 		if ( end_chapter < 1 || end_chapter > 99 ) {
324 			print_help();
325 			exit(1);
326 		}
327 	}
328 
329 	if ( end_chapter_temp != NULL || start_chapter_temp != NULL) {
330 		if( end_chapter_temp == NULL) {
331 			end_chapter = 99;
332 		} else if ( start_chapter_temp == NULL) {
333 			start_chapter = 1;
334 		}
335 		if ( end_chapter < start_chapter ) {
336 			print_help();
337 			exit(1);
338 		}
339 	}
340 
341 	if ( titles_temp != NULL && ((end_chapter_temp != NULL) || (start_chapter_temp != NULL))) {
342 		do_chapter = 1;
343 	} else if ((titles_temp != NULL) && ((end_chapter_temp == NULL) && (start_chapter_temp == NULL))) {
344 		do_titles=1;
345 	}
346 	if (do_chapter && (titles_temp == NULL)) {
347 		titles = 0;
348 	}
349 
350 	if ( title_set_temp != NULL ) {
351 		title_set = atoi(title_set_temp);
352 		if ( title_set > 99 || title_set < 0 ) {
353 			print_help();
354 			exit(1);
355 		}
356 		do_title_set = 1;
357 	}
358 
359 	if (do_info + do_titles + do_chapter + do_feature + do_title_set + do_mirror > 1 ) {
360 		print_help();
361 		exit(1);
362 	} else if ( do_info + do_titles + do_chapter + do_feature + do_title_set + do_mirror == 0) {
363 		print_help();
364 		exit(1);
365 	}
366 #ifdef DEBUG
367 	fprintf(stderr,"After args\n");
368 #endif
369 
370 
371 	_dvd = DVDOpen(dvd);
372 	if (!_dvd) {
373 		fprintf(stderr,_("Cannot open specified device %s - check your DVD device\n"), dvd);
374 		exit(-1);
375 	}
376 
377 	if (do_info) {
378 		DVDDisplayInfo(_dvd, dvd);
379 		DVDClose(_dvd);
380 		exit(0);
381 	}
382 
383 
384 	if(provided_title_name == NULL) {
385 		if (DVDGetTitleName(dvd,title_name) != 0) {
386 			fprintf(stderr,_("You must provide a title name when you read your DVD-Video structure direct from the HD\n"));
387 			DVDClose(_dvd);
388 			exit(1);
389 		}
390 		if (strstr(title_name, "DVD_VIDEO") != NULL) {
391 			fprintf(stderr,_("The DVD-Video title on the disk is DVD_VIDEO, which is too generic; please provide a title with the -n switch\n"));
392 			DVDClose(_dvd);
393 			exit(2);
394 		}
395 
396 	} else {
397 		if (strlen(provided_title_name) > 32) {
398 			fprintf(stderr,_("The title name specified is longer than 32 characters; truncating the title name\n"));
399 			strncpy(title_name,provided_title_name, 32);
400 			title_name[32]='\0';
401 		} else {
402 			strcpy(title_name,provided_title_name);
403 		}
404 	}
405 
406 
407 
408 	sprintf(targetname,"%s",targetdir);
409 
410 	if (stat(targetname, &fileinfo) == 0) {
411 		if (! S_ISDIR(fileinfo.st_mode)) {
412 			fprintf(stderr,_("The target directory is not valid; it may be an ordinary file.\n"));
413 		}
414 	} else {
415 		if (mkdir(targetname, 0777) != 0) {
416 			fprintf(stderr,_("Failed creating target directory %s\n"), targetname);
417 			perror("");
418 			DVDClose(_dvd);
419 			exit(-1);
420 		}
421 	}
422 
423 
424 	sprintf(targetname,"%s/%s",targetdir, title_name);
425 
426 	if (stat(targetname, &fileinfo) == 0) {
427 		if (! S_ISDIR(fileinfo.st_mode)) {
428 			fprintf(stderr,_("The title directory is not valid; it may be an ordinary file.\n"));
429 		}
430 	} else {
431 		if (mkdir(targetname, 0777) != 0) {
432 			fprintf(stderr,_("Failed creating title directory\n"));
433 			perror("");
434 			DVDClose(_dvd);
435 			exit(-1);
436 		}
437 	}
438 
439 	sprintf(targetname,"%s/%s/VIDEO_TS",targetdir, title_name);
440 
441 	if (stat(targetname, &fileinfo) == 0) {
442 		if (! S_ISDIR(fileinfo.st_mode)) {
443 			fprintf(stderr,_("The VIDEO_TS directory is not valid; it may be an ordinary file.\n"));
444 		}
445 	} else {
446 		if (mkdir(targetname, 0777) != 0) {
447 			fprintf(stderr,_("Failed creating VIDEO_TS directory\n"));
448 			perror("");
449 			DVDClose(_dvd);
450 			exit(-1);
451 		}
452 	}
453 
454 
455 #ifdef DEBUG
456 	fprintf(stderr,"After dirs\n");
457 #endif
458 
459 
460 	if(do_mirror) {
461 		if ( DVDMirror(_dvd, targetdir, title_name, errorstrat) != 0 ) {
462 			fprintf(stderr, _("Mirror of DVD failed\n"));
463 			return_code = -1;
464 		} else {
465 			return_code = 0;
466 		}
467 	}
468 #ifdef DEBUG
469 	fprintf(stderr,"After Mirror\n");
470 #endif
471 
472 
473 	if (do_title_set) {
474 		if (DVDMirrorTitleSet(_dvd, targetdir, title_name, title_set, errorstrat) != 0) {
475 			fprintf(stderr, _("Mirror of title set %d failed\n"), title_set);
476 			return_code = -1;
477 		} else {
478 			return_code = 0;
479 		}
480 
481 	}
482 #ifdef DEBUG
483 	fprintf(stderr,"After Title Set\n");
484 #endif
485 
486 
487 
488 	if(do_feature) {
489 		if ( DVDMirrorMainFeature(_dvd, targetdir, title_name, errorstrat) != 0 ) {
490 			fprintf(stderr, _("Mirror of main feature film of DVD failed\n"));
491 			return_code = -1;
492 		} else {
493 			return_code = 0;
494 		}
495 	}
496 
497 	if(do_titles) {
498 		if (DVDMirrorTitles(_dvd, targetdir, title_name, titles) != 0) {
499 			fprintf(stderr, _("Mirror of title %d failed\n"), titles);
500 			return_code = -1;
501 		} else {
502 			return_code = 0;
503 		}
504 	}
505 
506 
507 	if(do_chapter) {
508 		if (DVDMirrorChapters(_dvd, targetdir, title_name, start_chapter, end_chapter, titles) != 0) {
509 			fprintf(stderr, _("Mirror of chapters %d to %d in title %d failed\n"), start_chapter, end_chapter, titles);
510 			return_code = -1;
511 		} else {
512 			return_code = 0;
513 		}
514 	}
515 
516 
517 	DVDClose(_dvd);
518 	exit(return_code);
519 }
520