diff --git a/tags/CdrskinZeroTwoFour/AUTHORS b/tags/CdrskinZeroTwoFour/AUTHORS
new file mode 100644
index 00000000..59a18579
--- /dev/null
+++ b/tags/CdrskinZeroTwoFour/AUTHORS
@@ -0,0 +1,5 @@
+Developers:
+
+Mario Danic
+Thomas Schmitt
+Lorenzo Taylor
diff --git a/tags/CdrskinZeroTwoFour/CONTRIBUTORS b/tags/CdrskinZeroTwoFour/CONTRIBUTORS
new file mode 100644
index 00000000..c774a038
--- /dev/null
+++ b/tags/CdrskinZeroTwoFour/CONTRIBUTORS
@@ -0,0 +1,2 @@
+Joe Neeman
+Philippe Rouquier
diff --git a/tags/CdrskinZeroTwoFour/COPYING b/tags/CdrskinZeroTwoFour/COPYING
new file mode 100644
index 00000000..5a965fbc
--- /dev/null
+++ b/tags/CdrskinZeroTwoFour/COPYING
@@ -0,0 +1,280 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
diff --git a/tags/CdrskinZeroTwoFour/COPYRIGHT b/tags/CdrskinZeroTwoFour/COPYRIGHT
new file mode 100644
index 00000000..a6b57b3c
--- /dev/null
+++ b/tags/CdrskinZeroTwoFour/COPYRIGHT
@@ -0,0 +1,19 @@
+Derek Foreman and Ben Jansens
+Copyright (C) 2002-2006 Derek Foreman and Ben Jansens
+Mario Danic , Thomas Schmitt
+Copyright (C) 2006 Mario Danic, Thomas Schmitt
+
+
+ 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/tags/CdrskinZeroTwoFour/Makefile.am b/tags/CdrskinZeroTwoFour/Makefile.am
new file mode 100644
index 00000000..34401637
--- /dev/null
+++ b/tags/CdrskinZeroTwoFour/Makefile.am
@@ -0,0 +1,223 @@
+pkgconfigdir=$(libdir)/pkgconfig
+libincludedir=$(includedir)/libburn
+
+lib_LTLIBRARIES = libburn/libburn.la libisofs/libisofs.la
+
+## ========================================================================= ##
+
+# Build libraries
+libburn_libburn_la_LDFLAGS = \
+ -version-info $(LT_CURRENT):$(LT_REVISION):$(LT_AGE)
+libburn_libburn_la_LIBADD = $(LIBBURN_ARCH_LIBS) $(THREAD_LIBS)
+libburn_libburn_la_SOURCES = \
+ libburn/async.c \
+ libburn/async.h \
+ libburn/back_hacks.h \
+ libburn/cleanup.c \
+ libburn/cleanup.h \
+ libburn/crc.c \
+ libburn/crc.h \
+ libburn/debug.c \
+ libburn/debug.h \
+ libburn/drive.c \
+ libburn/drive.h \
+ libburn/error.h \
+ libburn/file.c \
+ libburn/file.h \
+ libburn/init.c \
+ libburn/init.h \
+ libburn/lec.c \
+ libburn/lec.h \
+ libburn/libburn.h \
+ libburn/libdax_audioxtr.h \
+ libburn/libdax_audioxtr.c \
+ libburn/libdax_msgs.h \
+ libburn/libdax_msgs.c \
+ libburn/mmc.c \
+ libburn/mmc.h \
+ libburn/null.c \
+ libburn/null.h \
+ libburn/options.c \
+ libburn/options.h \
+ libburn/read.c \
+ libburn/read.h \
+ libburn/sbc.c \
+ libburn/sbc.h \
+ libburn/sector.c \
+ libburn/sector.h \
+ libburn/sg.c \
+ libburn/sg.h \
+ libburn/source.h \
+ libburn/source.c \
+ libburn/spc.c \
+ libburn/spc.h \
+ libburn/structure.c \
+ libburn/structure.h \
+ libburn/toc.c \
+ libburn/toc.h \
+ libburn/transport.h \
+ libburn/util.c \
+ libburn/util.h \
+ libburn/write.c \
+ libburn/write.h \
+ version.h
+
+## libburn/sg-@ARCH@.c \
+
+libisofs_libisofs_la_LDFLAGS = \
+ -version-info $(LT_CURRENT):$(LT_REVISION):$(LT_AGE)
+libisofs_libisofs_la_SOURCES = \
+ libisofs/tree.h \
+ libisofs/tree.c \
+ libisofs/volume.h \
+ libisofs/volume.c \
+ libisofs/util.h \
+ libisofs/util.c \
+ libisofs/ecma119.c \
+ libisofs/ecma119.h \
+ libisofs/ecma119_tree.c \
+ libisofs/ecma119_tree.h \
+ libisofs/susp.h \
+ libisofs/susp.c \
+ libisofs/rockridge.h \
+ libisofs/rockridge.c \
+ libisofs/joliet.c \
+ libisofs/joliet.h \
+ libisofs/exclude.c \
+ libisofs/exclude.h \
+ libisofs/hash.h \
+ libisofs/hash.c
+
+libinclude_HEADERS = \
+ libburn/libburn.h \
+ libisofs/libisofs.h
+
+## ========================================================================= ##
+
+## Build test applications
+noinst_PROGRAMS = \
+ test/libburner \
+ test/dewav \
+ test/fake_au \
+ test/iso \
+ test/poll \
+ test/toc \
+ test/structest
+
+bin_PROGRAMS = \
+ cdrskin/cdrskin
+
+test_libburner_CPPFLAGS = -Ilibburn
+test_libburner_LDADD = $(libburn_libburn_la_OBJECTS) $(THREAD_LIBS)
+test_libburner_SOURCES = test/libburner.c
+test_dewav_CPPFLAGS = -Ilibburn
+test_dewav_LDADD = $(libburn_libburn_la_OBJECTS) $(THREAD_LIBS)
+test_dewav_SOURCES = test/dewav.c
+test_fake_au_CPPFLAGS =
+test_fake_au_LDADD =
+test_fake_au_SOURCES = test/fake_au.c
+test_poll_CPPFLAGS = -Ilibburn
+test_poll_LDADD = $(libburn_libburn_la_OBJECTS) $(THREAD_LIBS)
+test_poll_SOURCES = test/poll.c
+test_toc_CPPFLAGS = -Ilibburn
+test_toc_LDADD = $(libburn_libburn_la_OBJECTS) $(THREAD_LIBS)
+test_toc_SOURCES = test/toc.c
+test_structest_CPPFLAGS = -Ilibburn
+test_structest_LDADD = $(libburn_libburn_la_OBJECTS) $(THREAD_LIBS)
+test_structest_SOURCES = test/structest.c
+test_iso_CPPFLAGS = -Ilibisofs
+test_iso_LDADD = $(libisofs_libisofs_la_OBJECTS) $(THREAD_LIBS)
+test_iso_SOURCES = test/iso.c
+
+## cdrskin construction site - ts A60816
+cdrskin_cdrskin_CPPFLAGS = -Ilibburn
+cdrskin_cdrskin_CFLAGS = -DCdrskin_libburn_0_2_3
+cdrskin_cdrskin_LDADD = $(libburn_libburn_la_OBJECTS) $(THREAD_LIBS)
+cdrskin_cdrskin_SOURCES = cdrskin/cdrskin.c cdrskin/cdrfifo.c cdrskin/cdrfifo.h cdrskin/cdrskin_timestamp.h
+## cdrskin_cdrskin_SOURCES = cdrskin/cdrskin.c cdrskin/cdrfifo.c cdrskin/cdrfifo.h cdrskin/cleanup.c cdrskin/cleanup.h cdrskin/cdrskin_timestamp.h
+##
+## Open questions: how to compute $timestamp and express -DX="$timestamp"
+##
+
+
+
+## ========================================================================= ##
+
+## Build documentation (You need Doxygen for this to work)
+webhost = http://libburn-api.pykix.org
+webpath = /
+docdir = $(DESTDIR)$(prefix)/share/doc/$(PACKAGE)-$(VERSION)
+
+doc: doc/html
+
+doc/html: doc/doxygen.conf
+ if [ -f ./doc/doc.lock ]; then \
+ $(RM) -r doc/html; \
+ doxygen doc/doxygen.conf; \
+ fi
+
+doc-upload: doc/html
+ scp -r $* $(webhost):$(webpath)
+
+all: doc
+
+install-data-local:
+ if [ -f ./doc/doc.lock ]; then \
+ $(mkinstalldirs) $(docdir)/html; \
+ $(INSTALL_DATA) doc/html/* $(docdir)/html; \
+ fi
+
+uninstall-local:
+ rm -rf $(docdir)
+
+## ========================================================================= ##
+
+# Indent source files
+indent_files = \
+ $(libisofs_libisofs_la_SOURCES) \
+ $(libburn_libburn_la_SOURCES) \
+ $(test_libburner_SOURCES) \
+ $(test_poll_SOURCES) \
+ $(test_toc_SOURCES) \
+ $(test_structest_SOURCES) \
+ $(test_iso_SOURCES)
+
+
+indent: $(indent_files)
+ indent -bad -bap -nbbb -nbbo -nbc -bli0 -br -bls \
+ -cdw -ce -cli0 -ncs -nbfda -i8 -l79 -lc79 \
+ -lp -saf -sai -nprs -npsl -saw -sob -ss -ut \
+ -sbi0 -nsc -ts8 -npcs -ncdb -fca \
+ $^
+
+.PHONY: indent
+
+## ========================================================================= ##
+
+# Extra things
+nodist_pkgconfig_DATA = \
+ libburn-1.pc \
+ libisofs-1.pc
+
+EXTRA_DIST = \
+ libburn-1.pc.in \
+ libisofs-1.pc.in \
+ version.h.in \
+ doc/comments \
+ doc/doxygen.conf.in \
+ README \
+ AUTHORS \
+ CONTRIBUTORS \
+ COPYRIGHT \
+ cdrskin/README \
+ cdrskin/cdrecord_spy.sh \
+ cdrskin/compile_cdrskin.sh \
+ cdrskin/changelog.txt \
+ cdrskin/cdrskin_eng.html \
+ cdrskin/wiki_plain.txt \
+ cdrskin/cleanup.h \
+ cdrskin/cleanup.c \
+ libburn/sg-freebsd.c \
+ libburn/sg-linux.c \
+ COPYING
+
diff --git a/tags/CdrskinZeroTwoFour/README b/tags/CdrskinZeroTwoFour/README
new file mode 100644
index 00000000..8c6ce3a0
--- /dev/null
+++ b/tags/CdrskinZeroTwoFour/README
@@ -0,0 +1,211 @@
+------------------------------------------------------------------------------
+ libburn.pykix.org
+------------------------------------------------------------------------------
+This all is under GPL.
+(See GPL reference, our clarification and commitment at the end of this text)
+------------------------------------------------------------------------------
+libburn.pykix.org
+By Mario Danic and Thomas Schmitt
+Copyright (C) 2006 Mario Danic, Thomas Schmitt
+
+Still containing parts of
+Libburn. By Derek Foreman and
+ Ben Jansens
+Copyright (C) 2002-2006 Derek Foreman and Ben Jansens
+These parts are to be replaced by own code of above libburn.pykix.org-copyright
+holders and then libburn.pykix.org is to be their sole copyright.
+This is done to achieve the right to issue the clarification and the
+commitment as written at the end of this text.
+The rights and merits of the Libburn-copyright holders Derek Foreman and
+Ben Jansens will be duely respected.
+
+This libburn.pykix.org toplevel README (C) 2006 Thomas Schmitt
+------------------------------------------------------------------------------
+
+ Build and Installation
+
+Our build system is based on autotools. For preparing the build of a SVN
+snapshot you will need autotools of at least version 1.7.
+Check out from SVN by
+ svn co http://libburn-svn.pykix.org/trunk libburn_pykix
+and apply autotools by
+ ./bootstrap
+
+Alternatively you may unpack a release tarball for which you do not need
+autotools installed.
+
+To build libburn.pykix.org and its subprojects it should be sufficient to go
+into its toplevel directory (here: "libburn_pykix") and execute
+ ./configure
+ make
+
+To make the libraries accessible for running resp. developing applications
+ make install
+
+------------------------------------------------------------------------------
+
+ Overview of libburn.pykix.org
+
+libburn.pykix.org is an open-source library for reading, mastering and writing
+optical discs. For now this means only CD-R and CD-RW.
+
+The project comprises of several more or less interdependent parts which
+together strive to be a usable foundation for application development.
+These are libraries, language bindings, and middleware binaries which emulate
+classical (and valuable) Linux tools.
+
+Our scope is currently Linux 2.4 and 2.6 and we will have a hard time to widen
+this for now, because of our history. The project could need advise from or
+membership of skilled kernel people and people who know how to talk CD/DVD
+drives into doing things.
+
+We do have a workable code base for burning data CDs, though. The burn API is
+quite comprehensively documented and can be used to build a presentable
+application.
+We do have a functional binary which emulates parts of cdrecord in order to
+prove that usability, and in order to allow you to explore libburn's scope
+by help of existing cdrecord frontends.
+
+The project components (list subject to growth, hopefully):
+
+- libburn is the library by which preformatted data get onto optical media.
+ It uses either /dev/sgN (e.g. on kernel 2.4 with ide-scsi) or
+ /dev/hdX (e.g. on kernel 2.6).
+ libburn is the foundation of our cdrecord emulation.
+
+- libisofs is the library to pack up hard disk files and directories into a
+ ISO 9660 disk image. This may then be brought to CD via libburn.
+ libisofs is to be the foundation of our upcoming mkisofs emulation.
+
+- cdrskin is a limited cdrecord compatibility wrapper for libburn.
+ Cdrecord is a powerful GPL'ed burn program included in Joerg
+ Schilling's cdrtools. cdrskin strives to be a second source for
+ the services traditionally provided by cdrecord.
+ cdrskin does not contain any bytes copied from cdrecord's sources.
+ Many bytes have been copied from the message output of cdrecord
+ runs, though.
+ See cdrskin/README for more.
+
+- test is a collection of application gestures and examples given by the
+ authors of the library features. The main API example for libburn
+ is test/libburner.c .
+ Explore these examples if you look for inspiration.
+
+We plan to be a responsive upstream. Bear with us. We are still practicing.
+
+
+------------------------------------------------------------------------------
+Project history as far as known to me:
+
+- Founded in 2002 as it seems. See mailing list archives
+ http://lists.freedesktop.org/archives/libburn/
+ The site of this founder team is reachable and offers download of a
+ (somewhat outdated) tarball and from CVS :
+ http://icculus.org/burn/
+ Copyright holders and most probably founders:
+ Derek Foreman and Ben Jansens.
+
+- I came to using libburn in 2005. Founded the cdrskin project and submitted
+ necessary patches which were accepted or implemented better. Except one
+ remaining patch which prevented cdrskin from using vanilla libburn from CVS.
+ The cdrskin project site is reachable and offers download of the heavily
+ patched (elsewise outdated) tarball under the name cdrskin-0.1.2 :
+ http://scdbackup.sourceforge.net/cdrskin_eng.html
+ It has meanwhile moved to use vanilla libburn.pykix.org , though.
+ Version 0.1.4 constitutes the first release of this kind.
+
+- In Juli 2006 our team mate Mario Danic announced a revival of libburn
+ which by about nearly everybody else was perceived as unfriendly fork.
+ Derek Foreman four days later posted a message which expressed his
+ discontent.
+ The situation first caused me to publically regret it and then - after i
+ got the opportunity to move in with cdrskin - gave me true reason to
+ personally apologize to Derek Foreman, Ben Jansens and the contibutors at
+ icculus.org/burn. Posted to both projects:
+ http://lists.freedesktop.org/archives/libburn/2006-August/000446.html
+ http://mailman-mail1.webfaction.com/pipermail/libburn-hackers/2006-August/000024.html
+
+- Mid August 2006 project cdrskin established a branch office in
+ libburn.pykix.org so that all maintainers of our tools have one single place
+ to get the current (at least slightely) usable coordinated versions of
+ everything.
+ Project cdrskin will live forth independendly for a while but it is committed
+ to stay in sync with libburn.pykix.org (or some successor, if ever).
+ cdrskin is also committed to support icculus.org/burn if the pending fork
+ is made reality by content changes in that project. It will cease to maintain
+ a patched version of icculus.org/burn though. Precondition for a new
+ release of cdrskin on base of icculus.org/burn would be the pending
+ "whitelist patch" therefore.
+ I would rather prefer if both projects find consense and merge, or at least
+ cooperate. I have not given up hope totally, yet.
+ I, personally, will honor any approach.
+
+- 2nd September 2006 the decision is made to strive for a consolidation of
+ copyright and a commitment to GPL in a reasonable and open minded way.
+ This is to avoid long term problems with code of unknown origin and
+ with finding consense among the not so clearly defined group of copyright
+ claimers and -holders.
+ libisofs is already claimed sole copyright Mario Danic.
+ cdrskin and libburner are already claimed sole copyright Thomas Schmitt.
+ Rewrites of other components will follow and concluded by claiming full
+ copyright within the group of libburn.pykix.org-copyright holders.
+
+- 16th September 2006 feature freeze for release of libburn-0.2.2 .
+
+- 20th September 2006 release of libburn-0.2.2 .
+
+- 26th October 2006 feature freeze for cdrskin-0.2.4 based on libburn-0.2.3 .
+ This version of cdrskin is much more cdrecord compatible in repect
+ to drive addressing and audio features.
+
+
+------------------------------------------------------------------------------
+
+ 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
+
+------------------------------------------------------------------------------
+Clarification in my name and in the name of Mario Danic, upcoming copyright
+holders on toplevel of libburn. To be fully in effect after the remaining other
+copyrighted code has been replaced by ours and by copyright-free contributions
+of our friends:
+------------------------------------------------------------------------------
+
+We, the copyright holders, agree on the interpretation that
+dynamical linking of our libraries constitutes "use of" and
+not "derivation from" our work in the sense of GPL, provided
+those libraries are compiled from our unaltered code.
+
+Thus you may link our libraries dynamically with applications
+which are not under GPL. You may distribute our libraries and
+application tools in binary form, if you fulfill the usual
+condition of GPL to offer a copy of the source code -altered
+or unaltered- under GPL.
+
+We ask you politely to use our work in open source spirit
+and with the due reference to the entire open source community.
+
+If there should really arise the case where above clarification
+does not suffice to fulfill a clear and neat request in open source
+spirit that would otherwise be declined for mere formal reasons,
+only in that case we will duely consider to issue a special license
+covering only that special case.
+It is the open source idea of responsible freedom which will be
+decisive and you will have to prove that you exhausted all own
+means to qualify for GPL.
+
+For now we are firmly committed to maintain one single license: GPL.
+
+signed: Mario Danic, Thomas Schmitt
+
diff --git a/tags/CdrskinZeroTwoFour/acinclude.m4 b/tags/CdrskinZeroTwoFour/acinclude.m4
new file mode 100644
index 00000000..861847bb
--- /dev/null
+++ b/tags/CdrskinZeroTwoFour/acinclude.m4
@@ -0,0 +1,22 @@
+AC_DEFUN([TARGET_SHIZZLE],
+[
+ ARCH=""
+
+ AC_MSG_CHECKING([target operating system])
+
+ case $target in
+ *-*-linux*)
+ ARCH=linux
+ LIBBURN_ARCH_LIBS=
+ ;;
+ *-*-freebsd*)
+ ARCH=freebsd
+ LIBBURN_ARCH_LIBS=-lcam
+ ;;
+ *)
+ AC_ERROR([You are attempting to compile for an unsupported platform])
+ ;;
+ esac
+
+ AC_MSG_RESULT([$ARCH])
+])
diff --git a/tags/CdrskinZeroTwoFour/bootstrap b/tags/CdrskinZeroTwoFour/bootstrap
new file mode 100755
index 00000000..a0b64694
--- /dev/null
+++ b/tags/CdrskinZeroTwoFour/bootstrap
@@ -0,0 +1,7 @@
+#!/bin/sh -x
+
+aclocal
+libtoolize --copy --force
+autoconf
+autoheader
+automake --foreign --add-missing --copy --include-deps
diff --git a/tags/CdrskinZeroTwoFour/cdrskin/README b/tags/CdrskinZeroTwoFour/cdrskin/README
new file mode 100644
index 00000000..9b4e55ef
--- /dev/null
+++ b/tags/CdrskinZeroTwoFour/cdrskin/README
@@ -0,0 +1,434 @@
+------------------------------------------------------------------------------
+ libburn.pykix.org scdbackup.sourceforge.net/cdrskin_eng.html
+------------------------------------------------------------------------------
+Installation instructions at about line 60. First the legal stuff:
+------------------------------------------------------------------------------
+This all is under GPL.
+(See GPL reference, our clarification and commitment at the end of this text)
+------------------------------------------------------------------------------
+Based on and sub project of:
+libburn.pykix.org
+By Mario Danic and Thomas Schmitt
+Copyright (C) 2006 Mario Danic, Thomas Schmitt
+
+libburn.pykix.org is inspired by and in other components still containing
+parts of
+Libburn. By Derek Foreman and
+ Ben Jansens
+Copyright (C) 2002-2006 Derek Foreman and Ben Jansens
+See toplevel README for an overview of the current copyright situation in
+libburn.pykix.org.
+
+------------------------------------------------------------------------------
+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.2.4.tar.gz
+Copyright (C) 2006 Thomas Schmitt
+
+------------------------------------------------------------------------------
+
+On top of libburn there is implemented cdrskin 0.2.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 or libburn-hackers@pykix.org .
+We will keep copyright narrow but will of course acknowledge valuable
+contributions in a due way.
+
+
+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, 2002
+SuSE 9.0, kernel 2.4.21, ide-scsi emulation, LG GSA-4082B CD/DVD burner, 2004
+ NEC ND-4570A CD/DVD burner, 2006
+RIP-14.4, kernel 2.6.14, no ide-scsi, with all 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.2.4.tar.gz , take it to a directory of your choice and do:
+
+ tar xzf cdrskin-0.2.4.tar.gz
+ cd cdrskin-0.2.4
+
+Or obtain a libburn.pykix.org SVN snapshot,
+go into the toplevel directory of the snapshot (e.g. cd libburn_pykix ),
+and execute the autotools script ./bootstrap . Use autools version >= 1.7 .
+
+Within that toplevel directory of either cdrskin-0.2.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
+
+Version identification an help texts available afterwards:
+ cdrskin/cdrskin -version
+ cdrskin/cdrskin --help
+ cdrskin/cdrskin -help
+
+Install (eventually as superuser) cdrskin to a directory where it can be found:
+If cdrskin was already installed by a previous version, or by "make install"
+in the course of this installation, then find out where:
+ which cdrskin
+Copy your standalone binary to exactly the address which you get as reply
+
+ cp cdrskin/cdrskin /usr/bin/cdrskin
+
+Check the version timestamps of the globally installed binary
+ cdrskin -version
+
+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.)
+
+
+ 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- : '_NEC' 'DVD_RW ND-4570A'
+ 1 dev='/dev/sg1' rwrw-- : 'HL-DT-ST' 'DVDRAM GSA-4082B'
+
+So full and insecure enabling of both for everybody would look like
+
+ chmod a+rw /dev/sg0 /dev/sg1
+
+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
+ cdrskin dev=ATA -scanbus
+
+Note: Adresses reported with dev=ATA are to be used with prefix "ATA:". You may
+ well use device file addresses as reported with --devices. Examples:
+ dev=0,1,0 dev=/dev/sg1 dev=ATA:1,0,0 dev=/dev/hdc
+ See also "Drive Addressing".
+Note: Address numbers have changed since cdrskin-0.2.2 in order to become
+ compatible with cdrecord numbers. To get the old number scheme, use
+ option --old_pseudo_scsi_adr . See also "Pseudo-SCSI Adresses".
+ Sorry for any inconvenience.
+
+
+Obtain some info about the drive
+ cdrskin dev=0,1,0 -checkdrive
+
+Obtain some info about the drive and the inserted media
+ cdrskin dev=0,1,0 -atip
+
+Thoroughly blank a CD-RW
+ cdrskin -v dev=0,1,0 blank=all -eject
+
+Blank CD-RW sufficiently for making it ready for overwrite
+ cdrskin -v dev=0,1,0 blank=fast -eject
+
+Burn image file my_image.iso to CD
+ cdrskin -v dev=0,1,0 speed=12 fs=8m -sao driveropts=burnfree padsize=300k \
+ -eject my_image.iso
+
+Burn a compressed afio archive to CD on-the-fly
+ find . | afio -oZ - | cdrskin -v dev=0,1,0 fs=32m speed=8 -sao \
+ driveropts=burnfree padsize=300k tsize=650m -
+
+Burn 6 audio tracks from files with different formats to CD.
+Anything except .wav or .au files has to be converted into raw format first.
+See below "Audio CD" for specifications.
+ ogg123 -d raw -f track01.cd /path/to/track1.ogg
+ oggdec -R -o track02.cd /path/to/track2.ogg
+ lame --decode -t /path/to/track3.mp3 track03.cd
+ madplay -o raw:track04.cd /path/to/track4.mp3
+ mppdec --raw-le /path/to/track5.mpc track05.cd
+
+ cdrskin dev=0,1,0 blank=fast -eject speed=48 -sao \
+ -audio -swab track0[1-5].cd /path/to/track6.wav
+
+
+ Usage example with http://scdbackup.sourceforge.net
+
+Address may be a cdrecord-style "scsibus,target,lun" as listed with
+cdrskin -scanbus (and hopefully as listed with cdrecord -scanbus) :
+
+ export SCDBACKUP_SCSI_ADR="0,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"
+
+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.
+
+No multi session yet ... 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.
+
+
+ Drive Addressing
+
+Drives get addressed either via their cdrecord-style addresses as listed
+with option -scanbus (see below "Pseudo-SCSI Adresses") or via the paths
+of device files.
+Not only device files listed by --devices may be used but also device files
+which via their major,minor numbers point to the same device driver as
+a listed device file.
+
+Helpful with Linux kernel 2.4 is a special SCSI feature:
+It is possible to address a scsi(-emulated) drive via associated device files
+which are not listed by option --devices but point to the same SCSI addresses
+as listed device files. This addressing via e.g. /dev/sr0 or /dev/scd1 is
+compatible with generic read programs like dd and with write program growisofs.
+
+ Pseudo-SCSI Adresses
+
+cdrecord and cdrskin share the syntax of SCSI addresses but not necessarily
+the meaning of the components. A cdrecord-style address for cdrskin
+ [prefix:]scsibus,target,lun
+can be interpreted in two different modes.
+
+Standard mode tries to be compatible to original cdrecord. This should be true
+with (emulated) SCSI where the /dev/sgN with is looked up with matching
+scsibus,target,lun as given by the operating system.
+With dev=ATA: or dev=ATAPI: the translation to /dev/hdX is purely literal
+but matches the cdrecord addresses on all systems tested so far:
+ X = 'a' + 2 * scsibus + target
+where target only may have the values 0 or 1.
+
+In this mode, option -scanbus will list only SCSI devices unless option
+dev=ATA or dev=ATAPI are given, which will suppress SCSI devices and only
+show IDE drives (i.e. /dev/hdX without ide-scsi emulation).
+
+
+In mode --old_pseudo_scsi_adr there is a scsibus,target,lun representation
+which has nothing to do with SCSI and thus is not compatible to cdrecord.
+Each number triple 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.
+
+ User Defined Device Address Translation
+
+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.
+Standard mode will hopefully be fully compatible with their ideas.
+
+Old frontends which do not know dev=ATA or dev=ATAPI and which do ask their
+"cdrecord" via -scanbus may be well served with option --old_pseudo_scsi_adr .
+
+To direct any remaining stubborn 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"-0,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 0,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.
+A first character '#' marks a comment, empty lines are ignored.
+
+Example content of a startup file:
+# This is the default device
+dev=0,1,0
+# To accomodate to eventual remnant cdrskin-0.2.2 addresses
+dev_translation=+1,0,0+0,1,0
+
+# Some more options
+--fifo_start_empty
+fs=16m
+
+
+ Audio CD
+
+Lorenzo Taylor enabled option -audio in cdrskin (thanks !) and reports neat
+results with audio data files which are :
+ headerless PCM (i.e. uncompressed)
+ 44100 Hz sampling rate
+ 16 bits per sample
+ stereo (2 channels)
+ little-endian byte order with option -swab, or big-endian without -swab
+
+Files with name extension .wav get examined wether they are in Microsoft WAVE
+format with above parameters and eventually get extracted by cdrskin itself.
+In the same way files with name extension .au get examined wether they are
+in SUN's audio format. For both formats, track format -audio and eventual
+endianness option -swab are enabled automatically.
+
+Any other formats are to be converted to format .wav with above parameters
+or to be extracted as raw CD track data by commands like those given above
+under "Usage examples". Those raw files need option -audio and in most cases
+option -swab to mark them as little-endian/Intel/LSB-first 16-bit data.
+Incorrect endianness setting results in random noise on CD.
+
+I myself am not into audio. So libburn-hackers@pykix.org might be the
+best address for suggestions, requests and bug reports.
+
+
+ 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.
+
+------------------------------------------------------------------------------
+
+ 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
+
+------------------------------------------------------------------------------
+cdrskin is currently copyright Thomas Schmitt only.
+It adopts the following commitment by the toplevel copyright holders:
+------------------------------------------------------------------------------
+
+We, the copyright holders, agree on the interpretation that
+dynamical linking of our libraries constitutes "use of" and
+not "derivation from" our work in the sense of GPL, provided
+those libraries are compiled from our unaltered code.
+
+Thus you may link our libraries dynamically with applications
+which are not under GPL. You may distribute our libraries and
+application tools in binary form, if you fulfill the usual
+condition of GPL to offer a copy of the source code -altered
+or unaltered- under GPL.
+
+We ask you politely to use our work in open source spirit
+and with the due reference to the entire open source community.
+
+If there should really arise the case where above clarification
+does not suffice to fulfill a clear and neat request in open source
+spirit that would otherwise be declined for mere formal reasons,
+only in that case we will duely consider to issue a special license
+covering only that special case.
+It is the open source idea of responsible freedom which will be
+decisive and you will have to prove that you exhausted all own
+means to qualify for GPL.
+
+For now we are firmly committed to maintain one single license: GPL.
+
+signed for cdrskin: Thomas Schmitt
diff --git a/tags/CdrskinZeroTwoFour/cdrskin/add_ts_changes_to_libburn_0_2_4 b/tags/CdrskinZeroTwoFour/cdrskin/add_ts_changes_to_libburn_0_2_4
new file mode 100755
index 00000000..975c5656
--- /dev/null
+++ b/tags/CdrskinZeroTwoFour/cdrskin/add_ts_changes_to_libburn_0_2_4
@@ -0,0 +1,203 @@
+#!/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
+# Downloaded by:
+# $ svn co http://libburn-svn.pykix.org/trunk libburn_pykix
+# packed up in a tarball just to save it from inadverted changes by
+# $ tar czf libburn_svn.tgz libburn_pykix
+original="./libburn_svn.tgz"
+# Historic moments:
+# original="./libburn_svn_A60815.tgz"
+# original="./libburn_cdrskin_A60819.tgz"
+
+# The top level directory in that snapshot is named
+intermediate="./libburn_pykix"
+
+# My changes are in libburn-0.2.3.ts.develop , mainly in ./cdrskin
+
+changes="./libburn-0.2.3.ts.develop"
+skin_rev="0.2.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"
+
+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
+ exit 3
+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"/*.o
+rm "$cdrskin_target"/cdrfifo
+rm "$cdrskin_target"/cdrskin
+rm "$cdrskin_target"/cleanup
+for i in std new make old
+do
+ if test -e "$cdrskin_target"/cdrskin_"$i"
+ then
+ rm "$cdrskin_target"/cdrskin_"$i"
+ fi
+done
+for i in .deps .dirstamp .libs
+do
+ if test -e "$cdrskin_target"/"$i"
+ then
+ rm -rf "$cdrskin_target"/"$i"
+ fi
+done
+
+# 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
+
+## No more : Add own libburn-README in toplevel
+# cp -a "$changes"/README "$target"
+
+## No more : Add modified Makefile.am
+# cp -a "$changes"/Makefile.am "$target"
+
+
+# Make SVN state tarball for the libburn team
+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 )
+
+# Remove unwanted stuff after bootstrap
+for i in "$target"/autom4te.cache
+do
+ if echo "$i" | grep '\*' >/dev/null
+ then
+ dummy=dummy
+ else
+ if test -e "$i"
+ then
+ rm -rf "$i"
+ fi
+ fi
+done
+
+
+# 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
+)
+
+# Remove the build area
+# Disable this for debugging the merge process
+rm -rf "$target"
+
+# Show the result
+./"$bintarget_dynamic" -version
+./"$bintarget_static" -version
+ls -l "$cdrskin_tarball"
+ls -l "$bintarget_dynamic"
+ls -l "$bintarget_static"
+
diff --git a/tags/CdrskinZeroTwoFour/cdrskin/cdrecord_spy.sh b/tags/CdrskinZeroTwoFour/cdrskin/cdrecord_spy.sh
new file mode 100755
index 00000000..54d7c344
--- /dev/null
+++ b/tags/CdrskinZeroTwoFour/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/tags/CdrskinZeroTwoFour/cdrskin/cdrfifo.c b/tags/CdrskinZeroTwoFour/cdrskin/cdrfifo.c
new file mode 100644
index 00000000..da6dc598
--- /dev/null
+++ b/tags/CdrskinZeroTwoFour/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= 0;
+ 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; i1024.0*1024.0*1024.0) {
+ fprintf(stderr,
+ "cdrfifo: FATAL : bs=N expects a size between 1 and 1g\n");
+ {exit_value= 2; goto ex;}
+ }
+ } else if(strncmp(argv[i],"fl=",3)==0) {
+ sscanf(argv[i]+3,"%d",&fill_buffer);
+ fill_buffer= !!fill_buffer;
+ } else if(strncmp(argv[i],"fs=",3)==0) {
+ fs_value= Scanf_io_size(argv[i]+3,0);
+ if(fs_value<=0 || fs_value>1024.0*1024.0*1024.0) {
+ fprintf(stderr,
+ "cdrfifo: FATAL : fs=N expects a size between 1 and 1g\n");
+ {exit_value= 2; goto ex;}
+ }
+ } else if(strncmp(argv[i],"iv=",3)==0) {
+ sscanf(argv[i]+3,"%lf",&interval);
+ if(interval<0.001 || interval>1000.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/tags/CdrskinZeroTwoFour/cdrskin/cdrfifo.h b/tags/CdrskinZeroTwoFour/cdrskin/cdrfifo.h
new file mode 100644
index 00000000..42461d2d
--- /dev/null
+++ b/tags/CdrskinZeroTwoFour/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/tags/CdrskinZeroTwoFour/cdrskin/cdrskin.c b/tags/CdrskinZeroTwoFour/cdrskin/cdrskin.c
new file mode 100644
index 00000000..495ef5cd
--- /dev/null
+++ b/tags/CdrskinZeroTwoFour/cdrskin/cdrskin.c
@@ -0,0 +1,5153 @@
+
+/*
+ cdrskin.c , Copyright 2006 Thomas Schmitt
+Provided under GPL. See future commitment below.
+
+A cdrecord compatible command line interface for libburn.
+
+This project is neither directed against original cdrecord nor does it exploit
+any source code of said program. It rather tries to be an alternative method
+to burn CD 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.
+
+------------------------------------------------------------------------------
+About compliance with *strong urge* of API towards burn_drive_scan_and_grab()
+
+For a more comprehensive example of the advised way to behave with libburn
+see test/libburner.c .
+
+cdrskin was the initiator of the whitelist functionality within libburn.
+Now it has problems to obviously comply with the new API best practice
+presciptions literally. Therefore this explanation:
+
+On start it restricts the library to a single drive if it already knows the
+persistent address by option dev= . This is done with a combination of
+burn_drive_add_whitelist() and burn_drive_scan(). Not compliant to the
+literal strong urge but in fact exactly fulfilling the reason for that
+urge in the API: any scanned drive might be opened exclusively after
+burn_drive_scan(). It is kernel dependent wether this behavior is on, off
+or switchable. The sysdamin will want it on - but only for one drive.
+
+So with dev=... cdrskin complies to the spirit of the strong urge.
+Without dev=... it has to leave out the whitelist in order to enable bus
+scanning and implicit drive address 0. A tradition of 9 months shall not
+be broken. So burns without dev= will stay possible - but harmless only
+on single drive systems.
+
+Burns without dev= resp. with dev=number are harmless on multi-drive systems.
+
+This is because Cdrskin_grab_drive() either drops the unwanted drives or
+it enforces a restart of the library with the desired drive's persistent
+address. This restart then really uses the strongly urged function
+burn_drive_scan_and_grab().
+Thus, cdrskin complies with the new spirit of API by closing down libburn
+or by dropping unused drives as soon as the persistent drive address is
+known and the drive is to be used with a long running operation. To my
+knowlege all long running operations in cdrskin need a grabbed drive.
+
+This spaghetti approach seems necessary to keep small the impact of new API
+urge on cdrskin's stability. cdrskin suffers from having donated the body
+parts which have been transplanted to libburn in order to create
+ burn_drive_scan_and_grab() . The desired sysadmin friendlyness was already
+achieved by most cdrskin runs. The remaining problem situations should now
+be defused by releasing any short time grabbed flocks of drives during the
+restart of libburn.
+
+------------------------------------------------------------------------------
+This program is currently copyright Thomas Schmitt only.
+The copyrights of several components of libburn.pykix.org are willfully tangled
+at toplevel to form an irrevocable commitment to true open source spirit.
+We have chosen the GPL for legal compatibility and clearly express that it
+shall not hamper the use of our software by non-GPL applications which show
+otherwise the due respect to the open source community.
+See toplevel README and cdrskin/README for that commitment.
+
+For a short time, this place showed a promise to release a BSD license on
+mere request. I have to retract that promise now, and replace it by the
+promise to make above commitment reality in a way that any BSD conformant
+usage in due open source spirit will be made possible somehow and in the
+particular special case. I will not raise public protest if you spawn yourself
+a BSD license from an (outdated) cdrskin.c which still bears that old promise.
+Note that this extended commitment is valid only for cdrskin.[ch],
+cdrfifo.[ch] and cleanup.[ch], but not for libburn.pykix.org as a whole.
+
+cdrskin is originally inspired by libburn-0.2/test/burniso.c :
+(c) Derek Foreman and Ben Jansens
+
+------------------------------------------------------------------------------
+
+Compilation within cdrskin-* :
+
+ cd cdrskin
+ cc -g -I.. -DCdrskin_build_timestamP='...' \
+ -o cdrskin cdrskin.c cdrfifo.c cleanup.c \
+ -L../libburn/.libs -lburn -lpthread
+
+or
+
+ cd ..
+ cc -g -I. -DCdrskin_build_timestamP='...' \
+ -o cdrskin/cdrskin cdrskin/cdrskin.c cdrskin/cdrfifo.c cdrskin/cleanup.c \
+ libburn/async.o libburn/crc.o libburn/debug.o libburn/drive.o \
+ libburn/file.o libburn/init.o libburn/lec.o \
+ libburn/mmc.o libburn/options.o libburn/sbc.o libburn/sector.o \
+ libburn/sg.o libburn/spc.o libburn/source.o libburn/structure.o \
+ libburn/toc.o libburn/util.o libburn/write.o \
+ libburn/libdax_audioxtr.o libburn/libdax_msgs.o \
+ -lpthread
+
+*/
+
+
+/** The official program version */
+#ifndef Cdrskin_prog_versioN
+#define Cdrskin_prog_versioN "0.2.4"
+#endif
+
+/** The source code release timestamp */
+#include "cdrskin_timestamp.h"
+#ifndef Cdrskin_timestamP
+#define Cdrskin_timestamP "-none-given-"
+#endif
+
+/** The binary build timestamp is to be set externally by the compiler */
+#ifndef Cdrskin_build_timestamP
+#define Cdrskin_build_timestamP "-none-given-"
+#endif
+
+
+#ifdef Cdrskin_libburn_versioN
+#undef Cdrskin_libburn_versioN
+#endif
+
+/** use this to accomodate to the CVS version as of Feb 20, 2006
+#define Cdrskin_libburn_cvs_A60220_tS 1
+*/
+#ifdef Cdrskin_libburn_cvs_A60220_tS
+
+#define Cdrskin_libburn_versioN "0.2.tsA60220"
+#define Cdrskin_libburn_no_burn_preset_device_opeN 1
+#ifndef Cdrskin_oldfashioned_api_usE
+#define Cdrskin_oldfashioned_api_usE 1
+#endif
+
+#endif /* Cdrskin_libburn_cvs_A60220_tS */
+
+
+#ifdef Cdrskin_libburn_0_2_2
+#define Cdrskin_libburn_versioN "0.2.2"
+#define Cdrskin_libburn_from_pykix_svN 1
+#endif
+
+#ifdef Cdrskin_libburn_0_2_3
+#define Cdrskin_libburn_versioN "0.2.3"
+#define Cdrskin_libburn_from_pykix_svN 1
+#define Cdrskin_libburn_has_is_enumerablE 1
+#define Cdrskin_libburn_has_convert_fs_adR 1
+#define Cdrskin_libburn_has_convert_scsi_adR 1
+#define Cdrskin_libburn_has_burn_msgS 1
+#define Cdrskin_libburn_has_burn_aborT 1
+#define Cdrskin_libburn_has_audioxtR 1
+#define Cdrskin_libburn_has_get_start_end_lbA 1
+#define Cdrskin_libburn_has_burn_disc_unsuitablE 1
+#define Cdrskin_libburn_has_read_atiP 1
+#define Cdrskin_libburn_has_buffer_progresS 1
+#endif
+
+#ifndef Cdrskin_libburn_versioN
+#define Cdrskin_libburn_versioN "0.2.2"
+#define Cdrskin_libburn_from_pykix_svN 1
+#endif
+
+#ifdef Cdrskin_libburn_from_pykix_svN
+
+#define Cdrskin_libburn_does_ejecT 1
+#define Cdrskin_libburn_has_drive_get_adR 1
+#define Cdrskin_progress_track_does_worK 1
+#define Cdrskin_is_erasable_on_load_does_worK 1
+#define Cdrskin_grab_abort_does_worK 1
+
+#ifdef Cdrskin_new_api_tesT
+
+/* put macros under test caveat here */
+#define Cdrskin_libburn_has_cleanup_handleR 1
+
+#endif
+
+#ifdef Cdrskin_oldfashioned_api_usE
+
+/* switch back to pre-0.2.2 libburn usage */;
+
+#endif
+
+#endif /* Cdrskin_libburn_from_pykix_svN */
+
+
+#ifndef _LARGEFILE_SOURCE
+#define _LARGEFILE_SOURCE 1
+#endif
+#ifndef _FILE_OFFSET_BITS
+#define _FILE_OFFSET_BITS 64
+#endif
+
+
+/* These macros activate cdrskin workarounds for deficiencies resp.
+ problematic features of libburn which hopefully will change in
+ future. */
+
+/** Work around the fact that neither /dev/sg0 (kernel 2.4 + ide-scsi) nor
+ /dev/hdc (kernel 2.6) get ejected by icculus.org/burn */
+#ifndef Cdrskin_libburn_does_ejecT
+#define Cdrskin_burn_drive_eject_brokeN 1
+#endif
+
+/** Work around the fact that after loading media speed report is wrong */
+#define Cdrskin_atip_speed_brokeN 1
+
+/** Work around the fact that burn_drive_get_status() always reports to do
+ track 0 in icculus.org/burn */
+#ifndef Cdrskin_progress_track_does_worK
+#define Cdrskin_progress_track_brokeN 1
+#endif
+
+/** Work around the fact that a drive interrupted at burn_drive_grab() never
+ leaves status BURN_DRIVE_GRABBING in icculus.org/burn */
+#ifndef Cdrskin_grab_abort_does_worK
+#define Cdrskin_grab_abort_brokeN 1
+#endif
+
+/** Work around the fact that a freshly loaded tray with media reports
+ arbitrary media erasability in icculuc.org/burn */
+#ifndef Cdrskin_is_erasable_on_load_does_worK
+#define Cdrskin_is_erasable_on_load_is_brokeN 1
+#endif
+
+/** http://libburn.pykix.org/ticket/41 reports of big trouble without
+ padding any track to a full sector
+*/
+#define Cdrskin_all_tracks_with_sector_paD 1
+
+
+/** A macro which is able to eat up a function call like printf() */
+#ifdef Cdrskin_extra_leaN
+#define ClN(x)
+#else
+#define ClN(x) x
+#endif
+
+
+/** Verbosity level for pacifying progress messages */
+#define Cdrskin_verbose_progresS 1
+
+/** Verbosity level for command recognition and execution logging */
+#define Cdrskin_verbose_cmD 2
+
+/** Verbosity level for reporting of debugging messages */
+#define Cdrskin_verbose_debuG 3
+
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+/* #include */
+#include "../libburn/libburn.h"
+
+#ifdef Cdrskin_libburn_has_audioxtR
+#include "../libburn/libdax_audioxtr.h"
+#endif
+
+#ifdef Cdrskin_libburn_has_cleanup_handleR
+#define Cleanup_set_handlers burn_set_signal_handling
+#define Cleanup_app_handler_T burn_abort_handler_t
+#else
+#include "cleanup.h"
+#endif
+
+
+/** The size of a string buffer for pathnames and similar texts */
+#define Cdrskin_strleN 4096
+
+/** The maximum length +1 of a drive address */
+#ifndef Cdrskin_oldfashioned_api_usE
+#define Cdrskin_adrleN BURN_DRIVE_ADR_LEN
+#else
+#define Cdrskin_adrleN 80
+#endif
+
+
+/* --------------------------------------------------------------------- */
+
+/* Imported from scdbackup-0.8.5/src/cd_backup_planer.c */
+
+/** Macro for creation of arrays of objects (or single objects) */
+#define TSOB_FELD(typ,anz) (typ *) malloc((anz)*sizeof(typ));
+
+
+/** Convert a text so that eventual characters special to the shell are
+ made literal. Note: this does not make a text terminal-safe !
+ @param in_text The text to be converted
+ @param out_text The buffer for the result.
+ It should have size >= strlen(in_text)*5+2
+ @param flag Unused yet
+ @return For convenience out_text is returned
+*/
+char *Text_shellsafe(char *in_text, char *out_text, int flag)
+{
+ int l,i,w=0;
+
+ /* enclose everything by hard quotes */
+ l= strlen(in_text);
+ out_text[w++]= '\'';
+ for(i=0;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; i0)
+ fprintf(stderr,"cdrskin: DEBUG : Reading arguments from file '%s'\n",
+ filenames[i]);
+#endif
+
+ line_no= 0;
+ while(Sfile_fgets(buf,sizeof(buf)-1,fp)!=NULL) {
+ line_no++;
+ l= strlen(buf);
+ if(l==0 || buf[0]=='#')
+ continue;
+ if(pass==0){
+ if(l>maxl)
+ maxl= l;
+ } else {
+ if(argcount >= *argc)
+ break;
+ (*argv)[argcount]= (char *) malloc(l+1);
+ if((*argv)[argcount]==NULL)
+ {ret= -1; goto ex;}
+ strcpy((*argv)[argcount],buf);
+ (*argidx)[argcount]= i;
+ (*arglno)[argcount]= line_no;
+ }
+ argcount++;
+ }
+ fclose(fp); fp= NULL;
+ }
+ if(pass==0){
+ *argc= argcount;
+ if(argcount>0) {
+ *argv= (char **) malloc(argcount*sizeof(char *));
+ *argidx= (int *) malloc(argcount*sizeof(int));
+ *arglno= (int *) malloc(argcount*sizeof(int));
+ if(*argv==NULL || *argidx==NULL || *arglno==NULL)
+ {ret= -1; goto ex;}
+ }
+ for(i=0;i<*argc;i++) {
+ (*argv)[i]= NULL;
+ (*argidx)[i]= -1;
+ (*arglno)[i]= -1;
+ }
+ }
+ }
+
+ ret= 1;
+ex:;
+ if(fp!=NULL)
+ fclose(fp);
+ return(ret);
+}
+
+
+/** Combine environment variable HOME with given filename
+ @param filename Address relative to $HOME
+ @param fileadr Resulting combined address
+ @param fa_size Size of array fileadr
+ @param flag Unused yet
+ @return 1=ok , 0=no HOME variable , -1=result address too long
+*/
+int Sfile_home_adr_s(char *filename, char *fileadr, int fa_size, int flag)
+{
+ char *home;
+
+ strcpy(fileadr,filename);
+ home= getenv("HOME");
+ if(home==NULL)
+ return(0);
+ if(strlen(home)+strlen(filename)+1>=fa_size)
+ return(-1);
+ strcpy(fileadr,home);
+ if(filename[0]!=0){
+ strcat(fileadr,"/");
+ strcat(fileadr,filename);
+ }
+ return(1);
+}
+
+
+#endif /* ! Cdrskin_extra_leaN */
+
+
+/* --------------------------------------------------------------------- */
+
+/** Address translation table for users/applications which do not look
+ for the output of -scanbus but guess a Bus,Target,Lun on their own.
+*/
+
+/** The maximum number of entries in the address translation table */
+#define Cdradrtrn_leN 256
+
+/** The address prefix which will prevent translation */
+#define Cdrskin_no_transl_prefiX "LITERAL_ADR:"
+
+
+struct CdradrtrN {
+ char *from_address[Cdradrtrn_leN];
+ char *to_address[Cdradrtrn_leN];
+ int fill_counter;
+};
+
+
+#ifndef Cdrskin_extra_leaN
+
+/** Create a device address translator object */
+int Cdradrtrn_new(struct CdradrtrN **trn, int flag)
+{
+ struct CdradrtrN *o;
+ int i;
+
+ (*trn)= o= TSOB_FELD(struct CdradrtrN,1);
+ if(o==NULL)
+ return(-1);
+ for(i= 0;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->is_from_stdin= !!(flag&2);
+ o->fixed_size= 0.0;
+ o->padding= 0.0;
+ o->set_by_padsize= 0;
+ o->sector_pad_up= Cdrskin_all_tracks_with_sector_paD;
+ o->track_type= BURN_MODE1;
+ o->sector_size= 2048.0;
+ o->track_type_by_default= 1;
+ o->swap_audio_bytes= 0;
+ o->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),
+ &(o->set_by_padsize),&(skin_track_type),
+ &(o->track_type_by_default),&(o->swap_audio_bytes),
+ 0);
+ if(ret<=0)
+ goto failed;
+ Cdrtrack_set_track_type(o,skin_track_type,0);
+
+#ifndef Cdrskin_extra_leaN
+ ret= Cdrskin_get_fifo_par(boss, &(o->fifo_enabled),&(o->fifo_size),
+ &(o->fifo_start_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_set_track_type(struct CdrtracK *o, int track_type, int flag)
+{
+ if(track_type==BURN_AUDIO) {
+ o->track_type= BURN_AUDIO;
+ o->sector_size= 2352.0;
+ } else {
+ o->track_type= BURN_MODE1;
+ o->sector_size= 2048.0;
+ }
+ return(1);
+}
+
+
+int Cdrtrack_get_size(struct CdrtracK *track, double *size, double *padding,
+ double *sector_size, int flag)
+{
+ *size= track->fixed_size;
+ *padding= track->padding;
+ *sector_size= track->sector_size;
+ return(1);
+}
+
+
+int Cdrtrack_get_fifo(struct CdrtracK *track, struct CdrfifO **fifo, int flag)
+{
+ *fifo= track->fifo;
+ return(1);
+}
+
+
+/** Try wether automatic audio extraction is appropriate and eventually open
+ a file descriptor to the raw data.
+ @return -3 identified as .wav but with cdrecord-inappropriate parameters
+ -2 could not open track source, no use in retrying
+ -1 severe error
+ 0 not appropriate to extract, burn plain file content
+ 1 to be extracted, *fd is a filedescriptor delivering raw data
+*/
+int Cdrtrack_extract_audio(struct CdrtracK *track, int *fd, off_t *xtr_size,
+ int flag)
+{
+ int l, ok= 0;
+#ifdef Cdrskin_libburn_has_audioxtR
+ struct libdax_audioxtr *xtr= NULL;
+ char *fmt,*fmt_info;
+ int num_channels,sample_rate,bits_per_sample,msb_first,ret;
+#endif
+
+ *fd= -1;
+
+ if(track->track_type!=BURN_AUDIO && !track->track_type_by_default)
+ return(0);
+ l= strlen(track->source_path);
+ if(l>=4)
+ if(strcmp(track->source_path+l-4,".wav")==0)
+ ok= 1;
+ if(l>=3)
+ if(strcmp(track->source_path+l-3,".au")==0)
+ ok= 1;
+ if(!ok)
+ return(0);
+
+ if(track->track_type_by_default) {
+ Cdrtrack_set_track_type(track,BURN_AUDIO,0);
+ track->track_type_by_default= 2;
+ fprintf(stderr,"cdrskin: NOTE : Activated -audio for '%s'\n",
+ track->source_path);
+ }
+
+#ifdef Cdrskin_libburn_has_audioxtR
+
+ ret= libdax_audioxtr_new(&xtr,track->source_path,0);
+ if(ret<=0)
+ return(ret);
+ libdax_audioxtr_get_id(xtr,&fmt,&fmt_info,
+ &num_channels,&sample_rate,&bits_per_sample,&msb_first,0);
+ if((strcmp(fmt,".wav")!=0 && strcmp(fmt,".au")!=0) ||
+ num_channels!=2 || sample_rate!=44100 || bits_per_sample!=16) {
+ fprintf(stderr,"cdrskin: ( %s )\n",fmt_info);
+ fprintf(stderr,"cdrskin: FATAL : Inappropriate audio coding in '%s'.\n",
+ track->source_path);
+ {ret= -3; goto ex;}
+ }
+ libdax_audioxtr_get_size(xtr,xtr_size,0);
+ ret= libdax_audioxtr_detach_fd(xtr,fd,0);
+ if(ret<=0)
+ {ret= -1*!!ret; goto ex;}
+ track->swap_audio_bytes= !!msb_first;
+ fprintf(stderr,"cdrskin: NOTE : %.f %saudio bytes in '%s'\n",
+ (double) *xtr_size, (msb_first ? "" : "(-swab) "),
+ track->source_path);
+ ret= 1;
+ex:
+ libdax_audioxtr_destroy(&xtr,0);
+ return(ret);
+
+#else /* Cdrskin_libburn_has_audioxtR */
+
+ return(0);
+
+#endif
+}
+
+
+/** Deliver an open file descriptor corresponding to the source path of track.
+ @return <=0 error, 1 success
+*/
+int Cdrtrack_open_source_path(struct CdrtracK *track, int *fd, int flag)
+{
+ int is_wav= 0;
+ off_t xtr_size= 0;
+
+ if(track->source_path[0]=='-' && track->source_path[1]==0)
+ *fd= 0;
+ else if(track->source_path[0]=='#' &&
+ (track->source_path[1]>='0' && track->source_path[1]<='9'))
+ *fd= atoi(track->source_path+1);
+ else {
+ *fd= -1;
+ is_wav= Cdrtrack_extract_audio(track,fd,&xtr_size,0);
+ if(is_wav==-1)
+ return(-1);
+ if(is_wav==-3)
+ return(0);
+ if(is_wav==0)
+ *fd= open(track->source_path,O_RDONLY);
+ if(*fd==-1) {
+ fprintf(stderr,"cdrskin: failed to open source address '%s'\n",
+ track->source_path);
+ fprintf(stderr,"cdrskin: errno=%d , \"%s\"\n",errno,
+ errno==0?"-no error code available-":strerror(errno));
+ return(0);
+ }
+ if(track->fixed_size<=0) {
+ if(xtr_size>0)
+ track->fixed_size= xtr_size;
+ else {
+ struct stat stbuf;
+ if(fstat(*fd,&stbuf)!=-1)
+ track->fixed_size= stbuf.st_size;
+ }
+ }
+ }
+
+ if(track->fixed_size < Cdrtrack_minimum_sizE * track->sector_size) {
+ if(track->track_type == BURN_AUDIO) {
+ /* >>> cdrecord: We differ in automatic padding with audio:
+ Audio tracks must be at least 705600 bytes and a multiple of 2352.
+ */
+ fprintf(stderr,
+ "cdrskin: FATAL : Audio tracks must be at least %.f bytes\n",
+ Cdrtrack_minimum_sizE*track->sector_size);
+ return(0);
+ } else {
+ fprintf(stderr,
+ "cdrskin: NOTE : Enforcing minimum track size of %.f bytes\n",
+ Cdrtrack_minimum_sizE*track->sector_size);
+ track->fixed_size= Cdrtrack_minimum_sizE*track->sector_size;
+ }
+ }
+ track->source_fd= *fd;
+ return(*fd>=0);
+}
+
+
+#ifndef Cdrskin_extra_leaN
+
+/** Install a fifo object between data source and libburn.
+ Its parameters are known to track.
+ @param outlet_fd Returns the filedescriptor of the fifo outlet.
+ @param previous_fifo Object address for chaining or follow-up attachment.
+ @param flag Bitfield for control purposes:
+ bit0= Debugging verbosity
+ bit1= Do not create and attach a new fifo
+ but attach new follow-up fd pair to previous_fifo
+ @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 {
+
+ /* >>> ??? obtain track sector size and use instead of 2048 ? */
+
+ ret= Cdrfifo_new(&ff,source_fd,pipe_fds[1],2048,track->fifo_size,0);
+ if(ret<=0)
+ return(ret);
+ if(previous_fifo!=NULL)
+ Cdrfifo_attach_peer(previous_fifo,ff,0);
+ track->fifo= 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,buffer_fill,buffer_space;
+
+ 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);
+
+/** Ticket 55: check fifos for input, throw error on 0-bytes from stdin
+ @return <=0 abort run, 1 go on with burning
+*/
+ if(track->is_from_stdin) {
+ ret= Cdrfifo_get_buffer_state(track->fifo,&buffer_fill,&buffer_space,0);
+ if(ret<0 || buffer_fill<=0) {
+ fprintf(stderr,"\ncdrskin: FATAL : (First track) fifo did not read a single byte from stdin\n");
+ return(0);
+ }
+ }
+ return(1);
+}
+
+#endif /* ! Cdrskin_extra_leaN */
+
+
+/** Create a corresponding libburn track object and add it to the libburn
+ session. This may change the trackno index set by Cdrtrack_new().
+*/
+int Cdrtrack_add_to_session(struct CdrtracK *track, int trackno,
+ struct burn_session *session, int flag)
+/*
+ bit0= debugging verbosity
+ bit1= apply padding hack (<<< should be unused for now)
+*/
+{
+ struct burn_track *tr;
+ struct burn_source *src= NULL;
+ double padding,lib_padding;
+ int ret,sector_pad_up;
+ double fixed_size;
+ int source_fd;
+
+ track->trackno= trackno;
+ tr= burn_track_create();
+ track->libburn_track= tr;
+
+ /* Note: track->track_type may get set in here */
+ if(track->source_fd==-1) {
+ ret= Cdrtrack_open_source_path(track,&source_fd,0);
+ if(ret<=0)
+ goto ex;
+ }
+
+ padding= 0.0;
+ sector_pad_up= track->sector_pad_up;
+ if(track->padding>0) {
+ if(track->set_by_padsize || track->track_type!=BURN_AUDIO)
+ padding= track->padding;
+ else
+ sector_pad_up= 1;
+ }
+ if(flag&2)
+ lib_padding= 0.0;
+ else
+ lib_padding= padding;
+ if(flag&1) {
+ if(sector_pad_up) {
+ ClN(fprintf(stderr,"cdrskin_debug: track %d telling burn_track_define_data() to pad up last sector\n",trackno+1));
+ }
+ if(lib_padding>0 || !sector_pad_up) {
+ ClN(fprintf(stderr,
+ "cdrskin_debug: track %d telling burn_track_define_data() to pad %.f bytes\n",
+ trackno+1,lib_padding));
+ }
+ }
+ burn_track_define_data(tr,0,(int) lib_padding,sector_pad_up,
+ track->track_type);
+ burn_track_set_byte_swap(tr,
+ (track->track_type==BURN_AUDIO && track->swap_audio_bytes));
+ fixed_size= track->fixed_size;
+ if((flag&2) && track->padding>0) {
+ if(flag&1)
+ ClN(fprintf(stderr,"cdrskin_debug: padding hack : %.f + %.f = %.f\n",
+ track->fixed_size,track->padding,
+ track->fixed_size+track->padding));
+ fixed_size+= track->padding;
+ }
+ src= burn_fd_source_new(track->source_fd,-1,(off_t) fixed_size);
+
+ if(src==NULL) {
+ fprintf(stderr,
+ "cdrskin: FATAL : Could not create libburn data source object\n");
+ {ret= 0; goto ex;}
+ }
+ if(burn_track_set_source(tr,src)!=BURN_SOURCE_OK) {
+ fprintf(stderr,"cdrskin: FATAL : libburn rejects data source object\n");
+ {ret= 0; goto ex;}
+ }
+ burn_session_add_track(session,tr,BURN_POS_END);
+ ret= 1;
+ex:
+ if(src!=NULL)
+ burn_source_free(src);
+ return(ret);
+}
+
+
+/** Release libburn track information after a session is done */
+int Cdrtrack_cleanup(struct CdrtracK *track, int flag)
+{
+ if(track->libburn_track==NULL)
+ return(0);
+ burn_track_free(track->libburn_track);
+ track->libburn_track= NULL;
+ return(1);
+}
+
+
+int Cdrtrack_ensure_padding(struct CdrtracK *track, int flag)
+/*
+flag:
+ bit0= debugging verbosity
+*/
+{
+ if(track->track_type!=BURN_AUDIO)
+ return(2);
+ if(flag&1)
+ fprintf(stderr,"cdrskin_debug: enforcing -pad on last -audio track\n");
+ track->sector_pad_up= 1;
+ return(1);
+}
+
+
+#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 creation of the CdrskiN
+ object. It finally becomes a managed subordinate of the CdrskiN object.
+*/
+struct CdrpreskiN {
+
+ /* to be transfered into skin */
+ int verbosity;
+ char queue_severity[81];
+ char print_severity[81];
+
+ /** Stores eventually given absolute device address before translation */
+ char raw_device_adr[Cdrskin_adrleN];
+
+ /** Stores an eventually given translated absolute device address between
+ Cdrpreskin_setup() and Cdrskin_create() .
+ */
+ char device_adr[Cdrskin_adrleN];
+
+ /** The eventual address translation table */
+ struct CdradrtrN *adr_trn;
+
+ /** Memorizes the abort handling mode from presetup to creation of
+ control object. Defined handling modes are:
+ 0= no abort handling
+ 1= try to cancel, release, exit (leave signal mode as set by caller)
+ 2= try to ignore all signals
+ 3= mode 1 in normal operation, mode 2 during abort handling
+ 4= mode 1 in normal operation, mode 0 during abort handling
+ -1= install abort handling 1 only in Cdrskin_burn() after burning started
+ */
+ int abort_handler;
+
+ /** Wether to allow getuid()!=geteuid() */
+ int allow_setuid;
+
+ /** Wether to allow user provided addresses like #4 */
+ int allow_fd_source;
+
+ /** Wether an option is given which needs a full bus scan */
+ int no_whitelist;
+
+ /** Wether the translated device address shall not follow softlinks, device
+ clones and SCSI addresses */
+ int no_convert_fs_adr;
+
+ /** Wether Bus,Target,Lun addresses shall be converted literally as old
+ Pseudo SCSI-Adresses. New default is to use (possibly system emulated)
+ real SCSI addresses via burn_drive_convert_scsi_adr() and literally
+ emulated and cdrecord-incompatible ATA: addresses. */
+ int old_pseudo_scsi_adr;
+
+ /** Wether bus scans shall exit!=0 if no drive was found */
+ int scan_demands_drive;
+
+ /** Wether to abort when a busy drive is encountered during bus scan */
+ int abort_on_busy_drive;
+
+ /** Wether to try to avoid collisions when opening drives */
+ int drive_exclusive;
+
+ /** Wether to try to wait for unwilling drives to become willing to open */
+ int drive_blocking;
+
+
+#ifndef Cdrskin_extra_leaN
+
+ /** List of startupfiles */
+ char rc_filenames[Cdrpreskin_rc_nuM][Cdrskin_strleN];
+ int rc_filename_count;
+
+ /** Non-argument options from startupfiles */
+ int pre_argc;
+ char **pre_argv;
+ int *pre_argidx;
+ int *pre_arglno;
+
+#endif /* ! Cdrskin_extra_leaN */
+
+};
+
+
+
+/** Create a preliminary cdrskin program run control object. It will become
+ part of the final control object.
+ @param preskin Returns pointer to resulting
+ @param flag Bitfield for control purposes: unused yet
+ @return <=0 error, 1 success
+*/
+int Cdrpreskin_new(struct CdrpreskiN **preskin, int flag)
+{
+ struct CdrpreskiN *o;
+ int i;
+
+ (*preskin)= o= TSOB_FELD(struct CdrpreskiN,1);
+ if(o==NULL)
+ return(-1);
+
+ o->verbosity= 0;
+ strcpy(o->queue_severity,"NEVER");
+ strcpy(o->print_severity,"SORRY");
+ o->raw_device_adr[0]= 0;
+ o->device_adr[0]= 0;
+ o->adr_trn= NULL;
+ o->abort_handler= 3;
+ o->allow_setuid= 0;
+ o->allow_fd_source= 0;
+ o->no_whitelist= 0;
+ o->no_convert_fs_adr= 0;
+#ifdef Cdrskin_libburn_has_convert_scsi_adR
+ o->old_pseudo_scsi_adr= 0;
+#else
+ o->old_pseudo_scsi_adr= 1;
+#endif
+ o->scan_demands_drive= 0;
+ o->abort_on_busy_drive= 0;
+ o->drive_exclusive= 1;
+ o->drive_blocking= 0;
+
+#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);
+}
+
+
+int Cdrpreskin_set_severities(struct CdrpreskiN *preskin, char *queue_severity,
+ char *print_severity, int flag)
+{
+/*
+ if(preskin->verbosity>=Cdrskin_verbose_debuG)
+ fprintf(stderr,
+ "cdrskin: DEBUG : queue_severity='%s' print_severity='%s'\n",
+ queue_severity,print_severity);
+*/
+
+ if(queue_severity!=NULL)
+ strcpy(preskin->queue_severity,queue_severity);
+ if(print_severity!=NULL)
+ strcpy(preskin->print_severity,print_severity);
+#ifdef Cdrskin_libburn_has_burn_msgS
+ burn_msgs_set_severities(preskin->queue_severity, preskin->print_severity,
+ "cdrskin: ");
+#endif
+ return(1);
+}
+
+
+int Cdrpreskin_initialize_lib(struct CdrpreskiN *preskin, int flag)
+{
+ int ret;
+
+ ret= burn_initialize();
+ if(ret==0) {
+ fprintf(stderr,"cdrskin : FATAL : Initialization of libburn failed\n");
+ return(0);
+ }
+ Cdrpreskin_set_severities(preskin,NULL,NULL,0);
+ return(1);
+}
+
+
+/** Enable queuing of libburn messages or disable and print queue content.
+ @param flag Bitfield for control purposes:
+ bit0= enable queueing, else disable and print
+*/
+int Cdrpreskin_queue_msgs(struct CdrpreskiN *o, int flag)
+{
+#ifdef Cdrskin_libburn_has_burn_msgS
+#ifndef Cdrskin_extra_leaN
+#define Cdrskin_debug_libdax_msgS 1
+#endif
+/* <<< In cdrskin there is not much sense in queueing library messages.
+ It is done here only for debugging */
+#ifdef Cdrskin_debug_libdax_msgS
+
+ int ret;
+ static char queue_severity[81]= {"NEVER"}, print_severity[81]= {"SORRY"};
+ static int queueing= 0;
+ char msg[BURN_MSGS_MESSAGE_LEN],msg_severity[81],filler[81];
+ int error_code,os_errno,first,i;
+
+ if(flag&1) {
+ if(!queueing) {
+ strcpy(queue_severity,o->queue_severity);
+ strcpy(print_severity,o->print_severity);
+ }
+ if(o->verbosity>=Cdrskin_verbose_debuG)
+ Cdrpreskin_set_severities(o,"DEBUG","NEVER",0);
+ else
+ Cdrpreskin_set_severities(o,"SORRY","NEVER",0);
+ queueing= 1;
+ return(1);
+ }
+
+ if(queueing)
+ Cdrpreskin_set_severities(o,queue_severity,print_severity,0);
+ queueing= 0;
+
+ for(first= 1; ; first= 0) {
+ ret= burn_msgs_obtain("ALL",&error_code,msg,&os_errno,msg_severity);
+ if(ret==0)
+ break;
+ if(ret<0) {
+ fprintf(stderr,
+ "cdrskin: NOTE : Please inform libburn-hackers@pykix.org about:\n");
+ fprintf(stderr,
+ "cdrskin: burn_msgs_obtain() returns %d\n",ret);
+ break;
+ }
+ if(first)
+ fprintf(stderr,
+"cdrskin: -------------------- Messages from Libburn ---------------------\n");
+ for(i=0;msg_severity[i]!=0;i++)
+ filler[i]= ' ';
+ filler[i]= 0;
+ fprintf(stderr,"cdrskin: %s : %s\n",msg_severity,msg);
+ if(strcmp(msg_severity,"DEBUG")!=0 && os_errno!=0)
+ fprintf(stderr,"cdrskin: %s ( errno=%d '%s')\n",
+ filler,os_errno,strerror(os_errno));
+ }
+ if(first==0)
+ fprintf(stderr,
+"cdrskin: ----------------------------------------------------------------\n");
+
+#endif /* Cdrskin_debug_libdax_msgS */
+#endif /* Cdrskin_libburn_has_burn_msgS */
+
+ return(1);
+}
+
+
+/** Convert a cdrecord-style device address into a libburn device address or
+ into a libburn drive number. It depends on the "scsibus" number of the
+ cdrecord-style address which kind of libburn address emerges:
+ bus=0 : drive number , bus=1 : /dev/sgN , bus=2 : /dev/hdX
+ (This call intentionally has no CdrpreskiN argument)
+ @param flag Bitfield for control purposes:
+ bit0= old_pseudo_scsi_adr
+ @return 1 success, 0=no recognizable format, -1=severe error,
+ -2 could not find scsi device, -3 address format error
+*/
+int Cdrpreskin__cdrecord_to_dev(char *adr, char device_adr[Cdrskin_adrleN],
+ int *driveno, int flag)
+{
+ int comma_seen= 0,digit_seen= 0,busno= 0,k,lun_no= -1;
+
+ *driveno= -1;
+ device_adr[0]= 0;
+ if(strlen(adr)==0)
+ return(0);
+
+ /* read the trailing numeric string as device address code */
+ /* accepts "1" , "0,1,0" , "ATA:0,1,0" , ... */
+ for(k= strlen(adr)-1;k>=0;k--) {
+ if(adr[k]==',' && !comma_seen) {
+ sscanf(adr+k+1,"%d",&lun_no);
+ comma_seen= 1;
+ digit_seen= 0;
+ continue;
+ }
+ if(adr[k]<'0' || adr[k]>'9')
+ break;
+ digit_seen= 1;
+ }
+ if(!digit_seen) {
+ k= strlen(adr)-1;
+ if(adr[k]==':' || (adr[k]>='A' && adr[k]<='Z')) { /* empty prefix ? */
+ *driveno= 0;
+ return(1);
+ }
+ return(0);
+ }
+ sscanf(adr+k+1,"%d",driveno);
+
+ digit_seen= 0;
+ if(k>0) if(adr[k]==',') {
+ for(k--;k>=0;k--) {
+ if(adr[k]<'0' || adr[k]>'9')
+ break;
+ digit_seen= 1;
+ }
+ if(digit_seen) {
+ sscanf(adr+k+1,"%d",&busno);
+ if(flag&1) {
+ /* look for symbolic bus : 1=/dev/sgN 2=/dev/hdX */
+ if(busno==1) {
+ sprintf(device_adr,"/dev/sg%d",*driveno);
+ } else if(busno==2) {
+ sprintf(device_adr,"/dev/hd%c",'a'+(*driveno));
+ } else if(busno!=0) {
+ fprintf(stderr,
+ "cdrskin: FATAL : dev=[Prefix:]Bus,Target,Lun expects Bus out of {0,1,2}\n");
+ return(-3);
+ }
+ } else {
+ if(busno<0) {
+ fprintf(stderr,
+ "cdrskin: FATAL : dev=[Prefix:]Bus,Target,Lun expects Bus number >= 0\n");
+ return(-3);
+ }
+ if((strncmp(adr,"ATA",3)==0 && (adr[3]==0 || adr[3]==':')) ||
+ (strncmp(adr,"ATAPI",5)==0 && (adr[5]==0 || adr[5]==':'))) {
+
+ if(busno>12 || (*driveno)<0 || (*driveno)>1) {
+ fprintf(stderr,
+"cdrskin: FATAL : dev=ATA:Bus,Target,Lun expects Bus {0..12}, Target {0,1}\n");
+ return(-3);
+ }
+ sprintf(device_adr,"/dev/hd%c",'a'+(2*busno)+(*driveno));
+
+#ifdef Cdrskin_libburn_has_convert_scsi_adR
+ } else {
+ int ret;
+
+ ret= burn_drive_convert_scsi_adr(busno,-1,-1,*driveno,lun_no,
+ device_adr);
+ if(ret==0) {
+ fprintf(stderr,
+ "cdrskin: FATAL : Cannot find /dev/sgN with Bus,Target,Lun = %d,%d,%d\n",
+ busno,*driveno,lun_no);
+ fprintf(stderr,
+ "cdrskin: HINT : This drive may be in use by another program currently\n");
+ return(-2);
+ } else if(ret<0)
+ return(-1);
+ return(1);
+
+#endif /* Cdrskin_libburn_has_convert_scsi_adR */
+ }
+ }
+ }
+ }
+ return(1);
+}
+
+
+#ifndef Cdrskin_extra_leaN
+
+/** Load content startup files into preskin cache */
+int Cdrpreskin_read_rc(struct CdrpreskiN *o, char *progname, int flag)
+{
+ int ret,i;
+ char *filenames_v[3];
+
+ for(i=0;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 || strcmp(argv[1],"-version")==0 ||
+ strcmp(argv[1],"--help")==0 || strcmp(argv[1],"-help")==0)
+ flag|= 2;
+ }
+ if(!(flag&2)) {
+ ret= Cdrpreskin_read_rc(o,argv[0],0);
+ if(ret<0)
+ return(-1);
+ if(o->pre_argc>1) {
+ ret= Cdrpreskin_setup(o,o->pre_argc,o->pre_argv,flag|1|2);
+ if(ret<=0)
+ return(ret);
+ /* ??? abort on ret==2 ? */
+ }
+ }
+#endif
+
+ if(argc==1) {
+ fprintf(stderr,"cdrskin: SORRY : no options given. Try option --help\n");
+ return(0);
+ }
+ for (i= 1;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],"--bragg_with_audio")==0) {
+ /* OBSOLETE 0.2.3 */;
+
+ } else if(strcmp(argv[i],"--demand_a_drive")==0) {
+ o->scan_demands_drive= 1;
+
+ } else if(strcmp(argv[i],"--devices")==0) {
+ printf("Note: If this hangs for a while then there is a drive with\n");
+ printf(" unexpected problems (e.g. ill DMA).\n");
+ printf(" One may exclude such a device file by removing r- and w-\n");
+ printf(" permissions for all cdrskin users.\n");
+ o->no_whitelist= 1;
+
+ } else if(strncmp(argv[i],"dev_translation=",16)==0) {
+
+#ifndef Cdrskin_extra_leaN
+
+ if(o->adr_trn==NULL) {
+ ret= Cdradrtrn_new(&(o->adr_trn),0);
+ if(ret<=0)
+ goto no_adr_trn_mem;
+ }
+ if(argv[i][16]==0) {
+ fprintf(stderr,
+ "cdrskin: FATAL : dev_translation= : missing separator character\n");
+ return(0);
+ }
+ ret= Cdradrtrn_add(o->adr_trn,argv[i]+17,argv[i]+16,1);
+ if(ret==-2) {
+no_adr_trn_mem:;
+ fprintf(stderr,
+ "cdrskin: FATAL : address_translation= : cannot allocate memory\n");
+ } else if(ret==-1)
+ fprintf(stderr,
+ "cdrskin: FATAL : address_translation= : table full (%d items)\n",
+ Cdradrtrn_leN);
+ else if(ret==0)
+ fprintf(stderr,
+ "cdrskin: FATAL : address_translation= : no address separator '%c' found\n",
+ argv[i][17]);
+ if(ret<=0)
+ return(0);
+
+#else /* ! Cdrskin_extra_leaN */
+
+ fprintf(stderr,
+ "cdrskin: FATAL : dev_translation= is not available in lean version\n");
+ return(0);
+
+#endif /* Cdrskin_extra_leaN */
+
+
+ } else if(strncmp(argv[i],"-dev=",5)==0) {
+ value_pt= argv[i]+5;
+ goto set_dev;
+ } else if(strncmp(argv[i],"dev=",4)==0) {
+ value_pt= argv[i]+4;
+set_dev:;
+ if(strcmp(value_pt,"help")==0) {
+
+#ifndef Cdrskin_extra_leaN
+
+ printf("Supported SCSI transports for this platform:\n");
+ fflush(stdout);
+ 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],"--drive_scsi_exclusive")==0) {
+ o->drive_exclusive= 2;
+
+ } else if(strcmp(argv[i],"driveropts=help")==0 ||
+ strcmp(argv[i],"-driveropts=help")==0) {
+
+#ifndef Cdrskin_extra_leaN
+
+ fprintf(stderr,"Driver options:\n");
+ fprintf(stderr,"burnfree\tPrepare writer to use BURN-Free technology\n");
+ fprintf(stderr,"noburnfree\tDisable using BURN-Free technology\n");
+
+#else /* ! Cdrskin_extra_leaN */
+
+ goto see_cdrskin_eng_html;
+
+#endif /* Cdrskin_extra_leaN */
+
+ if(argc==2 || (i==2 && argc==3 && strncmp(argv[1],"dev=",4)==0))
+ {ret= 2; goto final_checks;}
+
+ } else if(strcmp(argv[i],"--help")==0) {
+
+#ifndef Cdrskin_extra_leaN
+
+ printf("\n");
+ printf("Usage: %s [options|source_addresses]\n", argv[0]);
+ printf("Burns preformatted data to CD-R 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(
+ " --drive_scsi_exclusive try to exclusively reserve device files\n");
+ printf(" /dev/srN, /dev/scdM, /dev/stK of drive.\n");
+#ifdef Cdrskin_burn_drive_eject_brokeN
+ printf(
+ " eject_device= set the device address for command eject\n");
+#endif
+ printf(" --fifo_disable disable fifo despite any fs=...\n");
+ printf(" --fifo_per_track use a separate fifo for each track\n");
+ printf(
+ " --fifo_start_empty do not wait for full fifo before burn start\n");
+ printf(
+ " grab_drive_and_wait= grab drive, wait given number of\n");
+ printf(
+ " seconds, release drive, and do normal work\n");
+ printf(
+ " --ignore_signals try to ignore any signals rather than to abort\n");
+ printf(" --no_abort_handler exit even if the drive is in busy state\n");
+ printf(" --no_blank_appendable refuse to blank appendable CD-RW\n");
+ printf(" --no_convert_fs_adr only literal translations of dev=\n");
+ printf(
+ " --no_rc as first argument: do not read startup files\n");
+ printf(" --old_pseudo_scsi_adr use and report literal Bus,Target,Lun\n");
+ printf(" rather than real SCSI and pseudo ATA.\n");
+ printf(
+ " --single_track accept only last argument as source_address\n");
+ printf(
+ " tao_to_sao_tsize= substitute -tao by -sao and eventually\n");
+ printf(" augment 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 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-toc\t\tretrieve and print TOC/PMA data\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-audio\t\tSubsequent tracks are CD-DA audio tracks\n");
+ fprintf(stderr,
+ "\t-data\t\tSubsequent tracks are CD-ROM data mode 1 (default)\n");
+ fprintf(stderr,"\t-pad\t\tpadsize=30k\n");
+ fprintf(stderr,"\t-nopad\t\tDo not pad (default)\n");
+ fprintf(stderr,
+ "\t-swab\t\tAudio data source is byte-swapped (little-endian/Intel)\n");
+ fprintf(stderr,"\t-help\t\tprint this text to stderr and exit\n");
+ fprintf(stderr,
+ "Option -audio does automatic extraction of .wav but not of .au .\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");
+ fprintf(stderr,
+ "the source delivers fewer bytes. But 0 bytes from stdin with fifo\n");
+ fprintf(stderr,
+ "enabled will lead to abort and no burn attempt at all.\n");
+
+#else /* ! Cdrskin_extra_leaN */
+
+ fprintf(stderr,"Note: This is not cdrecord. See cdrskin start message on stdout.\n");
+ fprintf(stderr,
+ "(writer profile: -atip retrieve, blank=type, -eject after work)\n");
+ goto see_cdrskin_eng_html;
+
+#endif /* Cdrskin_extra_leaN */
+
+ {ret= 2; goto final_checks;}
+
+ } else if(strcmp(argv[i],"--ignore_signals")==0) {
+ o->abort_handler= 2;
+
+ } else if(strcmp(argv[i],"--no_abort_handler")==0) {
+ o->abort_handler= 0;
+
+ } else if(strcmp(argv[i],"--no_convert_fs_adr")==0) {
+ o->no_convert_fs_adr= 1;
+
+ } else if(strcmp(argv[i],"--old_pseudo_scsi_adr")==0) {
+ o->old_pseudo_scsi_adr= 1;
+
+ } else if(strcmp(argv[i],"--no_rc")==0) {
+ if(i!=1)
+ fprintf(stderr,
+ "cdrskin: NOTE : option --no_rc would only work as first argument.\n");
+
+ } else if(strcmp(argv[i],"-scanbus")==0) {
+ o->no_whitelist= 1;
+
+ } else if(strcmp(argv[i],"-v")==0 || strcmp(argv[i],"-verbose")==0) {
+ (o->verbosity)++;
+ printf("cdrskin: verbosity level : %d\n",o->verbosity);
+ if(o->verbosity>=Cdrskin_verbose_debuG)
+ Cdrpreskin_set_severities(o,"NEVER","DEBUG",0);
+
+ } else if(strcmp(argv[i],"-version")==0) {
+ printf(
+ "Cdrecord 2.01-Emulation Copyright (C) 2006, see libburn.pykix.org\n");
+ printf("libburn version : %s\n",Cdrskin_libburn_versioN);
+
+#ifndef Cdrskin_extra_leaN
+ printf("cdrskin version : %s\n",Cdrskin_prog_versioN);
+#else
+ printf("cdrskin version : %s.lean (capability reduced lean version)\n",
+ Cdrskin_prog_versioN);
+#endif
+
+ printf("Version timestamp : %s\n",Cdrskin_timestamP);
+ printf("Build timestamp : %s\n",Cdrskin_build_timestamP);
+ {ret= 2; goto final_checks;}
+ }
+
+ }
+ ret= 1;
+final_checks:;
+ if(flag&1)
+ goto ex;
+
+ if(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; goto ex;
+ }
+
+ if(strlen(o->raw_device_adr)>0 && !o->no_whitelist) {
+ int driveno,hret;
+ char *adr,buf[Cdrskin_adrleN];
+
+ adr= o->raw_device_adr;
+
+#ifndef Cdrskin_extra_leaN
+ if(o->adr_trn!=NULL) {
+ hret= Cdradrtrn_translate(o->adr_trn,adr,-1,buf,0);
+ if(hret<=0) {
+ fprintf(stderr,
+ "cdrskin: FATAL : address translation failed (address too long ?) \n");
+ {ret= 0; goto ex;}
+ }
+ adr= buf;
+ }
+#endif /* ! Cdrskin_extra_leaN */
+
+ if(adr[0]=='/') {
+ if(strlen(adr)>=sizeof(o->device_adr)) {
+dev_too_long:;
+ fprintf(stderr,
+ "cdrskin: FATAL : dev=... too long (max. %d characters)\n",
+ sizeof(o->device_adr)-1);
+ {ret= 0; goto ex;}
+ }
+ strcpy(o->device_adr,adr);
+ } else {
+ ret= Cdrpreskin__cdrecord_to_dev(adr,o->device_adr,&driveno,
+ !!o->old_pseudo_scsi_adr);
+ if(ret==-2 || ret==-3)
+ {ret= 0; goto ex;}
+ if(ret<0)
+ goto ex;
+ if(ret==0) {
+ strcpy(o->device_adr,adr);
+ ret= 1;
+ }
+ }
+
+#ifdef Cdrskin_libburn_has_convert_fs_adR
+
+ if(strlen(o->device_adr)>0 && !o->no_convert_fs_adr) {
+ int lret;
+ char link_adr[Cdrskin_strleN+1];
+
+ strcpy(link_adr,o->device_adr);
+ lret = burn_drive_convert_fs_adr(link_adr,o->device_adr);
+ if(lret<0) {
+ fprintf(stderr,
+ "cdrskin: NOTE : Please inform libburn-hackers@pykix.org about:\n");
+ fprintf(stderr,
+ "cdrskin: burn_drive_convert_fs_adr() returned %d\n",lret);
+ }
+ }
+
+#endif /* Cdrskin_libburn_has_convert_fs_adR */
+
+ }
+
+ /* A60927 : note to myself : no "ret= 1;" here. It breaks --help , -version */
+
+ex:;
+
+#ifndef Cdrskin_extra_leaN
+ if(ret<=0 || !(flag&1))
+ Cdradrtrn_destroy(&(o->adr_trn),0);
+#endif
+
+ return(ret);
+}
+
+
+/* --------------------------------------------------------------------- */
+
+
+
+/** The maximum number of tracks */
+#define Cdrskin_track_maX 99
+
+
+/** Work around the fact that libburn leaves the track input fds open
+ after the track is done. This can hide a few overflow bytes buffered
+ by the fifo-to-libburn pipe which would cause a broken-pipe error
+ if libburn would close that outlet.
+ This macro enables a coarse workaround which tries to read bytes from
+ the track inlets after burning has ended. Probably not a good idea if
+ libburn would close the inlet fds.
+*/
+#define Cdrskin_libburn_leaves_inlet_opeN 1
+
+
+/** List of furter wishes towards libburn:
+ - write mode which does not demand a track size in advance
+ - obtain minimum drive speed (for cdrskin -atip)
+ - obtain MMC profile of inserted media (for cdrskin -v -atip)
+ - a possibility to implement cdrskin -multi
+ - a possibilty to implement cdrskin -reset
+*/
+
+
+/** Limit to prevent int rollovers within libburn as long as not everything is
+ changed to 64 bit off_t : 2 GB minus 800 MB for eventual computations. */
+#define Cdrskin_tracksize_maX 1308622848
+
+
+/* Some constants obtained by hearsay and experiments */
+
+/** The 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 no_blank_appendable;
+
+ 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;
+
+ /** track_type may be set to BURN_MODE1, BURN_AUDIO, etc. */
+ int track_type;
+ int track_type_by_default; /* 0= explicit, 1=not set, 2=by file extension */
+ int swap_audio_bytes;
+
+ /** 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_grab_drive(struct CdrskiN *skin, 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= preskin->verbosity;
+ 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->no_blank_appendable= 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;
+ o->track_type= BURN_MODE1;
+ o->swap_audio_bytes= 1; /* cdrecord default is big-endian (msb_first) */
+ o->track_type_by_default= 1;
+ 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 *set_by_padsize, int *track_type,
+ int *track_type_by_default, int *swap_audio_bytes,
+ int flag)
+{
+ strcpy(source_path,skin->source_path);
+ *fixed_size= skin->fixed_size;
+ *padding= skin->padding;
+ *set_by_padsize= skin->set_by_padsize;
+ *track_type= skin->track_type;
+ *track_type_by_default= skin->track_type_by_default;
+ *swap_audio_bytes= skin->swap_audio_bytes;
+ 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);
+}
+
+
+/** Shutdown library and restart again on single drive which gets grabbed.
+ Does only work with a valid skin->driveno or with an already set
+ skin->preskin->device_adr .
+ @param flag Bitfield for control purposes:
+ bit0= skin->driveno points to a valid drive. The library
+ will get reopened with that drive listed as only one
+ and already grabbed.
+ bit1= do not load drive tray
+ @return 1 = success ,
+ 0 = failure, drive is released, library initialized
+ -1 = failure, library is finished (and could not get initialized)
+*/
+int Cdrskin_reinit_lib_with_adr(struct CdrskiN *skin, int flag)
+{
+ int ret;
+
+ if(skin->verbosity>=Cdrskin_verbose_debuG)
+ ClN(fprintf(stderr,
+ "cdrskin_debug: Restarting libburn. flag= %d driveno= %d grabbed= %d \n",
+ flag,skin->driveno,skin->drive_is_grabbed));
+
+ if(skin->drive_is_grabbed)
+ Cdrskin_release_drive(skin,0);
+ if(flag&1)
+ burn_drive_get_adr(&(skin->drives[skin->driveno]),
+ skin->preskin->device_adr);
+ if(strlen(skin->preskin->device_adr)<=0) {
+ fprintf(stderr,
+ "cdrskin: FATAL : unable to determine persistent drive address\n");
+ ret= 0; goto ex;
+ }
+
+/* >>> A60827: this causes a SIGSEGV when releasing the re-initialized drive
+ skin->drives[skin->driveno].drive after burn (then as
+ skin->grabbed_drive, but already being 0xb0 in Cdrskin_grab) )
+
+*/
+ burn_drive_info_free(skin->drives);
+
+ if(skin->verbosity>=Cdrskin_verbose_debuG)
+ ClN(fprintf(stderr,"cdrskin_debug: Finishing libburn.\n"));
+
+ burn_finish();
+
+ if(skin->verbosity>=Cdrskin_verbose_debuG)
+ ClN(fprintf(stderr,"cdrskin_debug: Initializing libburn.\n"));
+
+ if(!burn_initialize()) {
+ fflush(stdout);
+ fprintf(stderr,"cdrskin : FATAL : Re-initialization of libburn failed\n");
+ {ret= -1; goto ex;}
+ }
+
+ if(skin->verbosity>=Cdrskin_verbose_debuG)
+ ClN(fprintf(stderr,"cdrskin_debug: Grabbing drive.\n"));
+
+ ret= Cdrskin_grab_drive(skin,1|(flag&2));/* uses burn_drive_scan_and_grab() */
+ if(ret<=0)
+ {ret=0; goto ex;}
+
+ ret= 1;
+ex:
+
+ if(skin->verbosity>=Cdrskin_verbose_debuG)
+ ClN(fprintf(stderr,"cdrskin_debug: Restarting of libburn done. ret= %d\n",
+ ret));
+
+ return(ret);
+}
+
+
+/** Obtain access to a libburn drive for writing or information retrieval.
+ If libburn is not restricted to a single persistent address then the
+ unused drives are dropped. This might be done by shutting down and
+ restartiing libburn with the wanted drive only. Thus, after this call,
+ libburn is supposed to have open only the reserved drive.
+ All other drives should be free for other use.
+ Warning: Do not store struct burn_drive pointer over this call.
+ Any such pointer might be invalid afterwards.
+ @param flag Bitfield for control purposes:
+ bit0= bus is unscanned, device is known,
+ use burn_drive_scan_and_grab()
+ bit1= do not load drive tray
+ bit2= do not issue error message on failure
+ @return <=0 error, 1 success
+*/
+int Cdrskin_grab_drive(struct CdrskiN *skin, int flag)
+{
+ int ret,i;
+ struct burn_drive *drive;
+#ifdef Cdrskin_grab_abort_brokeN
+ int restore_handler= 0;
+#endif
+
+ i= 0;/* as long as its use is conditional, so gcc -Wall does not complain */
+
+ if(skin->drive_is_grabbed)
+ Cdrskin_release_drive(skin,0);
+
+ if(flag&1) {
+ skin->driveno= 0;
+ drive= NULL;
+ skin->grabbed_drive= drive;
+ } else {
+ 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;
+ }
+
+#else
+
+ if(skin->verbosity>=Cdrskin_verbose_debuG)
+ ClN(fprintf(stderr,
+ "cdrskin_debug: Trusting in abortability of grabbing process\n"));
+
+#endif /* ! Cdrskin_grab_abort_brokeN */
+
+#ifndef Cdrskin_oldfashioned_api_usE
+
+
+ if(flag&1) {
+ if(skin->verbosity>=Cdrskin_verbose_debuG)
+ ClN(fprintf(stderr,
+ "cdrskin_debug: Cdrskin_grab_drive() from shutdown libburn\n"));
+
+ ret= burn_drive_scan_and_grab(&(skin->drives),skin->preskin->device_adr,
+ !(flag&2));
+ if(ret<=0) {
+ if(skin->verbosity>=Cdrskin_verbose_debuG)
+ ClN(fprintf(stderr,
+ "cdrskin_debug: burn_drive_scan_and_grab ret=%d\n",ret));
+ if(!(flag&4))
+ fprintf(stderr,"cdrskin: FATAL : unable to open drive '%s'\n",
+ skin->preskin->device_adr);
+ goto ex;
+ }
+ skin->driveno= 0;
+ drive= skin->drives[skin->driveno].drive;
+ skin->grabbed_drive= drive;
+ } else {
+ if(skin->verbosity>=Cdrskin_verbose_debuG)
+ ClN(fprintf(stderr,
+ "cdrskin_debug: Cdrskin_grab_drive() on active libburn\n"));
+ if(strlen(skin->preskin->device_adr)<=0) {
+
+#define Cdrskin_drop_drives_by_forgeT 1
+#ifdef Cdrskin_drop_drives_by_forgeT
+
+ if(skin->verbosity>=Cdrskin_verbose_debuG)
+ ClN(fprintf(stderr,
+ "cdrskin_debug: Cdrskin_grab_drive() dropping unwanted drives (%d)\n",
+ skin->n_drives-1));
+ for(i=0;in_drives;i++) {
+ if(i==skin->driveno)
+ continue;
+ if(skin->verbosity>=Cdrskin_verbose_debuG)
+ ClN(fprintf(stderr,
+ "cdrskin_debug: Cdrskin_grab_drive() dropped drive number %d\n",i));
+ ret= burn_drive_info_forget(&(skin->drives[i]), 0);
+ if(ret==1 || ret==2)
+ continue;
+ fprintf(stderr,
+ "cdrskin: NOTE : Please inform libburn-hackers@pykix.org about:\n");
+ fprintf(stderr,
+ "cdrskin: burn_drive_info_forget() returns %d\n",ret);
+ }
+
+#else
+
+ ret= Cdrskin_reinit_lib_with_adr(skin,1|(flag&2));
+ goto ex; /* this calls Cdrskin_grab() with persistent address or fails */
+
+#endif /* ! Cdrskin_drop_drives_by_forgeT */
+
+ }
+
+#else
+
+ {
+
+#endif /* Cdrskin_oldfashioned_api_usE */
+
+ ret= burn_drive_grab(drive,!(flag&2));
+ if(ret==0) {
+ if(!(flag&4))
+ fprintf(stderr,"cdrskin: FATAL : unable to open drive %d\n",
+ skin->driveno);
+ goto ex;
+ }
+
+#ifdef Cdrskin_is_erasable_on_load_is_brokeN
+ /* RIP-14.5 + LITE-ON 48125S produce a false status if tray was unloaded */
+ /* Therefore the first grab was just for loading */
+ skin->drive_is_grabbed= 1; /* message to eventual abort handler */
+ burn_drive_release(drive,0);
+ skin->drive_is_grabbed= 0;
+
+ /* now grab the drive for real */
+ ret= burn_drive_grab(drive,!(flag&2));
+ if(ret==0) {
+ if(!(flag&4))
+ fprintf(stderr,"cdrskin: FATAL : unable to open drive %d\n",
+ skin->driveno);
+ goto ex;
+ }
+#else
+ if(skin->verbosity>=Cdrskin_verbose_debuG)
+ ClN(fprintf(stderr,
+ "cdrskin_debug: Trusting in burn_disc_erasable() after first grab\n"));
+#endif /* ! Cdrskin_is_erasable_on_load_is_brokeN */
+
+ }
+ 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
+ @param flag Bitfield for control purposes:
+ bit0= eject
+*/
+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,(flag&1));
+ 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)
+{
+
+#ifdef Cdrskin_libburn_has_burn_aborT
+
+ int ret;
+
+#else
+
+ int wait_grain= 100000,first_status= 1;
+ double start_time,last_time,current_time;
+
+#endif /* ! Cdrskin_libburn_has_burn_aborT */
+
+ struct burn_progress p;
+ enum burn_drive_status drive_status= BURN_DRIVE_GRABBING;
+
+ if(getpid()!=skin->control_pid) {
+ if(skin->verbosity>=Cdrskin_verbose_debuG)
+ ClN(fprintf(stderr,
+ "\ncdrskin_debug: ABORT : [%d] Thread rejected: pid=%d, signum=%d\n",
+ skin->control_pid,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
+
+#ifdef Cdrskin_libburn_has_burn_aborT
+
+ /* Only for user info */
+ if(skin->grabbed_drive!=NULL)
+ drive_status= burn_drive_get_status(skin->grabbed_drive,&p);
+ if(drive_status!=BURN_DRIVE_IDLE) {
+ fprintf(stderr,"cdrskin: ABORT : Abort processing depends on CD speed and buffer size\n");
+ fprintf(stderr,"cdrskin: ABORT : Usually it is done with 4x speed after about a MINUTE\n");
+ fprintf(stderr,"cdrskin: URGE : But wait at least the normal burning time before any kill -9\n");
+ }
+
+ ret= burn_abort(skin->abort_max_wait, burn_abort_pacifier, "cdrskin: ");
+ if(ret<=0) {
+ fprintf(stderr,
+ "\ncdrskin: ABORT : Cannot cancel burn session and release drive.\n");
+ return(0);
+ }
+ fprintf(stderr,"\n");
+
+#else /* Cdrskin_libburn_has_burn_aborT */
+
+ 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) {
+
+#ifndef Cdrskin_oldfashioned_api_usE
+ int ret;
+
+ fprintf(stderr,
+ "cdrskin: ABORT : Trying to close drive in process of grabbing\n");
+
+ /* >>> ??? rather inquire driveno from
+ skin->grabbed_drive->global_index ? */;
+
+ ret= burn_drive_info_forget(&(skin->drives[skin->driveno]),1);
+ if(ret<=0)
+ fprintf(stderr,
+ "cdrskin: ABORT : Attempt to close drive failed (ret= %d)\n",ret);
+ else {
+ skin->drive_is_grabbed= 0;
+ skin->grabbed_drive= NULL;
+ goto try_to_finish_lib;
+ }
+
+#else
+ /* >>> what to do in this state ? */;
+#endif /* Cdrskin_oldfashioned_api_usE */
+
+ } else if(drive_status!=BURN_DRIVE_IDLE) {
+ fprintf(stderr,
+ "cdrskin: ABORT : Will wait for current operation to end\n");
+ }
+ if(drive_status!=BURN_DRIVE_IDLE) {
+ fprintf(stderr,"cdrskin: ABORT : Abort processing depends on CD speed and buffer size\n");
+ fprintf(stderr,"cdrskin: ABORT : Usually it is done with 4x speed after about a MINUTE\n");
+ fprintf(stderr,"cdrskin: URGE : But wait at least the normal burning time before any kill -9\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);
+ }
+
+#ifndef Cdrskin_oldfashioned_api_usE
+try_to_finish_lib:;
+#endif
+
+ if(skin->lib_is_initialized) {
+ fprintf(stderr,"cdrskin: ABORT : Trying to finish libburn.\n");
+ burn_finish();
+ }
+
+#endif /* ! Cdrskin_libburn_has_burn_aborT */
+
+ 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,ret;
+ char adr[Cdrskin_adrleN];
+
+ for(i=0;in_drives;i++) {
+
+#ifdef Cdrskin_libburn_has_drive_get_adR
+ ret= burn_drive_get_adr(&(skin->drives[i]), adr);
+ if(ret<=0)
+ continue;
+#else
+ ret= 1; /* to please gcc -Wall */
+ strcpy(adr,skin->drives[i].location);
+#endif
+
+ if(strcmp(adr,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,
+ !!skin->preskin->old_pseudo_scsi_adr);
+ 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,
+ pseudo transport groups:
+ 0 volatile drive number,
+ 1 /dev/sgN, 2 /dev/hdX,
+ 1000000+busno = non-pseudo SCSI bus
+ 2000000+busno = pseudo-ATA|ATAPI SCSI bus (currently busno==2)
+*/
+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],adr[Cdrskin_adrleN];
+
+ if(driveno<0 || driveno>skin->n_drives)
+ goto fallback;
+
+#ifdef Cdrskin_libburn_has_drive_get_adR
+ ret= burn_drive_get_adr(&(skin->drives[driveno]), adr);
+ if(ret<=0)
+ goto fallback;
+ loc= adr;
+#else
+ adr[0]= 0; /* to please gcc -Wall */
+ loc= skin->drives[driveno].location;
+ if(loc==NULL)
+ goto fallback;
+#endif
+
+#ifdef Cdrskin_libburn_has_convert_scsi_adR
+ if(!skin->preskin->old_pseudo_scsi_adr) {
+ int host_no= -1,channel_no= -1,target_no= -1,lun_no= -1, bus_no= -1;
+
+ ret= burn_drive_obtain_scsi_adr(loc,&bus_no,&host_no,&channel_no,
+ &target_no,&lun_no);
+ if(ret<=0) {
+ if(strncmp(loc,"/dev/hd",7)==0)
+ if(loc[7]>='a' && loc[7]<='z')
+ if(loc[8]==0) {
+ bus_no= (loc[7]-'a')/2;
+ sprintf(btldev,"%d,%d,0",bus_no,(loc[7]-'a')%2);
+ {ret= 2000000 + bus_no; goto adr_translation;}
+ }
+ goto fallback;
+ } else {
+ sprintf(btldev,"%d,%d,%d",bus_no,target_no,lun_no);
+ ret= 1000000+bus_no;
+ goto adr_translation;
+ }
+ }
+#endif
+
+ 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 adr_translation;}
+ }
+ }
+ 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 adr_translation;}
+ }
+fallback:;
+ if(skin->preskin->old_pseudo_scsi_adr) {
+ sprintf(btldev,"0,%d,0",driveno);
+ } else {
+ if(loc!=NULL)
+ strcpy(btldev,loc);
+ else
+ sprintf(btldev,"%d",driveno);
+ }
+ ret= 0;
+
+adr_translation:;
+#ifndef Cdrskin_extra_leaN
+ /* user defined address translation */
+ if(!(flag&1)) {
+ if(ret>0) {
+ /* 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 if(s==BURN_DISC_UNREADY) {
+ printf("BURN_DISC_UNREADY \"The current status is not yet known\"\n");
+
+#ifdef Cdrskin_libburn_has_burn_disc_unsuitablE
+
+ } else if(s==BURN_DISC_UNGRABBED) {
+ printf("BURN_DISC_UNGRABBED \"API usage error: drive not grabbed\"\n");
+ } else if(s==BURN_DISC_UNSUITABLE) {
+ printf("BURN_DISC_UNSUITABLE \"Media is not suitable\"\n");
+
+#endif /* Cdrskin_libburn_has_burn_disc_unsuitablE */
+
+ } 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,pseudo_transport_group= 0,skipped_devices= 0;
+ int busmax= 16;
+ char shellsafe[5*Cdrskin_strleN+2],perms[40],btldev[Cdrskin_adrleN];
+ char adr[Cdrskin_adrleN],*raw_dev,*drives_shown= NULL;
+ struct stat stbuf;
+
+ drives_shown= malloc(skin->n_drives+1);
+ if(drives_shown==NULL)
+ {ret= -1; goto ex;}
+ for(i=0;in_drives;i++)
+ drives_shown[i]= 0;
+ if(flag&1) {
+ printf("cdrskin: Overview of accessible drives (%d found) :\n",
+ skin->n_drives);
+ printf("-----------------------------------------------------------------------------\n");
+ for(i=0;in_drives;i++) {
+
+#ifdef Cdrskin_libburn_has_drive_get_adR
+ ret= burn_drive_get_adr(&(skin->drives[i]), adr);
+ if(ret<=0) {
+ /* >>> one should massively complain */;
+ continue;
+ }
+#else
+ strcpy(adr,skin->drives[i].location);
+#endif
+
+ if(stat(adr,&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(adr)>=Cdrskin_strleN)
+ Text_shellsafe("failure:oversized string",shellsafe,0);
+ else
+ Text_shellsafe(adr,shellsafe,0);
+ printf("%d dev=%s %s : '%s' '%s'\n",
+ i,shellsafe,perms,skin->drives[i].vendor,skin->drives[i].product);
+ }
+ printf("-----------------------------------------------------------------------------\n");
+ } else {
+ if(!skin->preskin->old_pseudo_scsi_adr) {
+ pseudo_transport_group= 1000000;
+ raw_dev= skin->preskin->raw_device_adr;
+ if(strncmp(raw_dev,"ATA",3)==0 && (raw_dev[3]==0 || raw_dev[3]==':'))
+ pseudo_transport_group= 2000000;
+ if(strncmp(raw_dev,"ATAPI",5)==0 && (raw_dev[5]==0 || raw_dev[5]==':'))
+ pseudo_transport_group= 2000000;
+ if(pseudo_transport_group==2000000) {
+ fprintf(stderr,"scsidev: 'ATA'\ndevname: 'ATA'\n");
+ fprintf(stderr,"scsibus: -2 target: -2 lun: -2\n");
+ }
+ }
+ /* >>> fprintf(stderr,"Linux sg driver version: 3.1.25\n"); */
+ printf("Using libburn version '%s'.\n", Cdrskin_libburn_versioN);
+ if(pseudo_transport_group!=1000000)
+ if(skin->preskin->old_pseudo_scsi_adr)
+ printf("cdrskin: NOTE : The printed addresses are not cdrecord compatible !\n");
+
+ for(busno= 0;busno<=busmax;busno++) {
+ first_on_bus= 1;
+ for(i=0;in_drives;i++) {
+ ret= Cdrskin_driveno_to_btldev(skin,i,btldev,1);
+ if(busno==busmax && drives_shown[i]==0) {
+ if(ret/1000000 != pseudo_transport_group) {
+ skipped_devices++;
+ if(skin->verbosity>=Cdrskin_verbose_debuG)
+ ClN(fprintf(stderr,"cdrskin_debug: skipping drive '%s%s'\n",
+ ((ret/1000000)==2?"ATA:":""), btldev));
+ continue;
+ }
+ } else if(ret != pseudo_transport_group + busno)
+ continue;
+ if(first_on_bus)
+ printf("scsibus%d:\n",busno);
+ first_on_bus= 0;
+ printf("\t%s\t %d) '%-8s' '%-16s' '%-4s' Removable CD-ROM\n",
+ btldev,i,skin->drives[i].vendor,skin->drives[i].product,
+ skin->drives[i].revision);
+ drives_shown[i]= 1;
+ }
+ }
+ }
+ if(skipped_devices>0) {
+ if(skipped_devices>1)
+ printf("cdrskin: NOTE : There were %d drives not shown.\n",
+ skipped_devices);
+ else
+ printf("cdrskin: NOTE : There was 1 drive not shown.\n");
+ printf("cdrskin: HINT : To surely see all drives try option: --devices\n");
+ if(pseudo_transport_group!=2000000)
+ printf("cdrskin: HINT : or try options: dev=ATA -scanbus\n");
+ }
+ ret= 1;
+ex:;
+ if(drives_shown!=NULL)
+ free((char *) drives_shown);
+ return(ret);
+}
+
+
+/** 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("Revision : '%s'\n",drive_info->revision);
+ printf("Driver flags : %s\n","BURNFREE");
+ printf("Supported modes: %s\n","SAO RAW/R96R");
+ ret= 1;
+ex:;
+ return(ret);
+}
+
+
+/** Perform -toc under control of Cdrskin_atip().
+ @return <=0 error, 1 success
+*/
+int Cdrskin_toc(struct CdrskiN *skin, int flag)
+{
+ int num_sessions= 0,num_tracks= 0,lba;
+ int session_no, track_no;
+ struct burn_drive *drive;
+ struct burn_disc *disc= NULL;
+ struct burn_session **sessions;
+ struct burn_track **tracks;
+ struct burn_toc_entry toc_entry;
+
+ drive= skin->drives[skin->driveno].drive;
+
+ disc= burn_drive_get_disc(drive);
+ if(disc==NULL)
+ goto cannot_read;
+ sessions= burn_disc_get_sessions(disc,&num_sessions);
+ if(disc==NULL)
+ goto cannot_read;
+ for(session_no= 0; session_no>> From where does cdrecord take "mode" ? */
+
+ /* This is not the "mode" as printed by cdrecord :
+ printf(" mode: %d\n",burn_track_get_mode(tracks[track_no]));
+ */
+ /* own guess: cdrecord says "1" on data and "0" on audio : */
+ printf(" mode: %d\n",((toc_entry.control&7)<4?0:1));
+
+ }
+ burn_session_get_leadout_entry(sessions[session_no],&toc_entry);
+ lba= burn_msf_to_lba(toc_entry.pmin,toc_entry.psec,toc_entry.pframe);
+ printf("track:lout lba: %9d (%9d) %2.2u:%2.2u:%2.2u",
+ lba,4*lba,toc_entry.pmin,toc_entry.psec,toc_entry.pframe);
+ printf(" adr: %d control: %d",toc_entry.adr,toc_entry.control);
+ printf(" mode: -1\n");
+ }
+
+ if(disc!=NULL)
+ burn_disc_free(disc);
+ return(1);
+cannot_read:;
+ fprintf(stderr,"cdrecord_emulation: Cannot read TOC header\n");
+ fprintf(stderr,"cdrecord_emulation: Cannot read TOC/PMA\n");
+ return(0);
+}
+
+
+/** Perform -atip .
+ @param flag Bitfield for control purposes:
+ bit0= perform -toc
+ @return <=0 error, 1 success
+*/
+int Cdrskin_atip(struct CdrskiN *skin, int flag)
+{
+ int ret,is_not_really_erasable= 0;
+ double x_speed_max, x_speed_min= -1.0;
+ 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);
+ ret= Cdrskin_grab_drive(skin,0);
+ if(ret<=0)
+ return(ret);
+ drive= skin->drives[skin->driveno].drive;
+ while(burn_drive_get_status(drive,NULL) != BURN_DRIVE_IDLE)
+ usleep(100002);
+ while ((s= burn_disc_get_status(drive)) == BURN_DISC_UNREADY)
+ usleep(100002);
+ Cdrskin_report_disc_status(skin,s,0);
+ if(s==BURN_DISC_APPENDABLE && skin->no_blank_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"));
+
+
+#ifndef Cdrskin_oldfashioned_api_usE
+
+ if(strlen(skin->preskin->device_adr)<=0)
+ burn_drive_get_adr(&(skin->drives[skin->driveno]),
+ skin->preskin->device_adr);
+
+ 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;}
+ }
+ ret= Cdrskin_grab_drive(skin,1); /* uses burn_drive_scan_and_grab() */
+ if(ret<=0)
+ return(ret);
+ drive= skin->drives[skin->driveno].drive;
+
+#else /* ! Cdrskin_oldfashioned_api_usE */
+
+ 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)))
+ usleep(1002);
+ ret= Cdrskin_grab_drive(skin,0);
+ if(ret<=0)
+ return(ret);
+ drive= skin->drives[skin->driveno].drive;
+
+#endif /* Cdrskin_oldfashioned_api_usE */
+
+#endif /* Cdrskin_atip_speed_brokeN */
+
+#ifdef Cdrskin_libburn_has_read_atiP
+ ret= burn_disc_read_atip(drive);
+ if(ret>0) {
+ ret= burn_drive_get_min_write_speed(drive);
+ x_speed_min= ((double) ret)/Cdrskin_libburn_cd_speed_factoR;
+ }
+#endif
+
+#ifdef Cdrskin_libburn_has_burn_disc_unsuitablE
+ if(burn_disc_get_status(drive) == BURN_DISC_UNSUITABLE) {
+ printf("Current: UNSUITABLE MEDIA\n");
+ {ret= 0; goto ex;}
+ }
+#endif
+
+ ret= burn_drive_get_write_speed(drive);
+ x_speed_max= ((double) ret)/Cdrskin_libburn_cd_speed_factoR;
+ if(x_speed_min<0)
+ x_speed_min= x_speed_max;
+ printf("cdrskin: burn_drive_get_write_speed = %d (%.1fx)\n",ret,x_speed_max);
+ 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");
+ }
+
+#ifdef Cdrskin_libburn_has_get_start_end_lbA
+ { int start_lba,end_lba,min,sec,fr;
+ ret= burn_drive_get_start_end_lba(drive,&start_lba,&end_lba,0);
+ if(ret>0) {
+ burn_lba_to_msf(start_lba,&min,&sec,&fr);
+ printf(" ATIP start of lead in: %d (%-2.2d:%-2.2d/%-2.2d)\n",
+ start_lba,min,sec,fr);
+ burn_lba_to_msf(end_lba,&min,&sec,&fr);
+ printf(" ATIP start of lead out: %d (%-2.2d:%-2.2d/%-2.2d)\n",
+ end_lba,min,sec,fr);
+ }
+ }
+#endif /* Cdrskin_libburn_has_get_start_end_lbA */
+
+ printf(" 1T speed low: %.f 1T speed high: %.f\n",x_speed_min,x_speed_max);
+ ret= 1;
+ if(flag&1)
+ ret= Cdrskin_toc(skin,0);
+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 */
+ ret= Cdrskin_grab_drive(skin,0);
+ if(ret<=0)
+ return(ret);
+ drive= skin->drives[skin->driveno].drive;
+
+ while(burn_drive_get_status(drive,NULL) != BURN_DRIVE_IDLE)
+ usleep(100002);
+ while ((s = burn_disc_get_status(drive)) == BURN_DISC_UNREADY)
+ usleep(100002);
+ if(skin->verbosity>=Cdrskin_verbose_progresS)
+ Cdrskin_report_disc_status(skin,s,0);
+ if(s!=BURN_DISC_FULL &&
+ (s!=BURN_DISC_APPENDABLE || skin->no_blank_appendable)) {
+ 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)
+ fprintf(stderr,
+ "\rcdrskin: blanking sector %d (%lu seconds elapsed) ",
+ p.sector,(unsigned long) (Sfile_microtime(0)-start_time));
+ 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 *min_buffer_fill, int flag)
+/*
+ bit0= growisofs style
+*/
+{
+ double bytes_to_write,written_bytes= 0.0,written_total_bytes= 0.0,buffer_size;
+ double fixed_size,padding,sector_size,speed_factor;
+ 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,buffer_fill;
+ char fifo_text[80],mb_text[40];
+ char *debug_mark= ""; /* use this to prepend a marker text for experiments */
+
+ /* 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;
+
+ old_track_idx= skin->supposed_track_idx;
+#ifdef Cdrskin_progress_track_brokeN
+ /* with libburn.0.2 there is always reported 0 as p->track */
+ 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,
+ §or_size,0);
+ if(skin->verbosity>=Cdrskin_verbose_progresS)
+ printf("\n");
+ printf("%sTrack %-2.2d: Total bytes read/written: %.f/%.f (%.f sectors).\n",
+ debug_mark,old_track_idx+1,fixed_size,fixed_size,
+ fixed_size/sector_size);
+ }
+
+ sector_size= 2048.0;
+ if(skin->supposed_track_idx>=0 &&
+ skin->supposed_track_idxtrack_counter)
+ Cdrtrack_get_size(skin->tracklist[skin->supposed_track_idx],&fixed_size,
+ &padding,§or_size,0);
+
+ bytes_to_write= ((double) p->sectors)*sector_size;
+ written_total_bytes= ((double) p->sector)*sector_size;
+ written_bytes= written_total_bytes-*last_count;
+
+ 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) {
+ /* fixed_size,padding are fetched above via Cdrtrack_get_size() */;
+ } 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));
+ speed_factor= Cdrskin_cd_speed_factoR*sector_size/2048;
+
+ buffer_fill= 50;
+#ifdef Cdrskin_libburn_has_buffer_progresS
+ if(p->buffer_capacity>0)
+ buffer_fill= (double) (p->buffer_capacity - p->buffer_available)*100.0
+ / (double) p->buffer_capacity;
+
+#endif /* Cdrskin_libburn_has_buffer_progresS */
+ if(buffer_fill<*min_buffer_fill)
+ *min_buffer_fill= buffer_fill;
+
+ printf("\r%sTrack %-2.2d: %s MB written %s[buf %3d%%] %4.1fx.",
+ debug_mark,skin->supposed_track_idx+1,mb_text,fifo_text,
+ buffer_fill,measured_speed/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,min_buffer_fill= 101;
+ 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,sector_size= 2048.0;
+
+ printf("cdrskin: beginning to burn disk\n");
+
+ disc= burn_disc_create();
+ session= burn_session_create();
+ ret= burn_disc_add_session(disc,session,BURN_POS_END);
+ if(ret==0) {
+ fprintf(stderr,"cdrskin: FATAL : cannot add session to disc object.\n");
+ return(0);
+ }
+
+ skin->fixed_size= 0.0;
+ for(i=0;itrack_counter;i++) {
+ hflag= (skin->verbosity>=Cdrskin_verbose_debuG);
+ if(i==skin->track_counter-1)
+ Cdrtrack_ensure_padding(skin->tracklist[i],hflag&1);
+ 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,§or_size,0);
+ skin->fixed_size+= size+padding;
+ }
+
+ ret= Cdrskin_grab_drive(skin,0);
+ if(ret<=0)
+ return(ret);
+ drive= skin->drives[skin->driveno].drive;
+
+ while(burn_drive_get_status(drive, NULL) != BURN_DRIVE_IDLE)
+ usleep(100002); /* >>> ??? add a timeout ? */
+
+ while((s= burn_disc_get_status(drive)) == BURN_DISC_UNREADY)
+ usleep(100002); /* >>> ??? 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,§or_size,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/sector_size));
+ 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/sector_size));
+ }
+ }
+
+ 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,&min_buffer_fill,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/sector_size);
+ } else {
+ Cdrtrack_get_size(skin->tracklist[max_track],&size,&padding,§or_size,0);
+ printf(
+ "Track %-2.2d: Total bytes read/written: %.f/%.f (%.f sectors).\n",
+ max_track+1,size,size,size/sector_size);
+ }
+ 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);
+ }
+ if(min_buffer_fill>100)
+ min_buffer_fill= 50;
+ printf("Min drive buffer fill was %d%%\n", min_buffer_fill);
+ }
+
+#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)
+{
+
+#ifndef Cdrskin_burn_drive_eject_brokeN
+
+#ifndef Cdrskin_oldfashioned_api_usE
+ int i,ret,max_try= 5;
+
+ if(!skin->do_eject)
+ return(1);
+
+ /* A60923 :
+ Still not in libburn-0.2.2 : prevent SIGSEV on non-existent drive */
+ if(skin->n_drives<=skin->driveno || skin->driveno < 0)
+ return(2);
+
+ /* <<< A61012 : retry loop might now be obsolete
+ (a matching bug in burn_disc_write_sync() was removed ) */
+ for(i= 0;i0 || i>=max_try-1)
+ break;
+ if(skin->verbosity>=Cdrskin_verbose_progresS)
+ fprintf(stderr,
+ "cdrskin: NOTE : Attempt #%d of %d failed to grab drive for eject\n",
+ i+1,max_try);
+ usleep(1000000);
+ }
+ if(ret>0) {
+ ret= Cdrskin_release_drive(skin,1);
+ if(ret>0) {
+ if(skin->verbosity>=Cdrskin_verbose_debuG)
+ ClN(fprintf(stderr,
+ "cdrskin_debug: supposing drive eject to have worked\n"));
+ } else
+ goto sorry_failed_to_eject;
+ } else {
+sorry_failed_to_eject:;
+ fprintf(stderr,"cdrskin: SORRY : Failed to finally eject tray.\n");
+ return(0);
+ }
+ return(1);
+
+#else
+
+ if(!skin->do_eject)
+ return(1);
+ if(Cdrskin_grab_drive(skin,2)>0) {
+ Cdrskin_release_drive(skin,1);
+ if(skin->verbosity>=Cdrskin_verbose_debuG)
+ ClN(fprintf(stderr,
+ "cdrskin_debug: supposing drive eject to have worked\n"));
+ } else {
+ fprintf(stderr,"cdrskin: SORRY : Failed to finally eject tray.\n");
+ return(0);
+ }
+ return(1);
+
+#endif
+
+#else /* Cdrskin_burn_drive_eject_brokeN */
+
+ int ret;
+ char adr[Cdrskin_adrleN];
+ 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);
+ }
+
+#ifdef Cdrskin_libburn_has_drive_get_adR
+ ret= burn_drive_get_adr(&(skin->drives[skin->driveno]), adr);
+ if(ret<=0)
+ adr[0]= 0;
+#else
+ strcpy(adr,skin->drives[skin->driveno].location);
+#endif
+
+ if(strlen(skin->eject_device)>0)
+ sprintf(cmd,"eject %s",Text_shellsafe(skin->eject_device,shellsafe,0));
+ else if(strcmp(adr,"/dev/sg0")==0)
+ sprintf(cmd,"eject /dev/sr0");
+ else
+ sprintf(cmd,"eject %s",Text_shellsafe(adr,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,grab_and_wait_value= -1.0;
+ char *cpt,*value_pt,adr[Cdrskin_adrleN];
+
+ /* 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", "-multi", "-fix", "-nofix", "-waiti",
+ "-immed", "-force", "-raw", "-raw96p", "-raw16",
+ "-clone", "-text", "-mode2", "-xa", "-xa1", "-xa2", "-xamix",
+ "-cdi", "-isosize", "-preemp", "-nopreemp", "-copy", "-nocopy",
+ "-scms", "-shorttrack", "-noshorttrack", "-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= Scanf_io_size(argv[i]+22,0);
+ fprintf(stderr,
+ "cdrskin: NOTE : will perform synthetic abort after %.f bytes\n",
+ skin->abort_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) {
+ if(skin->do_atip<1)
+ skin->do_atip= 1;
+ if(skin->verbosity>=Cdrskin_verbose_cmD)
+ printf("cdrskin: will put out some -atip style lines\n");
+
+ } else if(strcmp(argv[i],"-audio")==0) {
+ skin->track_type= BURN_AUDIO;
+ skin->track_type_by_default= 0;
+
+ } 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],"--bragg_with_audio")==0) {
+ /* OBSOLETE 0.2.3 : was handled in Cdrpreskin_setup() */;
+
+ } else if(strcmp(argv[i],"-checkdrive")==0) {
+ skin->do_checkdrive= 1;
+
+ } else if(strcmp(argv[i],"-data")==0) {
+
+ /* >>> !!! All Subsequent Tracks Option */
+
+ skin->track_type= BURN_MODE1;
+ skin->track_type_by_default= 0;
+
+ } 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 || strcmp(value_pt,"burnproof")==0) {
+ skin->burnfree= 1;
+ if(skin->verbosity>=Cdrskin_verbose_cmD)
+ printf("cdrskin: burnfree : on\n");
+ } else if(strcmp(argv[i]+11,"noburnfree")==0 ||
+ strcmp(argv[i]+11,"noburnproof")==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],"grab_drive_and_wait=",20)==0) {
+ value_pt= argv[i]+20;
+ grab_and_wait_value= Scanf_io_size(value_pt,0);
+
+ } 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_blank_appendable")==0) {
+ skin->no_blank_appendable= 1;
+
+ } else if(strcmp(argv[i],"--no_convert_fs_adr")==0) {
+ /* is handled in Cdrpreskin_setup() */;
+
+ } else if(strcmp(argv[i],"--old_pseudo_scsi_adr")==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],"-swab")==0) {
+ skin->swap_audio_bytes= 0;
+
+ } 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(strcmp(argv[i],"-toc")==0) {
+ skin->do_atip= 2;
+ if(skin->verbosity>=Cdrskin_verbose_cmD)
+ printf("cdrskin: will put out some -atip style lines plus -toc\n");
+
+ } 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) {
+ /* is handled in Cdrpreskin_setup() */;
+
+ } 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->fixed_size<=0) {
+ fprintf(stderr,
+ "cdrskin: FATAL : \"-\" (stdin) needs a fixed tsize= or tao_to_sao_tsize=\n");
+ return(0);
+ }
+ } 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,
+ (strcmp(skin->source_path,"-")==0)<<1);
+ 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 ||
+ strlen(skin->preskin->device_adr)>0) {
+ if(strlen(skin->preskin->device_adr)>0)
+ cpt= skin->preskin->device_adr;
+ else
+ cpt= skin->preskin->raw_device_adr;
+ if(strcmp(cpt,"ATA")!=0 && strcmp(cpt,"ATAPI")!=0 && strcmp(cpt,"SCSI")!=0){
+ ret= Cdrskin_dev_to_driveno(skin,cpt,&(skin->driveno),0);
+ if(ret<=0)
+ return(ret);
+ if(skin->verbosity>=Cdrskin_verbose_cmD) {
+
+#ifdef Cdrskin_libburn_has_drive_get_adR
+ ret= burn_drive_get_adr(&(skin->drives[skin->driveno]), adr);
+ if(ret<=0)
+ adr[0]= 0;
+#else
+ strcpy(adr,skin->drives[skin->driveno].location);
+#endif
+
+ printf("cdrskin: active drive number : %d '%s'\n",
+ skin->driveno,adr);
+ }
+ }
+ }
+ if(grab_and_wait_value>0) {
+ Cdrskin_grab_drive(skin,0);
+ for(k= 0; ktrack_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 *exit_value, int flag)
+{
+ int ret;
+ struct CdrskiN *skin;
+
+ *o= NULL;
+ *exit_value= 0;
+
+#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 ...\n");
+ fflush(stdout);
+
+ /* In cdrskin there is not much sense in queueing library messages.
+ It is done here only for debugging */
+ Cdrpreskin_queue_msgs(skin->preskin,1);
+
+ while (!burn_drive_scan(&(skin->drives), &(skin->n_drives))) {
+ usleep(20000);
+ /* >>> ??? set a timeout ? */
+ }
+
+ /* This prints the eventual queued messages */
+ Cdrpreskin_queue_msgs(skin->preskin,0);
+
+ printf("cdrskin: ... scanning for devices 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,!!(skin->do_atip>1));
+ 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;}
+ }
+
+ /* <<< A60925: i would prefer to do this later, after it is clear that no
+ -version or -help cause idle end. But address conversion and its debug
+ messaging need libburn running */
+ ret= Cdrpreskin_initialize_lib(preskin,0);
+ if(ret<=0) {
+ fprintf(stderr,"cdrskin: FATAL : Initializiation of burn library failed\n");
+ {exit_value= 2; goto ex;}
+ }
+ lib_initialized= 1;
+
+ 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,&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 : Run this program as superuser with option --devices\n");
+ fprintf(stderr,
+ "cdrskin: HINT : Allow rw-access to the dev='...' file of the burner.\n");
+ fprintf(stderr,
+ "cdrskin: HINT : Busy drives are invisible. (Busy = open O_EXCL)\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]);
+
+ if(skin->verbosity>=Cdrskin_verbose_debuG) {
+#ifdef Cdrskin_oldfashioned_api_usE
+ ClN(fprintf(stderr,"cdrskin_debug: Compiled with option -oldfashioned\n"));
+#endif
+#ifdef Cdrskin_new_api_tesT
+ ClN(fprintf(stderr,"cdrskin_debug: Compiled with option -experimental\n"));
+#endif
+ }
+
+ 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/tags/CdrskinZeroTwoFour/cdrskin/cdrskin_eng.html b/tags/CdrskinZeroTwoFour/cdrskin/cdrskin_eng.html
new file mode 100644
index 00000000..0b7fe6cc
--- /dev/null
+++ b/tags/CdrskinZeroTwoFour/cdrskin/cdrskin_eng.html
@@ -0,0 +1,422 @@
+
+
+
+
+
+
+cdrskin homepage english
+
+
+
+
+
+
+Homepage of
cdrskin
+
+
+Limited cdrecord compatibility wrapper for libburn
+
+
+
+
Purpose:
+
+- Burns preformatted data to CD-R or CD-RW
+
+
+
+This program system has been tested on Intel/AMD Linux systems only.
+Ports to other usable systems are appreciated. Reports are welcome.
+
+
+
+
+
+Example how to setup K3b to use cdrskin for burning data CD projects.
+
+(K3b
+is a GUI frontend which uses cdrecord for CD burning.)
+
+
+
+
+