You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
6051 lines
184 KiB
6051 lines
184 KiB
|
|
/* |
|
cdrskin.c , Copyright 2006-2007 Thomas Schmitt <scdbackup@gmx.net> |
|
Provided under GPL. See future commitment below. |
|
|
|
A cdrecord compatible command line interface for libburn. |
|
|
|
This project is neither directed against original cdrecord nor does it exploit |
|
any source code of said program. It rather tries to be an alternative method |
|
to burn CD or DVD, which is not based on the same code as cdrecord. |
|
See also : http://scdbackup.sourceforge.net/cdrskin_eng.html |
|
|
|
Interested users of cdrecord are encouraged to contribute further option |
|
implementations as they need them. Contributions will get published under GPL |
|
but it is essential that the authors allow a future release under LGPL and/or |
|
BSD license. |
|
|
|
There is a script test/cdrecord_spy.sh which may be installed between |
|
the cdrecord command and real cdrecord in order to learn about the options |
|
used by your favorite cdrecord frontend. Edit said script and install it |
|
according to the instructions given inside. |
|
|
|
The implementation of an option would probably consist of |
|
- necessary structure members for structs CdrpreskiN and/or CdrskiN |
|
- code in Cdrpreskin_setup() and Cdrskin_setup() which converts |
|
argv[i] into CdrpreskiN/CdrskiN members (or into direct actions) |
|
- removal of option from ignore list "ignored_partial_options" resp. |
|
"ignored_full_options" in Cdrskin_setup() |
|
- functions which implement the option's run time functionality |
|
- eventually calls of those functions in Cdrskin_run() |
|
- changes to be made within Cdrskin_burn() or Cdrskin_blank() or other |
|
existing methods |
|
See option blank= for an example. |
|
|
|
------------------------------------------------------------------------------ |
|
|
|
For a more comprehensive example of the advised way to write an application |
|
of libburn see test/libburner.c . |
|
|
|
------------------------------------------------------------------------------ |
|
This program is currently copyright Thomas Schmitt only. |
|
The copyrights of several components of libburnia.pykix.org are willfully |
|
tangled at toplevel to form an irrevocable commitment to true open source |
|
spirit. |
|
We have chosen the GPL for legal compatibility and clearly express that it |
|
shall not hamper the use of our software by non-GPL applications which show |
|
otherwise the due respect to the open source community. |
|
See toplevel README and cdrskin/README for that commitment. |
|
|
|
For a short time, this place showed a promise to release a BSD license on |
|
mere request. I have to retract that promise now, and replace it by the |
|
promise to make above commitment reality in a way that any BSD conformant |
|
usage in due open source spirit will be made possible somehow and in the |
|
particular special case. I will not raise public protest if you spawn yourself |
|
a BSD license from an (outdated) cdrskin.c which still bears that old promise. |
|
Note that this extended commitment is valid only for cdrskin.[ch], |
|
cdrfifo.[ch] and cleanup.[ch], but not for libburnia.pykix.org as a whole. |
|
|
|
cdrskin is originally inspired by libburn-0.2/test/burniso.c : |
|
(c) Derek Foreman <derek@signalmarketing.com> and Ben Jansens <xor@orodu.net> |
|
|
|
------------------------------------------------------------------------------ |
|
|
|
Compilation within cdrskin-* : |
|
|
|
cd cdrskin |
|
cc -g -I.. -DCdrskin_build_timestamP='...' \ |
|
-o cdrskin cdrskin.c cdrfifo.c cleanup.c \ |
|
-L../libburn/.libs -lburn -lpthread |
|
|
|
or |
|
|
|
cd .. |
|
cc -g -I. -DCdrskin_build_timestamP='...' \ |
|
-o cdrskin/cdrskin cdrskin/cdrskin.c cdrskin/cdrfifo.c cdrskin/cleanup.c \ |
|
libburn/async.o libburn/crc.o libburn/debug.o libburn/drive.o \ |
|
libburn/file.o libburn/init.o libburn/lec.o \ |
|
libburn/mmc.o libburn/options.o libburn/sbc.o libburn/sector.o \ |
|
libburn/sg.o libburn/spc.o libburn/source.o libburn/structure.o \ |
|
libburn/toc.o libburn/util.o libburn/write.o \ |
|
libburn/libdax_audioxtr.o libburn/libdax_msgs.o \ |
|
-lpthread |
|
|
|
*/ |
|
|
|
|
|
/** The official program version */ |
|
#ifndef Cdrskin_prog_versioN |
|
#define Cdrskin_prog_versioN "0.3.1" |
|
#endif |
|
|
|
/** The source code release timestamp */ |
|
#include "cdrskin_timestamp.h" |
|
#ifndef Cdrskin_timestamP |
|
#define Cdrskin_timestamP "-none-given-" |
|
#endif |
|
|
|
/** The binary build timestamp is to be set externally by the compiler */ |
|
#ifndef Cdrskin_build_timestamP |
|
#define Cdrskin_build_timestamP "-none-given-" |
|
#endif |
|
|
|
|
|
#ifdef Cdrskin_libburn_versioN |
|
#undef Cdrskin_libburn_versioN |
|
#endif |
|
|
|
/** use this to accomodate to the CVS version as of Feb 20, 2006 |
|
#define Cdrskin_libburn_cvs_A60220_tS 1 |
|
*/ |
|
#ifdef Cdrskin_libburn_cvs_A60220_tS |
|
|
|
#define Cdrskin_libburn_versioN "0.2.tsA60220" |
|
#define Cdrskin_libburn_no_burn_preset_device_opeN 1 |
|
#ifndef Cdrskin_oldfashioned_api_usE |
|
#define Cdrskin_oldfashioned_api_usE 1 |
|
#endif |
|
|
|
#endif /* Cdrskin_libburn_cvs_A60220_tS */ |
|
|
|
|
|
#ifdef Cdrskin_libburn_0_3_0 |
|
#define Cdrskin_libburn_versioN "0.3.0" |
|
#define Cdrskin_libburn_from_pykix_svN 1 |
|
#endif /* Cdrskin_libburn_0_3_0 */ |
|
|
|
#ifdef Cdrskin_libburn_0_3_1 |
|
#define Cdrskin_libburn_versioN "0.3.1" |
|
#define Cdrskin_libburn_from_pykix_svN 1 |
|
|
|
#define Cdrskin_libburn_has_get_msc1 1 |
|
#define Cdrskin_libburn_has_toc_entry_extensionS 1 |
|
#define Cdrskin_libburn_has_get_multi_capS 1 |
|
|
|
#endif /* Cdrskin_libburn_0_3_1 */ |
|
|
|
#ifndef Cdrskin_libburn_versioN |
|
#define Cdrskin_libburn_versioN "0.3.0" |
|
#define Cdrskin_libburn_from_pykix_svN 1 |
|
#endif |
|
|
|
#ifdef Cdrskin_libburn_from_pykix_svN |
|
#ifndef Cdrskin_oldfashioned_api_usE |
|
|
|
#define Cdrskin_libburn_does_ejecT 1 |
|
#define Cdrskin_libburn_has_drive_get_adR 1 |
|
#define Cdrskin_progress_track_does_worK 1 |
|
#define Cdrskin_is_erasable_on_load_does_worK 1 |
|
#define Cdrskin_grab_abort_does_worK 1 |
|
#define Cdrskin_allow_libburn_taO 1 |
|
#define Cdrskin_libburn_has_is_enumerablE 1 |
|
#define Cdrskin_libburn_has_convert_fs_adR 1 |
|
#define Cdrskin_libburn_has_convert_scsi_adR 1 |
|
#define Cdrskin_libburn_has_burn_msgS 1 |
|
#define Cdrskin_libburn_has_burn_aborT 1 |
|
#define Cdrskin_libburn_has_cleanup_handleR 1 |
|
#define Cdrskin_libburn_has_audioxtR 1 |
|
#define Cdrskin_libburn_has_get_start_end_lbA 1 |
|
#define Cdrskin_libburn_has_burn_disc_unsuitablE 1 |
|
#define Cdrskin_libburn_has_read_atiP 1 |
|
#define Cdrskin_libburn_has_buffer_progresS 1 |
|
#define Cdrskin_libburn_has_pretend_fulL 1 |
|
#define Cdrskin_libburn_has_multI 1 |
|
#define Cdrskin_libburn_has_buffer_min_filL 1 |
|
#define Cdrskin_atip_speed_is_oK 1 |
|
#define Cdrskin_libburn_has_get_profilE 1 |
|
#define Cdrskin_libburn_has_set_start_bytE 1 |
|
#define Cdrskin_libburn_has_wrote_welL 1 |
|
#define Cdrskin_libburn_has_bd_formattinG 1 |
|
#define Cdrskin_libburn_has_burn_disc_formaT 1 |
|
|
|
#ifdef Cdrskin_new_api_tesT |
|
|
|
/* put macros under test caveat here */ |
|
#define Cdrskin_allow_sao_for_appendablE 1 |
|
|
|
#endif /* Cdrskin_new_api_tesT */ |
|
|
|
|
|
#endif /* ! Cdrskin_oldfashioned_api_usE */ |
|
#endif /* Cdrskin_libburn_from_pykix_svN */ |
|
|
|
|
|
/* These macros activate cdrskin workarounds for deficiencies resp. |
|
problematic features of libburn which hopefully will change in |
|
future. */ |
|
|
|
/** Work around the fact that neither /dev/sg0 (kernel 2.4 + ide-scsi) nor |
|
/dev/hdc (kernel 2.6) get ejected by icculus.org/burn */ |
|
#ifndef Cdrskin_libburn_does_ejecT |
|
#define Cdrskin_burn_drive_eject_brokeN 1 |
|
#endif |
|
|
|
/** Work around the fact that after loading media speed report is wrong */ |
|
#ifndef Cdrskin_atip_speed_is_oK |
|
#define Cdrskin_atip_speed_brokeN 1 |
|
#endif |
|
|
|
/** Work around the fact that burn_drive_get_status() always reports to do |
|
track 0 in icculus.org/burn */ |
|
#ifndef Cdrskin_progress_track_does_worK |
|
#define Cdrskin_progress_track_brokeN 1 |
|
#endif |
|
|
|
/** Work around the fact that a drive interrupted at burn_drive_grab() never |
|
leaves status BURN_DRIVE_GRABBING in icculus.org/burn */ |
|
#ifndef Cdrskin_grab_abort_does_worK |
|
#define Cdrskin_grab_abort_brokeN 1 |
|
#endif |
|
|
|
/** Work around the fact that a freshly loaded tray with media reports |
|
arbitrary media erasability in icculuc.org/burn */ |
|
#ifndef Cdrskin_is_erasable_on_load_does_worK |
|
#define Cdrskin_is_erasable_on_load_is_brokeN 1 |
|
#endif |
|
|
|
/** http://libburnia.pykix.org/ticket/41 reports of big trouble without |
|
padding any track to a full sector |
|
*/ |
|
#define Cdrskin_all_tracks_with_sector_paD 1 |
|
|
|
|
|
/** A macro which is able to eat up a function call like printf() */ |
|
#ifdef Cdrskin_extra_leaN |
|
#define ClN(x) |
|
#else |
|
#define ClN(x) x |
|
#endif |
|
|
|
|
|
/** Verbosity level for pacifying progress messages */ |
|
#define Cdrskin_verbose_progresS 1 |
|
|
|
/** Verbosity level for command recognition and execution logging */ |
|
#define Cdrskin_verbose_cmD 2 |
|
|
|
/** Verbosity level for reporting of debugging messages */ |
|
#define Cdrskin_verbose_debuG 3 |
|
|
|
/** Verbosity level for fifo debugging */ |
|
#define Cdrskin_verbose_debug_fifO 4 |
|
|
|
|
|
#include <stdio.h> |
|
#include <sys/types.h> |
|
#include <sys/stat.h> |
|
#include <fcntl.h> |
|
#include <unistd.h> |
|
#include <string.h> |
|
#include <stdlib.h> |
|
#include <errno.h> |
|
#include <sys/time.h> |
|
|
|
#include "../libburn/libburn.h" |
|
|
|
#ifdef Cdrskin_libburn_has_audioxtR |
|
#include "../libburn/libdax_audioxtr.h" |
|
#endif |
|
|
|
#ifdef Cdrskin_libburn_has_cleanup_handleR |
|
#define Cleanup_set_handlers burn_set_signal_handling |
|
#define Cleanup_app_handler_T burn_abort_handler_t |
|
#else |
|
#include "cleanup.h" |
|
#endif |
|
|
|
|
|
/** The size of a string buffer for pathnames and similar texts */ |
|
#define Cdrskin_strleN 4096 |
|
|
|
/** The maximum length +1 of a drive address */ |
|
#ifndef Cdrskin_oldfashioned_api_usE |
|
#define Cdrskin_adrleN BURN_DRIVE_ADR_LEN |
|
#else |
|
#define Cdrskin_adrleN 80 |
|
#endif |
|
|
|
|
|
/* --------------------------------------------------------------------- */ |
|
|
|
/* Imported from scdbackup-0.8.5/src/cd_backup_planer.c */ |
|
|
|
/** Macro for creation of arrays of objects (or single objects) */ |
|
#define TSOB_FELD(typ,anz) (typ *) malloc((anz)*sizeof(typ)); |
|
|
|
|
|
/** Convert a text so that eventual characters special to the shell are |
|
made literal. Note: this does not make a text terminal-safe ! |
|
@param in_text The text to be converted |
|
@param out_text The buffer for the result. |
|
It should have size >= strlen(in_text)*5+2 |
|
@param flag Unused yet |
|
@return For convenience out_text is returned |
|
*/ |
|
char *Text_shellsafe(char *in_text, char *out_text, int flag) |
|
{ |
|
int l,i,w=0; |
|
|
|
/* enclose everything by hard quotes */ |
|
l= strlen(in_text); |
|
out_text[w++]= '\''; |
|
for(i=0;i<l;i++){ |
|
if(in_text[i]=='\''){ |
|
/* escape hard quote within the text */ |
|
out_text[w++]= '\''; |
|
out_text[w++]= '"'; |
|
out_text[w++]= '\''; |
|
out_text[w++]= '"'; |
|
out_text[w++]= '\''; |
|
} else { |
|
out_text[w++]= in_text[i]; |
|
} |
|
} |
|
out_text[w++]= '\''; |
|
out_text[w++]= 0; |
|
return(out_text); |
|
} |
|
|
|
|
|
/** Convert a text into a number of type double and multiply it by unit code |
|
[kmgtpe] (2^10 to 2^60) or [s] (2048). (Also accepts capital letters.) |
|
@param text Input like "42", "2k", "3.14m" or "-1g" |
|
@param flag Bitfield for control purposes: |
|
bit0= return -1 rathern than 0 on failure |
|
@return The derived double value |
|
*/ |
|
double Scanf_io_size(char *text, int flag) |
|
/* |
|
bit0= default value -1 rather than 0 |
|
*/ |
|
{ |
|
int c; |
|
double ret= 0.0; |
|
|
|
if(flag&1) |
|
ret= -1.0; |
|
if(text[0]==0) |
|
return(ret); |
|
sscanf(text,"%lf",&ret); |
|
c= text[strlen(text)-1]; |
|
if(c=='k' || c=='K') ret*= 1024.0; |
|
if(c=='m' || c=='M') ret*= 1024.0*1024.0; |
|
if(c=='g' || c=='G') ret*= 1024.0*1024.0*1024.0; |
|
if(c=='t' || c=='T') ret*= 1024.0*1024.0*1024.0*1024.0; |
|
if(c=='p' || c=='P') ret*= 1024.0*1024.0*1024.0*1024.0*1024.0; |
|
if(c=='e' || c=='E') ret*= 1024.0*1024.0*1024.0*1024.0*1024.0*1024.0; |
|
if(c=='s' || c=='S') ret*= 2048.0; |
|
return(ret); |
|
} |
|
|
|
|
|
/** Return a double representing seconds and microseconds since 1 Jan 1970 */ |
|
double Sfile_microtime(int flag) |
|
{ |
|
struct timeval tv; |
|
struct timezone tz; |
|
gettimeofday(&tv,&tz); |
|
return((double) (tv.tv_sec+1.0e-6*tv.tv_usec)); |
|
} |
|
|
|
|
|
#ifndef Cdrskin_extra_leaN |
|
|
|
/** Read a line from fp and strip LF or CRLF */ |
|
char *Sfile_fgets(char *line, int maxl, FILE *fp) |
|
{ |
|
int l; |
|
char *ret; |
|
ret= fgets(line,maxl,fp); |
|
if(ret==NULL) return(NULL); |
|
l= strlen(line); |
|
if(l>0) if(line[l-1]=='\r') line[--l]= 0; |
|
if(l>0) if(line[l-1]=='\n') line[--l]= 0; |
|
if(l>0) if(line[l-1]=='\r') line[--l]= 0; |
|
return(ret); |
|
} |
|
|
|
|
|
/** Destroy a synthetic argument array */ |
|
int Sfile_destroy_argv(int *argc, char ***argv, int flag) |
|
{ |
|
int i; |
|
|
|
if(*argc>0 && *argv!=NULL){ |
|
for(i=0;i<*argc;i++){ |
|
if((*argv)[i]!=NULL) |
|
free((*argv)[i]); |
|
} |
|
free((char *) *argv); |
|
} |
|
*argc= 0; |
|
*argv= NULL; |
|
return(1); |
|
} |
|
|
|
|
|
/** Read a synthetic argument array from a list of files. |
|
@param progname The content for argv[0] |
|
@param filenames The paths of the filex from where to read |
|
@param filenamecount The number of paths in filenames |
|
@param argc Returns the number of read arguments (+1 for progname) |
|
@param argv Returns the array of synthetic arguments |
|
@param argidx Returns source file indice of argv[] items |
|
@param arglno Returns source file line numbers of argv[] items |
|
@param flag Bitfield for control purposes: |
|
bit0= read progname as first argument from line |
|
bit1= just release argument array argv and return |
|
bit2= tolerate failure to open file |
|
@return 1=ok , 0=cannot open file , -1=cannot create memory objects |
|
*/ |
|
int Sfile_multi_read_argv(char *progname, char **filenames, int filename_count, |
|
int *argc, char ***argv, int **argidx, int **arglno, |
|
int flag) |
|
{ |
|
int ret,i,pass,maxl=0,l,argcount=0,line_no; |
|
char buf[Cdrskin_strleN]; |
|
FILE *fp= NULL; |
|
|
|
Sfile_destroy_argv(argc,argv,0); |
|
if(flag&2) |
|
return(1); |
|
if((*argidx)!=NULL) |
|
free((char *) *argidx); |
|
if((*arglno)!=NULL) |
|
free((char *) *arglno); |
|
*argidx= *arglno= NULL; |
|
|
|
for(pass=0;pass<2;pass++) { |
|
if(!(flag&1)){ |
|
argcount= 1; |
|
if(pass==0) |
|
maxl= strlen(progname)+1; |
|
else { |
|
(*argv)[0]= (char *) malloc(strlen(progname)+1); |
|
if((*argv)[0]==NULL) |
|
{ret= -1; goto ex;} |
|
strcpy((*argv)[0],progname); |
|
} |
|
} else { |
|
argcount= 0; |
|
if(pass==0) |
|
maxl= 1; |
|
} |
|
for(i=0; i<filename_count;i++) { |
|
if(strlen(filenames[i])==0) |
|
continue; |
|
fp= fopen(filenames[i],"rb"); |
|
if(fp==NULL) { |
|
if(flag&4) |
|
continue; |
|
{ret= 0; goto ex;} |
|
} |
|
|
|
#ifdef Cdrskin_new_api_tesT |
|
if(pass>0) |
|
fprintf(stderr,"cdrskin: DEBUG : Reading arguments from file '%s'\n", |
|
filenames[i]); |
|
#endif |
|
|
|
line_no= 0; |
|
while(Sfile_fgets(buf,sizeof(buf)-1,fp)!=NULL) { |
|
line_no++; |
|
l= strlen(buf); |
|
if(l==0 || buf[0]=='#') |
|
continue; |
|
if(pass==0){ |
|
if(l>maxl) |
|
maxl= l; |
|
} else { |
|
if(argcount >= *argc) |
|
break; |
|
(*argv)[argcount]= (char *) malloc(l+1); |
|
if((*argv)[argcount]==NULL) |
|
{ret= -1; goto ex;} |
|
strcpy((*argv)[argcount],buf); |
|
(*argidx)[argcount]= i; |
|
(*arglno)[argcount]= line_no; |
|
} |
|
argcount++; |
|
} |
|
fclose(fp); fp= NULL; |
|
} |
|
if(pass==0){ |
|
*argc= argcount; |
|
if(argcount>0) { |
|
*argv= (char **) malloc(argcount*sizeof(char *)); |
|
*argidx= (int *) malloc(argcount*sizeof(int)); |
|
*arglno= (int *) malloc(argcount*sizeof(int)); |
|
if(*argv==NULL || *argidx==NULL || *arglno==NULL) |
|
{ret= -1; goto ex;} |
|
} |
|
for(i=0;i<*argc;i++) { |
|
(*argv)[i]= NULL; |
|
(*argidx)[i]= -1; |
|
(*arglno)[i]= -1; |
|
} |
|
} |
|
} |
|
|
|
ret= 1; |
|
ex:; |
|
if(fp!=NULL) |
|
fclose(fp); |
|
return(ret); |
|
} |
|
|
|
|
|
/** Combine environment variable HOME with given filename |
|
@param filename Address relative to $HOME |
|
@param fileadr Resulting combined address |
|
@param fa_size Size of array fileadr |
|
@param flag Unused yet |
|
@return 1=ok , 0=no HOME variable , -1=result address too long |
|
*/ |
|
int Sfile_home_adr_s(char *filename, char *fileadr, int fa_size, int flag) |
|
{ |
|
char *home; |
|
|
|
strcpy(fileadr,filename); |
|
home= getenv("HOME"); |
|
if(home==NULL) |
|
return(0); |
|
if(strlen(home)+strlen(filename)+1>=fa_size) |
|
return(-1); |
|
strcpy(fileadr,home); |
|
if(filename[0]!=0){ |
|
strcat(fileadr,"/"); |
|
strcat(fileadr,filename); |
|
} |
|
return(1); |
|
} |
|
|
|
|
|
#endif /* ! Cdrskin_extra_leaN */ |
|
|
|
|
|
/* --------------------------------------------------------------------- */ |
|
|
|
/** Address translation table for users/applications which do not look |
|
for the output of -scanbus but guess a Bus,Target,Lun on their own. |
|
*/ |
|
|
|
/** The maximum number of entries in the address translation table */ |
|
#define Cdradrtrn_leN 256 |
|
|
|
/** The address prefix which will prevent translation */ |
|
#define Cdrskin_no_transl_prefiX "LITERAL_ADR:" |
|
|
|
|
|
struct CdradrtrN { |
|
char *from_address[Cdradrtrn_leN]; |
|
char *to_address[Cdradrtrn_leN]; |
|
int fill_counter; |
|
}; |
|
|
|
|
|
#ifndef Cdrskin_extra_leaN |
|
|
|
/** Create a device address translator object */ |
|
int Cdradrtrn_new(struct CdradrtrN **trn, int flag) |
|
{ |
|
struct CdradrtrN *o; |
|
int i; |
|
|
|
(*trn)= o= TSOB_FELD(struct CdradrtrN,1); |
|
if(o==NULL) |
|
return(-1); |
|
for(i= 0;i<Cdradrtrn_leN;i++) { |
|
o->from_address[i]= NULL; |
|
o->to_address[i]= NULL; |
|
} |
|
o->fill_counter= 0; |
|
return(1); |
|
} |
|
|
|
|
|
/** Release from memory a device address translator object */ |
|
int Cdradrtrn_destroy(struct CdradrtrN **o, int flag) |
|
{ |
|
int i; |
|
struct CdradrtrN *trn; |
|
|
|
trn= *o; |
|
if(trn==NULL) |
|
return(0); |
|
for(i= 0;i<trn->fill_counter;i++) { |
|
if(trn->from_address[i]!=NULL) |
|
free(trn->from_address[i]); |
|
if(trn->to_address[i]!=NULL) |
|
free(trn->to_address[i]); |
|
} |
|
free((char *) trn); |
|
*o= NULL; |
|
return(1); |
|
} |
|
|
|
|
|
/** Add a translation pair to the table |
|
@param trn The translator which shall learn |
|
@param from The user side address |
|
@param to The cdrskin side address |
|
@param flag Bitfield for control purposes: |
|
bit0= "from" contains from+to address, to[0] contains delimiter |
|
*/ |
|
int Cdradrtrn_add(struct CdradrtrN *trn, char *from, char *to, int flag) |
|
{ |
|
char buf[2*Cdrskin_adrleN+1],*from_pt,*to_pt; |
|
int cnt; |
|
|
|
cnt= trn->fill_counter; |
|
if(cnt>=Cdradrtrn_leN) |
|
return(-1); |
|
if(flag&1) { |
|
if(strlen(from)>=sizeof(buf)) |
|
return(0); |
|
strcpy(buf,from); |
|
to_pt= strchr(buf,to[0]); |
|
if(to_pt==NULL) |
|
return(0); |
|
*(to_pt)= 0; |
|
from_pt= buf; |
|
to_pt++; |
|
} else { |
|
from_pt= from; |
|
to_pt= to; |
|
} |
|
if(strlen(from)>=Cdrskin_adrleN || strlen(to)>=Cdrskin_adrleN) |
|
return(0); |
|
trn->from_address[cnt]= malloc(strlen(from_pt)+1); |
|
trn->to_address[cnt]= malloc(strlen(to_pt)+1); |
|
if(trn->from_address[cnt]==NULL || |
|
trn->to_address[cnt]==NULL) |
|
return(-2); |
|
strcpy(trn->from_address[cnt],from_pt); |
|
strcpy(trn->to_address[cnt],to_pt); |
|
trn->fill_counter++; |
|
return(1); |
|
} |
|
|
|
|
|
/** Apply eventual device address translation |
|
@param trn The translator |
|
@param from The address from which to translate |
|
@param driveno With backward translation only: The libburn drive number |
|
@param to The result of the translation |
|
@param flag Bitfield for control purposes: |
|
bit0= translate backward |
|
@return <=0 error, 1=no translation found, 2=translation found, |
|
3=collision avoided |
|
*/ |
|
int Cdradrtrn_translate(struct CdradrtrN *trn, char *from, int driveno, |
|
char to[Cdrskin_adrleN], int flag) |
|
{ |
|
int i,ret= 1; |
|
char *adr; |
|
|
|
to[0]= 0; |
|
adr= from; |
|
if(flag&1) |
|
goto backward; |
|
|
|
if(strncmp(adr,Cdrskin_no_transl_prefiX, |
|
strlen(Cdrskin_no_transl_prefiX))==0) { |
|
adr= adr+strlen(Cdrskin_no_transl_prefiX); |
|
ret= 2; |
|
} else { |
|
for(i=0;i<trn->fill_counter;i++) |
|
if(strcmp(adr,trn->from_address[i])==0) |
|
break; |
|
if(i<trn->fill_counter) { |
|
adr= trn->to_address[i]; |
|
ret= 2; |
|
} |
|
} |
|
if(strlen(adr)>=Cdrskin_adrleN) |
|
return(-1); |
|
strcpy(to,adr); |
|
return(ret); |
|
|
|
backward:; |
|
if(strlen(from)>=Cdrskin_adrleN) |
|
sprintf(to,"%s%d",Cdrskin_no_transl_prefiX,driveno); |
|
else |
|
strcpy(to,from); |
|
for(i=0;i<trn->fill_counter;i++) |
|
if(strcmp(from,trn->to_address[i])==0 && |
|
strlen(trn->from_address[i])<Cdrskin_adrleN) |
|
break; |
|
if(i<trn->fill_counter) { |
|
ret= 2; |
|
strcpy(to,trn->from_address[i]); |
|
} else { |
|
for(i=0;i<trn->fill_counter;i++) |
|
if(strcmp(from,trn->from_address[i])==0) |
|
break; |
|
if(i<trn->fill_counter) |
|
if(strlen(from)+strlen(Cdrskin_no_transl_prefiX)<Cdrskin_adrleN) { |
|
ret= 3; |
|
sprintf(to,"%s%s",Cdrskin_no_transl_prefiX,from); |
|
} |
|
} |
|
return(ret); |
|
} |
|
|
|
#endif /* Cdrskin_extra_leaN */ |
|
|
|
|
|
/* --------------------------------------------------------------------- */ |
|
|
|
|
|
#ifndef Cdrskin_extra_leaN |
|
|
|
/* Program is to be linked with cdrfifo.c */ |
|
#include "cdrfifo.h" |
|
|
|
#else /* ! Cdrskin_extra_leaN */ |
|
|
|
/* Dummy */ |
|
|
|
struct CdrfifO { |
|
int dummy; |
|
}; |
|
|
|
#endif /* Cdrskin_extra_leaN */ |
|
|
|
|
|
/* --------------------------------------------------------------------- */ |
|
|
|
/** cdrecord pads up to 600 kB in any case. |
|
libburn yields blank result on tracks <~ 600 kB |
|
cdrecord demands 300 sectors = 705600 bytes for -audio */ |
|
static double Cdrtrack_minimum_sizE= 300; |
|
|
|
|
|
/** This structure represents a track resp. a data source */ |
|
struct CdrtracK { |
|
|
|
struct CdrskiN *boss; |
|
int trackno; |
|
|
|
char source_path[Cdrskin_strleN]; |
|
int source_fd; |
|
int is_from_stdin; |
|
double fixed_size; |
|
double tao_to_sao_tsize; |
|
double padding; |
|
int set_by_padsize; |
|
int sector_pad_up; /* enforce single sector padding */ |
|
int track_type; |
|
double sector_size; |
|
int track_type_by_default; |
|
int swap_audio_bytes; |
|
|
|
/* wether the data source is a container of defined size with possible tail */ |
|
int extracting_container; |
|
|
|
/** Optional fifo between input fd and libburn. It uses a pipe(2) to transfer |
|
data to libburn. |
|
*/ |
|
int fifo_enabled; |
|
|
|
/** The eventual own fifo object managed by this track object. */ |
|
struct CdrfifO *fifo; |
|
|
|
/** fd[0] of the fifo pipe. This is from where libburn reads its data. */ |
|
int fifo_outlet_fd; |
|
int fifo_size; |
|
int fifo_start_at; |
|
|
|
/** The possibly external fifo object which knows the real input fd and |
|
the fd[1] of the pipe. */ |
|
struct CdrfifO *ff_fifo; |
|
/** The index number if fifo follow up fd item, -1= own fifo */ |
|
int ff_idx; |
|
|
|
struct burn_track *libburn_track; |
|
|
|
}; |
|
|
|
int Cdrtrack_destroy(struct CdrtracK **o, int flag); |
|
int Cdrtrack_set_track_type(struct CdrtracK *o, int track_type, int flag); |
|
|
|
|
|
/** Create a track resp. data source object. |
|
@param track Returns the address of the new object. |
|
@param boss The cdrskin control object (corresponds to session) |
|
@param trackno The index in the cdrskin tracklist array (is not constant) |
|
@param flag Bitfield for control purposes: |
|
bit0= set fifo_start_at to 0 |
|
bit1= track is originally stdin |
|
*/ |
|
int Cdrtrack_new(struct CdrtracK **track, struct CdrskiN *boss, |
|
int trackno, int flag) |
|
{ |
|
struct CdrtracK *o; |
|
int ret,skin_track_type; |
|
int Cdrskin_get_source(struct CdrskiN *skin, char *source_path, |
|
double *fixed_size, double *tao_to_sao_tsize, |
|
double *padding, int *set_by_padsize, |
|
int *track_type, int *track_type_by_default, |
|
int *swap_audio_bytes, int flag); |
|
int Cdrskin_get_fifo_par(struct CdrskiN *skin, int *fifo_enabled, |
|
int *fifo_size, int *fifo_start_at, int flag); |
|
|
|
(*track)= o= TSOB_FELD(struct CdrtracK,1); |
|
if(o==NULL) |
|
return(-1); |
|
o->boss= boss; |
|
o->trackno= trackno; |
|
o->source_path[0]= 0; |
|
o->source_fd= -1; |
|
o->is_from_stdin= !!(flag&2); |
|
o->fixed_size= 0.0; |
|
o->tao_to_sao_tsize= 0.0; |
|
o->padding= 0.0; |
|
o->set_by_padsize= 0; |
|
o->sector_pad_up= Cdrskin_all_tracks_with_sector_paD; |
|
o->track_type= BURN_MODE1; |
|
o->sector_size= 2048.0; |
|
o->track_type_by_default= 1; |
|
o->swap_audio_bytes= 0; |
|
o->extracting_container= 0; |
|
o->fifo_enabled= 0; |
|
o->fifo= NULL; |
|
o->fifo_outlet_fd= -1; |
|
o->fifo_size= 0; |
|
o->fifo_start_at= -1; |
|
o->ff_fifo= NULL; |
|
o->ff_idx= -1; |
|
o->libburn_track= NULL; |
|
ret= Cdrskin_get_source(boss,o->source_path,&(o->fixed_size), |
|
&(o->tao_to_sao_tsize),&(o->padding), |
|
&(o->set_by_padsize),&(skin_track_type), |
|
&(o->track_type_by_default),&(o->swap_audio_bytes), |
|
0); |
|
if(ret<=0) |
|
goto failed; |
|
Cdrtrack_set_track_type(o,skin_track_type,0); |
|
|
|
#ifndef Cdrskin_extra_leaN |
|
ret= Cdrskin_get_fifo_par(boss, &(o->fifo_enabled),&(o->fifo_size), |
|
&(o->fifo_start_at),0); |
|
if(ret<=0) |
|
goto failed; |
|
#endif /* ! Cdrskin_extra_leaN */ |
|
|
|
if(flag&1) |
|
o->fifo_start_at= 0; |
|
return(1); |
|
failed:; |
|
Cdrtrack_destroy(track,0); |
|
return(-1); |
|
} |
|
|
|
|
|
/** Release from memory a track object previously created by Cdrtrack_new() */ |
|
int Cdrtrack_destroy(struct CdrtracK **o, int flag) |
|
{ |
|
struct CdrtracK *track; |
|
|
|
track= *o; |
|
if(track==NULL) |
|
return(0); |
|
|
|
#ifndef Cdrskin_extra_leaN |
|
Cdrfifo_destroy(&(track->fifo),0); |
|
#endif |
|
|
|
if(track->libburn_track!=NULL) |
|
burn_track_free(track->libburn_track); |
|
free((char *) track); |
|
*o= NULL; |
|
return(1); |
|
} |
|
|
|
|
|
int Cdrtrack_set_track_type(struct CdrtracK *o, int track_type, int flag) |
|
{ |
|
if(track_type==BURN_AUDIO) { |
|
o->track_type= BURN_AUDIO; |
|
o->sector_size= 2352.0; |
|
} else { |
|
o->track_type= BURN_MODE1; |
|
o->sector_size= 2048.0; |
|
} |
|
return(1); |
|
} |
|
|
|
|
|
int Cdrtrack_get_track_type(struct CdrtracK *o, int *track_type, |
|
int *sector_size, int flag) |
|
{ |
|
*track_type= o->track_type; |
|
*sector_size= o->sector_size; |
|
return(1); |
|
} |
|
|
|
|
|
/** |
|
@param flag Bitfield for control purposes: |
|
bit0= size returns number of actually processed source bytes |
|
rather than the predicted fixed_size (if available). |
|
padding returns the difference from number of written |
|
bytes. |
|
bit1= size returns fixed_size, padding returns tao_to_sao_tsize |
|
*/ |
|
int Cdrtrack_get_size(struct CdrtracK *track, double *size, double *padding, |
|
double *sector_size, int flag) |
|
{ |
|
|
|
*size= track->fixed_size; |
|
*padding= track->padding; |
|
#ifdef Cdrskin_allow_libburn_taO |
|
if((flag&1) && track->libburn_track!=NULL) { |
|
off_t readcounter= 0,writecounter= 0; |
|
|
|
burn_track_get_counters(track->libburn_track,&readcounter,&writecounter); |
|
*size= readcounter; |
|
*padding= writecounter-readcounter; |
|
/* |
|
fprintf(stderr,"cdrskin_debug: sizeof(off_t)=%d\n", |
|
sizeof(off_t)); |
|
*/ |
|
} else if(flag&2) |
|
*padding= track->tao_to_sao_tsize; |
|
|
|
#endif |
|
*sector_size= track->sector_size; |
|
return(1); |
|
} |
|
|
|
|
|
int Cdrtrack_get_fifo(struct CdrtracK *track, struct CdrfifO **fifo, int flag) |
|
{ |
|
*fifo= track->fifo; |
|
return(1); |
|
} |
|
|
|
|
|
/** Try wether automatic audio extraction is appropriate and eventually open |
|
a file descriptor to the raw data. |
|
@return -3 identified as .wav but with cdrecord-inappropriate parameters |
|
-2 could not open track source, no use in retrying |
|
-1 severe error |
|
0 not appropriate to extract, burn plain file content |
|
1 to be extracted, *fd is a filedescriptor delivering raw data |
|
*/ |
|
int Cdrtrack_extract_audio(struct CdrtracK *track, int *fd, off_t *xtr_size, |
|
int flag) |
|
{ |
|
int l, ok= 0; |
|
#ifdef Cdrskin_libburn_has_audioxtR |
|
struct libdax_audioxtr *xtr= NULL; |
|
char *fmt,*fmt_info; |
|
int num_channels,sample_rate,bits_per_sample,msb_first,ret; |
|
#endif |
|
|
|
*fd= -1; |
|
|
|
if(track->track_type!=BURN_AUDIO && !track->track_type_by_default) |
|
return(0); |
|
l= strlen(track->source_path); |
|
if(l>=4) |
|
if(strcmp(track->source_path+l-4,".wav")==0) |
|
ok= 1; |
|
if(l>=3) |
|
if(strcmp(track->source_path+l-3,".au")==0) |
|
ok= 1; |
|
if(!ok) |
|
return(0); |
|
|
|
if(track->track_type_by_default) { |
|
Cdrtrack_set_track_type(track,BURN_AUDIO,0); |
|
track->track_type_by_default= 2; |
|
fprintf(stderr,"cdrskin: NOTE : Activated -audio for '%s'\n", |
|
track->source_path); |
|
} |
|
|
|
#ifdef Cdrskin_libburn_has_audioxtR |
|
|
|
ret= libdax_audioxtr_new(&xtr,track->source_path,0); |
|
if(ret<=0) |
|
return(ret); |
|
libdax_audioxtr_get_id(xtr,&fmt,&fmt_info, |
|
&num_channels,&sample_rate,&bits_per_sample,&msb_first,0); |
|
if((strcmp(fmt,".wav")!=0 && strcmp(fmt,".au")!=0) || |
|
num_channels!=2 || sample_rate!=44100 || bits_per_sample!=16) { |
|
fprintf(stderr,"cdrskin: ( %s )\n",fmt_info); |
|
fprintf(stderr,"cdrskin: FATAL : Inappropriate audio coding in '%s'.\n", |
|
track->source_path); |
|
{ret= -3; goto ex;} |
|
} |
|
libdax_audioxtr_get_size(xtr,xtr_size,0); |
|
ret= libdax_audioxtr_detach_fd(xtr,fd,0); |
|
if(ret<=0) |
|
{ret= -1*!!ret; goto ex;} |
|
track->swap_audio_bytes= !!msb_first; |
|
track->extracting_container= 1; |
|
fprintf(stderr,"cdrskin: NOTE : %.f %saudio bytes in '%s'\n", |
|
(double) *xtr_size, (msb_first ? "" : "(-swab) "), |
|
track->source_path); |
|
ret= 1; |
|
ex: |
|
libdax_audioxtr_destroy(&xtr,0); |
|
return(ret); |
|
|
|
#else /* Cdrskin_libburn_has_audioxtR */ |
|
|
|
return(0); |
|
|
|
#endif |
|
} |
|
|
|
|
|
/** Deliver an open file descriptor corresponding to the source path of track. |
|
@return <=0 error, 1 success |
|
*/ |
|
int Cdrtrack_open_source_path(struct CdrtracK *track, int *fd, int flag) |
|
{ |
|
int is_wav= 0, size_from_file= 0; |
|
off_t xtr_size= 0; |
|
struct stat stbuf; |
|
|
|
if(track->source_path[0]=='-' && track->source_path[1]==0) |
|
*fd= 0; |
|
else if(track->source_path[0]=='#' && |
|
(track->source_path[1]>='0' && track->source_path[1]<='9')) |
|
*fd= atoi(track->source_path+1); |
|
else { |
|
*fd= -1; |
|
is_wav= Cdrtrack_extract_audio(track,fd,&xtr_size,0); |
|
if(is_wav==-1) |
|
return(-1); |
|
if(is_wav==-3) |
|
return(0); |
|
if(is_wav==0) |
|
*fd= open(track->source_path,O_RDONLY); |
|
if(*fd==-1) { |
|
fprintf(stderr,"cdrskin: failed to open source address '%s'\n", |
|
track->source_path); |
|
fprintf(stderr,"cdrskin: errno=%d , \"%s\"\n",errno, |
|
errno==0?"-no error code available-":strerror(errno)); |
|
return(0); |
|
} |
|
if(track->fixed_size<=0) { |
|
if(xtr_size>0) { |
|
track->fixed_size= xtr_size; |
|
size_from_file= 1; |
|
} else { |
|
if(fstat(*fd,&stbuf)!=-1) { |
|
if((stbuf.st_mode&S_IFMT)==S_IFREG) { |
|
track->fixed_size= stbuf.st_size; |
|
size_from_file= 1; |
|
} /* all other types are assumed of open ended size */ |
|
} |
|
} |
|
} |
|
} |
|
|
|
#ifdef Cdrskin_allow_libburn_taO |
|
|
|
if(track->fixed_size < Cdrtrack_minimum_sizE * track->sector_size |
|
&& (track->fixed_size>0 || size_from_file)) { |
|
|
|
#else |
|
|
|
if(track->fixed_size < Cdrtrack_minimum_sizE * track->sector_size) { |
|
|
|
#endif |
|
|
|
if(track->track_type == BURN_AUDIO) { |
|
/* >>> cdrecord: We differ in automatic padding with audio: |
|
Audio tracks must be at least 705600 bytes and a multiple of 2352. |
|
*/ |
|
fprintf(stderr, |
|
"cdrskin: FATAL : Audio tracks must be at least %.f bytes\n", |
|
Cdrtrack_minimum_sizE*track->sector_size); |
|
return(0); |
|
} else { |
|
fprintf(stderr, |
|
"cdrskin: NOTE : Enforcing minimum track size of %.f bytes\n", |
|
Cdrtrack_minimum_sizE*track->sector_size); |
|
track->fixed_size= Cdrtrack_minimum_sizE*track->sector_size; |
|
} |
|
} |
|
track->source_fd= *fd; |
|
return(*fd>=0); |
|
} |
|
|
|
|
|
#ifndef Cdrskin_extra_leaN |
|
|
|
/** Install a fifo object between data source and libburn. |
|
Its parameters are known to track. |
|
@param outlet_fd Returns the filedescriptor of the fifo outlet. |
|
@param previous_fifo Object address for chaining or follow-up attachment. |
|
@param flag Bitfield for control purposes: |
|
bit0= Debugging verbosity |
|
bit1= Do not create and attach a new fifo |
|
but attach new follow-up fd pair to previous_fifo |
|
bit2= Do not enforce fixed_size if not container extraction |
|
@return <=0 error, 1 success |
|
*/ |
|
int Cdrtrack_attach_fifo(struct CdrtracK *track, int *outlet_fd, |
|
struct CdrfifO *previous_fifo, int flag) |
|
{ |
|
struct CdrfifO *ff; |
|
int source_fd,pipe_fds[2],ret; |
|
|
|
*outlet_fd= -1; |
|
if(track->fifo_size<=0) |
|
return(2); |
|
ret= Cdrtrack_open_source_path(track,&source_fd,0); |
|
if(ret<=0) |
|
return(ret); |
|
if(pipe(pipe_fds)==-1) |
|
return(0); |
|
|
|
Cdrfifo_destroy(&(track->fifo),0); |
|
if(flag&2) { |
|
ret= Cdrfifo_attach_follow_up_fds(previous_fifo,source_fd,pipe_fds[1],0); |
|
if(ret<=0) |
|
return(ret); |
|
track->ff_fifo= previous_fifo; |
|
track->ff_idx= ret-1; |
|
} else { |
|
|
|
/* >>> ??? obtain track sector size and use instead of 2048 ? */ |
|
|
|
ret= Cdrfifo_new(&ff,source_fd,pipe_fds[1],2048,track->fifo_size,0); |
|
if(ret<=0) |
|
return(ret); |
|
if(previous_fifo!=NULL) |
|
Cdrfifo_attach_peer(previous_fifo,ff,0); |
|
track->fifo= track->ff_fifo= ff; |
|
track->ff_idx= -1; |
|
} |
|
track->fifo_outlet_fd= pipe_fds[0]; |
|
|
|
if((track->extracting_container || !(flag&4)) && track->fixed_size>0) |
|
Cdrfifo_set_fd_in_limit(track->ff_fifo,track->fixed_size,track->ff_idx,0); |
|
|
|
if(flag&1) |
|
printf( |
|
"cdrskin_debug: track %d fifo replaced source_address '%s' by '#%d'\n", |
|
track->trackno+1,track->source_path,track->fifo_outlet_fd); |
|
sprintf(track->source_path,"#%d",track->fifo_outlet_fd); |
|
track->source_fd= track->fifo_outlet_fd; |
|
*outlet_fd= track->fifo_outlet_fd; |
|
return(1); |
|
} |
|
|
|
|
|
/** Read data into the fifo until either it is full or the data source is |
|
exhausted. |
|
@return <=0 error, 1 success |
|
*/ |
|
int Cdrtrack_fill_fifo(struct CdrtracK *track, int flag) |
|
{ |
|
int ret,buffer_fill,buffer_space; |
|
|
|
if(track->fifo==NULL || track->fifo_start_at==0) |
|
return(2); |
|
if(track->fifo_start_at>0 && track->fifo_start_at<track->fifo_size) |
|
printf( |
|
"cdrskin: NOTE : Input buffer will be initially filled up to %d bytes\n", |
|
track->fifo_start_at); |
|
printf("Waiting for reader process to fill input buffer ... "); |
|
fflush(stdout); |
|
ret= Cdrfifo_fill(track->fifo,track->fifo_start_at,0); |
|
if(ret<=0) |
|
return(ret); |
|
|
|
/** Ticket 55: check fifos for input, throw error on 0-bytes from stdin |
|
@return <=0 abort run, 1 go on with burning |
|
*/ |
|
if(track->is_from_stdin) { |
|
ret= Cdrfifo_get_buffer_state(track->fifo,&buffer_fill,&buffer_space,0); |
|
if(ret<0 || buffer_fill<=0) { |
|
fprintf(stderr,"\ncdrskin: FATAL : (First track) fifo did not read a single byte from stdin\n"); |
|
return(0); |
|
} |
|
} |
|
return(1); |
|
} |
|
|
|
#endif /* ! Cdrskin_extra_leaN */ |
|
|
|
|
|
/** Create a corresponding libburn track object and add it to the libburn |
|
session. This may change the trackno index set by Cdrtrack_new(). |
|
*/ |
|
int Cdrtrack_add_to_session(struct CdrtracK *track, int trackno, |
|
struct burn_session *session, int flag) |
|
/* |
|
bit0= debugging verbosity |
|
bit1= apply padding hack (<<< should be unused for now) |
|
*/ |
|
{ |
|
struct burn_track *tr; |
|
struct burn_source *src= NULL; |
|
double padding,lib_padding; |
|
int ret,sector_pad_up; |
|
double fixed_size; |
|
int source_fd; |
|
|
|
track->trackno= trackno; |
|
tr= burn_track_create(); |
|
track->libburn_track= tr; |
|
|
|
/* Note: track->track_type may get set in here */ |
|
if(track->source_fd==-1) { |
|
ret= Cdrtrack_open_source_path(track,&source_fd,0); |
|
if(ret<=0) |
|
goto ex; |
|
} |
|
|
|
padding= 0.0; |
|
sector_pad_up= track->sector_pad_up; |
|
if(track->padding>0) { |
|
if(track->set_by_padsize || track->track_type!=BURN_AUDIO) |
|
padding= track->padding; |
|
else |
|
sector_pad_up= 1; |
|
} |
|
if(flag&2) |
|
lib_padding= 0.0; |
|
else |
|
lib_padding= padding; |
|
if(flag&1) { |
|
if(sector_pad_up) { |
|
ClN(fprintf(stderr,"cdrskin_debug: track %d telling burn_track_define_data() to pad up last sector\n",trackno+1)); |
|
} |
|
if(lib_padding>0 || !sector_pad_up) { |
|
ClN(fprintf(stderr, |
|
"cdrskin_debug: track %d telling burn_track_define_data() to pad %.f bytes\n", |
|
trackno+1,lib_padding)); |
|
} |
|
} |
|
burn_track_define_data(tr,0,(int) lib_padding,sector_pad_up, |
|
track->track_type); |
|
burn_track_set_byte_swap(tr, |
|
(track->track_type==BURN_AUDIO && track->swap_audio_bytes)); |
|
fixed_size= track->fixed_size; |
|
if((flag&2) && track->padding>0) { |
|
if(flag&1) |
|
ClN(fprintf(stderr,"cdrskin_debug: padding hack : %.f + %.f = %.f\n", |
|
track->fixed_size,track->padding, |
|
track->fixed_size+track->padding)); |
|
fixed_size+= track->padding; |
|
} |
|
src= burn_fd_source_new(track->source_fd,-1,(off_t) fixed_size); |
|
|
|
if(src==NULL) { |
|
fprintf(stderr, |
|
"cdrskin: FATAL : Could not create libburn data source object\n"); |
|
{ret= 0; goto ex;} |
|
} |
|
if(burn_track_set_source(tr,src)!=BURN_SOURCE_OK) { |
|
fprintf(stderr,"cdrskin: FATAL : libburn rejects data source object\n"); |
|
{ret= 0; goto ex;} |
|
} |
|
burn_session_add_track(session,tr,BURN_POS_END); |
|
ret= 1; |
|
ex: |
|
if(src!=NULL) |
|
burn_source_free(src); |
|
return(ret); |
|
} |
|
|
|
|
|
/** Release libburn track information after a session is done */ |
|
int Cdrtrack_cleanup(struct CdrtracK *track, int flag) |
|
{ |
|
if(track->libburn_track==NULL) |
|
return(0); |
|
burn_track_free(track->libburn_track); |
|
track->libburn_track= NULL; |
|
return(1); |
|
} |
|
|
|
|
|
int Cdrtrack_ensure_padding(struct CdrtracK *track, int flag) |
|
/* |
|
flag: |
|
bit0= debugging verbosity |
|
*/ |
|
{ |
|
if(track->track_type!=BURN_AUDIO) |
|
return(2); |
|
if(flag&1) |
|
fprintf(stderr,"cdrskin_debug: enforcing -pad on last -audio track\n"); |
|
track->sector_pad_up= 1; |
|
return(1); |
|
} |
|
|
|
|
|
int Cdrtrack_activate_tao_tsize(struct CdrtracK *track, int flag) |
|
{ |
|
if(track->fixed_size<=0.0) |
|
track->fixed_size= track->tao_to_sao_tsize; |
|
return(track->fixed_size>0.0); |
|
} |
|
|
|
|
|
#ifndef Cdrskin_extra_leaN |
|
|
|
/** Try to read bytes from the track's fifo outlet and eventually discard |
|
them. Not to be called unless the track is completely written. |
|
*/ |
|
int Cdrtrack_has_input_left(struct CdrtracK *track, int flag) |
|
{ |
|
struct timeval wt; |
|
fd_set rds,wts,exs; |
|
int ready,ret; |
|
char buf[2]; |
|
|
|
if(track->fifo_outlet_fd<=0) |
|
return(0); |
|
FD_ZERO(&rds); |
|
FD_ZERO(&wts); |
|
FD_ZERO(&exs); |
|
FD_SET(track->fifo_outlet_fd,&rds); |
|
wt.tv_sec= 0; |
|
wt.tv_usec= 0; |
|
ready= select(track->fifo_outlet_fd+1,&rds,&wts,&exs,&wt); |
|
if(ready<=0) |
|
return(0); |
|
ret= read(track->fifo_outlet_fd,buf,1); |
|
if(ret>0) |
|
return(1); |
|
return(0); |
|
} |
|
|
|
#endif /* ! Cdrskin_extra_leaN */ |
|
|
|
|
|
/* --------------------------------------------------------------------- */ |
|
|
|
/** The list of startup file names */ |
|
#define Cdrpreskin_rc_nuM 4 |
|
|
|
static char Cdrpreskin_sys_rc_nameS[Cdrpreskin_rc_nuM][80]= { |
|
"/etc/default/cdrskin", |
|
"/etc/opt/cdrskin/rc", |
|
"/etc/cdrskin/cdrskin.conf", |
|
"placeholder for $HOME/.cdrskinrc" |
|
}; |
|
|
|
|
|
/** A structure which bundles several parameters for creation of the CdrskiN |
|
object. It finally becomes a managed subordinate of the CdrskiN object. |
|
*/ |
|
struct CdrpreskiN { |
|
|
|
/* to be transfered into skin */ |
|
int verbosity; |
|
char queue_severity[81]; |
|
char print_severity[81]; |
|
|
|
/** Stores eventually given absolute device address before translation */ |
|
char raw_device_adr[Cdrskin_adrleN]; |
|
|
|
/** Stores an eventually given translated absolute device address between |
|
Cdrpreskin_setup() and Cdrskin_create() . |
|
*/ |
|
char device_adr[Cdrskin_adrleN]; |
|
|
|
/** The eventual address translation table */ |
|
struct CdradrtrN *adr_trn; |
|
|
|
/** Memorizes the abort handling mode from presetup to creation of |
|
control object. Defined handling modes are: |
|
0= no abort handling |
|
1= try to cancel, release, exit (leave signal mode as set by caller) |
|
2= try to ignore all signals |
|
3= mode 1 in normal operation, mode 2 during abort handling |
|
4= mode 1 in normal operation, mode 0 during abort handling |
|
-1= install abort handling 1 only in Cdrskin_burn() after burning started |
|
*/ |
|
int abort_handler; |
|
|
|
/** Wether to allow getuid()!=geteuid() */ |
|
int allow_setuid; |
|
|
|
/** Wether to allow user provided addresses like #4 */ |
|
int allow_fd_source; |
|
|
|
/** Wether an option is given which needs a full bus scan */ |
|
int no_whitelist; |
|
|
|
/** Wether the translated device address shall not follow softlinks, device |
|
clones and SCSI addresses */ |
|
int no_convert_fs_adr; |
|
|
|
/** Wether Bus,Target,Lun addresses shall be converted literally as old |
|
Pseudo SCSI-Adresses. New default is to use (possibly system emulated) |
|
real SCSI addresses via burn_drive_convert_scsi_adr() and literally |
|
emulated and cdrecord-incompatible ATA: addresses. */ |
|
int old_pseudo_scsi_adr; |
|
|
|
/** Wether bus scans shall exit!=0 if no drive was found */ |
|
int scan_demands_drive; |
|
|
|
/** Wether to abort when a busy drive is encountered during bus scan */ |
|
int abort_on_busy_drive; |
|
|
|
/** Wether to try to avoid collisions when opening drives */ |
|
int drive_exclusive; |
|
|
|
/** Wether to try to wait for unwilling drives to become willing to open */ |
|
int drive_blocking; |
|
|
|
/** Explicit write mode option is determined before skin processes |
|
any track arguments */ |
|
char write_mode_name[80]; |
|
|
|
#ifndef Cdrskin_extra_leaN |
|
|
|
/** List of startupfiles */ |
|
char rc_filenames[Cdrpreskin_rc_nuM][Cdrskin_strleN]; |
|
int rc_filename_count; |
|
|
|
/** Non-argument options from startupfiles */ |
|
int pre_argc; |
|
char **pre_argv; |
|
int *pre_argidx; |
|
int *pre_arglno; |
|
|
|
#endif /* ! Cdrskin_extra_leaN */ |
|
|
|
}; |
|
|
|
|
|
|
|
/** Create a preliminary cdrskin program run control object. It will become |
|
part of the final control object. |
|
@param preskin Returns pointer to resulting |
|
@param flag Bitfield for control purposes: unused yet |
|
@return <=0 error, 1 success |
|
*/ |
|
int Cdrpreskin_new(struct CdrpreskiN **preskin, int flag) |
|
{ |
|
struct CdrpreskiN *o; |
|
int i; |
|
|
|
(*preskin)= o= TSOB_FELD(struct CdrpreskiN,1); |
|
if(o==NULL) |
|
return(-1); |
|
|
|
o->verbosity= 0; |
|
strcpy(o->queue_severity,"NEVER"); |
|
strcpy(o->print_severity,"SORRY"); |
|
o->raw_device_adr[0]= 0; |
|
o->device_adr[0]= 0; |
|
o->adr_trn= NULL; |
|
o->abort_handler= 3; |
|
o->allow_setuid= 0; |
|
o->allow_fd_source= 0; |
|
o->no_whitelist= 0; |
|
o->no_convert_fs_adr= 0; |
|
#ifdef Cdrskin_libburn_has_convert_scsi_adR |
|
o->old_pseudo_scsi_adr= 0; |
|
#else |
|
o->old_pseudo_scsi_adr= 1; |
|
#endif |
|
o->scan_demands_drive= 0; |
|
o->abort_on_busy_drive= 0; |
|
o->drive_exclusive= 1; |
|
o->drive_blocking= 0; |
|
strcpy(o->write_mode_name,"DEFAULT"); |
|
|
|
#ifndef Cdrskin_extra_leaN |
|
o->rc_filename_count= Cdrpreskin_rc_nuM; |
|
for(i=0;i<o->rc_filename_count-1;i++) |
|
strcpy(o->rc_filenames[i],Cdrpreskin_sys_rc_nameS[i]); |
|
o->rc_filenames[o->rc_filename_count-1][0]= 0; |
|
o->pre_argc= 0; |
|
o->pre_argv= NULL; |
|
o->pre_argidx= NULL; |
|
o->pre_arglno= NULL; |
|
#endif /* ! Cdrskin_extra_leaN */ |
|
|
|
return(1); |
|
} |
|
|
|
|
|
int Cdrpreskin_destroy(struct CdrpreskiN **preskin, int flag) |
|
{ |
|
struct CdrpreskiN *o; |
|
|
|
o= *preskin; |
|
if(o==NULL) |
|
return(0); |
|
|
|
#ifndef Cdrskin_extra_leaN |
|
if((o->pre_arglno)!=NULL) |
|
free((char *) o->pre_arglno); |
|
if((o->pre_argidx)!=NULL) |
|
free((char *) o->pre_argidx); |
|
if(o->pre_argc>0 && o->pre_argv!=NULL) |
|
Sfile_destroy_argv(&(o->pre_argc),&(o->pre_argv),0); |
|
Cdradrtrn_destroy(&(o->adr_trn),0); |
|
#endif /* ! Cdrskin_extra_leaN */ |
|
|
|
free((char *) o); |
|
*preskin= NULL; |
|
return(1); |
|
} |
|
|
|
|
|
int Cdrpreskin_set_severities(struct CdrpreskiN *preskin, char *queue_severity, |
|
char *print_severity, int flag) |
|
{ |
|
/* |
|
if(preskin->verbosity>=Cdrskin_verbose_debuG) |
|
fprintf(stderr, |
|
"cdrskin: DEBUG : queue_severity='%s' print_severity='%s'\n", |
|
queue_severity,print_severity); |
|
*/ |
|
|
|
if(queue_severity!=NULL) |
|
strcpy(preskin->queue_severity,queue_severity); |
|
if(print_severity!=NULL) |
|
strcpy(preskin->print_severity,print_severity); |
|
#ifdef Cdrskin_libburn_has_burn_msgS |
|
burn_msgs_set_severities(preskin->queue_severity, preskin->print_severity, |
|
"cdrskin: "); |
|
#endif |
|
return(1); |
|
} |
|
|
|
|
|
int Cdrpreskin_initialize_lib(struct CdrpreskiN *preskin, int flag) |
|
{ |
|
int ret; |
|
|
|
ret= burn_initialize(); |
|
if(ret==0) { |
|
fprintf(stderr,"cdrskin : FATAL : Initialization of libburn failed\n"); |
|
return(0); |
|
} |
|
Cdrpreskin_set_severities(preskin,NULL,NULL,0); |
|
return(1); |
|
} |
|
|
|
|
|
/** Enable queuing of libburn messages or disable and print queue content. |
|
@param flag Bitfield for control purposes: |
|
bit0= enable queueing, else disable and print |
|
*/ |
|
int Cdrpreskin_queue_msgs(struct CdrpreskiN *o, int flag) |
|
{ |
|
#ifdef Cdrskin_libburn_has_burn_msgS |
|
#ifndef Cdrskin_extra_leaN |
|
#define Cdrskin_debug_libdax_msgS 1 |
|
#endif |
|
/* <<< In cdrskin there is not much sense in queueing library messages. |
|
It is done here only for debugging */ |
|
#ifdef Cdrskin_debug_libdax_msgS |
|
|
|
int ret; |
|
static char queue_severity[81]= {"NEVER"}, print_severity[81]= {"SORRY"}; |
|
static int queueing= 0; |
|
char msg[BURN_MSGS_MESSAGE_LEN],msg_severity[81],filler[81]; |
|
int error_code,os_errno,first,i; |
|
|
|
if(flag&1) { |
|
if(!queueing) { |
|
strcpy(queue_severity,o->queue_severity); |
|
strcpy(print_severity,o->print_severity); |
|
} |
|
if(o->verbosity>=Cdrskin_verbose_debuG) |
|
Cdrpreskin_set_severities(o,"DEBUG","NEVER",0); |
|
else |
|
Cdrpreskin_set_severities(o,"SORRY","NEVER",0); |
|
queueing= 1; |
|
return(1); |
|
} |
|
|
|
if(queueing) |
|
Cdrpreskin_set_severities(o,queue_severity,print_severity,0); |
|
queueing= 0; |
|
|
|
for(first= 1; ; first= 0) { |
|
ret= burn_msgs_obtain("ALL",&error_code,msg,&os_errno,msg_severity); |
|
if(ret==0) |
|
break; |
|
if(ret<0) { |
|
fprintf(stderr, |
|
"cdrskin: NOTE : Please inform libburn-hackers@pykix.org about:\n"); |
|
fprintf(stderr, |
|
"cdrskin: burn_msgs_obtain() returns %d\n",ret); |
|
break; |
|
} |
|
if(first) |
|
fprintf(stderr, |
|
"cdrskin: -------------------- Messages from Libburn ---------------------\n"); |
|
for(i=0;msg_severity[i]!=0;i++) |
|
filler[i]= ' '; |
|
filler[i]= 0; |
|
fprintf(stderr,"cdrskin: %s : %s\n",msg_severity,msg); |
|
if(strcmp(msg_severity,"DEBUG")!=0 && os_errno!=0) |
|
fprintf(stderr,"cdrskin: %s ( errno=%d '%s')\n", |
|
filler,os_errno,strerror(os_errno)); |
|
} |
|
if(first==0) |
|
fprintf(stderr, |
|
"cdrskin: ----------------------------------------------------------------\n"); |
|
|
|
#endif /* Cdrskin_debug_libdax_msgS */ |
|
#endif /* Cdrskin_libburn_has_burn_msgS */ |
|
|
|
return(1); |
|
} |
|
|
|
|
|
/** Convert a cdrecord-style device address into a libburn device address or |
|
into a libburn drive number. It depends on the "scsibus" number of the |
|
cdrecord-style address which kind of libburn address emerges: |
|
bus=0 : drive number , bus=1 : /dev/sgN , bus=2 : /dev/hdX |
|
(This call intentionally has no CdrpreskiN argument) |
|
@param flag Bitfield for control purposes: |
|
bit0= old_pseudo_scsi_adr |
|
@return 1 success, 0=no recognizable format, -1=severe error, |
|
-2 could not find scsi device, -3 address format error |
|
*/ |
|
int Cdrpreskin__cdrecord_to_dev(char *adr, char device_adr[Cdrskin_adrleN], |
|
int *driveno, int flag) |
|
{ |
|
int comma_seen= 0,digit_seen= 0,busno= 0,k,lun_no= -1; |
|
|
|
*driveno= -1; |
|
device_adr[0]= 0; |
|
if(strlen(adr)==0) |
|
return(0); |
|
|
|
/* read the trailing numeric string as device address code */ |
|
/* accepts "1" , "0,1,0" , "ATA:0,1,0" , ... */ |
|
for(k= strlen(adr)-1;k>=0;k--) { |
|
if(adr[k]==',' && !comma_seen) { |
|
sscanf(adr+k+1,"%d",&lun_no); |
|
comma_seen= 1; |
|
digit_seen= 0; |
|
continue; |
|
} |
|
if(adr[k]<'0' || adr[k]>'9') |
|
break; |
|
digit_seen= 1; |
|
} |
|
if(!digit_seen) { |
|
k= strlen(adr)-1; |
|
if(adr[k]==':' || (adr[k]>='A' && adr[k]<='Z')) { /* empty prefix ? */ |
|
*driveno= 0; |
|
return(1); |
|
} |
|
return(0); |
|
} |
|
sscanf(adr+k+1,"%d",driveno); |
|
|
|
digit_seen= 0; |
|
if(k>0) if(adr[k]==',') { |
|
for(k--;k>=0;k--) { |
|
if(adr[k]<'0' || adr[k]>'9') |
|
break; |
|
digit_seen= 1; |
|
} |
|
if(digit_seen) { |
|
sscanf(adr+k+1,"%d",&busno); |
|
if(flag&1) { |
|
/* look for symbolic bus : 1=/dev/sgN 2=/dev/hdX */ |
|
if(busno==1) { |
|
sprintf(device_adr,"/dev/sg%d",*driveno); |
|
} else if(busno==2) { |
|
sprintf(device_adr,"/dev/hd%c",'a'+(*driveno)); |
|
} else if(busno!=0) { |
|
fprintf(stderr, |
|
"cdrskin: FATAL : dev=[Prefix:]Bus,Target,Lun expects Bus out of {0,1,2}\n"); |
|
return(-3); |
|
} |
|
} else { |
|
if(busno<0) { |
|
fprintf(stderr, |
|
"cdrskin: FATAL : dev=[Prefix:]Bus,Target,Lun expects Bus number >= 0\n"); |
|
return(-3); |
|
} |
|
if(busno>=1000) { |
|
busno-= 1000; |
|
goto ata_bus; |
|
} else if((strncmp(adr,"ATA",3)==0 && (adr[3]==0 || adr[3]==':')) || |
|
(strncmp(adr,"ATAPI",5)==0 && (adr[5]==0 || adr[5]==':'))) { |
|
ata_bus:; |
|
if(busno>12 || (*driveno)<0 || (*driveno)>1) { |
|
fprintf(stderr, |
|
"cdrskin: FATAL : dev=ATA:Bus,Target,Lun expects Bus {0..12}, Target {0,1}\n"); |
|
return(-3); |
|
} |
|
sprintf(device_adr,"/dev/hd%c",'a'+(2*busno)+(*driveno)); |
|
|
|
#ifdef Cdrskin_libburn_has_convert_scsi_adR |
|
} else { |
|
int ret; |
|
|
|
ret= burn_drive_convert_scsi_adr(busno,-1,-1,*driveno,lun_no, |
|
device_adr); |
|
if(ret==0) { |
|
fprintf(stderr, |
|
"cdrskin: FATAL : Cannot find /dev/* with Bus,Target,Lun = %d,%d,%d\n", |
|
busno,*driveno,lun_no); |
|
fprintf(stderr, |
|
"cdrskin: HINT : This drive may be in use by another program currently\n"); |
|
return(-2); |
|
} else if(ret<0) |
|
return(-1); |
|
return(1); |
|
|
|
#endif /* Cdrskin_libburn_has_convert_scsi_adR */ |
|
} |
|
} |
|
} |
|
} |
|
return(1); |
|
} |
|
|
|
|
|
#ifndef Cdrskin_extra_leaN |
|
|
|
/** Load content startup files into preskin cache */ |
|
int Cdrpreskin_read_rc(struct CdrpreskiN *o, char *progname, int flag) |
|
{ |
|
int ret,i; |
|
char *filenames_v[3]; |
|
|
|
for(i=0;i<o->rc_filename_count;i++) |
|
filenames_v[i]= o->rc_filenames[i]; |
|
Sfile_home_adr_s(".cdrskinrc",o->rc_filenames[o->rc_filename_count-1], |
|
Cdrskin_strleN,0); |
|
ret= Sfile_multi_read_argv(progname,filenames_v,o->rc_filename_count, |
|
&(o->pre_argc),&(o->pre_argv), |
|
&(o->pre_argidx),&(o->pre_arglno),4); |
|
return(ret); |
|
} |
|
|
|
#endif /* ! Cdrskin_extra_leaN */ |
|
|
|
|
|
/** Interpret those arguments which do not need libburn or which influence the |
|
startup of libburn and/or the creation of the CdrskiN object. This is run |
|
before libburn gets initialized and before Cdrskin_new() is called. |
|
Options which need libburn or a CdrskiN object are processed in a different |
|
function named Cdrskin_setup(). |
|
@param flag Bitfield for control purposes: |
|
bit0= do not finalize setup |
|
bit1= do not read and interpret rc files |
|
@return <=0 error, 1 success , 2 end program run with exit value 0 |
|
*/ |
|
int Cdrpreskin_setup(struct CdrpreskiN *o, int argc, char **argv, int flag) |
|
/* |
|
return: |
|
<=0 error |
|
1 ok |
|
2 end program run (--help) |
|
*/ |
|
{ |
|
int i,ret; |
|
char *value_pt; |
|
|
|
#ifndef Cdrskin_extra_leaN |
|
if(argc>1) { |
|
if(strcmp(argv[1],"--no_rc")==0 || strcmp(argv[1],"-version")==0 || |
|
strcmp(argv[1],"--help")==0 || strcmp(argv[1],"-help")==0) |
|
flag|= 2; |
|
} |
|
if(!(flag&2)) { |
|
ret= Cdrpreskin_read_rc(o,argv[0],0); |
|
if(ret<0) |
|
return(-1); |
|
if(o->pre_argc>1) { |
|
ret= Cdrpreskin_setup(o,o->pre_argc,o->pre_argv,flag|1|2); |
|
if(ret<=0) |
|
return(ret); |
|
/* ??? abort on ret==2 ? */ |
|
} |
|
} |
|
#endif |
|
|
|
if(argc==1) { |
|
fprintf(stderr,"cdrskin: SORRY : no options given. Try option --help\n"); |
|
return(0); |
|
} |
|
for (i= 1;i<argc;i++) { |
|
|
|
if(strcmp(argv[i],"--abort_handler")==0) { |
|
o->abort_handler= 3; |
|
|
|
} else if(strcmp(argv[i],"--allow_setuid")==0) { |
|
o->allow_setuid= 1; |
|
|
|
} else if(strcmp(argv[i],"blank=help")==0 || |
|
strcmp(argv[i],"-blank=help")==0) { |
|
|
|
#ifndef Cdrskin_extra_leaN |
|
|
|
fprintf(stderr,"Blanking options:\n"); |
|
fprintf(stderr,"\tall\t\tblank the entire disk\n"); |
|
fprintf(stderr,"\tdisc\t\tblank the entire disk\n"); |
|
fprintf(stderr,"\tdisk\t\tblank the entire disk\n"); |
|
fprintf(stderr,"\tfast\t\tminimally blank the entire disk\n"); |
|
fprintf(stderr,"\tminimal\t\tminimally blank the entire disk\n"); |
|
fprintf(stderr, |
|
"\tformat_overwrite\tformat a DVD-RW to \"Restricted Overwrite\"\n"); |
|
fprintf(stderr, |
|
"\tformat_overwrite_quickest\tto \"Restricted Overwrite intermediate\"\n"); |
|
fprintf(stderr, |
|
"\tformat_overwrite_full\tfull-size format a DVD-RW or DVD+RW\n"); |
|
fprintf(stderr, |
|
"\tdeformat_sequential\tfully blank, even formatted DVD-RW\n"); |
|
fprintf(stderr, |
|
"\tdeformat_sequential_quickest\tminimally blank, even DVD-RW\n"); |
|
|
|
#else /* ! Cdrskin_extra_leaN */ |
|
|
|
goto see_cdrskin_eng_html; |
|
|
|
#endif /* ! Cdrskin_extra_leaN */ |
|
|
|
if(argc==2) |
|
{ret= 2; goto final_checks;} |
|
|
|
} else if(strcmp(argv[i],"--bragg_with_audio")==0) { |
|
/* OBSOLETE 0.2.3 */; |
|
|
|
} else if(strcmp(argv[i],"--demand_a_drive")==0) { |
|
o->scan_demands_drive= 1; |
|
|
|
} else if(strcmp(argv[i],"--devices")==0) { |
|
printf("Note: If this hangs for a while then there is a drive with\n"); |
|
printf(" unexpected problems (e.g. ill DMA).\n"); |
|
printf(" One may exclude such a device file by removing r- and w-\n"); |
|
printf(" permissions for all cdrskin users.\n"); |
|
o->no_whitelist= 1; |
|
|
|
} else if(strncmp(argv[i],"dev_translation=",16)==0) { |
|
|
|
#ifndef Cdrskin_extra_leaN |
|
|
|
if(o->adr_trn==NULL) { |
|
ret= Cdradrtrn_new(&(o->adr_trn),0); |
|
if(ret<=0) |
|
goto no_adr_trn_mem; |
|
} |
|
if(argv[i][16]==0) { |
|
fprintf(stderr, |
|
"cdrskin: FATAL : dev_translation= : missing separator character\n"); |
|
return(0); |
|
} |
|
ret= Cdradrtrn_add(o->adr_trn,argv[i]+17,argv[i]+16,1); |
|
if(ret==-2) { |
|
no_adr_trn_mem:; |
|
fprintf(stderr, |
|
"cdrskin: FATAL : address_translation= : cannot allocate memory\n"); |
|
} else if(ret==-1) |
|
fprintf(stderr, |
|
"cdrskin: FATAL : address_translation= : table full (%d items)\n", |
|
Cdradrtrn_leN); |
|
else if(ret==0) |
|
fprintf(stderr, |
|
"cdrskin: FATAL : address_translation= : no address separator '%c' found\n", |
|
argv[i][17]); |
|
if(ret<=0) |
|
return(0); |
|
|
|
#else /* ! Cdrskin_extra_leaN */ |
|
|
|
fprintf(stderr, |
|
"cdrskin: FATAL : dev_translation= is not available in lean version\n"); |
|
return(0); |
|
|
|
#endif /* Cdrskin_extra_leaN */ |
|
|
|
|
|
} else if(strncmp(argv[i],"-dev=",5)==0) { |
|
value_pt= argv[i]+5; |
|
goto set_dev; |
|
} else if(strncmp(argv[i],"dev=",4)==0) { |
|
value_pt= argv[i]+4; |
|
set_dev:; |
|
if(strcmp(value_pt,"help")==0) { |
|
|
|
#ifndef Cdrskin_extra_leaN |
|
|
|
printf("Supported SCSI transports for this platform:\n"); |
|
fflush(stdout); |
|
if(o->old_pseudo_scsi_adr) { |
|
fprintf(stderr,"\nTransport name:\t\tlibburn OLD_PSEUDO\n"); |
|
fprintf(stderr, |
|
"Transport descr.:\tBus0=DriveNum , Bus1=/dev/sgN , Bus2=/dev/hdX\n"); |
|
} else { |
|
fprintf(stderr,"\nTransport name:\t\tlibburn SCSI\n"); |
|
fprintf(stderr, |
|
"Transport descr.:\tSCSI Bus,Id,Lun as of operating system\n"); |
|
} |
|
fprintf(stderr,"Transp. layer ind.:\t\n"); |
|
fprintf(stderr,"Target specifier:\tbus,target,lun\n"); |
|
fprintf(stderr,"Target example:\t\t1,2,0\n"); |
|
fprintf(stderr,"SCSI Bus scanning:\tsupported\n"); |
|
fprintf(stderr,"Open via UNIX device:\tsupported\n"); |
|
if(!o->old_pseudo_scsi_adr) { |
|
fprintf(stderr,"\nTransport name:\t\tlibburn HD\n"); |
|
fprintf(stderr, |
|
"Transport descr.:\tLinux specific alias for /dev/hdX\n"); |
|
fprintf(stderr,"Transp. layer ind.:\tATA:\n"); |
|
fprintf(stderr,"Target specifier:\tbus,target,lun\n"); |
|
fprintf(stderr,"Target example:\t\tATA:1,0,0\n"); |
|
fprintf(stderr,"SCSI Bus scanning:\tsupported\n"); |
|
fprintf(stderr,"Open via UNIX device:\tsupported\n"); |
|
} |
|
|
|
#else /* ! Cdrskin_extra_leaN */ |
|
|
|
goto see_cdrskin_eng_html; |
|
|
|
#endif /* Cdrskin_extra_leaN */ |
|
|
|
{ret= 2; goto final_checks;} |
|
} |
|
if(strlen(value_pt)>=sizeof(o->raw_device_adr)) |
|
goto dev_too_long; |
|
strcpy(o->raw_device_adr,value_pt); |
|
|
|
} else if(strcmp(argv[i],"--drive_abort_on_busy")==0) { |
|
o->abort_on_busy_drive= 1; |
|
|
|
} else if(strcmp(argv[i],"--drive_blocking")==0) { |
|
o->drive_blocking= 1; |
|
|
|
} else if(strcmp(argv[i],"--drive_not_exclusive")==0) { |
|
o->drive_exclusive= 0; |
|
|
|
} else if(strcmp(argv[i],"--drive_scsi_exclusive")==0) { |
|
o->drive_exclusive= 2; |
|
|
|
} else if(strcmp(argv[i],"driveropts=help")==0 || |
|
strcmp(argv[i],"-driveropts=help")==0) { |
|
|
|
#ifndef Cdrskin_extra_leaN |
|
|
|
fprintf(stderr,"Driver options:\n"); |
|
fprintf(stderr,"burnfree\tPrepare writer to use BURN-Free technology\n"); |
|
fprintf(stderr,"noburnfree\tDisable using BURN-Free technology\n"); |
|
|
|
#else /* ! Cdrskin_extra_leaN */ |
|
|
|
goto see_cdrskin_eng_html; |
|
|
|
#endif /* Cdrskin_extra_leaN */ |
|
|
|
if(argc==2 || (i==2 && argc==3 && strncmp(argv[1],"dev=",4)==0)) |
|
{ret= 2; goto final_checks;} |
|
|
|
} else if(strcmp(argv[i],"--help")==0) { |
|
|
|
#ifndef Cdrskin_extra_leaN |
|
|
|
printf("\n"); |
|
printf("Usage: %s [options|source_addresses]\n", argv[0]); |
|
printf("Burns preformatted data to CD-R, CD-RW or DVD+RW via libburn.\n"); |
|
printf("For the cdrecord compatible options which control the work of\n"); |
|
printf( |
|
"blanking and burning see output of option -help rather than --help.\n"); |
|
printf("Non-cdrecord options:\n"); |
|
printf(" --abort_handler do not leave the drive in busy state\n"); |
|
printf( |
|
" --allow_setuid disable setuid warning (setuid is insecure !)\n"); |
|
printf( |
|
" --any_track allow source_addresses to match '^-.' or '='\n"); |
|
printf(" --demand_a_drive exit !=0 on bus scans with empty result\n"); |
|
printf(" --devices list accessible devices (tells /dev/...)\n"); |
|
printf( |
|
" dev_translation=<sep><from><sep><to> set input address alias\n"); |
|
printf(" e.g.: dev_translation=+ATA:1,0,0+/dev/sg1\n"); |
|
printf(" --drive_abort_on_busy abort process if busy drive is found\n"); |
|
printf(" (might be triggered by a busy hard disk)\n"); |
|
printf(" --drive_blocking try to wait for busy drive to become free\n"); |
|
printf(" (might be stalled by a busy hard disk)\n"); |
|
printf(" --drive_not_exclusive do not ask kernel to prevent opening\n"); |
|
printf(" busy drives. Effect is kernel dependend.\n"); |
|
printf( |
|
" --drive_scsi_exclusive try to exclusively reserve device files\n"); |
|
printf(" /dev/srN, /dev/scdM, /dev/stK of drive.\n"); |
|
#ifdef Cdrskin_burn_drive_eject_brokeN |
|
printf( |
|
" eject_device=<path> set the device address for command eject\n"); |
|
#endif |
|
printf(" --fifo_disable disable fifo despite any fs=...\n"); |
|
printf(" --fifo_per_track use a separate fifo for each track\n"); |
|
printf( |
|
" fifo_start_at=<number> do not wait for full fifo but start burning\n"); |
|
printf( |
|
" as soon as the given number of bytes is read\n"); |
|
printf( |
|
" grab_drive_and_wait=<num> grab drive, wait given number of\n"); |
|
printf( |
|
" seconds, release drive, and do normal work\n"); |
|
printf( |
|
" --ignore_signals try to ignore any signals rather than to abort\n"); |
|
printf(" --list_ignored_options list all ignored cdrecord options.\n"); |
|
printf(" --no_abort_handler exit even if the drive is in busy state\n"); |
|
printf(" --no_blank_appendable refuse to blank appendable CD-RW\n"); |
|
printf(" --no_convert_fs_adr only literal translations of dev=\n"); |
|
printf( |
|
" --no_rc as first argument: do not read startup files\n"); |
|
printf(" --old_pseudo_scsi_adr use and report literal Bus,Target,Lun\n"); |
|
printf(" rather than real SCSI and pseudo ATA.\n"); |
|
printf(" --prodvd_cli_compatible react on some DVD types more like\n"); |
|
printf(" cdrecord-ProDVD with blank= and -multi\n"); |
|
printf( |
|
" --single_track accept only last argument as source_address\n"); |
|
|
|
#ifdef Cdrskin_allow_libburn_taO |
|
printf( |
|
" tao_to_sao_tsize=<num> use num as fixed track size if in a\n"); |
|
printf( |
|
" non-TAO mode track data are read from \"-\"\n"); |
|
printf( |
|
" and no tsize= was specified.\n"); |
|
#else |
|
printf( |
|
" tao_to_sao_tsize=<num> substitute -tao by -sao and eventually\n"); |
|
printf(" augment input from \"-\" by tsize=<num>\n"); |
|
printf(" (set tao_to_sao_tsize=0 to disable it)\n"); |
|
#endif |
|
|
|
printf( |
|
" write_start_address=<num> write to given byte address (DVD+RW)\n"); |
|
printf( |
|
"Preconfigured arguments are read from the following startup files\n"); |
|
printf( |
|
"if they exist and are readable. The sequence is as listed here:\n"); |
|
printf(" /etc/default/cdrskin /etc/opt/cdrskin/rc\n"); |
|
printf(" /etc/cdrskin/cdrskin.conf $HOME/.cdrskinrc\n"); |
|
printf("Each file line is a single argument. No whitespace.\n"); |
|
printf( |
|
"By default any argument that does not match grep '^-.' or '=' is\n"); |
|
printf( |
|
"used as track source. If it is \"-\" then stdin is used.\n"); |
|
printf("cdrskin : http://scdbackup.sourceforge.net/cdrskin_eng.html\n"); |
|
printf(" mailto:scdbackup@gmx.net (Thomas Schmitt)\n"); |
|
printf("libburn : http://libburnia.pykix.org\n"); |
|
printf("cdrecord : ftp://ftp.berlios.de/pub/cdrecord/\n"); |
|
printf("My respect to the authors of cdrecord and libburn.\n"); |
|
printf("scdbackup: http://scdbackup.sourceforge.net/main_eng.html\n"); |
|
printf("\n"); |
|
|
|
#else /* ! Cdrskin_extra_leaN */ |
|
|
|
see_cdrskin_eng_html:; |
|
printf("This is a capability reduced lean version without help texts.\n"); |
|
printf("See http://scdbackup.sourceforge.net/cdrskin_eng.html\n"); |
|
|
|
#endif /* Cdrskin_extra_leaN */ |
|
|
|
|
|
{ret= 2; goto final_checks;} |
|
} else if(strcmp(argv[i],"-help")==0) { |
|
|
|
#ifndef Cdrskin_extra_leaN |
|
|
|
fprintf(stderr,"Usage: %s [options|source_addresses]\n",argv[0]); |
|
fprintf(stderr,"Note: This is not cdrecord. See cdrskin start message on stdout. See --help.\n"); |
|
fprintf(stderr,"Options:\n"); |
|
fprintf(stderr,"\t-version\tprint version information and exit\n"); |
|
fprintf(stderr, |
|
"\tdev=target\tpseudo-SCSI target to use as CD-Recorder\n"); |
|
fprintf(stderr, |
|
"\tgracetime=#\tset the grace time before starting to write to #.\n"); |
|
fprintf(stderr,"\t-v\t\tincrement verbose level by one\n"); |
|
fprintf(stderr, |
|
"\tdriveropts=opt\topt= one of {burnfree,noburnfree,help}\n"); |
|
fprintf(stderr, |
|
"\t-checkdrive\tcheck if a driver for the drive is present\n"); |
|
fprintf(stderr,"\t-scanbus\tscan the SCSI bus and exit\n"); |
|
fprintf(stderr,"\tspeed=#\t\tset speed of drive\n"); |
|
fprintf(stderr,"\tblank=type\tblank a CD-RW disc (see blank=help)\n"); |
|
fprintf(stderr, |
|
"\tfs=#\t\tSet fifo size to # (0 to disable, default is 4 MB)\n"); |
|
fprintf(stderr, |
|
"\t-eject\t\teject the disk after doing the work (might be ignored)\n"); |
|
fprintf(stderr,"\t-dummy\t\tdo everything with laser turned off\n"); |
|
#ifdef Cdrskin_libburn_has_multI |
|
fprintf(stderr, |
|
"\t-msinfo\t\tretrieve multi-session info for mkisofs >= 1.10\n"); |
|
fprintf(stderr,"\tmsifile=path\trun -msinfo and copy output to file\n"); |
|
#endif |
|
fprintf(stderr,"\t-toc\t\tretrieve and print TOC/PMA data\n"); |
|
fprintf(stderr, |
|
"\t-atip\t\tretrieve media state, print \"Is *erasable\"\n"); |
|
#ifdef Cdrskin_libburn_has_multI |
|
fprintf(stderr, |
|
"\t-multi\t\tgenerate a TOC that allows multi session\n"); |
|
#endif |
|
fprintf(stderr, |
|
"\t-force\t\tforce to continue on some errors to allow blanking\n"); |
|
#ifdef Cdrskin_allow_libburn_taO |
|
fprintf(stderr,"\t-tao\t\tWrite disk in TAO mode.\n"); |
|
#endif |
|
fprintf(stderr,"\t-dao\t\tWrite disk in SAO mode.\n"); |
|
fprintf(stderr,"\t-sao\t\tWrite disk in SAO mode.\n"); |
|
fprintf(stderr,"\t-raw96r\t\tWrite disk in RAW/RAW96R mode\n"); |
|
fprintf(stderr,"\ttsize=#\t\tannounces exact size of source data\n"); |
|
fprintf(stderr,"\tpadsize=#\tAmount of padding\n"); |
|
fprintf(stderr,"\t-audio\t\tSubsequent tracks are CD-DA audio tracks\n"); |
|
fprintf(stderr, |
|
"\t-data\t\tSubsequent tracks are CD-ROM data mode 1 (default)\n"); |
|
fprintf(stderr,"\t-pad\t\tpadsize=30k\n"); |
|
fprintf(stderr, |
|
"\t-nopad\t\tDo not pad (default, but applies only to data tracks)\n"); |
|
fprintf(stderr, |
|
"\t-swab\t\tAudio data source is byte-swapped (little-endian/Intel)\n"); |
|
fprintf(stderr,"\t-help\t\tprint this text to stderr and exit\n"); |
|
fprintf(stderr, |
|
"Without option -data, .wav and .au files are extracted and burned as -audio.\n"); |
|
fprintf(stderr, |
|
"By default any argument that does not match grep '^-.' or '=' is used\n"); |
|
fprintf(stderr, |
|
"as track source address. Address \"-\" means stdin.\n"); |
|
fprintf(stderr, |
|
"cdrskin will ensure that an announced tsize= is written even if\n"); |
|
fprintf(stderr, |
|
"the source delivers fewer bytes. But 0 bytes from stdin with fifo\n"); |
|
fprintf(stderr, |
|
"enabled will lead to abort and no burn attempt at all.\n"); |
|
|
|
#else /* ! Cdrskin_extra_leaN */ |
|
|
|
fprintf(stderr,"Note: This is not cdrecord. See cdrskin start message on stdout.\n"); |
|
fprintf(stderr, |
|
"(writer profile: -atip retrieve, blank=type, -eject after work)\n"); |
|
goto see_cdrskin_eng_html; |
|
|
|
#endif /* Cdrskin_extra_leaN */ |
|
|
|
{ret= 2; goto final_checks;} |
|
|
|
} else if(strcmp(argv[i],"--ignore_signals")==0) { |
|
o->abort_handler= 2; |
|
|
|
} else if(strcmp(argv[i],"--no_abort_handler")==0) { |
|
o->abort_handler= 0; |
|
|
|
} else if(strcmp(argv[i],"--no_convert_fs_adr")==0) { |
|
o->no_convert_fs_adr= 1; |
|
|
|
} else if(strcmp(argv[i],"--old_pseudo_scsi_adr")==0) { |
|
o->old_pseudo_scsi_adr= 1; |
|
|
|
} else if(strcmp(argv[i],"--no_rc")==0) { |
|
if(i!=1) |
|
fprintf(stderr, |
|
"cdrskin: NOTE : option --no_rc would only work as first argument.\n"); |
|
|
|
} else if(strcmp(argv[i],"-raw96r")==0) { |
|
strcpy(o->write_mode_name,"RAW/RAW96R"); |
|
|
|
} else if(strcmp(argv[i],"-sao")==0 || strcmp(argv[i],"-dao")==0) { |
|
strcpy(o->write_mode_name,"SAO"); |
|
|
|
} else if(strcmp(argv[i],"-scanbus")==0) { |
|
o->no_whitelist= 1; |
|
|
|
} else if(strcmp(argv[i],"-tao")==0) { |
|
strcpy(o->write_mode_name,"TAO"); |
|
|
|
} else if(strcmp(argv[i],"-v")==0 || strcmp(argv[i],"-verbose")==0) { |
|
(o->verbosity)++; |
|
printf("cdrskin: verbosity level : %d\n",o->verbosity); |
|
set_severities:; |
|
if(o->verbosity>=Cdrskin_verbose_debuG) |
|
Cdrpreskin_set_severities(o,"NEVER","DEBUG",0); |
|
} else if(strcmp(argv[i],"-vv")==0 || strcmp(argv[i],"-vvv")==0 || |
|
strcmp(argv[i],"-vvvv")==0) { |
|
(o->verbosity)+= strlen(argv[i])-1; |
|
goto set_severities; |
|
|
|
} else if(strcmp(argv[i],"-version")==0) { |
|
printf( |
|
"Cdrecord 2.01-Emulation Copyright (C) 2006-2007, see libburnia.pykix.org\n"); |
|
printf("libburn version : %s\n",Cdrskin_libburn_versioN); |
|
|
|
#ifndef Cdrskin_extra_leaN |
|
printf("cdrskin version : %s\n",Cdrskin_prog_versioN); |
|
#else |
|
printf("cdrskin version : %s.lean (capability reduced lean version)\n", |
|
Cdrskin_prog_versioN); |
|
#endif |
|
|
|
printf("Version timestamp : %s\n",Cdrskin_timestamP); |
|
printf("Build timestamp : %s\n",Cdrskin_build_timestamP); |
|
{ret= 2; goto final_checks;} |
|
} |
|
|
|
} |
|
ret= 1; |
|
final_checks:; |
|
if(flag&1) |
|
goto ex; |
|
|
|
if(strlen(o->raw_device_adr)>0 && !o->no_whitelist) { |
|
int driveno,hret; |
|
char *adr,buf[Cdrskin_adrleN]; |
|
|
|
adr= o->raw_device_adr; |
|
|
|
#ifndef Cdrskin_extra_leaN |
|
if(o->adr_trn!=NULL) { |
|
hret= Cdradrtrn_translate(o->adr_trn,adr,-1,buf,0); |
|
if(hret<=0) { |
|
fprintf(stderr, |
|
"cdrskin: FATAL : address translation failed (address too long ?) \n"); |
|
{ret= 0; goto ex;} |
|
} |
|
adr= buf; |
|
} |
|
#endif /* ! Cdrskin_extra_leaN */ |
|
|
|
if(adr[0]=='/') { |
|
if(strlen(adr)>=sizeof(o->device_adr)) { |
|
dev_too_long:; |
|
fprintf(stderr, |
|
"cdrskin: FATAL : dev=... too long (max. %d characters)\n", |
|
sizeof(o->device_adr)-1); |
|
{ret= 0; goto ex;} |
|
} |
|
strcpy(o->device_adr,adr); |
|
} else { |
|
ret= Cdrpreskin__cdrecord_to_dev(adr,o->device_adr,&driveno, |
|
!!o->old_pseudo_scsi_adr); |
|
if(ret==-2 || ret==-3) |
|
{ret= 0; goto ex;} |
|
if(ret<0) |
|
goto ex; |
|
if(ret==0) { |
|
strcpy(o->device_adr,adr); |
|
ret= 1; |
|
} |
|
} |
|
|
|
#ifdef Cdrskin_libburn_has_convert_fs_adR |
|
|
|
if(strlen(o->device_adr)>0 && !o->no_convert_fs_adr) { |
|
int lret; |
|
char link_adr[Cdrskin_strleN+1]; |
|
|
|
strcpy(link_adr,o->device_adr); |
|
lret = burn_drive_convert_fs_adr(link_adr,o->device_adr); |
|
if(lret<0) { |
|
fprintf(stderr, |
|
"cdrskin: NOTE : Please inform libburn-hackers@pykix.org about:\n"); |
|
fprintf(stderr, |
|
"cdrskin: burn_drive_convert_fs_adr() returned %d\n",lret); |
|
} |
|
} |
|
|
|
#endif /* Cdrskin_libburn_has_convert_fs_adR */ |
|
|
|
} |
|
|
|
/* A60927 : note to myself : no "ret= 1;" here. It breaks --help , -version */ |
|
|
|
ex:; |
|
|
|
#ifndef Cdrskin_extra_leaN |
|
if(ret<=0 || !(flag&1)) |
|
Cdradrtrn_destroy(&(o->adr_trn),0); |
|
#endif |
|
|
|
return(ret); |
|
} |
|
|
|
|
|
/* --------------------------------------------------------------------- */ |
|
|
|
|
|
|
|
/** The maximum number of tracks */ |
|
#define Cdrskin_track_maX 99 |
|
|
|
|
|
/** Work around the fact that libburn leaves the track input fds open |
|
after the track is done. This can hide a few overflow bytes buffered |
|
by the fifo-to-libburn pipe which would cause a broken-pipe error |
|
if libburn would close that outlet. |
|
This macro enables a coarse workaround which tries to read bytes from |
|
the track inlets after burning has ended. Probably not a good idea if |
|
libburn would close the inlet fds. |
|
*/ |
|
#define Cdrskin_libburn_leaves_inlet_opeN 1 |
|
|
|
|
|
/** List of furter wishes towards libburn: |
|
- write mode which does not demand a track size in advance |
|
- obtain minimum drive speed (for cdrskin -atip) |
|
- obtain MMC profile of inserted media (for cdrskin -v -atip) |
|
- a possibility to implement cdrskin -multi |
|
- a possibilty to implement cdrskin -reset |
|
*/ |
|
|
|
|
|
/** Limit to prevent int rollovers within libburn as long as not everything is |
|
changed to 64 bit off_t : 2 GB minus 800 MB for eventual computations. */ |
|
#define Cdrskin_tracksize_maX 1308622848 |
|
|
|
|
|
/* Some constants obtained by hearsay and experiments */ |
|
|
|
/** The CD payload speed factor for reporting progress: 1x = 150 kB/s */ |
|
static double Cdrskin_cd_speed_factoR= 150.0*1024.0; |
|
/** The DVD payload speed factor for reporting progress: 1x */ |
|
static double Cdrskin_dvd_speed_factoR= 1385000; |
|
|
|
/** The effective payload speed factor for reporting progress */ |
|
static double Cdrskin_speed_factoR= 150.0*1024.0; |
|
|
|
/** The speed conversion factors consumer x-speed to libburn speed as used with |
|
burn_drive_set_speed() burn_drive_get_write_speed() |
|
*/ |
|
static double Cdrskin_libburn_cd_speed_factoR= 176.4; |
|
static double Cdrskin_libburn_dvd_speed_factoR= 1385.0; |
|
|
|
/* The effective speed conversion factor for burn_drive_set_speed() */ |
|
static double Cdrskin_libburn_speed_factoR= 176.4; |
|
|
|
/** Add-on for burn_drive_set_speed() to accomodate to the slightley oversized |
|
speed ideas of my LG DVDRAM GSA-4082B. LITE-ON LTR-48125S tolerates it. |
|
*/ |
|
static double Cdrskin_libburn_cd_speed_addoN= 40.0; |
|
static double Cdrskin_libburn_dvd_speed_addoN= 1.0; /*poor accuracy with 2.4x*/ |
|
static double Cdrskin_libburn_speed_addoN = 40.0; |
|
|
|
|
|
/** The program run control object. Defaults: see Cdrskin_new(). */ |
|
struct CdrskiN { |
|
|
|
/** Settings already interpreted by Cdrpreskin_setup */ |
|
struct CdrpreskiN *preskin; |
|
|
|
/** Job: what to do, plus some parameters. */ |
|
int verbosity; |
|
double x_speed; |
|
int gracetime; |
|
int dummy_mode; |
|
int force_is_set; |
|
int single_track; |
|
int prodvd_cli_compatible; |
|
|
|
int do_devices; |
|
|
|
int do_scanbus; |
|
|
|
int do_checkdrive; |
|
|
|
int do_msinfo; |
|
int msinfo_fd; |
|
char msifile[Cdrskin_strleN]; |
|
|
|
int do_atip; |
|
|
|
int do_blank; |
|
int blank_fast; |
|
int no_blank_appendable; |
|
int blank_format_type; /* 0=blank |
|
bit0-7: |
|
1=format_overwrite |
|
bit8-15: bit0-7 of burn_disc_format(flag) |
|
bit8 = write zeros after formatting |
|
bit9 = insist in size 0 |
|
bit10= format to maximum available size |
|
2=deformat_sequential (blank_fast might matter) |
|
*/ |
|
double blank_format_size; /* to be used with burn_disc_format() */ |
|
< |