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