diff --git a/cdrskin/README b/cdrskin/README new file mode 100644 index 0000000..21e76a7 --- /dev/null +++ b/cdrskin/README @@ -0,0 +1,317 @@ +------------------------------------------------------------------------------ + libburn.pykix.org scdbackup.sourceforge.net/cdrskin +------------------------------------------------------------------------------ +This all is under GPL. Wether it can become LGPL is currently very unclear. +(So for now see explanation and GPL reference at the end of this text) +------------------------------------------------------------------------------ + +Libburn. By Derek Foreman and + Ben Jansens +Copyright (C) 2002-2006 Derek Foreman and Ben Jansens + +Mario Danic , Luke Biddell , +Anant Narayanan , Thomas Schmitt +Copyright (C) 2006 Mario Danic, Luke Biddell, Anant Narayanan, Thomas Schmitt + +------------------------------------------------------------------------------ +My thanks to the above authors (except myself, of course) for making the +following possible. + +cdrskin. By Thomas Schmitt +Integrated sub project of libburn.pykix.org but also published via: +http://scdbackup.sourceforge.net/cdrskin_eng.html +http://scdbackup.sourceforge.net/cdrskin-0.1.4.tar.gz +Copyright (C) 2006 Thomas Schmitt + +------------------------------------------------------------------------------ + +On top of libburn there is implemented cdrskin 0.1.4, a limited cdrecord +compatibility wrapper which allows to use some libburn features from +the command line. +Interested users of cdrecord are invited to participate in the development +of cdrskin. Contact: scdbackup@gmx.net + + +Important : +This software is provided as is. There is no warranty implied and no +protection against possible damages. You use this on your own risk. +Don't blame me or other authors of libburn if anything goes wrong. + +I used it on my own risk with : +SuSE 7.2, kernel 2.4.4, ide-scsi emulation, LITE-ON LTR48125S CD burner +SuSE 9.0, kernel 2.4.21, ide-scsi emulation, LG GSA-4082B CD/DVD burner +RIP-14.4, kernel 2.6.14, no ide-scsi, with both above burners + +It fails to compile or run on SuSE 6.4 (kernel 2.2.14). +It does not find the IDE CD burner on SuSE 7.2 without ide-scsi. +Other people sucessfully tested cdrskin on several kernel 2.6 based x86 Linux +systems, including 64 bit systems. (Further reports are welcome.) + + + Compilation, First Glimpse, Installation + +Obtain cdrskin-0.1.4.tar.gz , go to a directory of your choice and do: + + tar xzf cdrskin-0.1.4.tar.gz + cd cdrskin-0.1.4 + +Or obtain a libburn.pykix.org SVN snapshot, go into toplevel directory libburn, +and execute the autotools command ./bootstrap + +Within that toplevel directory of either cdrskin-0.1.4 or libburn then execute: + + ./configure + make + +(Note: there are next-level directories "libburn" and "cdrskin". Those +would be the wrong ones. Meant is the highest directory of tarball resp. +SVN download. Among others containing files "AUTHORS", "configure", +"Makefile.am", as well as directories "libburn" and "cdrskin".) + +This will already produce a cdrskin binary. But it might be necessary to +install libburn in order to use this binary. Installation of libburn is +beyond the scope of cdrskin. For this, see included libburn docs. + +In order to surely get a standalone binary, execute + + cdrskin/compile_cdrskin.sh + +Help texts : + cdrskin/cdrskin --help + cdrskin/cdrskin -help + +Install (eventually as superuser) cdrskin to a directory where it can be found: + cp cdrskin/cdrskin /usr/bin + +It is not necessary for the standalone cdrskin binary to have libburn +installed, since it incorporates the necessary libburn parts at compile time. +It will not collide with an installed version of libburn either. +But libpthread must be installed on the system and glibc has to match. (See +below for a way to create a statically linked binary.) + +Up to now i discourage to install the emerging libraries and to use them +with other programs. Unless you need my patches, better use vanilla libburn +for that. + + + Usage + +The user of cdrskin needs rw-permission for the CD burner device. +A list of rw-accessible drives can be obtained by + + cdrskin --devices + +CD devices which offer no rw-permission are invisible to normal users. +The superuser should be able to see any usable drive and then set the +permissions as needed. If this hangs then there is a drive with +unexpected problems (locked, busy, broken, whatever). You might have to +guess the address of your (non-broken) burner by other means, then. +On Linux 2.4 this would be some /dev/sgN and on 2.6. some /dev/hdX. + +The output of cdrskin --devices might look like + + 0 dev='/dev/sg0' rwrwr- : 'TEAC' 'CD-ROM CD-532S' + 1 dev='/dev/sg1' rwrw-- : 'LITE-ON' 'LTR-48125S' + +So full and insecure enabling of both for everybody would look like + + chmod a+rw /dev/sg0 /dev/sg1 + +(The CD-ROM is in these examples only for demonstrating the presence of another + SCSI device. This /dev/sg0 may be left as it is and stay invisible for normal + users.) + +I strongly discourage to run cdrskin with setuid root or via sudo ! +It is not checked for the necessary degree of hacker safety. + + + Usage examples + +Get an overview of cdrecord style addresses of available devices + cdrskin -scanbus + +Obtain some info about the drive + cdrskin dev=1,1,0 -checkdrive + +Obtain some info about the drive and the inserted media + cdrskin dev=1,1,0 -atip + +Thoroughly blank a CD-RW + cdrskin -v dev=1,1,0 blank=all eject_device=/dev/cdrom -eject + +Blank CD-RW sufficiently for making it ready for overwrite + cdrskin -v dev=1,1,0 blank=fast eject_device=/dev/cdrom -eject + +Burn image file my_image.iso to CD + cdrskin -v dev=1,1,0 speed=12 fs=8m -sao driveropts=burnfree padsize=300k \ + eject_device=/dev/cdrom -eject my_image.iso + +Burn a compressed afio archive to CD on-the-fly + find . | afio -oZ - | cdrskin -v dev=1,1,0 fs=32m speed=8 -sao \ + driveropts=burnfree padsize=300k tsize=650m - + + + Usage example with http://scdbackup.sourceforge.net + +Address may be a cdrecord-style "scsibus,target,lun" as listed with +cdrskin -scanbus (but not as listed with cdrecord -scanbus) : + + export SCDBACKUP_SCSI_ADR="1,1,0" + +or a device file address as listed by --devices with an accessible drive : + + export SCDBACKUP_SCSI_ADR="/dev/sg1" + +Set usage of cdrskin with appropriate options rather than cdrecord : + + export SCDBACKUP_CDRECORD="cdrskin -v -v tao_to_sao_tsize=650m eject_device=/dev/cdrw" + +Run a backup : + + scdbackup_home + + + Restrictions + +The convenient burn mode TAO is not available with libburn yet. +Therefore it has to be defaulted to mode SAO which needs to know the track +size in advance. non-cdrecord option tao_to_sao_tsize=650m causes each CD +to get burned up to 650 MB regardless of the payload size. + +Command eject does not work with /dev/sgX and there is no easy way to determine +a drive's device file address which is suitable for eject. +So this address has to be supplied by eject_device=... unless your drive is +/dev/sg0 which is guessed as eject_device=/dev/sr0 . + +No audio features, no multi session ... Please report your wishes. + + + Inspiration and Standard + +For the original meaning of cdrecord options see : + man cdrecord + (http://cdrecord.berlios.de/old/private/man/cdrecord-2.0.html) +Do not bother Joerg Schilling with any cdrskin problems. +(Be cursed if you install cdrskin as "cdrecord" without clearly forwarding + this "don't bother Joerg" demand.) +cdrskin does not contain any bytes copied from cdrecord's sources. Many bytes +have been copied from the message output of cdrecord runs, though. I am +thankful to Joerg Schilling for every single one of them. + +Actually i, Thomas Schmitt, am a devoted user of cdrecord via my project +scdbackup which still runs a bit better with cdrecord than with cdrskin. TAO. +I have the hope that Joerg feels more flattered than annoyed by cdrskin. + + + Pseudo-SCSI Adresses + +cdrecord and cdrskin share only some syntax of addresses but not the meaning +of the components. A cdrecord-style address for cdrskin + [[prefix:]scsibus,]target,lun +corresponds either to a device file address or to a libburn drive number. +Component "scsibus" indicates the translation method. Defined busses are: + 0 target is the libburn drivenumber as listed with --devices + 1 associated to device file /dev/sgN , target chooses N + 2 associated to device file /dev/hdX , target 0='a', 1='b' ..., 25='z' + +So "1,1,0" is /dev/sg1, "2,3,0" is /dev/hdd, "0,2,0" is libburn drive #2 at +some unspecified device file. +This scheme shall help to keep cdrecord-style addresses stable and exchangeable +between users without excluding drives with unexpected device addresses. +The numbering on bus 0 is prone to arbitrary changes caused by changes in +drive accessability. +Further busses may emerge as libburn evolves. "prefix" and "lun" may get +a meaning. To stay upward compatible, use addresses as printed by -scanbus. + +Some programs or users have their own ideas about the address of their burner. +K3b 0.10 for example derives cdrecord addresses by own examination of the +devices and not by calling cdrecord -scanbus. +To direct such callers to the appropriate drives, cdrskin allows to define +device address aliases. Like + cdrskin dev_translation=+1,0,0+/dev/sg1 \ + dev_translation=+ATA:1,0,0+/dev/sg1 \ + dev_translation=-"cd+dvd"-1,1,0 \ + ... +Any of the addresses dev=1,0,0, dev=ATA:1,0,0, dev=cd+dvd will be mapped to +/dev/sg1 resp. to its standard alias 1,1,0. +The first character after "dev_translation=" defines the character which +separates the two parts of the translation pair. (Above: "+" and "-".) + +In K3b 0.10 it is possible to employ alternative writer programs by setting +their full path (e.g. /usr/bin/cdrskin) in menu + Settings:Configure K3b...:Programs:Search Path +and to make them default in menu + Settings:Configure K3b...:Programs:Programs: +A suitable setting for "cdrecord" in menu + Settings:Configure K3b...:Programs:User Parameters +would then probably be + -v dev_translation=+1,0,0+/dev/sg1 +You will learn from button "Show Debugging Output" after a failed burn run +what cdrecord command was used with what address "dev=...". This address "..." +will be the right one to replace "1,0,0" in above example. + + + Startup Files + +If not --no_rc is the first argument then cdrskin attempts on startup to read +arguments from the following three files: + /etc/defaults/cdrskin + /etc/opt/cdrskin/rc + $HOME/.cdrskinrc +The files are read in the sequence given above. +Each readable line is treated as one single argument. No extra blanks, +no comments, no empty lines are permitted. + +Example content of a startup file: +dev=1,1,0 +dev_translation=+1,0,0+1,1,0 +--fifo_start_empty +fs=16m + + + Special compilation variations + +You may get a (super fat) statically linked binary by : + cdrskin/compile_cdrskin.sh -static +if your system supports static linking, at all. This will not help with kernels +which do not properly support the necessary low-level interfaces chosen by +your compile-time libraries. + +A size reduced but fully functional binary may be produced by + cdrskin/compile_cdrskin.sh -do_strip + +An extra lean binary with reduced capabilities is created by + cdrskin/compile_cdrskin.sh -do_diet -do_strip +It will not read startup files, will abort on option dev_translation= , +will not have a fifo buffer, and will not be able to put out help texts or +debugging messages. + + +------------------------------------------------------------------------------ +It is my understanding that you may put a binary of unchanged cdrskin on any +suitable system if you only tell the user that the source is available for +free in the internet. Search engines will find it. Better than any URL here. + +If you link to the libraries or if you make changes in our source, you will +currently have to release your own programs under GPL and nothing else, i fear. +As it looks no single one of us currently has the right to issue any other +license. +You may submit source changes which affect our standalone binaries and if +they get included you may distribute binaries derived from our new code base. + +signed: Thomas Schmitt (and his understanding of GPL), author of this README. +------------------------------------------------------------------------------ + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA diff --git a/cdrskin/add_ts_changes_to_libburn_0_2_1 b/cdrskin/add_ts_changes_to_libburn_0_2_1 new file mode 100755 index 0000000..0f55993 --- /dev/null +++ b/cdrskin/add_ts_changes_to_libburn_0_2_1 @@ -0,0 +1,163 @@ +#!/bin/sh + +set -x + +# This script documents how this cdrskin version was derived from +# a vanilla libburn version. It is not intended nor needed for any +# use of cdrskin but included here only to show the technical +# relationship between both projects - which are close friends +# and issue roughly the same software. +# +# Package maintainers are advised to cover rather libburn than +# cdrskin unless they put only emphasis on the cdrecord emulation +# provided by cdrskin. libburn contains cdrskin - cdrskin is an +# oscillating, friendly and coordinated fork of libburn. +# +# Script results are a source tarball and two binaries +# one dynamic and one static in respect to system libs. +# Both binaries are static in respect to libburn. +# +# The script is to be run in the directory above the toplevel +# directory of libburn resp. cdrskin development. +# +# libburn version used: http://libburn.pykix.org SVN of Aug 15 2006 +# packed up in a tarball just to save it from inadverted changes. +original="./libburn_svn_A60815.tgz" + +# The top level directory in that snapshot is named +intermediate="./libburn" + +# My changes are in libburn-0.2.1.ts.develop , mainly in ./cdrskin + +changes="./libburn-0.2.1.ts.develop" +skin_rev="0.1.4" + +# The result directory and the name of the result tarballs +target="./cdrskin-${skin_rev}" +cdrskin_tarball="./cdrskin-${skin_rev}.tar.gz" +cdrskin_tarball_svn="./cdrskin-${skin_rev}.svn.tar.gz" + +# (This once earned me an embarrassingly blooping source tarball) +# compile_dir="$changes" + +compile_dir="$target" +compile_cmd="./cdrskin/compile_cdrskin.sh" +compile_static_opts="-static" +compile_result="cdrskin/cdrskin" + + +# addresses relative to compile_dir : +bintarget_dynamic="../cdrskin_${skin_rev}-x86-suse9_0" +bintarget_static="$bintarget_dynamic"-static + +if test -d "$changes" +then + dummy=dummy +else + echo "$0 : FATAL : no directory $changes" >&2 + exit 1 +fi + +for i in "$target" "$intermediate" +do + if test -e "$i" + then + echo "$0 : FATAL : already existing $i" >&2 + exit 2 + fi +done + +if test -f "$original" +then + dummy=dummy +else + echo "$0 : FATAL : no file $original" >&2 +fi + + +# Unpack SVN snapshot. +tar xzf "$original" + + +# Rename the directory to the cdrskin name +mv "$intermediate" "$target" + + +# Copy the changes from the development tree +# +cdrskin_dir="$changes"/cdrskin +libburn_dir="$changes"/libburn +cdrskin_target="$target"/cdrskin +libburn_target="$target"/libburn + +# Create version timestamp +timestamp="$(date -u '+%Y.%m.%d.%H%M%S')" +echo "$timestamp" +echo '#define Cdrskin_timestamP "'"$timestamp"'"' >"$cdrskin_dir"/cdrskin_timestamp.h + +# Add the cdrskin files +if test -e "$cdrskin_target" +then + rm -rf "$cdrskin_target" +fi +cp -a "$cdrskin_dir" "$cdrskin_target" + +# Remove copied binaries +rm "$cdrskin_target"/cdrfifo +rm "$cdrskin_target"/cdrskin +rm "$cdrskin_target"/cleanup + +# Remove unwanted SVN stuff (TODO: avoid downloading it) +for i in "$target"/.svn "$target"/*/.svn +do + if test "$i" = "$target"'/*/.svn' + then + dummy=dummy + else + if test -e "$i" + then + rm -rf "$i" + fi + fi +done + +# For now: Add own libburn-README in toplevel +cp -a "$changes"/README "$target" + +# Add modified Makefile.am +cp -a "$changes"/Makefile.am "$target" + + +# Make SVN state tarball for the libburn team +# TODO: will probably be obsoleted after sucessful merge +tar czf "$cdrskin_tarball_svn" "$target" + + +# Get over dependecy on autotools. Rely only on cc, make et. al. +# This is not the same as "make dist" but i can do it without +# having to evaluate the quality of said "make dist" +# +( cd "$target" ; ./bootstrap ) + + +# Pack it up to the new libburn+cdrskin-tarball +tar czf "$cdrskin_tarball" "$target" + +# Produce a static and a dynamic binary +( + cd "$compile_dir" || exit 1 + ./configure + make + $compile_cmd -do_strip + cp "$compile_result" "$bintarget_dynamic" + if test -n "$compile_static_opts" + then + $compile_cmd $compile_static_opts -do_strip + cp "$compile_result" "$bintarget_static" + fi +) + +# Disable this for debugging the merge process +rm -rf "$target" + + diff --git a/cdrskin/cdrecord_spy.sh b/cdrskin/cdrecord_spy.sh new file mode 100755 index 0000000..54d7c34 --- /dev/null +++ b/cdrskin/cdrecord_spy.sh @@ -0,0 +1,37 @@ +#!/bin/sh +# +# Spying on the call to cdrecord. +# +# Move $(which cdrecord) to $(dirname $(which cdrecord))/real_cdrecord . +# Install this sript instead. (Do not forget to revoke this after the test.) +# + +# The report target is set in variable rt. +# The default is this file : +rt=/tmp/cdrecord_spy_log + +# To use a bystanding xterm as target i find out the pty address by +# executing in that terminal +# sleep 12345 +# and then running in another terminal +# ps -ef | grep 'sleep 12345' +# which answers something like +# thomas 21303 30518 0 14:02 pts/23 00:00:00 sleep 12345 +# thomas 21421 30523 0 14:02 pts/24 00:00:00 grep sleep 12345 +# from which i learn that pts/23 is sleeping 12345. Now sleep can be aborted. +# +# rt=/dev/pts/23 + +echo '------------------------------------- cdrecord_spy 0.1.0 -------' >>"$rt" +date >>"$rt" +echo '----------------------------------------------------------------' >>"$rt" +echo "$0" >>"$rt" +for i in "$@" +do + echo "$i" >>"$rt" +done +echo '------------------------------------- cdrecord_spy 0.1.0 - end -' >>"$rt" + +real_cdrecord "$@" + + diff --git a/cdrskin/cdrfifo.c b/cdrskin/cdrfifo.c new file mode 100644 index 0000000..634d85c --- /dev/null +++ b/cdrskin/cdrfifo.c @@ -0,0 +1,1036 @@ +/* + cdrfifo.c , Copyright 2006 Thomas Schmitt + + A fd-to-fd or fd-to-memory fifo to be used within cdrskin or independently. + By chaining of fifo objects, several fifos can be run simultaneously + in fd-to-fd mode. Modes are controlled by parameter flag of + Cdrfifo_try_to_work(). + + Provided under GPL license within cdrskin and under BSD license elsewise. +*/ + +/* + Compile as standalone tool : + cc -g -o cdrfifo -DCdrfifo_standalonE cdrfifo.c +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "cdrfifo.h" + + +/* Macro for creation of arrays of objects (or single objects) */ +#define TSOB_FELD(typ,anz) (typ *) malloc((anz)*sizeof(typ)); + + +#define Cdrfifo_buffer_chunK 2048 + +/** Number of follow-up fd pairs */ +#define Cdrfifo_ffd_maX 100 + + +/* 1= enable , 0= disable status messages to stderr */ +static int Cdrfifo_debuG= 0; + + +struct CdrfifO { + int chunk_size; + + int source_fd; + double in_counter; + + char *buffer; + int buffer_size; + int buffer_is_full; + int write_idx; + int read_idx; + + int dest_fd; + double out_counter; + + struct timeval start_time; + double speed_limit; + + /* statistics */ + double interval_counter; + struct timeval interval_start_time; + double interval_start_counter; + int total_min_fill; + int interval_min_fill; + + double put_counter; + double get_counter; + double empty_counter; + double full_counter; + + /* (sequential) fd chaining */ + int follow_up_fds[Cdrfifo_ffd_maX][2]; + /* index of first byte in buffer which does not belong to predecessor fd */ + int follow_up_eop[Cdrfifo_ffd_maX]; + /* index of first byte in buffer which belongs to [this] fd pair */ + int follow_up_sod[Cdrfifo_ffd_maX]; + /* number of defined follow-ups */ + int follow_up_fd_counter; + /* index of currently active (i.e. reading) follow-up */ + int follow_up_fd_idx; + + /* (simultaneous) peer chaining */ + struct CdrfifO *next; + struct CdrfifO *prev; +}; + + +/** Create a fifo object. + @param ff Returns the address of the new object. + @param source_fd Filedescriptor opened to a readable data stream. + @param dest_fd Filedescriptor opened to a writable data stream. + To work with libburn, it needs to be attached to a + struct burn_source object. + @param chunk_size Size of buffer block for a single transaction (0=default) + @param buffer_size Size of fifo buffer + @param flag Unused yet + @return 1 on success, <=0 on failure +*/ +int Cdrfifo_new(struct CdrfifO **ff, int source_fd, int dest_fd, + int chunk_size, int buffer_size, int flag) +{ + struct CdrfifO *o; + struct timezone tz; + int i; + + (*ff)= o= TSOB_FELD(struct CdrfifO,1); + if(o==NULL) + return(-1); + if(chunk_size<=0) + chunk_size= Cdrfifo_buffer_chunK; + o->chunk_size= chunk_size; + if(buffer_size%chunk_size) + buffer_size+= chunk_size-(buffer_size%chunk_size); + o->source_fd= source_fd; + o->in_counter= 0.0; + o->buffer= NULL; + o->buffer_is_full= 0; + o->buffer_size= buffer_size; + o->write_idx= 0; + o->read_idx= 0; + o->dest_fd= dest_fd; + o->out_counter= 0.0; + memset(&(o->start_time),0,sizeof(o->start_time)); + gettimeofday(&(o->start_time),&tz); + o->speed_limit= 0.0; + o->interval_counter= 0.0; + memset(&(o->interval_start_time),0,sizeof(o->interval_start_time)); + gettimeofday(&(o->interval_start_time),&tz); + o->interval_start_counter= 0.0; + o->total_min_fill= buffer_size; + o->interval_min_fill= buffer_size; + o->put_counter= 0.0; + o->get_counter= 0.0; + o->empty_counter= 0.0; + o->full_counter= 0.0; + for(i= 0; ifollow_up_fds[i][0]= o->follow_up_fds[i][1]= -1; + o->follow_up_eop[i]= o->follow_up_sod[i]= -1; + } + o->follow_up_fd_counter= 0; + o->follow_up_fd_idx= -1; + o->next= o->prev= NULL; + o->buffer= TSOB_FELD(char,buffer_size); + if(o->buffer==NULL) + goto failed; + return(1); +failed:; + Cdrfifo_destroy(ff,0); + return(-1); +} + + +/** Close any output fds */ +int Cdrfifo_close(struct CdrfifO *o, int flag) +{ + int i; + + if(o->dest_fd!=-1) + close(o->dest_fd); + o->dest_fd= -1; + for(i=0; ifollow_up_fd_counter; i++) + if(o->follow_up_fds[i][1]!=-1) + close(o->follow_up_fds[i][1]); + o->follow_up_fds[i][1]= -1; + return(1); +} + + +/** Release from memory a fifo object previously created by Cdrfifo_new(). + @param ff The victim (gets returned as NULL, call can stand *ff==NULL)) + @param flag Bitfield for control purposes: + bit0= do not close destination fd +*/ +int Cdrfifo_destroy(struct CdrfifO **ff, int flag) +/* flag + bit0= do not close destination fd +*/ +{ + struct CdrfifO *o; + + o= *ff; + if(o==NULL) + return(0); + if(o->next!=NULL) + o->next->prev= o->prev; + if(o->prev!=NULL) + o->prev->next= o->next; + if(!(flag&1)) + Cdrfifo_close(o,0); + + /* eventual closing of source fds is the job of the calling application */ + + if(o->buffer!=NULL) + free((char *) o->buffer); + free((char *) o); + (*ff)= NULL; + return(1); +} + + +int Cdrfifo_get_sizes(struct CdrfifO *o, int *chunk_size, int *buffer_size, + int flag) +{ + *chunk_size= o->chunk_size; + *buffer_size= o->buffer_size; + return(1); +} + +/** Set a speed limit for buffer output. + @param o The fifo object + @param bytes_per_second >0 catch up slowdowns over the whole run time + <0 catch up slowdowns only over one interval + =0 disable speed limit +*/ +int Cdrfifo_set_speed_limit(struct CdrfifO *o, double bytes_per_second, + int flag) +{ + o->speed_limit= bytes_per_second; + return(1); +} + + +int Cdrfifo_set_fds(struct CdrfifO *o, int source_fd, int dest_fd, int flag) +{ + o->source_fd= source_fd; + o->dest_fd= dest_fd; + return(1); +} + + +int Cdrfifo_get_fds(struct CdrfifO *o, int *source_fd, int *dest_fd, int flag) +{ + *source_fd= o->source_fd; + *dest_fd= o->dest_fd; + return(1); +} + + +/** Attach a further pair of input and output fd which will use the same + fifo buffer when its predecessors are exhausted. Reading will start as + soon as reading of the predecessor encounters EOF. Writing will start + as soon as all pending predecessor data are written. +*/ +int Cdrfifo_attach_follow_up_fds(struct CdrfifO *o, int source_fd, int dest_fd, + int flag) +{ + if(o->follow_up_fd_counter>=Cdrfifo_ffd_maX) + return(0); + o->follow_up_fds[o->follow_up_fd_counter][0]= source_fd; + o->follow_up_fds[o->follow_up_fd_counter][1]= dest_fd; + o->follow_up_fd_counter++; + return(1); +} + + +/** Attach a further fifo which shall be processed simultaneously with this + one by Cdrfifo_try_to_work() in fd-to-fd mode. +*/ +int Cdrfifo_attach_peer(struct CdrfifO *o, struct CdrfifO *next, int flag) +{ + for(;o->next!=NULL;o= o->next); /* determine end of o-chain */ + for(;next->prev!=NULL;next= next->prev); /* determine start of next-chain */ + next->prev= o; + o->next= next; + return(1); +} + + +static int Cdrfifo_tell_buffer_space(struct CdrfifO *o, int flag) +{ + if(o->buffer_is_full) + return(0); + if(o->write_idx>=o->read_idx) + return((o->buffer_size - o->write_idx) + o->read_idx); + return(o->read_idx - o->write_idx); +} + + +/** Obtain buffer state. + @param o The buffer object + @param fill Returns the number of pending payload bytes in the buffer + @param space Returns the number of unused buffer bytes + @param flag Unused yet + @return -1=error , 0=inactive , 1=reading and writing , + 2=reading ended (but still writing) +*/ +int Cdrfifo_get_buffer_state(struct CdrfifO *o,int *fill,int *space,int flag) +/* return : + -1=error + 0=inactive + 1=reading and writing + 2=reading ended, still writing +*/ +{ + *space= Cdrfifo_tell_buffer_space(o,0); + *fill= o->buffer_size-(*space); + if(o->dest_fd==-1) + return(0); + if(o->source_fd<0) + return(2); + return(1); +} + + +int Cdrfifo_get_counters(struct CdrfifO *o, + double *in_counter, double *out_counter, int flag) +{ + *in_counter= o->in_counter; + *out_counter= o->out_counter; + return(1); +} + + +/** reads min_fill and begins measurement interval for next min_fill */ +int Cdrfifo_next_interval(struct CdrfifO *o, int *min_fill, int flag) +{ + struct timezone tz; + + o->interval_counter++; + gettimeofday(&(o->interval_start_time),&tz); + o->interval_start_counter= o->out_counter; + *min_fill= o->interval_min_fill; + o->interval_min_fill= o->buffer_size - Cdrfifo_tell_buffer_space(o,0); + return(1); +} + + +int Cdrfifo_get_min_fill(struct CdrfifO *o, int *total_min_fill, + int *interval_min_fill, int flag) +{ + *total_min_fill= o->total_min_fill; + *interval_min_fill= o->interval_min_fill; + return(1); +} + + +/** Get counters which are mentioned by cdrecord at the end of burning. + It still has to be examined wether they mean what i believe they do. +*/ +int Cdrfifo_get_cdr_counters(struct CdrfifO *o, + double *put_counter, double *get_counter, + double *empty_counter, double *full_counter, + int flag) +{ + *put_counter= o->put_counter;; + *get_counter= o->get_counter; + *empty_counter= o->empty_counter; + *full_counter= o->full_counter; + return(1); +} + + +/** Adjust a given buffer fill value so it will not cross an eop boundary. + @param o The fifo to exploit. + @param buffer_fill The byte count to adjust. + @param eop_idx If eop boundary exactly hit: index of follow-up fd pair + @param flag Unused yet. + @return 0= nothing changed , 1= buffer_fill adjusted +*/ +int Cdrfifo_eop_adjust(struct CdrfifO *o,int *buffer_fill, int *eop_idx, + int flag) +{ + int i,eop_is_near= 0,valid_fill; + + *eop_idx= -1; + valid_fill= *buffer_fill; + for(i=0; i<=o->follow_up_fd_idx; i++) { + if(o->follow_up_eop[i]>=0 && o->follow_up_eop[i]>=o->read_idx) { + eop_is_near= 1; + valid_fill= o->follow_up_eop[i]-o->read_idx; + if(valid_fill==0) + *eop_idx= i; + else if(valid_fill<=o->chunk_size) + eop_is_near= 2; /* for debugging. to carry a break point */ + break; + } + } + if(*buffer_fill>valid_fill) + *buffer_fill= valid_fill; + return(!!eop_is_near); +} + + +/* Perform pre-select activities of Cdrfifo_try_to_work() */ +static int Cdrfifo_setup_try(struct CdrfifO *o, struct timeval start_tv, + double start_out_counter, int *still_to_wait, + int *speed_limiter, int *ready_to_write, + fd_set *rds, fd_set *wts, int *max_fd, int flag) +/* flag: + bit0= enable debug pacifier (same with Cdrfifo_debuG) + bit1= do not write, just fill buffer + bit2= fd-to-memory mode (else fd-to-fd mode): + rather than writing a chunk return it and its size in reply_* + bit3= with bit2: do not check destination fd for readiness +*/ +{ + int buffer_space,buffer_fill,eop_reached= -1,eop_is_near= 0,was_closed; + int fd_buffer_fill; + struct timeval current_tv; + struct timezone tz; + double diff_time,diff_counter,limit,min_wait_time; + +setup_try:; + buffer_space= Cdrfifo_tell_buffer_space(o,0); + fd_buffer_fill= buffer_fill= o->buffer_size - buffer_space; + +#ifdef NIX + fprintf(stderr,"cdrfifo_debug: o->write_idx=%d o->read_idx=%d o->source_fd=%d\n",o->write_idx,o->read_idx,o->source_fd); + if(buffer_fill>10) + sleep(1); +#endif + + if(o->follow_up_fd_idx>=0) + eop_is_near= Cdrfifo_eop_adjust(o,&fd_buffer_fill,&eop_reached,0); + + if(fd_buffer_fill<=0 && (o->source_fd==-1 || eop_reached>=0) ) { + was_closed= 0; + if(o->dest_fd!=-1 && !(flag&4)) + close(o->dest_fd); + if(o->dest_fd<0) + was_closed= 1; + else + o->dest_fd= -1; + + if(eop_reached>=0) { /* switch to next output fd */ + o->dest_fd= o->follow_up_fds[eop_reached][1]; + o->read_idx= o->follow_up_sod[eop_reached]; + o->follow_up_eop[eop_reached]= -1; + eop_is_near= 0; + eop_reached= -1; + goto setup_try; + } else { + /* work is really done */ + if((!was_closed) && ((flag&1)||Cdrfifo_debuG)) + fprintf(stderr, + "\ncdrfifo_debug: w=%d r=%d | b=%d s=%d | i=%.f o=%.f (done)\n", + o->write_idx,o->read_idx,buffer_fill,buffer_space, + o->in_counter,o->out_counter); + return(2); + } + } + if(o->interval_counter>0) { + if(o->total_min_fill>buffer_fill && o->source_fd>=0) + o->total_min_fill= buffer_fill; + if(o->interval_min_fill>buffer_fill) + o->interval_min_fill= buffer_fill; + } + *speed_limiter= 0; + if(o->speed_limit!=0) { + gettimeofday(¤t_tv,&tz); + if(o->speed_limit>0) { + diff_time= ((double) current_tv.tv_sec)-((double) o->start_time.tv_sec)+ + (((double) current_tv.tv_usec)-((double) o->start_time.tv_usec))*1e-6; + diff_counter= o->out_counter; + limit= o->speed_limit; + } else if(flag&4) { + if(o->interval_start_time.tv_sec==0) + o->interval_start_time= start_tv; + diff_time= ((double) current_tv.tv_sec) + - ((double) o->interval_start_time.tv_sec) + + (((double) current_tv.tv_usec) + -((double) o->interval_start_time.tv_usec))*1e-6; + diff_counter= o->out_counter - o->interval_start_counter; + limit= -o->speed_limit; + } else { + diff_time= ((double) current_tv.tv_sec) - ((double) start_tv.tv_sec) + + (((double) current_tv.tv_usec) + -((double)start_tv.tv_usec))*1e-6; + diff_counter= o->out_counter - start_out_counter; + limit= -o->speed_limit; + } + if(diff_time>0.0) + if(diff_counter/diff_time>limit) { + min_wait_time= (diff_counter/limit - diff_time)*1.0e6; + if(min_wait_time<*still_to_wait) + *still_to_wait= min_wait_time; + if(*still_to_wait>0) + *speed_limiter= 1; + } + } + if(o->source_fd>=0) { + if(buffer_space>0) { + FD_SET((o->source_fd),rds); + if(*max_fdsource_fd) + *max_fd= o->source_fd; + } else if(o->interval_counter>0) + o->full_counter++; + } + *ready_to_write= 0; + if(o->dest_fd>=0 && (!(flag&2)) && !*speed_limiter) { + if(fd_buffer_fill>=o->chunk_size || o->source_fd<0 || eop_is_near) { + if((flag&(4|8))==(4|8)) { + *still_to_wait= 0; + *ready_to_write= 1; + } else { + FD_SET((o->dest_fd),wts); + if(*max_fddest_fd) + *max_fd= o->dest_fd; + } + } else if(o->interval_counter>0) + o->empty_counter++; + } + return(1); +} + + +/* Perform post-select activities of Cdrfifo_try_to_work() */ +static int Cdrfifo_transact(struct CdrfifO *o, fd_set *rds, fd_set *wts, + char *reply_buffer, int *reply_count, int flag) +/* flag: + bit0= enable debug pacifier (same with Cdrfifo_debuG) + bit1= do not write, just fill buffer + bit2= fd-to-memory mode (else fd-to-fd mode): + rather than writing a chunk return it and its size in reply_* + bit3= with bit2: do not check destination fd for readiness +return: <0 = error , 0 = idle , 1 = did some work +*/ +{ + double buffer_space; + int can_read,can_write,ret,did_work= 0,idx,sod,eop_is_near,eop_idx; + + buffer_space= Cdrfifo_tell_buffer_space(o,0); + if(o->dest_fd>=0) if(FD_ISSET((o->dest_fd),wts)) { + can_write= o->buffer_size - buffer_space; + if(can_write>o->chunk_size) + can_write= o->chunk_size; + if(o->read_idx+can_write > o->buffer_size) + can_write= o->buffer_size - o->read_idx; + if(o->follow_up_fd_idx>=0) { + eop_is_near= Cdrfifo_eop_adjust(o,&can_write,&eop_idx,0); + if(can_write<=0) + goto after_write; + } + if(flag&4) { + memcpy(reply_buffer,o->buffer+o->read_idx,can_write); + *reply_count= ret= can_write; + } else { + ret= write(o->dest_fd,o->buffer+o->read_idx,can_write); + } + if(ret==-1) { + + /* >>> handle broken pipe */; + fprintf(stderr,"\ncdrfifo: on write: errno=%d , \"%s\"\n",errno, + errno==0?"-no error code available-":strerror(errno)); + + if(!(flag&4)) + close(o->dest_fd); + o->dest_fd= -1; + {ret= -1; goto ex;} + } + did_work= 1; + o->get_counter++; + o->out_counter+= can_write; + o->read_idx+= can_write; + if(o->read_idx>=o->buffer_size) + o->read_idx= 0; + o->buffer_is_full= 0; + } +after_write:; + if(o->source_fd>=0) if(FD_ISSET((o->source_fd),rds)) { + can_read= o->buffer_size - o->write_idx; + if(can_read>o->chunk_size) + can_read= o->chunk_size; + if(o->write_idxread_idx && o->write_idx+can_read > o->read_idx) + can_read= o->read_idx - o->write_idx; + ret= read(o->source_fd,o->buffer+o->write_idx,can_read); + if(ret==-1) { + + /* >>> handle input error */; + fprintf(stderr,"\ncdrfifo: on read: errno=%d , \"%s\"\n",errno, + errno==0?"-no error code available-":strerror(errno)); + + o->source_fd= -1; + } else if(ret==0) { /* eof */ + /* activate eventual follow-up source fd */ + if(Cdrfifo_debuG || (flag&1)) + fprintf(stderr,"\ncdrfifo: on read(%d,buffer,%d): eof\n", + o->source_fd,can_read); + if(o->follow_up_fd_idx+1 < o->follow_up_fd_counter) { + idx= ++(o->follow_up_fd_idx); + o->source_fd= o->follow_up_fds[idx][0]; + /* End-Of-Previous */ + if(o->write_idx==0) + o->follow_up_eop[idx]= o->buffer_size; + else + o->follow_up_eop[idx]= o->write_idx; + /* Start-Of-Data . Try to start at next full chunk */ + sod= o->write_idx; + if(o->write_idx%o->chunk_size) + sod+= o->chunk_size - (o->write_idx%o->chunk_size); + /* but do not catch up to the read pointer */ + if((o->write_idx<=o->read_idx && o->read_idx<=sod) || sod==o->read_idx) + sod= o->write_idx; + if(sod>=o->buffer_size) + sod= 0; + o->follow_up_sod[idx]= sod; + o->write_idx= sod; + if(Cdrfifo_debuG || (flag&1)) + fprintf(stderr,"\ncdrfio: new fifo source fd : %d\n",o->source_fd); + } else { + o->source_fd= -1; + } + } else { + did_work= 1; + o->put_counter++; + o->in_counter+= ret; + o->write_idx+= ret; + if(o->write_idx>=o->buffer_size) + o->write_idx= 0; + if(o->write_idx==o->read_idx) + o->buffer_is_full= 1; + } + } + ret= !!did_work; +ex:; + return(ret); +} + + +/** Check for pending data at the fifo's source file descriptor and wether the + fifo is ready to take them. Simultaneously check the buffer for existing + data and the destination fd for readiness to accept some. If so, a small + chunk of data is transfered to and/or from the fifo. + This is done for the given fifo object and all members of its next-chain. + The check and transactions are repeated until a given timespan has elapsed. + libburn applications call this function in the burn loop instead of sleep(). + It may also be used instead of read(). Then it returns as soon as an output + transaction would be performed. See flag:bit2. + @param o The fifo object + @param wait_usec The time in microseconds after which the function shall + return. + @param reply_buffer with bit2: Returns write-ready buffer chunk and must + be able to take at least chunk_size bytes + @param reply_count with bit2: Returns number of writeable bytes in reply + @param flag Bitfield for control purposes: + bit0= Enable debug pacifier (same with Cdrfifo_debuG) + bit1= Do not write, just fill buffer + bit2= fd-to-memory mode (else fd-to-fd mode): + Rather than writing a chunk return it and its size. + No simultaneous processing of chained fifos. + bit3= With bit2: do not check destination fd for readiness + @return <0 = error , 0 = idle , 1 = did some work , 2 = all work is done +*/ +int Cdrfifo_try_to_work(struct CdrfifO *o, int wait_usec, + char *reply_buffer, int *reply_count, int flag) +{ + struct timeval wt,start_tv,current_tv; + struct timezone tz; + fd_set rds,wts,exs; + int ready,ret,max_fd= -1,buffer_space,dummy,still_active= 0; + int did_work= 0,elapsed,still_to_wait,speed_limiter= 0,ready_to_write; + double start_out_counter; + struct CdrfifO *ff; + + start_out_counter= o->out_counter; + gettimeofday(&start_tv,&tz); + still_to_wait= wait_usec; + if(flag&4) + *reply_count= 0; + +try_again:; + /* is there still a destination open ? */ + for(ff= o; ff!=NULL; ff= ff->next) + if(ff->dest_fd!=-1) + break; + if(ff==NULL) + return(2); + FD_ZERO(&rds); + FD_ZERO(&wts); + FD_ZERO(&exs); + + for(ff= o; ff!=NULL; ff= ff->next) { + ret= Cdrfifo_setup_try(ff,start_tv,start_out_counter, + &still_to_wait,&speed_limiter,&ready_to_write, + &rds,&wts,&max_fd,flag&15); + if(ret<=0) + return(ret); + else if(ret==2) { + /* This fifo is done */; + } else + still_active= 1; + if(flag&2) + break; + } + if(!still_active) + return(2); + + if(still_to_wait>0 || max_fd>=0) { + wt.tv_sec= still_to_wait/1000000; + wt.tv_usec= still_to_wait%1000000; + ready= select(max_fd+1,&rds,&wts,&exs,&wt); + } else + ready= 0; + if(ready<=0) { + if(!ready_to_write) + goto check_wether_done; + FD_ZERO(&rds); + } + if(ready_to_write) + FD_SET((o->dest_fd),&wts); + + for(ff= o; ff!=NULL; ff= ff->next) { + ret= Cdrfifo_transact(ff,&rds,&wts,reply_buffer,reply_count,flag&15); + if(ret<0) + goto ex; + if(ret>0) + did_work= 1; + if(flag&2) + break; + } + +check_wether_done:; + if((flag&4) && *reply_count>0) + {ret= 1; goto ex;} + gettimeofday(¤t_tv,&tz); + elapsed= (current_tv.tv_sec-start_tv.tv_sec)*1000000 + + (((int) current_tv.tv_usec) - ((int) start_tv.tv_usec)); + still_to_wait= wait_usec-elapsed; + if(still_to_wait>0) + goto try_again; + ret= !!did_work; +ex:; + if(flag&4) { + gettimeofday(¤t_tv,&tz); + elapsed= (current_tv.tv_sec - o->interval_start_time.tv_sec)*1000000 + + (((int) current_tv.tv_usec) + - ((int) o->interval_start_time.tv_usec)); + } else + elapsed= wait_usec; + if(elapsed>=wait_usec) { + if((flag&1)||Cdrfifo_debuG) { + fprintf(stderr,"\n"); + for(ff= o; ff!=NULL; ff= ff->next) { + buffer_space= Cdrfifo_tell_buffer_space(ff,0); + fprintf(stderr, + "cdrfifo_debug: w=%d r=%d | b=%d s=%d | i=%.f o=%.f\n", + ff->write_idx,ff->read_idx, + ff->buffer_size-buffer_space,buffer_space, + ff->in_counter,ff->out_counter); + } + } + if(flag&4) + Cdrfifo_next_interval(o,&dummy,0); + } + return(ret); +} + + +/** Fill the fifo as far as possible without writing to destination fd */ +int Cdrfifo_fill(struct CdrfifO *o, int flag) +{ + int ret,fill= 0,space,state; + + while(1) { + state= Cdrfifo_get_buffer_state(o,&fill,&space,0); + if(state==-1) { + + /* >>> handle error */; + + return(0); + } else if(state!=1) + break; + if(space<=0) + break; + ret= Cdrfifo_try_to_work(o,100000,NULL,NULL,2); + if(ret<0) { + + /* >>> handle error */; + + return(0); + } + if(ret==2) + break; + } + o->total_min_fill= fill; + o->interval_min_fill= fill; + return(1); +} + + +int Cdrfifo_close_all(struct CdrfifO *o, int flag) +{ + struct CdrfifO *ff; + + if(o==NULL) + return(0); + for(ff= o; ff->prev!=NULL; ff= ff->prev); + for(; ff!=NULL; ff= ff->next) + Cdrfifo_close(ff,0); + return(1); +} + + + +#ifdef Cdrfifo_standalonE + +/* ---------------------------------------------------------------------- */ + +/** Application example. See also cdrskin.c */ + + +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); +} + + +/* This is a hardcoded test mock-up for two simultaneous fifos of which the + first one simulates the cdrskin fifo feeding libburn and the second one + simulates libburn and the burner at given speed. Both have two fd pairs + (i.e. tracks). The tracks are read from /u/test/cdrskin/in_[12] and + written to /u/test/cdrskin/out_[12]. +*/ +int Test_multi(int fs_size, double speed_limit, double interval, int flag) +/* + bit0= debugging verbousity +*/ +{ + int fd_in[4],fd_out[4],ret,pipe_fds[4][2],real_out[4],pipe_idx; + int i,iv; + char buf[10240]; + struct CdrfifO *ff1= NULL,*ff2= NULL; + + /* open four pairs of fds */ + fd_in[0]= open("/u/test/cdrskin/in_1",O_RDONLY); + fd_in[1]= open("/u/test/cdrskin/in_2",O_RDONLY); + fd_out[2]= open("/u/test/cdrskin/out_1", + O_WRONLY|O_CREAT|O_TRUNC, S_IRUSR|S_IWUSR); + fd_out[3]= open("/u/test/cdrskin/out_2", + O_WRONLY|O_CREAT|O_TRUNC, S_IRUSR|S_IWUSR); + if(pipe(pipe_fds[0])==-1) + return(-3); + if(pipe(pipe_fds[1])==-1) + return(-3); + fd_out[0]= pipe_fds[0][1]; + fd_out[1]= pipe_fds[1][1]; + fd_in[2]= pipe_fds[0][0]; + fd_in[3]= pipe_fds[1][0]; + for(i=0;i<4;i++) { + if(fd_in[i]==-1) + return(-1); + if(fd_out[i]==-1) + return(-2); + } + + /* Create two fifos with two sequential fd pairs each and chain them for + simultaneous usage. */ + Cdrfifo_new(&ff1,fd_in[0],fd_out[0],2048,fs_size,0); + Cdrfifo_new(&ff2,fd_in[2],fd_out[2],2048,2*1024*1024,0); /*burner cache 2 MB*/ + if(ff1==NULL || ff2==NULL) + return(-3); + Cdrfifo_set_speed_limit(ff2,speed_limit,0); + ret= Cdrfifo_attach_follow_up_fds(ff1,fd_in[1],fd_out[1],0); + if(ret<=0) + return(-4); + ret= Cdrfifo_attach_follow_up_fds(ff2,fd_in[3],fd_out[3],0); + if(ret<=0) + return(-4); + Cdrfifo_attach_peer(ff1,ff2,0); + + /* Let the fifos work */ + iv= interval*1e6; + while(1) { + ret= Cdrfifo_try_to_work(ff1,iv,NULL,NULL,flag&1); + if(ret<0 || ret==2) { /* <0 = error , 2 = work is done */ + fprintf(stderr,"\ncdrfifo: fifo ended work with ret=%d\n",ret); + if(ret<0) + return(-7); + break; + } + } + return(1); +} + + +int main(int argc, char **argv) +{ + int i,ret,exit_value= 0,verbous= 1,fill_buffer= 0,min_fill,fifo_percent,fd; + double fs_value= 4.0*1024.0*1024.0,bs_value= 2048,in_counter,out_counter; + double interval= 1.0,speed_limit= 0.0; + char output_file[4096]; + struct CdrfifO *ff= NULL; + + strcpy(output_file,"-"); + fd= 1; + + for(i= 1; i1000.0) + interval= 1; + } else if(strncmp(argv[i],"of=",3)==0) { + if(strcmp(argv[i]+3,"-")==0 || argv[i][3]==0) + continue; + fd= open(argv[i]+3,O_WRONLY|O_CREAT); + if(fd<0) { + fprintf(stderr,"cdrfifo: FATAL : cannot open output file '%s'\n", + argv[i]+3); + fprintf(stderr,"cdrfifo: errno=%d , \"%s\"\n", + errno,errno==0?"-no error code available-":strerror(errno)); + {exit_value= 4; goto ex;} + } + } else if(strncmp(argv[i],"sl=",3)==0) { + speed_limit= Scanf_io_size(argv[i]+3,0); + } else if(strncmp(argv[i],"vb=",3)==0) { + sscanf(argv[i]+3,"%d",&verbous); + + } else if(strcmp(argv[i],"-multi_test")==0) { + + if(speed_limit==0.0) + speed_limit= 10*150*1024; + ret= Test_multi((int) fs_value,speed_limit,interval,(verbous>=2)); + fprintf(stderr,"Test_multi(): ret= %d\n",ret); + exit(ret<0); + + } else { + fprintf(stderr,"cdrfifo 0.3 : stdin-to-stdout fifo buffer.\n"); + fprintf(stderr,"usage : %s [bs=block_size] [fl=fillfirst]\n",argv[0]); + fprintf(stderr," [fs=fifo_size] [iv=interval] [of=output_file]\n"); + fprintf(stderr," [sl=bytes_per_second_limit] [vb=verbosity]\n"); + fprintf(stderr,"fl=1 reads full buffer before writing starts.\n"); + fprintf(stderr,"sl>0 allows catch up for whole run time.\n"); + fprintf(stderr,"sl<0 allows catch up for single interval.\n"); + fprintf(stderr,"vb=0 is silent, vb=2 is debug.\n"); + fprintf(stderr,"example: cdrfifo bs=8k fl=1 fs=32m iv=0.1 sl=-5400k\n"); + if(strcmp(argv[i],"-help")!=0 && strcmp(argv[i],"--help")!=0) { + fprintf(stderr,"\ncdrfifo: FATAL : option not recognized: '%s'\n", + argv[i]); + exit_value= 1; + } + goto ex; + } + } + if(verbous>=1) { + fprintf(stderr, + "cdrfifo: bs=%.lf fl=%d fs=%.lf iv=%lf of='%s' sl=%.lf vb=%d\n", + bs_value,fill_buffer,fs_value,interval,output_file,speed_limit, + verbous); + } + + ret= Cdrfifo_new(&ff,0,fd,(int) bs_value,(int) fs_value,0); + if(ret<=0) { + fprintf(stderr, + "cdrfifo: FATAL : creation of fifo object with %.lf bytes failed\n", + fs_value); + {exit_value= 3; goto ex;} + } + if(speed_limit!=0.0) + Cdrfifo_set_speed_limit(ff,speed_limit,0); + if(fill_buffer) { + ret= Cdrfifo_fill(ff,0); + if(ret<=0) { + fprintf(stderr, + "cdrfifo: FATAL : initial filling of fifo buffer failed\n"); + {exit_value= 4; goto ex;} + } + } + while(1) { + ret= Cdrfifo_try_to_work(ff,(int) (interval*1000000.0), + NULL,NULL,(verbous>=2)); + if(ret<0) { + fprintf(stderr,"\ncdrfifo: FATAL : fifo aborted. errno=%d , \"%s\"\n", + errno,errno==0?"-no error code available-":strerror(errno)); + {exit_value= 4; goto ex;} + } else if(ret==2) { + if(verbous>=1) { + double put_counter,get_counter,empty_counter,full_counter; + int total_min_fill; + Cdrfifo_get_counters(ff,&in_counter,&out_counter,0); + fprintf(stderr,"\ncdrfifo: done : %.lf bytes in , %.lf bytes out\n", + in_counter,out_counter); + Cdrfifo_get_min_fill(ff,&total_min_fill,&min_fill,0); + fifo_percent= 100.0*((double) total_min_fill)/fs_value; + if(fifo_percent==0 && total_min_fill>0) + fifo_percent= 1; + Cdrfifo_get_cdr_counters(ff,&put_counter,&get_counter, + &empty_counter,&full_counter,0); + fprintf(stderr,"cdrfifo: fifo had %.lf puts and %.lf gets.\n", + put_counter,get_counter); + fprintf(stderr, +"cdrfifo: fifo was %.lf times empty and %.lf times full, min fill was %d%%.\n", + empty_counter,full_counter,fifo_percent); + } + break; + } + Cdrfifo_next_interval(ff,&min_fill,0); + } + +ex:; + Cdrfifo_destroy(&ff,0); + exit(exit_value); +} + + +#endif /* Cdrfifo_standalonE */ + diff --git a/cdrskin/cdrfifo.h b/cdrskin/cdrfifo.h new file mode 100644 index 0000000..42461d2 --- /dev/null +++ b/cdrskin/cdrfifo.h @@ -0,0 +1,144 @@ + +/* + cdrfifo.c , Copyright 2006 Thomas Schmitt + + A fd-to-fd or fd-to-memory fifo to be used within cdrskin or independently. + By chaining of fifo objects, several fifos can be run simultaneously + in fd-to-fd mode. Modes are controlled by parameter flag of + Cdrfifo_try_to_work(). + + Provided under GPL license within cdrskin and under BSD license elsewise. +*/ + +#ifndef Cdrfifo_headerfile_includeD +#define Cdrfifo_headerfile_includeD + + +/** The fifo buffer which will smoothen the data stream from data provider + to data consumer. Although this is not a mandatory lifesavier for modern + burners any more, a fifo can speed up burning of data which is delivered + with varying bandwidths (e.g. compressed archives created on the fly + or mkisofs running at its speed limit.). + This structure is opaque to applications and may only be used via + the Cdrfifo*() methods described in cdrfifo.h . +*/ +struct CdrfifO; + + +/** Create a fifo object. + @param ff Returns the address of the new object. + @param source_fd Filedescriptor opened to a readable data stream. + @param dest_fd Filedescriptor opened to a writable data stream. + To work with libburn, it needs to be attached to a + struct burn_source object. + @param chunk_size Size of buffer block for a single transaction (0=default) + @param buffer_size Size of fifo buffer + @param flag unused yet + @return 1 on success, <=0 on failure +*/ +int Cdrfifo_new(struct CdrfifO **ff, int source_fd, int dest_fd, + int chunk_size, int buffer_size, int flag); + +/** Release from memory a fifo object previously created by Cdrfifo_new(). + @param ff The victim (gets returned as NULL, call can stand *ff==NULL) + @param flag Bitfield for control purposes: + bit0= do not close destination fd +*/ +int Cdrfifo_destroy(struct CdrfifO **ff, int flag); + +/** Close any output fds */ +int Cdrfifo_close(struct CdrfifO *o, int flag); + +/** Close any output fds of o and its chain peers */ +int Cdrfifo_close_all(struct CdrfifO *o, int flag); + +int Cdrfifo_get_sizes(struct CdrfifO *o, int *chunk_size, int *buffer_size, + int flag); + +/** Set a speed limit for buffer output. + @param o The fifo object + @param bytes_per_second >0 catch up slowdowns over the whole run time + <0 catch up slowdowns only over one interval + =0 disable speed limit +*/ +int Cdrfifo_set_speed_limit(struct CdrfifO *o, double bytes_per_second, + int flag); + +int Cdrfifo_set_fds(struct CdrfifO *o, int source_fd, int dest_fd, int flag); +int Cdrfifo_get_fds(struct CdrfifO *o, int *source_fd, int *dest_fd, int flag); + + +/** Attach a further pair of input and output fd which will use the same + fifo buffer when its predecessors are exhausted. Reading will start as + soon as reading of the predecessor encounters EOF. Writing will start + as soon as all pending predecessor data are written. +*/ +int Cdrfifo_attach_follow_up_fds(struct CdrfifO *o, int source_fd, int dest_fd, + int flag); + +/** Attach a further fifo which shall be processed simultaneously with this + one by Cdrfifo_try_to_work() in fd-to-fd mode. +*/ +int Cdrfifo_attach_peer(struct CdrfifO *o, struct CdrfifO *next, int flag); + + +/** Obtain buffer state. + @param o The buffer object + @param fill Returns the number of pending payload bytes in the buffer + @param space Returns the number of unused buffer bytes + @param flag unused yet + @return -1=error , 0=inactive , 1=reading and writing , + 2=reading ended (but still writing) +*/ +int Cdrfifo_get_buffer_state(struct CdrfifO *o,int *fill,int *space,int flag); + +int Cdrfifo_get_counters(struct CdrfifO *o, + double *in_counter, double *out_counter, int flag); + +/** reads min_fill and begins measurement interval for next min_fill */ +int Cdrfifo_next_interval(struct CdrfifO *o, int *min_fill, int flag); + +int Cdrfifo_get_min_fill(struct CdrfifO *o, int *total_min_fill, + int *interval_min_fill, int flag); + +int Cdrfifo_get_cdr_counters(struct CdrfifO *o, + double *put_counter, double *get_counter, + double *empty_counter, double *full_counter, + int flag); + + +/** Check for pending data at the fifo's source file descriptor and wether the + fifo is ready to take them. Simultaneously check the buffer for existing + data and the destination fd for readiness to accept some. If so, a small + chunk of data is transfered to and/or from the fifo. + This is done for the given fifo object and all members of its next-chain. + The check and transactions are repeated until a given timespan has elapsed. + libburn applications call this function in the burn loop instead of sleep(). + It may also be used instead of read(). Then it returns as soon as an output + transaction would be performed. See flag:bit2. + @param o The fifo object + @param wait_usec The time in microseconds after which the function shall + return. + @param reply_buffer with bit2: Returns write-ready buffer chunk and must + be able to take at least chunk_size bytes + @param reply_count with bit2: Returns number of writeable bytes in reply_pt + @param flag Bitfield for control purposes: + bit0= Enable debug pacifier (same with Cdrfifo_debuG) + bit1= Do not write, just fill buffer + bit2= fd-to-memory mode (else fd-to-fd mode): + Rather than writing a chunk return it and its size. + No simultaneous processing of chained fifos. + bit3= With bit2: do not check destination fd for readiness + @return <0 = error , 0 = idle , 1 = did some work , 2 = all work is done +*/ +int Cdrfifo_try_to_work(struct CdrfifO *o, int wait_usec, + char *reply_buffer, int *reply_count, int flag); + +/** Fill the fifo as far as possible without writing to destination fd + @return 1 on success, <=0 on failure +*/ +int Cdrfifo_fill(struct CdrfifO *o, int flag); + + +#endif /* Cdrfifo_headerfile_includeD */ + diff --git a/cdrskin/cdrskin.c b/cdrskin/cdrskin.c new file mode 100644 index 0000000..103803c --- /dev/null +++ b/cdrskin/cdrskin.c @@ -0,0 +1,3956 @@ + +/* + cdrskin.c , Copyright 2006 Thomas Schmitt + +A cdrecord compatible command line interface for libburn. + +Originally inspired by libburn-0.2/test/burniso.c +(c) Derek Foreman and Ben Jansens +Provided under GPL. + +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 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. + + +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/message.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 \ + -lpthread + +*/ + + +/** The official program version */ +#define Cdrskin_prog_versioN "0.1.4" + +/** 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 + +/** use this to accomodate to the CVS version as of Dec 8, 2005 +#define Cdrskin_libburn_cvs_A51208_tS 1 +*/ +#ifdef Cdrskin_libburn_cvs_A51208_tS +#define Cdrskin_libburn_versioN "0.2.tsA51208" +#define Cdrskin_libburn_p_sectoR 1 +/* forever: */ +#define Cdrskin_libburn_no_burn_preset_device_opeN 1 +#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_p_sectoR 1 +#define Cdrskin_libburn_with_fd_sourcE 1 +#define Cdrskin_libburn_largefilE 1 +#define Cdrskin_libburn_padding_does_worK 1 +#define Cdrskin_libburn_no_burn_preset_device_opeN 1 + +#endif /* Cdrskin_libburn_cvs_A60220_tS */ + +#ifdef Cdrskin_libburn_0_2_1 + +#define Cdrskin_libburn_versioN "0.2.1" +#define Cdrskin_libburn_p_sectoR 1 +#define Cdrskin_libburn_with_fd_sourcE 1 +#define Cdrskin_libburn_largefilE 1 +#define Cdrskin_libburn_padding_does_worK 1 + +/* <<< just for now: */ +#define Cdrskin_libburn_no_burn_preset_device_opeN 1 + +#endif /* Cdrskin_libburn_0_2_1 */ + +#ifndef Cdrskin_libburn_versioN +#define Cdrskin_libburn_versioN "0.2.ts" +#endif + +#ifdef Cdrskin_libburn_largefilE +#ifndef _LARGEFILE_SOURCE +#define _LARGEFILE_SOURCE 1 +#endif +#ifndef _FILE_OFFSET_BITS +#define _FILE_OFFSET_BITS 64 +#endif +#endif /* Cdrskin_libburn_largefilE */ + + +/* These macros activate cdrskin workarounds for deficiencies resp. + problematic features of libburn which hopefully will change in + future. */ + +/** Work around the fact that padding is not performed by libburn */ +#ifndef Cdrskin_libburn_padding_does_worK +#define Cdrskin_burn_track_padding_brokeN 1 +#endif + +/** Work around the fact that neither /dev/sg0 (kernel 2.4 + ide-scsi) nor + /dev/hdc (kernel 2.6) get ejected by libburn */ +#define Cdrskin_burn_drive_eject_brokeN 1 + +/** Work around the fact that after loading media speed report is wrong */ +#define Cdrskin_atip_speed_brokeN 1 + +/** Work around the fact that burn_drive_get_status() always reports to do + track 0 */ +#define Cdrskin_progress_track_brokeN 1 + +/** Work around the fact that a drive interrupted at burn_drive_grab() never + leaves status BURN_DRIVE_GRABBING */ +#define Cdrskin_grab_abort_brokeN 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 + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "cleanup.h" + + +/** 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 80 + + +/* --------------------------------------------------------------------- */ + +/* 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;i0) 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; imaxl) + 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;ifrom_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;ifill_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;ifill_counter;i++) + if(strcmp(adr,trn->from_address[i])==0) + break; + if(ifill_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;ifill_counter;i++) + if(strcmp(from,trn->to_address[i])==0 && + strlen(trn->from_address[i])fill_counter) { + ret= 2; + strcpy(to,trn->from_address[i]); + } else { + for(i=0;ifill_counter;i++) + if(strcmp(from,trn->from_address[i])==0) + break; + if(ifill_counter) + if(strlen(from)+strlen(Cdrskin_no_transl_prefiX)boss= boss; + o->trackno= trackno; + o->source_path[0]= 0; + o->source_fd= -1; + o->fixed_size= 0.0; + o->padding= 0.0; + o->fifo_enabled= 0; + o->fifo= NULL; + o->fifo_outlet_fd= -1; + o->fifo_size= 0; + o->fifo_start_empty= 0; + o->libburn_track= NULL; + ret= Cdrskin_get_source(boss,o->source_path,&(o->fixed_size),&(o->padding),0); + if(ret<=0) + goto failed; + +#ifndef Cdrskin_extra_leaN + ret= Cdrskin_get_fifo_par(boss, &(o->fifo_enabled),&(o->fifo_size), + &(o->fifo_start_empty),0); + if(ret<=0) + goto failed; +#endif /* ! Cdrskin_extra_leaN */ + + if(flag&1) + o->fifo_start_empty= 1; + 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_get_size(struct CdrtracK *track, double *size, double *padding, + int flag) +{ + *size= track->fixed_size; + *padding= track->padding; + return(1); +} + + +int Cdrtrack_get_fifo(struct CdrtracK *track, struct CdrfifO **fifo, int flag) +{ + *fifo= track->fifo; + return(1); +} + + +/** 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) +{ + 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= 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)); + } else { + if(track->fixed_size<=0) { + struct stat stbuf; + if(fstat(*fd,&stbuf)!=-1) + track->fixed_size= stbuf.st_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 + @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); + } else { + 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= ff; + } + track->fifo_outlet_fd= pipe_fds[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; + + if(track->fifo==NULL || track->fifo_start_empty) + return(2); + printf("Waiting for reader process to fill input buffer ... "); + fflush(stdout); + ret= Cdrfifo_fill(track->fifo,0); + if(ret<=0) + return(ret); + 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 +*/ +{ + struct burn_track *tr; + struct burn_source *src= NULL; + double padding,lib_padding; + int ret; +#ifdef Cdrskin_libburn_with_fd_sourcE + double fixed_size; + int source_fd; +#endif /* ! Cdrskin_libburn_with_fd_sourcE */ + + track->trackno= trackno; + tr= burn_track_create(); + track->libburn_track= tr; + if(track->padding>0) + padding= track->padding; + else + padding= 0.0; + if(flag&2) + lib_padding= 0.0; + else + lib_padding= padding; + if(flag&1) + 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,1,BURN_MODE1); + +#ifdef Cdrskin_libburn_with_fd_sourcE + if(track->source_fd==-1) { + ret= Cdrtrack_open_source_path(track,&source_fd,0); + if(ret<=0) + goto ex; + } + 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); +#else + src = burn_file_source_new3(track->source_path,NULL,(int) track->fixed_size); +#endif /* ! Cdrskin_libburn_with_fd_sourcE */ + + assert(src); + +#ifndef Cdrskin_libburn_with_fd_sourcE + track->fixed_size= burn_source_get_size(src); + if((flag&2) && track->padding>0) { + if(flag&1) + ClN(fprintf(stderr, + "cdrskin_debug: padding : %.f + %.f = %.f\n", + (double) burn_source_get_size(src),track->padding, + ((double) burn_source_get_size(src))+track->padding)); + + src->fixed_size = burn_source_get_size(src) + track->padding; + + if(flag&1) + ClN(fprintf(stderr,"cdrskin_debug: source size now : %.f\n", + (double) burn_source_get_size(src))); + } +#endif /* ! Cdrskin_libburn_with_fd_sourcE */ + + + if(burn_track_set_source(tr,src) != BURN_SOURCE_OK) + {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); +} + + +#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 3 + +static char Cdrpreskin_sys_rc_nameS[Cdrpreskin_rc_nuM][80]= { + "/etc/default/cdrskin", + "/etc/opt/cdrskin/rc", + "placeholder for $HOME/.cdrskinrc" +}; + + +/** A structure which bundles several parameters for initialization of + libburn and creation of the CdrskiN object. It finally becomes a managed + subordinate of the CdrskiN object. +*/ +struct CdrpreskiN { + + /** 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 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; + + +#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->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->scan_demands_drive= 0; + o->abort_on_busy_drive= 0; + o->drive_exclusive= 1; + o->drive_blocking= 0; + +#ifndef Cdrskin_extra_leaN + o->rc_filename_count= Cdrpreskin_rc_nuM; + for(i=0;irc_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); +} + + +/** 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) + @return <=0 error, 1 success +*/ +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; + + *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) { + 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); + /* look for symbolic bus : 1=/dev/sgN 2=/dev/hdX */ + 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(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(0); + } + } + } + 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;irc_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) + 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;iabort_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"); + +#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],"--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); + fprintf(stderr,"\nTransport name:\t\tlibburn\n"); + fprintf(stderr, + "Transport descr.:\tOpen-source library for writing optical discs\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 (see option --devices)\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],"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 or CD-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 blocker (very 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= 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( + " eject_device= set the device address for command eject\n"); + printf(" --fifo_disable disable fifo despite any fs=...\n"); + printf(" --fifo_per_track use a separate fifo for each track\n"); + printf( + " --fifo_start_empty do not wait for full fifo before burn start\n"); + printf( + " --ignore_signals try to ignore any signals rather than to abort\n"); + printf(" --no_abort_handler exit even if the drive is in busy state\n"); + printf( + " --no_rc as first argument: do not read startup files\n"); + printf( + " --single_track accept only last argument as source_address\n"); + printf(" tao_to_sao_tsize= substitute -tao by -sao and augment\n"); + printf(" input from \"-\" by tsize=\n"); + printf(" (set tao_to_sao_tsize=0 to disable it)\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 $HOME/.cdrskinrc\n"); + printf( + "Each file line is a single argument. No remarks, 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. In this\n"); + printf("case the total byte count of the source must be announced via\n"); + printf("tsize= previous to the source address.\n"); + printf("cdrskin : http://scdbackup.sourceforge.net/cdrskin_eng.html\n"); + printf(" mailto:scdbackup@gmx.net (Thomas Schmitt)\n"); + printf("libburn : http://libburn.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"); + fprintf(stderr, + "\t-atip\t\tretrieve media state, print \"Is *erasable\"\n"); + fprintf(stderr,"\t-raw96r\t\tWrite disk in RAW/RAW96R mode\n"); + fprintf(stderr,"\t-dao\t\tWrite disk in SAO mode.\n"); + fprintf(stderr,"\t-sao\t\tWrite disk in SAO mode.\n"); + fprintf(stderr,"\ttsize=#\t\tannounces exact size of source data\n"); + fprintf(stderr,"\tpadsize=#\tAmount of padding\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)\n"); + fprintf(stderr,"\t-help\t\tprint this text to stderr and exit\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. In this case\n"); + fprintf(stderr, + "the total byte count of the source must be announced via tsize=#.\n"); + fprintf(stderr, + "cdrskin will ensure that the announced tsize= is written even if\n"); + +#ifdef Cdrskin_burn_track_padding_brokeN + fprintf(stderr, + "the source delivers fewer bytes. If the source delivers surplus\n"); + fprintf(stderr, + "bytes, they will replace the eventual padding.\n"); +#else /* Cdrskin_burn_track_padding_brokeN */ + fprintf(stderr,"the source delivers fewer bytes.\n"); +#endif /* ! Cdrskin_burn_track_padding_brokeN */ + +#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_rc")==0) { + if(i!=1) + fprintf(stderr, + "cdrskin: NOTE : option --no_rc would only work as first argument.\n"); + + } else if(strcmp(argv[i],"-scanbus")==0) { + o->no_whitelist= 1; + + } else if(strcmp(argv[i],"-version")==0) { + printf( + "Cdrecord 2.01-Emulation Copyright (C) 2006, see libburn + cdrskin\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(o->allow_setuid==0 && getuid()!=geteuid()) { + fprintf(stderr, + "cdrskin: SORRY : uid and euid differ. Will abort for safety concerns.\n"); + fprintf(stderr, + "cdrskin: HINT : Consider to allow rw-access to the writer device and\n"); + fprintf(stderr, + "cdrskin: HINT : to run cdrskin under your normal user identity.\n"); + fprintf(stderr, + "cdrskin: HINT : Option --allow_setuid disables this safety check.\n"); + ret= 0; + } + + 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,0); + if(ret<=0) { + fprintf(stderr, + "cdrskin: FATAL : dev= expects /dev/xyz, Bus,Target,0 or a number\n"); + {ret= 0; goto ex;} + } + } + } +ex:; + +#ifndef Cdrskin_extra_leaN + if(ret<=0 || !(flag&1)) + Cdradrtrn_destroy(&(o->adr_trn),0); +#endif + + return(ret); +} + + +/* --------------------------------------------------------------------- */ + + + +/** 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 + + +/** 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 payload speed factor for reporting progress: 1x = 150 kB/s */ +static double Cdrskin_cd_speed_factoR= 150.0*1024.0; + +/** The speed conversion factor 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.0; + +/** 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= 50.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 single_track; + + int do_devices; + + int do_scanbus; + + int do_checkdrive; + + int do_atip; + + int do_blank; + int blank_fast; + + int do_burn; + int burnfree; + char write_mode_name[40]; + /** The write mode (like SAO or RAW96/R). See libburn. */ + enum burn_write_types write_type; + int block_type; + + int do_eject; + char eject_device[Cdrskin_strleN]; + + + /** The current data source and its eventual parameters. + source_path may be either "-" for stdin, "#N" for open filedescriptor N + or the address of a readable file. + */ + char source_path[Cdrskin_strleN]; + double fixed_size; + double padding; + int set_by_padsize; + + /** The list of tracks with their data sources and parameters */ + struct CdrtracK *tracklist[Cdrskin_track_maX]; + int track_counter; + /* a guess about what track might be processing right now */ + int supposed_track_idx; + + + int fifo_enabled; + /** Optional fifo between input fd and libburn. It uses a pipe(2) to transfer + data to libburn. This fifo may be actually the start of a chain of fifos + which are to be processed simultaneously. + The fifo object knows the real input fd and the fd[1] of the pipe. + This is just a reference pointer. The fifos are managed by the tracks + which either line up their fifos or share the fifo of the first track. + */ + 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_empty; + int fifo_per_track; + + + /** User defined address translation */ + struct CdradrtrN *adr_trn; + + + /** The drives known to libburn after scan */ + struct burn_drive_info *drives; + unsigned int n_drives; + /** The drive selected for operation by CdrskiN */ + int driveno; + + + /** Progress state info: wether libburn is actually processing payload data */ + int is_writing; + + + /** abort parameters */ + int abort_max_wait; + + /** Engagement info for eventual abort */ + int lib_is_initialized; + pid_t control_pid; /* pid of the thread that calls libburn */ + int drive_is_grabbed; + int drive_is_busy; /* Wether drive was told to do something cancel-worthy */ + struct burn_drive *grabbed_drive; + + /** Abort test facility */ + double abort_after_bytecount; + + + /** Some intermediate option info which is stored until setup finalization */ + double tao_to_sao_tsize; + int stdin_source_used; + +}; + +int Cdrskin_destroy(struct CdrskiN **o, int flag); +int Cdrskin_release_drive(struct CdrskiN *skin, int flag); + + +/** Create a cdrskin program run control object. + @param skin Returns pointer to resulting + @param flag Bitfield for control purposes: + bit0= library is already initialized + @return <=0 error, 1 success +*/ +int Cdrskin_new(struct CdrskiN **skin, struct CdrpreskiN *preskin, int flag) +{ + struct CdrskiN *o; + int ret,i; + + (*skin)= o= TSOB_FELD(struct CdrskiN,1); + if(o==NULL) + return(-1); + o->preskin= preskin; + o->verbosity= 0; + o->x_speed= -1.0; + o->gracetime= 0; + o->dummy_mode= 0; + o->single_track= 0; + o->do_devices= 0; + o->do_scanbus= 0; + o->do_checkdrive= 0; + o->do_atip= 0; + o->do_blank= 0; + o->blank_fast= 0; + o->do_burn= 0; + strcpy(o->write_mode_name,"SAO"); + o->write_type= BURN_WRITE_SAO; + o->block_type= BURN_BLOCK_SAO; + o->burnfree= 0; + o->do_eject= 0; + o->eject_device[0]= 0; + o->source_path[0]= 0; + o->fixed_size= 0.0; + o->padding= 0.0; + o->set_by_padsize= 0; + for(i=0;itracklist[i]= NULL; + o->track_counter= 0; + o->supposed_track_idx= -1; + o->fifo_enabled= 1; + o->fifo= NULL; + o->fifo_outlet_fd= -1; + o->fifo_size= 4*1024*1024; + o->fifo_start_empty= 0; + o->fifo_per_track= 0; + o->adr_trn= NULL; + o->drives= NULL; + o->n_drives= 0; + o->driveno= 0; + o->is_writing= 0; + o->abort_max_wait= 74*60; + o->lib_is_initialized= (flag&1); + o->control_pid= getpid(); + o->drive_is_grabbed= 0; + o->drive_is_busy= 0; + o->grabbed_drive= NULL; + o->abort_after_bytecount= -1.0; + o->tao_to_sao_tsize= 0.0; + o->stdin_source_used= 0; + +#ifndef Cdrskin_extra_leaN + ret= Cdradrtrn_new(&(o->adr_trn),0); + if(ret<=0) + goto failed; +#endif /* ! Cdrskin_extra_leaN */ + + return(1); +failed:; + Cdrskin_destroy(skin,0); + return(-1); +} + + +/** Release from memory a cdrskin object */ +int Cdrskin_destroy(struct CdrskiN **o, int flag) +{ + struct CdrskiN *skin; + int i; + + skin= *o; + if(skin==NULL) + return(0); + if(skin->drive_is_grabbed) + Cdrskin_release_drive(skin,0); + for(i=0;itrack_counter;i++) + Cdrtrack_destroy(&(skin->tracklist[i]),0); + +#ifndef Cdrskin_extra_leaN + Cdradrtrn_destroy(&(skin->adr_trn),0); +#endif /* ! Cdrskin_extra_leaN */ + + Cdrpreskin_destroy(&(skin->preskin),0); + if(skin->drives!=NULL) + burn_drive_info_free(skin->drives); + free((char *) skin); + *o= NULL; + return(1); +} + + +/** Return information about current track source */ +int Cdrskin_get_source(struct CdrskiN *skin, char *source_path, + double *fixed_size, double *padding, int flag) +{ + strcpy(source_path,skin->source_path); + *fixed_size= skin->fixed_size; + *padding= skin->padding; + return(1); +} + + +#ifndef Cdrskin_extra_leaN + +/** Return information about current fifo setting */ +int Cdrskin_get_fifo_par(struct CdrskiN *skin, int *fifo_enabled, + int *fifo_size, int *fifo_start_empty, int flag) +{ + *fifo_enabled= skin->fifo_enabled; + *fifo_size= skin->fifo_size; + *fifo_start_empty= skin->fifo_start_empty; + return(1); +} + + +/** Create and install fifo objects between track data sources and libburn. + The sources and parameters are known to skin. + @return <=0 error, 1 success +*/ +int Cdrskin_attach_fifo(struct CdrskiN *skin, int flag) +{ + struct CdrfifO *ff= NULL; + int ret,i,hflag; + + skin->fifo= NULL; + for(i=0;itrack_counter;i++) { + hflag= (skin->verbosity>=Cdrskin_verbose_debuG); + if(skin->verbosity>=Cdrskin_verbose_cmD) { + if(skin->fifo_per_track) + printf("cdrskin: track %d establishing fifo of %d bytes\n", + i+1,skin->fifo_size); + else if(i==0) + printf("cdrskin: establishing fifo of %d bytes\n",skin->fifo_size); + else { + if(skin->verbosity>=Cdrskin_verbose_debuG) + ClN(fprintf(stderr,"cdrskin_debug: attaching track %d to fifo\n",i+1)); + hflag|= 2; + } + } + ret= Cdrtrack_attach_fifo(skin->tracklist[i],&(skin->fifo_outlet_fd),ff, + hflag); + if(ret<=0) { + fprintf(stderr,"cdrskin: FATAL : failed to attach fifo.\n"); + return(0); + } + if(i==0 || skin->fifo_per_track) + Cdrtrack_get_fifo(skin->tracklist[i],&ff,0); + if(i==0) + skin->fifo= ff; + } + return(1); +} + + +/** Read data into the track fifos until either #1 is full or its data source + is exhausted. + @return <=0 error, 1 success +*/ +int Cdrskin_fill_fifo(struct CdrskiN *skin, int flag) +{ + int ret; + + ret= Cdrtrack_fill_fifo(skin->tracklist[0],0); + if(ret<=0) + return(ret); + printf("input buffer ready.\n"); + fflush(stdout); + return(1); +} + +#endif /* ! Cdrskin_extra_leaN */ + + +/** Inform libburn about the consumer x-speed factor of skin */ +int Cdrskin_adjust_speed(struct CdrskiN *skin, int flag) +{ + int k_speed; + + if(skin->x_speed<0) + k_speed= 0; /* libburn.h promises 0 to be max speed. */ + else if(skin->x_speed==0) /* cdrecord specifies 0 as minimum speed. */ + k_speed= Cdrskin_libburn_cd_speed_factoR+Cdrskin_libburn_cd_speed_addoN; + else + k_speed= skin->x_speed*Cdrskin_libburn_cd_speed_factoR + + Cdrskin_libburn_cd_speed_addoN; + + if(skin->verbosity>=Cdrskin_verbose_debuG) + ClN(fprintf(stderr,"cdrskin_debug: k_speed= %d\n",k_speed)); + + burn_drive_set_speed(skin->drives[skin->driveno].drive,k_speed,k_speed); + return(1); +} + + +/** Obtain access to a libburn drive for writing or information retrieval + @return <=0 error, 1 success +*/ +int Cdrskin_grab_drive(struct CdrskiN *skin, int flag) +{ + int restore_handler= 0,ret; + struct burn_drive *drive; + + drive= skin->drives[skin->driveno].drive; + skin->grabbed_drive= drive; + +#ifdef Cdrskin_grab_abort_brokeN + /* There seems to be no way to get a drive out of status BURN_DRIVE_GRABBING + So try to block out signals if there is a signal handler installed */ + if(skin->preskin->abort_handler==1 || + skin->preskin->abort_handler==3 || + skin->preskin->abort_handler==4) { + Cleanup_set_handlers(NULL,NULL,2); + restore_handler= 1; + } +#endif /* Cdrskin_grab_abort_brokeN */ + + /* RIP-14.5 + LITE-ON 48125S produce a false status if tray was unloaded */ + /* Therefore the first grab is just for loading */ + if(!burn_drive_grab(drive, 1)) { +unable:; + fprintf(stderr, "cdrskin: FATAL : unable to open drive %d\n",skin->driveno); + ret= 0; goto ex; + } + skin->drive_is_grabbed= 1; + burn_drive_release(drive,0); + skin->drive_is_grabbed= 0; + /* now grab the drive for real */ + if(!burn_drive_grab(drive, 1)) + goto unable; + skin->drive_is_grabbed= 1; + ret= 1; +ex:; + +#ifdef Cdrskin_grab_abort_brokeN + if(restore_handler) { + int Cdrskin_abort_handler(struct CdrskiN *, int, int); + Cleanup_set_handlers(skin,(Cleanup_app_handler_T) Cdrskin_abort_handler,4); + } +#endif /* Cdrskin_grab_abort_brokeN */ + + if(ret<=0) { + skin->drive_is_grabbed= 0; + skin->grabbed_drive= NULL; + } + return(ret); +} + + +/** Release grabbed libburn drive */ +int Cdrskin_release_drive(struct CdrskiN *skin, int flag) +{ + if((!skin->drive_is_grabbed) || skin->grabbed_drive==NULL) { + fprintf(stderr,"cdrskin: CAUGHT : release of non-grabbed drive.\n"); + return(0); + } + burn_drive_release(skin->grabbed_drive,skin->do_eject); + skin->drive_is_grabbed= 0; + skin->grabbed_drive= NULL; + return(1); +} + + +/** Clean up resources in abort situations. To be called by Cleanup subsystem + but hardly ever by the application. The program must exit afterwards. +*/ +int Cdrskin_abort_handler(struct CdrskiN *skin, int signum, int flag) +{ + int wait_grain= 100000,first_status= 1; + struct burn_progress p; + enum burn_drive_status drive_status= BURN_DRIVE_GRABBING; + double start_time,last_time,current_time; + + if(getpid()!=skin->control_pid) { + if(skin->verbosity>=Cdrskin_verbose_debuG) + ClN(fprintf(stderr, + "cdrskin_debug: ABORT : thread rejected: pid=%d, signum=%d\n", + getpid(),signum)); + return(2); /* do only process the control thread */ + } + + if(skin->preskin->abort_handler==3) + Cleanup_set_handlers(NULL,NULL,2); /* ignore all signals */ + else if(skin->preskin->abort_handler==4) + Cleanup_set_handlers(NULL,NULL,1); /* allow abort */ + fprintf(stderr, + "\ncdrskin: ABORT : handling started. Please do not press CTRL+C now.\n"); + if(skin->preskin->abort_handler==3) + fprintf(stderr,"cdrskin: ABORT : trying to ignore any further signals\n"); + +#ifndef Cdrskin_extra_leaN + if(skin->fifo!=NULL) + Cdrfifo_close_all(skin->fifo,0); +#endif + + if(skin->grabbed_drive!=NULL) { + drive_status= burn_drive_get_status(skin->grabbed_drive,&p); + if(drive_status!=BURN_DRIVE_IDLE && !skin->drive_is_grabbed) + skin->drive_is_grabbed= 2; + if(drive_status!=BURN_DRIVE_IDLE && !skin->drive_is_busy) + skin->drive_is_busy= 2; + if(skin->verbosity>=Cdrskin_verbose_debuG) + ClN(fprintf(stderr,"cdrskin_debug: ABORT : drive status: %d\n", + (int) drive_status)); + } + if(skin->verbosity>=Cdrskin_verbose_debuG) + ClN(fprintf(stderr, + "cdrskin_debug: ABORT : drive_is_grabbed=%d , drive_is_busy=%d (%X)\n", + skin->drive_is_grabbed,skin->drive_is_busy, + (unsigned int) skin->grabbed_drive)); + + if(skin->drive_is_grabbed) { + if(skin->drive_is_busy && skin->grabbed_drive!=NULL) { + if(drive_status==BURN_DRIVE_WRITING || drive_status==BURN_DRIVE_READING) { + fprintf(stderr,"cdrskin: ABORT : trying to cancel drive operation.\n"); + burn_drive_cancel(skin->grabbed_drive); + } else if(drive_status==BURN_DRIVE_GRABBING) { + + /* >>> what to do in this state ? */; + + } else if(drive_status!=BURN_DRIVE_IDLE) { + fprintf(stderr, + "cdrskin: ABORT : will wait for current operation to end.\n"); + } + + last_time= start_time= Sfile_microtime(0); + while(1) { + drive_status= burn_drive_get_status(skin->grabbed_drive,&p); + if(drive_status==BURN_DRIVE_IDLE) + break; + usleep(wait_grain); + current_time= Sfile_microtime(0); + if(current_time-last_time>=1.0) { + if(first_status) + fprintf(stderr,"\n"); + first_status= 0; + fprintf(stderr,"\rcdrskin: ABORT : Status %d. Waiting for status %d since %d seconds (%d max)", + (int) drive_status, (int) BURN_DRIVE_IDLE, + (int) (current_time-start_time),skin->abort_max_wait); + last_time= current_time; + } + if(current_time-start_time>=skin->abort_max_wait) { + fprintf(stderr, + "\ncdrskin: ABORT : cannot cancel burn session and release drive.\n"); + return(0); + } + } + fprintf(stderr,"\ncdrskin: ABORT : Status %d.\n",(int) drive_status); + } + fprintf(stderr,"cdrskin: ABORT : trying to release drive.\n"); + Cdrskin_release_drive(skin,0); + } + if(skin->lib_is_initialized) { + fprintf(stderr,"cdrskin: ABORT : trying to finish libburn.\n"); + burn_finish(); + } + fprintf(stderr, + "cdrskin: ABORT : Drive is released and library is shut down now.\n"); + fprintf(stderr, + "cdrskin: ABORT : Program done. Even if you do not see a shell prompt.\n"); + return(1); +} + + +/** Convert a libburn device address into a libburn drive number + @return <=0 error, 1 success +*/ +int Cdrskin_driveno_of_location(struct CdrskiN *skin, char *devicename, + int *driveno, int flag) +{ + int i; + + for(i=0;in_drives;i++) { + if(strcmp(skin->drives[i].location,devicename)==0) { + *driveno= i; + return(1); + } + } + return(0); +} + + +/** Convert a cdrskin address into a libburn drive number + @return <=0 error, 1 success +*/ +int Cdrskin_dev_to_driveno(struct CdrskiN *skin, char *in_adr, int *driveno, + int flag) +{ + int ret; + char *adr,translated_adr[Cdrskin_adrleN],synthetic_adr[Cdrskin_adrleN]; + + adr= in_adr; + +#ifndef Cdrskin_extra_leaN + /* user defined address translation */ + ret= Cdradrtrn_translate(skin->adr_trn,adr,-1,translated_adr,0); + if(ret<=0) { + fprintf(stderr, + "cdrskin: FATAL : address translation failed (address too long ?) \n"); + return(0); + } + if(skin->verbosity>=Cdrskin_verbose_cmD && strcmp(adr,translated_adr)!=0) + printf("cdrskin: dev_translation=... : dev='%s' to dev='%s'\n", + adr,translated_adr); + adr= translated_adr; +#endif /* ! Cdrskin_extra_leaN */ + + if(adr[0]=='/') { + ret= Cdrskin_driveno_of_location(skin,adr,driveno,0); + if(ret<=0) { +location_not_found:; + fprintf(stderr, + "cdrskin: FATAL : cannot find '%s' among accessible drive devices.\n", + adr); + fprintf(stderr, + "cdrskin: HINT : use option --devices for a list of drive devices.\n"); + return(0); + } + return(1); + } + ret= Cdrpreskin__cdrecord_to_dev(adr,synthetic_adr,driveno,0); + if(ret<=0) { +wrong_devno:; + if(skin->n_drives<=0) { + fprintf(stderr,"cdrskin: FATAL : No accessible drives.\n"); + } else { + fprintf(stderr, + "cdrskin: FATAL : Address does not lead to an accessible drive: %s\n", + in_adr); + fprintf(stderr, + "cdrskin: HINT : dev= expects /dev/xyz, Bus,Target,0 or a number [0,%d]\n", + skin->n_drives-1); + } + return(0); + } + if(strlen(synthetic_adr)>0) { + if(skin->verbosity>=Cdrskin_verbose_cmD) + printf("cdrskin: converted address '%s' to '%s'\n",adr,synthetic_adr); + ret= Cdrskin_driveno_of_location(skin,synthetic_adr,driveno,0); + if(ret<=0) { + fprintf(stderr, + "cdrskin: failure while using address converted from '%s'\n",adr); + adr= synthetic_adr; + goto location_not_found; + } + } + if((*driveno)>=skin->n_drives || (*driveno)<0) { + fprintf(stderr,"cdrskin: obtained drive number %d from '%s'\n", + *driveno,adr); + goto wrong_devno; + } + return(1); +} + + +/** Convert a libburn drive number into a cdrecord-style address which + represents a device address if possible and the drive number else. + @param flag Bitfield for control purposes: + bit0= do not apply user defined address translation + @return <0 error, 0 drive number, 1 /dev/sgN, 2 /dev/hdX +*/ +int Cdrskin_driveno_to_btldev(struct CdrskiN *skin, int driveno, + char btldev[Cdrskin_adrleN], int flag) +{ + int k,ret,still_untranslated= 1,hret; + char *loc= NULL,buf[Cdrskin_adrleN]; + + if(driveno<0 || driveno>skin->n_drives) + goto fallback; + loc= skin->drives[driveno].location; + if(loc==NULL) + goto fallback; + if(strncmp(loc,"/dev/sg",7)==0) { + for(k= 7;loc[k]!=0;k++) + if(loc[k]<'0' || loc[k]>'9') + break; + if(loc[k]==0 && k>7) { + sprintf(btldev,"1,%s,0",loc+7); + {ret= 1; goto ex;} + } + } + if(strncmp(loc,"/dev/hd",7)==0) + if(loc[7]>='a' && loc[7]<='z') + if(loc[8]==0) { + sprintf(btldev,"2,%d,0",loc[7]-'a'); + {ret= 2; goto ex;} + } +fallback:; + sprintf(btldev,"0,%d,0",driveno); + ret= 0; +ex:; + +#ifndef Cdrskin_extra_leaN + /* user defined address translation */ + if(!(flag&1)) { + if((ret==1 || ret==2)) { + /* try wether a translation points to loc */ + hret= Cdradrtrn_translate(skin->adr_trn,loc,driveno,buf,1); + if(hret==2) { + still_untranslated= 0; + strcpy(btldev,buf); + } + } + if(still_untranslated) + Cdradrtrn_translate(skin->adr_trn,btldev,driveno,buf,1); + strcpy(btldev,buf); + } +#endif /* ! Cdrskin_extra_leaN */ + + return(ret); +} + + +/** Report media status s to the user */ +int Cdrskin_report_disc_status(struct CdrskiN *skin, enum burn_disc_status s, + int flag) +{ + printf("cdrskin: status %d ",s); + if (s==BURN_DISC_FULL) { + printf("burn_disc_full \"There is a disc with data on it in the drive\"\n"); + } else if(s==BURN_DISC_BLANK) { + printf("burn_disc_blank \"The drive holds a blank disc\"\n"); + } else if(s==BURN_DISC_APPENDABLE) { + printf( + "BURN_DISC_APPENDABLE \"There is an incomplete disc in the drive\"\n"); + } else if(s==BURN_DISC_EMPTY) { + printf("BURN_DISC_EMPTY \"There is no disc at all in the drive\"\n"); + } else + printf("-unknown status code-\n"); + return(1); +} + + +/** Perform operations -scanbus or --devices + @param flag Bitfield for control purposes: + bit0= perform --devices rather than -scanbus + @return <=0 error, 1 success +*/ +int Cdrskin_scanbus(struct CdrskiN *skin, int flag) +{ + int ret,i,busno,first_on_bus; + char shellsafe[5*Cdrskin_strleN+2],perms[40],btldev[Cdrskin_adrleN]; + struct stat stbuf; + + if(flag&1) { + printf("cdrskin: Overview of accessible drives (%d found) :\n", + skin->n_drives); + printf("-----------------------------------------------------------------------------\n"); + for(i=0;in_drives;i++) { + if(stat(skin->drives[i].location,&stbuf)==-1) { + sprintf(perms,"errno=%d",errno); + } else { + strcpy(perms,"------"); + if(stbuf.st_mode&S_IRUSR) perms[0]= 'r'; + if(stbuf.st_mode&S_IWUSR) perms[1]= 'w'; + if(stbuf.st_mode&S_IRGRP) perms[2]= 'r'; + if(stbuf.st_mode&S_IWGRP) perms[3]= 'w'; + if(stbuf.st_mode&S_IROTH) perms[4]= 'r'; + if(stbuf.st_mode&S_IWOTH) perms[5]= 'w'; + } + if(strlen(skin->drives[i].location)>=Cdrskin_strleN) + Text_shellsafe("failure:oversized string",shellsafe,0); + else + Text_shellsafe(skin->drives[i].location,shellsafe,0); + printf("%d dev=%s %s : '%s' '%s'\n", + i,shellsafe,perms,skin->drives[i].vendor,skin->drives[i].product); + } + printf("-----------------------------------------------------------------------------\n"); + } else { + printf("Using libburn version '%s'.\n",Cdrskin_libburn_versioN); + for(busno= 0;busno<16;busno++) { + first_on_bus= 1; + for(i=0;in_drives;i++) { + ret= Cdrskin_driveno_to_btldev(skin,i,btldev,1); + if(ret!=busno) + continue; + if(first_on_bus) + printf("scsibus%d:\n",busno); + first_on_bus= 0; + printf("\t%s\t %d) '%s' '%s' '%s' Removable CD-ROM\n", + btldev,i,skin->drives[i].vendor,skin->drives[i].product,"?"); + } + } + } + return(1); +} + + +/** Perform -checkdrive . + @param flag Bitfield for control purposes: + bit0= do not print message about pseudo-checkdrive + @return <=0 error, 1 success +*/ +int Cdrskin_checkdrive(struct CdrskiN *skin, int flag) +{ + struct burn_drive_info *drive_info; + int ret; + char btldev[Cdrskin_adrleN]; + + if(!(flag&1)) + printf("cdrskin: pseudo-checkdrive on drive %d\n",skin->driveno); + if(skin->driveno>=skin->n_drives || skin->driveno<0) { + fprintf(stderr,"cdrskin: FATAL : there is no drive #%d\n",skin->driveno); + {ret= 0; goto ex;} + } + drive_info= &(skin->drives[skin->driveno]); + ret= Cdrskin_driveno_to_btldev(skin,skin->driveno,btldev,0); + if(ret>=0) + fprintf(stderr,"scsidev: '%s'\n",btldev); + printf("Device type : %s\n","Removable CD-ROM"); + printf("Vendor_info : '%s'\n",drive_info->vendor); + printf("Identifikation : '%s'\n",drive_info->product); + printf("Driver flags : %s\n","BURNFREE"); + printf("Supported modes: %s\n","SAO RAW/R96R"); + ret= 1; +ex:; + return(ret); +} + + +/** Perform -atip . + @return <=0 error, 1 success +*/ +int Cdrskin_atip(struct CdrskiN *skin, int flag) +{ + int ret,is_not_really_erasable= 0; + double x_speed; + enum burn_disc_status s; + struct burn_drive *drive; + + printf("cdrskin: pseudo-atip on drive %d\n",skin->driveno); + ret= Cdrskin_checkdrive(skin,1); + if(ret<=0) + return(ret); + drive= skin->drives[skin->driveno].drive; + ret= Cdrskin_grab_drive(skin,0); + if(ret<=0) + return(ret); + while(burn_drive_get_status(drive,NULL)) + sleep(2); + while ((s = burn_disc_get_status(drive)) == BURN_DISC_UNREADY) + sleep(2); + Cdrskin_report_disc_status(skin,s,0); + if(s==BURN_DISC_APPENDABLE) { + is_not_really_erasable= 1; + } else if(s==BURN_DISC_EMPTY) { + if(skin->verbosity>=Cdrskin_verbose_progresS) + printf("Current: none\n"); + ret= 0; goto ex; + } + + +#ifdef Cdrskin_atip_speed_brokeN + + /* <<< terrible stunt to get correct media speed info */ + if(skin->verbosity>=Cdrskin_verbose_debuG) + ClN(fprintf(stderr, + "cdrskin_debug: redoing startup for speed inquiry stabilization\n")); + Cdrskin_release_drive(skin,0); + burn_finish(); + if(!burn_initialize()) { + fflush(stdout); + fprintf(stderr,"cdrskin : FATAL : Re-initialization of libburn failed\n"); + {ret= 0; goto ex;} + } + if(strlen(skin->preskin->device_adr)>0) + burn_drive_add_whitelist(skin->preskin->device_adr); + while (!burn_drive_scan(&(skin->drives), &(skin->n_drives))) ; + drive= skin->drives[skin->driveno].drive; + ret= Cdrskin_grab_drive(skin,0); + if(ret<=0) + return(ret); + +#endif /* Cdrskin_atip_speed_brokeN */ + + + ret= burn_drive_get_write_speed(drive); + x_speed= ((double) ret)/Cdrskin_libburn_cd_speed_factoR; + printf("cdrskin: burn_drive_get_write_speed = %d (%.1fx)\n",ret,x_speed); + if(skin->verbosity>=Cdrskin_verbose_progresS) { + if(burn_disc_erasable(drive)) + printf("Current: CD-RW\n"); + else + printf("Current: CD-R\n"); + } + printf("ATIP info from disk:\n"); + if(burn_disc_erasable(drive)) { + if(is_not_really_erasable) + printf(" Is erasable (but not while in this incomplete state)\n"); + else + printf(" Is erasable\n"); + } else { + printf(" Is not erasable\n"); + } + printf(" 1T speed low: %.f 1T speed high: %.f\n",x_speed,x_speed); + ret= 1; +ex:; + Cdrskin_release_drive(skin,0); + return(ret); +} + + +#ifndef Cdrskin_extra_leaN + +/** Emulate the gracetime= behavior of cdrecord + @param flag Bitfield for control purposes: + bit0= do not print message about pseudo-checkdrive +*/ +int Cdrskin_wait_before_action(struct CdrskiN *skin, int flag) +/* flag: bit0= BLANK rather than write mode */ +{ + int i; + + if(skin->verbosity>=Cdrskin_verbose_progresS) { + char speed_text[80]; + if(skin->x_speed<0) + strcpy(speed_text,"MAX"); + else if(skin->x_speed==0) + strcpy(speed_text,"MIN"); + else + sprintf(speed_text,"%.f",skin->x_speed); + printf( + "Starting to write CD/DVD at speed %s in real %s mode for single session.\n", + speed_text,(flag&1?"BLANK":skin->write_mode_name)); + printf("Last chance to quit, starting real write in %3d seconds.", + skin->gracetime); + fflush(stdout); + } + for(i= skin->gracetime-1;i>=0;i--) { + usleep(1000000); + if(skin->verbosity>=Cdrskin_verbose_progresS) { + printf("\b\b\b\b\b\b\b\b\b\b\b\b\b %3d seconds.",i); + fflush(stdout); + } + } + if(skin->verbosity>=Cdrskin_verbose_progresS) + printf(" Operation starts.\n"); + return(1); +} + +#endif /* Cdrskin_extra_leaN */ + + +/** Perform blank=[all|fast] + @return <=0 error, 1 success +*/ +int Cdrskin_blank(struct CdrskiN *skin, int flag) +{ + enum burn_disc_status s; + struct burn_progress p; + struct burn_drive *drive; + int ret,loop_counter= 0; + double start_time; + + start_time= Sfile_microtime(0); /* will be refreshed later */ + drive= skin->drives[skin->driveno].drive; + ret= Cdrskin_grab_drive(skin,0); + if(ret<=0) + return(ret); + + while(burn_drive_get_status(drive,NULL)) + sleep(2); + while ((s = burn_disc_get_status(drive)) == BURN_DISC_UNREADY) + sleep(2); + if(skin->verbosity>=Cdrskin_verbose_progresS) + Cdrskin_report_disc_status(skin,s,0); + if (s != BURN_DISC_FULL) { + Cdrskin_release_drive(skin,0); + if(s==BURN_DISC_BLANK) { + fprintf(stderr, + "cdrskin: NOTE : blank=... : media was already blank (and still is)\n"); + return(2); + } else if(s==BURN_DISC_APPENDABLE) { + fprintf(stderr, + "cdrskin: FATAL : blank=... : media is still appendable\n"); + } else { + fprintf(stderr, + "cdrskin: FATAL : blank=... : no blankworthy disc found\n"); + } + return(0); + } + if(!burn_disc_erasable(drive)) { + fprintf(stderr, + "cdrskin: FATAL : blank=... : media is not erasable\n"); + return(0); + } + if(skin->dummy_mode) { + fprintf(stderr, + "cdrskin: would have begun to blank disc if not in -dummy mode\n"); + goto blanking_done; + } + fprintf(stderr,"cdrskin: beginning to blank disc\n"); + Cdrskin_adjust_speed(skin,0); + +#ifndef Cdrskin_extra_leaN + Cdrskin_wait_before_action(skin,1); +#endif /* ! Cdrskin_extra_leaN */ + + skin->drive_is_busy= 1; + burn_disc_erase(drive,skin->blank_fast); + + loop_counter= 0; + start_time= Sfile_microtime(0); + while (burn_drive_get_status(drive, &p)!=BURN_DRIVE_IDLE) { + if(loop_counter>0) + if(skin->verbosity>=Cdrskin_verbose_progresS) + +#ifdef Cdrskin_libburn_p_sectoR + fprintf(stderr, + "\rcdrskin: blanking sector %d (%lu seconds elapsed) ", + p.sector,(unsigned long) (Sfile_microtime(0)-start_time)); +#else /* Cdrskin_libburn_p_sectoR */ + fprintf(stderr, + "\rcdrskin: blanking sector %d (%lu seconds elapsed) ", + p.current_sector,(unsigned long) (Sfile_microtime(0)-start_time)); +#endif /* ! Cdrskin_libburn_p_sectoR */ + + sleep(2); + loop_counter++; + } +blanking_done:; + skin->drive_is_busy= 0; + if(skin->verbosity>=Cdrskin_verbose_progresS) { + fprintf(stderr,"\n"); + printf("Blanking time: %.3fs\n",Sfile_microtime(0)-start_time); + fprintf(stderr,"cdrskin: blanking done\n"); + } + Cdrskin_release_drive(skin,0); + return(1); +} + + +/** Report burn progress. This is done partially in cdrecord style. + Actual reporting happens only if write progress hit the next MB or if in + non-write-progress states a second has elapsed since the last report. + After an actual report a new statistics interval begins. + @param drive_status As obtained from burn_drive_get_status() + @param p Progress information from burn_drive_get_status() + @param start_time Timestamp of burn start in seconds + @param last_time Timestamp of report interval start in seconds + @param total_count Returns the total number of bytes written so far + @param total_count Returns the number of bytes written during interval + @param flag Bitfield for control purposes: + bit0= report in growisofs style rather than cdrecord style + @return <=0 error, 1 seems to be writing payload, 2 doing something else +*/ +int Cdrskin_burn_pacifier(struct CdrskiN *skin, + enum burn_drive_status drive_status, + struct burn_progress *p, + double start_time, double *last_time, + double *total_count, double *last_count, int flag) +/* + bit0= growisofs style +*/ +{ + double bytes_to_write,written_bytes= 0.0,written_total_bytes= 0.0,buffer_size; + double fixed_size,padding; + double measured_total_speed,measured_speed; + double elapsed_time,elapsed_total_time,current_time; + double estim_time,estim_minutes,estim_seconds,percent; + int ret,fifo_percent,fill,space,advance_interval=0,new_mb,old_mb,time_to_tell; + int fs,bs,old_track_idx; + char fifo_text[80],mb_text[40]; + + /* for debugging */ + static double last_fifo_in= 0.0,last_fifo_out= 0.0,curr_fifo_in,curr_fifo_out; + + current_time= Sfile_microtime(0); + elapsed_total_time= current_time-start_time; + elapsed_time= current_time-*last_time; + time_to_tell= (elapsed_time>=1.0); + + if(drive_status==BURN_DRIVE_WRITING) { + ; + } else if(drive_status==BURN_DRIVE_WRITING_LEADIN) { + if(time_to_tell || skin->is_writing) { + if(skin->verbosity>=Cdrskin_verbose_progresS) { + if(skin->is_writing) + fprintf(stderr,"\n"); + fprintf(stderr, + "\rcdrskin: writing lead-in since %.f seconds ", + elapsed_total_time); + } + skin->is_writing= 0; + advance_interval= 1; + } + {ret= 2; goto ex;} + } else if(drive_status==BURN_DRIVE_WRITING_LEADOUT) { + if(time_to_tell || skin->is_writing) { + if(skin->verbosity>=Cdrskin_verbose_progresS) { + if(skin->is_writing) + fprintf(stderr,"\n"); + fprintf(stderr, + "\rcdrskin: writing lead-out after %.f seconds ", + elapsed_total_time); + } + skin->is_writing= 0; + advance_interval= 1; + } + {ret= 2; goto ex;} + } else + goto thank_you_for_patience; + + bytes_to_write= ((double) p->sectors)*2048.0; + +#ifdef Cdrskin_libburn_p_sectoR + written_total_bytes= ((double) p->sector)*2048.0; +#else /* Cdrskin_libburn_p_sectoR */ + written_total_bytes= ((double) p->current_sector)*2048.0; +#endif /* ! Cdrskin_libburn_p_sectoR */ + + written_bytes= written_total_bytes-*last_count; + +#ifdef Cdrskin_progress_track_brokeN + /* with libburn.0.2 there is always reported 0 as p->track */ + old_track_idx= skin->supposed_track_idx; + if(written_bytes<0) { /* track hop ? */ + if(skin->supposed_track_idx+1track_counter) + skin->supposed_track_idx++; + } + /* >>> ask eventual fifo about writing fd */; + if(p->track>0) + skin->supposed_track_idx= p->track; +#else /* Cdrskin_progress_track_brokeN */ + skin->supposed_track_idx= p->track; +#endif /* ! Cdrskin_progress_track_brokeN */ + + if(old_track_idx>=0 && old_track_idxsupposed_track_idx) { + Cdrtrack_get_size(skin->tracklist[old_track_idx],&fixed_size,&padding,0); + if(skin->verbosity>=Cdrskin_verbose_progresS) + printf("\n"); + printf("Track %-2.2d: Total bytes read/written: %.f/%.f (%.f sectors).\n", + old_track_idx+1,fixed_size,fixed_size,fixed_size/2048.0); + } + + if(written_total_bytes<1024*1024) { +thank_you_for_patience:; + if(time_to_tell || skin->is_writing) { + if(skin->verbosity>=Cdrskin_verbose_progresS) { + if(skin->is_writing) + fprintf(stderr,"\n"); + fprintf(stderr, + "\rcdrskin: thank you for being patient since %.f seconds ", + elapsed_total_time); + } + skin->is_writing= 0; + advance_interval= 1; + } + {ret= 2; goto ex;} + } + new_mb= written_total_bytes/(1024*1024); + old_mb= (*last_count)/(1024*1024); + if(new_mb==old_mb && !(written_total_bytes>=skin->fixed_size && + skin->fixed_size>0 && time_to_tell)) + {ret= 1; goto ex;} + + +#ifndef Cdrskin_extra_leaN + + percent= 0.0; + if(bytes_to_write>0) + percent= written_total_bytes/bytes_to_write*100.0; + measured_total_speed= 0.0; + measured_speed= 0.0; + estim_time= -1.0; + estim_minutes= -1.0; + estim_seconds= -1.0; + if(elapsed_total_time>0.0) { + measured_total_speed= written_total_bytes/elapsed_total_time; + estim_time= (bytes_to_write-written_bytes)/measured_total_speed; + if(estim_time>0.0 && estim_time<86400.0) { + estim_minutes= ((int) estim_time)/60; + estim_seconds= estim_time-estim_minutes*60.0; + if(estim_seconds<0.0) + estim_seconds= 0.0; + } + } + if(elapsed_time>0.0) + measured_speed= written_bytes/elapsed_time; + if(measured_speed<=0.0 && written_total_bytes>=skin->fixed_size && + skin->fixed_size>0) { + if(!skin->is_writing) + goto thank_you_for_patience; + skin->is_writing= 0; + measured_speed= measured_total_speed; + } else + skin->is_writing= 1; + if(skin->supposed_track_idx<0) + skin->supposed_track_idx= 0; + if(*last_count<=0.0) + printf("%-78.78s\r",""); + if(skin->verbosity>=Cdrskin_verbose_progresS) { + if(flag&1) { + printf("%.f/%.f (%2.1f%%) @%1.1f, remaining %.f:%2.2d\n", + written_total_bytes,bytes_to_write,percent, + measured_speed/Cdrskin_cd_speed_factoR, + estim_minutes,(int) estim_seconds); + } else { + fill= 0; + fifo_percent= 50; + fifo_text[0]= 0; + curr_fifo_in= last_fifo_in; + curr_fifo_out= last_fifo_out; + if(skin->fifo!=NULL) { + ret= Cdrfifo_get_buffer_state(skin->fifo,&fill,&space,0); + buffer_size= fill+space; + if(ret==2 || ret==0) { + fifo_percent= 100; + } else if(ret>0 && buffer_size>0.0) { + /* obtain minimum fill of pacifier interval */ + Cdrfifo_next_interval(skin->fifo,&fill,0); + fifo_percent= 100.0*((double) fill)/buffer_size; + if(fifo_percent<100 && fill>0) + fifo_percent++; + } + if(skin->verbosity>=Cdrskin_verbose_debuG) { + Cdrfifo_get_counters(skin->fifo,&curr_fifo_in,&curr_fifo_out,0); + Cdrfifo_get_sizes(skin->fifo,&bs,&fs,0); + } + } + if(skin->fifo_size>0) { + sprintf(fifo_text,"(fifo %3d%%) ",fifo_percent); + if(skin->verbosity>=Cdrskin_verbose_debuG) { + fprintf(stderr, + "\ncdrskin_debug: fifo >= %9d / %d : %8.f in, %8.f out\n", + fill,space+fill, + curr_fifo_in-last_fifo_in,curr_fifo_out-last_fifo_out); + last_fifo_in= curr_fifo_in; + last_fifo_out= curr_fifo_out; + } + } + if(skin->supposed_track_idx >= 0 && + skin->supposed_track_idx < skin->track_counter) { + Cdrtrack_get_size(skin->tracklist[skin->supposed_track_idx], + &fixed_size,&padding,0); + } else if(skin->fixed_size!=0) { + fixed_size= skin->fixed_size; + padding= skin->padding; + } + if(fixed_size) { + sprintf(mb_text,"%4d of %4d", + (int) (written_total_bytes/1024.0/1024.0), + (int) ((fixed_size+padding)/1024.0/1024.0)); + } else + sprintf(mb_text,"%4d",(int) (written_total_bytes/1024.0/1024.0)); + printf("\rTrack %-2.2d: %s MB written %s[buf 50%%] %4.1fx.", + skin->supposed_track_idx+1,mb_text,fifo_text, + measured_speed/Cdrskin_cd_speed_factoR); + fflush(stdout); + } + if(skin->is_writing==0) { + printf("\n"); + goto thank_you_for_patience; + } + } + +#else /* ! Cdrskin_extra_leaN */ + + if(skin->supposed_track_idx<0) + skin->supposed_track_idx= 0; + if(written_bytes<=0.0 && written_total_bytes>=skin->fixed_size && + skin->fixed_size>0) { + if(!skin->is_writing) + goto thank_you_for_patience; + skin->is_writing= 0; + } else { + if(!skin->is_writing) + printf("\n"); + skin->is_writing= 1; + } + printf("\rTrack %-2.2d: %3d MB written ", + skin->supposed_track_idx+1,(int) (written_total_bytes/1024.0/1024.0)); + fflush(stdout); + if(skin->is_writing==0) + printf("\n"); + +#endif /* Cdrskin_extra_leaN */ + + + advance_interval= 1; + ret= 1; +ex:; + if(advance_interval) { + if(written_total_bytes>0) + *last_count= written_total_bytes; + else + *last_count= 0.0; + if(*last_count>*total_count) + *total_count= *last_count; + *last_time= current_time; + } + return(ret); +} + + +/** Burn data via libburn according to the parameters set in skin. + @return <=0 error, 1 success +*/ +int Cdrskin_burn(struct CdrskiN *skin, int flag) +{ + struct burn_disc *disc; + struct burn_session *session; + struct burn_write_opts *o; + enum burn_disc_status s; + enum burn_drive_status drive_status; + struct burn_progress p; + struct burn_drive *drive; + int ret,loop_counter= 0,max_track= -1,i,hflag; + int fifo_disabled= 0,fifo_percent,total_min_fill,mb; + double put_counter,get_counter,empty_counter,full_counter; + double start_time,last_time; + double total_count= 0.0,last_count= 0.0,size,padding; + + printf("cdrskin: beginning to burn disk\n"); + + drive= skin->drives[skin->driveno].drive; + disc= burn_disc_create(); + session= burn_session_create(); + burn_disc_add_session(disc,session,BURN_POS_END); + + skin->fixed_size= 0.0; + for(i=0;itrack_counter;i++) { + hflag= (skin->verbosity>=Cdrskin_verbose_debuG); +#ifdef Cdrskin_burn_track_padding_brokeN + hflag|= 2; +#endif + ret= Cdrtrack_add_to_session(skin->tracklist[i],i,session,hflag); + if(ret<=0) { + fprintf(stderr,"cdrskin: FATAL : cannot add track %d to session.\n",i+1); + return(0); + } + Cdrtrack_get_size(skin->tracklist[i],&size,&padding,0); + skin->fixed_size+= size+padding; + } + + ret= Cdrskin_grab_drive(skin,0); + if(ret<=0) + return(ret); + + while (burn_drive_get_status(drive, NULL)) + sleep(2); /* >>> ??? add a timeout ? */ + + while ((s = burn_disc_get_status(drive)) == BURN_DISC_UNREADY) + sleep(2); /* >>> ??? add a timeout ? */ + + if(skin->verbosity>=Cdrskin_verbose_progresS) + Cdrskin_report_disc_status(skin,s,0); + + if (s != BURN_DISC_BLANK) { + Cdrskin_release_drive(skin,0); + fprintf(stderr,"cdrskin: FATAL : no blank media detected.\n"); + return(0); + } + + +#ifndef Cdrskin_extra_leaN + + if(skin->verbosity>=Cdrskin_verbose_progresS) { + for(i=0;itrack_counter;i++) { + Cdrtrack_get_size(skin->tracklist[i],&size,&padding,0); + if(size<=0) { + printf("Track %-2.2d: data unknown length",i+1); + } else { + mb= size/1024.0/1024.0; + printf("Track %-2.2d: data %5d MB ",i+1,mb); + } + if(padding>0) + printf(" padsize: %.f KB\n",padding/1024.0); + else + printf("\n"); + } + if(skin->fixed_size<=0) { + printf("Total size: 0 MB (00:00.00) = 0 sectors\n"); + printf("Lout start: 0 MB (00:02/00) = 0 sectors\n"); + } else { + /* >>> This is quite a fake. Need to learn about 12:35.25 and "Lout" + ??? Is there a way to obtain the toc in advance (print_cue()) ? */ + double seconds; + int min,sec,frac; + + mb= skin->fixed_size/1024.0/1024.0; + seconds= skin->fixed_size/150.0/1024.0+2.0; + min= seconds/60.0; + sec= seconds-min*60; + frac= (seconds-min*60-sec)*100; + if(frac>99) + frac= 99; + printf("Total size: %5d MB (%-2.2d:%-2.2d.%-2.2d) = %d sectors\n", + mb,min,sec,frac,(int) (skin->fixed_size/2048)); + seconds+= 2; + min= seconds/60.0; + sec= seconds-min*60; + frac= (seconds-min*60-sec)*100; + if(frac>99) + frac= 99; + printf("Lout start: %5d MB (%-2.2d:%-2.2d/%-2.2d) = %d sectors\n", + mb,min,sec,frac,(int) (skin->fixed_size/2048)); + } + } + + Cdrskin_wait_before_action(skin,0); + ret= Cdrskin_fill_fifo(skin,0); + if(ret<=0) { + fprintf(stderr,"cdrskin: FATAL : filling of fifo failed\n"); + goto ex; + } + +#endif /* ! Cdrskin_extra_leaN */ + + + o= burn_write_opts_new(drive); + burn_write_opts_set_perform_opc(o, 0); + + burn_write_opts_set_write_type(o,skin->write_type,skin->block_type); + if(skin->dummy_mode) { + fprintf(stderr, + "cdrskin: NOTE : -dummy mode will prevent actual writing\n"); + burn_write_opts_set_simulate(o, 1); + } + burn_write_opts_set_underrun_proof(o,skin->burnfree); + + Cdrskin_adjust_speed(skin,0); + if(skin->verbosity>=Cdrskin_verbose_progresS) + printf("Starting new track at sector: 0\n"); + skin->drive_is_busy= 1; + burn_disc_write(o, disc); + if(skin->preskin->abort_handler==-1) + Cleanup_set_handlers(skin,(Cleanup_app_handler_T) Cdrskin_abort_handler,4); + start_time= Sfile_microtime(0); + + burn_write_opts_free(o); + + while (burn_drive_get_status(drive, NULL) == BURN_DRIVE_SPAWNING) { + + /* >>> how do i learn about success or failure ? */ + + ; + } + loop_counter= 0; + while (1) { + drive_status= burn_drive_get_status(drive, &p); + if(drive_status==BURN_DRIVE_IDLE) + break; + + /* >>> how do i learn about success or failure ? */ + + if(loop_counter>0) + Cdrskin_burn_pacifier(skin,drive_status,&p,start_time,&last_time, + &total_count,&last_count,0); + + + /* <<< debugging : artificial abort without a previous signal */; + if(skin->abort_after_bytecount>=0.0 && + total_count>=skin->abort_after_bytecount) { + /* whatever signal handling is installed: this thread is the boss now */ + fprintf(stderr, + "cdrskin: DEVELOPMENT : synthetic abort by abort_after_bytecount=%.f\n", + skin->abort_after_bytecount); + skin->control_pid= getpid(); + ret= Cdrskin_abort_handler(skin,0,0); + fprintf(stderr,"cdrskin: done (aborted)\n"); + exit(1); + } + + + if(max_tracksupposed_track_idx) + max_track= skin->supposed_track_idx; + +#ifndef Cdrskin_extra_leaN + if(skin->fifo==NULL || fifo_disabled) { + usleep(20000); + } else { + ret= Cdrfifo_try_to_work(skin->fifo,20000,NULL,NULL,0); + if(ret<0) { + int abh; + + abh= skin->preskin->abort_handler; + if(abh!=2) + fprintf(stderr, + "\ncdrskin: FATAL : fifo encountered error during burn loop.\n"); + if(abh==0) { + ret= -1; goto ex; + } else if(abh==1 || abh==3 || abh==4 || abh==-1) { + Cdrskin_abort_handler(skin,0,0); + fprintf(stderr,"cdrskin: done (aborted)\n"); + exit(10); + } else { + if(skin->verbosity>=Cdrskin_verbose_debuG) + fprintf(stderr, + "\ncdrskin_debug: Cdrfifo_try_to_work() returns %d\n",ret); + } + } + if(ret==2) { /* <0 = error , 2 = work is done */ + if(skin->verbosity>=Cdrskin_verbose_debuG) + fprintf(stderr,"\ncdrskin_debug: fifo ended work with ret=%d\n",ret); + fifo_disabled= 1; + } + } +#else /* ! Cdrskin_extra_leaN */ + usleep(20000); +#endif /* Cdrskin_extra_leaN */ + + loop_counter++; + } + skin->drive_is_busy= 0; + if(skin->verbosity>=Cdrskin_verbose_progresS) + printf("\n"); + if(max_track<=0) { + printf("Track 01: Total bytes read/written: %.f/%.f (%.f sectors).\n", + total_count,total_count,total_count/2048.0); + } else { + Cdrtrack_get_size(skin->tracklist[max_track],&size,&padding,0); + printf( + "Track %-2.2d: Total bytes read/written: %.f/%.f (%.f sectors).\n", + max_track+1,size,size,size/2048.0); + } + if(skin->verbosity>=Cdrskin_verbose_progresS) + printf("Writing time: %.3fs\n",Sfile_microtime(0)-start_time); + + +#ifndef Cdrskin_extra_leaN + + if(skin->fifo!=NULL && skin->fifo_size>0) { + int dummy,final_fill; + Cdrfifo_get_buffer_state(skin->fifo,&final_fill,&dummy,0); + if(final_fill>0) { +fifo_full_at_end:; + fprintf(stderr, + "cdrskin: FATAL : Fifo still contains data after burning has ended.\n"); + fprintf(stderr, + "cdrskin: FATAL : %.d bytes left.\n",final_fill); + 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"); + ret= -1; goto ex; + } + +#ifdef Cdrskin_libburn_leaves_inlet_opeN + for(i= 0;itrack_counter;i++) { + ret= Cdrtrack_has_input_left(skin->tracklist[i],0); + if(ret>0) { + fprintf(stderr, + "cdrskin: FATAL : fifo outlet of track #%d is still buffering some bytes.\n", + i+1); + goto fifo_full_at_end; + } + } +#endif /* Cdrskin_libburn_leaves_inlet_opeN */ + + } + + if(skin->verbosity>=Cdrskin_verbose_progresS) { + if(skin->fifo!=NULL && skin->fifo_size>0) { + int dummy; + + Cdrfifo_get_min_fill(skin->fifo,&total_min_fill,&dummy,0); + fifo_percent= 100.0*((double) total_min_fill)/(double) skin->fifo_size; + if(fifo_percent==0 && total_min_fill>0) + fifo_percent= 1; + Cdrfifo_get_cdr_counters(skin->fifo,&put_counter,&get_counter, + &empty_counter,&full_counter,0); + fflush(stdout); + fprintf(stderr,"Cdrskin: fifo had %.f puts and %.f gets.\n", + put_counter,get_counter); + fprintf(stderr, + "Cdrskin: fifo was %.f times empty and %.f times full, min fill was %d%%.\n", + empty_counter,full_counter,fifo_percent); + } + printf("Min drive buffer fill was 50%%\n"); + } + +#endif /* ! Cdrskin_extra_leaN */ + + + if(skin->verbosity>=Cdrskin_verbose_progresS) + printf("cdrskin: burning done\n"); + ret= 1; +ex:; + skin->drive_is_busy= 0; + if(skin->verbosity>=Cdrskin_verbose_debuG) + ClN(printf("cdrskin_debug: do_eject= %d\n",skin->do_eject)); + Cdrskin_release_drive(skin,0); + for(i= 0;itrack_counter;i++) + Cdrtrack_cleanup(skin->tracklist[i],0); + burn_session_free(session); + burn_disc_free(disc); + return(ret); +} + + +/** Work around the failure of libburn to eject the tray. + This employs a system(2) call and is therefore an absolute no-no for any + pseudo user identities. + @return <=0 error, 1 success +*/ +int Cdrskin_eject(struct CdrskiN *skin, int flag) +{ + int ret; + +#ifndef Cdrskin_burn_drive_eject_brokeN + + if(skin->verbosity>=Cdrskin_verbose_debuG) + ClN(fprintf(stderr,"cdrskin_debug: supposing drive eject to have worked\n")); + return(1); + +#else /* Cdrskin_burn_drive_eject_brokeN */ + + char cmd[5*Cdrskin_strleN+16],shellsafe[5*Cdrskin_strleN+2]; + + if(!skin->do_eject) + return(1); + if(skin->verbosity>=Cdrskin_verbose_progresS) + printf("cdrskin: trying to eject media\n"); + if(getuid()!=geteuid()) { + fprintf(stderr, + "cdrskin: SORRY : uid and euid differ. Will not start external eject.\n"); + fprintf(stderr, + "cdrskin: HINT : Consider to allow rw-access to the writer device and\n"); + fprintf(stderr, + "cdrskin: HINT : to run cdrskin under your normal user identity.\n"); + return(0); + } + if(strlen(skin->eject_device)>0) + sprintf(cmd,"eject %s",Text_shellsafe(skin->eject_device,shellsafe,0)); + else if(strcmp(skin->drives[skin->driveno].location,"/dev/sg0")==0) + sprintf(cmd,"eject /dev/sr0"); + else + sprintf(cmd,"eject %s",Text_shellsafe(skin->drives[skin->driveno].location, + shellsafe,0)); + ret= system(cmd); + if(ret==0) + return(1); + return(0); + +#endif /* Cdrskin_burn_drive_eject_brokeN */ + +} + + +/** Interpret all arguments of the program after libburn has been initialized + and drives have been scanned. This call reports to stderr any valid + cdrecord options which are not implemented yet. + @param flag Bitfield for control purposes: + bit0= do not finalize setup + bit1= do not interpret (again) skin->preskin->pre_argv + @return <=0 error, 1 success +*/ +int Cdrskin_setup(struct CdrskiN *skin, int argc, char **argv, int flag) +{ + int i,k,ret; + double value; + char *cpt,*value_pt; + + /* cdrecord 2.01 options which are not scheduled for implementation, yet */ + static char ignored_partial_options[][41]= { + "timeout=", "debug=", "kdebug=", "kd=", "driver=", "ts=", + "pregap=", "defpregap=", "mcn=", "isrc=", "index=", "textfile=", + "pktsize=", "cuefile=", + "" + }; + static char ignored_full_options[][41]= { + "-d", "-Verbose", "-V", "-silent", "-s", "-setdropts", "-prcap", "-inq", + "-reset", "-abort", "-overburn", "-ignsize", "-useinfo", "-format", "-load", + "-lock", "-msinfo", "-toc", "-multi", "-fix", "-nofix", "-waiti", + "-immed", "-force", "-raw", "-raw96p", "-raw16", + "-clone", "-text", "-audio", "-mode2", "-xa", "-xa1", "-xa2", "-xamix", + "-cdi", "-isosize", "-preemp", "-nopreemp", "-copy", "-nocopy", + "-scms", "-shorttrack", "-noshorttrack", "-swab", "-packet", "-noclose", + "" + }; + + /* are we pretending to be cdrecord ? */ + cpt= strrchr(argv[0],'/'); + if(cpt==NULL) + cpt= argv[0]; + else + cpt++; + if(strcmp(cpt,"cdrecord")==0 && !(flag&1)) { + fprintf(stderr,"\n"); + fprintf(stderr, + "Note: This is not cdrecord by Joerg Schilling. Do not bother him.\n"); + fprintf(stderr, + " See cdrskin start message on stdout. See --help. See -version.\n"); + fprintf(stderr,"\n"); + /* allow automatic -tao to -sao redirection */ + skin->tao_to_sao_tsize=650*1024*1024; + } + +#ifndef Cdrskin_extra_leaN + if(!(flag&2)) { + if(skin->preskin->pre_argc>1) { + ret= Cdrskin_setup(skin,skin->preskin->pre_argc,skin->preskin->pre_argv, + flag|1|2); + if(ret<=0) + return(ret); + } + } +#endif + + for (i= 1;iabort_after_bytecount); + + } else if(strcmp(argv[i],"--abort_handler")==0) { + /* is handled in Cdrpreskin_setup() */; + + } else if(strncmp(argv[i],"-abort_max_wait=",16)==0) { + value_pt= argv[i]+16; + goto set_abort_max_wait; + } else if(strncmp(argv[i],"abort_max_wait=",15)==0) { + value_pt= argv[i]+15; +set_abort_max_wait:; + value= Scanf_io_size(value_pt,0); + if(value<0 || value>86400) { + fprintf(stderr, + "cdrskin: NOTE : ignored out-of-range value: abort_max_wait=%s\n", + value_pt); + } else { + skin->abort_max_wait= value; + if(skin->verbosity>=Cdrskin_verbose_cmD) + printf( + "cdrskin: maximum waiting time with abort handling : %d seconds\n", + skin->abort_max_wait); + } + + } else if(strcmp(argv[i],"--allow_setuid")==0) { + /* is handled in Cdrpreskin_setup() */; + + } else if(strcmp(argv[i],"--any_track")==0) { + skin->single_track= -1; + if(skin->verbosity>=Cdrskin_verbose_cmD) + printf( + "cdrskin: --any_track : will accept any unknown option as track source\n"); + + } else if(strcmp(argv[i],"-atip")==0) { + skin->do_atip= 1; + if(skin->verbosity>=Cdrskin_verbose_cmD) + printf("cdrskin: will put out some -atip style line\n"); + + } else if(strncmp(argv[i],"-blank=",7)==0) { + cpt= argv[i]+7; + goto set_blank; + } else if(strncmp(argv[i],"blank=",6)==0) { + cpt= argv[i]+6; +set_blank:; + if(strcmp(cpt,"all")==0 || strcmp(cpt,"disc")==0 + || strcmp(cpt,"disk")==0) { + skin->do_blank= 1; + skin->blank_fast= 0; + } else if(strcmp(cpt,"fast")==0 || strcmp(cpt,"minimal")==0) { + skin->do_blank= 1; + skin->blank_fast= 1; + } else if(strcmp(cpt,"help")==0) { + /* is handled in Cdrpreskin_setup() */; + } else { + fprintf(stderr,"cdrskin: FATAL : blank option '%s' not supported yet\n", + cpt); + return(0); + } + if(skin->verbosity>=Cdrskin_verbose_cmD) + printf("cdrskin: blank mode : blank=%s\n", + (skin->blank_fast?"fast":"all")); + + } else if(strcmp(argv[i],"-checkdrive")==0) { + skin->do_checkdrive= 1; + + } else if(strcmp(argv[i],"-data")==0) { + + /* >>> !!! All Subsequent Tracks Option */ + + /* ??? do we have a non-data mode at all ? */; + + } else if(strcmp(argv[i],"--demand_a_drive")==0) { + /* is handled in Cdrpreskin_setup() */; + + } else if(strcmp(argv[i],"--devices")==0) { + skin->do_devices= 1; + + +#ifndef Cdrskin_extra_leaN + + } else if(strncmp(argv[i],"dev_translation=",16)==0) { + + if(argv[i][16]==0) { + fprintf(stderr, + "cdrskin: FATAL : dev_translation= : missing separator character\n"); + return(0); + } + ret= Cdradrtrn_add(skin->adr_trn,argv[i]+17,argv[i]+16,1); + if(ret==-2) + 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); + +#endif /* Cdrskin_extra_leaN */ + + + } else if(strncmp(argv[i],"-dev=",5)==0) { + /* is handled in Cdrpreskin_setup() */; + } else if(strncmp(argv[i],"dev=",4)==0) { + /* is handled in Cdrpreskin_setup() */; + + } else if(strcmp(argv[i],"--drive_abort_on_busy")==0) { + /* is handled in Cdrpreskin_setup() */; + + } else if(strcmp(argv[i],"--drive_blocking")==0) { + /* is handled in Cdrpreskin_setup() */; + + } else if(strcmp(argv[i],"--drive_not_exclusive")==0) { + /* is handled in Cdrpreskin_setup() */; + + } else if(strncmp(argv[i],"-driveropts=",12)==0) { + value_pt= argv[i]+12; + goto set_driveropts; + } else if(strncmp(argv[i],"driveropts=",11)==0) { + value_pt= argv[i]+11; +set_driveropts:; + if(strcmp(value_pt,"burnfree")==0) { + skin->burnfree= 1; + if(skin->verbosity>=Cdrskin_verbose_cmD) + printf("cdrskin: burnfree : on\n"); + } else if(strcmp(argv[i]+11,"noburnfree")==0) { + skin->burnfree= 0; + if(skin->verbosity>=Cdrskin_verbose_cmD) + printf("cdrskin: burnfree : off\n"); + } else if(strcmp(argv[i]+11,"help")==0) { + /* handled in Cdrpreskin_setup() */; + } else + goto ignore_unknown; + + } else if(strcmp(argv[i],"-dummy")==0) { + skin->dummy_mode= 1; + + } else if(strcmp(argv[i],"-eject")==0) { + skin->do_eject= 1; + if(skin->verbosity>=Cdrskin_verbose_cmD) + printf("cdrskin: eject after work : on\n"); + + } else if(strncmp(argv[i],"eject_device=",13)==0) { + if(strlen(argv[i]+13)>=sizeof(skin->eject_device)) { + fprintf(stderr, + "cdrskin: FATAL : eject_device=... too long. (max: %d, given: %d)\n", + sizeof(skin->eject_device)-1,strlen(argv[i]+13)); + return(0); + } + strcpy(skin->eject_device,argv[i]+13); + if(skin->verbosity>=Cdrskin_verbose_cmD) + printf("cdrskin: eject_device : %s\n",skin->eject_device); + + +#ifndef Cdrskin_extra_leaN + + } else if(strcmp(argv[i],"--fifo_disable")==0) { + skin->fifo_enabled= 0; + skin->fifo_size= 0; + if(skin->verbosity>=Cdrskin_verbose_cmD) + printf("cdrskin: option fs=... disabled\n"); + + } else if(strcmp(argv[i],"--fifo_start_empty")==0) { + skin->fifo_start_empty= 1; + + } else if(strcmp(argv[i],"--fifo_per_track")==0) { + skin->fifo_per_track= 1; + + } else if(strncmp(argv[i],"-fs=",4)==0) { + value_pt= argv[i]+4; + goto fs_equals; + } else if(strncmp(argv[i],"fs=",3)==0) { + value_pt= argv[i]+3; +fs_equals:; + if(skin->fifo_enabled) { + value= Scanf_io_size(value_pt,0); + if(value<0.0 || value>1024.0*1024.0*1024.0) { + fprintf(stderr, + "cdrskin: FATAL : fs=N expects a size between 0 and 1g\n"); + return(0); + } + skin->fifo_size= value; + if(skin->verbosity>=Cdrskin_verbose_cmD) + printf("cdrskin: fifo size : %d\n",skin->fifo_size); + } + + } else if(strncmp(argv[i],"-gracetime=",11)==0) { + value_pt= argv[i]+11; + goto gracetime_equals; + } else if(strncmp(argv[i],"gracetime=",10)==0) { + value_pt= argv[i]+10; +gracetime_equals:; + sscanf(value_pt,"%d",&(skin->gracetime)); + +#else /* ! Cdrskin_extra_leaN */ + + } else if( + strcmp(argv[i],"--fifo_disable")==0 || + strcmp(argv[i],"--fifo_start_empty")==0 || + strcmp(argv[i],"--fifo_per_track")==0 || + strncmp(argv[i],"-fs=",4)==0 || + strncmp(argv[i],"fs=",3)==0 || + strncmp(argv[i],"-gracetime=",11)==0 || + strncmp(argv[i],"gracetime=",10)==0) { + fprintf(stderr, + "cdrskin: NOTE : lean version ignores option: '%s'\n", + argv[i]); + +#endif /* Cdrskin_extra_leaN */ + + + } else if(strcmp(argv[i],"--help")==0) { + /* is handled in Cdrpreskin_setup() */; + + } else if(strcmp(argv[i],"-help")==0) { + /* is handled in Cdrpreskin_setup() */; + + } else if(strcmp(argv[i],"--ignore_signals")==0) { + /* is handled in Cdrpreskin_setup() */; + + } else if(strcmp(argv[i],"--no_abort_handler")==0) { + /* is handled in Cdrpreskin_setup() */; + + } else if(strcmp(argv[i],"--no_rc")==0) { + /* is handled in Cdrpreskin_setup() */; + + } else if(strcmp(argv[i],"-nopad")==0) { + skin->padding= 0.0; + if(skin->verbosity>=Cdrskin_verbose_cmD) + printf("cdrskin: padding : off\n"); + + } else if(strcmp(argv[i],"-pad")==0) { + skin->padding= 15*2048; + skin->set_by_padsize= 0; + if(skin->verbosity>=Cdrskin_verbose_cmD) + printf("cdrskin: padding : %.f\n",skin->padding); + + } else if(strncmp(argv[i],"-padsize=",9)==0) { + value_pt= argv[i]+9; + goto set_padsize; + } else if(strncmp(argv[i],"padsize=",8)==0) { + value_pt= argv[i]+8; +set_padsize:; + skin->padding= Scanf_io_size(argv[i]+8,0); + skin->set_by_padsize= 1; + if(skin->verbosity>=Cdrskin_verbose_cmD) + printf("cdrskin: padding : %.f\n",skin->padding); + + } else if(strcmp(argv[i],"-raw96r")==0) { + strcpy(skin->write_mode_name,"RAW/RAW96R"); + skin->write_type= BURN_WRITE_RAW; + skin->block_type= BURN_BLOCK_RAW96R; + if(skin->verbosity>=Cdrskin_verbose_cmD) + printf("cdrskin: write type : RAW/RAW96R\n"); + + } else if(strcmp(argv[i],"-sao")==0 || strcmp(argv[i],"-dao")==0) { +set_sao:; + strcpy(skin->write_mode_name,"SAO"); + skin->write_type= BURN_WRITE_SAO; + skin->block_type= BURN_BLOCK_SAO; + if(skin->verbosity>=Cdrskin_verbose_cmD) + printf("cdrskin: write type : SAO\n"); + + } else if(strcmp(argv[i],"-scanbus")==0) { + skin->do_scanbus= 1; + + } else if(strcmp(argv[i],"--single_track")==0) { + skin->single_track= 1; + if(skin->verbosity>=Cdrskin_verbose_cmD) + printf( + "cdrskin: --single_track : will only accept last argument as track source\n"); + + } else if(strncmp(argv[i],"-speed=",7)==0) { + value_pt= argv[i]+7; + goto set_speed; + } else if(strncmp(argv[i],"speed=",6)==0) { + value_pt= argv[i]+6; +set_speed:; + sscanf(value_pt,"%lf",&(skin->x_speed)); + if(skin->x_speed<1.0 && skin->x_speed!=0.0 && skin->x_speed!=-1) { + fprintf(stderr,"cdrskin: FATAL : speed= must be -1, 0 or at least 1\n"); + return(0); + } + + /* >>> cdrecord speed=0 -> minimum speed , libburn -> maximum speed */; + + if(skin->verbosity>=Cdrskin_verbose_cmD) + printf("cdrskin: speed : %f\n",skin->x_speed); + + } else if(strcmp(argv[i],"-tao")==0) { + if(skin->tao_to_sao_tsize<=0.0) { + fprintf(stderr,"cdrskin: FATAL : libburn does not support -tao yet.\n"); + fprintf(stderr,"cdrskin: HINT : Try option tao_to_sao_tsize=650m\n"); + return(0); + } + printf("cdrskin: NOTE : substituting mode -tao by mode -sao\n"); + goto set_sao; + + } else if(strncmp(argv[i],"tao_to_sao_tsize=",17)==0) { + skin->tao_to_sao_tsize= Scanf_io_size(argv[i]+17,0); + if(skin->tao_to_sao_tsize>Cdrskin_tracksize_maX) + goto track_too_large; + if(skin->verbosity>=Cdrskin_verbose_cmD) + printf("cdrskin: replace -tao by -sao with fixed size : %.f\n", + skin->tao_to_sao_tsize); + + } else if(strncmp(argv[i],"-tsize=",7)==0) { + value_pt= argv[i]+7; + goto set_tsize; + } else if(strncmp(argv[i],"tsize=",6)==0) { + value_pt= argv[i]+6; +set_tsize:; + skin->fixed_size= Scanf_io_size(value_pt,0); + if(skin->fixed_size>Cdrskin_tracksize_maX) { +track_too_large:; + fprintf(stderr,"cdrskin: FATAL : track size too large\n"); + return(0); + } + if(skin->verbosity>=Cdrskin_verbose_cmD) + printf("cdrskin: fixed track size : %.f\n",skin->fixed_size); + + } else if(strcmp(argv[i],"-v")==0 || strcmp(argv[i],"-verbose")==0) { + (skin->verbosity)++; + /* <<< is much too verbous: burn_set_verbosity(skin->verbosity); */ + printf("cdrskin: verbosity level : %d\n",skin->verbosity); + + } else if( i==argc-1 || + (skin->single_track==0 && strchr(argv[i],'=')==NULL + && !(argv[i][0]=='-' && argv[i][1]!=0) ) || + (skin->single_track==-1)) { + if(strlen(argv[i])>=sizeof(skin->source_path)) { + fprintf(stderr, + "cdrskin: FATAL : source_address too long. (max: %d, given: %d)\n", + sizeof(skin->source_path)-1,strlen(argv[i])); + return(0); + } + strcpy(skin->source_path,argv[i]); + if(strcmp(skin->source_path,"-")==0) { + if(skin->stdin_source_used) { + fprintf(stderr, + "cdrskin: FATAL : \"-\" (stdin) can be used as track source only once.\n"); + return(0); + } + skin->stdin_source_used= 1; + if(skin->write_type!=BURN_WRITE_TAO && + skin->fixed_size<=0.0 && skin->tao_to_sao_tsize>0.0) { + skin->fixed_size= skin->tao_to_sao_tsize; + printf( + "cdrskin: NOTE : augmenting non-tao write mode by tao_to_sao_tsize\n"); + printf("cdrskin: NOTE : fixed size : %.f\n",skin->fixed_size); + } + } else if(skin->preskin->allow_fd_source==0 && + argv[i][0]=='#' && (argv[i][1]>='0' && argv[i][1]<='9')) { + fprintf(stderr, + "cdrskin: SORRY : '%s' is a reserved source path with cdrskin\n", + argv[i]); + fprintf(stderr, + "cdrskin: SORRY : which would use an open file descriptor as source.\n"); + fprintf(stderr, + "cdrskin: SORRY : Its usage is dangerous and disabled for now.\n"); + return(0); + } + + if(skin->track_counter>=Cdrskin_track_maX) { + fprintf(stderr,"cdrskin: FATAL : too many tracks given. (max %d)\n", + Cdrskin_track_maX); + return(0); + } + ret= Cdrtrack_new(&(skin->tracklist[skin->track_counter]),skin, + skin->track_counter,0); + if(ret<=0) { + fprintf(stderr, + "cdrskin: FATAL : creation of track control object failed.\n"); + return(ret); + } + skin->track_counter++; + if(skin->verbosity>=Cdrskin_verbose_cmD) { + if(strcmp(skin->source_path,"-")==0) + printf("cdrskin: track %d data source : '-' (i.e. standard input)\n", + skin->track_counter); + else + printf("cdrskin: track %d data source : '%s'\n", + skin->track_counter,skin->source_path); + } + /* reset track options */ + if(skin->set_by_padsize) + skin->padding= 0; /* cdrecord-ProDVD-2.01b31 resets to 30k + the man page says padsize= is reset to 0 + Joerg Schilling will change in 2.01.01 to 0 */ + skin->fixed_size= 0; + } else { +ignore_unknown:; + fprintf(stderr,"cdrskin: NOTE : ignoring unknown option : '%s'\n", + argv[i]); + } + } + + if(flag&1) /* no finalizing yet */ + return(1); + + if(skin->verbosity>=Cdrskin_verbose_cmD) { + if(skin->preskin->abort_handler==1) + printf("cdrskin: installed abort handler.\n"); + else if(skin->preskin->abort_handler==2) + printf("cdrskin: will try to ignore any signals.\n"); + else if(skin->preskin->abort_handler==3) + printf("cdrskin: installed hard abort handler.\n"); + else if(skin->preskin->abort_handler==4) + printf("cdrskin: installed soft abort handler.\n"); + else if(skin->preskin->abort_handler==-1) + printf("cdrskin: will install abort handler in eventual burn loop.\n"); + } + + if(strlen(skin->preskin->raw_device_adr)>0) { + ret= Cdrskin_dev_to_driveno(skin,skin->preskin->raw_device_adr, + &(skin->driveno),0); + if(ret<=0) + return(ret); + if(skin->verbosity>=Cdrskin_verbose_cmD) + printf("cdrskin: active drive number : %d '%s'\n", + skin->driveno,skin->drives[skin->driveno].location); + } + if(skin->track_counter>0) { + skin->do_burn= 1; + +#ifndef Cdrskin_extra_leaN + ret= Cdrskin_attach_fifo(skin,0); + if(ret<=0) + return(ret); +#endif /* ! Cdrskin_extra_leaN */ + + } + return(1); +} + + +/** Initialize libburn, create a CdrskiN program run control object, + set eventual device whitelist, and obtain the list of available drives. + @param o Returns the CdrskiN object created + @param lib_initialized Returns wether libburn was initialized here + @param exit_value Returns after error the proposal for an exit value + @param flag Unused yet + @return <=0 error, 1 success +*/ +int Cdrskin_create(struct CdrskiN **o, struct CdrpreskiN **preskin, + int *lib_initialized, int *exit_value, int flag) +{ + int ret; + struct CdrskiN *skin; + + *o= NULL; + *exit_value= 0; + *lib_initialized= 0; + + printf("cdrskin: initializing libburn ..."); + fflush(stdout); + if(burn_initialize()) { + printf(" ok\n"); + fflush(stdout); + } else { + printf(" failed\n"); + fflush(stdout); + fprintf(stderr,"cdrskin : FATAL : initialization of libburn failed\n"); + {*exit_value= 11; goto ex;} + } + *lib_initialized= 1; + +#ifndef Cdrskin_libburn_no_burn_preset_device_opeN + burn_preset_device_open((*preskin)->drive_exclusive, + (*preskin)->drive_blocking, + (*preskin)->abort_on_busy_drive); +#endif + + if(strlen((*preskin)->device_adr)>0) { /* disable scan for all others */ + printf("cdrskin: NOTE : greying out all drives besides given dev='%s'\n", + (*preskin)->device_adr); + burn_drive_add_whitelist((*preskin)->device_adr); + } + + ret= Cdrskin_new(&skin,*preskin,1); + if(ret<=0) { + fprintf(stderr,"cdrskin: FATAL : creation of control object failed\n"); + {*exit_value= 2; goto ex;} + } + *preskin= NULL; /* the preskin object now is under management of skin */ + *o= skin; + if(skin->preskin->abort_handler==1 || skin->preskin->abort_handler==3 || + skin->preskin->abort_handler==4) + Cleanup_set_handlers(skin,(Cleanup_app_handler_T) Cdrskin_abort_handler,4); + else if(skin->preskin->abort_handler==2) + Cleanup_set_handlers(skin,(Cleanup_app_handler_T) Cdrskin_abort_handler,2|8); + + printf("cdrskin: scanning for devices ..."); + fflush(stdout); + while (!burn_drive_scan(&(skin->drives), &(skin->n_drives))) { + if(skin->verbosity>=Cdrskin_verbose_debuG) + ClN(fprintf(stderr,"\ncdrskin_debug: ... still scanning ...")); + /* >>> ??? wait a while ? */ + /* >>> ??? set a timeout ? */ + } + printf(" done\n"); + fflush(stdout); +ex:; + return((*exit_value)==0); +} + + +/** Perform the activities which were ordered by setup + @param skin Knows what to do + @param exit_value Returns the proposal for an exit value + @param flag Unused yet + @return <=0 error, 1 success +*/ +int Cdrskin_run(struct CdrskiN *skin, int *exit_value, int flag) +{ + int ret; + + *exit_value= 0; + if(skin->do_devices) { + if(skin->n_drives<=0 && skin->preskin->scan_demands_drive) + {*exit_value= 4; goto no_drive;} + ret= Cdrskin_scanbus(skin,1); + if(ret<=0) { + fprintf(stderr,"cdrskin: FATAL : --devices failed.\n"); + {*exit_value= 4; goto ex;} + } + } + if(skin->do_scanbus) { + if(skin->n_drives<=0 && skin->preskin->scan_demands_drive) + {*exit_value= 5; goto no_drive;} + ret= Cdrskin_scanbus(skin,0); + if(ret<=0) + fprintf(stderr,"cdrskin: FATAL : -scanbus failed.\n"); + {*exit_value= 5*(ret<=0); goto ex;} + } + if(skin->do_checkdrive) { + ret= Cdrskin_checkdrive(skin,0); + {*exit_value= 6*(ret<=0); goto ex;} + } + if(skin->do_atip) { + if(skin->n_drives<=0) + {*exit_value= 7; goto no_drive;} + ret= Cdrskin_atip(skin,0); + if(ret<=0) + {*exit_value= 7; goto ex;} + } + if(skin->do_blank) { + if(skin->n_drives<=0) + {*exit_value= 8; goto no_drive;} + ret= Cdrskin_blank(skin,0); + if(ret<=0) + {*exit_value= 8; goto ex;} + } + if(skin->do_burn) { + if(skin->n_drives<=0) + {*exit_value= 10; goto no_drive;} + ret= Cdrskin_burn(skin,0); + if(ret<=0) + {*exit_value= 10; goto ex;} + } +ex:; + return((*exit_value)==0); +no_drive:; + fprintf(stderr,"cdrskin: FATAL : This run would need an accessible drive\n"); + goto ex; +} + + +int main(int argc, char **argv) +{ + int ret,exit_value= 0,lib_initialized= 0; + struct CdrpreskiN *preskin= NULL; + struct CdrskiN *skin= NULL; + char *lean_id= ""; +#ifdef Cdrskin_extra_leaN + lean_id= ".lean"; +#endif + + printf("cdrskin %s%s : limited cdrecord compatibility wrapper for libburn\n", + Cdrskin_prog_versioN,lean_id); + fflush(stdout); + + ret= Cdrpreskin_new(&preskin,0); + if(ret<=0) { + fprintf(stderr,"cdrskin: FATAL : creation of control object failed\n"); + {exit_value= 2; goto ex;} + } + ret= Cdrpreskin_setup(preskin,argc,argv,0); + if(ret<=0) + {exit_value= 11; goto ex;} + if(ret==2) + {exit_value= 0; goto ex;} + + ret= Cdrskin_create(&skin,&preskin,&lib_initialized,&exit_value,0); + if(ret<=0) + {exit_value= 2; goto ex;} + if(skin->n_drives<=0) { + fprintf(stderr,"cdrskin: NOTE : no usable drive detected.\n"); + if(getuid()!=0) { + fprintf(stderr, + "cdrskin: HINT : Busy drives are invisible. (Busy = open O_EXCL)\n"); + fprintf(stderr, + "cdrskin: HINT : Run this program as superuser with option --devices\n"); + fprintf(stderr, + "cdrskin: HINT : Allow rw-access to the dev='...' file of the burner.\n"); + } + } + + ret= Cdrskin_setup(skin,argc,argv,0); + if(ret<=0) + {exit_value= 3; goto ex;} + if(skin->verbosity>=Cdrskin_verbose_cmD) + printf("cdrskin: called as : %s\n",argv[0]); + + Cdrskin_run(skin,&exit_value,0); + +ex:; + if(skin!=NULL) { + Cleanup_set_handlers(NULL,NULL,1); + Cdrskin_eject(skin,0); + Cdrskin_destroy(&skin,0); + } + Cdrpreskin_destroy(&preskin,0); + if(lib_initialized) + burn_finish(); + exit(exit_value); +} diff --git a/cdrskin/cdrskin_timestamp.h b/cdrskin/cdrskin_timestamp.h new file mode 100644 index 0000000..6af7d76 --- /dev/null +++ b/cdrskin/cdrskin_timestamp.h @@ -0,0 +1 @@ +#define Cdrskin_timestamP "2006.08.17.171327" diff --git a/cdrskin/cleanup.c b/cdrskin/cleanup.c new file mode 100644 index 0000000..e561893 --- /dev/null +++ b/cdrskin/cleanup.c @@ -0,0 +1,184 @@ +/* + cleanup.c , Copyright 2006 Thomas Schmitt + + A signal handler which cleans up an application and exits. + + Provided under GPL license within cdrskin and under BSD license elsewise. +*/ + +/* + cc -g -o cleanup -DCleanup_standalonE cleanup.c +*/ + +#include +#include +#include +#include +#include +#include + +#include +typedef void (*sighandler_t)(int); + + +#include "cleanup.h" + +/* Signals to be caught */ +static int signal_list[]= { + SIGHUP, SIGINT, SIGQUIT, SIGILL, SIGABRT, + SIGFPE, SIGSEGV, SIGPIPE, SIGALRM, SIGTERM, + SIGUSR1, SIGUSR2, SIGXCPU, SIGTSTP, SIGTTIN, + SIGTTOU, + SIGBUS, SIGPOLL, SIGPROF, SIGSYS, SIGTRAP, + SIGVTALRM, SIGXCPU, SIGXFSZ, -1 +}; +static char *signal_name_list[]= { + "SIGHUP", "SIGINT", "SIGQUIT", "SIGILL", "SIGABRT", + "SIGFPE", "SIGSEGV", "SIGPIPE", "SIGALRM", "SIGTERM", + "SIGUSR1", "SIGUSR2", "SIGXCPU", "SIGTSTP", "SIGTTIN", + "SIGTTOU", + "SIGBUS", "SIGPOLL", "SIGPROF", "SIGSYS", "SIGTRAP", + "SIGVTALRM", "SIGXCPU", "SIGXFSZ", "@" +}; +static int signal_list_count= 24; + +/* Signals not to be caught */ +static int non_signal_list[]= { + SIGKILL, SIGCHLD, SIGSTOP, SIGURG, -1 +}; +static int non_signal_list_count= 4; + + +/* run time dynamic part */ +static char cleanup_msg[4096]= {""}; +static int cleanup_exiting= 0; + +static void *cleanup_app_handle= NULL; +static Cleanup_app_handler_T cleanup_app_handler= NULL; +static int cleanup_perform_app_handler_first= 0; + + +static int Cleanup_handler_exit(int exit_value, int signum, int flag) +{ + int ret; + + if(cleanup_perform_app_handler_first) + if(cleanup_app_handler!=NULL) { + ret= (*cleanup_app_handler)(cleanup_app_handle,signum,0); + if(ret==2) + return(2); + } + if(cleanup_exiting) { + if(cleanup_msg[0]!=0) + fprintf(stderr,"%s\n",cleanup_msg); + fprintf(stderr,"cleanup: ABORT : repeat by pid=%d, signum=%d\n", + getpid(),signum); + return(0); + } + cleanup_exiting= 1; + if(cleanup_msg[0]!=0) + fprintf(stderr,"%s\n",cleanup_msg); + alarm(0); + if(!cleanup_perform_app_handler_first) + if(cleanup_app_handler!=NULL) + (*cleanup_app_handler)(cleanup_app_handle,signum,0); + exit(exit_value); +} + + +static void Cleanup_handler_generic(int signum) +{ + int i; + + sprintf(cleanup_msg,"UNIX-SIGNAL caught: %d errno= %d",signum,errno); + for(i= 0; imax_sig) + max_sig= signal_list[i]; + if(signal_list[i]=non_signal_list_count) { + if(i==SIGABRT && (flag&8)) + signal(i,Cleanup_handler_generic); + else + signal(i,sig_handler); + } + } + return(1); +} + + +#ifdef Cleanup_standalonE + +struct Demo_apP { + char *msg; +}; + + +int Demo_app_handler(struct Demo_apP *demoapp, int signum, int flag) +{ + printf("Handling exit of demo application on signal %d. msg=\"%s\"\n", + signum,demoapp->msg); + return(1); +} + + +main() +{ + struct Demo_apP demoapp; + + demoapp.msg= "Good Bye"; + Cleanup_set_handlers(&demoapp,(Cleanup_app_handler_T) Demo_app_handler,0); + + if(1) { /* change to 0 in order to wait for external signals */ + char *cpt= NULL,c; + printf("Intentionally provoking SIGSEGV ...\n"); + c= *cpt; + } else { + printf("killme: %d\n",getpid()); + sleep(3600); + } + + Cleanup_set_handlers(NULL,NULL,1); + exit(0); +} + +#endif /* Cleanup_standalonE */ diff --git a/cdrskin/cleanup.h b/cdrskin/cleanup.h new file mode 100644 index 0000000..a155e05 --- /dev/null +++ b/cdrskin/cleanup.h @@ -0,0 +1,34 @@ +/* + cleanup.c , Copyright 2006 Thomas Schmitt + + A signal handler which cleans up an application and exits. + + Provided under GPL license within cdrskin and under BSD license elsewise. +*/ + +#ifndef Cleanup_includeD +#define Cleanup_includeD 1 + + +/** Layout of an application provided cleanup function using an application + provided handle as first argument and the signal number as second + argument. The third argument is a flag bit field with no defined bits yet. + If the handler returns 2 then it has delegated exit() to some other + instance and the Cleanup handler shall return rather than exit. +*/ +typedef int (*Cleanup_app_handler_T)(void *, int, int); + + +/** Establish exiting signal handlers on (hopefully) all signals that are + not ignored by default or non-catchable. + @param handle Opaque object which knows how to cleanup application + @param handler Function which uses handle to perform application cleanup + @param flag Control Bitfield + bit0= reset to default signal handling +*/ +int Cleanup_set_handlers(void *handle, Cleanup_app_handler_T handler, + int flag); + + +#endif /* ! Cleanup_includeD */ + diff --git a/cdrskin/compile_cdrskin.sh b/cdrskin/compile_cdrskin.sh new file mode 100755 index 0000000..028daed --- /dev/null +++ b/cdrskin/compile_cdrskin.sh @@ -0,0 +1,133 @@ +#!/bin/sh + +# compile_cdrskin.sh +# Copyright 2005 - 2006 Thomas Schmitt, scdbackup@gmx.net, GPL +# to be executed within ./libburn-0.2.1 resp ./cdrskin-0.1.4 + +debug_opts= +def_opts= +libvers="-DCdrskin_libburn_0_2_1" +do_strip=0 +static_opts= +warn_opts="-Wall" +fifo_source="cdrskin/cdrfifo.c" +compile_cdrskin=1 +compile_cdrfifo=0 + +for i in "$@" +do + if test "$i" = "-compile_cdrfifo" + then + compile_cdrfifo=1 + elif test "$i" = "-tarball_0_2" + then + libvers= + elif test "$i" = "-cvs_A51208" + then + libvers="-DCdrskin_libburn_cvs_A51208_tS" + elif test "$i" = "-cvs_A60220" + then + libvers="-DCdrskin_libburn_cvs_A60220_tS" + elif test "$i" = "-do_not_compile_cdrskin" + then + compile_cdrskin=0 + elif test "$i" = "-do_diet" + then + fifo_source= + def_opts="$def_opts -DCdrskin_extra_leaN" + warn_opts= + elif test "$i" = "-do_strip" + then + do_strip=1 + elif test "$i" = "-g" + then + debug_opts="$debug_opts -g" + elif test "$i" = "-O2" + then + debug_opts="$debug_opts -O2" + elif test "$i" = "-help" -o "$i" = "--help" -o "$i" = "-h" + then + echo "cdrskin/compile_cdrskin.sh : to be executed within ./cdrskin-0.1.3.0.2.ts" + echo "Options:" + echo " -compile_cdrfifo compile program cdrskin/cdrfifo." + echo " -tarball_0_2 set macro to match libburn-0.2.ts.tar.gz" + echo " -cvs_A51208 set macro to match libburn-CVS of 8 Dec 2005." + echo " -cvs_A60220 set macro to match libburn-CVS of 20 Feb 2006." + echo " -do_not_compile_cdrskin omit compilation of cdrskin/cdrskin." + echo " -do_diet produce capability reduced lean version." + echo " -do_strip apply program strip to compiled programs." + echo " -g compile with cc option -g." + echo " -O2 compile with cc option -O2." + echo " -static compile with cc option -static." + exit 0 + elif test "$i" = "-static" + then + static_opts="-static" + fi +done + + +timestamp="$(date -u '+%Y.%m.%d.%H%M%S')" +echo "Version timestamp : $(sed -e 's/#define Cdrskin_timestamP "//' -e 's/"$//' cdrskin/cdrskin_timestamp.h)" +echo "Build timestamp : $timestamp" + +if test "$compile_cdrskin" +then + echo "compiling program cdrskin/cdrskin.c $static_opts $debug_opts $libvers $def_opts" + cc $warn_opts -I. $static_opts $debug_opts $libvers $def_opts \ + -DCdrskin_build_timestamP='"'"$timestamp"'"' \ + \ + -o cdrskin/cdrskin \ + \ + cdrskin/cdrskin.c \ + $fifo_source \ + cdrskin/cleanup.c \ + \ + libburn/async.o \ + libburn/debug.o \ + libburn/drive.o \ + libburn/file.o \ + libburn/init.o \ + libburn/options.o \ + libburn/source.o \ + libburn/structure.o \ + \ + libburn/message.o \ + libburn/sg.o \ + libburn/write.o \ + \ + libburn/mmc.o \ + libburn/sbc.o \ + libburn/spc.o \ + libburn/util.o \ + \ + libburn/sector.o \ + libburn/toc.o \ + \ + libburn/crc.o \ + libburn/lec.o \ + \ + -lpthread +fi + +if test "$compile_cdrfifo" = 1 +then + echo "compiling program cdrskin/cdrfifo.c $static_opts $debug_opts" + cc $static_opts $debug_opts \ + -DCdrfifo_standalonE \ + -o cdrskin/cdrfifo \ + cdrskin/cdrfifo.c +fi + +if test "$do_strip" = 1 +then + echo "stripping result cdrskin/cdrskin" + strip cdrskin/cdrskin + if test "$compile_cdrfifo" = 1 + then + echo "stripping result cdrskin/cdrfifo" + strip cdrskin/cdrfifo + fi +fi + +echo 'done.'