From 923c919f2965a0cfbb231d397b0c7863afdddee5 Mon Sep 17 00:00:00 2001
From: Mario Danic
Date: Wed, 20 Sep 2006 15:07:09 +0000
Subject: [PATCH] Tagged ZeroTwoTwo release
---
tags/ZeroTwoTwo/AUTHORS | 5 +
tags/ZeroTwoTwo/CONTRIBUTORS | 2 +
tags/ZeroTwoTwo/COPYING | 280 +
tags/ZeroTwoTwo/COPYRIGHT | 19 +
tags/ZeroTwoTwo/Makefile.am | 203 +
tags/ZeroTwoTwo/README | 198 +
tags/ZeroTwoTwo/acinclude.m4 | 37 +
tags/ZeroTwoTwo/bootstrap | 6 +
tags/ZeroTwoTwo/cdrskin/README | 377 ++
.../cdrskin/add_ts_changes_to_libburn_0_2_1 | 165 +
tags/ZeroTwoTwo/cdrskin/cdrecord_spy.sh | 37 +
tags/ZeroTwoTwo/cdrskin/cdrfifo.c | 1036 ++++
tags/ZeroTwoTwo/cdrskin/cdrfifo.h | 144 +
tags/ZeroTwoTwo/cdrskin/cdrskin.c | 4489 +++++++++++++++++
tags/ZeroTwoTwo/cdrskin/cdrskin_eng.html | 394 ++
tags/ZeroTwoTwo/cdrskin/cdrskin_timestamp.h | 1 +
tags/ZeroTwoTwo/cdrskin/changelog.txt | 518 ++
tags/ZeroTwoTwo/cdrskin/cleanup.c | 184 +
tags/ZeroTwoTwo/cdrskin/cleanup.h | 34 +
tags/ZeroTwoTwo/cdrskin/compile_cdrskin.sh | 145 +
tags/ZeroTwoTwo/cdrskin/make_timestamp.sh | 9 +
tags/ZeroTwoTwo/cdrskin/wiki_plain.txt | 212 +
tags/ZeroTwoTwo/configure.ac | 129 +
tags/ZeroTwoTwo/doc/Makefile | 4 +
tags/ZeroTwoTwo/doc/comments | 121 +
tags/ZeroTwoTwo/doc/comments_test_ts | 121 +
tags/ZeroTwoTwo/doc/doxygen.conf.in | 186 +
tags/ZeroTwoTwo/libburn-1.pc.in | 11 +
tags/ZeroTwoTwo/libburn/Makefile | 4 +
tags/ZeroTwoTwo/libburn/Makefile.am | 65 +
tags/ZeroTwoTwo/libburn/async.c | 191 +
tags/ZeroTwoTwo/libburn/async.h | 8 +
tags/ZeroTwoTwo/libburn/back_hacks.h | 54 +
tags/ZeroTwoTwo/libburn/crc.c | 122 +
tags/ZeroTwoTwo/libburn/crc.h | 9 +
tags/ZeroTwoTwo/libburn/debug.c | 35 +
tags/ZeroTwoTwo/libburn/debug.h | 8 +
tags/ZeroTwoTwo/libburn/drive.c | 665 +++
tags/ZeroTwoTwo/libburn/drive.h | 57 +
tags/ZeroTwoTwo/libburn/error.h | 8 +
tags/ZeroTwoTwo/libburn/file.c | 187 +
tags/ZeroTwoTwo/libburn/file.h | 22 +
tags/ZeroTwoTwo/libburn/init.c | 80 +
tags/ZeroTwoTwo/libburn/init.h | 8 +
tags/ZeroTwoTwo/libburn/lec.c | 451 ++
tags/ZeroTwoTwo/libburn/lec.h | 12 +
tags/ZeroTwoTwo/libburn/libburn.h | 1040 ++++
tags/ZeroTwoTwo/libburn/message.c | 108 +
tags/ZeroTwoTwo/libburn/message.h | 19 +
tags/ZeroTwoTwo/libburn/mmc.c | 544 ++
tags/ZeroTwoTwo/libburn/mmc.h | 37 +
tags/ZeroTwoTwo/libburn/null.c | 27 +
tags/ZeroTwoTwo/libburn/null.h | 10 +
tags/ZeroTwoTwo/libburn/options.c | 169 +
tags/ZeroTwoTwo/libburn/options.h | 78 +
tags/ZeroTwoTwo/libburn/read.c | 264 +
tags/ZeroTwoTwo/libburn/read.h | 14 +
tags/ZeroTwoTwo/libburn/sbc.c | 40 +
tags/ZeroTwoTwo/libburn/sbc.h | 11 +
tags/ZeroTwoTwo/libburn/sector.c | 649 +++
tags/ZeroTwoTwo/libburn/sector.h | 31 +
tags/ZeroTwoTwo/libburn/sg.c | 500 ++
tags/ZeroTwoTwo/libburn/sg.h | 19 +
tags/ZeroTwoTwo/libburn/source.c | 36 +
tags/ZeroTwoTwo/libburn/source.h | 8 +
tags/ZeroTwoTwo/libburn/spc.c | 405 ++
tags/ZeroTwoTwo/libburn/spc.h | 25 +
tags/ZeroTwoTwo/libburn/structure.c | 305 ++
tags/ZeroTwoTwo/libburn/structure.h | 70 +
tags/ZeroTwoTwo/libburn/toc.c | 130 +
tags/ZeroTwoTwo/libburn/toc.h | 48 +
tags/ZeroTwoTwo/libburn/transport.h | 171 +
tags/ZeroTwoTwo/libburn/util.c | 44 +
tags/ZeroTwoTwo/libburn/util.h | 8 +
tags/ZeroTwoTwo/libburn/write.c | 546 ++
tags/ZeroTwoTwo/libburn/write.h | 25 +
tags/ZeroTwoTwo/libisofs-1.pc.in | 11 +
tags/ZeroTwoTwo/libisofs/Makefile | 4 +
tags/ZeroTwoTwo/libisofs/Makefile.am | 49 +
tags/ZeroTwoTwo/libisofs/ecma119.c | 694 +++
tags/ZeroTwoTwo/libisofs/ecma119.h | 267 +
tags/ZeroTwoTwo/libisofs/ecma119_tree.c | 312 ++
tags/ZeroTwoTwo/libisofs/ecma119_tree.h | 95 +
tags/ZeroTwoTwo/libisofs/exclude.c | 42 +
tags/ZeroTwoTwo/libisofs/exclude.h | 12 +
tags/ZeroTwoTwo/libisofs/hash.c | 158 +
tags/ZeroTwoTwo/libisofs/hash.h | 46 +
tags/ZeroTwoTwo/libisofs/joliet.c | 379 ++
tags/ZeroTwoTwo/libisofs/joliet.h | 84 +
tags/ZeroTwoTwo/libisofs/libisofs.h | 225 +
tags/ZeroTwoTwo/libisofs/rockridge.c | 300 ++
tags/ZeroTwoTwo/libisofs/rockridge.h | 26 +
tags/ZeroTwoTwo/libisofs/susp.c | 280 +
tags/ZeroTwoTwo/libisofs/susp.h | 62 +
tags/ZeroTwoTwo/libisofs/tree.c | 223 +
tags/ZeroTwoTwo/libisofs/tree.h | 159 +
tags/ZeroTwoTwo/libisofs/util.c | 577 +++
tags/ZeroTwoTwo/libisofs/util.h | 121 +
tags/ZeroTwoTwo/libisofs/volume.c | 189 +
tags/ZeroTwoTwo/libisofs/volume.h | 45 +
tags/ZeroTwoTwo/test/Makefile | 4 +
tags/ZeroTwoTwo/test/iso.c | 107 +
tags/ZeroTwoTwo/test/iso.py | 297 ++
tags/ZeroTwoTwo/test/libburner.c | 680 +++
tags/ZeroTwoTwo/test/poll.c | 78 +
tags/ZeroTwoTwo/test/structest.c | 48 +
tags/ZeroTwoTwo/test/toc.c | 103 +
tags/ZeroTwoTwo/test/tree.py | 77 +
tags/ZeroTwoTwo/version.h.in | 3 +
109 files changed, 22191 insertions(+)
create mode 100644 tags/ZeroTwoTwo/AUTHORS
create mode 100644 tags/ZeroTwoTwo/CONTRIBUTORS
create mode 100644 tags/ZeroTwoTwo/COPYING
create mode 100644 tags/ZeroTwoTwo/COPYRIGHT
create mode 100644 tags/ZeroTwoTwo/Makefile.am
create mode 100644 tags/ZeroTwoTwo/README
create mode 100644 tags/ZeroTwoTwo/acinclude.m4
create mode 100755 tags/ZeroTwoTwo/bootstrap
create mode 100644 tags/ZeroTwoTwo/cdrskin/README
create mode 100755 tags/ZeroTwoTwo/cdrskin/add_ts_changes_to_libburn_0_2_1
create mode 100755 tags/ZeroTwoTwo/cdrskin/cdrecord_spy.sh
create mode 100644 tags/ZeroTwoTwo/cdrskin/cdrfifo.c
create mode 100644 tags/ZeroTwoTwo/cdrskin/cdrfifo.h
create mode 100644 tags/ZeroTwoTwo/cdrskin/cdrskin.c
create mode 100644 tags/ZeroTwoTwo/cdrskin/cdrskin_eng.html
create mode 100644 tags/ZeroTwoTwo/cdrskin/cdrskin_timestamp.h
create mode 100644 tags/ZeroTwoTwo/cdrskin/changelog.txt
create mode 100644 tags/ZeroTwoTwo/cdrskin/cleanup.c
create mode 100644 tags/ZeroTwoTwo/cdrskin/cleanup.h
create mode 100755 tags/ZeroTwoTwo/cdrskin/compile_cdrskin.sh
create mode 100755 tags/ZeroTwoTwo/cdrskin/make_timestamp.sh
create mode 100644 tags/ZeroTwoTwo/cdrskin/wiki_plain.txt
create mode 100644 tags/ZeroTwoTwo/configure.ac
create mode 100644 tags/ZeroTwoTwo/doc/Makefile
create mode 100644 tags/ZeroTwoTwo/doc/comments
create mode 100644 tags/ZeroTwoTwo/doc/comments_test_ts
create mode 100644 tags/ZeroTwoTwo/doc/doxygen.conf.in
create mode 100644 tags/ZeroTwoTwo/libburn-1.pc.in
create mode 100644 tags/ZeroTwoTwo/libburn/Makefile
create mode 100644 tags/ZeroTwoTwo/libburn/Makefile.am
create mode 100644 tags/ZeroTwoTwo/libburn/async.c
create mode 100644 tags/ZeroTwoTwo/libburn/async.h
create mode 100644 tags/ZeroTwoTwo/libburn/back_hacks.h
create mode 100644 tags/ZeroTwoTwo/libburn/crc.c
create mode 100644 tags/ZeroTwoTwo/libburn/crc.h
create mode 100644 tags/ZeroTwoTwo/libburn/debug.c
create mode 100644 tags/ZeroTwoTwo/libburn/debug.h
create mode 100644 tags/ZeroTwoTwo/libburn/drive.c
create mode 100644 tags/ZeroTwoTwo/libburn/drive.h
create mode 100644 tags/ZeroTwoTwo/libburn/error.h
create mode 100644 tags/ZeroTwoTwo/libburn/file.c
create mode 100644 tags/ZeroTwoTwo/libburn/file.h
create mode 100644 tags/ZeroTwoTwo/libburn/init.c
create mode 100644 tags/ZeroTwoTwo/libburn/init.h
create mode 100644 tags/ZeroTwoTwo/libburn/lec.c
create mode 100644 tags/ZeroTwoTwo/libburn/lec.h
create mode 100644 tags/ZeroTwoTwo/libburn/libburn.h
create mode 100644 tags/ZeroTwoTwo/libburn/message.c
create mode 100644 tags/ZeroTwoTwo/libburn/message.h
create mode 100644 tags/ZeroTwoTwo/libburn/mmc.c
create mode 100644 tags/ZeroTwoTwo/libburn/mmc.h
create mode 100644 tags/ZeroTwoTwo/libburn/null.c
create mode 100644 tags/ZeroTwoTwo/libburn/null.h
create mode 100644 tags/ZeroTwoTwo/libburn/options.c
create mode 100644 tags/ZeroTwoTwo/libburn/options.h
create mode 100644 tags/ZeroTwoTwo/libburn/read.c
create mode 100644 tags/ZeroTwoTwo/libburn/read.h
create mode 100644 tags/ZeroTwoTwo/libburn/sbc.c
create mode 100644 tags/ZeroTwoTwo/libburn/sbc.h
create mode 100644 tags/ZeroTwoTwo/libburn/sector.c
create mode 100644 tags/ZeroTwoTwo/libburn/sector.h
create mode 100644 tags/ZeroTwoTwo/libburn/sg.c
create mode 100644 tags/ZeroTwoTwo/libburn/sg.h
create mode 100644 tags/ZeroTwoTwo/libburn/source.c
create mode 100644 tags/ZeroTwoTwo/libburn/source.h
create mode 100644 tags/ZeroTwoTwo/libburn/spc.c
create mode 100644 tags/ZeroTwoTwo/libburn/spc.h
create mode 100644 tags/ZeroTwoTwo/libburn/structure.c
create mode 100644 tags/ZeroTwoTwo/libburn/structure.h
create mode 100644 tags/ZeroTwoTwo/libburn/toc.c
create mode 100644 tags/ZeroTwoTwo/libburn/toc.h
create mode 100644 tags/ZeroTwoTwo/libburn/transport.h
create mode 100644 tags/ZeroTwoTwo/libburn/util.c
create mode 100644 tags/ZeroTwoTwo/libburn/util.h
create mode 100644 tags/ZeroTwoTwo/libburn/write.c
create mode 100644 tags/ZeroTwoTwo/libburn/write.h
create mode 100644 tags/ZeroTwoTwo/libisofs-1.pc.in
create mode 100755 tags/ZeroTwoTwo/libisofs/Makefile
create mode 100755 tags/ZeroTwoTwo/libisofs/Makefile.am
create mode 100755 tags/ZeroTwoTwo/libisofs/ecma119.c
create mode 100755 tags/ZeroTwoTwo/libisofs/ecma119.h
create mode 100644 tags/ZeroTwoTwo/libisofs/ecma119_tree.c
create mode 100644 tags/ZeroTwoTwo/libisofs/ecma119_tree.h
create mode 100644 tags/ZeroTwoTwo/libisofs/exclude.c
create mode 100644 tags/ZeroTwoTwo/libisofs/exclude.h
create mode 100644 tags/ZeroTwoTwo/libisofs/hash.c
create mode 100644 tags/ZeroTwoTwo/libisofs/hash.h
create mode 100644 tags/ZeroTwoTwo/libisofs/joliet.c
create mode 100644 tags/ZeroTwoTwo/libisofs/joliet.h
create mode 100755 tags/ZeroTwoTwo/libisofs/libisofs.h
create mode 100755 tags/ZeroTwoTwo/libisofs/rockridge.c
create mode 100755 tags/ZeroTwoTwo/libisofs/rockridge.h
create mode 100755 tags/ZeroTwoTwo/libisofs/susp.c
create mode 100755 tags/ZeroTwoTwo/libisofs/susp.h
create mode 100755 tags/ZeroTwoTwo/libisofs/tree.c
create mode 100755 tags/ZeroTwoTwo/libisofs/tree.h
create mode 100755 tags/ZeroTwoTwo/libisofs/util.c
create mode 100755 tags/ZeroTwoTwo/libisofs/util.h
create mode 100755 tags/ZeroTwoTwo/libisofs/volume.c
create mode 100755 tags/ZeroTwoTwo/libisofs/volume.h
create mode 100644 tags/ZeroTwoTwo/test/Makefile
create mode 100644 tags/ZeroTwoTwo/test/iso.c
create mode 100644 tags/ZeroTwoTwo/test/iso.py
create mode 100644 tags/ZeroTwoTwo/test/libburner.c
create mode 100644 tags/ZeroTwoTwo/test/poll.c
create mode 100644 tags/ZeroTwoTwo/test/structest.c
create mode 100644 tags/ZeroTwoTwo/test/toc.c
create mode 100644 tags/ZeroTwoTwo/test/tree.py
create mode 100644 tags/ZeroTwoTwo/version.h.in
diff --git a/tags/ZeroTwoTwo/AUTHORS b/tags/ZeroTwoTwo/AUTHORS
new file mode 100644
index 00000000..59a18579
--- /dev/null
+++ b/tags/ZeroTwoTwo/AUTHORS
@@ -0,0 +1,5 @@
+Developers:
+
+Mario Danic
+Thomas Schmitt
+Lorenzo Taylor
diff --git a/tags/ZeroTwoTwo/CONTRIBUTORS b/tags/ZeroTwoTwo/CONTRIBUTORS
new file mode 100644
index 00000000..c774a038
--- /dev/null
+++ b/tags/ZeroTwoTwo/CONTRIBUTORS
@@ -0,0 +1,2 @@
+Joe Neeman
+Philippe Rouquier
diff --git a/tags/ZeroTwoTwo/COPYING b/tags/ZeroTwoTwo/COPYING
new file mode 100644
index 00000000..5a965fbc
--- /dev/null
+++ b/tags/ZeroTwoTwo/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/ZeroTwoTwo/COPYRIGHT b/tags/ZeroTwoTwo/COPYRIGHT
new file mode 100644
index 00000000..a6b57b3c
--- /dev/null
+++ b/tags/ZeroTwoTwo/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/ZeroTwoTwo/Makefile.am b/tags/ZeroTwoTwo/Makefile.am
new file mode 100644
index 00000000..ee2447e5
--- /dev/null
+++ b/tags/ZeroTwoTwo/Makefile.am
@@ -0,0 +1,203 @@
+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_SOURCES = \
+ libburn/async.c \
+ libburn/async.h \
+ libburn/back_hacks.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/message.c \
+ libburn/message.h \
+ 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
+
+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/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_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_2
+cdrskin_cdrskin_LDADD = $(libburn_libburn_la_OBJECTS) $(THREAD_LIBS)
+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 \
+ COPYING
+
diff --git a/tags/ZeroTwoTwo/README b/tags/ZeroTwoTwo/README
new file mode 100644
index 00000000..ebd0d0b9
--- /dev/null
+++ b/tags/ZeroTwoTwo/README
@@ -0,0 +1,198 @@
+------------------------------------------------------------------------------
+ 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.
+User experience tells us that you will need at least autotools version 1.7.
+
+To build libburn.pykix.org and its subprojects it should be sufficient to go
+into its toplevel directory and execute
+ ./bootstrap (needed if you downloaded from SVN and not a release tarball)
+ ./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 .
+
+------------------------------------------------------------------------------
+
+ 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/ZeroTwoTwo/acinclude.m4 b/tags/ZeroTwoTwo/acinclude.m4
new file mode 100644
index 00000000..985cf386
--- /dev/null
+++ b/tags/ZeroTwoTwo/acinclude.m4
@@ -0,0 +1,37 @@
+dnl Function to link an architecture specific file
+dnl LINK_ARCH_SRC(source_dir, arch, source_file)
+AC_DEFUN([COPY_ARCH_SRC],
+[
+ echo "copying $1/$2/$3 -> $1/$3"
+ old="$srcdir/$1/$2/$3"
+ new="$srcdir/$1/$3"
+ cat >$new <<__EOF__
+/* WARNING: This file was automatically generated!
+ * Original: $old
+ */
+__EOF__
+ cat >>$new <$old
+])
+
+AC_DEFUN([TARGET_SHIZZLE],
+[
+ ARCH=""
+
+ AC_MSG_CHECKING([target operating system])
+
+ case $target in
+ *-*-linux*)
+ ARCH=linux
+ ;;
+ *)
+ AC_ERROR([You are attempting to compile for an unsupported platform])
+ ;;
+ esac
+
+ AC_MSG_RESULT([$ARCH])
+
+ # this doesn't actually do anything yet.. but it will someday when we port
+ # libburn
+
+ #COPY_ARCH_SRC(libburn, $ARCH, transport.c)
+])
diff --git a/tags/ZeroTwoTwo/bootstrap b/tags/ZeroTwoTwo/bootstrap
new file mode 100755
index 00000000..ad2bf022
--- /dev/null
+++ b/tags/ZeroTwoTwo/bootstrap
@@ -0,0 +1,6 @@
+#!/bin/sh -x
+
+aclocal
+libtoolize --copy --force
+autoconf
+automake --foreign --add-missing --copy --include-deps
diff --git a/tags/ZeroTwoTwo/cdrskin/README b/tags/ZeroTwoTwo/cdrskin/README
new file mode 100644
index 00000000..bbe952c4
--- /dev/null
+++ b/tags/ZeroTwoTwo/cdrskin/README
@@ -0,0 +1,377 @@
+------------------------------------------------------------------------------
+ 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.2.tar.gz
+Copyright (C) 2006 Thomas Schmitt
+
+------------------------------------------------------------------------------
+
+On top of libburn there is implemented cdrskin 0.2.2, 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
+SuSE 9.0, kernel 2.4.21, ide-scsi emulation, LG GSA-4082B CD/DVD burner
+RIP-14.4, kernel 2.6.14, no ide-scsi, with both above burners
+
+It fails to compile or run on SuSE 6.4 (kernel 2.2.14).
+It does not find the IDE CD burner on SuSE 7.2 without ide-scsi.
+Other people sucessfully tested cdrskin on several kernel 2.6 based x86 Linux
+systems, including 64 bit systems. (Further reports are welcome.)
+
+
+ Compilation, First Glimpse, Installation
+
+Obtain cdrskin-0.2.2.tar.gz , take it to a directory of your choice and do:
+
+ tar xzf cdrskin-0.2.2.tar.gz
+ cd cdrskin-0.2.2
+
+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.2 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- : 'TEAC' 'CD-ROM CD-532S'
+ 1 dev='/dev/sg1' rwrw-- : 'LITE-ON' 'LTR-48125S'
+
+So full and insecure enabling of both for everybody would look like
+
+ chmod a+rw /dev/sg0 /dev/sg1
+
+(The CD-ROM is in these examples only for demonstrating the presence of another
+ SCSI device. This /dev/sg0 may be left as it is and stay invisible for normal
+ users.)
+
+I strongly discourage to run cdrskin with setuid root or via sudo !
+It is not checked for the necessary degree of hacker safety.
+
+
+ Usage examples
+
+Get an overview of cdrecord style addresses of available devices
+ cdrskin -scanbus
+
+Obtain some info about the drive
+ cdrskin dev=1,1,0 -checkdrive
+
+Obtain some info about the drive and the inserted media
+ cdrskin dev=1,1,0 -atip
+
+Thoroughly blank a CD-RW
+ cdrskin -v dev=1,1,0 blank=all -eject
+
+Blank CD-RW sufficiently for making it ready for overwrite
+ cdrskin -v dev=1,1,0 blank=fast -eject
+
+Burn image file my_image.iso to CD
+ cdrskin -v dev=1,1,0 speed=12 fs=8m -sao driveropts=burnfree padsize=300k \
+ -eject my_image.iso
+
+Burn a compressed afio archive to CD on-the-fly
+ find . | afio -oZ - | cdrskin -v dev=1,1,0 fs=32m speed=8 -sao \
+ driveropts=burnfree padsize=300k tsize=650m -
+
+Burn 5 audio tracks to CD
+ 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=/dev/hdc blank=fast fs=0 -eject -audio track0[1-5].cd
+
+
+
+ Usage example with http://scdbackup.sourceforge.net
+
+Address may be a cdrecord-style "scsibus,target,lun" as listed with
+cdrskin -scanbus (but not as listed with cdrecord -scanbus) :
+
+ export SCDBACKUP_SCSI_ADR="1,1,0"
+
+or a device file address as listed by --devices with an accessible drive :
+
+ export SCDBACKUP_SCSI_ADR="/dev/sg1"
+
+Set usage of cdrskin with appropriate options rather than cdrecord :
+
+ export SCDBACKUP_CDRECORD="cdrskin -v -v tao_to_sao_tsize=650m"
+
+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.
+
+Audio features are incomplete in respect to cdrecord. Well prepaired track
+files should get burned flawlessly, thanks to Lorenzo Taylor.
+Builtin extraction of raw audio data from filetypes .au and .wav is not
+implemented yet. See chapter "Audio CD" for details.
+
+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.
+
+
+ Pseudo-SCSI Adresses
+
+cdrecord and cdrskin share only some syntax of addresses but not the meaning
+of the components. A cdrecord-style address for cdrskin
+ [[prefix:]scsibus,]target,lun
+corresponds either to a device file address or to a libburn drive number.
+Component "scsibus" indicates the translation method. Defined busses are:
+ 0 target is the libburn drivenumber as listed with --devices
+ 1 associated to device file /dev/sgN , target chooses N
+ 2 associated to device file /dev/hdX , target 0='a', 1='b' ..., 25='z'
+
+So "1,1,0" is /dev/sg1, "2,3,0" is /dev/hdd, "0,2,0" is libburn drive #2 at
+some unspecified device file.
+This scheme shall help to keep cdrecord-style addresses stable and exchangeable
+between users without excluding drives with unexpected device addresses.
+The numbering on bus 0 is prone to arbitrary changes caused by changes in
+drive accessability.
+Further busses may emerge as libburn evolves. "prefix" and "lun" may get
+a meaning. To stay upward compatible, use addresses as printed by -scanbus.
+
+Some programs or users have their own ideas about the address of their burner.
+K3b 0.10 for example derives cdrecord addresses by own examination of the
+devices and not by calling cdrecord -scanbus.
+To direct such callers to the appropriate drives, cdrskin allows to define
+device address aliases. Like
+ cdrskin dev_translation=+1,0,0+/dev/sg1 \
+ dev_translation=+ATA:1,0,0+/dev/sg1 \
+ dev_translation=-"cd+dvd"-1,1,0 \
+ ...
+Any of the addresses dev=1,0,0, dev=ATA:1,0,0, dev=cd+dvd will be mapped to
+/dev/sg1 resp. to its standard alias 1,1,0.
+The first character after "dev_translation=" defines the character which
+separates the two parts of the translation pair. (Above: "+" and "-".)
+
+In K3b 0.10 it is possible to employ alternative writer programs by setting
+their full path (e.g. /usr/bin/cdrskin) in menu
+ Settings:Configure K3b...:Programs:Search Path
+and to make them default in menu
+ Settings:Configure K3b...:Programs:Programs:
+A suitable setting for "cdrecord" in menu
+ Settings:Configure K3b...:Programs:User Parameters
+would then probably be
+ -v dev_translation=+1,0,0+/dev/sg1
+You will learn from button "Show Debugging Output" after a failed burn run
+what cdrecord command was used with what address "dev=...". This address "..."
+will be the right one to replace "1,0,0" in above example.
+
+
+ Startup Files
+
+If not --no_rc is the first argument then cdrskin attempts on startup to read
+arguments from the following three files:
+ /etc/defaults/cdrskin
+ /etc/opt/cdrskin/rc
+ $HOME/.cdrskinrc
+The files are read in the sequence given above.
+Each readable line is treated as one single argument. No extra blanks,
+no comments, no empty lines are permitted.
+
+Example content of a startup file:
+dev=1,1,0
+dev_translation=+1,0,0+1,1,0
+--fifo_start_empty
+fs=16m
+
+
+ Audio CD
+
+Builtin extraction of raw audio data from filetypes .au and .wav is not
+implemented yet. Lorenzo Taylor enabled option -audio in cdrskin (thanks !)
+and reports neat results with audio data files which are :
+ headerless
+ 44100Hz
+ 16bit, stereo (or 4-channel if the 4-channel bit is set),
+ little-endian byte order
+He proposes to extract them from usual audio formats by commands like
+given above under "Usage examples".
+
+The existence of cdrecord-builtin .wav extraction seems to have
+hampered the development of a standalone stripping tool. If you know
+a command line that would do the trick, contact me or libburn.pykix.org .
+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/ZeroTwoTwo/cdrskin/add_ts_changes_to_libburn_0_2_1 b/tags/ZeroTwoTwo/cdrskin/add_ts_changes_to_libburn_0_2_1
new file mode 100755
index 00000000..7d525774
--- /dev/null
+++ b/tags/ZeroTwoTwo/cdrskin/add_ts_changes_to_libburn_0_2_1
@@ -0,0 +1,165 @@
+#!/bin/sh
+
+set -x
+
+# This script documents how this cdrskin version was derived from
+# a vanilla libburn version. It is not intended nor needed for any
+# use of cdrskin but included here only to show the technical
+# relationship between both projects - which are close friends
+# and issue roughly the same software.
+#
+# Package maintainers are advised to cover rather libburn than
+# cdrskin unless they put only emphasis on the cdrecord emulation
+# provided by cdrskin. libburn contains cdrskin - cdrskin is an
+# oscillating, friendly and coordinated fork of libburn.
+#
+# Script results are a source tarball and two binaries
+# one dynamic and one static in respect to system libs.
+# Both binaries are static in respect to libburn.
+#
+# The script is to be run in the directory above the toplevel
+# directory of libburn resp. cdrskin development.
+#
+# libburn version used: http://libburn.pykix.org SVN of Aug 15 2006
+# packed up in a tarball just to save it from inadverted changes.
+# original="./libburn_svn_A60815.tgz"
+original="./libburn_cdrskin_A60819.tgz"
+
+# The top level directory in that snapshot is named
+intermediate="./libburn_pykix"
+
+# My changes are in libburn-0.2.1.ts.develop , mainly in ./cdrskin
+
+changes="./libburn-0.2.1.ts.develop"
+skin_rev="0.1.5"
+
+# The result directory and the name of the result tarballs
+target="./cdrskin-${skin_rev}"
+cdrskin_tarball="./cdrskin-${skin_rev}.tar.gz"
+cdrskin_tarball_svn="./cdrskin-${skin_rev}.svn.tar.gz"
+
+# (This once earned me an embarrassingly blooping source tarball)
+# compile_dir="$changes"
+
+compile_dir="$target"
+compile_cmd="./cdrskin/compile_cdrskin.sh"
+compile_static_opts="-static"
+compile_result="cdrskin/cdrskin"
+
+
+# addresses relative to compile_dir :
+bintarget_dynamic="../cdrskin_${skin_rev}-x86-suse9_0"
+bintarget_static="$bintarget_dynamic"-static
+
+if test -d "$changes"
+then
+ dummy=dummy
+else
+ echo "$0 : FATAL : no directory $changes" >&2
+ exit 1
+fi
+
+for i in "$target" "$intermediate"
+do
+ if test -e "$i"
+ then
+ echo "$0 : FATAL : already existing $i" >&2
+ exit 2
+ fi
+done
+
+if test -f "$original"
+then
+ dummy=dummy
+else
+ echo "$0 : FATAL : no file $original" >&2
+ 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"/cdrfifo
+rm "$cdrskin_target"/cdrskin
+rm "$cdrskin_target"/cleanup
+
+# Remove unwanted SVN stuff (TODO: avoid downloading it)
+for i in "$target"/.svn "$target"/*/.svn
+do
+ if test "$i" = "$target"'/*/.svn'
+ then
+ dummy=dummy
+ else
+ if test -e "$i"
+ then
+ rm -rf "$i"
+ fi
+ fi
+done
+
+# For now: Add own libburn-README in toplevel
+cp -a "$changes"/README "$target"
+
+# Add modified Makefile.am
+cp -a "$changes"/Makefile.am "$target"
+
+
+# Make SVN state tarball for the libburn team
+# TODO: will probably be obsoleted after sucessful merge
+tar czf "$cdrskin_tarball_svn" "$target"
+
+
+# Get over dependecy on autotools. Rely only on cc, make et. al.
+# This is not the same as "make dist" but i can do it without
+# having to evaluate the quality of said "make dist"
+#
+( cd "$target" ; ./bootstrap )
+
+
+# Pack it up to the new libburn+cdrskin-tarball
+tar czf "$cdrskin_tarball" "$target"
+
+# Produce a static and a dynamic binary
+(
+ cd "$compile_dir" || exit 1
+ ./configure
+ make
+ $compile_cmd -do_strip
+ cp "$compile_result" "$bintarget_dynamic"
+ if test -n "$compile_static_opts"
+ then
+ $compile_cmd $compile_static_opts -do_strip
+ cp "$compile_result" "$bintarget_static"
+ fi
+)
+
+# Disable this for debugging the merge process
+rm -rf "$target"
+
+
diff --git a/tags/ZeroTwoTwo/cdrskin/cdrecord_spy.sh b/tags/ZeroTwoTwo/cdrskin/cdrecord_spy.sh
new file mode 100755
index 00000000..54d7c344
--- /dev/null
+++ b/tags/ZeroTwoTwo/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/ZeroTwoTwo/cdrskin/cdrfifo.c b/tags/ZeroTwoTwo/cdrskin/cdrfifo.c
new file mode 100644
index 00000000..da6dc598
--- /dev/null
+++ b/tags/ZeroTwoTwo/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/ZeroTwoTwo/cdrskin/cdrfifo.h b/tags/ZeroTwoTwo/cdrskin/cdrfifo.h
new file mode 100644
index 00000000..42461d2d
--- /dev/null
+++ b/tags/ZeroTwoTwo/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/ZeroTwoTwo/cdrskin/cdrskin.c b/tags/ZeroTwoTwo/cdrskin/cdrskin.c
new file mode 100644
index 00000000..fcf70ad3
--- /dev/null
+++ b/tags/ZeroTwoTwo/cdrskin/cdrskin.c
@@ -0,0 +1,4489 @@
+
+/*
+ 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.
+
+*
+Only if the new API compliance is enabled by macro Cdrskin_new_api_tesT .
+Not yet by default. In many aspects this is already superior, note well :
+
+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/message.o \
+ libburn/mmc.o libburn/options.o libburn/sbc.o libburn/sector.o \
+ libburn/sg.o libburn/spc.o libburn/source.o libburn/structure.o \
+ libburn/toc.o libburn/util.o libburn/write.o \
+ -lpthread
+
+*/
+
+
+/** The official program version */
+#ifndef Cdrskin_prog_versioN
+#define Cdrskin_prog_versioN "0.2.2"
+#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
+
+/** use this to accomodate to the CVS version as of Dec 8, 2005
+#define Cdrskin_libburn_cvs_A51208_tS 1
+*/
+#ifdef Cdrskin_libburn_cvs_A51208_tS
+#define Cdrskin_libburn_versioN "0.2.tsA51208"
+#define Cdrskin_libburn_p_sectoR 1
+/* forever: */
+#define Cdrskin_libburn_no_burn_preset_device_opeN 1
+#endif
+
+/** use this to accomodate to the CVS version as of Feb 20, 2006
+#define Cdrskin_libburn_cvs_A60220_tS 1
+*/
+#ifdef Cdrskin_libburn_cvs_A60220_tS
+
+#define Cdrskin_libburn_versioN "0.2.tsA60220"
+#define Cdrskin_libburn_p_sectoR 1
+#define Cdrskin_libburn_with_fd_sourcE 1
+#define Cdrskin_libburn_largefilE 1
+#define Cdrskin_libburn_padding_does_worK 1
+#define Cdrskin_libburn_no_burn_preset_device_opeN 1
+
+#endif /* Cdrskin_libburn_cvs_A60220_tS */
+
+
+#ifdef Cdrskin_libburn_0_2_1
+#define Cdrskin_libburn_versioN "0.2.1"
+#define Cdrskin_libburn_from_pykix_svN 1
+#endif
+
+#ifdef Cdrskin_libburn_0_2_2
+#define Cdrskin_libburn_versioN "0.2.2"
+#define Cdrskin_libburn_from_pykix_svN 1
+#endif
+
+#ifdef Cdrskin_libburn_from_pykix_svN
+#define Cdrskin_libburn_p_sectoR 1
+#define Cdrskin_libburn_with_fd_sourcE 1
+#define Cdrskin_libburn_largefilE 1
+#define Cdrskin_libburn_padding_does_worK 1
+#define Cdrskin_libburn_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
+
+/* switches from old behavior with aquiring drives to new behavior */
+
+/* (put parasite macros under test caveat here) */
+
+#endif
+
+#endif /* Cdrskin_libburn_from_pykix_svN */
+
+#ifndef Cdrskin_libburn_versioN
+#define Cdrskin_libburn_versioN "0.2.2"
+#endif
+
+#ifdef Cdrskin_libburn_largefilE
+#ifndef _LARGEFILE_SOURCE
+#define _LARGEFILE_SOURCE 1
+#endif
+#ifndef _FILE_OFFSET_BITS
+#define _FILE_OFFSET_BITS 64
+#endif
+#endif /* Cdrskin_libburn_largefilE */
+
+
+/* These macros activate cdrskin workarounds for deficiencies resp.
+ problematic features of libburn which hopefully will change in
+ future. */
+
+/** Work around the fact that padding is not performed by libburn */
+#ifndef Cdrskin_libburn_padding_does_worK
+#define Cdrskin_burn_track_padding_brokeN 1
+#endif
+
+/** Work around the fact that neither /dev/sg0 (kernel 2.4 + ide-scsi) nor
+ /dev/hdc (kernel 2.6) get ejected by libburn */
+#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 */
+#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 */
+#ifndef Cdrskin_grab_abort_does_worK
+#define Cdrskin_grab_abort_brokeN 1
+#endif
+
+#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 that */
+#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
+
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+
+#include "cleanup.h"
+
+
+/** The size of a string buffer for pathnames and similar texts */
+#define Cdrskin_strleN 4096
+
+/** The maximum length +1 of a drive address */
+#ifdef Cdrskin_new_api_tesT
+#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; imaxl)
+ maxl= l;
+ } else {
+ if(argcount >= *argc)
+ break;
+ (*argv)[argcount]= (char *) malloc(l+1);
+ if((*argv)[argcount]==NULL)
+ {ret= -1; goto ex;}
+ strcpy((*argv)[argcount],buf);
+ (*argidx)[argcount]= i;
+ (*arglno)[argcount]= line_no;
+ }
+ argcount++;
+ }
+ fclose(fp); fp= NULL;
+ }
+ if(pass==0){
+ *argc= argcount;
+ if(argcount>0) {
+ *argv= (char **) malloc(argcount*sizeof(char *));
+ *argidx= (int *) malloc(argcount*sizeof(int));
+ *arglno= (int *) malloc(argcount*sizeof(int));
+ if(*argv==NULL || *argidx==NULL || *arglno==NULL)
+ {ret= -1; goto ex;}
+ }
+ for(i=0;i<*argc;i++) {
+ (*argv)[i]= NULL;
+ (*argidx)[i]= -1;
+ (*arglno)[i]= -1;
+ }
+ }
+ }
+
+ ret= 1;
+ex:;
+ if(fp!=NULL)
+ fclose(fp);
+ return(ret);
+}
+
+
+/** Combine environment variable HOME with given filename
+ @param filename Address relative to $HOME
+ @param fileadr Resulting combined address
+ @param fa_size Size of array fileadr
+ @param flag Unused yet
+ @return 1=ok , 0=no HOME variable , -1=result address too long
+*/
+int Sfile_home_adr_s(char *filename, char *fileadr, int fa_size, int flag)
+{
+ char *home;
+
+ strcpy(fileadr,filename);
+ home= getenv("HOME");
+ if(home==NULL)
+ return(0);
+ if(strlen(home)+strlen(filename)+1>=fa_size)
+ return(-1);
+ strcpy(fileadr,home);
+ if(filename[0]!=0){
+ strcat(fileadr,"/");
+ strcat(fileadr,filename);
+ }
+ return(1);
+}
+
+
+#endif /* ! Cdrskin_extra_leaN */
+
+
+/* --------------------------------------------------------------------- */
+
+/** Address translation table for users/applications which do not look
+ for the output of -scanbus but guess a Bus,Target,Lun on their own.
+*/
+
+/** The maximum number of entries in the address translation table */
+#define Cdradrtrn_leN 256
+
+/** The address prefix which will prevent translation */
+#define Cdrskin_no_transl_prefiX "LITERAL_ADR:"
+
+
+struct CdradrtrN {
+ char *from_address[Cdradrtrn_leN];
+ char *to_address[Cdradrtrn_leN];
+ int fill_counter;
+};
+
+
+#ifndef Cdrskin_extra_leaN
+
+/** Create a device address translator object */
+int Cdradrtrn_new(struct CdradrtrN **trn, int flag)
+{
+ struct CdradrtrN *o;
+ int i;
+
+ (*trn)= o= TSOB_FELD(struct CdradrtrN,1);
+ if(o==NULL)
+ return(-1);
+ for(i= 0;ifrom_address[i]= NULL;
+ o->to_address[i]= NULL;
+ }
+ o->fill_counter= 0;
+ return(1);
+}
+
+
+/** Release from memory a device address translator object */
+int Cdradrtrn_destroy(struct CdradrtrN **o, int flag)
+{
+ int i;
+ struct CdradrtrN *trn;
+
+ trn= *o;
+ if(trn==NULL)
+ return(0);
+ for(i= 0;ifill_counter;i++) {
+ if(trn->from_address[i]!=NULL)
+ free(trn->from_address[i]);
+ if(trn->to_address[i]!=NULL)
+ free(trn->to_address[i]);
+ }
+ free((char *) trn);
+ *o= NULL;
+ return(1);
+}
+
+
+/** Add a translation pair to the table
+ @param trn The translator which shall learn
+ @param from The user side address
+ @param to The cdrskin side address
+ @param flag Bitfield for control purposes:
+ bit0= "from" contains from+to address, to[0] contains delimiter
+*/
+int Cdradrtrn_add(struct CdradrtrN *trn, char *from, char *to, int flag)
+{
+ char buf[2*Cdrskin_adrleN+1],*from_pt,*to_pt;
+ int cnt;
+
+ cnt= trn->fill_counter;
+ if(cnt>=Cdradrtrn_leN)
+ return(-1);
+ if(flag&1) {
+ if(strlen(from)>=sizeof(buf))
+ return(0);
+ strcpy(buf,from);
+ to_pt= strchr(buf,to[0]);
+ if(to_pt==NULL)
+ return(0);
+ *(to_pt)= 0;
+ from_pt= buf;
+ to_pt++;
+ } else {
+ from_pt= from;
+ to_pt= to;
+ }
+ if(strlen(from)>=Cdrskin_adrleN || strlen(to)>=Cdrskin_adrleN)
+ return(0);
+ trn->from_address[cnt]= malloc(strlen(from_pt)+1);
+ trn->to_address[cnt]= malloc(strlen(to_pt)+1);
+ if(trn->from_address[cnt]==NULL ||
+ trn->to_address[cnt]==NULL)
+ return(-2);
+ strcpy(trn->from_address[cnt],from_pt);
+ strcpy(trn->to_address[cnt],to_pt);
+ trn->fill_counter++;
+ return(1);
+}
+
+
+/** Apply eventual device address translation
+ @param trn The translator
+ @param from The address from which to translate
+ @param driveno With backward translation only: The libburn drive number
+ @param to The result of the translation
+ @param flag Bitfield for control purposes:
+ bit0= translate backward
+ @return <=0 error, 1=no translation found, 2=translation found,
+ 3=collision avoided
+*/
+int Cdradrtrn_translate(struct CdradrtrN *trn, char *from, int driveno,
+ char to[Cdrskin_adrleN], int flag)
+{
+ int i,ret= 1;
+ char *adr;
+
+ to[0]= 0;
+ adr= from;
+ if(flag&1)
+ goto backward;
+
+ if(strncmp(adr,Cdrskin_no_transl_prefiX,
+ strlen(Cdrskin_no_transl_prefiX))==0) {
+ adr= adr+strlen(Cdrskin_no_transl_prefiX);
+ ret= 2;
+ } else {
+ for(i=0;ifill_counter;i++)
+ if(strcmp(adr,trn->from_address[i])==0)
+ break;
+ if(ifill_counter) {
+ adr= trn->to_address[i];
+ ret= 2;
+ }
+ }
+ if(strlen(adr)>=Cdrskin_adrleN)
+ return(-1);
+ strcpy(to,adr);
+ return(ret);
+
+backward:;
+ if(strlen(from)>=Cdrskin_adrleN)
+ sprintf(to,"%s%d",Cdrskin_no_transl_prefiX,driveno);
+ else
+ strcpy(to,from);
+ for(i=0;ifill_counter;i++)
+ if(strcmp(from,trn->to_address[i])==0 &&
+ strlen(trn->from_address[i])fill_counter) {
+ ret= 2;
+ strcpy(to,trn->from_address[i]);
+ } else {
+ for(i=0;ifill_counter;i++)
+ if(strcmp(from,trn->from_address[i])==0)
+ break;
+ if(ifill_counter)
+ if(strlen(from)+strlen(Cdrskin_no_transl_prefiX)boss= boss;
+ o->trackno= trackno;
+ o->source_path[0]= 0;
+ o->source_fd= -1;
+ o->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->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),&(o->track_type),0);
+ if(ret<=0)
+ goto failed;
+
+#ifndef Cdrskin_extra_leaN
+ ret= Cdrskin_get_fifo_par(boss, &(o->fifo_enabled),&(o->fifo_size),
+ &(o->fifo_start_empty),0);
+ if(ret<=0)
+ goto failed;
+#endif /* ! Cdrskin_extra_leaN */
+
+ if(flag&1)
+ o->fifo_start_empty= 1;
+ return(1);
+failed:;
+ Cdrtrack_destroy(track,0);
+ return(-1);
+}
+
+
+/** Release from memory a track object previously created by Cdrtrack_new() */
+int Cdrtrack_destroy(struct CdrtracK **o, int flag)
+{
+ struct CdrtracK *track;
+
+ track= *o;
+ if(track==NULL)
+ return(0);
+
+#ifndef Cdrskin_extra_leaN
+ Cdrfifo_destroy(&(track->fifo),0);
+#endif
+
+ if(track->libburn_track!=NULL)
+ burn_track_free(track->libburn_track);
+ free((char *) track);
+ *o= NULL;
+ return(1);
+}
+
+
+int Cdrtrack_get_size(struct CdrtracK *track, double *size, double *padding,
+ int flag)
+{
+ *size= track->fixed_size;
+ *padding= track->padding;
+ return(1);
+}
+
+
+int Cdrtrack_get_fifo(struct CdrtracK *track, struct CdrfifO **fifo, int flag)
+{
+ *fifo= track->fifo;
+ return(1);
+}
+
+
+/** Deliver an open file descriptor corresponding to the source path of track.
+ @return <=0 error, 1 success
+*/
+int Cdrtrack_open_source_path(struct CdrtracK *track, int *fd, int flag)
+{
+ if(track->source_path[0]=='-' && track->source_path[1]==0)
+ *fd= 0;
+ else if(track->source_path[0]=='#' &&
+ (track->source_path[1]>='0' && track->source_path[1]<='9'))
+ *fd= atoi(track->source_path+1);
+ else {
+ *fd= open(track->source_path,O_RDONLY);
+ if(*fd==-1) {
+ fprintf(stderr,"cdrskin: failed to open source address '%s'\n",
+ track->source_path);
+ fprintf(stderr,"cdrskin: errno=%d , \"%s\"\n",errno,
+ errno==0?"-no error code available-":strerror(errno));
+ } else {
+ if(track->fixed_size<=0) {
+ struct stat stbuf;
+ if(fstat(*fd,&stbuf)!=-1)
+ track->fixed_size= stbuf.st_size;
+ }
+ }
+ }
+ if(track->fixed_size=0) {
+ fprintf(stderr,
+ "cdrskin: NOTE : Enforcing minimum track size of %.f bytes\n",
+ Cdrtrack_minimum_sizE);
+ track->fixed_size= Cdrtrack_minimum_sizE;
+ }
+ track->source_fd= *fd;
+ return(*fd>=0);
+}
+
+
+#ifndef Cdrskin_extra_leaN
+
+/** Install a fifo object between data source and libburn.
+ Its parameters are known to track.
+ @param outlet_fd Returns the filedescriptor of the fifo outlet.
+ @param previous_fifo Object address for chaining or follow-up attachment.
+ @param flag Bitfield for control purposes:
+ bit0= Debugging verbosity
+ bit1= Do not create and attach a new fifo
+ but attach new follow-up fd pair to previous_fifo
+ @return <=0 error, 1 success
+*/
+int Cdrtrack_attach_fifo(struct CdrtracK *track, int *outlet_fd,
+ struct CdrfifO *previous_fifo, int flag)
+{
+ struct CdrfifO *ff;
+ int source_fd,pipe_fds[2],ret;
+
+ *outlet_fd= -1;
+ if(track->fifo_size<=0)
+ return(2);
+ ret= Cdrtrack_open_source_path(track,&source_fd,0);
+ if(ret<=0)
+ return(ret);
+ if(pipe(pipe_fds)==-1)
+ return(0);
+
+ Cdrfifo_destroy(&(track->fifo),0);
+ if(flag&2) {
+ ret= Cdrfifo_attach_follow_up_fds(previous_fifo,source_fd,pipe_fds[1],0);
+ if(ret<=0)
+ return(ret);
+ } else {
+ ret= Cdrfifo_new(&ff,source_fd,pipe_fds[1],2048,track->fifo_size,0);
+ if(ret<=0)
+ return(ret);
+ if(previous_fifo!=NULL)
+ Cdrfifo_attach_peer(previous_fifo,ff,0);
+ track->fifo= ff;
+ }
+ track->fifo_outlet_fd= pipe_fds[0];
+ if(flag&1)
+ printf(
+ "cdrskin_debug: track %d fifo replaced source_address '%s' by '#%d'\n",
+ track->trackno+1,track->source_path,track->fifo_outlet_fd);
+ sprintf(track->source_path,"#%d",track->fifo_outlet_fd);
+ track->source_fd= track->fifo_outlet_fd;
+ *outlet_fd= track->fifo_outlet_fd;
+ return(1);
+}
+
+
+/** Read data into the fifo until either it is full or the data source is
+ exhausted.
+ @return <=0 error, 1 success
+*/
+int Cdrtrack_fill_fifo(struct CdrtracK *track, int flag)
+{
+ int ret,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
+*/
+{
+ struct burn_track *tr;
+ struct burn_source *src= NULL;
+ double padding,lib_padding;
+ int ret,sector_pad_up;
+#ifdef Cdrskin_libburn_with_fd_sourcE
+ double fixed_size;
+ int source_fd;
+#endif /* ! Cdrskin_libburn_with_fd_sourcE */
+
+ track->trackno= trackno;
+ tr= burn_track_create();
+ track->libburn_track= tr;
+ 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);
+
+#ifdef Cdrskin_libburn_with_fd_sourcE
+ if(track->source_fd==-1) {
+ ret= Cdrtrack_open_source_path(track,&source_fd,0);
+ if(ret<=0)
+ goto ex;
+ }
+ fixed_size= track->fixed_size;
+ if((flag&2) && track->padding>0) {
+ if(flag&1)
+ ClN(fprintf(stderr,"cdrskin_debug: padding hack : %.f + %.f = %.f\n",
+ track->fixed_size,track->padding,
+ track->fixed_size+track->padding));
+ fixed_size+= track->padding;
+ }
+ src= burn_fd_source_new(track->source_fd,-1,(off_t) fixed_size);
+#else
+ src = burn_file_source_new3(track->source_path,NULL,(int) track->fixed_size);
+#endif /* ! Cdrskin_libburn_with_fd_sourcE */
+
+ assert(src);
+
+#ifndef Cdrskin_libburn_with_fd_sourcE
+ track->fixed_size= burn_source_get_size(src);
+ if((flag&2) && track->padding>0) {
+ if(flag&1)
+ ClN(fprintf(stderr,
+ "cdrskin_debug: padding : %.f + %.f = %.f\n",
+ (double) burn_source_get_size(src),track->padding,
+ ((double) burn_source_get_size(src))+track->padding));
+
+ src->fixed_size = burn_source_get_size(src) + track->padding;
+
+ if(flag&1)
+ ClN(fprintf(stderr,"cdrskin_debug: source size now : %.f\n",
+ (double) burn_source_get_size(src)));
+ }
+#endif /* ! Cdrskin_libburn_with_fd_sourcE */
+
+
+ if(burn_track_set_source(tr,src) != BURN_SOURCE_OK)
+ {ret= 0; goto ex;}
+ burn_session_add_track(session,tr,BURN_POS_END);
+ ret= 1;
+ex:
+ if(src!=NULL)
+ burn_source_free(src);
+ return(ret);
+}
+
+
+/** Release libburn track information after a session is done */
+int Cdrtrack_cleanup(struct CdrtracK *track, int flag)
+{
+ if(track->libburn_track==NULL)
+ return(0);
+ burn_track_free(track->libburn_track);
+ track->libburn_track= NULL;
+ return(1);
+}
+
+
+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 initialization of
+ libburn and creation of the CdrskiN object. It finally becomes a managed
+ subordinate of the CdrskiN object.
+*/
+struct CdrpreskiN {
+
+ /** Stores eventually given absolute device address before translation */
+ char raw_device_adr[Cdrskin_adrleN];
+
+ /** Stores an eventually given translated absolute device address between
+ Cdrpreskin_setup() and Cdrskin_create() .
+ */
+ char device_adr[Cdrskin_adrleN];
+
+ /** The eventual address translation table */
+ struct CdradrtrN *adr_trn;
+
+ /** Memorizes the abort handling mode from presetup to creation of
+ control object. Defined handling modes are:
+ 0= no abort handling
+ 1= try to cancel, release, exit (leave signal mode as set by caller)
+ 2= try to ignore all signals
+ 3= mode 1 in normal operation, mode 2 during abort handling
+ 4= mode 1 in normal operation, mode 0 during abort handling
+ -1= install abort handling 1 only in Cdrskin_burn() after burning started
+ */
+ int abort_handler;
+
+ /** Wether to allow getuid()!=geteuid() */
+ int allow_setuid;
+
+ /** Wether to allow user provided addresses like #4 */
+ int allow_fd_source;
+
+ /** Wether an option is given which needs a full bus scan */
+ int no_whitelist;
+
+ /** Wether bus scans shall exit!=0 if no drive was found */
+ int scan_demands_drive;
+
+ /** Wether to abort when a busy drive is encountered during bus scan */
+ int abort_on_busy_drive;
+
+ /** Wether to try to avoid collisions when opening drives */
+ int drive_exclusive;
+
+ /** Wether to try to wait for unwilling drives to become willing to open */
+ int drive_blocking;
+
+
+#ifndef Cdrskin_extra_leaN
+
+ /** List of startupfiles */
+ char rc_filenames[Cdrpreskin_rc_nuM][Cdrskin_strleN];
+ int rc_filename_count;
+
+ /** Non-argument options from startupfiles */
+ int pre_argc;
+ char **pre_argv;
+ int *pre_argidx;
+ int *pre_arglno;
+
+#endif /* ! Cdrskin_extra_leaN */
+
+};
+
+
+
+/** Create a preliminary cdrskin program run control object. It will become
+ part of the final control object.
+ @param preskin Returns pointer to resulting
+ @param flag Bitfield for control purposes: unused yet
+ @return <=0 error, 1 success
+*/
+int Cdrpreskin_new(struct CdrpreskiN **preskin, int flag)
+{
+ struct CdrpreskiN *o;
+ int i;
+
+ (*preskin)= o= TSOB_FELD(struct CdrpreskiN,1);
+ if(o==NULL)
+ return(-1);
+
+ o->raw_device_adr[0]= 0;
+ o->device_adr[0]= 0;
+ o->adr_trn= NULL;
+ o->abort_handler= 3;
+ o->allow_setuid= 0;
+ o->allow_fd_source= 0;
+ o->no_whitelist= 0;
+ o->scan_demands_drive= 0;
+ o->abort_on_busy_drive= 0;
+ o->drive_exclusive= 1;
+ o->drive_blocking= 0;
+
+#ifndef Cdrskin_extra_leaN
+ o->rc_filename_count= Cdrpreskin_rc_nuM;
+ for(i=0;irc_filename_count-1;i++)
+ strcpy(o->rc_filenames[i],Cdrpreskin_sys_rc_nameS[i]);
+ o->rc_filenames[o->rc_filename_count-1][0]= 0;
+ o->pre_argc= 0;
+ o->pre_argv= NULL;
+ o->pre_argidx= NULL;
+ o->pre_arglno= NULL;
+#endif /* ! Cdrskin_extra_leaN */
+
+ return(1);
+}
+
+
+int Cdrpreskin_destroy(struct CdrpreskiN **preskin, int flag)
+{
+ struct CdrpreskiN *o;
+
+ o= *preskin;
+ if(o==NULL)
+ return(0);
+
+#ifndef Cdrskin_extra_leaN
+ if((o->pre_arglno)!=NULL)
+ free((char *) o->pre_arglno);
+ if((o->pre_argidx)!=NULL)
+ free((char *) o->pre_argidx);
+ if(o->pre_argc>0 && o->pre_argv!=NULL)
+ Sfile_destroy_argv(&(o->pre_argc),&(o->pre_argv),0);
+ Cdradrtrn_destroy(&(o->adr_trn),0);
+#endif /* ! Cdrskin_extra_leaN */
+
+ free((char *) o);
+ *preskin= NULL;
+ return(1);
+}
+
+
+/** Convert a cdrecord-style device address into a libburn device address or
+ into a libburn drive number. It depends on the "scsibus" number of the
+ cdrecord-style address which kind of libburn address emerges:
+ bus=0 : drive number , bus=1 : /dev/sgN , bus=2 : /dev/hdX
+ (This call intentionally has no CdrpreskiN argument)
+ @return <=0 error, 1 success
+*/
+int Cdrpreskin__cdrecord_to_dev(char *adr, char device_adr[Cdrskin_adrleN],
+ int *driveno, int flag)
+{
+ int comma_seen= 0,digit_seen= 0,busno= 0,k;
+
+ *driveno= -1;
+ device_adr[0]= 0;
+ if(strlen(adr)==0)
+ return(0);
+
+ /* read the trailing numeric string as device address code */
+ /* accepts "1" , "0,1,0" , "ATA:0,1,0" , ... */
+ for(k= strlen(adr)-1;k>=0;k--) {
+ if(adr[k]==',' && !comma_seen) {
+ comma_seen= 1;
+ digit_seen= 0;
+ continue;
+ }
+ if(adr[k]<'0' || adr[k]>'9')
+ break;
+ digit_seen= 1;
+ }
+ if(!digit_seen) {
+ k= strlen(adr)-1;
+ if(adr[k]==':' || (adr[k]>='A' && adr[k]<='Z')) { /* empty prefix ? */
+ *driveno= 0;
+ return(1);
+ }
+ return(0);
+ }
+ sscanf(adr+k+1,"%d",driveno);
+ /* look for symbolic bus : 1=/dev/sgN 2=/dev/hdX */
+ digit_seen= 0;
+ if(k>0) if(adr[k]==',') {
+ for(k--;k>=0;k--) {
+ if(adr[k]<'0' || adr[k]>'9')
+ break;
+ digit_seen= 1;
+ }
+ if(digit_seen) {
+ sscanf(adr+k+1,"%d",&busno);
+ if(busno==1) {
+ sprintf(device_adr,"/dev/sg%d",*driveno);
+ } else if(busno==2) {
+ sprintf(device_adr,"/dev/hd%c",'a'+(*driveno));
+ } else if(busno!=0) {
+ fprintf(stderr,
+ "cdrskin: FATAL : dev=[Prefix:]Bus,Target,Lun expects Bus out of {0,1,2}\n");
+ return(0);
+ }
+ }
+ }
+ return(1);
+}
+
+
+#ifndef Cdrskin_extra_leaN
+
+/** Load content startup files into preskin cache */
+int Cdrpreskin_read_rc(struct CdrpreskiN *o, char *progname,int flag)
+{
+ int ret,i;
+ char *filenames_v[3];
+
+ for(i=0;irc_filename_count;i++)
+ filenames_v[i]= o->rc_filenames[i];
+ Sfile_home_adr_s(".cdrskinrc",o->rc_filenames[o->rc_filename_count-1],
+ Cdrskin_strleN,0);
+ ret= Sfile_multi_read_argv(progname,filenames_v,o->rc_filename_count,
+ &(o->pre_argc),&(o->pre_argv),
+ &(o->pre_argidx),&(o->pre_arglno),4);
+ return(ret);
+}
+
+#endif /* ! Cdrskin_extra_leaN */
+
+
+/** Interpret those arguments which do not need libburn or which influence the
+ startup of libburn and/or the creation of the CdrskiN object. This is run
+ before libburn gets initialized and before Cdrskin_new() is called.
+ Options which need libburn or a CdrskiN object are processed in a different
+ function named Cdrskin_setup().
+ @param flag Bitfield for control purposes:
+ bit0= do not finalize setup
+ bit1= do not read and interpret rc files
+ @return <=0 error, 1 success , 2 end program run with exit value 0
+*/
+int Cdrpreskin_setup(struct CdrpreskiN *o, int argc, char **argv, int flag)
+/*
+return:
+ <=0 error
+ 1 ok
+ 2 end program run (--help)
+*/
+{
+ int i,ret,bragg_with_audio= 0;
+ char *value_pt;
+
+#ifndef Cdrskin_extra_leaN
+ if(argc>1)
+ if(strcmp(argv[1],"--no_rc")==0)
+ flag|= 2;
+ if(!(flag&2)) {
+ ret= Cdrpreskin_read_rc(o,argv[0],0);
+ if(ret<0)
+ return(-1);
+ if(o->pre_argc>1) {
+ ret= Cdrpreskin_setup(o,o->pre_argc,o->pre_argv,flag|1|2);
+ if(ret<=0)
+ return(ret);
+ /* ??? abort on ret==2 ? */
+ }
+ }
+#endif
+
+ if(argc==1) {
+ fprintf(stderr,"cdrskin: SORRY : no options given. Try option --help\n");
+ return(0);
+ }
+ for (i= 1;iabort_handler= 3;
+
+ } else if(strcmp(argv[i],"--allow_setuid")==0) {
+ o->allow_setuid= 1;
+
+ } else if(strcmp(argv[i],"blank=help")==0 ||
+ strcmp(argv[i],"-blank=help")==0) {
+
+#ifndef Cdrskin_extra_leaN
+
+ fprintf(stderr,"Blanking options:\n");
+ fprintf(stderr,"\tall\t\tblank the entire disk\n");
+ fprintf(stderr,"\tdisc\t\tblank the entire disk\n");
+ fprintf(stderr,"\tdisk\t\tblank the entire disk\n");
+ fprintf(stderr,
+ "\tfast\t\tminimally blank the entire disk\n");
+ fprintf(stderr,
+ "\tminimal\t\tminimally blank the entire disk\n");
+
+#else /* ! Cdrskin_extra_leaN */
+
+ goto see_cdrskin_eng_html;
+
+#endif /* ! Cdrskin_extra_leaN */
+
+ if(argc==2)
+ {ret= 2; goto final_checks;}
+
+ } else if(strcmp(argv[i],"--bragg_with_audio")==0) {
+ bragg_with_audio= 1;
+
+ } else if(strcmp(argv[i],"--demand_a_drive")==0) {
+ o->scan_demands_drive= 1;
+
+ } else if(strcmp(argv[i],"--devices")==0) {
+ printf("Note: If this hangs for a while then there is a drive with\n");
+ printf(" unexpected problems (e.g. ill DMA).\n");
+ printf(" One may exclude such a device file by removing r- and w-\n");
+ printf(" permissions for all cdrskin users.\n");
+ o->no_whitelist= 1;
+
+ } else if(strncmp(argv[i],"dev_translation=",16)==0) {
+
+#ifndef Cdrskin_extra_leaN
+
+ if(o->adr_trn==NULL) {
+ ret= Cdradrtrn_new(&(o->adr_trn),0);
+ if(ret<=0)
+ goto no_adr_trn_mem;
+ }
+ if(argv[i][16]==0) {
+ fprintf(stderr,
+ "cdrskin: FATAL : dev_translation= : missing separator character\n");
+ return(0);
+ }
+ ret= Cdradrtrn_add(o->adr_trn,argv[i]+17,argv[i]+16,1);
+ if(ret==-2) {
+no_adr_trn_mem:;
+ fprintf(stderr,
+ "cdrskin: FATAL : address_translation= : cannot allocate memory\n");
+ } else if(ret==-1)
+ fprintf(stderr,
+ "cdrskin: FATAL : address_translation= : table full (%d items)\n",
+ Cdradrtrn_leN);
+ else if(ret==0)
+ fprintf(stderr,
+ "cdrskin: FATAL : address_translation= : no address separator '%c' found\n",
+ argv[i][17]);
+ if(ret<=0)
+ return(0);
+
+#else /* ! Cdrskin_extra_leaN */
+
+ fprintf(stderr,
+ "cdrskin: FATAL : dev_translation= is not available in lean version\n");
+ return(0);
+
+#endif /* Cdrskin_extra_leaN */
+
+
+ } else if(strncmp(argv[i],"-dev=",5)==0) {
+ value_pt= argv[i]+5;
+ goto set_dev;
+ } else if(strncmp(argv[i],"dev=",4)==0) {
+ value_pt= argv[i]+4;
+set_dev:;
+ if(strcmp(value_pt,"help")==0) {
+
+#ifndef Cdrskin_extra_leaN
+
+ printf("Supported SCSI transports for this platform:\n");
+ fflush(stdout);
+ fprintf(stderr,"\nTransport name:\t\tlibburn\n");
+ fprintf(stderr,
+ "Transport descr.:\tOpen-source library for writing optical discs\n");
+ fprintf(stderr,"Transp. layer ind.:\t\n");
+ fprintf(stderr,"Target specifier:\tbus,target,lun\n");
+ fprintf(stderr,"Target example:\t\t1,2,0\n");
+ fprintf(stderr,"SCSI Bus scanning:\tsupported\n");
+ fprintf(stderr,
+ "Open via UNIX device:\tsupported (see option --devices)\n");
+
+#else /* ! Cdrskin_extra_leaN */
+
+ goto see_cdrskin_eng_html;
+
+#endif /* Cdrskin_extra_leaN */
+
+ {ret= 2; goto final_checks;}
+ }
+ if(strlen(value_pt)>=sizeof(o->raw_device_adr))
+ goto dev_too_long;
+ strcpy(o->raw_device_adr,value_pt);
+
+ } else if(strcmp(argv[i],"--drive_abort_on_busy")==0) {
+ o->abort_on_busy_drive= 1;
+
+ } else if(strcmp(argv[i],"--drive_blocking")==0) {
+ o->drive_blocking= 1;
+
+ } else if(strcmp(argv[i],"--drive_not_exclusive")==0) {
+ o->drive_exclusive= 0;
+
+ } else if(strcmp(argv[i],"driveropts=help")==0 ||
+ strcmp(argv[i],"-driveropts=help")==0) {
+
+#ifndef Cdrskin_extra_leaN
+
+ fprintf(stderr,"Driver options:\n");
+ fprintf(stderr,"burnfree\tPrepare writer to use BURN-Free technology\n");
+ fprintf(stderr,"noburnfree\tDisable using BURN-Free technology\n");
+
+#else /* ! Cdrskin_extra_leaN */
+
+ goto see_cdrskin_eng_html;
+
+#endif /* Cdrskin_extra_leaN */
+
+ if(argc==2 || (i==2 && argc==3 && strncmp(argv[1],"dev=",4)==0))
+ {ret= 2; goto final_checks;}
+
+ } else if(strcmp(argv[i],"--help")==0) {
+
+#ifndef Cdrskin_extra_leaN
+
+ printf("\n");
+ printf("Usage: %s [options|source_addresses]\n", argv[0]);
+ printf("Burns preformatted data to CD-R or CD-RW via libburn.\n");
+ printf("For the cdrecord compatible options which control the work of\n");
+ printf(
+ "blanking and burning see output of option -help rather than --help.\n");
+ printf("Non-cdrecord options:\n");
+ printf(" --abort_handler do not leave the drive in busy state\n");
+ printf(" --allow_setuid disable setuid blocker (very insecure !)\n");
+ printf(
+ " --any_track allow source_addresses to match '^-.' or '='\n");
+ printf(
+ " --bragg_with_audio list -audio as supported option with -help\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");
+#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_rc as first argument: do not read startup files\n");
+ printf(
+ " --single_track accept only last argument as source_address\n");
+ printf(
+ " tao_to_sao_tsize= substitute -tao by -sao and 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 remarks, no whitespace.\n");
+ printf(
+ "By default any argument that does not match grep '^-.' or '=' is\n");
+ printf(
+ "used as track source. If it is \"-\" then stdin is used. In this\n");
+ printf("case the total byte count of the source must be announced via\n");
+ printf("tsize= previous to the source address.\n");
+ printf("cdrskin : http://scdbackup.sourceforge.net/cdrskin_eng.html\n");
+ printf(" mailto:scdbackup@gmx.net (Thomas Schmitt)\n");
+ printf("libburn : http://libburn.pykix.org\n");
+ printf("cdrecord : ftp://ftp.berlios.de/pub/cdrecord/\n");
+ printf("My respect to the authors of cdrecord and libburn.\n");
+ printf("scdbackup: http://scdbackup.sourceforge.net/main_eng.html\n");
+ printf("\n");
+
+#else /* ! Cdrskin_extra_leaN */
+
+see_cdrskin_eng_html:;
+ printf("This is a capability reduced lean version without help texts.\n");
+ printf("See http://scdbackup.sourceforge.net/cdrskin_eng.html\n");
+
+#endif /* Cdrskin_extra_leaN */
+
+
+ {ret= 2; goto final_checks;}
+ } else if(strcmp(argv[i],"-help")==0) {
+
+#ifndef Cdrskin_extra_leaN
+
+ fprintf(stderr,"Usage: %s [options|source_addresses]\n",argv[0]);
+ fprintf(stderr,"Note: This is not cdrecord. See cdrskin start message on stdout. See --help.\n");
+ fprintf(stderr,"Options:\n");
+ fprintf(stderr,"\t-version\tprint version information and exit\n");
+ fprintf(stderr,
+ "\tdev=target\tpseudo-SCSI target to use as CD-Recorder\n");
+ fprintf(stderr,
+ "\tgracetime=#\tset the grace time before starting to write to #.\n");
+ fprintf(stderr,"\t-v\t\tincrement verbose level by one\n");
+ fprintf(stderr,
+ "\tdriveropts=opt\topt= one of {burnfree,noburnfree,help}\n");
+ fprintf(stderr,
+ "\t-checkdrive\tcheck if a driver for the drive is present\n");
+ fprintf(stderr,"\t-scanbus\tscan the SCSI bus and exit\n");
+ fprintf(stderr,"\tspeed=#\t\tset speed of drive\n");
+ fprintf(stderr,"\tblank=type\tblank a CD-RW disc (see blank=help)\n");
+ fprintf(stderr,
+ "\tfs=#\t\tSet fifo size to # (0 to disable, default is 4 MB)\n");
+ fprintf(stderr,
+ "\t-eject\t\teject the disk after doing the work (might be ignored)\n");
+ fprintf(stderr,"\t-dummy\t\tdo everything with laser turned off\n");
+ fprintf(stderr,
+ "\t-atip\t\tretrieve media state, print \"Is *erasable\"\n");
+ fprintf(stderr,"\t-raw96r\t\tWrite disk in RAW/RAW96R mode\n");
+ fprintf(stderr,"\t-dao\t\tWrite disk in SAO mode.\n");
+ fprintf(stderr,"\t-sao\t\tWrite disk in SAO mode.\n");
+ fprintf(stderr,"\ttsize=#\t\tannounces exact size of source data\n");
+ fprintf(stderr,"\tpadsize=#\tAmount of padding\n");
+ if(bragg_with_audio)
+ 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-help\t\tprint this text to stderr and exit\n");
+ fprintf(stderr,
+ "Option -audio is enabled but does no special .au or .wav processing.\n");
+ fprintf(stderr,
+ "By default any argument that does not match grep '^-.' or '=' is used\n");
+ fprintf(stderr,
+ "as track source address. Address \"-\" means stdin. In this case\n");
+ fprintf(stderr,
+ "the total byte count of the source must be announced via tsize=#.\n");
+ fprintf(stderr,
+ "cdrskin will ensure that the announced tsize= is written even if\n");
+
+#ifdef Cdrskin_burn_track_padding_brokeN
+ fprintf(stderr,
+ "the source delivers fewer bytes. If the source delivers surplus\n");
+ fprintf(stderr,
+ "bytes, they will replace the eventual padding.\n");
+#else /* Cdrskin_burn_track_padding_brokeN */
+ fprintf(stderr,"the source delivers fewer bytes.\n");
+#endif /* ! Cdrskin_burn_track_padding_brokeN */
+
+#else /* ! Cdrskin_extra_leaN */
+
+ fprintf(stderr,"Note: This is not cdrecord. See cdrskin start message on stdout.\n");
+ fprintf(stderr,
+ "(writer profile: -atip retrieve, blank=type, -eject after work)\n");
+ goto see_cdrskin_eng_html;
+
+#endif /* Cdrskin_extra_leaN */
+
+ {ret= 2; goto final_checks;}
+
+ } else if(strcmp(argv[i],"--ignore_signals")==0) {
+ o->abort_handler= 2;
+
+ } else if(strcmp(argv[i],"--no_abort_handler")==0) {
+ o->abort_handler= 0;
+
+ } else if(strcmp(argv[i],"--no_rc")==0) {
+ if(i!=1)
+ fprintf(stderr,
+ "cdrskin: NOTE : option --no_rc would only work as first argument.\n");
+
+ } else if(strcmp(argv[i],"-scanbus")==0) {
+ o->no_whitelist= 1;
+
+ } else if(strcmp(argv[i],"-version")==0) {
+ printf(
+ "Cdrecord 2.01-Emulation Copyright (C) 2006, see libburn.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;
+ }
+
+ if(strlen(o->raw_device_adr)>0 && !o->no_whitelist) {
+ int driveno,hret;
+ char *adr,buf[Cdrskin_adrleN];
+
+ adr= o->raw_device_adr;
+
+#ifndef Cdrskin_extra_leaN
+ if(o->adr_trn!=NULL) {
+ hret= Cdradrtrn_translate(o->adr_trn,adr,-1,buf,0);
+ if(hret<=0) {
+ fprintf(stderr,
+ "cdrskin: FATAL : address translation failed (address too long ?) \n");
+ {ret= 0; goto ex;}
+ }
+ adr= buf;
+ }
+#endif /* ! Cdrskin_extra_leaN */
+
+ if(adr[0]=='/') {
+ if(strlen(adr)>=sizeof(o->device_adr)) {
+dev_too_long:;
+ fprintf(stderr,
+ "cdrskin: FATAL : dev=... too long (max. %d characters)\n",
+ sizeof(o->device_adr)-1);
+ {ret= 0; goto ex;}
+ }
+ strcpy(o->device_adr,adr);
+ } else {
+ ret= Cdrpreskin__cdrecord_to_dev(adr,o->device_adr,&driveno,0);
+ if(ret<=0) {
+ fprintf(stderr,
+ "cdrskin: FATAL : dev= expects /dev/xyz, Bus,Target,0 or a number\n");
+ {ret= 0; goto ex;}
+ }
+ }
+ }
+ex:;
+
+#ifndef Cdrskin_extra_leaN
+ if(ret<=0 || !(flag&1))
+ Cdradrtrn_destroy(&(o->adr_trn),0);
+#endif
+
+ return(ret);
+}
+
+
+/* --------------------------------------------------------------------- */
+
+
+
+/** Verbosity level for pacifying progress messages */
+#define Cdrskin_verbose_progresS 1
+
+/** Verbosity level for command recognition and execution logging */
+#define Cdrskin_verbose_cmD 2
+
+/** Verbosity level for reporting of debugging messages */
+#define Cdrskin_verbose_debuG 3
+
+
+/** The maximum number of tracks */
+#define Cdrskin_track_maX 99
+
+
+/** Work around the fact that libburn leaves the track input fds open
+ after the track is done. This can hide a few overflow bytes buffered
+ by the fifo-to-libburn pipe which would cause a broken-pipe error
+ if libburn would close that outlet.
+ This macro enables a coarse workaround which tries to read bytes from
+ the track inlets after burning has ended. Probably not a good idea if
+ libburn would close the inlet fds.
+*/
+#define Cdrskin_libburn_leaves_inlet_opeN 1
+
+
+/** List of furter wishes towards libburn:
+ - write mode which does not demand a track size in advance
+ - obtain minimum drive speed (for cdrskin -atip)
+ - obtain MMC profile of inserted media (for cdrskin -v -atip)
+ - a possibility to implement cdrskin -multi
+ - a possibilty to implement cdrskin -reset
+*/
+
+
+/** Limit to prevent int rollovers within libburn as long as not everything is
+ changed to 64 bit off_t : 2 GB minus 800 MB for eventual computations. */
+#define Cdrskin_tracksize_maX 1308622848
+
+
+/* Some constants obtained by hearsay and experiments */
+
+/** The payload speed factor for reporting progress: 1x = 150 kB/s */
+static double Cdrskin_cd_speed_factoR= 150.0*1024.0;
+
+/** The speed conversion factor consumer x-speed to libburn speed as used with
+ burn_drive_set_speed() burn_drive_get_write_speed()
+*/
+static double Cdrskin_libburn_cd_speed_factoR= 176.0;
+
+/** Add-on for burn_drive_set_speed() to accomodate to the slightley oversized
+ speed ideas of my LG DVDRAM GSA-4082B. LITE-ON LTR-48125S tolerates it.
+*/
+static double Cdrskin_libburn_cd_speed_addoN= 50.0;
+
+
+/** The program run control object. Defaults: see Cdrskin_new(). */
+struct CdrskiN {
+
+ /** Settings already interpreted by Cdrpreskin_setup */
+ struct CdrpreskiN *preskin;
+
+ /** Job: what to do, plus some parameters. */
+ int verbosity;
+ double x_speed;
+ int gracetime;
+ int dummy_mode;
+ int single_track;
+
+ int do_devices;
+
+ int do_scanbus;
+
+ int do_checkdrive;
+
+ int do_atip;
+
+ int do_blank;
+ int blank_fast;
+ int 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;
+
+ /** 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= 0;
+ o->x_speed= -1.0;
+ o->gracetime= 0;
+ o->dummy_mode= 0;
+ o->single_track= 0;
+ o->do_devices= 0;
+ o->do_scanbus= 0;
+ o->do_checkdrive= 0;
+ o->do_atip= 0;
+ o->do_blank= 0;
+ o->blank_fast= 0;
+ o->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;
+ 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 flag)
+{
+ strcpy(source_path,skin->source_path);
+ *fixed_size= skin->fixed_size;
+ *padding= skin->padding;
+ *track_type= skin->track_type;
+ *set_by_padsize= skin->set_by_padsize;
+ 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.
+ Extended behavior with Cdrskin_new_api_tesT:
+ If libburn is not restricted to a single persistent address then it
+ gets shutdown and restarted with the wanted drive only. Thus, after
+ this call, libburn is supposed to have open only the grabbed 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
+ @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 */
+
+#ifdef Cdrskin_new_api_tesT
+
+
+ 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));
+
+ 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_new_api_tesT */
+
+ ret= burn_drive_grab(drive,!(flag&2));
+ if(ret==0) {
+ 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) {
+ 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)
+{
+ int wait_grain= 100000,first_status= 1;
+ struct burn_progress p;
+ enum burn_drive_status drive_status= BURN_DRIVE_GRABBING;
+ double start_time,last_time,current_time;
+
+ if(getpid()!=skin->control_pid) {
+ if(skin->verbosity>=Cdrskin_verbose_debuG)
+ ClN(fprintf(stderr,
+ "cdrskin_debug: ABORT : Thread rejected: pid=%d, signum=%d\n",
+ getpid(),signum));
+ return(2); /* do only process the control thread */
+ }
+
+ if(skin->preskin->abort_handler==3)
+ Cleanup_set_handlers(NULL,NULL,2); /* ignore all signals */
+ else if(skin->preskin->abort_handler==4)
+ Cleanup_set_handlers(NULL,NULL,1); /* allow abort */
+ fprintf(stderr,
+ "\ncdrskin: ABORT : Handling started. Please do not press CTRL+C now.\n");
+ if(skin->preskin->abort_handler==3)
+ fprintf(stderr,"cdrskin: ABORT : Trying to ignore any further signals\n");
+
+#ifndef Cdrskin_extra_leaN
+ if(skin->fifo!=NULL)
+ Cdrfifo_close_all(skin->fifo,0);
+#endif
+
+ if(skin->grabbed_drive!=NULL) {
+ drive_status= burn_drive_get_status(skin->grabbed_drive,&p);
+ if(drive_status!=BURN_DRIVE_IDLE && !skin->drive_is_grabbed)
+ skin->drive_is_grabbed= 2;
+ if(drive_status!=BURN_DRIVE_IDLE && !skin->drive_is_busy)
+ skin->drive_is_busy= 2;
+ if(skin->verbosity>=Cdrskin_verbose_debuG)
+ ClN(fprintf(stderr,"cdrskin_debug: ABORT : Drive status: %d\n",
+ (int) drive_status));
+ }
+ if(skin->verbosity>=Cdrskin_verbose_debuG)
+ ClN(fprintf(stderr,
+ "cdrskin_debug: ABORT : drive_is_grabbed=%d , drive_is_busy=%d (%X)\n",
+ skin->drive_is_grabbed,skin->drive_is_busy,
+ (unsigned int) skin->grabbed_drive));
+
+ if(skin->drive_is_grabbed) {
+ if(skin->drive_is_busy && skin->grabbed_drive!=NULL) {
+ if(drive_status==BURN_DRIVE_WRITING || drive_status==BURN_DRIVE_READING) {
+ fprintf(stderr,"cdrskin: ABORT : Trying to cancel drive operation.\n");
+ burn_drive_cancel(skin->grabbed_drive);
+ } else if(drive_status==BURN_DRIVE_GRABBING) {
+
+#ifdef Cdrskin_new_api_tesT
+ 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_new_api_tesT */
+
+ } 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);
+ }
+
+#ifdef Cdrskin_new_api_tesT
+try_to_finish_lib:;
+#endif
+
+ if(skin->lib_is_initialized) {
+ fprintf(stderr,"cdrskin: ABORT : Trying to finish libburn.\n");
+ burn_finish();
+ }
+ fprintf(stderr,
+ "cdrskin: ABORT : Drive is released and library is shut down now.\n");
+ fprintf(stderr,
+ "cdrskin: ABORT : Program done. Even if you do not see a shell prompt.\n");
+ return(1);
+}
+
+
+/** Convert a libburn device address into a libburn drive number
+ @return <=0 error, 1 success
+*/
+int Cdrskin_driveno_of_location(struct CdrskiN *skin, char *devicename,
+ int *driveno, int flag)
+{
+ int i,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,0);
+ if(ret<=0) {
+wrong_devno:;
+ if(skin->n_drives<=0) {
+ fprintf(stderr,"cdrskin: FATAL : No accessible drives.\n");
+ } else {
+ fprintf(stderr,
+ "cdrskin: FATAL : Address does not lead to an accessible drive: %s\n",
+ in_adr);
+ fprintf(stderr,
+ "cdrskin: HINT : dev= expects /dev/xyz, Bus,Target,0 or a number [0,%d]\n",
+ skin->n_drives-1);
+ }
+ return(0);
+ }
+ if(strlen(synthetic_adr)>0) {
+ if(skin->verbosity>=Cdrskin_verbose_cmD)
+ printf("cdrskin: converted address '%s' to '%s'\n",adr,synthetic_adr);
+ ret= Cdrskin_driveno_of_location(skin,synthetic_adr,driveno,0);
+ if(ret<=0) {
+ fprintf(stderr,
+ "cdrskin: failure while using address converted from '%s'\n",adr);
+ adr= synthetic_adr;
+ goto location_not_found;
+ }
+ }
+ if((*driveno)>=skin->n_drives || (*driveno)<0) {
+ fprintf(stderr,"cdrskin: obtained drive number %d from '%s'\n",
+ *driveno,adr);
+ goto wrong_devno;
+ }
+ return(1);
+}
+
+
+/** Convert a libburn drive number into a cdrecord-style address which
+ represents a device address if possible and the drive number else.
+ @param flag Bitfield for control purposes:
+ bit0= do not apply user defined address translation
+ @return <0 error, 0 drive number, 1 /dev/sgN, 2 /dev/hdX
+*/
+int Cdrskin_driveno_to_btldev(struct CdrskiN *skin, int driveno,
+ char btldev[Cdrskin_adrleN], int flag)
+{
+ int k,ret,still_untranslated= 1,hret;
+ char *loc= NULL,buf[Cdrskin_adrleN],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;
+#endif
+
+ if(loc==NULL)
+ goto fallback;
+ if(strncmp(loc,"/dev/sg",7)==0) {
+ for(k= 7;loc[k]!=0;k++)
+ if(loc[k]<'0' || loc[k]>'9')
+ break;
+ if(loc[k]==0 && k>7) {
+ sprintf(btldev,"1,%s,0",loc+7);
+ {ret= 1; goto ex;}
+ }
+ }
+ if(strncmp(loc,"/dev/hd",7)==0)
+ if(loc[7]>='a' && loc[7]<='z')
+ if(loc[8]==0) {
+ sprintf(btldev,"2,%d,0",loc[7]-'a');
+ {ret= 2; goto ex;}
+ }
+fallback:;
+ sprintf(btldev,"0,%d,0",driveno);
+ ret= 0;
+ex:;
+
+#ifndef Cdrskin_extra_leaN
+ /* user defined address translation */
+ if(!(flag&1)) {
+ if((ret==1 || ret==2)) {
+ /* try wether a translation points to loc */
+ hret= Cdradrtrn_translate(skin->adr_trn,loc,driveno,buf,1);
+ if(hret==2) {
+ still_untranslated= 0;
+ strcpy(btldev,buf);
+ }
+ }
+ if(still_untranslated)
+ Cdradrtrn_translate(skin->adr_trn,btldev,driveno,buf,1);
+ strcpy(btldev,buf);
+ }
+#endif /* ! Cdrskin_extra_leaN */
+
+ return(ret);
+}
+
+
+/** Report media status s to the user */
+int Cdrskin_report_disc_status(struct CdrskiN *skin, enum burn_disc_status s,
+ int flag)
+{
+ printf("cdrskin: status %d ",s);
+ if (s==BURN_DISC_FULL) {
+ printf("burn_disc_full \"There is a disc with data on it in the drive\"\n");
+ } else if(s==BURN_DISC_BLANK) {
+ printf("burn_disc_blank \"The drive holds a blank disc\"\n");
+ } else if(s==BURN_DISC_APPENDABLE) {
+ printf(
+ "BURN_DISC_APPENDABLE \"There is an incomplete disc in the drive\"\n");
+ } else if(s==BURN_DISC_EMPTY) {
+ printf("BURN_DISC_EMPTY \"There is no disc at all in the drive\"\n");
+ } else
+ printf("-unknown status code-\n");
+ return(1);
+}
+
+
+/** Perform operations -scanbus or --devices
+ @param flag Bitfield for control purposes:
+ bit0= perform --devices rather than -scanbus
+ @return <=0 error, 1 success
+*/
+int Cdrskin_scanbus(struct CdrskiN *skin, int flag)
+{
+ int ret,i,busno,first_on_bus;
+ char shellsafe[5*Cdrskin_strleN+2],perms[40],btldev[Cdrskin_adrleN];
+ char adr[Cdrskin_adrleN];
+ struct stat stbuf;
+
+ if(flag&1) {
+ printf("cdrskin: Overview of accessible drives (%d found) :\n",
+ skin->n_drives);
+ printf("-----------------------------------------------------------------------------\n");
+ for(i=0;in_drives;i++) {
+
+#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 {
+ printf("Using libburn version '%s'.\n",Cdrskin_libburn_versioN);
+ for(busno= 0;busno<16;busno++) {
+ first_on_bus= 1;
+ for(i=0;in_drives;i++) {
+ ret= Cdrskin_driveno_to_btldev(skin,i,btldev,1);
+ if(ret!=busno)
+ continue;
+ if(first_on_bus)
+ printf("scsibus%d:\n",busno);
+ first_on_bus= 0;
+ printf("\t%s\t %d) '%s' '%s' '%s' Removable CD-ROM\n",
+ btldev,i,skin->drives[i].vendor,skin->drives[i].product,"?");
+ }
+ }
+ }
+ return(1);
+}
+
+
+/** Perform -checkdrive .
+ @param flag Bitfield for control purposes:
+ bit0= do not print message about pseudo-checkdrive
+ @return <=0 error, 1 success
+*/
+int Cdrskin_checkdrive(struct CdrskiN *skin, int flag)
+{
+ struct burn_drive_info *drive_info;
+ int ret;
+ char btldev[Cdrskin_adrleN];
+
+ if(!(flag&1))
+ printf("cdrskin: pseudo-checkdrive on drive %d\n",skin->driveno);
+ if(skin->driveno>=skin->n_drives || skin->driveno<0) {
+ fprintf(stderr,"cdrskin: FATAL : there is no drive #%d\n",skin->driveno);
+ {ret= 0; goto ex;}
+ }
+ drive_info= &(skin->drives[skin->driveno]);
+ ret= Cdrskin_driveno_to_btldev(skin,skin->driveno,btldev,0);
+ if(ret>=0)
+ fprintf(stderr,"scsidev: '%s'\n",btldev);
+ printf("Device type : %s\n","Removable CD-ROM");
+ printf("Vendor_info : '%s'\n",drive_info->vendor);
+ printf("Identifikation : '%s'\n",drive_info->product);
+ printf("Driver flags : %s\n","BURNFREE");
+ printf("Supported modes: %s\n","SAO RAW/R96R");
+ ret= 1;
+ex:;
+ return(ret);
+}
+
+
+/** Perform -atip .
+ @return <=0 error, 1 success
+*/
+int Cdrskin_atip(struct CdrskiN *skin, int flag)
+{
+ int ret,is_not_really_erasable= 0;
+ double x_speed;
+ enum burn_disc_status s;
+ struct burn_drive *drive;
+
+ printf("cdrskin: pseudo-atip on drive %d\n",skin->driveno);
+ ret= Cdrskin_checkdrive(skin,1);
+ if(ret<=0)
+ return(ret);
+ ret= Cdrskin_grab_drive(skin,0);
+ if(ret<=0)
+ return(ret);
+ drive= skin->drives[skin->driveno].drive;
+ while(burn_drive_get_status(drive,NULL))
+ 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"));
+
+
+#ifdef Cdrskin_new_api_tesT
+
+ if(skin->verbosity>=Cdrskin_verbose_debuG)
+ ClN(fprintf(stderr,
+ "cdrskin_debug: Cdrskin_atip() on Cdrskin_new_api_tesT\n"));
+
+ 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_new_api_tesT */
+
+ 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_new_api_tesT */
+
+#endif /* Cdrskin_atip_speed_brokeN */
+
+
+ ret= burn_drive_get_write_speed(drive);
+ x_speed= ((double) ret)/Cdrskin_libburn_cd_speed_factoR;
+ printf("cdrskin: burn_drive_get_write_speed = %d (%.1fx)\n",ret,x_speed);
+ if(skin->verbosity>=Cdrskin_verbose_progresS) {
+ if(burn_disc_erasable(drive))
+ printf("Current: CD-RW\n");
+ else
+ printf("Current: CD-R\n");
+ }
+ printf("ATIP info from disk:\n");
+ if(burn_disc_erasable(drive)) {
+ if(is_not_really_erasable)
+ printf(" Is erasable (but not while in this incomplete state)\n");
+ else
+ printf(" Is erasable\n");
+ } else {
+ printf(" Is not erasable\n");
+ }
+ printf(" 1T speed low: %.f 1T speed high: %.f\n",x_speed,x_speed);
+ ret= 1;
+ex:;
+ Cdrskin_release_drive(skin,0);
+ return(ret);
+}
+
+
+#ifndef Cdrskin_extra_leaN
+
+/** Emulate the gracetime= behavior of cdrecord
+ @param flag Bitfield for control purposes:
+ bit0= do not print message about pseudo-checkdrive
+*/
+int Cdrskin_wait_before_action(struct CdrskiN *skin, int flag)
+/* flag: bit0= BLANK rather than write mode */
+{
+ int i;
+
+ if(skin->verbosity>=Cdrskin_verbose_progresS) {
+ char speed_text[80];
+ if(skin->x_speed<0)
+ strcpy(speed_text,"MAX");
+ else if(skin->x_speed==0)
+ strcpy(speed_text,"MIN");
+ else
+ sprintf(speed_text,"%.f",skin->x_speed);
+ printf(
+ "Starting to write CD/DVD at speed %s in real %s mode for single session.\n",
+ speed_text,(flag&1?"BLANK":skin->write_mode_name));
+ printf("Last chance to quit, starting real write in %3d seconds.",
+ skin->gracetime);
+ fflush(stdout);
+ }
+ for(i= skin->gracetime-1;i>=0;i--) {
+ usleep(1000000);
+ if(skin->verbosity>=Cdrskin_verbose_progresS) {
+ printf("\b\b\b\b\b\b\b\b\b\b\b\b\b %3d seconds.",i);
+ fflush(stdout);
+ }
+ }
+ if(skin->verbosity>=Cdrskin_verbose_progresS)
+ printf(" Operation starts.\n");
+ return(1);
+}
+
+#endif /* Cdrskin_extra_leaN */
+
+
+/** Perform blank=[all|fast]
+ @return <=0 error, 1 success
+*/
+int Cdrskin_blank(struct CdrskiN *skin, int flag)
+{
+ enum burn_disc_status s;
+ struct burn_progress p;
+ struct burn_drive *drive;
+ int ret,loop_counter= 0;
+ double start_time;
+
+ start_time= Sfile_microtime(0); /* will be refreshed later */
+ ret= Cdrskin_grab_drive(skin,0);
+ if(ret<=0)
+ return(ret);
+ drive= skin->drives[skin->driveno].drive;
+
+ while(burn_drive_get_status(drive,NULL))
+ 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)
+
+#ifdef Cdrskin_libburn_p_sectoR
+ fprintf(stderr,
+ "\rcdrskin: blanking sector %d (%lu seconds elapsed) ",
+ p.sector,(unsigned long) (Sfile_microtime(0)-start_time));
+#else /* Cdrskin_libburn_p_sectoR */
+ fprintf(stderr,
+ "\rcdrskin: blanking sector %d (%lu seconds elapsed) ",
+ p.current_sector,(unsigned long) (Sfile_microtime(0)-start_time));
+#endif /* ! Cdrskin_libburn_p_sectoR */
+
+ sleep(2);
+ loop_counter++;
+ }
+blanking_done:;
+ skin->drive_is_busy= 0;
+ if(skin->verbosity>=Cdrskin_verbose_progresS) {
+ fprintf(stderr,"\n");
+ printf("Blanking time: %.3fs\n",Sfile_microtime(0)-start_time);
+ fprintf(stderr,"cdrskin: blanking done\n");
+ }
+ Cdrskin_release_drive(skin,0);
+ return(1);
+}
+
+
+/** Report burn progress. This is done partially in cdrecord style.
+ Actual reporting happens only if write progress hit the next MB or if in
+ non-write-progress states a second has elapsed since the last report.
+ After an actual report a new statistics interval begins.
+ @param drive_status As obtained from burn_drive_get_status()
+ @param p Progress information from burn_drive_get_status()
+ @param start_time Timestamp of burn start in seconds
+ @param last_time Timestamp of report interval start in seconds
+ @param total_count Returns the total number of bytes written so far
+ @param total_count Returns the number of bytes written during interval
+ @param flag Bitfield for control purposes:
+ bit0= report in growisofs style rather than cdrecord style
+ @return <=0 error, 1 seems to be writing payload, 2 doing something else
+*/
+int Cdrskin_burn_pacifier(struct CdrskiN *skin,
+ enum burn_drive_status drive_status,
+ struct burn_progress *p,
+ double start_time, double *last_time,
+ double *total_count, double *last_count, int flag)
+/*
+ bit0= growisofs style
+*/
+{
+ double bytes_to_write,written_bytes= 0.0,written_total_bytes= 0.0,buffer_size;
+ double fixed_size,padding;
+ double measured_total_speed,measured_speed;
+ double elapsed_time,elapsed_total_time,current_time;
+ double estim_time,estim_minutes,estim_seconds,percent;
+ int ret,fifo_percent,fill,space,advance_interval=0,new_mb,old_mb,time_to_tell;
+ int fs,bs,old_track_idx;
+ char fifo_text[80],mb_text[40];
+ 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;
+
+ bytes_to_write= ((double) p->sectors)*2048.0;
+
+#ifdef Cdrskin_libburn_p_sectoR
+ written_total_bytes= ((double) p->sector)*2048.0;
+#else /* Cdrskin_libburn_p_sectoR */
+ written_total_bytes= ((double) p->current_sector)*2048.0;
+#endif /* ! Cdrskin_libburn_p_sectoR */
+
+ written_bytes= written_total_bytes-*last_count;
+
+ 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,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/2048.0);
+ }
+
+ if(written_total_bytes<1024*1024) {
+thank_you_for_patience:;
+ if(time_to_tell || skin->is_writing) {
+ if(skin->verbosity>=Cdrskin_verbose_progresS) {
+ if(skin->is_writing)
+ fprintf(stderr,"\n");
+ fprintf(stderr,
+ "\rcdrskin: thank you for being patient since %.f seconds ",
+ elapsed_total_time);
+ }
+ skin->is_writing= 0;
+ advance_interval= 1;
+ }
+ {ret= 2; goto ex;}
+ }
+ new_mb= written_total_bytes/(1024*1024);
+ old_mb= (*last_count)/(1024*1024);
+ if(new_mb==old_mb && !(written_total_bytes>=skin->fixed_size &&
+ skin->fixed_size>0 && time_to_tell))
+ {ret= 1; goto ex;}
+
+
+#ifndef Cdrskin_extra_leaN
+
+ percent= 0.0;
+ if(bytes_to_write>0)
+ percent= written_total_bytes/bytes_to_write*100.0;
+ measured_total_speed= 0.0;
+ measured_speed= 0.0;
+ estim_time= -1.0;
+ estim_minutes= -1.0;
+ estim_seconds= -1.0;
+ if(elapsed_total_time>0.0) {
+ measured_total_speed= written_total_bytes/elapsed_total_time;
+ estim_time= (bytes_to_write-written_bytes)/measured_total_speed;
+ if(estim_time>0.0 && estim_time<86400.0) {
+ estim_minutes= ((int) estim_time)/60;
+ estim_seconds= estim_time-estim_minutes*60.0;
+ if(estim_seconds<0.0)
+ estim_seconds= 0.0;
+ }
+ }
+ if(elapsed_time>0.0)
+ measured_speed= written_bytes/elapsed_time;
+ if(measured_speed<=0.0 && written_total_bytes>=skin->fixed_size &&
+ skin->fixed_size>0) {
+ if(!skin->is_writing)
+ goto thank_you_for_patience;
+ skin->is_writing= 0;
+ measured_speed= measured_total_speed;
+ } else
+ skin->is_writing= 1;
+ if(skin->supposed_track_idx<0)
+ skin->supposed_track_idx= 0;
+ if(*last_count<=0.0)
+ printf("%-78.78s\r","");
+ if(skin->verbosity>=Cdrskin_verbose_progresS) {
+ if(flag&1) {
+ printf("%.f/%.f (%2.1f%%) @%1.1f, remaining %.f:%2.2d\n",
+ written_total_bytes,bytes_to_write,percent,
+ measured_speed/Cdrskin_cd_speed_factoR,
+ estim_minutes,(int) estim_seconds);
+ } else {
+ fill= 0;
+ fifo_percent= 50;
+ fifo_text[0]= 0;
+ curr_fifo_in= last_fifo_in;
+ curr_fifo_out= last_fifo_out;
+ if(skin->fifo!=NULL) {
+ ret= Cdrfifo_get_buffer_state(skin->fifo,&fill,&space,0);
+ buffer_size= fill+space;
+ if(ret==2 || ret==0) {
+ fifo_percent= 100;
+ } else if(ret>0 && buffer_size>0.0) {
+ /* obtain minimum fill of pacifier interval */
+ Cdrfifo_next_interval(skin->fifo,&fill,0);
+ fifo_percent= 100.0*((double) fill)/buffer_size;
+ if(fifo_percent<100 && fill>0)
+ fifo_percent++;
+ }
+ if(skin->verbosity>=Cdrskin_verbose_debuG) {
+ Cdrfifo_get_counters(skin->fifo,&curr_fifo_in,&curr_fifo_out,0);
+ Cdrfifo_get_sizes(skin->fifo,&bs,&fs,0);
+ }
+ }
+ if(skin->fifo_size>0) {
+ sprintf(fifo_text,"(fifo %3d%%) ",fifo_percent);
+ if(skin->verbosity>=Cdrskin_verbose_debuG) {
+ fprintf(stderr,
+ "\ncdrskin_debug: fifo >= %9d / %d : %8.f in, %8.f out\n",
+ fill,space+fill,
+ curr_fifo_in-last_fifo_in,curr_fifo_out-last_fifo_out);
+ last_fifo_in= curr_fifo_in;
+ last_fifo_out= curr_fifo_out;
+ }
+ }
+ if(skin->supposed_track_idx >= 0 &&
+ skin->supposed_track_idx < skin->track_counter) {
+ Cdrtrack_get_size(skin->tracklist[skin->supposed_track_idx],
+ &fixed_size,&padding,0);
+ } else if(skin->fixed_size!=0) {
+ fixed_size= skin->fixed_size;
+ padding= skin->padding;
+ }
+ if(fixed_size) {
+ sprintf(mb_text,"%4d of %4d",
+ (int) (written_total_bytes/1024.0/1024.0),
+ (int) ((fixed_size+padding)/1024.0/1024.0));
+ } else
+ sprintf(mb_text,"%4d",(int) (written_total_bytes/1024.0/1024.0));
+ printf("\r%sTrack %-2.2d: %s MB written %s[buf 50%%] %4.1fx.",
+ debug_mark,skin->supposed_track_idx+1,mb_text,fifo_text,
+ measured_speed/Cdrskin_cd_speed_factoR);
+ fflush(stdout);
+ }
+ if(skin->is_writing==0) {
+ printf("\n");
+ goto thank_you_for_patience;
+ }
+ }
+
+#else /* ! Cdrskin_extra_leaN */
+
+ if(skin->supposed_track_idx<0)
+ skin->supposed_track_idx= 0;
+ if(written_bytes<=0.0 && written_total_bytes>=skin->fixed_size &&
+ skin->fixed_size>0) {
+ if(!skin->is_writing)
+ goto thank_you_for_patience;
+ skin->is_writing= 0;
+ } else {
+ if(!skin->is_writing)
+ printf("\n");
+ skin->is_writing= 1;
+ }
+ printf("\rTrack %-2.2d: %3d MB written ",
+ skin->supposed_track_idx+1,(int) (written_total_bytes/1024.0/1024.0));
+ fflush(stdout);
+ if(skin->is_writing==0)
+ printf("\n");
+
+#endif /* Cdrskin_extra_leaN */
+
+
+ advance_interval= 1;
+ ret= 1;
+ex:;
+ if(advance_interval) {
+ if(written_total_bytes>0)
+ *last_count= written_total_bytes;
+ else
+ *last_count= 0.0;
+ if(*last_count>*total_count)
+ *total_count= *last_count;
+ *last_time= current_time;
+ }
+ return(ret);
+}
+
+
+/** Burn data via libburn according to the parameters set in skin.
+ @return <=0 error, 1 success
+*/
+int Cdrskin_burn(struct CdrskiN *skin, int flag)
+{
+ struct burn_disc *disc;
+ struct burn_session *session;
+ struct burn_write_opts *o;
+ enum burn_disc_status s;
+ enum burn_drive_status drive_status;
+ struct burn_progress p;
+ struct burn_drive *drive;
+ int ret,loop_counter= 0,max_track= -1,i,hflag;
+ int fifo_disabled= 0,fifo_percent,total_min_fill,mb;
+ double put_counter,get_counter,empty_counter,full_counter;
+ double start_time,last_time;
+ double total_count= 0.0,last_count= 0.0,size,padding;
+
+ printf("cdrskin: beginning to burn disk\n");
+
+ disc= burn_disc_create();
+ session= burn_session_create();
+ burn_disc_add_session(disc,session,BURN_POS_END);
+
+ skin->fixed_size= 0.0;
+ for(i=0;itrack_counter;i++) {
+ hflag= (skin->verbosity>=Cdrskin_verbose_debuG);
+#ifdef Cdrskin_burn_track_padding_brokeN
+ hflag|= 2;
+#endif
+ 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,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))
+ 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,0);
+ if(size<=0) {
+ printf("Track %-2.2d: data unknown length",i+1);
+ } else {
+ mb= size/1024.0/1024.0;
+ printf("Track %-2.2d: data %5d MB ",i+1,mb);
+ }
+ if(padding>0)
+ printf(" padsize: %.f KB\n",padding/1024.0);
+ else
+ printf("\n");
+ }
+ if(skin->fixed_size<=0) {
+ printf("Total size: 0 MB (00:00.00) = 0 sectors\n");
+ printf("Lout start: 0 MB (00:02/00) = 0 sectors\n");
+ } else {
+ /* >>> This is quite a fake. Need to learn about 12:35.25 and "Lout"
+ ??? Is there a way to obtain the toc in advance (print_cue()) ? */
+ double seconds;
+ int min,sec,frac;
+
+ mb= skin->fixed_size/1024.0/1024.0;
+ seconds= skin->fixed_size/150.0/1024.0+2.0;
+ min= seconds/60.0;
+ sec= seconds-min*60;
+ frac= (seconds-min*60-sec)*100;
+ if(frac>99)
+ frac= 99;
+ printf("Total size: %5d MB (%-2.2d:%-2.2d.%-2.2d) = %d sectors\n",
+ mb,min,sec,frac,(int) (skin->fixed_size/2048));
+ seconds+= 2;
+ min= seconds/60.0;
+ sec= seconds-min*60;
+ frac= (seconds-min*60-sec)*100;
+ if(frac>99)
+ frac= 99;
+ printf("Lout start: %5d MB (%-2.2d:%-2.2d/%-2.2d) = %d sectors\n",
+ mb,min,sec,frac,(int) (skin->fixed_size/2048));
+ }
+ }
+
+ Cdrskin_wait_before_action(skin,0);
+ ret= Cdrskin_fill_fifo(skin,0);
+ if(ret<=0) {
+ fprintf(stderr,"cdrskin: FATAL : filling of fifo failed\n");
+ goto ex;
+ }
+
+#endif /* ! Cdrskin_extra_leaN */
+
+
+ o= burn_write_opts_new(drive);
+ burn_write_opts_set_perform_opc(o, 0);
+
+ burn_write_opts_set_write_type(o,skin->write_type,skin->block_type);
+ if(skin->dummy_mode) {
+ fprintf(stderr,
+ "cdrskin: NOTE : -dummy mode will prevent actual writing\n");
+ burn_write_opts_set_simulate(o, 1);
+ }
+ burn_write_opts_set_underrun_proof(o,skin->burnfree);
+
+ Cdrskin_adjust_speed(skin,0);
+ if(skin->verbosity>=Cdrskin_verbose_progresS)
+ printf("Starting new track at sector: 0\n");
+ skin->drive_is_busy= 1;
+ burn_disc_write(o, disc);
+ if(skin->preskin->abort_handler==-1)
+ Cleanup_set_handlers(skin,(Cleanup_app_handler_T) Cdrskin_abort_handler,4);
+ start_time= Sfile_microtime(0);
+
+ burn_write_opts_free(o);
+
+ while (burn_drive_get_status(drive, NULL) == BURN_DRIVE_SPAWNING) {
+
+ /* >>> how do i learn about success or failure ? */
+
+ ;
+ }
+ loop_counter= 0;
+ while (1) {
+ drive_status= burn_drive_get_status(drive, &p);
+ if(drive_status==BURN_DRIVE_IDLE)
+ break;
+
+ /* >>> how do i learn about success or failure ? */
+
+ if(loop_counter>0)
+ Cdrskin_burn_pacifier(skin,drive_status,&p,start_time,&last_time,
+ &total_count,&last_count,0);
+
+
+ /* <<< debugging : artificial abort without a previous signal */;
+ if(skin->abort_after_bytecount>=0.0 &&
+ total_count>=skin->abort_after_bytecount) {
+ /* whatever signal handling is installed: this thread is the boss now */
+ fprintf(stderr,
+ "cdrskin: DEVELOPMENT : synthetic abort by abort_after_bytecount=%.f\n",
+ skin->abort_after_bytecount);
+ skin->control_pid= getpid();
+ ret= Cdrskin_abort_handler(skin,0,0);
+ fprintf(stderr,"cdrskin: done (aborted)\n");
+ exit(1);
+ }
+
+
+ if(max_tracksupposed_track_idx)
+ max_track= skin->supposed_track_idx;
+
+#ifndef Cdrskin_extra_leaN
+ if(skin->fifo==NULL || fifo_disabled) {
+ usleep(20000);
+ } else {
+ ret= Cdrfifo_try_to_work(skin->fifo,20000,NULL,NULL,0);
+ if(ret<0) {
+ int abh;
+
+ abh= skin->preskin->abort_handler;
+ if(abh!=2)
+ fprintf(stderr,
+ "\ncdrskin: FATAL : fifo encountered error during burn loop.\n");
+ if(abh==0) {
+ ret= -1; goto ex;
+ } else if(abh==1 || abh==3 || abh==4 || abh==-1) {
+ Cdrskin_abort_handler(skin,0,0);
+ fprintf(stderr,"cdrskin: done (aborted)\n");
+ exit(10);
+ } else {
+ if(skin->verbosity>=Cdrskin_verbose_debuG)
+ fprintf(stderr,
+ "\ncdrskin_debug: Cdrfifo_try_to_work() returns %d\n",ret);
+ }
+ }
+ if(ret==2) { /* <0 = error , 2 = work is done */
+ if(skin->verbosity>=Cdrskin_verbose_debuG)
+ fprintf(stderr,"\ncdrskin_debug: fifo ended work with ret=%d\n",ret);
+ fifo_disabled= 1;
+ }
+ }
+#else /* ! Cdrskin_extra_leaN */
+ usleep(20000);
+#endif /* Cdrskin_extra_leaN */
+
+ loop_counter++;
+ }
+ skin->drive_is_busy= 0;
+ if(skin->verbosity>=Cdrskin_verbose_progresS)
+ printf("\n");
+ if(max_track<=0) {
+ printf("Track 01: Total bytes read/written: %.f/%.f (%.f sectors).\n",
+ total_count,total_count,total_count/2048.0);
+ } else {
+ Cdrtrack_get_size(skin->tracklist[max_track],&size,&padding,0);
+ printf(
+ "Track %-2.2d: Total bytes read/written: %.f/%.f (%.f sectors).\n",
+ max_track+1,size,size,size/2048.0);
+ }
+ if(skin->verbosity>=Cdrskin_verbose_progresS)
+ printf("Writing time: %.3fs\n",Sfile_microtime(0)-start_time);
+
+
+#ifndef Cdrskin_extra_leaN
+
+ if(skin->fifo!=NULL && skin->fifo_size>0) {
+ int dummy,final_fill;
+ Cdrfifo_get_buffer_state(skin->fifo,&final_fill,&dummy,0);
+ if(final_fill>0) {
+fifo_full_at_end:;
+ fprintf(stderr,
+ "cdrskin: FATAL : Fifo still contains data after burning has ended.\n");
+ fprintf(stderr,
+ "cdrskin: FATAL : %.d bytes left.\n",final_fill);
+ fprintf(stderr,
+ "cdrskin: FATAL : This indicates an overflow of the last track.\n");
+ fprintf(stderr,
+ "cdrskin: NOTE : The media might appear ok but is probably truncated.\n");
+ ret= -1; goto ex;
+ }
+
+#ifdef Cdrskin_libburn_leaves_inlet_opeN
+ for(i= 0;itrack_counter;i++) {
+ ret= Cdrtrack_has_input_left(skin->tracklist[i],0);
+ if(ret>0) {
+ fprintf(stderr,
+ "cdrskin: FATAL : fifo outlet of track #%d is still buffering some bytes.\n",
+ i+1);
+ goto fifo_full_at_end;
+ }
+ }
+#endif /* Cdrskin_libburn_leaves_inlet_opeN */
+
+ }
+
+ if(skin->verbosity>=Cdrskin_verbose_progresS) {
+ if(skin->fifo!=NULL && skin->fifo_size>0) {
+ int dummy;
+
+ Cdrfifo_get_min_fill(skin->fifo,&total_min_fill,&dummy,0);
+ fifo_percent= 100.0*((double) total_min_fill)/(double) skin->fifo_size;
+ if(fifo_percent==0 && total_min_fill>0)
+ fifo_percent= 1;
+ Cdrfifo_get_cdr_counters(skin->fifo,&put_counter,&get_counter,
+ &empty_counter,&full_counter,0);
+ fflush(stdout);
+ fprintf(stderr,"Cdrskin: fifo had %.f puts and %.f gets.\n",
+ put_counter,get_counter);
+ fprintf(stderr,
+ "Cdrskin: fifo was %.f times empty and %.f times full, min fill was %d%%.\n",
+ empty_counter,full_counter,fifo_percent);
+ }
+ printf("Min drive buffer fill was 50%%\n");
+ }
+
+#endif /* ! Cdrskin_extra_leaN */
+
+
+ if(skin->verbosity>=Cdrskin_verbose_progresS)
+ printf("cdrskin: burning done\n");
+ ret= 1;
+ex:;
+ skin->drive_is_busy= 0;
+ if(skin->verbosity>=Cdrskin_verbose_debuG)
+ ClN(printf("cdrskin_debug: do_eject= %d\n",skin->do_eject));
+ Cdrskin_release_drive(skin,0);
+ for(i= 0;itrack_counter;i++)
+ Cdrtrack_cleanup(skin->tracklist[i],0);
+ burn_session_free(session);
+ burn_disc_free(disc);
+ return(ret);
+}
+
+
+/** Work around the failure of libburn to eject the tray.
+ This employs a system(2) call and is therefore an absolute no-no for any
+ pseudo user identities.
+ @return <=0 error, 1 success
+*/
+int Cdrskin_eject(struct CdrskiN *skin, int flag)
+{
+
+#ifndef Cdrskin_burn_drive_eject_brokeN
+
+ 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);
+
+#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", "-toc", "-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", "-swab", "-packet", "-noclose",
+ ""
+ };
+
+ /* are we pretending to be cdrecord ? */
+ cpt= strrchr(argv[0],'/');
+ if(cpt==NULL)
+ cpt= argv[0];
+ else
+ cpt++;
+ if(strcmp(cpt,"cdrecord")==0 && !(flag&1)) {
+ fprintf(stderr,"\n");
+ fprintf(stderr,
+ "Note: This is not cdrecord by Joerg Schilling. Do not bother him.\n");
+ fprintf(stderr,
+ " See cdrskin start message on stdout. See --help. See -version.\n");
+ fprintf(stderr,"\n");
+ /* allow automatic -tao to -sao redirection */
+ skin->tao_to_sao_tsize=650*1024*1024;
+ }
+
+#ifndef Cdrskin_extra_leaN
+ if(!(flag&2)) {
+ if(skin->preskin->pre_argc>1) {
+ ret= Cdrskin_setup(skin,skin->preskin->pre_argc,skin->preskin->pre_argv,
+ flag|1|2);
+ if(ret<=0)
+ return(ret);
+ }
+ }
+#endif
+
+ for (i= 1;iabort_after_bytecount= 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) {
+ skin->do_atip= 1;
+ if(skin->verbosity>=Cdrskin_verbose_cmD)
+ printf("cdrskin: will put out some -atip style line\n");
+
+ } else if(strcmp(argv[i],"-audio")==0) {
+ skin->track_type= BURN_AUDIO;
+
+ } 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) {
+ /* is 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;
+
+ } 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_rc")==0) {
+ /* is handled in Cdrpreskin_setup() */;
+
+ } else if(strcmp(argv[i],"-nopad")==0) {
+ skin->padding= 0.0;
+ if(skin->verbosity>=Cdrskin_verbose_cmD)
+ printf("cdrskin: padding : off\n");
+
+ } else if(strcmp(argv[i],"-pad")==0) {
+ skin->padding= 15*2048;
+ skin->set_by_padsize= 0;
+ if(skin->verbosity>=Cdrskin_verbose_cmD)
+ printf("cdrskin: padding : %.f\n",skin->padding);
+
+ } else if(strncmp(argv[i],"-padsize=",9)==0) {
+ value_pt= argv[i]+9;
+ goto set_padsize;
+ } else if(strncmp(argv[i],"padsize=",8)==0) {
+ value_pt= argv[i]+8;
+set_padsize:;
+ skin->padding= Scanf_io_size(argv[i]+8,0);
+ skin->set_by_padsize= 1;
+ if(skin->verbosity>=Cdrskin_verbose_cmD)
+ printf("cdrskin: padding : %.f\n",skin->padding);
+
+ } else if(strcmp(argv[i],"-raw96r")==0) {
+ strcpy(skin->write_mode_name,"RAW/RAW96R");
+ skin->write_type= BURN_WRITE_RAW;
+ skin->block_type= BURN_BLOCK_RAW96R;
+ if(skin->verbosity>=Cdrskin_verbose_cmD)
+ printf("cdrskin: write type : RAW/RAW96R\n");
+
+ } else if(strcmp(argv[i],"-sao")==0 || strcmp(argv[i],"-dao")==0) {
+set_sao:;
+ strcpy(skin->write_mode_name,"SAO");
+ skin->write_type= BURN_WRITE_SAO;
+ skin->block_type= BURN_BLOCK_SAO;
+ if(skin->verbosity>=Cdrskin_verbose_cmD)
+ printf("cdrskin: write type : SAO\n");
+
+ } else if(strcmp(argv[i],"-scanbus")==0) {
+ skin->do_scanbus= 1;
+
+ } else if(strcmp(argv[i],"--single_track")==0) {
+ skin->single_track= 1;
+ if(skin->verbosity>=Cdrskin_verbose_cmD)
+ printf(
+ "cdrskin: --single_track : will only accept last argument as track source\n");
+
+ } else if(strncmp(argv[i],"-speed=",7)==0) {
+ value_pt= argv[i]+7;
+ goto set_speed;
+ } else if(strncmp(argv[i],"speed=",6)==0) {
+ value_pt= argv[i]+6;
+set_speed:;
+ sscanf(value_pt,"%lf",&(skin->x_speed));
+ if(skin->x_speed<1.0 && skin->x_speed!=0.0 && skin->x_speed!=-1) {
+ fprintf(stderr,"cdrskin: FATAL : speed= must be -1, 0 or at least 1\n");
+ return(0);
+ }
+
+ /* >>> cdrecord speed=0 -> minimum speed , libburn -> maximum speed */;
+
+ if(skin->verbosity>=Cdrskin_verbose_cmD)
+ printf("cdrskin: speed : %f\n",skin->x_speed);
+
+ } else if(strcmp(argv[i],"-tao")==0) {
+ if(skin->tao_to_sao_tsize<=0.0) {
+ fprintf(stderr,"cdrskin: FATAL : libburn does not support -tao yet.\n");
+ fprintf(stderr,"cdrskin: HINT : Try option tao_to_sao_tsize=650m\n");
+ return(0);
+ }
+ printf("cdrskin: NOTE : substituting mode -tao by mode -sao\n");
+ goto set_sao;
+
+ } else if(strncmp(argv[i],"tao_to_sao_tsize=",17)==0) {
+ skin->tao_to_sao_tsize= Scanf_io_size(argv[i]+17,0);
+ if(skin->tao_to_sao_tsize>Cdrskin_tracksize_maX)
+ goto track_too_large;
+ if(skin->verbosity>=Cdrskin_verbose_cmD)
+ printf("cdrskin: replace -tao by -sao with fixed size : %.f\n",
+ skin->tao_to_sao_tsize);
+
+ } else if(strncmp(argv[i],"-tsize=",7)==0) {
+ value_pt= argv[i]+7;
+ goto set_tsize;
+ } else if(strncmp(argv[i],"tsize=",6)==0) {
+ value_pt= argv[i]+6;
+set_tsize:;
+ skin->fixed_size= Scanf_io_size(value_pt,0);
+ if(skin->fixed_size>Cdrskin_tracksize_maX) {
+track_too_large:;
+ fprintf(stderr,"cdrskin: FATAL : track size too large\n");
+ return(0);
+ }
+ if(skin->verbosity>=Cdrskin_verbose_cmD)
+ printf("cdrskin: fixed track size : %.f\n",skin->fixed_size);
+
+ } else if(strcmp(argv[i],"-v")==0 || strcmp(argv[i],"-verbose")==0) {
+ (skin->verbosity)++;
+ /* <<< is much too verbous: burn_set_verbosity(skin->verbosity); */
+ printf("cdrskin: verbosity level : %d\n",skin->verbosity);
+
+ } else if( i==argc-1 ||
+ (skin->single_track==0 && strchr(argv[i],'=')==NULL
+ && !(argv[i][0]=='-' && argv[i][1]!=0) ) ||
+ (skin->single_track==-1)) {
+ if(strlen(argv[i])>=sizeof(skin->source_path)) {
+ fprintf(stderr,
+ "cdrskin: FATAL : source_address too long. (max: %d, given: %d)\n",
+ sizeof(skin->source_path)-1,strlen(argv[i]));
+ return(0);
+ }
+ strcpy(skin->source_path,argv[i]);
+ if(strcmp(skin->source_path,"-")==0) {
+ if(skin->stdin_source_used) {
+ fprintf(stderr,
+ "cdrskin: FATAL : \"-\" (stdin) can be used as track source only once.\n");
+ return(0);
+ }
+ skin->stdin_source_used= 1;
+ if(skin->write_type!=BURN_WRITE_TAO &&
+ skin->fixed_size<=0.0 && skin->tao_to_sao_tsize>0.0) {
+ skin->fixed_size= skin->tao_to_sao_tsize;
+ printf(
+ "cdrskin: NOTE : augmenting non-tao write mode by tao_to_sao_tsize\n");
+ printf("cdrskin: NOTE : fixed size : %.f\n",skin->fixed_size);
+ } else if(skin->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) {
+ ret= Cdrskin_dev_to_driveno(skin,skin->preskin->raw_device_adr,
+ &(skin->driveno),0);
+ if(ret<=0)
+ return(ret);
+ if(skin->verbosity>=Cdrskin_verbose_cmD) {
+
+#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 *lib_initialized, int *exit_value, int flag)
+{
+ int ret;
+ struct CdrskiN *skin;
+
+ *o= NULL;
+ *exit_value= 0;
+ *lib_initialized= 0;
+
+ printf("cdrskin: initializing libburn ...");
+ fflush(stdout);
+ if(burn_initialize()) {
+ printf(" ok\n");
+ fflush(stdout);
+ } else {
+ printf(" failed\n");
+ fflush(stdout);
+ fprintf(stderr,"cdrskin : FATAL : initialization of libburn failed\n");
+ {*exit_value= 11; goto ex;}
+ }
+ *lib_initialized= 1;
+
+#ifndef Cdrskin_libburn_no_burn_preset_device_opeN
+ burn_preset_device_open((*preskin)->drive_exclusive,
+ (*preskin)->drive_blocking,
+ (*preskin)->abort_on_busy_drive);
+#endif
+
+ if(strlen((*preskin)->device_adr)>0) { /* disable scan for all others */
+ printf("cdrskin: NOTE : greying out all drives besides given dev='%s'\n",
+ (*preskin)->device_adr);
+ burn_drive_add_whitelist((*preskin)->device_adr);
+ }
+
+ ret= Cdrskin_new(&skin,*preskin,1);
+ if(ret<=0) {
+ fprintf(stderr,"cdrskin: FATAL : creation of control object failed\n");
+ {*exit_value= 2; goto ex;}
+ }
+ *preskin= NULL; /* the preskin object now is under management of skin */
+ *o= skin;
+ if(skin->preskin->abort_handler==1 || skin->preskin->abort_handler==3 ||
+ skin->preskin->abort_handler==4)
+ Cleanup_set_handlers(skin,(Cleanup_app_handler_T) Cdrskin_abort_handler,4);
+ else if(skin->preskin->abort_handler==2)
+ Cleanup_set_handlers(skin,(Cleanup_app_handler_T) Cdrskin_abort_handler,2|8);
+
+ printf("cdrskin: scanning for devices ...");
+ fflush(stdout);
+ while (!burn_drive_scan(&(skin->drives), &(skin->n_drives))) {
+ if(skin->verbosity>=Cdrskin_verbose_debuG)
+ ClN(fprintf(stderr,"\ncdrskin_debug: ... still scanning ..."));
+ /* >>> ??? wait a while ? */
+ /* >>> ??? set a timeout ? */
+ }
+ printf(" done\n");
+ fflush(stdout);
+ex:;
+ return((*exit_value)==0);
+}
+
+
+/** Perform the activities which were ordered by setup
+ @param skin Knows what to do
+ @param exit_value Returns the proposal for an exit value
+ @param flag Unused yet
+ @return <=0 error, 1 success
+*/
+int Cdrskin_run(struct CdrskiN *skin, int *exit_value, int flag)
+{
+ int ret;
+
+ *exit_value= 0;
+ if(skin->do_devices) {
+ if(skin->n_drives<=0 && skin->preskin->scan_demands_drive)
+ {*exit_value= 4; goto no_drive;}
+ ret= Cdrskin_scanbus(skin,1);
+ if(ret<=0) {
+ fprintf(stderr,"cdrskin: FATAL : --devices failed.\n");
+ {*exit_value= 4; goto ex;}
+ }
+ }
+ if(skin->do_scanbus) {
+ if(skin->n_drives<=0 && skin->preskin->scan_demands_drive)
+ {*exit_value= 5; goto no_drive;}
+ ret= Cdrskin_scanbus(skin,0);
+ if(ret<=0)
+ fprintf(stderr,"cdrskin: FATAL : -scanbus failed.\n");
+ {*exit_value= 5*(ret<=0); goto ex;}
+ }
+ if(skin->do_checkdrive) {
+ ret= Cdrskin_checkdrive(skin,0);
+ {*exit_value= 6*(ret<=0); goto ex;}
+ }
+ if(skin->do_atip) {
+ if(skin->n_drives<=0)
+ {*exit_value= 7; goto no_drive;}
+ ret= Cdrskin_atip(skin,0);
+ if(ret<=0)
+ {*exit_value= 7; goto ex;}
+ }
+ if(skin->do_blank) {
+ if(skin->n_drives<=0)
+ {*exit_value= 8; goto no_drive;}
+ ret= Cdrskin_blank(skin,0);
+ if(ret<=0)
+ {*exit_value= 8; goto ex;}
+ }
+ if(skin->do_burn) {
+ if(skin->n_drives<=0)
+ {*exit_value= 10; goto no_drive;}
+ ret= Cdrskin_burn(skin,0);
+ if(ret<=0)
+ {*exit_value= 10; goto ex;}
+ }
+ex:;
+ return((*exit_value)==0);
+no_drive:;
+ fprintf(stderr,"cdrskin: FATAL : This run would need an accessible drive\n");
+ goto ex;
+}
+
+
+int main(int argc, char **argv)
+{
+ int ret,exit_value= 0,lib_initialized= 0;
+ struct CdrpreskiN *preskin= NULL;
+ struct CdrskiN *skin= NULL;
+ char *lean_id= "";
+#ifdef Cdrskin_extra_leaN
+ lean_id= ".lean";
+#endif
+
+ printf("cdrskin %s%s : limited cdrecord compatibility wrapper for libburn\n",
+ Cdrskin_prog_versioN,lean_id);
+ fflush(stdout);
+
+ ret= Cdrpreskin_new(&preskin,0);
+ if(ret<=0) {
+ fprintf(stderr,"cdrskin: FATAL : creation of control object failed\n");
+ {exit_value= 2; goto ex;}
+ }
+ ret= Cdrpreskin_setup(preskin,argc,argv,0);
+ if(ret<=0)
+ {exit_value= 11; goto ex;}
+ if(ret==2)
+ {exit_value= 0; goto ex;}
+
+ ret= Cdrskin_create(&skin,&preskin,&lib_initialized,&exit_value,0);
+ if(ret<=0)
+ {exit_value= 2; goto ex;}
+ if(skin->n_drives<=0) {
+ fprintf(stderr,"cdrskin: NOTE : No usable drive detected.\n");
+ if(getuid()!=0) {
+ fprintf(stderr,
+ "cdrskin: HINT : 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]);
+
+ 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/ZeroTwoTwo/cdrskin/cdrskin_eng.html b/tags/ZeroTwoTwo/cdrskin/cdrskin_eng.html
new file mode 100644
index 00000000..59848e66
--- /dev/null
+++ b/tags/ZeroTwoTwo/cdrskin/cdrskin_eng.html
@@ -0,0 +1,394 @@
+
+
+
+
+
+
+cdrskin homepage english
+
+
+
+
+
+
+Homepage of
cdrskin
+
+
+Limited cdrecord compatibility wrapper for libburn
+
+
+
+
Purpose:
+
+- Burns preformatted data to CD-R or CD-RW
+
+
+
+
+
+
+
+
Hardware requirements:
+A CD recorder suitable for
+libburn.pykix.org
+(SCSI or IDE/ATAPI writers compliant to mmc standard).
+
+
+
+
+
Software requirements :
+
+- Linux kernel 2.4 or higher
+- With kernel 2.4 the drive has to be under ide-scsi emulation.
+- With kernel 2.6 the drive should not be under ide-scsi.
+- libpthread
+- is supposed to be a standard system component.
+
+
+
+
+
+GPL software included:
+
+
+- libburn-0.2.1 (inofficially stable SVN snapshot)
+- (by Derek Foreman, Ben Jansens, and team of libburn.pykix.org)
+- transfers data to CD
+
+
+
+
+This program system has been tested on Intel/AMD Linux systems only.
+Ports to other usable systems are appreciated. Reports are welcome.
+
+
+
+
+
+
Special features:
+
+- Source code is independent of
+cdrecord
+
+
+
+
+
+
Commands:
+
+- The goal is to provide some of cdrecord's options in a compatible way.
+This has been achieved quite sufficiently for the needs of backup tool
+scdbackup
+and for data CD projects of K3b
+(see examples).
+Further enhancements depend on people who can describe and discuss their
+wishes as well as on the development of libburn.
+
+- cdrskin -help
+- reports the cdrecord compatible options
+- cdrskin --help
+- reports the non-cdrecord options
+- man cdrecord
+- documents the standard for which cdrskin is striving.
+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.)
+
+- Currently (and sparsely) supported gestures :
+- $ cdrskin -scanbus
+- $ cdrskin dev=1,1,0 -checkdrive
+- $ cdrskin dev=1,1,0 -atip
+- $ cdrskin -v dev=1,1,0 blank=all eject_device=/dev/cdrom -eject
+- $ cdrskin -v dev=1,1,0 blank=fast eject_device=/dev/cdrom -eject
+- $ cdrskin -v dev=1,1,0 speed=12 fs=8m -sao driveropts=burnfree eject_device=/dev/cdrom -eject padsize=300k my_image.iso
+- $ cdrskin -v dev=1,1,0 ... ... track_1.iso padsize=300k track_2.afio
+- $ find . | afio -oZ - | cdrskin -v dev=1,1,0 fs=32m speed=8 -sao driveropts=burnfree padsize=300k tsize=650m -
+
+
+
+
+
Known deficiencies:
+
+
+-
+No audio features yet. (Note: Option -audio is enabled in cdrskin-0.1.5 as
+offered below, but no beheading of .au or .wav files takes place yet.)
+
+-
+Burns only a single closed session. No -multi option yet.
+
+-
+No TAO mode and therefore no writing on-the-fly without a predefined
+source size.
+
+-
+cdrskin -scanbus or --devices hangs for quite a while if there is
+a CD drive which does not work properly (e.g. because it has individual
+problems with DMA).
+So if the superuser gets no result with cdrskin --devices then one should
+disable DMA with the problematic CD drives
+(like: hdparm -d0 /dev/hdd )
+and try again.
+In severe cases it might be necessary to guess the device name /dev/sgN resp.
+/dev/hdX of the non-ill burner if it cannot be found otherwise among its
+ill peers. Alternatively one can guess the address of the ill device, remove
+rw-permissions and retry the bus scan as non-superuser.
+ -
+Burners other than /dev/sg0 and /dev/hdX (i.e. without ide-scsi) need a user
+supplied device address for program eject.
+(Note: -eject is now working in libburn-0.2.1 SVN and the workaround described
+here is obsolete with cdrskin-0.1.5 as offered below.)
+
+
+
+
+
+
+
+
+- Download as source code (see README):
+- cdrskin-0.1.4.tar.gz
+(580 KB).
+
+-
+The "stable" cdrskin tarballs are tested and eventually slightly modified
+SVN snapshots from libburn.pykix.org . All modifications are to be re-merged
+into that SVN repository.
+cdrskin is part of libburn - full libburn is provided with cdrskin releases.
+
+-
+- Download as single x86 binaries (untar and move to /usr/bin/cdrskin):
+-
+cdrskin_0.1.4-x86-suse9_0.tar.gz, (50 KB),
+
+- runs on SuSE 9.0 (2.4.21) , RIP-14.4 (2.6.14) ,
+ Gentoo (2.6.15 x86_64 Athlon).
+
+-
+cdrskin_0.1.4-x86-suse9_0-static.tar.gz, (250 KB), -static compiled,
+
+- runs on SuSE 7.2 (2.4.4), and on the systems above.
+
+
+-
+
+- Documentation:
+- README a short introduction
+- cdrskin --help non-cdrecord options
+- cdrskin -help cdrecord compatible options
+-
+
+- Contact:
+- Thomas Schmitt, scdbackup@gmx.net
+
+- License:
+- GPL, an Open Source approved license
+-
+
+
+
+
+
+
+
+- Development snapshot:
+-
+- README 0.1.5
+
- cdrskin_0.1.5 --help
+- cdrskin_0.1.5 -help
+-
+- Maintainers of cdrskin unstable packages please use SVN of
+ libburn.pykix.org
+- Download: svn co http://libburn-svn.pykix.org/trunk libburn_pykix
+
+- Build: cd libburn_pykix ; ./bootstrap ; ./configure ; make
+
+- Build of SVN versions needs
+autotools of at least version 1.7 installed.
+But after the run of ./bootstrap, only
+vanilla tools like make and gcc are needed.
+
+-
+- The following downloads are intended for adventurous end users or
+admins with full system souvereignty.
+- Source (./bootstrap is already applied, build tested, for more see above
+upcoming README ):
+
+-
+cdrskin-0.1.5.tar.gz
+(580 KB).
+
+- Binary (untar and move to /usr/bin/cdrskin):
+-
+cdrskin_0.1.5-x86-suse9_0.tar.gz, (50 KB).
+
+-
+cdrskin_0.1.5-x86-suse9_0-static.tar.gz, (250 KB)
+
+
+
+
+
+
+
+Many thanks to Joerg Schilling for cdrecord,
+
+and to Derek Foreman and Ben Jansens for creating libburn.
+
+Historic versions based on Derek's and Ben's
+icculus.org/burn :
+cdrskin-0.1.2.0.2.ts.tar.gz
+cdrskin-0.1.3.0.2.ts.tar.gz
+
+
+
+
+
+
+
+- Example for a setup of device permissions. To be done by the superuser:
+- (CD devices which offer no r-permission are invisible to normal users.)
+- (CD devices which offer no w-permission are not useable.)
+- # cdrskin --devices
+- ...
+- 0 dev='/dev/sg0' rwrwr- : 'TEAC' 'CD-ROM CD-532S'
+- 1 dev='/dev/hdc' rwrw-- : 'LITE-ON' 'LTR-48125S'
+- # chmod a+rw /dev/sg0 /dev/hdc
+
+
+
+
+
+
+
+
+Example how to setup K3b to use cdrskin for burning data CD projects.
+
+(K3b
+is a GUI frontend which uses cdrecord for CD burning.)
+
+
+
+
+
+
+
+- Example for a test session with a cdrecord based scdbackup installation:
+- $ cdrskin -scanbus
+- ...
+- scsibus1:
+- 1,0,0 0) 'TEAC' 'CD-ROM CD-532S' '?' Removable CD-ROM
+- scsibus2:
+- 2,2,0 1) 'LITE-ON' 'LTR-48125S' '?' Removable CD-ROM
+- $ export SCDBACKUP_SCSI_ADR="2,2,0"
+- $ export SCDBACKUP_CDRECORD="cdrskin -v -v tao_to_sao_tsize=650m eject_device=/dev/cdrecorder"
+- (eject_device= has to be the appropriate address for program eject)
+- $ scdbackup_home
+
+
+- Example for a permanent configuration of cdrskin based scdbackup
+- $ cd scdbackup-0.8.6/inst
+- $ export SCDBACKUP_USE_CDRSKIN=1
+- $ export SCDBACKUP_EJECT_ADR=/dev/cdrecorder
+- $ ./CONFIGURE_CD
+- ...
+- cdrskin 0.1.2 : limited cdrecord compatibility wrapper for libburn
+- ...
+- ------------------- SCSI devices. To be used like 0,0,0
+
+If your system is stricken with some ill CD device then this can stall
+and you will have to press Ctrl+C to abort.
+In this case, you may execute
+export SCDBACKUP_NO_SCANBUS=1
+and try again.
+
+
+- 1,0,0 0) 'TEAC' 'CD-ROM CD-532S' '?' Removable CD-ROM
+- 2,2,0 1) 'LITE-ON' 'LTR-48125S' '?' Removable CD-ROM
+- ------------------- end of SCSI device list
+- ...
+- * Your cdrecord offers -driveropts=burnfree with your recorder.
+- ...
+- scdbackup for CD 0.8.6 : First stage of installation done.
+- ...
+- Now give it a try. Run : scdbackup_home
+- $ unset SCDBACKUP_USE_CDRSKIN SCDBACKUP_EJECT_ADR
+
+
+- To get back to using cdrecord :
+- $ cd scdbackup-0.8.6/inst
+- $ export SCDBACKUP_USE_CDRSKIN=0
+- $ ./CONFIGURE_CD
+- ...
+- $ unset SCDBACKUP_USE_CDRSKIN
+
+
+
+
+
+
+
+
About the relationship of cdrecord and cdrskin
+First of all: this relationship is single sided, as cdrskin has to be aware of
+cdrecord but not vice versa.
+
+
+I am a long time user of cdrecord and it works fine for me.
+Especially i do appreciate its write mode -tao which allows to pipe arbitrary
+data on CD and CD-RW via stdin. cdrecord is reliable, versatile and well
+maintained. So for me - there would be not problem with it.
+
+But the author of cdrecord and the Linux kernel people foster a very hostile
+relationship. Ok, that's their business, not mine (or ours if you are with me).
+One has to be aware, though, that this relationship might lead to a situation
+where cdrecord is no longer available for certain Linux kernels.
+
+To have my own project prepared for such a time, i began to implement its
+cdrecord gestures on top of libburn.
+From now on i invite other interested users of cdrecord to teach cdrskin
+the gestures necessary for their cdrecord applications.
+Contact me. Let's see what we can achieve.
+
+
+I am aware that libburn and cdrskin still have way to go until you can simply
+install cdrskin as cdrecord and may expect any application to run with it.
+Currently i do not encourage this approach, but of course such a replacement
+opportunity is the long term goal of a cdrecord compatibility wrapper.
+
+
+It is very important to me that this project is not perceived as hostile
+towards Joerg Schilling and his ongoing work.
+I owe him much. For cdrecord, for mkisofs, for star. Chapeau.
+
+
+
+
+
+
+Enjoying free Open Source hosting by www.webframe.org
+
+
+and by sourceforge.net
+
+
+
+
+
+
+- Links to my other published software projects :
+
-
+scdbackup, multi volume CD backup
+
-
+(a second source of above)
+- Some Tools for Image Collectors
+
- (a second source of above)
+-
+pppoem, a DSL throughput monitor (mainly for Linux kernel 2.4)
+
+
+Legal statement: This website does not serve any commercial purpose.
+
+
+
diff --git a/tags/ZeroTwoTwo/cdrskin/cdrskin_timestamp.h b/tags/ZeroTwoTwo/cdrskin/cdrskin_timestamp.h
new file mode 100644
index 00000000..4e99be54
--- /dev/null
+++ b/tags/ZeroTwoTwo/cdrskin/cdrskin_timestamp.h
@@ -0,0 +1 @@
+#define Cdrskin_timestamP "2006.09.20.134219"
diff --git a/tags/ZeroTwoTwo/cdrskin/changelog.txt b/tags/ZeroTwoTwo/cdrskin/changelog.txt
new file mode 100644
index 00000000..cc4a5e6b
--- /dev/null
+++ b/tags/ZeroTwoTwo/cdrskin/changelog.txt
@@ -0,0 +1,518 @@
+------------------------------------------------------------------------------
+ libburn.pykix.org scdbackup.sourceforge.net/cdrskin
+------------------------------------------------------------------------------
+ Changelog
+------------------------------------------------------------------------------
+
+19 Aug 2006 [committed]
+README
+cdrskin/README cdrskin/cdrskin.c cdrskin/add_ts_changes_to_libburn_0_2_1 \
+cdrskin/cdrskin_timestamp.h cdrskin/compile_cdrskin.sh
+cdrskin-0.1.4 "stable" released on base of August 15 2006 version of
+libburn-svn.pykix.org/trunk after initial merge of libburn and cdrskin-0.1.3 .
+
+------------------------------------------------------------------------------
+
+19 Aug 2006 [committed 16]
+cdrskin/changelog.txt
+cdrskin-0.1.5 development started.
+Introduced this changelog.
+---------------------------------- Format (still emerging):
+
+Day Month Year [eventual commit mark with revision number]
+affected files , naked, one by line,
+[eventually = internal snapshot tarball]
+More ore less concise description.
+
+---------------------------------- End of Format
+
+20 Aug 2006 [committed together with next change, i fear]
+libburn/sg.c
+Hopefully fixed a file descriptor resource leak in sg_grab().
+All scanned drives (seem to) stay open once, the grabbed one got re-opened
+and its stored first file descriptor got forgotten. Now we try to detect
+and re-use the still open fd.
+
+21 Aug 2006 [committed]
+libburn/libburn.h
+libburn/sg.c
+libburn/init.c
+= libburn_cdrskin_A60819.tgz
+cdrskin/cdrskin.c [committed later as revision 11]
+O_EXCL experiments imported from cdrskin-0.1.3
+In default configuration and on compliant kernels expect that a busy drive is
+invisible to further cdrskin instances. The user gets hints in case of empty
+bus scan result resp. busy drive given with dev=...
+Wether cdrecord and cdrskin respect each other would have to be evaluated.
+Options to play with:
+ --demand_a_drive exit !=0 on bus scans with empty result
+ --drive_abort_on_busy abort process if busy drive is found
+ (might be triggered by a busy hard disk)
+ --drive_blocking try to wait for busy drive to become free
+ (might be stalled by a busy hard disk)
+ --drive_not_exclusive do not ask kernel to prevent opening
+ busy drives. Effect is kernel dependend.
+ grab_drive_and_wait= grab drive, wait given number of
+ seconds, release drive, and do normal work
+
+21 Aug 2006 [committed]
+libburn/write.c
+Rectified non-ANSI-C comment, complained by gcc.
+
+21 Aug 2006 [committed 13]
+cdrskin/cdrskin_eng.html
+The homepage moved in from scdbackup's internal doc collection.
+
+21 Aug 2006 [committed with revision 11, i fear]
+cdrskin/cdrskin.c
+Removed flaw:
+Help text of tao_to_sao_tsize= clarified.
+
+21 Aug 2006 [committed 12]
+cdrskin/wiki_plain.txt
+The initial filling of the cdrskin wiki on libburn.pykix.org .
+I am not sure about the future fate of this text. I plan to keep it up to
+date with the online wiki for now. The online version will be considered
+the original.
+
+21 Aug 2006 [committed 15]
+cdrskin/cdrskin_timestamp.h
+Timestamping the new version.
+
+21 Aug 2006 [committed 14]
+cdrskin/add_ts_changes_to_libburn_0_2_1
+cdrskin/compile_cdrskin.sh
+Readjusted relationship glue of libburn and cdrskin.
+
+--------------------------------------------------------------------- cycled -
+
+27 Aug 2006 [40]
+libburn/mmc.c
+libburn/sg.c
+Inserted prints to see how sg_issue_command() is called (printing is disabled now)
+
+21 Aug 2006 [17]
+README
+Reported obvious need for automake >=1.7
+
+21 Aug 2006 [18]
+cdrskin/cdrskin_eng.html
+cdrskin/README
+Reported obvious need for automake >=1.7
+
+22 Aug 2006 [19]
+libburn/drive.c
+libburn/drive.h
+New internal function burn_drive_is_open()
+
+23 Aug 2006 [20]
+cdrskin/cdrskin.c
+Implemented Lorenzo Taylor's audio patch manually by copy+paste
+as i wanted to fully understand it.
+Hopefully did not break it that way.
+
+24 Aug 2006 [21]
+libburn/libburn.h
+libburn/drive.c
+Introduced API functions burn_drive_scan_and_grab() burn_drive_get_adr()
+
+24 Aug 2006 [22]
+cdrskin/compile_cdrskin.sh
+Experimental option cdrskin/compile_cdrskin.sh -newapi
+
+24 Aug 2006 [23]
+cdrskin/cdrskin.c
+First test of possibility to obey the self imposed rules of Revison 21
+
+24 Aug 2006 [25]
+cdrskin/cdrskin.c
+Fixed undefined track_type introduced by revision 20.
+(We broke cdrskin, but i did not break the patch. Success.)
+
+24 Aug 2006 [30]
+libburn/libburn.h
+Hopefully fixed an unintended line break in API doxygen
+
+25 Aug 2006 [32]
+cdrskin/cdrskin.c
+Installed protection against resource leak in Cdrskin_grab_drive()
+Just to be sure.
+
+25 Aug 2006 [33]
+libburn/drive.c
+burn_drive_free() now closes all open drive file descriptors
+
+25 Aug 2006 [34]
+libburn/libburn.h
+Adjusted statement at API documention of burn_initialize()
+
+25 Aug 2006 [35]
+cdrskin/cdrskin.c
+Worked forth in order to make cdrskin fully newapi compliant
+
+26 Aug 2006 [37]
+Makefile.am
+libburn/back_hacks.h
+libburn/drive.c
+libburn/init.c
+Allowed to blank appendable files and installed first back_hacks.h variable ever
+
+26 Aug 2006 [38]
+test/burniso.c
+Rewrote it to new API practice, inflated explanation comments, SAO mode
+
+27 Aug 2006 [39]
+cdrskin/cdrskin.c
+Implemented Lorenzos blank-appendable patch plus option --no_blank_appendable
+
+27 Aug 2006 [44]
+test/blank.c
+Rewrote test/blank.c to new API practice, inflated explanation comments
+
+27 Aug 2006 [41]
+cdrskin/cdrskin.c
+Fixed obscure sigsegv introduced with 35 or 39 by obeying libburn.h text
+(could be a fandango starting in burn_drive_info_free)
+
+27 Aug 2006 [45]
+test/burniso.c
+Disabled inner burn_drive_info_free like in cdrskin, polished a bit
+
+27 Aug 2006 [43]
+libburn/libburn.h
+Changed some 'release' to 'close' with specs of burn_drive_scan_and_grab
+
+28 Aug 2006 [46]
+test/burniso.c
+Polished a bit more for doxygen
+
+28 Aug 2006 [50]
+libburn/libburn.h
+cdrskin/cdrskin.c
+Wrote into API the imperative not to use drive.location but burn_drive_get_adr
+
+28 Aug 2006 [47]
+test/burniso.c
+Integrated functionality of test/devices.c into test/burniso.c
+Proposed to rename it to test/libburner.c
+
+28 Aug 2006 [51]
+cdrskin/cdrskin.c
+Closed a pitfall with reading from '-' and no tsize= or tao_to_sao_tsize=
+Ticket 55
+
+28 Aug 2006 [52]
+cdrskin/cdrskin.c
+Added personal commitment to grant BSD license on request. Insisted in GPL for now.
+
+28 Aug 2006 [53]
+cdrskin/cdrskin.c
+Forced each track to have a minimum size of 600 kB
+Ticket 55
+
+29 Aug 2006 [58]
+test/burniso.c
+Integrated functionality of test/blank.c into test/burniso.c
+
+29 Aug 2006 [55]
+cdrskin/cdrskin.c
+Made cdrskin ready to make good use of now working libburn-eject
+
+29 Aug 2006 [56]
+cdrskin/cdrskin.c
+Avoided unwanted tray loading on eject of never grabbed drive
+
+29 Aug 2006 [57]
+cdrskin/cdrskin.c
+Disabled unconditionality of eject introduced by 55 or 56
+
+30 Aug 2006 [59]
+test/burniso.c
+Repaired SIGSEGV caused by releasing ungrabbed drive after mere bus scan
+
+30 Aug 2006 [60]
+test/libburner.c
+Makefile.am
+My proposal for new souvereign app as API doc and reference for API decisions
+
+31 Aug 2006 [61]
+libburn/sg.c
+cdrskin/cdrskin.c
+Outcommented "experimental:" messages of O_EXCL development
+
+31 Aug 2006 [62]
+test/libburner.c
+Added 300 kB of padding to get rid of warning in doc, plus end sector padding
+
+31 Aug 2006 [63]
+cdrskin/cdrskin.c
+Promoted "newapi" functionality and libburn-eject from test to standard
+
+31 Aug 2006 [64]
+cdrskin/cdrskin.c
+Made cdrskin abort if fifo filling before burn yields 0 bytes (ticket 55)
+
+1 Sep 2006 [65]
+cdrskin/README
+cdrskin/cdrskin.c
+cdrskin/cdrskin_eng.html
+Updated cdrskin help tests and docs: -audio, obsolete eject_device=
+
+1 Sep 2006 [66]
+libburn/write.c
+cdrskin/cdrskin.c
+Implemented track number patch by bonfire-app@wanadoo.fr, tickets 58 and 9
+
+1 Sep 2006 [67]
+cdrskin/cdrskin.c
+Added clarifying URGE to ABORT texts
+
+1 Sep 2006 [71]
+test/libburner.c
+Made "read-ahead" comment sufficiently ambigous: "buffer"|"filesystem" == ""
+
+1 Sep 2006 [72] [73]
+cdrskin/cdrskin.c
+cdrskin/compile_cdrskin.sh
+Rowed back from revision 64. Now #ifdef Cdrskin_fifo_abort_on_emptY
+
+1 Sep 2006 [74]
+cdrskin/cdrskin.c
+cdrskin/compile_cdrskin.sh
+Rowed forth from revision 73. Now hopefully compliant to man cdrecord.
+
+1 Sep 2006 [78]
+cdrskin/cdrskin.c
+Made -pad behave cdrecord-ly on audio tracks (not tested acousticly)
+
+2 Sep 2006 [85]
+test/libburner.c
+Added upcoming clarification of copyright and license aspiration
+
+3 Sep 2006 [86]
+README
+Added upcoming clarification of copyright and license aspiration
+
+3 Sep 2006 [87] [88]
+cdrskin/README
+cdrskin/cdrskin.c
+Added upcoming clarification of copyright and license aspiration
+
+3 Sep 2006 [91]
+cdrskin/changelog.txt
+cdrskin/cdrskin_timestamp.h
+Opened new cdrskin-0.1.5 upload cycle. This marks a should-be-stable phase.
+
+---------------------------------------------------- cycled - 2006.09.03.132934
+
+
+3 Sep 2006 [89]
+doc/comments_test_ts
+Made a try to get doxygen portal page readable by html dl lists
+
+4 Sep 2006 [92]
+cdrskin/README
+Made changes as reported by Lorenzo on libburn-hackers today
+
+4 Sep 2006 [93]
+libburn/transport.h
+libburn/drive.h
+libburn/drive.c
+libburn/init.c
+libburn/libburn.h
+Integrated elmom patch proposal #3 from ticket #62
+/* ts A60904 : ticket 62, contribution by elmom */
+
+4 Sep 2006 [94]
+cdrskin/README
+Removed reference to frontend "burn" (needs a patch to work for .mp3)
+
+5 Sep 2006 [95]
+doc/comments_test_ts
+Made a try to get doxygen portal page readable by pre tags and truncation
+
+5 Sep 2006 [96]
+test/libburner.c
+Rearranged definitions and header inclusions. Is safer so.
+
+5 Sep 2006 [97]
+test/libburner.c
+Re-inserted lost tab.
+
+6 Sep 2006 [trac]
+closed ticket 55: burn of empty tracks from stdin is now forbidden
+
+6 Sep 2006 [98]
+libburn/libburn.h
+libburn/drive.c
+Added new parameter "force" to API-experimental burn_drive_info_forget()
+
+6 Sep 2006 [99]
+doc/comments
+Made doc test portal the official doc portal
+
+6 Sep 2006 [100]
+cdrskin/cdrfifo.c
+Added an initial value on proposal by Bart Vanherck
+
+7 Sep 2006 [101]
+test/libburner.c
+Changed a macro name from Burniso_ to Libburner_
+
+7 Sep 2006 [102] [103]
+libburn/libburn.h
+libburn/drive.c
+cdrskin/cdrskin.c
+Implemented first use of API-experimental burn_drive_info_forget() in cdrskin signal handler
+
+7 Sep 2006 [104]
+cdrskin/cdrskin.c
+Tried to make abort messages clearer
+
+7 Sep 2006 [105]
+Makefile.am
+test/testburner.c
+Prepared test bed for burn_drive_info_forget() as regular API call
+
+7 Sep 2006 [106]
+test/testburner.c
+Removed a remnant piece of rather unhealthy test code
+
+7 Sep 2006 [106]
+test/testburner.c
+Corrected test reciepe
+
+7 Sep 2006 [107]
+test/libburner.c
+Added constraint --stdin_size >= 600k, better bus scan behavior
+
+8 Sep 2006 2006 [109]
+libburn/drive.c
+test/testburner.c
+cdrskin/cdrskin.c
+Hopefully ensured correct burn_disc_erasable() already after first grab
+
+9 Sep 2006 2006 [112]
+libburn/drive.c
+cdrskin/cdrskin.c
+Hunted down the bug which let newapi-cdrskin fail with drive 1
+
+10 Sep 2006 [113]
+libburn/drive.c
+test/libburner.c
+test/testburner.c
+cdrskin/cdrskin.c
+Slowed down highspeed loops waiting for drive status changes
+
+10 Sep 2006 [114]
+cdrskin/compile_cdrskin.sh
+Aliased switch name -newapi by -experimental
+
+10 Sep 2006 [115]
+test/libburner.c
+test/testburner.c
+cdrskin/cdrskin.c
+Re-enabled call to burn_drive_info_free() after repair by revision 93
+
+11 Sep 2006 [116]
+cdrskin/compile_cdrskin.sh
+Changed -newapi to -experimental in help text
+
+11 Sep 2006 [117]
+libburn/drive.c
+Removed a bug introduced with revison 93
+
+11 Sep 2006 [118]
+libburn/libburn.h
+test/libburner.c
+Officialized burn_drive_info_forget()
+
+11 Sep 2006 [119] [120]
+Makefile.am
+test/testburner.c
+Deleted until next occasion: testburner
+
+12 Sep Sep 2006 [124]
+cdrskin/cdrskin.c
+Repaired regression of -eject which loaded tray again
+
+12 Sep 2006 [126]
+cdrskin/cdrskin.c
+Replaced -experimental method of closing libburn by burn_drive_info_forget()
+
+12 Sep 2006 [129]
+cdrskin/cdrskin.c
+Added automated padding to last audio track (ticket 41)
+
+2006.09.13.093350 [130]
+cdrskin/make_timestamp.sh
+Prepared for new revision timestamps to mark cdrskin test versions
+From now on cdrskin/cdrskin_timestamp.h is to be part of any commit.
+
+15 Sep 2006 [135]
+Makefile.am
+Replaced a few 8-blanks by tab
+
+15 Sep 2006 [136]
+README
+Moved installation instructions in front of overview paragraph
+
+15 Sep 2006 [137]
+Makefile.am
+cdrskin/README
+Made cdrskin an installable program
+
+2006.09.15.092509 [138]
+cdrskin/cdrskin.c
+cdrskin/compile_cdrskin.sh
+Prepared cdrskin-build for version leap
+
+
+----------------------------- cycled (last cdrskin-0.1.5 ?) - 2006.09.15.101326
+
+2006.09.15.101326 []
+cdrskin/README
+cdrskin/changelog.txt
+New upload of scdbackup.sourceforge.net/cdrskin-0.1.5.tar.gz
+
+
+
+Sep 2006 []
+000_CAUTION_RELEASE_CANDIDATE
+zzz_CAUTION_RELEASE_CANDIDATE
+README
+Makefile.am
+cdrskin/README
+cdrskin/cdrskin_eng.html
+cdrskin/add_ts_changes_to_libburn_0_2_1
+cdrskin/add_ts_changes_to_libburn_0_2_2
+cdrskin/compile_cdrskin.sh
+( cd_backup_planer_dir/aux/upload )
+Perfom version leap
+
+------------------------------ cycle
+
+2006 []
+test/libburner.c
+--audio and multi-track
+
+2006 []
+Resolve softlinks (ticket 33)
+
+2006 []
+Find matching /dev/sgN from srM or scdM
+
+2006 []
+Implement a .wav decapitator (ticket 38)
+
+2006 []
+Clear outdated persistent read buffer after small CD image was read (ticket 57)
+
+
+2006 []
+>>> libburn/libburn.h
+>>> libburn/drive.c
+>>> libburn/sg.c
+>>> Promoted burn_drive_is_open() to official API
+
+
diff --git a/tags/ZeroTwoTwo/cdrskin/cleanup.c b/tags/ZeroTwoTwo/cdrskin/cleanup.c
new file mode 100644
index 00000000..e5618937
--- /dev/null
+++ b/tags/ZeroTwoTwo/cdrskin/cleanup.c
@@ -0,0 +1,184 @@
+/*
+ cleanup.c , Copyright 2006 Thomas Schmitt
+
+ A signal handler which cleans up an application and exits.
+
+ Provided under GPL license within cdrskin and under BSD license elsewise.
+*/
+
+/*
+ cc -g -o cleanup -DCleanup_standalonE cleanup.c
+*/
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+typedef void (*sighandler_t)(int);
+
+
+#include "cleanup.h"
+
+/* Signals to be caught */
+static int signal_list[]= {
+ SIGHUP, SIGINT, SIGQUIT, SIGILL, SIGABRT,
+ SIGFPE, SIGSEGV, SIGPIPE, SIGALRM, SIGTERM,
+ SIGUSR1, SIGUSR2, SIGXCPU, SIGTSTP, SIGTTIN,
+ SIGTTOU,
+ SIGBUS, SIGPOLL, SIGPROF, SIGSYS, SIGTRAP,
+ SIGVTALRM, SIGXCPU, SIGXFSZ, -1
+};
+static char *signal_name_list[]= {
+ "SIGHUP", "SIGINT", "SIGQUIT", "SIGILL", "SIGABRT",
+ "SIGFPE", "SIGSEGV", "SIGPIPE", "SIGALRM", "SIGTERM",
+ "SIGUSR1", "SIGUSR2", "SIGXCPU", "SIGTSTP", "SIGTTIN",
+ "SIGTTOU",
+ "SIGBUS", "SIGPOLL", "SIGPROF", "SIGSYS", "SIGTRAP",
+ "SIGVTALRM", "SIGXCPU", "SIGXFSZ", "@"
+};
+static int signal_list_count= 24;
+
+/* Signals not to be caught */
+static int non_signal_list[]= {
+ SIGKILL, SIGCHLD, SIGSTOP, SIGURG, -1
+};
+static int non_signal_list_count= 4;
+
+
+/* run time dynamic part */
+static char cleanup_msg[4096]= {""};
+static int cleanup_exiting= 0;
+
+static void *cleanup_app_handle= NULL;
+static Cleanup_app_handler_T cleanup_app_handler= NULL;
+static int cleanup_perform_app_handler_first= 0;
+
+
+static int Cleanup_handler_exit(int exit_value, int signum, int flag)
+{
+ int ret;
+
+ if(cleanup_perform_app_handler_first)
+ if(cleanup_app_handler!=NULL) {
+ ret= (*cleanup_app_handler)(cleanup_app_handle,signum,0);
+ if(ret==2)
+ return(2);
+ }
+ if(cleanup_exiting) {
+ if(cleanup_msg[0]!=0)
+ fprintf(stderr,"%s\n",cleanup_msg);
+ fprintf(stderr,"cleanup: ABORT : repeat by pid=%d, signum=%d\n",
+ getpid(),signum);
+ return(0);
+ }
+ cleanup_exiting= 1;
+ if(cleanup_msg[0]!=0)
+ fprintf(stderr,"%s\n",cleanup_msg);
+ alarm(0);
+ if(!cleanup_perform_app_handler_first)
+ if(cleanup_app_handler!=NULL)
+ (*cleanup_app_handler)(cleanup_app_handle,signum,0);
+ exit(exit_value);
+}
+
+
+static void Cleanup_handler_generic(int signum)
+{
+ int i;
+
+ sprintf(cleanup_msg,"UNIX-SIGNAL caught: %d errno= %d",signum,errno);
+ for(i= 0; imax_sig)
+ max_sig= signal_list[i];
+ if(signal_list[i]=non_signal_list_count) {
+ if(i==SIGABRT && (flag&8))
+ signal(i,Cleanup_handler_generic);
+ else
+ signal(i,sig_handler);
+ }
+ }
+ return(1);
+}
+
+
+#ifdef Cleanup_standalonE
+
+struct Demo_apP {
+ char *msg;
+};
+
+
+int Demo_app_handler(struct Demo_apP *demoapp, int signum, int flag)
+{
+ printf("Handling exit of demo application on signal %d. msg=\"%s\"\n",
+ signum,demoapp->msg);
+ return(1);
+}
+
+
+main()
+{
+ struct Demo_apP demoapp;
+
+ demoapp.msg= "Good Bye";
+ Cleanup_set_handlers(&demoapp,(Cleanup_app_handler_T) Demo_app_handler,0);
+
+ if(1) { /* change to 0 in order to wait for external signals */
+ char *cpt= NULL,c;
+ printf("Intentionally provoking SIGSEGV ...\n");
+ c= *cpt;
+ } else {
+ printf("killme: %d\n",getpid());
+ sleep(3600);
+ }
+
+ Cleanup_set_handlers(NULL,NULL,1);
+ exit(0);
+}
+
+#endif /* Cleanup_standalonE */
diff --git a/tags/ZeroTwoTwo/cdrskin/cleanup.h b/tags/ZeroTwoTwo/cdrskin/cleanup.h
new file mode 100644
index 00000000..a155e052
--- /dev/null
+++ b/tags/ZeroTwoTwo/cdrskin/cleanup.h
@@ -0,0 +1,34 @@
+/*
+ cleanup.c , Copyright 2006 Thomas Schmitt
+
+ A signal handler which cleans up an application and exits.
+
+ Provided under GPL license within cdrskin and under BSD license elsewise.
+*/
+
+#ifndef Cleanup_includeD
+#define Cleanup_includeD 1
+
+
+/** Layout of an application provided cleanup function using an application
+ provided handle as first argument and the signal number as second
+ argument. The third argument is a flag bit field with no defined bits yet.
+ If the handler returns 2 then it has delegated exit() to some other
+ instance and the Cleanup handler shall return rather than exit.
+*/
+typedef int (*Cleanup_app_handler_T)(void *, int, int);
+
+
+/** Establish exiting signal handlers on (hopefully) all signals that are
+ not ignored by default or non-catchable.
+ @param handle Opaque object which knows how to cleanup application
+ @param handler Function which uses handle to perform application cleanup
+ @param flag Control Bitfield
+ bit0= reset to default signal handling
+*/
+int Cleanup_set_handlers(void *handle, Cleanup_app_handler_T handler,
+ int flag);
+
+
+#endif /* ! Cleanup_includeD */
+
diff --git a/tags/ZeroTwoTwo/cdrskin/compile_cdrskin.sh b/tags/ZeroTwoTwo/cdrskin/compile_cdrskin.sh
new file mode 100755
index 00000000..c46091ff
--- /dev/null
+++ b/tags/ZeroTwoTwo/cdrskin/compile_cdrskin.sh
@@ -0,0 +1,145 @@
+#!/bin/sh
+
+# compile_cdrskin.sh
+# Copyright 2005 - 2006 Thomas Schmitt, scdbackup@gmx.net, GPL
+# to be executed within ./libburn-* resp ./cdrskin-*
+
+debug_opts=
+def_opts=
+libvers="-DCdrskin_libburn_0_2_2"
+do_strip=0
+static_opts=
+warn_opts="-Wall"
+fifo_source="cdrskin/cdrfifo.c"
+compile_cdrskin=1
+compile_cdrfifo=0
+
+for i in "$@"
+do
+ if test "$i" = "-compile_cdrfifo"
+ then
+ compile_cdrfifo=1
+ elif test "$i" = "-tarball_0_2"
+ then
+ libvers=
+ elif test "$i" = "-cvs_A51208"
+ then
+ libvers="-DCdrskin_libburn_cvs_A51208_tS"
+ elif test "$i" = "-cvs_A60220"
+ then
+ libvers="-DCdrskin_libburn_cvs_A60220_tS"
+ elif test "$i" = "-libburn_0_2_1"
+ then
+ libvers="-DCdrskin_libburn_0_2_1"
+ elif test "$i" = "-libburn_0_2_2"
+ then
+ libvers="-DCdrskin_libburn_0_2_2"
+ elif test "$i" = "-newapi" -o "$i" = "-experimental"
+ then
+ def_opts="$def_opts -DCdrskin_new_api_tesT"
+ elif test "$i" = "-do_not_compile_cdrskin"
+ then
+ compile_cdrskin=0
+ elif test "$i" = "-do_diet"
+ then
+ fifo_source=
+ def_opts="$def_opts -DCdrskin_extra_leaN"
+ warn_opts=
+ elif test "$i" = "-do_strip"
+ then
+ do_strip=1
+ elif test "$i" = "-g"
+ then
+ debug_opts="$debug_opts -g"
+ elif test "$i" = "-O2"
+ then
+ debug_opts="$debug_opts -O2"
+ elif test "$i" = "-help" -o "$i" = "--help" -o "$i" = "-h"
+ then
+ echo "cdrskin/compile_cdrskin.sh : to be executed within ./cdrskin-0.1.3.0.2.ts"
+ echo "Options:"
+ echo " -compile_cdrfifo compile program cdrskin/cdrfifo."
+ echo " -tarball_0_2 set macro to match libburn-0.2.ts.tar.gz"
+ echo " -cvs_A51208 set macro to match libburn-CVS of 8 Dec 2005."
+ echo " -cvs_A60220 set macro to match libburn-CVS of 20 Feb 2006."
+ echo " -libburn_0_2_1 set macro to match libburn-SVN of 15 Sep 2006."
+ echo " -libburn_0_2_2 set macro to match libburn-0.2.2"
+ echo " -do_not_compile_cdrskin omit compilation of cdrskin/cdrskin."
+ echo " -experimental use newly introduced libburn features."
+ echo " -do_diet produce capability reduced lean version."
+ echo " -do_strip apply program strip to compiled programs."
+ echo " -g compile with cc option -g."
+ echo " -O2 compile with cc option -O2."
+ echo " -static compile with cc option -static."
+ exit 0
+ elif test "$i" = "-static"
+ then
+ static_opts="-static"
+ fi
+done
+
+
+timestamp="$(date -u '+%Y.%m.%d.%H%M%S')"
+echo "Version timestamp : $(sed -e 's/#define Cdrskin_timestamP "//' -e 's/"$//' cdrskin/cdrskin_timestamp.h)"
+echo "Build timestamp : $timestamp"
+
+if test "$compile_cdrskin"
+then
+ echo "compiling program cdrskin/cdrskin.c $static_opts $debug_opts $libvers $def_opts"
+ cc $warn_opts -I. $static_opts $debug_opts $libvers $def_opts \
+ -DCdrskin_build_timestamP='"'"$timestamp"'"' \
+ \
+ -o cdrskin/cdrskin \
+ \
+ cdrskin/cdrskin.c \
+ $fifo_source \
+ cdrskin/cleanup.c \
+ \
+ libburn/async.o \
+ libburn/debug.o \
+ libburn/drive.o \
+ libburn/file.o \
+ libburn/init.o \
+ libburn/options.o \
+ libburn/source.o \
+ libburn/structure.o \
+ \
+ libburn/message.o \
+ libburn/sg.o \
+ libburn/write.o \
+ \
+ libburn/mmc.o \
+ libburn/sbc.o \
+ libburn/spc.o \
+ libburn/util.o \
+ \
+ libburn/sector.o \
+ libburn/toc.o \
+ \
+ libburn/crc.o \
+ libburn/lec.o \
+ \
+ -lpthread
+fi
+
+if test "$compile_cdrfifo" = 1
+then
+ echo "compiling program cdrskin/cdrfifo.c $static_opts $debug_opts"
+ cc $static_opts $debug_opts \
+ -DCdrfifo_standalonE \
+ -o cdrskin/cdrfifo \
+ cdrskin/cdrfifo.c
+fi
+
+if test "$do_strip" = 1
+then
+ echo "stripping result cdrskin/cdrskin"
+ strip cdrskin/cdrskin
+ if test "$compile_cdrfifo" = 1
+ then
+ echo "stripping result cdrskin/cdrfifo"
+ strip cdrskin/cdrfifo
+ fi
+fi
+
+echo 'done.'
diff --git a/tags/ZeroTwoTwo/cdrskin/make_timestamp.sh b/tags/ZeroTwoTwo/cdrskin/make_timestamp.sh
new file mode 100755
index 00000000..957a2c4e
--- /dev/null
+++ b/tags/ZeroTwoTwo/cdrskin/make_timestamp.sh
@@ -0,0 +1,9 @@
+#!/bin/sh
+
+# Create version timestamp cdrskin/cdrskin_timestamp.h
+# to be executed within ./libburn-* resp ./cdrskin-*
+
+timestamp="$(date -u '+%Y.%m.%d.%H%M%S')"
+echo "Version timestamp : $timestamp"
+echo '#define Cdrskin_timestamP "'"$timestamp"'"' >cdrskin/cdrskin_timestamp.h
+
diff --git a/tags/ZeroTwoTwo/cdrskin/wiki_plain.txt b/tags/ZeroTwoTwo/cdrskin/wiki_plain.txt
new file mode 100644
index 00000000..351395c3
--- /dev/null
+++ b/tags/ZeroTwoTwo/cdrskin/wiki_plain.txt
@@ -0,0 +1,212 @@
+--------------------------------------------------------------------------
+ cdrskin Wiki - plain text copy
+--------------------------------------------------------------------------
+
+cdrskin is the cdrecord compatibility middleware of libburn.
+
+Its paragon, 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. Currently it does CD-R and CD-RW.
+Its future ability to burn DVD media depends on the development of libburn.
+
+cdrskin does not contain any bytes copied from cdrecord's sources.
+Many bytes have been copied from the message output of cdrecord
+runs, though. The most comprehensive technical overview of cdrskin
+can be found in cdrskin/README .
+
+--------------------------------------------------------------------------
+
+cdrskin with CD media fails to match its paragon cdrecord on three major
+fields: convenient TAO burn mode, multi session, audio features.
+
+Audio features are the only topic where cdrskin did not yet exploit current
+libburn to the maximum. This is due to my own lack of audiophile motivation
+and due to the lack of sincere users who provide me with cdrecord use cases,
+help me to explore the original cdrecord behavior and serve as dedicated
+testers for eventual newly introduced cdrskin audio features.
+
+cdrskin does not provide DVD burning yet. See advise to use dvd+rw-tools
+at the end of this text.
+
+--------------------------------------------------------------------------
+
+About the command line options of cdrskin:
+
+There are two families of options: cdrecord-compatible ones and options
+which are specific to cdrskin. The latter are mostly used to configure
+cdrskin for its task to emulate cdrecord. There are some, nevertheless,
+which provide rather exotic unique features of cdrskin.
+
+The cdrecord-compatible options are listed in the output of
+
+cdrskin -help
+
+where the option "help" has *one* dash.
+For these options you may expect program behavior that is roughly the
+same as described in original man 1 cdrecord .
+
+Online: http://cdrecord.berlios.de/old/private/man/cdrecord-2.0.html
+
+The cdrskin-specific options are listed by
+
+cdrskin --help
+
+where the option "help" has *two* dashes.
+
+Those have no man page yet. Some are very experimental and should only be
+used in coordination with the libburn developer team.
+Some are of general user interest, though:
+
+--------------------------------------------------------------------------
+
+--devices allows the sysadmin to scan the system for possible drives
+and displays their detected properties.
+The drives are listed one per line, with fields:
+
+libburn-drive-number sysadmin-device-file permissions : vendor type
+
+0 dev='/dev/sg0' rwrw-- : 'HL-DT-ST' 'DVDRAM GSA-4082B'
+
+This feature is valuable since cdrskin -scanbus will not give you
+the device file name and its current permissions.
+cdrskin will accept of course the proposed dev= option as address
+for any usage of the drive.
+
+Different from cdrecord, cdrskin is intended to be run without special
+privileges, i.e. no superuser setuid. It is intended that the sysadmin
+controls drive accessability by rw-permissions of the drive rather than
+by x-permission of the burn binary. To be usable with cdrskin, the drive
+has to offer both, r- and w-permission.
+
+--------------------------------------------------------------------------
+
+eject_device= is needed to work around yet broken tray ejection of
+drives. cdrskin makes a bold shell call to program "eject" and regrettably
+this program does not like our addresses for SCSI devices.
+/dev/hdX work fine and /dev/sg0 is quite safely guess-translated to
+/dev/sr0 . /dev/sg1 et.al. need the user's help. must work with eject.
+
+--------------------------------------------------------------------------
+
+dev_translation= is needed to foist cdrskin to frontend
+programs of cdrecord which do *not* ask cdrecord -scanbus but which make
+own assumptions and guesses about cdrecord's device addresses.
+cdrskin project - at least for now - refuses to try to provide a similar
+guesswork but uses own cdrecord style addresses which have a mere
+semi-automatic text mapping to real libburn addresses. See cdrskin/README,
+"Pseudo-SCSI Adresses".
+
+If you need to foist cdrskin under a frontend then you may be lucky and
+both ideas of an address coincide. Especially if the frontend has the
+decency to ask its "cdrecord" via option -scanbus for a list of drives.
+If not, look into the error protocol of the frontend, look at the output
+of a run of cdrskin --devices and give cdrskin the necessary hint.
+
+If your frontend insists in using "0,0,0" and --devices reported
+dev='/dev/sg0' resp. cdrskin -scanbus reported "1,0,0" then this
+would be the appropriate translation
+
+dev_translation=+0,0,0+/dev/sg0
+
+The "+" character is a separator to be choosen by you.
+Currently i am not aware of the need to choose any other than "+"
+unless you get playful with custom translations like
+
+dev_translation=-"cd+dvd"-1,0,0
+
+See http://scdbackup.sourceforge.net/k3b_on_cdrskin.html
+for an illustrated example with K3b 0.10 .
+
+--------------------------------------------------------------------------
+
+--fifo_start_empty is a throughput enhancer for unsteady data streams
+like they are produced by a compressing archiver program when piping to
+CD on-the-fly. It makes better use of the general property of a FIFO
+buffer to transport surplus bandwidth into the future. Yep. A time machine.
+One-way, i fear.
+
+FIFO originally was introduced by cdrecord's author Joerg Schilling in order
+to protect mediocre burner hardware from suffering buffer underruns
+and thus producing misburns (at 1x speed on CD-R media at the price of a
+DVD-RAM nowadays). This purpose would not justify a fifo any more -
+given the limited life time of burners and the seamless underrun protection
+of contemporary consumer drives.
+
+With an unsteady data stream the task of the buffer is to soak up peak
+performance and to release it steadily at the drive's maximum speed.
+The larger the buffer the more reserves can be built up and the longer
+input drought can be compensated.
+
+Original cdrecord has the historical property, though, to first wait until
+the buffer is completely filled. Best practice for fighting drive
+underruns, of course.
+With a very fat fs=# buffer (128 MB for 12x CD is not unrealistic) this
+can cause a big delay until burning finally starts and takes its due time.
+
+--fifo_start_empty makes cdrskin start burning without waiting for the
+FIFO to be full resp. the data stream to end. It can make use of the
+seconds spend with drive preparation and lead-in, it risks a few drive
+buffer underruns at the beginning of burn - but modern drives stand this.
+
+Note: no FIFO can give you better average throughput than the average
+throughput of the data source and the throughput of the burner.
+It can be used, though, to bring the effective throughput very close
+to the theoretical limit. Especially with high speed media.
+
+--------------------------------------------------------------------------
+
+--no_rc allows you to surely ban influence from systemwide or user specific
+default settings of cdrskin. Possible locations for such settings:
+
+/etc/default/cdrskin
+
+/etc/opt/cdrskin/rc
+
+$HOME/.cdrskinrc
+
+--------------------------------------------------------------------------
+
+tao_to_sao_tsize= allows the - actually unsupported - cdrecord option
+-tao and defines a default track size to be used if - as custom with -tao -
+no option tsize=# is given.
+
+As in general with cdrskin tsize=# the data source does not have to provide
+the full annouced amount of data. Missing data will be padded up by 0-bytes.
+Surplus data is supposed to cause an error, though. The burn will then
+be a failure in any way.
+
+--------------------------------------------------------------------------
+
+DVD advise:
+
+For burning of DVD media the cdrskin project currently advises to use
+Andy Polyakov's dvd+rw-tools which despite their historic name burn
+for me on above burner: DVD+RW, DVD+R, DVD-RW, DVD-R .
+
+http://fy.chalmers.se/~appro/linux/DVD+RW/tools
+
+They are not compatible or related to cdrecord resp. cdrecord-ProDVD
+(now obsoleted by original source cdrtools cdrecord with identical
+capabilities besides the license key).
+
+If there is sincere and well motivated interest, the cdrskin project could try
+to employ growisofs as DVD burning engine. The cdrskin project would prefer to
+wait for DVD support being included in libburn, though.
+A very limited and specialized cdrecord-compatibility wrapper for growisofs
+serves in my project scdbackup. It is not overly hard to make one that serves
+some very few fixed use cases.
+
+To my knowledge, Linux kernels 2.6 do write to DVD+RW via block devices as
+they would write to a traditional tape device. Try old tape archiver
+commands with addresses like /dev/sr0 or /dev/hdc rather than /dev/mt0 .
+I have heard rumors that DVD-RW in mode "restricted overwrite" would be
+block device ready, too. My burner is not a real friend of DVD-RW and
+in an experiment the burn worked fine - but the result was not identical
+to the stream sent to the device. I had similar failure with DVD-RAM, too.
+
+Beware of the impact of a slow block device on overall system i/o buffering.
+It is wise to curb its input to a speed which it is able to deliver to media.
+Else your i/o dedicated RAM might buffer a big amount of stream data.
+
+--------------------------------------------------------------------------
+
diff --git a/tags/ZeroTwoTwo/configure.ac b/tags/ZeroTwoTwo/configure.ac
new file mode 100644
index 00000000..9179c866
--- /dev/null
+++ b/tags/ZeroTwoTwo/configure.ac
@@ -0,0 +1,129 @@
+AC_INIT([libburn], [0.2.2], [http://libburn.pykix.org])
+AC_PREREQ([2.50])
+dnl AC_CONFIG_HEADER([config.h])
+
+AC_CANONICAL_HOST
+AC_CANONICAL_TARGET
+
+AM_INIT_AUTOMAKE([subdir-objects])
+
+dnl Making releases:
+dnl BURN_MICRO_VERSION += 1;
+dnl BURN_INTERFACE_AGE += 1;
+dnl BURN_BINARY_AGE += 1;
+dnl if any functions have been added, set BURN_INTERFACE_AGE to 0.
+dnl if backwards compatibility has been broken,
+dnl set BURN_BINARY_AGE and BURN_INTERFACE_AGE to 0.
+dnl
+dnl if MAJOR or MINOR version changes, be sure to change AC_INIT above to match
+dnl
+BURN_MAJOR_VERSION=0
+BURN_MINOR_VERSION=2
+BURN_MICRO_VERSION=2
+BURN_INTERFACE_AGE=0
+BURN_BINARY_AGE=0
+BURN_VERSION=$BURN_MAJOR_VERSION.$BURN_MINOR_VERSION.$BURN_MICRO_VERSION
+
+AC_SUBST(BURN_MAJOR_VERSION)
+AC_SUBST(BURN_MINOR_VERSION)
+AC_SUBST(BURN_MICRO_VERSION)
+AC_SUBST(BURN_INTERFACE_AGE)
+AC_SUBST(BURN_BINARY_AGE)
+AC_SUBST(BURN_VERSION)
+
+dnl Libtool versioning
+LT_RELEASE=$BURN_MAJOR_VERSION.$BURN_MINOR_VERSION
+LT_CURRENT=`expr $BURN_MICRO_VERSION - $BURN_INTERFACE_AGE`
+LT_REVISION=$BURN_INTERFACE_AGE
+LT_AGE=`expr $BURN_BINARY_AGE - $BURN_INTERFACE_AGE`
+LT_CURRENT_MINUS_AGE=`expr $LT_CURRENT - $LT_AGE`
+
+AC_SUBST(LT_RELEASE)
+AC_SUBST(LT_CURRENT)
+AC_SUBST(LT_REVISION)
+AC_SUBST(LT_AGE)
+AC_SUBST(LT_CURRENT_MINUS_AGE)
+
+AC_PREFIX_DEFAULT([/usr/local])
+test "$prefix" = "NONE" && prefix=$ac_default_prefix
+
+AM_MAINTAINER_MODE
+
+AM_PROG_CC_C_O
+AC_C_CONST
+AC_C_INLINE
+AC_C_BIGENDIAN
+
+dnl Large file support
+AC_SYS_LARGEFILE
+AC_FUNC_FSEEKO
+if test ! $ac_cv_func_fseeko; then
+ AC_ERROR([Libburn requires largefile support.])
+fi
+
+AC_ARG_ENABLE(static, enable_static=yes)
+if test x$enable_static != xyes; then
+ AC_MSG_RESULT([no])
+ AM_DISABLE_STATIC=no
+else
+ AC_MSG_RESULT([yes])
+ AM_DISABLE_STATIC=yes
+fi
+
+AC_PROG_LIBTOOL
+AC_SUBST(LIBTOOL_DEPS)
+LIBTOOL="$LIBTOOL --silent"
+
+AC_PROG_INSTALL
+
+AC_CHECK_HEADERS()
+
+THREAD_LIBS=-lpthread
+AC_SUBST(THREAD_LIBS)
+
+TARGET_SHIZZLE
+
+dnl Add compiler-specific flags
+
+dnl See if the user wants aggressive optimizations of the code
+AC_ARG_ENABLE(debug,
+[ --enable-debug Disable aggressive optimizations [default=yes]],
+ , enable_debug=yes)
+if test x$enable_debug != xyes; then
+ if test x$GCC = xyes; then
+ CFLAGS="$CFLAGS -O3"
+ CFLAGS="$CFLAGS -fexpensive-optimizations"
+ fi
+ CFLAGS="$CFLAGS -DNDEBUG"
+else
+ if test x$GCC = xyes; then
+ CFLAGS="$CFLAGS -g -pedantic -Wall"
+ fi
+ CFLAGS="$CFLAGS -DDEBUG"
+fi
+
+AC_MSG_CHECKING([if we build the HTML documentation])
+AC_ARG_ENABLE(docs,
+[ --enable-docs Use doxygen to build HTML documentation],
+[case "${enableval}" in
+ yes) build_docs=true ;;
+ no) build_docs=false ;;
+ *) AC_MSG_ERROR(bad value ${enableval} for --enable-docs) ;;
+esac],[build_docs=false])
+
+if test "x$build_docs" = "xtrue"; then
+ AC_MSG_RESULT([yes])
+ touch doc/doc.lock
+else
+ AC_MSG_RESULT([no (use --enable-docs to enable)])
+ rm -f doc/doc.lock
+fi
+
+AC_CONFIG_FILES([
+ Makefile
+ doc/doxygen.conf
+ version.h
+ libburn-1.pc
+ libisofs-1.pc
+ ])
+AC_OUTPUT
diff --git a/tags/ZeroTwoTwo/doc/Makefile b/tags/ZeroTwoTwo/doc/Makefile
new file mode 100644
index 00000000..062350dd
--- /dev/null
+++ b/tags/ZeroTwoTwo/doc/Makefile
@@ -0,0 +1,4 @@
+all clean:
+ $(MAKE) -C .. -$(MAKEFLAGS) $@
+
+.PHONY: all clean
diff --git a/tags/ZeroTwoTwo/doc/comments b/tags/ZeroTwoTwo/doc/comments
new file mode 100644
index 00000000..a4e23c55
--- /dev/null
+++ b/tags/ZeroTwoTwo/doc/comments
@@ -0,0 +1,121 @@
+/**
+ @author Mario Danic, Thomas Schmitt
+
+ @mainpage Libburn Documentation Index
+
+ @section intro Introduction
+
+Libburn 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.
+
+@subsection components 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 of libburn
+ is named test/libburner.c .
+ Explore these examples if you look for inspiration.
+
+We plan to be a responsive upstream. Bear with us.
+
+
+ @section using Using the libraries
+
+Our build system is based on autotools.
+User experience tells us that you will need at least autotools version 1.7.
+
+To build libburn and its subprojects it should be sufficient to go into
+its toplevel directory and execute
+
+- ./bootstrap (needed if you downloaded from SVN)
+
+- ./configure
+
+- make
+
+To make the libraries accessible for running resp. developing applications
+
+- make install
+
+Both libraries are written in C language and get built by autotools.
+Thus we expect them to be useable by a wide range of Linux-implemented
+languages and development tools.
+
+
+@section libburner Libburner
+
+libburner is a minimal demo application for the library libburn
+(see: libburn/libburn.h) as provided on http://libburn.pykix.org .
+It can list the available devices, can blank a CD-RW and
+can burn to CD-R or CD-RW.
+
+It's main purpose, nevertheless, is to show you how to use libburn and also
+to serve the libburn team as reference application. libburner does indeed
+define the standard way how above three gestures can be implemented and
+stay upward compatible for a good while.
+
+ @subsection libburner-help Libburner --help
+
+Usage: test/libburner
+ [--drive ||"-"]
+ [--verbose ] [--blank_fast|--blank_full]
+ [--burn_for_real|--try_to_simulate] [--stdin_size ]
+ [|"-"]
+Examples
+A bus scan (needs rw-permissions to see a drive):
+ test/libburner --drive -
+Burn a file to drive chosen by number:
+ test/libburner --drive 0 --burn_for_real my_image_file
+Burn a file to drive chosen by persistent address:
+ test/libburner --drive /dev/hdc --burn_for_real my_image_file
+Blank a used CD-RW (is combinable with burning in one run):
+ test/libburner --drive 0 --blank_fast
+Burn a compressed afio archive on-the-fly, pad up to 700 MB:
+ ( cd my_directory ; find . -print | afio -oZ - ) | \
+ test/libburner --drive /dev/hdc --burn_for_real --stdin_size 734003200 -
+To be read from *not mounted* CD via:
+ afio -tvZ /dev/hdc
+Program tar would need a clean EOF which our padded CD cannot deliver.
+
+
+ @subsection libburner-source Sourceode of libburner
+
+Click on blue names of functions, structures, variables, etc in oder to
+get to the according specs of libburn API or libburner sourcecode.
+
+@include libburner.c
+*/
diff --git a/tags/ZeroTwoTwo/doc/comments_test_ts b/tags/ZeroTwoTwo/doc/comments_test_ts
new file mode 100644
index 00000000..a4e23c55
--- /dev/null
+++ b/tags/ZeroTwoTwo/doc/comments_test_ts
@@ -0,0 +1,121 @@
+/**
+ @author Mario Danic, Thomas Schmitt
+
+ @mainpage Libburn Documentation Index
+
+ @section intro Introduction
+
+Libburn 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.
+
+@subsection components 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 of libburn
+ is named test/libburner.c .
+ Explore these examples if you look for inspiration.
+
+We plan to be a responsive upstream. Bear with us.
+
+
+ @section using Using the libraries
+
+Our build system is based on autotools.
+User experience tells us that you will need at least autotools version 1.7.
+
+To build libburn and its subprojects it should be sufficient to go into
+its toplevel directory and execute
+
+- ./bootstrap (needed if you downloaded from SVN)
+
+- ./configure
+
+- make
+
+To make the libraries accessible for running resp. developing applications
+
+- make install
+
+Both libraries are written in C language and get built by autotools.
+Thus we expect them to be useable by a wide range of Linux-implemented
+languages and development tools.
+
+
+@section libburner Libburner
+
+libburner is a minimal demo application for the library libburn
+(see: libburn/libburn.h) as provided on http://libburn.pykix.org .
+It can list the available devices, can blank a CD-RW and
+can burn to CD-R or CD-RW.
+
+It's main purpose, nevertheless, is to show you how to use libburn and also
+to serve the libburn team as reference application. libburner does indeed
+define the standard way how above three gestures can be implemented and
+stay upward compatible for a good while.
+
+ @subsection libburner-help Libburner --help
+
+Usage: test/libburner
+ [--drive ||"-"]
+ [--verbose ] [--blank_fast|--blank_full]
+ [--burn_for_real|--try_to_simulate] [--stdin_size ]
+ [|"-"]
+Examples
+A bus scan (needs rw-permissions to see a drive):
+ test/libburner --drive -
+Burn a file to drive chosen by number:
+ test/libburner --drive 0 --burn_for_real my_image_file
+Burn a file to drive chosen by persistent address:
+ test/libburner --drive /dev/hdc --burn_for_real my_image_file
+Blank a used CD-RW (is combinable with burning in one run):
+ test/libburner --drive 0 --blank_fast
+Burn a compressed afio archive on-the-fly, pad up to 700 MB:
+ ( cd my_directory ; find . -print | afio -oZ - ) | \
+ test/libburner --drive /dev/hdc --burn_for_real --stdin_size 734003200 -
+To be read from *not mounted* CD via:
+ afio -tvZ /dev/hdc
+Program tar would need a clean EOF which our padded CD cannot deliver.
+
+
+ @subsection libburner-source Sourceode of libburner
+
+Click on blue names of functions, structures, variables, etc in oder to
+get to the according specs of libburn API or libburner sourcecode.
+
+@include libburner.c
+*/
diff --git a/tags/ZeroTwoTwo/doc/doxygen.conf.in b/tags/ZeroTwoTwo/doc/doxygen.conf.in
new file mode 100644
index 00000000..829b4970
--- /dev/null
+++ b/tags/ZeroTwoTwo/doc/doxygen.conf.in
@@ -0,0 +1,186 @@
+# Doxyfile 1.2.18
+
+#---------------------------------------------------------------------------
+# General configuration options
+#---------------------------------------------------------------------------
+PROJECT_NAME = @PACKAGE_NAME@
+PROJECT_NUMBER = @PACKAGE_VERSION@
+OUTPUT_DIRECTORY =
+OUTPUT_LANGUAGE = English
+EXTRACT_ALL = YES
+EXTRACT_PRIVATE = YES
+EXTRACT_STATIC = YES
+EXTRACT_LOCAL_CLASSES = YES
+HIDE_UNDOC_MEMBERS = NO
+HIDE_UNDOC_CLASSES = NO
+HIDE_FRIEND_COMPOUNDS = NO
+BRIEF_MEMBER_DESC = YES
+REPEAT_BRIEF = YES
+ALWAYS_DETAILED_SEC = NO
+INLINE_INHERITED_MEMB = NO
+FULL_PATH_NAMES = YES
+STRIP_FROM_PATH = @top_srcdir@
+INTERNAL_DOCS = NO
+STRIP_CODE_COMMENTS = NO
+CASE_SENSE_NAMES = NO
+SHORT_NAMES = NO
+HIDE_SCOPE_NAMES = NO
+VERBATIM_HEADERS = YES
+SHOW_INCLUDE_FILES = YES
+JAVADOC_AUTOBRIEF = YES
+MULTILINE_CPP_IS_BRIEF = YES
+DETAILS_AT_TOP = YES
+INHERIT_DOCS = YES
+INLINE_INFO = YES
+SORT_MEMBER_DOCS = YES
+DISTRIBUTE_GROUP_DOC = NO
+TAB_SIZE = 4
+GENERATE_TODOLIST = YES
+GENERATE_TESTLIST = YES
+GENERATE_BUGLIST = YES
+GENERATE_DEPRECATEDLIST= YES
+ALIASES =
+ENABLED_SECTIONS =
+MAX_INITIALIZER_LINES = 30
+OPTIMIZE_OUTPUT_FOR_C = YES
+OPTIMIZE_OUTPUT_JAVA = NO
+SHOW_USED_FILES = YES
+#---------------------------------------------------------------------------
+# configuration options related to warning and progress messages
+#---------------------------------------------------------------------------
+QUIET = YES
+WARNINGS = YES
+WARN_IF_UNDOCUMENTED = YES
+WARN_FORMAT = "$file:$line: $text"
+WARN_LOGFILE =
+#---------------------------------------------------------------------------
+# configuration options related to the input files
+#---------------------------------------------------------------------------
+INPUT = libburn libisofs doc test
+FILE_PATTERNS = libburn.h libisofs.h comments libburner.c
+RECURSIVE = NO
+EXCLUDE =
+EXCLUDE_SYMLINKS = NO
+EXCLUDE_PATTERNS =
+EXAMPLE_PATH = test
+EXAMPLE_PATTERNS =
+EXAMPLE_RECURSIVE = NO
+IMAGE_PATH =
+INPUT_FILTER =
+FILTER_SOURCE_FILES = NO
+#---------------------------------------------------------------------------
+# configuration options related to source browsing
+#---------------------------------------------------------------------------
+SOURCE_BROWSER = YES
+INLINE_SOURCES = YES
+REFERENCED_BY_RELATION = YES
+REFERENCES_RELATION = YES
+#---------------------------------------------------------------------------
+# configuration options related to the alphabetical class index
+#---------------------------------------------------------------------------
+ALPHABETICAL_INDEX = NO
+COLS_IN_ALPHA_INDEX = 5
+IGNORE_PREFIX = OB OTK _
+#---------------------------------------------------------------------------
+# configuration options related to the HTML output
+#---------------------------------------------------------------------------
+GENERATE_HTML = YES
+HTML_OUTPUT = doc/html
+HTML_FILE_EXTENSION = .html
+HTML_HEADER =
+HTML_FOOTER =
+HTML_STYLESHEET =
+HTML_ALIGN_MEMBERS = YES
+GENERATE_HTMLHELP = NO
+CHM_FILE =
+HHC_LOCATION =
+GENERATE_CHI = NO
+BINARY_TOC = NO
+TOC_EXPAND = NO
+DISABLE_INDEX = NO
+ENUM_VALUES_PER_LINE = 4
+GENERATE_TREEVIEW = NO
+TREEVIEW_WIDTH = 200
+#---------------------------------------------------------------------------
+# configuration options related to the LaTeX output
+#---------------------------------------------------------------------------
+GENERATE_LATEX = NO
+LATEX_OUTPUT = latex
+LATEX_CMD_NAME = latex
+MAKEINDEX_CMD_NAME = makeindex
+COMPACT_LATEX = NO
+PAPER_TYPE = letter
+EXTRA_PACKAGES =
+LATEX_HEADER =
+PDF_HYPERLINKS = YES
+USE_PDFLATEX = NO
+LATEX_BATCHMODE = NO
+#---------------------------------------------------------------------------
+# configuration options related to the RTF output
+#---------------------------------------------------------------------------
+GENERATE_RTF = NO
+RTF_OUTPUT = rtf
+COMPACT_RTF = NO
+RTF_HYPERLINKS = NO
+RTF_STYLESHEET_FILE =
+RTF_EXTENSIONS_FILE =
+#---------------------------------------------------------------------------
+# configuration options related to the man page output
+#---------------------------------------------------------------------------
+GENERATE_MAN = NO
+MAN_OUTPUT = man
+MAN_EXTENSION = .3
+MAN_LINKS = NO
+#---------------------------------------------------------------------------
+# configuration options related to the XML output
+#---------------------------------------------------------------------------
+GENERATE_XML = NO
+XML_SCHEMA =
+XML_DTD =
+#---------------------------------------------------------------------------
+# configuration options for the AutoGen Definitions output
+#---------------------------------------------------------------------------
+GENERATE_AUTOGEN_DEF = NO
+#---------------------------------------------------------------------------
+# Configuration options related to the preprocessor
+#---------------------------------------------------------------------------
+ENABLE_PREPROCESSING = YES
+MACRO_EXPANSION = NO
+EXPAND_ONLY_PREDEF = NO
+SEARCH_INCLUDES = YES
+INCLUDE_PATH =
+INCLUDE_FILE_PATTERNS =
+PREDEFINED = DOXYGEN
+EXPAND_AS_DEFINED =
+SKIP_FUNCTION_MACROS = YES
+#---------------------------------------------------------------------------
+# Configuration::addtions related to external references
+#---------------------------------------------------------------------------
+TAGFILES =
+GENERATE_TAGFILE =
+ALLEXTERNALS = NO
+EXTERNAL_GROUPS = YES
+PERL_PATH = /usr/bin/perl
+#---------------------------------------------------------------------------
+# Configuration options related to the dot tool
+#---------------------------------------------------------------------------
+CLASS_DIAGRAMS = YES
+HIDE_UNDOC_RELATIONS = YES
+HAVE_DOT = YES
+CLASS_GRAPH = YES
+COLLABORATION_GRAPH = YES
+TEMPLATE_RELATIONS = YES
+INCLUDE_GRAPH = YES
+INCLUDED_BY_GRAPH = YES
+GRAPHICAL_HIERARCHY = NO
+DOT_IMAGE_FORMAT = png
+DOT_PATH =
+DOTFILE_DIRS =
+MAX_DOT_GRAPH_WIDTH = 1024
+MAX_DOT_GRAPH_HEIGHT = 1024
+GENERATE_LEGEND = YES
+DOT_CLEANUP = YES
+#---------------------------------------------------------------------------
+# Configuration::addtions related to the search engine
+#---------------------------------------------------------------------------
+SEARCHENGINE = NO
diff --git a/tags/ZeroTwoTwo/libburn-1.pc.in b/tags/ZeroTwoTwo/libburn-1.pc.in
new file mode 100644
index 00000000..6c72f9e6
--- /dev/null
+++ b/tags/ZeroTwoTwo/libburn-1.pc.in
@@ -0,0 +1,11 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: libburn
+Description: Disc reading/writing library
+Version: @VERSION@
+Requires:
+Libs: -L${libdir} -lburn @THREAD_LIBS@
+Cflags: -I${includedir}/libburn
diff --git a/tags/ZeroTwoTwo/libburn/Makefile b/tags/ZeroTwoTwo/libburn/Makefile
new file mode 100644
index 00000000..062350dd
--- /dev/null
+++ b/tags/ZeroTwoTwo/libburn/Makefile
@@ -0,0 +1,4 @@
+all clean:
+ $(MAKE) -C .. -$(MAKEFLAGS) $@
+
+.PHONY: all clean
diff --git a/tags/ZeroTwoTwo/libburn/Makefile.am b/tags/ZeroTwoTwo/libburn/Makefile.am
new file mode 100644
index 00000000..144ece71
--- /dev/null
+++ b/tags/ZeroTwoTwo/libburn/Makefile.am
@@ -0,0 +1,65 @@
+pkgconfigdir=$(libdir)/pkgconfig
+libincludedir=$(includedir)/libburn
+
+lib_LTLIBRARIES = libburn.la
+
+libburn_la_SOURCES = \
+ async.c \
+ async.h \
+ crc.c \
+ crc.h \
+ debug.c \
+ debug.h \
+ drive.c \
+ drive.h \
+ file.c \
+ file.h \
+ init.c \
+ init.h \
+ lec.c \
+ lec.h \
+ message.c \
+ message.h \
+ mmc.c \
+ mmc.h \
+ null.c \
+ null.h \
+ options.c \
+ options.h \
+ read.c \
+ read.h \
+ sbc.c \
+ sbc.h \
+ sector.c \
+ sector.h \
+ sg.c \
+ sg.h \
+ spc.c \
+ spc.h \
+ source.h \
+ source.c \
+ structure.c \
+ structure.h \
+ toc.c \
+ toc.h \
+ transport.h \
+ util.c \
+ util.h \
+ write.c \
+ write.h
+
+libinclude_HEADERS = libburn.h
+
+## ========================================================================= ##
+indent_files = $(libburn_la_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
+
+## ========================================================================= ##
diff --git a/tags/ZeroTwoTwo/libburn/async.c b/tags/ZeroTwoTwo/libburn/async.c
new file mode 100644
index 00000000..0e01f543
--- /dev/null
+++ b/tags/ZeroTwoTwo/libburn/async.c
@@ -0,0 +1,191 @@
+/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */
+
+#include "libburn.h"
+#include "transport.h"
+#include "drive.h"
+#include "write.h"
+#include "options.h"
+#include "async.h"
+
+#include
+#include
+#include
+
+#define SCAN_GOING() (workers && !workers->drive)
+
+typedef void *(*WorkerFunc) (void *);
+
+struct scan_opts
+{
+ struct burn_drive_info **drives;
+ unsigned int *n_drives;
+
+ int done;
+};
+
+struct erase_opts
+{
+ struct burn_drive *drive;
+ int fast;
+};
+
+struct write_opts
+{
+ struct burn_drive *drive;
+ struct burn_write_opts *opts;
+ struct burn_disc *disc;
+};
+
+struct w_list
+{
+ struct burn_drive *drive;
+ pthread_t thread;
+
+ struct w_list *next;
+
+ union w_list_data
+ {
+ struct scan_opts scan;
+ struct erase_opts erase;
+ struct write_opts write;
+ } u;
+};
+
+static struct w_list *workers;
+
+static struct w_list *find_worker(struct burn_drive *d)
+{
+ struct w_list *a;
+
+ for (a = workers; a; a = a->next)
+ if (a->drive == d)
+ return a;
+ return NULL;
+}
+
+static void add_worker(struct burn_drive *d, WorkerFunc f, void *data)
+{
+ struct w_list *a;
+ struct w_list *tmp;
+
+ a = malloc(sizeof(struct w_list));
+ a->drive = d;
+ a->u = *(union w_list_data *)data;
+
+ /* insert at front of the list */
+ a->next = workers;
+ tmp = workers;
+ workers = a;
+
+ if (d)
+ d->busy = BURN_DRIVE_SPAWNING;
+
+ if (pthread_create(&a->thread, NULL, f, a)) {
+ free(a);
+ workers = tmp;
+ return;
+ }
+}
+
+static void remove_worker(pthread_t th)
+{
+ struct w_list *a, *l = NULL;
+
+ for (a = workers; a; l = a, a = a->next)
+ if (a->thread == th) {
+ if (l)
+ l->next = a->next;
+ else
+ workers = a->next;
+ free(a);
+ break;
+ }
+ assert(a != NULL); /* wasn't found.. this should not be possible */
+}
+
+static void *scan_worker_func(struct w_list *w)
+{
+ burn_drive_scan_sync(w->u.scan.drives, w->u.scan.n_drives);
+ w->u.scan.done = 1;
+ return NULL;
+}
+
+int burn_drive_scan(struct burn_drive_info *drives[], unsigned int *n_drives)
+{
+ struct scan_opts o;
+ int ret = 0;
+
+ /* cant be anything working! */
+ assert(!(workers && workers->drive));
+
+ if (!workers) {
+ /* start it */
+ o.drives = drives;
+ o.n_drives = n_drives;
+ o.done = 0;
+ add_worker(NULL, (WorkerFunc) scan_worker_func, &o);
+ } else if (workers->u.scan.done) {
+ /* its done */
+ ret = workers->u.scan.done;
+ remove_worker(workers->thread);
+ assert(workers == NULL);
+ } else {
+ /* still going */
+ }
+ return ret;
+}
+
+static void *erase_worker_func(struct w_list *w)
+{
+ burn_disc_erase_sync(w->u.erase.drive, w->u.erase.fast);
+ remove_worker(pthread_self());
+ return NULL;
+}
+
+void burn_disc_erase(struct burn_drive *drive, int fast)
+{
+ struct erase_opts o;
+
+ assert(drive);
+ assert(!SCAN_GOING());
+ assert(!find_worker(drive));
+
+ o.drive = drive;
+ o.fast = fast;
+ add_worker(drive, (WorkerFunc) erase_worker_func, &o);
+}
+
+static void *write_disc_worker_func(struct w_list *w)
+{
+ burn_disc_write_sync(w->u.write.opts, w->u.write.disc);
+
+ /* the options are refcounted, free out ref count which we added below
+ */
+ burn_write_opts_free(w->u.write.opts);
+
+ remove_worker(pthread_self());
+ return NULL;
+}
+
+void burn_disc_write(struct burn_write_opts *opts, struct burn_disc *disc)
+{
+ struct write_opts o;
+
+ assert(!SCAN_GOING());
+ assert(!find_worker(opts->drive));
+ o.drive = opts->drive;
+ o.opts = opts;
+ o.disc = disc;
+
+ opts->refcount++;
+
+ add_worker(opts->drive, (WorkerFunc) write_disc_worker_func, &o);
+}
+
+void burn_async_join_all(void)
+{
+ void *ret;
+
+ while (workers)
+ pthread_join(workers->thread, &ret);
+}
diff --git a/tags/ZeroTwoTwo/libburn/async.h b/tags/ZeroTwoTwo/libburn/async.h
new file mode 100644
index 00000000..0e1d6677
--- /dev/null
+++ b/tags/ZeroTwoTwo/libburn/async.h
@@ -0,0 +1,8 @@
+/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */
+
+#ifndef BURN__ASYNC_H
+#define BURN__ASYNC_H
+
+void burn_async_join_all(void);
+struct burn_write_opts;
+#endif /* BURN__ASYNC_H */
diff --git a/tags/ZeroTwoTwo/libburn/back_hacks.h b/tags/ZeroTwoTwo/libburn/back_hacks.h
new file mode 100644
index 00000000..b086620a
--- /dev/null
+++ b/tags/ZeroTwoTwo/libburn/back_hacks.h
@@ -0,0 +1,54 @@
+/**
+
+ This file bundles variables which disable changes in libburn which are
+ not yet completely accepted.
+
+ The use of these variables is *strongly discouraged* unless you have sincere
+ reason and are willing to share your gained knowledge with the libburn
+ developers.
+
+ Do *not silently rely* on these variables with your application. Tell us
+ that you needed one or more of them. They are subject to removal as soon
+ as consense has been found about correctness of the change they revoke.
+
+ Value 0 means that the new behavior is enabled. Any other value enables
+ the described old time behavior.
+
+ If you doubt one of the changes here broke your application, then do
+ *in your application*, *not here* :
+
+ - #include "libburn/back_hacks.h" like you include "libburn/libburn.h"
+
+ - Set the libburn_back_hack_* variable of your choice to 1.
+ In your app. Not here.
+
+ - Then start and use libburn as usual. Watch out for results.
+
+ - If you believe to have detected a flaw in our change, come forward
+ and report it to the libburn developers. Thanks in advance. :)
+
+*/
+
+/** Do not define this macro in your application. Only libburn/init.c is
+ entitled to set it.
+*/
+#ifdef BURN_BACK_HACKS_INIT
+
+
+/** Corresponds to http://libburn.pykix.org/ticket/42
+ Reinstates the old ban not to blank appendable CD-RW. We see no reason
+ for this ban yet. It appears unusual. But maybe it patches a bug.
+*/
+int libburn_back_hack_42= 0;
+
+
+#else /* BURN_BACK_HACKS_INIT */
+
+/* Note: no application programmer info beyond this point */
+
+
+extern int libburn_back_hack_42;
+
+#endif /* ! BURN_BACK_HACKS_INIT */
+
+
diff --git a/tags/ZeroTwoTwo/libburn/crc.c b/tags/ZeroTwoTwo/libburn/crc.c
new file mode 100644
index 00000000..fddc5b4f
--- /dev/null
+++ b/tags/ZeroTwoTwo/libburn/crc.c
@@ -0,0 +1,122 @@
+/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */
+
+#include "crc.h"
+
+static unsigned short ccitt_table[256] = {
+ 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7,
+ 0x8108, 0x9129, 0xA14A, 0xB16B, 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF,
+ 0x1231, 0x0210, 0x3273, 0x2252, 0x52B5, 0x4294, 0x72F7, 0x62D6,
+ 0x9339, 0x8318, 0xB37B, 0xA35A, 0xD3BD, 0xC39C, 0xF3FF, 0xE3DE,
+ 0x2462, 0x3443, 0x0420, 0x1401, 0x64E6, 0x74C7, 0x44A4, 0x5485,
+ 0xA56A, 0xB54B, 0x8528, 0x9509, 0xE5EE, 0xF5CF, 0xC5AC, 0xD58D,
+ 0x3653, 0x2672, 0x1611, 0x0630, 0x76D7, 0x66F6, 0x5695, 0x46B4,
+ 0xB75B, 0xA77A, 0x9719, 0x8738, 0xF7DF, 0xE7FE, 0xD79D, 0xC7BC,
+ 0x48C4, 0x58E5, 0x6886, 0x78A7, 0x0840, 0x1861, 0x2802, 0x3823,
+ 0xC9CC, 0xD9ED, 0xE98E, 0xF9AF, 0x8948, 0x9969, 0xA90A, 0xB92B,
+ 0x5AF5, 0x4AD4, 0x7AB7, 0x6A96, 0x1A71, 0x0A50, 0x3A33, 0x2A12,
+ 0xDBFD, 0xCBDC, 0xFBBF, 0xEB9E, 0x9B79, 0x8B58, 0xBB3B, 0xAB1A,
+ 0x6CA6, 0x7C87, 0x4CE4, 0x5CC5, 0x2C22, 0x3C03, 0x0C60, 0x1C41,
+ 0xEDAE, 0xFD8F, 0xCDEC, 0xDDCD, 0xAD2A, 0xBD0B, 0x8D68, 0x9D49,
+ 0x7E97, 0x6EB6, 0x5ED5, 0x4EF4, 0x3E13, 0x2E32, 0x1E51, 0x0E70,
+ 0xFF9F, 0xEFBE, 0xDFDD, 0xCFFC, 0xBF1B, 0xAF3A, 0x9F59, 0x8F78,
+ 0x9188, 0x81A9, 0xB1CA, 0xA1EB, 0xD10C, 0xC12D, 0xF14E, 0xE16F,
+ 0x1080, 0x00A1, 0x30C2, 0x20E3, 0x5004, 0x4025, 0x7046, 0x6067,
+ 0x83B9, 0x9398, 0xA3FB, 0xB3DA, 0xC33D, 0xD31C, 0xE37F, 0xF35E,
+ 0x02B1, 0x1290, 0x22F3, 0x32D2, 0x4235, 0x5214, 0x6277, 0x7256,
+ 0xB5EA, 0xA5CB, 0x95A8, 0x8589, 0xF56E, 0xE54F, 0xD52C, 0xC50D,
+ 0x34E2, 0x24C3, 0x14A0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405,
+ 0xA7DB, 0xB7FA, 0x8799, 0x97B8, 0xE75F, 0xF77E, 0xC71D, 0xD73C,
+ 0x26D3, 0x36F2, 0x0691, 0x16B0, 0x6657, 0x7676, 0x4615, 0x5634,
+ 0xD94C, 0xC96D, 0xF90E, 0xE92F, 0x99C8, 0x89E9, 0xB98A, 0xA9AB,
+ 0x5844, 0x4865, 0x7806, 0x6827, 0x18C0, 0x08E1, 0x3882, 0x28A3,
+ 0xCB7D, 0xDB5C, 0xEB3F, 0xFB1E, 0x8BF9, 0x9BD8, 0xABBB, 0xBB9A,
+ 0x4A75, 0x5A54, 0x6A37, 0x7A16, 0x0AF1, 0x1AD0, 0x2AB3, 0x3A92,
+ 0xFD2E, 0xED0F, 0xDD6C, 0xCD4D, 0xBDAA, 0xAD8B, 0x9DE8, 0x8DC9,
+ 0x7C26, 0x6C07, 0x5C64, 0x4C45, 0x3CA2, 0x2C83, 0x1CE0, 0x0CC1,
+ 0xEF1F, 0xFF3E, 0xCF5D, 0xDF7C, 0xAF9B, 0xBFBA, 0x8FD9, 0x9FF8,
+ 0x6E17, 0x7E36, 0x4E55, 0x5E74, 0x2E93, 0x3EB2, 0x0ED1, 0x1EF0
+};
+
+unsigned long crc32_table[256] = {
+ 0x00000000L, 0x90910101L, 0x91210201L, 0x01B00300L,
+ 0x92410401L, 0x02D00500L, 0x03600600L, 0x93F10701L,
+ 0x94810801L, 0x04100900L, 0x05A00A00L, 0x95310B01L,
+ 0x06C00C00L, 0x96510D01L, 0x97E10E01L, 0x07700F00L,
+ 0x99011001L, 0x09901100L, 0x08201200L, 0x98B11301L,
+ 0x0B401400L, 0x9BD11501L, 0x9A611601L, 0x0AF01700L,
+ 0x0D801800L, 0x9D111901L, 0x9CA11A01L, 0x0C301B00L,
+ 0x9FC11C01L, 0x0F501D00L, 0x0EE01E00L, 0x9E711F01L,
+ 0x82012001L, 0x12902100L, 0x13202200L, 0x83B12301L,
+ 0x10402400L, 0x80D12501L, 0x81612601L, 0x11F02700L,
+ 0x16802800L, 0x86112901L, 0x87A12A01L, 0x17302B00L,
+ 0x84C12C01L, 0x14502D00L, 0x15E02E00L, 0x85712F01L,
+ 0x1B003000L, 0x8B913101L, 0x8A213201L, 0x1AB03300L,
+ 0x89413401L, 0x19D03500L, 0x18603600L, 0x88F13701L,
+ 0x8F813801L, 0x1F103900L, 0x1EA03A00L, 0x8E313B01L,
+ 0x1DC03C00L, 0x8D513D01L, 0x8CE13E01L, 0x1C703F00L,
+ 0xB4014001L, 0x24904100L, 0x25204200L, 0xB5B14301L,
+ 0x26404400L, 0xB6D14501L, 0xB7614601L, 0x27F04700L,
+ 0x20804800L, 0xB0114901L, 0xB1A14A01L, 0x21304B00L,
+ 0xB2C14C01L, 0x22504D00L, 0x23E04E00L, 0xB3714F01L,
+ 0x2D005000L, 0xBD915101L, 0xBC215201L, 0x2CB05300L,
+ 0xBF415401L, 0x2FD05500L, 0x2E605600L, 0xBEF15701L,
+ 0xB9815801L, 0x29105900L, 0x28A05A00L, 0xB8315B01L,
+ 0x2BC05C00L, 0xBB515D01L, 0xBAE15E01L, 0x2A705F00L,
+ 0x36006000L, 0xA6916101L, 0xA7216201L, 0x37B06300L,
+ 0xA4416401L, 0x34D06500L, 0x35606600L, 0xA5F16701L,
+ 0xA2816801L, 0x32106900L, 0x33A06A00L, 0xA3316B01L,
+ 0x30C06C00L, 0xA0516D01L, 0xA1E16E01L, 0x31706F00L,
+ 0xAF017001L, 0x3F907100L, 0x3E207200L, 0xAEB17301L,
+ 0x3D407400L, 0xADD17501L, 0xAC617601L, 0x3CF07700L,
+ 0x3B807800L, 0xAB117901L, 0xAAA17A01L, 0x3A307B00L,
+ 0xA9C17C01L, 0x39507D00L, 0x38E07E00L, 0xA8717F01L,
+ 0xD8018001L, 0x48908100L, 0x49208200L, 0xD9B18301L,
+ 0x4A408400L, 0xDAD18501L, 0xDB618601L, 0x4BF08700L,
+ 0x4C808800L, 0xDC118901L, 0xDDA18A01L, 0x4D308B00L,
+ 0xDEC18C01L, 0x4E508D00L, 0x4FE08E00L, 0xDF718F01L,
+ 0x41009000L, 0xD1919101L, 0xD0219201L, 0x40B09300L,
+ 0xD3419401L, 0x43D09500L, 0x42609600L, 0xD2F19701L,
+ 0xD5819801L, 0x45109900L, 0x44A09A00L, 0xD4319B01L,
+ 0x47C09C00L, 0xD7519D01L, 0xD6E19E01L, 0x46709F00L,
+ 0x5A00A000L, 0xCA91A101L, 0xCB21A201L, 0x5BB0A300L,
+ 0xC841A401L, 0x58D0A500L, 0x5960A600L, 0xC9F1A701L,
+ 0xCE81A801L, 0x5E10A900L, 0x5FA0AA00L, 0xCF31AB01L,
+ 0x5CC0AC00L, 0xCC51AD01L, 0xCDE1AE01L, 0x5D70AF00L,
+ 0xC301B001L, 0x5390B100L, 0x5220B200L, 0xC2B1B301L,
+ 0x5140B400L, 0xC1D1B501L, 0xC061B601L, 0x50F0B700L,
+ 0x5780B800L, 0xC711B901L, 0xC6A1BA01L, 0x5630BB00L,
+ 0xC5C1BC01L, 0x5550BD00L, 0x54E0BE00L, 0xC471BF01L,
+ 0x6C00C000L, 0xFC91C101L, 0xFD21C201L, 0x6DB0C300L,
+ 0xFE41C401L, 0x6ED0C500L, 0x6F60C600L, 0xFFF1C701L,
+ 0xF881C801L, 0x6810C900L, 0x69A0CA00L, 0xF931CB01L,
+ 0x6AC0CC00L, 0xFA51CD01L, 0xFBE1CE01L, 0x6B70CF00L,
+ 0xF501D001L, 0x6590D100L, 0x6420D200L, 0xF4B1D301L,
+ 0x6740D400L, 0xF7D1D501L, 0xF661D601L, 0x66F0D700L,
+ 0x6180D800L, 0xF111D901L, 0xF0A1DA01L, 0x6030DB00L,
+ 0xF3C1DC01L, 0x6350DD00L, 0x62E0DE00L, 0xF271DF01L,
+ 0xEE01E001L, 0x7E90E100L, 0x7F20E200L, 0xEFB1E301L,
+ 0x7C40E400L, 0xECD1E501L, 0xED61E601L, 0x7DF0E700L,
+ 0x7A80E800L, 0xEA11E901L, 0xEBA1EA01L, 0x7B30EB00L,
+ 0xE8C1EC01L, 0x7850ED00L, 0x79E0EE00L, 0xE971EF01L,
+ 0x7700F000L, 0xE791F101L, 0xE621F201L, 0x76B0F300L,
+ 0xE541F401L, 0x75D0F500L, 0x7460F600L, 0xE4F1F701L,
+ 0xE381F801L, 0x7310F900L, 0x72A0FA00L, 0xE231FB01L,
+ 0x71C0FC00L, 0xE151FD01L, 0xE0E1FE01L, 0x7070FF00L
+};
+
+unsigned short crc_ccitt(unsigned char *q, int len)
+{
+ unsigned short crc = 0;
+
+ while (len-- > 0)
+ crc = ccitt_table[(crc >> 8 ^ *q++) & 0xff] ^ (crc << 8);
+ return ~crc;
+}
+unsigned int crc_32(unsigned char *data, int len)
+{
+ unsigned int crc = 0;
+
+ while (len-- > 0)
+ crc = crc32_table[(crc ^ *data++) & 0xffL] ^ (crc >> 8);
+ return crc;
+}
diff --git a/tags/ZeroTwoTwo/libburn/crc.h b/tags/ZeroTwoTwo/libburn/crc.h
new file mode 100644
index 00000000..a4846a33
--- /dev/null
+++ b/tags/ZeroTwoTwo/libburn/crc.h
@@ -0,0 +1,9 @@
+/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */
+
+#ifndef BURN__CRC_H
+#define BURN__CRC_H
+
+unsigned short crc_ccitt(unsigned char *, int len);
+unsigned int crc_32(unsigned char *, int len);
+
+#endif /* BURN__CRC_H */
diff --git a/tags/ZeroTwoTwo/libburn/debug.c b/tags/ZeroTwoTwo/libburn/debug.c
new file mode 100644
index 00000000..b4abab77
--- /dev/null
+++ b/tags/ZeroTwoTwo/libburn/debug.c
@@ -0,0 +1,35 @@
+/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */
+
+#ifdef WIN32
+#include
+#endif
+
+#include
+#include
+#include "libburn.h"
+#include "debug.h"
+
+static int burn_verbosity = 0;
+
+void burn_set_verbosity(int v)
+{
+ burn_verbosity = v;
+}
+
+void burn_print(int level, const char *a, ...)
+{
+#ifdef WIN32
+ char debug_string_data[256];
+#endif
+ va_list vl;
+
+ if (level <= burn_verbosity) {
+ va_start(vl, a);
+#ifdef WIN32
+ vsprintf(debug_string_data, a, vl);
+ OutputDebugString(debug_string_data);
+#else
+ vfprintf(stderr, a, vl);
+#endif
+ }
+}
diff --git a/tags/ZeroTwoTwo/libburn/debug.h b/tags/ZeroTwoTwo/libburn/debug.h
new file mode 100644
index 00000000..b566de0e
--- /dev/null
+++ b/tags/ZeroTwoTwo/libburn/debug.h
@@ -0,0 +1,8 @@
+/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */
+
+#ifndef BURN__DEBUG_H
+#define BURN__DEBUG_H
+
+void burn_print(int level, const char *a, ...);
+
+#endif /* BURN__DEBUG_H */
diff --git a/tags/ZeroTwoTwo/libburn/drive.c b/tags/ZeroTwoTwo/libburn/drive.c
new file mode 100644
index 00000000..9acc0b68
--- /dev/null
+++ b/tags/ZeroTwoTwo/libburn/drive.c
@@ -0,0 +1,665 @@
+/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include "libburn.h"
+#include "drive.h"
+#include "transport.h"
+#include "message.h"
+#include "debug.h"
+#include "init.h"
+#include "toc.h"
+#include "util.h"
+#include "sg.h"
+#include "structure.h"
+#include "back_hacks.h"
+
+static struct burn_drive drive_array[255];
+static int drivetop = -1;
+
+int burn_drive_is_open(struct burn_drive *d);
+
+/* ts A60904 : ticket 62, contribution by elmom */
+/* splitting former burn_drive_free() (which freed all, into two calls) */
+void burn_drive_free(struct burn_drive *d)
+{
+ if (d->global_index == -1)
+ return;
+ /* ts A60822 : close open fds before forgetting them */
+ if (burn_drive_is_open(d))
+ close(d->fd);
+ free((void *) d->idata);
+ free((void *) d->mdata);
+ free((void *) d->toc_entry);
+ free(d->devname);
+ d->global_index = -1;
+}
+
+void burn_drive_free_all(void)
+{
+ int i;
+
+ for (i = 0; i < drivetop + 1; i++)
+ burn_drive_free(&(drive_array[i]));
+ drivetop = -1;
+ memset(drive_array, 0, sizeof(drive_array));
+}
+
+
+/* ts A60822 */
+int burn_drive_is_open(struct burn_drive *d)
+{
+ /* a bit more detailed case distinction than needed */
+ if (d->fd == -1337)
+ return 0;
+ if (d->fd < 0)
+ return 0;
+ return 1;
+}
+
+
+/* ts A60906 */
+int burn_drive_force_idle(struct burn_drive *d)
+{
+ d->busy = BURN_DRIVE_IDLE;
+ return 1;
+}
+
+
+/* ts A60906 */
+int burn_drive_is_released(struct burn_drive *d)
+{
+ return !!d->released;
+}
+
+
+/* ts A60906 */
+/** Inquires drive status in respect to degree of app usage.
+ @param return -2 = drive is forgotten
+ -1 = drive is closed (i.e. released explicitely)
+ 0 = drive is open, not grabbed (after scan, before 1st grab)
+ 1 = drive is grabbed but BURN_DRIVE_IDLE
+ 10 = drive is grabbing (BURN_DRIVE_GRABBING)
+ 100 = drive is busy in cancelable state
+ 1000 = drive is in non-cancelable state
+ Expect a monotonous sequence of usage severity to emerge in future.
+*/
+int burn_drive_is_occupied(struct burn_drive *d)
+{
+ if(d->global_index < 0)
+ return -2;
+ if(!burn_drive_is_open(d))
+ return -1;
+ if(d->busy == BURN_DRIVE_GRABBING)
+ return 10;
+ if(d->released)
+ return 0;
+ if(d->busy == BURN_DRIVE_IDLE)
+ return 1;
+ if(d->busy == BURN_DRIVE_READING || d->busy == BURN_DRIVE_WRITING)
+ return 50;
+ return 1000;
+}
+
+
+/*
+void drive_read_lead_in(int dnum)
+{
+ mmc_read_lead_in(&drive_array[dnum], get_4k());
+}
+*/
+unsigned int burn_drive_count(void)
+{
+ return drivetop + 1;
+}
+
+int burn_drive_grab(struct burn_drive *d, int le)
+{
+ int errcode;
+ int was_equal = 0, must_equal = 3, max_loop = 20;
+
+ /* ts A60907 */
+ int loop_count, old_speed = -1234567890, new_speed = -987654321;
+ int old_erasable = -1234567890, new_erasable = -987654321;
+
+ if (!d->released) {
+ burn_print(1, "can't grab - already grabbed\n");
+ return 0;
+ }
+ errcode = d->grab(d);
+
+ if (errcode == 0) {
+ burn_print(1, "low level drive grab failed\n");
+ return 0;
+ }
+ d->busy = BURN_DRIVE_GRABBING;
+
+ if (le)
+ d->load(d);
+
+ d->lock(d);
+ d->status = BURN_DISC_BLANK;
+ if (d->mdata->cdr_write || d->mdata->cdrw_write ||
+ d->mdata->dvdr_write || d->mdata->dvdram_write) {
+
+#ifdef Libburn_grab_release_and_grab_agaiN
+
+ d->read_disc_info(d);
+
+#else
+ /* ts A60908 */
+ /* Trying to stabilize the disc status after eventual load
+ without closing and re-opening the drive */
+ /* This seems to work for burn_disc_erasable() .
+ Speed values on RIP-14 and LITE-ON 48125S are stable
+ and false, nevertheless. So cdrskin -atip is still
+ forced to finish-initialize. */
+ /*
+ fprintf(stderr,"libburn: experimental: read_disc_info()\n");
+ */
+ for (loop_count = 0; loop_count < max_loop; loop_count++){
+ old_speed = new_speed;
+ old_erasable = new_erasable;
+
+ d->read_disc_info(d);
+
+ new_speed = burn_drive_get_write_speed(d);
+ new_erasable = burn_disc_erasable(d);
+ if (new_speed == old_speed &&
+ new_erasable == old_erasable) {
+ was_equal++;
+ if (was_equal >= must_equal)
+ break;
+ } else
+ was_equal = 0;
+ /*
+ if (loop_count >= 1 && was_equal == 0)
+ fprintf(stderr,"libburn: experimental: %d : speed %d:%d erasable %d:%d\n",
+ loop_count,old_speed,new_speed,old_erasable,new_erasable);
+ */
+ usleep(100000);
+ }
+#endif /* ! Libburn_grab_release_and_grab_agaiN */
+
+ } else {
+ d->read_toc(d);
+ }
+ d->busy = BURN_DRIVE_IDLE;
+ return 1;
+}
+
+struct burn_drive *burn_drive_register(struct burn_drive *d)
+{
+#ifdef Libburn_ticket_62_re_register_is_possiblE
+ int i;
+#endif
+
+ d->block_types[0] = 0;
+ d->block_types[1] = 0;
+ d->block_types[2] = 0;
+ d->block_types[3] = 0;
+ d->toc_temp = 0;
+ d->nwa = 0;
+ d->alba = 0;
+ d->rlba = 0;
+ d->cancel = 0;
+ d->busy = BURN_DRIVE_IDLE;
+ d->toc_entries = 0;
+ d->toc_entry = NULL;
+ d->disc = NULL;
+ d->erasable = 0;
+
+#ifdef Libburn_ticket_62_re_register_is_possiblE
+ /* ts A60904 : ticket 62, contribution by elmom */
+ /* Not yet accepted because no use case seen yet */
+
+ /* This is supposed to find an already freed drive struct among
+ all the the ones that have been used before */
+ for (i = 0; i < drivetop + 1; i++)
+ if (drive_array[i].global_index == -1)
+ break;
+ d->global_index = i;
+ memcpy(&drive_array[i], d, sizeof(struct burn_drive));
+ pthread_mutex_init(&drive_array[i].access_lock, NULL);
+ if (drivetop < i)
+ drivetop = i;
+ return &(drive_array[i]);
+
+#else /* Libburn_ticket_62_re_register_is_possiblE */
+ /* old A60904 : */
+ /* Still active by default */
+
+ d->global_index = drivetop + 1;
+ memcpy(&drive_array[drivetop + 1], d, sizeof(struct burn_drive));
+ pthread_mutex_init(&drive_array[drivetop + 1].access_lock, NULL);
+ return &drive_array[++drivetop];
+
+#endif /* ! Libburn_ticket_62_re_register_is_possiblE */
+
+}
+
+void burn_drive_release(struct burn_drive *d, int le)
+{
+ if (d->released)
+ burn_print(1, "second release on drive!\n");
+
+ /* ts A60906: one should not assume BURN_DRIVE_IDLE == 0 */
+ assert(d->busy == BURN_DRIVE_IDLE);
+
+ d->unlock(d);
+ if (le)
+ d->eject(d);
+
+ d->release(d);
+
+ d->status = BURN_DISC_UNREADY;
+ d->released = 1;
+ if (d->toc_entry)
+ free(d->toc_entry);
+ d->toc_entry = NULL;
+ d->toc_entries = 0;
+ if (d->disc != NULL) {
+ burn_disc_free(d->disc);
+ d->disc = NULL;
+ }
+}
+
+void burn_wait_all(void)
+{
+ unsigned int i;
+ int finished = 0;
+ struct burn_drive *d;
+
+ while (!finished) {
+ finished = 1;
+ d = drive_array;
+ for (i = burn_drive_count(); i > 0; --i, ++d) {
+
+ /* ts A60904 : ticket 62, contribution by elmom */
+ if (d->global_index==-1)
+ continue;
+
+ assert(d->released);
+ }
+ if (!finished)
+ sleep(1);
+ }
+}
+
+void burn_disc_erase_sync(struct burn_drive *d, int fast)
+{
+ burn_message_clear_queue();
+
+ burn_print(1, "erasing drive %s %s\n", d->idata->vendor,
+ d->idata->product);
+
+ /* ts A60825 : allow on parole to blank appendable CDs */
+ if ( ! (d->status == BURN_DISC_FULL ||
+ (d->status == BURN_DISC_APPENDABLE &&
+ ! libburn_back_hack_42) ) )
+ return;
+ d->cancel = 0;
+ d->busy = BURN_DRIVE_ERASING;
+ d->erase(d, fast);
+ /* reset the progress */
+ d->progress.session = 0;
+ d->progress.sessions = 1;
+ d->progress.track = 0;
+ d->progress.tracks = 1;
+ d->progress.index = 0;
+ d->progress.indices = 1;
+ d->progress.start_sector = 0;
+ d->progress.sectors = 0x10000;
+ d->progress.sector = 0;
+ /* read the initial 0 stage */
+ while (!d->test_unit_ready(d) && d->get_erase_progress(d) == 0)
+ sleep(1);
+ while ((d->progress.sector = d->get_erase_progress(d)) > 0 ||
+ !d->test_unit_ready(d))
+ sleep(1);
+ d->progress.sector = 0x10000;
+ d->busy = BURN_DRIVE_IDLE;
+}
+
+enum burn_disc_status burn_disc_get_status(struct burn_drive *d)
+{
+ assert(!d->released);
+ return d->status;
+}
+
+int burn_disc_erasable(struct burn_drive *d)
+{
+ return d->erasable;
+}
+enum burn_drive_status burn_drive_get_status(struct burn_drive *d,
+ struct burn_progress *p)
+{
+ if (p) {
+ memcpy(p, &(d->progress), sizeof(struct burn_progress));
+ /* TODO: add mutex */
+ }
+ return d->busy;
+}
+
+void burn_drive_cancel(struct burn_drive *d)
+{
+ pthread_mutex_lock(&d->access_lock);
+ d->cancel = 1;
+ pthread_mutex_unlock(&d->access_lock);
+}
+
+int burn_drive_get_block_types(struct burn_drive *d,
+ enum burn_write_types write_type)
+{
+ burn_print(12, "write type: %d\n", write_type);
+ assert( /* (write_type >= BURN_WRITE_PACKET) && */
+ (write_type <= BURN_WRITE_RAW));
+ return d->block_types[write_type];
+}
+
+static void strip_spaces(char *str)
+{
+ char *tmp;
+
+ tmp = str + strlen(str) - 1;
+ while (isspace(*tmp))
+ *(tmp--) = '\0';
+
+ tmp = str;
+ while (*tmp) {
+ if (isspace(*tmp) && isspace(*(tmp + 1))) {
+ char *tmp2;
+
+ for (tmp2 = tmp + 1; *tmp2; ++tmp2)
+ *(tmp2 - 1) = *tmp2;
+ *(tmp2 - 1) = '\0';
+ } else
+ ++tmp;
+ }
+}
+
+static int drive_getcaps(struct burn_drive *d, struct burn_drive_info *out)
+{
+ struct scsi_inquiry_data *id;
+
+ assert(d->idata);
+ assert(d->mdata);
+ if (!d->idata->valid || !d->mdata->valid)
+ return 0;
+
+ id = (struct scsi_inquiry_data *)d->idata;
+
+ memcpy(out->vendor, id->vendor, sizeof(id->vendor));
+ strip_spaces(out->vendor);
+ memcpy(out->product, id->product, sizeof(id->product));
+ strip_spaces(out->product);
+ memcpy(out->revision, id->revision, sizeof(id->revision));
+ strip_spaces(out->revision);
+ strncpy(out->location, d->devname, 16);
+ out->location[16] = '\0';
+ out->buffer_size = d->mdata->buffer_size;
+ out->read_dvdram = !!d->mdata->dvdram_read;
+ out->read_dvdr = !!d->mdata->dvdr_read;
+ out->read_dvdrom = !!d->mdata->dvdrom_read;
+ out->read_cdr = !!d->mdata->cdr_read;
+ out->read_cdrw = !!d->mdata->cdrw_read;
+ out->write_dvdram = !!d->mdata->dvdram_write;
+ out->write_dvdr = !!d->mdata->dvdr_write;
+ out->write_cdr = !!d->mdata->cdr_write;
+ out->write_cdrw = !!d->mdata->cdrw_write;
+ out->write_simulate = !!d->mdata->simulate;
+ out->c2_errors = !!d->mdata->c2_pointers;
+ out->drive = d;
+ /* update available block types for burners */
+ if (out->write_dvdram || out->write_dvdr ||
+ out->write_cdrw || out->write_cdr)
+ d->probe_write_modes(d);
+ out->tao_block_types = d->block_types[BURN_WRITE_TAO];
+ out->sao_block_types = d->block_types[BURN_WRITE_SAO];
+ out->raw_block_types = d->block_types[BURN_WRITE_RAW];
+ out->packet_block_types = d->block_types[BURN_WRITE_PACKET];
+ return 1;
+}
+
+int burn_drive_scan_sync(struct burn_drive_info *drives[],
+ unsigned int *n_drives)
+{
+ /* state vars for the scan process */
+ /* ts A60904 : did set some default values to feel comfortable */
+ static int scanning = 0, scanned = 0, found = 0;
+ static unsigned num_scanned = 0, count = 0;
+ unsigned int i;
+#ifdef Libburn_ticket_62_enable_redundant_asserT
+ struct burn_drive *d;
+#endif
+
+ assert(burn_running);
+
+ if (!scanning) {
+ scanning = 1;
+ /* make sure the drives aren't in use */
+ burn_wait_all(); /* make sure the queue cleans up
+ before checking for the released
+ state */
+
+ /* ts A60904 : ticket 62, contribution by elmom */
+ /* this is redundant with what is done in burn_wait_all() */
+#ifdef Libburn_ticket_62_enable_redundant_asserT
+ d = drive_array;
+ count = burn_drive_count();
+ for (i = 0; i < count; ++i, ++d)
+ assert(d->released == 1);
+#endif /* Libburn_ticket_62_enable_redundant_asserT */
+
+ /* refresh the lib's drives */
+ sg_enumerate();
+ ata_enumerate();
+ count = burn_drive_count();
+ if (count)
+ *drives =
+ malloc(sizeof(struct burn_drive_info) * count);
+ else
+ *drives = NULL;
+ *n_drives = scanned = found = num_scanned = 0;
+ }
+
+ for (i = 0; i < count; ++i) {
+ if (scanned & (1 << i))
+ continue; /* already scanned the device */
+
+ while (!drive_getcaps(&drive_array[i],
+ &(*drives)[num_scanned])) {
+ sleep(1);
+ }
+ scanned |= 1 << i;
+ found |= 1 << i;
+ num_scanned++;
+ (*n_drives)++;
+ }
+
+ if (num_scanned == count) {
+ /* done scanning */
+ scanning = 0;
+ return 1;
+ }
+ return 0;
+}
+
+
+void burn_drive_info_free(struct burn_drive_info drive_infos[])
+{
+/* ts A60904 : ticket 62, contribution by elmom */
+/* clarifying the meaning and the identity of the victim */
+
+ /* ts A60904 : This looks a bit weird.
+ burn_drive_info is not the manager of burn_drive but only its
+ spokesperson. To my knowlege drive_infos from burn_drive_scan()
+ are not memorized globally. */
+ if(drive_infos != NULL) /* and this NULL test is deadly necessary */
+ free((void *) drive_infos);
+
+ burn_drive_free_all();
+}
+
+/* Experimental API call */
+int burn_drive_info_forget(struct burn_drive_info *info, int force)
+{
+ int occup;
+ struct burn_drive *d;
+
+ d = info->drive;
+ occup = burn_drive_is_occupied(d);
+/*
+ fprintf(stderr, "libburn: experimental: occup == %d\n",occup);
+*/
+ if(occup <= -2)
+ return 2;
+ if(occup > 0)
+ if(force < 1)
+ return 0;
+ if(occup > 10)
+ return 0;
+
+ /* >>> do any drive calming here */;
+
+
+ burn_drive_force_idle(d);
+ if(occup > 0 && !burn_drive_is_released(d))
+ burn_drive_release(d,0);
+ burn_drive_free(d);
+ return 1;
+}
+
+struct burn_disc *burn_drive_get_disc(struct burn_drive *d)
+{
+ d->disc->refcnt++;
+ return d->disc;
+}
+
+void burn_drive_set_speed(struct burn_drive *d, int r, int w)
+{
+ d->set_speed(d, r, w);
+}
+
+int burn_msf_to_sectors(int m, int s, int f)
+{
+ return (m * 60 + s) * 75 + f;
+}
+
+void burn_sectors_to_msf(int sectors, int *m, int *s, int *f)
+{
+ *m = sectors / (60 * 75);
+ *s = (sectors - *m * 60 * 75) / 75;
+ *f = sectors - *m * 60 * 75 - *s * 75;
+}
+
+int burn_drive_get_read_speed(struct burn_drive *d)
+{
+ return d->mdata->max_read_speed;
+}
+
+int burn_drive_get_write_speed(struct burn_drive *d)
+{
+ return d->mdata->max_write_speed;
+}
+
+
+/* ts A51221 */
+static char *enumeration_whitelist[BURN_DRIVE_WHITELIST_LEN];
+static int enumeration_whitelist_top = -1;
+
+/** Add a device to the list of permissible drives. As soon as some entry is in
+ the whitelist all non-listed drives are banned from enumeration.
+ @return 1 success, <=0 failure
+*/
+int burn_drive_add_whitelist(char *device_address)
+{
+ char *new_item;
+ if(enumeration_whitelist_top+1 >= BURN_DRIVE_WHITELIST_LEN)
+ return 0;
+ enumeration_whitelist_top++;
+ new_item = malloc(strlen(device_address) + 1);
+ if (new_item == NULL)
+ return -1;
+ strcpy(new_item, device_address);
+ enumeration_whitelist[enumeration_whitelist_top] = new_item;
+ return 1;
+}
+
+/** Remove all drives from whitelist. This enables all possible drives. */
+void burn_drive_clear_whitelist(void)
+{
+ int i;
+ for (i = 0; i <= enumeration_whitelist_top; i++)
+ free(enumeration_whitelist[i]);
+ enumeration_whitelist_top = -1;
+}
+
+int burn_drive_is_banned(char *device_address)
+{
+ int i;
+ if(enumeration_whitelist_top<0)
+ return 0;
+ for (i = 0; i <= enumeration_whitelist_top; i++)
+ if (strcmp(enumeration_whitelist[i], device_address) == 0)
+ return 0;
+ return 1;
+}
+
+/* ts A60823 */
+/** Aquire a drive with known persistent address.
+*/
+int burn_drive_scan_and_grab(struct burn_drive_info *drive_infos[], char* adr,
+ int load)
+{
+ unsigned int n_drives;
+ int ret;
+
+ burn_drive_clear_whitelist();
+ burn_drive_add_whitelist(adr);
+/*
+ fprintf(stderr,"libburn: experimental: burn_drive_scan_and_grab(%s)\n",
+ adr);
+*/
+ while (!burn_drive_scan(drive_infos, &n_drives))
+ usleep(1002);
+ if (n_drives <= 0)
+ return 0;
+/*
+ fprintf(stderr, "libburn: experimental: n_drives == %d\n",n_drives);
+*/
+
+/* ts A60908 : seems we get rid of this :) */
+#ifdef Libburn_grab_release_and_grab_agaiN
+ if (load) {
+ /* RIP-14.5 + LITE-ON 48125S produce a false status
+ if tray was unloaded */
+ /* Therefore the first grab is just for loading */
+ ret= burn_drive_grab(drive_infos[0]->drive, 1);
+ if (ret != 1)
+ return -1;
+ burn_drive_release(drive_infos[0]->drive,0);
+ }
+#endif /* Libburn_grab_release_and_grab_agaiN */
+
+ ret = burn_drive_grab(drive_infos[0]->drive, load);
+ if (ret != 1)
+ return -1;
+ return 1;
+}
+
+/* ts A60823 */
+/** Inquire the persistent address of the given drive. */
+int burn_drive_get_adr(struct burn_drive_info *drive_info, char adr[])
+{
+ assert(strlen(drive_info->location) < BURN_DRIVE_ADR_LEN);
+ strcpy(adr,drive_info->location);
+ return 1;
+}
+
diff --git a/tags/ZeroTwoTwo/libburn/drive.h b/tags/ZeroTwoTwo/libburn/drive.h
new file mode 100644
index 00000000..2714658a
--- /dev/null
+++ b/tags/ZeroTwoTwo/libburn/drive.h
@@ -0,0 +1,57 @@
+/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */
+
+#ifndef __DRIVE
+#define __DRIVE
+
+#include "libburn.h"
+#include "toc.h"
+#include "structure.h"
+
+struct burn_drive;
+struct command;
+struct mempage;
+
+#define LEAD_IN 1
+#define GAP 2
+#define USER_DATA 3
+#define LEAD_OUT 4
+#define SYNC 5
+
+#define SESSION_LEADOUT_ENTRY(d,s) (d)->toc->session[(s)].leadout_entry
+
+#define CURRENT_SESSION_START(d) \
+ burn_msf_to_lba(d->toc->session[d->currsession].start_m, \
+ d->toc->session[d->currsession].start_s, \
+ d->toc->session[d->currsession].start_f)
+
+#define SESSION_END(d,s) \
+ TOC_ENTRY_PLBA((d)->toc, SESSION_LEADOUT_ENTRY((d), (s)))
+
+#define PREVIOUS_SESSION_END(d) \
+ TOC_ENTRY_PLBA((d)->toc, SESSION_LEADOUT_ENTRY((d), (d)->currsession-1))
+
+#define LAST_SESSION_END(d) \
+ TOC_ENTRY_PLBA((d)->toc, \
+ SESSION_LEADOUT_ENTRY((d), (d)->toc->sessions-1))
+
+struct burn_drive *burn_drive_register(struct burn_drive *);
+
+unsigned int burn_drive_count(void);
+void burn_wait_all(void);
+int burn_sector_length_write(struct burn_drive *d);
+int burn_track_control(struct burn_drive *d, int);
+void burn_write_empty_sector(int fd);
+void burn_write_empty_subcode(int fd);
+void burn_drive_free(struct burn_drive *d);
+void burn_drive_free_all(void);
+
+int burn_drive_scan_sync(struct burn_drive_info *drives[],
+ unsigned int *n_drives);
+void burn_disc_erase_sync(struct burn_drive *d, int fast);
+int burn_drive_get_block_types(struct burn_drive *d,
+ enum burn_write_types write_type);
+
+/* ts A60822 */
+int burn_drive_is_open(struct burn_drive *d);
+
+#endif /* __DRIVE */
diff --git a/tags/ZeroTwoTwo/libburn/error.h b/tags/ZeroTwoTwo/libburn/error.h
new file mode 100644
index 00000000..74d4f68d
--- /dev/null
+++ b/tags/ZeroTwoTwo/libburn/error.h
@@ -0,0 +1,8 @@
+/* -*- indent-tabs-mode; t; tab-width: 8; c-basic-offset: 8; -*- */
+
+#ifndef __ERROR_H
+#define __ERROR_H
+
+#define BE_CANCELLED 1
+
+#endif /* __ERROR_H */
diff --git a/tags/ZeroTwoTwo/libburn/file.c b/tags/ZeroTwoTwo/libburn/file.c
new file mode 100644
index 00000000..809b8428
--- /dev/null
+++ b/tags/ZeroTwoTwo/libburn/file.c
@@ -0,0 +1,187 @@
+/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */
+
+#include
+#include
+#include
+#include
+#include
+#include "source.h"
+#include "libburn.h"
+#include "file.h"
+
+/* main channel data can be padded on read, but 0 padding the subs will make
+an unreadable disc */
+
+
+/* This is a generic OS oriented function wrapper which compensates
+ shortcommings of read() in respect to a guaranteed amount of return data.
+ See man 2 read , paragraph "RETURN VALUE".
+ Possibly libburn/file.c is not the right storage location for this.
+ To make it ready for a move, this function is not declared static.
+*/
+static int read_full_buffer(int fd, unsigned char *buffer, int size)
+{
+ int ret,summed_ret = 0;
+
+ /* make safe against partial buffer returns */
+ while (1) {
+ ret = read(fd, buffer + summed_ret, size - summed_ret);
+ if (ret <= 0)
+ break;
+ summed_ret += ret;
+ if (summed_ret >= size)
+ break;
+ }
+ if (ret < 0) /* error encountered. abort immediately */
+ return ret;
+ return summed_ret;
+}
+
+
+static int file_read(struct burn_source *source,
+ unsigned char *buffer,
+ int size)
+{
+ struct burn_source_fd *fs = source->data;
+
+ return read_full_buffer(fs->datafd, buffer, size);
+}
+
+static int file_read_sub(struct burn_source *source,
+ unsigned char *buffer,
+ int size)
+{
+ struct burn_source_file *fs = source->data;
+
+ return read_full_buffer(fs->subfd, buffer, size);
+}
+
+static void file_free(struct burn_source *source)
+{
+ struct burn_source_file *fs = source->data;
+
+ close(fs->datafd);
+ if (source->read_sub)
+ close(fs->subfd);
+ free(fs);
+}
+
+static off_t file_size(struct burn_source *source)
+{
+ struct stat buf;
+ struct burn_source_file *fs = source->data;
+
+ if (fstat(fs->datafd, &buf) == -1)
+ return (off_t) 0;
+ /* for now we keep it compatible to the old (int) return value */
+ if(buf.st_size >= 1308622848) /* 2 GB - 800 MB to prevent rollover */
+ return (off_t) 1308622848;
+ return (off_t) buf.st_size;
+}
+
+struct burn_source *burn_file_source_new(const char *path, const char *subpath)
+{
+ struct burn_source_file *fs;
+ struct burn_source *src;
+ int fd1, fd2 = 0;
+
+ if (!path)
+ return NULL;
+ fd1 = open(path, O_RDONLY);
+ if (fd1 == -1)
+ return NULL;
+ if (subpath) {
+ fd2 = open(subpath, O_RDONLY);
+ if (fd2 == -1) {
+ close(fd1);
+ return NULL;
+ }
+ }
+ fs = malloc(sizeof(struct burn_source_file));
+ fs->datafd = fd1;
+
+ if (subpath)
+ fs->subfd = fd2;
+
+ src = burn_source_new();
+ src->read = file_read;
+ if (subpath)
+ src->read_sub = file_read_sub;
+
+ src->get_size = file_size;
+ src->free_data = file_free;
+ src->data = fs;
+ return src;
+}
+
+
+/* ------ provisory location for the new source subclass fd --------- */
+
+static off_t fd_get_size(struct burn_source *source)
+{
+ struct stat buf;
+ struct burn_source_fd *fs = source->data;
+
+ if (fs->fixed_size > 0)
+ return fs->fixed_size;
+ if (fstat(fs->datafd, &buf) == -1)
+ return (off_t) 0;
+ /* for now we keep it compatible to the old (int) return value */
+ if (buf.st_size >= 1308622848) /* 2 GB - 800 MB to prevent rollover */
+ return (off_t) 1308622848;
+ return buf.st_size;
+}
+
+static int fd_read(struct burn_source *source,
+ unsigned char *buffer,
+ int size)
+{
+ struct burn_source_fd *fs = source->data;
+
+ return read_full_buffer(fs->datafd, buffer, size);
+}
+
+
+static int fd_read_sub(struct burn_source *source,
+ unsigned char *buffer,
+ int size)
+{
+ struct burn_source_fd *fs = source->data;
+
+ return read_full_buffer(fs->subfd, buffer, size);
+}
+
+
+static void fd_free_data(struct burn_source *source)
+{
+ struct burn_source_fd *fs = source->data;
+
+ close(fs->datafd);
+ if (source->read_sub)
+ close(fs->subfd);
+ free(fs);
+}
+
+
+struct burn_source *burn_fd_source_new(int datafd, int subfd, off_t size)
+{
+ struct burn_source_fd *fs;
+ struct burn_source *src;
+
+ if (datafd == -1)
+ return NULL;
+ fs = malloc(sizeof(struct burn_source_fd));
+ fs->datafd = datafd;
+ fs->subfd = subfd;
+ fs->fixed_size = size;
+
+ src = burn_source_new();
+ src->read = fd_read;
+ if(subfd != -1)
+ src->read = fd_read_sub;
+ src->get_size = fd_get_size;
+ src->free_data = fd_free_data;
+ src->data = fs;
+ return src;
+}
+
diff --git a/tags/ZeroTwoTwo/libburn/file.h b/tags/ZeroTwoTwo/libburn/file.h
new file mode 100644
index 00000000..96c1a692
--- /dev/null
+++ b/tags/ZeroTwoTwo/libburn/file.h
@@ -0,0 +1,22 @@
+/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */
+
+#ifndef BURN__FILE_H
+#define BURN__FILE_H
+
+struct burn_source_file
+{
+ int datafd;
+ int subfd;
+};
+
+
+/* ------ provisory location for the new source subclass fd --------- */
+
+struct burn_source_fd
+{
+ int datafd;
+ int subfd;
+ off_t fixed_size;
+};
+
+#endif /* LIBBURN__FILE_H */
diff --git a/tags/ZeroTwoTwo/libburn/init.c b/tags/ZeroTwoTwo/libburn/init.c
new file mode 100644
index 00000000..552229dc
--- /dev/null
+++ b/tags/ZeroTwoTwo/libburn/init.c
@@ -0,0 +1,80 @@
+/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */
+
+#include
+#include
+#include
+#include
+#include "init.h"
+#include "sg.h"
+#include "error.h"
+#include "libburn.h"
+#include "drive.h"
+
+/* ts A60825 : The storage location for back_hacks.h variables. */
+#define BURN_BACK_HACKS_INIT 1
+#include "back_hacks.h"
+
+int burn_running = 0;
+
+/* ts A60813 : wether to use O_EXCL and/or O_NONBLOCK in libburn/sg.c */
+int burn_sg_open_o_excl = 1;
+
+/* O_NONBLOCK was hardcoded in enumerate_ata() which i hardly use.
+ For enumerate_sg() it seems ok.
+ So it should stay default mode until enumerate_ata() without O_NONBLOCK
+ has been thoroughly tested. */
+int burn_sg_open_o_nonblock = 1;
+
+/* wether to take a busy drive as an error */
+/* Caution: this is implemented by a rough hack and eventually leads
+ to unconditional abort of the process */
+int burn_sg_open_abort_busy = 0;
+
+
+int burn_initialize(void)
+{
+ if (burn_running)
+ return 1;
+
+ burn_running = 1;
+ return 1;
+}
+
+void burn_finish(void)
+{
+ assert(burn_running);
+
+ burn_wait_all();
+
+ /* ts A60904 : ticket 62, contribution by elmom : name addon "_all" */
+ burn_drive_free_all();
+
+ burn_running = 0;
+}
+
+
+/* ts A60813 */
+/** Set parameters for behavior on opening device files. To be called early
+ after burn_initialize() and before any bus scan. But not mandatory at all.
+ @param exclusive Try to open only devices which are not marked as busy
+ and try to mark them busy if opened sucessfully. (O_EXCL)
+ There are kernels which simply don't care about O_EXCL.
+ Some have it off, some have it on, some are switchable.
+ @param blocking Try to wait for drives which do not open immediately but
+ also do not return an error as well. (O_NONBLOCK)
+ This might stall indefinitely with /dev/hdX hard disks.
+ @param abort_on_busy Unconditionally abort process when a non blocking
+ exclusive opening attempt indicates a busy drive.
+ Use this only after thorough tests with your app.
+ Parameter value 1 enables a feature, 0 disables.
+ Default is (1,0,0). Have a good reason before you change it.
+*/
+void burn_preset_device_open(int exclusive, int blocking, int abort_on_busy)
+{
+ assert(burn_running);
+
+ burn_sg_open_o_excl= !!exclusive;
+ burn_sg_open_o_nonblock= !blocking;
+ burn_sg_open_abort_busy= !!abort_on_busy;
+}
+
diff --git a/tags/ZeroTwoTwo/libburn/init.h b/tags/ZeroTwoTwo/libburn/init.h
new file mode 100644
index 00000000..c3e9b9a8
--- /dev/null
+++ b/tags/ZeroTwoTwo/libburn/init.h
@@ -0,0 +1,8 @@
+/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */
+
+#ifndef BURN__INIT_H
+#define BURN__INIT_H
+
+extern int burn_running;
+
+#endif /* BURN__INIT_H */
diff --git a/tags/ZeroTwoTwo/libburn/lec.c b/tags/ZeroTwoTwo/libburn/lec.c
new file mode 100644
index 00000000..9141593a
--- /dev/null
+++ b/tags/ZeroTwoTwo/libburn/lec.c
@@ -0,0 +1,451 @@
+/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */
+
+/* borrowed HEAVILY from cdrdao */
+
+#include
+#include "lec.h"
+
+#define LEC_HEADER_OFFSET 12
+#define LEC_MODE1_P_PARITY_OFFSET 2076
+#define LEC_MODE1_Q_PARITY_OFFSET 2248
+
+static unsigned char gf8_ilog[255] = {
+ 1, 2, 4, 8, 16, 32, 64, 128, 29, 58, 116, 232, 205, 135, 19, 38, 76,
+ 152, 45, 90, 180, 117, 234, 201, 143, 3, 6, 12, 24, 48, 96,
+ 192, 157, 39, 78, 156, 37, 74, 148, 53, 106, 212, 181, 119,
+ 238, 193, 159, 35, 70, 140, 5, 10, 20, 40, 80, 160, 93, 186,
+ 105, 210, 185, 111, 222, 161, 95, 190, 97, 194, 153, 47, 94,
+ 188, 101, 202, 137, 15, 30, 60, 120, 240, 253, 231, 211, 187,
+ 107, 214, 177, 127, 254, 225, 223, 163, 91, 182, 113, 226, 217,
+ 175, 67, 134, 17, 34, 68, 136, 13, 26, 52, 104, 208, 189, 103,
+ 206, 129, 31, 62, 124, 248, 237, 199, 147, 59, 118, 236, 197,
+ 151, 51, 102, 204, 133, 23, 46, 92, 184, 109, 218, 169, 79,
+ 158, 33, 66, 132, 21, 42, 84, 168, 77, 154, 41, 82, 164, 85,
+ 170, 73, 146, 57, 114, 228, 213, 183, 115, 230, 209, 191, 99,
+ 198, 145, 63, 126, 252, 229, 215, 179, 123, 246, 241, 255, 227,
+ 219, 171, 75, 150, 49, 98, 196, 149, 55, 110, 220, 165, 87,
+ 174, 65, 130, 25, 50, 100, 200, 141, 7, 14, 28, 56, 112, 224,
+ 221, 167, 83, 166, 81, 162, 89, 178, 121, 242, 249, 239, 195,
+ 155, 43, 86, 172, 69, 138, 9, 18, 36, 72, 144, 61, 122, 244,
+ 245, 247, 243, 251, 235, 203, 139, 11, 22, 44, 88, 176, 125,
+ 250, 233, 207, 131, 27, 54, 108, 216, 173, 71, 142,
+};
+static unsigned char gf8_log[256] = {
+ 0, 0, 1, 25, 2, 50, 26, 198, 3, 223, 51, 238, 27, 104, 199, 75, 4, 100,
+ 224, 14, 52, 141, 239, 129, 28, 193, 105, 248, 200, 8, 76, 113,
+ 5, 138, 101, 47, 225, 36, 15, 33, 53, 147, 142, 218, 240, 18,
+ 130, 69, 29, 181, 194, 125, 106, 39, 249, 185, 201, 154, 9,
+ 120, 77, 228, 114, 166, 6, 191, 139, 98, 102, 221, 48, 253,
+ 226, 152, 37, 179, 16, 145, 34, 136, 54, 208, 148, 206, 143,
+ 150, 219, 189, 241, 210, 19, 92, 131, 56, 70, 64, 30, 66, 182,
+ 163, 195, 72, 126, 110, 107, 58, 40, 84, 250, 133, 186, 61,
+ 202, 94, 155, 159, 10, 21, 121, 43, 78, 212, 229, 172, 115,
+ 243, 167, 87, 7, 112, 192, 247, 140, 128, 99, 13, 103, 74, 222,
+ 237, 49, 197, 254, 24, 227, 165, 153, 119, 38, 184, 180, 124,
+ 17, 68, 146, 217, 35, 32, 137, 46, 55, 63, 209, 91, 149, 188,
+ 207, 205, 144, 135, 151, 178, 220, 252, 190, 97, 242, 86, 211,
+ 171, 20, 42, 93, 158, 132, 60, 57, 83, 71, 109, 65, 162, 31,
+ 45, 67, 216, 183, 123, 164, 118, 196, 23, 73, 236, 127, 12,
+ 111, 246, 108, 161, 59, 82, 41, 157, 85, 170, 251, 96, 134,
+ 177, 187, 204, 62, 90, 203, 89, 95, 176, 156, 169, 160, 81, 11,
+ 245, 22, 235, 122, 117, 44, 215, 79, 174, 213, 233, 230, 231,
+ 173, 232, 116, 214, 244, 234, 168, 80, 88, 175,
+};
+static unsigned char gf8_q_coeffs[2][45] = {
+ {97, 251, 133, 60, 82, 160, 155, 201, 8, 112, 246, 11, 21, 42, 157,
+ 169, 80, 174, 232, 230, 172, 211, 241, 18, 68, 216, 44, 121, 9, 200,
+ 75, 103, 221, 252, 96, 176, 88, 167, 114, 76, 199, 26, 1, 0, 0},
+ {190, 96, 250, 132, 59, 81, 159, 154, 200, 7, 111, 245, 10, 20, 41,
+ 156, 168, 79, 173, 231, 229, 171, 210, 240, 17, 67, 215, 43, 120, 8,
+ 199, 74, 102, 220, 251, 95, 175, 87, 166, 113, 75, 198, 25, 0, 0}
+};
+static unsigned char gf8_p_coeffs[2][26] = {
+ {230, 172, 211, 241, 18, 68, 216, 44, 121, 9, 200, 75, 103, 221, 252,
+ 96, 176, 88, 167, 114, 76, 199, 26, 1, 0, 0},
+ {231, 229, 171, 210, 240, 17, 67, 215, 43, 120, 8, 199, 74, 102, 220,
+ 251, 95, 175, 87, 166, 113, 75, 198, 25, 0, 0}
+};
+
+static unsigned char yellowbook_scrambler[2340] = {
+ 1, 128, 0, 96, 0, 40, 0, 30, 128, 8, 96, 6, 168, 2, 254, 129, 128, 96,
+ 96, 40, 40, 30, 158,
+ 136, 104, 102, 174, 170, 252, 127, 1, 224, 0, 72, 0, 54, 128, 22, 224,
+ 14, 200, 4, 86, 131, 126, 225,
+ 224, 72, 72, 54, 182, 150, 246, 238, 198, 204, 82, 213, 253, 159, 1,
+ 168, 0, 126, 128, 32, 96, 24, 40,
+ 10, 158, 135, 40, 98, 158, 169, 168, 126, 254, 160, 64, 120, 48, 34,
+ 148, 25, 175, 74, 252, 55, 1, 214,
+ 128, 94, 224, 56, 72, 18, 182, 141, 182, 229, 182, 203, 54, 215, 86,
+ 222, 190, 216, 112, 90, 164, 59, 59,
+ 83, 83, 125, 253, 225, 129, 136, 96, 102, 168, 42, 254, 159, 0, 104, 0,
+ 46, 128, 28, 96, 9, 232, 6,
+ 206, 130, 212, 97, 159, 104, 104, 46, 174, 156, 124, 105, 225, 238,
+ 200, 76, 86, 181, 254, 247, 0, 70, 128,
+ 50, 224, 21, 136, 15, 38, 132, 26, 227, 75, 9, 247, 70, 198, 178, 210,
+ 245, 157, 135, 41, 162, 158, 249,
+ 168, 66, 254, 177, 128, 116, 96, 39, 104, 26, 174, 139, 60, 103, 81,
+ 234, 188, 79, 49, 244, 20, 71, 79,
+ 114, 180, 37, 183, 91, 54, 187, 86, 243, 126, 197, 224, 83, 8, 61, 198,
+ 145, 146, 236, 109, 141, 237, 165,
+ 141, 187, 37, 179, 91, 53, 251, 87, 3, 126, 129, 224, 96, 72, 40, 54,
+ 158, 150, 232, 110, 206, 172, 84,
+ 125, 255, 97, 128, 40, 96, 30, 168, 8, 126, 134, 160, 98, 248, 41, 130,
+ 158, 225, 168, 72, 126, 182, 160,
+ 118, 248, 38, 194, 154, 209, 171, 28, 127, 73, 224, 54, 200, 22, 214,
+ 142, 222, 228, 88, 75, 122, 183, 99,
+ 54, 169, 214, 254, 222, 192, 88, 80, 58, 188, 19, 49, 205, 212, 85,
+ 159, 127, 40, 32, 30, 152, 8, 106,
+ 134, 175, 34, 252, 25, 129, 202, 224, 87, 8, 62, 134, 144, 98, 236, 41,
+ 141, 222, 229, 152, 75, 42, 183,
+ 95, 54, 184, 22, 242, 142, 197, 164, 83, 59, 125, 211, 97, 157, 232,
+ 105, 142, 174, 228, 124, 75, 97, 247,
+ 104, 70, 174, 178, 252, 117, 129, 231, 32, 74, 152, 55, 42, 150, 159,
+ 46, 232, 28, 78, 137, 244, 102, 199,
+ 106, 210, 175, 29, 188, 9, 177, 198, 244, 82, 199, 125, 146, 161, 173,
+ 184, 125, 178, 161, 181, 184, 119, 50,
+ 166, 149, 186, 239, 51, 12, 21, 197, 207, 19, 20, 13, 207, 69, 148, 51,
+ 47, 85, 220, 63, 25, 208, 10,
+ 220, 7, 25, 194, 138, 209, 167, 28, 122, 137, 227, 38, 201, 218, 214,
+ 219, 30, 219, 72, 91, 118, 187, 102,
+ 243, 106, 197, 239, 19, 12, 13, 197, 197, 147, 19, 45, 205, 221, 149,
+ 153, 175, 42, 252, 31, 1, 200, 0,
+ 86, 128, 62, 224, 16, 72, 12, 54, 133, 214, 227, 30, 201, 200, 86, 214,
+ 190, 222, 240, 88, 68, 58, 179,
+ 83, 53, 253, 215, 1, 158, 128, 104, 96, 46, 168, 28, 126, 137, 224,
+ 102, 200, 42, 214, 159, 30, 232, 8,
+ 78, 134, 180, 98, 247, 105, 134, 174, 226, 252, 73, 129, 246, 224, 70,
+ 200, 50, 214, 149, 158, 239, 40, 76,
+ 30, 181, 200, 119, 22, 166, 142, 250, 228, 67, 11, 113, 199, 100, 82,
+ 171, 125, 191, 97, 176, 40, 116, 30,
+ 167, 72, 122, 182, 163, 54, 249, 214, 194, 222, 209, 152, 92, 106, 185,
+ 239, 50, 204, 21, 149, 207, 47, 20,
+ 28, 15, 73, 196, 54, 211, 86, 221, 254, 217, 128, 90, 224, 59, 8, 19,
+ 70, 141, 242, 229, 133, 139, 35,
+ 39, 89, 218, 186, 219, 51, 27, 85, 203, 127, 23, 96, 14, 168, 4, 126,
+ 131, 96, 97, 232, 40, 78, 158,
+ 180, 104, 119, 110, 166, 172, 122, 253, 227, 1, 137, 192, 102, 208, 42,
+ 220, 31, 25, 200, 10, 214, 135, 30,
+ 226, 136, 73, 166, 182, 250, 246, 195, 6, 209, 194, 220, 81, 153, 252,
+ 106, 193, 239, 16, 76, 12, 53, 197,
+ 215, 19, 30, 141, 200, 101, 150, 171, 46, 255, 92, 64, 57, 240, 18,
+ 196, 13, 147, 69, 173, 243, 61, 133,
+ 209, 163, 28, 121, 201, 226, 214, 201, 158, 214, 232, 94, 206, 184, 84,
+ 114, 191, 101, 176, 43, 52, 31, 87,
+ 72, 62, 182, 144, 118, 236, 38, 205, 218, 213, 155, 31, 43, 72, 31,
+ 118, 136, 38, 230, 154, 202, 235, 23,
+ 15, 78, 132, 52, 99, 87, 105, 254, 174, 192, 124, 80, 33, 252, 24, 65,
+ 202, 176, 87, 52, 62, 151, 80,
+ 110, 188, 44, 113, 221, 228, 89, 139, 122, 231, 99, 10, 169, 199, 62,
+ 210, 144, 93, 172, 57, 189, 210, 241,
+ 157, 132, 105, 163, 110, 249, 236, 66, 205, 241, 149, 132, 111, 35,
+ 108, 25, 237, 202, 205, 151, 21, 174, 143,
+ 60, 100, 17, 235, 76, 79, 117, 244, 39, 7, 90, 130, 187, 33, 179, 88,
+ 117, 250, 167, 3, 58, 129, 211,
+ 32, 93, 216, 57, 154, 146, 235, 45, 143, 93, 164, 57, 187, 82, 243,
+ 125, 133, 225, 163, 8, 121, 198, 162,
+ 210, 249, 157, 130, 233, 161, 142, 248, 100, 66, 171, 113, 191, 100,
+ 112, 43, 100, 31, 107, 72, 47, 118, 156,
+ 38, 233, 218, 206, 219, 20, 91, 79, 123, 116, 35, 103, 89, 234, 186,
+ 207, 51, 20, 21, 207, 79, 20, 52,
+ 15, 87, 68, 62, 179, 80, 117, 252, 39, 1, 218, 128, 91, 32, 59, 88, 19,
+ 122, 141, 227, 37, 137, 219,
+ 38, 219, 90, 219, 123, 27, 99, 75, 105, 247, 110, 198, 172, 82, 253,
+ 253, 129, 129, 160, 96, 120, 40, 34,
+ 158, 153, 168, 106, 254, 175, 0, 124, 0, 33, 192, 24, 80, 10, 188, 7,
+ 49, 194, 148, 81, 175, 124, 124,
+ 33, 225, 216, 72, 90, 182, 187, 54, 243, 86, 197, 254, 211, 0, 93, 192,
+ 57, 144, 18, 236, 13, 141, 197,
+ 165, 147, 59, 45, 211, 93, 157, 249, 169, 130, 254, 225, 128, 72, 96,
+ 54, 168, 22, 254, 142, 192, 100, 80,
+ 43, 124, 31, 97, 200, 40, 86, 158, 190, 232, 112, 78, 164, 52, 123, 87,
+ 99, 126, 169, 224, 126, 200, 32,
+ 86, 152, 62, 234, 144, 79, 44, 52, 29, 215, 73, 158, 182, 232, 118,
+ 206, 166, 212, 122, 223, 99, 24, 41,
+ 202, 158, 215, 40, 94, 158, 184, 104, 114, 174, 165, 188, 123, 49, 227,
+ 84, 73, 255, 118, 192, 38, 208, 26,
+ 220, 11, 25, 199, 74, 210, 183, 29, 182, 137, 182, 230, 246, 202, 198,
+ 215, 18, 222, 141, 152, 101, 170, 171,
+ 63, 63, 80, 16, 60, 12, 17, 197, 204, 83, 21, 253, 207, 1, 148, 0, 111,
+ 64, 44, 48, 29, 212, 9,
+ 159, 70, 232, 50, 206, 149, 148, 111, 47, 108, 28, 45, 201, 221, 150,
+ 217, 174, 218, 252, 91, 1, 251, 64,
+ 67, 112, 49, 228, 20, 75, 79, 119, 116, 38, 167, 90, 250, 187, 3, 51,
+ 65, 213, 240, 95, 4, 56, 3,
+ 82, 129, 253, 160, 65, 184, 48, 114, 148, 37, 175, 91, 60, 59, 81, 211,
+ 124, 93, 225, 249, 136, 66, 230,
+ 177, 138, 244, 103, 7, 106, 130, 175, 33, 188, 24, 113, 202, 164, 87,
+ 59, 126, 147, 96, 109, 232, 45, 142,
+ 157, 164, 105, 187, 110, 243, 108, 69, 237, 243, 13, 133, 197, 163, 19,
+ 57, 205, 210, 213, 157, 159, 41, 168,
+ 30, 254, 136, 64, 102, 176, 42, 244, 31, 7, 72, 2, 182, 129, 182, 224,
+ 118, 200, 38, 214, 154, 222, 235,
+ 24, 79, 74, 180, 55, 55, 86, 150, 190, 238, 240, 76, 68, 53, 243, 87,
+ 5, 254, 131, 0, 97, 192, 40,
+ 80, 30, 188, 8, 113, 198, 164, 82, 251, 125, 131, 97, 161, 232, 120,
+ 78, 162, 180, 121, 183, 98, 246, 169,
+ 134, 254, 226, 192, 73, 144, 54, 236, 22, 205, 206, 213, 148, 95, 47,
+ 120, 28, 34, 137, 217, 166, 218, 250,
+ 219, 3, 27, 65, 203, 112, 87, 100, 62, 171, 80, 127, 124, 32, 33, 216,
+ 24, 90, 138, 187, 39, 51, 90,
+ 149, 251, 47, 3, 92, 1, 249, 192, 66, 208, 49, 156, 20, 105, 207, 110,
+ 212, 44, 95, 93, 248, 57, 130,
+ 146, 225, 173, 136, 125, 166, 161, 186, 248, 115, 2, 165, 193, 187, 16,
+ 115, 76, 37, 245, 219, 7, 27, 66,
+ 139, 113, 167, 100, 122, 171, 99, 63, 105, 208, 46, 220, 28, 89, 201,
+ 250, 214, 195, 30, 209, 200, 92, 86,
+ 185, 254, 242, 192, 69, 144, 51, 44, 21, 221, 207, 25, 148, 10, 239,
+ 71, 12, 50, 133, 213, 163, 31, 57,
+ 200, 18, 214, 141, 158, 229, 168, 75, 62, 183, 80, 118, 188, 38, 241,
+ 218, 196, 91, 19, 123, 77, 227, 117,
+ 137, 231, 38, 202, 154, 215, 43, 30, 159, 72, 104, 54, 174, 150, 252,
+ 110, 193, 236, 80, 77, 252, 53, 129,
+ 215, 32, 94, 152, 56, 106, 146, 175, 45, 188, 29, 177, 201, 180, 86,
+ 247, 126, 198, 160, 82, 248, 61, 130,
+ 145, 161, 172, 120, 125, 226, 161, 137, 184, 102, 242, 170, 197, 191,
+ 19, 48, 13, 212, 5, 159, 67, 40, 49,
+ 222, 148, 88, 111, 122, 172, 35, 61, 217, 209, 154, 220, 107, 25, 239,
+ 74, 204, 55, 21, 214, 143, 30, 228,
+ 8, 75, 70, 183, 114, 246, 165, 134, 251, 34, 195, 89, 145, 250, 236,
+ 67, 13, 241, 197, 132, 83, 35, 125,
+ 217, 225, 154, 200, 107, 22, 175, 78, 252, 52, 65, 215, 112, 94, 164,
+ 56, 123, 82, 163, 125, 185, 225, 178,
+ 200, 117, 150, 167, 46, 250, 156, 67, 41, 241, 222, 196, 88, 83, 122,
+ 189, 227, 49, 137, 212, 102, 223, 106,
+ 216, 47, 26, 156, 11, 41, 199, 94, 210, 184, 93, 178, 185, 181, 178,
+ 247, 53, 134, 151, 34, 238, 153, 140,
+ 106, 229, 239, 11, 12, 7, 69, 194, 179, 17, 181, 204, 119, 21, 230,
+ 143, 10, 228, 7, 11, 66, 135, 113,
+ 162, 164, 121, 187, 98, 243, 105, 133, 238, 227, 12, 73, 197, 246, 211,
+ 6, 221, 194, 217, 145, 154, 236, 107,
+ 13, 239, 69, 140, 51, 37, 213, 219, 31, 27, 72, 11, 118, 135, 102, 226,
+ 170, 201, 191, 22, 240, 14, 196,
+ 4, 83, 67, 125, 241, 225, 132, 72, 99, 118, 169, 230, 254, 202, 192,
+ 87, 16, 62, 140, 16, 101, 204, 43,
+ 21, 223, 79, 24, 52, 10, 151, 71, 46, 178, 156, 117, 169, 231, 62, 202,
+ 144, 87, 44, 62, 157, 208, 105,
+ 156, 46, 233, 220, 78, 217, 244, 90, 199, 123, 18, 163, 77, 185, 245,
+ 178, 199, 53, 146, 151, 45, 174, 157,
+ 188, 105, 177, 238, 244, 76, 71, 117, 242, 167, 5, 186, 131, 51, 33,
+ 213, 216, 95, 26, 184, 11, 50, 135,
+ 85, 162, 191, 57, 176, 18, 244, 13, 135, 69, 162, 179, 57, 181, 210,
+ 247, 29, 134, 137, 162, 230, 249, 138,
+ 194, 231, 17, 138, 140, 103, 37, 234, 155, 15, 43, 68, 31, 115, 72, 37,
+ 246, 155, 6, 235, 66, 207, 113,
+ 148, 36, 111, 91, 108, 59, 109, 211, 109, 157, 237, 169, 141, 190, 229,
+ 176, 75, 52, 55, 87, 86, 190, 190,
+ 240, 112, 68, 36, 51, 91, 85, 251, 127, 3, 96, 1, 232, 0, 78, 128, 52,
+ 96, 23, 104, 14, 174, 132,
+ 124, 99, 97, 233, 232, 78, 206, 180, 84, 119, 127, 102, 160, 42, 248,
+ 31, 2, 136, 1, 166, 128, 122, 224,
+ 35, 8, 25, 198, 138, 210, 231, 29, 138, 137, 167, 38, 250, 154, 195,
+ 43, 17, 223, 76, 88, 53, 250, 151,
+ 3, 46, 129, 220, 96, 89, 232, 58, 206, 147, 20, 109, 207, 109, 148, 45,
+ 175, 93, 188, 57, 177, 210, 244,
+ 93, 135, 121, 162, 162, 249, 185, 130, 242, 225, 133, 136, 99, 38, 169,
+ 218, 254, 219, 0, 91, 64, 59, 112,
+ 19, 100, 13, 235, 69, 143, 115, 36, 37, 219, 91, 27, 123, 75, 99, 119,
+ 105, 230, 174, 202, 252, 87, 1,
+ 254, 128, 64, 96, 48, 40, 20, 30, 143, 72, 100, 54, 171, 86, 255, 126,
+ 192, 32, 80, 24, 60, 10, 145,
+ 199, 44, 82, 157, 253, 169, 129, 190, 224, 112, 72, 36, 54, 155, 86,
+ 235, 126, 207, 96, 84, 40, 63, 94,
+ 144, 56, 108, 18, 173, 205, 189, 149, 177, 175, 52, 124, 23, 97, 206,
+ 168, 84, 126, 191, 96, 112, 40, 36,
+ 30, 155, 72, 107, 118, 175, 102, 252, 42, 193, 223, 16, 88, 12, 58,
+ 133, 211, 35, 29, 217, 201, 154, 214,
+ 235, 30, 207, 72, 84, 54, 191, 86, 240, 62, 196, 16, 83, 76, 61, 245,
+ 209, 135, 28, 98, 137, 233, 166,
+ 206, 250, 212, 67, 31, 113, 200, 36, 86, 155, 126, 235, 96, 79, 104,
+ 52, 46, 151, 92, 110, 185, 236, 114,
+ 205, 229, 149, 139, 47, 39, 92, 26, 185, 203, 50, 215, 85, 158, 191,
+ 40, 112, 30, 164, 8, 123, 70, 163,
+ 114, 249, 229, 130, 203, 33, 151, 88, 110, 186, 172, 115, 61, 229, 209,
+ 139, 28, 103, 73, 234, 182, 207, 54,
+ 212, 22, 223, 78, 216, 52, 90, 151, 123, 46, 163, 92, 121, 249, 226,
+ 194, 201, 145, 150, 236, 110, 205, 236,
+ 85, 141, 255, 37, 128, 27, 32, 11, 88, 7, 122, 130, 163, 33, 185, 216,
+ 114, 218, 165, 155, 59, 43, 83,
+ 95, 125, 248, 33, 130, 152, 97, 170, 168, 127, 62, 160, 16, 120, 12,
+ 34, 133, 217, 163, 26, 249, 203, 2,
+ 215, 65, 158, 176, 104, 116, 46, 167, 92, 122, 185, 227, 50, 201, 213,
+ 150, 223, 46, 216, 28, 90, 137, 251,
+ 38, 195, 90, 209, 251, 28, 67, 73, 241, 246, 196, 70, 211, 114, 221,
+ 229, 153,
+};
+
+void scramble(unsigned char *inout)
+{
+ unsigned char *r = inout + 12;
+ unsigned char *s = yellowbook_scrambler;
+ unsigned int i;
+
+ for (i = 2340; i; i--) {
+ *r++ ^= *s++;
+ }
+}
+
+/* Calculate the P parities for the sector.
+ * The 43 P vectors of length 24 are combined with the GF8_P_COEFFS.
+ */
+void parity_p(unsigned char *sector)
+{
+ int i, j;
+ unsigned char p0_msb, p1_msb;
+ unsigned char p0_lsb, p1_lsb;
+ unsigned char *p_msb_start, *p_lsb_start;
+ unsigned char *p_msb, *p_lsb;
+ unsigned char *coeffs0, *coeffs1;
+ unsigned char *p0, *p1;
+ unsigned char d;
+ unsigned short c;
+
+ p_lsb_start = sector + LEC_HEADER_OFFSET;
+ p_msb_start = sector + LEC_HEADER_OFFSET + 1;
+
+ p1 = sector + LEC_MODE1_P_PARITY_OFFSET;
+ p0 = sector + LEC_MODE1_P_PARITY_OFFSET + 2 * 43;
+
+ for (i = 0; i <= 42; i++) {
+ p_lsb = p_lsb_start;
+ p_msb = p_msb_start;
+
+ coeffs0 = gf8_p_coeffs[0];
+ coeffs1 = gf8_p_coeffs[1];
+
+ p0_lsb = p1_lsb = p0_msb = p1_msb = 0;
+
+ for (j = 0; j <= 23; j++) {
+ d = *p_lsb;
+
+ if (d != 0) {
+ c = gf8_log[d] + *coeffs0;
+ if (c >= 255)
+ c -= 255;
+ p0_lsb ^= gf8_ilog[c];
+
+ c = gf8_log[d] + *coeffs1;
+ if (c >= 255)
+ c -= 255;
+ p1_lsb ^= gf8_ilog[c];
+ }
+
+ d = *p_msb;
+
+ if (d != 0) {
+ c = gf8_log[d] + *coeffs0;
+ if (c >= 255)
+ c -= 255;
+ p0_msb ^= gf8_ilog[c];
+
+ c = gf8_log[d] + *coeffs1;
+ if (c >= 255)
+ c -= 255;
+ p1_msb ^= gf8_ilog[c];
+ }
+
+ coeffs0++;
+ coeffs1++;
+
+ p_lsb += 2 * 43;
+ p_msb += 2 * 43;
+ }
+
+ *p0 = p0_lsb;
+ *(p0 + 1) = p0_msb;
+
+ *p1 = p1_lsb;
+ *(p1 + 1) = p1_msb;
+
+ p0 += 2;
+ p1 += 2;
+
+ p_lsb_start += 2;
+ p_msb_start += 2;
+ }
+}
+
+/* Calculate the Q parities for the sector.
+ * The 26 Q vectors of length 43 are combined with the GF8_Q_COEFFS.
+ */
+void parity_q(unsigned char *sector)
+{
+ int i, j;
+ unsigned char q0_msb, q1_msb;
+ unsigned char q0_lsb, q1_lsb;
+ unsigned char *q_msb_start, *q_lsb_start;
+ unsigned char *q_msb, *q_lsb;
+ unsigned char *coeffs0, *coeffs1;
+ unsigned char *q0, *q1, *q_start;
+ unsigned char d;
+ unsigned short c;
+
+ q_lsb_start = sector + LEC_HEADER_OFFSET;
+ q_msb_start = sector + LEC_HEADER_OFFSET + 1;
+
+ q_start = sector + LEC_MODE1_Q_PARITY_OFFSET;
+ q1 = sector + LEC_MODE1_Q_PARITY_OFFSET;
+ q0 = sector + LEC_MODE1_Q_PARITY_OFFSET + 2 * 26;
+
+ for (i = 0; i <= 25; i++) {
+ q_lsb = q_lsb_start;
+ q_msb = q_msb_start;
+
+ coeffs0 = gf8_q_coeffs[0];
+ coeffs1 = gf8_q_coeffs[1];
+
+ q0_lsb = q1_lsb = q0_msb = q1_msb = 0;
+
+ for (j = 0; j <= 42; j++) {
+ d = *q_lsb;
+
+ if (d != 0) {
+ c = gf8_log[d] + *coeffs0;
+ if (c >= 255)
+ c -= 255;
+ q0_lsb ^= gf8_ilog[c];
+
+ c = gf8_log[d] + *coeffs1;
+ if (c >= 255)
+ c -= 255;
+ q1_lsb ^= gf8_ilog[c];
+ }
+
+ d = *q_msb;
+
+ if (d != 0) {
+ c = gf8_log[d] + *coeffs0;
+ if (c >= 255)
+ c -= 255;
+ q0_msb ^= gf8_ilog[c];
+
+ c = gf8_log[d] + *coeffs1;
+ if (c >= 255)
+ c -= 255;
+ q1_msb ^= gf8_ilog[c];
+ }
+
+ coeffs0++;
+ coeffs1++;
+
+ q_lsb += 2 * 44;
+ q_msb += 2 * 44;
+
+ if (q_lsb >= q_start) {
+ q_msb -= 2 * 1118;
+ q_lsb -= 2 * 1118;
+ }
+ }
+
+ *q0 = q0_lsb;
+ *(q0 + 1) = q0_msb;
+
+ *q1 = q1_lsb;
+ *(q1 + 1) = q1_msb;
+
+ q0 += 2;
+ q1 += 2;
+
+ q_lsb_start += 2 * 43;
+ q_msb_start += 2 * 43;
+ }
+}
diff --git a/tags/ZeroTwoTwo/libburn/lec.h b/tags/ZeroTwoTwo/libburn/lec.h
new file mode 100644
index 00000000..f6980301
--- /dev/null
+++ b/tags/ZeroTwoTwo/libburn/lec.h
@@ -0,0 +1,12 @@
+/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */
+
+#ifndef __LEC
+#define __LEC
+
+#define RS_L12_BITS 8
+
+void scramble(unsigned char *);
+void parity_p(unsigned char *in);
+void parity_q(unsigned char *in);
+
+#endif /* __LEC */
diff --git a/tags/ZeroTwoTwo/libburn/libburn.h b/tags/ZeroTwoTwo/libburn/libburn.h
new file mode 100644
index 00000000..aa64455f
--- /dev/null
+++ b/tags/ZeroTwoTwo/libburn/libburn.h
@@ -0,0 +1,1040 @@
+/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */
+
+#ifndef LIBBURN_H
+#define LIBBURN_H
+
+/* Needed for off_t which is the (POSIX-ly) appropriate type for
+ expressing a file or stream size.
+
+ XXX we should enforce 64-bitness for off_t
+*/
+#include
+
+#ifndef DOXYGEN
+
+#if defined(__cplusplus)
+#define BURN_BEGIN_DECLS \
+ namespace burn { \
+ extern "C" {
+#define BURN_END_DECLS \
+ } \
+ }
+#else
+#define BURN_BEGIN_DECLS
+#define BURN_END_DECLS
+#endif
+
+BURN_BEGIN_DECLS
+
+#endif
+
+/** References a physical drive in the system */
+struct burn_drive;
+
+/** References a whole disc */
+struct burn_disc;
+
+/** References a single session on a disc */
+struct burn_session;
+
+/** References a single track on a disc */
+struct burn_track;
+
+/** Session format for normal audio or data discs */
+#define BURN_CDROM 0
+/** Session format for obsolete CD-I discs */
+#define BURN_CDI 0x10
+/** Session format for CDROM-XA discs */
+#define BURN_CDXA 0x20
+
+#define BURN_POS_END 100
+
+/** Mask for mode bits */
+#define BURN_MODE_BITS 127
+
+/** Track mode - mode 0 data
+ 0 bytes of user data. it's all 0s. mode 0. get it? HAH
+*/
+#define BURN_MODE0 (1 << 0)
+/** Track mode - mode "raw" - all 2352 bytes supplied by app
+ FOR DATA TRACKS ONLY!
+*/
+#define BURN_MODE_RAW (1 << 1)
+/** Track mode - mode 1 data
+ 2048 bytes user data, and all the LEC money can buy
+*/
+#define BURN_MODE1 (1 << 2)
+/** Track mode - mode 2 data
+ defaults to formless, 2336 bytes of user data, unprotected
+ | with a data form if required.
+*/
+#define BURN_MODE2 (1 << 3)
+/** Track mode modifier - Form 1, | with MODE2 for reasonable results
+ 2048 bytes of user data, 4 bytes of subheader
+*/
+#define BURN_FORM1 (1 << 4)
+/** Track mode modifier - Form 2, | with MODE2 for reasonable results
+ lots of user data. not much LEC.
+*/
+#define BURN_FORM2 (1 << 5)
+/** Track mode - audio
+ 2352 bytes per sector. may be | with 4ch or preemphasis.
+ NOT TO BE CONFUSED WITH BURN_MODE_RAW
+ Audio data must be 44100Hz 16bit stereo with no riff or other header at
+ beginning. Extra header data will cause pops or clicks. Audio data should
+ also be in little-endian byte order. Big-endian audio data causes static.
+*/
+#define BURN_AUDIO (1 << 6)
+/** Track mode modifier - 4 channel audio. */
+#define BURN_4CH (1 << 7)
+/** Track mode modifier - Digital copy permitted, can be set on any track.*/
+#define BURN_COPY (1 << 8)
+/** Track mode modifier - 50/15uS pre-emphasis */
+#define BURN_PREEMPHASIS (1 << 9)
+/** Input mode modifier - subcodes present packed 16 */
+#define BURN_SUBCODE_P16 (1 << 10)
+/** Input mode modifier - subcodes present packed 96 */
+#define BURN_SUBCODE_P96 (1 << 11)
+/** Input mode modifier - subcodes present raw 96 */
+#define BURN_SUBCODE_R96 (1 << 12)
+
+/** Possible disc writing style/modes */
+enum burn_write_types
+{
+ /** Packet writing.
+ currently unsupported
+ */
+ BURN_WRITE_PACKET,
+ /** Track At Once recording.
+ 2s gaps between tracks, no fonky lead-ins
+ */
+ BURN_WRITE_TAO,
+ /** Session At Once.
+ block type MUST be BURN_BLOCK_SAO
+ */
+ BURN_WRITE_SAO,
+ /** Raw disc at once recording.
+ all subcodes must be provided by lib or user
+ only raw block types are supported
+ */
+ BURN_WRITE_RAW
+};
+
+/** Data format to send to the drive */
+enum burn_block_types
+{
+ /** sync, headers, edc/ecc provided by lib/user */
+ BURN_BLOCK_RAW0 = 1,
+ /** sync, headers, edc/ecc and p/q subs provided by lib/user */
+ BURN_BLOCK_RAW16 = 2,
+ /** sync, headers, edc/ecc and packed p-w subs provided by lib/user */
+ BURN_BLOCK_RAW96P = 4,
+ /** sync, headers, edc/ecc and raw p-w subs provided by lib/user */
+ BURN_BLOCK_RAW96R = 8,
+ /** only 2048 bytes of user data provided by lib/user */
+ BURN_BLOCK_MODE1 = 256,
+ /** 2336 bytes of user data provided by lib/user */
+ BURN_BLOCK_MODE2R = 512,
+ /** 2048 bytes of user data provided by lib/user
+ subheader provided in write parameters
+ are we ever going to support this shit? I vote no.
+ (supposed to be supported on all drives...)
+ */
+ BURN_BLOCK_MODE2_PATHETIC = 1024,
+ /** 2048 bytes of data + 8 byte subheader provided by lib/user
+ hey, this is also dumb
+ */
+ BURN_BLOCK_MODE2_LAME = 2048,
+ /** 2324 bytes of data provided by lib/user
+ subheader provided in write parameters
+ no sir, I don't like it.
+ */
+ BURN_BLOCK_MODE2_OBSCURE = 4096,
+ /** 2332 bytes of data supplied by lib/user
+ 8 bytes sub header provided in write parameters
+ this is the second least suck mode2, and is mandatory for
+ all drives to support.
+ */
+ BURN_BLOCK_MODE2_OK = 8192,
+ /** SAO block sizes are based on cue sheet, so use this. */
+ BURN_BLOCK_SAO = 16384
+};
+
+/** Possible status' of the drive in regard to the disc in it. */
+enum burn_disc_status
+{
+ /** The current status is not yet known */
+ BURN_DISC_UNREADY,
+ /** The drive holds a blank disc */
+ BURN_DISC_BLANK,
+ /** There is no disc at all in the drive */
+ BURN_DISC_EMPTY,
+ /** There is an incomplete disc in the drive */
+ BURN_DISC_APPENDABLE,
+ /** There is a disc with data on it in the drive */
+ BURN_DISC_FULL
+};
+
+/** Possible types of messages form the library. */
+enum burn_message_type
+{
+ /** Diagnostic/Process information. For the curious user. */
+ BURN_MESSAGE_INFO,
+ /** A warning regarding a possible problem. The user should probably
+ be notified, but its not fatal. */
+ BURN_MESSAGE_WARNING,
+ /** An error message. This usually means the current process will be
+ aborted, and the user should definately see these. */
+ BURN_MESSAGE_ERROR
+};
+
+/** Possible information messages */
+enum burn_message_info
+{
+ BURN_INFO_FOO
+};
+
+/** Possible warning messages */
+enum burn_message_warning
+{
+ BURN_WARNING_FOO
+};
+
+/** Possible error messages */
+enum burn_message_error
+{
+ BURN_ERROR_CANCELLED
+};
+
+/** Possible data source return values */
+enum burn_source_status
+{
+ /** The source is ok */
+ BURN_SOURCE_OK,
+ /** The source is at end of file */
+ BURN_SOURCE_EOF,
+ /** The source is unusable */
+ BURN_SOURCE_FAILED
+};
+
+
+/** Possible busy states for a drive */
+enum burn_drive_status
+{
+ /** The drive is not in an operation */
+ BURN_DRIVE_IDLE,
+ /** The library is spawning the processes to handle a pending
+ operation (A read/write/etc is about to start but hasn't quite
+ yet) */
+ BURN_DRIVE_SPAWNING,
+ /** The drive is reading data from a disc */
+ BURN_DRIVE_READING,
+ /** The drive is writing data to a disc */
+ BURN_DRIVE_WRITING,
+ /** The drive is writing Lead-In */
+ BURN_DRIVE_WRITING_LEADIN,
+ /** The drive is writing Lead-Out */
+ BURN_DRIVE_WRITING_LEADOUT,
+ /** The drive is erasing a disc */
+ BURN_DRIVE_ERASING,
+ /** The drive is being grabbed */
+ BURN_DRIVE_GRABBING
+};
+
+/** Information about a track on a disc - this is from the q sub channel of the
+ lead-in area of a disc. The documentation here is very terse.
+ See a document such as mmc3 for proper information.
+*/
+struct burn_toc_entry
+{
+ /** Session the track is in */
+ unsigned char session;
+ /** Type of data. for this struct to be valid, it must be 1 */
+ unsigned char adr;
+ /** Type of data in the track */
+ unsigned char control;
+ /** Zero. Always. Really. */
+ unsigned char tno;
+ /** Track number or special information */
+ unsigned char point;
+ unsigned char min;
+ unsigned char sec;
+ unsigned char frame;
+ unsigned char zero;
+ /** Track start time minutes for normal tracks */
+ unsigned char pmin;
+ /** Track start time seconds for normal tracks */
+ unsigned char psec;
+ /** Track start time frames for normal tracks */
+ unsigned char pframe;
+};
+
+
+/** Data source for tracks */
+struct burn_source {
+ /** Reference count for the data source. Should be 1 when a new source
+ is created. Increment it to take a reference for yourself. Use
+ burn_source_free to destroy your reference to it. */
+ int refcount;
+
+ /** Read data from the source */
+ int (*read)(struct burn_source *,
+ unsigned char *buffer,
+ int size);
+
+ /** Read subchannel data from the source (NULL if lib generated) */
+ int (*read_sub)(struct burn_source *,
+ unsigned char *buffer,
+ int size);
+
+ /** Get the size of the source's data */
+ off_t (*get_size)(struct burn_source *);
+
+ /** Clean up the source specific data */
+ void (*free_data)(struct burn_source *);
+
+ /** Next source, for when a source runs dry and padding is disabled
+ THIS IS AUTOMATICALLY HANDLED, DO NOT TOUCH
+ */
+ struct burn_source *next;
+
+ /** Source specific data */
+ void *data;
+};
+
+
+/** Information on a drive in the system */
+struct burn_drive_info
+{
+ /** Name of the vendor of the drive */
+ char vendor[9];
+ /** Name of the drive */
+ char product[17];
+ /** Revision of the drive */
+ char revision[5];
+ /** Location of the drive in the filesystem. */
+ char location[17];
+ /** This is currently the string which is used as persistent
+ drive address. But be warned: there is NO GUARANTEE that this
+ will stay so. Always use function burn_drive_get_adr() to
+ inquire a persisten address. ^^^^^^ ALWAYS ^^^^^^ */
+
+ /** Can the drive read DVD-RAM discs */
+ unsigned int read_dvdram:1;
+ /** Can the drive read DVD-R discs */
+ unsigned int read_dvdr:1;
+ /** Can the drive read DVD-ROM discs */
+ unsigned int read_dvdrom:1;
+ /** Can the drive read CD-R discs */
+ unsigned int read_cdr:1;
+ /** Can the drive read CD-RW discs */
+ unsigned int read_cdrw:1;
+
+ /** Can the drive write DVD-RAM discs */
+ unsigned int write_dvdram:1;
+ /** Can the drive write DVD-R discs */
+ unsigned int write_dvdr:1;
+ /** Can the drive write CD-R discs */
+ unsigned int write_cdr:1;
+ /** Can the drive write CD-RW discs */
+ unsigned int write_cdrw:1;
+
+ /** Can the drive simulate a write */
+ unsigned int write_simulate:1;
+
+ /** Can the drive report C2 errors */
+ unsigned int c2_errors:1;
+
+ /** The size of the drive's buffer (in kilobytes) */
+ int buffer_size;
+ /**
+ * The supported block types in tao mode.
+ * They should be tested with the desired block type.
+ * See also burn_block_types.
+ */
+ int tao_block_types;
+ /**
+ * The supported block types in sao mode.
+ * They should be tested with the desired block type.
+ * See also burn_block_types.
+ */
+ int sao_block_types;
+ /**
+ * The supported block types in raw mode.
+ * They should be tested with the desired block type.
+ * See also burn_block_types.
+ */
+ int raw_block_types;
+ /**
+ * The supported block types in packet mode.
+ * They should be tested with the desired block type.
+ * See also burn_block_types.
+ */
+ int packet_block_types;
+
+ /** The value by which this drive can be indexed when using functions
+ in the library. This is the value to pass to all libbburn functions
+ that operate on a drive. */
+ struct burn_drive *drive;
+};
+
+/** Messages from the library */
+struct burn_message
+{
+ /** The drive associated with the message. NULL if the error is not
+ related to a specific drive. */
+ struct burn_drive *drive;
+
+ /** The type of message this is. See message_type for details. */
+ enum burn_message_type type;
+
+ /** The actual message */
+ union detail {
+ struct {
+ enum burn_message_info message;
+ } info;
+ struct {
+ enum burn_message_warning message;
+ } warning;
+ struct {
+ enum burn_message_error message;
+ } error;
+ } detail;
+};
+
+/** Operation progress report. All values are 0 based indices.
+ * */
+struct burn_progress {
+ /** The total number of sessions */
+ int sessions;
+ /** Current session.*/
+ int session;
+ /** The total number of tracks */
+ int tracks;
+ /** Current track. */
+ int track;
+ /** The total number of indices */
+ int indices;
+ /** Curent index. */
+ int index;
+ /** The starting logical block address */
+ int start_sector;
+ /** The number of sector */
+ int sectors;
+ /** The current sector being processed */
+ int sector;
+};
+
+/** Initialize the library.
+ This must be called before using any other functions in the library. It
+ may be called more than once with no effect.
+ It is possible to 'restart' the library by shutting it down and
+ re-initializing it. This is necessary if you follow the older and
+ more general way of accessing a drive via burn_drive_scan() and
+ burn_drive_grab(). See burn_drive_scan_and_grab() with its strong
+ urges and its explanations.
+ @return Nonzero if the library was able to initialize; zero if
+ initialization failed.
+*/
+int burn_initialize(void);
+
+/** Shutdown the library.
+ This should be called before exiting your application. Make sure that all
+ drives you have grabbed are released before calling this.
+*/
+void burn_finish(void);
+
+/** Set the verbosity level of the library. The default value is 0, which means
+ that nothing is output on stderr. The more you increase this, the more
+ debug output should be displayed on stderr for you.
+ @param level The verbosity level desired. 0 for nothing, higher positive
+ values for more information output.
+*/
+void burn_set_verbosity(int level);
+
+/* ts A60813 */
+/** Set parameters for behavior on opening device files. To be called early
+ after burn_initialize() and before any bus scan. But not mandatory at all.
+ Parameter value 1 enables a feature, 0 disables.
+ Default is (1,0,0). Have a good reason before you change it.
+ @param exclusive Try to open only devices which are not marked as busy
+ and try to mark them busy if opened sucessfully. (O_EXCL)
+ There are kernels which simply don't care about O_EXCL.
+ Some have it off, some have it on, some are switchable.
+ @param blocking Try to wait for drives which do not open immediately but
+ also do not return an error as well. (O_NONBLOCK)
+ This might stall indefinitely with /dev/hdX hard disks.
+ @param abort_on_busy Unconditionally abort process when a non blocking
+ exclusive opening attempt indicates a busy drive.
+ Use this only after thorough tests with your app.
+*/
+void burn_preset_device_open(int exclusive, int blocking, int abort_on_busy);
+
+/** Returns a newly allocated burn_message structure. This message should be
+ freed with burn_message_free() when you are finished with it.
+ @return A message or NULL when there are no more messages to retrieve.
+*/
+struct burn_message* burn_get_message(void);
+
+/** Frees a burn_message structure */
+void burn_message_free(struct burn_message *msg);
+
+
+/* ts A60823 */
+/** Aquire a drive with known persistent address.This is the sysadmin friendly
+ way to open one drive and to leave all others untouched. It bundles
+ the following API calls to form a non-obtrusive way to use libburn:
+ burn_drive_add_whitelist() , burn_drive_scan() , burn_drive_grab()
+ You are *strongly urged* to use this call whenever you know the drive
+ address in advance.
+ If not, then you have to use directly above calls. In that case, you are
+ *strongly urged* to drop any unintended drive which will be exclusively
+ occupied and not closed by burn_drive_scan().
+ This can be done by shutting down the library including a call to
+ burn_finish(). You may later start a new libburn session and should then
+ use the function described here with an address obtained after
+ burn_drive_scan() via burn_drive_get_adr(&(drive_infos[driveno]), adr) .
+ Another way is to drop the unwanted drives by burn_drive_info_forget().
+ @param drive_infos On success returns a one element array with the drive
+ (cdrom/burner). Thus use with driveno 0 only. On failure
+ the array has no valid elements at all.
+ The returned array should be freed via burn_drive_info_free()
+ when it is no longer needed, and before calling a scan
+ function again.
+ This is a result from call burn_drive_scan(). See there.
+ Use with driveno 0 only.
+ @param adr The persistent address of the desired drive. Either obtained
+ by burn_drive_get_adr() or guessed skillfully by application
+ resp. its user.
+ @param load Nonzero to make the drive attempt to load a disc (close its
+ tray door, etc).
+ @return 1 = success , 0 = drive not found , -1 = other error
+*/
+int burn_drive_scan_and_grab(struct burn_drive_info *drive_infos[],
+ char* adr, int load);
+
+
+/* ts A51221 */
+/** Maximum number of particularly permissible drive addresses */
+#define BURN_DRIVE_WHITELIST_LEN 255
+/** Add a device to the list of permissible drives. As soon as some entry is in
+ the whitelist all non-listed drives are banned from scanning.
+ @return 1 success, <=0 failure
+*/
+int burn_drive_add_whitelist(char *device_address);
+
+/** Remove all drives from whitelist. This enables all possible drives. */
+void burn_drive_clear_whitelist(void);
+
+
+/** Scan for drives. This function MUST be called until it returns nonzero.
+ No drives can be in use when this is called or it will assert.
+ All drive pointers are invalidated by using this function. Do NOT store
+ drive pointers across calls to this function or death AND pain will ensue.
+ After this call all drives depicted by the returned array are subject
+ to eventual (O_EXCL) locking. See burn_preset_device_open(). This state
+ ends either with burn_drive_info_forget() or with burn_drive_release().
+ It is unfriendly to other processes on the system to hold drives locked
+ which one does not definitely plan to use soon.
+ @param drive_infos Returns an array of drive info items (cdroms/burners).
+ The returned array must be freed by burn_drive_info_free()
+ before burn_finish(), and also before calling this function
+ burn_drive_scan() again.
+ @param n_drives Returns the number of drive items in drive_infos.
+ @return Zero while scanning is not complete; non-zero when it is finished.
+*/
+int burn_drive_scan(struct burn_drive_info *drive_infos[],
+ unsigned int *n_drives);
+
+/* ts A60904 : ticket 62, contribution by elmom */
+/** Release memory about a single drive and any exclusive lock on it.
+ Become unable to inquire or grab it. Expect FATAL consequences if you try.
+ @param drive_info pointer to a single element out of the array
+ obtained from burn_drive_scan() : &(drive_infos[driveno])
+ @param force controls degree of permissible drive usage at the moment this
+ function is called, and the amount of automatically provided
+ drive shutdown :
+ 0= drive must be ungrabbed and BURN_DRIVE_IDLE
+ 1= try to release drive resp. accept BURN_DRIVE_GRABBING
+ Use these two only. Further values are to be defined.
+ @return 1 on success, 2 if drive was already forgotten,
+ 0 if not permissible, <0 on other failures,
+*/
+int burn_drive_info_forget(struct burn_drive_info *drive_info, int force);
+
+
+/** Free a burn_drive_info array returned by burn_drive_scan
+*/
+void burn_drive_info_free(struct burn_drive_info drive_infos[]);
+
+
+/* ts A60823 */
+/** Maximum length+1 to expect with a persistent drive address string */
+#define BURN_DRIVE_ADR_LEN 1024
+
+/** Inquire the persistent address of the given drive.
+ @param drive_info The drive to inquire. Usually some &(drive_infos[driveno])
+ @param adr An application provided array of at least BURN_DRIVE_ADR_LEN
+ characters size. The persistent address gets copied to it.
+*/
+int burn_drive_get_adr(struct burn_drive_info *drive_info, char adr[]);
+
+
+/** Grab a drive. This must be done before the drive can be used (for reading,
+ writing, etc).
+ @param drive The drive to grab. This is found in a returned
+ burn_drive_info struct.
+ @param load Nonzero to make the drive attempt to load a disc (close its
+ tray door, etc).
+ @return 1 if it was possible to grab the drive, else 0
+*/
+int burn_drive_grab(struct burn_drive *drive, int load);
+
+
+/** Release a drive. This should not be done until the drive is no longer
+ busy (see burn_drive_get_status). The drive is (O_EXCL) unlocked
+ afterwards.
+ @param drive The drive to release.
+ @param eject Nonzero to make the drive eject the disc in it.
+*/
+void burn_drive_release(struct burn_drive *drive, int eject);
+
+
+/** Returns what kind of disc a drive is holding. This function may need to be
+ called more than once to get a proper status from it. See burn_status
+ for details.
+ @param drive The drive to query for a disc.
+ @return The status of the drive, or what kind of disc is in it.
+*/
+enum burn_disc_status burn_disc_get_status(struct burn_drive *drive);
+
+/** Tells whether a disc can be erased or not
+ @return Non-zero means erasable
+*/
+int burn_disc_erasable(struct burn_drive *d);
+
+/** Returns the progress and status of a drive.
+ @param drive The drive to query busy state for.
+ @param p Returns the progress of the operation, NULL if you don't care
+ @return the current status of the drive. See also burn_drive_status.
+*/
+enum burn_drive_status burn_drive_get_status(struct burn_drive *drive,
+ struct burn_progress *p);
+
+/** Creates a write_opts struct for burning to the specified drive
+ must be freed with burn_write_opts_free
+ @param drive The drive to write with
+ @return The write_opts
+*/
+struct burn_write_opts *burn_write_opts_new(struct burn_drive *drive);
+
+/** Frees a write_opts struct created with burn_write_opts_new
+ @param opts write_opts to free
+*/
+void burn_write_opts_free(struct burn_write_opts *opts);
+
+/** Creates a write_opts struct for reading from the specified drive
+ must be freed with burn_write_opts_free
+ @param drive The drive to read from
+ @return The read_opts
+*/
+struct burn_read_opts *burn_read_opts_new(struct burn_drive *drive);
+
+/** Frees a read_opts struct created with burn_read_opts_new
+ @param opts write_opts to free
+*/
+void burn_read_opts_free(struct burn_read_opts *opts);
+
+/** Erase a disc in the drive. The drive must be grabbed successfully BEFORE
+ calling this functions. Always ensure that the drive reports a status of
+ BURN_DISC_FULL before calling this function. An erase operation is not
+ cancellable, as control of the operation is passed wholly to the drive and
+ there is no way to interrupt it safely.
+ @param drive The drive with which to erase a disc.
+ @param fast Nonzero to do a fast erase, where only the disc's headers are
+ erased; zero to erase the entire disc.
+*/
+void burn_disc_erase(struct burn_drive *drive, int fast);
+
+/** Read a disc from the drive and write it to an fd pair. The drive must be
+ grabbed successfully BEFORE calling this function. Always ensure that the
+ drive reports a status of BURN_DISC_FULL before calling this function.
+ @param drive The drive from which to read a disc.
+ @param o The options for the read operation.
+*/
+void burn_disc_read(struct burn_drive *drive, const struct burn_read_opts *o);
+
+/** Write a disc in the drive. The drive must be grabbed successfully BEFORE
+ calling this function. Always ensure that the drive reports a status of
+ BURN_DISC_BLANK or BURN_STATUS_FULL (to append a new session to the
+ disc) before calling this function.
+ @param o The options for the writing operation.
+ @param disc The struct burn_disc * that described the disc to be created
+*/
+void burn_disc_write(struct burn_write_opts *o, struct burn_disc *disc);
+
+/** Cancel an operation on a drive.
+ This will only work when the drive's busy state is BURN_DRIVE_READING or
+ BURN_DRIVE_WRITING.
+ @param drive The drive on which to cancel the current operation.
+*/
+void burn_drive_cancel(struct burn_drive *drive);
+
+/** Convert a minute-second-frame (MSF) value to sector count
+ @param m Minute component
+ @param s Second component
+ @param f Frame component
+ @return The sector count
+*/
+int burn_msf_to_sectors(int m, int s, int f);
+
+/** Convert a sector count to minute-second-frame (MSF)
+ @param sectors The sector count
+ @param m Returns the minute component
+ @param s Returns the second component
+ @param f Returns the frame component
+*/
+void burn_sectors_to_msf(int sectors, int *m, int *s, int *f);
+
+/** Convert a minute-second-frame (MSF) value to an lba
+ @param m Minute component
+ @param s Second component
+ @param f Frame component
+ @return The lba
+*/
+int burn_msf_to_lba(int m, int s, int f);
+
+/** Convert an lba to minute-second-frame (MSF)
+ @param lba The lba
+ @param m Returns the minute component
+ @param s Returns the second component
+ @param f Returns the frame component
+*/
+void burn_lba_to_msf(int lba, int *m, int *s, int *f);
+
+/** Create a new disc (for DAO recording)*/
+struct burn_disc *burn_disc_create(void);
+
+/** Delete disc and decrease the reference count on all its sessions
+ @param d The disc to be freed
+*/
+void burn_disc_free(struct burn_disc *d);
+
+/** Create a new session (For SAO at once recording, or to be added to a
+ disc for DAO)
+*/
+struct burn_session *burn_session_create(void);
+
+/** Free a session (and decrease reference count on all tracks inside)
+ @param s Session to be freed
+*/
+void burn_session_free(struct burn_session *s);
+
+/** Add a session to a disc at a specific position, increasing the
+ sessions's reference count.
+ @param d Disc to add the session to
+ @param s Session to add to the disc
+ @param pos position to add at (BURN_POS_END is "at the end")
+ @return 0 for failure, 1 for success
+*/
+int burn_disc_add_session(struct burn_disc *d, struct burn_session *s,
+ unsigned int pos);
+
+/** Remove a session from a disc
+ @param d Disc to remove session from
+ @param s Session pointer to find and remove
+*/
+int burn_disc_remove_session(struct burn_disc *d, struct burn_session *s);
+
+
+/** Create a track (for TAO recording, or to put in a session) */
+struct burn_track *burn_track_create(void);
+
+/** Free a track
+ @param t Track to free
+*/
+void burn_track_free(struct burn_track *t);
+
+/** Add a track to a session at specified position
+ @param s Session to add to
+ @param t Track to insert in session
+ @param pos position to add at (BURN_POS_END is "at the end")
+ @return 0 for failure, 1 for success
+*/
+int burn_session_add_track(struct burn_session *s, struct burn_track *t,
+ unsigned int pos);
+
+/** Remove a track from a session
+ @param s Session to remove track from
+ @param t Track pointer to find and remove
+ @return 0 for failure, 1 for success
+*/
+int burn_session_remove_track(struct burn_session *s, struct burn_track *t);
+
+
+/** Define the data in a track
+ @param t the track to define
+ @param offset The lib will write this many 0s before start of data
+ @param tail The number of extra 0s to write after data
+ @param pad 1 means the lib should pad the last sector with 0s if the
+ track isn't exactly sector sized. (otherwise the lib will
+ begin reading from the next track)
+ @param mode data format (bitfield)
+*/
+void burn_track_define_data(struct burn_track *t, int offset, int tail,
+ int pad, int mode);
+
+/** Set the ISRC details for a track
+ @param t The track to change
+ @param country the 2 char country code. Each character must be
+ only numbers or letters.
+ @param owner 3 char owner code. Each character must be only numbers
+ or letters.
+ @param year 2 digit year. A number in 0-99 (Yep, not Y2K friendly).
+ @param serial 5 digit serial number. A number in 0-99999.
+*/
+void burn_track_set_isrc(struct burn_track *t, char *country, char *owner,
+ unsigned char year, unsigned int serial);
+
+/** Disable ISRC parameters for a track
+ @param t The track to change
+*/
+void burn_track_clear_isrc(struct burn_track *t);
+
+/** Hide the first track in the "pre gap" of the disc
+ @param s session to change
+ @param onoff 1 to enable hiding, 0 to disable
+*/
+void burn_session_hide_first_track(struct burn_session *s, int onoff);
+
+/** Get the drive's disc struct - free when done
+ @param d drive to query
+ @return the disc struct
+*/
+struct burn_disc *burn_drive_get_disc(struct burn_drive *d);
+
+/** Set the track's data source
+ @param t The track to set the data source for
+ @param s The data source to use for the contents of the track
+ @return An error code stating if the source is ready for use for
+ writing the track, or if an error occured
+
+*/
+enum burn_source_status burn_track_set_source(struct burn_track *t,
+ struct burn_source *s);
+
+/** Free a burn_source (decrease its refcount and maybe free it)
+ @param s Source to free
+*/
+void burn_source_free(struct burn_source *s);
+
+/** Creates a data source for an image file (and maybe subcode file) */
+struct burn_source *burn_file_source_new(const char *path,
+ const char *subpath);
+
+/** Creates a data source for an image file (a track) from an open
+ readable filedescriptor, an eventually open readable subcodes file
+ descriptor and eventually a fixed size in bytes.
+ @param datafd The source of data.
+ @param subfd The eventual source for subcodes. Not used if -1.
+ @param size The eventual fixed size of eventually both fds.
+ If this value is 0, the size will be determined from datafd.
+*/
+struct burn_source *burn_fd_source_new(int datafd, int subfd, off_t size);
+
+/** Tells how long a track will be on disc */
+int burn_track_get_sectors(struct burn_track *);
+
+
+/** Sets drive read and write speed
+ @param d The drive to set speed for
+ @param read Read speed in k/s (0 is max)
+ @param write Write speed in k/s (0 is max)
+*/
+void burn_drive_set_speed(struct burn_drive *d, int read, int write);
+
+/* these are for my debugging, they will disappear */
+void burn_structure_print_disc(struct burn_disc *d);
+void burn_structure_print_session(struct burn_session *s);
+void burn_structure_print_track(struct burn_track *t);
+
+/** Sets the write type for the write_opts struct
+ @param opts The write opts to change
+ @param write_type The write type to use
+ @param block_type The block type to use
+ @return Returns 1 on success and 0 on failure.
+*/
+int burn_write_opts_set_write_type(struct burn_write_opts *opts,
+ enum burn_write_types write_type,
+ int block_type);
+
+/** Supplies toc entries for writing - not normally required for cd mastering
+ @param opts The write opts to change
+ @param count The number of entries
+ @param toc_entries
+*/
+void burn_write_opts_set_toc_entries(struct burn_write_opts *opts,
+ int count,
+ struct burn_toc_entry *toc_entries);
+
+/** Sets the session format for a disc
+ @param opts The write opts to change
+ @param format The session format to set
+*/
+void burn_write_opts_set_format(struct burn_write_opts *opts, int format);
+
+/** Sets the simulate value for the write_opts struct
+ @param opts The write opts to change
+ @param sim If non-zero, the drive will perform a simulation instead of a burn
+ @return Returns 1 on success and 0 on failure.
+*/
+int burn_write_opts_set_simulate(struct burn_write_opts *opts, int sim);
+
+/** Controls buffer underrun prevention
+ @param opts The write opts to change
+ @param underrun_proof if non-zero, buffer underrun protection is enabled
+ @return Returns 1 on success and 0 on failure.
+*/
+int burn_write_opts_set_underrun_proof(struct burn_write_opts *opts,
+ int underrun_proof);
+
+/** Sets whether to use opc or not with the write_opts struct
+ @param opts The write opts to change
+ @param opc If non-zero, optical power calibration will be performed at
+ start of burn
+
+*/
+void burn_write_opts_set_perform_opc(struct burn_write_opts *opts, int opc);
+
+void burn_write_opts_set_has_mediacatalog(struct burn_write_opts *opts, int has_mediacatalog);
+
+void burn_write_opts_set_mediacatalog(struct burn_write_opts *opts, unsigned char mediacatalog[13]);
+
+/** Sets whether to read in raw mode or not
+ @param opts The read opts to change
+ @param raw_mode If non-zero, reading will be done in raw mode, so that everything in the data tracks on the
+ disc is read, including headers.
+*/
+void burn_read_opts_set_raw(struct burn_read_opts *opts, int raw_mode);
+
+/** Sets whether to report c2 errors or not
+ @param opts The read opts to change
+ @param c2errors If non-zero, report c2 errors.
+*/
+void burn_read_opts_set_c2errors(struct burn_read_opts *opts, int c2errors);
+
+/** Sets whether to read subcodes from audio tracks or not
+ @param opts The read opts to change
+ @param subcodes_audio If non-zero, read subcodes from audio tracks on the disc.
+*/
+void burn_read_opts_read_subcodes_audio(struct burn_read_opts *opts,
+ int subcodes_audio);
+
+/** Sets whether to read subcodes from data tracks or not
+ @param opts The read opts to change
+ @param subcodes_data If non-zero, read subcodes from data tracks on the disc.
+*/
+void burn_read_opts_read_subcodes_data(struct burn_read_opts *opts,
+ int subcodes_data);
+
+/** Sets whether to recover errors if possible
+ @param opts The read opts to change
+ @param hardware_error_recovery If non-zero, attempt to recover errors if possible.
+*/
+void burn_read_opts_set_hardware_error_recovery(struct burn_read_opts *opts,
+ int hardware_error_recovery);
+
+/** Sets whether to report recovered errors or not
+ @param opts The read opts to change
+ @param report_recovered_errors If non-zero, recovered errors will be reported.
+*/
+void burn_read_opts_report_recovered_errors(struct burn_read_opts *opts,
+ int report_recovered_errors);
+
+/** Sets whether blocks with unrecoverable errors should be read or not
+ @param opts The read opts to change
+ @param transfer_damaged_blocks If non-zero, blocks with unrecoverable errors will still be read.
+*/
+void burn_read_opts_transfer_damaged_blocks(struct burn_read_opts *opts,
+ int transfer_damaged_blocks);
+
+/** Sets the number of retries to attempt when trying to correct an error
+ @param opts The read opts to change
+ @param hardware_error_retries The number of retries to attempt when correcting an error.
+*/
+void burn_read_opts_set_hardware_error_retries(struct burn_read_opts *opts,
+ unsigned char hardware_error_retries);
+
+/** Gets the maximum write speed for a drive
+ @param d Drive to query
+ @return Maximum write speed in K/s
+*/
+int burn_drive_get_write_speed(struct burn_drive *d);
+
+/** Gets the maximum read speed for a drive
+ @param d Drive to query
+ @return Maximum read speed in K/s
+*/
+int burn_drive_get_read_speed(struct burn_drive *d);
+
+/** Gets a copy of the toc_entry structure associated with a track
+ @param t Track to get the entry from
+ @param entry Struct for the library to fill out
+*/
+void burn_track_get_entry(struct burn_track *t, struct burn_toc_entry *entry);
+
+/** Gets a copy of the toc_entry structure associated with a session's lead out
+ @param s Session to get the entry from
+ @param entry Struct for the library to fill out
+*/
+void burn_session_get_leadout_entry(struct burn_session *s,
+ struct burn_toc_entry *entry);
+
+/** Gets an array of all the sessions for the disc
+ THIS IS NO LONGER VALID AFTER YOU ADD OR REMOVE A SESSION
+ @param d Disc to get session array for
+ @param num Returns the number of sessions in the array
+ @return array of sessions
+*/
+struct burn_session **burn_disc_get_sessions(struct burn_disc *d,
+ int *num);
+
+int burn_disc_get_sectors(struct burn_disc *d);
+
+/** Gets an array of all the tracks for a session
+ THIS IS NO LONGER VALID AFTER YOU ADD OR REMOVE A TRACK
+ @param s session to get track array for
+ @param num Returns the number of tracks in the array
+ @return array of tracks
+*/
+struct burn_track **burn_session_get_tracks(struct burn_session *s,
+ int *num);
+
+int burn_session_get_sectors(struct burn_session *s);
+
+/** Gets the mode of a track
+ @param track the track to query
+ @return the track's mode
+*/
+int burn_track_get_mode(struct burn_track *track);
+
+/** Returns whether the first track of a session is hidden in the pregap
+ @param session the session to query
+ @return non-zero means the first track is hidden
+*/
+int burn_session_get_hidefirst(struct burn_session *session);
+
+/** Returns the library's version in its parts
+ @param major The major version number
+ @param minor The minor version number
+ @param micro The micro version number
+*/
+void burn_version(int *major, int *minor, int *micro);
+
+#ifndef DOXYGEN
+
+BURN_END_DECLS
+
+#endif
+
+#endif /*LIBBURN_H*/
diff --git a/tags/ZeroTwoTwo/libburn/message.c b/tags/ZeroTwoTwo/libburn/message.c
new file mode 100644
index 00000000..71f35bc9
--- /dev/null
+++ b/tags/ZeroTwoTwo/libburn/message.c
@@ -0,0 +1,108 @@
+/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */
+
+#include "message.h"
+#include "libburn.h"
+#include "debug.h"
+
+#include
+
+struct message_list
+{
+ struct message_list *next;
+
+ struct burn_message *msg;
+};
+
+static struct message_list *queue;
+
+void burn_message_free(struct burn_message *msg)
+{
+ free(msg);
+}
+
+void burn_message_clear_queue(void)
+{
+ struct burn_message *msg;
+
+ if ((msg = burn_get_message())) {
+ burn_print(0,
+ "YOU HAVE MESSAGES QUEUED FROM THE LAST OPERATION. "
+ "YOU SHOULD BE GRABBING THEM ALL!\n");
+ do {
+ burn_message_free(msg);
+ } while ((msg = burn_get_message()));
+ }
+}
+
+struct burn_message *burn_get_message()
+{
+ struct burn_message *msg = NULL;
+
+ if (queue) {
+ struct message_list *next;
+
+ next = queue->next;
+ msg = queue->msg;
+ free(queue);
+ queue = next;
+ }
+
+ return msg;
+}
+
+static void queue_push_tail(struct burn_message *msg)
+{
+ struct message_list *node;
+
+ node = malloc(sizeof(struct message_list));
+ node->next = NULL;
+ node->msg = msg;
+
+ if (!queue)
+ queue = node;
+ else {
+ struct message_list *it;
+
+ for (it = queue; it->next; it = it->next) ;
+ it->next = node;
+ }
+}
+
+void burn_message_info_new(struct burn_drive *drive,
+ enum burn_message_info message)
+{
+ struct burn_message *msg;
+
+ msg = malloc(sizeof(struct burn_message));
+ msg->drive = drive;
+ msg->type = BURN_MESSAGE_INFO;
+ msg->detail.info.message = message;
+
+ queue_push_tail(msg);
+}
+
+void burn_message_warning_new(struct burn_drive *drive,
+ enum burn_message_info message)
+{
+ struct burn_message *msg;
+
+ msg = malloc(sizeof(struct burn_message));
+ msg->drive = drive;
+ msg->type = BURN_MESSAGE_WARNING;
+ msg->detail.warning.message = message;
+
+ queue_push_tail(msg);
+}
+
+void burn_message_error_new(struct burn_drive *drive,
+ enum burn_message_info message)
+{
+ struct burn_message *msg;
+
+ msg = malloc(sizeof(struct burn_message));
+ msg->drive = drive;
+ msg->type = BURN_MESSAGE_ERROR;
+ msg->detail.error.message = message;
+
+ queue_push_tail(msg);
+}
diff --git a/tags/ZeroTwoTwo/libburn/message.h b/tags/ZeroTwoTwo/libburn/message.h
new file mode 100644
index 00000000..32613cde
--- /dev/null
+++ b/tags/ZeroTwoTwo/libburn/message.h
@@ -0,0 +1,19 @@
+/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */
+
+#ifndef __MESSAGE
+#define __MESSAGE
+
+#include "libburn.h"
+
+void burn_message_clear_queue(void);
+
+void burn_message_info_new(struct burn_drive *drive,
+ enum burn_message_info message);
+
+void burn_message_warning_new(struct burn_drive *drive,
+ enum burn_message_info message);
+
+void burn_message_error_new(struct burn_drive *drive,
+ enum burn_message_info message);
+
+#endif
diff --git a/tags/ZeroTwoTwo/libburn/mmc.c b/tags/ZeroTwoTwo/libburn/mmc.c
new file mode 100644
index 00000000..17705e2c
--- /dev/null
+++ b/tags/ZeroTwoTwo/libburn/mmc.c
@@ -0,0 +1,544 @@
+/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include "error.h"
+#include "sector.h"
+#include "libburn.h"
+#include "transport.h"
+#include "mmc.h"
+#include "spc.h"
+#include "drive.h"
+#include "debug.h"
+#include "toc.h"
+#include "structure.h"
+#include "options.h"
+
+static unsigned char MMC_GET_TOC[] = { 0x43, 2, 2, 0, 0, 0, 0, 16, 0, 0 };
+static unsigned char MMC_GET_ATIP[] = { 0x43, 2, 4, 0, 0, 0, 0, 16, 0, 0 };
+static unsigned char MMC_GET_DISC_INFO[] =
+ { 0x51, 0, 0, 0, 0, 0, 0, 16, 0, 0 };
+static unsigned char MMC_READ_CD[] = { 0xBE, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+static unsigned char MMC_ERASE[] = { 0xA1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+static unsigned char MMC_SEND_OPC[] = { 0x54, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+static unsigned char MMC_SET_SPEED[] =
+ { 0xBB, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+static unsigned char MMC_WRITE_12[] =
+ { 0xAA, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+static unsigned char MMC_WRITE_10[] = { 0x2A, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+static unsigned char MMC_GET_CONFIGURATION[] =
+ { 0x46, 0, 0, 0, 0, 0, 16, 0, 0 };
+static unsigned char MMC_SYNC_CACHE[] = { 0x35, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+static unsigned char MMC_GET_EVENT[] = { 0x4A, 1, 0, 0, 16, 0, 0, 0, 8, 0 };
+static unsigned char MMC_CLOSE[] = { 0x5B, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+static unsigned char MMC_TRACK_INFO[] = { 0x52, 0, 0, 0, 0, 0, 0, 16, 0, 0 };
+static unsigned char MMC_SEND_CUE_SHEET[] =
+ { 0x5D, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+
+
+static int mmc_function_spy_do_tell = 0;
+
+int mmc_function_spy(char * text)
+{
+
+ if (mmc_function_spy_do_tell)
+ fprintf(stderr,"libburn: experimental: mmc_function_spy: %s\n",
+ text);
+ return 1;
+}
+
+int mmc_function_spy_ctrl(int do_tell)
+{
+ mmc_function_spy_do_tell= !!do_tell;
+ return 1;
+}
+
+
+void mmc_send_cue_sheet(struct burn_drive *d, struct cue_sheet *s)
+{
+ struct buffer buf;
+ struct command c;
+
+
+ mmc_function_spy("mmc_send_cue_sheet");
+ c.retry = 1;
+ c.oplen = sizeof(MMC_SEND_CUE_SHEET);
+ memcpy(c.opcode, MMC_SEND_CUE_SHEET, sizeof(MMC_SEND_CUE_SHEET));
+ c.page = &buf;
+ c.page->bytes = s->count * 8;
+ c.page->sectors = 0;
+ c.opcode[6] = (c.page->bytes >> 16) & 0xFF;
+ c.opcode[7] = (c.page->bytes >> 8) & 0xFF;
+ c.opcode[8] = c.page->bytes & 0xFF;
+ c.dir = TO_DRIVE;
+ memcpy(c.page->data, s->data, c.page->bytes);
+ d->issue_command(d, &c);
+}
+
+int mmc_get_nwa(struct burn_drive *d)
+{
+ struct buffer buf;
+ struct command c;
+ unsigned char *data;
+
+ mmc_function_spy("mmc_get_nwa");
+ c.retry = 1;
+ c.oplen = sizeof(MMC_TRACK_INFO);
+ memcpy(c.opcode, MMC_TRACK_INFO, sizeof(MMC_TRACK_INFO));
+ c.opcode[1] = 1;
+ c.opcode[5] = 0xFF;
+ c.page = &buf;
+ c.dir = FROM_DRIVE;
+ d->issue_command(d, &c);
+ data = c.page->data;
+ return (data[12] << 24) + (data[13] << 16)
+ + (data[14] << 8) + data[15];
+}
+
+void mmc_close_disc(struct burn_drive *d, struct burn_write_opts *o)
+{
+ mmc_function_spy("mmc_close_disc");
+ assert(o->drive == d);
+ o->multi = 0;
+ spc_select_write_params(d, o);
+ mmc_close(d, 1, 0);
+}
+
+void mmc_close_session(struct burn_drive *d, struct burn_write_opts *o)
+{
+ mmc_function_spy("mmc_close_session");
+ assert(o->drive == d);
+ o->multi = 3;
+ spc_select_write_params(d, o);
+ mmc_close(d, 1, 0);
+}
+
+void mmc_close(struct burn_drive *d, int session, int track)
+{
+ struct command c;
+
+ mmc_function_spy("mmc_close_session");
+ c.retry = 1;
+ c.oplen = sizeof(MMC_CLOSE);
+ memcpy(c.opcode, MMC_CLOSE, sizeof(MMC_CLOSE));
+ c.opcode[2] = session | !!track;
+ c.opcode[4] = track >> 8;
+ c.opcode[5] = track & 0xFF;
+ c.page = NULL;
+ c.dir = NO_TRANSFER;
+ d->issue_command(d, &c);
+}
+
+void mmc_get_event(struct burn_drive *d)
+{
+ struct buffer buf;
+ struct command c;
+
+ mmc_function_spy("mmc_get_event");
+ c.retry = 1;
+ c.oplen = sizeof(MMC_GET_EVENT);
+ memcpy(c.opcode, MMC_GET_EVENT, sizeof(MMC_GET_EVENT));
+ c.page = &buf;
+ c.page->bytes = 0;
+ c.page->sectors = 0;
+ c.dir = FROM_DRIVE;
+ d->issue_command(d, &c);
+ burn_print(12, "0x%x:0x%x:0x%x:0x%x\n",
+ c.page->data[0], c.page->data[1], c.page->data[2],
+ c.page->data[3]);
+ burn_print(12, "event: %d:%d:%d:%d\n", c.page->data[4],
+ c.page->data[5], c.page->data[6], c.page->data[7]);
+}
+
+void mmc_write_12(struct burn_drive *d, int start, struct buffer *buf)
+{
+ struct command c;
+ int len;
+
+ mmc_function_spy("mmc_write_12");
+ len = buf->sectors;
+ assert(buf->bytes >= buf->sectors); /* can be == at 0... */
+ burn_print(100, "trying to write %d at %d\n", len, start);
+ memcpy(c.opcode, MMC_WRITE_12, sizeof(MMC_WRITE_12));
+ c.retry = 1;
+ c.oplen = sizeof(MMC_WRITE_12);
+ c.opcode[2] = start >> 24;
+ c.opcode[3] = (start >> 16) & 0xFF;
+ c.opcode[4] = (start >> 8) & 0xFF;
+ c.opcode[5] = start & 0xFF;
+ c.opcode[6] = len >> 24;
+ c.opcode[7] = (len >> 16) & 0xFF;
+ c.opcode[8] = (len >> 8) & 0xFF;
+ c.opcode[9] = len & 0xFF;
+ c.page = buf;
+ c.dir = TO_DRIVE;
+
+ d->issue_command(d, &c);
+}
+
+int mmc_write(struct burn_drive *d, int start, struct buffer *buf)
+{
+ int cancelled;
+ struct command c;
+ int len;
+
+ mmc_function_spy("mmc_write");
+ pthread_mutex_lock(&d->access_lock);
+ cancelled = d->cancel;
+ pthread_mutex_unlock(&d->access_lock);
+
+ if (cancelled)
+ return BE_CANCELLED;
+
+ len = buf->sectors;
+ assert(buf->bytes >= buf->sectors); /* can be == at 0... */
+ burn_print(100, "trying to write %d at %d\n", len, start);
+ memcpy(c.opcode, MMC_WRITE_10, sizeof(MMC_WRITE_10));
+ c.retry = 1;
+ c.oplen = sizeof(MMC_WRITE_10);
+ c.opcode[2] = start >> 24;
+ c.opcode[3] = (start >> 16) & 0xFF;
+ c.opcode[4] = (start >> 8) & 0xFF;
+ c.opcode[5] = start & 0xFF;
+ c.opcode[6] = 0;
+ c.opcode[7] = (len >> 8) & 0xFF;
+ c.opcode[8] = len & 0xFF;
+ c.page = buf;
+ c.dir = TO_DRIVE;
+/*
+ burn_print(12, "%d, %d, %d, %d - ", c->opcode[2], c->opcode[3], c->opcode[4], c->opcode[5]);
+ burn_print(12, "%d, %d, %d, %d\n", c->opcode[6], c->opcode[7], c->opcode[8], c->opcode[9]);
+*/
+
+/* write(fileno(stderr), c.page->data, c.page->bytes);*/
+
+ d->issue_command(d, &c);
+ return 0;
+}
+
+void mmc_read_toc(struct burn_drive *d)
+{
+/* read full toc, all sessions, in m/s/f form, 4k buffer */
+ struct burn_track *track;
+ struct burn_session *session;
+ struct buffer buf;
+ struct command c;
+ int dlen;
+ int i;
+ unsigned char *tdata;
+
+ mmc_function_spy("mmc_read_toc");
+ memcpy(c.opcode, MMC_GET_TOC, sizeof(MMC_GET_TOC));
+ c.retry = 1;
+ c.oplen = sizeof(MMC_GET_TOC);
+ c.page = &buf;
+ c.page->bytes = 0;
+ c.page->sectors = 0;
+ c.dir = FROM_DRIVE;
+ d->issue_command(d, &c);
+
+ if (c.error) {
+ d->busy = BURN_DRIVE_IDLE;
+ return;
+ }
+
+ dlen = c.page->data[0] * 256 + c.page->data[1];
+ d->toc_entries = (dlen - 2) / 11;
+/*
+ some drives fail this check.
+
+ assert(((dlen - 2) % 11) == 0);
+*/
+ d->toc_entry = malloc(d->toc_entries * sizeof(struct burn_toc_entry));
+ tdata = c.page->data + 4;
+
+ burn_print(12, "TOC:\n");
+
+ d->disc = burn_disc_create();
+
+ for (i = 0; i < c.page->data[3]; i++) {
+ session = burn_session_create();
+ burn_disc_add_session(d->disc, session, BURN_POS_END);
+ burn_session_free(session);
+ }
+ for (i = 0; i < d->toc_entries; i++, tdata += 11) {
+ burn_print(12, "S %d, PT %d, TNO %d : ", tdata[0], tdata[3],
+ tdata[2]);
+ burn_print(12, "(%d:%d:%d)", tdata[8], tdata[9], tdata[10]);
+ burn_print(12, "A(%d:%d:%d)", tdata[4], tdata[5], tdata[6]);
+ burn_print(12, " - control %d, adr %d\n", tdata[1] & 0xF,
+ tdata[1] >> 4);
+
+ if (tdata[3] == 1) {
+ if (burn_msf_to_lba(tdata[8], tdata[9], tdata[10])) {
+ d->disc->session[0]->hidefirst = 1;
+ track = burn_track_create();
+ burn_session_add_track(d->disc->
+ session[tdata[0] - 1],
+ track, BURN_POS_END);
+ burn_track_free(track);
+
+ }
+ }
+ if (tdata[3] < 100) {
+ track = burn_track_create();
+ burn_session_add_track(d->disc->session[tdata[0] - 1],
+ track, BURN_POS_END);
+ track->entry = &d->toc_entry[i];
+ burn_track_free(track);
+ }
+ d->toc_entry[i].session = tdata[0];
+ d->toc_entry[i].adr = tdata[1] >> 4;
+ d->toc_entry[i].control = tdata[1] & 0xF;
+ d->toc_entry[i].tno = tdata[2];
+ d->toc_entry[i].point = tdata[3];
+ d->toc_entry[i].min = tdata[4];
+ d->toc_entry[i].sec = tdata[5];
+ d->toc_entry[i].frame = tdata[6];
+ d->toc_entry[i].zero = tdata[7];
+ d->toc_entry[i].pmin = tdata[8];
+ d->toc_entry[i].psec = tdata[9];
+ d->toc_entry[i].pframe = tdata[10];
+ if (tdata[3] == 0xA0)
+ d->disc->session[tdata[0] - 1]->firsttrack = tdata[8];
+ if (tdata[3] == 0xA1)
+ d->disc->session[tdata[0] - 1]->lasttrack = tdata[8];
+ if (tdata[3] == 0xA2)
+ d->disc->session[tdata[0] - 1]->leadout_entry =
+ &d->toc_entry[i];
+ }
+ if (d->status != BURN_DISC_APPENDABLE)
+ d->status = BURN_DISC_FULL;
+ toc_find_modes(d);
+}
+
+void mmc_read_disc_info(struct burn_drive *d)
+{
+ struct buffer buf;
+ unsigned char *data;
+ struct command c;
+
+ mmc_function_spy("mmc_read_disc_info");
+ memcpy(c.opcode, MMC_GET_DISC_INFO, sizeof(MMC_GET_DISC_INFO));
+ c.retry = 1;
+ c.oplen = sizeof(MMC_GET_DISC_INFO);
+ c.page = &buf;
+ c.page->sectors = 0;
+ c.page->bytes = 0;
+ c.dir = FROM_DRIVE;
+ d->issue_command(d, &c);
+
+ if (c.error) {
+ d->busy = BURN_DRIVE_IDLE;
+ return;
+ }
+
+ data = c.page->data;
+ d->erasable = !!(data[2] & 16);
+ switch (data[2] & 3) {
+ case 0:
+ d->toc_entries = 0;
+ d->start_lba = burn_msf_to_lba(data[17], data[18], data[19]);
+ d->end_lba = burn_msf_to_lba(data[21], data[22], data[23]);
+ d->status = BURN_DISC_BLANK;
+ break;
+ case 1:
+ d->status = BURN_DISC_APPENDABLE;
+ case 2:
+ mmc_read_toc(d);
+ break;
+ }
+}
+
+void mmc_read_atip(struct burn_drive *d)
+{
+ struct buffer buf;
+ struct command c;
+
+ mmc_function_spy("mmc_read_atip");
+ memcpy(c.opcode, MMC_GET_ATIP, sizeof(MMC_GET_ATIP));
+ c.retry = 1;
+ c.oplen = sizeof(MMC_GET_ATIP);
+ c.page = &buf;
+ c.page->bytes = 0;
+ c.page->sectors = 0;
+
+ c.dir = FROM_DRIVE;
+ d->issue_command(d, &c);
+ burn_print(1, "atip shit for you\n");
+}
+
+void mmc_read_sectors(struct burn_drive *d,
+ int start,
+ int len,
+ const struct burn_read_opts *o, struct buffer *buf)
+{
+ int temp;
+ int errorblock, req;
+ struct command c;
+
+ mmc_function_spy("mmc_read_sectors");
+ assert(len >= 0);
+/* if the drive isn't busy, why the hell are we here? */
+ assert(d->busy);
+ burn_print(12, "reading %d from %d\n", len, start);
+ memcpy(c.opcode, MMC_READ_CD, sizeof(MMC_READ_CD));
+ c.retry = 1;
+ c.oplen = sizeof(MMC_READ_CD);
+ temp = start;
+ c.opcode[5] = temp & 0xFF;
+ temp >>= 8;
+ c.opcode[4] = temp & 0xFF;
+ temp >>= 8;
+ c.opcode[3] = temp & 0xFF;
+ temp >>= 8;
+ c.opcode[2] = temp & 0xFF;
+ c.opcode[8] = len & 0xFF;
+ len >>= 8;
+ c.opcode[7] = len & 0xFF;
+ len >>= 8;
+ c.opcode[6] = len & 0xFF;
+ req = 0xF8;
+ if (d->busy == BURN_DRIVE_GRABBING || o->report_recovered_errors)
+ req |= 2;
+
+ c.opcode[10] = 0;
+/* always read the subcode, throw it away later, since we don't know
+ what we're really reading
+*/
+ if (d->busy == BURN_DRIVE_GRABBING || (o->subcodes_audio)
+ || (o->subcodes_data))
+ c.opcode[10] = 1;
+
+ c.opcode[9] = req;
+ c.page = buf;
+ c.dir = FROM_DRIVE;
+ d->issue_command(d, &c);
+
+ if (c.error) {
+ burn_print(12, "got an error over here\n");
+ burn_print(12, "%d, %d, %d, %d\n", c.sense[3], c.sense[4],
+ c.sense[5], c.sense[6]);
+ errorblock =
+ (c.sense[3] << 24) + (c.sense[4] << 16) +
+ (c.sense[5] << 8) + c.sense[6];
+ c.page->sectors = errorblock - start + 1;
+ burn_print(1, "error on block %d\n", errorblock);
+ burn_print(12, "error on block %d\n", errorblock);
+ burn_print(12, "returning %d sectors\n", c.page->sectors);
+ }
+}
+
+void mmc_erase(struct burn_drive *d, int fast)
+{
+ struct command c;
+
+ mmc_function_spy("mmc_erase");
+ memcpy(c.opcode, MMC_ERASE, sizeof(MMC_ERASE));
+ c.opcode[1] = 16; /* IMMED set to 1 */
+ c.opcode[1] |= !!fast;
+ c.retry = 1;
+ c.oplen = sizeof(MMC_ERASE);
+ c.page = NULL;
+ c.dir = NO_TRANSFER;
+ d->issue_command(d, &c);
+}
+
+void mmc_read_lead_in(struct burn_drive *d, struct buffer *buf)
+{
+ int len;
+ struct command c;
+
+ mmc_function_spy("mmc_read_lead_in");
+ len = buf->sectors;
+ memcpy(c.opcode, MMC_READ_CD, sizeof(MMC_READ_CD));
+ c.retry = 1;
+ c.oplen = sizeof(MMC_READ_CD);
+ c.opcode[5] = 0;
+ c.opcode[4] = 0;
+ c.opcode[3] = 0;
+ c.opcode[2] = 0xF0;
+ c.opcode[8] = 1;
+ c.opcode[7] = 0;
+ c.opcode[6] = 0;
+ c.opcode[9] = 0;
+ c.opcode[10] = 2;
+ c.page = buf;
+ c.dir = FROM_DRIVE;
+ d->issue_command(d, &c);
+}
+
+void mmc_perform_opc(struct burn_drive *d)
+{
+ struct command c;
+
+ mmc_function_spy("mmc_perform_opc");
+ memcpy(c.opcode, MMC_SEND_OPC, sizeof(MMC_SEND_OPC));
+ c.retry = 1;
+ c.oplen = sizeof(MMC_SEND_OPC);
+ c.opcode[1] = 1;
+ c.page = NULL;
+ c.dir = NO_TRANSFER;
+ d->issue_command(d, &c);
+}
+
+void mmc_set_speed(struct burn_drive *d, int r, int w)
+{
+ struct command c;
+
+ mmc_function_spy("mmc_set_speed");
+ memcpy(c.opcode, MMC_SET_SPEED, sizeof(MMC_SET_SPEED));
+ c.retry = 1;
+ c.oplen = sizeof(MMC_SET_SPEED);
+ c.opcode[2] = r >> 8;
+ c.opcode[3] = r & 0xFF;
+ c.opcode[4] = w >> 8;
+ c.opcode[5] = w & 0xFF;
+ c.page = NULL;
+ c.dir = NO_TRANSFER;
+ d->issue_command(d, &c);
+}
+
+void mmc_get_configuration(struct burn_drive *d)
+{
+ struct buffer buf;
+ int len;
+ struct command c;
+
+ mmc_function_spy("mmc_get_configuration");
+ memcpy(c.opcode, MMC_GET_CONFIGURATION, sizeof(MMC_GET_CONFIGURATION));
+ c.retry = 1;
+ c.oplen = sizeof(MMC_GET_CONFIGURATION);
+ c.page = &buf;
+ c.page->sectors = 0;
+ c.page->bytes = 0;
+ c.dir = FROM_DRIVE;
+ d->issue_command(d, &c);
+
+ burn_print(1, "got it back\n");
+ len = (c.page->data[0] << 24)
+ + (c.page->data[1] << 16)
+ + (c.page->data[2] << 8)
+ + c.page->data[3];
+ burn_print(1, "all %d bytes of it\n", len);
+ burn_print(1, "%d, %d, %d, %d\n",
+ c.page->data[0],
+ c.page->data[1], c.page->data[2], c.page->data[3]);
+}
+
+void mmc_sync_cache(struct burn_drive *d)
+{
+ struct command c;
+
+ mmc_function_spy("mmc_sync_cache");
+ memcpy(c.opcode, MMC_SYNC_CACHE, sizeof(MMC_SYNC_CACHE));
+ c.retry = 1;
+ c.oplen = sizeof(MMC_SYNC_CACHE);
+ c.page = NULL;
+ c.dir = NO_TRANSFER;
+ d->issue_command(d, &c);
+}
diff --git a/tags/ZeroTwoTwo/libburn/mmc.h b/tags/ZeroTwoTwo/libburn/mmc.h
new file mode 100644
index 00000000..95275b7c
--- /dev/null
+++ b/tags/ZeroTwoTwo/libburn/mmc.h
@@ -0,0 +1,37 @@
+/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */
+
+#ifndef __MMC
+#define __MMC
+
+struct burn_drive;
+struct burn_write_opts;
+struct command;
+struct buffer;
+struct cue_sheet;
+
+/* MMC commands */
+
+void mmc_read(struct burn_drive *);
+void mmc_close_session(struct burn_drive *, struct burn_write_opts *);
+void mmc_close_disc(struct burn_drive *, struct burn_write_opts *);
+void mmc_close(struct burn_drive *, int session, int track);
+void mmc_get_event(struct burn_drive *);
+int mmc_write(struct burn_drive *, int start, struct buffer *buf);
+void mmc_write_12(struct burn_drive *d, int start, struct buffer *buf);
+void mmc_sync_cache(struct burn_drive *);
+void mmc_load(struct burn_drive *);
+void mmc_eject(struct burn_drive *);
+void mmc_erase(struct burn_drive *, int);
+void mmc_read_toc(struct burn_drive *);
+void mmc_read_disc_info(struct burn_drive *);
+void mmc_read_atip(struct burn_drive *);
+void mmc_read_sectors(struct burn_drive *,
+ int,
+ int, const struct burn_read_opts *, struct buffer *);
+void mmc_set_speed(struct burn_drive *, int, int);
+void mmc_read_lead_in(struct burn_drive *, struct buffer *);
+void mmc_perform_opc(struct burn_drive *);
+void mmc_get_configuration(struct burn_drive *);
+int mmc_get_nwa(struct burn_drive *);
+void mmc_send_cue_sheet(struct burn_drive *, struct cue_sheet *);
+#endif /*__MMC*/
diff --git a/tags/ZeroTwoTwo/libburn/null.c b/tags/ZeroTwoTwo/libburn/null.c
new file mode 100644
index 00000000..415e0a49
--- /dev/null
+++ b/tags/ZeroTwoTwo/libburn/null.c
@@ -0,0 +1,27 @@
+/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */
+
+#include "null.h"
+#include "libburn.h"
+#include
+
+#include
+int null_read(struct burn_source *source, unsigned char *buffer, int size)
+{
+ memset(buffer, 0, size);
+ return size;
+}
+
+struct burn_source *burn_null_source_new(void)
+{
+ struct burn_source *src;
+
+ src = malloc(sizeof(struct burn_source));
+ src->refcount = 1;
+ src->read = null_read;
+ src->read_sub = NULL;
+
+ src->get_size = 0;
+ src->free_data = NULL;
+ src->data = NULL;
+ return src;
+}
diff --git a/tags/ZeroTwoTwo/libburn/null.h b/tags/ZeroTwoTwo/libburn/null.h
new file mode 100644
index 00000000..1a7aae33
--- /dev/null
+++ b/tags/ZeroTwoTwo/libburn/null.h
@@ -0,0 +1,10 @@
+/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */
+
+#ifndef BURN__NULL_H
+#define BURN__NULL_H
+
+struct burn_source;
+int null_read(struct burn_source *source, unsigned char *buffer, int size);
+struct burn_source *burn_null_source_new(void);
+
+#endif /* LIBBURN__NULL_H */
diff --git a/tags/ZeroTwoTwo/libburn/options.c b/tags/ZeroTwoTwo/libburn/options.c
new file mode 100644
index 00000000..938a23c1
--- /dev/null
+++ b/tags/ZeroTwoTwo/libburn/options.c
@@ -0,0 +1,169 @@
+#include "libburn.h"
+#include "options.h"
+#include "transport.h"
+
+#include
+#include
+#include
+struct burn_write_opts *burn_write_opts_new(struct burn_drive *drive)
+{
+ struct burn_write_opts *opts;
+
+ opts = malloc(sizeof(struct burn_write_opts));
+ opts->drive = drive;
+ opts->refcount = 1;
+ opts->write_type = BURN_WRITE_TAO;
+ opts->block_type = BURN_BLOCK_MODE1;
+ opts->toc_entry = NULL;
+ opts->toc_entries = 0;
+ opts->simulate = 0;
+ opts->underrun_proof = drive->mdata->underrun_proof;
+ opts->perform_opc = 1;
+ opts->has_mediacatalog = 0;
+ opts->format = BURN_CDROM;
+ opts->multi = 0;
+ opts->control = 0;
+ return opts;
+}
+
+void burn_write_opts_free(struct burn_write_opts *opts)
+{
+ if (--opts->refcount <= 0)
+ free(opts);
+}
+
+struct burn_read_opts *burn_read_opts_new(struct burn_drive *drive)
+{
+ struct burn_read_opts *opts;
+
+ opts = malloc(sizeof(struct burn_read_opts));
+ opts->drive = drive;
+ opts->refcount = 1;
+ opts->raw = 0;
+ opts->c2errors = 0;
+ opts->subcodes_audio = 0;
+ opts->subcodes_data = 0;
+ opts->hardware_error_recovery = 0;
+ opts->report_recovered_errors = 0;
+ opts->transfer_damaged_blocks = 0;
+ opts->hardware_error_retries = 3;
+
+ return opts;
+}
+
+void burn_read_opts_free(struct burn_read_opts *opts)
+{
+ if (--opts->refcount <= 0)
+ free(opts);
+}
+
+int burn_write_opts_set_write_type(struct burn_write_opts *opts,
+ enum burn_write_types write_type,
+ int block_type)
+{
+ if ((write_type == BURN_WRITE_SAO && block_type == BURN_BLOCK_SAO) ||
+ (opts->drive->block_types[write_type] & block_type)) {
+ opts->write_type = write_type;
+ opts->block_type = block_type;
+ return 1;
+ }
+ assert(0);
+ return 0;
+}
+
+void burn_write_opts_set_toc_entries(struct burn_write_opts *opts, int count,
+ struct burn_toc_entry *toc_entries)
+{
+ opts->toc_entries = count;
+ opts->toc_entry = malloc(count * sizeof(struct burn_toc_entry));
+ memcpy(opts->toc_entry, &toc_entries,
+ sizeof(struct burn_toc_entry) * count);
+}
+
+void burn_write_opts_set_format(struct burn_write_opts *opts, int format)
+{
+ opts->format = format;
+}
+
+int burn_write_opts_set_simulate(struct burn_write_opts *opts, int sim)
+{
+ if (opts->drive->mdata->simulate) {
+ opts->simulate = sim;
+ return 1;
+ }
+ return 0;
+}
+
+int burn_write_opts_set_underrun_proof(struct burn_write_opts *opts,
+ int underrun_proof)
+{
+ if (opts->drive->mdata->underrun_proof) {
+ opts->underrun_proof = underrun_proof;
+ return 1;
+ }
+ return 0;
+}
+
+void burn_write_opts_set_perform_opc(struct burn_write_opts *opts, int opc)
+{
+ opts->perform_opc = opc;
+}
+
+void burn_write_opts_set_has_mediacatalog(struct burn_write_opts *opts,
+ int has_mediacatalog)
+{
+ opts->has_mediacatalog = has_mediacatalog;
+}
+
+void burn_write_opts_set_mediacatalog(struct burn_write_opts *opts,
+ unsigned char mediacatalog[13])
+{
+ memcpy(opts->mediacatalog, &mediacatalog, 13);
+}
+
+void burn_read_opts_set_raw(struct burn_read_opts *opts, int raw)
+{
+ opts->raw = raw;
+}
+
+void burn_read_opts_set_c2errors(struct burn_read_opts *opts, int c2errors)
+{
+ opts->c2errors = c2errors;
+}
+
+void burn_read_opts_read_subcodes_audio(struct burn_read_opts *opts,
+ int subcodes_audio)
+{
+ opts->subcodes_audio = subcodes_audio;
+}
+
+void burn_read_opts_read_subcodes_data(struct burn_read_opts *opts,
+ int subcodes_data)
+{
+ opts->subcodes_data = subcodes_data;
+}
+
+void burn_read_opts_set_hardware_error_recovery(struct burn_read_opts *opts,
+ int hardware_error_recovery)
+{
+ opts->hardware_error_recovery = hardware_error_recovery;
+}
+
+void burn_read_opts_report_recovered_errors(struct burn_read_opts *opts,
+ int report_recovered_errors)
+{
+ opts->report_recovered_errors = report_recovered_errors;
+}
+
+void burn_read_opts_transfer_damaged_blocks(struct burn_read_opts *opts,
+ int transfer_damaged_blocks)
+{
+ opts->transfer_damaged_blocks = transfer_damaged_blocks;
+}
+
+void burn_read_opts_set_hardware_error_retries(struct burn_read_opts *opts,
+ unsigned char
+ hardware_error_retries)
+{
+ opts->hardware_error_retries = hardware_error_retries;
+}
diff --git a/tags/ZeroTwoTwo/libburn/options.h b/tags/ZeroTwoTwo/libburn/options.h
new file mode 100644
index 00000000..aebfdb5d
--- /dev/null
+++ b/tags/ZeroTwoTwo/libburn/options.h
@@ -0,0 +1,78 @@
+#ifndef BURN__OPTIONS_H
+#define BURN__OPTIONS_H
+
+#include "libburn.h"
+
+/** Options for disc writing operations. This should be created with
+ burn_write_opts_new() and freed with burn_write_opts_free(). */
+struct burn_write_opts
+{
+ /** Drive the write opts are good for */
+ struct burn_drive *drive;
+
+ /** For internal use. */
+ int refcount;
+
+ /** The method/style of writing to use. */
+ enum burn_write_types write_type;
+ /** format of the data to send to the drive */
+ enum burn_block_types block_type;
+
+ /** Number of toc entries. if this is 0, they will be auto generated*/
+ int toc_entries;
+ /** Toc entries for the disc */
+ struct burn_toc_entry *toc_entry;
+
+ /** Simulate the write so that the disc is not actually written */
+ unsigned int simulate:1;
+ /** If available, enable a drive feature which prevents buffer
+ underruns if not enough data is available to keep up with the
+ drive. */
+ unsigned int underrun_proof:1;
+ /** Perform calibration of the drive's laser before beginning the
+ write. */
+ unsigned int perform_opc:1;
+ /** A disc can have a media catalog number */
+ int has_mediacatalog;
+ unsigned char mediacatalog[13];
+ /** Session format */
+ int format;
+ /* internal use only */
+ unsigned char control;
+ unsigned char multi;
+};
+
+/** Options for disc reading operations. This should be created with
+ burn_read_opts_new() and freed with burn_read_opts_free(). */
+struct burn_read_opts
+{
+ /** Drive the read opts are good for */
+ struct burn_drive *drive;
+
+ /** For internal use. */
+ int refcount;
+
+ /** Read in raw mode, so that everything in the data tracks on the
+ disc is read, including headers. Not needed if just reading a
+ filesystem off a disc, but it should usually be used when making a
+ disc image or copying a disc. */
+ unsigned int raw:1;
+ /** Report c2 errors. Useful for statistics reporting */
+ unsigned int c2errors:1;
+ /** Read subcodes from audio tracks on the disc */
+ unsigned int subcodes_audio:1;
+ /** Read subcodes from data tracks on the disc */
+ unsigned int subcodes_data:1;
+ /** Have the drive recover errors if possible */
+ unsigned int hardware_error_recovery:1;
+ /** Report errors even when they were recovered from */
+ unsigned int report_recovered_errors:1;
+ /** Read blocks even when there are unrecoverable errors in them */
+ unsigned int transfer_damaged_blocks:1;
+
+ /** The number of retries the hardware should make to correct
+ errors. */
+ unsigned char hardware_error_retries;
+};
+
+#endif /* BURN__OPTIONS_H */
diff --git a/tags/ZeroTwoTwo/libburn/read.c b/tags/ZeroTwoTwo/libburn/read.c
new file mode 100644
index 00000000..4417ef1d
--- /dev/null
+++ b/tags/ZeroTwoTwo/libburn/read.c
@@ -0,0 +1,264 @@
+/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include "sector.h"
+#include "libburn.h"
+#include "drive.h"
+#include "transport.h"
+#include "message.h"
+#include "crc.h"
+#include "debug.h"
+#include "init.h"
+#include "lec.h"
+#include "toc.h"
+#include "util.h"
+#include "sg.h"
+#include "read.h"
+#include "options.h"
+
+void burn_disc_read(struct burn_drive *d, const struct burn_read_opts *o)
+{
+#if 0
+ int i, end, maxsects, finish;
+ int seclen;
+ int drive_lba;
+ unsigned short crc;
+ unsigned char fakesub[96];
+ struct buffer page;
+ int speed;
+
+ assert((o->version & 0xfffff000) == (OPTIONS_VERSION & 0xfffff000));
+
+ assert(!d->busy);
+ assert(d->toc->valid);
+ assert(o->datafd != -1);
+/* XXX not sure this is a good idea. copy it? */
+/* XXX also, we have duplicated data now, do we remove the fds from struct
+drive, or only store a subset of the _opts structs in drives */
+
+ /* set the speed on the drive */
+ speed = o->speed > 0 ? o->speed : d->mdata->max_read_speed;
+ d->set_speed(d, speed, 0);
+
+ d->params.retries = o->hardware_error_retries;
+ d->send_parameters(d, o);
+
+ d->cancel = 0;
+ d->busy = BURN_DRIVE_READING;
+ d->currsession = 0;
+/* drive_lba = 232000;
+ d->currtrack = 18;
+*/
+ d->currtrack = 0;
+ drive_lba = 0;
+/* XXX removal of this line obviously breaks *
+ d->track_end = burn_track_end(d, d->currsession, d->currtrack);*/
+ printf("track ends at %d\n", d->track_end);
+ page.sectors = 0;
+ page.bytes = 0;
+
+ if (o->subfd != -1) {
+ memset(fakesub, 0xFF, 12);
+ memset(fakesub + 12, 0, 84);
+ fakesub[13] = 1;
+ fakesub[14] = 1;
+ fakesub[20] = 2;
+ fakesub[12] = (d->toc->toc_entry[0].control << 4) +
+ d->toc->toc_entry[0].adr;
+ crc = crc_ccitt(fakesub + 12, 10);
+ fakesub[22] = crc >> 8;
+ fakesub[23] = crc & 0xFF;
+ write(o->subfd, fakesub, 96);
+ }
+ while (1) {
+ seclen = burn_sector_length_read(d, o);
+
+ burn_print(12, "received %d blocks\n", page.sectors);
+ for (i = 0; i < page.sectors; i++) {
+ burn_packet_process(d, page.data + seclen * i, o);
+ d->track_end--;
+ drive_lba++;
+ }
+
+ if ((d->cancel) || (drive_lba == LAST_SESSION_END(d))) {
+ burn_print(1, "finished or cancelled\n");
+ d->busy = BURN_DRIVE_IDLE;
+ if (!d->cancel)
+ d->toc->complete = 1;
+ return;
+ }
+/* XXX: removal of this line obviously breaks *
+ end = burn_track_end(d, d->currsession, d->currtrack); */
+
+ if (drive_lba == end) {
+ d->currtrack++;
+ if (d->currtrack >
+ d->toc->session[d->currsession].lasttrack) {
+ d->currsession++;
+ burn_print(12, "session switch to %d\n",
+ d->currsession);
+ burn_print(12, "skipping a lead out\n");
+ drive_lba = CURRENT_SESSION_START(d);
+ burn_print(12, "new lba %d\n", drive_lba);
+/* XXX more of the same
+ end = burn_track_end(d, d->currsession,
+ d->currtrack);
+*/ }
+ burn_print(12, "track switch to %d\n", d->currtrack);
+ }
+
+ page.sectors = 0;
+ page.bytes = 0;
+
+ maxsects = BUFFER_SIZE / seclen;
+ finish = end - drive_lba;
+
+ d->track_end = finish;
+
+ page.sectors = (finish < maxsects) ? finish : maxsects;
+ printf("reading %d sectors from %d\n", page.sectors,
+ drive_lba);
+ d->read_sectors(d, drive_lba, page.sectors, o, &page);
+ printf("Read %d\n", page.sectors);
+ }
+#endif
+}
+int burn_sector_length_read(struct burn_drive *d,
+ const struct burn_read_opts *o)
+{
+ int dlen = 2352;
+ int data;
+
+/*XXX how do we handle this crap now?*/
+/* data = d->toc->track[d->currtrack].toc_entry->control & 4;*/
+ data = 1;
+ if (o->report_recovered_errors)
+ dlen += 294;
+ if ((o->subcodes_data) && data)
+ dlen += 96;
+ if ((o->subcodes_audio) && !data)
+ dlen += 96;
+ return dlen;
+}
+
+static int bitcount(unsigned char *data, int n)
+{
+ int i, j, count = 0;
+ unsigned char tem;
+
+ for (i = 0; i < n; i++) {
+ tem = data[i];
+ for (j = 0; j < 8; j++) {
+ count += tem & 1;
+ tem >>= 1;
+ }
+ }
+ return count;
+}
+
+void burn_packet_process(struct burn_drive *d, unsigned char *data,
+ const struct burn_read_opts *o)
+{
+ unsigned char sub[96];
+ unsigned short crc;
+ int ptr = 2352, i, j, code, fb;
+ int audio = 1;
+
+ if (o->c2errors) {
+ fb = bitcount(data + ptr, 294);
+ if (fb) {
+ burn_print(1, "%d damaged bits\n",
+ bitcount(data + ptr, 294));
+ burn_print(1, "sending error on %s %s\n",
+ d->idata->vendor, d->idata->product);
+ /* XXX send a burn_message! burn_message_error(d,
+ something); */
+ }
+ ptr += 294;
+ }
+/*
+ if (d->toc->track[d->currtrack].mode == BURN_MODE_UNINITIALIZED) {
+ if ((d->toc->track[d->currtrack].toc_entry->control & 4) == 0)
+ d->toc->track[d->currtrack].mode = BURN_MODE_AUDIO;
+ else
+ switch (data[15]) {
+ case 0:
+ d->toc->track[d->currtrack].mode = BURN_MODE0;
+ break;
+ case 1:
+ d->toc->track[d->currtrack].mode = BURN_MODE1;
+ break;
+ case 2:
+ d->toc->track[d->currtrack].mode =
+ BURN_MODE2_FORMLESS;
+ break;
+ }
+ }
+*/
+ if ((audio && o->subcodes_audio)
+ || (!audio && o->subcodes_data)) {
+ memset(sub, 0, sizeof(sub));
+ for (i = 0; i < 12; i++) {
+ for (j = 0; j < 8; j++) {
+ for (code = 0; code < 8; code++) {
+ sub[code * 12 + i] <<= 1;
+ if (data[ptr + j + i * 8] &
+ (1 << (7 - code)))
+ sub[code * 12 + i]++;
+ }
+ }
+ }
+ crc = (*(sub + 22) << 8) + *(sub + 23);
+ if (crc != crc_ccitt(sub + 12, 10)) {
+ burn_print(1, "sending error on %s %s\n",
+ d->idata->vendor, d->idata->product);
+/* e = burn_error();
+ e->drive = d;
+*/
+ burn_print(1, "crc mismatch in Q\n");
+ }
+ /* else process_q(d, sub + 12); */
+ /*
+ if (o->subfd != -1) write(o->subfd, sub, 96); */
+ }
+/*
+ if ((d->track_end <= 150)
+ && (drive_lba + 150 < CURRENT_SESSION_END(d))
+ && (TOC_ENTRY(d->toc, d->currtrack).control == 4)
+ && (TOC_ENTRY(d->toc, d->currtrack + 1).control == 0)) {
+ burn_print(12, "pregap : %d\n", d->track_end);
+ write(o->binfd, zeros, 2352);
+
+#warning XXX WHERE ARE MY SUBCODES
+ } else
+*//* write(o->datafd, data, 2352); */
+}
+
+/* so yeah, when you uncomment these, make them write zeros insted of crap
+static void write_empty_sector(int fd)
+{
+ char sec[2352];
+
+ burn_print(1, "writing an 'empty' sector\n");
+ write(fd, sec, 2352);
+}
+
+static void write_empty_subcode(int fd)
+{
+ char sub[96];
+
+ write(fd, sub, 96);
+}
+
+static void flipq(unsigned char *sub)
+{
+ *(sub + 12 + 10) = ~*(sub + 12 + 10);
+ *(sub + 12 + 11) = ~*(sub + 12 + 11);
+}
+*/
diff --git a/tags/ZeroTwoTwo/libburn/read.h b/tags/ZeroTwoTwo/libburn/read.h
new file mode 100644
index 00000000..fc079583
--- /dev/null
+++ b/tags/ZeroTwoTwo/libburn/read.h
@@ -0,0 +1,14 @@
+/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */
+
+#ifndef __LIBBURN_READ
+#define __LIBBURN_READ
+
+struct burn_drive;
+struct burn_read_opts;
+
+int burn_sector_length_read(struct burn_drive *d,
+ const struct burn_read_opts *o);
+void burn_packet_process(struct burn_drive *d, unsigned char *data,
+ const struct burn_read_opts *o);
+
+#endif /* __LIBBURN_READ */
diff --git a/tags/ZeroTwoTwo/libburn/sbc.c b/tags/ZeroTwoTwo/libburn/sbc.c
new file mode 100644
index 00000000..dcd64ae0
--- /dev/null
+++ b/tags/ZeroTwoTwo/libburn/sbc.c
@@ -0,0 +1,40 @@
+/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */
+
+/* scsi block commands */
+
+#include
+#include
+#include
+
+#include "transport.h"
+#include "sbc.h"
+#include "options.h"
+
+/* spc command set */
+static char SBC_LOAD[] = { 0x1b, 0, 0, 0, 3, 0 };
+static char SBC_UNLOAD[] = { 0x1b, 0, 0, 0, 2, 0 };
+
+void sbc_load(struct burn_drive *d)
+{
+ struct command c;
+
+ memcpy(c.opcode, SBC_LOAD, sizeof(SBC_LOAD));
+ c.retry = 1;
+ c.oplen = sizeof(SBC_LOAD);
+ c.dir = NO_TRANSFER;
+ c.page = NULL;
+ d->issue_command(d, &c);
+}
+
+void sbc_eject(struct burn_drive *d)
+{
+ struct command c;
+
+ c.page = NULL;
+ memcpy(c.opcode, SBC_UNLOAD, sizeof(SBC_UNLOAD));
+ c.oplen = 1;
+ c.oplen = sizeof(SBC_UNLOAD);
+ c.page = NULL;
+ c.dir = NO_TRANSFER;
+ d->issue_command(d, &c);
+}
diff --git a/tags/ZeroTwoTwo/libburn/sbc.h b/tags/ZeroTwoTwo/libburn/sbc.h
new file mode 100644
index 00000000..3ca028cf
--- /dev/null
+++ b/tags/ZeroTwoTwo/libburn/sbc.h
@@ -0,0 +1,11 @@
+/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */
+
+#ifndef __SBC
+#define __SBC
+
+struct burn_drive;
+
+void sbc_load(struct burn_drive *);
+void sbc_eject(struct burn_drive *);
+
+#endif /* __SBC */
diff --git a/tags/ZeroTwoTwo/libburn/sector.c b/tags/ZeroTwoTwo/libburn/sector.c
new file mode 100644
index 00000000..2d7ce17b
--- /dev/null
+++ b/tags/ZeroTwoTwo/libburn/sector.c
@@ -0,0 +1,649 @@
+/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */
+
+#include
+#include
+#include
+#include
+#include "error.h"
+#include "options.h"
+#include "transport.h"
+#include "libburn.h"
+#include "drive.h"
+#include "sector.h"
+#include "crc.h"
+#include "debug.h"
+#include "lec.h"
+#include "toc.h"
+#include "write.h"
+
+/*static unsigned char isrc[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";*/
+
+#define sector_common(X) d->alba++; d->rlba X;
+
+static void uncook_subs(unsigned char *dest, unsigned char *source)
+{
+ int i, j, code;
+
+ memset(dest, 0, 96);
+
+ for (i = 0; i < 12; i++) {
+ for (j = 0; j < 8; j++) {
+ for (code = 0; code < 8; code++) {
+ if (source[code * 12 + i] & 0x80)
+ dest[j + i * 8] |= (1 << (7 - code));
+ source[code * 12 + i] <<= 1;
+ }
+ }
+ }
+}
+
+/* 0 means "same as inmode" */
+static int get_outmode(struct burn_write_opts *o)
+{
+ if (o->write_type == BURN_WRITE_SAO)
+ return 0;
+ else
+ switch (o->block_type) {
+ case BURN_BLOCK_RAW0:
+ return BURN_MODE_RAW;
+ case BURN_BLOCK_RAW16:
+ return BURN_MODE_RAW | BURN_SUBCODE_P16;
+ case BURN_BLOCK_RAW96P:
+ return BURN_MODE_RAW | BURN_SUBCODE_P96;
+ case BURN_BLOCK_RAW96R:
+ return BURN_MODE_RAW | BURN_SUBCODE_R96;
+ case BURN_BLOCK_MODE1:
+ return BURN_MODE1;
+ }
+ assert(0); /* return BURN_MODE_UNIMPLEMENTED :) */
+}
+
+static void get_bytes(struct burn_track *track, int count, unsigned char *data)
+{
+ int valid, shortage, curr;
+
+/* no track pointer means we're just generating 0s */
+ if (!track) {
+ memset(data, 0, count);
+ return;
+ }
+
+/* first we use up any offset */
+ valid = track->offset - track->offsetcount;
+ if (valid > count)
+ valid = count;
+
+ if (valid) {
+ track->offsetcount += valid;
+ memset(data, 0, valid);
+ }
+ shortage = count - valid;
+
+ if (!shortage)
+ return;
+
+/* Next we use source data */
+ curr = valid;
+ if (!track->eos) {
+ valid = track->source->read(track->source, data + curr, count - curr);
+ } else valid = 0;
+
+ if (valid == -1) {
+ track->eos = 1;
+ valid = 0;
+ }
+
+ curr += valid;
+ shortage = count - curr;
+
+ if (!shortage)
+ return;
+
+/* Before going to the next track, we run through any tail */
+
+ valid = track->tail - track->tailcount;
+ if (valid > count - curr)
+ valid = count - curr;
+
+ if (valid) {
+ track->tailcount += valid;
+ memset(data + curr, 0, valid);
+ }
+ curr += valid;
+ shortage -= valid;
+
+ if (!shortage)
+ return;
+
+/* If we're still short, and there's a "next" pointer, we pull from that.
+ if that depletes, we'll just fill with 0s.
+*/
+ if (track->source->next) {
+ struct burn_source *src;
+ printf("pulling from next track\n");
+ src = track->source->next;
+ valid = src->read(src, data + curr, shortage);
+ if (valid > 0) {
+ shortage -= valid;
+ curr += valid;
+ }
+ }
+ if (!shortage)
+ return;
+ memset(data + curr, 0, shortage);
+}
+static unsigned char *get_sector(struct burn_write_opts *opts, int inmode)
+{
+ struct burn_drive *d = opts->drive;
+ struct buffer *out = d->buffer;
+ int outmode;
+ int seclen;
+ unsigned char *ret;
+
+ outmode = get_outmode(opts);
+ if (outmode == 0)
+ outmode = inmode;
+
+ seclen = burn_sector_length(outmode) + burn_subcode_length(outmode);
+
+ if (out->bytes + (seclen) >= BUFFER_SIZE) {
+ int err;
+ err = d->write(d, d->nwa, out);
+ if (err == BE_CANCELLED)
+ return NULL;
+ d->nwa += out->sectors;
+ out->bytes = 0;
+ out->sectors = 0;
+ }
+
+ ret = out->data + out->bytes;
+ out->bytes += seclen;
+ out->sectors++;
+
+ return ret;
+}
+
+/* either inmode == outmode, or outmode == raw. anything else is bad news */
+static void convert_data(struct burn_write_opts *o, struct burn_track *track,
+ int inmode, unsigned char *data)
+{
+ int outlen, inlen;
+ int offset = -1;
+ int outmode;
+
+ outmode = get_outmode(o);
+ if (outmode == 0)
+ outmode = inmode;
+
+ outlen = burn_sector_length(outmode);
+ inlen = burn_sector_length(inmode);
+ assert(outlen >= inlen);
+
+ if ((outmode & BURN_MODE_BITS) == (inmode & BURN_MODE_BITS)) {
+ get_bytes(track, inlen, data);
+ return;
+ }
+
+ assert(outmode & BURN_MODE_RAW);
+
+ if (inmode & BURN_MODE1)
+ offset = 16;
+ if (inmode & BURN_MODE_RAW)
+ offset = 0;
+ if (inmode & BURN_AUDIO)
+ offset = 0;
+ assert(offset != -1);
+ get_bytes(track, inlen, data + offset);
+}
+static void convert_subs(struct burn_write_opts *o, int inmode,
+ unsigned char *subs, unsigned char *sector)
+{
+ unsigned char *out;
+ int outmode;
+
+ outmode = get_outmode(o);
+ if (outmode == 0)
+ outmode = inmode;
+ sector += burn_sector_length(outmode);
+/* XXX for sao with subs, we'd need something else... */
+
+ switch (o->block_type) {
+ case BURN_BLOCK_RAW96R:
+ uncook_subs(sector, subs);
+ break;
+
+ case BURN_BLOCK_RAW16:
+ memcpy(sector, subs + 12, 12);
+ out = sector + 12;
+ out[0] = 0;
+ out[1] = 0;
+ out[2] = 0;
+/*XXX find a better way to deal with partially damaged P channels*/
+ if (subs[2] != 0)
+ out[3] = 0x80;
+ else
+ out[3] = 0;
+ out = sector + 10;
+
+ out[0] = ~out[0];
+ out[1] = ~out[1];
+ break;
+ }
+}
+
+static void subcode_toc(struct burn_drive *d, int mode, unsigned char *data)
+{
+ unsigned char *q;
+ int track;
+ int crc;
+ int min, sec, frame;
+
+ track = d->toc_temp / 3;
+ memset(data, 0, 96);
+ q = data + 12;
+
+ burn_lba_to_msf(d->rlba, &min, &sec, &frame);
+/*XXX track numbers are BCD
+a0 - 1st track ctrl
+a1 - last track ctrl
+a2 - lout ctrl
+*/
+ q[0] = (d->toc_entry[track].control << 4) + 1;
+ q[1] = 0;
+ if (d->toc_entry[track].point < 100)
+ q[2] = dec_to_bcd(d->toc_entry[track].point);
+ else
+ q[2] = d->toc_entry[track].point;
+ q[3] = dec_to_bcd(min);
+ q[4] = dec_to_bcd(sec);
+ q[5] = dec_to_bcd(frame);
+ q[6] = 0;
+ q[7] = dec_to_bcd(d->toc_entry[track].pmin);
+ q[8] = dec_to_bcd(d->toc_entry[track].psec);
+ q[9] = dec_to_bcd(d->toc_entry[track].pframe);
+ crc = crc_ccitt(q, 10);
+ q[10] = crc >> 8;
+ q[11] = crc & 0xFF;
+ d->toc_temp++;
+ d->toc_temp %= (d->toc_entries * 3);
+}
+
+int sector_toc(struct burn_write_opts *o, int mode)
+{
+ struct burn_drive *d = o->drive;
+ unsigned char *data;
+ unsigned char subs[96];
+
+ data = get_sector(o, mode);
+ if (!data)
+ return 0;
+ convert_data(o, NULL, mode, data);
+ subcode_toc(d, mode, subs);
+ convert_subs(o, mode, subs, data);
+ sector_headers(o, data, mode, 1);
+ sector_common(++)
+ return 1;
+}
+
+int sector_pregap(struct burn_write_opts *o,
+ unsigned char tno, unsigned char control, int mode)
+{
+ struct burn_drive *d = o->drive;
+ unsigned char *data;
+ unsigned char subs[96];
+
+ data = get_sector(o, mode);
+ if (!data)
+ return 0;
+ convert_data(o, NULL, mode, data);
+ subcode_user(o, subs, tno, control, 0, NULL, 1);
+ convert_subs(o, mode, subs, data);
+ sector_headers(o, data, mode, 0);
+ sector_common(--)
+ return 1;
+}
+
+int sector_postgap(struct burn_write_opts *o,
+ unsigned char tno, unsigned char control, int mode)
+{
+ struct burn_drive *d = o->drive;
+ unsigned char subs[96];
+ unsigned char *data;
+
+ data = get_sector(o, mode);
+ if (!data)
+ return 0;
+ convert_data(o, NULL, mode, data);
+/* use last index in track */
+ subcode_user(o, subs, tno, control, 1, NULL, 1);
+ convert_subs(o, mode, subs, data);
+ sector_headers(o, data, mode, 0);
+ sector_common(++)
+ return 1;
+}
+
+static void subcode_lout(struct burn_write_opts *o, unsigned char control,
+ unsigned char *data)
+{
+ struct burn_drive *d = o->drive;
+ unsigned char *q;
+ int crc;
+ int rmin, min, rsec, sec, rframe, frame;
+
+ memset(data, 0, 96);
+ q = data + 12;
+
+ burn_lba_to_msf(d->alba, &min, &sec, &frame);
+ burn_lba_to_msf(d->rlba, &rmin, &rsec, &rframe);
+
+ if (((rmin == 0) && (rsec == 0) && (rframe == 0)) ||
+ ((rsec >= 2) && !((rframe / 19) % 2)))
+ memset(data, 0xFF, 12);
+ q[0] = (control << 4) + 1;
+ q[1] = 0xAA;
+ q[2] = 0x01;
+ q[3] = dec_to_bcd(rmin);
+ q[4] = dec_to_bcd(rsec);
+ q[5] = dec_to_bcd(rframe);
+ q[6] = 0;
+ q[7] = dec_to_bcd(min);
+ q[8] = dec_to_bcd(sec);
+ q[9] = dec_to_bcd(frame);
+ crc = crc_ccitt(q, 10);
+ q[10] = crc >> 8;
+ q[11] = crc & 0xFF;
+}
+
+static char char_to_isrc(char c)
+{
+ if (c >= '0' && c <= '9')
+ return c - '0';
+ if (c >= 'A' && c <= 'Z')
+ return 0x11 + (c - 'A');
+ if (c >= 'a' && c <= 'z')
+ return 0x11 + (c - 'a');
+ assert(0);
+ return 0;
+}
+
+void subcode_user(struct burn_write_opts *o, unsigned char *subcodes,
+ unsigned char tno, unsigned char control,
+ unsigned char indx, struct isrc *isrc, int psub)
+{
+ struct burn_drive *d = o->drive;
+ unsigned char *p, *q;
+ int crc;
+ int m, s, f, c, qmode; /* 1, 2 or 3 */
+
+ memset(subcodes, 0, 96);
+
+ p = subcodes;
+ if ((tno == 1) && (d->rlba == -150))
+ memset(p, 0xFF, 12);
+
+ if (psub)
+ memset(p, 0xFF, 12);
+ q = subcodes + 12;
+
+ qmode = 1;
+ /* every 1 in 10 we can do something different */
+ if (d->rlba % 10 == 0) {
+ /* each of these can occur 1 in 100 */
+ if ((d->rlba / 10) % 10 == 0) {
+ if (o->has_mediacatalog)
+ qmode = 2;
+ } else if ((d->rlba / 10) % 10 == 1) {
+ if (isrc && isrc->has_isrc)
+ qmode = 3;
+ }
+ }
+
+ assert(qmode == 1 || qmode == 2 || qmode == 3);
+
+ switch (qmode) {
+ case 1:
+ q[1] = dec_to_bcd(tno); /* track number */
+ q[2] = dec_to_bcd(indx); /* index XXX read this shit
+ from the track array */
+ burn_lba_to_msf(d->rlba, &m, &s, &f);
+ q[3] = dec_to_bcd(m); /* rel min */
+ q[4] = dec_to_bcd(s); /* rel sec */
+ q[5] = dec_to_bcd(f); /* rel frame */
+ q[6] = 0; /* zero */
+ burn_lba_to_msf(d->alba, &m, &s, &f);
+ q[7] = dec_to_bcd(m); /* abs min */
+ q[8] = dec_to_bcd(s); /* abs sec */
+ q[9] = dec_to_bcd(f); /* abs frame */
+ break;
+ case 2:
+ /* media catalog number */
+ q[1] = (o->mediacatalog[0] << 4) + o->mediacatalog[1];
+ q[2] = (o->mediacatalog[2] << 4) + o->mediacatalog[3];
+ q[3] = (o->mediacatalog[4] << 4) + o->mediacatalog[5];
+ q[4] = (o->mediacatalog[6] << 4) + o->mediacatalog[7];
+ q[5] = (o->mediacatalog[8] << 4) + o->mediacatalog[9];
+ q[6] = (o->mediacatalog[10] << 4) + o->mediacatalog[11];
+ q[7] = o->mediacatalog[12] << 4;
+
+ q[8] = 0;
+ burn_lba_to_msf(d->alba, &m, &s, &f);
+ q[9] = dec_to_bcd(f); /* abs frame */
+ break;
+ case 3:
+ c = char_to_isrc(isrc->country[0]);
+ /* top 6 bits of [1] is the first country code */
+ q[1] = c << 2;
+ c = char_to_isrc(isrc->country[1]);
+ /* bottom 2 bits of [1] is part of the second country code */
+ q[1] += (c >> 4);
+ /* top 4 bits if [2] is the rest of the second country code */
+ q[2] = c << 4;
+
+ c = char_to_isrc(isrc->owner[0]);
+ /* bottom 4 bits of [2] is part of the first owner code */
+ q[2] += (c >> 2);
+ /* top 2 bits of [3] is the rest of the first owner code */
+ q[3] = c << 6;
+ c = char_to_isrc(isrc->owner[1]);
+ /* bottom 6 bits of [3] is the entire second owner code */
+ q[3] += c;
+ c = char_to_isrc(isrc->owner[2]);
+ /* top 6 bits of [4] are the third owner code */
+ q[4] = c << 2;
+
+ /* [5] is the year in 2 BCD numbers */
+ q[5] = dec_to_bcd(isrc->year % 100);
+ /* [6] is the first 2 digits in the serial */
+ q[6] = dec_to_bcd(isrc->serial % 100);
+ /* [7] is the next 2 digits in the serial */
+ q[7] = dec_to_bcd((isrc->serial / 100) % 100);
+ /* the top 4 bits of [8] is the last serial digit, the rest is
+ zeros */
+ q[8] = dec_to_bcd((isrc->serial / 10000) % 10) << 4;
+ burn_lba_to_msf(d->alba, &m, &s, &f);
+ q[9] = dec_to_bcd(f); /* abs frame */
+ break;
+ }
+ q[0] = (control << 4) + qmode;
+
+ crc = crc_ccitt(q, 10);
+ q[10] = crc >> 8;
+ q[11] = crc & 0xff;
+}
+
+int sector_lout(struct burn_write_opts *o, unsigned char control, int mode)
+{
+ struct burn_drive *d = o->drive;
+ unsigned char subs[96];
+ unsigned char *data;
+
+ data = get_sector(o, mode);
+ if (!data)
+ return 0;
+ convert_data(o, NULL, mode, data);
+ subcode_lout(o, control, subs);
+ convert_subs(o, mode, subs, data);
+ sector_headers(o, data, mode, 0);
+ sector_common(++)
+ return 1;
+}
+
+int sector_data(struct burn_write_opts *o, struct burn_track *t, int psub)
+{
+ struct burn_drive *d = o->drive;
+ unsigned char subs[96];
+ unsigned char *data;
+
+ data = get_sector(o, t->mode);
+ if (!data)
+ return 0;
+ convert_data(o, t, t->mode, data);
+
+ if (!t->source->read_sub)
+ subcode_user(o, subs, t->entry->point,
+ t->entry->control, 1, &t->isrc, psub);
+ else if (!t->source->read_sub(t->source, subs, 96))
+ subcode_user(o, subs, t->entry->point,
+ t->entry->control, 1, &t->isrc, psub);
+ convert_subs(o, t->mode, subs, data);
+
+ sector_headers(o, data, t->mode, 0);
+ sector_common(++)
+ return 1;
+}
+
+int burn_msf_to_lba(int m, int s, int f)
+{
+ if (m < 90)
+ return (m * 60 + s) * 75 + f - 150;
+ else
+ return (m * 60 + s) * 75 + f - 450150;
+}
+
+void burn_lba_to_msf(int lba, int *m, int *s, int *f)
+{
+ if (lba >= -150) {
+ *m = (lba + 150) / (60 * 75);
+ *s = (lba + 150 - *m * 60 * 75) / 75;
+ *f = lba + 150 - *m * 60 * 75 - *s * 75;
+ } else {
+ *m = (lba + 450150) / (60 * 75);
+ *s = (lba + 450150 - *m * 60 * 75) / 75;
+ *f = lba + 450150 - *m * 60 * 75 - *s * 75;
+ }
+}
+
+int dec_to_bcd(int d)
+{
+ int top, bottom;
+
+ top = d / 10;
+ bottom = d - (top * 10);
+ return (top << 4) + bottom;
+}
+
+void sector_headers(struct burn_write_opts *o, unsigned char *out,
+ int mode, int leadin)
+{
+ struct burn_drive *d = o->drive;
+ unsigned int crc;
+ int min, sec, frame;
+ int modebyte = -1;
+
+ if (mode & BURN_AUDIO) /* no headers for "audio" */
+ return;
+ if (o->write_type == BURN_WRITE_SAO)
+ return;
+ if (mode & BURN_MODE1)
+ modebyte = 1;
+
+ assert(modebyte == 1);
+
+ out[0] = 0;
+ memset(out + 1, 0xFF, 10); /* sync */
+ out[11] = 0;
+
+ if (leadin) {
+ burn_lba_to_msf(d->rlba, &min, &sec, &frame);
+ out[12] = dec_to_bcd(min) + 0xA0;
+ out[13] = dec_to_bcd(sec);
+ out[14] = dec_to_bcd(frame);
+ out[15] = modebyte;
+ } else {
+ burn_lba_to_msf(d->alba, &min, &sec, &frame);
+ out[12] = dec_to_bcd(min);
+ out[13] = dec_to_bcd(sec);
+ out[14] = dec_to_bcd(frame);
+ out[15] = modebyte;
+ }
+ if (mode & BURN_MODE1) {
+ crc = crc_32(out, 2064);
+ out[2064] = crc & 0xFF;
+ crc >>= 8;
+ out[2065] = crc & 0xFF;
+ crc >>= 8;
+ out[2066] = crc & 0xFF;
+ crc >>= 8;
+ out[2067] = crc & 0xFF;
+ }
+ if (mode & BURN_MODE1) {
+ memset(out + 2068, 0, 8);
+ parity_p(out);
+ parity_q(out);
+ }
+ scramble(out);
+}
+
+#if 0
+void process_q(struct burn_drive *d, unsigned char *q)
+{
+ unsigned char i[5];
+ int mode;
+
+ mode = q[0] & 0xF;
+/* burn_print(12, "mode: %d : ", mode);*/
+ switch (mode) {
+ case 1:
+/* burn_print(12, "tno = %d : ", q[1]);
+ burn_print(12, "index = %d\n", q[2]);
+*/
+ /* q[1] is the track number (starting at 1) q[2] is the index
+ number (starting at 0) */
+#warning this is totally bogus
+ if (q[1] - 1 > 99)
+ break;
+ if (q[2] > d->toc->track[q[1] - 1].indices) {
+ burn_print(12, "new index at %d\n", d->alba);
+ d->toc->track[q[1] - 1].index[q[2]] = d->alba;
+ d->toc->track[q[1] - 1].indices++;
+ }
+ break;
+ case 2:
+ /* XXX dont ignore these */
+ break;
+ case 3:
+/* burn_print(12, "ISRC data in mode 3 q\n");*/
+ i[0] = isrc[(q[1] << 2) >> 2];
+/* burn_print(12, "0x%x 0x%x 0x%x 0x%x 0x%x\n", q[1], q[2], q[3], q[4], q[5]);
+ burn_print(12, "ISRC - %c%c%c%c%c\n", i[0], i[1], i[2], i[3], i[4]);
+*/
+ break;
+ default:
+ assert(0);
+ }
+}
+#endif
+
+/* this needs more info. subs in the data? control/adr? */
+#warning sector_identify needs to be written
+int sector_identify(unsigned char *data)
+{
+ scramble(data);
+/*
+check mode byte for 1 or 2
+test parity to see if it's a valid sector
+if invalid, return BURN_MODE_AUDIO;
+else return mode byte (what about mode 2 formless? heh)
+*/
+ return BURN_MODE1;
+}
diff --git a/tags/ZeroTwoTwo/libburn/sector.h b/tags/ZeroTwoTwo/libburn/sector.h
new file mode 100644
index 00000000..91b68d3b
--- /dev/null
+++ b/tags/ZeroTwoTwo/libburn/sector.h
@@ -0,0 +1,31 @@
+/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */
+
+#ifndef __SECTOR
+#define __SECTOR
+
+#include "libburn.h"
+#include "transport.h"
+
+struct burn_drive;
+struct isrc;
+
+int dec_to_bcd(int);
+
+int sector_toc(struct burn_write_opts *, int mode);
+int sector_pregap(struct burn_write_opts *, unsigned char tno,
+ unsigned char control, int mode);
+int sector_postgap(struct burn_write_opts *, unsigned char tno,
+ unsigned char control, int mode);
+int sector_lout(struct burn_write_opts *, unsigned char control, int mode);
+int sector_data(struct burn_write_opts *, struct burn_track *t, int psub);
+void sector_headers(struct burn_write_opts *, unsigned char *,
+ int mode, int leadin);
+void subcode_user(struct burn_write_opts *, unsigned char *s,
+ unsigned char tno, unsigned char control,
+ unsigned char index, struct isrc *isrc, int psub);
+
+int sector_identify(unsigned char *);
+
+void process_q(struct burn_drive *d, unsigned char *q);
+
+#endif /* __SECTOR */
diff --git a/tags/ZeroTwoTwo/libburn/sg.c b/tags/ZeroTwoTwo/libburn/sg.c
new file mode 100644
index 00000000..7cfd6e84
--- /dev/null
+++ b/tags/ZeroTwoTwo/libburn/sg.c
@@ -0,0 +1,500 @@
+/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "transport.h"
+#include "drive.h"
+#include "sg.h"
+#include "spc.h"
+#include "mmc.h"
+#include "sbc.h"
+#include "debug.h"
+#include "toc.h"
+#include "util.h"
+
+static void enumerate_common(char *fname);
+
+/* ts A51221 */
+int burn_drive_is_banned(char *device_address);
+
+/* ts A60813 : storage objects are in libburn/init.c
+ wether to use O_EXCL
+ wether to use O_NOBLOCK with open(2) on devices
+ wether to take O_EXCL rejection as fatal error */
+extern int burn_sg_open_o_excl;
+extern int burn_sg_open_o_nonblock;
+extern int burn_sg_open_abort_busy;
+
+
+/* ts A60821
+ <<< debug: for tracing calls which might use open drive fds */
+int mmc_function_spy(char * text);
+
+
+static int sgio_test(int fd)
+{
+ unsigned char test_ops[] = { 0, 0, 0, 0, 0, 0 };
+ sg_io_hdr_t s;
+
+ memset(&s, 0, sizeof(sg_io_hdr_t));
+ s.interface_id = 'S';
+ s.dxfer_direction = SG_DXFER_NONE;
+ s.cmd_len = 6;
+ s.cmdp = test_ops;
+ s.timeout = 12345;
+ return ioctl(fd, SG_IO, &s);
+}
+
+void ata_enumerate(void)
+{
+ struct hd_driveid tm;
+ int i, fd;
+ char fname[10];
+
+ /* ts A60813 */
+ int open_mode = O_RDWR;
+
+ /* ts A60813
+ O_EXCL with block devices is an unpublished feature
+ of Linux kernels. Possibly introduced 2002.
+ It can only be used if libburn stops opening several
+ file descriptor on the same block device.
+ See comment in sg_grab() */
+ if(burn_sg_open_o_excl)
+ open_mode |= O_EXCL;
+ /* ts A60813
+ O_NONBLOCK was already hardcoded in ata_ but not in sg_.
+ There must be some reason for this. So O_NONBLOCK is
+ default mode for both now. Disable on own risk. */
+ if(burn_sg_open_o_nonblock)
+ open_mode |= O_NONBLOCK;
+
+ for (i = 0; i < 26; i++) {
+ sprintf(fname, "/dev/hd%c", 'a' + i);
+ /* open O_RDWR so we don't think read only drives are
+ in some way useful
+ */
+ /* ts A51221 */
+ if (burn_drive_is_banned(fname))
+ continue;
+ fd = open(fname, open_mode);
+ if (fd == -1) {
+/* <<< debugging
+ fprintf(stderr,
+ "\nlibburn: experimental: fname= %s , errno= %d\n",
+ fname,errno);
+*/
+ /* ts A60814 : i see no way to do this more nicely */
+ if (errno == EBUSY && burn_sg_open_abort_busy) {
+ fprintf(stderr,
+ "\nlibburn: FATAL : Application triggered abort on busy drive '%s'\n",
+ fname);
+ /* <<< maybe one should plainly exit here */
+ assert("drive busy" == "non fatal");
+ }
+ continue;
+ }
+ /* found a drive */
+ ioctl(fd, HDIO_GET_IDENTITY, &tm);
+
+ /* not atapi */
+ if (!(tm.config & 0x8000) || (tm.config & 0x4000)) {
+ close(fd);
+ continue;
+ }
+
+ /* if SG_IO fails on an atapi device, we should stop trying to
+ use hd* devices */
+ if (sgio_test(fd) == -1) {
+ close(fd);
+ return;
+ }
+ close(fd);
+ enumerate_common(fname);
+ }
+}
+
+void sg_enumerate(void)
+{
+ struct sg_scsi_id sid;
+ int i, fd;
+ char fname[10];
+
+ /* ts A60813 */
+ int open_mode = O_RDWR;
+
+ /* ts A60813
+ O_EXCL with block devices is an unpublished feature
+ of Linux kernels. Possibly introduced 2002.
+ It can only be used if libburn stops opening several
+ file descriptor on the same block device.
+ See comment in sg_grab() */
+ if(burn_sg_open_o_excl)
+ open_mode |= O_EXCL;
+ /* ts A60813
+ O_NONBLOCK was not hardcoded in sg_ but was in ata_.
+ I myself test mainly sg_ and it seems to be ok with
+ O_NONBLOCK. So it should stay default mode. */
+ if(burn_sg_open_o_nonblock)
+ open_mode |= O_NONBLOCK;
+
+/* <<< debugging
+ fprintf(stderr,
+ "\nlibburn: experimental: o_excl= %d , o_nonblock= %d, abort_on_busy= %d\n",
+ burn_sg_open_o_excl,burn_sg_open_o_nonblock,burn_sg_open_abort_busy);
+ fprintf(stderr,
+ "libburn: experimental: O_EXCL= %d , O_NONBLOCK= %d\n",
+ !!(open_mode&O_EXCL),!!(open_mode&O_NONBLOCK));
+
+*/
+
+ for (i = 0; i < 32; i++) {
+ sprintf(fname, "/dev/sg%d", i);
+ /* open RDWR so we don't accidentally think read only drives
+ are in some way useful
+ */
+ /* ts A51221 */
+ if (burn_drive_is_banned(fname))
+ continue;
+ fd = open(fname, open_mode);
+
+ if (fd == -1) {
+/* <<< debugging
+ fprintf(stderr,
+ "\n cdrskin: experimental: fname= %s , errno= %d\n",
+ fname,errno);
+*/
+ /* ts A60814 : i see no way to do this more nicely */
+ if (errno == EBUSY && burn_sg_open_abort_busy) {
+ fprintf(stderr,
+ "\nlibburn: FATAL : Application triggered abort on busy drive '%s'\n",
+ fname);
+ /* <<< maybe one should plainly exit here */
+ assert("drive busy" == "non fatal");
+ }
+ continue;
+ }
+ /* found a drive */
+ ioctl(fd, SG_GET_SCSI_ID, &sid);
+ close(fd);
+ if (sid.scsi_type != TYPE_ROM)
+ continue;
+
+ enumerate_common(fname);
+ }
+}
+
+static void enumerate_common(char *fname)
+{
+ struct burn_drive *t;
+ struct burn_drive out;
+
+ out.devname = burn_strdup(fname);
+ out.fd = -1337;
+
+ out.grab = sg_grab;
+ out.release = sg_release;
+ out.issue_command = sg_issue_command;
+ out.getcaps = spc_getcaps;
+ out.released = 1;
+ out.status = BURN_DISC_UNREADY;
+
+ out.eject = sbc_eject;
+ out.load = sbc_load;
+ out.lock = spc_prevent;
+ out.unlock = spc_allow;
+ out.read_disc_info = spc_sense_write_params;
+ out.get_erase_progress = spc_get_erase_progress;
+ out.test_unit_ready = spc_test_unit_ready;
+ out.probe_write_modes = spc_probe_write_modes;
+ out.read_toc = mmc_read_toc;
+ out.write = mmc_write;
+ out.erase = mmc_erase;
+ out.read_sectors = mmc_read_sectors;
+ out.perform_opc = mmc_perform_opc;
+ out.set_speed = mmc_set_speed;
+ out.send_parameters = spc_select_error_params;
+ out.send_write_parameters = spc_select_write_params;
+ out.send_cue_sheet = mmc_send_cue_sheet;
+ out.sync_cache = mmc_sync_cache;
+ out.get_nwa = mmc_get_nwa;
+ out.close_disc = mmc_close_disc;
+ out.close_session = mmc_close_session;
+ out.idata = malloc(sizeof(struct scsi_inquiry_data));
+ out.idata->valid = 0;
+ out.mdata = malloc(sizeof(struct scsi_mode_data));
+ out.mdata->valid = 0;
+ memset(&out.params, 0, sizeof(struct params));
+ t = burn_drive_register(&out);
+
+/* ts A60821
+ <<< debug: for tracing calls which might use open drive fds */
+ mmc_function_spy("enumerate_common : -------- doing grab");
+
+/* try to get the drive info */
+ if (sg_grab(t)) {
+ burn_print(2, "getting drive info\n");
+ t->getcaps(t);
+ t->unlock(t);
+ t->released = 1;
+ } else {
+ burn_print(2, "unable to grab new located drive\n");
+ }
+
+/* ts A60821
+ <<< debug: for tracing calls which might use open drive fds */
+ mmc_function_spy("enumerate_common : ----- would release ");
+
+}
+
+/*
+ we use the sg reference count to decide whether we can use the
+ drive or not.
+ if refcount is not one, drive is open somewhere else.
+
+ ts A60813: this test is too late. O_EXCL is the stronger solution.
+ After all the test was diabled already in icculus.org/burn CVS.
+*/
+int sg_grab(struct burn_drive *d)
+{
+ int fd, count;
+
+ /* ts A60813 */
+ int open_mode = O_RDWR;
+
+/* ts A60821
+ <<< debug: for tracing calls which might use open drive fds */
+ mmc_function_spy("sg_grab");
+
+
+ /* ts A60813
+ O_EXCL with block devices is an unpublished feature
+ of Linux kernels. Possibly introduced 2002.
+ It can only be used if libburn stops opening several
+ file descriptor on the same block device.
+ See comment below */
+ if(burn_sg_open_o_excl)
+ open_mode |= O_EXCL;
+
+ /* ts A60813
+ O_NONBLOCK was hardcoded here. So it should stay default mode. */
+ if(burn_sg_open_o_nonblock)
+ open_mode |= O_NONBLOCK;
+
+ /* ts A60813
+ After enumeration the drive fd is probably still open.
+ -1337 is the initial value of burn_drive.fd and the value after
+ relase of drive. Unclear why not the official error return
+ value -1 of open(2) war used. */
+ /* ts A60822: was if(d->fd == -1337) { */
+ if(! burn_drive_is_open(d)) {
+
+ /* ts A60821
+ <<< debug: for tracing calls which might use open drive fds */
+ mmc_function_spy("sg_grab ----------- opening");
+
+ fd = open(d->devname, open_mode);
+ } else
+ fd= d->fd;
+
+ assert(fd != -1337);
+ if (-1 != fd) {
+
+ /* ts A60814:
+ according to my experiments this test would work now ! */
+
+/* er = ioctl(fd, SG_GET_ACCESS_COUNT, &count);*/
+ count = 1;
+ if (1 == count) {
+ d->fd = fd;
+ fcntl(fd, F_SETOWN, getpid());
+ d->released = 0;
+ return 1;
+ }
+ burn_print(1, "could not acquire drive - already open\n");
+ close(fd);
+ return 0;
+ }
+ burn_print(1, "could not acquire drive\n");
+ return 0;
+}
+
+/*
+ non zero return means you still have the drive and it's not
+ in a state to be released? (is that even possible?)
+*/
+
+int sg_release(struct burn_drive *d)
+{
+ /* ts A60821
+ <<< debug: for tracing calls which might use open drive fds */
+ mmc_function_spy("sg_release");
+
+ if (d->fd < 1) {
+ burn_print(1, "release an ungrabbed drive. die\n");
+ return 0;
+ }
+
+ /* ts A60821
+ <<< debug: for tracing calls which might use open drive fds */
+ mmc_function_spy("sg_release ----------- closing");
+
+ close(d->fd);
+ d->fd = -1337;
+ return 0;
+}
+
+int sg_issue_command(struct burn_drive *d, struct command *c)
+{
+ int done = 0;
+ int err;
+ sg_io_hdr_t s;
+
+
+/* ts A60821
+ <<< debug: for tracing calls which might use open drive fds */
+ char buf[161];
+ sprintf(buf,"sg_issue_command d->fd= %d d->released= %d\n",
+ d->fd,d->released);
+ mmc_function_spy(buf);
+
+
+ c->error = 0;
+/*
+this is valid during the mode probe in scan
+ if (d->fd < 1 || d->released) {
+ burn_print(1,
+ "command issued on ungrabbed drive, chaos.\n");
+ burn_print(1, "fd = %d, released = %d\n", d->fd,
+ d->released);
+ }
+*/
+ memset(&s, 0, sizeof(sg_io_hdr_t));
+
+ s.interface_id = 'S';
+
+ if (c->dir == TO_DRIVE)
+ s.dxfer_direction = SG_DXFER_TO_DEV;
+ else if (c->dir == FROM_DRIVE)
+ s.dxfer_direction = SG_DXFER_FROM_DEV;
+ else if (c->dir == NO_TRANSFER) {
+ s.dxfer_direction = SG_DXFER_NONE;
+ assert(!c->page);
+ }
+ s.cmd_len = c->oplen;
+ s.cmdp = c->opcode;
+ s.mx_sb_len = 32;
+ s.sbp = c->sense;
+ memset(c->sense, 0, sizeof(c->sense));
+ s.timeout = 200000;
+ if (c->page) {
+ s.dxferp = c->page->data;
+ if (c->dir == FROM_DRIVE) {
+ s.dxfer_len = BUFFER_SIZE;
+/* touch page so we can use valgrind */
+ memset(c->page->data, 0, BUFFER_SIZE);
+ } else {
+ assert(c->page->bytes > 0);
+ s.dxfer_len = c->page->bytes;
+ }
+ } else {
+ s.dxferp = NULL;
+ s.dxfer_len = 0;
+ }
+ s.usr_ptr = c;
+
+ do {
+ err = ioctl(d->fd, SG_IO, &s);
+ assert(err != -1);
+ if (s.sb_len_wr) {
+ if (!c->retry) {
+ c->error = 1;
+ return 1;
+ }
+ switch (scsi_error(d, s.sbp, s.sb_len_wr)) {
+ case RETRY:
+ done = 0;
+ break;
+ case FAIL:
+ done = 1;
+ c->error = 1;
+ break;
+ }
+ } else {
+ done = 1;
+ }
+ } while (!done);
+ return 1;
+}
+
+enum response scsi_error(struct burn_drive *d, unsigned char *sense,
+ int senselen)
+{
+ int key, asc, ascq;
+
+ senselen = senselen;
+ key = sense[2];
+ asc = sense[12];
+ ascq = sense[13];
+
+ burn_print(12, "CONDITION: 0x%x 0x%x 0x%x on %s %s\n",
+ key, asc, ascq, d->idata->vendor, d->idata->product);
+
+ switch (asc) {
+ case 0:
+ burn_print(12, "NO ERROR!\n");
+ return RETRY;
+
+ case 2:
+ burn_print(1, "not ready\n");
+ return RETRY;
+ case 4:
+ burn_print(1,
+ "logical unit is in the process of becoming ready\n");
+ return RETRY;
+ case 0x20:
+ if (key == 5)
+ burn_print(1, "bad opcode\n");
+ return FAIL;
+ case 0x21:
+ burn_print(1, "invalid address or something\n");
+ return FAIL;
+ case 0x24:
+ if (key == 5)
+ burn_print(1, "invalid field in cdb\n");
+ else
+ break;
+ return FAIL;
+ case 0x26:
+ if ( key == 5 )
+ burn_print( 1, "invalid field in parameter list\n" );
+ return FAIL;
+ case 0x28:
+ if (key == 6)
+ burn_print(1,
+ "Not ready to ready change, medium may have changed\n");
+ else
+ break;
+ return RETRY;
+ case 0x3A:
+ burn_print(12, "Medium not present in %s %s\n",
+ d->idata->vendor, d->idata->product);
+
+ d->status = BURN_DISC_EMPTY;
+ return FAIL;
+ }
+ burn_print(1, "unknown failure\n");
+ burn_print(1, "key:0x%x, asc:0x%x, ascq:0x%x\n", key, asc, ascq);
+ return FAIL;
+}
diff --git a/tags/ZeroTwoTwo/libburn/sg.h b/tags/ZeroTwoTwo/libburn/sg.h
new file mode 100644
index 00000000..8f431c5f
--- /dev/null
+++ b/tags/ZeroTwoTwo/libburn/sg.h
@@ -0,0 +1,19 @@
+/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */
+
+#ifndef __SG
+#define __SG
+
+struct burn_drive;
+struct command;
+
+enum response
+{ RETRY, FAIL };
+
+void sg_enumerate(void);
+void ata_enumerate(void);
+int sg_grab(struct burn_drive *);
+int sg_release(struct burn_drive *);
+int sg_issue_command(struct burn_drive *, struct command *);
+enum response scsi_error(struct burn_drive *, unsigned char *, int);
+
+#endif /* __SG */
diff --git a/tags/ZeroTwoTwo/libburn/source.c b/tags/ZeroTwoTwo/libburn/source.c
new file mode 100644
index 00000000..fd0c56b6
--- /dev/null
+++ b/tags/ZeroTwoTwo/libburn/source.c
@@ -0,0 +1,36 @@
+/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */
+
+#include
+#include
+#include "libburn.h"
+#include "source.h"
+#include "structure.h"
+
+void burn_source_free(struct burn_source *src)
+{
+ if (--src->refcount < 1) {
+ if (src->free_data)
+ src->free_data(src);
+ free(src);
+ }
+}
+
+enum burn_source_status burn_track_set_source(struct burn_track *t,
+ struct burn_source *s)
+{
+ if (!s->read)
+ return BURN_SOURCE_FAILED;
+ s->refcount++;
+ t->source = s;
+ return BURN_SOURCE_OK;
+}
+
+struct burn_source *burn_source_new(void)
+{
+ struct burn_source *out;
+
+ out = malloc(sizeof(struct burn_source));
+ memset(out, 0, sizeof(struct burn_source));
+ out->refcount = 1;
+ return out;
+}
diff --git a/tags/ZeroTwoTwo/libburn/source.h b/tags/ZeroTwoTwo/libburn/source.h
new file mode 100644
index 00000000..e0a69aa1
--- /dev/null
+++ b/tags/ZeroTwoTwo/libburn/source.h
@@ -0,0 +1,8 @@
+/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */
+
+#ifndef __SOURCE
+#define __SOURCE
+
+struct burn_source *burn_source_new(void);
+
+#endif /*__SOURCE*/
diff --git a/tags/ZeroTwoTwo/libburn/spc.c b/tags/ZeroTwoTwo/libburn/spc.c
new file mode 100644
index 00000000..89fd5de6
--- /dev/null
+++ b/tags/ZeroTwoTwo/libburn/spc.c
@@ -0,0 +1,405 @@
+/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */
+
+/* scsi primary commands */
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "transport.h"
+#include "spc.h"
+#include "mmc.h"
+#include "sbc.h"
+#include "drive.h"
+#include "debug.h"
+#include "options.h"
+
+/* spc command set */
+static unsigned char SPC_INQUIRY[] = { 0x12, 0, 0, 0, 255, 0 };
+
+/*static char SPC_TEST[]={0,0,0,0,0,0};*/
+static unsigned char SPC_PREVENT[] = { 0x1e, 0, 0, 0, 1, 0 };
+static unsigned char SPC_ALLOW[] = { 0x1e, 0, 0, 0, 0, 0 };
+static unsigned char SPC_MODE_SENSE[] = { 0x5a, 0, 0, 0, 0, 0, 0, 16, 0, 0 };
+static unsigned char SPC_MODE_SELECT[] =
+ { 0x55, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+static unsigned char SPC_REQUEST_SENSE[] = { 0x03, 0, 0, 0, 18, 0 };
+static unsigned char SPC_TEST_UNIT_READY[] = { 0x00, 0, 0, 0, 0, 0 };
+
+int spc_test_unit_ready(struct burn_drive *d)
+{
+ struct command c;
+
+ c.retry = 0;
+ c.oplen = sizeof(SPC_TEST_UNIT_READY);
+ memcpy(c.opcode, SPC_TEST_UNIT_READY, sizeof(SPC_TEST_UNIT_READY));
+ c.page = NULL;
+ c.dir = NO_TRANSFER;
+ d->issue_command(d, &c);
+ if (c.error)
+ return (c.sense[2] & 0xF) == 0;
+ return 1;
+}
+
+void spc_request_sense(struct burn_drive *d, struct buffer *buf)
+{
+ struct command c;
+
+ c.retry = 0;
+ c.oplen = sizeof(SPC_REQUEST_SENSE);
+ memcpy(c.opcode, SPC_REQUEST_SENSE, sizeof(SPC_REQUEST_SENSE));
+ c.page = buf;
+ c.page->sectors = 0;
+ c.page->bytes = 0;
+ c.dir = FROM_DRIVE;
+ d->issue_command(d, &c);
+}
+
+int spc_get_erase_progress(struct burn_drive *d)
+{
+ struct buffer b;
+
+ spc_request_sense(d, &b);
+ return (b.data[16] << 8) | b.data[17];
+}
+
+void spc_inquiry(struct burn_drive *d)
+{
+ struct buffer buf;
+ struct scsi_inquiry_data *id;
+ struct command c;
+
+ memcpy(c.opcode, SPC_INQUIRY, sizeof(SPC_INQUIRY));
+ c.retry = 1;
+ c.oplen = sizeof(SPC_INQUIRY);
+ c.page = &buf;
+ c.page->bytes = 0;
+ c.page->sectors = 0;
+ c.dir = FROM_DRIVE;
+ d->issue_command(d, &c);
+
+ id = (struct scsi_inquiry_data *)d->idata;
+ id->vendor[8] = 0;
+ id->product[16] = 0;
+ id->revision[4] = 0;
+
+ memcpy(id->vendor, c.page->data + 8, 8);
+ memcpy(id->product, c.page->data + 16, 16);
+ memcpy(id->revision, c.page->data + 32, 4);
+
+ id->valid = 1;
+ return;
+}
+
+void spc_prevent(struct burn_drive *d)
+{
+ struct command c;
+
+ memcpy(c.opcode, SPC_PREVENT, sizeof(SPC_PREVENT));
+ c.retry = 1;
+ c.oplen = sizeof(SPC_PREVENT);
+ c.page = NULL;
+ c.dir = NO_TRANSFER;
+ d->issue_command(d, &c);
+}
+
+void spc_allow(struct burn_drive *d)
+{
+ struct command c;
+
+ memcpy(c.opcode, SPC_ALLOW, sizeof(SPC_ALLOW));
+ c.retry = 1;
+ c.oplen = sizeof(SPC_ALLOW);
+ c.page = NULL;
+ c.dir = NO_TRANSFER;
+ d->issue_command(d, &c);
+}
+
+void spc_sense_caps(struct burn_drive *d)
+{
+ struct buffer buf;
+ struct scsi_mode_data *m;
+ int size;
+ unsigned char *page;
+ struct command c;
+
+ memcpy(c.opcode, SPC_MODE_SENSE, sizeof(SPC_MODE_SENSE));
+ c.retry = 1;
+ c.oplen = sizeof(SPC_MODE_SENSE);
+ c.opcode[2] = 0x2A;
+ c.page = &buf;
+ c.page->bytes = 0;
+ c.page->sectors = 0;
+ c.dir = FROM_DRIVE;
+ d->issue_command(d, &c);
+
+ size = c.page->data[0] * 256 + c.page->data[1];
+ m = d->mdata;
+ page = c.page->data + 8;
+
+ m->buffer_size = page[12] * 256 + page[13];
+ m->dvdram_read = page[2] & 32;
+ m->dvdram_write = page[3] & 32;
+ m->dvdr_read = page[2] & 16;
+ m->dvdr_write = page[3] & 16;
+ m->dvdrom_read = page[2] & 8;
+ m->simulate = page[3] & 4;
+ m->cdrw_read = page[2] & 2;
+ m->cdrw_write = page[3] & 2;
+ m->cdr_read = page[2] & 1;
+ m->cdr_write = page[3] & 1;
+ m->max_read_speed = page[8] * 256 + page[9];
+ m->cur_read_speed = page[14] * 256 + page[15];
+ m->max_write_speed = page[18] * 256 + page[19];
+ m->cur_write_speed = page[20] * 256 + page[21];
+ m->c2_pointers = page[5] & 16;
+ m->valid = 1;
+ m->underrun_proof = page[4] & 128;
+}
+
+void spc_sense_error_params(struct burn_drive *d)
+{
+ struct buffer buf;
+ struct scsi_mode_data *m;
+ int size;
+ unsigned char *page;
+ struct command c;
+
+ memcpy(c.opcode, SPC_MODE_SENSE, sizeof(SPC_MODE_SENSE));
+ c.retry = 1;
+ c.oplen = sizeof(SPC_MODE_SENSE);
+ c.opcode[2] = 0x01;
+ c.page = &buf;
+ c.page->bytes = 0;
+ c.page->sectors = 0;
+ c.dir = FROM_DRIVE;
+ d->issue_command(d, &c);
+
+ size = c.page->data[0] * 256 + c.page->data[1];
+ m = d->mdata;
+ page = c.page->data + 8;
+ d->params.retries = page[3];
+ m->retry_page_length = page[1];
+ m->retry_page_valid = 1;
+}
+
+void spc_select_error_params(struct burn_drive *d,
+ const struct burn_read_opts *o)
+{
+ struct buffer buf;
+ struct command c;
+
+ memcpy(c.opcode, SPC_MODE_SELECT, sizeof(SPC_MODE_SELECT));
+ c.retry = 1;
+ c.oplen = sizeof(SPC_MODE_SELECT);
+ c.opcode[8] = 8 + 2 + d->mdata->retry_page_length;
+ c.page = &buf;
+ c.page->bytes = 0;
+ c.page->sectors = 0;
+ assert(d->mdata->valid);
+ memset(c.page->data, 0, 8 + 2 + d->mdata->retry_page_length);
+ c.page->bytes = 8 + 2 + d->mdata->retry_page_length;
+ c.page->data[8] = 1;
+ c.page->data[9] = d->mdata->retry_page_length;
+ if (o->transfer_damaged_blocks)
+ c.page->data[10] |= 32;
+ if (o->report_recovered_errors)
+ c.page->data[10] |= 4;
+ if (!o->hardware_error_recovery)
+ c.page->data[10] |= 1;
+/*burn_print(1, "error parameter 0x%x\n", c->page->data[10]);*/
+ c.page->data[11] = d->params.retries;
+ c.dir = TO_DRIVE;
+ d->issue_command(d, &c);
+}
+
+void spc_sense_write_params(struct burn_drive *d)
+{
+ struct buffer buf;
+ struct scsi_mode_data *m;
+ int size;
+ unsigned char *page;
+ struct command c;
+
+ assert(d->mdata->cdr_write || d->mdata->cdrw_write ||
+ d->mdata->dvdr_write || d->mdata->dvdram_write);
+
+ memcpy(c.opcode, SPC_MODE_SENSE, sizeof(SPC_MODE_SENSE));
+ c.retry = 1;
+ c.oplen = sizeof(SPC_MODE_SENSE);
+ c.opcode[2] = 0x05;
+ c.page = &buf;
+ c.page->bytes = 0;
+ c.page->sectors = 0;
+ c.dir = FROM_DRIVE;
+ d->issue_command(d, &c);
+
+ size = c.page->data[0] * 256 + c.page->data[1];
+ m = d->mdata;
+ page = c.page->data + 8;
+ burn_print(1, "write page length 0x%x\n", page[1]);
+ m->write_page_length = page[1];
+ m->write_page_valid = 1;
+ mmc_read_disc_info(d);
+}
+
+void spc_select_write_params(struct burn_drive *d,
+ const struct burn_write_opts *o)
+{
+ struct buffer buf;
+ struct command c;
+ int bufe, sim;
+
+ assert(o->drive == d);
+ memcpy(c.opcode, SPC_MODE_SELECT, sizeof(SPC_MODE_SELECT));
+ c.retry = 1;
+ c.oplen = sizeof(SPC_MODE_SELECT);
+ c.opcode[8] = 8 + 2 + d->mdata->write_page_length;
+ c.page = &buf;
+ c.page->bytes = 0;
+ c.page->sectors = 0;
+ assert(d->mdata->valid);
+ memset(c.page->data, 0, 8 + 2 + d->mdata->write_page_length);
+ c.page->bytes = 8 + 2 + d->mdata->write_page_length;
+ c.page->data[8] = 5;
+ c.page->data[9] = d->mdata->write_page_length;
+
+ burn_print(12, "using write page length %d (valid %d)\n",
+ d->mdata->write_page_length, d->mdata->write_page_valid);
+ bufe = o->underrun_proof;
+ sim = o->simulate;
+ c.page->data[10] = (bufe << 6)
+ + (sim << 4)
+ + o->write_type;
+ c.page->data[11] = (o->multi << 6) | o->control;
+ c.page->data[12] = spc_block_type(o->block_type);
+ c.page->data[22] = 0;
+ c.page->data[23] = 150; /* audio pause length */
+/*XXX need session format! */
+ c.dir = TO_DRIVE;
+ d->issue_command(d, &c);
+}
+
+void spc_getcaps(struct burn_drive *d)
+{
+ spc_inquiry(d);
+ spc_sense_caps(d);
+ spc_sense_error_params(d);
+}
+
+/*
+only called when a blank is present, so we set type to blank
+(on the last pass)
+
+don't check totally stupid modes (raw/raw0)
+some drives say they're ok, and they're not.
+*/
+
+void spc_probe_write_modes(struct burn_drive *d)
+{
+ struct buffer buf;
+ int try_write_type = 1;
+ int try_block_type = 0;
+ int key, asc, ascq;
+ struct command c;
+
+ while (try_write_type != 4) {
+ burn_print(9, "trying %d, %d\n", try_write_type,
+ try_block_type);
+ memcpy(c.opcode, SPC_MODE_SELECT, sizeof(SPC_MODE_SELECT));
+ c.retry = 1;
+ c.oplen = sizeof(SPC_MODE_SELECT);
+ c.opcode[8] = 8 + 2 + 0x32;
+ c.page = &buf;
+
+ memset(c.page->data, 0, 8 + 2 + 0x32);
+ c.page->bytes = 8 + 2 + 0x32;
+
+ c.page->data[8] = 5;
+ c.page->data[9] = 0x32;
+ c.page->data[10] = try_write_type;
+ if (try_block_type > 4)
+ c.page->data[11] = 4;
+ else
+ c.page->data[11] = 0;
+ c.page->data[12] = try_block_type;
+ c.page->data[23] = 150;
+ c.dir = TO_DRIVE;
+ d->issue_command(d, &c);
+
+ key = c.sense[2];
+ asc = c.sense[12];
+ ascq = c.sense[13];
+
+ if (key)
+ burn_print(7, "%d not supported\n", try_block_type);
+ else {
+ burn_print(7, "%d:%d SUPPORTED MODE!\n",
+ try_write_type, try_block_type);
+ if (try_write_type == 2) /* sao */
+ d->block_types[try_write_type] =
+ BURN_BLOCK_SAO;
+ else
+ d->block_types[try_write_type] |=
+ 1 << try_block_type;
+ }
+ switch (try_block_type) {
+ case 0:
+ case 1:
+ case 2:
+ try_block_type++;
+ break;
+ case 3:
+ try_block_type = 8;
+ break;
+ case 8:
+ case 9:
+ case 10:
+ case 11:
+ case 12:
+ try_block_type++;
+ break;
+ case 13:
+ try_block_type = 0;
+ try_write_type++;
+ break;
+ default:
+ return;
+ }
+ }
+}
+
+int spc_block_type(enum burn_block_types b)
+{
+ switch (b) {
+ case BURN_BLOCK_SAO:
+ return 0; /* ignored bitz */
+ case BURN_BLOCK_RAW0:
+ return 0;
+ case BURN_BLOCK_RAW16:
+ return 1;
+ case BURN_BLOCK_RAW96P:
+ return 2;
+ case BURN_BLOCK_RAW96R:
+ return 3;
+ case BURN_BLOCK_MODE1:
+ return 8;
+ case BURN_BLOCK_MODE2R:
+ return 9;
+ case BURN_BLOCK_MODE2_PATHETIC:
+ return 10;
+ case BURN_BLOCK_MODE2_LAME:
+ return 11;
+ case BURN_BLOCK_MODE2_OBSCURE:
+ return 12;
+ case BURN_BLOCK_MODE2_OK:
+ return 13;
+ }
+ assert(0);
+}
diff --git a/tags/ZeroTwoTwo/libburn/spc.h b/tags/ZeroTwoTwo/libburn/spc.h
new file mode 100644
index 00000000..2bca4ded
--- /dev/null
+++ b/tags/ZeroTwoTwo/libburn/spc.h
@@ -0,0 +1,25 @@
+/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */
+
+#ifndef __SPC
+#define __SPC
+
+#include "libburn.h"
+
+void spc_inquiry(struct burn_drive *);
+void spc_prevent(struct burn_drive *);
+void spc_allow(struct burn_drive *);
+void spc_sense_caps(struct burn_drive *);
+void spc_sense_error_params(struct burn_drive *);
+void spc_select_error_params(struct burn_drive *,
+ const struct burn_read_opts *);
+void spc_getcaps(struct burn_drive *d);
+void spc_sense_write_params(struct burn_drive *);
+void spc_select_write_params(struct burn_drive *,
+ const struct burn_write_opts *);
+void spc_probe_write_modes(struct burn_drive *);
+void spc_request_sense(struct burn_drive *d, struct buffer *buf);
+int spc_block_type(enum burn_block_types b);
+int spc_get_erase_progress(struct burn_drive *d);
+int spc_test_unit_ready(struct burn_drive *d);
+
+#endif /*__SPC*/
diff --git a/tags/ZeroTwoTwo/libburn/structure.c b/tags/ZeroTwoTwo/libburn/structure.c
new file mode 100644
index 00000000..6719ab2e
--- /dev/null
+++ b/tags/ZeroTwoTwo/libburn/structure.c
@@ -0,0 +1,305 @@
+#include
+#include
+#include
+#include
+#include "libburn.h"
+#include "structure.h"
+#include "write.h"
+#include "debug.h"
+
+#define RESIZE(TO, NEW, pos) {\
+ void *tmp;\
+\
+ assert(!(pos > BURN_POS_END));\
+ if (pos == BURN_POS_END)\
+ pos = TO->NEW##s;\
+ if (pos > TO->NEW##s)\
+ return 0;\
+\
+ tmp = realloc(TO->NEW, sizeof(struct NEW *) * (TO->NEW##s + 1));\
+ if (!tmp)\
+ return 0;\
+ TO->NEW = tmp;\
+ memmove(TO->NEW + pos + 1, TO->NEW + pos,\
+ sizeof(struct NEW *) * (TO->NEW##s - pos));\
+ TO->NEW##s++;\
+}
+
+struct burn_disc *burn_disc_create(void)
+{
+ struct burn_disc *d;
+ d = malloc(sizeof(struct burn_disc));
+ memset(d, 0, sizeof(struct burn_disc));
+ d->refcnt = 1;
+ d->sessions = 0;
+ d->session = NULL;
+ return d;
+}
+
+void burn_disc_free(struct burn_disc *d)
+{
+ d->refcnt--;
+ if (d->refcnt == 0) {
+ /* dec refs on all elements */
+ int i;
+
+ for (i = 0; i < d->sessions; i++)
+ burn_session_free(d->session[i]);
+ free(d->session);
+ free(d);
+ }
+}
+
+struct burn_session *burn_session_create(void)
+{
+ struct burn_session *s;
+ s = malloc(sizeof(struct burn_session));
+ memset(s, 0, sizeof(struct burn_session));
+ s->refcnt = 1;
+ s->tracks = 0;
+ s->track = NULL;
+ s->hidefirst = 0;
+ return s;
+}
+
+void burn_session_hide_first_track(struct burn_session *s, int onoff)
+{
+ s->hidefirst = onoff;
+}
+
+void burn_session_free(struct burn_session *s)
+{
+ s->refcnt--;
+ if (s->refcnt == 0) {
+ /* dec refs on all elements */
+ int i;
+
+ for (i = 0; i < s->tracks; i++)
+ burn_track_free(s->track[i]);
+ free(s->track);
+ free(s);
+ }
+
+}
+
+int burn_disc_add_session(struct burn_disc *d, struct burn_session *s,
+ unsigned int pos)
+{
+ RESIZE(d, session, pos);
+ d->session[pos] = s;
+ s->refcnt++;
+ return 1;
+}
+
+struct burn_track *burn_track_create(void)
+{
+ struct burn_track *t;
+ t = malloc(sizeof(struct burn_track));
+ memset(t, 0, sizeof(struct burn_track));
+ t->refcnt = 1;
+ t->indices = 0;
+ t->offset = 0;
+ t->offsetcount = 0;
+ t->tail = 0;
+ t->tailcount = 0;
+ t->mode = BURN_MODE1;
+ t->isrc.has_isrc = 0;
+ t->pad = 1;
+ t->entry = NULL;
+ t->source = NULL;
+ t->postgap = 0;
+ t->pregap1 = 0;
+ t->pregap2 = 0;
+ return t;
+}
+
+void burn_track_free(struct burn_track *t)
+{
+ t->refcnt--;
+ if (t->refcnt == 0) {
+ /* dec refs on all elements */
+ if (t->source)
+ burn_source_free(t->source);
+ free(t);
+ }
+}
+
+int burn_session_add_track(struct burn_session *s, struct burn_track *t,
+ unsigned int pos)
+{
+ RESIZE(s, track, pos);
+ s->track[pos] = t;
+ t->refcnt++;
+ return 1;
+}
+
+int burn_session_remove_track(struct burn_session *s, struct burn_track *t)
+{
+ struct burn_track **tmp;
+ int i, pos = -1;
+
+ assert(s->track != NULL);
+
+ burn_track_free(t);
+
+ /* Find the position */
+ for (i = 0; i < s->tracks; i++) {
+ if (t == s->track[i])
+ pos = i;
+ }
+
+ if (pos == -1)
+ return 0;
+
+ /* Is it the last track? */
+ if (pos != s->tracks) {
+ memmove(s->track[pos], s->track[pos + 1],
+ sizeof(struct burn_track *) * (s->tracks - (pos + 1)));
+ }
+
+ s->tracks--;
+ tmp = realloc(s->track, sizeof(struct burn_track *) * s->tracks);
+ if (!tmp)
+ return 0;
+ s->track = tmp;
+ return 1;
+}
+
+void burn_structure_print_disc(struct burn_disc *d)
+{
+ int i;
+
+ burn_print(12, "This disc has %d sessions\n", d->sessions);
+ for (i = 0; i < d->sessions; i++) {
+ burn_structure_print_session(d->session[i]);
+ }
+}
+void burn_structure_print_session(struct burn_session *s)
+{
+ int i;
+
+ burn_print(12, " Session has %d tracks\n", s->tracks);
+ for (i = 0; i < s->tracks; i++) {
+ burn_structure_print_track(s->track[i]);
+ }
+}
+void burn_structure_print_track(struct burn_track *t)
+{
+ burn_print(12, "(%p) track size %d sectors\n", t,
+ burn_track_get_sectors(t));
+}
+
+void burn_track_define_data(struct burn_track *t, int offset, int tail,
+ int pad, int mode)
+{
+ t->offset = offset;
+ t->pad = pad;
+ t->mode = mode;
+ t->tail = tail;
+}
+
+void burn_track_set_isrc(struct burn_track *t, char *country, char *owner,
+ unsigned char year, unsigned int serial)
+{
+ int i;
+
+ t->isrc.has_isrc = 1;
+ for (i = 0; i < 2; ++i) {
+ assert((country[i] >= '0' || country[i] < '9') &&
+ (country[i] >= 'a' || country[i] < 'z') &&
+ (country[i] >= 'A' || country[i] < 'Z'));
+ t->isrc.country[i] = country[i];
+ }
+ for (i = 0; i < 3; ++i) {
+ assert((owner[i] >= '0' || owner[i] < '9') &&
+ (owner[i] >= 'a' || owner[i] < 'z') &&
+ (owner[i] >= 'A' || owner[i] < 'Z'));
+ t->isrc.owner[i] = owner[i];
+ }
+ assert(year <= 99);
+ t->isrc.year = year;
+ assert(serial <= 99999);
+ t->isrc.serial = serial;
+}
+
+void burn_track_clear_isrc(struct burn_track *t)
+{
+ t->isrc.has_isrc = 0;
+}
+
+int burn_track_get_sectors(struct burn_track *t)
+{
+ int size;
+ int sectors, seclen;
+
+ seclen = burn_sector_length(t->mode);
+ size = t->offset + t->source->get_size(t->source) + t->tail;
+ sectors = size / seclen;
+ if (size % seclen)
+ sectors++;
+ burn_print(1, "%d sectors of %d length\n", sectors, seclen);
+ return sectors;
+}
+
+int burn_track_get_shortage(struct burn_track *t)
+{
+ int size;
+ int seclen;
+
+ seclen = burn_sector_length(t->mode);
+ size = t->offset + t->source->get_size(t->source) + t->tail;
+ if (size % seclen)
+ return seclen - size % seclen;
+ return 0;
+}
+
+int burn_session_get_sectors(struct burn_session *s)
+{
+ int sectors = 0, i;
+
+ for (i = 0; i < s->tracks; i++)
+ sectors += burn_track_get_sectors(s->track[i]);
+ return sectors;
+}
+
+int burn_disc_get_sectors(struct burn_disc *d)
+{
+ int sectors = 0, i;
+
+ for (i = 0; i < d->sessions; i++)
+ sectors += burn_session_get_sectors(d->session[i]);
+ return sectors;
+}
+
+void burn_track_get_entry(struct burn_track *t, struct burn_toc_entry *entry)
+{
+ memcpy(entry, t->entry, sizeof(struct burn_toc_entry));
+}
+
+void burn_session_get_leadout_entry(struct burn_session *s,
+ struct burn_toc_entry *entry)
+{
+ memcpy(entry, s->leadout_entry, sizeof(struct burn_toc_entry));
+}
+
+struct burn_session **burn_disc_get_sessions(struct burn_disc *d, int *num)
+{
+ *num = d->sessions;
+ return d->session;
+}
+
+struct burn_track **burn_session_get_tracks(struct burn_session *s, int *num)
+{
+ *num = s->tracks;
+ return s->track;
+}
+
+int burn_track_get_mode(struct burn_track *track)
+{
+ return track->mode;
+}
+
+int burn_session_get_hidefirst(struct burn_session *session)
+{
+ return session->hidefirst;
+}
diff --git a/tags/ZeroTwoTwo/libburn/structure.h b/tags/ZeroTwoTwo/libburn/structure.h
new file mode 100644
index 00000000..61f083f1
--- /dev/null
+++ b/tags/ZeroTwoTwo/libburn/structure.h
@@ -0,0 +1,70 @@
+#ifndef BURN__STRUCTURE_H
+#define BURN__STRUCTURE_H
+
+struct isrc
+{
+ int has_isrc;
+ char country[2]; /* each must be 0-9, A-Z */
+ char owner[3]; /* each must be 0-9, A-Z */
+ unsigned char year; /* must be 0-99 */
+ unsigned int serial; /* must be 0-99999 */
+};
+
+struct burn_track
+{
+ int refcnt;
+ struct burn_toc_entry *entry;
+ unsigned char indices;
+ /* lba address of the index */
+ unsigned int index[99];
+ /** number of 0 bytes to write before data */
+ int offset;
+ /** how much offset has been used */
+ int offsetcount;
+ /** Number of zeros to write after data */
+ int tail;
+ /** how much tail has been used */
+ int tailcount;
+ /** 1 means Pad with zeros, 0 means start reading the next track */
+ int pad;
+ /** Data source */
+ struct burn_source *source;
+ /** End of Source flag */
+ int eos;
+ /** The audio/data mode for the entry. Derived from control and
+ possibly from reading the track's first sector. */
+ int mode;
+ /** The track contains interval one of a pregap */
+ int pregap1;
+ /** The track contains interval two of a pregap */
+ int pregap2;
+ /** The track contains a postgap */
+ int postgap;
+ struct isrc isrc;
+};
+
+struct burn_session
+{
+ unsigned char firsttrack;
+ unsigned char lasttrack;
+ int hidefirst;
+ unsigned char start_m;
+ unsigned char start_s;
+ unsigned char start_f;
+ struct burn_toc_entry *leadout_entry;
+
+ int tracks;
+ struct burn_track **track;
+ int refcnt;
+};
+
+struct burn_disc
+{
+ int sessions;
+ struct burn_session **session;
+ int refcnt;
+};
+
+int burn_track_get_shortage(struct burn_track *t);
+
+#endif /* BURN__STRUCTURE_H */
diff --git a/tags/ZeroTwoTwo/libburn/toc.c b/tags/ZeroTwoTwo/libburn/toc.c
new file mode 100644
index 00000000..8d0d3fd3
--- /dev/null
+++ b/tags/ZeroTwoTwo/libburn/toc.c
@@ -0,0 +1,130 @@
+/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */
+
+#include
+#include
+#include
+#include
+#include "toc.h"
+#include "transport.h"
+#include "libburn.h"
+#include "sector.h"
+#include "options.h"
+
+#if 0
+static void write_clonecd2(volatile struct toc *toc, int f);
+
+static void write_clonecd2(volatile struct toc *toc, int f)
+{
+ int i;
+
+ /* header */
+ dprintf(f, "[CloneCD]\r\n");
+ dprintf(f, "Version=2\r\n");
+ dprintf(f, "\r\n");
+
+ /* disc data */
+ dprintf(f, "[Disc]\r\n");
+
+ dprintf(f, "TocEntries=%d\r\n", toc->toc_entries);
+ dprintf(f, "Sessions=%d\r\n", toc->sessions);
+ dprintf(f, "DataTracksScrambled=%d\r\n", toc->datatracksscrambled);
+ dprintf(f, "CDTextLength=%d\r\n", toc->cdtextlength);
+ dprintf(f, "\r\n");
+
+ /* session data */
+ for (i = 0; i < toc->sessions; ++i) {
+ dprintf(f, "[Session %d]\r\n", i + 1);
+
+ {
+ int m;
+
+ switch (toc->session[i].track[0]->mode) {
+ case BURN_MODE_RAW_DATA:
+ case BURN_MODE_AUDIO:
+ m = 0;
+ break;
+ case BURN_MODE0:
+ m = 1;
+ break;
+ case BURN_MODE1:
+ case BURN_MODE2_FORMLESS:
+ case BURN_MODE2_FORM1:
+ case BURN_MODE2_FORM2:
+ case BURN_MODE_UNINITIALIZED:
+ assert(0); /* unhandled! find out ccd's
+ value for these modes! */
+ }
+ dprintf(f, "PreGapMode=%d\r\n", m);
+ }
+ dprintf(f, "\r\n");
+ }
+
+ for (i = 0; i < toc->toc_entries; ++i) {
+ dprintf(f, "[Entry %d]\r\n", i);
+
+ dprintf(f, "Session=%d\r\n", toc->toc_entry[i].session);
+ dprintf(f, "Point=0x%02x\r\n", toc->toc_entry[i].point);
+ dprintf(f, "ADR=0x%02x\r\n", toc->toc_entry[i].adr);
+ dprintf(f, "Control=0x%02x\r\n", toc->toc_entry[i].control);
+ dprintf(f, "TrackNo=%d\r\n", toc->toc_entry[i].tno);
+ dprintf(f, "AMin=%d\r\n", toc->toc_entry[i].min);
+ dprintf(f, "ASec=%d\r\n", toc->toc_entry[i].sec);
+ dprintf(f, "AFrame=%d\r\n", toc->toc_entry[i].frame);
+ dprintf(f, "ALBA=%d\r\n",
+ burn_msf_to_lba(toc->toc_entry[i].min,
+ toc->toc_entry[i].sec,
+ toc->toc_entry[i].frame));
+ dprintf(f, "Zero=%d\r\n", toc->toc_entry[i].zero);
+ dprintf(f, "PMin=%d\r\n", toc->toc_entry[i].pmin);
+ dprintf(f, "PSec=%d\r\n", toc->toc_entry[i].psec);
+ dprintf(f, "PFrame=%d\r\n", toc->toc_entry[i].pframe);
+ dprintf(f, "PLBA=%d\r\n",
+ burn_msf_to_lba(toc->toc_entry[i].pmin,
+ toc->toc_entry[i].psec,
+ toc->toc_entry[i].pframe));
+ dprintf(f, "\r\n");
+ }
+}
+#endif
+
+void toc_find_modes(struct burn_drive *d)
+{
+ struct burn_read_opts o;
+ int lba;
+ int i, j;
+ struct buffer mem;
+ struct burn_toc_entry *e;
+
+ assert(d->busy);
+
+ mem.bytes = 0;
+ mem.sectors = 1;
+ o.raw = 1;
+ o.c2errors = 0;
+ o.subcodes_audio = 1;
+ o.subcodes_data = 1;
+ o.hardware_error_recovery = 1;
+ o.report_recovered_errors = 0;
+ o.transfer_damaged_blocks = 1;
+ o.hardware_error_retries = 1;
+
+ for (i = 0; i < d->disc->sessions; i++)
+ for (j = 0; j < d->disc->session[i]->tracks; j++) {
+ struct burn_track *t = d->disc->session[i]->track[j];
+
+ e = t->entry;
+ if (!e)
+ lba = 0;
+ else
+ lba = burn_msf_to_lba(e->pmin, e->psec,
+ e->pframe);
+/* XXX | in the subcodes if appropriate! */
+ if (e && !(e->control & 4)) {
+ t->mode = BURN_AUDIO;
+ } else {
+ mem.sectors = 1;
+ d->read_sectors(d, lba, mem.sectors, &o, &mem);
+ t->mode = sector_identify(mem.data);
+ }
+ }
+}
diff --git a/tags/ZeroTwoTwo/libburn/toc.h b/tags/ZeroTwoTwo/libburn/toc.h
new file mode 100644
index 00000000..1d804e75
--- /dev/null
+++ b/tags/ZeroTwoTwo/libburn/toc.h
@@ -0,0 +1,48 @@
+/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */
+
+#ifndef __TOC_H
+#define __TOC_H
+
+struct command;
+
+#include "libburn.h"
+#include "structure.h"
+
+/* return if a given entry refers to a track position */
+#define TOC_ENTRY_IS_TRACK(drive, entrynum) \
+ ((drive)->toc_entry[entrynum].point < 100)
+
+/* return if a given entry is in audio or data format */
+#define TOC_ENTRY_IS_AUDIO(drive, entrynum) \
+ (~(drive)->toc_entry[entrynum].control & 4)
+
+/* return the point value for a given entry number */
+#define TOC_POINT(drive, entrynum) ((drive)->toc_entry[entrynum].point)
+
+/* return the track struct for a given entry number */
+#define TOC_TRACK(drive, entrynum) \
+ ((drive)->track[TOC_POINT(drive, entrynum) - 1])
+
+/* return the lba of a toc entry */
+#define TOC_ENTRY_PLBA(drive, entrynum) \
+ burn_msf_to_lba((drive)->toc_entry[(entrynum)].pmin, \
+ (drive)->toc_entry[(entrynum)].psec, \
+ (drive)->toc_entry[(entrynum)].pframe)
+
+/* flags for the q subchannel control field */
+#define TOC_CONTROL_AUDIO (0)
+#define TOC_CONTROL_DATA (1 << 2)
+#define TOC_CONTROL_AUDIO_TWO_CHANNELS (0)
+#define TOC_CONTROL_AUDIO_FOUR_CHANNELS (1 << 3)
+#define TOC_CONTROL_AUDIO_PRE_EMPHASIS (1 << 0)
+#define TOC_CONTROL_DATA_RECORDED_UNINTERRUPTED (0)
+#define TOC_CONTROL_DATA_RECORDED_INCREMENT (1 << 0)
+#define TOC_CONTROL_COPY_PROHIBITED (0)
+#define TOC_CONTROL_COPY_PERMITTED (1 << 1)
+
+/** read a sector from each track on disc to determine modes
+ @param d The drive.
+*/
+void toc_find_modes(struct burn_drive *d);
+
+#endif /*__TOC_H*/
diff --git a/tags/ZeroTwoTwo/libburn/transport.h b/tags/ZeroTwoTwo/libburn/transport.h
new file mode 100644
index 00000000..fb71ad80
--- /dev/null
+++ b/tags/ZeroTwoTwo/libburn/transport.h
@@ -0,0 +1,171 @@
+/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */
+
+#ifndef __TRANSPORT
+#define __TRANSPORT
+
+#include "libburn.h"
+
+#include
+/* sg data structures */
+#include
+#include
+#include
+
+/* kludge! glibc headers don't define all the SCSI shit that we use! */
+#ifndef SG_GET_ACCESS_COUNT
+# define SG_GET_ACCESS_COUNT 0x2289
+#endif
+
+#define BUFFER_SIZE 65536
+
+enum transfer_direction
+{ TO_DRIVE, FROM_DRIVE, NO_TRANSFER };
+
+/* end of sg data structures */
+
+/* generic 'drive' data structures */
+
+struct cue_sheet
+{
+ int count;
+ unsigned char *data;
+};
+
+struct params
+{
+ int speed;
+ int retries;
+};
+
+struct buffer
+{
+ unsigned char data[BUFFER_SIZE];
+ int sectors;
+ int bytes;
+};
+
+struct command
+{
+ unsigned char opcode[16];
+ int oplen;
+ int dir;
+ unsigned char sense[128];
+ int error;
+ int retry;
+ struct buffer *page;
+};
+
+struct scsi_inquiry_data
+{
+ char vendor[9];
+ char product[17];
+ char revision[5];
+ int valid;
+};
+
+struct scsi_mode_data
+{
+ int buffer_size;
+ int dvdram_read;
+ int dvdram_write;
+ int dvdr_read;
+ int dvdr_write;
+ int dvdrom_read;
+ int cdrw_read;
+ int cdrw_write;
+ int cdr_read;
+ int cdr_write;
+ int simulate;
+ int max_read_speed;
+ int max_write_speed;
+ int cur_read_speed;
+ int cur_write_speed;
+ int retry_page_length;
+ int retry_page_valid;
+ int write_page_length;
+ int write_page_valid;
+ int c2_pointers;
+ int valid;
+ int underrun_proof;
+};
+
+
+/** Gets initialized in enumerate_common() and burn_drive_register() */
+struct burn_drive
+{
+ int host;
+ int id;
+ int channel;
+ int lun;
+ char *devname;
+ int fd;
+
+ /* ts A60904 : ticket 62, contribution by elmom */
+ /**
+ Tells the index in scanned burn_drive_info array.
+ -1 if fallen victim to burn_drive_info_forget()
+ */
+ int global_index;
+
+ pthread_mutex_t access_lock;
+
+ enum burn_disc_status status;
+ int erasable;
+ volatile int released;
+ int nwa; /* next writeable address */
+ int alba; /* absolute lba */
+ int rlba; /* relative lba in section */
+ int start_lba;
+ int end_lba;
+ int toc_temp;
+ struct burn_disc *disc; /* disc structure */
+ int block_types[4];
+ struct buffer *buffer;
+ struct burn_progress progress;
+
+ volatile int cancel;
+ volatile enum burn_drive_status busy;
+/* transport functions */
+ int (*grab) (struct burn_drive *);
+ int (*release) (struct burn_drive *);
+ int (*issue_command) (struct burn_drive *, struct command *);
+
+/* lower level functions */
+ void (*erase) (struct burn_drive *, int);
+ void (*getcaps) (struct burn_drive *);
+ int (*write) (struct burn_drive *, int, struct buffer *);
+ void (*read_toc) (struct burn_drive *);
+ void (*lock) (struct burn_drive *);
+ void (*unlock) (struct burn_drive *);
+ void (*eject) (struct burn_drive *);
+ void (*load) (struct burn_drive *);
+ void (*read_disc_info) (struct burn_drive *);
+ void (*read_sectors) (struct burn_drive *,
+ int start,
+ int len,
+ const struct burn_read_opts *, struct buffer *);
+ void (*perform_opc) (struct burn_drive *);
+ void (*set_speed) (struct burn_drive *, int, int);
+ void (*send_parameters) (struct burn_drive *,
+ const struct burn_read_opts *);
+ void (*send_write_parameters) (struct burn_drive *,
+ const struct burn_write_opts *);
+ void (*send_cue_sheet) (struct burn_drive *, struct cue_sheet *);
+ void (*sync_cache) (struct burn_drive *);
+ int (*get_erase_progress) (struct burn_drive *);
+ int (*get_nwa) (struct burn_drive *);
+ void (*close_disc) (struct burn_drive * d, struct burn_write_opts * o);
+ void (*close_session) (struct burn_drive * d,
+ struct burn_write_opts * o);
+ int (*test_unit_ready) (struct burn_drive * d);
+ void (*probe_write_modes) (struct burn_drive * d);
+ struct params params;
+ struct scsi_inquiry_data *idata;
+ struct scsi_mode_data *mdata;
+ int toc_entries;
+ struct burn_toc_entry *toc_entry;
+};
+
+/* end of generic 'drive' data structures */
+
+#endif /* __TRANSPORT */
diff --git a/tags/ZeroTwoTwo/libburn/util.c b/tags/ZeroTwoTwo/libburn/util.c
new file mode 100644
index 00000000..cad28587
--- /dev/null
+++ b/tags/ZeroTwoTwo/libburn/util.c
@@ -0,0 +1,44 @@
+#include
+#include
+#include
+#include "../version.h"
+#include "util.h"
+#include "libburn.h"
+
+char *burn_strdup(char *s)
+{
+ char *ret;
+ int l;
+
+ assert(s);
+
+ l = strlen(s) + 1;
+ ret = malloc(l);
+ memcpy(ret, s, l);
+
+ return ret;
+}
+
+char *burn_strndup(char *s, int n)
+{
+ char *ret;
+ int l;
+
+ assert(s);
+ assert(n > 0);
+
+ l = strlen(s);
+ ret = malloc(l < n ? l : n);
+
+ memcpy(ret, s, l < n - 1 ? l : n - 1);
+ ret[n - 1] = '\0';
+
+ return ret;
+}
+
+void burn_version(int *major, int *minor, int *micro)
+{
+ *major = BURN_MAJOR_VERSION;
+ *minor = BURN_MINOR_VERSION;
+ *micro = BURN_MICRO_VERSION;
+}
diff --git a/tags/ZeroTwoTwo/libburn/util.h b/tags/ZeroTwoTwo/libburn/util.h
new file mode 100644
index 00000000..4e74895d
--- /dev/null
+++ b/tags/ZeroTwoTwo/libburn/util.h
@@ -0,0 +1,8 @@
+#ifndef __UTIL
+#define __UTIL
+
+char *burn_strdup(char *s);
+
+char *burn_strndup(char *s, int n);
+
+#endif
diff --git a/tags/ZeroTwoTwo/libburn/write.c b/tags/ZeroTwoTwo/libburn/write.c
new file mode 100644
index 00000000..9bd88e35
--- /dev/null
+++ b/tags/ZeroTwoTwo/libburn/write.c
@@ -0,0 +1,546 @@
+/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include "error.h"
+#include "sector.h"
+#include "libburn.h"
+#include "drive.h"
+#include "transport.h"
+#include "message.h"
+#include "crc.h"
+#include "debug.h"
+#include "init.h"
+#include "lec.h"
+#include "toc.h"
+#include "util.h"
+#include "sg.h"
+#include "write.h"
+#include "options.h"
+
+static int type_to_ctrl(int mode)
+{
+ int ctrl = 0;
+
+ int data = BURN_MODE2 | BURN_MODE1 | BURN_MODE0;
+
+ if (mode & data) {
+ ctrl |= 4;
+ } else if (mode & BURN_AUDIO) {
+ if (mode & BURN_4CH)
+ ctrl |= 8;
+ if (mode & BURN_PREEMPHASIS)
+ ctrl |= 1;
+ } else
+ assert(0);
+
+ if (mode & BURN_COPY)
+ ctrl |= 2;
+
+ return ctrl;
+}
+
+/* only the ctrl nibble is set here (not adr) */
+static void type_to_form(int mode, unsigned char *ctladr, int *form)
+{
+ *ctladr = type_to_ctrl(mode) << 4;
+
+ if (mode & BURN_AUDIO)
+ *form = 0;
+ if (mode & BURN_MODE0)
+ assert(0);
+ if (mode & BURN_MODE1)
+ *form = 0x10;
+ if (mode & BURN_MODE2)
+ assert(0); /* XXX someone's gonna want this sometime */
+ if (mode & BURN_MODE_RAW)
+ *form = 0;
+ if (mode & BURN_SUBCODE_P16) /* must be expanded to R96 */
+ *form |= 0x40;
+ if (mode & BURN_SUBCODE_P96)
+ *form |= 0xC0;
+ if (mode & BURN_SUBCODE_R96)
+ *form |= 0x40;
+}
+
+int burn_write_flush(struct burn_write_opts *o)
+{
+ struct burn_drive *d = o->drive;
+
+ if (d->buffer->bytes && !d->cancel) {
+ int err;
+ err = d->write(d, d->nwa, d->buffer);
+ if (err == BE_CANCELLED)
+ return 0;
+ d->nwa += d->buffer->sectors;
+ }
+ d->sync_cache(d);
+ return 1;
+}
+
+
+/* ts A60819:
+ This is unused since about Feb 2006, icculus.org/burn CVS.
+ The compiler complains. We shall please our compiler.
+*/
+#ifdef Libburn_write_with_function_print_cuE
+
+static void print_cue(struct cue_sheet *sheet)
+{
+ int i;
+ unsigned char *unit;
+
+ printf("\n");
+ printf("ctladr|trno|indx|form|scms| msf\n");
+ printf("------+----+----+----+----+--------\n");
+ for (i = 0; i < sheet->count; i++) {
+ unit = sheet->data + 8 * i;
+ printf(" %1X %1X | %02X | %02X | %02X | %02X |",
+ (unit[0] & 0xf0) >> 4, unit[0] & 0xf, unit[1], unit[2],
+ unit[3], unit[4]);
+ printf("%02X:%02X:%02X\n", unit[5], unit[6], unit[7]);
+ }
+}
+
+#endif /* Libburn_write_with_print_cuE */
+
+
+static void add_cue(struct cue_sheet *sheet, unsigned char ctladr,
+ unsigned char tno, unsigned char indx,
+ unsigned char form, unsigned char scms, int lba)
+{
+ unsigned char *unit;
+ unsigned char *ptr;
+ int m, s, f;
+
+ burn_lba_to_msf(lba, &m, &s, &f);
+
+ sheet->count++;
+ ptr = realloc(sheet->data, sheet->count * 8);
+ assert(ptr);
+ sheet->data = ptr;
+ unit = sheet->data + (sheet->count - 1) * 8;
+ unit[0] = ctladr;
+ unit[1] = tno;
+ unit[2] = indx;
+ unit[3] = form;
+ unit[4] = scms;
+ unit[5] = m;
+ unit[6] = s;
+ unit[7] = f;
+}
+
+struct cue_sheet *burn_create_toc_entries(struct burn_write_opts *o,
+ struct burn_session *session)
+{
+ int i, m, s, f, form, pform, runtime = -150;
+ unsigned char ctladr;
+ struct burn_drive *d;
+ struct burn_toc_entry *e;
+ struct cue_sheet *sheet;
+ struct burn_track **tar = session->track;
+ int ntr = session->tracks;
+ int rem = 0;
+
+ d = o->drive;
+
+ sheet = malloc(sizeof(struct cue_sheet));
+ sheet->data = NULL;
+ sheet->count = 0;
+
+ type_to_form(tar[0]->mode, &ctladr, &form);
+ add_cue(sheet, ctladr | 1, 0, 0, 1, 0, runtime);
+ add_cue(sheet, ctladr | 1, 1, 0, form, 0, runtime);
+ runtime += 150;
+
+ burn_print(1, "toc for %d tracks:\n", ntr);
+ d->toc_entries = ntr + 3;
+ assert(d->toc_entry == NULL);
+ d->toc_entry = malloc(d->toc_entries * sizeof(struct burn_toc_entry));
+ e = d->toc_entry;
+ memset((void *)e, 0, d->toc_entries * sizeof(struct burn_toc_entry));
+ e[0].point = 0xA0;
+ if (tar[0]->mode & BURN_AUDIO)
+ e[0].control = TOC_CONTROL_AUDIO;
+ else
+ e[0].control = TOC_CONTROL_DATA;
+ e[0].pmin = 1;
+ e[0].psec = o->format;
+ e[0].adr = 1;
+ e[1].point = 0xA1;
+ e[1].pmin = ntr;
+ e[1].adr = 1;
+ if (tar[ntr - 1]->mode & BURN_AUDIO)
+ e[1].control = TOC_CONTROL_AUDIO;
+ else
+ e[1].control = TOC_CONTROL_DATA;
+ e[2].point = 0xA2;
+ e[2].control = e[1].control;
+ e[2].adr = 1;
+
+ tar[0]->pregap2 = 1;
+ pform = form;
+ for (i = 0; i < ntr; i++) {
+ type_to_form(tar[i]->mode, &ctladr, &form);
+ if (pform != form) {
+ add_cue(sheet, ctladr | 1, i + 1, 0, form, 0, runtime);
+ runtime += 150;
+/* XXX fix pregap interval 1 for data tracks */
+/* ts A60813 silence righteous compiler warning about C++ style comments
+ This is possibly not a comment but rather a trace of Derek Foreman
+ experiments. Thus not to be beautified - but to be preserved rectified.
+/ / if (!(form & BURN_AUDIO))
+/ / tar[i]->pregap1 = 1;
+*/
+ tar[i]->pregap2 = 1;
+ }
+/* XXX HERE IS WHERE WE DO INDICES IN THE CUE SHEET */
+/* XXX and we should make sure the gaps conform to ecma-130... */
+ tar[i]->entry = &e[3 + i];
+ e[3 + i].point = i + 1;
+ burn_lba_to_msf(runtime, &m, &s, &f);
+ e[3 + i].pmin = m;
+ e[3 + i].psec = s;
+ e[3 + i].pframe = f;
+ e[3 + i].adr = 1;
+ e[3 + i].control = type_to_ctrl(tar[i]->mode);
+ burn_print(1, "track %d control %d\n", tar[i]->mode,
+ e[3 + i].control);
+ add_cue(sheet, ctladr | 1, i + 1, 1, form, 0, runtime);
+ runtime += burn_track_get_sectors(tar[i]);
+/* if we're padding, we'll clear any current shortage.
+ if we're not, we'll slip toc entries by a sector every time our
+ shortage is more than a sector
+XXX this is untested :)
+*/
+ if (!tar[i]->pad) {
+ rem += burn_track_get_shortage(tar[i]);
+ if (i +1 != ntr)
+ tar[i]->source->next = tar[i+1]->source;
+ } else if (rem) {
+ rem = 0;
+ runtime++;
+ }
+ if (rem > burn_sector_length(tar[i]->mode)) {
+ rem -= burn_sector_length(tar[i]->mode);
+ runtime--;
+ }
+ pform = form;
+ }
+ burn_lba_to_msf(runtime, &m, &s, &f);
+ e[2].pmin = m;
+ e[2].psec = s;
+ e[2].pframe = f;
+ burn_print(1, "run time is %d (%d:%d:%d)\n", runtime, m, s, f);
+ for (i = 0; i < d->toc_entries; i++)
+ burn_print(1, "point %d (%02d:%02d:%02d)\n",
+ d->toc_entry[i].point, d->toc_entry[i].pmin,
+ d->toc_entry[i].psec, d->toc_entry[i].pframe);
+ add_cue(sheet, ctladr | 1, 0xAA, 1, 1, 0, runtime);
+ return sheet;
+}
+
+int burn_sector_length(int tracktype)
+{
+ if (tracktype & BURN_AUDIO)
+ return 2352;
+ if (tracktype & BURN_MODE_RAW)
+ return 2352;
+ if (tracktype & BURN_MODE1)
+ return 2048;
+ assert(0);
+ return 12345;
+}
+
+int burn_subcode_length(int tracktype)
+{
+ if (tracktype & BURN_SUBCODE_P16)
+ return 16;
+ if ((tracktype & BURN_SUBCODE_P96) || (tracktype & BURN_SUBCODE_R96))
+ return 96;
+ return 0;
+}
+
+int burn_write_leadin(struct burn_write_opts *o,
+ struct burn_session *s, int first)
+{
+ struct burn_drive *d = o->drive;
+ int count;
+
+ d->busy = BURN_DRIVE_WRITING_LEADIN;
+
+ burn_print(5, first ? " first leadin\n" : " leadin\n");
+
+ if (first)
+ count = 0 - d->alba - 150;
+ else
+ count = 4500;
+
+ d->progress.start_sector = d->alba;
+ d->progress.sectors = count;
+ d->progress.sector = 0;
+
+ while (count != 0) {
+ if (!sector_toc(o, s->track[0]->mode))
+ return 0;
+ count--;
+ d->progress.sector++;
+ }
+ d->busy = BURN_DRIVE_WRITING;
+ return 1;
+}
+
+int burn_write_leadout(struct burn_write_opts *o,
+ int first, unsigned char control, int mode)
+{
+ struct burn_drive *d = o->drive;
+ int count;
+
+ d->busy = BURN_DRIVE_WRITING_LEADOUT;
+
+ d->rlba = -150;
+ burn_print(5, first ? " first leadout\n" : " leadout\n");
+ if (first)
+ count = 6750;
+ else
+ count = 2250;
+ d->progress.start_sector = d->alba;
+ d->progress.sectors = count;
+ d->progress.sector = 0;
+
+ while (count != 0) {
+ if (!sector_lout(o, control, mode))
+ return 0;
+ count--;
+ d->progress.sector++;
+ }
+ d->busy = BURN_DRIVE_WRITING;
+ return 1;
+}
+
+int burn_write_session(struct burn_write_opts *o, struct burn_session *s)
+{
+ struct burn_drive *d = o->drive;
+ struct burn_track *prev = NULL, *next = NULL;
+ int i;
+
+ d->rlba = 0;
+ burn_print(1, " writing a session\n");
+ for (i = 0; i < s->tracks; i++) {
+ if (i > 0)
+ prev = s->track[i - 1];
+ if (i + 1 < s->tracks)
+ next = s->track[i + 1];
+ else
+ next = NULL;
+
+ if (!burn_write_track(o, s, i))
+ return 0;
+ }
+ return 1;
+}
+
+int burn_write_track(struct burn_write_opts *o, struct burn_session *s,
+ int tnum)
+{
+ struct burn_track *t = s->track[tnum];
+ struct burn_drive *d = o->drive;
+ int i, tmp = 0;
+ int sectors;
+
+ d->rlba = -150;
+
+/* XXX for tao, we don't want the pregaps but still want post? */
+ if (o->write_type != BURN_WRITE_TAO) {
+ if (t->pregap1)
+ d->rlba += 75;
+ if (t->pregap2)
+ d->rlba += 150;
+
+ if (t->pregap1) {
+ struct burn_track *pt = s->track[tnum - 1];
+
+ if (tnum == 0) {
+ printf("first track should not have a pregap1\n");
+ pt = t;
+ }
+ for (i = 0; i < 75; i++)
+ if (!sector_pregap(o, t->entry->point,
+ pt->entry->control, pt->mode))
+ return 0;
+ }
+ if (t->pregap2)
+ for (i = 0; i < 150; i++)
+ if (!sector_pregap(o, t->entry->point,
+ t->entry->control, t->mode))
+ return 0;
+ } else {
+ o->control = t->entry->control;
+ d->send_write_parameters(d, o);
+ }
+
+/* user data */
+ sectors = burn_track_get_sectors(t);
+
+ /* Update progress */
+ d->progress.start_sector = d->nwa;
+ d->progress.sectors = sectors;
+ d->progress.sector = 0;
+
+ /* ts A60831: added tnum-line, extended print message on proposal
+ by bonfire-app@wanadoo.fr in http://libburn.pykix.org/ticket/58 */
+ d->progress.track = tnum;
+ burn_print(12, "track %d is %d sectors long\n", tnum, sectors);
+
+ if (tnum == s->tracks)
+ tmp = sectors > 150 ? 150 : sectors;
+
+ for (i = 0; i < sectors - tmp; i++) {
+ if (!sector_data(o, t, 0))
+ return 0;
+
+ /* update current progress */
+ d->progress.sector++;
+ }
+ for (; i < sectors; i++) {
+ burn_print(1, "last track, leadout prep\n");
+ if (!sector_data(o, t, 1))
+ return 0;
+
+ /* update progress */
+ d->progress.sector++;
+ }
+
+ if (t->postgap)
+ for (i = 0; i < 150; i++)
+ if (!sector_postgap(o, t->entry->point, t->entry->control,
+ t->mode))
+ return 0;
+ i = t->offset;
+ if (o->write_type == BURN_WRITE_SAO) {
+ if (d->buffer->bytes) {
+ int err;
+ err = d->write(d, d->nwa, d->buffer);
+ if (err == BE_CANCELLED)
+ return 0;
+ d->nwa += d->buffer->sectors;
+ d->buffer->bytes = 0;
+ d->buffer->sectors = 0;
+ }
+ }
+ if (o->write_type == BURN_WRITE_TAO)
+ if (!burn_write_flush(o))
+ return 0;
+ return 1;
+}
+
+void burn_disc_write_sync(struct burn_write_opts *o, struct burn_disc *disc)
+{
+ struct cue_sheet *sheet;
+ struct burn_drive *d = o->drive;
+ struct buffer buf;
+ struct burn_track *lt;
+ int first = 1, i;
+ int res;
+
+ burn_message_clear_queue();
+
+ burn_print(1, "sync write of %d sessions\n", disc->sessions);
+ d->buffer = &buf;
+ memset(d->buffer, 0, sizeof(struct buffer));
+
+ d->rlba = -150;
+
+ d->toc_temp = 9;
+
+/* Apparently some drives require this command to be sent, and a few drives
+return crap. so we send the command, then ignore the result.
+*/
+ res = d->get_nwa(d);
+/* printf("ignored nwa: %d\n", res);*/
+
+ d->alba = d->start_lba;
+ d->nwa = d->alba;
+
+ if (o->write_type != BURN_WRITE_TAO)
+ d->send_write_parameters(d, o);
+
+ /* init progress before showing the state */
+ d->progress.session = 0;
+ d->progress.sessions = disc->sessions;
+ d->progress.track = 0;
+ d->progress.tracks = disc->session[0]->tracks;
+ /* TODO: handle indices */
+ d->progress.index = 0;
+ d->progress.indices = disc->session[0]->track[0]->indices;
+ /* TODO: handle multissession discs */
+ /* XXX: sectors are only set during write track */
+ d->progress.start_sector = 0;
+ d->progress.sectors = 0;
+ d->progress.sector = 0;
+
+ d->busy = BURN_DRIVE_WRITING;
+
+ for (i = 0; i < disc->sessions; i++) {
+ /* update progress */
+ d->progress.session = i;
+ d->progress.tracks = disc->session[i]->tracks;
+
+ sheet = burn_create_toc_entries(o, disc->session[i]);
+/* print_cue(sheet);*/
+ if (o->write_type == BURN_WRITE_SAO)
+ d->send_cue_sheet(d, sheet);
+ free(sheet);
+
+ if (o->write_type == BURN_WRITE_RAW) {
+ if (!burn_write_leadin(o, disc->session[i], first))
+ goto fail;
+ } else {
+ if (first) {
+ d->nwa = -150;
+ d->alba = -150;
+ } else {
+ d->nwa += 4500;
+ d->alba += 4500;
+ }
+ }
+ if (!burn_write_session(o, disc->session[i]))
+ goto fail;
+
+ lt = disc->session[i]->track[disc->session[i]->tracks - 1];
+ if (o->write_type == BURN_WRITE_RAW) {
+ if (!burn_write_leadout(o, first, lt->entry->control,
+ lt->mode))
+ goto fail;
+ } else {
+ if (!burn_write_flush(o))
+ goto fail;
+ d->nwa += first ? 6750 : 2250;
+ d->alba += first ? 6750 : 2250;
+ }
+ if (first)
+ first = 0;
+
+ /* XXX: currently signs an end of session */
+ d->progress.sector = 0;
+ d->progress.start_sector = 0;
+ d->progress.sectors = 0;
+ }
+ if (o->write_type != BURN_WRITE_SAO)
+ if (!burn_write_flush(o))
+ goto fail;
+ sleep(1);
+
+ burn_print(1, "done\n");
+ d->busy = BURN_DRIVE_IDLE;
+
+fail:
+ d->sync_cache(d);
+ burn_print(1, "done - failed\n");
+ d->busy = BURN_DRIVE_IDLE;
+}
diff --git a/tags/ZeroTwoTwo/libburn/write.h b/tags/ZeroTwoTwo/libburn/write.h
new file mode 100644
index 00000000..d7eb733f
--- /dev/null
+++ b/tags/ZeroTwoTwo/libburn/write.h
@@ -0,0 +1,25 @@
+/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */
+
+#ifndef BURN__WRITE_H
+#define BURN__WRITE_H
+
+struct cue_sheet;
+struct burn_session;
+struct burn_write_opts;
+struct burn_disc;
+
+struct cue_sheet *burn_create_toc_entries(struct burn_write_opts *o,
+ struct burn_session *session);
+int burn_sector_length(int trackmode);
+int burn_subcode_length(int trackmode);
+void burn_disc_write_sync(struct burn_write_opts *o, struct burn_disc *disc);
+int burn_write_leadin(struct burn_write_opts *o,
+ struct burn_session *s, int first);
+int burn_write_leadout(struct burn_write_opts *o,
+ int first, unsigned char control, int mode);
+int burn_write_session(struct burn_write_opts *o, struct burn_session *s);
+int burn_write_track(struct burn_write_opts *o, struct burn_session *s,
+ int tnum);
+int burn_write_flush(struct burn_write_opts *o);
+
+#endif /* BURN__WRITE_H */
diff --git a/tags/ZeroTwoTwo/libisofs-1.pc.in b/tags/ZeroTwoTwo/libisofs-1.pc.in
new file mode 100644
index 00000000..32f3070a
--- /dev/null
+++ b/tags/ZeroTwoTwo/libisofs-1.pc.in
@@ -0,0 +1,11 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: libisofs
+Description: ISO9660 filesystem creation library
+Version: @VERSION@
+Requires:
+Libs: -L${libdir} -lisofs
+Cflags: -I${includedir}/libburn
diff --git a/tags/ZeroTwoTwo/libisofs/Makefile b/tags/ZeroTwoTwo/libisofs/Makefile
new file mode 100755
index 00000000..062350dd
--- /dev/null
+++ b/tags/ZeroTwoTwo/libisofs/Makefile
@@ -0,0 +1,4 @@
+all clean:
+ $(MAKE) -C .. -$(MAKEFLAGS) $@
+
+.PHONY: all clean
diff --git a/tags/ZeroTwoTwo/libisofs/Makefile.am b/tags/ZeroTwoTwo/libisofs/Makefile.am
new file mode 100755
index 00000000..e2f197ee
--- /dev/null
+++ b/tags/ZeroTwoTwo/libisofs/Makefile.am
@@ -0,0 +1,49 @@
+pkgconfigdir=$(libdir)/pkgconfig
+libincludedir=$(includedir)/libburn
+
+##bin_PROGRAMS = test
+
+lib_LTLIBRARIES = libisofs.la
+
+libisofs_la_SOURCES = \
+ tree.h \
+ tree.c \
+ volume.h \
+ volume.c \
+ util.h \
+ util.c \
+ ecma119.c \
+ ecma119.h \
+ ecma119_tree.c \
+ ecma119_tree.h \
+ susp.h \
+ susp.c \
+ rockridge.h \
+ rockridge.c \
+ joliet.c \
+ joliet.h
+
+libinclude_HEADERS = libisofs.h
+
+##test_SOURCES = test.c
+##test_LDADD = libisofs.la
+
+##noinst_PROGRAMS = test
+##test_SOURCES = test.c
+##test_LDADD = $(libisofs_la_OBJECTS)
+
+##INCLUDES = -I../burn/libburn
+
+## ========================================================================= ##
+indent_files = $(libisofs_la_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
+
+## ========================================================================= ##
diff --git a/tags/ZeroTwoTwo/libisofs/ecma119.c b/tags/ZeroTwoTwo/libisofs/ecma119.c
new file mode 100755
index 00000000..a1ac6810
--- /dev/null
+++ b/tags/ZeroTwoTwo/libisofs/ecma119.c
@@ -0,0 +1,694 @@
+/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */
+/* vim: set noet ts=8 sts=8 sw=8 : */
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "ecma119.h"
+#include "ecma119_tree.h"
+#include "susp.h"
+#include "rockridge.h"
+#include "joliet.h"
+#include "volume.h"
+#include "tree.h"
+#include "util.h"
+#include "libisofs.h"
+#include "libburn/libburn.h"
+
+/* burn-source compatible stuff */
+static int
+bs_read(struct burn_source *bs, unsigned char *buf, int size);
+static off_t
+bs_get_size(struct burn_source *bs);
+static void
+bs_free_data(struct burn_source *bs);
+
+typedef void (*write_fn)(struct ecma119_write_target*, uint8_t*);
+
+/* return true if the given state is only required for Joliet volumes */
+static int
+is_joliet_state(enum ecma119_write_state);
+
+static void
+next_state(struct ecma119_write_target *t);
+
+/* write t->state_data to the buf, one block at a time */
+static void
+write_data_chunk(struct ecma119_write_target *t, uint8_t *buf);
+
+/* writing functions. All these functions assume the buf is large enough */
+static void
+write_pri_vol_desc(struct ecma119_write_target *t, uint8_t *buf);
+static void
+write_vol_desc_terminator(struct ecma119_write_target *t, uint8_t *buf);
+static void
+write_path_table(struct ecma119_write_target *t, int l_type, uint8_t *buf);
+static void
+write_l_path_table(struct ecma119_write_target *t, uint8_t *buf);
+static void
+write_m_path_table(struct ecma119_write_target *t, uint8_t *buf);
+static void
+write_one_dir_record(struct ecma119_write_target *t,
+ struct ecma119_tree_node *dir,
+ int file_id,
+ uint8_t *buf);
+static void
+write_one_dir(struct ecma119_write_target *t,
+ struct ecma119_tree_node *dir,
+ uint8_t *buf);
+static void
+write_dirs(struct ecma119_write_target *t, uint8_t *buf);
+
+/* wrapper functions for writing */
+static void wr_system_area(struct ecma119_write_target*, uint8_t*);
+static void wr_pri_vol_desc(struct ecma119_write_target*, uint8_t*);
+static void wr_vol_desc_term(struct ecma119_write_target*, uint8_t*);
+static void wr_l_path_table(struct ecma119_write_target*, uint8_t*);
+static void wr_m_path_table(struct ecma119_write_target*, uint8_t*);
+static void wr_dir_records(struct ecma119_write_target*, uint8_t*);
+static void wr_files(struct ecma119_write_target*, uint8_t*);
+
+static const write_fn writers[] =
+{
+ NULL,
+ wr_system_area,
+ wr_pri_vol_desc,
+ joliet_wr_sup_vol_desc,
+ wr_vol_desc_term,
+ wr_l_path_table,
+ wr_m_path_table,
+ joliet_wr_l_path_table,
+ joliet_wr_m_path_table,
+ wr_dir_records,
+ joliet_wr_dir_records,
+ wr_files
+};
+
+/* When a writer is created, we
+ * 1) create an ecma119 tree
+ * 2) add SUSP fields (if necessary)
+ * 3) calculate the size and position of all nodes in the tree
+ * 4) finalize SUSP fields (if necessary)
+ */
+
+static void
+add_susp_fields_rec(struct ecma119_write_target *t,
+ struct ecma119_tree_node *node)
+{
+ size_t i;
+
+ if (!node->iso_self)
+ return;
+
+ rrip_add_PX(t, node);
+ rrip_add_NM(t, node);
+ rrip_add_TF(t, node);
+ if (node->iso_self->attrib.st_rdev)
+ rrip_add_PN(t, node);
+ if (S_ISLNK(node->iso_self->attrib.st_mode))
+ rrip_add_SL(t, node);
+ if (node->type == ECMA119_FILE && node->file.real_me)
+ rrip_add_CL(t, node);
+ if (node->type == ECMA119_DIR
+ && node->dir.real_parent != node->parent) {
+ rrip_add_RE(t, node);
+ rrip_add_PL(t, node);
+ }
+ susp_add_CE(t, node);
+
+ if (node->type == ECMA119_DIR) {
+ for (i = 0; i < node->dir.nchildren; i++) {
+ add_susp_fields_rec(t, node->dir.children[i]);
+ }
+ }
+}
+
+static void
+add_susp_fields(struct ecma119_write_target *t)
+{
+ susp_add_SP(t, t->root);
+ rrip_add_ER(t, t->root);
+ add_susp_fields_rec(t, t->root);
+}
+
+/**
+ * Fill out the dir.len and dir.CE_len fields for each
+ * ecma119_tree_node that is a directory. Also calculate the total number of
+ * directories and the number of files for which we need to write out data.
+ * (dirlist_len and filelist_len)
+ */
+static void
+calc_dir_size(struct ecma119_write_target *t,
+ struct ecma119_tree_node *dir)
+{
+ size_t i;
+
+ assert(dir->type == ECMA119_DIR);
+
+ t->dirlist_len++;
+ dir->dir.len = 34 + dir->dir.self_susp.non_CE_len
+ + 34 + dir->dir.parent_susp.non_CE_len;
+ dir->dir.CE_len = dir->dir.self_susp.CE_len
+ + dir->dir.parent_susp.CE_len;
+ for (i = 0; i < dir->dir.nchildren; i++) {
+ struct ecma119_tree_node *ch = dir->dir.children[i];
+
+ dir->dir.len += ch->dirent_len + ch->susp.non_CE_len;
+ dir->dir.CE_len += ch->susp.CE_len;
+ }
+ t->total_dir_size += round_up(dir->dir.len + dir->dir.CE_len,
+ t->block_size);
+
+ for (i = 0; i < dir->dir.nchildren; i++) {
+ struct ecma119_tree_node *ch = dir->dir.children[i];
+ struct iso_tree_node *iso = ch->iso_self;
+ if (ch->type == ECMA119_DIR) {
+ calc_dir_size(t, ch);
+ } else if (iso && iso->attrib.st_size
+ && iso->loc.type == LIBISO_FILESYS
+ && iso->loc.path) {
+ t->filelist_len++;
+ }
+ }
+}
+
+/**
+ * Fill out the block field in each ecma119_tree_node that is a directory and
+ * fill out t->dirlist.
+ */
+static void
+calc_dir_pos(struct ecma119_write_target *t,
+ struct ecma119_tree_node *dir)
+{
+ size_t i;
+
+ assert(dir->type == ECMA119_DIR);
+
+ /* we don't need to set iso_self->block since each tree writes
+ * its own directories */
+ dir->block = t->curblock;
+ t->curblock += div_up(dir->dir.len + dir->dir.CE_len, t->block_size);
+ t->dirlist[t->curfile++] = dir;
+ for (i = 0; i < dir->dir.nchildren; i++) {
+ struct ecma119_tree_node *ch = dir->dir.children[i];
+ if (ch->type == ECMA119_DIR)
+ calc_dir_pos(t, ch);
+ }
+
+ /* reset curfile when we're finished */
+ if (!dir->parent) {
+ t->curfile = 0;
+ }
+}
+
+/**
+ * Fill out the block field for each ecma119_tree_node that is a file and fill
+ * out t->filelist.
+ */
+static void
+calc_file_pos(struct ecma119_write_target *t,
+ struct ecma119_tree_node *dir)
+{
+ size_t i;
+
+ assert(dir->type == ECMA119_DIR);
+
+ for (i = 0; i < dir->dir.nchildren; i++) {
+ struct ecma119_tree_node *ch = dir->dir.children[i];
+ if (ch->type == ECMA119_FILE && ch->iso_self) {
+ struct iso_tree_node *iso = ch->iso_self;
+ off_t size = iso->attrib.st_size;
+
+ iso->block = ch->block = t->curblock;
+ t->curblock += div_up(size, t->block_size);
+ if (size && iso->loc.type == LIBISO_FILESYS
+ && iso->loc.path)
+ t->filelist[t->curfile++] = ch;
+ }
+ }
+
+ for (i = 0; i < dir->dir.nchildren; i++) {
+ struct ecma119_tree_node *ch = dir->dir.children[i];
+ if (ch->type == ECMA119_DIR)
+ calc_file_pos(t, ch);
+ }
+
+ /* reset curfile when we're finished */
+ if (!dir->parent) {
+ t->curfile = 0;
+ }
+}
+
+struct ecma119_write_target*
+ecma119_target_new(struct iso_volset *volset,
+ int volnum,
+ int level,
+ int flags)
+{
+ struct ecma119_write_target *t =
+ calloc(1, sizeof(struct ecma119_write_target));
+ size_t i, j, cur;
+ struct iso_tree_node *iso_root = volset->volume[volnum]->root;
+
+ volset->refcount++;
+ t->root = ecma119_tree_create(t, iso_root);
+ t->joliet = (flags & ECMA119_JOLIET) ? 1 : 0;
+ if (t->joliet)
+ t->joliet_root = joliet_tree_create(t, iso_root);
+ t->volset = volset;
+ t->volnum = volnum;
+ t->now = time(NULL);
+
+ t->rockridge = (flags & ECMA119_ROCKRIDGE) ? 1 : 0;
+ t->iso_level = level;
+ t->block_size = 2048;
+
+ if (t->rockridge)
+ add_susp_fields(t);
+ calc_dir_size(t, t->root);
+ if (t->joliet) {
+ joliet_calc_dir_size(t, t->joliet_root);
+ t->pathlist_joliet = calloc(1, sizeof(void*) * t->dirlist_len);
+ t->dirlist_joliet = calloc(1, sizeof(void*) * t->dirlist_len);
+ }
+
+ t->dirlist = calloc(1, sizeof(void*) * t->dirlist_len);
+ t->pathlist = calloc(1, sizeof(void*) * t->dirlist_len);
+ t->filelist = calloc(1, sizeof(void*) * t->filelist_len);
+
+ /* fill out the pathlist */
+ t->pathlist[0] = t->root;
+ t->path_table_size = 10; /* root directory record */
+ cur = 1;
+ for (i = 0; i < t->dirlist_len; i++) {
+ struct ecma119_tree_node *dir = t->pathlist[i];
+ for (j = 0; j < dir->dir.nchildren; j++) {
+ struct ecma119_tree_node *ch = dir->dir.children[j];
+ if (ch->type == ECMA119_DIR) {
+ size_t len = 8 + strlen(ch->name);
+ t->pathlist[cur++] = ch;
+ t->path_table_size += len + len % 2;
+ }
+ }
+ }
+
+ t->curblock = 16 /* system area */
+ + 1 /* volume desc */
+ + 1; /* volume desc terminator */
+
+ if (t->joliet) /* supplementary vol desc */
+ t->curblock += div_up (2048, t->block_size);
+
+ t->l_path_table_pos = t->curblock;
+ t->curblock += div_up(t->path_table_size, t->block_size);
+ t->m_path_table_pos = t->curblock;
+ t->curblock += div_up(t->path_table_size, t->block_size);
+ if (t->joliet) {
+ joliet_prepare_path_tables(t);
+ t->l_path_table_pos_joliet = t->curblock;
+ t->curblock += div_up(t->path_table_size_joliet, t->block_size);
+ t->m_path_table_pos_joliet = t->curblock;
+ t->curblock += div_up(t->path_table_size_joliet, t->block_size);
+ }
+
+ calc_dir_pos(t, t->root);
+ if (t->joliet)
+ joliet_calc_dir_pos(t, t->joliet_root);
+ calc_file_pos(t, t->root);
+ if (t->joliet)
+ joliet_update_file_pos (t, t->joliet_root);
+
+ if (t->rockridge) {
+ susp_finalize(t, t->root);
+ rrip_finalize(t, t->root);
+ }
+
+ t->total_size = t->curblock * t->block_size;
+ t->vol_space_size = t->curblock;
+
+ /* prepare for writing */
+ t->curblock = 0;
+ t->state = ECMA119_WRITE_SYSTEM_AREA;
+
+ return t;
+}
+
+static int
+is_joliet_state(enum ecma119_write_state state)
+{
+ return state == ECMA119_WRITE_SUP_VOL_DESC_JOLIET
+ || state == ECMA119_WRITE_L_PATH_TABLE_JOLIET
+ || state == ECMA119_WRITE_M_PATH_TABLE_JOLIET
+ || state == ECMA119_WRITE_DIR_RECORDS_JOLIET;
+}
+
+static void
+next_state(struct ecma119_write_target *t)
+{
+ t->state++;
+ while (!t->joliet && is_joliet_state(t->state))
+ t->state++;
+
+ printf ("now in state %d, curblock=%d\n", (int)t->state, (int)t->curblock);
+}
+
+static void
+wr_system_area(struct ecma119_write_target *t, uint8_t *buf)
+{
+ memset(buf, 0, t->block_size);
+ if (t->curblock == 15) {
+ next_state(t);
+ }
+}
+static void
+wr_pri_vol_desc(struct ecma119_write_target *t, uint8_t *buf)
+{
+ ecma119_start_chunking(t, write_pri_vol_desc, 2048, buf);
+}
+
+static void
+wr_vol_desc_term(struct ecma119_write_target *t, uint8_t *buf)
+{
+ ecma119_start_chunking(t, write_vol_desc_terminator, 2048, buf);
+}
+
+static void
+wr_l_path_table(struct ecma119_write_target *t, uint8_t *buf)
+{
+ ecma119_start_chunking(t, write_l_path_table, t->path_table_size, buf);
+}
+
+static void
+wr_m_path_table(struct ecma119_write_target *t, uint8_t *buf)
+{
+ ecma119_start_chunking(t, write_m_path_table, t->path_table_size, buf);
+}
+
+static void
+wr_dir_records(struct ecma119_write_target *t, uint8_t *buf)
+{
+ ecma119_start_chunking(t, write_dirs, t->total_dir_size, buf);
+}
+
+static void
+wr_files(struct ecma119_write_target *t, uint8_t *buf)
+{
+ struct state_files *f_st = &t->state_files;
+ size_t nread;
+ struct ecma119_tree_node *f = t->filelist[f_st->file];
+ const char *path = f->iso_self->loc.path;
+
+ if (!f_st->fd) {
+ f_st->data_len = f->iso_self->attrib.st_size;
+ f_st->fd = fopen(path, "r");
+ if (!f_st->fd)
+ err(1, "couldn't open %s for reading", path);
+ assert(t->curblock == f->block);
+ }
+
+ nread = fread(buf, 1, t->block_size, f_st->fd);
+ f_st->pos += t->block_size;
+ if (nread < 0)
+ warn("problem reading from %s", path);
+ else if (nread != t->block_size && f_st->pos < f_st->data_len)
+ warnx("incomplete read from %s", path);
+ if (f_st->pos >= f_st->data_len) {
+ fclose(f_st->fd);
+ f_st->fd = 0;
+ f_st->pos = 0;
+ f_st->file++;
+ if (f_st->file >= t->filelist_len)
+ next_state(t);
+ }
+}
+
+static void
+write_pri_vol_desc(struct ecma119_write_target *t, uint8_t *buf)
+{
+ struct ecma119_pri_vol_desc *vol = (struct ecma119_pri_vol_desc*)buf;
+ struct iso_volume *volume = t->volset->volume[t->volnum];
+ char *vol_id = str2ascii(volume->volume_id);
+ char *pub_id = str2ascii(volume->publisher_id);
+ char *data_id = str2ascii(volume->data_preparer_id);
+ char *volset_id = str2ascii(t->volset->volset_id);
+
+ vol->vol_desc_type[0] = 1;
+ memcpy(vol->std_identifier, "CD001", 5);
+ vol->vol_desc_version[0] = 1;
+ memcpy(vol->system_id, "SYSID", 5);
+ if (vol_id)
+ strncpy((char*)vol->volume_id, vol_id, 32);
+ iso_bb(vol->vol_space_size, t->vol_space_size, 4);
+ iso_bb(vol->vol_set_size, t->volset->volset_size, 2);
+ iso_bb(vol->vol_seq_number, t->volnum + 1, 2);
+ iso_bb(vol->block_size, t->block_size, 2);
+ iso_bb(vol->path_table_size, t->path_table_size, 4);
+ iso_lsb(vol->l_path_table_pos, t->l_path_table_pos, 4);
+ iso_msb(vol->m_path_table_pos, t->m_path_table_pos, 4);
+
+ write_one_dir_record(t, t->root, 3, vol->root_dir_record);
+
+ strncpy((char*)vol->vol_set_id, volset_id, 128);
+ strncpy((char*)vol->publisher_id, pub_id, 128);
+ strncpy((char*)vol->data_prep_id, data_id, 128);
+ strncpy((char*)vol->application_id, "APPID", 128);
+
+ iso_datetime_17(vol->vol_creation_time, t->now);
+ iso_datetime_17(vol->vol_modification_time, t->now);
+ iso_datetime_17(vol->vol_effective_time, t->now);
+ vol->file_structure_version[0] = 1;
+
+ free(vol_id);
+ free(volset_id);
+ free(pub_id);
+ free(data_id);
+}
+
+static void
+write_vol_desc_terminator(struct ecma119_write_target *t, uint8_t *buf)
+{
+ struct ecma119_vol_desc_terminator *vol =
+ (struct ecma119_vol_desc_terminator*) buf;
+
+ vol->vol_desc_type[0] = 255;
+ memcpy(vol->std_identifier, "CD001", 5);
+ vol->vol_desc_version[0] = 1;
+}
+
+static void
+write_path_table(struct ecma119_write_target *t, int l_type, uint8_t *buf)
+{
+ void (*write_int)(uint8_t*, uint32_t, int) = l_type ? iso_lsb
+ : iso_msb;
+ size_t i;
+ struct ecma119_path_table_record *rec;
+ struct ecma119_tree_node *dir;
+ int parent = 0;
+
+ for (i = 0; i < t->dirlist_len; i++) {
+ dir = t->pathlist[i];
+ while ((i) && t->pathlist[parent] != dir->parent)
+ parent++;
+ assert(parent < i || i == 0);
+
+ rec = (struct ecma119_path_table_record*) buf;
+ rec->len_di[0] = dir->parent ? (uint8_t) strlen(dir->name) : 1;
+ rec->len_xa[0] = 0;
+ write_int(rec->block, dir->block, 4);
+ write_int(rec->parent, parent + 1, 2);
+ if (dir->parent)
+ memcpy(rec->dir_id, dir->name, rec->len_di[0]);
+ buf += 8 + rec->len_di[0] + (rec->len_di[0] % 2);
+ }
+}
+
+static void
+write_l_path_table(struct ecma119_write_target *t, uint8_t *buf)
+{
+ write_path_table(t, 1, buf);
+}
+
+static void
+write_m_path_table(struct ecma119_write_target *t, uint8_t *buf)
+{
+ write_path_table(t, 0, buf);
+}
+
+/* if file_id is >= 0, we use it instead of the filename. As a magic number,
+ * file_id == 3 means that we are writing the root directory record (in order
+ * to distinguish it from the "." entry in the root directory) */
+static void
+write_one_dir_record(struct ecma119_write_target *t,
+ struct ecma119_tree_node *node,
+ int file_id,
+ uint8_t *buf)
+{
+ uint8_t len_dr = (file_id >= 0) ? 34 : node->dirent_len;
+ uint8_t len_fi = (file_id >= 0) ? 1 : strlen(node->name);
+ uint8_t f_id = (uint8_t) ((file_id == 3) ? 0 : file_id);
+ uint8_t *name = (file_id >= 0) ? &f_id : (uint8_t*)node->name;
+ uint32_t len = (node->type == ECMA119_DIR) ? node->dir.len
+ : node->file.real_me ? 0 : node->iso_self->attrib.st_size;
+ struct ecma119_dir_record *rec = (struct ecma119_dir_record*)buf;
+
+ /* we don't write out susp fields for the root node */
+ if (t->rockridge) {
+ if (file_id == 0) {
+ susp_write(t, &node->dir.self_susp, &buf[len_dr]);
+ len_dr += node->dir.self_susp.non_CE_len;
+ } else if (file_id == 1) {
+ susp_write(t, &node->dir.parent_susp, &buf[len_dr]);
+ len_dr += node->dir.parent_susp.non_CE_len;
+ } else if (file_id < 0) {
+ susp_write(t, &node->susp, &buf[len_dr]);
+ len_dr += node->susp.non_CE_len;
+ }
+ }
+ if (file_id == 1 && node->parent)
+ node = node->parent;
+
+ rec->len_dr[0] = len_dr;
+ iso_bb(rec->block, node->block, 4);
+ iso_bb(rec->length, len, 4);
+ iso_datetime_7(rec->recording_time, t->now);
+ rec->flags[0] = (node->type == ECMA119_DIR) ? 2 : 0;
+ iso_bb(rec->vol_seq_number, t->volnum + 1, 2);
+ rec->len_fi[0] = len_fi;
+ memcpy(rec->file_id, name, len_fi);
+}
+
+static void
+write_one_dir(struct ecma119_write_target *t,
+ struct ecma119_tree_node *dir,
+ uint8_t *buf)
+{
+ size_t i;
+ uint8_t *orig_buf = buf;
+
+ assert(dir->type == ECMA119_DIR);
+ /* write the "." and ".." entries first */
+ write_one_dir_record(t, dir, 0, buf);
+ buf += ((struct ecma119_dir_record*) buf)->len_dr[0];
+
+ write_one_dir_record(t, dir, 1, buf);
+ buf += ((struct ecma119_dir_record*) buf)->len_dr[0];
+
+ for (i = 0; i < dir->dir.nchildren; i++) {
+ write_one_dir_record(t, dir->dir.children[i], -1, buf);
+ buf += ((struct ecma119_dir_record*) buf)->len_dr[0];
+ }
+
+ /* write the susp continuation areas */
+ if (t->rockridge) {
+ susp_write_CE(t, &dir->dir.self_susp, buf);
+ buf += dir->dir.self_susp.CE_len;
+ susp_write_CE(t, &dir->dir.parent_susp, buf);
+ buf += dir->dir.parent_susp.CE_len;
+ for (i = 0; i < dir->dir.nchildren; i++) {
+ susp_write_CE(t, &dir->dir.children[i]->susp, buf);
+ buf += dir->dir.children[i]->susp.CE_len;
+ }
+ }
+ assert (buf - orig_buf == dir->dir.len + dir->dir.CE_len);
+}
+
+static void
+write_dirs(struct ecma119_write_target *t, uint8_t *buf)
+{
+ size_t i;
+ struct ecma119_tree_node *dir;
+ for (i = 0; i < t->dirlist_len; i++) {
+ dir = t->dirlist[i];
+ write_one_dir(t, dir, buf);
+ buf += round_up(dir->dir.len + dir->dir.CE_len, t->block_size);
+ }
+}
+
+void
+ecma119_start_chunking(struct ecma119_write_target *t,
+ write_fn writer,
+ off_t data_size,
+ uint8_t *buf)
+{
+ if (data_size != t->state_data_size) {
+ data_size = round_up(data_size, t->block_size);
+ t->state_data = realloc(t->state_data, data_size);
+ t->state_data_size = data_size;
+ }
+ memset(t->state_data, 0, t->state_data_size);
+ t->state_data_off = 0;
+ t->state_data_valid = 1;
+ writer(t, t->state_data);
+ write_data_chunk(t, buf);
+}
+
+static void
+write_data_chunk(struct ecma119_write_target *t, uint8_t *buf)
+{
+ memcpy(buf, t->state_data + t->state_data_off, t->block_size);
+ t->state_data_off += t->block_size;
+ if (t->state_data_off >= t->state_data_size) {
+ assert (t->state_data_off <= t->state_data_size);
+ t->state_data_valid = 0;
+ next_state(t);
+ }
+}
+
+static int
+bs_read(struct burn_source *bs, unsigned char *buf, int size)
+{
+ struct ecma119_write_target *t = (struct ecma119_write_target*)bs->data;
+ if (size != t->block_size) {
+ warnx("you must read data in block-sized chunks (%d bytes)",
+ (int)t->block_size);
+ return 0;
+ } else if (t->curblock >= t->vol_space_size) {
+ return 0;
+ }
+ if (t->state_data_valid)
+ write_data_chunk(t, buf);
+ else
+ writers[t->state](t, buf);
+ t->curblock++;
+ return size;
+}
+
+static off_t
+bs_get_size(struct burn_source *bs)
+{
+ struct ecma119_write_target *t = (struct ecma119_write_target*)bs->data;
+ return t->total_size;
+}
+
+static void
+bs_free_data(struct burn_source *bs)
+{
+ struct ecma119_write_target *t = (struct ecma119_write_target*)bs->data;
+ ecma119_tree_free(t->root);
+ free(t->dirlist);
+ free(t->pathlist);
+ free(t->dirlist_joliet);
+ free(t->pathlist_joliet);
+ free(t->filelist);
+ free(t->state_data);
+ if (t->state_files.fd)
+ fclose(t->state_files.fd);
+}
+
+struct burn_source *iso_source_new_ecma119(struct iso_volset *volset,
+ int volnum,
+ int level,
+ int flags)
+{
+ struct burn_source *ret = calloc(1, sizeof(struct burn_source));
+ ret->refcount = 1;
+ ret->read = bs_read;
+ ret->get_size = bs_get_size;
+ ret->free_data = bs_free_data;
+ ret->data = ecma119_target_new(volset, volnum, level, flags);
+ return ret;
+}
diff --git a/tags/ZeroTwoTwo/libisofs/ecma119.h b/tags/ZeroTwoTwo/libisofs/ecma119.h
new file mode 100755
index 00000000..79505509
--- /dev/null
+++ b/tags/ZeroTwoTwo/libisofs/ecma119.h
@@ -0,0 +1,267 @@
+/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */
+/* vim: set noet ts=8 sts=8 sw=8 : */
+
+/**
+ * \file ecma119.h
+ *
+ * Structures and definitions used for writing an emca119 (ISO9660) compatible
+ * volume.
+ */
+
+#ifndef LIBISO_ECMA119_H
+#define LIBISO_ECMA119_H
+
+#include
+#include
+#include /* for FILE */
+#include
+#include "susp.h"
+
+struct ecma119_tree_node;
+struct joliet_tree_node;
+
+/**
+ * The possible states that the ecma119 writer can be in.
+ */
+enum ecma119_write_state
+{
+ ECMA119_WRITE_BEFORE,
+
+ ECMA119_WRITE_SYSTEM_AREA,
+ ECMA119_WRITE_PRI_VOL_DESC,
+ ECMA119_WRITE_SUP_VOL_DESC_JOLIET,
+ ECMA119_WRITE_VOL_DESC_TERMINATOR,
+ ECMA119_WRITE_L_PATH_TABLE,
+ ECMA119_WRITE_M_PATH_TABLE,
+ ECMA119_WRITE_L_PATH_TABLE_JOLIET,
+ ECMA119_WRITE_M_PATH_TABLE_JOLIET,
+ ECMA119_WRITE_DIR_RECORDS,
+ ECMA119_WRITE_DIR_RECORDS_JOLIET,
+ ECMA119_WRITE_FILES,
+
+ ECMA119_WRITE_DONE
+};
+
+/**
+ * Data describing the state of the ecma119 writer. Everything here should be
+ * considered private!
+ */
+struct ecma119_write_target
+{
+ struct ecma119_tree_node *root;
+ struct joliet_tree_node *joliet_root;
+ struct iso_volset *volset;
+ int volnum;
+
+ time_t now; /**< Time at which writing began. */
+ off_t total_size; /**< Total size of the output. This only
+ * includes the current volume. */
+ uint32_t vol_space_size;
+
+ unsigned int rockridge:1;
+ unsigned int joliet:1;
+ unsigned int iso_level:2;
+
+ int curblock;
+ uint16_t block_size;
+ uint32_t path_table_size;
+ uint32_t path_table_size_joliet;
+ uint32_t l_path_table_pos;
+ uint32_t m_path_table_pos;
+ uint32_t l_path_table_pos_joliet;
+ uint32_t m_path_table_pos_joliet;
+ uint32_t total_dir_size;
+ uint32_t total_dir_size_joliet;
+
+ struct ecma119_tree_node **dirlist;
+ /**< A pre-order list of directories
+ * (this is the order in which we write
+ * out directory records).
+ */
+ struct ecma119_tree_node **pathlist;
+ /**< A breadth-first list of
+ * directories. This is used for
+ * writing out the path tables.
+ */
+ size_t dirlist_len; /**< The length of the previous 2 lists.
+ */
+
+ struct ecma119_tree_node **filelist;
+ /**< A pre-order list of files with
+ * non-NULL paths and non-zero sizes.
+ */
+ size_t filelist_len; /* Length of the previous list. */
+
+ int curfile; /**< Used as a helper field for writing
+ out filelist and dirlist */
+
+ /* Joliet versions of the above lists. Since Joliet doesn't require
+ * directory relocation, the order of these lists might be different
+ * from the lists above (but they will be the same length).
+ */
+ struct joliet_tree_node **dirlist_joliet;
+ struct joliet_tree_node **pathlist_joliet;
+
+ enum ecma119_write_state state; /* The current state of the writer. */
+
+ /* Most writers work by
+ * 1) making sure state_data is big enough for their data
+ * 2) writing _all_ their data into state_data
+ * 3) relying on write_data_chunk to write the data block
+ * by block.
+ */
+ uint8_t *state_data;
+ off_t state_data_size;
+ off_t state_data_off;
+ int state_data_valid;
+
+ /* for writing out files */
+ struct state_files {
+ off_t pos; /* The number of bytes we have written
+ * so far in the current file.
+ */
+ off_t data_len;/* The number of bytes in the currently
+ * open file.
+ */
+ FILE *fd; /* The currently open file. */
+ int file; /* The index in filelist that we are
+ * currently writing (or about to write). */
+ } state_files;
+};
+
+/**
+ * Create a new ecma119_write_target from the given volume number of the
+ * given volume set.
+ *
+ * \pre \p volnum is less than \p volset-\>volset_size.
+ * \post For each node in the tree, writer_data has been allocated.
+ * \post The directory heirarchy has been reorganised to be ecma119-compatible.
+ */
+struct ecma119_write_target *ecma119_target_new(struct iso_volset *volset,
+ int volnum,
+ int level,
+ int flags);
+
+#define BP(a,b) [(b) - (a) + 1]
+
+struct ecma119_pri_vol_desc
+{
+ uint8_t vol_desc_type BP(1, 1);
+ uint8_t std_identifier BP(2, 6);
+ uint8_t vol_desc_version BP(7, 7);
+ uint8_t unused1 BP(8, 8);
+ uint8_t system_id BP(9, 40);
+ uint8_t volume_id BP(41, 72);
+ uint8_t unused2 BP(73, 80);
+ uint8_t vol_space_size BP(81, 88);
+ uint8_t unused3 BP(89, 120);
+ uint8_t vol_set_size BP(121, 124);
+ uint8_t vol_seq_number BP(125, 128);
+ uint8_t block_size BP(129, 132);
+ uint8_t path_table_size BP(133, 140);
+ uint8_t l_path_table_pos BP(141, 144);
+ uint8_t opt_l_path_table_pos BP(145, 148);
+ uint8_t m_path_table_pos BP(149, 152);
+ uint8_t opt_m_path_table_pos BP(153, 156);
+ uint8_t root_dir_record BP(157, 190);
+ uint8_t vol_set_id BP(191, 318);
+ uint8_t publisher_id BP(319, 446);
+ uint8_t data_prep_id BP(447, 574);
+ uint8_t application_id BP(575, 702);
+ uint8_t copyright_file_id BP(703, 739);
+ uint8_t abstract_file_id BP(740, 776);
+ uint8_t bibliographic_file_id BP(777, 813);
+ uint8_t vol_creation_time BP(814, 830);
+ uint8_t vol_modification_time BP(831, 847);
+ uint8_t vol_expiration_time BP(848, 864);
+ uint8_t vol_effective_time BP(865, 881);
+ uint8_t file_structure_version BP(882, 882);
+ uint8_t reserved1 BP(883, 883);
+ uint8_t app_use BP(884, 1395);
+ uint8_t reserved2 BP(1396, 2048);
+};
+
+struct ecma119_sup_vol_desc
+{
+ uint8_t vol_desc_type BP(1, 1);
+ uint8_t std_identifier BP(2, 6);
+ uint8_t vol_desc_version BP(7, 7);
+ uint8_t vol_flags BP(8, 8);
+ uint8_t system_id BP(9, 40);
+ uint8_t volume_id BP(41, 72);
+ uint8_t unused2 BP(73, 80);
+ uint8_t vol_space_size BP(81, 88);
+ uint8_t esc_sequences BP(89, 120);
+ uint8_t vol_set_size BP(121, 124);
+ uint8_t vol_seq_number BP(125, 128);
+ uint8_t block_size BP(129, 132);
+ uint8_t path_table_size BP(133, 140);
+ uint8_t l_path_table_pos BP(141, 144);
+ uint8_t opt_l_path_table_pos BP(145, 148);
+ uint8_t m_path_table_pos BP(149, 152);
+ uint8_t opt_m_path_table_pos BP(153, 156);
+ uint8_t root_dir_record BP(157, 190);
+ uint8_t vol_set_id BP(191, 318);
+ uint8_t publisher_id BP(319, 446);
+ uint8_t data_prep_id BP(447, 574);
+ uint8_t application_id BP(575, 702);
+ uint8_t copyright_file_id BP(703, 739);
+ uint8_t abstract_file_id BP(740, 776);
+ uint8_t bibliographic_file_id BP(777, 813);
+ uint8_t vol_creation_time BP(814, 830);
+ uint8_t vol_modification_time BP(831, 847);
+ uint8_t vol_expiration_time BP(848, 864);
+ uint8_t vol_effective_time BP(865, 881);
+ uint8_t file_structure_version BP(882, 882);
+ uint8_t reserved1 BP(883, 883);
+ uint8_t app_use BP(884, 1395);
+ uint8_t reserved2 BP(1396, 2048);
+};
+
+struct ecma119_vol_desc_terminator
+{
+ uint8_t vol_desc_type BP(1, 1);
+ uint8_t std_identifier BP(2, 6);
+ uint8_t vol_desc_version BP(7, 7);
+ uint8_t reserved BP(8, 2048);
+};
+
+struct ecma119_dir_record
+{
+ uint8_t len_dr BP(1, 1);
+ uint8_t len_xa BP(2, 2);
+ uint8_t block BP(3, 10);
+ uint8_t length BP(11, 18);
+ uint8_t recording_time BP(19, 25);
+ uint8_t flags BP(26, 26);
+ uint8_t file_unit_size BP(27, 27);
+ uint8_t interleave_gap_size BP(28, 28);
+ uint8_t vol_seq_number BP(29, 32);
+ uint8_t len_fi BP(33, 33);
+ uint8_t file_id BP(34, 34); /* 34 to 33+len_fi */
+ /* padding field (if len_fi is even) */
+ /* system use (len_dr - len_su + 1 to len_dr) */
+};
+
+struct ecma119_path_table_record
+{
+ uint8_t len_di BP(1, 1);
+ uint8_t len_xa BP(2, 2);
+ uint8_t block BP(3, 6);
+ uint8_t parent BP(7, 8);
+ uint8_t dir_id BP(9, 9); /* 9 to 8+len_di */
+ /* padding field (if len_di is odd) */
+};
+
+/**
+ * A utility function for writers that want to write their data all at once
+ * rather than block-by-block. This creates a buffer of size \p size, passes
+ * it to the given writer, then hands out block-sized chunks.
+ */
+void
+ecma119_start_chunking(struct ecma119_write_target *t,
+ void (*)(struct ecma119_write_target*, uint8_t*),
+ off_t size,
+ uint8_t *buf);
+
+#endif /* LIBISO_ECMA119_H */
diff --git a/tags/ZeroTwoTwo/libisofs/ecma119_tree.c b/tags/ZeroTwoTwo/libisofs/ecma119_tree.c
new file mode 100644
index 00000000..0cbd387a
--- /dev/null
+++ b/tags/ZeroTwoTwo/libisofs/ecma119_tree.c
@@ -0,0 +1,312 @@
+/* vim: set noet ts=8 sts=8 sw=8 : */
+
+#include
+#include
+#include
+#include
+
+#include "ecma119.h"
+#include "ecma119_tree.h"
+#include "tree.h"
+#include "util.h"
+
+static size_t calc_dirent_len(struct ecma119_tree_node *n)
+{
+ int ret = n->name ? strlen(n->name) + 33 : 34;
+ if (ret % 2) ret++;
+ return ret;
+}
+
+static struct ecma119_tree_node*
+create_dir(struct ecma119_write_target *t,
+ struct ecma119_tree_node *parent,
+ struct iso_tree_node *iso)
+{
+ struct ecma119_tree_node *ret;
+
+ assert(t && (!parent || parent->type == ECMA119_DIR)
+ && iso && S_ISDIR(iso->attrib.st_mode));
+
+ ret = calloc(1, sizeof(struct ecma119_tree_node));
+ ret->name = iso->name ? ((t->iso_level == 1) ? iso_1_dirid(iso->name)
+ : iso_2_dirid(iso->name))
+ : NULL;
+ ret->dirent_len = calc_dirent_len(ret);
+ ret->iso_self = iso;
+ ret->target = t;
+ ret->type = ECMA119_DIR;
+ ret->parent = ret->dir.real_parent = parent;
+ ret->dir.depth = parent ? parent->dir.depth + 1 : 1;
+ ret->dir.nchildren = iso->nchildren;
+ ret->dir.children = calloc(1, sizeof(void*) * iso->nchildren);
+ return ret;
+}
+
+static struct ecma119_tree_node*
+create_file(struct ecma119_write_target *t,
+ struct ecma119_tree_node *parent,
+ struct iso_tree_node *iso)
+{
+ struct ecma119_tree_node *ret;
+
+ assert(t && iso && parent && parent->type == ECMA119_DIR);
+
+ ret = calloc(1, sizeof(struct ecma119_tree_node));
+ ret->name = iso->name ? ((t->iso_level == 1) ? iso_1_fileid(iso->name)
+ : iso_2_fileid(iso->name))
+ : NULL;
+ ret->dirent_len = calc_dirent_len(ret);
+ ret->parent = parent;
+ ret->iso_self = iso;
+ ret->target = t;
+ ret->type = ECMA119_FILE;
+ return ret;
+}
+
+static struct ecma119_tree_node*
+create_tree(struct ecma119_write_target *t,
+ struct ecma119_tree_node *parent,
+ struct iso_tree_node *iso)
+{
+ struct ecma119_tree_node *ret;
+ size_t i;
+
+ assert(t && iso);
+
+ ret = (S_ISDIR(iso->attrib.st_mode) ? create_dir : create_file)
+ (t, parent, iso);
+ for (i = 0; i < iso->nchildren; i++) {
+ ret->dir.children[i] = create_tree(t, ret, iso->children[i]);
+ }
+ return ret;
+}
+
+void
+ecma119_tree_free(struct ecma119_tree_node *root)
+{
+ size_t i;
+
+ if (root->type == ECMA119_DIR) {
+ for (i=0; i < root->dir.nchildren; i++) {
+ ecma119_tree_free(root->dir.children[i]);
+ }
+ free(root->dir.children);
+ }
+ free(root->name);
+ free(root);
+}
+
+static size_t
+max_child_name_len(struct ecma119_tree_node *root)
+{
+ size_t ret = 0, i;
+
+ assert(root->type == ECMA119_DIR);
+ for (i=0; i < root->dir.nchildren; i++) {
+ size_t len = strlen(root->dir.children[i]->name);
+ ret = MAX(ret, len);
+ }
+ return ret;
+}
+
+static void
+reparent(struct ecma119_tree_node *child,
+ struct ecma119_tree_node *parent)
+{
+ int found = 0;
+ size_t i;
+ struct ecma119_tree_node *placeholder;
+
+ assert(child && parent && parent->type == ECMA119_DIR && child->parent);
+
+ /* replace the child in the original parent with a placeholder */
+ for (i=0; i < child->parent->dir.nchildren; i++) {
+ if (child->parent->dir.children[i] == child) {
+ placeholder = create_file(child->target,
+ child->parent,
+ child->iso_self);
+ placeholder->file.real_me = child;
+ child->parent->dir.children[i] = placeholder;
+ found = 1;
+ break;
+ }
+ }
+ assert(found);
+
+ /* add the child to its new parent */
+ child->parent = parent;
+ parent->dir.nchildren++;
+ parent->dir.children = realloc( parent->dir.children,
+ sizeof(void*) * parent->dir.nchildren );
+ parent->dir.children[parent->dir.nchildren-1] = child;
+}
+
+/**
+ * Reorder the tree, if necessary, to ensure that
+ * - the depth is at most 8
+ * - each path length is at most 255 characters
+ */
+static void
+reorder_tree(struct ecma119_tree_node *root,
+ struct ecma119_tree_node *cur)
+{
+ size_t max_path;
+
+ assert(root && cur && cur->type == ECMA119_DIR);
+
+ cur->dir.depth = cur->parent ? cur->parent->dir.depth + 1 : 1;
+ cur->dir.path_len = cur->parent ? cur->parent->dir.path_len
+ + strlen(cur->name) : 0;
+ max_path = cur->dir.path_len + cur->dir.depth + max_child_name_len(cur);
+
+ if (cur->dir.depth > 8 || max_path > 255) {
+ reparent(cur, root);
+ /* we are appended to the root's children now, so there is no
+ * need to recurse (the root will hit us again) */
+ } else {
+ size_t i;
+
+ for (i=0; i < cur->dir.nchildren; i++) {
+ if (cur->dir.children[i]->type == ECMA119_DIR)
+ reorder_tree(root, cur->dir.children[i]);
+ }
+ }
+}
+
+static int
+cmp_node(const void *f1, const void *f2)
+{
+ struct ecma119_tree_node *f = *((struct ecma119_tree_node**)f1);
+ struct ecma119_tree_node *g = *((struct ecma119_tree_node**)f2);
+ return strcmp(f->name, g->name);
+}
+
+static void
+sort_tree(struct ecma119_tree_node *root)
+{
+ size_t i;
+
+ assert(root && root->type == ECMA119_DIR);
+
+ qsort(root->dir.children, root->dir.nchildren, sizeof(void*), cmp_node);
+ for (i=0; i < root->dir.nchildren; i++) {
+ if (root->dir.children[i]->type == ECMA119_DIR)
+ sort_tree(root->dir.children[i]);
+ }
+}
+
+/**
+ * Change num_change characters of the given filename in order to ensure the
+ * name is unique. If the name is short enough (depending on the ISO level),
+ * we can append the characters instead of changing them.
+ *
+ * \p seq_num is the index of this file in the sequence of identical filenames.
+ *
+ * For example, seq_num=3, num_change=2, name="HELLOTHERE.TXT" changes name to
+ * "HELLOTHE03.TXT"
+ */
+static void
+mangle_name(char **name, int num_change, int level, int seq_num)
+{
+ char *dot = strrchr(*name, '.');
+ char *semi = strrchr(*name, ';');
+ size_t len = strlen(*name);
+ char base[len+1], ext[len+1];
+ char fmt[12];
+ size_t baselen, extlen;
+
+ if (num_change >= len) {
+ return;
+ }
+ strncpy(base, *name, len+1);
+ if (dot) {
+ base[dot - *name] = '\0';
+ strncpy(ext, dot+1, len+1);
+ if (semi) {
+ ext[semi - dot - 1] = '\0';
+ }
+ } else {
+ base[len-2] = '\0';
+ ext[0] = '\0';
+ }
+ baselen = strlen(base);
+ extlen = strlen(ext);
+ if (level == 1 && baselen + num_change > 8) {
+ base[8 - num_change] = '\0';
+ } else if (level != 1 && baselen + extlen + num_change > 30) {
+ base[30 - extlen - num_change] = '\0';
+ }
+
+ sprintf(fmt, "%%s%%0%1dd.%%s;1", num_change);
+ *name = realloc(*name, baselen + extlen + num_change + 4);
+ sprintf(*name, fmt, base, seq_num, ext);
+}
+
+static void
+mangle_all(struct ecma119_tree_node *dir)
+{
+ size_t i, j, k;
+ struct ecma119_dir_info d = dir->dir;
+ size_t n_change;
+ int changed;
+
+ assert(dir->type == ECMA119_DIR);
+ do {
+ changed = 0;
+ for (i=0; i < d.nchildren; i++) {
+ /* find the number of consecutive equal names */
+ j = 1;
+ while ( i+j < d.nchildren &&
+ !strcmp(d.children[i]->name,
+ d.children[i+j]->name) )
+ j++;
+ if (j == 1) continue;
+
+ /* mangle the names */
+ changed = 1;
+ n_change = j / 10 + 1;
+ for (k=0; k < j; k++) {
+ mangle_name(&(d.children[i+k]->name),
+ n_change,
+ dir->target->iso_level,
+ k);
+ d.children[i+k]->dirent_len =
+ calc_dirent_len(d.children[i+k]);
+ }
+
+ /* skip ahead by the number of mangled names */
+ i += j - 1;
+ }
+ } while (changed);
+
+ for (i=0; i < d.nchildren; i++) {
+ if (d.children[i]->type == ECMA119_DIR)
+ mangle_all(d.children[i]);
+ }
+}
+
+struct ecma119_tree_node*
+ecma119_tree_create(struct ecma119_write_target *t,
+ struct iso_tree_node *iso_root)
+{
+ t->root = create_tree(t, NULL, iso_root);
+ reorder_tree(t->root, t->root);
+ sort_tree(t->root);
+ mangle_all(t->root);
+ return t->root;
+}
+
+void
+ecma119_tree_print(struct ecma119_tree_node *root, int spaces)
+{
+ size_t i;
+ char sp[spaces+1];
+
+ memset(sp, ' ', spaces);
+ sp[spaces] = '\0';
+
+ printf("%s%s\n", sp, root->name);
+ if (root->type == ECMA119_DIR)
+ for (i=0; i < root->dir.nchildren; i++)
+ ecma119_tree_print(root->dir.children[i], spaces+2);
+}
diff --git a/tags/ZeroTwoTwo/libisofs/ecma119_tree.h b/tags/ZeroTwoTwo/libisofs/ecma119_tree.h
new file mode 100644
index 00000000..1d6023b8
--- /dev/null
+++ b/tags/ZeroTwoTwo/libisofs/ecma119_tree.h
@@ -0,0 +1,95 @@
+/* vim: set noet ts=8 sts=8 sw=8 : */
+
+/**
+ * \file ecma119_tree.h
+ *
+ * Declarations for creating, modifying and printing filesystem trees that
+ * are compatible with ecma119.
+ */
+
+#ifndef LIBISO_ECMA119_TREE_H
+#define LIBISO_ECMA119_TREE_H
+
+struct ecma119_write_target;
+
+enum {
+ ECMA119_FILE,
+ ECMA119_DIR
+};
+
+struct ecma119_dir_info {
+ struct susp_info self_susp; /**< susp entries for "." */
+ struct susp_info parent_susp; /**< susp entries for ".." */
+
+ size_t len; /**< sum of the lengths of children's
+ * Directory Records (including SU) */
+ size_t CE_len; /**< sum of the lengths of children's
+ * SUSP CE areas */
+
+ int depth;
+ size_t path_len; /**< The length of a path up to, and
+ * including, this directory. This
+ * cannot exceed 255. */
+ size_t nchildren;
+ struct ecma119_tree_node **children;
+
+ struct ecma119_tree_node *real_parent;
+ /**< The parent before relocation */
+};
+
+struct ecma119_file_info
+{
+ struct ecma119_tree_node *real_me;
+ /**< If this is non-NULL, the file is
+ * a placeholder for a relocated
+ * directory and this field points to
+ * that relocated directory.
+ */
+};
+
+/**
+ * A node for a tree containing all the information necessary for writing
+ * an ISO9660 volume.
+ */
+struct ecma119_tree_node
+{
+ char *name; /**< in ASCII, conforming to the
+ * current ISO level. */
+ size_t dirent_len; /**< Length of the directory record,
+ * not including SU. */
+ size_t block;
+
+ struct ecma119_tree_node *parent;
+ struct iso_tree_node *iso_self;
+ struct ecma119_write_target *target;
+
+ struct susp_info susp;
+
+ int type; /**< file or directory */
+ /* union {*/
+ struct ecma119_dir_info dir;
+ struct ecma119_file_info file;
+ /* };*/
+};
+
+/**
+ * Create a new ecma119_tree that corresponds to the tree represented by
+ * \p iso_root.
+ */
+struct ecma119_tree_node*
+ecma119_tree_create(struct ecma119_write_target *target,
+ struct iso_tree_node *iso_root);
+
+/**
+ * Free an ecma119 tree.
+ */
+void
+ecma119_tree_free(struct ecma119_tree_node *root);
+
+/**
+ * Print an ecma119 tree.
+ */
+void
+ecma119_tree_print(struct ecma119_tree_node *root, int spaces);
+
+#endif /* LIBISO_ECMA119_TREE_H */
diff --git a/tags/ZeroTwoTwo/libisofs/exclude.c b/tags/ZeroTwoTwo/libisofs/exclude.c
new file mode 100644
index 00000000..48e7fa21
--- /dev/null
+++ b/tags/ZeroTwoTwo/libisofs/exclude.c
@@ -0,0 +1,42 @@
+#include "hash.h"
+#include "exclude.h"
+
+static struct iso_hash_node *table[HASH_NODES]={0,};
+static int num=0;
+
+void
+iso_exclude_add_path(const char *path)
+{
+ if (!path)
+ return;
+
+ num += iso_hash_insert(table, path);
+}
+
+void
+iso_exclude_remove_path(const char *path)
+{
+ if (!num || !path)
+ return;
+
+ num -= iso_hash_remove(table, path);
+}
+
+void
+iso_exclude_empty(void)
+{
+ if (!num)
+ return;
+
+ iso_hash_empty(table);
+ num=0;
+}
+
+int
+iso_exclude_lookup(const char *path)
+{
+ if (!num || !path)
+ return 0;
+
+ return iso_hash_lookup(table, path);
+}
diff --git a/tags/ZeroTwoTwo/libisofs/exclude.h b/tags/ZeroTwoTwo/libisofs/exclude.h
new file mode 100644
index 00000000..319c4189
--- /dev/null
+++ b/tags/ZeroTwoTwo/libisofs/exclude.h
@@ -0,0 +1,12 @@
+#ifndef ISO_EXCLUDE_H
+#define ISO_EXCLUDE_H
+
+/**
+ * Add a path to ignore when adding a directory recursively.
+ *
+ * \param path The path, on the local filesystem, of the file.
+ */
+int
+iso_exclude_lookup(const char *path);
+
+#endif /* ISO_EXCLUDE */
diff --git a/tags/ZeroTwoTwo/libisofs/hash.c b/tags/ZeroTwoTwo/libisofs/hash.c
new file mode 100644
index 00000000..1976efa9
--- /dev/null
+++ b/tags/ZeroTwoTwo/libisofs/hash.c
@@ -0,0 +1,158 @@
+#include
+#include
+
+#include "hash.h"
+
+static unsigned int
+iso_hash_path(const char *path)
+{
+ unsigned int hash_num=0;
+ const char *c;
+
+ c=path;
+ while(*c)
+ hash_num = (hash_num << 15) + (hash_num << 3) + (hash_num >> 3) + *c++;
+
+ return hash_num % HASH_NODES;
+}
+
+int
+iso_hash_lookup(struct iso_hash_node **table, const char *path)
+{
+ struct iso_hash_node *node;
+ unsigned int hash_num;
+
+ hash_num = iso_hash_path(path);
+
+ node=table[hash_num];
+
+ if (!node)
+ return 0;
+
+ if (!strcmp(path, node->path))
+ return 1;
+
+ while (node->next) {
+ node=node->next;
+
+ if (!strcmp(path, node->path))
+ return 1;
+ }
+
+ return 0;
+ }
+
+static struct iso_hash_node*
+iso_hash_node_new (const char *path)
+{
+ struct iso_hash_node *node;
+
+ /*create an element to be inserted in the hash table */
+ node=malloc(sizeof(struct iso_hash_node));
+ node->path=strdup(path);
+ node->next=NULL;
+
+ return node;
+}
+
+int
+iso_hash_insert(struct iso_hash_node **table, const char *path)
+{
+ struct iso_hash_node *node;
+ unsigned int hash_num;
+
+ /* find the hash number */
+ hash_num = iso_hash_path(path);
+
+ /* insert it */
+ node = table[hash_num];
+
+ /* unfortunately, we can't safely consider that a path
+ * won't be twice in the hash table so make sure it
+ * doesn't already exists */
+ if (!node) {
+ table[hash_num]=iso_hash_node_new(path);
+ return 1;
+ }
+
+ /* if it's already in, we don't do anything */
+ if (!strcmp(path, node->path))
+ return 0;
+
+ while (node->next) {
+ node = node->next;
+
+ /* if it's already in, we don't do anything */
+ if (!strcmp (path, node->path))
+ return 0;
+ }
+
+ node->next = iso_hash_node_new(path);
+ return 1;
+}
+
+static void
+iso_hash_node_free(struct iso_hash_node *node)
+{
+ free(node->path);
+ free(node);
+}
+
+int
+iso_hash_remove(struct iso_hash_node **table, const char *path)
+{
+ unsigned int hash_num;
+ struct iso_hash_node *node;
+
+ hash_num = iso_hash_path(path);
+
+ node=table[hash_num];
+ if (!node)
+ return 0;
+
+ if (!strcmp(path, node->path)) {
+ table[hash_num]=node->next;
+ iso_hash_node_free(node);
+ return 1;
+ }
+
+ while (node->next) {
+ struct iso_hash_node *prev;
+
+ prev = node;
+ node = node->next;
+
+ if (!strcmp (path, node->path)) {
+ prev->next=node->next;
+ iso_hash_node_free(node);
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+void
+iso_hash_empty(struct iso_hash_node **table)
+{
+ int i;
+
+ for (i=0; i < HASH_NODES; i++) {
+ struct iso_hash_node *node;
+
+ node=table[i];
+ if (!node)
+ continue;
+
+ table[i]=NULL;
+
+ do {
+ struct iso_hash_node *next;
+
+ next=node->next;
+ iso_hash_node_free(node);
+ node=next;
+ } while (node);
+ }
+}
+
diff --git a/tags/ZeroTwoTwo/libisofs/hash.h b/tags/ZeroTwoTwo/libisofs/hash.h
new file mode 100644
index 00000000..2d691c78
--- /dev/null
+++ b/tags/ZeroTwoTwo/libisofs/hash.h
@@ -0,0 +1,46 @@
+#ifndef ISO_HASH_H
+#define ISO_HASH_H
+
+struct iso_hash_node {
+ struct iso_hash_node *next;
+ char *path;
+};
+
+#define HASH_NODES 128
+
+/**
+ * Searches in the hash table if the path exists.
+ *
+ *Â \param table The hash table.
+ * \param path The path of the file to look for.
+ *
+ * \return 1 if the path exists in the hash table, 0 otherwise.
+ */
+int iso_hash_lookup(struct iso_hash_node **table, const char *path);
+
+/**
+ * Insert a new path in the hash table.
+ *
+ * \param table The hash table.
+ *Â \param path The path of a file to add to the hash table.
+ *
+ * \return 1 if the file wasn't already in the hash table, 0 otherwise.
+ */
+int iso_hash_insert(struct iso_hash_node **table, const char *path);
+
+/**
+ * Remove a path from the hash table.
+ *
+ * \param table The hash table.
+ * \param path The path of a file to remove from the hash table.
+ *
+ * \return 1 if the file was found and removed, 0 otherwise.
+ */
+int iso_hash_remove(struct iso_hash_node **table, const char *path);
+
+/**
+ * Empty the hash table.
+ */
+void iso_hash_empty(struct iso_hash_node **table);
+
+#endif /* ISO_HASH_H */
diff --git a/tags/ZeroTwoTwo/libisofs/joliet.c b/tags/ZeroTwoTwo/libisofs/joliet.c
new file mode 100644
index 00000000..d64675b3
--- /dev/null
+++ b/tags/ZeroTwoTwo/libisofs/joliet.c
@@ -0,0 +1,379 @@
+/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */
+/* vim: set noet ts=8 sts=8 sw=8 : */
+
+#include "joliet.h"
+#include "ecma119.h"
+#include "ecma119_tree.h"
+#include "tree.h"
+#include "util.h"
+#include "volume.h"
+
+#include
+#include
+
+static struct joliet_tree_node*
+create_node(struct ecma119_write_target *t,
+ struct joliet_tree_node *parent,
+ struct iso_tree_node *iso)
+{
+ struct joliet_tree_node *ret =
+ calloc(1, sizeof(struct joliet_tree_node));
+
+ ret->name = iso_j_id(iso->name);
+ ret->dirent_len = 34 + (ret->name ? ucslen(ret->name) * 2 : 0);
+ ret->len = iso->attrib.st_size; /* for dirs, we'll change this */
+ ret->block = iso->block; /* only actually for files, not dirs */
+ ret->parent = parent;
+ ret->iso_self = iso;
+ ret->target = t;
+ ret->nchildren = iso->nchildren;
+ if (ret->nchildren)
+ ret->children = calloc(sizeof(void*), ret->nchildren);
+ return ret;
+}
+
+static struct joliet_tree_node*
+create_tree(struct ecma119_write_target *t,
+ struct joliet_tree_node *parent,
+ struct iso_tree_node *iso_root)
+{
+ struct joliet_tree_node *root = create_node(t, parent, iso_root);
+ size_t i;
+
+ for (i = 0; i < root->nchildren; i++) {
+ struct iso_tree_node *iso_ch = iso_root->children[i];
+ if (ISO_ISDIR(iso_ch))
+ root->children[i] = create_tree(t, root, iso_ch);
+ else
+ root->children[i] = create_node(t, root, iso_ch);
+ }
+ return root;
+}
+
+static int
+cmp_node(const void *f1, const void *f2)
+{
+ struct joliet_tree_node *f = *((struct joliet_tree_node**)f1);
+ struct joliet_tree_node *g = *((struct joliet_tree_node**)f2);
+ return ucscmp(f->name, g->name);
+}
+
+static void
+sort_tree(struct joliet_tree_node *root)
+{
+ size_t i;
+
+ assert(root && ISO_ISDIR(root->iso_self));
+
+ qsort(root->children, root->nchildren, sizeof(void*), cmp_node);
+ for (i = 0; i < root->nchildren; i++)
+ if (ISO_ISDIR(root->children[i]->iso_self))
+ sort_tree(root->children[i]);
+}
+
+void
+joliet_prepare_path_tables(struct ecma119_write_target *t)
+{
+ size_t cur, i, j;
+
+ t->pathlist_joliet[0] = t->joliet_root;
+ t->path_table_size_joliet = 10; /* root directory record */
+ cur = 1;
+
+ for (i = 0; i < t->dirlist_len; i++) {
+ struct joliet_tree_node *dir = t->pathlist_joliet[i];
+ for (j = 0; j < dir->nchildren; j++) {
+ struct joliet_tree_node *ch = dir->children[j];
+ if (ISO_ISDIR(ch->iso_self)) {
+ size_t len = 8 + ucslen(ch->name)*2;
+ t->pathlist_joliet[cur++] = ch;
+ t->path_table_size_joliet += len;
+ }
+ }
+ }
+}
+
+/**
+ * Calculate the size of each directory.
+ */
+void
+joliet_calc_dir_size(struct ecma119_write_target *t,
+ struct joliet_tree_node *root)
+{
+ size_t i;
+ struct joliet_tree_node *ch;
+
+ assert(root && ISO_ISDIR(root->iso_self));
+
+ root->len = 68; /* for "." and ".." entries */
+ for (i = 0; i < root->nchildren; i++) {
+ ch = root->children[i];
+ root->len += ch->dirent_len;
+ if (ISO_ISDIR(ch->iso_self))
+ joliet_calc_dir_size(t, ch);
+ }
+ t->total_dir_size_joliet += round_up (root->len, t->block_size);
+}
+
+/**
+ * Calculate the position of each directory. Also fill out t->dirlist_joliet.
+ */
+void
+joliet_calc_dir_pos(struct ecma119_write_target *t,
+ struct joliet_tree_node *root)
+{
+ size_t i;
+ struct joliet_tree_node *ch;
+
+ assert(root && ISO_ISDIR(root->iso_self));
+
+ root->block = t->curblock;
+ t->curblock += div_up(root->len, t->block_size);
+
+ t->dirlist_joliet[t->curfile++] = root;
+ for (i = 0; i < root->nchildren; i++) {
+ ch = root->children[i];
+ if (ISO_ISDIR(ch->iso_self))
+ joliet_calc_dir_pos(t, ch);
+ }
+
+ /* reset curfile when we're finished */
+ if (!root->parent)
+ t->curfile = 0;
+}
+
+void
+joliet_update_file_pos(struct ecma119_write_target *t,
+ struct joliet_tree_node *dir)
+{
+ size_t i;
+
+ assert(dir && ISO_ISDIR(dir->iso_self));
+ for (i = 0; i < dir->nchildren; i++) {
+ struct joliet_tree_node *ch;
+ ch = dir->children[i];
+
+ if (!ISO_ISDIR (ch->iso_self)) {
+ struct iso_tree_node *iso = ch->iso_self;
+ ch->block = iso->block;
+ }
+ else
+ joliet_update_file_pos(t, ch);
+ }
+
+ /* reset curfile when we're finished */
+ if (!dir->parent)
+ t->curfile = 0;
+}
+
+struct joliet_tree_node*
+joliet_tree_create(struct ecma119_write_target *t,
+ struct iso_tree_node *iso_root)
+{
+ struct joliet_tree_node *root = create_tree(t, NULL, iso_root);
+
+ sort_tree(root);
+ return root;
+}
+
+/* ugh. this is mostly C&P */
+static void
+write_path_table(struct ecma119_write_target *t,
+ int l_type,
+ uint8_t *buf)
+{
+ void (*write_int)(uint8_t*, uint32_t, int) = l_type ?
+ iso_lsb : iso_msb;
+
+ size_t i;
+ struct ecma119_path_table_record *rec;
+ struct joliet_tree_node *dir;
+ int parent = 0;
+
+ assert (t->joliet);
+
+ for (i = 0; i < t->dirlist_len; i++) {
+ dir = t->pathlist_joliet[i];
+ while ((i) && t->pathlist_joliet[parent] != dir->parent)
+ parent++;
+ assert(parent < i || i == 0);
+
+ rec = (struct ecma119_path_table_record*) buf;
+ rec->len_di[0] = dir->parent ?
+ (uint8_t) ucslen(dir->name) * 2 : 1;
+ rec->len_xa[0] = 0;
+ write_int(rec->block, dir->block, 4);
+ write_int(rec->parent, parent + 1, 2);
+ if (dir->parent)
+ memcpy(rec->dir_id, dir->name, rec->len_di[0]);
+ buf += 8 + rec->len_di[0] + (rec->len_di[0] % 2);
+ }
+
+}
+
+/* if file_id is >= 0, we use it instead of the filename. As a magic number,
+ * file_id == 3 means that we are writing the root directory record (in order
+ * to distinguish it from the "." entry in the root directory) */
+static void
+write_one_dir_record(struct ecma119_write_target *t,
+ struct joliet_tree_node *node,
+ int file_id,
+ uint8_t *buf)
+{
+ uint8_t len_dr = (file_id >= 0) ? 34 : node->dirent_len;
+ uint8_t len_fi = (file_id >= 0) ? 1 : ucslen(node->name) * 2;
+ uint8_t f_id = (uint8_t) ((file_id == 3) ? 0 : file_id);
+ uint8_t *name = (file_id >= 0) ? &f_id : (uint8_t*)node->name;
+ struct ecma119_dir_record *rec = (struct ecma119_dir_record*)buf;
+
+ if (file_id == 1 && node->parent)
+ node = node->parent;
+
+ rec->len_dr[0] = len_dr;
+ iso_bb(rec->block, node->block, 4);
+ iso_bb(rec->length, node->len, 4);
+ iso_datetime_7(rec->recording_time, t->now);
+ rec->flags[0] = ISO_ISDIR(node->iso_self) ? 2 : 0;
+ iso_bb(rec->vol_seq_number, t->volnum + 1, 2);
+ rec->len_fi[0] = len_fi;
+ memcpy(rec->file_id, name, len_fi);
+}
+
+static void
+write_l_path_table(struct ecma119_write_target *t, uint8_t *buf)
+{
+ write_path_table (t, 1, buf);
+}
+
+static void
+write_m_path_table(struct ecma119_write_target *t, uint8_t *buf)
+{
+ write_path_table (t, 0, buf);
+}
+
+static void
+write_sup_vol_desc(struct ecma119_write_target *t, uint8_t *buf)
+{
+ struct ecma119_sup_vol_desc *vol = (struct ecma119_sup_vol_desc*)buf;
+ struct iso_volume *volume = t->volset->volume[t->volnum];
+ uint16_t *vol_id = str2ucs(volume->volume_id);
+ uint16_t *pub_id = str2ucs(volume->publisher_id);
+ uint16_t *data_id = str2ucs(volume->data_preparer_id);
+ uint16_t *volset_id = str2ucs(t->volset->volset_id);
+ int vol_id_len = MIN(32, ucslen(vol_id) * 2);
+ int pub_id_len = MIN(128, ucslen(pub_id) * 2);
+ int data_id_len = MIN(128, ucslen(data_id) * 2);
+ int volset_id_len = MIN(128, ucslen(volset_id) * 2);
+
+ vol->vol_desc_type[0] = 2;
+ memcpy(vol->std_identifier, "CD001", 5);
+ vol->vol_desc_version[0] = 1;
+ memcpy(vol->system_id, "SYSID", 5);
+ if (vol_id)
+ memcpy(vol->volume_id, vol_id, vol_id_len);
+ memcpy(vol->esc_sequences, "%/E", 3);
+ iso_bb(vol->vol_space_size, t->vol_space_size, 4);
+ iso_bb(vol->vol_set_size, t->volset->volset_size, 2);
+ iso_bb(vol->vol_seq_number, t->volnum + 1, 2);
+ iso_bb(vol->block_size, t->block_size, 2);
+ iso_bb(vol->path_table_size, t->path_table_size_joliet, 4);
+ iso_lsb(vol->l_path_table_pos, t->l_path_table_pos_joliet, 4);
+ iso_msb(vol->m_path_table_pos, t->m_path_table_pos_joliet, 4);
+
+ write_one_dir_record(t, t->joliet_root, 3, vol->root_dir_record);
+
+ memcpy(vol->vol_set_id, volset_id, volset_id_len);
+ memcpy(vol->publisher_id, pub_id, pub_id_len);
+ memcpy(vol->data_prep_id, data_id, data_id_len);
+ /*memcpy(vol->application_id, "APPID", app_id_len);*/
+
+ iso_datetime_17(vol->vol_creation_time, t->now);
+ iso_datetime_17(vol->vol_modification_time, t->now);
+ iso_datetime_17(vol->vol_effective_time, t->now);
+ vol->file_structure_version[0] = 1;
+
+ free(vol_id);
+ free(volset_id);
+ free(pub_id);
+ free(data_id);
+
+}
+
+static void
+write_one_dir(struct ecma119_write_target *t,
+ struct joliet_tree_node *dir,
+ uint8_t *buf)
+{
+ size_t i;
+ uint8_t *orig_buf = buf;
+
+ assert(ISO_ISDIR (dir->iso_self));
+ /* write the "." and ".." entries first */
+ write_one_dir_record(t, dir, 0, buf);
+ buf += ((struct ecma119_dir_record*) buf)->len_dr[0];
+
+ write_one_dir_record(t, dir, 1, buf);
+ buf += ((struct ecma119_dir_record*) buf)->len_dr[0];
+
+ for (i = 0; i < dir->nchildren; i++) {
+ write_one_dir_record(t, dir->children[i], -1, buf);
+ buf += ((struct ecma119_dir_record*) buf)->len_dr[0];
+ }
+
+ assert (buf - orig_buf == dir->len);
+}
+
+static void
+write_dirs(struct ecma119_write_target *t, uint8_t *buf)
+{
+ size_t i;
+ struct joliet_tree_node *dir;
+
+ assert (t->curblock == t->dirlist_joliet[0]->block);
+ for (i = 0; i < t->dirlist_len; i++) {
+ dir = t->dirlist_joliet[i];
+ write_one_dir(t, dir, buf);
+ buf += round_up(dir->len, t->block_size);
+ }
+}
+
+void
+joliet_wr_sup_vol_desc(struct ecma119_write_target *t,
+ uint8_t *buf)
+{
+ ecma119_start_chunking(t,
+ write_sup_vol_desc,
+ 2048,
+ buf);
+}
+
+void
+joliet_wr_l_path_table(struct ecma119_write_target *t,
+ uint8_t *buf)
+{
+ ecma119_start_chunking(t,
+ write_l_path_table,
+ t->path_table_size_joliet,
+ buf);
+}
+
+void
+joliet_wr_m_path_table(struct ecma119_write_target *t,
+ uint8_t *buf)
+{
+ ecma119_start_chunking(t,
+ write_m_path_table,
+ t->path_table_size_joliet,
+ buf);
+}
+
+void
+joliet_wr_dir_records(struct ecma119_write_target *t,
+ uint8_t *buf)
+{
+ ecma119_start_chunking(t,
+ write_dirs,
+ t->total_dir_size_joliet,
+ buf);
+}
+
diff --git a/tags/ZeroTwoTwo/libisofs/joliet.h b/tags/ZeroTwoTwo/libisofs/joliet.h
new file mode 100644
index 00000000..e5b7582d
--- /dev/null
+++ b/tags/ZeroTwoTwo/libisofs/joliet.h
@@ -0,0 +1,84 @@
+/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */
+/* vim: set noet ts=8 sts=8 sw=8 : */
+
+/**
+ * \file joliet.h
+ *
+ * Declare the filesystems trees that are Joliet-compatible and the public
+ * functions for tying them into an ecma119 volume.
+ */
+
+#ifndef LIBISO_JOLIET_H
+#define LIBISO_JOLIET_H
+
+#include
+#include
+
+struct ecma119_write_target;
+struct iso_tree_node;
+
+struct joliet_tree_node
+{
+ uint16_t *name; /**< In UCS-2BE. */
+ size_t dirent_len;
+ size_t len;
+ size_t block;
+
+ struct joliet_tree_node *parent;
+ struct iso_tree_node *iso_self;
+ struct ecma119_write_target *target;
+
+ struct joliet_tree_node **children;
+ size_t nchildren;
+};
+
+/**
+ * Create a new joliet_tree that corresponds to the tree represented by
+ * \p iso_root.
+ */
+struct joliet_tree_node*
+joliet_tree_create(struct ecma119_write_target *target,
+ struct iso_tree_node *iso_root);
+
+/**
+ * Calculate the size of each directory in the joliet heirarchy.
+ */
+void
+joliet_calc_dir_size(struct ecma119_write_target *t, struct joliet_tree_node*);
+
+/**
+ * Calculate the position of each directory in the joliet heirarchy.
+ */
+void
+joliet_calc_dir_pos(struct ecma119_write_target *t, struct joliet_tree_node*);
+
+/**
+ * Update the position of each file in the joliet hierarchy (to be called
+ * AFTER the file positions in the iso tree have been set).
+ */
+void
+joliet_update_file_pos(struct ecma119_write_target *t, struct joliet_tree_node*);
+
+/**
+ * Calculate the size of the joliet path table and fill in the list of
+ * directories.
+ */
+void
+joliet_prepare_path_tables(struct ecma119_write_target *t);
+
+void
+joliet_tree_free(struct joliet_tree_node *root);
+
+void
+joliet_wr_sup_vol_desc(struct ecma119_write_target *t, uint8_t *buf);
+
+void
+joliet_wr_l_path_table(struct ecma119_write_target *t, uint8_t *buf);
+
+void
+joliet_wr_m_path_table(struct ecma119_write_target *t, uint8_t *buf);
+
+void
+joliet_wr_dir_records(struct ecma119_write_target *t, uint8_t *buf);
+
+#endif /* LIBISO_JOLIET_H */
diff --git a/tags/ZeroTwoTwo/libisofs/libisofs.h b/tags/ZeroTwoTwo/libisofs/libisofs.h
new file mode 100755
index 00000000..0d29cad1
--- /dev/null
+++ b/tags/ZeroTwoTwo/libisofs/libisofs.h
@@ -0,0 +1,225 @@
+/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */
+/* vim: set noet ts=8 sts=8 sw=8 : */
+
+/**
+ * Create an ISO-9660 data volume with Rock Ridge and Joliet extensions.
+ * Usage is easy:
+ * - Create a new volume.
+ * - Add files and directories.
+ * - Write the volume to a file or create a burn source for use with Libburn.
+ */
+
+#ifndef LIBISO_LIBISOFS_H
+#define LIBISO_LIBISOFS_H
+
+/* #include */
+struct burn_source;
+
+/**
+ * Data volume.
+ * @see volume.h for details.
+ */
+struct iso_volume;
+
+/**
+ * A set of data volumes.
+ * @see volume.h for details.
+ */
+struct iso_volset;
+
+/**
+ * A node in the filesystem tree.
+ * \see tree.h
+ */
+struct iso_tree_node;
+
+enum ecma119_extension_flag {
+ ECMA119_ROCKRIDGE = (1<<0),
+ ECMA119_JOLIET = (1<<1)
+};
+
+/**
+ * Create a new volume.
+ * The parameters can be set to NULL if you wish to set them later.
+ */
+struct iso_volume *iso_volume_new(const char *volume_id,
+ const char *publisher_id,
+ const char *data_preparer_id);
+
+struct iso_volume *iso_volume_new_with_root(const char *volume_id,
+ const char *publisher_id,
+ const char *data_preparer_id,
+ struct iso_tree_node *root);
+
+/**
+ * Free a volume.
+ */
+void iso_volume_free(struct iso_volume *volume);
+
+/**
+ * Free a set of data volumes.
+ */
+void iso_volset_free(struct iso_volset *volume);
+
+/**
+ * Get the root directory for a volume.
+ */
+struct iso_tree_node *iso_volume_get_root(const struct iso_volume *volume);
+
+/**
+ * Fill in the volume identifier for a volume.
+ */
+void iso_volume_set_volume_id(struct iso_volume *volume,
+ const char *volume_id);
+
+/**
+ * Fill in the publisher for a volume.
+ */
+void iso_volume_set_publisher_id(struct iso_volume *volume,
+ const char *publisher_id);
+
+/**
+ * Fill in the data preparer for a volume.
+ */
+void iso_volume_set_data_preparer_id(struct iso_volume *volume,
+ const char *data_preparer_id);
+
+/**
+ * Locate a node by its path on disc.
+ *
+ * \param volume The volume to search in.
+ * \param path The path, in the image, of the file.
+ *
+ * \return The node found or NULL.
+ *
+ */
+struct iso_tree_node *iso_tree_volume_path_to_node(struct iso_volume *volume, const char *path);
+
+/**
+ * Add a file or a directory (recursively) to a volume by specifying its path on the volume.
+ *
+ * \param volume The volume to add the file to.
+ * \param disc_path The path on the disc at which to add the disc.
+ * \param path The path, on the local filesystem, of the file.
+ *
+ * \return The node for the file or NULL if the parent doesn't exists on the disc.
+ */
+struct iso_tree_node *iso_tree_volume_add_path(struct iso_volume *volume,
+ const char *disc_path,
+ const char *path);
+
+/**
+ * Creates a new, empty directory on the volume.
+ *
+ * \param volume The volume to add the directory to.
+ * \param disc_path The path on the volume at which to add the directory.
+ *
+ * \return A pointer to the newly created directory.
+ */
+struct iso_tree_node *iso_tree_volume_add_new_dir(struct iso_volume *volume,
+ const char *disc_path);
+
+/**
+ * Create a new Volume Set consisting of only one volume.
+ * @param volume The first and only volume for the volset to contain.
+ * @param volset_id The Volume Set ID.
+ * @return A new iso_volset.
+ */
+struct iso_volset *iso_volset_new(struct iso_volume *volume,
+ const char *volset_id);
+
+/**
+ * Add a file to a directory.
+ *
+ * \param path The path, on the local filesystem, of the file.
+ *
+ * \pre \p parent is NULL or is a directory.
+ * \pre \p path is non-NULL and is a valid path to a non-directory on the local
+ * filesystem.
+ * \return An iso_tree_node whose path is \p path and whose parent is \p parent.
+ */
+struct iso_tree_node *iso_tree_add_node(struct iso_tree_node *parent,
+ const char *path);
+
+/**
+ * Recursively add an existing directory to the tree.
+ * Warning: when using this, you'll lose pointers to files or subdirectories.
+ * If you want to have pointers to all files and directories,
+ * use iso_tree_add_file and iso_tree_add_dir.
+ *
+ * \param path The path, on the local filesystem, of the directory to add.
+ *
+ * \pre \p parent is NULL or is a directory.
+ * \pre \p path is non-NULL and is a valid path to a directory on the local
+ * filesystem.
+ * \return a pointer to the newly created directory.
+ */
+struct iso_tree_node *iso_tree_radd_dir(struct iso_tree_node *parent,
+ const char *path);
+
+
+/**
+ * Add the path of a file or directory to ignore when adding a directory recursively.
+ *
+ * \param path The path, on the local filesystem, of the file.
+ */
+void iso_exclude_add_path(const char *path);
+
+/**
+ * Remove a path that was set to be ignored when adding a directory recusively.
+ *
+ * \param path The path, on the local filesystem, of the file.
+ */
+void iso_exclude_remove_path(const char *path);
+
+/**
+ * Remove all paths that were set to be ignored when adding a directory recusively.
+ */
+void iso_exclude_empty(void);
+
+/**
+ * Creates a new, empty directory on the volume.
+ *
+ * \pre \p parent is NULL or is a directory.
+ * \pre \p name is unique among the children and files belonging to \p parent.
+ * Also, it doesn't contain '/' characters.
+ *
+ * \post \p parent contains a child directory whose name is \p name and whose
+ * POSIX attributes are the same as \p parent's.
+ * \return a pointer to the newly created directory.
+ */
+struct iso_tree_node *iso_tree_add_new_dir(struct iso_tree_node *parent,
+ const char *name);
+
+/**
+ * Set the name of a file (using the current locale).
+ */
+void iso_tree_node_set_name(struct iso_tree_node *file, const char *name);
+
+/**
+ * Recursively print a directory to stdout.
+ * \param spaces The initial number of spaces on the left. Set to 0 if you
+ * supply a root directory.
+ */
+void iso_tree_print(const struct iso_tree_node *root, int spaces);
+
+/** Create a burn_source which can be used as a data source for a track
+ *
+ * The volume set used to create the libburn_source can _not_ be modified
+ * until the libburn_source is freed.
+ *
+ * \param volumeset The volume set from which you want to write
+ * \param volnum The volume in the set which you want to write (usually 0)
+ * \param level ISO level to write at.
+ * \param flags Which extensions to support.
+ *
+ * \pre \p volumeset is non-NULL
+ * \pre \p volnum is less than \p volset->volset_size.
+ * \return A burn_source to be used for the data source for a track
+ */
+struct burn_source* iso_source_new_ecma119 (struct iso_volset *volumeset,
+ int volnum,
+ int level,
+ int flags);
+
+#endif /* LIBISO_LIBISOFS_H */
diff --git a/tags/ZeroTwoTwo/libisofs/rockridge.c b/tags/ZeroTwoTwo/libisofs/rockridge.c
new file mode 100755
index 00000000..3662ac65
--- /dev/null
+++ b/tags/ZeroTwoTwo/libisofs/rockridge.c
@@ -0,0 +1,300 @@
+/* vim: set noet ts=8 sts=8 sw=8 : */
+
+#include "rockridge.h"
+#include "util.h"
+#include "ecma119.h"
+#include "ecma119_tree.h"
+#include "tree.h"
+#include "susp.h"
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+/* create a PX field from the permissions on the current node. */
+uint8_t *rrip_make_PX(struct ecma119_write_target *t,
+ struct ecma119_tree_node *node)
+{
+ uint8_t *PX = malloc(44);
+
+ PX[0] = 'P';
+ PX[1] = 'X';
+ PX[2] = 44;
+ PX[3] = 1;
+ iso_bb(&PX[4], node->iso_self->attrib.st_mode, 4);
+ iso_bb(&PX[12], node->iso_self->attrib.st_nlink, 4);
+ iso_bb(&PX[20], node->iso_self->attrib.st_uid, 4);
+ iso_bb(&PX[28], node->iso_self->attrib.st_gid, 4);
+ iso_bb(&PX[36], node->iso_self->attrib.st_ino, 4);
+ return PX;
+}
+
+/** See IEEE 1282 4.1.1 */
+void rrip_add_PX(struct ecma119_write_target *t, struct ecma119_tree_node *node)
+{
+ susp_append(t, &node->susp, rrip_make_PX(t, node));
+ if (node->type == ECMA119_DIR) {
+ susp_append(t, &node->dir.self_susp, rrip_make_PX(t, node));
+ susp_append(t, &node->dir.parent_susp, rrip_make_PX(t, node));
+ }
+}
+
+void rrip_add_PN(struct ecma119_write_target *t, struct ecma119_tree_node *node)
+{
+ uint8_t *PN = malloc(20);
+
+ PN[0] = 'P';
+ PN[1] = 'N';
+ PN[2] = 20;
+ PN[3] = 1;
+ iso_bb(&PN[4], node->iso_self->attrib.st_dev >> 32, 4);
+ iso_bb(&PN[12], node->iso_self->attrib.st_dev & 0xffffffff, 4);
+ susp_append(t, &node->susp, PN);
+}
+
+static void rrip_SL_append_comp(int *n, uint8_t ***comps,
+ char *s, int size, char fl)
+{
+ uint8_t *comp = malloc(size + 2);
+
+ (*n)++;
+ comp[0] = fl;
+ comp[1] = size;
+ *comps = realloc(*comps, (*n) * sizeof(void*));
+ (*comps)[(*n) - 1] = comp;
+
+ if (size) {
+ memcpy(&comp[2], s, size);
+ }
+}
+
+static void rrip_SL_add_component(char *prev, char *cur, int *n_comp,
+ uint8_t ***comps)
+{
+ int size = cur - prev;
+
+ if (size == 0) {
+ rrip_SL_append_comp(n_comp, comps, prev, 0, 1 << 3);
+ return;
+ }
+
+ if (size == 1 && prev[0] == '.') {
+ rrip_SL_append_comp(n_comp, comps, prev, 0, 1 << 1);
+ return;
+ }
+ if (size == 2 && !strncmp(prev, "..", 2)) {
+ rrip_SL_append_comp(n_comp, comps, prev, 0, 1 << 2);
+ return;
+ }
+
+ /* we can't make a component any bigger than 250 (is this really a
+ problem)? because then it won't fit inside the SL field */
+ while (size > 248) {
+ size -= 248;
+ rrip_SL_append_comp(n_comp, comps, prev, 248, 1 << 0);
+ }
+
+ rrip_SL_append_comp(n_comp, comps, prev, size, 0);
+}
+
+void rrip_add_SL(struct ecma119_write_target *t, struct ecma119_tree_node *node)
+{
+ int ret, pathsize = 0;
+ char *path = NULL, *cur, *prev;
+ int i, j;
+
+ uint8_t **comp = NULL;
+ int n_comp = 0;
+ int total_comp_len = 0;
+ int written = 0, pos;
+
+ uint8_t *SL;
+
+ do {
+ pathsize += 128;
+ path = realloc(path, pathsize);
+ /* FIXME: what if the file is not on the local fs? */
+ ret = readlink(node->iso_self->loc.path, path, pathsize);
+ } while (ret == pathsize);
+ if (ret == -1) {
+ fprintf(stderr, "Error: couldn't read symlink: %s\n",
+ strerror(errno));
+ return;
+ }
+ path[ret] = '\0';
+
+ prev = path;
+ for (cur = strchr(path, '/'); cur && *cur; cur = strchr(cur, '/')) {
+ rrip_SL_add_component(prev, cur, &n_comp, &comp);
+ cur++;
+ prev = cur;
+ }
+
+ /* if there was no trailing '/', we need to add the last component. */
+ if (prev == path || prev != &path[ret - 1]) {
+ rrip_SL_add_component(prev, &path[ret], &n_comp, &comp);
+ }
+
+ for (i = 0; i < n_comp; i++) {
+ total_comp_len += comp[i][1] + 2;
+ if (total_comp_len > 250) {
+ total_comp_len -= comp[i][1] + 2;
+ SL = malloc(total_comp_len + 5);
+ SL[0] = 'S';
+ SL[1] = 'L';
+ SL[2] = total_comp_len + 5;
+ SL[3] = 1;
+ SL[4] = 1; /* CONTINUE */
+ pos = 5;
+ for (j = written; j < i; j++) {
+ memcpy(&SL[pos], comp[j], comp[j][2]);
+ pos += comp[j][2];
+ }
+ susp_append(t, &node->susp, SL);
+ written = i - 1;
+ total_comp_len = comp[i][1];
+ }
+ }
+ SL = malloc(total_comp_len + 5);
+ SL[0] = 'S';
+ SL[1] = 'L';
+ SL[2] = total_comp_len + 5;
+ SL[3] = 1;
+ SL[4] = 0;
+ pos = 5;
+
+ for (j = written; j < n_comp; j++) {
+ memcpy(&SL[pos], comp[j], comp[j][1] + 2);
+ pos += comp[j][1] + 2;
+ }
+ susp_append(t, &node->susp, SL);
+
+ free(path);
+ /* free the components */
+ for (i = 0; i < n_comp; i++) {
+ free(comp[i]);
+ }
+ free(comp);
+}
+
+static void rrip_add_NM_single(struct ecma119_write_target *t,
+ struct susp_info *susp,
+ char *name, int size, int flags)
+{
+ uint8_t *NM = malloc(size + 5);
+
+ NM[0] = 'N';
+ NM[1] = 'M';
+ NM[2] = size + 5;
+ NM[3] = 1;
+ NM[4] = flags;
+ if (size) {
+ memcpy(&NM[5], name, size);
+ }
+ susp_append(t, susp, NM);
+}
+
+void
+rrip_add_NM(struct ecma119_write_target *t, struct ecma119_tree_node *node)
+{
+ char *name = iso_p_fileid(node->iso_self->name);
+ int len = name ? strlen(name) : 0;
+ char *pos = name;
+
+ if (!len)
+ return;
+
+ if (node->type == ECMA119_DIR) {
+ rrip_add_NM_single(t, &node->dir.self_susp, pos, 0, 1 << 1);
+ rrip_add_NM_single(t, &node->dir.parent_susp, pos, 0, 1 << 2);
+ }
+
+ while (len > 250) {
+ rrip_add_NM_single(t, &node->susp, pos, 250, 1);
+ len -= 250;
+ pos += 250;
+ }
+ rrip_add_NM_single(t, &node->susp, pos, len, 0);
+}
+
+void rrip_add_CL(struct ecma119_write_target *t, struct ecma119_tree_node *node)
+{
+ uint8_t *CL = calloc(1, 12);
+
+ CL[0] = 'C';
+ CL[1] = 'L';
+ CL[2] = 12;
+ CL[3] = 1;
+ susp_append(t, &node->susp, CL);
+}
+
+void
+rrip_add_PL(struct ecma119_write_target *t, struct ecma119_tree_node *node)
+{
+ uint8_t *PL = calloc(1, 12);
+
+ PL[0] = 'P';
+ PL[1] = 'L';
+ PL[2] = 12;
+ PL[3] = 1;
+ susp_append(t, &node->dir.parent_susp, PL);
+}
+
+void
+rrip_add_RE(struct ecma119_write_target *t, struct ecma119_tree_node *node)
+{
+ uint8_t *RE = malloc(4);
+
+ RE[0] = 'R';
+ RE[1] = 'E';
+ RE[2] = 4;
+ RE[3] = 1;
+ susp_append(t, &node->susp, RE);
+}
+
+void
+rrip_add_TF(struct ecma119_write_target *t, struct ecma119_tree_node *node)
+{
+ uint8_t *TF = malloc(5 + 3 * 7);
+
+ TF[0] = 'T';
+ TF[1] = 'F';
+ TF[2] = 5 + 3 * 7;
+ TF[3] = 1;
+ TF[4] = (1 << 1) | (1 << 2) | (1 << 3) | (1 << 7);
+ iso_datetime_7(&TF[5], node->iso_self->attrib.st_mtime);
+ iso_datetime_7(&TF[12], node->iso_self->attrib.st_atime);
+ iso_datetime_7(&TF[19], node->iso_self->attrib.st_ctime);
+ susp_append(t, &node->susp, TF);
+}
+
+void
+rrip_finalize(struct ecma119_write_target *t, struct ecma119_tree_node *dir)
+{
+ int i;
+
+ assert(dir->type == ECMA119_DIR);
+
+ if (dir->parent != dir->dir.real_parent) {
+ uint8_t *PL = susp_find(&dir->dir.parent_susp, "PL");
+
+ assert(PL);
+ iso_bb(&PL[4], dir->dir.real_parent->block, 4);
+ }
+
+ for (i = 0; i < dir->dir.nchildren; i++) {
+ struct ecma119_tree_node *ch = dir->dir.children[i];
+
+ if (ch->type == ECMA119_FILE && ch->file.real_me) {
+ uint8_t *CL = susp_find(&ch->susp, "CL");
+
+ assert(CL);
+ iso_bb(&CL[4], ch->file.real_me->block, 4);
+ } else if (ch->type == ECMA119_DIR) {
+ rrip_finalize(t, ch);
+ }
+ }
+}
diff --git a/tags/ZeroTwoTwo/libisofs/rockridge.h b/tags/ZeroTwoTwo/libisofs/rockridge.h
new file mode 100755
index 00000000..7909a0ce
--- /dev/null
+++ b/tags/ZeroTwoTwo/libisofs/rockridge.h
@@ -0,0 +1,26 @@
+/* vim: set noet ts=8 sts=8 sw=8 : */
+
+/** Functions and structures used for Rock Ridge support. */
+
+#ifndef ISO_ROCKRIDGE_H
+#define ISO_ROCKRIDGE_H
+
+struct ecma119_write_target;
+struct ecma119_tree_node;
+
+void rrip_add_PX(struct ecma119_write_target *, struct ecma119_tree_node *);
+void rrip_add_PN(struct ecma119_write_target *, struct ecma119_tree_node *);
+void rrip_add_SL(struct ecma119_write_target *, struct ecma119_tree_node *);
+void rrip_add_NM(struct ecma119_write_target *, struct ecma119_tree_node *);
+void rrip_add_CL(struct ecma119_write_target *, struct ecma119_tree_node *);
+void rrip_add_RE(struct ecma119_write_target *, struct ecma119_tree_node *);
+void rrip_add_TF(struct ecma119_write_target *, struct ecma119_tree_node *);
+
+/* This is special because it doesn't modify the susp fields of the directory
+ * that gets passed to it; it modifies the susp fields of the ".." entry in
+ * that directory. */
+void rrip_add_PL(struct ecma119_write_target *, struct ecma119_tree_node *);
+
+void rrip_finalize(struct ecma119_write_target *, struct ecma119_tree_node *);
+
+#endif /* ISO_ROCKRIDGE_H */
diff --git a/tags/ZeroTwoTwo/libisofs/susp.c b/tags/ZeroTwoTwo/libisofs/susp.c
new file mode 100755
index 00000000..0d2d6c5b
--- /dev/null
+++ b/tags/ZeroTwoTwo/libisofs/susp.c
@@ -0,0 +1,280 @@
+/* vim: set noet ts=8 sts=8 sw=8 : */
+
+#include "susp.h"
+#include "util.h"
+#include "ecma119.h"
+#include "ecma119_tree.h"
+
+#include
+#include
+#include
+
+void susp_insert(struct ecma119_write_target *t,
+ struct susp_info *susp,
+ uint8_t *data,
+ int pos)
+{
+ int i;
+
+ if (pos < 0) {
+ pos = susp->n_susp_fields;
+ }
+
+ assert(pos <= susp->n_susp_fields);
+ susp->n_susp_fields++;
+ susp->susp_fields = realloc(susp->susp_fields,
+ sizeof(void*) * susp->n_susp_fields);
+
+ for (i = susp->n_susp_fields-1; i > pos; i--) {
+ susp->susp_fields[i] = susp->susp_fields[i - 1];
+ }
+ susp->susp_fields[pos] = data;
+}
+
+void susp_append(struct ecma119_write_target *t,
+ struct susp_info *susp,
+ uint8_t *data)
+{
+ susp_insert(t, susp, data, susp->n_susp_fields);
+}
+
+uint8_t *susp_find(struct susp_info *susp, const char *name)
+{
+ int i;
+
+ for (i = 0; i < susp->n_susp_fields; i++) {
+ if (!strncmp((char *)susp->susp_fields[i], name, 2)) {
+ return susp->susp_fields[i];
+ }
+ }
+ return NULL;
+}
+
+/** Utility function for susp_add_CE because susp_add_CE needs to act 3 times
+ * on directories (for the "." and ".." entries.
+ *
+ * \param len The amount of space available for the System Use area.
+ */
+#define CE_LEN 28
+static unsigned char *susp_add_single_CE(struct ecma119_write_target *t,
+ struct susp_info *susp,
+ int len)
+{
+ int susp_length = 0, tmp_len;
+ int i;
+ unsigned char *CE;
+
+ for (i = 0; i < susp->n_susp_fields; i++) {
+ susp_length += susp->susp_fields[i][2];
+ }
+ if (susp_length <= len) {
+ /* no need for a CE field */
+ susp->non_CE_len = susp_length;
+ susp->n_fields_fit = susp->n_susp_fields;
+ return NULL;
+ }
+
+ tmp_len = susp_length;
+ for (i = susp->n_susp_fields - 1; i >= 0; i--) {
+ tmp_len -= susp->susp_fields[i][2];
+ if (tmp_len + CE_LEN <= len) {
+ susp->non_CE_len = tmp_len + CE_LEN;
+ susp->CE_len = susp_length - tmp_len;
+
+ /* i+1 because we have to count the CE field */
+ susp->n_fields_fit = i + 1;
+
+ CE = calloc(1, CE_LEN);
+ /* we don't fill in the BLOCK LOCATION or OFFSET
+ fields yet. */
+ CE[0] = 'C';
+ CE[1] = 'E';
+ CE[2] = (char)CE_LEN;
+ CE[3] = (char)1;
+ iso_bb(&CE[20], susp_length - tmp_len, 4);
+
+ return CE;
+ }
+ }
+ assert(0);
+ return NULL;
+}
+
+static void
+try_add_CE(struct ecma119_write_target *t,
+ struct susp_info *susp,
+ size_t dirent_len)
+{
+ uint8_t *CE = susp_add_single_CE(t, susp, 255 - dirent_len);
+ if (CE)
+ susp_insert(t, susp, CE, susp->n_fields_fit - 1);
+}
+
+/** See IEEE P1281 Draft Version 1.12/5.2. Because this function depends on the
+ * length of the other SUSP fields, it should always be calculated last. */
+void
+susp_add_CE(struct ecma119_write_target *t, struct ecma119_tree_node *node)
+{
+ try_add_CE(t, &node->susp, node->dirent_len);
+ if (node->type == ECMA119_DIR) {
+ try_add_CE(t, &node->dir.self_susp, 34);
+ try_add_CE(t, &node->dir.parent_susp, 34);
+ }
+}
+
+/** See IEEE P1281 Draft Version 1.12/5.3 */
+void
+susp_add_SP(struct ecma119_write_target *t, struct ecma119_tree_node *dir)
+{
+ unsigned char *SP = malloc(7);
+
+ assert(dir->type == ECMA119_DIR);
+
+ SP[0] = 'S';
+ SP[1] = 'P';
+ SP[2] = (char)7;
+ SP[3] = (char)1;
+ SP[4] = 0xbe;
+ SP[5] = 0xef;
+ SP[6] = 0;
+ susp_append(t, &dir->dir.self_susp, SP);
+}
+
+#if 0
+/** See IEEE P1281 Draft Version 1.12/5.4 */
+static void susp_add_ST(struct ecma119_write_target *t,
+ struct iso_tree_node *node)
+{
+ unsigned char *ST = malloc(4);
+
+ ST[0] = 'S';
+ ST[1] = 'T';
+ ST[2] = (char)4;
+ ST[3] = (char)1;
+ susp_append(t, node, ST);
+}
+#endif
+
+/** See IEEE P1281 Draft Version 1.12/5.5 FIXME: this is rockridge */
+void
+rrip_add_ER(struct ecma119_write_target *t, struct ecma119_tree_node *dir)
+{
+ unsigned char *ER = malloc(182);
+
+ assert(dir->type == ECMA119_DIR);
+
+ ER[0] = 'E';
+ ER[1] = 'R';
+ ER[2] = 182;
+ ER[3] = 1;
+ ER[4] = 9;
+ ER[5] = 72;
+ ER[6] = 93;
+ ER[7] = 1;
+ memcpy(&ER[8], "IEEE_1282", 9);
+ memcpy(&ER[17], "THE IEEE 1282 PROTOCOL PROVIDES SUPPORT FOR POSIX "
+ "FILE SYSTEM SEMANTICS.", 72);
+ memcpy(&ER[89], "PLEASE CONTACT THE IEEE STANDARDS DEPARTMENT, "
+ "PISCATAWAY, NJ, USA FOR THE 1282 SPECIFICATION.", 93);
+ susp_append(t, &dir->dir.self_susp, ER);
+}
+
+/* calculate the location of the CE areas. Since CE areas don't need to be
+ * aligned to a block boundary, we contatenate all CE areas from a single
+ * directory and dump them immediately after all the directory records.
+ *
+ * Requires that the following be known:
+ * - position of the current directory (dir->block)
+ * - length of the current directory (dir->dir.len)
+ * - sum of the children's CE lengths (dir->dir.CE_len)
+ */
+static void
+susp_fin_1_CE(struct ecma119_write_target *t,
+ struct susp_info *susp,
+ size_t block,
+ size_t *offset)
+{
+ uint8_t *CE = susp->susp_fields[susp->n_fields_fit - 1];
+
+ if (!susp->CE_len) {
+ return;
+ }
+ iso_bb(&CE[4], block + (*offset) / t->block_size, 4);
+ iso_bb(&CE[12], (*offset) % t->block_size, 4);
+ *offset += susp->CE_len;
+}
+
+static void susp_fin_CE(struct ecma119_write_target *t,
+ struct ecma119_tree_node *dir)
+{
+ int i;
+ size_t CE_offset = dir->dir.len;
+
+ assert(dir->type == ECMA119_DIR);
+
+ susp_fin_1_CE(t, &dir->dir.self_susp, dir->block, &CE_offset);
+ susp_fin_1_CE(t, &dir->dir.parent_susp, dir->block, &CE_offset);
+
+ for (i = 0; i < dir->dir.nchildren; i++) {
+ struct ecma119_tree_node *ch = dir->dir.children[i];
+ susp_fin_1_CE(t, &ch->susp, dir->block, &CE_offset);
+ }
+ assert(CE_offset == dir->dir.len + dir->dir.CE_len);
+}
+
+void
+susp_finalize(struct ecma119_write_target *t, struct ecma119_tree_node *dir)
+{
+ int i;
+
+ assert(dir->type = ECMA119_DIR);
+
+ if (dir->dir.depth != 1) {
+ susp_fin_CE(t, dir);
+ }
+
+ for (i = 0; i < dir->dir.nchildren; i++) {
+ if (dir->dir.children[i]->type == ECMA119_DIR)
+ susp_finalize(t, dir->dir.children[i]);
+ }
+}
+
+void susp_write(struct ecma119_write_target *t,
+ struct susp_info *susp,
+ unsigned char *buf)
+{
+ int i;
+ int pos = 0;
+
+ for (i = 0; i < susp->n_fields_fit; i++) {
+ memcpy(&buf[pos], susp->susp_fields[i],
+ susp->susp_fields[i][2]);
+ pos += susp->susp_fields[i][2];
+ }
+}
+
+void susp_write_CE(struct ecma119_write_target *t, struct susp_info *susp,
+ unsigned char *buf)
+{
+ int i;
+ int pos = 0;
+
+ for (i = susp->n_fields_fit; i < susp->n_susp_fields; i++) {
+ memcpy(&buf[pos], susp->susp_fields[i],
+ susp->susp_fields[i][2]);
+ pos += susp->susp_fields[i][2];
+ }
+}
+
+void susp_free_fields(struct susp_info *susp)
+{
+ int i;
+
+ for (i=0; in_susp_fields; i++) {
+ free(susp->susp_fields[i]);
+ }
+ if (susp->susp_fields) {
+ free(susp->susp_fields);
+ }
+ memset(susp, 0, sizeof(struct susp_info));
+}
diff --git a/tags/ZeroTwoTwo/libisofs/susp.h b/tags/ZeroTwoTwo/libisofs/susp.h
new file mode 100755
index 00000000..31dd2cfd
--- /dev/null
+++ b/tags/ZeroTwoTwo/libisofs/susp.h
@@ -0,0 +1,62 @@
+/* vim: set noet ts=8 sts=8 sw=8 : */
+
+/** Functions and structures used for SUSP (IEEE 1281).
+ */
+
+#ifndef __ISO_SUSP
+#define __ISO_SUSP
+
+#include
+
+/* SUSP is only present in standard ecma119 */
+struct ecma119_write_target;
+struct ecma119_tree_node;
+
+/** This contains the information that needs to go in the SUSP area of a file.
+ */
+struct susp_info
+{
+ int n_susp_fields; /**< Number of SUSP fields */
+ uint8_t **susp_fields; /**< Data for each SUSP field */
+
+ /* the next 3 relate to CE and are filled out by susp_add_CE. */
+ int n_fields_fit; /**< How many of the above SUSP fields fit
+ * within this node's dirent. */
+ int non_CE_len; /**< Length of the part of the SUSP area that
+ * fits in the dirent. */
+ int CE_len; /**< Length of the part of the SUSP area that
+ * will go in a CE area. */
+};
+
+void susp_add_CE(struct ecma119_write_target *, struct ecma119_tree_node *);
+
+/* these next 2 are special because they don't modify the susp fields of the
+ * directory; they modify the susp fields of the
+ * "." entry in the directory. */
+void susp_add_SP(struct ecma119_write_target *, struct ecma119_tree_node *);
+void rrip_add_ER(struct ecma119_write_target *, struct ecma119_tree_node *);
+
+/** Once all the directories and files are laid out, recurse through the tree
+ * and finalize all SUSP CE entries. */
+void susp_finalize(struct ecma119_write_target *, struct ecma119_tree_node *);
+
+void susp_append(struct ecma119_write_target *,
+ struct susp_info *,
+ uint8_t *);
+void susp_insert(struct ecma119_write_target *,
+ struct susp_info *,
+ uint8_t *,
+ int pos);
+uint8_t *susp_find(struct susp_info *,
+ const char *);
+
+void susp_write(struct ecma119_write_target *,
+ struct susp_info *,
+ uint8_t *);
+void susp_write_CE(struct ecma119_write_target *,
+ struct susp_info *,
+ uint8_t *);
+
+void susp_free_fields(struct susp_info *);
+
+#endif /* __ISO_SUSP */
diff --git a/tags/ZeroTwoTwo/libisofs/tree.c b/tags/ZeroTwoTwo/libisofs/tree.c
new file mode 100755
index 00000000..57aa0dfd
--- /dev/null
+++ b/tags/ZeroTwoTwo/libisofs/tree.c
@@ -0,0 +1,223 @@
+/* vim: set noet ts=8 sts=8 sw=8 : */
+
+/**
+ * \file tree.c
+ *
+ * Implement filesystem trees.
+ */
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "tree.h"
+#include "util.h"
+#include "volume.h"
+#include "exclude.h"
+
+static void
+set_default_stat(struct stat *s)
+{
+ time_t now = time(NULL);
+
+ memset(s, 0, sizeof(struct stat));
+ s->st_mode = 0777 | S_IFREG;
+ s->st_atime = s->st_mtime = s->st_ctime = now;
+}
+
+static struct stat
+get_attrib(const struct iso_tree_node *node)
+{
+ struct stat st;
+
+ if (node) {
+ return node->attrib;
+ }
+ set_default_stat(&st);
+ return st;
+}
+
+static void
+append_node(struct iso_tree_node *parent,
+ struct iso_tree_node *child)
+{
+ assert((!parent || S_ISDIR(parent->attrib.st_mode)) && child);
+ if (!parent)
+ return;
+
+ parent->nchildren++;
+ parent->children =
+ realloc(parent->children, parent->nchildren * sizeof(void*));
+ parent->children[parent->nchildren-1] = child;
+}
+
+struct iso_tree_node*
+iso_tree_new_root(struct iso_volume *vol)
+{
+ assert(vol);
+
+ if (vol->root) {
+ iso_tree_free(vol->root);
+ }
+
+ vol->root = calloc(1, sizeof(struct iso_tree_node));
+ vol->root->volume = vol;
+ set_default_stat(&vol->root->attrib);
+ vol->root->attrib.st_mode = S_IFDIR | 0777;
+ vol->root->loc.type = LIBISO_NONE;
+ return vol->root;
+}
+
+struct iso_tree_node*
+iso_tree_add_new_file(struct iso_tree_node *parent, const char *name)
+{
+ struct iso_tree_node *f = calloc(1, sizeof(struct iso_tree_node));
+
+ assert((!parent || S_ISDIR(parent->attrib.st_mode)) && name);
+
+ f->volume = parent ? parent->volume : NULL;
+ f->parent = parent;
+ f->name = parent ? strdup(name) : NULL;
+ f->attrib = get_attrib(parent);
+ f->attrib.st_mode = 0777 | S_IFREG;
+ f->loc.type = LIBISO_NONE;
+ append_node(parent, f);
+ return f;
+}
+
+struct iso_tree_node*
+iso_tree_add_new_dir(struct iso_tree_node *parent, const char *name)
+{
+ struct iso_tree_node *d = iso_tree_add_new_file(parent, name);
+
+ assert((!parent || S_ISDIR(parent->attrib.st_mode)) && name);
+
+ d->attrib.st_mode = (d->attrib.st_mode & ~S_IFMT) | S_IFDIR;
+ return d;
+}
+
+struct iso_tree_node*
+iso_tree_add_node(struct iso_tree_node *parent, const char *path)
+{
+ char *p;
+ struct stat st;
+ struct iso_tree_node *ret;
+
+ assert((!parent || S_ISDIR(parent->attrib.st_mode)) && path);
+
+ if (lstat(path, &st) == -1)
+ return NULL;
+
+ p = strdup(path); /* because basename() might modify its arg */
+
+ /* it doesn't matter if we add a file or directory since we modify
+ * attrib anyway. */
+ ret = iso_tree_add_new_file(parent, basename(p));
+ ret->attrib = st;
+ ret->loc.type = LIBISO_FILESYS;
+ ret->loc.path = strdup(path);
+ free(p);
+
+ return ret;
+}
+
+struct iso_tree_node*
+iso_tree_radd_dir (struct iso_tree_node *parent, const char *path)
+{
+ struct iso_tree_node *new;
+ DIR *dir;
+ struct dirent *ent;
+
+ assert((!parent || S_ISDIR(parent->attrib.st_mode)) && path);
+
+ new = iso_tree_add_node(parent, path);
+ if (!new || !S_ISDIR(new->attrib.st_mode)) {
+ return new;
+ }
+
+ dir = opendir(path);
+ if (!dir) {
+ warn("couldn't open directory %s: %s\n", path, strerror(errno));
+ return new;
+ }
+
+ while ((ent = readdir(dir))) {
+ char child[strlen(ent->d_name) + strlen(path) + 2];
+
+ if (strcmp(ent->d_name, ".") == 0 ||
+ strcmp(ent->d_name, "..") == 0)
+ continue;
+
+ sprintf(child, "%s/%s", path, ent->d_name);
+
+ /* see if this child is excluded. */
+ if (iso_exclude_lookup(child))
+ continue;
+
+ iso_tree_radd_dir(new, child);
+ }
+ closedir(dir);
+
+ return new;
+}
+
+void
+iso_tree_free(struct iso_tree_node *root)
+{
+ size_t i;
+
+ for (i=0; i < root->nchildren; i++) {
+ iso_tree_free(root->children[i]);
+ }
+ free(root->name);
+ free(root->children);
+ free(root);
+}
+
+void
+iso_tree_print(const struct iso_tree_node *root, int spaces)
+{
+ size_t i;
+ char sp[spaces+1];
+
+ memset(sp, ' ', spaces);
+ sp[spaces] = '\0';
+
+ printf("%s%sn", sp, root->name);
+ for (i=0; i < root->nchildren; i++) {
+ iso_tree_print(root->children[i], spaces+2);
+ }
+}
+
+void
+iso_tree_print_verbose(const struct iso_tree_node *root,
+ print_dir_callback dir,
+ print_file_callback file,
+ void *callback_data,
+ int spaces)
+{
+ size_t i;
+
+ (S_ISDIR(root->attrib.st_mode) ? dir : file)
+ (root, callback_data, spaces);
+ for (i=0; i < root->nchildren; i++) {
+ iso_tree_print_verbose(root->children[i], dir,
+ file, callback_data, spaces+2);
+ }
+}
+
+void
+iso_tree_node_set_name(struct iso_tree_node *file, const char *name)
+{
+ free(file->name);
+ file->name = strdup(name);
+}
diff --git a/tags/ZeroTwoTwo/libisofs/tree.h b/tags/ZeroTwoTwo/libisofs/tree.h
new file mode 100755
index 00000000..e0e04609
--- /dev/null
+++ b/tags/ZeroTwoTwo/libisofs/tree.h
@@ -0,0 +1,159 @@
+/* vim: set noet ts=8 sts=8 sw=8 : */
+
+/**
+ * \file tree.h
+ *
+ * Declare the structure of a libisofs filesystem tree. The files in this
+ * tree can come from either the local filesystem or from another .iso image
+ * (for multisession).
+ *
+ * This tree preserves as much information as it can about the files; names
+ * are stored in wchar_t and we preserve POSIX attributes. This tree does
+ * *not* include information that is necessary for writing out, for example,
+ * an ISO level 1 tree. That information will go in a different tree because
+ * the structure is sufficiently different.
+ */
+
+#ifndef LIBISO_TREE_H
+#define LIBISO_TREE_H
+
+#include
+#include
+#include
+#include
+
+#include "libisofs.h"
+
+enum file_location {
+ LIBISO_FILESYS,
+ LIBISO_PREVSESSION,
+ LIBISO_NONE /**< for files/dirs that were added with
+ * iso_tree_add_new_XXX. */
+};
+
+/**
+ * This tells us where to read the data from a file. Either we read from the
+ * local filesystem or we just point to the block on a previous session.
+ */
+struct iso_file_location
+{
+ enum file_location type;
+ /* union {*/
+ char *path; /* in the current locale */
+ uint32_t block;
+ /* };*/
+};
+
+/**
+ * A node in the filesystem tree.
+ */
+struct iso_tree_node
+{
+ struct iso_volume *volume;
+ struct iso_tree_node *parent;
+ char *name;
+ struct stat attrib; /**< The POSIX attributes of this node as
+ * documented in "man 2 stat". */
+ struct iso_file_location loc;
+ /**< Only used for regular files and symbolic
+ * links (ie. files for which we might have to
+ * copy data). */
+
+ size_t nchildren; /**< The number of children of this
+ * directory (if this is a directory). */
+ struct iso_tree_node **children;
+
+ size_t block; /**< The block at which this file will
+ * reside on disk. We store this here as
+ * well as in the various mangled trees
+ * because many different trees might point
+ * to the same file and they need to share the
+ * block location. */
+};
+
+/**
+ * Create a new root directory for a volume.
+ *
+ * \param vol The volume for which to create a new root directory.
+ *
+ * \pre \p vol is non-NULL.
+ * \post \p vol has a non-NULL, empty root directory with permissions 777.
+ * \return \p vol's new non-NULL, empty root directory.
+ */
+struct iso_tree_node *iso_tree_new_root(struct iso_volume *vol);
+
+/**
+ * Create a new, empty, file.
+ *
+ * \param parent The parent directory of the new file. If this is null, create
+ * and return a new file node without adding it to any tree.
+ * \param name The name of the new file, encoded in the current locale.
+ * \pre \p name is non-NULL and it does not match any other file or directory
+ * name in \p parent.
+ * \post \p parent (if non-NULL) contains a file with the following properties:
+ * - the file's name is \p name (converted to wchar_t)
+ * - the file's POSIX permissions are the same as \p parent's
+ * - the file is a regular file
+ * - the file is empty
+ *
+ * \return \p parent's newly created file.
+ */
+struct iso_tree_node *iso_tree_add_new_file(struct iso_tree_node *parent,
+ const char *name);
+
+/**
+ * Recursively free a directory.
+ *
+ * \param root The root of the directory heirarchy to free.
+ *
+ * \pre \p root is non-NULL.
+ */
+void iso_tree_free(struct iso_tree_node *root);
+
+/**
+ * A function that prints verbose information about a directory.
+ *
+ * \param dir The directory about which to print information.
+ * \param data Unspecified function-dependent data.
+ * \param spaces The number of spaces to prepend to the output.
+ *
+ * \see iso_tree_print_verbose
+ */
+typedef void (*print_dir_callback) (const struct iso_tree_node *dir,
+ void *data,
+ int spaces);
+/**
+ * A function that prints verbose information about a file.
+ *
+ * \param dir The file about which to print information.
+ * \param data Unspecified function-dependent data.
+ * \param spaces The number of spaces to prepend to the output.
+ *
+ * \see iso_tree_print_verbose
+ */
+typedef void (*print_file_callback) (const struct iso_tree_node *file,
+ void *data,
+ int spaces);
+
+/**
+ * Recursively print a directory heirarchy. For each node in the directory
+ * heirarchy, call a callback function to print information more verbosely.
+ *
+ * \param root The root of the directory heirarchy to print.
+ * \param dir The callback function to call for each directory in the tree.
+ * \param file The callback function to call for each file in the tree.
+ * \param callback_data The data to pass to the callback functions.
+ * \param spaces The number of spaces to prepend to the output.
+ *
+ * \pre \p root is not NULL.
+ * \pre Neither of the callback functions modifies the directory heirarchy.
+ */
+void iso_tree_print_verbose(const struct iso_tree_node *root,
+ print_dir_callback dir,
+ print_file_callback file,
+ void *callback_data,
+ int spaces);
+
+#define ISO_ISDIR(n) S_ISDIR(n->attrib.st_mode)
+
+#endif /* LIBISO_TREE_H */
diff --git a/tags/ZeroTwoTwo/libisofs/util.c b/tags/ZeroTwoTwo/libisofs/util.c
new file mode 100755
index 00000000..186c7951
--- /dev/null
+++ b/tags/ZeroTwoTwo/libisofs/util.c
@@ -0,0 +1,577 @@
+/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */
+/* vim: set noet ts=8 sts=8 sw=8 : */
+
+/**
+ * Utility functions for the Libisofs library.
+ */
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "util.h"
+
+/* avoids warning and names in iso, joliet and rockridge can't be > 255 bytes
+ * anyway. There are at most 31 characters in iso level 1, 255 for rockridge,
+ * 64 characters (* 2 since UCS) for joliet. */
+#define NAME_BUFFER_SIZE 255
+
+int div_up(int n, int div)
+{
+ return (n + div - 1) / div;
+}
+
+int round_up(int n, int mul)
+{
+ return div_up(n, mul) * mul;
+}
+
+/* this function must always return a name
+ * since the caller never checks if a NULL
+ * is returned. It also avoids some warnings. */
+char *str2ascii(const char *src_arg)
+{
+ wchar_t wsrc_[NAME_BUFFER_SIZE];
+ char *src = (char*)wsrc_;
+ char *ret_;
+ char *ret;
+ mbstate_t state;
+ iconv_t conv;
+ size_t numchars;
+ size_t outbytes;
+ size_t inbytes;
+ size_t n;
+
+ if (!src_arg)
+ return NULL;
+
+ /* convert the string to a wide character string. Note: outbytes
+ * is in fact the number of characters in the string and doesn't
+ * include the last NULL character. */
+ memset(&state, 0, sizeof(state));
+ numchars = mbsrtowcs(wsrc_, &src_arg, NAME_BUFFER_SIZE-1, &state);
+ if (numchars < 0)
+ return NULL;
+
+ inbytes = numchars * sizeof(wchar_t);
+
+ ret_ = malloc(numchars+1);
+ outbytes = numchars;
+ ret = ret_;
+
+ /* initialize iconv */
+ conv = iconv_open("ASCII", "WCHAR_T");
+ if (conv == (iconv_t)-1)
+ return NULL;
+
+ n = iconv(conv, &src, &inbytes, &ret, &outbytes);
+ while(n == -1) {
+ /* The destination buffer is too small. Stops here. */
+ if(errno == E2BIG)
+ break;
+
+ /* An incomplete multi bytes sequence was found. We
+ * can't do anything here. That's quite unlikely. */
+ if(errno == EINVAL)
+ break;
+
+ /* The last possible error is an invalid multi bytes
+ * sequence. Just replace the character with a "_".
+ * Probably the character doesn't exist in ascii like
+ * "é, è, à , ç, ..." in French. */
+ *ret++ = '_';
+ outbytes--;
+
+ if(!outbytes)
+ break;
+
+ /* There was an error with one character but some other remain
+ * to be converted. That's probably a multibyte character.
+ * See above comment. */
+ src += sizeof(wchar_t);
+ inbytes -= sizeof(wchar_t);
+
+ if(!inbytes)
+ break;
+
+ n = iconv(conv, &src, &inbytes, &ret, &outbytes);
+ }
+
+ iconv_close(conv);
+
+ *ret='\0';
+ return ret_;
+}
+
+/* FIXME: C&P */
+uint16_t *str2ucs(const char *src_arg)
+{
+ wchar_t wsrc_[NAME_BUFFER_SIZE];
+ char *src = (char*)wsrc_;
+ char *ret_;
+ char *ret;
+ mbstate_t state;
+ iconv_t conv;
+ size_t outbytes;
+ size_t numchars;
+ size_t inbytes;
+ size_t n;
+
+ if (!src_arg)
+ return calloc(2, 1); /* empty UCS string */
+
+ /* convert the string to a wide character string. Note: outbytes
+ * is in fact the number of characters in the string and doesn't
+ * include the last NULL character. */
+ memset(&state, 0, sizeof(state));
+ numchars = mbsrtowcs(wsrc_, &src_arg, NAME_BUFFER_SIZE-1, &state);
+ if (numchars < 0)
+ return calloc(2, 1); /* empty UCS string */
+
+ inbytes = numchars * sizeof(wchar_t);
+
+ outbytes = numchars * sizeof(uint16_t);
+ ret_ = malloc ((numchars+1) * sizeof(uint16_t));
+ ret = ret_;
+
+ /* initialize iconv */
+ conv = iconv_open("UCS-2BE", "WCHAR_T");
+ if (conv == (iconv_t)-1)
+ return calloc(2, 1); /* empty UCS string */
+
+ n = iconv(conv, &src, &inbytes, &ret, &outbytes);
+ while(n == -1) {
+ /* The destination buffer is too small. Stops here. */
+ if(errno == E2BIG)
+ break;
+
+ /* An incomplete multi bytes sequence was found. We
+ * can't do anything here. That's quite unlikely. */
+ if(errno == EINVAL)
+ break;
+
+ /* The last possible error is an invalid multi bytes
+ * sequence. Just replace the character with a "_".
+ * Probably the character doesn't exist in ascii like
+ * "é, è, à , ç, ..." in French. */
+ *((uint16_t*) ret) = '_';
+ ret += sizeof(uint16_t);
+ outbytes -= sizeof(uint16_t);
+
+ if(!outbytes)
+ break;
+
+ /* There was an error with one character but some other remain
+ * to be converted. That's probably a multibyte character.
+ * See above comment. */
+ src += sizeof(wchar_t);
+ inbytes -= sizeof(wchar_t);
+
+ if(!inbytes)
+ break;
+
+ n = iconv(conv, &src, &inbytes, &ret, &outbytes);
+ }
+
+ iconv_close(conv);
+
+ /* close the ucs string */
+ *((uint16_t*) ret) = 0;
+
+ return (uint16_t*)ret_;
+}
+
+
+static int valid_d_char(char c)
+{
+ return (c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z') || (c == '_');
+}
+
+static int valid_a_char(char c)
+{
+ return (c >= ' ' && c <= '"') || (c >= '%' && c <= '?')
+ || (c >= 'A' && c <= 'Z')
+ || (c == '_');
+}
+
+static int valid_j_char(uint16_t c)
+{
+ return !(c < (uint16_t)' ' || c == (uint16_t)'*' || c == (uint16_t)'/'
+ || c == (uint16_t)':' || c == (uint16_t)';'
+ || c == (uint16_t)'?' || c == (uint16_t)'\\');
+}
+
+/* FIXME: where are these documented? */
+static int valid_p_char(char c)
+{
+ return (c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z')
+ || (c >= 'a' && c <= 'z')
+ || (c == '.') || (c == '_') || (c == '-');
+}
+
+static char *iso_dirid(const char *src, int size)
+{
+ char *ret = str2ascii(src);
+ size_t len, i;
+
+ if (!ret)
+ return NULL;
+
+ len = strlen(ret);
+ if (len > size) {
+ ret[size] = '\0';
+ len = size;
+ }
+ for (i = 0; i < len; i++) {
+ char c = toupper(ret[i]);
+ ret[i] = valid_d_char(c) ? c : '_';
+ }
+
+ return ret;
+}
+
+char *iso_1_dirid(const char *src)
+{
+ return iso_dirid(src, 8);
+}
+
+char *iso_2_dirid(const char *src)
+{
+ return iso_dirid(src, 31);
+}
+
+char *iso_1_fileid(const char *src_arg)
+{
+ char *src = str2ascii(src_arg);
+ char *dest;
+ char *dot; /* Position of the last dot in the
+ filename, will be used to calculate
+ lname and lext. */
+ int lname, lext, pos, i;
+
+ if (!src)
+ return NULL;
+
+ dest = malloc(15); /* 15 = 8 (name) + 1 (.) + 3 (ext) + 2
+ (;1) + 1 (\0) */
+ dot = strrchr(src, '.');
+
+ lext = dot ? strlen(dot + 1) : 0;
+ lname = strlen(src) - lext - (dot ? 1 : 0);
+
+ /* If we can't build a filename, return NULL. */
+ if (lname == 0 && lext == 0) {
+ free(src);
+ free(dest);
+ return NULL;
+ }
+
+ pos = 0;
+ /* Convert up to 8 characters of the filename. */
+ for (i = 0; i < lname && i < 8; i++) {
+ char c = toupper(src[i]);
+
+ dest[pos++] = valid_d_char(c) ? c : '_';
+ }
+ /* This dot is mandatory, even if there is no extension. */
+ dest[pos++] = '.';
+ /* Convert up to 3 characters of the extension, if any. */
+ for (i = 0; i < lext && i < 3; i++) {
+ char c = toupper(src[lname + 1 + i]);
+
+ dest[pos++] = valid_d_char(c) ? c : '_';
+ }
+ /* File versions are mandatory, even if they aren't used. */
+ dest[pos++] = ';';
+ dest[pos++] = '1';
+ dest[pos] = '\0';
+ dest = (char *)realloc(dest, pos + 1);
+
+ free(src);
+ return dest;
+}
+
+char *iso_2_fileid(const char *src_arg)
+{
+ char *src = str2ascii(src_arg);
+ char *dest;
+ char *dot;
+ int lname, lext, lnname, lnext, pos, i;
+
+ if (!src)
+ return NULL;
+
+ dest = malloc(34); /* 34 = 30 (name + ext) + 1 (.) + 2
+ (;1) + 1 (\0) */
+ dot = strrchr(src, '.');
+
+ /* Since the maximum length can be divided freely over the name and
+ extension, we need to calculate their new lengths (lnname and
+ lnext). If the original filename is too long, we start by trimming
+ the extension, but keep a minimum extension length of 3. */
+ if (dot == NULL || dot == src || *(dot + 1) == '\0') {
+ lname = strlen(src);
+ lnname = (lname > 30) ? 30 : lname;
+ lext = lnext = 0;
+ } else {
+ lext = strlen(dot + 1);
+ lname = strlen(src) - lext - 1;
+ lnext = (strlen(src) > 31 && lext > 3)
+ ? (lname < 27 ? 30 - lname : 3) : lext;
+ lnname = (strlen(src) > 31) ? 30 - lnext : lname;
+ }
+
+ if (lnname == 0 && lnext == 0) {
+ free(src);
+ free(dest);
+ return NULL;
+ }
+
+ pos = 0;
+ /* Convert up to lnname characters of the filename. */
+ for (i = 0; i < lnname; i++) {
+ char c = toupper(src[i]);
+
+ dest[pos++] = valid_d_char(c) ? c : '_';
+ }
+ dest[pos++] = '.';
+ /* Convert up to lnext characters of the extension, if any. */
+ for (i = 0; i < lnext; i++) {
+ char c = toupper(src[lname + 1 + i]);
+
+ dest[pos++] = valid_d_char(c) ? c : '_';
+ }
+ dest[pos++] = ';';
+ dest[pos++] = '1';
+ dest[pos] = '\0';
+ dest = (char *)realloc(dest, pos + 1);
+
+ free(src);
+ return dest;
+}
+
+char *
+iso_p_fileid(const char *src)
+{
+ char *ret = str2ascii(src);
+ size_t i, len;
+
+ if (!ret)
+ return NULL;
+ len = strlen(ret);
+ for (i = 0; i < len; i++) {
+ if (!valid_p_char(ret[i])) {
+ ret[i] = (uint16_t)'_';
+ }
+ }
+ return ret;
+}
+
+uint16_t *
+iso_j_id(const char *src_arg)
+{
+ uint16_t *j_str = str2ucs(src_arg);
+ size_t len = ucslen(j_str);
+ size_t n;
+
+ if (len > 128) {
+ j_str[128] = '\0';
+ len = 128;
+ }
+
+ for (n = 0; n < len; n++)
+ if (!valid_j_char(j_str[n]))
+ j_str[n] = '_';
+ return j_str;
+}
+
+void iso_lsb(uint8_t *buf, uint32_t num, int bytes)
+{
+ int i;
+
+ assert(bytes <= 4);
+
+ for (i = 0; i < bytes; ++i)
+ buf[i] = (num >> (8 * i)) & 0xff;
+}
+
+void iso_msb(uint8_t *buf, uint32_t num, int bytes)
+{
+ int i;
+
+ assert(bytes <= 4);
+
+ for (i = 0; i < bytes; ++i)
+ buf[bytes - 1 - i] = (num >> (8 * i)) & 0xff;
+}
+
+void iso_bb(uint8_t *buf, uint32_t num, int bytes)
+{
+ iso_lsb(buf, num, bytes);
+ iso_msb(buf+bytes, num, bytes);
+}
+
+
+void iso_datetime_7(unsigned char *buf, time_t t)
+{
+ static int tzsetup = 0;
+ static int tzoffset;
+ struct tm tm;
+
+ if (!tzsetup) {
+ tzset();
+
+ tzoffset = -timezone / 60 / 15;
+ if (tzoffset < -48)
+ tzoffset += 101;
+
+ tzsetup = 1;
+ }
+
+ localtime_r(&t, &tm);
+
+ buf[0] = tm.tm_year;
+ buf[1] = tm.tm_mon + 1;
+ buf[2] = tm.tm_mday;
+ buf[3] = tm.tm_hour;
+ buf[4] = tm.tm_min;
+ buf[5] = tm.tm_sec;
+ buf[6] = tzoffset;
+}
+
+time_t iso_datetime_read_7(const uint8_t *buf)
+{
+ struct tm tm;
+
+ tm.tm_year = buf[0];
+ tm.tm_mon = buf[1] + 1;
+ tm.tm_mday = buf[2];
+ tm.tm_hour = buf[3];
+ tm.tm_min = buf[4];
+ tm.tm_sec = buf[5];
+
+ return mktime(&tm) - buf[6] * 60 * 60;
+}
+
+void iso_datetime_17(unsigned char *buf, time_t t)
+{
+ static int tzsetup = 0;
+ static int tzoffset;
+ struct tm tm;
+
+ if (t == (time_t) - 1) {
+ /* unspecified time */
+ memset(buf, '0', 16);
+ buf[16] = 0;
+ } else {
+ if (!tzsetup) {
+ tzset();
+
+ tzoffset = -timezone / 60 / 15;
+ if (tzoffset < -48)
+ tzoffset += 101;
+
+ tzsetup = 1;
+ }
+
+ localtime_r(&t, &tm);
+
+ sprintf((char*)&buf[0], "%04d", tm.tm_year + 1900);
+ sprintf((char*)&buf[4], "%02d", tm.tm_mon + 1);
+ sprintf((char*)&buf[6], "%02d", tm.tm_mday);
+ sprintf((char*)&buf[8], "%02d", tm.tm_hour);
+ sprintf((char*)&buf[10], "%02d", tm.tm_min);
+ sprintf((char*)&buf[12], "%02d", MIN(59, tm.tm_sec));
+ memcpy(&buf[14], "00", 2);
+ buf[16] = tzoffset;
+ }
+}
+
+time_t iso_datetime_read_17(const uint8_t *buf)
+{
+ struct tm tm;
+
+ sscanf((char*)&buf[0], "%4d", &tm.tm_year);
+ sscanf((char*)&buf[4], "%2d", &tm.tm_mon);
+ sscanf((char*)&buf[6], "%2d", &tm.tm_mday);
+ sscanf((char*)&buf[8], "%2d", &tm.tm_hour);
+ sscanf((char*)&buf[10], "%2d", &tm.tm_min);
+ sscanf((char*)&buf[12], "%2d", &tm.tm_sec);
+ tm.tm_year -= 1900;
+ tm.tm_mon -= 1;
+
+ return mktime(&tm) - buf[16] * 60 * 60;
+}
+
+size_t ucslen(const uint16_t *str)
+{
+ int i;
+
+ for (i=0; str[i]; i++)
+ ;
+ return i;
+}
+
+/**
+ * Although each character is 2 bytes, we actually compare byte-by-byte
+ * (thats what the spec says).
+ */
+int ucscmp(const uint16_t *s1, const uint16_t *s2)
+{
+ const char *s = (const char*)s1;
+ const char *t = (const char*)s2;
+ size_t len1 = ucslen(s1);
+ size_t len2 = ucslen(s2);
+ size_t i, len = MIN(len1, len2) * 2;
+
+ for (i=0; i < len; i++) {
+ if (s[i] < t[i]) {
+ return -1;
+ } else if (s[i] > t[i]) {
+ return 1;
+ }
+ }
+
+ if (len1 < len2)
+ return -1;
+ else if (len1 > len2)
+ return 1;
+ return 0;
+}
+
+uint32_t iso_read_lsb(const uint8_t *buf, int bytes)
+{
+ int i;
+ uint32_t ret = 0;
+
+ for (i=0; i
+#include
+#include
+
+#ifndef MAX
+# define MAX(a, b) (((a) > (b)) ? (a) : (b))
+#endif
+
+#ifndef MIN
+# define MIN(a, b) (((a) < (b)) ? (a) : (b))
+#endif
+
+extern inline int div_up(int n, int div)
+{
+ return (n + div - 1) / div;
+}
+
+extern inline int round_up(int n, int mul)
+{
+ return div_up(n, mul) * mul;
+}
+
+wchar_t *towcs(const char *);
+char *str2ascii(const char*);
+uint16_t *str2ucs(const char*);
+
+/**
+ * Create a level 1 directory identifier.
+ */
+char *iso_1_dirid(const char *src);
+
+/**
+ * Create a level 2 directory identifier.
+ */
+char *iso_2_dirid(const char *src);
+
+/**
+ * Create a level 1 file identifier that consists of a name, extension and
+ * version number. The resulting string will have a file name of maximum
+ * length 8, followed by a separator (.), an optional extension of maximum
+ * length 3, followed by a separator (;) and a version number (digit 1).
+ * @return NULL if the original name and extension both are of length 0.
+ */
+char *iso_1_fileid(const char *src);
+
+/**
+ * Create a level 2 file identifier that consists of a name, extension and
+ * version number. The combined file name and extension length will not exceed
+ * 30, the name and extension will be separated (.), and the extension will be
+ * followed by a separator (;) and a version number (digit 1).
+ * @return NULL if the original name and extension both are of length 0.
+ */
+char *iso_2_fileid(const char *src);
+
+/**
+ * Create a Joliet file or directory identifier that consists of a name,
+ * extension and version number. The combined name and extension length will
+ * not exceed 128 bytes, the name and extension will be separated (.),
+ * and the extension will be followed by a separator (;) and a version number
+ * (digit 1). All characters consist of 2 bytes and the resulting string is
+ * NULL-terminated by a 2-byte NULL. Requires the locale to be set correctly.
+ *
+ * @param size will be set to the size (in bytes) of the identifier.
+ * @return NULL if the original name and extension both are of length 0 or the conversion from the current codeset to UCS-2BE is not available.
+ */
+uint16_t *iso_j_id(const char *src);
+
+/**
+ * FIXME: what are the requirements for these next two? Is this for RR?
+ *
+ * Create a POSIX portable file name that consists of a name and extension.
+ * The resulting file name will not exceed 250 characters.
+ * @return NULL if the original name and extension both are of length 0.
+ */
+char *iso_p_fileid(const char *src);
+
+/**
+ * Create a POSIX portable directory name.
+ * The resulting directory name will not exceed 250 characters.
+ * @return NULL if the original name is of length 0.
+ */
+char *iso_p_dirid(const char *src);
+
+void iso_lsb(uint8_t *buf, uint32_t num, int bytes);
+void iso_msb(uint8_t *buf, uint32_t num, int bytes);
+void iso_bb(uint8_t *buf, uint32_t num, int bytes);
+
+uint32_t iso_read_lsb(const uint8_t *buf, int bytes);
+uint32_t iso_read_msb(const uint8_t *buf, int bytes);
+uint32_t iso_read_bb(const uint8_t *buf, int bytes);
+
+/** Records the date/time into a 7 byte buffer (9.1.5) */
+void iso_datetime_7(uint8_t *buf, time_t t);
+
+/** Records the date/time into a 17 byte buffer (8.4.26.1) */
+void iso_datetime_17(uint8_t *buf, time_t t);
+
+time_t iso_datetime_read_7(const uint8_t *buf);
+time_t iso_datetime_read_17(const uint8_t *buf);
+
+/**
+ * Like strlen, but for Joliet strings.
+ */
+size_t ucslen(const uint16_t *str);
+
+/**
+ * Like strcmp, but for Joliet strings.
+ */
+int ucscmp(const uint16_t *s1, const uint16_t *s2);
+
+#endif /* LIBISO_UTIL_H */
diff --git a/tags/ZeroTwoTwo/libisofs/volume.c b/tags/ZeroTwoTwo/libisofs/volume.c
new file mode 100755
index 00000000..65cbbbf9
--- /dev/null
+++ b/tags/ZeroTwoTwo/libisofs/volume.c
@@ -0,0 +1,189 @@
+/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */
+/* vim: set ts=8 sts=8 sw=8 noet : */
+
+#include
+#include
+#include
+
+#include "libisofs.h"
+#include "tree.h"
+#include "util.h"
+#include "volume.h"
+
+struct iso_volset*
+iso_volset_new(struct iso_volume *vol, const char *id)
+{
+ struct iso_volset *volset = calloc(1, sizeof(struct iso_volset));
+
+ volset->volset_size = 1;
+ volset->refcount = 1;
+ volset->volume = malloc(sizeof(void *));
+ volset->volume[0] = vol;
+ volset->volset_id = strdup(id);
+
+ vol->refcount++;
+ return volset;
+}
+
+void
+iso_volset_free(struct iso_volset *volset)
+{
+ if (--volset->refcount < 1) {
+ int i;
+ for (i = 0; i < volset->volset_size; i++) {
+ iso_volume_free(volset->volume[i]);
+ }
+ free(volset->volume);
+ free(volset->volset_id);
+ }
+}
+
+struct iso_volume*
+iso_volume_new(const char *volume_id,
+ const char *publisher_id,
+ const char *data_preparer_id)
+{
+ return iso_volume_new_with_root(volume_id,
+ publisher_id,
+ data_preparer_id,
+ NULL);
+}
+
+struct iso_volume*
+iso_volume_new_with_root(const char *volume_id,
+ const char *publisher_id,
+ const char *data_preparer_id,
+ struct iso_tree_node *root)
+{
+ struct iso_volume *volume;
+
+ volume = calloc(1, sizeof(struct iso_volume));
+ volume->refcount = 1;
+
+ volume->root = root ? root : iso_tree_new_root(volume);
+
+ if (volume_id != NULL)
+ volume->volume_id = strdup(volume_id);
+ if (publisher_id != NULL)
+ volume->publisher_id = strdup(publisher_id);
+ if (data_preparer_id != NULL)
+ volume->data_preparer_id = strdup(data_preparer_id);
+ return volume;
+}
+
+void
+iso_volume_free(struct iso_volume *volume)
+{
+ /* Only free if no references are in use. */
+ if (--volume->refcount < 1) {
+ iso_tree_free(volume->root);
+
+ free(volume->volume_id);
+ free(volume->publisher_id);
+ free(volume->data_preparer_id);
+
+ free(volume);
+ }
+}
+
+struct iso_tree_node *
+iso_volume_get_root(const struct iso_volume *volume)
+{
+ return volume->root;
+}
+
+struct iso_tree_node *
+iso_tree_volume_path_to_node(struct iso_volume *volume, const char *path)
+{
+ struct iso_tree_node *node;
+ char *ptr, *brk_info, *component;
+
+ /* get the first child at the root of the volume
+ * that is "/" */
+ node=iso_volume_get_root(volume);
+ if (!strcmp (path, "/"))
+ return node;
+
+ if (!node->nchildren)
+ return NULL;
+
+ /* the name of the nodes is in wide characters so first convert path
+ * into wide characters. */
+ ptr = strdup(path);
+
+ /* get the first component of the path */
+ component=strtok_r(ptr, "/", &brk_info);
+ while (component) {
+ size_t max;
+ size_t i;
+
+ /* search among all the children of this directory if this path component exists */
+ max=node->nchildren;
+ for (i=0; i < max; i++) {
+ if (!strcmp(component, node->children[i]->name)) {
+ node=node->children[i];
+ break;
+ }
+ }
+
+ /* see if a node could be found */
+ if (i==max) {
+ node=NULL;
+ break;
+ }
+
+ component=strtok_r(NULL, "/", &brk_info);
+ }
+
+ free(ptr);
+ return node;
+}
+
+struct iso_tree_node *
+iso_tree_volume_add_path(struct iso_volume *volume,
+ const char *disc_path,
+ const char *path)
+{
+ char *tmp;
+ struct iso_tree_node *node;
+ struct iso_tree_node *parent_node;
+
+ tmp=strdup(disc_path);
+ parent_node = iso_tree_volume_path_to_node(volume, dirname(tmp));
+ free(tmp);
+
+ if (!parent_node)
+ return NULL;
+
+ node = iso_tree_radd_dir(parent_node, path);
+ if (!node)
+ return NULL;
+
+ tmp=strdup(disc_path);
+ iso_tree_node_set_name(node, basename(tmp));
+ free(tmp);
+
+ return node;
+}
+
+struct iso_tree_node *
+iso_tree_volume_add_new_dir(struct iso_volume *volume,
+ const char *disc_path)
+{
+ char *tmp;
+ struct iso_tree_node *node;
+ struct iso_tree_node *parent_node;
+
+ tmp=strdup(disc_path);
+ parent_node = iso_tree_volume_path_to_node(volume, dirname(tmp));
+ free(tmp);
+
+ if (!parent_node)
+ return NULL;
+
+ tmp=strdup(disc_path);
+ node = iso_tree_add_new_dir(parent_node, basename(tmp));
+ free(tmp);
+
+ return node;
+}
diff --git a/tags/ZeroTwoTwo/libisofs/volume.h b/tags/ZeroTwoTwo/libisofs/volume.h
new file mode 100755
index 00000000..89031860
--- /dev/null
+++ b/tags/ZeroTwoTwo/libisofs/volume.h
@@ -0,0 +1,45 @@
+/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */
+/* vim: set noet sts=8 ts=8 sw=8 : */
+
+/**
+ * Extra declarations for use with the iso_volume structure.
+ */
+
+#ifndef LIBISO_VOLUME_H
+#define LIBISO_VOLUME_H
+
+#include "libisofs.h"
+
+/**
+ * Data volume.
+ */
+struct iso_volume
+{
+ int refcount; /**< Number of used references to this
+ volume. */
+
+ struct iso_tree_node *root; /**< Root of the directory tree for the
+ volume. */
+
+ char *volume_id; /**< Volume identifier. */
+ char *publisher_id; /**< Volume publisher. */
+ char *data_preparer_id; /**< Volume data preparer. */
+};
+
+/**
+ * A set of data volumes.
+ */
+struct iso_volset
+{
+ int refcount;
+
+ struct iso_volume **volume; /**< The volumes belonging to this
+ volume set. */
+ int volset_size; /**< The number of volumes in this
+ volume set. */
+
+ char *volset_id; /**< The id of this volume set, encoded
+ in the current locale. */
+};
+
+#endif /* __ISO_VOLUME */
diff --git a/tags/ZeroTwoTwo/test/Makefile b/tags/ZeroTwoTwo/test/Makefile
new file mode 100644
index 00000000..062350dd
--- /dev/null
+++ b/tags/ZeroTwoTwo/test/Makefile
@@ -0,0 +1,4 @@
+all clean:
+ $(MAKE) -C .. -$(MAKEFLAGS) $@
+
+.PHONY: all clean
diff --git a/tags/ZeroTwoTwo/test/iso.c b/tags/ZeroTwoTwo/test/iso.c
new file mode 100644
index 00000000..d20c631c
--- /dev/null
+++ b/tags/ZeroTwoTwo/test/iso.c
@@ -0,0 +1,107 @@
+/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */
+/* vim: set ts=8 sts=8 sw=8 noet : */
+
+#define _GNU_SOURCE
+
+#include "libisofs.h"
+#include "libburn/libburn.h"
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#define SECSIZE 2048
+
+const char * const optstring = "JRL:h";
+extern char *optarg;
+extern int optind;
+
+void usage()
+{
+ printf("test [OPTIONS] DIRECTORY OUTPUT\n");
+}
+
+void help()
+{
+ printf(
+"Options:\n"
+" -J Add Joliet support\n"
+" -R Add Rock Ridge support\n"
+" -L Set the ISO level (1 or 2)\n"
+" -h Print this message\n"
+);
+}
+
+int main(int argc, char **argv)
+{
+ struct iso_volset *volset;
+ struct iso_volume *volume;
+ struct iso_tree_node *root;
+ struct burn_source *src;
+ unsigned char buf[2048];
+ FILE *fd;
+ int c;
+ int level=1, flags=0;
+
+ while ((c = getopt(argc, argv, optstring)) != -1) {
+ switch(c) {
+ case 'h':
+ usage();
+ help();
+ exit(0);
+ break;
+ case 'J':
+ flags |= ECMA119_JOLIET;
+ break;
+ case 'R':
+ flags |= ECMA119_ROCKRIDGE;
+ break;
+ case 'L':
+ level = atoi(optarg);
+ break;
+ case '?':
+ usage();
+ exit(1);
+ break;
+ }
+ }
+
+ if (argc < 2) {
+ printf ("must pass directory to build iso from\n");
+ usage();
+ return 1;
+ }
+ if (argc < 3) {
+ printf ("must supply output file\n");
+ usage();
+ return 1;
+ }
+ fd = fopen(argv[optind+1], "w");
+ if (!fd) {
+ err(1, "error opening output file");
+ }
+
+ root = iso_tree_radd_dir(NULL, argv[optind]);
+ if (!root) {
+ err(1, "error opening input directory");
+ }
+ volume = iso_volume_new_with_root( "VOLID", "PUBID", "PREPID", root );
+ volset = iso_volset_new( volume, "VOLSETID" );
+
+ src = iso_source_new_ecma119(volset, 0, level, flags);
+
+ while (src->read(src, buf, 2048) == 2048) {
+ fwrite(buf, 1, 2048, fd);
+ }
+ fclose(fd);
+
+ return 0;
+}
diff --git a/tags/ZeroTwoTwo/test/iso.py b/tags/ZeroTwoTwo/test/iso.py
new file mode 100644
index 00000000..61da5fd7
--- /dev/null
+++ b/tags/ZeroTwoTwo/test/iso.py
@@ -0,0 +1,297 @@
+
+import struct
+import tree
+import sys
+
+voldesc_fmt = "B" "5s" "B" "2041x"
+
+# all these fields are common between the pri and sec voldescs
+privoldesc_fmt = "B" "5s" "B" "x" "32s" "32s" "8x" "8s" "32x" "4s" "4s" "4s" "8s" "4s4s" "4s4s" "34s" "128s" \
+ "128s" "128s" "128s" "37s" "37s" "37s" "17s" "17s" "17s" "17s" "B" "x" "512s" "653x"
+
+# the fields unique to the sec_vol_desc
+secvoldesc_fmt = "x" "5x" "x" "B" "32x" "32x" "8x" "8x" "32s" "4x" "4x" "4x" "8x" "4x4x" "4x4x" "34x" "128x" \
+ "128x" "128x" "128x" "37x" "37x" "37x" "17x" "17x" "17x" "17x" "x" "x" "512x" "653x"
+
+dirrecord_fmt = "B" "B" "8s" "8s" "7s" "B" "B" "B" "4s" "B" # + file identifier, padding field and SU area
+
+pathrecord_fmt = "B" "B" "4s" "2s" # + directory identifier and padding field
+
+def read_bb(str, le, be):
+ val1, = struct.unpack(le, str)
+ val2, = struct.unpack(be, str)
+ if val1 != val2:
+ print "val1=%d, val2=%d" % (val1, val2)
+ raise AssertionError, "values are not equal in dual byte-order field"
+ return val1
+
+def read_bb4(str):
+ return read_bb(str, "4xI")
+
+def read_bb2(str):
+ return read_bb(str, "2xH")
+
+def read_lsb4(str):
+ return struct.unpack("I", str)[0]
+
+def read_msb2(str):
+ return struct.unpack(">H", str)[0]
+
+class VolDesc(object):
+ def __init__(self, data):
+ print "fmt len=%d, data len=%d" % ( struct.calcsize(voldesc_fmt), len(data) )
+ self.vol_desc_type, self.standard_id, self.vol_desc_version = struct.unpack(voldesc_fmt, data)
+
+class PriVolDesc(VolDesc):
+ def __init__(self, data):
+ self.vol_desc_type, \
+ self.standard_id, \
+ self.vol_desc_version, \
+ self.system_id, \
+ self.volume_id, \
+ self.vol_space_size, \
+ self.vol_set_size, \
+ self.vol_seq_num, \
+ self.block_size, \
+ self.path_table_size, \
+ self.l_table_pos, \
+ self.l_table2_pos, \
+ self.m_table_pos, \
+ self.m_table2_pos, \
+ self.root_record, \
+ self.volset_id, \
+ self.publisher_id, \
+ self.preparer_id, \
+ self.application_id, \
+ self.copyright_file, \
+ self.abstract_file, \
+ self.bibliographic_file, \
+ self.creation_timestamp, \
+ self.modification_timestamp, \
+ self.expiration_timestamp, \
+ self.effective_timestamp, \
+ self.file_struct_version, \
+ self.application_use = struct.unpack(privoldesc_fmt, data)
+
+ # take care of reading the integer types
+ self.vol_space_size = read_bb4(self.vol_space_size)
+ self.vol_set_size = read_bb2(self.vol_set_size)
+ self.vol_seq_num = read_bb2(self.vol_seq_num)
+ self.block_size = read_bb2(self.block_size)
+ self.path_table_size = read_bb4(self.path_table_size)
+ self.l_table_pos = read_lsb4(self.l_table_pos)
+ self.l_table2_pos = read_lsb4(self.l_table2_pos)
+ self.m_table_pos = read_msb4(self.m_table_pos)
+ self.m_table2_pos = read_msb4(self.m_table2_pos)
+
+ # parse the root directory record
+ self.root_record = DirRecord(self.root_record)
+
+ def readPathTables(self, file):
+ file.seek( self.block_size * self.l_table_pos )
+ self.l_table = PathTable( file.read(self.path_table_size), 0 )
+ file.seek( self.block_size * self.m_table_pos )
+ self.m_table = PathTable( file.read(self.path_table_size), 1 )
+
+ if self.l_table2_pos:
+ file.seek( self.block_size * self.l_table2_pos )
+ self.l_table2 = PathTable( file.read(self.path_table_size), 0 )
+ else:
+ self.l_table2 = None
+
+ if self.m_table2_pos:
+ file.seek( self.block_size * self.m_table2_pos )
+ self.m_table2 = PathTable( file.read(self.path_table_size), 1 )
+ else:
+ self.m_table2 = None
+
+ def toTree(self, isofile):
+ ret = tree.Tree(isofile=isofile.name)
+ ret.root = self.root_record.toTreeNode(parent=None, isofile=isofile)
+ return ret
+
+class SecVolDesc(PriVolDesc):
+ def __init__(self, data):
+ super(SecVolDesc,self).__init__(data)
+ self.flags, self.escape_sequences = struct.unpack(secvoldesc_fmt, data)
+
+# return a single volume descriptor of the appropriate type
+def readVolDesc(data):
+ desc = VolDesc(data)
+ if desc.standard_id != "CD001":
+ print "Unexpected standard_id " +desc.standard_id
+ return None
+ if desc.vol_desc_type == 1:
+ return PriVolDesc(data)
+ elif desc.vol_desc_type == 2:
+ return SecVolDesc(data)
+ elif desc.vol_desc_type == 3:
+ print "I don't know about partitions yet!"
+ return None
+ elif desc.vol_desc_type == 255:
+ return desc
+ else:
+ print "Unknown volume descriptor type %d" % (desc.vol_desc_type,)
+ return None
+
+def readVolDescSet(file):
+ ret = [ readVolDesc(file.read(2048)) ]
+ while ret[-1].vol_desc_type != 255:
+ ret.append( readVolDesc(file.read(2048)) )
+
+ for vol in ret:
+ if vol.vol_desc_type == 1 or vol.vol_desc_type == 2:
+ vol.readPathTables(file)
+
+ return ret
+
+class DirRecord:
+ def __init__(self, data):
+ self.len_dr, \
+ self.len_xa, \
+ self.block, \
+ self.len_data, \
+ self.timestamp, \
+ self.flags, \
+ self.unit_size, \
+ self.gap_size, \
+ self.vol_seq_number, \
+ self.len_fi = struct.unpack(dirrecord_fmt, data[:33])
+ self.children = []
+
+ if self.len_dr > len(data):
+ raise AssertionError, "Error: not enough data to read in DirRecord()"
+ elif self.len_dr < 34:
+ raise AssertionError, "Error: directory record too short"
+
+ fmt = str(self.len_fi) + "s"
+ if self.len_fi % 2 == 0:
+ fmt += "1x"
+ len_su = self.len_dr - (33 + self.len_fi + 1 - (self.len_fi % 2))
+ fmt += str(len_su) + "s"
+
+ if len(data) >= self.len_dr:
+ self.file_id, self.su = struct.unpack(fmt, data[33 : self.len_dr])
+ else:
+ print "Error: couldn't read file_id: not enough data"
+ self.file_id = "BLANK"
+ self.su = ""
+
+ # convert to integers
+ self.block = read_bb4(self.block)
+ self.len_data = read_bb4(self.len_data)
+ self.vol_seq_number = read_bb2(self.vol_seq_number)
+
+ def toTreeNode(self, parent, isofile, path=""):
+ ret = tree.TreeNode(parent=parent, isofile=isofile.name)
+ if len(path) > 0:
+ path += "/"
+ path += self.file_id
+ ret.path = path
+
+ if self.flags & 2: # we are a directory, recurse
+ isofile.seek( 2048 * self.block )
+ data = isofile.read( self.len_data )
+ pos = 0
+ while pos < self.len_data:
+ try:
+ child = DirRecord( data[pos:] )
+ pos += child.len_dr
+ if child.len_fi == 1 and (child.file_id == "\x00" or child.file_id == "\x01"):
+ continue
+ print "read child named " +child.file_id
+ self.children.append( child )
+ ret.children.append( child.toTreeNode(ret, isofile, path) )
+ except AssertionError:
+ print "Couldn't read child of directory %s, position is %d, len is %d" % \
+ (path, pos, self.len_data)
+ raise
+
+ return ret
+
+class PathTableRecord:
+ def __init__(self, data, readint2, readint4):
+ self.len_di, self.len_xa, self.block, self.parent_number = struct.unpack(pathrecord_fmt, data[:8])
+
+ if len(data) < self.len_di + 8:
+ raise AssertionError, "Error: not enough data to read path table record"
+
+ fmt = str(self.len_di) + "s"
+ self.dir_id, = struct.unpack(fmt, data[8:8+self.len_di])
+
+ self.block = readint4(self.block)
+ self.parent_number = readint2(self.parent_number)
+
+class PathTable:
+ def __init__(self, data, m_type):
+ if m_type:
+ readint2 = read_msb2
+ readint4 = read_msb4
+ else:
+ readint2 = read_lsb2
+ readint4 = read_lsb4
+ pos = 0
+ self.records = []
+ while pos < len(data):
+ try:
+ self.records.append( PathTableRecord(data[pos:], readint2, readint4) )
+ print "Read path record %d: dir_id %s, block %d, parent_number %d" %\
+ (len(self.records), self.records[-1].dir_id, self.records[-1].block, self.records[-1].parent_number)
+ pos += self.records[-1].len_di + 8
+ pos += pos % 2
+ except AssertionError:
+ print "Last successfully read path table record had dir_id %s, block %d, parent_number %d" % \
+ (self.records[-1].dir_id, self.records[-1].block, self.records[-1].parent_number)
+ print "Error was near offset %x" % (pos,)
+ raise
+
+ def findRecord(self, dir_id, block, parent_number):
+ number=1
+ for record in self.records:
+ if record.dir_id == dir_id and record.block == block and record.parent_number == parent_number:
+ return number, record
+ number += 1
+
+ return None, None
+
+ # check this path table for consistency against the actual directory heirarchy
+ def crossCheckDirRecords(self, root, parent_number=1):
+ number, rec = self.findRecord(root.file_id, root.block, parent_number)
+
+ if not rec:
+ print "Error: directory record parent_number %d, dir_id %s, block %d doesn't match a path table record" % \
+ (parent_number, root.file_id, root.block)
+ parent = self.records[parent_number]
+ print "Parent has parent_number %d, dir_id %s, block %d" % (parent.parent_number, parent.dir_id, parent.block)
+ return 0
+
+ for child in root.children:
+ if child.flags & 2:
+ self.crossCheckDirRecords(child, number)
+
+
+if len(sys.argv) != 2:
+ print "Please enter the name of the .iso file to open"
+ sys.exit(1)
+
+f = file(sys.argv[1])
+f.seek(2048 * 16) # system area
+volumes = readVolDescSet(f)
+vol = volumes[0]
+t = vol.toTree(f)
+vol.l_table.crossCheckDirRecords(vol.root_record)
+vol.m_table.crossCheckDirRecords(vol.root_record)
+
+vol = volumes[1]
+try:
+ t = vol.toTree(f)
+ vol.l_table.crossCheckDirRecords(vol.root_record)
+ vol.m_table.crossCheckDirRecords(vol.root_record)
+except AttributeError:
+ pass
diff --git a/tags/ZeroTwoTwo/test/libburner.c b/tags/ZeroTwoTwo/test/libburner.c
new file mode 100644
index 00000000..85b81f5f
--- /dev/null
+++ b/tags/ZeroTwoTwo/test/libburner.c
@@ -0,0 +1,680 @@
+
+/* test/libburner.c , API illustration of burning a single data track to CD */
+/* Copyright (C) 2005 - 2006 Thomas Schmitt */
+/* Provided under GPL, see also "License and copyright aspects" at file end */
+
+
+/** Overview
+
+ libburner is a minimal demo application for the library libburn as provided
+ on http://libburn.pykix.org . It can list the available devices, can
+ blank a CD-RW and can burn to CD-R or CD-RW.
+ It's main purpose, nevertheless, is to show you how to use libburn and also
+ to serve the libburn team as reference application. libburner.c does indeed
+ define the standard way how above three gestures can be implemented and
+ stay upward compatible for a good while.
+
+ Before you can do anything, you have to initialize libburn by
+ burn_initialize()
+ as it is done in main() at the end of this file. Then you aquire a
+ drive in an appropriate way conforming to the API. The two main
+ approaches are shown here in application functions:
+ libburner_aquire_by_adr() demonstrates usage as of cdrecord traditions
+ libburner_aquire_by_driveno() demonstrates a scan-and-choose approach
+ With that aquired drive you can blank a CD-RW
+ libburner_blank_disc()
+ Between blanking and burning one eventually has to reload the drive status
+ libburner_regrab()
+ With the aquired drive you can burn to CD-R or blank CD-RW
+ libburner_payload()
+ When everything is done, main() releases the drive and shuts down libburn:
+ burn_drive_release();
+ burn_finish()
+
+*/
+
+/* We shall prepare for times when more than 2 GB of data are to be handled.
+ This gives POSIX-ly 64 bit off_t */
+#ifndef _LARGEFILE_SOURCE
+#define _LARGEFILE_SOURCE 1
+#endif
+#ifndef _FILE_OFFSET_BITS
+#define _FILE_OFFSET_BITS 64
+#endif
+
+/** See this for the decisive API specs . libburn.h is The Original */
+#include
+
+/* libburn is intended for Linux systems with kernel 2.4 or 2.6 for now */
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+
+/** For simplicity i use global variables to represent the drives.
+ Drives are systemwide global, so we do not give away much of good style.
+*/
+
+/** This list will hold the drives known to libburn. This might be all CD
+ drives of the system and thus might impose severe impact on the system.
+*/
+static struct burn_drive_info *drive_list;
+
+/** If you start a long lasting operation with drive_count > 1 then you are
+ not friendly to the users of other drives on those systems. Beware. */
+static unsigned int drive_count;
+
+/** This variable indicates wether the drive is grabbed and must be
+ finally released */
+static int drive_is_grabbed = 0;
+
+
+/** Here you may enable simulated burn by default. This does not apply to
+ blanking. Anyway, some CD recorders obey the request to simulate, some do
+ not. Explicit options are: --burn_for_real and --try_to_simulate
+*/
+static int simulate_burn = 0;
+
+
+/* Some in-advance definitions to allow a more comprehensive ordering
+ of the functions and their explanations in here */
+int libburner_aquire_by_adr(char *drive_adr);
+int libburner_aquire_by_driveno(int *drive_no);
+
+
+/* ------------------------------- API gestures ---------------------------- */
+
+/** You need to aquire a drive before burning. The API offers this as one
+ compact call and alternatively as application controllable gestures of
+ whitelisting, scanning for drives and finally grabbing one of them.
+
+ If you have a persistent address of the drive, then the compact call is
+ to prefer because it only touches one drive. On modern Linux kernels,
+ there should be no fatal disturbance of ongoing burns of other libburn
+ instances with any of our approaches. We use open(O_EXCL) by default.
+ On /dev/hdX it should cooperate with growisofs and some cdrecord variants.
+ On /dev/sgN versus /dev/scdM expect it not to respect other programs.
+*/
+int libburner_aquire_drive(char *drive_adr, int *driveno)
+{
+ int ret;
+
+ if(drive_adr != NULL && drive_adr[0] != 0)
+ ret = libburner_aquire_by_adr(drive_adr);
+ else
+ ret = libburner_aquire_by_driveno(driveno);
+ return ret;
+}
+
+
+/** If the persistent drive address is known, then this approach is much
+ more un-obtrusive to the systemwide livestock of drives. Only the
+ given drive device will be opened during this procedure.
+*/
+int libburner_aquire_by_adr(char *drive_adr)
+{
+ int ret;
+
+ printf("Aquiring drive '%s' ...\n",drive_adr);
+ ret = burn_drive_scan_and_grab(&drive_list,drive_adr,1);
+ if (ret <= 0) {
+ fprintf(stderr,"FAILURE with persistent drive address '%s'\n",
+ drive_adr);
+ if (strncmp(drive_adr,"/dev/sg",7) != 0 &&
+ strncmp(drive_adr,"/dev/hd",7) != 0)
+ fprintf(stderr,"\nHINT: Consider addresses like '/dev/hdc' or '/dev/sg0'\n");
+ } else {
+ printf("Done\n");
+ drive_is_grabbed = 1;
+ }
+ return ret;
+}
+
+
+/** This method demonstrates how to use libburn without knowing a persistent
+ drive address in advance. It has to make sure that after assessing the list
+ of available drives, all unwanted drives get closed again. As long as they
+ are open, no other libburn instance can see them. This is an intended
+ locking feature. The application is responsible for giving up the locks
+ by either burn_drive_release() (only after burn_drive_grab() !),
+ burn_drive_info_forget(), burn_drive_info_free(), or burn_finish().
+ @param driveno the index number in libburn's drive list. This will get
+ set to 0 on success and will then be the drive index to
+ use in the further dourse of processing.
+ @return 1 success , <= 0 failure
+*/
+int libburner_aquire_by_driveno(int *driveno)
+{
+ char adr[BURN_DRIVE_ADR_LEN];
+ int ret, i;
+
+ printf("Beginning to scan for devices ...\n");
+ while (!burn_drive_scan(&drive_list, &drive_count))
+ usleep(1002);
+ if (drive_count <= 0 && *driveno >= 0) {
+ printf("FAILED (no drives found)\n");
+ return 0;
+ }
+ printf("Done\n");
+
+ /*
+ Interactive programs may choose the drive number at this moment.
+
+ drive[0] to drive[drive_count-1] are struct burn_drive_info
+ as defined in libburn/libburn.h . This structure is part of API
+ and thus will strive for future compatibility on source level.
+ Have a look at the info offered.
+ Caution: do not take .location for drive address. Always use
+ burn_drive_get_adr() or you might become incompatible
+ in future.
+ Note: bugs with struct burn_drive_info - if any - will not be
+ easy to fix. Please report them but also strive for
+ workarounds on application level.
+ */
+ printf("\nOverview of accessible drives (%d found) :\n",
+ drive_count);
+ printf("-----------------------------------------------------------------------------\n");
+ for (i = 0; i < drive_count; i++) {
+ if (burn_drive_get_adr(&(drive_list[i]), adr) <=0)
+ strcpy(adr, "-get_adr_failed-");
+ printf("%d --drive '%s' : '%s' '%s'\n",
+ i,adr,drive_list[i].vendor,drive_list[i].product);
+ }
+ printf("-----------------------------------------------------------------------------\n\n");
+
+
+ /*
+ On multi-drive systems save yourself from sysadmins' revenge.
+
+ Be aware that you hold reserved all available drives at this point.
+ So either make your choice quick enough not to annoy other system
+ users, or set free the drives for a while.
+
+ The tested way of setting free all drives is to shutdown the library
+ and to restart when the choice has been made. The list of selectable
+ drives should also hold persistent drive addresses as obtained
+ above by burn_drive_get_adr(). By such an address one may use
+ burn_drive_scan_and_grab() to finally aquire exactly one drive.
+
+ A not yet tested shortcut should be to call burn_drive_info_free()
+ and to call either burn_drive_scan() or burn_drive_scan_and_grab()
+ before accessing any drives again.
+
+ In both cases you have to be aware that the desired drive might get
+ aquired in the meantime by another user resp. libburn process.
+ */
+
+ /* We already made our choice via command line. (default is 0)
+ So we just have to keep our desired drive and drop all others.
+ No other libburn instance will have a chance to steal our drive.
+ */
+ if (*driveno < 0) {
+ printf("Pseudo-drive \"-\" given : bus scanning done.\n");
+ return 2; /* the program will end after this */
+ }
+ if (drive_count <= *driveno) {
+ fprintf(stderr,
+ "Found only %d drives. Number %d not available.\n",
+ drive_count, *driveno);
+ return 0; /* the program will end after this */
+ }
+
+ /* Drop all drives which we do not want to use */
+ for (i = 0; i < drive_count; i++) {
+ if (i == *driveno) /* the one drive we want to keep */
+ continue;
+ ret = burn_drive_info_forget(&(drive_list[i]),0);
+ if (ret != 1)
+ fprintf(stderr, "Cannot drop drive %d. Please report \"ret=%d\" to libburn-hackers@pykix.org\n",
+ i, ret);
+ else
+ printf("Dropped unwanted drive %d\n",i);
+ }
+ /* Make the one we want ready for blanking or burning */
+ ret= burn_drive_grab(drive_list[*driveno].drive, 1);
+ if (ret != 1)
+ return 0;
+ drive_is_grabbed = 1;
+ return 1;
+}
+
+
+/** Makes a previously used CD-RW ready for thorough re-usal.
+
+ To our knowledge it is hardly possible to abort an ongoing blank operation
+ because after start it is entirely handled by the drive.
+ So expect a blank run to survive the end of the blanking process and be
+ patient for the usual timespan of a normal blank run. Only after that
+ time has surely elapsed, only then you should start any rescue attempts
+ with the drive. If necessary at all.
+*/
+int libburner_blank_disc(struct burn_drive *drive, int blank_fast)
+{
+ enum burn_disc_status disc_state;
+ struct burn_progress progress;
+
+ while (burn_drive_get_status(drive, NULL))
+ usleep(1001);
+
+ while ((disc_state = burn_disc_get_status(drive)) == BURN_DISC_UNREADY)
+ usleep(1001);
+ printf(
+ "Drive media status: %d (see libburn/libburn.h BURN_DISC_*)\n",
+ disc_state);
+ if (disc_state == BURN_DISC_BLANK) {
+ fprintf(stderr,
+ "IDLE: Blank CD media detected. Will leave it untouched\n");
+ return 2;
+ } else if (disc_state == BURN_DISC_FULL ||
+ disc_state == BURN_DISC_APPENDABLE) {
+ ; /* this is what libburn is willing to blank */
+ } else if (disc_state == BURN_DISC_EMPTY) {
+ fprintf(stderr,"FATAL: No media detected in drive\n");
+ return 0;
+ } else {
+ fprintf(stderr,
+ "FATAL: Cannot recognize drive and media state\n");
+ return 0;
+ }
+ if(!burn_disc_erasable(drive)) {
+ fprintf(stderr,
+ "FATAL : Media is not of erasable type\n");
+ return 0;
+ }
+ printf(
+ "Beginning to %s-blank CD media.\n", (blank_fast?"fast":"full"));
+ printf(
+ "Expect some garbage sector numbers and some zeros at first.\n");
+ burn_disc_erase(drive, blank_fast);
+ while (burn_drive_get_status(drive, &progress)) {
+ printf("Blanking sector %d\n", progress.sector);
+ sleep(1);
+ }
+ printf("Done\n");
+ return 1;
+}
+
+
+/** This gesture is necessary to get the drive info after blanking.
+ It opens a small gap for losing the drive to another libburn instance.
+ We will work on closing this gap.
+*/
+int libburner_regrab(struct burn_drive *drive) {
+ int ret;
+
+ printf("Releasing and regrabbing drive ...\n");
+ if (drive_is_grabbed)
+ burn_drive_release(drive, 0);
+ drive_is_grabbed = 0;
+ ret = burn_drive_grab(drive, 0);
+ if (ret != 0) {
+ drive_is_grabbed = 1;
+ printf("Done\n");
+ } else
+ printf("FAILED\n");
+ return !!ret;
+}
+
+
+/** Brings the preformatted image (ISO 9660, afio, ext2, whatever) onto media.
+
+ To make sure your image is fully readable on any Linux machine, this
+ function adds 300 kB of padding to the track.
+
+ Without a signal handler it is quite dangerous to abort the process
+ while this function is active. See cdrskin/cdrskin.c and its usage
+ of cdrskin/cleanup.[ch] for an example of application provided
+ abort handling. It must cope with 2 of 3 threads reporting for
+ being handled.
+
+ Without signal handler have ready a command line
+ cdrecord dev=... -reset
+ with a dev= previously inquired by cdrecord [dev=ATA] -scanbus
+ in order to get your drive out of shock state after raw abort.
+ Thanks to Joerg Schilling for helping out unquestioned. :)
+
+ In general, libburn is less prone to system problems than cdrecord,
+ i believe. But cdrecord had long years of time to complete itself.
+ We are still practicing. Help us with that. :))
+*/
+int libburner_payload(struct burn_drive *drive, const char *source_adr,
+ off_t size)
+{
+ struct burn_source *data_src;
+ struct burn_disc *target_disc;
+ struct burn_session *session;
+ struct burn_write_opts *burn_options;
+ enum burn_disc_status disc_state;
+ struct burn_track *track;
+ struct burn_progress progress;
+ time_t start_time;
+ int last_sector = 0;
+
+ target_disc = burn_disc_create();
+ session = burn_session_create();
+ burn_disc_add_session(target_disc, session, BURN_POS_END);
+ track = burn_track_create();
+
+ /* a padding of 300 kB is helpful to avoid the read-ahead bug */
+ burn_track_define_data(track, 0, 300*1024, 1, BURN_MODE1);
+
+ if (source_adr[0] == '-' && source_adr[1] == 0) {
+ data_src = burn_fd_source_new(0, -1, size);
+ printf("Note: using standard input as source with %.f bytes\n",
+ (double) size);
+ } else
+ data_src = burn_file_source_new(source_adr, NULL);
+ if (data_src == NULL) {
+ fprintf(stderr,
+ "FATAL: Could not open data source '%s'.\n",source_adr);
+ if(errno!=0)
+ fprintf(stderr,"(Most recent system error: %s )\n",
+ strerror(errno));
+ return 0;
+ }
+
+ if (burn_track_set_source(track, data_src) != BURN_SOURCE_OK) {
+ printf("FATAL: Cannot attach source object to track object\n");
+ return 0;
+ }
+ burn_session_add_track(session, track, BURN_POS_END);
+ burn_source_free(data_src);
+
+ while (burn_drive_get_status(drive, NULL))
+ usleep(1001);
+
+ /* Evaluate drive and media */
+ while ((disc_state = burn_disc_get_status(drive)) == BURN_DISC_UNREADY)
+ usleep(1001);
+ if (disc_state != BURN_DISC_BLANK) {
+ if (disc_state == BURN_DISC_FULL ||
+ disc_state == BURN_DISC_APPENDABLE) {
+ fprintf(stderr,
+ "FATAL: Media with data detected. Need blank media.\n");
+ if (burn_disc_erasable(drive))
+ fprintf(stderr, "HINT: Try --blank_fast\n\n");
+ } else if (disc_state == BURN_DISC_EMPTY)
+ fprintf(stderr,"FATAL: No media detected in drive\n");
+ else
+ fprintf(stderr,
+ "FATAL: Cannot recognize drive and media state\n");
+ return 0;
+ }
+
+ burn_options = burn_write_opts_new(drive);
+ burn_write_opts_set_perform_opc(burn_options, 0);
+
+#ifdef Libburner_raw_mode_which_i_do_not_likE
+ /* This yields higher CD capacity but hampers my IDE controller
+ with burning on one drive and reading on another simultaneously.
+ My burner does not obey the order --try_to_simulate in this mode.
+ */
+ burn_write_opts_set_write_type(burn_options,
+ BURN_WRITE_RAW, BURN_BLOCK_RAW96R);
+#else
+
+ /* This is by what cdrskin competes with cdrecord -sao which
+ i understand is the mode preferrably advised by Joerg Schilling */
+ burn_write_opts_set_write_type(burn_options,
+ BURN_WRITE_SAO, BURN_BLOCK_SAO);
+
+#endif
+ if(simulate_burn)
+ printf("\n*** Will TRY to SIMULATE burning ***\n\n");
+ burn_write_opts_set_simulate(burn_options, simulate_burn);
+ burn_structure_print_disc(target_disc);
+ burn_drive_set_speed(drive, 0, 0);
+ burn_write_opts_set_underrun_proof(burn_options, 1);
+
+ printf("Burning starts. With e.g. 4x media expect up to a minute of zero progress.\n");
+ start_time = time(0);
+ burn_disc_write(burn_options, target_disc);
+
+ burn_write_opts_free(burn_options);
+ while (burn_drive_get_status(drive, NULL) == BURN_DRIVE_SPAWNING)
+ usleep(1002);
+ while (burn_drive_get_status(drive, &progress)) {
+ if( progress.sectors <= 0 || progress.sector == last_sector)
+ printf(
+ "Thank you for being patient since %d seconds.\n",
+ (int) (time(0) - start_time));
+ else
+ printf("Burning sector %d of %d\n",
+ progress.sector, progress.sectors);
+ last_sector = progress.sector;
+ sleep(1);
+ }
+ printf("\n");
+ burn_track_free(track);
+ burn_session_free(session);
+ burn_disc_free(target_disc);
+ if(simulate_burn)
+ printf("\n*** Did TRY to SIMULATE burning ***\n\n");
+ return 0;
+}
+
+
+/** Converts command line arguments into a few program parameters.
+ drive_adr[] must provide at least BURN_DRIVE_ADR_LEN bytes.
+ source_adr[] must provide at least 4096 bytes.
+*/
+int libburner_setup(int argc, char **argv, char drive_adr[], int *driveno,
+ int *do_blank, char source_adr[], off_t *size)
+{
+ int i, insuffient_parameters = 0;
+ int print_help = 0;
+
+ drive_adr[0] = 0;
+ *driveno = 0;
+ *do_blank = 0;
+ source_adr[0] = 0;
+ *size = 650*1024*1024;
+
+ for (i = 1; i < argc; ++i) {
+ if (!strcmp(argv[i], "--blank_fast")) {
+ *do_blank = 1;
+
+ } else if (!strcmp(argv[i], "--blank_full")) {
+ *do_blank = 2;
+
+ } else if (!strcmp(argv[i], "--burn_for_real")) {
+ simulate_burn = 0;
+
+ } else if (!strcmp(argv[i], "--drive")) {
+ ++i;
+ if (i >= argc) {
+ fprintf(stderr,"--drive requires an argument\n");
+ return 1;
+ } else if (strcmp(argv[i], "-") == 0) {
+ drive_adr[0] = 0;
+ *driveno = -1;
+ } else if (isdigit(argv[i][0])) {
+ drive_adr[0] = 0;
+ *driveno = atoi(argv[i]);
+ } else {
+ if(strlen(argv[i]) >= BURN_DRIVE_ADR_LEN) {
+ fprintf(stderr,"--drive address too long (max. %d)\n",
+ BURN_DRIVE_ADR_LEN-1);
+ return 2;
+ }
+ strcpy(drive_adr, argv[i]);
+ }
+ } else if (!strcmp(argv[i], "--stdin_size")) {
+ ++i;
+ if (i >= argc) {
+ fprintf(stderr,"--stdin_size requires an argument\n");
+ return 3;
+ } else
+ *size = atoi(argv[i]);
+ if (*size < 600*1024) /* seems to be minimum readable track size */
+ *size = 600*1024;
+ } else if (!strcmp(argv[i], "--try_to_simulate")) {
+ simulate_burn = 1;
+
+ } else if (!strcmp(argv[i], "--verbose")) {
+ ++i;
+ if (i >= argc) {
+ fprintf(stderr,"--verbose requires an argument\n");
+ return 4;
+ } else
+ burn_set_verbosity(atoi(argv[i]));
+ } else if (!strcmp(argv[i], "--help")) {
+ print_help = 1;
+
+ } else {
+ if(strlen(argv[i]) >= 4096) {
+ fprintf(stderr, "Source address too long (max. %d)\n", 4096-1);
+ return 5;
+ }
+ strcpy(source_adr, argv[i]);
+ }
+ }
+ insuffient_parameters = 1;
+ if (*driveno < 0)
+ insuffient_parameters = 0;
+ if (source_adr[0] != 0)
+ insuffient_parameters = 0;
+ if (*do_blank)
+ insuffient_parameters = 0;
+ if (print_help || insuffient_parameters ) {
+ printf("Usage: %s\n", argv[0]);
+ printf(" [--drive ||\"-\"]\n");
+ printf(" [--verbose ] [--blank_fast|--blank_full]\n");
+ printf(" [--burn_for_real|--try_to_simulate] [--stdin_size ]\n");
+ printf(" [|\"-\"]\n");
+ printf("Examples\n");
+ printf("A bus scan (needs rw-permissions to see a drive):\n");
+ printf(" %s --drive -\n",argv[0]);
+ printf("Burn a file to drive chosen by number:\n");
+ printf(" %s --drive 0 --burn_for_real my_image_file\n",
+ argv[0]);
+ printf("Burn a file to drive chosen by persistent address:\n");
+ printf(" %s --drive /dev/hdc --burn_for_real my_image_file\n", argv[0]);
+ printf("Blank a used CD-RW (is combinable with burning in one run):\n");
+ printf(" %s --drive 0 --blank_fast\n",argv[0]);
+ printf("Burn a compressed afio archive on-the-fly, pad up to 700 MB:\n");
+ printf(" ( cd my_directory ; find . -print | afio -oZ - ) | \\\n");
+ printf(" %s --drive /dev/hdc --burn_for_real --stdin_size 734003200 -\n", argv[0]);
+ printf("To be read from *not mounted* CD via:\n");
+ printf(" afio -tvZ /dev/hdc\n");
+ printf("Program tar would need a clean EOF which our padded CD cannot deliver.\n");
+ if (insuffient_parameters)
+ return 6;
+ }
+ return 0;
+}
+
+
+int main(int argc, char **argv)
+{
+ int driveno, ret, do_blank;
+ char source_adr[4096], drive_adr[BURN_DRIVE_ADR_LEN];
+ off_t stdin_size;
+
+ ret = libburner_setup(argc, argv, drive_adr, &driveno, &do_blank,
+ source_adr, &stdin_size);
+ if (ret)
+ exit(ret);
+
+ printf("Initializing library ...\n");
+ if (burn_initialize())
+ printf("Done\n");
+ else {
+ printf("FAILED\n");
+ fprintf(stderr,"\nFATAL: Failed to initialize libburn.\n");
+ exit(33);
+ }
+
+ /** Note: driveno might change its value in this call */
+ ret = libburner_aquire_drive(drive_adr, &driveno);
+ if (ret<=0) {
+ fprintf(stderr,"\nFATAL: Failed to aquire drive.\n");
+ { ret = 34; goto finish_libburn; }
+ }
+ if (ret == 2)
+ { ret = 0; goto release_drive; }
+ if (do_blank) {
+ ret = libburner_blank_disc(drive_list[driveno].drive,
+ do_blank == 1);
+ if (ret<=0)
+ { ret = 36; goto release_drive; }
+ if (ret != 2 && source_adr[0] != 0)
+ ret = libburner_regrab(drive_list[driveno].drive);
+ if (ret<=0) {
+ fprintf(stderr,
+ "FATAL: Cannot release and grab again drive after blanking\n");
+ { ret = 37; goto finish_libburn; }
+ }
+ }
+ if (source_adr[0] != 0) {
+ ret = libburner_payload(drive_list[driveno].drive, source_adr,
+ stdin_size);
+ if (ret<=0)
+ { ret = 38; goto release_drive; }
+ }
+ ret = 0;
+release_drive:;
+ if (drive_is_grabbed)
+ burn_drive_release(drive_list[driveno].drive, 0);
+
+finish_libburn:;
+ /* This app does not bother to know about exact scan state.
+ Better to accept a memory leak here. We are done anyway. */
+ /* burn_drive_info_free(drive_list); */
+
+ burn_finish();
+ return ret;
+}
+
+
+/* License and copyright aspects:
+
+This all is provided under GPL.
+Read. Try. Think. Play. Write yourself some code. Be free of my copyright.
+
+Be also invited to study the code of cdrskin/cdrskin.c et al.
+
+
+Clarification in my name and in the name of Mario Danic, copyright holder
+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.
+
+History:
+libburner is a compilation of my own contributions to test/burniso.c and
+fresh code which replaced the remaining parts under copyright of
+Derek Foreman.
+My respect and my thanks to Derek for providing me a start back in 2005.
+
+*/
+
diff --git a/tags/ZeroTwoTwo/test/poll.c b/tags/ZeroTwoTwo/test/poll.c
new file mode 100644
index 00000000..cd22f158
--- /dev/null
+++ b/tags/ZeroTwoTwo/test/poll.c
@@ -0,0 +1,78 @@
+/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */
+
+#include "libburn/libburn.h"
+#include "libburn/toc.h"
+#include "libburn/mmc.h"
+
+#include
+#include
+#include
+#include
+
+static struct burn_drive_info *drives;
+static unsigned int n_drives;
+int NEXT;
+
+static void catch_int ()
+{
+ NEXT = 1;
+}
+
+static void poll_drive(int d)
+{
+ enum burn_disc_status s;
+
+ fprintf(stderr, "polling disc in %s - %s:\n",
+ drives[d].vendor, drives[d].product);
+
+ if (!burn_drive_grab(drives[d].drive, 1)) {
+ fprintf(stderr, "Unable to open the drive!\n");
+ return;
+ }
+
+ while (burn_drive_get_status(drives[d].drive, NULL))
+ usleep(1000);
+
+ while ((s = burn_disc_get_status(drives[d].drive))
+ == BURN_DISC_UNREADY)
+ usleep(1000);
+
+ while (NEXT == 0) {
+ sleep(2);
+ mmc_get_event(drives[d].drive);
+ }
+ burn_drive_release(drives[d].drive, 0);
+}
+
+int main()
+{
+ int i;
+ struct sigaction newact;
+ struct sigaction oldact;
+ fprintf(stderr, "Initializing library...");
+ if (burn_initialize())
+ fprintf(stderr, "Success\n");
+ else {
+ printf("Failed\n");
+ return 1;
+ }
+
+ fprintf(stderr, "Scanning for devices...");
+ while (!burn_drive_scan(&drives, &n_drives)) ;
+ fprintf(stderr, "Done\n");
+ if (!drives) {
+ printf("No burner found\n");
+ return 1;
+ }
+
+ newact.sa_handler = catch_int;
+ sigaction(SIGINT, &newact, &oldact);
+ for (i = 0; i < n_drives; i++) {
+ NEXT=0;
+ poll_drive(i);
+ }
+ sigaction(SIGINT, &oldact, NULL);
+ burn_drive_info_free(drives);
+ burn_finish();
+ return 0;
+}
diff --git a/tags/ZeroTwoTwo/test/structest.c b/tags/ZeroTwoTwo/test/structest.c
new file mode 100644
index 00000000..defa274e
--- /dev/null
+++ b/tags/ZeroTwoTwo/test/structest.c
@@ -0,0 +1,48 @@
+#include
+#include
+#include
+#include
+
+int main(int argc, char **argv)
+{
+ int i;
+ const char *path;
+ struct burn_track *track;
+ struct burn_disc *disc;
+ struct burn_session *session;
+ struct burn_source *src;
+
+ disc = burn_disc_create();
+ session = burn_session_create();
+ burn_disc_add_session(disc, session, BURN_POS_END);
+
+ /* Define a source for all of the tracks */
+ path = strdup("/etc/hosts");
+ src = burn_file_source_new(path, NULL);
+
+ /* Add ten tracks to a session */
+ for (i = 0; i < 10; i++) {
+ track = burn_track_create();
+ burn_session_add_track(session, track, 0);
+ if (burn_track_set_source(track, src) != BURN_SOURCE_OK) {
+ printf("problem with the source\n");
+ return 0;
+ }
+ }
+
+ /* Add ten tracks to a session */
+ for (i = 0; i < 10; i++) {
+ track = burn_track_create();
+ burn_session_add_track(session, track, 0);
+ if (burn_track_set_source(track, src) != BURN_SOURCE_OK) {
+ printf("problem with the source\n");
+ return 0;
+ }
+ }
+
+ /* Delete a session */
+ burn_session_remove_track(session, track);
+
+ burn_structure_print_disc(disc);
+ return EXIT_SUCCESS;
+}
diff --git a/tags/ZeroTwoTwo/test/toc.c b/tags/ZeroTwoTwo/test/toc.c
new file mode 100644
index 00000000..1ec7bcfd
--- /dev/null
+++ b/tags/ZeroTwoTwo/test/toc.c
@@ -0,0 +1,103 @@
+/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */
+
+#include
+#include
+#include
+#include
+#include
+
+static struct burn_drive_info *drives;
+static unsigned int n_drives;
+
+static void show_tocs()
+{
+ struct burn_session **sessions;
+ struct burn_track **tracks;
+ struct burn_disc *disc;
+ int nses, ntracks, hidefirst;
+ unsigned int i, j, k;
+ struct burn_toc_entry e;
+ enum burn_disc_status s;
+
+ for (i = 0; i < n_drives; ++i) {
+ fprintf(stderr, "TOC for disc in %s - %s:\n",
+ drives[i].vendor, drives[i].product);
+
+ if (!burn_drive_grab(drives[i].drive, 1)) {
+ fprintf(stderr, "Unable to open the drive!\n");
+ continue;
+ }
+
+ while (burn_drive_get_status(drives[i].drive, NULL))
+ usleep(1000);
+
+ while ((s = burn_disc_get_status(drives[i].drive))
+ == BURN_DISC_UNREADY)
+ usleep(1000);
+ if (s != BURN_DISC_FULL) {
+ burn_drive_release(drives[i].drive, 0);
+ fprintf(stderr, "No disc found!\n");
+ continue;
+ }
+
+ disc = burn_drive_get_disc(drives[i].drive);
+
+ sessions = burn_disc_get_sessions(disc, &nses);
+ for (k = 0; k < nses; ++k) {
+ tracks = burn_session_get_tracks(sessions[k],
+ &ntracks);
+ hidefirst = burn_session_get_hidefirst(sessions[k]);
+ if (hidefirst)
+ fprintf(stderr,
+ "track: GAP (%2d) lba: %9d (%9d) %02d:%02d:%02d adr: X control: X mode: %d\n",
+ k + 1, 0, 0, 0, 2, 0,
+ burn_track_get_mode(tracks[0]));
+
+ for (j = !!hidefirst; j < ntracks; ++j) {
+ burn_track_get_entry(tracks[j], &e);
+ fprintf(stderr,
+ "track: %3d (%2d) lba: %9d (%9d) %02d:%02d:%02d "
+ "adr: %d control: %d mode: %d\n",
+ e.point, e.session,
+ burn_msf_to_lba(e.pmin, e.psec,
+ e.pframe),
+ burn_msf_to_lba(e.pmin, e.psec,
+ e.pframe) * 4,
+ e.pmin, e.psec, e.pframe, e.adr,
+ e.control,
+ burn_track_get_mode(tracks[j]));
+ }
+ burn_session_get_leadout_entry(sessions[k], &e);
+ fprintf(stderr,
+ "track:lout (%2d) lba: %9d (%9d) %02d:%02d:%02d "
+ "adr: %d control: %d mode: %d\n",
+ k + 1, burn_msf_to_lba(e.pmin, e.psec,
+ e.pframe),
+ burn_msf_to_lba(e.pmin, e.psec,
+ e.pframe) * 4, e.pmin,
+ e.psec, e.pframe, e.adr, e.control, -1);
+ }
+ burn_disc_free(disc);
+ burn_drive_release(drives[i].drive, 0);
+ }
+}
+
+int main()
+{
+ fprintf(stderr, "Initializing library...");
+ if (burn_initialize())
+ fprintf(stderr, "Success\n");
+ else {
+ printf("Failed\n");
+ return 1;
+ }
+
+ fprintf(stderr, "Scanning for devices...");
+ while (!burn_drive_scan(&drives, &n_drives)) ;
+ fprintf(stderr, "Done\n");
+
+ show_tocs();
+ burn_drive_info_free(drives);
+ burn_finish();
+ return 0;
+}
diff --git a/tags/ZeroTwoTwo/test/tree.py b/tags/ZeroTwoTwo/test/tree.py
new file mode 100644
index 00000000..a7be3d80
--- /dev/null
+++ b/tags/ZeroTwoTwo/test/tree.py
@@ -0,0 +1,77 @@
+# a module to help with handling of filenames, directory trees, etc.
+
+import os
+import os.path
+import stat
+
+def pathsubtract(a, b):
+ index = a.find(b)
+ if index == -1:
+ return None
+ res = a[ (index + len(b)): ]
+
+ if res.find("/") == 0:
+ res = res[1:]
+ return res
+
+# same as C strcmp()
+def strcmp(a, b):
+ if a < b:
+ return -1
+ if a > b:
+ return 1
+ return 0
+
+class TreeNode:
+
+ # path is the location of the file/directory. It is either a full path or
+ # a path relative to $PWD
+ def __init__(self, parent, path=".", root=".", isofile=None):
+ if isofile:
+ self.root = os.path.abspath(isofile)
+ self.path = ""
+ else:
+ fullpath = os.path.abspath( path )
+ fullroot = os.path.abspath( root )
+ self.root = fullroot
+ self.path = pathsubtract( fullpath, fullroot )
+ self.parent = parent
+ self.children = []
+
+ if self.path == None:
+ raise NameError, "Invalid paths %s and %s" % (fullpath, fullroot)
+
+ # if this is a directory, add its children recursively
+ def addchildren(self):
+ if not stat.S_ISDIR( os.lstat(self.root + "/" + self.path).st_mode ):
+ return
+
+ children = os.listdir( self.root + "/" + self.path )
+ for child in children:
+ if self.path:
+ child = self.path + "/" + child
+ self.children.append( TreeNode(self, child, self.root) )
+ for child in self.children:
+ child.addchildren()
+
+ def printAll(self, spaces=0):
+ print " "*spaces + self.root + "/" + self.path
+ for child in self.children:
+ child.printAll(spaces + 2)
+
+ def isValidISO1(self):
+ pass
+
+class Tree:
+ def __init__(self, root=None, isofile=None):
+ if isofile:
+ self.root = TreeNode(parent=None, isofile=isofile)
+ else:
+ self.root = TreeNode(parent=None, path=root, root=root)
+ self.root.addchildren()
+
+ def isValidISO1(self):
+ return root.isValidISO1();
+
+#t = Tree(root=".")
+#t.root.printAll()
diff --git a/tags/ZeroTwoTwo/version.h.in b/tags/ZeroTwoTwo/version.h.in
new file mode 100644
index 00000000..13ada991
--- /dev/null
+++ b/tags/ZeroTwoTwo/version.h.in
@@ -0,0 +1,3 @@
+#define BURN_MAJOR_VERSION @BURN_MAJOR_VERSION@
+#define BURN_MINOR_VERSION @BURN_MINOR_VERSION@
+#define BURN_MICRO_VERSION @BURN_MICRO_VERSION@