9730 lines
308 KiB
C
9730 lines
308 KiB
C
|
|
/*
|
|
cdrskin.c , Copyright 2006-2015 Thomas Schmitt <scdbackup@gmx.net>
|
|
Provided under GPL version 2 or later.
|
|
|
|
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, DVD, or BD, 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.
|
|
|
|
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-project.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 fork 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-project.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='...' \
|
|
-D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE=1 \
|
|
-o cdrskin cdrskin.c cdrfifo.c cleanup.c \
|
|
-L../libburn/.libs -lburn -lpthread
|
|
|
|
or
|
|
|
|
cd ..
|
|
cc -g -I. -DCdrskin_build_timestamP='...' \
|
|
-D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE=1 \
|
|
-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/read.o \
|
|
libburn/libdax_audioxtr.o libburn/libdax_msgs.o \
|
|
-lpthread
|
|
|
|
*/
|
|
|
|
|
|
/** The official program version */
|
|
#ifndef Cdrskin_prog_versioN
|
|
#define Cdrskin_prog_versioN "1.4.1"
|
|
#endif
|
|
|
|
/** The official libburn interface revision to use.
|
|
(May get changed further below)
|
|
*/
|
|
#ifndef Cdrskin_libburn_majoR
|
|
#define Cdrskin_libburn_majoR 1
|
|
#endif
|
|
#ifndef Cdrskin_libburn_minoR
|
|
#define Cdrskin_libburn_minoR 4
|
|
#endif
|
|
#ifndef Cdrskin_libburn_micrO
|
|
#define Cdrskin_libburn_micrO 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
|
|
|
|
#ifdef Cdrskin_libburn_1_4_0
|
|
#define Cdrskin_libburn_versioN "1.4.0"
|
|
#endif
|
|
|
|
#ifdef Cdrskin_libburn_1_4_1
|
|
#define Cdrskin_libburn_versioN "1.4.1"
|
|
#endif
|
|
|
|
#ifndef Cdrskin_libburn_versioN
|
|
#define Cdrskin_libburn_1_4_0
|
|
#define Cdrskin_libburn_versioN "1.4.0"
|
|
#endif
|
|
|
|
#ifdef Cdrskin_libburn_1_4_0
|
|
#undef Cdrskin_libburn_majoR
|
|
#undef Cdrskin_libburn_minoR
|
|
#undef Cdrskin_libburn_micrO
|
|
#define Cdrskin_libburn_majoR 1
|
|
#define Cdrskin_libburn_minoR 4
|
|
#define Cdrskin_libburn_micrO 0
|
|
#endif
|
|
#ifdef Cdrskin_libburn_1_4_1
|
|
#undef Cdrskin_libburn_majoR
|
|
#undef Cdrskin_libburn_minoR
|
|
#undef Cdrskin_libburn_micrO
|
|
#define Cdrskin_libburn_majoR 1
|
|
#define Cdrskin_libburn_minoR 4
|
|
#define Cdrskin_libburn_micrO 1
|
|
#endif
|
|
|
|
|
|
/* History of development macros.
|
|
As of version 1.1.8 they are now unconditional, thus removing the option
|
|
to compile a heavily restricted cdrskin for the old libburn at icculus.org.
|
|
*/
|
|
|
|
/* 0.2.2 */
|
|
/* Cdrskin_libburn_does_ejecT */
|
|
/* Cdrskin_libburn_has_drive_get_adR */
|
|
/* Cdrskin_progress_track_does_worK */
|
|
/* Cdrskin_is_erasable_on_load_does_worK */
|
|
/* Cdrskin_grab_abort_does_worK */
|
|
|
|
/* 0.2.4 */
|
|
/* Cdrskin_allow_libburn_taO */
|
|
/* Cdrskin_libburn_has_is_enumerablE */
|
|
/* Cdrskin_libburn_has_convert_fs_adR */
|
|
/* Cdrskin_libburn_has_convert_scsi_adR */
|
|
/* Cdrskin_libburn_has_burn_msgS */
|
|
/* Cdrskin_libburn_has_burn_aborT */
|
|
/* Cdrskin_libburn_has_cleanup_handleR */
|
|
/* Cdrskin_libburn_has_audioxtR */
|
|
/* Cdrskin_libburn_has_get_start_end_lbA */
|
|
/* Cdrskin_libburn_has_burn_disc_unsuitablE */
|
|
/* Cdrskin_libburn_has_read_atiP */
|
|
/* Cdrskin_libburn_has_buffer_progresS */
|
|
|
|
/* 0.2.6 */
|
|
/* Cdrskin_libburn_has_pretend_fulL */
|
|
/* Cdrskin_libburn_has_multI */
|
|
/* Cdrskin_libburn_has_buffer_min_filL */
|
|
|
|
/* 0.3.0 */
|
|
/* Cdrskin_atip_speed_is_oK */
|
|
/* Cdrskin_libburn_has_get_profilE */
|
|
/* Cdrskin_libburn_has_set_start_bytE */
|
|
/* Cdrskin_libburn_has_wrote_welL */
|
|
/* Cdrskin_libburn_has_bd_formattinG */
|
|
/* Cdrskin_libburn_has_burn_disc_formaT */
|
|
|
|
/* 0.3.2 */
|
|
/* Cdrskin_libburn_has_get_msc1 */
|
|
/* Cdrskin_libburn_has_toc_entry_extensionS */
|
|
/* Cdrskin_libburn_has_get_multi_capS */
|
|
|
|
/* 0.3.4 */
|
|
/* Cdrskin_libburn_has_set_filluP */
|
|
/* Cdrskin_libburn_has_get_spacE */
|
|
/* Cdrskin_libburn_write_mode_ruleS */
|
|
/* Cdrskin_libburn_has_allow_untested_profileS */
|
|
/* Cdrskin_libburn_has_set_forcE */
|
|
|
|
/* 0.3.6 */
|
|
/* Cdrskin_libburn_preset_device_familY */
|
|
/* Cdrskin_libburn_has_track_set_sizE */
|
|
|
|
/* 0.3.8 */
|
|
/* Cdrskin_libburn_has_set_waitinG */
|
|
/* Cdrskin_libburn_has_get_best_speeD */
|
|
|
|
/* 0.4.0 */
|
|
/* Cdrskin_libburn_has_random_access_rW */
|
|
/* Cdrskin_libburn_has_get_drive_rolE */
|
|
/* Cdrskin_libburn_has_drive_equals_adR */
|
|
|
|
/* 0.4.2 */
|
|
/* no novel libburn features but rather organizational changes */
|
|
|
|
/* 0.4.4 */
|
|
/* novel libburn features are transparent to cdrskin */
|
|
|
|
/* 0.4.6 */
|
|
/* Cdrskin_libburn_has_stream_recordinG */
|
|
|
|
/* 0.4.8 */
|
|
/* Bug fix release for write_start_address=... on DVD-RAM and BD-RE */
|
|
|
|
/* 0.5.0 , 0.5.2 , 0.5.4 , 0.5.6 , 0.5.8 , 0.6.0 , 0.6.2 */
|
|
/* novel libburn features are transparent to cdrskin */
|
|
|
|
/* 0.6.4 */
|
|
/* Ended to mark novelties by macros.
|
|
libburnia libburn and cdrskin are fixely in sync now.
|
|
icculus libburn did not move for 30 months.
|
|
*/
|
|
|
|
/* 1.1.8 */
|
|
/* The code which got enabled by novelty macros was made unconditional.
|
|
*/
|
|
|
|
|
|
#ifdef Cdrskin_new_api_tesT
|
|
|
|
/* put macros under test caveat here */
|
|
|
|
|
|
#endif /* Cdrskin_new_api_tesT */
|
|
|
|
|
|
/** ts A90901
|
|
The raw write modes of libburn depend in part on code borrowed from cdrdao.
|
|
Since this code is not understood by the current developers and since CDs
|
|
written with cdrskin -raw96r seem unreadable anyway, -raw96r is given up
|
|
for now.
|
|
*/
|
|
#define Cdrskin_disable_raw96R 1
|
|
|
|
|
|
/** A macro which is able to eat up a function call like printf() */
|
|
#ifdef Cdrskin_extra_leaN
|
|
#define ClN(x)
|
|
#define Cdrskin_no_cdrfifO 1
|
|
#else
|
|
#define ClN(x) x
|
|
#ifdef Cdrskin_use_libburn_fifO
|
|
/*
|
|
# define Cdrskin_no_cdrfifO 1
|
|
*/
|
|
#endif
|
|
#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 <ctype.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"
|
|
|
|
|
|
#define Cleanup_set_handlers burn_set_signal_handling
|
|
#define Cleanup_app_handler_T burn_abort_handler_t
|
|
|
|
/*
|
|
# define Cdrskin_use_libburn_cleanuP 1
|
|
*/
|
|
/* May not use libburn cleanup with cdrskin fifo */
|
|
#ifndef Cdrskin_use_libburn_fifO
|
|
#ifdef Cdrskin_use_libburn_cleanuP
|
|
#undef Cdrskin_use_libburn_cleanuP
|
|
#endif
|
|
#endif
|
|
|
|
#ifdef Cdrskin_use_libburn_cleanuP
|
|
#define Cleanup_handler_funC NULL
|
|
#define Cleanup_handler_handlE "cdrskin: "
|
|
#define Cleanup_handler_flaG 48
|
|
#else
|
|
#define Cleanup_handler_funC (Cleanup_app_handler_T) Cdrskin_abort_handler
|
|
#define Cleanup_handler_handlE skin
|
|
#define Cleanup_handler_flaG 4
|
|
#endif /* ! Cdrskin_use_libburn_cleanuP */
|
|
|
|
/* 0= no abort going on, -1= Cdrskin_abort_handler was called
|
|
*/
|
|
static int Cdrskin_abort_leveL= 0;
|
|
|
|
|
|
/** The size of a string buffer for pathnames and similar texts */
|
|
#define Cdrskin_strleN 4096
|
|
|
|
/** The maximum length +1 of a drive address */
|
|
#define Cdrskin_adrleN BURN_DRIVE_ADR_LEN
|
|
|
|
|
|
/** If tsize= sets a value smaller than media capacity divided by this
|
|
number then there will be a warning and gracetime set at least to 15 */
|
|
#define Cdrskin_minimum_tsize_quotienT 2048.0
|
|
|
|
|
|
/* --------------------------------------------------------------------- */
|
|
|
|
/* 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 *) calloc(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));
|
|
}
|
|
|
|
|
|
/** 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);
|
|
}
|
|
|
|
#ifndef Cdrskin_extra_leaN
|
|
|
|
|
|
/** 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 *) calloc(1, 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;}
|
|
}
|
|
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 *) calloc(1, 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 **) calloc(argcount, sizeof(char *));
|
|
*argidx= (int *) calloc(argcount, sizeof(int));
|
|
*arglno= (int *) calloc(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((int) (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 */
|
|
|
|
|
|
/* -------------------------- other misc functions ----------------------- */
|
|
|
|
|
|
/* Learned from reading growisofs.c ,
|
|
watching mkisofs, and viewing its results via od -c */
|
|
/* @return 0=no size found , 1=*size_in_bytes is valid */
|
|
int Scan_for_iso_size(unsigned char data[2048], double *size_in_bytes,
|
|
int flag)
|
|
{
|
|
double sectors= 0.0;
|
|
|
|
if(data[0]!=1)
|
|
return(0);
|
|
if(strncmp((char *) (data+1),"CD001",5)!=0)
|
|
return(0);
|
|
sectors= data[80] | (data[81]<<8) | (data[82]<<16) | (data[83]<<24);
|
|
*size_in_bytes= sectors*2048.0;
|
|
return(1);
|
|
}
|
|
|
|
|
|
int Set_descr_iso_size(unsigned char data[2048], double size_in_bytes,
|
|
int flag)
|
|
{
|
|
unsigned int sectors, i;
|
|
|
|
sectors= size_in_bytes/2048.0;
|
|
if(size_in_bytes>((double) sectors) * 2048.0)
|
|
sectors++;
|
|
for(i=0;i<4;i++)
|
|
data[87-i]= data[80+i]= (sectors >> (8*i)) & 0xff;
|
|
return(1);
|
|
}
|
|
|
|
|
|
int Wait_for_input(int fd, int microsec, int flag)
|
|
{
|
|
struct timeval wt;
|
|
fd_set rds,wts,exs;
|
|
int ready;
|
|
|
|
FD_ZERO(&rds);
|
|
FD_ZERO(&wts);
|
|
FD_ZERO(&exs);
|
|
FD_SET(fd,&rds);
|
|
FD_SET(fd,&exs);
|
|
wt.tv_sec= microsec/1000000;
|
|
wt.tv_usec= microsec%1000000;
|
|
ready= select(fd+1,&rds,&wts,&exs,&wt);
|
|
if(ready<=0)
|
|
return(0);
|
|
if(FD_ISSET(fd,&exs))
|
|
return(-1);
|
|
if(FD_ISSET(fd,&rds))
|
|
return(1);
|
|
return(0);
|
|
}
|
|
|
|
|
|
/* --------------------------------------------------------------------- */
|
|
|
|
/** 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]= calloc(1, strlen(from_pt)+1);
|
|
trn->to_address[cnt]= calloc(1, 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_no_cdrfifO
|
|
|
|
/* Program is to be linked with cdrfifo.c */
|
|
#include "cdrfifo.h"
|
|
|
|
#else /* ! Cdrskin_no_cdrfifO */
|
|
|
|
/* Dummy */
|
|
|
|
struct CdrfifO {
|
|
int dummy;
|
|
};
|
|
|
|
#endif /* Cdrskin_no_cdrfifO */
|
|
|
|
|
|
/* --------------------------------------------------------------------- */
|
|
|
|
/** 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];
|
|
char original_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;
|
|
int mode_modifiers;
|
|
double sector_size;
|
|
int track_type_by_default;
|
|
int swap_audio_bytes;
|
|
int cdxa_conversion; /* bit0-30: for burn_track_set_cdxa_conv()
|
|
bit31 : ignore bits 0 to 30
|
|
*/
|
|
char isrc[13];
|
|
|
|
char *index_string;
|
|
|
|
int sao_pregap;
|
|
int sao_postgap;
|
|
|
|
/** Eventually detected data image size */
|
|
double data_image_size;
|
|
char *iso_fs_descr; /* eventually block 16 to 31 of input during detection */
|
|
/** Whether to demand a detected data image size and use it (or else abort) */
|
|
int use_data_image_size; /* 0=no, 1=size not defined yet, 2=size defined */
|
|
|
|
/* Whether 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;
|
|
|
|
/** 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 libburn_track_is_own;
|
|
|
|
#ifdef Cdrskin_use_libburn_fifO
|
|
struct burn_source *libburn_fifo;
|
|
#endif /* Cdrskin_use_libburn_fifO */
|
|
|
|
};
|
|
|
|
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= do not set parameters by Cdrskin_get_source()
|
|
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,fifo_start_at;
|
|
int Cdrskin_get_source(struct CdrskiN *skin, char *source_path,
|
|
double *fixed_size, double *tao_to_sao_tsize,
|
|
int *use_data_image_size,
|
|
double *padding, int *set_by_padsize,
|
|
int *track_type, int *track_type_by_default,
|
|
int *mode_mods, int *swap_audio_bytes,
|
|
int *cdxa_conversion, 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->original_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= 1;
|
|
o->track_type= BURN_MODE1;
|
|
o->mode_modifiers= 0;
|
|
o->sector_size= 2048.0;
|
|
o->track_type_by_default= 1;
|
|
o->swap_audio_bytes= 0;
|
|
o->cdxa_conversion= 0;
|
|
o->isrc[0]= 0;
|
|
o->index_string= NULL;
|
|
o->sao_pregap= -1;
|
|
o->sao_postgap= -1;
|
|
o->data_image_size= -1.0;
|
|
o->iso_fs_descr= NULL;
|
|
o->use_data_image_size= 0;
|
|
o->extracting_container= 0;
|
|
o->fifo_enabled= 0;
|
|
o->fifo= NULL;
|
|
o->fifo_outlet_fd= -1;
|
|
o->fifo_size= 0;
|
|
o->ff_fifo= NULL;
|
|
o->ff_idx= -1;
|
|
o->libburn_track= NULL;
|
|
o->libburn_track_is_own= 0;
|
|
#ifdef Cdrskin_use_libburn_fifO
|
|
o->libburn_fifo= NULL;
|
|
#endif /* Cdrskin_use_libburn_fifO */
|
|
|
|
if(flag & 1)
|
|
return(1);
|
|
|
|
ret= Cdrskin_get_source(boss,o->source_path,&(o->fixed_size),
|
|
&(o->tao_to_sao_tsize),&(o->use_data_image_size),
|
|
&(o->padding),&(o->set_by_padsize),&(skin_track_type),
|
|
&(o->track_type_by_default), &(o->mode_modifiers),
|
|
&(o->swap_audio_bytes), &(o->cdxa_conversion), 0);
|
|
if(ret<=0)
|
|
goto failed;
|
|
strcpy(o->original_source_path,o->source_path);
|
|
if(o->fixed_size>0.0)
|
|
o->extracting_container= 1;
|
|
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),
|
|
&fifo_start_at,0);
|
|
if(ret<=0)
|
|
goto failed;
|
|
#endif /* ! Cdrskin_extra_leaN */
|
|
|
|
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_no_cdrfifO
|
|
Cdrfifo_destroy(&(track->fifo),0);
|
|
#endif
|
|
|
|
if(track->libburn_track != NULL && track->libburn_track_is_own)
|
|
burn_track_free(track->libburn_track);
|
|
if(track->iso_fs_descr!=NULL)
|
|
free((char *) track->iso_fs_descr);
|
|
if(track->index_string != NULL)
|
|
free(track->index_string);
|
|
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 *use_data_image_size, int flag)
|
|
{
|
|
off_t readcounter= 0,writecounter= 0;
|
|
|
|
*size= track->fixed_size;
|
|
*padding= track->padding;
|
|
*use_data_image_size= track->use_data_image_size;
|
|
if((flag&1) && track->libburn_track!=NULL) {
|
|
burn_track_get_counters(track->libburn_track,&readcounter,&writecounter);
|
|
*size= readcounter;
|
|
*padding= writecounter-readcounter;
|
|
} else if(flag&2)
|
|
*padding= track->tao_to_sao_tsize;
|
|
*sector_size= track->sector_size;
|
|
return(1);
|
|
}
|
|
|
|
|
|
int Cdrtrack_get_iso_fs_descr(struct CdrtracK *track,
|
|
char **descr, double *size, int flag)
|
|
{
|
|
*descr= track->iso_fs_descr;
|
|
*size= track->data_image_size;
|
|
return(*descr != NULL && *size > 0.0);
|
|
}
|
|
|
|
|
|
int Cdrtrack_get_source_path(struct CdrtracK *track,
|
|
char **source_path, int *source_fd, int *is_from_stdin, int flag)
|
|
{
|
|
*source_path= track->original_source_path;
|
|
*source_fd= track->source_fd;
|
|
*is_from_stdin= track->is_from_stdin;
|
|
return(1);
|
|
}
|
|
|
|
|
|
#ifdef Cdrskin_use_libburn_fifO
|
|
|
|
int Cdrtrack_get_libburn_fifo(struct CdrtracK *track,
|
|
struct burn_source **fifo, int flag)
|
|
{
|
|
*fifo= track->libburn_fifo;
|
|
return(1);
|
|
}
|
|
|
|
|
|
int Cdrtrack_report_fifo(struct CdrtracK *track, int flag)
|
|
{
|
|
int size, free_bytes, ret;
|
|
int total_min_fill, interval_min_fill, put_counter, get_counter;
|
|
int empty_counter, full_counter;
|
|
double fifo_percent;
|
|
char *status_text;
|
|
|
|
if(track->libburn_fifo == NULL)
|
|
return(0);
|
|
|
|
/* Check for open input or leftover bytes in liburn fifo */
|
|
ret = burn_fifo_inquire_status(track->libburn_fifo, &size, &free_bytes,
|
|
&status_text);
|
|
if(ret >= 0 && size - free_bytes > 1) {
|
|
/* not clear why free_bytes is reduced by 1 */
|
|
fprintf(stderr,
|
|
"cdrskin: FATAL : Fifo still contains data after burning has ended.\n");
|
|
fprintf(stderr,
|
|
"cdrskin: FATAL : %d bytes left.\n", size - free_bytes - 1);
|
|
fprintf(stderr,
|
|
"cdrskin: FATAL : This indicates an overflow of the last track.\n");
|
|
fprintf(stderr,
|
|
"cdrskin: NOTE : The media might appear ok but is probably truncated.\n");
|
|
return(-1);
|
|
}
|
|
|
|
burn_fifo_get_statistics(track->libburn_fifo, &total_min_fill,
|
|
&interval_min_fill, &put_counter, &get_counter,
|
|
&empty_counter, &full_counter);
|
|
fifo_percent= 100.0*((double) total_min_fill)/(double) size;
|
|
if(fifo_percent==0 && total_min_fill>0)
|
|
fifo_percent= 1;
|
|
fflush(stdout);
|
|
fprintf(stderr,"Cdrskin: fifo had %d puts and %d gets.\n",
|
|
put_counter,get_counter);
|
|
fprintf(stderr,
|
|
"Cdrskin: fifo was %d times empty and %d times full, min fill was %.f%%.\n",
|
|
empty_counter, full_counter, fifo_percent);
|
|
return(1);
|
|
}
|
|
|
|
#endif /* Cdrskin_use_libburn_fifO */
|
|
|
|
|
|
int Cdrtrack_get_fifo(struct CdrtracK *track, struct CdrfifO **fifo, int flag)
|
|
{
|
|
*fifo= track->fifo;
|
|
return(1);
|
|
}
|
|
|
|
|
|
/** Try whether 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;
|
|
struct libdax_audioxtr *xtr= NULL;
|
|
char *fmt,*fmt_info;
|
|
int num_channels,sample_rate,bits_per_sample,msb_first,ret;
|
|
|
|
*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);
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
|
|
/* @param flag bit0=set *size_used as the detected data image size
|
|
*/
|
|
int Cdrtrack_activate_image_size(struct CdrtracK *track, double *size_used,
|
|
int flag)
|
|
{
|
|
if(flag&1)
|
|
track->data_image_size= *size_used;
|
|
else
|
|
*size_used= track->data_image_size;
|
|
if(track->use_data_image_size!=1)
|
|
return(2);
|
|
if(*size_used<=0)
|
|
return(0);
|
|
track->fixed_size= *size_used;
|
|
track->use_data_image_size= 2;
|
|
if(track->libburn_track!=NULL)
|
|
burn_track_set_size(track->libburn_track, (off_t) *size_used);
|
|
/* man cdrecord prescribes automatic -pad with -isosize.
|
|
cdrskin obeys only if the current padding is less than that. */
|
|
if(track->padding<15*2048) {
|
|
track->padding= 15*2048;
|
|
track->set_by_padsize= 0;
|
|
}
|
|
track->extracting_container= 1;
|
|
|
|
#ifndef Cdrskin_no_cdrfifO
|
|
if(track->ff_fifo!=NULL)
|
|
Cdrfifo_set_fd_in_limit(track->ff_fifo,track->fixed_size,track->ff_idx,0);
|
|
#endif
|
|
|
|
return(1);
|
|
}
|
|
|
|
|
|
int Cdrtrack_seek_isosize(struct CdrtracK *track, int fd, int flag)
|
|
{
|
|
struct stat stbuf;
|
|
char secbuf[2048];
|
|
int ret,got,i;
|
|
double size;
|
|
|
|
if(fstat(fd,&stbuf)==-1)
|
|
return(0);
|
|
if((stbuf.st_mode&S_IFMT)!=S_IFREG && (stbuf.st_mode&S_IFMT)!=S_IFBLK)
|
|
return(2);
|
|
|
|
if(track->iso_fs_descr!=NULL)
|
|
free((char *) track->iso_fs_descr);
|
|
track->iso_fs_descr= TSOB_FELD(char,16*2048);
|
|
if(track->iso_fs_descr==NULL)
|
|
return(-1);
|
|
for(i=0;i<32 && track->data_image_size<=0;i++) {
|
|
for(got= 0; got<2048;got+= ret) {
|
|
ret= read(fd, secbuf+got, 2048-got);
|
|
if(ret<=0)
|
|
return(0);
|
|
}
|
|
if(i<16)
|
|
continue;
|
|
memcpy(track->iso_fs_descr+(i-16)*2048,secbuf,2048);
|
|
if(i>16)
|
|
continue;
|
|
ret= Scan_for_iso_size((unsigned char *) secbuf, &size, 0);
|
|
if(ret<=0)
|
|
break;
|
|
track->data_image_size= size;
|
|
if(track->use_data_image_size) {
|
|
Cdrtrack_activate_image_size(track,&size,1);
|
|
track->fixed_size= size;
|
|
track->use_data_image_size= 2;
|
|
}
|
|
}
|
|
ret= lseek(fd, (off_t) 0, SEEK_SET);
|
|
if(ret!=0) {
|
|
fprintf(stderr,
|
|
"cdrskin: FATAL : Cannot lseek() to 0 after -isosize determination\n");
|
|
if(errno!=0)
|
|
fprintf(stderr, "cdrskin: errno=%d : %s\n", errno, strerror(errno));
|
|
return(-1);
|
|
}
|
|
return(track->data_image_size>0);
|
|
}
|
|
|
|
|
|
/** Deliver an open file descriptor corresponding to the source path of track.
|
|
@param flag Bitfield for control purposes:
|
|
bit0=debugging verbosity
|
|
bit1=open as source for direct write:
|
|
no audio extract, no minimum track size
|
|
bit2=permission to use burn_os_open_track_src() (evtl O_DIRECT)
|
|
@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, ret;
|
|
off_t xtr_size= 0;
|
|
struct stat stbuf;
|
|
char *device_adr,*raw_adr;
|
|
int no_convert_fs_adr;
|
|
int Cdrskin_get_device_adr(struct CdrskiN *skin,
|
|
char **device_adr, char **raw_adr, int *no_convert_fs_adr,int flag);
|
|
int Cdrskin_get_drive(struct CdrskiN *skin, struct burn_drive **drive,
|
|
int flag);
|
|
struct burn_drive *drive;
|
|
|
|
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;
|
|
|
|
ret= Cdrskin_get_device_adr(track->boss,&device_adr,&raw_adr,
|
|
&no_convert_fs_adr,0);
|
|
if(ret <= 0) {
|
|
fprintf(stderr,
|
|
"cdrskin: FATAL : No drive found. Cannot prepare track.\n");
|
|
return(0);
|
|
}
|
|
/*
|
|
fprintf(stderr,
|
|
"cdrskin: DEBUG : device_adr='%s' , raw_adr='%s' , ncfs=%d\n",
|
|
device_adr, raw_adr, no_convert_fs_adr);
|
|
*/
|
|
if(!no_convert_fs_adr) {
|
|
if(flag&1)
|
|
ClN(fprintf(stderr,
|
|
"cdrskin_debug: checking track source for identity with drive\n"));
|
|
|
|
ret= Cdrskin_get_drive(track->boss,&drive,0);
|
|
if(ret<=0) {
|
|
fprintf(stderr,
|
|
"cdrskin: FATAL : Program error. Cannot determine libburn drive.\n");
|
|
return(0);
|
|
}
|
|
if(burn_drive_equals_adr(drive,track->source_path,2)>0) {
|
|
fprintf(stderr,
|
|
"cdrskin: FATAL : track source address leads to burner drive\n");
|
|
fprintf(stderr,
|
|
"cdrskin: : dev='%s' -> '%s' <- track source '%s'\n",
|
|
raw_adr, device_adr, track->source_path);
|
|
return(0);
|
|
}
|
|
}
|
|
/*
|
|
fprintf(stderr,"cdrskin: EXPERIMENTAL : Deliberate abort\n");
|
|
return(0);
|
|
*/
|
|
|
|
if(!(flag&2))
|
|
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) {
|
|
if(track->track_type != BURN_MODE1 ||
|
|
(track->cdxa_conversion & 0x7fffffff))
|
|
flag&= ~4; /* Better avoid O_DIRECT with odd sectors */
|
|
if(flag & 4)
|
|
*fd= burn_os_open_track_src(track->source_path, O_RDONLY, 0);
|
|
else
|
|
*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->use_data_image_size==1 && xtr_size<=0) {
|
|
ret= Cdrtrack_seek_isosize(track,*fd,0);
|
|
if(ret==-1)
|
|
return(-1);
|
|
} else if(track->fixed_size<=0) {
|
|
|
|
/* >>> ??? is it intentional that tsize overrides .wav header ? */
|
|
if(xtr_size>0) {
|
|
|
|
track->fixed_size= xtr_size;
|
|
if(track->use_data_image_size==1)
|
|
track->use_data_image_size= 2; /* count this as image size found */
|
|
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 */
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if(track->fixed_size < Cdrtrack_minimum_sizE * track->sector_size
|
|
&& (track->fixed_size>0 || size_from_file) && !(flag&2)) {
|
|
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_no_cdrfifO
|
|
|
|
/** 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,
|
|
(flag&1) | (4 * (track->fifo_size >= 256 * 1024)));
|
|
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, flag & 1);
|
|
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);
|
|
}
|
|
|
|
#endif /* ! Cdrskin_no_cdrfifO */
|
|
|
|
|
|
#ifndef Cdrskin_extra_leaN
|
|
|
|
#ifdef Cdrskin_use_libburn_fifO
|
|
|
|
/** Read data into the eventual libburn fifo until either fifo_start_at bytes
|
|
are read (-1 = no limit), it is full or or the data source is exhausted.
|
|
@return <=0 error, 1 success
|
|
*/
|
|
int Cdrtrack_fill_libburn_fifo(struct CdrtracK *track, int fifo_start_at,
|
|
int flag)
|
|
{
|
|
int ret, bs= 32 * 1024;
|
|
int buffer_size, buffer_free;
|
|
double data_image_size;
|
|
char buf[64 * 1024], *buffer_text;
|
|
|
|
if(fifo_start_at == 0)
|
|
return(2);
|
|
if(track->libburn_fifo == NULL)
|
|
return(2);
|
|
|
|
if(fifo_start_at>0 && fifo_start_at<track->fifo_size)
|
|
printf(
|
|
"cdrskin: NOTE : Input buffer will be initially filled up to %d bytes\n",
|
|
fifo_start_at);
|
|
printf("Waiting for reader process to fill input buffer ... ");
|
|
fflush(stdout);
|
|
ret= burn_fifo_fill(track->libburn_fifo, fifo_start_at,
|
|
(fifo_start_at == -1));
|
|
if(ret < 0)
|
|
return(0);
|
|
|
|
/** Ticket 55: check fifos for input, throw error on 0-bytes from stdin
|
|
@return <=0 abort run, 1 go on with burning
|
|
*/
|
|
ret= burn_fifo_inquire_status(track->libburn_fifo, &buffer_size,
|
|
&buffer_free, &buffer_text);
|
|
if(track->is_from_stdin) {
|
|
if(ret<0 || buffer_size <= buffer_free) {
|
|
fprintf(stderr,"\ncdrskin: FATAL : (First track) fifo did not read a single byte from stdin\n");
|
|
return(0);
|
|
}
|
|
}
|
|
|
|
/* Try to obtain ISO 9660 Volume Descriptors and size from fifo.
|
|
Not an error if there is no ISO 9660. */
|
|
if(track->iso_fs_descr != NULL)
|
|
free(track->iso_fs_descr);
|
|
track->iso_fs_descr = NULL;
|
|
if(buffer_size - buffer_free >= 64 * 1024) {
|
|
ret= burn_fifo_peek_data(track->libburn_fifo, buf, 64 * 1024, 0);
|
|
if(ret == 1) {
|
|
track->iso_fs_descr = calloc(1, bs);
|
|
if(track->iso_fs_descr == NULL)
|
|
return(-1);
|
|
memcpy(track->iso_fs_descr, buf + bs, bs);
|
|
ret= Scan_for_iso_size((unsigned char *) buf + bs, &data_image_size, 0);
|
|
if(ret > 0)
|
|
track->data_image_size= data_image_size;
|
|
}
|
|
}
|
|
return(1);
|
|
}
|
|
|
|
#endif /* Cdrskin_use_libburn_fifO */
|
|
|
|
|
|
#ifdef Cdrskin_no_cdrfifO
|
|
|
|
int Cdrtrack_fill_fifo(struct CdrtracK *track, int fifo_start_at, int flag)
|
|
{
|
|
return(Cdrtrack_fill_libburn_fifo(track, fifo_start_at, 0));
|
|
}
|
|
|
|
#else /* Cdrskin_no_cdrfifO */
|
|
|
|
int Cdrtrack_fill_fifo(struct CdrtracK *track, int fifo_start_at, int flag)
|
|
{
|
|
int ret,buffer_fill,buffer_space;
|
|
double data_image_size;
|
|
|
|
if(fifo_start_at==0)
|
|
return(2);
|
|
if(track->fifo==NULL) {
|
|
#ifdef Cdrskin_use_libburn_fifO
|
|
ret= Cdrtrack_fill_libburn_fifo(track, fifo_start_at, 0);
|
|
return(ret);
|
|
#else
|
|
return(2);
|
|
#endif
|
|
}
|
|
if(fifo_start_at>0 && fifo_start_at<track->fifo_size)
|
|
printf(
|
|
"cdrskin: NOTE : Input buffer will be initially filled up to %d bytes\n",
|
|
fifo_start_at);
|
|
printf("Waiting for reader process to fill input buffer ... ");
|
|
fflush(stdout);
|
|
ret= Cdrfifo_fill(track->fifo,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);
|
|
}
|
|
}
|
|
ret= Cdrfifo_get_iso_fs_size(track->fifo,&data_image_size,0);
|
|
if(ret>0)
|
|
track->data_image_size= data_image_size;
|
|
if(track->iso_fs_descr!=NULL)
|
|
free((char *) track->iso_fs_descr);
|
|
Cdrfifo_adopt_iso_fs_descr(track->fifo,&(track->iso_fs_descr),0);
|
|
return(1);
|
|
}
|
|
|
|
#endif /* ! Cdrskin_no_cdrfifO */
|
|
#endif /* ! Cdrskin_extra_leaN */
|
|
|
|
|
|
int Cdrtrack_set_indice(struct CdrtracK *track, int flag)
|
|
{
|
|
int idx= 1, adr, prev_adr= -1, ret;
|
|
char *cpt, *ept;
|
|
|
|
if(track->sao_pregap >= 0) {
|
|
ret= burn_track_set_pregap_size(track->libburn_track, track->sao_pregap, 0);
|
|
if(ret <= 0)
|
|
return(ret);
|
|
}
|
|
if(track->sao_postgap >= 0) {
|
|
ret= burn_track_set_postgap_size(track->libburn_track, track->sao_postgap,
|
|
0);
|
|
if(ret <= 0)
|
|
return(ret);
|
|
}
|
|
|
|
if(track->index_string == NULL)
|
|
return(2);
|
|
|
|
for(ept= cpt= track->index_string; ept != NULL; cpt= ept + 1) {
|
|
ept= strchr(cpt, ',');
|
|
if(ept != NULL)
|
|
*ept= 0;
|
|
adr= -1;
|
|
sscanf(cpt, "%d", &adr);
|
|
if(adr < 0) {
|
|
fprintf(stderr, "cdrskin: SORRY : Bad address number with index=\n");
|
|
return(0);
|
|
}
|
|
if(idx == 1 && adr != 0) {
|
|
fprintf(stderr,
|
|
"cdrskin: SORRY : First address number of index= is not 0\n");
|
|
return(0);
|
|
}
|
|
if(idx > 1 && adr < prev_adr) {
|
|
fprintf(stderr,
|
|
"cdrskin: SORRY : Backward address number with index=\n");
|
|
return(0);
|
|
}
|
|
ret= burn_track_set_index(track->libburn_track, idx, adr, 0);
|
|
if(ret <= 0)
|
|
return(ret);
|
|
prev_adr= adr;
|
|
if(ept != NULL)
|
|
*ept= ',';
|
|
idx++;
|
|
}
|
|
|
|
return(1);
|
|
}
|
|
|
|
/** 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)
|
|
bit2= permission to use O_DIRECT (if enabled at compile time)
|
|
*/
|
|
{
|
|
struct burn_track *tr;
|
|
struct burn_source *src= NULL, *fd_src= NULL;
|
|
double padding,lib_padding;
|
|
int ret,sector_pad_up;
|
|
double fixed_size;
|
|
int source_fd;
|
|
|
|
track->trackno= trackno;
|
|
tr= burn_track_create();
|
|
if(tr == NULL)
|
|
{ret= -1; goto ex;}
|
|
track->libburn_track= tr;
|
|
track->libburn_track_is_own= 1;
|
|
|
|
/* Note: track->track_type may get set in here */
|
|
if(track->source_fd==-1) {
|
|
ret= Cdrtrack_open_source_path(track, &source_fd, flag & (4 | 1));
|
|
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 | track->mode_modifiers);
|
|
burn_track_set_default_size(tr, (off_t) track->tao_to_sao_tsize);
|
|
burn_track_set_byte_swap(tr,
|
|
(track->track_type==BURN_AUDIO && track->swap_audio_bytes));
|
|
if(!(track->cdxa_conversion & (1 << 31)))
|
|
burn_track_set_cdxa_conv(tr, track->cdxa_conversion & 0x7fffffff);
|
|
|
|
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);
|
|
|
|
#ifdef Cdrskin_use_libburn_fifO
|
|
|
|
if(src != NULL && track->fifo == NULL) {
|
|
int fifo_enabled, fifo_size, fifo_start_at, chunksize, chunks;
|
|
int Cdrskin_get_fifo_par(struct CdrskiN *skin, int *fifo_enabled,
|
|
int *fifo_size, int *fifo_start_at, int flag);
|
|
|
|
Cdrskin_get_fifo_par(track->boss, &fifo_enabled, &fifo_size, &fifo_start_at,
|
|
0);
|
|
|
|
if(track->track_type == BURN_AUDIO)
|
|
chunksize= 2352;
|
|
else if (track->cdxa_conversion == 1)
|
|
chunksize= 2056;
|
|
else
|
|
chunksize= 2048;
|
|
chunks= fifo_size / chunksize;
|
|
if(chunks > 1 && fifo_enabled) {
|
|
fd_src= src;
|
|
src= burn_fifo_source_new(fd_src, chunksize, chunks,
|
|
(chunksize * chunks >= 128 * 1024));
|
|
if((flag & 1) || src == NULL)
|
|
fprintf(stderr, "cdrskin_DEBUG: %s libburn fifo of %d bytes\n",
|
|
src != NULL ? "installed" : "failed to install",
|
|
chunksize * chunks);
|
|
track->libburn_fifo= src;
|
|
if(src == NULL) {
|
|
src= fd_src;
|
|
fd_src= NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
#endif /* Cdrskin_use_libburn_fifO */
|
|
|
|
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;}
|
|
}
|
|
ret= Cdrtrack_set_indice(track, 0);
|
|
if(ret <= 0)
|
|
goto ex;
|
|
|
|
burn_session_add_track(session,tr,BURN_POS_END);
|
|
ret= 1;
|
|
ex:
|
|
if(fd_src!=NULL)
|
|
burn_source_free(fd_src);
|
|
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);
|
|
if(track->libburn_track_is_own)
|
|
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_get_sectors(struct CdrtracK *track, int flag)
|
|
{
|
|
return(burn_track_get_sectors(track->libburn_track));
|
|
}
|
|
|
|
|
|
#ifndef Cdrskin_no_cdrfifO
|
|
|
|
/** 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)
|
|
{
|
|
int ready,ret;
|
|
char buf[2];
|
|
|
|
if(track->fifo_outlet_fd<=0)
|
|
return(0);
|
|
ready= Wait_for_input(track->fifo_outlet_fd, 0, 0);
|
|
if(ready<=0)
|
|
return(0);
|
|
ret= read(track->fifo_outlet_fd,buf,1);
|
|
if(ret>0)
|
|
return(1);
|
|
return(0);
|
|
}
|
|
|
|
#endif /* ! Cdrskin_no_cdrfifO */
|
|
|
|
|
|
/* --------------------------------------------------------------------- */
|
|
|
|
/** 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];
|
|
|
|
/** Whether to wait for available standard input data before touching drives*/
|
|
int do_waiti;
|
|
|
|
/** 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;
|
|
|
|
/** Whether to allow getuid()!=geteuid() */
|
|
int allow_setuid;
|
|
|
|
/** Whether to allow user provided addresses like #4 */
|
|
int allow_fd_source;
|
|
|
|
/** Whether to support media types which are implemented but yet untested */
|
|
int allow_untested_media;
|
|
|
|
/** Whether to allow libburn pseudo-drives "stdio:<path>" .
|
|
0=forbidden, 1=seems ok,
|
|
2=potentially forbidden (depends on uid, euid, file type)
|
|
*/
|
|
int allow_emulated_drives;
|
|
|
|
/** Whether an option is given which needs a full bus scan */
|
|
int no_whitelist;
|
|
|
|
/** Whether the translated device address shall not follow softlinks, device
|
|
clones and SCSI addresses */
|
|
int no_convert_fs_adr;
|
|
|
|
/** Whether 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;
|
|
|
|
/** Whether to omit bus scanning */
|
|
int do_not_scan;
|
|
|
|
/** Whether bus scans shall exit!=0 if no drive was found */
|
|
int scan_demands_drive;
|
|
|
|
/** Whether to abort when a busy drive is encountered during bus scan */
|
|
int abort_on_busy_drive;
|
|
|
|
/** Linux specific : Whether to try to avoid collisions when opening drives */
|
|
int drive_exclusive;
|
|
|
|
/** Linux specific : Whether to obtain an exclusive drive lock via fcntl() */
|
|
int drive_fcntl_f_setlk;
|
|
|
|
/** Linux specific : Device file address family to use :
|
|
0=default , 1=sr , 2=scd , 4=sg */
|
|
int drive_scsi_dev_family;
|
|
|
|
|
|
/** Whether 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 */
|
|
|
|
/* The eventual name of a program to be executed if demands_cdrecord_caps
|
|
is >0 and demands_cdrskin_caps is <=0
|
|
*/
|
|
char fallback_program[Cdrskin_strleN];
|
|
int demands_cdrecord_caps;
|
|
int demands_cdrskin_caps;
|
|
|
|
int result_fd;
|
|
|
|
};
|
|
|
|
|
|
/** 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->do_waiti= 0;
|
|
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->allow_untested_media= 0;
|
|
o->allow_emulated_drives= 0;
|
|
o->no_whitelist= 0;
|
|
o->no_convert_fs_adr= 0;
|
|
o->old_pseudo_scsi_adr= 0;
|
|
o->do_not_scan= 0;
|
|
o->scan_demands_drive= 0;
|
|
o->abort_on_busy_drive= 0;
|
|
o->drive_exclusive= 1;
|
|
o->drive_fcntl_f_setlk= 1;
|
|
o->drive_scsi_dev_family= 0;
|
|
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 */
|
|
|
|
o->fallback_program[0]= 0;
|
|
o->demands_cdrecord_caps= 0;
|
|
o->demands_cdrskin_caps= 0;
|
|
o->result_fd = -1;
|
|
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(queue_severity!=NULL)
|
|
strcpy(preskin->queue_severity,queue_severity);
|
|
if(print_severity!=NULL)
|
|
strcpy(preskin->print_severity,print_severity);
|
|
burn_msgs_set_severities(preskin->queue_severity, preskin->print_severity,
|
|
"cdrskin: ");
|
|
return(1);
|
|
}
|
|
|
|
|
|
int Cdrpreskin_initialize_lib(struct CdrpreskiN *preskin, int flag)
|
|
{
|
|
int ret, major, minor, micro;
|
|
|
|
/* Needed are at least 44 bits in signed type off_t .
|
|
This is a popular mistake in configuration or compilation.
|
|
*/
|
|
if(sizeof(off_t) < 6) {
|
|
fprintf(stderr,
|
|
"\ncdrskin: FATAL : Compile time misconfiguration. sizeof(off_t) too small.\n"
|
|
);
|
|
return(0);
|
|
}
|
|
|
|
/* This is the minimum requirement of cdrskin towards the libburn header
|
|
at compile time.
|
|
It gets compared against the version macros in libburn/libburn.h :
|
|
burn_header_version_major
|
|
burn_header_version_minor
|
|
burn_header_version_micro
|
|
If the header is too old then the following code shall cause failure of
|
|
cdrskin compilation rather than to allow production of a program with
|
|
unpredictable bugs or memory corruption.
|
|
The compiler message supposed to appear in this case is:
|
|
error: 'LIBBURN_MISCONFIGURATION' undeclared (first use in this function)
|
|
error: 'INTENTIONAL_ABORT_OF_COMPILATION__HEADERFILE_libburn_dot_h_TOO_OLD__SEE_cdrskin_dot_c' undeclared (first use in this function)
|
|
error: 'LIBBURN_MISCONFIGURATION_' undeclared (first use in this function)
|
|
*/
|
|
|
|
/* The indendation is an advise of man gcc to help old compilers ignoring */
|
|
#if Cdrskin_libburn_majoR > burn_header_version_major
|
|
#define Cdrskin_libburn_dot_h_too_olD 1
|
|
#endif
|
|
#if Cdrskin_libburn_majoR == burn_header_version_major && Cdrskin_libburn_minoR > burn_header_version_minor
|
|
#define Cdrskin_libburn_dot_h_too_olD 1
|
|
#endif
|
|
#if Cdrskin_libburn_minoR == burn_header_version_minor && Cdrskin_libburn_micrO > burn_header_version_micro
|
|
#define Cdrskin_libburn_dot_h_too_olD 1
|
|
#endif
|
|
|
|
#ifdef Cdrskin_libburn_dot_h_too_olD
|
|
LIBBURN_MISCONFIGURATION = 0;
|
|
INTENTIONAL_ABORT_OF_COMPILATION__HEADERFILE_libburn_dot_h_TOO_OLD__SEE_cdrskin_dot_c = 0;
|
|
LIBBURN_MISCONFIGURATION_ = 0;
|
|
#endif
|
|
|
|
ret= burn_initialize();
|
|
if(ret==0) {
|
|
fprintf(stderr,"cdrskin: FATAL : Initialization of libburn failed\n");
|
|
return(0);
|
|
}
|
|
|
|
/* This is the runtime check towards eventual dynamically linked libburn.
|
|
cdrskin deliberately does not to allow the library to be older than
|
|
the header file which was seen at compile time. More liberal would be
|
|
to use here Cdrskin_libburn_* instead of burn_header_version_* .
|
|
*/
|
|
burn_version(&major, &minor, µ);
|
|
if(major<burn_header_version_major ||
|
|
(major==burn_header_version_major && (minor<burn_header_version_minor ||
|
|
(minor==burn_header_version_minor && micro<burn_header_version_micro)))) {
|
|
fprintf(stderr,"cdrskin: FATAL : libburn version too old: %d.%d.%d . Need at least: %d.%d.%d .\n",
|
|
major, minor, micro,
|
|
Cdrskin_libburn_majoR, Cdrskin_libburn_minoR, Cdrskin_libburn_micrO);
|
|
return(-1);
|
|
}
|
|
Cdrpreskin_set_severities(preskin,NULL,NULL,0);
|
|
burn_allow_drive_role_4(1);
|
|
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)
|
|
{
|
|
/* In cdrskin there is not much sense in queueing library messages.
|
|
#ifndef Cdrskin_extra_leaN
|
|
#define Cdrskin_debug_libdax_msgS 1
|
|
#endif
|
|
It would be 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 */
|
|
|
|
return(1);
|
|
}
|
|
|
|
|
|
/** Evaluate whether the user would be allowed in any case to use device_adr
|
|
as pseudo-drive */
|
|
int Cdrpreskin__allows_emulated_drives(char *device_adr, char reason[4096],
|
|
int flag)
|
|
{
|
|
struct stat stbuf;
|
|
|
|
reason[0]= 0;
|
|
if(device_adr[0]) {
|
|
if(strcmp(device_adr,"/dev/null")==0)
|
|
return(1);
|
|
strcat(reason,"File object is not /dev/null. ");
|
|
}
|
|
|
|
if(getuid()!=geteuid()) {
|
|
strcat(reason,"UID and EUID differ");
|
|
return(0);
|
|
}
|
|
if(getuid()!=0)
|
|
return(1);
|
|
|
|
strcat(reason,"UID is 0. ");
|
|
/* Directory must be owned by root and write protected against any others*/
|
|
if(lstat("/root/cdrskin_permissions",&stbuf)==-1 || !S_ISDIR(stbuf.st_mode)) {
|
|
strcat(reason, "No directory /root/cdrskin_permissions exists");
|
|
return(0);
|
|
}
|
|
if(stbuf.st_uid!=0) {
|
|
strcat(reason, "Directory /root/cdrskin_permissions not owned by UID 0");
|
|
return(0);
|
|
}
|
|
if(stbuf.st_mode & (S_IWGRP | S_IWOTH)) {
|
|
strcat(reason,
|
|
"Directory /root/cdrskin_permissions has w-permission for group or others");
|
|
return(0);
|
|
}
|
|
if(stat("/root/cdrskin_permissions/allow_emulated_drives",&stbuf)==-1) {
|
|
strcat(reason,
|
|
"No file /root/cdrskin_permissions/allow_emulated_drives exists");
|
|
return(0);
|
|
}
|
|
reason[0]= 0;
|
|
return(1);
|
|
}
|
|
|
|
|
|
int Cdrpreskin_consider_normal_user(int flag)
|
|
{
|
|
fprintf(stderr,
|
|
"cdrskin: HINT : Consider to allow rw-access to the writer devices and\n");
|
|
fprintf(stderr,
|
|
"cdrskin: HINT : to run cdrskin under your normal user identity.\n");
|
|
return(1);
|
|
}
|
|
|
|
|
|
/* Start the fallback program as replacement of the cdrskin run.
|
|
@param flag bit0=do not report start command
|
|
*/
|
|
int Cdrpreskin_fallback(struct CdrpreskiN *preskin, int argc, char **argv,
|
|
int flag)
|
|
{
|
|
char **hargv= NULL;
|
|
int i, wp= 1;
|
|
char *ept, *upt;
|
|
|
|
if(preskin->fallback_program[0] == 0)
|
|
return(1);
|
|
if(getuid()!=geteuid() && !preskin->allow_setuid) {
|
|
fprintf(stderr,
|
|
"cdrskin: SORRY : uid and euid differ. Will not start external fallback program.\n");
|
|
Cdrpreskin_consider_normal_user(0);
|
|
fprintf(stderr,
|
|
"cdrskin: HINT : Option --allow_setuid disables this safety check.\n");
|
|
goto failure;
|
|
}
|
|
if(!(flag&1)) {
|
|
fprintf(stderr,"cdrskin: --------------------------------------------------------------------\n");
|
|
fprintf(stderr,"cdrskin: Starting fallback program:\n");
|
|
}
|
|
hargv= TSOB_FELD(char *,argc+1);
|
|
if(hargv==NULL)
|
|
goto failure;
|
|
hargv[0]= strdup(preskin->fallback_program);
|
|
if(argv[0]==NULL)
|
|
goto failure;
|
|
if(!(flag&1))
|
|
fprintf(stderr," %s", hargv[0]);
|
|
for(i= 1; i<argc; i++) {
|
|
/* filter away all cdrskin specific options : --?* and *_*=* */
|
|
if(argv[i][0]=='-' && argv[i][1]=='-' && argv[i][2])
|
|
continue;
|
|
ept= strchr(argv[i],'=');
|
|
if(ept!=NULL) {
|
|
upt= strchr(argv[i],'_');
|
|
if(upt!=NULL && upt<ept)
|
|
continue;
|
|
}
|
|
hargv[wp]= strdup(argv[i]);
|
|
if(hargv[wp]==NULL)
|
|
goto failure;
|
|
if(!(flag&1))
|
|
fprintf(stderr," %s", hargv[wp]);
|
|
wp++;
|
|
}
|
|
hargv[wp]= NULL;
|
|
if(!(flag&1)) {
|
|
fprintf(stderr,"\n");
|
|
fprintf(stderr,"cdrskin: --------------------------------------------------------------------\n");
|
|
}
|
|
execvp(hargv[0], hargv);
|
|
failure:;
|
|
fprintf(stderr,"cdrskin: FATAL : Cannot start fallback program '%s'\n",
|
|
preskin->fallback_program);
|
|
fprintf(stderr,"cdrskin: errno=%d \"%s\"\n",
|
|
errno, (errno > 0 ? strerror(errno) : "unidentified error"));
|
|
exit(15);
|
|
}
|
|
|
|
|
|
/** 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);
|
|
if(strncmp(adr,"stdio:",6)==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));
|
|
|
|
} 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);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return(1);
|
|
}
|
|
|
|
|
|
/** Set the eventual output fd for the result of Cdrskin_msinfo()
|
|
*/
|
|
int Cdrpreskin_set_result_fd(struct CdrpreskiN *o, int result_fd, int flag)
|
|
{
|
|
o->result_fd= result_fd;
|
|
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;
|
|
|
|
filenames_v= TSOB_FELD(char *, o->rc_filename_count+1);
|
|
if(filenames_v==NULL)
|
|
return(-1);
|
|
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);
|
|
free((char *) filenames_v);
|
|
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, reason[4096], *argpt;
|
|
|
|
#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 ||
|
|
strncmp(argv[1], "textfile_to_v07t=", 17) == 0 ||
|
|
strncmp(argv[1], "-textfile_to_v07t=", 18) == 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);
|
|
}
|
|
|
|
/* The two predefined fallback personalities are triggered by the progname */
|
|
value_pt= strrchr(argv[0],'/');
|
|
if(value_pt==NULL)
|
|
value_pt= argv[0];
|
|
else
|
|
value_pt++;
|
|
if(strcmp(value_pt,"unicord")==0)
|
|
strcpy(o->fallback_program,"cdrecord");
|
|
else if(strcmp(value_pt,"codim")==0)
|
|
strcpy(o->fallback_program,"wodim");
|
|
|
|
for (i= 1;i<argc;i++) {
|
|
|
|
argpt= argv[i];
|
|
if (strncmp(argpt, "--", 2) == 0 && strlen(argpt) > 3)
|
|
argpt++;
|
|
|
|
if(strcmp(argv[i],"--abort_handler")==0) {
|
|
o->abort_handler= 3;
|
|
|
|
} else if(strcmp(argv[i],"--allow_emulated_drives")==0) {
|
|
if(Cdrpreskin__allows_emulated_drives("",reason,0)<=0) {
|
|
fprintf(stderr,"cdrskin: WARNING : %s.\n",reason);
|
|
fprintf(stderr,
|
|
"cdrskin: WARNING : Only /dev/null will be available with \"stdio:\".\n");
|
|
Cdrpreskin_consider_normal_user(0);
|
|
o->allow_emulated_drives= 2;
|
|
} else
|
|
o->allow_emulated_drives= 1;
|
|
|
|
} else if(strcmp(argv[i],"--allow_setuid")==0) {
|
|
o->allow_setuid= 1;
|
|
|
|
} else if(strcmp(argv[i],"--allow_untested_media")==0) {
|
|
o->allow_untested_media= 1;
|
|
|
|
} else if(strcmp(argpt, "blank=help") == 0 ||
|
|
strcmp(argpt, "-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,
|
|
"\tas_needed\tblank or format media to make it ready for (re-)use\n");
|
|
fprintf(stderr,
|
|
"\tdeformat_sequential\t\tfully blank, even formatted DVD-RW\n");
|
|
fprintf(stderr,
|
|
"\tdeformat_sequential_quickest\tminimally blank, even DVD-RW\n");
|
|
fprintf(stderr,
|
|
"\tformat_if_needed\t\tmake overwriteable if needed and possible\n");
|
|
fprintf(stderr,
|
|
"\tformat_overwrite\t\tformat a DVD-RW to \"Restricted Overwrite\"\n");
|
|
fprintf(stderr,
|
|
"\tformat_overwrite_quickest\tto \"Restricted Overwrite intermediate\"\n");
|
|
fprintf(stderr,
|
|
"\tformat_overwrite_full\t\tfull-size format a DVD-RW or DVD+RW\n");
|
|
fprintf(stderr,
|
|
"\tformat_defectmgt[_max|_min|_none]\tformat DVD-RAM or BD-R[E]\n");
|
|
fprintf(stderr,
|
|
"\tformat_defectmgt[_cert_on|_cert_off]\tcertification slow|quick\n");
|
|
fprintf(stderr,
|
|
"\tformat_defectmgt_payload_<size>\tformat DVD-RAM or BD-R[E]\n");
|
|
fprintf(stderr,
|
|
"\tformat_by_index_<number>\t\tformat by index from --list_formats\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;
|
|
o->demands_cdrskin_caps= 1;
|
|
|
|
} else if(strcmp(argv[i],"--devices") == 0 ||
|
|
strcmp(argv[i],"--device_links") == 0) {
|
|
o->no_whitelist= 1;
|
|
o->demands_cdrskin_caps= 1;
|
|
|
|
} else if(strncmp(argv[i],"dev_translation=",16)==0) {
|
|
o->demands_cdrskin_caps= 1;
|
|
|
|
#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(argpt, "-dev=", 5) == 0) {
|
|
value_pt= argpt + 5;
|
|
goto set_dev;
|
|
} else if(strncmp(argpt, "dev=", 4) == 0) {
|
|
value_pt= argpt + 4;
|
|
set_dev:;
|
|
if(strcmp(value_pt,"help")==0) {
|
|
|
|
#ifndef Cdrskin_extra_leaN
|
|
|
|
printf("\nSupported 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,
|
|