Imported sources.

This commit is contained in:
cvs 2000-05-23 01:55:31 +00:00
commit 5615fa90d4
77 changed files with 39084 additions and 0 deletions

1104
nufxlib-0/Archive.c Normal file

File diff suppressed because it is too large Load Diff

420
nufxlib-0/ArchiveIO.c Normal file
View File

@ -0,0 +1,420 @@
/*
* NuFX archive manipulation library
* Copyright (C) 2000 by Andy McFadden, All Rights Reserved.
* This is free software; you can redistribute it and/or modify it under the
* terms of the GNU Library General Public License, see the file COPYING.LIB.
*
* Functions for reading from and writing to the archive. These are
* specialized functions that deal with byte ordering and CRC computation.
* The functions associated with reading from an archive work equally well
* with streaming archives.
*/
#include "NufxLibPriv.h"
/*
* ===========================================================================
* Read and write
* ===========================================================================
*/
/*
* Read one little-endian bytes, optionally computing a CRC.
*/
uchar
Nu_ReadOneC(NuArchive* pArchive, FILE* fp, ushort* pCrc)
{
int ic;
Assert(pArchive != nil);
Assert(fp != nil);
Assert(pCrc != nil);
ic = getc(fp);
*pCrc = Nu_UpdateCRC16((uchar)ic, *pCrc);
return ic;
}
uchar
Nu_ReadOne(NuArchive* pArchive, FILE* fp)
{
ushort dummyCrc /*= 0*/;
return Nu_ReadOneC(pArchive, fp, &dummyCrc);
}
/*
* Write one byte, optionally computing a CRC.
*/
void
Nu_WriteOneC(NuArchive* pArchive, FILE* fp, uchar val, ushort* pCrc)
{
Assert(pArchive != nil);
Assert(fp != nil);
Assert(pCrc != nil);
putc(val, fp);
}
void
Nu_WriteOne(NuArchive* pArchive, FILE* fp, uchar val)
{
ushort dummyCrc /*= 0*/;
Nu_WriteOneC(pArchive, fp, val, &dummyCrc);
}
/*
* Read two little-endian bytes, optionally computing a CRC.
*/
ushort
Nu_ReadTwoC(NuArchive* pArchive, FILE* fp, ushort* pCrc)
{
int ic1, ic2;
Assert(pArchive != nil);
Assert(fp != nil);
Assert(pCrc != nil);
ic1 = getc(fp);
*pCrc = Nu_UpdateCRC16((uchar)ic1, *pCrc);
ic2 = getc(fp);
*pCrc = Nu_UpdateCRC16((uchar)ic2, *pCrc);
return ic1 | ic2 << 8;
}
ushort
Nu_ReadTwo(NuArchive* pArchive, FILE* fp)
{
ushort dummyCrc /*= 0*/;
return Nu_ReadTwoC(pArchive, fp, &dummyCrc);
}
/*
* Write two little-endian bytes, optionally computing a CRC.
*/
void
Nu_WriteTwoC(NuArchive* pArchive, FILE* fp, ushort val, ushort* pCrc)
{
int ic1, ic2;
Assert(pArchive != nil);
Assert(fp != nil);
Assert(pCrc != nil);
ic1 = val & 0xff;
*pCrc = Nu_UpdateCRC16((uchar)ic1, *pCrc);
ic2 = val >> 8;
*pCrc = Nu_UpdateCRC16((uchar)ic2, *pCrc);
putc(ic1, fp);
putc(ic2, fp);
}
void
Nu_WriteTwo(NuArchive* pArchive, FILE* fp, ushort val)
{
ushort dummyCrc /*= 0*/;
Nu_WriteTwoC(pArchive, fp, val, &dummyCrc);
}
/*
* Read four little-endian bytes, optionally computing a CRC.
*/
ulong
Nu_ReadFourC(NuArchive* pArchive, FILE* fp, ushort* pCrc)
{
int ic1, ic2, ic3, ic4;
Assert(pArchive != nil);
Assert(fp != nil);
Assert(pCrc != nil);
ic1 = getc(fp);
*pCrc = Nu_UpdateCRC16((uchar)ic1, *pCrc);
ic2 = getc(fp);
*pCrc = Nu_UpdateCRC16((uchar)ic2, *pCrc);
ic3 = getc(fp);
*pCrc = Nu_UpdateCRC16((uchar)ic3, *pCrc);
ic4 = getc(fp);
*pCrc = Nu_UpdateCRC16((uchar)ic4, *pCrc);
return ic1 | ic2 << 8 | (ulong)ic3 << 16 | (ulong)ic4 << 24;
}
ulong
Nu_ReadFour(NuArchive* pArchive, FILE* fp)
{
ushort dummyCrc /*= 0*/;
return Nu_ReadFourC(pArchive, fp, &dummyCrc);
}
/*
* Write four little-endian bytes, optionally computing a CRC.
*/
void
Nu_WriteFourC(NuArchive* pArchive, FILE* fp, ulong val, ushort* pCrc)
{
int ic1, ic2, ic3, ic4;
Assert(pArchive != nil);
Assert(fp != nil);
Assert(pCrc != nil);
ic1 = val & 0xff;
*pCrc = Nu_UpdateCRC16((uchar)ic1, *pCrc);
ic2 = (val >> 8) & 0xff;
*pCrc = Nu_UpdateCRC16((uchar)ic2, *pCrc);
ic3 = (val >> 16) & 0xff;
*pCrc = Nu_UpdateCRC16((uchar)ic3, *pCrc);
ic4 = val >> 24;
*pCrc = Nu_UpdateCRC16((uchar)ic4, *pCrc);
putc(ic1, fp);
putc(ic2, fp);
putc(ic3, fp);
putc(ic4, fp);
}
void
Nu_WriteFour(NuArchive* pArchive, FILE* fp, ulong val)
{
ushort dummyCrc /*=0*/;
Nu_WriteFourC(pArchive, fp, val, &dummyCrc);
}
/*
* Read an 8-byte NuFX Date/Time structure.
*
* I've chosen *not* to filter away the Y2K differences between P8 ShrinkIt
* and GS/ShrinkIt. It's easy enough to deal with, and I figure the less
* messing-with, the better.
*/
NuDateTime
Nu_ReadDateTimeC(NuArchive* pArchive, FILE* fp, ushort* pCrc)
{
NuDateTime temp;
int ic;
Assert(pArchive != nil);
Assert(fp != nil);
Assert(pCrc != nil);
ic = getc(fp);
*pCrc = Nu_UpdateCRC16((uchar)ic, *pCrc);
temp.second = ic;
ic = getc(fp);
*pCrc = Nu_UpdateCRC16((uchar)ic, *pCrc);
temp.minute = ic;
ic = getc(fp);
*pCrc = Nu_UpdateCRC16((uchar)ic, *pCrc);
temp.hour = ic;
ic = getc(fp);
*pCrc = Nu_UpdateCRC16((uchar)ic, *pCrc);
temp.year = ic;
ic = getc(fp);
*pCrc = Nu_UpdateCRC16((uchar)ic, *pCrc);
temp.day = ic;
ic = getc(fp);
*pCrc = Nu_UpdateCRC16((uchar)ic, *pCrc);
temp.month = ic;
ic = getc(fp);
*pCrc = Nu_UpdateCRC16((uchar)ic, *pCrc);
temp.extra = ic;
ic = getc(fp);
*pCrc = Nu_UpdateCRC16((uchar)ic, *pCrc);
temp.weekDay = ic;
return temp;
}
NuDateTime
Nu_ReadDateTime(NuArchive* pArchive, FILE* fp, ushort* pCrc)
{
ushort dummyCrc /*= 0*/;
return Nu_ReadDateTimeC(pArchive, fp, &dummyCrc);
}
/*
* Write an 8-byte NuFX Date/Time structure.
*/
void
Nu_WriteDateTimeC(NuArchive* pArchive, FILE* fp, NuDateTime dateTime,
ushort* pCrc)
{
int ic;
Assert(pArchive != nil);
Assert(fp != nil);
Assert(pCrc != nil);
ic = dateTime.second;
*pCrc = Nu_UpdateCRC16((uchar)ic, *pCrc);
putc(ic, fp);
ic = dateTime.minute;
*pCrc = Nu_UpdateCRC16((uchar)ic, *pCrc);
putc(ic, fp);
ic = dateTime.hour;
*pCrc = Nu_UpdateCRC16((uchar)ic, *pCrc);
putc(ic, fp);
ic = dateTime.year;
*pCrc = Nu_UpdateCRC16((uchar)ic, *pCrc);
putc(ic, fp);
ic = dateTime.day;
*pCrc = Nu_UpdateCRC16((uchar)ic, *pCrc);
putc(ic, fp);
ic = dateTime.month;
*pCrc = Nu_UpdateCRC16((uchar)ic, *pCrc);
putc(ic, fp);
ic = dateTime.extra;
*pCrc = Nu_UpdateCRC16((uchar)ic, *pCrc);
putc(ic, fp);
ic = dateTime.weekDay;
*pCrc = Nu_UpdateCRC16((uchar)ic, *pCrc);
putc(ic, fp);
}
void
Nu_WriteDateTime(NuArchive* pArchive, FILE* fp, NuDateTime dateTime)
{
ushort dummyCrc /*= 0*/;
Nu_WriteDateTimeC(pArchive, fp, dateTime, &dummyCrc);
}
/*
* Read N bytes from the stream, optionally computing a CRC.
*/
void
Nu_ReadBytesC(NuArchive* pArchive, FILE* fp, void* vbuffer, long count,
ushort* pCrc)
{
uchar* buffer = vbuffer;
int ic;
Assert(pArchive != nil);
Assert(fp != nil);
Assert(pCrc != nil);
Assert(buffer != nil);
Assert(count > 0);
while (count--) {
ic = getc(fp);
*pCrc = Nu_UpdateCRC16((uchar)ic, *pCrc);
*buffer++ = ic;
}
}
void
Nu_ReadBytes(NuArchive* pArchive, FILE* fp, void* vbuffer, long count)
{
ushort dummyCrc /*= 0*/;
Nu_ReadBytesC(pArchive, fp, vbuffer, count, &dummyCrc);
}
/*
* Write N bytes to the stream, optionally computing a CRC.
*/
void
Nu_WriteBytesC(NuArchive* pArchive, FILE* fp, const void* vbuffer, long count,
ushort* pCrc)
{
const uchar* buffer = vbuffer;
int ic;
Assert(pArchive != nil);
Assert(fp != nil);
Assert(pCrc != nil);
Assert(buffer != nil);
Assert(count > 0);
while (count--) {
ic = *buffer++;
*pCrc = Nu_UpdateCRC16((uchar)ic, *pCrc);
putc(ic, fp);
}
}
void
Nu_WriteBytes(NuArchive* pArchive, FILE* fp, const void* vbuffer, long count)
{
ushort dummyCrc /*= 0*/;
Nu_WriteBytesC(pArchive, fp, vbuffer, count, &dummyCrc);
}
/*
* ===========================================================================
* General
* ===========================================================================
*/
/*
* Determine whether the stream completed the last set of operations
* successfully.
*/
NuError
Nu_HeaderIOFailed(NuArchive* pArchive, FILE* fp)
{
if (feof(fp) || ferror(fp))
return kNuErrFile;
else
return kNuErrNone;
}
/*
* Seek around in an archive file. If this is a streaming-mode archive,
* we only allow forward relative seeks, which are emulated with read calls.
*
* The values for "ptrname" are the same as for fseek().
*/
NuError
Nu_SeekArchive(NuArchive* pArchive, FILE* fp, long offset, int ptrname)
{
if (Nu_IsStreaming(pArchive)) {
Assert(ptrname == SEEK_CUR);
Assert(offset > 0);
/* might be faster to fread a chunk at a time */
while (offset--)
(void) getc(fp);
if (ferror(fp) || feof(fp))
return kNuErrFileSeek;
} else {
if (fseek(fp, offset, ptrname) < 0)
return kNuErrFileSeek;
}
return kNuErrNone;
}
/*
* Rewind an archive to the start of NuFX record data.
*
* Note that rewind(3S) resets the error indication, but this doesn't.
*/
NuError
Nu_RewindArchive(NuArchive* pArchive)
{
Assert(pArchive != nil);
Assert(!Nu_IsStreaming(pArchive));
if (Nu_SeekArchive(pArchive, pArchive->archiveFp,
pArchive->headerOffset + kNuMasterHeaderSize, SEEK_SET) != 0)
return kNuErrFileSeek;
pArchive->currentOffset = pArchive->headerOffset + kNuMasterHeaderSize;
return kNuErrNone;
}

482
nufxlib-0/COPYING-LIB Normal file
View File

@ -0,0 +1,482 @@
GNU LIBRARY GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 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.
[This is the first released version of the library GPL. It is
numbered 2 because it goes with version 2 of the ordinary GPL.]
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
Licenses are intended to guarantee your freedom to share and change
free software--to make sure the software is free for all its users.
This license, the Library General Public License, applies to some
specially designated Free Software Foundation software, and to any
other libraries whose authors decide to use it. You can use it for
your libraries, 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 library, or if you modify it.
For example, if you distribute copies of the library, whether gratis
or for a fee, you must give the recipients all the rights that we gave
you. You must make sure that they, too, receive or can get the source
code. If you link a program with the library, you must provide
complete object files to the recipients so that they can relink them
with the library, after making changes to the library and recompiling
it. And you must show them these terms so they know their rights.
Our method of protecting your rights has two steps: (1) copyright
the library, and (2) offer you this license which gives you legal
permission to copy, distribute and/or modify the library.
Also, for each distributor's protection, we want to make certain
that everyone understands that there is no warranty for this free
library. If the library is modified by someone else and passed on, we
want its recipients to know that what they have is not the original
version, 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 companies distributing free
software will individually obtain patent licenses, thus in effect
transforming the program into proprietary software. To prevent this,
we have made it clear that any patent must be licensed for everyone's
free use or not licensed at all.
Most GNU software, including some libraries, is covered by the ordinary
GNU General Public License, which was designed for utility programs. This
license, the GNU Library General Public License, applies to certain
designated libraries. This license is quite different from the ordinary
one; be sure to read it in full, and don't assume that anything in it is
the same as in the ordinary license.
The reason we have a separate public license for some libraries is that
they blur the distinction we usually make between modifying or adding to a
program and simply using it. Linking a program with a library, without
changing the library, is in some sense simply using the library, and is
analogous to running a utility program or application program. However, in
a textual and legal sense, the linked executable is a combined work, a
derivative of the original library, and the ordinary General Public License
treats it as such.
Because of this blurred distinction, using the ordinary General
Public License for libraries did not effectively promote software
sharing, because most developers did not use the libraries. We
concluded that weaker conditions might promote sharing better.
However, unrestricted linking of non-free programs would deprive the
users of those programs of all benefit from the free status of the
libraries themselves. This Library General Public License is intended to
permit developers of non-free programs to use free libraries, while
preserving your freedom as a user of such programs to change the free
libraries that are incorporated in them. (We have not seen how to achieve
this as regards changes in header files, but we have achieved it as regards
changes in the actual functions of the Library.) The hope is that this
will lead to faster development of free libraries.
The precise terms and conditions for copying, distribution and
modification follow. Pay close attention to the difference between a
"work based on the library" and a "work that uses the library". The
former contains code derived from the library, while the latter only
works together with the library.
Note that it is possible for a library to be covered by the ordinary
General Public License rather than by this special one.
GNU LIBRARY GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License Agreement applies to any software library which
contains a notice placed by the copyright holder or other authorized
party saying it may be distributed under the terms of this Library
General Public License (also called "this License"). Each licensee is
addressed as "you".
A "library" means a collection of software functions and/or data
prepared so as to be conveniently linked with application programs
(which use some of those functions and data) to form executables.
The "Library", below, refers to any such software library or work
which has been distributed under these terms. A "work based on the
Library" means either the Library or any derivative work under
copyright law: that is to say, a work containing the Library or a
portion of it, either verbatim or with modifications and/or translated
straightforwardly into another language. (Hereinafter, translation is
included without limitation in the term "modification".)
"Source code" for a work means the preferred form of the work for
making modifications to it. For a library, 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 library.
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running a program using the Library is not restricted, and output from
such a program is covered only if its contents constitute a work based
on the Library (independent of the use of the Library in a tool for
writing it). Whether that is true depends on what the Library does
and what the program that uses the Library does.
1. You may copy and distribute verbatim copies of the Library's
complete 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 distribute a copy of this License along with the
Library.
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 Library or any portion
of it, thus forming a work based on the Library, 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) The modified work must itself be a software library.
b) You must cause the files modified to carry prominent notices
stating that you changed the files and the date of any change.
c) You must cause the whole of the work to be licensed at no
charge to all third parties under the terms of this License.
d) If a facility in the modified Library refers to a function or a
table of data to be supplied by an application program that uses
the facility, other than as an argument passed when the facility
is invoked, then you must make a good faith effort to ensure that,
in the event an application does not supply such function or
table, the facility still operates, and performs whatever part of
its purpose remains meaningful.
(For example, a function in a library to compute square roots has
a purpose that is entirely well-defined independent of the
application. Therefore, Subsection 2d requires that any
application-supplied function or table used by this function must
be optional: if the application does not supply it, the square
root function must still compute square roots.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Library,
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 Library, 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 Library.
In addition, mere aggregation of another work not based on the Library
with the Library (or with a work based on the Library) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may opt to apply the terms of the ordinary GNU General Public
License instead of this License to a given copy of the Library. To do
this, you must alter all the notices that refer to this License, so
that they refer to the ordinary GNU General Public License, version 2,
instead of to this License. (If a newer version than version 2 of the
ordinary GNU General Public License has appeared, then you can specify
that version instead if you wish.) Do not make any other change in
these notices.
Once this change is made in a given copy, it is irreversible for
that copy, so the ordinary GNU General Public License applies to all
subsequent copies and derivative works made from that copy.
This option is useful when you wish to copy part of the code of
the Library into a program that is not a library.
4. You may copy and distribute the Library (or a portion or
derivative of it, under Section 2) in object code or executable form
under the terms of Sections 1 and 2 above provided that you 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.
If distribution of 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 satisfies the requirement to
distribute the source code, even though third parties are not
compelled to copy the source along with the object code.
5. A program that contains no derivative of any portion of the
Library, but is designed to work with the Library by being compiled or
linked with it, is called a "work that uses the Library". Such a
work, in isolation, is not a derivative work of the Library, and
therefore falls outside the scope of this License.
However, linking a "work that uses the Library" with the Library
creates an executable that is a derivative of the Library (because it
contains portions of the Library), rather than a "work that uses the
library". The executable is therefore covered by this License.
Section 6 states terms for distribution of such executables.
When a "work that uses the Library" uses material from a header file
that is part of the Library, the object code for the work may be a
derivative work of the Library even though the source code is not.
Whether this is true is especially significant if the work can be
linked without the Library, or if the work is itself a library. The
threshold for this to be true is not precisely defined by law.
If such an object file uses only numerical parameters, data
structure layouts and accessors, and small macros and small inline
functions (ten lines or less in length), then the use of the object
file is unrestricted, regardless of whether it is legally a derivative
work. (Executables containing this object code plus portions of the
Library will still fall under Section 6.)
Otherwise, if the work is a derivative of the Library, you may
distribute the object code for the work under the terms of Section 6.
Any executables containing that work also fall under Section 6,
whether or not they are linked directly with the Library itself.
6. As an exception to the Sections above, you may also compile or
link a "work that uses the Library" with the Library to produce a
work containing portions of the Library, and distribute that work
under terms of your choice, provided that the terms permit
modification of the work for the customer's own use and reverse
engineering for debugging such modifications.
You must give prominent notice with each copy of the work that the
Library is used in it and that the Library and its use are covered by
this License. You must supply a copy of this License. If the work
during execution displays copyright notices, you must include the
copyright notice for the Library among them, as well as a reference
directing the user to the copy of this License. Also, you must do one
of these things:
a) Accompany the work with the complete corresponding
machine-readable source code for the Library including whatever
changes were used in the work (which must be distributed under
Sections 1 and 2 above); and, if the work is an executable linked
with the Library, with the complete machine-readable "work that
uses the Library", as object code and/or source code, so that the
user can modify the Library and then relink to produce a modified
executable containing the modified Library. (It is understood
that the user who changes the contents of definitions files in the
Library will not necessarily be able to recompile the application
to use the modified definitions.)
b) Accompany the work with a written offer, valid for at
least three years, to give the same user the materials
specified in Subsection 6a, above, for a charge no more
than the cost of performing this distribution.
c) If distribution of the work is made by offering access to copy
from a designated place, offer equivalent access to copy the above
specified materials from the same place.
d) Verify that the user has already received a copy of these
materials or that you have already sent this user a copy.
For an executable, the required form of the "work that uses the
Library" must include any data and utility programs needed for
reproducing the executable from it. 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.
It may happen that this requirement contradicts the license
restrictions of other proprietary libraries that do not normally
accompany the operating system. Such a contradiction means you cannot
use both them and the Library together in an executable that you
distribute.
7. You may place library facilities that are a work based on the
Library side-by-side in a single library together with other library
facilities not covered by this License, and distribute such a combined
library, provided that the separate distribution of the work based on
the Library and of the other library facilities is otherwise
permitted, and provided that you do these two things:
a) Accompany the combined library with a copy of the same work
based on the Library, uncombined with any other library
facilities. This must be distributed under the terms of the
Sections above.
b) Give prominent notice with the combined library of the fact
that part of it is a work based on the Library, and explaining
where to find the accompanying uncombined form of the same work.
8. You may not copy, modify, sublicense, link with, or distribute
the Library except as expressly provided under this License. Any
attempt otherwise to copy, modify, sublicense, link with, or
distribute the Library 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.
9. 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 Library or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Library (or any work based on the
Library), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Library or works based on it.
10. Each time you redistribute the Library (or any work based on the
Library), the recipient automatically receives a license from the
original licensor to copy, distribute, link with or modify the Library
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.
11. 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 Library at all. For example, if a patent
license would not permit royalty-free redistribution of the Library 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 Library.
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.
12. If the distribution and/or use of the Library is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Library 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.
13. The Free Software Foundation may publish revised and/or new
versions of the Library 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 Library
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 Library does not specify a
license version number, you may choose any version ever published by
the Free Software Foundation.
14. If you wish to incorporate parts of the Library into other free
programs whose distribution conditions are incompatible with these,
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
15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
OTHER PARTIES PROVIDE THE LIBRARY "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
LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. 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 LIBRARY 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
LIBRARY (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 LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
DAMAGES.
END OF TERMS AND CONDITIONS
Appendix: How to Apply These Terms to Your New Libraries
If you develop a new library, and you want it to be of the greatest
possible use to the public, we recommend making it free software that
everyone can redistribute and change. You can do so by permitting
redistribution under these terms (or, alternatively, under the terms of the
ordinary General Public License).
To apply these terms, attach the following notices to the library. It is
safest to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least the
"copyright" line and a pointer to where the full notice is found.
<one line to give the library's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with this library; if not, write to the Free
Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
MA 02111-1307, USA
Also add information on how to contact you by electronic and paper mail.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the library, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the
library `Frob' (a library for tweaking knobs) written by James Random Hacker.
<signature of Ty Coon>, 1 April 1990
Ty Coon, President of Vice
That's all there is to it!

154
nufxlib-0/ChangeLog.txt Normal file
View File

@ -0,0 +1,154 @@
2000/05/18 ***** v1.0.0 shipped *****
2000/05/18 fadden
- updated version information to indicate final release
2000/03/25 ***** v0.6.1 shipped *****
2000/03/25 fadden
- Sheppy says Mac OS X PPC v1.02 and v1.2 work with minor SysDefs tweak
2000/03/05 ***** v0.6.0 (beta) shipped *****
2000/03/05 fadden
- modified NuOpenRW to call mktemp or mkstemp if tmpPath looks like
a template
- removed DEBUG_MSGS from default CFLAGS
- updated version information to indicate beta release
2000/02/24 ***** v0.5.1 shipped *****
2000/02/20 changes from Scott Blackman
- portability fixes for DJGPP under Win95
2000/02/17 changes from Devin Reade
- portability fixes for BSD, AIX, and others
2000/02/09 ***** v0.5.0 (alpha) shipped *****
2000/02/08 fadden
- tweaked the BeOS/PPC config around a little
- deleted some commas to make "gcc -pendantic" happy
2000/02/06 fadden
- include @CFLAGS@ in case somebody wants to override them
2000/02/06 ***** v0.4.0b shipped *****
2000/02/06 fadden
- added "install-shared" make target
- portability fixes for HP/UX
- configure.in test for presence of snprintf/vsnprintf declarations
2000/02/06 ***** v0.4.0a shipped *****
2000/02/06 fadden
- massaged configure.in for BeOS, and added some type casts for mwerks
2000/02/06 ***** v0.4.0 shipped *****
2000/02/06 fadden
- added value range checking to Nu_SetValue
2000/02/05 fadden
- finished "test-basic"
- added an "install" target to copy libnufx and NufxLib.h
- added "mkinstalldirs"
- fixed a memory leak in NuTest
- made several implicit typecasts explicit for Visual C++'s benefit
- renamed MiscStuff's replacement function to "Nu_function"
- use "rb" or "wb" as fopen arg in sample code for Win32
2000/02/04 fadden
- wrote a fair piece of "test-basic"
- added "stickyErr" to "toBuffer" data sink so we can catch overruns
2000/02/02 fadden
- minor changes to get it working under Win32 (Visual C++ 6.0)
- added --enable-dmalloc to configuration
- instead of constantly allocating 16K buffers, use pArchive->compBuf
- ignore DataSink convertEOL value when doExpand is false
2000/02/01 fadden
- added system-specific PATH_SEP define for samples (imgconv, exerciser)
- set the pathname in ErrorStatus for CRC failures
2000/01/31 fadden
- fixed a typo causing zero-byte GSHK-damaged files to report CRC errors
- added support for DOS-ordered 2MG images to "imgconv"
2000/01/29 ***** v0.3.0 shipped *****
2000/01/29 fadden
- renamed "tests" to "samples"
- changed library version to x.y.z format (major, minor, bug-fix)
- added DEBUG_VERBOSE define, took some stuff out of DEBUG_MSGS
2000/01/28 fadden
- make the Skip result work when an input file can't be opened
- don't allow leading fssep chars in AddRecord
- don't treat a multi-file BNY that happens to have a ShrinkIt archive
in the first slot as a BXY
- added "-t" flag (write to temp) to "launder"
- in OpenReadWrite, treat zero-length archive files as newly-created
- added workaround for GSHK's zero-byte data fork bug
2000/01/26 fadden
- added status result flags to NuFlush
- dropped kNuAbortAll and added kNuIgnore
- implemented kNuValueIgnoreCRC
- update the storageType whenever we change the record
2000/01/25 fadden
- don't remove the temp file if the rename fails
- Nu_ReportError now optionally uses a callback instead of stderr
- pass NuArchive* and all the trimmings into Nu_ReportError so we can
do the callback thing; required adding arguments to lots of places
- clearly labeled BailError output as debug-only, then replaced most
of the BailErrorQuiet calls with BailError
- added global error message for when pArchive doesn't exist (e.g. Open)
2000/01/24 fadden
- added args to "launder", and made it work right with 0-length threads
- reject disk image threads that aren't a valid size
- in NuFlush, recognize when a "copy" set hasn't had any changes made
- AddThread no longer makes a copy of the DataSource
2000/01/24 ***** v0.2 shipped *****
2000/01/23 fadden
- added "sec" (Set ErrorHandler Callback) to exerciser
- wrote "launder" test program
- made "doExpand" option on data sinks work
2000/01/22 fadden
- added OnlyUpdateOlder attribute and implemented for add and extract
- made HandleExisting work for AddFile/AddRecord
- AddThread's validation now blocks data and control threads in same
record
- AddFile and AddRecord now use same validation function as AddThread
2000/01/20 fadden
- added Eric Shepherd's BeOS shared lib stuff to configure.in
- restructed the progress updater, and made it work when adding files
2000/01/19 fadden
- normalized SysDefs.h, changing UNIX to UNIX_LIKE and defining for BeOS
- added "shared" target to makefile
- added BeOS stuff to autoconf setup
2000/01/17 fadden
- fixed Makefile issue preventing "tests" from working with old GNU make
- fixed Lzw.c problem fouling up SunOS gcc v2.5.8
- discovered "<" vs "<=" flapping in GSHK, which I can't Mimic
- fixed option list dump in debug print
- properly return from all Malloc errors; abort is now debug-only again
- lots of BeOS/Metrowerks "it's not gcc" changes from Eric Shepherd
2000/01/17 ***** v0.1 shipped *****
(much time passes)
mid-1998 fadden
- work begins

363
nufxlib-0/Compress.c Normal file
View File

@ -0,0 +1,363 @@
/*
* NuFX archive manipulation library
* Copyright (C) 2000 by Andy McFadden, All Rights Reserved.
* This is free software; you can redistribute it and/or modify it under the
* terms of the GNU Library General Public License, see the file COPYING.LIB.
*
* Compress data into an archive.
*/
#include "NufxLibPriv.h"
/* for ShrinkIt-mimic mode, don't compress files under 512 bytes */
#define kNuSHKLZWThreshold 512
/*
* "Compress" an uncompressed thread.
*/
static NuError
Nu_CompressUncompressed(NuArchive* pArchive, NuStraw* pStraw,
FILE* fp, ulong srcLen, ulong* pDstLen, ushort *pCrc)
{
NuError err = kNuErrNone;
/*uchar* buffer = nil;*/
ulong count, getsize;
Assert(pArchive != nil);
Assert(pStraw != nil);
Assert(fp != nil);
Assert(srcLen > 0);
*pDstLen = srcLen; /* get this over with */
/* doesn't have to be same size as funnel, but it's not a bad idea */
/*buffer = Nu_Malloc(pArchive, kNuFunnelBufSize);*/
/*BailAlloc(buffer);*/
err = Nu_AllocCompressionBufferIFN(pArchive);
BailError(err);
if (pCrc != nil)
*pCrc = kNuInitialThreadCRC;
count = srcLen;
while (count) {
getsize = (count > kNuGenCompBufSize) ? kNuGenCompBufSize : count;
err = Nu_StrawRead(pArchive, pStraw, pArchive->compBuf, getsize);
BailError(err);
if (pCrc != nil)
*pCrc = Nu_CalcCRC16(*pCrc, pArchive->compBuf, getsize);
err = Nu_FWrite(fp, pArchive->compBuf, getsize);
BailError(err);
count -= getsize;
}
bail:
/*Nu_Free(pArchive, buffer);*/
return err;
}
/*
* Compress from a data source to an archive.
*
* All archive-specified fields in "pThread" will be filled in, as will
* "actualThreadEOF". The "nuThreadIdx" and "fileOffset" fields will
* not be modified.
*
* If "sourceFormat" is uncompressed:
* "targetFormat" will be used to compress the data
* the data source length will be placed into pThread->thThreadEOF
* the compressed size will be placed into pThread->thCompThreadEOF
*
* If "sourceFormat" is compressed:
* the data will be copied without compression (targetFormat is ignored)
* the data source "otherLen" value will be placed into pThread->thThreadEOF
* the data source length will be placed into pThread->thCompThreadEOF
*
* The actual format used will be placed in pThread->thThreadFormat, and
* the CRC of the uncompressed data will be placed in pThread->thThreadCRC.
* The remaining fields of "pThread", thThreadClass and thThreadKind, will
* be set based on the fields in "pDataSource".
*
* The output file will be positioned after the last byte of the output.
* (For a pre-sized buffer, this may not be the desired result.)
*/
NuError
Nu_CompressToArchive(NuArchive* pArchive, NuDataSource* pDataSource,
NuThreadID threadID, NuThreadFormat sourceFormat,
NuThreadFormat targetFormat, NuProgressData* pProgressData, FILE* dstFp,
NuThread* pThread)
{
NuError err;
long origOffset;
NuStraw* pStraw = nil;
NuDataSink* pDataSink = nil;
ulong srcLen, dstLen;
ushort threadCrc;
Assert(pArchive != nil);
Assert(pDataSource != nil);
/* okay if pProgressData is nil */
Assert(dstFp != nil);
Assert(pThread != nil);
/* remember file offset, so we can back up if compression fails */
err = Nu_FTell(dstFp, &origOffset);
BailError(err);
Assert(origOffset == pThread->fileOffset); /* can get rid of ftell? */
/* fill in some thread fields */
threadCrc = kNuInitialThreadCRC;
pThread->thThreadClass = NuThreadIDGetClass(threadID);
pThread->thThreadKind = NuThreadIDGetKind(threadID);
pThread->actualThreadEOF = (ulong)-1;
/* nuThreadIdx and fileOffset should already be set */
/*
* Get the input length. For "buffer" and "fp" sources, this is just
* a value passed in. For "file" sources, this is the length of the
* file on disk. The file should already have been opened successfully
* by the caller.
*
* If the input file is zero bytes long, "store" it uncompressed and
* bail immediately.
*
* (Our desire to store uncompressible data without compression clashes
* with a passing interest in doing CRLF conversions on input data. We
* want to know the length ahead of time, which potentially makes the
* compression code simpler, but prevents us from doing the conversion
* unless we pre-flight the conversion with a separate pass through the
* input file. Of course, it's still possible for the application to
* convert the file into a temp file and add from there, so all is
* not lost.)
*/
srcLen = Nu_DataSourceGetDataLen(pDataSource);
/*DBUG(("+++ input file length is %lu\n", srcLen));*/
/*
* Create a "Straw" to slurp the input through and track progress.
*/
err = Nu_StrawNew(pArchive, pDataSource, pProgressData, &pStraw);
BailError(err);
if (!srcLen) {
/* empty file! */
pThread->thThreadFormat = kNuThreadFormatUncompressed;
pThread->thThreadCRC = threadCrc;
pThread->thThreadEOF = 0;
pThread->thCompThreadEOF = 0;
pThread->actualThreadEOF = 0;
goto done; /* send final progress message */
}
if (sourceFormat == kNuThreadFormatUncompressed) {
/*
* Compress the input.
*/
/* GSHK doesn't compress anything under 512 bytes */
if (pArchive->valMimicSHK && srcLen < kNuSHKLZWThreshold)
targetFormat = kNuThreadFormatUncompressed;
if (pProgressData != nil) {
if (targetFormat != kNuThreadFormatUncompressed)
Nu_StrawSetProgressState(pStraw, kNuProgressCompressing);
else
Nu_StrawSetProgressState(pStraw, kNuProgressStoring);
}
err = Nu_ProgressDataCompressPrep(pArchive, pStraw, targetFormat,
srcLen);
BailError(err);
switch (targetFormat) {
case kNuThreadFormatUncompressed:
err = Nu_CompressUncompressed(pArchive, pStraw, dstFp, srcLen,
&dstLen, &threadCrc);
break;
case kNuThreadFormatLZW1:
err = Nu_CompressLZW1(pArchive, pStraw, dstFp, srcLen, &dstLen,
&threadCrc);
break;
case kNuThreadFormatLZW2:
err = Nu_CompressLZW2(pArchive, pStraw, dstFp, srcLen, &dstLen,
&threadCrc);
break;
default:
Assert(0);
err = kNuErrInternal;
goto bail;
}
BailError(err);
pThread->thThreadCRC = threadCrc; /* CRC of uncompressed data */
if (dstLen < srcLen ||
(dstLen == srcLen && targetFormat == kNuThreadFormatUncompressed))
{
/* got smaller, or we didn't try to compress it; keep it */
pThread->thThreadEOF = srcLen;
pThread->thCompThreadEOF = dstLen;
pThread->thThreadFormat = targetFormat;
} else {
/* got bigger, store it uncompressed */
err = Nu_FSeek(dstFp, origOffset, SEEK_SET);
BailError(err);
err = Nu_StrawRewind(pArchive, pStraw);
BailError(err);
if (pProgressData != nil)
Nu_StrawSetProgressState(pStraw, kNuProgressStoring);
err = Nu_ProgressDataCompressPrep(pArchive, pStraw,
kNuThreadFormatUncompressed, srcLen);
BailError(err);
DBUG(("--- compression (%d) failed (%ld vs %ld), storing\n",
targetFormat, dstLen, srcLen));
err = Nu_CompressUncompressed(pArchive, pStraw, dstFp, srcLen,
&dstLen, &threadCrc);
BailError(err);
/* [didn't need to recompute CRC, but I was being paranoid] */
Assert(threadCrc == pThread->thThreadCRC);
pThread->thThreadEOF = srcLen;
pThread->thCompThreadEOF = dstLen;
pThread->thThreadFormat = kNuThreadFormatUncompressed;
}
} else {
/*
* Copy the already-compressed input.
*/
if (pProgressData != nil)
Nu_StrawSetProgressState(pStraw, kNuProgressCopying);
err = Nu_ProgressDataCompressPrep(pArchive, pStraw,
kNuThreadFormatUncompressed, srcLen);
BailError(err);
err = Nu_CompressUncompressed(pArchive, pStraw, dstFp, srcLen,
&dstLen, nil);
BailError(err);
pThread->thThreadEOF = Nu_DataSourceGetOtherLen(pDataSource);
pThread->thCompThreadEOF = srcLen;
pThread->thThreadFormat = sourceFormat;
pThread->thThreadCRC = Nu_DataSourceGetRawCrc(pDataSource);
}
pThread->actualThreadEOF = pThread->thThreadEOF;
done:
DBUG(("+++ srcLen=%ld, dstLen=%ld, actual=%ld\n",
srcLen, dstLen, pThread->actualThreadEOF));
/* make sure we send a final "success" progress message at 100% */
if (pProgressData != nil) {
(void) Nu_StrawSetProgressState(pStraw, kNuProgressDone);
err = Nu_StrawSendProgressUpdate(pArchive, pStraw);
BailError(err);
}
bail:
(void) Nu_StrawFree(pArchive, pStraw);
(void) Nu_DataSinkFree(pDataSink);
return err;
}
/*
* Copy pre-sized data into the archive at the current offset.
*
* All archive-specified fields in "pThread" will be filled in, as will
* "actualThreadEOF". The "nuThreadIdx" and "fileOffset" fields will
* not be modified.
*
* Pre-sized data is always uncompressed, and doesn't have a CRC. This
* will copy the data, and then continue writing zeros to fill out the rest
* of the pre-sized buffer.
*/
NuError
Nu_CopyPresizedToArchive(NuArchive* pArchive, NuDataSource* pDataSource,
NuThreadID threadID, FILE* dstFp, NuThread* pThread, char** ppSavedCopy)
{
NuError err = kNuErrNone;
NuStraw* pStraw = nil;
/*uchar* buffer = nil;*/
ulong srcLen, bufferLen;
ulong count, getsize;
srcLen = Nu_DataSourceGetDataLen(pDataSource);
bufferLen = Nu_DataSourceGetOtherLen(pDataSource);
if (bufferLen < srcLen) {
/* hey, this won't fit! */
DBUG(("--- can't fit %lu into buffer of %lu!\n", srcLen, bufferLen));
err = kNuErrPreSizeOverflow;
goto bail;
}
DBUG(("+++ copying %lu into buffer of %lu\n", srcLen, bufferLen));
pThread->thThreadClass = NuThreadIDGetClass(threadID);
pThread->thThreadFormat = kNuThreadFormatUncompressed;
pThread->thThreadKind = NuThreadIDGetKind(threadID);
pThread->thThreadCRC = 0; /* no CRC on pre-sized stuff */
pThread->thThreadEOF = srcLen;
pThread->thCompThreadEOF = bufferLen;
pThread->actualThreadEOF = bufferLen;
/* nuThreadIdx and fileOffset should already be set */
/*
* Prepare to copy the data through a buffer. The "straw" thing
* is a convenient way to deal with the dataSource, even though we
* don't have a progress updater.
*/
err = Nu_StrawNew(pArchive, pDataSource, nil, &pStraw);
BailError(err);
count = srcLen;
/*buffer = Nu_Malloc(pArchive, kNuFunnelBufSize);*/
/*BailAlloc(buffer);*/
err = Nu_AllocCompressionBufferIFN(pArchive);
BailError(err);
while (count) {
getsize = (count > kNuGenCompBufSize) ? kNuGenCompBufSize : count;
err = Nu_StrawRead(pArchive, pStraw, pArchive->compBuf, getsize);
BailError(err);
err = Nu_FWrite(dstFp, pArchive->compBuf, getsize);
BailError(err);
if (ppSavedCopy != nil && *ppSavedCopy == nil) {
/*
* Grab a copy of the filename for our own use. This assumes
* that the filename fits in kNuGenCompBufSize, which is a
* pretty safe thing to assume.
*/
Assert(threadID == kNuThreadIDFilename);
Assert(count == getsize);
*ppSavedCopy = Nu_Malloc(pArchive, getsize+1);
BailAlloc(*ppSavedCopy);
memcpy(*ppSavedCopy, pArchive->compBuf, getsize);
(*ppSavedCopy)[getsize] = '\0'; /* make sure it's terminated */
}
count -= getsize;
}
/*
* Pad out the rest of the buffer. Could probably do this more
* efficiently through the buffer we've allocated, but these regions
* tend to be either 32 or 200 bytes.
*/
count = bufferLen - srcLen;
while (count--)
Nu_WriteOne(pArchive, dstFp, 0);
bail:
(void) Nu_StrawFree(pArchive, pStraw);
/*Nu_Free(pArchive, buffer);*/
return err;
}

109
nufxlib-0/Crc16.c Normal file
View File

@ -0,0 +1,109 @@
/*
* NuFX archive manipulation library
* Copyright (C) 2000 by Andy McFadden, All Rights Reserved.
* This is free software; you can redistribute it and/or modify it under the
* terms of the GNU Library General Public License, see the file COPYING.LIB.
*
* Compute 16-bit CRCs.
*/
#define __Crc16_c__ 1
#include "NufxLibPriv.h"
#define CRC_TAB
#ifdef CRC_TAB
/*
* updcrc macro derived from article Copyright (C) 1986 Stephen Satchell.
* NOTE: First srgument must be in range 0 to 255.
* Second argument is referenced twice.
*
* Programmers may incorporate any or all code into their programs,
* giving proper credit within the source. Publication of the
* source routines is permitted so long as proper credit is given
* to Stephen Satchell, Satchell Evaluations and Chuck Forsberg,
* Omen Technology.
*/
/*#define updcrc(cp, crc) ( crctab[((crc >> 8) & 255)] ^ (crc << 8) ^ cp)*/
#define updcrc(cp, crc) ( (crctab[((crc >> 8) & 0xFF) ^ cp] ^ (crc << 8)) & 0xFFFF)
/* crctab calculated by Mark G. Mendel, Network Systems Corporation */
const ushort gNuCrc16Table[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
};
#endif
/*
* Calculate CRC on a region
*
* A CRC is the result of a mathematical operation based on the
* coefficients of a polynomial when multiplied by X^16 then divided by
* the generator polynomial (X^16 + X^12 + X^5 + 1) using modulo two
* arithmetic.
*
* This routine is a slightly modified verison of one found in:
* _Advanced Programming Techniques for the Apple //gs Toolbox_
* By Morgan Davis and Dan Gookin (Compute! Publications, Inc.)
* It can either calculate the CRC bit-by-bit or use a table.
*
* Depending on CPU architecture, one may be dramatically faster than
* the other.
*/
ushort
Nu_CalcCRC16(ushort seed, const uchar* ptr, int count)
{
ushort CRC = seed;
#ifndef CRC_TAB
int x;
Assert(sizeof(ushort) == 2); /* I think this is assumed */
#endif
do {
#ifndef CRC_TAB
CRC ^= *ptr++ << 8; /* XOR hi-byte of CRC w/dat */
for (x = 8; x; --x) /* Then, for 8 bit shifts... */
if (CRC & 0x8000) /* Test hi order bit of CRC */
CRC = CRC << 1 ^ 0x1021; /* if set, shift & XOR w/$1021 */
else
CRC <<= 1; /* Else, just shift left once. */
#else
CRC = Nu_UpdateCRC16(*ptr++, CRC); /* look up new value in table */
#endif
} while (--count);
return (CRC);
}

385
nufxlib-0/Debug.c Normal file
View File

@ -0,0 +1,385 @@
/*
* NuFX archive manipulation library
* Copyright (C) 2000 by Andy McFadden, All Rights Reserved.
* This is free software; you can redistribute it and/or modify it under the
* terms of the GNU Library General Public License, see the file COPYING.LIB.
*
* Debugging functions. These are omitted from the non-debug build.
*/
#include "NufxLibPriv.h"
#if defined(DEBUG_MSGS)
/* pull a string out of one of the static arrays */
#define GetStaticString(index, staticArray) ( \
(index) >= NELEM(staticArray) ? "<unknown>" : staticArray[index] \
)
/* thread's thread_class */
static const char* gThreadClassNames[] = {
"message_thread",
"control_thread",
"data_thread",
"filename_thread",
};
/* thread's thread_format */
static const char* gThreadFormatNames[] = {
"uncompressed",
"Huffman Squeeze",
"dynamic LZW/1",
"dynamic LZW/2",
"12-bit LZC",
"16-bit LZC",
};
/* days of the week */
static const char* gDayNames[] = {
"[ null ]",
"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
};
/* months of the year */
static const char* gMonths[] = {
"Jan", "Feb", "Mar", "Apr", "May", "Jun",
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
};
#define kNuDateOutputLen 64
/* file_sys_id values */
static const char* gFileSysIDs[] = {
"Reserved/unknown ($00)", "ProDOS/SOS", "DOS 3.3", "DOS 3.2",
"Apple II Pascal", "Macintosh (MFS)", "Macintosh (HFS)",
"LISA file system", "Apple CP/M", "Reserved 0x09", "MS-DOS",
"High-Sierra", "ISO 9660", "AppleShare"
};
/*
* Convert a DateTime structure into something printable.
*
* The buffer passed in must hold at least kNuDateOutputLen bytes.
*
* Returns "buffer" for the benefit of printf() calls.
*/
static char*
Nu_DebugDumpDate(const NuDateTime* pDateTime, char* buffer)
{
char* cp;
/* is it valid? */
if (pDateTime->day > 30 || pDateTime->month > 11 || pDateTime->hour > 24 ||
pDateTime->minute > 59)
{
strcpy(buffer, " <invalid> ");
goto bail;
}
/* is it empty? */
if ((pDateTime->second | pDateTime->minute | pDateTime->hour |
pDateTime->year | pDateTime->day | pDateTime->month |
pDateTime->extra | pDateTime->weekDay) == 0)
{
strcpy(buffer, " [No Date] ");
goto bail;
}
cp = buffer;
/* only print weekDay if one was stored */
if (pDateTime->weekDay) {
if (pDateTime->weekDay < NELEM(gDayNames))
sprintf(cp, "%s, ", gDayNames[pDateTime->weekDay]);
else
sprintf(cp, "??%d, ", pDateTime->weekDay);
cp += strlen(cp);
}
sprintf(cp, "%02d-%s-%04d %02d:%02d:%02d",
pDateTime->day+1, gMonths[pDateTime->month],
pDateTime->year < 40 ? pDateTime->year + 2000 : pDateTime->year + 1900,
pDateTime->hour, pDateTime->minute, pDateTime->second);
bail:
sprintf(buffer + strlen(buffer), " [s%d m%d h%d Y%d D%d M%d x%d w%d]",
pDateTime->second, pDateTime->minute, pDateTime->hour,
pDateTime->year, pDateTime->day, pDateTime->month, pDateTime->extra,
pDateTime->weekDay);
return buffer;
}
/*
* Convert a buffer into a hexadecimal character string.
*
* The result will be 2x the size of the original, +1 for a null byte.
*/
static void
ConvertToHexStr(const uchar* inBuf, int inLen, char* outBuf)
{
while (inLen--) {
*outBuf++ = HexConv((*inBuf >> 4) & 0x0f);
*outBuf++ = HexConv(*inBuf & 0x0f);
inBuf++;
}
*outBuf = '\0';
}
/*
* Dump everything we know about pThread.
*/
void
Nu_DebugDumpThread(const NuThread* pThread)
{
static const char* kInd = " ";
NuThreadID threadID;
const char* descr;
Assert(pThread != nil);
printf("%sThreadClass: 0x%04x (%s)\n", kInd,
pThread->thThreadClass,
GetStaticString(pThread->thThreadClass, gThreadClassNames));
printf("%sThreadFormat: 0x%04x (%s)\n", kInd,
pThread->thThreadFormat,
GetStaticString(pThread->thThreadFormat, gThreadFormatNames));
threadID = NuMakeThreadID(pThread->thThreadClass, pThread->thThreadKind);
switch (threadID) {
case kNuThreadIDOldComment: descr = "old comment"; break;
case kNuThreadIDComment: descr = "comment"; break;
case kNuThreadIDIcon: descr = "icon"; break;
case kNuThreadIDMkdir: descr = "mkdir"; break;
case kNuThreadIDDataFork: descr = "data fork"; break;
case kNuThreadIDDiskImage: descr = "disk image"; break;
case kNuThreadIDRsrcFork: descr = "rsrc fork"; break;
case kNuThreadIDFilename: descr = "filename"; break;
default: descr = "<unknown>"; break;
}
printf("%sThreadKind: 0x%04x (%s)\n", kInd,
pThread->thThreadKind, descr);
printf("%sThreadCRC: 0x%04x ThreadEOF: %lu CompThreadEOF: %lu\n", kInd,
pThread->thThreadCRC, pThread->thThreadEOF, pThread->thCompThreadEOF);
printf("%s*File data offset: %ld actualThreadEOF: %ld\n", kInd,
pThread->fileOffset, pThread->actualThreadEOF);
}
/*
* Dump everything we know about pRecord, including its threads and ThreadMods.
*
* Changes to existing records are made to the "copy" set, not the "orig"
* set. Pass in the "orig" copy in "pRecord", and optionally pass in the
* "copy" set in "pXrefRecord" to glean data from both.
*/
static void
Nu_DebugDumpRecord(NuArchive* pArchive, const NuRecord* pRecord,
const NuRecord* pXrefRecord, Boolean isDeleted)
{
NuError err; /* dummy */
static const char* kInd = " ";
char dateBuf[kNuDateOutputLen];
const NuThreadMod* pThreadMod;
const NuThread* pThread;
ulong idx;
Assert(pRecord != nil);
printf("%s%s%sFilename: '%s' (idx=%lu)\n", kInd,
isDeleted ? "[DEL] " : "",
pXrefRecord != nil && pXrefRecord->pThreadMods != nil ? "[MOD] " : "",
pRecord->filename == nil ? "<not specified>" : pRecord->filename,
pRecord->recordIdx);
printf("%sHeaderID: '%.4s' VersionNumber: 0x%04x HeaderCRC: 0x%04x\n",
kInd,
pRecord->recNufxID, pRecord->recVersionNumber, pRecord->recHeaderCRC);
printf("%sAttribCount: %u TotalThreads: %lu\n", kInd,
pRecord->recAttribCount, pRecord->recTotalThreads);
printf("%sFileSysID: %u (%s) FileSysInfo: 0x%04x ('%c')\n", kInd,
pRecord->recFileSysID,
GetStaticString(pRecord->recFileSysID, gFileSysIDs),
pRecord->recFileSysInfo,
NuGetSepFromSysInfo(pRecord->recFileSysInfo));
/* do something fancy for ProDOS? */
printf("%sFileType: 0x%08lx ExtraType: 0x%08lx Access: 0x%08lx\n", kInd,
pRecord->recFileType, pRecord->recExtraType, pRecord->recAccess);
printf("%sCreateWhen: %s\n", kInd,
Nu_DebugDumpDate(&pRecord->recCreateWhen, dateBuf));
printf("%sModWhen: %s\n", kInd,
Nu_DebugDumpDate(&pRecord->recModWhen, dateBuf));
printf("%sArchiveWhen: %s\n", kInd,
Nu_DebugDumpDate(&pRecord->recArchiveWhen, dateBuf));
printf("%sStorageType: %u OptionSize: %u FilenameLength: %u\n", kInd,
pRecord->recStorageType, pRecord->recOptionSize,
pRecord->recFilenameLength);
if (pRecord->recOptionSize) {
char* outBuf = Nu_Malloc(pArchive, pRecord->recOptionSize * 2 +1);
BailAlloc(outBuf);
Assert(pRecord->recOptionList != nil);
ConvertToHexStr(pRecord->recOptionList, pRecord->recOptionSize, outBuf);
printf("%sOptionList: [%s]\n", kInd, outBuf);
Nu_Free(pArchive, outBuf);
}
printf("%s*ExtraCount: %ld RecFileOffset: %ld RecHeaderLength: %ld\n",
kInd,
pRecord->extraCount, pRecord->fileOffset, pRecord->recHeaderLength);
for (idx = 0; idx < pRecord->recTotalThreads; idx++) {
pThread = Nu_GetThread(pRecord, idx);
Assert(pThread != nil);
printf("%s--Thread #%lu (idx=%lu)\n", kInd, idx, pThread->threadIdx);
Nu_DebugDumpThread(pThread);
}
if (pXrefRecord != nil)
pThreadMod = pXrefRecord->pThreadMods;
else
pThreadMod = pRecord->pThreadMods; /* probably empty */
if (pThreadMod != nil)
printf("%s*ThreadMods -----\n", kInd);
while (pThreadMod != nil) {
switch (pThreadMod->entry.kind) {
case kNuThreadModAdd:
printf("%s *-ThreadMod ADD 0x%08lx 0x%04x (sourceType=%d)\n", kInd,
pThreadMod->entry.add.threadID,
pThreadMod->entry.add.threadFormat,
Nu_DataSourceGetType(pThreadMod->entry.add.pDataSource));
break;
case kNuThreadModUpdate:
printf("%s *-ThreadMod UPDATE %06ld\n", kInd,
pThreadMod->entry.update.threadIdx);
break;
case kNuThreadModDelete:
printf("%s *-ThreadMod DELETE %06ld\n", kInd,
pThreadMod->entry.delete.threadIdx);
break;
case kNuThreadModUnknown:
default:
Assert(0);
printf("%s++ThreadMod UNKNOWN\n", kInd);
break;
}
pThreadMod = pThreadMod->pNext;
}
/*printf("%s*TotalLength: %ld TotalCompLength: %ld\n",
kInd, pRecord->totalLength, pRecord->totalCompLength);*/
printf("%s*TotalCompLength: %ld\n", kInd, pRecord->totalCompLength);
printf("\n");
bail:
return;
}
/*
* Dump the records in a RecordSet.
*/
static void
Nu_DebugDumpRecordSet(NuArchive* pArchive, const NuRecordSet* pRecordSet,
const NuRecordSet* pXrefSet)
{
const NuRecord* pRecord;
const NuRecord* pXrefRecord;
Boolean doXref;
long count;
doXref = false;
pXrefRecord = nil;
if (pXrefSet != nil && Nu_RecordSet_GetLoaded(pXrefSet)) {
pXrefRecord = Nu_RecordSet_GetListHead(pXrefSet);
doXref = true;
}
/* dump every record, if we've loaded them */
count = Nu_RecordSet_GetNumRecords(pRecordSet);
pRecord = Nu_RecordSet_GetListHead(pRecordSet);
if (pRecord != nil) {
Assert(count != 0);
while (count--) {
Assert(pRecord != nil);
if (pXrefRecord != nil &&
pRecord->recordIdx == pXrefRecord->recordIdx)
{
Nu_DebugDumpRecord(pArchive, pRecord, pXrefRecord, false);
pXrefRecord = pXrefRecord->pNext;
} else {
Nu_DebugDumpRecord(pArchive, pRecord, nil, doXref);
}
pRecord = pRecord->pNext;
}
} else {
Assert(count == 0);
}
}
/*
* Dump the master header block.
*/
static void
Nu_DebugDumpMH(const NuMasterHeader* pMasterHeader)
{
static const char* kInd = " ";
char dateBuf1[kNuDateOutputLen];
Assert(pMasterHeader != nil);
printf("%sNufileID: '%.6s' MasterCRC: 0x%04x TotalRecords: %lu\n", kInd,
pMasterHeader->mhNufileID, pMasterHeader->mhMasterCRC,
pMasterHeader->mhTotalRecords);
printf("%sArchiveCreateWhen: %s\n", kInd,
Nu_DebugDumpDate(&pMasterHeader->mhArchiveCreateWhen, dateBuf1));
printf("%sArchiveModWhen: %s\n", kInd,
Nu_DebugDumpDate(&pMasterHeader->mhArchiveModWhen, dateBuf1));
printf("%sMasterVersion: %u MasterEOF: %lu\n", kInd,
pMasterHeader->mhMasterVersion, pMasterHeader->mhMasterEOF);
}
/*
* Dump everything we know about pArchive.
*
* This will only print the records that we have seen so far. If the
* application hasn't caused us to scan through all of the records in
* the archive, then this won't be very interesting. This will never
* show any records for streaming-mode archives.
*/
void
Nu_DebugDumpAll(NuArchive* pArchive)
{
Assert(pArchive != nil);
printf("*Archive pathname: '%s'\n", pArchive->archivePathname);
printf("*Archive type: %d\n", pArchive->archiveType);
printf("*Header offset: %ld\n", pArchive->headerOffset);
printf("*Num records: %ld orig, %ld copy, %ld new\n",
Nu_RecordSet_GetNumRecords(&pArchive->origRecordSet),
Nu_RecordSet_GetNumRecords(&pArchive->copyRecordSet),
Nu_RecordSet_GetNumRecords(&pArchive->newRecordSet));
printf("*NuRecordIdx seed: %lu NuRecordIdx next: %lu\n",
pArchive->recordIdxSeed, pArchive->nextRecordIdx);
/* master header */
Nu_DebugDumpMH(&pArchive->masterHeader);
printf(" *ORIG record set (x-ref with COPY):\n");
Nu_DebugDumpRecordSet(pArchive, &pArchive->origRecordSet,
&pArchive->copyRecordSet);
printf(" *NEW record set:\n");
Nu_DebugDumpRecordSet(pArchive, &pArchive->newRecordSet, nil);
if (!Nu_RecordSet_GetLoaded(&pArchive->origRecordSet) &&
!Nu_RecordSet_GetLoaded(&pArchive->newRecordSet))
{
printf("*** DEBUG: original records not loaded yet? ***\n");
}
}
#endif /*DEBUG_MSGS*/

2495
nufxlib-0/Deferred.c Normal file

File diff suppressed because it is too large Load Diff

794
nufxlib-0/Entry.c Normal file
View File

@ -0,0 +1,794 @@
/*
* NuFX archive manipulation library
* Copyright (C) 2000 by Andy McFadden, All Rights Reserved.
* This is free software; you can redistribute it and/or modify it under the
* terms of the GNU Library General Public License, see the file COPYING.LIB.
*
* All external entry points.
*/
#include "NufxLibPriv.h"
/*
* ===========================================================================
* Misc utils
* ===========================================================================
*/
/*
* Set the busy flag.
*
* The busy flag is intended to prevent the caller from executing illegal
* operations while inside a callback function. It is NOT intended to
* allow concurrent access to the same archive from multiple threads, so
* it does not follow all sorts of crazy semaphore semantics. If you
* have the need, go ahead and fix it.
*/
static inline void
Nu_SetBusy(NuArchive* pArchive)
{
pArchive->busy = true;
}
/*
* Clear the busy flag.
*/
static inline void
Nu_ClearBusy(NuArchive* pArchive)
{
pArchive->busy = false;
}
/*
* Do a partial validation on NuArchive. Some calls, such as GetExtraData,
* can be made during callback functions when the archive isn't fully
* consistent.
*/
static NuError
Nu_PartiallyValidateNuArchive(const NuArchive* pArchive)
{
if (pArchive == nil)
return kNuErrInvalidArg;
pArchive = pArchive;
if (pArchive->structMagic != kNuArchiveStructMagic)
return kNuErrBadStruct;
return kNuErrNone;
}
/*
* Validate the NuArchive* argument passed in to us.
*/
static NuError
Nu_ValidateNuArchive(const NuArchive* pArchive)
{
NuError err;
err = Nu_PartiallyValidateNuArchive(pArchive);
if (err != kNuErrNone)
return err;
/* explicitly block reentrant calls */
if (pArchive->busy)
return kNuErrBusy;
/* make sure the TOC state is consistent */
if (pArchive->haveToc) {
if (pArchive->masterHeader.mhTotalRecords != 0)
Assert(Nu_RecordSet_GetListHead(&pArchive->origRecordSet) != nil);
Assert(Nu_RecordSet_GetNumRecords(&pArchive->origRecordSet) ==
pArchive->masterHeader.mhTotalRecords);
} else {
Assert(Nu_RecordSet_GetListHead(&pArchive->origRecordSet) == nil);
}
/* make sure we have open files to work with */
Assert(pArchive->archivePathname == nil || pArchive->archiveFp != nil);
if (pArchive->archivePathname != nil && pArchive->archiveFp == nil)
return kNuErrInternal;
Assert(pArchive->tmpPathname == nil || pArchive->tmpFp != nil);
if (pArchive->tmpPathname != nil && pArchive->tmpFp == nil)
return kNuErrInternal;
/* further validations */
return kNuErrNone;
}
/*
* ===========================================================================
* Streaming and non-streaming read-only
* ===========================================================================
*/
NuError
NuStreamOpenRO(FILE* infp, NuArchive** ppArchive)
{
NuError err;
if (infp == nil || ppArchive == nil)
return kNuErrInvalidArg;
err = Nu_StreamOpenRO(infp, (NuArchive**) ppArchive);
return err;
}
NuError
NuContents(NuArchive* pArchive, NuCallback contentFunc)
{
NuError err;
if ((err = Nu_ValidateNuArchive(pArchive)) == kNuErrNone) {
Nu_SetBusy(pArchive);
if (Nu_IsStreaming(pArchive))
err = Nu_StreamContents(pArchive, contentFunc);
else
err = Nu_Contents(pArchive, contentFunc);
Nu_ClearBusy(pArchive);
}
return err;
}
NuError
NuExtract(NuArchive* pArchive)
{
NuError err;
if ((err = Nu_ValidateNuArchive(pArchive)) == kNuErrNone) {
Nu_SetBusy(pArchive);
if (Nu_IsStreaming(pArchive))
err = Nu_StreamExtract(pArchive);
else
err = Nu_Extract(pArchive);
Nu_ClearBusy(pArchive);
}
return err;
}
NuError
NuTest(NuArchive* pArchive)
{
NuError err;
if ((err = Nu_ValidateNuArchive(pArchive)) == kNuErrNone) {
Nu_SetBusy(pArchive);
if (Nu_IsStreaming(pArchive))
err = Nu_StreamTest(pArchive);
else
err = Nu_Test(pArchive);
Nu_ClearBusy(pArchive);
}
return err;
}
/*
* ===========================================================================
* Strictly non-streaming read-only
* ===========================================================================
*/
NuError
NuOpenRO(const char* filename, NuArchive** ppArchive)
{
NuError err;
err = Nu_OpenRO(filename, (NuArchive**) ppArchive);
return err;
}
NuError
NuExtractRecord(NuArchive* pArchive, NuRecordIdx recordIdx)
{
NuError err;
if ((err = Nu_ValidateNuArchive(pArchive)) == kNuErrNone) {
Nu_SetBusy(pArchive);
err = Nu_ExtractRecord(pArchive, recordIdx);
Nu_ClearBusy(pArchive);
}
return err;
}
NuError
NuExtractThread(NuArchive* pArchive, NuThreadIdx threadIdx,
NuDataSink* pDataSink)
{
NuError err;
if ((err = Nu_ValidateNuArchive(pArchive)) == kNuErrNone) {
Nu_SetBusy(pArchive);
err = Nu_ExtractThread(pArchive, threadIdx, pDataSink);
Nu_ClearBusy(pArchive);
}
return err;
}
NuError
NuGetRecord(NuArchive* pArchive, NuRecordIdx recordIdx,
const NuRecord** ppRecord)
{
NuError err;
if ((err = Nu_ValidateNuArchive(pArchive)) == kNuErrNone) {
Nu_SetBusy(pArchive);
err = Nu_GetRecord(pArchive, recordIdx, ppRecord);
Nu_ClearBusy(pArchive);
}
return err;
}
NuError
NuGetRecordIdxByName(NuArchive* pArchive, const char* name,
NuRecordIdx* pRecordIdx)
{
NuError err;
if ((err = Nu_ValidateNuArchive(pArchive)) == kNuErrNone) {
Nu_SetBusy(pArchive);
err = Nu_GetRecordIdxByName(pArchive, name, pRecordIdx);
Nu_ClearBusy(pArchive);
}
return err;
}
NuError
NuGetRecordIdxByPosition(NuArchive* pArchive, unsigned long position,
NuRecordIdx* pRecordIdx)
{
NuError err;
if ((err = Nu_ValidateNuArchive(pArchive)) == kNuErrNone) {
Nu_SetBusy(pArchive);
err = Nu_GetRecordIdxByPosition(pArchive, position, pRecordIdx);
Nu_ClearBusy(pArchive);
}
return err;
}
/*
* ===========================================================================
* Read/Write
* ===========================================================================
*/
NuError
NuOpenRW(const char* archivePathname, const char* tmpPathname,
unsigned long flags, NuArchive** ppArchive)
{
NuError err;
err = Nu_OpenRW(archivePathname, tmpPathname, flags,
(NuArchive**) ppArchive);
return err;
}
NuError
NuFlush(NuArchive* pArchive, long* pStatusFlags)
{
NuError err;
if ((err = Nu_ValidateNuArchive(pArchive)) == kNuErrNone) {
Nu_SetBusy(pArchive);
err = Nu_Flush(pArchive, pStatusFlags);
Nu_ClearBusy(pArchive);
}
return err;
}
NuError
NuAbort(NuArchive* pArchive)
{
NuError err;
if ((err = Nu_ValidateNuArchive(pArchive)) == kNuErrNone) {
Nu_SetBusy(pArchive);
err = Nu_Abort(pArchive);
Nu_ClearBusy(pArchive);
}
return err;
}
NuError
NuAddRecord(NuArchive* pArchive, const NuFileDetails* pFileDetails,
NuRecordIdx* pRecordIdx)
{
NuError err;
if ((err = Nu_ValidateNuArchive(pArchive)) == kNuErrNone) {
Nu_SetBusy(pArchive);
err = Nu_AddRecord(pArchive, pFileDetails, pRecordIdx, nil);
Nu_ClearBusy(pArchive);
}
return err;
}
NuError
NuAddThread(NuArchive* pArchive, NuRecordIdx recordIdx, NuThreadID threadID,
NuDataSource* pDataSource, NuThreadIdx* pThreadIdx)
{
NuError err;
if ((err = Nu_ValidateNuArchive(pArchive)) == kNuErrNone) {
Nu_SetBusy(pArchive);
err = Nu_AddThread(pArchive, recordIdx, threadID,
pDataSource, pThreadIdx);
Nu_ClearBusy(pArchive);
}
return err;
}
NuError
NuAddFile(NuArchive* pArchive, const char* pathname,
const NuFileDetails* pFileDetails, short isFromRsrcFork,
NuRecordIdx* pRecordIdx)
{
NuError err;
if ((err = Nu_ValidateNuArchive(pArchive)) == kNuErrNone) {
Nu_SetBusy(pArchive);
err = Nu_AddFile(pArchive, pathname, pFileDetails,
(Boolean)(isFromRsrcFork != 0), pRecordIdx);
Nu_ClearBusy(pArchive);
}
return err;
}
NuError
NuRename(NuArchive* pArchive, NuRecordIdx recordIdx, const char* pathname,
char fssep)
{
NuError err;
if ((err = Nu_ValidateNuArchive(pArchive)) == kNuErrNone) {
Nu_SetBusy(pArchive);
err = Nu_Rename(pArchive, recordIdx, pathname, fssep);
Nu_ClearBusy(pArchive);
}
return err;
}
NuError
NuSetRecordAttr(NuArchive* pArchive, NuRecordIdx recordIdx,
const NuRecordAttr* pRecordAttr)
{
NuError err;
if ((err = Nu_ValidateNuArchive(pArchive)) == kNuErrNone) {
Nu_SetBusy(pArchive);
err = Nu_SetRecordAttr(pArchive, recordIdx, pRecordAttr);
Nu_ClearBusy(pArchive);
}
return err;
}
NuError
NuUpdatePresizedThread(NuArchive* pArchive, NuThreadIdx threadIdx,
NuDataSource* pDataSource, long* pMaxLen)
{
NuError err;
if ((err = Nu_ValidateNuArchive(pArchive)) == kNuErrNone) {
Nu_SetBusy(pArchive);
err = Nu_UpdatePresizedThread(pArchive, threadIdx,
pDataSource, pMaxLen);
Nu_ClearBusy(pArchive);
}
return err;
}
NuError
NuDelete(NuArchive* pArchive)
{
NuError err;
if ((err = Nu_ValidateNuArchive(pArchive)) == kNuErrNone) {
Nu_SetBusy(pArchive);
err = Nu_Delete(pArchive);
Nu_ClearBusy(pArchive);
}
return err;
}
NuError
NuDeleteRecord(NuArchive* pArchive, NuRecordIdx recordIdx)
{
NuError err;
if ((err = Nu_ValidateNuArchive(pArchive)) == kNuErrNone) {
Nu_SetBusy(pArchive);
err = Nu_DeleteRecord(pArchive, recordIdx);
Nu_ClearBusy(pArchive);
}
return err;
}
NuError
NuDeleteThread(NuArchive* pArchive, NuThreadIdx threadIdx)
{
NuError err;
if ((err = Nu_ValidateNuArchive(pArchive)) == kNuErrNone) {
Nu_SetBusy(pArchive);
err = Nu_DeleteThread(pArchive, threadIdx);
Nu_ClearBusy(pArchive);
}
return err;
}
/*
* ===========================================================================
* General interfaces
* ===========================================================================
*/
NuError
NuClose(NuArchive* pArchive)
{
NuError err;
if ((err = Nu_ValidateNuArchive(pArchive)) == kNuErrNone) {
Nu_SetBusy(pArchive);
err = Nu_Close(pArchive);
/* on success, pArchive has been freed */
if (err != kNuErrNone)
Nu_ClearBusy(pArchive);
}
return err;
}
NuError
NuGetMasterHeader(NuArchive* pArchive, const NuMasterHeader** ppMasterHeader)
{
NuError err;
if ((err = Nu_ValidateNuArchive(pArchive)) == kNuErrNone)
err = Nu_GetMasterHeader(pArchive, ppMasterHeader);
return err;
}
NuError
NuGetExtraData(NuArchive* pArchive, void** ppData)
{
NuError err;
if (ppData == nil)
return kNuErrInvalidArg;
if ((err = Nu_PartiallyValidateNuArchive(pArchive)) == kNuErrNone)
*ppData = pArchive->extraData;
return err;
}
NuError
NuSetExtraData(NuArchive* pArchive, void* pData)
{
NuError err;
if ((err = Nu_PartiallyValidateNuArchive(pArchive)) == kNuErrNone)
pArchive->extraData = pData;
return err;
}
NuError
NuGetValue(NuArchive* pArchive, NuValueID ident, NuValue* pValue)
{
NuError err;
if ((err = Nu_PartiallyValidateNuArchive(pArchive)) == kNuErrNone)
return Nu_GetValue(pArchive, ident, pValue);
return err;
}
NuError
NuSetValue(NuArchive* pArchive, NuValueID ident, NuValue value)
{
NuError err;
if ((err = Nu_PartiallyValidateNuArchive(pArchive)) == kNuErrNone)
return Nu_SetValue(pArchive, ident, value);
return err;
}
NuError
NuGetAttr(NuArchive* pArchive, NuAttrID ident, NuAttr* pAttr)
{
NuError err;
if ((err = Nu_PartiallyValidateNuArchive(pArchive)) == kNuErrNone)
return Nu_GetAttr(pArchive, ident, pAttr);
return err;
}
const char*
NuStrError(NuError err)
{
return Nu_StrError(err);
}
NuError
NuDebugDumpArchive(NuArchive* pArchive)
{
#if defined(DEBUG_MSGS)
/* skip validation checks for this one */
Nu_DebugDumpAll(pArchive);
return kNuErrNone;
#else
/* function doesn't exist */
return kNuErrGeneric;
#endif
}
NuError
NuGetVersion(long* pMajorVersion, long* pMinorVersion, long* pBugVersion,
const char** ppBuildDate, const char** ppBuildFlags)
{
return Nu_GetVersion(pMajorVersion, pMinorVersion, pBugVersion,
ppBuildDate, ppBuildFlags);
}
/*
* ===========================================================================
* Sources and Sinks
* ===========================================================================
*/
NuError
NuCreateDataSourceForFile(NuThreadFormat threadFormat, short doClose,
unsigned long otherLen, const char* pathname, short isFromRsrcFork,
NuDataSource** ppDataSource)
{
return Nu_DataSourceFile_New(threadFormat, (Boolean)(doClose != 0),
otherLen, pathname, (Boolean)(isFromRsrcFork != 0), ppDataSource);
}
NuError
NuCreateDataSourceForFP(NuThreadFormat threadFormat, short doClose,
unsigned long otherLen, FILE* fp, long offset, long length,
NuDataSource** ppDataSource)
{
return Nu_DataSourceFP_New(threadFormat, (Boolean)(doClose != 0),
otherLen, fp, offset, length, ppDataSource);
}
NuError
NuCreateDataSourceForBuffer(NuThreadFormat threadFormat, short doClose,
unsigned long otherLen, const unsigned char* buffer, long offset,
long length, NuDataSource** ppDataSource)
{
return Nu_DataSourceBuffer_New(threadFormat, (Boolean)(doClose != 0),
otherLen, buffer, offset, length, ppDataSource);
}
NuError
NuFreeDataSource(NuDataSource* pDataSource)
{
return Nu_DataSourceFree(pDataSource);
}
NuError
NuDataSourceSetRawCrc(NuDataSource* pDataSource, unsigned short crc)
{
if (pDataSource == nil)
return kNuErrInvalidArg;
Nu_DataSourceSetRawCrc(pDataSource, crc);
return kNuErrNone;
}
NuError
NuCreateDataSinkForFile(short doExpand, NuValue convertEOL,
const char* pathname, char fssep, NuDataSink** ppDataSink)
{
return Nu_DataSinkFile_New((Boolean)(doExpand != 0), convertEOL, pathname,
fssep, ppDataSink);
}
NuError
NuCreateDataSinkForFP(short doExpand, NuValue convertEOL, FILE* fp,
NuDataSink** ppDataSink)
{
return Nu_DataSinkFP_New((Boolean)(doExpand != 0), convertEOL, fp,
ppDataSink);
}
NuError
NuCreateDataSinkForBuffer(short doExpand, NuValue convertEOL,
unsigned char* buffer, unsigned long bufLen, NuDataSink** ppDataSink)
{
return Nu_DataSinkBuffer_New((Boolean)(doExpand != 0), convertEOL, buffer,
bufLen, ppDataSink);
}
NuError
NuFreeDataSink(NuDataSink* pDataSink)
{
return Nu_DataSinkFree(pDataSink);
}
NuError
NuDataSinkGetOutCount(NuDataSink* pDataSink, ulong* pOutCount)
{
if (pDataSink == nil || pOutCount == nil)
return kNuErrInvalidArg;
*pOutCount = Nu_DataSinkGetOutCount(pDataSink);
return kNuErrNone;
}
/*
* ===========================================================================
* Non-archive operations
* ===========================================================================
*/
void
NuRecordCopyAttr(NuRecordAttr* pRecordAttr, const NuRecord* pRecord)
{
pRecordAttr->fileSysID = pRecord->recFileSysID;
/*pRecordAttr->fileSysInfo = pRecord->recFileSysInfo;*/
pRecordAttr->access = pRecord->recAccess;
pRecordAttr->fileType = pRecord->recFileType;
pRecordAttr->extraType = pRecord->recExtraType;
pRecordAttr->createWhen = pRecord->recCreateWhen;
pRecordAttr->modWhen = pRecord->recModWhen;
pRecordAttr->archiveWhen = pRecord->recArchiveWhen;
}
NuError
NuRecordCopyThreads(const NuRecord* pNuRecord, NuThread** ppThreads)
{
if (pNuRecord == nil || ppThreads == nil)
return kNuErrInvalidArg;
Assert(pNuRecord->pThreads != nil);
*ppThreads = Nu_Malloc(nil, pNuRecord->recTotalThreads * sizeof(NuThread));
if (*ppThreads == nil)
return kNuErrMalloc;
memcpy(*ppThreads, pNuRecord->pThreads,
pNuRecord->recTotalThreads * sizeof(NuThread));
return kNuErrNone;
}
unsigned long
NuRecordGetNumThreads(const NuRecord* pNuRecord)
{
if (pNuRecord == nil)
return -1;
return pNuRecord->recTotalThreads;
}
const NuThread*
NuThreadGetByIdx(const NuThread* pNuThread, long idx)
{
if (pNuThread == nil)
return nil;
return &pNuThread[idx]; /* can't range-check here */
}
short
NuIsPresizedThreadID(NuThreadID threadID)
{
return Nu_IsPresizedThreadID(threadID);
}
/*
* ===========================================================================
* Callback setters
* ===========================================================================
*/
NuError
NuSetSelectionFilter(NuArchive* pArchive, NuCallback filterFunc)
{
NuError err;
/*Assert(!((ulong)filterFunc % 4));*/
if ((err = Nu_ValidateNuArchive(pArchive)) == kNuErrNone)
pArchive->selectionFilterFunc = filterFunc;
return err;
}
NuError
NuSetOutputPathnameFilter(NuArchive* pArchive, NuCallback filterFunc)
{
NuError err;
/*Assert(!((ulong)filterFunc % 4));*/
if ((err = Nu_ValidateNuArchive(pArchive)) == kNuErrNone)
pArchive->outputPathnameFunc = filterFunc;
return err;
}
NuError
NuSetProgressUpdater(NuArchive* pArchive, NuCallback updateFunc)
{
NuError err;
/*Assert(!((ulong)updateFunc % 4));*/
if ((err = Nu_ValidateNuArchive(pArchive)) == kNuErrNone)
pArchive->progressUpdaterFunc = updateFunc;
return err;
}
NuError
NuSetErrorHandler(NuArchive* pArchive, NuCallback errorFunc)
{
NuError err;
/*Assert(!((ulong)errorFunc % 4));*/
if ((err = Nu_ValidateNuArchive(pArchive)) == kNuErrNone)
pArchive->errorHandlerFunc = errorFunc;
return err;
}
NuError
NuSetErrorMessageHandler(NuArchive* pArchive, NuCallback messageHandlerFunc)
{
NuError err;
/*Assert(!((ulong)messageHandlerFunc % 4));*/
if ((err = Nu_ValidateNuArchive(pArchive)) == kNuErrNone)
pArchive->messageHandlerFunc = messageHandlerFunc;
return err;
}
NuError
NuSetGlobalErrorMessageHandler(NuCallback messageHandlerFunc)
{
/*Assert(!((ulong)messageHandlerFunc % 4));*/
gNuGlobalErrorMessageHandler = messageHandlerFunc;
return kNuErrNone;
}

213
nufxlib-0/Expand.c Normal file
View File

@ -0,0 +1,213 @@
/*
* NuFX archive manipulation library
* Copyright (C) 2000 by Andy McFadden, All Rights Reserved.
* This is free software; you can redistribute it and/or modify it under the
* terms of the GNU Library General Public License, see the file COPYING.LIB.
*
* Expand a thread from an archive.
*/
#include "NufxLibPriv.h"
/*
* "Expand" an uncompressed thread.
*/
static NuError
Nu_ExpandUncompressed(NuArchive* pArchive, const NuRecord* pRecord,
const NuThread* pThread, FILE* infp, NuFunnel* pFunnel, ushort* pCrc)
{
NuError err;
/*uchar* buffer = nil;*/
ulong count, getsize;
Assert(pArchive != nil);
Assert(pThread != nil);
Assert(infp != nil);
Assert(pFunnel != nil);
/* doesn't have to be same size as funnel, but it's not a bad idea */
/*buffer = Nu_Malloc(pArchive, kNuFunnelBufSize);*/
/*BailAlloc(buffer);*/
err = Nu_AllocCompressionBufferIFN(pArchive);
BailError(err);
/* quick assert for bad archive that should have been caught earlier */
/* (filename threads are uncompressed, but compThreadEOF is buf len) */
if (pThread->thThreadClass == kNuThreadClassData)
Assert(pThread->actualThreadEOF == pThread->thCompThreadEOF);
count = pThread->actualThreadEOF;
while (count) {
getsize = (count > kNuGenCompBufSize) ? kNuGenCompBufSize : count;
err = Nu_FRead(infp, pArchive->compBuf, getsize);
BailError(err);
if (pCrc != nil)
*pCrc = Nu_CalcCRC16(*pCrc, pArchive->compBuf, getsize);
err = Nu_FunnelWrite(pArchive, pFunnel, pArchive->compBuf, getsize);
BailError(err);
count -= getsize;
}
err = Nu_FunnelFlush(pArchive, pFunnel);
BailError(err);
bail:
/*Nu_Free(pArchive, buffer);*/
return err;
}
/*
* Copy the "raw" data out of the thread. Unlike the preceeding function,
* this reads up to "thCompThreadEOF", and doesn't even try to compute a CRC.
*/
static NuError
Nu_ExpandRaw(NuArchive* pArchive, const NuThread* pThread, FILE* infp,
NuFunnel* pFunnel)
{
NuError err;
/*uchar* buffer = nil;*/
ulong count, getsize;
Assert(pArchive != nil);
Assert(pThread != nil);
Assert(infp != nil);
Assert(pFunnel != nil);
/* doesn't have to be same size as funnel, but it's not a bad idea */
/*buffer = Nu_Malloc(pArchive, kNuFunnelBufSize);*/
/*BailAlloc(buffer);*/
err = Nu_AllocCompressionBufferIFN(pArchive);
BailError(err);
count = pThread->thCompThreadEOF;
while (count) {
getsize = (count > kNuGenCompBufSize) ? kNuGenCompBufSize : count;
err = Nu_FRead(infp, pArchive->compBuf, getsize);
BailError(err);
err = Nu_FunnelWrite(pArchive, pFunnel, pArchive->compBuf, getsize);
BailError(err);
count -= getsize;
}
err = Nu_FunnelFlush(pArchive, pFunnel);
BailError(err);
bail:
/*Nu_Free(pArchive, buffer);*/
return err;
}
/*
* Expand a thread from "infp" to "pFunnel", using the compression
* and stream length specified by "pThread".
*/
NuError
Nu_ExpandStream(NuArchive* pArchive, const NuRecord* pRecord,
const NuThread* pThread, FILE* infp, NuFunnel* pFunnel)
{
NuError err = kNuErrNone;
ushort calcCrc;
ushort* pCalcCrc;
if (!pThread->thThreadEOF && !pThread->thCompThreadEOF) {
/* somebody stored an empty file! */
goto done;
}
/*
* A brief history of the "threadCRC" field in the thread header:
* record versions 0 and 1 didn't use the threadCRC field
* record version 2 put the CRC of the compressed data in threadCRC
* record version 3 put the CRC of the uncompressed data in threadCRC
*
* P8 ShrinkIt uses v1, GSHK uses v3. If something ever shipped with
* v2, it didn't last long enough to leave an impression, so I'm not
* going to support it. BTW, P8 ShrinkIt always uses LZW/1, which
* puts a CRC in the compressed stream. Your uncompressed data is,
* unfortunately, unprotected before v3.
*/
calcCrc = kNuInitialThreadCRC;
pCalcCrc = nil;
if (Nu_ThreadHasCRC(pRecord->recVersionNumber, NuGetThreadID(pThread)) &&
!pArchive->valIgnoreCRC)
{
pCalcCrc = &calcCrc;
}
err = Nu_ProgressDataExpandPrep(pArchive, pFunnel, pThread);
BailError(err);
/*
* If we're not expanding the data, use a simple copier.
*/
if (!Nu_FunnelGetDoExpand(pFunnel)) {
Nu_FunnelSetProgressState(pFunnel, kNuProgressCopying);
err = Nu_ExpandRaw(pArchive, pThread, infp, pFunnel);
BailError(err);
goto done;
}
Nu_FunnelSetProgressState(pFunnel, kNuProgressExpanding);
switch (pThread->thThreadFormat) {
case kNuThreadFormatUncompressed:
Nu_FunnelSetProgressState(pFunnel, kNuProgressCopying);
err = Nu_ExpandUncompressed(pArchive, pRecord, pThread, infp, pFunnel,
pCalcCrc);
break;
case kNuThreadFormatHuffmanSQ:
err = kNuErrBadFormat;
Nu_ReportError(NU_BLOB, kNuErrNone,
"Huffman-compressed threads not supported");
break;
case kNuThreadFormatLZW1:
case kNuThreadFormatLZW2:
err = Nu_ExpandLZW(pArchive, pRecord, pThread, infp, pFunnel, pCalcCrc);
break;
case kNuThreadFormatLZC12:
case kNuThreadFormatLZC16:
err = kNuErrBadFormat;
Nu_ReportError(NU_BLOB, kNuErrNone,
"LZC-compressed threads not supported");
break;
default:
err = kNuErrBadFormat;
Nu_ReportError(NU_BLOB, err,
"format %u unknown", pThread->thThreadFormat);
break;
}
BailError(err);
/*
* If we have a CRC to check, check it.
*/
if (pCalcCrc != nil) {
if (calcCrc != pThread->thThreadCRC) {
if (!Nu_ShouldIgnoreBadCRC(pArchive, pRecord, kNuErrBadThreadCRC)) {
err = kNuErrBadThreadCRC;
Nu_ReportError(NU_BLOB, err, "expected 0x%04x, got 0x%04x",
pThread->thThreadCRC, calcCrc);
goto bail;
}
} else {
DBUG(("--- thread CRCs match\n"));
}
}
done:
/* make sure we send a final "success" progress message at 100% */
(void) Nu_FunnelSetProgressState(pFunnel, kNuProgressDone);
err = Nu_FunnelSendProgressUpdate(pArchive, pFunnel);
BailError(err);
bail:
return err;
}

1293
nufxlib-0/FileIO.c Normal file

File diff suppressed because it is too large Load Diff

804
nufxlib-0/Funnel.c Normal file
View File

@ -0,0 +1,804 @@
/*
* NuFX archive manipulation library
* Copyright (C) 2000 by Andy McFadden, All Rights Reserved.
* This is free software; you can redistribute it and/or modify it under the
* terms of the GNU Library General Public License, see the file COPYING.LIB.
*
* Implementation of NuFunnel, NuStraw and ProgressUpdater.
*/
#include "NufxLibPriv.h"
/*
* ===========================================================================
* Progress updater
* ===========================================================================
*/
/*
* Initialize the fields in a ProgressData structure, prior to compressing
* data into a record.
*
* The same structure will be used when expanding all threads in a given
* record.
*/
NuError
Nu_ProgressDataInit_Compress(NuArchive* pArchive, NuProgressData* pProgressData,
const NuRecord* pRecord, const char* origPathname)
{
const char* cp;
Assert(pProgressData != nil);
Assert(pArchive != nil);
Assert(pRecord != nil);
Assert(origPathname != nil);
pProgressData->pRecord = pRecord;
pProgressData->origPathname = origPathname;
pProgressData->pathname = pRecord->filename;
cp = strrchr(pRecord->filename,
NuGetSepFromSysInfo(pRecord->recFileSysInfo));
if (cp == nil || *(cp+1) == '\0')
pProgressData->filename = pProgressData->pathname;
else
pProgressData->filename = cp+1;
pProgressData->operation = kNuOpAdd;
pProgressData->state = kNuProgressPreparing;
/*pProgressData->compressedLength = 0;*/
/*pProgressData->compressedProgress = 0;*/
pProgressData->uncompressedLength = 0;
pProgressData->uncompressedProgress = 0;
pProgressData->compress.threadFormat = (NuThreadFormat)-1;
/* ya know... if this is nil, none of the above matters much */
pProgressData->progressFunc = pArchive->progressUpdaterFunc;
return kNuErrNone;
}
/*
* Initialize the fields in a ProgressData structure, prior to expanding
* data from a record.
*
* The same structure will be used when expanding all threads in a given
* record.
*/
NuError
Nu_ProgressDataInit_Expand(NuArchive* pArchive, NuProgressData* pProgressData,
const NuRecord* pRecord, const char* newPathname, char newFssep,
NuValue convertEOL)
{
const NuThread* pThreadIter;
const char* cp;
int i;
Assert(pProgressData != nil);
Assert(pArchive != nil);
Assert(pRecord != nil);
Assert(newPathname != nil);
Assert(newFssep != 0);
pProgressData->pRecord = pRecord;
pProgressData->expand.pThread = nil;
pProgressData->origPathname = pRecord->filename;
pProgressData->pathname = newPathname;
cp = strrchr(newPathname, newFssep);
if (cp == nil || *(cp+1) == '\0')
pProgressData->filename = newPathname;
else
pProgressData->filename = cp+1;
pProgressData->expand.convertEOL = convertEOL;
/* total up the data threads */
pProgressData->expand.totalCompressedLength = 0;
pProgressData->expand.totalUncompressedLength = 0;
for (i = 0; i < (int)pRecord->recTotalThreads; i++) {
pThreadIter = Nu_GetThread(pRecord, i);
if (pThreadIter->thThreadClass != kNuThreadClassData)
continue;
pProgressData->expand.totalCompressedLength += pThreadIter->thCompThreadEOF;
pProgressData->expand.totalUncompressedLength += pThreadIter->actualThreadEOF;
}
pProgressData->operation = kNuOpExtract;
if (pArchive->testMode)
pProgressData->operation = kNuOpTest;
pProgressData->state = kNuProgressPreparing;
/*pProgressData->expand.compressedLength = 0;*/
/*pProgressData->expand.compressedProgress = 0;*/
pProgressData->uncompressedLength = 0;
pProgressData->uncompressedProgress = 0;
/* ya know... if this is nil, none of the above matters much */
pProgressData->progressFunc = pArchive->progressUpdaterFunc;
return kNuErrNone;
}
/*
* Do the setup on a ProgressData prior to compressing a thread.
*/
NuError
Nu_ProgressDataCompressPrep(NuArchive* pArchive, NuStraw* pStraw,
NuThreadFormat threadFormat, ulong sourceLen)
{
NuProgressData* pProgressData;
Assert(pArchive != nil);
Assert(pStraw != nil);
Assert(sourceLen < 32767*65536);
pProgressData = pStraw->pProgress;
if (pProgressData == nil)
return kNuErrNone;
pProgressData->uncompressedLength = sourceLen;
pProgressData->compress.threadFormat = threadFormat;
return kNuErrNone;
}
/*
* Do the setup on a ProgressData prior to expanding a thread.
*
* "pThread" is the thread being expanded.
*/
NuError
Nu_ProgressDataExpandPrep(NuArchive* pArchive, NuFunnel* pFunnel,
const NuThread* pThread)
{
NuProgressData* pProgressData;
Assert(pArchive != nil);
Assert(pFunnel != nil);
Assert(pThread != nil);
pProgressData = pFunnel->pProgress;
if (pProgressData == nil)
return kNuErrNone;
/*pProgressData->compressedLength = pThread->thCompThreadEOF;*/
pProgressData->uncompressedLength = pThread->actualThreadEOF;
pProgressData->expand.pThread = pThread;
return kNuErrNone;
}
/*
* Send the initial progress message, before the output file is opened
* (when extracting) or the input file is opened (when adding).
*/
NuError
Nu_SendInitialProgress(NuArchive* pArchive, const NuProgressData* pProgress)
{
NuResult result;
Assert(pArchive != nil);
Assert(pProgress != nil);
if (pProgress->progressFunc == nil)
return kNuErrNone;
result = (*pProgress->progressFunc)(pArchive, (NuProgressData*) pProgress);
if (result == kNuSkip)
return kNuErrSkipped; /* [dunno how well this works] */
if (result == kNuAbort)
return kNuErrAborted;
return kNuErrNone;
}
/*
* ===========================================================================
* NuFunnel object
* ===========================================================================
*/
/*
* Allocate and initialize a Funnel.
*/
NuError
Nu_FunnelNew(NuArchive* pArchive, NuDataSink* pDataSink, NuValue convertEOL,
NuValue convertEOLTo, NuProgressData* pProgress, NuFunnel** ppFunnel)
{
NuError err = kNuErrNone;
NuFunnel* pFunnel = nil;
Assert(ppFunnel != nil);
Assert(pDataSink != nil);
Assert(convertEOL == kNuConvertOff ||
convertEOL == kNuConvertOn ||
convertEOL == kNuConvertAuto);
pFunnel = Nu_Calloc(pArchive, sizeof(*pFunnel));
BailAlloc(pFunnel);
pFunnel->buffer = Nu_Malloc(pArchive, kNuFunnelBufSize);
BailAlloc(pFunnel->buffer);
pFunnel->pDataSink = pDataSink;
pFunnel->convertEOL = convertEOL;
pFunnel->convertEOLTo = convertEOLTo;
pFunnel->pProgress = pProgress;
bail:
if (err != kNuErrNone)
Nu_FunnelFree(pArchive, pFunnel);
else
*ppFunnel = pFunnel;
return err;
}
/*
* Free a Funnel.
*
* The data should already have been written; it's not the duty of a
* "free" function to flush data out.
*/
NuError
Nu_FunnelFree(NuArchive* pArchive, NuFunnel* pFunnel)
{
if (pFunnel == nil)
return kNuErrNone;
#ifdef DEBUG_MSGS
if (pFunnel->bufCount)
Nu_ReportError(NU_BLOB_DEBUG, kNuErrNone,
"freeing non-empty funnel");
#endif
Nu_Free(pArchive, pFunnel->buffer);
Nu_Free(pArchive, pFunnel);
return kNuErrNone;
}
#if 0
/*
* Set the maximum amount of output we're willing to push through the
* funnel. Attempts to write more than this many bytes will fail. This
* allows us to bail out as soon as it's apparent that compression is
* failing and is actually resulting in a larger file.
*/
void
Nu_FunnelSetMaxOutput(NuFunnel* pFunnel, ulong maxBytes)
{
Assert(pFunnel != nil);
Assert(maxBytes > 0);
pFunnel->outMax = maxBytes;
if (pFunnel->outCount >= pFunnel->outMax)
pFunnel->outMaxExceeded = true;
else
pFunnel->outMaxExceeded = false;
}
#endif
/*
* Table determining what's a binary character and what isn't. It would
* possibly be more compact to generate this from a simple description,
* but I'm hoping static/const data will end up in the code segment and
* save space on the heap.
*
* This corresponds to less-316's ISO-latin1 "8bcccbcc18b95.33b.". This
* may be too loose by itself; we may want to require that the lower-ASCII
* values appear in higher proportions than the upper-ASCII values.
* Otherwise we run the risk of converting a binary file with specific
* properties.
*
* The auto-detect mechanism will never be perfect though, so there's not
* much point in tweaking it to death.
*/
static const char gNuIsBinary[256] = {
1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1, 1, /* ^@-^O */
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* ^P-^_ */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* - / */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0 - ? */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* @ - O */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* P - _ */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* ` - o */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, /* p - DEL */
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x80 */
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x90 */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xa0 */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xb0 */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xc0 */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xd0 */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xe0 */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xf0 */
};
#define kNuMaxHighASCII 1 /* max #of binary chars per 100 bytes */
#define kNuMinConvThreshold 40 /* min of 40 chars for auto-detect */
/*
* Decide, based on the contents of the buffer, whether we should do an
* EOL conversion on the data.
*
* We need to decide if we are looking at text data, and if so, what kind
* of line terminator is in use.
*
* If we don't have enough data to make a determination, don't mess with it.
*
* We try to figure out whether it's CR, LF, or CRLF, so that we can
* skip the CPU-intensive conversion process if it isn't necessary.
*/
static NuValue
Nu_DetermineConversion(NuFunnel* pFunnel, const uchar* buffer, ulong count)
{
ulong bufCount, numBinary, numLF, numCR;
uchar val;
if (count < kNuMinConvThreshold)
return kNuConvertOff;
bufCount = count;
numBinary = numLF = numCR = 0;
while (bufCount--) {
val = *buffer++;
if (gNuIsBinary[val])
numBinary++;
if (val == kNuCharLF)
numLF++;
if (val == kNuCharCR)
numCR++;
}
/* if #found is > #allowed, it's a binary file */
if (count < 100) {
/* use simplified check on files between kNuMinConvThreshold and 100 */
if (numBinary > kNuMaxHighASCII)
return kNuConvertOff;
} else if (numBinary > (count / 100) * kNuMaxHighASCII)
return kNuConvertOff;
/*
* If our "convert to" setting is the same as what we're converting
* from, we can turn off the converter and speed things up.
*
* These are simplistic, but this is intended as an optimization. We
* will blow it if the input has lots of CRs and LFs scattered about,
* and they just happen to be in equal amounts, but it's not clear
* to me that an automatic EOL conversion makes sense on that sort
* of file anyway.
*/
if (numLF && !numCR)
pFunnel->convertEOLFrom = kNuEOLLF;
else if (!numLF && numCR)
pFunnel->convertEOLFrom = kNuEOLCR;
else if (numLF && numLF == numCR)
pFunnel->convertEOLFrom = kNuEOLCRLF;
else
pFunnel->convertEOLFrom = kNuEOLUnknown;
return kNuConvertOn;
}
/*
* Write a block of data to the appropriate output device. Test for
* excessive data, and raise "outMaxExceeded" if we overrun.
*
* This is either a Funnel function or a DataSink function, depending on
* your perspective.
*/
static inline void
Nu_FunnelPutBlock(NuFunnel* pFunnel, const uchar* buf, ulong len)
{
Assert(pFunnel != nil);
Assert(pFunnel->pDataSink != nil);
Assert(buf != nil);
Assert(len > 0);
#if 0
if (pFunnel->outMax) {
if (pFunnel->outMaxExceeded)
return;
if (pFunnel->outCount + len > pFunnel->outMax) {
pFunnel->outMaxExceeded = true;
return;
}
}
pFunnel->outCount += len;
#endif
Nu_DataSinkPutBlock(pFunnel->pDataSink, buf, len);
}
/*
* Output the EOL marker requested for this system.
*/
static inline void
Nu_PutEOL(NuFunnel* pFunnel)
{
uchar ch;
if (pFunnel->convertEOLTo == kNuEOLCR) {
ch = kNuCharCR;
Nu_FunnelPutBlock(pFunnel, &ch, 1);
} else if (pFunnel->convertEOLTo == kNuEOLLF) {
ch = kNuCharLF;
Nu_FunnelPutBlock(pFunnel, &ch, 1);
} else if (pFunnel->convertEOLTo == kNuEOLCRLF) {
ch = kNuCharCR;
Nu_FunnelPutBlock(pFunnel, &ch, 1);
ch = kNuCharLF;
Nu_FunnelPutBlock(pFunnel, &ch, 1);
} else {
Assert(0);
}
}
/*
* Write a buffer of data, using the EOL conversion associated with the
* funnel (if any).
*
* When converting to the system's EOL convention, we take anything
* that looks like an EOL mark and convert it. Doesn't matter if it's
* CR, LF, or CRLF; all three get converted to whatever the system uses.
*/
static NuError
Nu_FunnelWriteConvert(NuFunnel* pFunnel, const uchar* buffer, ulong count)
{
NuError err = kNuErrNone;
ulong progressCount = count;
/*if (pFunnel->outMaxExceeded)
return kNuErrOutMax;*/
if (pFunnel->convertEOL == kNuConvertAuto) {
/*
* This is the first write/flush we've done on this Funnel.
* Check the data we have buffered to decide whether or not
* we want to convert this.
*/
pFunnel->convertEOL = Nu_DetermineConversion(pFunnel, buffer, count);
DBUG(("+++ DetermineConversion --> %ld / %ld\n", pFunnel->convertEOL,
pFunnel->convertEOLFrom));
if (pFunnel->convertEOLFrom == pFunnel->convertEOLTo) {
DBUG(("+++ Switching redundant converter off\n"));
pFunnel->convertEOL = kNuConvertOff;
}
/* put it where the progress meter can see it */
if (pFunnel->pProgress != nil)
pFunnel->pProgress->expand.convertEOL = pFunnel->convertEOL;
}
if (pFunnel->convertEOL == kNuConvertOff) {
/* write it straight */
Nu_FunnelPutBlock(pFunnel, buffer, count);
} else {
/* do the LF conversion */
Boolean lastCR = pFunnel->lastCR; /* local copy */
uchar uch;
/*
* We could get a significant speed improvement here by writing
* non-EOL chars as a larger block instead of single bytes.
*/
while (count--) {
if (*buffer == kNuCharCR) {
Nu_PutEOL(pFunnel);
lastCR = true;
} else if (*buffer == kNuCharLF) {
if (!lastCR)
Nu_PutEOL(pFunnel);
lastCR = false;
} else {
uch = *buffer;
Nu_FunnelPutBlock(pFunnel, &uch, 1);
lastCR = false;
}
buffer++;
}
pFunnel->lastCR = lastCR;
}
/*if (pFunnel->outMaxExceeded)
err = kNuErrOutMax;*/
err = Nu_DataSinkGetError(pFunnel->pDataSink);
/* update progress counter with pre-LFCR count */
if (err == kNuErrNone && pFunnel->pProgress != nil)
pFunnel->pProgress->uncompressedProgress += progressCount;
return err;
}
/*
* Flush any data currently in the funnel.
*/
NuError
Nu_FunnelFlush(NuArchive* pArchive, NuFunnel* pFunnel)
{
NuError err = kNuErrNone;
if (!pFunnel->bufCount)
goto bail;
err = Nu_FunnelWriteConvert(pFunnel, pFunnel->buffer, pFunnel->bufCount);
BailError(err);
pFunnel->bufCount = 0;
err = Nu_FunnelSendProgressUpdate(pArchive, pFunnel);
/* fall through with error */
bail:
return err;
}
/*
* Write a bunch of bytes into a funnel. They will be held in the buffer
* if they fit, or flushed out the bottom if not.
*/
NuError
Nu_FunnelWrite(NuArchive* pArchive, NuFunnel* pFunnel, const uchar* buffer,
ulong count)
{
NuError err = kNuErrNone;
/*pFunnel->inCount += count;*/
/*
* If it will fit into the buffer, just copy it in.
*/
if (pFunnel->bufCount + count < kNuFunnelBufSize) {
memcpy(pFunnel->buffer + pFunnel->bufCount, buffer, count);
pFunnel->bufCount += count;
goto bail;
} else {
/*
* Won't fit. We have to flush what we have, and we can either
* blow out what we were just given or put it at the start of
* the buffer.
*/
err = Nu_FunnelFlush(pArchive, pFunnel);
BailError(err);
if (count >= kNuFunnelBufSize / 4) {
/* it's more than 25% of the buffer, just write it now */
err = Nu_FunnelWriteConvert(pFunnel, buffer, count);
BailError(err);
} else {
memcpy(pFunnel->buffer, buffer, count);
pFunnel->bufCount = count;
}
goto bail;
}
bail:
/*if (err == kNuErrNone)
err = Nu_FunnelSendProgressUpdate(pArchive, pFunnel);*/
return err;
}
/*
* Set the Funnel's progress state.
*/
NuError
Nu_FunnelSetProgressState(NuFunnel* pFunnel, NuProgressState state)
{
Assert(pFunnel != nil);
if (pFunnel->pProgress == nil)
return kNuErrNone;
pFunnel->pProgress->state = state;
return kNuErrNone;
}
/*
* Send a progress update to the application, if they're interested.
*/
NuError
Nu_FunnelSendProgressUpdate(NuArchive* pArchive, NuFunnel* pFunnel)
{
NuProgressData* pProgress;
Assert(pArchive != nil);
Assert(pFunnel != nil);
pProgress = pFunnel->pProgress;
if (pProgress == nil)
return kNuErrNone; /* no progress meter attached */
/* don't continue if they're not accepting progress messages */
if (pProgress->progressFunc == nil)
return kNuErrNone;
/* other than the choice of arguments, it's pretty much the same story */
return Nu_SendInitialProgress(pArchive, pProgress);
}
/*
* Pull the "doExpand" parameter out of the data source.
*/
Boolean
Nu_FunnelGetDoExpand(NuFunnel* pFunnel)
{
Assert(pFunnel != nil);
Assert(pFunnel->pDataSink != nil);
return Nu_DataSinkGetDoExpand(pFunnel->pDataSink);
}
/*
* ===========================================================================
* NuStraw object
* ===========================================================================
*/
/*
* Allocate and initialize a Straw.
*/
NuError
Nu_StrawNew(NuArchive* pArchive, NuDataSource* pDataSource,
NuProgressData* pProgress, NuStraw** ppStraw)
{
NuError err = kNuErrNone;
NuStraw* pStraw = nil;
Assert(ppStraw != nil);
Assert(pDataSource != nil);
pStraw = Nu_Calloc(pArchive, sizeof(*pStraw));
BailAlloc(pStraw);
pStraw->pDataSource = pDataSource;
pStraw->pProgress = pProgress;
pStraw->lastProgress = 0;
pStraw->lastDisplayed = 0;
bail:
if (err != kNuErrNone)
Nu_StrawFree(pArchive, pStraw);
else
*ppStraw = pStraw;
return err;
}
/*
* Free a Straw.
*/
NuError
Nu_StrawFree(NuArchive* pArchive, NuStraw* pStraw)
{
if (pStraw == nil)
return kNuErrNone;
/* we don't own the data source or progress meter */
Nu_Free(pArchive, pStraw);
return kNuErrNone;
}
/*
* Set the Funnel's progress state.
*/
NuError
Nu_StrawSetProgressState(NuStraw* pStraw, NuProgressState state)
{
Assert(pStraw != nil);
Assert(pStraw->pProgress != nil);
pStraw->pProgress->state = state;
return kNuErrNone;
}
/*
* Send a progress update to the application, if they're interested.
*/
NuError
Nu_StrawSendProgressUpdate(NuArchive* pArchive, NuStraw* pStraw)
{
NuProgressData* pProgress;
Assert(pArchive != nil);
Assert(pStraw != nil);
pProgress = pStraw->pProgress;
if (pProgress == nil)
return kNuErrNone; /* no progress meter attached */
/* don't continue if they're not accepting progress messages */
if (pProgress->progressFunc == nil)
return kNuErrNone;
/* other than the choice of arguments, it's pretty much the same story */
return Nu_SendInitialProgress(pArchive, pProgress);
}
/*
* Read data from a straw.
*/
NuError
Nu_StrawRead(NuArchive* pArchive, NuStraw* pStraw, uchar* buffer, long len)
{
NuError err;
Assert(pArchive != nil);
Assert(pStraw != nil);
Assert(buffer != nil);
Assert(len > 0);
/*
* No buffering going on, so this is straightforward.
*/
err = Nu_DataSourceGetBlock(pStraw->pDataSource, buffer, len);
BailError(err);
/*
* Progress updating for adding is a little more complicated than
* for extracting. When extracting, the funnel controls the size
* of the output buffer, and only pushes an update when the output
* buffer fills. Here, we don't know how much will be asked for at
* a time, so we have to pace the updates or we risk flooding the
* application.
*
* We also have another problem: we want to indicate how much data
* has been processed, not how much data is *about* to be processed.
* So we have to set the percentage based on how much was requested
* on the previous call. (This assumes that whatever they asked for
* last time has already been fully processed.)
*/
if (pStraw->pProgress != nil) {
pStraw->pProgress->uncompressedProgress = pStraw->lastProgress;
pStraw->lastProgress += len;
if (!pStraw->pProgress->uncompressedProgress ||
(pStraw->pProgress->uncompressedProgress - pStraw->lastDisplayed
> (kNuFunnelBufSize * 3 / 4)))
{
err = Nu_StrawSendProgressUpdate(pArchive, pStraw);
pStraw->lastDisplayed = pStraw->pProgress->uncompressedProgress;
BailError(err);
}
}
bail:
return err;
}
/*
* Rewind a straw. This rewinds the underlying data source, and resets
* some progress counters.
*/
NuError
Nu_StrawRewind(NuArchive* pArchive, NuStraw* pStraw)
{
assert(pStraw != nil);
assert(pStraw->pDataSource != nil);
pStraw->lastProgress = 0;
pStraw->lastDisplayed = 0;
return Nu_DataSourceRewind(pStraw->pDataSource);
}

183
nufxlib-0/INSTALL Normal file
View File

@ -0,0 +1,183 @@
Basic Installation
==================
These are generic installation instructions.
The `configure' shell script attempts to guess correct values for
various system-dependent variables used during compilation. It uses
those values to create a `Makefile' in each directory of the package.
It may also create one or more `.h' files containing system-dependent
definitions. Finally, it creates a shell script `config.status' that
you can run in the future to recreate the current configuration, a file
`config.cache' that saves the results of its tests to speed up
reconfiguring, and a file `config.log' containing compiler output
(useful mainly for debugging `configure').
If you need to do unusual things to compile the package, please try
to figure out how `configure' could check whether to do them, and mail
diffs or instructions to the address given in the `README' so they can
be considered for the next release. If at some point `config.cache'
contains results you don't want to keep, you may remove or edit it.
The file `configure.in' is used to create `configure' by a program
called `autoconf'. You only need `configure.in' if you want to change
it or regenerate `configure' using a newer version of `autoconf'.
The simplest way to compile this package is:
1. `cd' to the directory containing the package's source code and type
`./configure' to configure the package for your system. If you're
using `csh' on an old version of System V, you might need to type
`sh ./configure' instead to prevent `csh' from trying to execute
`configure' itself.
Running `configure' takes awhile. While running, it prints some
messages telling which features it is checking for.
2. Type `make' to compile the package.
3. Optionally, type `make check' to run any self-tests that come with
the package.
4. Type `make install' to install the programs and any data files and
documentation.
5. You can remove the program binaries and object files from the
source code directory by typing `make clean'. To also remove the
files that `configure' created (so you can compile the package for
a different kind of computer), type `make distclean'. There is
also a `make maintainer-clean' target, but that is intended mainly
for the package's developers. If you use it, you may have to get
all sorts of other programs in order to regenerate files that came
with the distribution.
Compilers and Options
=====================
Some systems require unusual options for compilation or linking that
the `configure' script does not know about. You can give `configure'
initial values for variables by setting them in the environment. Using
a Bourne-compatible shell, you can do that on the command line like
this:
CC=c89 CFLAGS=-O2 LIBS=-lposix ./configure
Or on systems that have the `env' program, you can do it like this:
env CPPFLAGS=-I/usr/local/include LDFLAGS=-s ./configure
Compiling For Multiple Architectures
====================================
You can compile the package for more than one kind of computer at the
same time, by placing the object files for each architecture in their
own directory. To do this, you must use a version of `make' that
supports the `VPATH' variable, such as GNU `make'. `cd' to the
directory where you want the object files and executables to go and run
the `configure' script. `configure' automatically checks for the
source code in the directory that `configure' is in and in `..'.
If you have to use a `make' that does not supports the `VPATH'
variable, you have to compile the package for one architecture at a time
in the source code directory. After you have installed the package for
one architecture, use `make distclean' before reconfiguring for another
architecture.
Installation Names
==================
By default, `make install' will install the package's files in
`/usr/local/bin', `/usr/local/man', etc. You can specify an
installation prefix other than `/usr/local' by giving `configure' the
option `--prefix=PATH'.
You can specify separate installation prefixes for
architecture-specific files and architecture-independent files. If you
give `configure' the option `--exec-prefix=PATH', the package will use
PATH as the prefix for installing programs and libraries.
Documentation and other data files will still use the regular prefix.
In addition, if you use an unusual directory layout you can give
options like `--bindir=PATH' to specify different values for particular
kinds of files. Run `configure --help' for a list of the directories
you can set and what kinds of files go in them.
If the package supports it, you can cause programs to be installed
with an extra prefix or suffix on their names by giving `configure' the
option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'.
Optional Features
=================
Some packages pay attention to `--enable-FEATURE' options to
`configure', where FEATURE indicates an optional part of the package.
They may also pay attention to `--with-PACKAGE' options, where PACKAGE
is something like `gnu-as' or `x' (for the X Window System). The
`README' should mention any `--enable-' and `--with-' options that the
package recognizes.
For packages that use the X Window System, `configure' can usually
find the X include and library files automatically, but if it doesn't,
you can use the `configure' options `--x-includes=DIR' and
`--x-libraries=DIR' to specify their locations.
Specifying the System Type
==========================
There may be some features `configure' can not figure out
automatically, but needs to determine by the type of host the package
will run on. Usually `configure' can figure that out, but if it prints
a message saying it can not guess the host type, give it the
`--host=TYPE' option. TYPE can either be a short name for the system
type, such as `sun4', or a canonical name with three fields:
CPU-COMPANY-SYSTEM
See the file `config.sub' for the possible values of each field. If
`config.sub' isn't included in this package, then this package doesn't
need to know the host type.
If you are building compiler tools for cross-compiling, you can also
use the `--target=TYPE' option to select the type of system they will
produce code for and the `--build=TYPE' option to select the type of
system on which you are compiling the package.
Sharing Defaults
================
If you want to set default values for `configure' scripts to share,
you can create a site shell script called `config.site' that gives
default values for variables like `CC', `cache_file', and `prefix'.
`configure' looks for `PREFIX/share/config.site' if it exists, then
`PREFIX/etc/config.site' if it exists. Or, you can set the
`CONFIG_SITE' environment variable to the location of the site script.
A warning: not all `configure' scripts look for a site script.
Operation Controls
==================
`configure' recognizes the following options to control how it
operates.
`--cache-file=FILE'
Use and save the results of the tests in FILE instead of
`./config.cache'. Set FILE to `/dev/null' to disable caching, for
debugging `configure'.
`--help'
Print a summary of the options to `configure', and exit.
`--quiet'
`--silent'
`-q'
Do not print messages saying which checks are being made. To
suppress all normal output, redirect it to `/dev/null' (any error
messages will still be shown).
`--srcdir=DIR'
Look for the package's source code in directory DIR. Usually
`configure' can determine that directory automatically.
`--version'
Print the version of Autoconf used to generate the `configure'
script, and exit.
`configure' also accepts some other, not widely useful, options.

1599
nufxlib-0/Lzw.c Normal file

File diff suppressed because it is too large Load Diff

110
nufxlib-0/Makefile.in Normal file
View File

@ -0,0 +1,110 @@
#
# Makefile for nufxlib stuff (should work with non-GNU "make").
#
# You can use:
# make (builds library and sample applications)
# make shared (builds shared library if you're using GNU ld or similar)
#
# NufxLib install location.
prefix = @prefix@
exec_prefix = @exec_prefix@
includedir = @includedir@
libdir = @libdir@
srcdir = @srcdir@
SHELL = @SHELL@
INSTALL = @INSTALL@
INSTALL_PROGRAM = @INSTALL_PROGRAM@
INSTALL_DATA = @INSTALL_DATA@
CC = @CC@
AR = ar rcv
#OPT = @CFLAGS@ -DNDEBUG
OPT = @CFLAGS@
#OPT = @CFLAGS@ -DDEBUG_MSGS
#OPT = @CFLAGS@ -DDEBUG_VERBOSE
GCC_FLAGS = -Wall -Wwrite-strings -Wstrict-prototypes -Wpointer-arith -Wshadow
CFLAGS = @BUILD_FLAGS@ -I. @DEFS@
SRCS = Archive.c ArchiveIO.c Compress.c Crc16.c Debug.c Deferred.c \
Entry.c Expand.c FileIO.c Funnel.c Lzw.c MiscStuff.c MiscUtils.c \
Record.c SourceSink.c Thread.c Value.c Version.c
OBJS = Archive.o ArchiveIO.o Compress.o Crc16.o Debug.o Deferred.o \
Entry.o Expand.o FileIO.o Funnel.o Lzw.o MiscStuff.o MiscUtils.o \
Record.o SourceSink.o Thread.o Value.o Version.o
STATIC_PRODUCT = libnufx.a
SHARED_PRODUCT = libnufx.so
PRODUCT = $(STATIC_PRODUCT)
#
# Build stuff
#
all: $(PRODUCT) samples
@true
install: $(STATIC_PRODUCT)
$(srcdir)/mkinstalldirs $(libdir)
$(INSTALL_DATA) $(STATIC_PRODUCT) $(libdir)
$(srcdir)/mkinstalldirs $(includedir)
$(INSTALL_DATA) NufxLib.h $(includedir)
install-shared: $(SHARED_PRODUCT)
$(srcdir)/mkinstalldirs $(libdir)
$(INSTALL_DATA) $(SHARED_PRODUCT) $(libdir)
$(srcdir)/mkinstalldirs $(includedir)
$(INSTALL_DATA) NufxLib.h $(includedir)
samples::
@echo "Building samples..."
@(cd samples; set +e; unset CFLAGS OBJS; set -e; \
@SET_MAKE@ LIB_PRODUCT="../$(PRODUCT)" $(MAKE))
shared::
PRODUCT="$(SHARED_PRODUCT)" $(MAKE) -e
$(STATIC_PRODUCT): $(OBJS)
-rm -f $(SHARED_PRODUCT)
$(AR) $@ $(OBJS)
@RANLIB@ $@
$(SHARED_PRODUCT): $(OBJS)
-rm -f $(STATIC_PRODUCT)
$(CC) @SHARE_FLAGS@ -o $@ $(OBJS)
# the build date is approximate, the build flags are accurate
Version.c: Version.c.in Makefile
(sed -e "s/BUILT/`date`/" -e "s/OPTFLAGS/$(OPT)/" < Version.c.in > Version.c)
clean:
(cd samples; make clean)
-rm -f *.o core
-rm -f $(SHARED_PRODUCT) $(STATIC_PRODUCT)
# build tags; assumes fancy GNU tag generation
tags::
@ctags -R --totals *
@#ctags *.[ch]
distclean: clean
(cd samples; make distclean)
-rm -f Version.c
-rm -f Makefile Makefile.bak
-rm -f config.log config.cache config.status config.h
-rm -f tags
baktar:
@tar cvf nufxlib.tar *.txt COPYING-LIB INSTALL configure *.in Makefile \
Makefile.msc install-sh config.guess config.sub mkinstalldirs *.[ch] \
samples/*.txt samples/Makefile* samples/*.[ch]
@gzip -9 nufxlib.tar
@mv -i nufxlib.tar.gz /home/fadden/BAK/
depend:
makedepend -- $(CFLAGS) -I/usr/local/include -- $(SRCS)
@(cd samples; unset CFLAGS OBJS; @SET_MAKE@ $(MAKE) depend)
# DO NOT DELETE THIS LINE -- make depend depends on it.

75
nufxlib-0/Makefile.msc Normal file
View File

@ -0,0 +1,75 @@
#
# Makefile for Microsoft C compilers. Tested against Visual C++ 6.0.
# Not pretty but it seems to work.
#
# Run with "nmake /f Makefile.msc".
#
# To build without debugging info, use "nmake nodebug=1".
#
# Windows magic
TARGETOS = BOTH
!include <ntwin32.mak>
# object files
OBJS1 = Archive.obj ArchiveIO.obj Compress.obj Crc16.obj Debug.obj Deferred.obj
OBJS2 = Entry.obj Expand.obj FileIO.obj Funnel.obj Lzw.obj MiscStuff.obj
OBJS3 = MiscUtils.obj Record.obj SourceSink.obj Thread.obj Value.obj Version.obj
OBJS = $(OBJS1) $(OBJS2) $(OBJS3)
!ifdef NODEBUG
#OPT = $(cdebug) /D NDEBUG /ML
OPT = $(cdebug) /ML
!else
OPT = $(cdebug) /MLd
!endif
BUILD_FLAGS = /W3 /GX /D "WIN32"
# how to compile sources
.c.obj:
@$(cc) $(OPT) $(BUILD_FLAGS) $(cflags) $(cvars) -o $@ $<
PRODUCT = nufxlib.lib
all: $(PRODUCT) samples
samples::
@echo -
@echo - Change to the samples directory and run $(MAKE) there.
@echo -
# this isn't great, but it'll have to do
Version.c: Version.c.in
copy Version.c.in Version.c
@echo -
@echo - NOTE: you must update Version.c by hand (use flags=$(OPT))
@echo -
nufxlib.lib: $(OBJS)
$(link) -lib /out:$(PRODUCT) $(OBJS) /nologo
clean::
del *.obj
del $(PRODUCT)
Archive.obj: Archive.c NufxLibPriv.h NufxLib.h MiscStuff.h SysDefs.h
ArchiveIO.obj: ArchiveIO.c NufxLibPriv.h NufxLib.h MiscStuff.h SysDefs.h
Compress.obj: Compress.c NufxLibPriv.h NufxLib.h MiscStuff.h SysDefs.h
Crc16.obj: Crc16.c NufxLibPriv.h NufxLib.h MiscStuff.h SysDefs.h
Debug.obj: Debug.c NufxLibPriv.h NufxLib.h MiscStuff.h SysDefs.h
Deferred.obj: Deferred.c NufxLibPriv.h NufxLib.h MiscStuff.h SysDefs.h
Entry.obj: Entry.c NufxLibPriv.h NufxLib.h MiscStuff.h SysDefs.h
Expand.obj: Expand.c NufxLibPriv.h NufxLib.h MiscStuff.h SysDefs.h
FileIO.obj: FileIO.c NufxLibPriv.h NufxLib.h MiscStuff.h SysDefs.h
Funnel.obj: Funnel.c NufxLibPriv.h NufxLib.h MiscStuff.h SysDefs.h
Lzw.obj: Lzw.c NufxLibPriv.h NufxLib.h MiscStuff.h SysDefs.h
MiscStuff.obj: MiscStuff.c NufxLibPriv.h NufxLib.h MiscStuff.h SysDefs.h
MiscUtils.obj: MiscUtils.c NufxLibPriv.h NufxLib.h MiscStuff.h SysDefs.h
Record.obj: Record.c NufxLibPriv.h NufxLib.h MiscStuff.h SysDefs.h
SourceSink.obj: SourceSink.c NufxLibPriv.h NufxLib.h MiscStuff.h SysDefs.h
Thread.obj: Thread.c NufxLibPriv.h NufxLib.h MiscStuff.h SysDefs.h
Value.obj: Value.c NufxLibPriv.h NufxLib.h MiscStuff.h SysDefs.h
Version.obj: Version.c NufxLibPriv.h NufxLib.h MiscStuff.h SysDefs.h

120
nufxlib-0/MiscStuff.c Normal file
View File

@ -0,0 +1,120 @@
/*
* Copyright (C) 2000 by Andy McFadden, All Rights Reserved.
* This is free software; you can redistribute it and/or modify it under the
* terms of the GNU Library General Public License, see the file COPYING.LIB.
*
* Misc stuff (shared between nufxlib and nulib2). This is a collection
* of standard functions that aren't available in libc on this system.
*/
#include "SysDefs.h"
#include "MiscStuff.h"
#include <ctype.h>
#ifndef HAVE_STRERROR
/*
* Return a pointer to the appropriate string in the system table, or NULL
* if the value is out of bounds.
*/
const char*
Nu_strerror(int errnum)
{
extern int sys_nerr;
extern char *sys_errlist[];
if (errnum < 0 || errnum > sys_nerr)
return NULL;
return sys_errlist[errnum];
}
#endif
#ifndef HAVE_MEMMOVE
/*
* Move a block of memory. Unlike memcpy, this is expected to work
* correctly with overlapping blocks.
*
* This is a straightforward implementation. A much faster implementation,
* from BSD, is available in the PGP 2.6.2 distribution, but this should
* suffice for those few systems that don't have memmove.
*/
void*
Nu_memmove(void* dst, const void* src, size_t n)
{
void* retval = dst;
char* srcp = (char*)src;
char* dstp = (char*)dst;
/* you can normally get away with this if n==0 */
assert(dst != NULL);
assert(src != NULL);
if (dstp == srcp || !n) {
/* nothing to do */
} else if (dstp > srcp) {
/* start from the end */
(char*)dstp += n-1;
(char*)srcp += n-1;
while (n--)
*dstp-- = *srcp--;
} else {
/* start from the front */
while (n--)
*dstp++ = *srcp++;
}
return retval;
}
#endif
#ifndef HAVE_STRTOUL
/*
* Perform strtol, but on an unsigned long.
*
* On systems that have strtol but don't have strtoul, the strtol
* function doesn't clamp the return value, making it similar in
* function to strtoul. The comparison is not exact, however,
* because strtoul is expected to lots of fancy things (like set
* errno to ERANGE).
*
* For our purposes here, strtol does all we need it to. Someday
* we should replace this with a "real" version.
*/
unsigned long
Nu_strtoul(const char *nptr, char **endptr, int base)
{
return strtol(nptr, endptr, base);
}
#endif
#ifndef HAVE_STRCASECMP
/*
* Compare two strings, case-insensitive.
*/
int
Nu_strcasecmp(const char *str1, const char *str2)
{
while (*str1 && *str2 && toupper(*str1) == toupper(*str2))
str1++, str2++;
return (toupper(*str1) - toupper(*str2));
}
#endif
#ifndef HAVE_STRNCASECMP
/*
* Compare two strings, case-insensitive, stopping after "n" chars.
*/
int
Nu_strncasecmp(const char *str1, const char *str2, size_t n)
{
while (n && *str1 && *str2 && toupper(*str1) == toupper(*str2))
str1++, str2++, n--;
if (n)
return (toupper(*str1) - toupper(*str2));
else
return 0; /* no mismatch in first n chars */
}
#endif

108
nufxlib-0/MiscStuff.h Normal file
View File

@ -0,0 +1,108 @@
/*
* Copyright (C) 2000 by Andy McFadden, All Rights Reserved.
* This is free software; you can redistribute it and/or modify it under the
* terms of the GNU Library General Public License, see the file COPYING.LIB.
*
* Misc stuff (shared between nufxlib and nulib2). This is a collection
* of miscellaneous types and macros that I find generally useful.
*/
#ifndef __MiscStuff__
#define __MiscStuff__
#include "SysDefs.h"
/*
* Use our versions of functions if they don't exist locally.
*/
#ifndef HAVE_STRERROR
#define strerror Nu_strerror
const char* Nu_strerror(int errnum);
#endif
#ifndef HAVE_MEMMOVE
#define memmove Nu_memmove
void* Nu_memmove(void *dest, const void *src, size_t n);
#endif
#ifndef HAVE_STRTOUL
#define strtoul Nu_strtoul
unsigned long Nu_strtoul(const char *nptr, char **endptr, int base);
#endif
#ifndef HAVE_STRCASECMP
#define strcasecmp Nu_strcasecmp
int Nu_strcasecmp(const char *s1, const char *s2);
#endif
#ifndef HAVE_STRNCASECMP
#define strncasecmp Nu_strncasecmp
int Nu_strncasecmp(const char *s1, const char *s2, size_t n);
#endif
/*
* Misc types.
*/
#include <sys/types.h>
#define nil NULL /* I can't seem to stop typing 'nil' now */
typedef uchar Boolean;
#define false (0)
#define true (!false)
/*
* Handy macros.
*/
/* compute #of elements in a static array */
#define NELEM(x) (sizeof(x) / sizeof((x)[0]))
/* convert single hex digit char to number */
#define HexDigit(x) ( !isxdigit((int)(x)) ? -1 : \
(x) <= '9' ? (x) - '0' : toupper(x) +10 - 'A' )
/* convert number from 0-15 to hex digit */
#define HexConv(x) ( ((uint)(x)) <= 15 ? \
( (x) <= 9 ? (x) + '0' : (x) -10 + 'A') : -1 )
/*
* Debug stuff.
*/
/*
* Redefine this if you want assertions to do something other than default.
* Changing the definition of assert is tough, because assert.h redefines
* it every time it's included. On a Solaris 2.7 system I was using, gcc
* pulled assert.h in with some of the system headers, and their definition
* resulted in corrupted core dumps.
*/
#define Assert assert
#if defined(DEBUG_VERBOSE)
/* quick debug printf macro */
#define DBUG(args) printf args
#else
#define DBUG(args) ((void)0)
#endif
#if defined(NDEBUG)
#define DebugFill(addr, len) ((void)0)
#define DebugAbort() ((void)0)
#else
/* when debugging, fill Malloc blocks with junk, unless we're using Purify */
#if !defined(PURIFY)
#define DebugFill(addr, len) memset(addr, 0xa3, len)
#else
#define DebugFill(addr, len) ((void)0)
#endif
#define DebugAbort() abort()
#endif
#define kInvalidFill (0xa3)
#define kInvalidPtr ((void*)0xa3a3a3a3)
#endif /*__MiscStuff__*/

358
nufxlib-0/MiscUtils.c Normal file
View File

@ -0,0 +1,358 @@
/*
* NuFX archive manipulation library
* Copyright (C) 2000 by Andy McFadden, All Rights Reserved.
* This is free software; you can redistribute it and/or modify it under the
* terms of the GNU Library General Public License, see the file COPYING.LIB.
*
* Miscellaneous NufxLib utility functions.
*/
#define __MiscUtils_c__
#include "NufxLibPriv.h"
/*
* Big fat hairy global. Unfortunately this is unavoidable.
*/
NuCallback gNuGlobalErrorMessageHandler = nil;
const char* kNufxLibName = "nufxlib";
/*
* strerror() equivalent for NufxLib errors.
*/
const char*
Nu_StrError(NuError err)
{
/*
* BUG: this should be set up as per-thread storage in an MT environment.
* I would be more inclined to worry about this if I was expecting
* it to be used. So long as valid values are passed in, and the
* switch statement is kept up to date, we should never have cause
* to return this.
*
* An easier solution, should this present a problem for someone, would
* be to have the function return nil or "unknown error" when the
* error value isn't recognized.
*/
static char defaultMsg[32];
switch (err) {
case kNuErrNone:
return "(no error)";
case kNuErrGeneric:
return "NufxLib generic error";
case kNuErrInternal:
return "NufxLib internal error";
case kNuErrUsage:
return "NufxLib usage error";
case kNuErrSyntax:
return "NufxLib syntax error";
case kNuErrMalloc:
return "NufxLib malloc error";
case kNuErrInvalidArg:
return "Invalid arguments to NufxLib";
case kNuErrBadStruct:
return "Bad NuArchive structure passed to NufxLib";
case kNuErrBusy:
return "Attempted invalid reentrant call";
case kNuErrSkipped:
return "Skipped by user";
case kNuErrAborted:
return "Processing aborted";
case kNuErrRename:
return "User wants to rename file";
case kNuErrFile:
return "NufxLib trouble with a file";
case kNuErrFileOpen:
return "NufxLib unable to open file";
case kNuErrFileClose:
return "NufxLib unable to close file";
case kNuErrFileRead:
return "NufxLib unable to read file";
case kNuErrFileWrite:
return "NufxLib unable to write file";
case kNuErrFileSeek:
return "NufxLib unable to seek file";
case kNuErrFileExists:
return "File already exists";
case kNuErrFileNotFound:
return "No such file or directory";
case kNuErrFileStat:
return "Couldn't get file info";
case kNuErrFileNotReadable:
return "Read access denied";
case kNuErrDirExists:
return "Directory already exists";
case kNuErrNotDir:
return "Not a directory";
case kNuErrNotRegularFile:
return "Not a regular file";
case kNuErrDirCreate:
return "Unable to create directory";
case kNuErrOpenDir:
return "Unable to open directory";
case kNuErrReadDir:
return "Unable to read directory";
case kNuErrFileSetDate:
return "Unable to set file date";
case kNuErrFileSetAccess:
return "Unable to set file access";
case kNuErrNotNuFX:
return "Input is not a NuFX archive";
case kNuErrBadMHVersion:
return "Unrecognized Master Header version";
case kNuErrRecHdrNotFound:
return "Next record not found";
case kNuErrNoRecords:
return "No records in archive";
case kNuErrBadRecord:
return "Bad data in record";
case kNuErrBadMHCRC:
return "Bad Master Header CRC";
case kNuErrBadRHCRC:
return "Bad Record header CRC";
case kNuErrBadThreadCRC:
return "Bad Thread header CRC";
case kNuErrBadDataCRC:
return "Data CRC mismatch";
case kNuErrBadFormat:
return "Thread compression format unsupported";
case kNuErrBadData:
return "Bad data found";
case kNuErrBufferOverrun:
return "Buffer overrun";
case kNuErrBufferUnderrun:
return "Buffer underrun";
case kNuErrOutMax:
return "Output limit exceeded";
case kNuErrNotFound:
return "Not found";
case kNuErrRecordNotFound:
return "Record not found";
case kNuErrRecIdxNotFound:
return "RecordIdx not found";
case kNuErrThreadIdxNotFound:
return "ThreadIdx not found";
case kNuErrThreadIDNotFound:
return "ThreadID not found";
case kNuErrRecNameNotFound:
return "Record name not found";
case kNuErrRecordExists:
return "Record already exists";
case kNuErrAllDeleted:
return "Tried to delete all files";
case kNuErrArchiveRO:
return "Archive is in read-only mode";
case kNuErrModRecChange:
return "Attempt to alter a modified record";
case kNuErrModThreadChange:
return "Attempt to alter a modified thread";
case kNuErrThreadAdd:
return "Can't add conflicting threadID";
case kNuErrNotPreSized:
return "Operation only permitted on pre-sized threads";
case kNuErrPreSizeOverflow:
return "Data exceeds pre-sized thread size";
case kNuErrInvalidFilename:
return "Invalid filename";
case kNuErrLeadingFssep:
return "Storage name started with fssep char";
case kNuErrNotNewer:
return "New item wasn't newer than existing";
case kNuErrDuplicateNotFound:
return "Can only update an existing item";
case kNuErrDamaged:
return "Original archive may have been damaged";
default:
sprintf(defaultMsg, "(error=%d)", err);
return defaultMsg;
}
}
#define kNuHeftyBufSize 256 /* all error messages should fit in this */
#define kNuExtraGoodies 8 /* leave room for "\0" and other trivial chars*/
/*
* Similar to perror(), but takes the error as an argument, and knows
* about NufxLib errors as well as system errors.
*
* Depending on the compiler, "file", "line", and "function" may be nil/zero.
*
* Calling here with "pArchive"==nil is allowed, but should only be done
* if the archive is inaccessible (perhaps because it failed to open). We
* can't invoke the error message callback if the pointer is nil.
*/
void
Nu_ReportError(NuArchive* pArchive, const char* file, int line,
const char* function, Boolean isDebug, NuError err, const char* format, ...)
{
NuErrorMessage errorMessage;
const char* msg;
va_list args;
char buf[kNuHeftyBufSize];
int count;
#if defined(HAVE_SNPRINTF) || defined(SPRINTF_RETURNS_INT)
int cc;
#endif
Assert(format != nil);
va_start(args, format);
#if defined(HAVE_VSNPRINTF) && defined(VSNPRINTF_DECLARED)
count = vsnprintf(buf, sizeof(buf)-kNuExtraGoodies, format, args);
#else
#ifdef SPRINTF_RETURNS_INT
count = vsprintf(buf, format, args);
#else
vsprintf(buf, format, args);
count = strlen(buf);
#endif
#endif
va_end(args);
Assert(count > 0);
if (count < 0)
goto bail;
/* print the error code data, if any */
if (err != kNuErrNone) {
/* we know we have room for ": ", because of kNuExtraGoodies */
strcpy(buf+count, ": ");
count += 2;
msg = nil;
if (err >= 0)
msg = strerror(err);
if (msg == nil)
msg = Nu_StrError(err);
#if defined(HAVE_SNPRINTF) && defined(SNPRINTF_DECLARED)
if (msg == nil)
cc = snprintf(buf+count, sizeof(buf) - count,
"(unknown err=%d)", err);
else
cc = snprintf(buf+count, sizeof(buf) - count, "%s", msg);
#else
#ifdef SPRINTF_RETURNS_INT
if (msg == nil)
cc = sprintf(buf+count, "(unknown err=%d)", err);
else
cc = sprintf(buf+count, "%s", msg);
Assert(cc > 0);
count += cc;
#else
if (msg == nil)
sprintf(buf+count, "(unknown err=%d)", err);
else
sprintf(buf+count, "%s", msg);
count += strlen(buf + count);
#endif
#endif
}
#if !defined(HAVE_SNPRINTF) || !defined(HAVE_VSNPRINTF) || \
!defined(SNPRINTF_DELCARED) || !defined(VSNPRINTF_DECLARED)
/* couldn't do it right, so check for overflow */
Assert(count <= kNuHeftyBufSize);
#endif
if ((pArchive != nil && pArchive->messageHandlerFunc == nil) ||
(pArchive == nil && gNuGlobalErrorMessageHandler == nil))
{
if (isDebug) {
fprintf(stderr, "%s: [%s:%d %s] %s\n", kNufxLibName,
file, line, function, buf);
} else {
fprintf(stderr, "%s: ERROR: %s\n", kNufxLibName, buf);
}
} else {
errorMessage.message = buf;
errorMessage.err = err;
errorMessage.isDebug = isDebug;
errorMessage.file = file;
errorMessage.line = line;
errorMessage.function = function;
if (pArchive == nil)
(void) (*gNuGlobalErrorMessageHandler)(pArchive, &errorMessage);
else
(void) (*pArchive->messageHandlerFunc)(pArchive, &errorMessage);
}
bail:
return;
}
/*
* Memory allocation wrappers.
*
* Under gcc these would be macros, but not all compilers can handle that.
*
* [ It should be possible to use mmalloc instead of malloc. Just tuck the
* mmalloc descriptor into the NuArchive struct. ]
*/
#ifndef USE_DMALLOC
void*
Nu_Malloc(NuArchive* pArchive, size_t size)
{
void* _result;
Assert(size > 0);
_result = malloc(size);
if (_result == nil) {
Nu_ReportError(NU_BLOB, kNuErrMalloc, "malloc(%u) failed", (uint) size);
DebugAbort(); /* leave a core dump if we're built for it */
}
DebugFill(_result, size);
return _result;
}
void*
Nu_Calloc(NuArchive* pArchive, size_t size)
{
void* _cresult = Nu_Malloc(pArchive, size);
memset(_cresult, 0, size);
return _cresult;
}
void*
Nu_Realloc(NuArchive* pArchive, void* ptr, size_t size)
{
void* _result;
Assert(ptr != nil); /* disallow this usage */
Assert(size > 0); /* disallow this usage */
_result = realloc(ptr, size);
if (_result == nil) {
Nu_ReportError(NU_BLOB, kNuErrMalloc, "realloc(%u) failed",(uint) size);
DebugAbort(); /* leave a core dump if we're built for it */
}
return _result;
}
void
Nu_Free(NuArchive* pArchive, void* ptr)
{
if (ptr != nil)
free(ptr);
}
#endif

206
nufxlib-0/NOTES.txt Normal file
View File

@ -0,0 +1,206 @@
NufxLib NOTES
Last revised: 2000/01/23
The interface is documented in "nufxlibapi.html", available from the
www.nulib.com web site. This discusses some of the internal design that
may be of interest.
Some familiarity with the NuFX file format is assumed.
Read-Write Data Structures
==========================
For both read-only and read-write files (but not streaming read-only files),
the archive is represented internally as a linked list of Records, each
of which has an array of Threads attached. No attempt is made to
optimize searches by filename, so use of the "replace existing entry when
filenames match" option should be restricted to situations where it is
necessary. Otherwise, O(N^2) behavior can result.
Modifications, such as deletions, changes to filename threads, and
additions of new records, are queued up in a separate list until a NuFlush
call is issued. The list works much the same way as the temporary file:
when the operation completes, the "new" list becomes the "original" list.
If the operation is aborted, the "new" list is scrubbed, and the "original"
list remains unmodified.
Just as it is inefficient to write data to the temp file when it's not
necessary to do so, it is inefficient to allocate a complete copy of the
records from the original list if none are changed. As a result, there are
actually two "new" lists, one with a copy of the original record list, and
one with new additions. The "copy" list starts out uninitialized, and
remains that way until one of the entries from the original list is
modified. When that happens, the entire original list is duplicated, and
the changes are made directly to members of the "copy" list. (This is
important for really large archives, like a by-file archive with the
entire contents of a hard drive, where the record index could be several
megabytes in size.)
It would be more *memory* efficient to simply maintain a list of what
has changed. However, we can't disturb the "original" list in any way or
we lose the ability to roll back quickly if the operation is aborted.
Consequently, we need to create a new list of records that reflects
the state of the new archive, so that when we rename the temp file over
the original, we can simply "rename" the new record list over the original.
Since we're going to need the new list eventually, we might as well create
it as soon as it is needed, and deal with memory allocation failures up
front rather than during the update process. (Some items, such as the
record's file offset in the archive, have to be updated even for records
that aren't themselves changing... which means we potentially need to
modify all existing record structures, so we need a complete copy of the
record list regardless of how little or how much has changed.)
This also ties into the "modify original archive file directly if possible"
option, which avoids the need for creating and renaming a temp file. If
the only changes are updates to pre-sized records (e.g. renaming a file
inside the archive, or updating a comment), or adding new records onto the
end, there is little risk and possibly a huge efficiency gain in just
modifying the archive in place. If none of the operations caused the
"copy" list to be initialized, then clearly there's no need to write to a
temp file. (It's not actually this simple, because updates to pre-sized
threads are annotated in the "copy" list.)
One of the goals was to be able to execute a sequence of operations like:
- open original archive
- read original archive
- modify archive
- flush (success)
- modify archive
- flush (failure, rollback)
- modify archive
- flush (success)
- close archive
The archive is opened at the start and held open across many operations.
There is never a need to re-read the entire archive. We could avoid the
need to allocate two complete Record lists by requiring that the archive be
re-scanned after changes are aborted; if we did that, we could just modify
the original record list in place, and let the changes become "permanent"
after a successful write. In many ways, though, its cleaner to have two
lists.
Archives with several thousand entries should be sufficiently rare, and
virtual memory should be sufficiently plentiful, that this won't be a
problem for anyone. Scanning repeatedly through a 15MB archive stored on a
CD-ROM is likely to be very annoying though, so the design makes every
attempt to avoid repeated scans of the archive. And in any event, this
only applies to archive updates. The memory requirements for simple file
extraction are minimal.
In summary:
"orig" list has original set of records, and is not disturbed until
the changes are committed.
"copy" list is created on first add/update/delete operation, and
initially contains a complete copy of "orig".
"new" list contains all new additions to the archive, including
new additions that replace existing entries (the existing entry
is deleted from "copy" and then added to "new").
Each Record in the list has a "thread modification" list attached to it.
Any changes to the record header or additions to the thread mod list are
made in the "copy" set; the "original" set remains untouched. The thread
mod list can have the following items in it:
- delete thread (NuThreadIdx)
- add thread (type, otherSize, format, +contents)
- update pre-sized thread (NuThreadIdx, +contents)
Contents are specified with a NuDataSource, which allows the application
to indicate that the data is already compressed. This is useful for
copying parts of records between archives without having to expand and
recompress the data.
Some interactions and concepts that are important to understand:
When a file is added, the file type information will be placed in the
"new" Record immediately (subject to some restrictions: adding a data
fork always causes the type info to be updated, adding a rsrc fork only
updates the type info if a data fork is not already present).
Deleting a record results in the Record being removed from the "copy"
list immediately. Future modify operations on that NuRecordIdx will
fail. Future read operations will work just fine until the next
NuFlush is issued, because read operations use the "original" list.
Deleting all threads from a record results in the record being
deleted, but not until the NuFlush call is issued. It is possible to
delete all the existing threads and then add new ones.
It is *not* allowed to delete a modified thread, modify a deleted thread,
or delete a record that has been modified. This limitation was added to
keep the system simple. Note this does not mean you can't delete a data
fork and add a data fork; doing so results in operations on two threads
with different NuThreadIdx values. What you can't do is update the
filename thread and then delete it, or vice-versa. (If anyone can think
of a reason why you'd want to rename a file and then delete it with the
same NuFlush call, I'll figure out a way to support it.)
Updating a filename thread is intercepted, and causes the Record's
filename cache to be updated as well. Adding a filename thread for
records where the filename is stored in the record itself cause the
"in-record" filename to be zeroed. Adding a filename thread to a
record that already has one isn't allowed; nufxlib restricts you to
a single filename thread per record.
Some actions on an archive are allowed but strongly discouraged. For
example, deleting a filename thread but leaving the data threads behind
is a valid thing to do, but leaves most archivers in a state of mild
confusion. Deleting the data threads but leaving the filename thread is
similarly perplexing.
You can't call "update thread" on a thread that doesn't yet exist,
even if an "add thread" call has been made. You can, however, call
"add thread" on a newly created Record.
When a new record is created because of a "create record" call, a filename
thread is created automatically. It is not necessary to explicitly add the
filename.
Failures encountered while committing changes to a record cause all
operations on that record to be rolled back. If, during a NuFlush, a
file add fails, the user is given the option of aborting the entire
operation or skipping the file in question (and perhaps retrying or other
options as well). Aborting the flush causes a complete rollback. If only
the thread mod operation is canceled, then all thread mods for that record
are ignored. The temp file (or archive file) will have its file pointer
reset to the original start of the record, and if the record already
existed in the original archive, the full original record will be copied
over. This may seem drastic, but it helps ensure that you don't end up
with a record in a partially created state.
If a failure occurs during an "update in place", it isn't possible to
roll back all changes. If the failure was due to a bug in NufxLib, it
is possible that the archive could be unrecoverably damaged. NufxLib
tries to identify such situations, and will leave the archive open in
read-only mode after rolling back any new file additions.
Updating Filenames
==================
Updating filenames is a small nightmare, because the filename can be
either in the record header or in a filename thread. It's possible,
but illogical, to have a single record with a filename in the record
header and two or more filenames in threads.
NufxLib will not automatically "fix" broken records, but it will prevent
applications from creating situations that should not exist.
When reading an archive, NufxLib will use the filename from the
first filename thread found. If no filename threads are found, the
filename from the record header will be used.
If you add a filename thread to a record that has a filename in the
record header, the header name will be removed.
If you update a filename thread in a record that has a filename in
the record header, the header name will be left untouched.
Adding a filename thread is only allowed if no filename thread exists,
or all existing filename threads have been deleted.

733
nufxlib-0/NufxLib.h Normal file
View File

@ -0,0 +1,733 @@
/*
* NuFX archive manipulation library
* Copyright (C) 2000 by Andy McFadden, All Rights Reserved.
* This is free software; you can redistribute it and/or modify it under the
* terms of the GNU Library General Public License, see the file COPYING.LIB.
*
* External interface (types, defines, and function prototypes).
*/
#ifndef __NufxLib__
#define __NufxLib__
#include <stdio.h>
#ifdef __cplusplus
extern "C" {
#endif
/*
* ===========================================================================
* Types
* ===========================================================================
*/
/*
* Error values returned from functions.
*
* These are negative so that they don't conflict with system-defined
* errors (like ENOENT). A NuError can hold either.
*/
typedef enum NuError {
kNuErrNone = 0,
kNuErrGeneric = -1,
kNuErrInternal = -2,
kNuErrUsage = -3,
kNuErrSyntax = -4,
kNuErrMalloc = -5,
kNuErrInvalidArg = -6,
kNuErrBadStruct = -7,
kNuErrUnexpectedNil = -8,
kNuErrBusy = -9,
kNuErrSkipped = -10, /* processing skipped by request */
kNuErrAborted = -11, /* processing aborted by request */
kNuErrRename = -12, /* user wants to rename before extracting */
kNuErrFile = -20,
kNuErrFileOpen = -21,
kNuErrFileClose = -22,
kNuErrFileRead = -23,
kNuErrFileWrite = -24,
kNuErrFileSeek = -25,
kNuErrFileExists = -26, /* existed when it shouldn't */
kNuErrFileNotFound = -27, /* didn't exist when it should have */
kNuErrFileStat = -28, /* some sort of GetFileInfo failure */
kNuErrFileNotReadable = -29, /* bad access permissions */
kNuErrDirExists = -30, /* dir exists, don't need to create it */
kNuErrNotDir = -31, /* expected a dir, got a regular file */
kNuErrNotRegularFile = -32, /* expected regular file, got weirdness */
kNuErrDirCreate = -33, /* unable to create a directory */
kNuErrOpenDir = -34, /* error opening directory */
kNuErrReadDir = -35, /* error reading directory */
kNuErrFileSetDate = -36, /* unable to set file date */
kNuErrFileSetAccess = -37, /* unable to set file access permissions */
kNuErrNotNuFX = -40, /* 'NuFile' missing; not a NuFX archive? */
kNuErrBadMHVersion = -41, /* bad master header version */
kNuErrRecHdrNotFound = -42, /* 'NuFX' missing; corrupted archive? */
kNuErrNoRecords = -43, /* archive doesn't have any records */
kNuErrBadRecord = -44, /* something about the record looked bad */
kNuErrBadMHCRC = -45, /* bad master header CRC */
kNuErrBadRHCRC = -46, /* bad record header CRC */
kNuErrBadThreadCRC = -47, /* bad thread header CRC */
kNuErrBadDataCRC = -48, /* bad CRC detected in the data */
kNuErrBadFormat = -50, /* compression type not supported */
kNuErrBadData = -51, /* expansion func didn't like input */
kNuErrBufferOverrun = -52, /* overflowed a user buffer */
kNuErrBufferUnderrun = -53, /* underflowed a user buffer */
kNuErrOutMax = -54, /* output limit exceeded */
kNuErrNotFound = -60, /* (generic) search unsuccessful */
kNuErrRecordNotFound = -61, /* search for specific record failed */
kNuErrRecIdxNotFound = -62, /* search by NuRecordIdx failed */
kNuErrThreadIdxNotFound = -63, /* search by NuThreadIdx failed */
kNuErrThreadIDNotFound = -64, /* search by NuThreadID failed */
kNuErrRecNameNotFound = -65, /* search by storageName failed */
kNuErrRecordExists = -66, /* found existing record with same name */
kNuErrAllDeleted = -70, /* attempt to delete everything */
kNuErrArchiveRO = -71, /* archive is open in read-only mode */
kNuErrModRecChange = -72, /* tried to change modified record */
kNuErrModThreadChange = -73, /* tried to change modified thread */
kNuErrThreadAdd = -74, /* adding that thread creates a conflict */
kNuErrNotPreSized = -75, /* tried to update a non-pre-sized thread */
kNuErrPreSizeOverflow = -76, /* too much data */
kNuErrInvalidFilename = -77, /* invalid filename */
kNuErrLeadingFssep = -80, /* names in archives must not start w/sep */
kNuErrNotNewer = -81, /* item same age or older than existing */
kNuErrDuplicateNotFound = -82, /* "must overwrite" was set, but item DNE */
kNuErrDamaged = -83 /* original archive may have been damaged */
} NuError;
/*
* Return values from callback functions.
*/
typedef enum NuResult {
kNuOK = 0,
kNuSkip = 1,
kNuAbort = 2,
/*kNuAbortAll = 3,*/
kNuRetry = 4,
kNuIgnore = 5,
kNuRename = 6,
kNuOverwrite = 7
} NuResult;
/*
* NuRecordIdxs are assigned to records in an archive. You may assume that
* the values are unique, but that is all.
*/
typedef unsigned long NuRecordIdx;
/*
* NuThreadIdxs are assigned to threads within a record. Again, you may
* assume that the values are unique within a record, but that is all.
*/
typedef unsigned long NuThreadIdx;
/*
* Thread ID, a combination of thread_class and thread_kind. Standard
* values have explicit identifiers.
*/
typedef unsigned long NuThreadID;
#define NuMakeThreadID(class, kind) /* construct a NuThreadID */ \
((unsigned long)class << 16 | (unsigned long)kind)
#define NuGetThreadID(pThread) /* pull NuThreadID out of NuThread */ \
(NuMakeThreadID(pThread->thThreadClass, pThread->thThreadKind))
#define NuThreadIDGetClass(threadID) /* get threadClass from NuThreadID */ \
((unsigned short) ((unsigned long)(threadID) >> 16))
#define NuThreadIDGetKind(threadID) /* get threadKind from NuThreadID */ \
((unsigned short) ((threadID) & 0xffff))
#define kNuThreadClassMessage 0x0000
#define kNuThreadClassControl 0x0001
#define kNuThreadClassData 0x0002
#define kNuThreadClassFilename 0x0003
#define kNuThreadIDOldComment NuMakeThreadID(kNuThreadClassMessage, 0x0000)
#define kNuThreadIDComment NuMakeThreadID(kNuThreadClassMessage, 0x0001)
#define kNuThreadIDIcon NuMakeThreadID(kNuThreadClassMessage, 0x0002)
#define kNuThreadIDMkdir NuMakeThreadID(kNuThreadClassControl, 0x0000)
#define kNuThreadIDDataFork NuMakeThreadID(kNuThreadClassData, 0x0000)
#define kNuThreadIDDiskImage NuMakeThreadID(kNuThreadClassData, 0x0001)
#define kNuThreadIDRsrcFork NuMakeThreadID(kNuThreadClassData, 0x0002)
#define kNuThreadIDFilename NuMakeThreadID(kNuThreadClassFilename, 0x0000)
#define kNuThreadIDWildcard NuMakeThreadID(0xffff, 0xffff)
/* enumerate the possible values for thThreadFormat */
typedef enum NuThreadFormat {
kNuThreadFormatUncompressed = 0x0000,
kNuThreadFormatHuffmanSQ = 0x0001,
kNuThreadFormatLZW1 = 0x0002,
kNuThreadFormatLZW2 = 0x0003,
kNuThreadFormatLZC12 = 0x0004,
kNuThreadFormatLZC16 = 0x0005
} NuThreadFormat;
/* extract the filesystem separator char from the "file_sys_info" field */
#define NuGetSepFromSysInfo(sysInfo) \
((char) ((sysInfo) & 0xff))
/* return a file_sys_info with a replaced filesystem separator */
#define NuSetSepInSysInfo(sysInfo, newSep) \
((ushort) (((sysInfo) & 0xff00) | ((newSep) & 0xff)) )
/* GS/OS-defined file system identifiers; sadly, UNIX is not among them */
typedef enum NuFileSysID {
kNuFileSysUnknown = 0, /* NuFX spec says use this */
kNuFileSysProDOS = 1,
kNuFileSysDOS33 = 2,
kNuFileSysDOS32 = 3,
kNuFileSysPascal = 4,
kNuFileSysMacHFS = 5,
kNuFileSysMacMFS = 6,
kNuFileSysLisa = 7,
kNuFileSysCPM = 8,
kNuFileSysCharFST = 9,
kNuFileSysMSDOS = 10,
kNuFileSysHighSierra = 11,
kNuFileSysISO9660 = 12,
kNuFileSysAppleShare = 13
} NuFileSysID;
/* simplified definition of storage types */
typedef enum NuStorageType {
kNuStorageUnknown = 0,
kNuStorageSeedling = 1, /* <= 512 bytes */
kNuStorageSapling = 2, /* < 128KB */
kNuStorageTree = 3, /* < 16MB */
kNuStorageExtended = 5, /* forked (any size) */
kNuStorageDirectory = 0x0d
} NuStorageType;
/* flags for NuOpenRW */
enum {
kNuOpenCreat = 0x0001,
kNuOpenExcl = 0x0002
};
/*
* The actual NuArchive structure is opaque, and should only be visible
* to the library. We define it here as an ambiguous struct.
*/
typedef struct NuArchive NuArchive;
/*
* Generic callback prototype.
*/
typedef NuResult (*NuCallback)(NuArchive* pArchive, void* args);
/*
* Parameters that affect archive operations.
*/
typedef enum NuValueID {
kNuValueInvalid = 0,
kNuValueIgnoreCRC = 1,
kNuValueDataCompression = 2,
kNuValueDiscardWrapper = 3,
kNuValueEOL = 4,
kNuValueConvertExtractedEOL = 5,
kNuValueOnlyUpdateOlder = 6,
kNuValueAllowDuplicates = 7,
kNuValueHandleExisting = 8,
kNuValueModifyOrig = 9,
kNuValueMimicSHK = 10
} NuValueID;
typedef unsigned long NuValue;
/*
* Enumerated values for things you pass in a NuValue.
*/
enum NuValueValue {
/* for the truly retentive */
kNuValueFalse = 0,
kNuValueTrue = 1,
/* for kNuValueDataCompression */
kNuCompressNone = 10,
kNuCompressSQ = 11,
kNuCompressLZW1 = 12,
kNuCompressLZW2 = 13,
kNuCompressLZC12 = 14,
kNuCompressLZC16 = 15,
/* for kNuValueEOL */
kNuEOLUnknown = 50,
kNuEOLCR = 51,
kNuEOLLF = 52,
kNuEOLCRLF = 53,
/* for kNuValueConvertExtractedEOL */
kNuConvertOff = 60,
kNuConvertOn = 61,
kNuConvertAuto = 62,
/* for kNuValueHandleExisting */
kNuMaybeOverwrite = 90,
kNuNeverOverwrite = 91,
kNuAlwaysOverwrite = 93,
kNuMustOverwrite = 94
};
/*
* Pull out archive attributes.
*/
typedef enum NuAttrID {
kNuAttrInvalid = 0,
kNuAttrArchiveType = 1,
kNuAttrNumRecords = 2
} NuAttrID;
typedef unsigned long NuAttr;
/*
* Archive types.
*/
typedef enum NuArchiveType {
kNuArchiveUnknown, /* .??? */
kNuArchiveNuFX, /* .SHK (sometimes .SDK) */
kNuArchiveNuFXInBNY, /* .BXY */
kNuArchiveNuFXSelfEx, /* .SEA */
kNuArchiveNuFXSelfExInBNY, /* .BSE */
kNuArchiveBNY /* .BNY, .BQY - not supported */
} NuArchiveType;
/*
* Some common values for "locked" and "unlocked". Under ProDOS each bit
* can be set independently, so don't use these defines to *interpret*
* what you see. They're reasonable things to *set* the access field to.
*/
#define kNuAccessLocked 0x21
#define kNuAccessUnlocked 0xe3
/*
* NuFlush result flags.
*/
#define kNuFlushSucceeded (1L)
#define kNuFlushAborted (1L << 1)
#define kNuFlushCorrupted (1L << 2)
#define kNuFlushReadOnly (1L << 3)
#define kNuFlushInaccessible (1L << 4)
/*
* ===========================================================================
* NuFX archive defintions
* ===========================================================================
*/
typedef struct NuThreadMod NuThreadMod; /* dummy def for internal struct */
typedef union NuDataSource NuDataSource; /* dummy def for internal struct */
typedef union NuDataSink NuDataSink; /* dummy def for internal struct */
/*
* NuFX Date/Time structure; same as TimeRec from IIgs "misctool.h".
*/
typedef struct NuDateTime {
unsigned char second;
unsigned char minute;
unsigned char hour;
unsigned char year;
unsigned char day;
unsigned char month;
unsigned char extra;
unsigned char weekDay;
} NuDateTime;
/*
* NuFX "thread" definition.
*/
typedef struct NuThread {
/* from the archive */
unsigned short thThreadClass;
NuThreadFormat thThreadFormat;
unsigned short thThreadKind;
unsigned short thThreadCRC; /* comp or uncomp data; see rec vers */
unsigned long thThreadEOF;
unsigned long thCompThreadEOF;
/* extra goodies */
NuThreadIdx threadIdx;
unsigned long actualThreadEOF; /* disk images might be off */
long fileOffset; /* fseek offset to data in file */
/* internal use only */
unsigned short used; /* mark as uninteresting */
} NuThread;
/*
* NuFX "record" definition.
*/
#define kNufxIDLen 4 /* len of 'NuFX' with funky MSBs */
#define kNuReasonableAttribCount 256
#define kNuReasonableFilenameLen 1024
#define kNuReasonableTotalThreads 16
#define kNuMaxRecordVersion 3 /* max we can handle */
#define kNuOurRecordVersion 3 /* what we write */
typedef struct NuRecord {
/* version 0+ */
unsigned char recNufxID[kNufxIDLen];
unsigned short recHeaderCRC;
unsigned short recAttribCount;
unsigned short recVersionNumber;
unsigned long recTotalThreads;
NuFileSysID recFileSysID;
unsigned short recFileSysInfo;
unsigned long recAccess;
unsigned long recFileType;
unsigned long recExtraType;
unsigned short recStorageType; /* NuStorage*,file_sys_block_size */
NuDateTime recCreateWhen;
NuDateTime recModWhen;
NuDateTime recArchiveWhen;
/* option lists only in version 1+ */
unsigned short recOptionSize;
unsigned char* recOptionList; /* NULL if v0 or recOptionSize==0 */
/* data specified by recAttribCount, not accounted for by option list */
long extraCount;
unsigned char* extraBytes;
unsigned short recFilenameLength; /* usually zero */
char* recFilename; /* doubles as disk volume_name */
/* extra goodies; "dirtyHeader" does not apply to anything below */
NuRecordIdx recordIdx; /* session-unique record index */
char* threadFilename; /* extracted from filename thread */
char* newFilename; /* memorized during "add file" call */
const char* filename; /* points at recFilen or threadFilen */
unsigned long recHeaderLength; /* size of rec hdr, incl thread hdrs */
unsigned long totalCompLength; /* total len of data in archive file */
long fileOffset; /* file offset of record header */
/* use provided interface to access this */
struct NuThread* pThreads; /* ptr to thread array */
/* private -- things the application shouldn't look at */
struct NuRecord* pNext; /* used internally */
NuThreadMod* pThreadMods; /* used internally */
short dirtyHeader; /* set in "copy" when hdr fields uptd */
short dropRecFilename; /* if set, we're dropping this name */
} NuRecord;
/*
* NuFX "master header" definition.
*
* The "mhReserved2" entry doesn't appear in my copy of the $e0/8002 File
* Type Note, but as best as I can recall the MH block must be 48 bytes.
*/
#define kNufileIDLen 6 /* length of 'NuFile' with funky MSBs */
#define kNufileMasterReserved1Len 8
#define kNufileMasterReserved2Len 6
#define kNuMaxMHVersion 2 /* max we can handle */
#define kNuOurMHVersion 2 /* what we write */
typedef struct NuMasterHeader {
unsigned char mhNufileID[kNufileIDLen];
unsigned short mhMasterCRC;
unsigned long mhTotalRecords;
NuDateTime mhArchiveCreateWhen;
NuDateTime mhArchiveModWhen;
unsigned short mhMasterVersion;
unsigned char mhReserved1[kNufileMasterReserved1Len];
unsigned long mhMasterEOF;
unsigned char mhReserved2[kNufileMasterReserved2Len];
/* private -- internal use only */
short isValid;
} NuMasterHeader;
/*
* ===========================================================================
* Misc declarations
* ===========================================================================
*/
/*
* Record attributes that can be changed with NuSetRecordAttr. This is
* a small subset of the full record.
*/
typedef struct NuRecordAttr {
NuFileSysID fileSysID;
/*unsigned short fileSysInfo;*/
unsigned long access;
unsigned long fileType;
unsigned long extraType;
NuDateTime createWhen;
NuDateTime modWhen;
NuDateTime archiveWhen;
} NuRecordAttr;
/*
* Some additional details about a file.
*/
typedef struct NuFileDetails {
/* used during AddFile call */
NuThreadID threadID; /* data, rsrc, disk img? */
/* these go straight into the NuRecord */
const char* storageName;
NuFileSysID fileSysID;
unsigned short fileSysInfo;
unsigned long access;
unsigned long fileType;
unsigned long extraType;
unsigned short storageType; /* use Unknown, or disk block size */
NuDateTime createWhen;
NuDateTime modWhen;
NuDateTime archiveWhen;
} NuFileDetails;
/*
* Passed into the SelectionFilter callback.
*/
typedef struct NuSelectionProposal {
const NuRecord* pRecord;
const NuThread* pThread;
} NuSelectionProposal;
/*
* Passed into the OuputPathnameFilter callback.
*/
typedef struct NuPathnameProposal {
const char* pathname;
char filenameSeparator;
const NuRecord* pRecord;
const NuThread* pThread;
const char* newPathname;
unsigned char newFilenameSeparator;
/*NuThreadID newStorage;*/
NuDataSink* newDataSink;
} NuPathnameProposal;
/* used by error handler and progress updater to indicate what we're doing */
typedef enum NuOperation {
kNuOpUnknown = 0,
kNuOpAdd,
kNuOpExtract,
kNuOpTest,
kNuOpDelete, /* not used for progress updates */
kNuOpContents /* not used for progress updates */
} NuOperation;
/* state of progress when adding or extracting */
typedef enum NuProgressState {
kNuProgressPreparing, /* not started yet */
kNuProgressOpening, /* opening files */
kNuProgressCompressing, /* compressing data */
kNuProgressStoring, /* storing (no compression) data */
kNuProgressExpanding, /* expanding data */
kNuProgressCopying, /* copying data (in or out) */
kNuProgressDone, /* all done, success */
kNuProgressSkipped, /* all done, we skipped this one */
kNuProgressFailed /* all done, failure */
} NuProgressState;
/*
* Passed into the ProgressUpdater callback.
*
* [ Thought for the day: add an optional flag that causes us to only
* call the progressFunc when the "percentComplete" changes by more
* than a specified amount. ]
*/
typedef struct NuProgressData {
/* what are we doing */
NuOperation operation;
/* what specifically are we doing */
NuProgressState state;
/* how far along are we */
short percentComplete; /* 0-100 */
/* original pathname (in archive for expand, on disk for compress) */
const char* origPathname;
/* processed pathname (PathnameFilter for expand, in-record for compress) */
const char* pathname;
/* basename of "pathname" */
const char* filename;
/* pointer to the record we're expanding from */
const NuRecord* pRecord;
unsigned long uncompressedLength; /* size of uncompressed data */
unsigned long uncompressedProgress; /* #of bytes in/out */
struct {
NuThreadFormat threadFormat; /* compression being applied */
} compress;
struct {
unsigned long totalCompressedLength; /* all "data" threads */
unsigned long totalUncompressedLength;
/*unsigned long compressedLength; * size of compressed data */
/*unsigned long compressedProgress; * #of compressed bytes in/out*/
const NuThread* pThread; /* thread we're working on */
NuValue convertEOL; /* set if LF/CR conv is on */
} expand;
/* pay no attention */
NuCallback progressFunc;
} NuProgressData;
/*
* Passed into the ErrorHandler callback.
*/
typedef struct NuErrorStatus {
NuOperation operation; /* were we adding, extracting, ?? */
NuError err; /* library error code */
int sysErr; /* system error code, if applicable */
const char* message; /* (optional) message to user */
const NuRecord* pRecord; /* relevant record, if any */
const char* pathname; /* relevant pathname, if any */
char filenameSeparator; /* fssep for this path, if any */
/*char origArchiveTouched;*/
char canAbort; /* give option to abort */
/*char canAbortAll;*/ /* abort + discard all recent changes */
char canRetry; /* give option to retry same op */
char canIgnore; /* give option to ignore error */
char canSkip; /* give option to skip this file/rec */
char canRename; /* give option to rename file */
char canOverwrite; /* give option to overwrite file */
} NuErrorStatus;
/*
* Error message callback gets one of these.
*/
typedef struct NuErrorMessage {
const char* message; /* the message itself */
NuError err; /* relevant error code (may be none) */
short isDebug; /* set for debug-only messages */
/* these identify where the message originated if lib built w/debug set */
const char* file; /* source file */
int line; /* line number */
const char* function; /* function name (might be nil) */
} NuErrorMessage;
/*
* ===========================================================================
* Function prototypes
* ===========================================================================
*/
/* streaming and non-streaming read-only interfaces */
NuError NuStreamOpenRO(FILE* infp, NuArchive** ppArchive);
NuError NuContents(NuArchive* pArchive, NuCallback contentFunc);
NuError NuExtract(NuArchive* pArchive);
NuError NuTest(NuArchive* pArchive);
/* strictly non-streaming read-only interfaces */
NuError NuOpenRO(const char* archivePathname, NuArchive** ppArchive);
NuError NuExtractRecord(NuArchive* pArchive, NuRecordIdx recordIdx);
NuError NuExtractThread(NuArchive* pArchive, NuThreadIdx threadIdx,
NuDataSink* pDataSink);
NuError NuGetRecord(NuArchive* pArchive, NuRecordIdx recordIdx,
const NuRecord** ppRecord);
NuError NuGetRecordIdxByName(NuArchive* pArchive, const char* name,
NuRecordIdx* pRecordIdx);
NuError NuGetRecordIdxByPosition(NuArchive* pArchive, unsigned long position,
NuRecordIdx* pRecordIdx);
/* read/write interfaces */
NuError NuOpenRW(const char* archivePathname, const char* tempPathname,
unsigned long flags, NuArchive** ppArchive);
NuError NuFlush(NuArchive* pArchive, long* pStatusFlags);
NuError NuAddRecord(NuArchive* pArchive, const NuFileDetails* pFileDetails,
NuRecordIdx* pRecordIdx);
NuError NuAddThread(NuArchive* pArchive, NuRecordIdx recordIdx,
NuThreadIdx threadID, NuDataSource* pDataSource,
NuThreadIdx* pThreadIdx);
NuError NuAddFile(NuArchive* pArchive, const char* pathname,
const NuFileDetails* pFileDetails, short fromRsrcFork,
NuRecordIdx* pRecordIdx);
NuError NuRename(NuArchive* pArchive, NuRecordIdx recordIdx,
const char* pathname, char fssep);
NuError NuSetRecordAttr(NuArchive* pArchive, NuRecordIdx recordIdx,
const NuRecordAttr* pRecordAttr);
NuError NuUpdatePresizedThread(NuArchive* pArchive, NuThreadIdx threadIdx,
NuDataSource* pDataSource, long* pMaxLen);
NuError NuDelete(NuArchive* pArchive);
NuError NuDeleteRecord(NuArchive* pArchive, NuRecordIdx recordIdx);
NuError NuDeleteThread(NuArchive* pArchive, NuRecordIdx threadIdx);
/* general interfaces */
NuError NuClose(NuArchive* pArchive);
NuError NuAbort(NuArchive* pArchive);
NuError NuGetMasterHeader(NuArchive* pArchive,
const NuMasterHeader** ppMasterHeader);
NuError NuGetExtraData(NuArchive* pArchive, void** ppData);
NuError NuSetExtraData(NuArchive* pArchive, void* pData);
NuError NuGetValue(NuArchive* pArchive, NuValueID ident, NuValue* pValue);
NuError NuSetValue(NuArchive* pArchive, NuValueID ident, NuValue value);
NuError NuGetAttr(NuArchive* pArchive, NuAttrID ident, NuAttr* pAttr);
NuError NuGetVersion(long* pMajorVersion, long* pMinorVersion,
long* pBugVersion, const char** ppBuildDate,
const char** ppBuildFlags);
const char* NuStrError(NuError err);
NuError NuDebugDumpArchive(NuArchive* pArchive);
/* sources and sinks */
NuError NuCreateDataSourceForFile(NuThreadFormat threadFormat, short doClose,
unsigned long otherLen, const char* pathname, short isFromRsrcFork,
NuDataSource** ppDataSource);
NuError NuCreateDataSourceForFP(NuThreadFormat threadFormat, short doClose,
unsigned long otherLen, FILE* fp, long offset, long length,
NuDataSource** ppDataSource);
NuError NuCreateDataSourceForBuffer(NuThreadFormat threadFormat, short doClose,
unsigned long otherLen, const unsigned char* buffer, long offset,
long length, NuDataSource** ppDataSource);
NuError NuFreeDataSource(NuDataSource* pDataSource);
NuError NuDataSourceSetRawCrc(NuDataSource* pDataSource, unsigned short crc);
NuError NuCreateDataSinkForFile(short doExpand, NuValue convertEOL,
const char* pathname, char fssep, NuDataSink** ppDataSink);
NuError NuCreateDataSinkForFP(short doExpand, NuValue convertEOL,
FILE* fp, NuDataSink** ppDataSink);
NuError NuCreateDataSinkForBuffer(short doExpand, NuValue convertEOL,
unsigned char* buffer, unsigned long bufLen,
NuDataSink** ppDataSink);
NuError NuFreeDataSink(NuDataSink* pDataSink);
NuError NuDataSinkGetOutCount(NuDataSink* pDataSink, unsigned long* pOutCount);
/* miscellaneous non-archive operations */
void NuRecordCopyAttr(NuRecordAttr* pRecordAttr, const NuRecord* pRecord);
NuError NuRecordCopyThreads(const NuRecord* pRecord, NuThread** ppThreads);
unsigned long NuRecordGetNumThreads(const NuRecord* pRecord);
const NuThread* NuThreadGetByIdx(const NuThread* pThread, long idx);
short NuIsPresizedThreadID(NuThreadID threadID);
#define NuGetThread(pRecord, idx) ( (const NuThread*) \
((unsigned long) (idx) < (unsigned long) (pRecord)->recTotalThreads ? \
&(pRecord)->pThreads[(idx)] : NULL) \
)
/* callback setters */
NuError NuSetSelectionFilter(NuArchive* pArchive, NuCallback filterFunc);
NuError NuSetOutputPathnameFilter(NuArchive* pArchive, NuCallback filterFunc);
NuError NuSetProgressUpdater(NuArchive* pArchive, NuCallback updateFunc);
NuError NuSetErrorHandler(NuArchive* pArchive, NuCallback errorFunc);
NuError NuSetErrorMessageHandler(NuArchive* pArchive,
NuCallback messageHandlerFunc);
NuError NuSetGlobalErrorMessageHandler(NuCallback messageHandlerFunc);
#ifdef __cplusplus
}
#endif
#endif /*__NufxLib__*/

827
nufxlib-0/NufxLibPriv.h Normal file
View File

@ -0,0 +1,827 @@
/*
* NuFX archive manipulation library
* Copyright (C) 2000 by Andy McFadden, All Rights Reserved.
* This is free software; you can redistribute it and/or modify it under the
* terms of the GNU Library General Public License, see the file COPYING.LIB.
*
* Global internal declarations and definitions.
*/
#ifndef __NufxLibPriv__
#define __NufxLibPriv__
/* include files that everybody needs */
#include "SysDefs.h"
#include "NufxLib.h"
#include "MiscStuff.h"
#ifdef USE_DMALLOC
/* enable with something like "dmalloc -l logfile -i 100 medium" */
# include "dmalloc.h"
#endif
/*
* ===========================================================================
* NuArchive definition
* ===========================================================================
*/
/*
* Archives can be opened in streaming read-only, non-streaming read-only,
* and non-streaming read-write mode.
*/
typedef enum NuOpenMode {
kNuOpenUnknown,
kNuOpenStreamingRO,
kNuOpenRO,
kNuOpenRW
} NuOpenMode;
#define Nu_IsStreaming(pArchive) ((pArchive)->openMode == kNuOpenStreamingRO)
#define Nu_IsReadOnly(pArchive) ((pArchive)->openMode == kNuOpenStreamingRO ||\
(pArchive)->openMode == kNuOpenRO)
#ifdef FOPEN_WANTS_B
# define kNuFileOpenReadOnly "rb"
# define kNuFileOpenReadWrite "r+b"
# define kNuFileOpenWriteTrunc "wb"
# define kNuFileOpenReadWriteCreat "w+b"
#else
# define kNuFileOpenReadOnly "r"
# define kNuFileOpenReadWrite "r+"
# define kNuFileOpenWriteTrunc "w"
# define kNuFileOpenReadWriteCreat "w+"
#endif
/*
* Some NuFX and Binary II definitions.
*/
#define kNuMasterHeaderSize 48 /* size of fixed-length master header */
#define kNuRecordHeaderBaseSize 58 /* size of rec hdr up to variable stuff */
#define kNuThreadHeaderSize 16 /* size of fixed-length thread header */
#define kNuDefaultFilenameThreadSize 32 /* default size of filename thred */
#define kNuDefaultCommentSize 200 /* size of GSHK-mimic comments */
#define kNuBinary2BlockSize 128 /* size of bxy header and padding */
#define kNuSEAOffset 0x2ee5 /* fixed(??) offset to data in SEA */
#define kNuInitialChunkCRC 0x0000 /* start for CRC in LZW/1 chunk */
#define kNuInitialThreadCRC 0xffff /* start for CRC in v3 thread header */
/* size of general-purpose compression buffer */
#define kNuGenCompBufSize 32768
#define kNuCharLF 0x0a
#define kNuCharCR 0x0d
/*
* A list of records. Generally we use one of these for read-only
* archives, and two for read-write.
*
* The "loaded" flag is set when we've made some use of the record set.
* Relying on "numRecords" won't always work; for example, if the "copy"
* record set was initialized from "orig", and then had all of its records
* deleted, you couldn't look at "numRecords" and decide whether it was
* appropriate to use "orig" or not.
*/
typedef struct NuRecordSet {
Boolean loaded;
ulong numRecords;
NuRecord* nuRecordHead;
NuRecord* nuRecordTail;
} NuRecordSet;
/*
* Archive state.
*/
struct NuArchive {
ulong structMagic;
Boolean busy;
NuOpenMode openMode;
Boolean newlyCreated;
char* archivePathname; /* pathname or "(stream)" */
FILE* archiveFp;
NuArchiveType archiveType;
long headerOffset; /* adjustment for BXY/SEA/BSE */
char* tmpPathname; /* temp file, for writes */
FILE* tmpFp;
/* used during initial processing; helps avoid ftell() calls */
long currentOffset;
/* setting this changes Extract into Test */
Boolean testMode;
/* clumsy way of remembering name used for other fork in forked file */
const char* lastFileCreated;
/* clumsy way to avoid trying to create the same subdir several times */
const char* lastDirCreated;
/* master header from the archive */
NuMasterHeader masterHeader; /* original */
NuMasterHeader newMasterHeader; /* working copy during update */
/* list of records from archive, plus some extra state */
NuRecordIdx recordIdxSeed; /* where the NuRecordIdxs start */
NuRecordIdx nextRecordIdx; /* next record gets this value */
Boolean haveToc; /* set if we have full TOC */
NuRecordSet origRecordSet; /* records from archive */
NuRecordSet copyRecordSet; /* copy of orig, for write ops */
NuRecordSet newRecordSet; /* newly-added records */
/* state for compression functions */
uchar* compBuf; /* large general-purpose buffer */
void* lzwCompressState; /* state for LZW/1 and LZW/2 */
void* lzwExpandState; /* state for LZW/1 and LZW/2 */
/* options and attributes that the user can set */
/* (these can be changed by a callback, so don't cache them internally) */
void* extraData; /* application-defined pointer */
NuValue valAllowDuplicates; /* allow dups when adding? */
NuValue valConvertExtractedEOL; /* convert EOL during extract? */
NuValue valDataCompression; /* how to compress when adding? */
NuValue valDiscardWrapper; /* remove BNY or SEA header? */
NuValue valEOL; /* EOL value to convert to */
NuValue valHandleExisting; /* how to deal with existing files*/
NuValue valIgnoreCRC; /* don't compute or test CRCs */
NuValue valMimicSHK; /* mimic some ShrinkIt quirks */
NuValue valModifyOrig; /* modify original arc in place? */
NuValue valOnlyUpdateOlder; /* modify original arc in place? */
/* callback functions */
NuCallback selectionFilterFunc;
NuCallback outputPathnameFunc;
NuCallback progressUpdaterFunc;
NuCallback errorHandlerFunc;
NuCallback messageHandlerFunc;
};
#define kNuArchiveStructMagic 0xc0edbabe
#define kNuDefaultRecordName "UNKNOWN"
/*
* ===========================================================================
* ThreadMod definition
* ===========================================================================
*/
/* operations we can perform on threads in a record */
typedef enum ThreadModKind {
kNuThreadModUnknown = 0,
kNuThreadModAdd,
kNuThreadModUpdate,
kNuThreadModDelete
} ThreadModKind;
/*
* We attach a list of these to records we plan to modify. Care is taken
* to ensure that they don't conflict, e.g. you can't update a thread
* right after you delete it, nor delete one you have modified.
*/
struct NuThreadMod {
union {
ThreadModKind kind;
struct {
ThreadModKind kind;
Boolean used;
} generic;
struct {
ThreadModKind kind;
Boolean used;
Boolean isPresized;
NuThreadIdx threadIdx;
NuThreadID threadID;
NuThreadFormat threadFormat;
NuDataSource* pDataSource;
} add;
struct {
ThreadModKind kind;
Boolean used;
NuThreadIdx threadIdx;
NuDataSource* pDataSource;
} update;
struct {
ThreadModKind kind;
Boolean used;
NuThreadIdx threadIdx;
NuThreadID threadID; /* used for watching filename threads */
} delete;
} entry;
struct NuThreadMod* pNext;
};
/*
* ===========================================================================
* NuFunnel/NuStraw definition
* ===========================================================================
*/
#define kNuFunnelBufSize 16384
/*
* File funnel definition. This is used for writing output to files
* (so we can do things like pipe compressed output through an LF->CR
* converter) and archive files (so we can halt compression when the
* output size exceeds the uncompressed original). [ for various reasons,
* I'm not using this on the archive anymore. ]
*
* Funnels are unidirectional. You write data into them with a
* function call; the top-level action (which is usually compressing or
* expanding data) reads from the input and crams things into the pipe.
* We could fully abstract the concept, and write the compression
* functions so that they operate as a Funnel filter, but it's much
* easier to write block-oriented compression than stream-oriented (and
* more to the point, the ShrinkIt LZW functions are very much
* block-oriented).
*/
typedef struct NuFunnel {
/* data storage */
uchar* buffer; /* kNuFunnelBufSize worth of storage */
long bufCount; /* #of bytes in buffer */
/* EOL conversion; if "auto", on first flush we convert to "on" or "off" */
NuValue convertEOL; /* on/off/auto */
NuValue convertEOLTo; /* EOL to switch to */
NuValue convertEOLFrom; /* EOL terminator we think we found */
Boolean lastCR; /* was last char a CR? */
#if 0
ulong inCount; /* total #of bytes in the top */
ulong outCount; /* total #of bytes out the bottom */
ulong outMax; /* flag an err when outCount exceeds this */
Boolean outMaxExceeded; /* in fact, it's this flag */
#endif
/* update this when stuff happens */
NuProgressData* pProgress;
/* data goeth out here */
NuDataSink* pDataSink;
} NuFunnel;
/*
* File straw definition. This is used for slurping up input data.
*
* Mostly this is an encapsulation of an input source and a progress
* updater, useful for reading uncompressed data and feeding it to a
* compressor. It doesn't make sense to show a thermometer based on
* compressed output, since we don't know how big the eventual result
* will be, so we want to do it for the input.
*/
typedef struct NuStraw {
/* update this when stuff happens */
NuProgressData* pProgress;
/* data cometh in here */
NuDataSource* pDataSource;
/* progress update fields */
ulong lastProgress;
ulong lastDisplayed;
} NuStraw;
/*NuError Nu_CopyStreamToStream(FILE* outfp, FILE* infp, ulong count);*/
/*
* ===========================================================================
* Data source and sink abstractions
* ===========================================================================
*/
/*
* DataSource is used when adding data to an archive.
*/
typedef enum NuDataSourceType {
kNuDataSourceUnknown = 0,
kNuDataSourceFromFile,
kNuDataSourceFromFP,
kNuDataSourceFromBuffer
} NuDataSourceType;
typedef struct NuDataSourceCommon {
NuDataSourceType sourceType;
NuThreadFormat threadFormat; /* is it already compressed? */
ushort rawCrc; /* crc for already-compressed data*/
Boolean doClose; /* close on completion? */
ulong dataLen; /* length of data (var for buf) */
ulong otherLen; /* uncomp len or preset buf size */
} NuDataSourceCommon;
union NuDataSource {
NuDataSourceType sourceType;
NuDataSourceCommon common;
struct {
NuDataSourceCommon common;
char* pathname;
Boolean fromRsrcFork;
/* temp storage; only valid when processing in library */
FILE* fp;
} fromFile;
struct {
NuDataSourceCommon common;
FILE* fp;
long offset; /* starting offset */
} fromFP;
struct {
NuDataSourceCommon common;
const uchar* buffer; /* non-const if doClose=true */
long offset; /* starting offset */
long curOffset; /* current offset */
long curDataLen; /* remaining data */
} fromBuffer;
};
/*
* DataSink is used when extracting data from an archive.
*/
typedef enum NuDataSinkType {
kNuDataSinkUnknown = 0,
kNuDataSinkToFile,
kNuDataSinkToFP,
kNuDataSinkToBuffer,
kNuDataSinkToVoid
} NuDataSinkType;
typedef struct NuDataSinkCommon {
NuDataSinkType sinkType;
Boolean doExpand; /* expand file? */
NuValue convertEOL; /* convert EOL? (req "expand") */
ulong outCount;
} NuDataSinkCommon;
union NuDataSink {
NuDataSinkType sinkType;
NuDataSinkCommon common;
struct {
NuDataSinkCommon common;
char* pathname; /* file to open */
char fssep;
/* temp storage; must be nil except when processing in library */
FILE* fp;
} toFile;
struct {
NuDataSinkCommon common;
FILE* fp;
} toFP;
struct {
NuDataSinkCommon common;
uchar* buffer;
ulong bufLen; /* max amount of data "buffer" holds */
NuError stickyErr;
} toBuffer;
};
/*
* ===========================================================================
* Function prototypes
* ===========================================================================
*/
/*
* This is a little unpleasant. This blob of stuff gets stuffed in as
* the first arguments to Nu_ReportError, so we don't have to type them
* in every time we use the function. It would've been much easier to
* use a gcc-style varargs macro, but not everybody uses gcc.
*/
#ifdef DEBUG_MSGS
#ifdef HAS__FUNCTION__
#define _FUNCTION_ __FUNCTION__
#else
#define _FUNCTION_ ""
#endif
#define NU_BLOB pArchive, __FILE__, __LINE__, _FUNCTION_, false
#define NU_BLOB_DEBUG pArchive, __FILE__, __LINE__, _FUNCTION_, true
#define NU_NILBLOB NULL, __FILE__, __LINE__, _FUNCTION_, false
#define DebugShowError(err) \
Nu_ReportError(pArchive, __FILE__, __LINE__, _FUNCTION_, \
true, err, "(DEBUG)");
#else
#define NU_BLOB pArchive, "", 0, "", false
#define NU_BLOB_DEBUG pArchive, "", 0, "", true
#define NU_NILBLOB NULL, "", 0, "", false
#define DebugShowError(err) ((void)0)
#endif
/*
* The BailError macro serves two purposes. First, it's a convenient
* way to avoid typing, "if (err != kNuErrNone) goto bail;". Second,
* when the library is built with debugging enabled, it vitually gives
* us a stack trace of exiting functions. This makes it easier to debug
* problems sent in as screen dumps via e-mail.
*/
#define BailError(err) { \
if ((err) != kNuErrNone) { \
/* [should this be debug-only, or all the time?] */ \
DebugShowError(err); \
goto bail; \
} \
}
#define BailErrorQuiet(err) { \
if ((err) != kNuErrNone) \
goto bail; \
}
#define BailNil(val) { \
if ((val) == nil) { \
err = kNuErrUnexpectedNil; \
BailError(err); \
} \
}
#define BailAlloc(val) { \
if ((val) == nil) { \
err = kNuErrMalloc; \
BailError(err); \
} \
}
/*
* Internal function prototypes and inline functions.
*/
/* Archive.c */
void Nu_MasterHeaderCopy(NuArchive* pArchive, NuMasterHeader* pDstHeader,
const NuMasterHeader* pSrcHeader);
NuError Nu_GetMasterHeader(NuArchive* pArchive,
const NuMasterHeader** ppMasterHeader);
NuRecordIdx Nu_GetNextRecordIdx(NuArchive* pArchive);
NuThreadIdx Nu_GetNextThreadIdx(NuArchive* pArchive);
NuError Nu_CopyWrapperToTemp(NuArchive* pArchive);
NuError Nu_UpdateWrapper(NuArchive* pArchive, FILE* fp);
NuError Nu_AdjustWrapperPadding(NuArchive* pArchive, FILE* fp);
NuError Nu_AllocCompressionBufferIFN(NuArchive* pArchive);
NuError Nu_StreamOpenRO(FILE* infp, NuArchive** ppArchive);
NuError Nu_OpenRO(const char* filename, NuArchive** ppArchive);
NuError Nu_OpenRW(const char* archivePathname, const char* tempPathname,
ulong flags, NuArchive** ppArchive);
NuError Nu_WriteMasterHeader(NuArchive* pArchive, FILE* fp,
NuMasterHeader* pMasterHeader);
NuError Nu_Close(NuArchive* pArchive);
NuError Nu_Abort(NuArchive* pArchive);
NuError Nu_RenameTempToArchive(NuArchive* pArchive);
NuError Nu_DeleteArchiveFile(NuArchive* pArchive);
/* ArchiveIO.c */
uchar Nu_ReadOneC(NuArchive* pArchive, FILE* fp, ushort* pCrc);
uchar Nu_ReadOne(NuArchive* pArchive, FILE* fp);
void Nu_WriteOneC(NuArchive* pArchive, FILE* fp, uchar val, ushort* pCrc);
void Nu_WriteOne(NuArchive* pArchive, FILE* fp, uchar val);
ushort Nu_ReadTwoC(NuArchive* pArchive, FILE* fp, ushort* pCrc);
ushort Nu_ReadTwo(NuArchive* pArchive, FILE* fp);
void Nu_WriteTwoC(NuArchive* pArchive, FILE* fp, ushort val, ushort* pCrc);
void Nu_WriteTwo(NuArchive* pArchive, FILE* fp, ushort val);
ulong Nu_ReadFourC(NuArchive* pArchive, FILE* fp, ushort* pCrc);
ulong Nu_ReadFour(NuArchive* pArchive, FILE* fp);
void Nu_WriteFourC(NuArchive* pArchive, FILE* fp, ulong val, ushort* pCrc);
void Nu_WriteFour(NuArchive* pArchive, FILE* fp, ulong val);
NuDateTime Nu_ReadDateTimeC(NuArchive* pArchive, FILE* fp, ushort* pCrc);
NuDateTime Nu_ReadDateTime(NuArchive* pArchive, FILE* fp, ushort* pCrc);
void Nu_WriteDateTimeC(NuArchive* pArchive, FILE* fp, NuDateTime dateTime,
ushort* pCrc);
void Nu_WriteDateTime(NuArchive* pArchive, FILE* fp, NuDateTime dateTime);
void Nu_ReadBytesC(NuArchive* pArchive, FILE* fp, void* vbuffer, long count,
ushort* pCrc);
void Nu_ReadBytes(NuArchive* pArchive, FILE* fp, void* vbuffer, long count);
void Nu_WriteBytesC(NuArchive* pArchive, FILE* fp, const void* vbuffer,
long count, ushort* pCrc);
void Nu_WriteBytes(NuArchive* pArchive, FILE* fp, const void* vbuffer,
long count);
NuError Nu_HeaderIOFailed(NuArchive* pArchive, FILE* fp);
NuError Nu_SeekArchive(NuArchive* pArchive, FILE* fp, long offset,
int ptrname);
NuError Nu_RewindArchive(NuArchive* pArchive);
/* Compress.c */
NuError Nu_CompressToArchive(NuArchive* pArchive, NuDataSource* pDataSource,
NuThreadID threadID, NuThreadFormat sourceFormat,
NuThreadFormat targetFormat, NuProgressData* progressData, FILE* dstFp,
NuThread* pThread);
NuError Nu_CopyPresizedToArchive(NuArchive* pArchive,
NuDataSource* pDataSource, NuThreadID threadID, FILE* dstFp,
NuThread* pThread, char** ppSavedCopy);
/* Crc16.c */
extern const ushort gNuCrc16Table[256];
ushort Nu_CalcCRC16(ushort seed, const uchar* ptr, int count);
#ifdef __Crc16_c__ /* just doing "static inline" warns def-but-not-used */
#define CRC_INLINE /**/
#else
#define CRC_INLINE extern inline
#endif
#if defined(inline) && !defined(__Crc16_c__) /* somebody ovrd inline def? */
ushort Nu_UpdateCRC16(uchar val, ushort crc);
#else
CRC_INLINE ushort
Nu_UpdateCRC16(uchar val, ushort crc)
{
return (gNuCrc16Table[((crc >> 8) & 0xFF) ^ val] ^ (crc << 8)) & 0xFFFF;
}
#endif
/* Debug.c */
#if defined(DEBUG_MSGS) || !defined(NDEBUG)
void Nu_DebugDumpAll(NuArchive* pArchive);
void Nu_DebugDumpThread(const NuThread* pThread);
#endif
/* Deferred.c */
NuError Nu_ThreadModAdd_New(NuArchive* pArchive, NuThreadID threadID,
NuThreadFormat threadFormat, NuDataSource* pDataSource,
NuThreadMod** ppThreadMod);
NuError Nu_ThreadModUpdate_New(NuArchive* pArchive, NuThreadIdx threadIdx,
NuDataSource* pDataSource, NuThreadMod** ppThreadMod);
NuError Nu_ThreadModDelete_New(NuArchive* pArchive, NuThreadIdx threadIdx,
NuThreadID threadID, NuThreadMod** ppThreadMod);
void Nu_ThreadModFree(NuArchive* pArchive, NuThreadMod* pThreadMod);
NuError Nu_ThreadModAdd_FindByThreadID(const NuRecord* pRecord,
NuThreadID threadID, NuThreadMod** ppThreadMod);
void Nu_FreeThreadMods(NuArchive* pArchive, NuRecord* pRecord);
NuThreadMod* Nu_ThreadMod_FindByThreadIdx(const NuRecord* pRecord,
NuThreadIdx threadIdx);
NuError Nu_Flush(NuArchive* pArchive, long* pStatusFlags);
/* Expand.c */
NuError Nu_ExpandStream(NuArchive* pArchive, const NuRecord* pRecord,
const NuThread* pThread, FILE* infp, NuFunnel* pFunnel);
/* FileIO.c */
void Nu_SetCurrentDateTime(NuDateTime* pDateTime);
Boolean Nu_IsOlder(const NuDateTime* pWhen1, const NuDateTime* pWhen2);
NuError Nu_OpenOutputFile(NuArchive* pArchive, const NuRecord* pRecord,
const NuThread* pThread, const char* newPathname, char newFssep,
FILE** pFp);
NuError Nu_CloseOutputFile(NuArchive* pArchive, const NuRecord* pRecord,
FILE* fp, const char* pathname);
NuError Nu_OpenInputFile(NuArchive* pArchive, const char* pathname,
Boolean openRsrc, FILE** pFp);
NuError Nu_DeleteFile(const char* pathname);
NuError Nu_RenameFile(const char* fromPath, const char* toPath);
NuError Nu_FTell(FILE* fp, long* pOffset);
NuError Nu_FSeek(FILE* fp, long offset, int ptrname);
NuError Nu_FRead(FILE* fp, void* buf, size_t nbyte);
NuError Nu_FWrite(FILE* fp, const void* buf, size_t nbyte);
NuError Nu_CopyFileSection(NuArchive* pArchive, FILE* dstFp, FILE* srcFp,
long length);
NuError Nu_GetFileLength(NuArchive* pArchive, FILE* fp, long* pLength);
NuError Nu_TruncateOpenFile(FILE* fp, long length);
/* Funnel.c */
NuError Nu_ProgressDataInit_Compress(NuArchive* pArchive,
NuProgressData* pProgressData, const NuRecord* pRecord,
const char* origPathname);
NuError Nu_ProgressDataInit_Expand(NuArchive* pArchive,
NuProgressData* pProgressData, const NuRecord* pRecord,
const char* newPathname, char newFssep, NuValue convertEOL);
NuError Nu_SendInitialProgress(NuArchive* pArchive,
const NuProgressData* pProgress);
NuError Nu_FunnelNew(NuArchive* pArchive, NuDataSink* pDataSink,
NuValue convertEOL, NuValue convertEOLTo, NuProgressData* pProgress,
NuFunnel** ppFunnel);
NuError Nu_FunnelFree(NuArchive* pArchive, NuFunnel* pFunnel);
/*void Nu_FunnelSetMaxOutput(NuFunnel* pFunnel, ulong maxBytes);*/
NuError Nu_FunnelWrite(NuArchive* pArchive, NuFunnel* pFunnel,
const uchar* buffer, ulong count);
NuError Nu_FunnelFlush(NuArchive* pArchive, NuFunnel* pFunnel);
NuError Nu_ProgressDataCompressPrep(NuArchive* pArchive, NuStraw* pStraw,
NuThreadFormat threadFormat, ulong sourceLen);
NuError Nu_ProgressDataExpandPrep(NuArchive* pArchive, NuFunnel* pFunnel,
const NuThread* pThread);
NuError Nu_FunnelSetProgressState(NuFunnel* pFunnel, NuProgressState state);
NuError Nu_FunnelSendProgressUpdate(NuArchive* pArchive, NuFunnel* pFunnel);
Boolean Nu_FunnelGetDoExpand(NuFunnel* pFunnel);
NuError Nu_StrawNew(NuArchive* pArchive, NuDataSource* pDataSource,
NuProgressData* pProgress, NuStraw** ppStraw);
NuError Nu_StrawFree(NuArchive* pArchive, NuStraw* pStraw);
NuError Nu_StrawSetProgressState(NuStraw* pStraw, NuProgressState state);
NuError Nu_StrawSendProgressUpdate(NuArchive* pArchive, NuStraw* pStraw);
NuError Nu_StrawRead(NuArchive* pArchive, NuStraw* pStraw, uchar* buffer,
long len);
NuError Nu_StrawRewind(NuArchive* pArchive, NuStraw* pStraw);
/* Lzw.c */
NuError Nu_CompressLZW1(NuArchive* pArchive, NuStraw* pStraw, FILE* fp,
ulong srcLen, ulong* pDstLen, ushort* pCrc);
NuError Nu_CompressLZW2(NuArchive* pArchive, NuStraw* pStraw, FILE* fp,
ulong srcLen, ulong* pDstLen, ushort* pCrc);
NuError Nu_ExpandLZW(NuArchive* pArchive, const NuRecord* pRecord,
const NuThread* pThread, FILE* infp, NuFunnel* pFunnel, ushort* pCrc);
/* MiscUtils.c */
extern const char* kNufxLibName;
extern NuCallback gNuGlobalErrorMessageHandler;
const char* Nu_StrError(NuError err);
void Nu_ReportError(NuArchive* pArchive, const char* file, int line,
const char* function, Boolean isDebug, NuError err, const char* format, ...)
#if defined(__GNUC__)
__attribute__ ((format(printf, 7, 8)))
#endif
;
#ifdef USE_DMALLOC /* want file and line numbers for calls */
# define Nu_Malloc(archive, size) malloc(size)
# define Nu_Calloc(archive, size) calloc(1, size)
# define Nu_Realloc(archive, ptr, size) realloc(ptr, size)
# define Nu_Free(archive, ptr) (ptr != nil ? free(ptr) : (void)0)
#else
void* Nu_Malloc(NuArchive* pArchive, size_t size);
void* Nu_Calloc(NuArchive* pArchive, size_t size);
void* Nu_Realloc(NuArchive* pArchive, void* ptr, size_t size);
void Nu_Free(NuArchive* pArchive, void* ptr);
#endif
/* Record.c */
void Nu_RecordAddThreadMod(NuRecord* pRecord, NuThreadMod* pThreadMod);
Boolean Nu_RecordIsEmpty(NuArchive* pArchive, const NuRecord* pRecord);
Boolean Nu_RecordSet_GetLoaded(const NuRecordSet* pRecordSet);
ulong Nu_RecordSet_GetNumRecords(const NuRecordSet* pRecordSet);
void Nu_RecordSet_SetNumRecords(NuRecordSet* pRecordSet, ulong val);
void Nu_RecordSet_IncNumRecords(NuRecordSet* pRecordSet);
NuRecord* Nu_RecordSet_GetListHead(const NuRecordSet* pRecordSet);
NuRecord** Nu_RecordSet_GetListHeadPtr(NuRecordSet* pRecordSet);
NuRecord* Nu_RecordSet_GetListTail(const NuRecordSet* pRecordSet);
Boolean Nu_RecordSet_IsEmpty(const NuRecordSet* pRecordSet);
NuError Nu_RecordSet_FreeAllRecords(NuArchive* pArchive,
NuRecordSet* pRecordSet);
NuError Nu_RecordSet_DeleteRecordPtr(NuArchive* pArchive,
NuRecordSet* pRecordSet, NuRecord** ppRecord);
NuError Nu_RecordSet_DeleteRecord(NuArchive* pArchive, NuRecordSet* pRecordSet,
NuRecord* pRecord);
NuError Nu_RecordSet_Clone(NuArchive* pArchive, NuRecordSet* pDstSet,
const NuRecordSet* pSrcSet);
NuError Nu_RecordSet_MoveAllRecords(NuArchive* pArchive, NuRecordSet* pDstSet,
NuRecordSet* pSrcSet);
NuError Nu_RecordSet_FindByIdx(const NuRecordSet* pRecordSet, NuRecordIdx rec,
NuRecord** ppRecord);
NuError Nu_RecordSet_FindByThreadIdx(NuRecordSet* pRecordSet,
NuThreadIdx threadIdx, NuRecord** ppRecord, NuThread** ppThread);
NuError Nu_RecordSet_ReplaceRecord(NuArchive* pArchive, NuRecordSet* pBadSet,
NuRecord* pBadRecord, NuRecordSet* pGoodSet, NuRecord** ppGoodRecord);
Boolean Nu_ShouldIgnoreBadCRC(NuArchive* pArchive, const NuRecord* pRecord,
NuError err);
NuError Nu_WriteRecordHeader(NuArchive* pArchive, NuRecord* pRecord, FILE* fp);
NuError Nu_GetTOCIfNeeded(NuArchive* pArchive);
NuError Nu_StreamContents(NuArchive* pArchive, NuCallback contentFunc);
NuError Nu_StreamExtract(NuArchive* pArchive);
NuError Nu_StreamTest(NuArchive* pArchive);
NuError Nu_Contents(NuArchive* pArchive, NuCallback contentFunc);
NuError Nu_Extract(NuArchive* pArchive);
NuError Nu_ExtractRecord(NuArchive* pArchive, NuRecordIdx recIdx);
NuError Nu_Test(NuArchive* pArchive);
NuError Nu_GetRecord(NuArchive* pArchive, NuRecordIdx recordIdx,
const NuRecord** ppRecord);
NuError Nu_GetRecordIdxByName(NuArchive* pArchive, const char* name,
NuRecordIdx* pRecordIdx);
NuError Nu_GetRecordIdxByPosition(NuArchive* pArchive, ulong position,
NuRecordIdx* pRecordIdx);
NuError Nu_FindRecordForWriteByIdx(NuArchive* pArchive, NuRecordIdx recIdx,
NuRecord** ppFoundRecord);
NuError Nu_AddFile(NuArchive* pArchive, const char* pathname,
const NuFileDetails* pFileDetails, Boolean fromRsrcFork,
NuRecordIdx* pRecordIdx);
NuError Nu_AddRecord(NuArchive* pArchive, const NuFileDetails* pFileDetails,
NuRecordIdx* pRecordIdx, NuRecord** ppRecord);
NuError Nu_Rename(NuArchive* pArchive, NuRecordIdx recIdx,
const char* pathname, char fssep);
NuError Nu_SetRecordAttr(NuArchive* pArchive, NuRecordIdx recordIdx,
const NuRecordAttr* pRecordAttr);
NuError Nu_Delete(NuArchive* pArchive);
NuError Nu_DeleteRecord(NuArchive* pArchive, NuRecordIdx rec);
/* SourceSink.c */
NuError Nu_DataSourceFile_New(NuThreadFormat threadFormat, Boolean doClose,
ulong otherLen, const char* pathname, Boolean isFromRsrcFork,
NuDataSource** ppDataSource);
NuError Nu_DataSourceFP_New(NuThreadFormat threadFormat, Boolean doClose,
ulong otherLen, FILE* fp, long offset, long length,
NuDataSource** ppDataSource);
NuError Nu_DataSourceBuffer_New(NuThreadFormat threadFormat,
Boolean doClose, ulong otherLen, const uchar* buffer, long offset,
long length, NuDataSource** ppDataSource);
NuDataSource* Nu_DataSourceCopy(NuDataSource* pDataSource);
NuError Nu_DataSourceFree(NuDataSource* pDataSource);
NuDataSourceType Nu_DataSourceGetType(const NuDataSource* pDataSource);
NuThreadFormat Nu_DataSourceGetThreadFormat(const NuDataSource* pDataSource);
ulong Nu_DataSourceGetDataLen(const NuDataSource* pDataSource);
ulong Nu_DataSourceGetOtherLen(const NuDataSource* pDataSource);
void Nu_DataSourceSetOtherLen(NuDataSource* pDataSource, long otherLen);
ushort Nu_DataSourceGetRawCrc(const NuDataSource* pDataSource);
void Nu_DataSourceSetRawCrc(NuDataSource* pDataSource, ushort crc);
NuError Nu_DataSourcePrepareInput(NuArchive* pArchive,
NuDataSource* pDataSource);
void Nu_DataSourceUnPrepareInput(NuArchive* pArchive,
NuDataSource* pDataSource);
const char* Nu_DataSourceFile_GetPathname(NuDataSource* pDataSource);
NuError Nu_DataSourceGetBlock(NuDataSource* pDataSource, uchar* buf, ulong len);
NuError Nu_DataSourceRewind(NuDataSource* pDataSource);
NuError Nu_DataSinkFile_New(Boolean doExpand, NuValue convertEOL,
const char* pathname, char fssep, NuDataSink** ppDataSink);
NuError Nu_DataSinkFP_New(Boolean doExpand, NuValue convertEOL, FILE* fp,
NuDataSink** ppDataSink);
NuError Nu_DataSinkBuffer_New(Boolean doExpand, NuValue convertEOL,
uchar* buffer, ulong bufLen, NuDataSink** ppDataSink);
NuError Nu_DataSinkVoid_New(Boolean doExpand, NuValue convertEOL,
NuDataSink** ppDataSink);
NuError Nu_DataSinkFree(NuDataSink* pDataSink);
NuDataSinkType Nu_DataSinkGetType(const NuDataSink* pDataSink);
Boolean Nu_DataSinkGetDoExpand(const NuDataSink* pDataSink);
NuValue Nu_DataSinkGetConvertEOL(const NuDataSink* pDataSink);
ulong Nu_DataSinkGetOutCount(const NuDataSink* pDataSink);
const char* Nu_DataSinkFile_GetPathname(const NuDataSink* pDataSink);
char Nu_DataSinkFile_GetFssep(const NuDataSink* pDataSink);
FILE* Nu_DataSinkFile_GetFP(const NuDataSink* pDataSink);
void Nu_DataSinkFile_SetFP(NuDataSink* pDataSink, FILE* fp);
void Nu_DataSinkFile_Close(NuDataSink* pDataSink);
NuError Nu_DataSinkPutBlock(NuDataSink* pDataSink, const uchar* buf, ulong len);
NuError Nu_DataSinkGetError(NuDataSink* pDataSink);
/* Thread.c */
#ifdef __Thread_c__
#define THREAD_INLINE /**/
#else
#define THREAD_INLINE extern inline
#endif
#if defined(inline) && !defined(__Thread_c__) /* somebody ovrd inline def? */
NuThread* Nu_GetThread(const NuRecord* pRecord, int idx);
#else
THREAD_INLINE NuThread*
Nu_GetThread(const NuRecord* pRecord, int idx)
{
if (idx >= (int)pRecord->recTotalThreads)
return nil;
else
return &pRecord->pThreads[idx];
}
#endif
void Nu_StripHiIfAllSet(char* str);
Boolean Nu_IsPresizedThreadID(NuThreadID threadID);
Boolean Nu_IsCompressibleThreadID(NuThreadID threadID);
Boolean Nu_ThreadHasCRC(long recordVersion, NuThreadID threadID);
NuError Nu_FindThreadByIdx(const NuRecord* pRecord, NuThreadIdx thread,
NuThread** ppThread);
NuError Nu_FindThreadByID(const NuRecord* pRecord, NuThreadID threadID,
NuThread** ppThread);
void Nu_CopyThreadContents(NuThread* pDstThread, const NuThread* pSrcThread);
NuError Nu_ReadThreadHeaders(NuArchive* pArchive, NuRecord* pRecord,
ushort* pCrc);
NuError Nu_WriteThreadHeaders(NuArchive* pArchive, NuRecord* pRecord, FILE* fp,
ushort* pCrc);
NuError Nu_ComputeThreadData(NuArchive* pArchive, NuRecord* pRecord);
NuError Nu_ScanThreads(NuArchive* pArchive, NuRecord* pRecord,long numThreads);
NuError Nu_ExtractThreadBulk(NuArchive* pArchive, const NuRecord* pRecord,
const NuThread* pThread);
NuError Nu_SkipThread(NuArchive* pArchive, const NuRecord* pRecord,
const NuThread* pThread);
NuError Nu_ExtractThread(NuArchive* pArchive, NuThreadIdx threadIdx,
NuDataSink* pDataSink);
NuError Nu_OkayToAddThread(NuArchive* pArchive, const NuRecord* pRecord,
NuThreadID threadID);
NuError Nu_AddThread(NuArchive* pArchive, NuRecordIdx rec, NuThreadID threadID,
NuDataSource* pDataSource, NuThreadIdx* pThreadIdx);
NuError Nu_UpdatePresizedThread(NuArchive* pArchive, NuThreadIdx threadIdx,
NuDataSource* pDataSource, long* pMaxLen);
NuError Nu_DeleteThread(NuArchive* pArchive, NuThreadIdx threadIdx);
/* Value.c */
NuError Nu_GetValue(NuArchive* pArchive, NuValueID ident, NuValue* pValue);
NuError Nu_SetValue(NuArchive* pArchive, NuValueID ident, NuValue value);
NuError Nu_GetAttr(NuArchive* pArchive, NuAttrID ident, NuAttr* pAttr);
NuThreadFormat Nu_ConvertCompressValToFormat(NuArchive* pArchive,
NuValue compValue);
/* Version.c */
NuError Nu_GetVersion(long* pMajorVersion, long* pMinorVersion,
long* pBugVersion, const char** ppBuildDate, const char** ppBuildFlags);
#endif /*__NufxLibPriv__*/

92
nufxlib-0/README.txt Normal file
View File

@ -0,0 +1,92 @@
NufxLib README, updated 2000/05/18
http://www.nulib.com/
See "COPYING-LIB" for distribution restrictions.
UNIX
====
Run the "configure" script. Read through "INSTALL" if you haven't used
one of these before, especially if you want to use a specific compiler
or a particular set of compiler flags.
Run "make depend" if you have makedepend, and then type "make". This will
build the library and all of the programs in the "samples" directory.
There are some useful programs in "samples", described in a README.txt
file there. In particular, you should run samples/test-basic to verify
that things are more or less working.
If you want to install the library and header file into standard system
locations (usually /usr/local), run "make install". To learn how to
specify different locations, read the INSTALL document.
There are some flags in "OPT" you may want to use. The "autoconf" default
for @CFLAGS@ is "-g -O2".
-DNDEBUG
Disable assert() calls and extra tests. This will speed things up,
but errors won't get caught until later on, making the root cause
harder to locate.
-DDEBUG_MSGS
Enable debug messages. This increases the size of the executable,
but shouldn't affect performance. When errors occur, more output is
produced. The "debug dump" feature is enabled by this flag.
-DDEBUG_VERBOSE
(Implicitly sets DEBUG_MSGS.) Spray lots of debugging output.
If you want to do benchmarks, use "-O2 -DNDEBUG". For pre-v1.0 sources,
setting -DNDEBUG is otherwise discouraged. The recommended configuration
is "-g -O2 -DDEBUG_MSGS", so that verbose debug output is available when
errors occur.
The flags are stuffed into Version.c, so the application program can
examine and display the flags that were used to build the library.
BeOS
====
This works just like the UNIX version, but certain defaults have been
changed. Running configure without arguments under BeOS is equivalent to:
./configure --prefix=/boot --includedir='${prefix}/develop/headers'
--libdir='${exec_prefix}/home/config/lib' --mandir='/tmp'
--bindir='${exec_prefix}/home/config/bin'
If you're using BeOS/PPC, it will also do:
CC=cc CFLAGS='-proc 603 -opt full'
Win32
=====
If you're using an environment that supports "configure" scripts, such as
DJGPP, follow the UNIX instructions.
NufxLib has been tested with Microsoft Visual C++ 6.0. To build NufxLib,
start up a DOS shell and run vcvars32.bat to set your environment. Run:
nmake -f makefile.msc
to build with debugging info, or
nmake -f makefile.msc nodebug=1
to build optimized.
Once the library has been built, "cd samples" and run the same command there.
When it finishes, run "test-basic.exe".
Other Notes
===========
All of the source code was formatted with four-space hard tabs.
If you want to use the library in a multithreaded application, you should
define "USE_REENTRANT_CALLS" to tell it to use reentrant versions of
certain library calls. This defines _REENTRANT, which causes Solaris to
add the appropriate goodies. (Seems to me you'd always want this on, but
for some reason Solaris makes you take an extra step, so I'm not going to
define it by default.)

2744
nufxlib-0/Record.c Normal file

File diff suppressed because it is too large Load Diff

866
nufxlib-0/SourceSink.c Normal file
View File

@ -0,0 +1,866 @@
/*
* NuFX archive manipulation library
* Copyright (C) 2000 by Andy McFadden, All Rights Reserved.
* This is free software; you can redistribute it and/or modify it under the
* terms of the GNU Library General Public License, see the file COPYING.LIB.
*
* Implementation of DataSource and DataSink objects.
*/
#include "NufxLibPriv.h"
/*
* ===========================================================================
* NuDataSource
* ===========================================================================
*/
/*
* Allocate a new DataSource structure.
*/
static NuError
Nu_DataSourceNew(NuDataSource** ppDataSource)
{
Assert(ppDataSource != nil);
*ppDataSource = Nu_Malloc(nil, sizeof(**ppDataSource));
if (*ppDataSource == nil)
return kNuErrMalloc;
(*ppDataSource)->sourceType = kNuDataSourceUnknown;
return kNuErrNone;
}
/*
* Make a copy of a DataSource.
*
* IMPORTANT: if the original had the "doClose" flag set, it will be cleared,
* but left set in the duplicate. The assumption is that the original will
* be thrown out before the duplicate. If this isn't the case, you will
* need to fix the flags on the copied data.
*
* Returns nil on error.
*/
NuDataSource*
Nu_DataSourceCopy(NuDataSource* pDataSource)
{
NuDataSource* pNewDataSource;
Assert(pDataSource != nil);
if (Nu_DataSourceNew(&pNewDataSource) != kNuErrNone)
return nil;
Assert(pNewDataSource != nil);
/* this gets most of it */
memcpy(pNewDataSource, pDataSource, sizeof(*pNewDataSource));
/* copy anything we're sure to free up */
if (pDataSource->sourceType == kNuDataSourceFromFile) {
Assert(pDataSource->fromFile.fp == nil); /* does this matter? */
pNewDataSource->fromFile.pathname =
strdup(pDataSource->fromFile.pathname);
}
/* don't let the original free up the resources */
if (pDataSource->common.doClose) {
DBUG(("--- clearing doClose on source-copy of data source\n"));
pDataSource->common.doClose = false;
}
return pNewDataSource;
}
/*
* Free a data source structure, and any type-specific elements.
*/
NuError
Nu_DataSourceFree(NuDataSource* pDataSource)
{
if (pDataSource == nil)
return kNuErrNone;
switch (pDataSource->sourceType) {
case kNuDataSourceFromFile:
Nu_Free(nil, pDataSource->fromFile.pathname);
if (pDataSource->fromFile.fp != nil) {
fclose(pDataSource->fromFile.fp);
pDataSource->fromFile.fp = nil;
}
break;
case kNuDataSourceFromFP:
if (pDataSource->common.doClose) {
fclose(pDataSource->fromFile.fp);
pDataSource->fromFile.fp = nil;
}
break;
case kNuDataSourceFromBuffer:
if (pDataSource->common.doClose) {
Nu_Free(nil, (char*)pDataSource->fromBuffer.buffer);
pDataSource->fromBuffer.buffer = nil;
}
break;
case kNuDataSourceUnknown:
break;
default:
Assert(0);
return kNuErrInternal;
}
Nu_Free(nil, pDataSource);
return kNuErrNone;
}
/*
* Create a data source for an unopened file.
*/
NuError
Nu_DataSourceFile_New(NuThreadFormat threadFormat, Boolean doClose,
ulong otherLen, const char* pathname, Boolean isFromRsrcFork,
NuDataSource** ppDataSource)
{
NuError err;
if (!(doClose == true || doClose == false) ||
pathname == nil ||
!(isFromRsrcFork == true || isFromRsrcFork == false) ||
ppDataSource == nil)
{
return kNuErrInvalidArg;
}
err = Nu_DataSourceNew(ppDataSource);
BailErrorQuiet(err);
(*ppDataSource)->common.sourceType = kNuDataSourceFromFile;
(*ppDataSource)->common.threadFormat = threadFormat;
(*ppDataSource)->common.doClose = doClose;
(*ppDataSource)->common.otherLen = otherLen;
(*ppDataSource)->fromFile.pathname = strdup(pathname);
(*ppDataSource)->fromFile.fromRsrcFork = isFromRsrcFork;
(*ppDataSource)->common.dataLen = 0; /* to be filled in later */
(*ppDataSource)->fromFile.fp = nil; /* to be filled in later */
bail:
return err;
}
/*
* Create a data source for an open file at a specific offset. The FILE*
* must be seekable.
*/
NuError
Nu_DataSourceFP_New(NuThreadFormat threadFormat, Boolean doClose,
ulong otherLen, FILE* fp, long offset, long length,
NuDataSource** ppDataSource)
{
NuError err;
if (!(doClose == true || doClose == false) ||
fp == nil || offset < 0 || length < 0 ||
ppDataSource == nil)
{
return kNuErrInvalidArg;
}
if (otherLen && otherLen < (ulong)length) {
DBUG(("--- rejecting FP len=%ld other=%ld\n", length, otherLen));
err = kNuErrPreSizeOverflow;
goto bail;
}
err = Nu_DataSourceNew(ppDataSource);
BailErrorQuiet(err);
(*ppDataSource)->common.sourceType = kNuDataSourceFromFP;
(*ppDataSource)->common.threadFormat = threadFormat;
(*ppDataSource)->common.doClose = doClose;
(*ppDataSource)->common.dataLen = length;
(*ppDataSource)->common.otherLen = otherLen;
(*ppDataSource)->fromFP.fp = fp;
(*ppDataSource)->fromFP.offset = offset;
bail:
return err;
}
/*
* Create a data source for a buffer.
*
* We allow "buffer" to be nil so long as "offset" and "length" are also
* nil. This is useful for creating empty pre-sized buffers, such as
* blank comment fields.
*/
NuError
Nu_DataSourceBuffer_New(NuThreadFormat threadFormat, Boolean doClose,
ulong otherLen, const uchar* buffer, long offset, long length,
NuDataSource** ppDataSource)
{
NuError err;
if (!(doClose == true || doClose == false) ||
offset < 0 || length < 0 || ppDataSource == nil)
{
return kNuErrInvalidArg;
}
if (buffer == nil && (offset != 0 || length != 0))
{
return kNuErrInvalidArg;
}
if (buffer == nil)
doClose = false;
if (otherLen && otherLen < (ulong)length) {
DBUG(("--- rejecting buffer len=%ld other=%ld\n", length, otherLen));
err = kNuErrPreSizeOverflow;
goto bail;
}
err = Nu_DataSourceNew(ppDataSource);
BailErrorQuiet(err);
(*ppDataSource)->common.sourceType = kNuDataSourceFromBuffer;
(*ppDataSource)->common.threadFormat = threadFormat;
(*ppDataSource)->common.doClose = doClose;
(*ppDataSource)->common.dataLen = length;
(*ppDataSource)->common.otherLen = otherLen;
(*ppDataSource)->fromBuffer.buffer = buffer;
(*ppDataSource)->fromBuffer.offset = offset;
(*ppDataSource)->fromBuffer.curOffset = offset;
(*ppDataSource)->fromBuffer.curDataLen = length;
bail:
return err;
}
/*
* Get the type of a NuDataSource.
*/
NuDataSourceType
Nu_DataSourceGetType(const NuDataSource* pDataSource)
{
Assert(pDataSource != nil);
return pDataSource->sourceType;
}
/*
* Get the threadFormat for a data source.
*/
NuThreadFormat
Nu_DataSourceGetThreadFormat(const NuDataSource* pDataSource)
{
Assert(pDataSource != nil);
return pDataSource->common.threadFormat;
}
/*
* Get "dataLen" from a dataSource.
*/
ulong
Nu_DataSourceGetDataLen(const NuDataSource* pDataSource)
{
Assert(pDataSource != nil);
if (pDataSource->sourceType == kNuDataSourceFromFile) {
/* dataLen can only be valid if file has been opened */
Assert(pDataSource->fromFile.fp != nil);
}
return pDataSource->common.dataLen;
}
/*
* Get "otherLen" from a dataSource.
*/
ulong
Nu_DataSourceGetOtherLen(const NuDataSource* pDataSource)
{
Assert(pDataSource != nil);
return pDataSource->common.otherLen;
}
/*
* Change the "otherLen" value.
*/
void
Nu_DataSourceSetOtherLen(NuDataSource* pDataSource, long otherLen)
{
Assert(pDataSource != nil && otherLen > 0);
pDataSource->common.otherLen = otherLen;
}
/*
* Get the "raw CRC" value.
*/
ushort
Nu_DataSourceGetRawCrc(const NuDataSource* pDataSource)
{
Assert(pDataSource != nil);
return pDataSource->common.rawCrc;
}
/*
* Set the "raw CRC" value. You would want to do this if the input was
* already-compressed data, and you wanted to propagate the thread CRC.
*/
void
Nu_DataSourceSetRawCrc(NuDataSource* pDataSource, ushort crc)
{
Assert(pDataSource != nil);
pDataSource->common.rawCrc = crc;
}
/*
* Prepare a data source for action.
*/
NuError
Nu_DataSourcePrepareInput(NuArchive* pArchive, NuDataSource* pDataSource)
{
NuError err = kNuErrNone;
FILE* fileFp = nil;
/*
* Doesn't apply to buffer sources.
*/
if (Nu_DataSourceGetType(pDataSource) == kNuDataSourceFromBuffer)
goto bail;
/*
* FP sources can be used several times, so we need to seek them
* to the correct offset before we begin.
*/
if (Nu_DataSourceGetType(pDataSource) == kNuDataSourceFromFP) {
err = Nu_FSeek(pDataSource->fromFP.fp, pDataSource->fromFP.offset,
SEEK_SET);
goto bail; /* return this err */
}
/*
* We're adding from a file on disk. Open it.
*/
err = Nu_OpenInputFile(pArchive,
pDataSource->fromFile.pathname, pDataSource->fromFile.fromRsrcFork,
&fileFp);
BailError(err);
Assert(fileFp != nil);
pDataSource->fromFile.fp = fileFp;
err = Nu_GetFileLength(pArchive, fileFp,
(long*)&pDataSource->common.dataLen);
BailError(err);
if (pDataSource->common.otherLen &&
pDataSource->common.otherLen < pDataSource->common.dataLen)
{
DBUG(("--- Uh oh, looks like file len is too small for presized\n"));
}
bail:
return err;
}
/*
* Un-prepare a data source. This really only affects "file" sources, and
* is only here so we don't end up with 200+ FILE* structures hanging around.
* If we don't do this, the first resource we're likely to run out of is
* file descriptors.
*
* It's not necessary to do this in all error cases -- the DataSource "Free"
* call will take care of this eventually -- but for normal operation on
* a large number of files, it's vital.
*/
void
Nu_DataSourceUnPrepareInput(NuArchive* pArchive, NuDataSource* pDataSource)
{
if (Nu_DataSourceGetType(pDataSource) != kNuDataSourceFromFile)
return;
if (pDataSource->fromFile.fp != nil) {
fclose(pDataSource->fromFile.fp);
pDataSource->fromFile.fp = nil;
pDataSource->common.dataLen = 0;
}
}
/*
* Get the pathname from a "from-file" dataSource.
*/
const char*
Nu_DataSourceFile_GetPathname(NuDataSource* pDataSource)
{
Assert(pDataSource != nil);
Assert(pDataSource->sourceType == kNuDataSourceFromFile);
Assert(pDataSource->fromFile.pathname != nil);
return pDataSource->fromFile.pathname;
}
/*
* Read a block of data from a dataSource.
*/
NuError
Nu_DataSourceGetBlock(NuDataSource* pDataSource, uchar* buf, ulong len)
{
NuError err;
Assert(pDataSource != nil);
Assert(buf != nil);
Assert(len > 0);
switch (pDataSource->sourceType) {
case kNuDataSourceFromFile:
Assert(pDataSource->fromFile.fp != nil);
err = Nu_FRead(pDataSource->fromFile.fp, buf, len);
if (feof(pDataSource->fromFile.fp))
Nu_ReportError(NU_NILBLOB, err, "EOF hit unexpectedly");
return err;
case kNuDataSourceFromFP:
err = Nu_FRead(pDataSource->fromFP.fp, buf, len);
if (feof(pDataSource->fromFP.fp))
Nu_ReportError(NU_NILBLOB, err, "EOF hit unexpectedly");
return err;
case kNuDataSourceFromBuffer:
if ((long)len > pDataSource->fromBuffer.curDataLen) {
/* buffer underrun */
return kNuErrBufferUnderrun;
}
memcpy(buf,
pDataSource->fromBuffer.buffer + pDataSource->fromBuffer.curOffset,
len);
pDataSource->fromBuffer.curOffset += len;
pDataSource->fromBuffer.curDataLen -= len;
return kNuErrNone;
default:
Assert(false);
return kNuErrInternal;
}
}
/*
* Rewind a data source to the start of its input.
*/
NuError
Nu_DataSourceRewind(NuDataSource* pDataSource)
{
NuError err;
Assert(pDataSource != nil);
switch (pDataSource->sourceType) {
case kNuDataSourceFromFile:
Assert(pDataSource->fromFile.fp != nil);
err = Nu_FSeek(pDataSource->fromFile.fp, 0, SEEK_SET);
break; /* fall through with error */
case kNuDataSourceFromFP:
err = Nu_FSeek(pDataSource->fromFP.fp, pDataSource->fromFP.offset,
SEEK_SET);
break; /* fall through with error */
case kNuDataSourceFromBuffer:
pDataSource->fromBuffer.curOffset = pDataSource->fromBuffer.offset;
pDataSource->fromBuffer.curDataLen = pDataSource->common.dataLen;
err = kNuErrNone;
break;
default:
Assert(false);
err = kNuErrInternal;
}
return err;
}
/*
* ===========================================================================
* NuDataSink
* ===========================================================================
*/
/*
* Allocate a new DataSink structure.
*/
static NuError
Nu_DataSinkNew(NuDataSink** ppDataSink)
{
Assert(ppDataSink != nil);
*ppDataSink = Nu_Malloc(nil, sizeof(**ppDataSink));
if (*ppDataSink == nil)
return kNuErrMalloc;
(*ppDataSink)->sinkType = kNuDataSinkUnknown;
return kNuErrNone;
}
/*
* Free a data sink structure, and any type-specific elements.
*/
NuError
Nu_DataSinkFree(NuDataSink* pDataSink)
{
if (pDataSink == nil)
return kNuErrNone;
switch (pDataSink->sinkType) {
case kNuDataSinkToFile:
Nu_DataSinkFile_Close(pDataSink);
Nu_Free(nil, pDataSink->toFile.pathname);
break;
case kNuDataSinkToFP:
break;
case kNuDataSinkToBuffer:
break;
case kNuDataSinkToVoid:
break;
case kNuDataSinkUnknown:
break;
default:
Assert(0);
return kNuErrInternal;
}
Nu_Free(nil, pDataSink);
return kNuErrNone;
}
/*
* Create a data sink for an unopened file.
*/
NuError
Nu_DataSinkFile_New(Boolean doExpand, NuValue convertEOL, const char* pathname,
char fssep, NuDataSink** ppDataSink)
{
NuError err;
if ((doExpand != true && doExpand != false) ||
(convertEOL != kNuConvertOff && convertEOL != kNuConvertOn &&
convertEOL != kNuConvertAuto) ||
pathname == nil ||
fssep == 0 ||
ppDataSink == nil)
{
return kNuErrInvalidArg;
}
err = Nu_DataSinkNew(ppDataSink);
BailErrorQuiet(err);
(*ppDataSink)->common.sinkType = kNuDataSinkToFile;
(*ppDataSink)->common.doExpand = doExpand;
if (doExpand)
(*ppDataSink)->common.convertEOL = convertEOL;
else
(*ppDataSink)->common.convertEOL = kNuConvertOff;
(*ppDataSink)->common.outCount = 0;
(*ppDataSink)->toFile.pathname = strdup(pathname);
(*ppDataSink)->toFile.fssep = fssep;
(*ppDataSink)->toFile.fp = nil;
bail:
return err;
}
/*
* Create a data sink based on a file pointer.
*/
NuError
Nu_DataSinkFP_New(Boolean doExpand, NuValue convertEOL, FILE* fp,
NuDataSink** ppDataSink)
{
NuError err;
if ((doExpand != true && doExpand != false) ||
(convertEOL != kNuConvertOff && convertEOL != kNuConvertOn &&
convertEOL != kNuConvertAuto) ||
fp == nil ||
ppDataSink == nil)
{
return kNuErrInvalidArg;
}
err = Nu_DataSinkNew(ppDataSink);
BailErrorQuiet(err);
(*ppDataSink)->common.sinkType = kNuDataSinkToFP;
(*ppDataSink)->common.doExpand = doExpand;
if (doExpand)
(*ppDataSink)->common.convertEOL = convertEOL;
else
(*ppDataSink)->common.convertEOL = kNuConvertOff;
(*ppDataSink)->common.outCount = 0;
(*ppDataSink)->toFP.fp = fp;
bail:
return err;
}
/*
* Create a data sink for a buffer in memory.
*/
NuError
Nu_DataSinkBuffer_New(Boolean doExpand, NuValue convertEOL, uchar* buffer,
ulong bufLen, NuDataSink** ppDataSink)
{
NuError err;
if ((doExpand != true && doExpand != false) ||
(convertEOL != kNuConvertOff && convertEOL != kNuConvertOn &&
convertEOL != kNuConvertAuto) ||
buffer == nil ||
bufLen == 0 ||
ppDataSink == nil)
{
return kNuErrInvalidArg;
}
err = Nu_DataSinkNew(ppDataSink);
BailErrorQuiet(err);
(*ppDataSink)->common.sinkType = kNuDataSinkToBuffer;
(*ppDataSink)->common.doExpand = doExpand;
if (doExpand)
(*ppDataSink)->common.convertEOL = convertEOL;
else
(*ppDataSink)->common.convertEOL = kNuConvertOff;
(*ppDataSink)->common.convertEOL = convertEOL;
(*ppDataSink)->common.outCount = 0;
(*ppDataSink)->toBuffer.buffer = buffer;
(*ppDataSink)->toBuffer.bufLen = bufLen;
(*ppDataSink)->toBuffer.stickyErr = kNuErrNone;
bail:
return err;
}
/*
* Create a data sink that goes nowhere.
*/
NuError
Nu_DataSinkVoid_New(Boolean doExpand, NuValue convertEOL,
NuDataSink** ppDataSink)
{
NuError err;
Assert(doExpand == true || doExpand == false);
Assert(ppDataSink != nil);
err = Nu_DataSinkNew(ppDataSink);
BailErrorQuiet(err);
(*ppDataSink)->common.sinkType = kNuDataSinkToVoid;
(*ppDataSink)->common.doExpand = doExpand;
(*ppDataSink)->common.convertEOL = convertEOL;
(*ppDataSink)->common.outCount = 0;
bail:
return err;
}
/*
* Get the type of a NuDataSink.
*/
NuDataSinkType
Nu_DataSinkGetType(const NuDataSink* pDataSink)
{
Assert(pDataSink != nil);
return pDataSink->sinkType;
}
/*
* Return the "doExpand" parameter from any kind of sink.
*/
Boolean
Nu_DataSinkGetDoExpand(const NuDataSink* pDataSink)
{
return pDataSink->common.doExpand;
}
/*
* Return the "convertEOL" parameter from any kind of sink.
*/
NuValue
Nu_DataSinkGetConvertEOL(const NuDataSink* pDataSink)
{
return pDataSink->common.convertEOL;
}
/*
* Return the #of bytes written to the sink.
*/
ulong
Nu_DataSinkGetOutCount(const NuDataSink* pDataSink)
{
return pDataSink->common.outCount;
}
/*
* Get "pathname" from a to-file sink.
*/
const char*
Nu_DataSinkFile_GetPathname(const NuDataSink* pDataSink)
{
Assert(pDataSink != nil);
Assert(pDataSink->sinkType == kNuDataSinkToFile);
return pDataSink->toFile.pathname;
}
/*
* Get "fssep" from a to-file sink.
*/
char
Nu_DataSinkFile_GetFssep(const NuDataSink* pDataSink)
{
Assert(pDataSink != nil);
Assert(pDataSink->sinkType == kNuDataSinkToFile);
return pDataSink->toFile.fssep;
}
/*
* Get the "fp" for a file sink.
*/
FILE*
Nu_DataSinkFile_GetFP(const NuDataSink* pDataSink)
{
Assert(pDataSink != nil);
Assert(pDataSink->sinkType == kNuDataSinkToFile);
return pDataSink->toFile.fp;
}
/*
* Set the "fp" for a file sink.
*/
void
Nu_DataSinkFile_SetFP(NuDataSink* pDataSink, FILE* fp)
{
Assert(pDataSink != nil);
Assert(pDataSink->sinkType == kNuDataSinkToFile);
pDataSink->toFile.fp = fp;
}
/*
* Close a to-file sink.
*/
void
Nu_DataSinkFile_Close(NuDataSink* pDataSink)
{
Assert(pDataSink != nil);
if (pDataSink->toFile.fp != nil) {
fclose(pDataSink->toFile.fp);
pDataSink->toFile.fp = nil;
}
}
/*
* Write a block of data to a DataSink.
*/
NuError
Nu_DataSinkPutBlock(NuDataSink* pDataSink, const uchar* buf, ulong len)
{
NuError err;
Assert(pDataSink != nil);
Assert(buf != nil);
Assert(len > 0);
switch (pDataSink->sinkType) {
case kNuDataSinkToFile:
err = Nu_FWrite(pDataSink->toFile.fp, buf, len);
if (err != kNuErrNone)
return err;
break;
case kNuDataSinkToFP:
err = Nu_FWrite(pDataSink->toFP.fp, buf, len);
if (err != kNuErrNone)
return err;
break;
case kNuDataSinkToBuffer:
if (len > pDataSink->toBuffer.bufLen) {
/* buffer overrun; set a "sticky" error, like FILE* does */
err = kNuErrBufferOverrun;
pDataSink->toBuffer.stickyErr = err;
return err;
}
memcpy(pDataSink->toBuffer.buffer, buf, len);
pDataSink->toBuffer.buffer += len;
pDataSink->toBuffer.bufLen -= len;
break;
case kNuDataSinkToVoid:
/* do nothing */
break;
default:
Assert(false);
return kNuErrInternal;
}
pDataSink->common.outCount += len;
return kNuErrNone;
}
/*
* Figure out if one of our earlier writes has failed.
*/
NuError
Nu_DataSinkGetError(NuDataSink* pDataSink)
{
NuError err = kNuErrNone;
Assert(pDataSink != nil);
switch (pDataSink->sinkType) {
case kNuDataSinkToFile:
if (ferror(pDataSink->toFile.fp))
err = kNuErrFileWrite;
break;
case kNuDataSinkToFP:
if (ferror(pDataSink->toFP.fp))
err = kNuErrFileWrite;
break;
case kNuDataSinkToBuffer:
err = pDataSink->toBuffer.stickyErr;
break;
case kNuDataSinkToVoid:
/* do nothing */
break;
default:
Assert(false);
err = kNuErrInternal;
break;
}
return err;
}

47
nufxlib-0/SunOS4.h Normal file
View File

@ -0,0 +1,47 @@
/*
* NuFX archive manipulation library
* Copyright (C) 2000 by Andy McFadden, All Rights Reserved.
* This is free software; you can redistribute it and/or modify it under the
* terms of the GNU Library General Public License, see the file COPYING.LIB.
*
* This file was adapted from Devin Reade's "sunos4.h" in NuLib 3.2.5.
* It is provided for compilation under SunOS 4.x, when an ANSI compiler
* (such as gcc) is used. The system header files aren't quite sufficient
* to eliminate hordes of warnings.
*/
#ifndef __SunOS4__
#define __SunOS4__
#ifdef __GNUC__
extern int _flsbuf(int, FILE*);
extern int _filbuf(FILE*);
#endif
extern void bcopy(char*, char*, int);
extern int fclose(FILE*);
extern int fflush(FILE*);
extern int fprintf(FILE*, const char*, ...);
extern int fread(char*, int, int, FILE *);
extern int fseek(FILE*, long, int);
extern int ftruncate(int, off_t);
extern int fwrite(const char*, int, int, FILE*);
extern char* mktemp(char *template);
extern time_t mktime(struct tm*);
extern int perror(const char*);
extern int printf(const char*, ...);
extern int remove(const char*);
extern int rename(const char*, const char*);
extern int tolower(int);
extern int setvbuf(FILE*, char*, int, int);
extern int sscanf(char*, const char*, ...);
extern int strcasecmp(const char*, const char*);
extern int strncasecmp(const char*, const char*, size_t);
extern long strtol(const char *, char **, int);
extern int system(const char*);
extern time_t timelocal(struct tm*);
extern time_t time(time_t*);
extern int toupper(int);
extern int vfprintf(FILE*, const char *, va_list);
extern char* vsprintf(char *str, const char *format, va_list ap);
#endif /*__SunOS4__*/

138
nufxlib-0/SysDefs.h Normal file
View File

@ -0,0 +1,138 @@
/*
* NuFX archive manipulation library
* Copyright (C) 2000 by Andy McFadden, All Rights Reserved.
* This is free software; you can redistribute it and/or modify it under the
* terms of the GNU Library General Public License, see the file COPYING.LIB.
*
* External type definitions and function prototypes.
*/
#ifndef __SysDefs__
#define __SysDefs__
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#ifdef DEBUG_VERBOSE
# define DEBUG_MSGS
#endif
/* these should exist everywhere */
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include <memory.h>
#include <time.h>
#include <errno.h>
#include <assert.h>
/* basic Win32 stuff -- info-zip has much more complete defs */
#if defined(WIN32) || defined(MSDOS)
# define WINDOWS_LIKE
# ifndef HAVE_CONFIG_H
# define HAVE_FCNTL_H
# define HAVE_MALLOC_H
# define HAVE_STDLIB_H
# define HAVE_SYS_STAT_H
# undef HAVE_SYS_TIME_H
# define HAVE_SYS_TYPES_H
# undef HAVE_UNISTD_H
# undef HAVE_UTIME_H
# define HAVE_SYS_UTIME_H
# define HAVE_WINDOWS_H
# define HAVE_FDOPEN
# undef HAVE_FTRUNCATE
# define HAVE_MEMMOVE
# undef HAVE_MKSTEMP
# define HAVE_MKTIME
# define HAVE_SNPRINTF
# undef HAVE_STRCASECMP
# undef HAVE_STRNCASECMP
# define HAVE_STRERROR
# define HAVE_STRTOUL
# define HAVE_VSNPRINTF
# define SNPRINTF_DECLARED
# define VSNPRINTF_DECLARED
# define SPRINTF_RETURNS_INT
# define uchar unsigned char
# define ushort unsigned short
# define uint unsigned int
# define ulong unsigned long
# define inline /*Visual C++6.0 can't inline ".c" files*/
# define mode_t int
# endif
# include <io.h>
# include <direct.h>
# define FOPEN_WANTS_B
# define HAVE_CHSIZE
# define snprintf _snprintf
# define vsnprintf _vsnprintf
#endif
#ifdef HAVE_MALLOC_H
# include <malloc.h>
#endif
#ifdef HAVE_STDLIB_H
# include <stdlib.h>
#endif
#ifdef HAVE_SYS_STAT_H
# include <sys/stat.h>
#endif
#ifdef HAVE_SYS_TIME_H
# include <sys/time.h>
#endif
#ifdef HAVE_SYS_TYPES_H
# include <sys/types.h>
#endif
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif
#ifdef HAVE_UTIME_H
# include <utime.h>
#endif
#ifdef HAVE_SYS_UTIME_H
# include <sys/utime.h>
#endif
#if defined(WINDOWS_LIKE)
# ifndef F_OK
# define F_OK 02
# endif
#endif
#if defined(__unix__) || defined(__unix) || defined(__BEOS__) || \
defined(__hpux) || defined(_AIX) || defined(__APPLE__)
# define UNIX_LIKE /* standardize */
#endif
#if defined(UNIX_LIKE)
# ifdef USE_REENTRANT_CALLS
# define _REENTRANT /* Solaris 2.x convention */
# endif
#endif
#ifdef MAC
# define HAS_RESOURCE_FORKS
#endif
#ifdef __ORCAC__
# define HAS_RESOURCE_FORKS
#endif
/* __FUNCTION__ was missing from BeOS __MWERKS__, and might be gcc-only */
#ifdef __GNUC__
# define HAS__FUNCTION__
#endif
#if defined(__sun__) && !defined(__SVR4)
# include "SunOS4.h"
#endif
#if defined(__linux__)
# define HAS_MALLOC_CHECK_
#endif
#endif /*__SysDefs__*/

1278
nufxlib-0/Thread.c Normal file

File diff suppressed because it is too large Load Diff

231
nufxlib-0/Value.c Normal file
View File

@ -0,0 +1,231 @@
/*
* NuFX archive manipulation library
* Copyright (C) 2000 by Andy McFadden, All Rights Reserved.
* This is free software; you can redistribute it and/or modify it under the
* terms of the GNU Library General Public License, see the file COPYING.LIB.
*
* Get/set certain values and attributes.
*/
#include "NufxLibPriv.h"
/*
* Get a configurable parameter.
*/
NuError
Nu_GetValue(NuArchive* pArchive, NuValueID ident, NuValue* pValue)
{
NuError err = kNuErrNone;
if (pValue == nil)
return kNuErrInvalidArg;
switch (ident) {
case kNuValueAllowDuplicates:
*pValue = pArchive->valAllowDuplicates;
break;
case kNuValueConvertExtractedEOL:
*pValue = pArchive->valConvertExtractedEOL;
break;
case kNuValueDataCompression:
*pValue = pArchive->valDataCompression;
break;
case kNuValueDiscardWrapper:
*pValue = pArchive->valDiscardWrapper;
break;
case kNuValueEOL:
*pValue = pArchive->valEOL;
break;
case kNuValueHandleExisting:
*pValue = pArchive->valHandleExisting;
break;
case kNuValueIgnoreCRC:
*pValue = pArchive->valIgnoreCRC;
break;
case kNuValueMimicSHK:
*pValue = pArchive->valMimicSHK;
break;
case kNuValueModifyOrig:
*pValue = pArchive->valModifyOrig;
break;
case kNuValueOnlyUpdateOlder:
*pValue = pArchive->valOnlyUpdateOlder;
break;
default:
err = kNuErrInvalidArg;
Nu_ReportError(NU_BLOB, err, "Unknown ValueID %d requested", ident);
goto bail;
}
bail:
return err;
}
/*
* Set a configurable parameter.
*/
NuError
Nu_SetValue(NuArchive* pArchive, NuValueID ident, NuValue value)
{
NuError err = kNuErrInvalidArg;
switch (ident) {
case kNuValueAllowDuplicates:
if (value != true && value != false) {
Nu_ReportError(NU_BLOB, err,
"Invalid kNuValueAllowDuplicates value %ld\n", value);
goto bail;
}
pArchive->valAllowDuplicates = value;
break;
case kNuValueConvertExtractedEOL:
if (value < kNuConvertOff || value > kNuConvertAuto) {
Nu_ReportError(NU_BLOB, err,
"Invalid kNuValueConvertExtractedEOL value %ld\n", value);
goto bail;
}
pArchive->valConvertExtractedEOL = value;
break;
case kNuValueDataCompression:
if (value < kNuCompressNone || value > kNuCompressLZC16) {
Nu_ReportError(NU_BLOB, err,
"Invalid kNuValueDataCompression value %ld\n", value);
goto bail;
}
pArchive->valDataCompression = value;
break;
case kNuValueDiscardWrapper:
if (value != true && value != false) {
Nu_ReportError(NU_BLOB, err,
"Invalid kNuValueDiscardWrapper value %ld\n", value);
goto bail;
}
pArchive->valDiscardWrapper = value;
break;
case kNuValueEOL:
if (value < kNuEOLUnknown || value > kNuEOLCRLF) {
Nu_ReportError(NU_BLOB, err,
"Invalid kNuValueEOL value %ld\n", value);
goto bail;
}
pArchive->valEOL = value;
break;
case kNuValueHandleExisting:
if (value < kNuMaybeOverwrite || value > kNuMustOverwrite) {
Nu_ReportError(NU_BLOB, err,
"Invalid kNuValueHandleExisting value %ld\n", value);
goto bail;
}
pArchive->valHandleExisting = value;
break;
case kNuValueIgnoreCRC:
if (value != true && value != false) {
Nu_ReportError(NU_BLOB, err,
"Invalid kNuValueIgnoreCRC value %ld\n", value);
goto bail;
}
pArchive->valIgnoreCRC = value;
break;
case kNuValueMimicSHK:
if (value != true && value != false) {
Nu_ReportError(NU_BLOB, err,
"Invalid kNuValueMimicSHK value %ld\n", value);
goto bail;
}
pArchive->valMimicSHK = value;
break;
case kNuValueModifyOrig:
if (value != true && value != false) {
Nu_ReportError(NU_BLOB, err,
"Invalid kNuValueModifyOrig value %ld\n", value);
goto bail;
}
pArchive->valModifyOrig = value;
break;
case kNuValueOnlyUpdateOlder:
if (value != true && value != false) {
Nu_ReportError(NU_BLOB, err,
"Invalid kNuValueOnlyUpdateOlder value %ld\n", value);
goto bail;
}
pArchive->valOnlyUpdateOlder = value;
break;
default:
Nu_ReportError(NU_BLOB, err, "Unknown ValueID %d requested", ident);
goto bail;
}
err = kNuErrNone;
bail:
return err;
}
/*
* Get an archive attribute. These are things that you would have to
* pry into pArchive to get at (like the archive type) or get the master
* header (like the number of records).
*/
NuError
Nu_GetAttr(NuArchive* pArchive, NuAttrID ident, NuAttr* pAttr)
{
NuError err = kNuErrNone;
if (pAttr == nil)
return kNuErrInvalidArg;
switch (ident) {
case kNuAttrArchiveType:
*pAttr = pArchive->archiveType;
break;
case kNuAttrNumRecords:
*pAttr = pArchive->masterHeader.mhTotalRecords;
break;
default:
err = kNuErrInvalidArg;
Nu_ReportError(NU_BLOB, err, "Unknown AttrID %d requested", ident);
goto bail;
}
bail:
return err;
}
/*
* Convert a NuValue compression type to a "phyiscal" ThreadFormat.
*
* Unsupported compression types cause a warning to be flagged.
*/
NuThreadFormat
Nu_ConvertCompressValToFormat(NuArchive* pArchive, NuValue compValue)
{
NuThreadFormat threadFormat;
Boolean unsup = false;
switch (compValue) {
case kNuCompressNone: threadFormat = kNuThreadFormatUncompressed; break;
case kNuCompressLZW1: threadFormat = kNuThreadFormatLZW1; break;
case kNuCompressLZW2: threadFormat = kNuThreadFormatLZW2; break;
case kNuCompressSQ: threadFormat = kNuThreadFormatHuffmanSQ; break;
case kNuCompressLZC12: threadFormat = kNuThreadFormatLZC12;
unsup = true; break;
case kNuCompressLZC16: threadFormat = kNuThreadFormatLZC16;
unsup = true; break;
default:
Assert(false);
Nu_ReportError(NU_BLOB, kNuErrInvalidArg,
"Unknown compress value %ld", compValue);
return kNuThreadFormatUncompressed;
}
if (unsup) {
Nu_ReportError(NU_BLOB, kNuErrNone,
"Unsupported compression type 0x%04x requested (%ld)",
threadFormat, compValue);
return kNuThreadFormatUncompressed;
}
return threadFormat;
}

42
nufxlib-0/Version.c.in Normal file
View File

@ -0,0 +1,42 @@
/*
* NuFX archive manipulation library
* Copyright (C) 2000 by Andy McFadden, All Rights Reserved.
* This is free software; you can redistribute it and/or modify it under the
* terms of the GNU Library General Public License, see the file COPYING.LIB.
*
* !!! NOTE !!!
* The file Version.c is automatically generated from Version.c.in. Don't
* edit the .c file if you want your changes to be permanent.
*/
#include "NufxLibPriv.h"
/* version number; edit by hand */
static const long gNuMajorVersion = 1;
static const long gNuMinorVersion = 0;
static const long gNuBugVersion = 0;
/* executable was build on or after this date (inserted automatically) */
static const char gNuBuildDate[] = "BUILT"; /* approximate */
static const char gNuBuildFlags[] = "OPTFLAGS";
/*
* Return the version number, date built, and build flags.
*/
NuError
Nu_GetVersion(long* pMajorVersion, long* pMinorVersion, long* pBugVersion,
const char** ppBuildDate, const char** ppBuildFlags)
{
if (pMajorVersion != nil)
*pMajorVersion = gNuMajorVersion;
if (pMinorVersion != nil)
*pMinorVersion = gNuMinorVersion;
if (pBugVersion != nil)
*pBugVersion = gNuBugVersion;
if (ppBuildDate != nil)
*ppBuildDate = gNuBuildDate;
if (ppBuildFlags != nil)
*ppBuildFlags = gNuBuildFlags;
return kNuErrNone;
}

1088
nufxlib-0/config.guess vendored Normal file

File diff suppressed because it is too large Load Diff

125
nufxlib-0/config.h.in Normal file
View File

@ -0,0 +1,125 @@
/* config.h.in. Generated automatically from configure.in by autoheader. */
/* Define to empty if the keyword does not work. */
#undef const
/* Define to empty if the keyword does not work. */
#undef inline
/* Define to `int' if <sys/types.h> doesn't define. */
#undef mode_t
/* Define to `long' if <sys/types.h> doesn't define. */
#undef off_t
/* Define to `unsigned' if <sys/types.h> doesn't define. */
#undef size_t
/* Define if you have the ANSI C header files. */
#undef STDC_HEADERS
/* Define if your <sys/time.h> declares struct tm. */
#undef TM_IN_SYS_TIME
/* Define to `unsigned char' if <sys/types.h> doesn't define. */
#undef uchar
/* Define to `unsigned short' if <sys/types.h> doesn't define. */
#undef ushort
/* Define to `unsigned int' if <sys/types.h> doesn't define. */
#undef uint
/* Define to `unsigned long' if <sys/types.h> doesn't define. */
#undef ulong
/* Define to `int' if <sys/types.h> doesn't define. */
#undef mode_t
/* Define to `long' if <sys/types.h> doesn't define. */
#undef off_t
/* Define to `unsigned' if <sys/types.h> doesn't define. */
#undef size_t
/* Define if you have the fdopen function. */
#undef HAVE_FDOPEN
/* Define if you have the ftruncate function. */
#undef HAVE_FTRUNCATE
/* Define if you have the localtime_r function. */
#undef HAVE_LOCALTIME_R
/* Define if you have the memmove function. */
#undef HAVE_MEMMOVE
/* Define if you have the mkdir function. */
#undef HAVE_MKDIR
/* Define if you have the mkstemp function. */
#undef HAVE_MKSTEMP
/* Define if you have the mktime function. */
#undef HAVE_MKTIME
/* Define if you have the snprintf function. */
#undef HAVE_SNPRINTF
/* Define if you have the strcasecmp function. */
#undef HAVE_STRCASECMP
/* Define if you have the strncasecmp function. */
#undef HAVE_STRNCASECMP
/* Define if you have the strerror function. */
#undef HAVE_STRERROR
/* Define if you have the strtoul function. */
#undef HAVE_STRTOUL
/* Define if you have the timelocal function. */
#undef HAVE_TIMELOCAL
/* Define if you have the vsnprintf function. */
#undef HAVE_VSNPRINTF
/* Define if you have the <fcntl.h> header file. */
#undef HAVE_FCNTL_H
/* Define if you have the <malloc.h> header file. */
#undef HAVE_MALLOC_H
/* Define if you have the <stdlib.h> header file. */
#undef HAVE_STDLIB_H
/* Define if you have the <sys/time.h> header file. */
#undef HAVE_SYS_STAT_H
/* Define if you have the <sys/time.h> header file. */
#undef HAVE_SYS_TIME_H
/* Define if you have the <sys/types.h> header file. */
#undef HAVE_SYS_TYPES_H
/* Define if you have the <sys/utime.h> header file. */
#undef HAVE_SYS_UTIME_H
/* Define if you have the <unistd.h> header file. */
#undef HAVE_UNISTD_H
/* Define if you have the <utime.h> header file. */
#undef HAVE_UTIME_H
/* Define if sprintf returns an int. */
#undef SPRINTF_RETURNS_INT
/* Define if SNPRINTF is declared in stdio.h. */
#undef SNPRINTF_DECLARED
/* Define if VSNPRINTF is declared in stdio.h. */
#undef VSNPRINTF_DECLARED
/* Define if we want to use the dmalloc library (--enable-dmalloc). */
#undef USE_DMALLOC

1220
nufxlib-0/config.sub vendored Normal file

File diff suppressed because it is too large Load Diff

2115
nufxlib-0/configure vendored Executable file

File diff suppressed because it is too large Load Diff

136
nufxlib-0/configure.in Normal file
View File

@ -0,0 +1,136 @@
dnl Process this file with autoconf to produce a configure script.
AC_INIT(NufxLibPriv.h)
AC_CONFIG_HEADER(config.h)
dnl Checks for programs.
AC_CANONICAL_HOST
dnl AC_PROG_AWK
AC_PROG_CC
AC_PROG_INSTALL
AC_PROG_MAKE_SET
AC_PROG_RANLIB
dnl Checks for libraries.
dnl Checks for header files.
AC_CHECK_HEADERS(fcntl.h malloc.h stdlib.h sys/stat.h sys/time.h sys/types.h \
sys/utime.h unistd.h utime.h)
dnl Checks for typedefs, structures, and compiler characteristics.
AC_C_CONST
AC_C_INLINE
AC_TYPE_MODE_T
AC_TYPE_OFF_T
AC_TYPE_SIZE_T
AC_STRUCT_TM
AC_CHECK_TYPE(uchar, unsigned char)
AC_CHECK_TYPE(ushort, unsigned short)
AC_CHECK_TYPE(uint, unsigned int)
AC_CHECK_TYPE(ulong, unsigned long)
dnl Checks for library functions.
AC_CHECK_FUNCS(fdopen ftruncate memmove mkdir mkstemp mktime timelocal \
localtime_r snprintf strcasecmp strncasecmp strtoul strerror vsnprintf)
dnl Kent says: snprintf doesn't always have a declaration
AC_MSG_CHECKING(if snprintf is declared)
AC_CACHE_VAL(nufxlib_cv_snprintf_in_header, [
AC_EGREP_HEADER(snprintf, stdio.h,
[nufxlib_cv_snprintf_in_header=yes],
[nufxlib_cv_snprintf_in_header=no])
])
if test $nufxlib_cv_snprintf_in_header = "yes"; then
AC_DEFINE(SNPRINTF_DECLARED)
fi
AC_MSG_RESULT($nufxlib_cv_snprintf_in_header)
dnl Kent says: vsnprintf doesn't always have a declaration
AC_MSG_CHECKING(if vsnprintf is declared)
AC_CACHE_VAL(nufxlib_cv_vsnprintf_in_header, [
AC_EGREP_HEADER(vsnprintf, stdio.h,
[nufxlib_cv_vsnprintf_in_header=yes],
[nufxlib_cv_vsnprintf_in_header=no])
])
if test $nufxlib_cv_vsnprintf_in_header = "yes"; then
AC_DEFINE(VSNPRINTF_DECLARED)
fi
AC_MSG_RESULT($nufxlib_cv_vsnprintf_in_header)
dnl Figure out what the build and link flags should be
SHARE_FLAGS='-shared'
if test "$host_cpu" = "powerpc" -a "$host_os" = "beos"; then
dnl BeOS/PPC, with Metrowerks compiler
CC=cc
GCC=
CFLAGS='-proc 603 -opt full'
SHARE_FLAGS='-shared -nostdlib'
echo "forcing CC to \"$CC\" and CFLAGS to \"$CFLAGS\""
elif test "$host_os" = "beos"; then
dnl BeOS/x86
SHARE_FLAGS='-nostartfiles -Xlinker -soname="$@"'
fi
dnl if we're using gcc, include gcc-specific warning flags
if test -z "$GCC"; then
BUILD_FLAGS='$(OPT)'
else
BUILD_FLAGS='$(OPT) $(GCC_FLAGS)'
fi
AC_SUBST(BUILD_FLAGS)
AC_SUBST(SHARE_FLAGS)
dnl BeOS doesn't like /usr/local/include, and gets feisty about it. If libdir
dnl and includedir are set to defaults, replace them with BeOS values. This
dnl might be going a little too far...
if test "$host_os" = "beos"; then
if test "$prefix" = "NONE" -a \
"$includedir" = '${prefix}/include' -a \
"$libdir" = '${exec_prefix}/lib' -a \
"$bindir" = '${exec_prefix}/bin' -a \
"$mandir" = '${prefix}/man'
then
echo replacing install locations with BeOS values
prefix=/boot
includedir='${prefix}/develop/headers'
libdir='${exec_prefix}/home/config/lib'
bindir='${exec_prefix}/home/config/bin'
mandir='/tmp'
AC_SUBST(prefix)
AC_SUBST(includedir)
AC_SUBST(libdir)
AC_SUBST(bindir)
AC_SUBST(mandir)
fi
fi
dnl Test to see if sprintf does something reasonable. I'm going to assume
dnl that vsprintf and (if available) vsnprintf return the same thing.
AC_MSG_CHECKING(if sprintf returns int)
AC_CACHE_VAL(nufxlib_cv_sprintf_returns_int, [
AC_TRY_RUN([
#include <stdio.h>
int main(void)
{
int count;
char buf[8];
count = sprintf(buf, "123"); /* should return three */
exit(count != 3);
}
],
[nufxlib_cv_sprintf_returns_int=yes], [nufxlib_cv_sprintf_returns_int=no],
[nufxlib_cv_sprintf_returns_int=no])
])
if test $nufxlib_cv_sprintf_returns_int = "yes"; then
AC_DEFINE(SPRINTF_RETURNS_INT)
fi
AC_MSG_RESULT($nufxlib_cv_sprintf_returns_int)
DMALLOC=
AC_ARG_ENABLE(dmalloc, [ --enable-dmalloc: do dmalloc stuff], \
[ echo "--- enabling dmalloc"; \
DMALLOC="-L/usr/local/lib -ldmalloc"; AC_DEFINE(USE_DMALLOC) ])
AC_SUBST(DMALLOC)
AC_OUTPUT(Makefile samples/Makefile)

251
nufxlib-0/install-sh Executable file
View File

@ -0,0 +1,251 @@
#!/bin/sh
#
# install - install a program, script, or datafile
# This comes from X11R5 (mit/util/scripts/install.sh).
#
# Copyright 1991 by the Massachusetts Institute of Technology
#
# Permission to use, copy, modify, distribute, and sell this software and its
# documentation for any purpose is hereby granted without fee, provided that
# the above copyright notice appear in all copies and that both that
# copyright notice and this permission notice appear in supporting
# documentation, and that the name of M.I.T. not be used in advertising or
# publicity pertaining to distribution of the software without specific,
# written prior permission. M.I.T. makes no representations about the
# suitability of this software for any purpose. It is provided "as is"
# without express or implied warranty.
#
# Calling this script install-sh is preferred over install.sh, to prevent
# `make' implicit rules from creating a file called install from it
# when there is no Makefile.
#
# This script is compatible with the BSD install script, but was written
# from scratch. It can only install one file at a time, a restriction
# shared with many OS's install programs.
# set DOITPROG to echo to test this script
# Don't use :- since 4.3BSD and earlier shells don't like it.
doit="${DOITPROG-}"
# put in absolute paths if you don't have them in your path; or use env. vars.
mvprog="${MVPROG-mv}"
cpprog="${CPPROG-cp}"
chmodprog="${CHMODPROG-chmod}"
chownprog="${CHOWNPROG-chown}"
chgrpprog="${CHGRPPROG-chgrp}"
stripprog="${STRIPPROG-strip}"
rmprog="${RMPROG-rm}"
mkdirprog="${MKDIRPROG-mkdir}"
transformbasename=""
transform_arg=""
instcmd="$mvprog"
chmodcmd="$chmodprog 0755"
chowncmd=""
chgrpcmd=""
stripcmd=""
rmcmd="$rmprog -f"
mvcmd="$mvprog"
src=""
dst=""
dir_arg=""
while [ x"$1" != x ]; do
case $1 in
-c) instcmd="$cpprog"
shift
continue;;
-d) dir_arg=true
shift
continue;;
-m) chmodcmd="$chmodprog $2"
shift
shift
continue;;
-o) chowncmd="$chownprog $2"
shift
shift
continue;;
-g) chgrpcmd="$chgrpprog $2"
shift
shift
continue;;
-s) stripcmd="$stripprog"
shift
continue;;
-t=*) transformarg=`echo $1 | sed 's/-t=//'`
shift
continue;;
-b=*) transformbasename=`echo $1 | sed 's/-b=//'`
shift
continue;;
*) if [ x"$src" = x ]
then
src=$1
else
# this colon is to work around a 386BSD /bin/sh bug
:
dst=$1
fi
shift
continue;;
esac
done
if [ x"$src" = x ]
then
echo "install: no input file specified"
exit 1
else
true
fi
if [ x"$dir_arg" != x ]; then
dst=$src
src=""
if [ -d $dst ]; then
instcmd=:
chmodcmd=""
else
instcmd=mkdir
fi
else
# Waiting for this to be detected by the "$instcmd $src $dsttmp" command
# might cause directories to be created, which would be especially bad
# if $src (and thus $dsttmp) contains '*'.
if [ -f $src -o -d $src ]
then
true
else
echo "install: $src does not exist"
exit 1
fi
if [ x"$dst" = x ]
then
echo "install: no destination specified"
exit 1
else
true
fi
# If destination is a directory, append the input filename; if your system
# does not like double slashes in filenames, you may need to add some logic
if [ -d $dst ]
then
dst="$dst"/`basename $src`
else
true
fi
fi
## this sed command emulates the dirname command
dstdir=`echo $dst | sed -e 's,[^/]*$,,;s,/$,,;s,^$,.,'`
# Make sure that the destination directory exists.
# this part is taken from Noah Friedman's mkinstalldirs script
# Skip lots of stat calls in the usual case.
if [ ! -d "$dstdir" ]; then
defaultIFS='
'
IFS="${IFS-${defaultIFS}}"
oIFS="${IFS}"
# Some sh's can't handle IFS=/ for some reason.
IFS='%'
set - `echo ${dstdir} | sed -e 's@/@%@g' -e 's@^%@/@'`
IFS="${oIFS}"
pathcomp=''
while [ $# -ne 0 ] ; do
pathcomp="${pathcomp}${1}"
shift
if [ ! -d "${pathcomp}" ] ;
then
$mkdirprog "${pathcomp}"
else
true
fi
pathcomp="${pathcomp}/"
done
fi
if [ x"$dir_arg" != x ]
then
$doit $instcmd $dst &&
if [ x"$chowncmd" != x ]; then $doit $chowncmd $dst; else true ; fi &&
if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dst; else true ; fi &&
if [ x"$stripcmd" != x ]; then $doit $stripcmd $dst; else true ; fi &&
if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dst; else true ; fi
else
# If we're going to rename the final executable, determine the name now.
if [ x"$transformarg" = x ]
then
dstfile=`basename $dst`
else
dstfile=`basename $dst $transformbasename |
sed $transformarg`$transformbasename
fi
# don't allow the sed command to completely eliminate the filename
if [ x"$dstfile" = x ]
then
dstfile=`basename $dst`
else
true
fi
# Make a temp file name in the proper directory.
dsttmp=$dstdir/#inst.$$#
# Move or copy the file name to the temp name
$doit $instcmd $src $dsttmp &&
trap "rm -f ${dsttmp}" 0 &&
# and set any options; do chmod last to preserve setuid bits
# If any of these fail, we abort the whole thing. If we want to
# ignore errors from any of these, just make sure not to ignore
# errors from the above "$doit $instcmd $src $dsttmp" command.
if [ x"$chowncmd" != x ]; then $doit $chowncmd $dsttmp; else true;fi &&
if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dsttmp; else true;fi &&
if [ x"$stripcmd" != x ]; then $doit $stripcmd $dsttmp; else true;fi &&
if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dsttmp; else true;fi &&
# Now rename the file to the real destination.
$doit $rmcmd -f $dstdir/$dstfile &&
$doit $mvcmd $dsttmp $dstdir/$dstfile
fi &&
exit 0

34
nufxlib-0/mkinstalldirs Executable file
View File

@ -0,0 +1,34 @@
#! /bin/sh
# mkinstalldirs --- make directory hierarchy
# Author: Noah Friedman <friedman@prep.ai.mit.edu>
# Created: 1993-05-16
# Last modified: 1995-03-05
# Public domain
errstatus=0
for file in ${1+"$@"} ; do
set fnord `echo ":$file" | sed -ne 's/^:\//#/;s/^://;s/\// /g;s/^#/\//;p'`
shift
pathcomp=
for d in ${1+"$@"} ; do
pathcomp="$pathcomp$d"
case "$pathcomp" in
-* ) pathcomp=./$pathcomp ;;
esac
if test ! -d "$pathcomp"; then
echo "mkdir $pathcomp" 1>&2
mkdir "$pathcomp" > /dev/null 2>&1 || lasterr=$?
fi
if test ! -d "$pathcomp"; then
errstatus=$lasterr
fi
pathcomp="$pathcomp/"
done
done
exit $errstatus

View File

@ -0,0 +1,66 @@
/*
* NuFX archive manipulation library
* Copyright (C) 2000 by Andy McFadden, All Rights Reserved.
* This is free software; you can redistribute it and/or modify it under the
* terms of the GNU Library General Public License, see the file COPYING.LIB.
*
* Common functions for NuLib tests
*/
#ifndef __Common__
#define __Common__
#include "SysDefs.h" /* might as well draft off the autoconf */
#include "NufxLib.h"
#ifdef USE_DMALLOC
# include "dmalloc.h"
#endif
#define nil NULL /* this is seriously habit-forming */
#define NELEM(x) (sizeof(x) / sizeof((x)[0]))
#ifndef __cplusplus
#define false 0
#define true (!false)
#endif
#ifdef FOPEN_WANTS_B
# define kNuFileOpenReadOnly "rb"
# define kNuFileOpenReadWrite "r+b"
# define kNuFileOpenWriteTrunc "wb"
# define kNuFileOpenReadWriteCreat "w+b"
#else
# define kNuFileOpenReadOnly "r"
# define kNuFileOpenReadWrite "r+"
# define kNuFileOpenWriteTrunc "w"
# define kNuFileOpenReadWriteCreat "w+"
#endif
/*
* Figure out what path separator to use.
*/
#ifdef MSDOS
# define PATH_SEP '\\'
#endif
#ifdef WIN32
# define PATH_SEP '\\'
#endif
#ifdef MACOS
# define PATH_SEP ':'
#endif
#if defined(APW) || defined(__ORCAC__)
# define PATH_SEP ':'
#endif
#ifndef PATH_SEP
# define PATH_SEP '/'
#endif
#endif /*__Common__*/

File diff suppressed because it is too large Load Diff

624
nufxlib-0/samples/ImgConv.c Normal file
View File

@ -0,0 +1,624 @@
/*
* NuFX archive manipulation library
* Copyright (C) 2000 by Andy McFadden, All Rights Reserved.
* This is free software; you can redistribute it and/or modify it under the
* terms of the GNU Library General Public License, see the file COPYING.LIB.
*
* 2IMG <-> SHK converter. This is a practical example of using the
* NufxLib Thread functions to add and extract data in the middle of a file.
*
* Conversions from 2IMG files do not work for raw nibble images.
* Conversions from SHK archives only work if the disk image is in the
* first record in the archive. (This is easy to fix, but I'm trying to
* keep it simple.)
*/
#include "NufxLib.h"
#include "Common.h"
/* we can get this out of NufxLib if the OS doesn't have it */
#ifndef HAVE_STRCASECMP
#define strcasecmp Nu_strcasecmp
int Nu_strcasecmp(const char *s1, const char *s2);
#endif
#define kTempFile "imgconv.tmp"
#define kLocalFssep PATH_SEP
#define false 0
#define true (!false)
/*
* ===========================================================================
* 2IMG stuff
* ===========================================================================
*/
#define kImgMagic "2IMG"
#define kMyCreator "NFXL"
#define kImageFormatDOS 0
#define kImageFormatProDOS 1
#define kImageFormatNibble 2
/*
* 2IMG header definition (http://www.magnet.ch/emutech/Tech/).
*/
typedef struct ImgHeader {
char magic[4];
char creator[4];
short headerLen;
short version;
long imageFormat;
unsigned long flags;
long numBlocks;
long dataOffset;
long dataLen;
long cmntOffset;
long cmntLen;
long creatorOffset;
long creatorLen;
long spare[4];
} ImgHeader;
/*
* Read a two-byte little-endian value.
*/
void
ReadShortLE(FILE* fp, short* pBuf)
{
*pBuf = getc(fp);
*pBuf += (short) getc(fp) << 8;
}
/*
* Write a two-byte little-endian value.
*/
void
WriteShortLE(FILE* fp, unsigned short val)
{
putc(val, fp);
putc(val >> 8, fp);
}
/*
* Read a four-byte little-endian value.
*/
void
ReadLongLE(FILE* fp, long* pBuf)
{
*pBuf = getc(fp);
*pBuf += (long) getc(fp) << 8;
*pBuf += (long) getc(fp) << 16;
*pBuf += (long) getc(fp) << 24;
}
/*
* Write a four-byte little-endian value.
*/
void
WriteLongLE(FILE* fp, unsigned long val)
{
putc(val, fp);
putc(val >> 8, fp);
putc(val >> 16, fp);
putc(val >> 24, fp);
}
/*
* Read the header from a 2IMG file.
*/
int
ReadImgHeader(FILE* fp, ImgHeader* pHeader)
{
fread(pHeader->magic, 4, 1, fp);
fread(pHeader->creator, 4, 1, fp);
ReadShortLE(fp, &pHeader->headerLen);
ReadShortLE(fp, &pHeader->version);
ReadLongLE(fp, &pHeader->imageFormat);
ReadLongLE(fp, (long*)&pHeader->flags);
ReadLongLE(fp, &pHeader->numBlocks);
ReadLongLE(fp, &pHeader->dataOffset);
ReadLongLE(fp, &pHeader->dataLen);
ReadLongLE(fp, &pHeader->cmntOffset);
ReadLongLE(fp, &pHeader->cmntLen);
ReadLongLE(fp, &pHeader->creatorOffset);
ReadLongLE(fp, &pHeader->creatorLen);
ReadLongLE(fp, &pHeader->spare[0]);
ReadLongLE(fp, &pHeader->spare[1]);
ReadLongLE(fp, &pHeader->spare[2]);
ReadLongLE(fp, &pHeader->spare[3]);
if (feof(fp) || ferror(fp))
return -1;
if (strncmp(pHeader->magic, kImgMagic, 4) != 0) {
fprintf(stderr, "ERROR: bad magic number on 2IMG file\n");
return -1;
}
if (pHeader->version > 1) {
fprintf(stderr, "WARNING: might not be able to handle version=%d\n",
pHeader->version);
}
return 0;
}
/*
* Write the header to a 2IMG file.
*/
int
WriteImgHeader(FILE* fp, ImgHeader* pHeader)
{
fwrite(pHeader->magic, 4, 1, fp);
fwrite(pHeader->creator, 4, 1, fp);
WriteShortLE(fp, pHeader->headerLen);
WriteShortLE(fp, pHeader->version);
WriteLongLE(fp, pHeader->imageFormat);
WriteLongLE(fp, pHeader->flags);
WriteLongLE(fp, pHeader->numBlocks);
WriteLongLE(fp, pHeader->dataOffset);
WriteLongLE(fp, pHeader->dataLen);
WriteLongLE(fp, pHeader->cmntOffset);
WriteLongLE(fp, pHeader->cmntLen);
WriteLongLE(fp, pHeader->creatorOffset);
WriteLongLE(fp, pHeader->creatorLen);
WriteLongLE(fp, pHeader->spare[0]);
WriteLongLE(fp, pHeader->spare[1]);
WriteLongLE(fp, pHeader->spare[2]);
WriteLongLE(fp, pHeader->spare[3]);
if (ferror(fp))
return -1;
return 0;
}
/*
* Dump the contents of an ImgHeader.
*/
void
DumpImgHeader(ImgHeader* pHeader)
{
printf("--- header contents:\n");
printf("\tmagic = '%.4s'\n", pHeader->magic);
printf("\tcreator = '%.4s'\n", pHeader->creator);
printf("\theaderLen = %d\n", pHeader->headerLen);
printf("\tversion = %d\n", pHeader->version);
printf("\timageFormat = %ld\n", pHeader->imageFormat);
printf("\tflags = 0x%08lx\n", pHeader->flags);
printf("\tnumBlocks = %ld\n", pHeader->numBlocks);
printf("\tdataOffset = %ld\n", pHeader->dataOffset);
printf("\tdataLen = %ld\n", pHeader->dataLen);
printf("\tcmntOffset = %ld\n", pHeader->cmntOffset);
printf("\tcmntLen = %ld\n", pHeader->cmntLen);
printf("\tcreatorOffset = %ld\n", pHeader->creatorOffset);
printf("\tcreatorLen = %ld\n", pHeader->creatorLen);
printf("\n");
}
/*
* ===========================================================================
* Main functions
* ===========================================================================
*/
typedef enum ArchiveKind { kKindUnknown, kKindShk, kKindImg } ArchiveKind;
/*
* Create a data source for a ProDOS-ordered image. Since this is already
* in the correct format, we just point at the correct offset in the 2MG file.
*/
NuError
CreateProdosSource(const ImgHeader* pHeader, FILE* fp,
NuDataSource** ppDataSource)
{
return NuCreateDataSourceForFP(kNuThreadFormatUncompressed, false, 0, fp,
pHeader->dataOffset, pHeader->dataLen, ppDataSource);
}
/*
* Create a data source for a DOS-ordered image. This is a little harder,
* since we have to reorder the blocks into ProDOS ordering for ShrinkIt.
*/
NuError
CreateDosSource(const ImgHeader* pHeader, FILE* fp,
NuDataSource** ppDataSource)
{
NuError err;
char* diskBuffer = nil;
long offset;
if (pHeader->dataLen % 4096) {
fprintf(stderr,
"ERROR: image size must be multiple of 4096 (%ld isn't)\n",
pHeader->dataLen);
err = kNuErrGeneric;
goto bail;
}
if (fseek(fp, pHeader->dataOffset, SEEK_SET) < 0) {
err = errno;
perror("fseek failed");
goto bail;
}
diskBuffer = malloc(pHeader->dataLen);
if (diskBuffer == nil) {
fprintf(stderr, "ERROR: malloc(%ld) failed\n", pHeader->dataLen);
err = kNuErrMalloc;
goto bail;
}
/*
* Run through the image, reordering each track. This is a
* reversible transformation, i.e. if you do this twice you're back
* to ProDOS ordering.
*/
for (offset = 0; offset < pHeader->dataLen; offset += 4096) {
fread(diskBuffer + offset + 0x0000, 256, 1, fp);
fread(diskBuffer + offset + 0x0e00, 256, 1, fp);
fread(diskBuffer + offset + 0x0d00, 256, 1, fp);
fread(diskBuffer + offset + 0x0c00, 256, 1, fp);
fread(diskBuffer + offset + 0x0b00, 256, 1, fp);
fread(diskBuffer + offset + 0x0a00, 256, 1, fp);
fread(diskBuffer + offset + 0x0900, 256, 1, fp);
fread(diskBuffer + offset + 0x0800, 256, 1, fp);
fread(diskBuffer + offset + 0x0700, 256, 1, fp);
fread(diskBuffer + offset + 0x0600, 256, 1, fp);
fread(diskBuffer + offset + 0x0500, 256, 1, fp);
fread(diskBuffer + offset + 0x0400, 256, 1, fp);
fread(diskBuffer + offset + 0x0300, 256, 1, fp);
fread(diskBuffer + offset + 0x0200, 256, 1, fp);
fread(diskBuffer + offset + 0x0100, 256, 1, fp);
fread(diskBuffer + offset + 0x0f00, 256, 1, fp);
}
if (feof(fp) || ferror(fp)) {
err = errno ? errno : -1;
fprintf(stderr, "ERROR: failed while reading source file\n");
goto bail;
}
/*
* Create a data source for the buffer. We set the "doClose" flag to
* "true", so NufxLib will free the buffer for us.
*/
err = NuCreateDataSourceForBuffer(kNuThreadFormatUncompressed, true, 0,
(const unsigned char*) diskBuffer, 0, pHeader->dataLen,
ppDataSource);
if (err == kNuErrNone)
diskBuffer = nil;
bail:
if (diskBuffer != nil)
free(diskBuffer);
return err;
}
/*
* Convert a 2IMG file into a new SHK archive.
*
* This requires opening up the 2IMG file, verifying that it's okay, and
* then creating a new disk image record and thread.
*/
int
ConvertFromImgToShk(const char* srcName, const char* dstName)
{
NuError err;
NuArchive* pArchive = nil;
NuDataSource* pDataSource = nil;
NuRecordIdx recordIdx;
NuFileDetails fileDetails;
ImgHeader header;
FILE* fp = nil;
long flushStatus;
char* storageName = nil;
char* cp;
printf("Converting 2IMG file '%s' to ShrinkIt archive '%s'\n\n",
srcName, dstName);
err = kNuErrGeneric;
fp = fopen(srcName, kNuFileOpenReadOnly);
if (fp == NULL) {
perror("fopen failed");
goto bail;
}
if (ReadImgHeader(fp, &header) < 0) {
fprintf(stderr, "ERROR: header read failed\n");
goto bail;
}
DumpImgHeader(&header);
if (header.imageFormat != kImageFormatDOS &&
header.imageFormat != kImageFormatProDOS)
{
fprintf(stderr, "ERROR: can only handle DOS and ProDOS images\n");
goto bail;
}
if (header.numBlocks > 1600)
printf("WARNING: that's a big honking image!\n");
/*
* Open a new archive read-write. This refuses to overwrite an
* existing file.
*/
(void) unlink(kTempFile);
err = NuOpenRW(dstName, kTempFile, kNuOpenCreat|kNuOpenExcl, &pArchive);
if (err != kNuErrNone) {
fprintf(stderr, "ERROR: unable to create archive (err=%d)\n", err);
goto bail;
}
/* create the name that will be stored in the archive */
storageName = strdup(dstName);
cp = strrchr(storageName, '.');
if (cp != nil)
*cp = '\0';
cp = strrchr(storageName, kLocalFssep);
if (cp != nil && *(cp+1) != '\0')
cp++;
else
cp = storageName;
/*
* We can't say "add file", because NufxLib doesn't know what a 2MG
* archive is. However, we can point a DataSource at the data in
* the file, and construct the record manually.
*/
/* set up the contents of the NuFX Record */
memset(&fileDetails, 0, sizeof(fileDetails));
fileDetails.storageName = cp;
fileDetails.fileSysID = kNuFileSysUnknown; /* DOS? ProDOS? */
fileDetails.fileSysInfo = kLocalFssep;
fileDetails.access = kNuAccessUnlocked;
fileDetails.extraType = header.numBlocks;
fileDetails.storageType = 512;
/* FIX - ought to set the file dates */
/* add a new record */
err = NuAddRecord(pArchive, &fileDetails, &recordIdx);
if (err != kNuErrNone) {
fprintf(stderr, "ERROR: unable to create record (err=%d)\n", err);
goto bail;
}
/*
* Create a data source for the 2IMG file. We do this differently
* for DOS and ProDOS, because we have to rearrange the sector
* ordering for DOS-ordered images (ShrinkIt always uses ProDOS order).
*/
switch (header.imageFormat) {
case kImageFormatDOS:
err = CreateDosSource(&header, fp, &pDataSource);
break;
case kImageFormatProDOS:
err = CreateProdosSource(&header, fp, &pDataSource);
break;
default:
fprintf(stderr, "How the heck did I get here?");
err = kNuErrInternal;
goto bail;
}
if (err != kNuErrNone) {
fprintf(stderr, "ERROR: unable to create data source (err=%d)\n", err);
goto bail;
}
/* add a disk image thread */
err = NuAddThread(pArchive, recordIdx, kNuThreadIDDiskImage, pDataSource,
nil);
if (err != kNuErrNone) {
fprintf(stderr, "ERROR: unable to create thread (err=%d)\n", err);
goto bail;
}
pDataSource = nil; /* library owns it now */
/* nothing happens until we Flush */
err = NuFlush(pArchive, &flushStatus);
if (err != kNuErrNone) {
fprintf(stderr, "ERROR: flush failed (err=%d, status=0x%04lx)\n",
err, flushStatus);
goto bail;
}
err = NuClose(pArchive);
if (err != kNuErrNone) {
fprintf(stderr, "ERROR: close failed (err=%d)\n", err);
goto bail;
}
pArchive = nil;
bail:
if (pArchive != nil) {
(void)NuAbort(pArchive);
(void)NuClose(pArchive);
}
NuFreeDataSource(pDataSource);
if (storageName != nil)
free(storageName);
if (fp != nil)
fclose(fp);
return (err == kNuErrNone) ? 0 : -1;
}
/*
* Convert an SHK archive into a 2IMG file.
*
* This takes a simple-minded approach and assumes that the first record
* in the archive has the disk image in it. If it doesn't, we give up.
*/
int
ConvertFromShkToImg(const char* srcName, const char* dstName)
{
NuError err;
NuArchive* pArchive = nil;
NuDataSink* pDataSink = nil;
NuRecordIdx recordIdx;
const NuRecord* pRecord;
const NuThread* pThread = nil;
ImgHeader header;
FILE* fp = nil;
int idx;
printf("Converting ShrinkIt archive '%s' to 2IMG file '%s'\n\n",
srcName, dstName);
/*
* Open the archive.
*/
err = NuOpenRO(srcName, &pArchive);
if (err != kNuErrNone) {
fprintf(stderr, "ERROR: unable to open archive (err=%d)\n", err);
goto bail;
}
/* get the first record */
err = NuGetRecordIdxByPosition(pArchive, 0, &recordIdx);
if (err != kNuErrNone) {
fprintf(stderr, "ERROR: unable to get first recordIdx (err=%d)\n", err);
goto bail;
}
err = NuGetRecord(pArchive, recordIdx, &pRecord);
if (err != kNuErrNone) {
fprintf(stderr, "ERROR: unable to get first record (err=%d)\n", err);
goto bail;
}
/* find a disk image thread */
for (idx = 0; idx < (int)NuRecordGetNumThreads(pRecord); idx++) {
pThread = NuGetThread(pRecord, idx);
if (NuGetThreadID(pThread) == kNuThreadIDDiskImage)
break;
}
if (idx == (int)NuRecordGetNumThreads(pRecord)) {
fprintf(stderr, "ERROR: no disk image found in first record\n");
err = -1;
goto bail;
}
/*
* Looks good. Open the 2IMG file, and create the header.
*/
if (access(dstName, F_OK) == 0) {
fprintf(stderr, "ERROR: output file already exists\n");
err = -1;
goto bail;
}
fp = fopen(dstName, kNuFileOpenWriteTrunc);
if (fp == NULL) {
perror("fopen failed");
goto bail;
}
/* set up the 2MG header, based on the NuFX Record */
memset(&header, 0, sizeof(header));
memcpy(header.magic, kImgMagic, sizeof(header.magic));
memcpy(header.creator, kMyCreator, sizeof(header.creator));
header.headerLen = 64;
header.version = 1;
header.imageFormat = kImageFormatProDOS; /* always ProDOS-order */
header.numBlocks = pRecord->recExtraType;
header.dataOffset = 64;
/* old versions of ShrinkIt blew the threadEOF, so use NufxLib's "actual" */
header.dataLen = pThread->actualThreadEOF;
DumpImgHeader(&header);
if (WriteImgHeader(fp, &header) < 0) {
fprintf(stderr, "ERROR: header write failed\n");
err = -1;
goto bail;
}
/*
* We want to expand the disk image thread into "fp" at the current
* offset.
*/
err = NuCreateDataSinkForFP(true, kNuConvertOff, fp, &pDataSink);
if (err != kNuErrNone) {
fprintf(stderr, "ERROR: unable to create data sink (err=%d)\n", err);
goto bail;
}
err = NuExtractThread(pArchive, pThread->threadIdx, pDataSink);
if (err != kNuErrNone) {
fprintf(stderr, "ERROR: unable to extract thread (err=%d)\n", err);
goto bail;
}
bail:
if (pArchive != nil)
NuClose(pArchive);
NuFreeDataSink(pDataSink);
if (fp != nil)
fclose(fp);
return (err == kNuErrNone) ? 0 : -1;
}
/*
* Figure out what kind of archive this is by looking at the filename.
*/
ArchiveKind
DetermineKind(const char* filename)
{
const char* dot;
dot = strrchr(filename, '.');
if (dot == nil)
return kKindUnknown;
if (strcasecmp(dot, ".shk") == 0 || strcasecmp(dot, ".sdk") == 0)
return kKindShk;
else if (strcasecmp(dot, ".2mg") == 0)
return kKindImg;
return kKindUnknown;
}
/*
* Figure out what we want to do.
*/
int
main(int argc, char** argv)
{
ArchiveKind kind;
int cc;
if (argc != 3) {
fprintf(stderr, "Usage: %s (input.2mg|input.shk) output\n", argv[0]);
exit(2);
}
kind = DetermineKind(argv[1]);
if (kind == kKindUnknown) {
fprintf(stderr, "ERROR: input name must end in '.shk' or '.2mg'\n");
exit(2);
}
if (kind == kKindShk)
cc = ConvertFromShkToImg(argv[1], argv[2]);
else
cc = ConvertFromImgToShk(argv[1], argv[2]);
if (cc)
fprintf(stderr, "Failed\n");
else
printf("Done!\n");
exit(cc != 0);
}

587
nufxlib-0/samples/Launder.c Normal file
View File

@ -0,0 +1,587 @@
/*
* NuFX archive manipulation library
* Copyright (C) 2000 by Andy McFadden, All Rights Reserved.
* This is free software; you can redistribute it and/or modify it under the
* terms of the GNU Library General Public License, see the file COPYING.LIB.
*
* Run an archive through the laundry. The net result is a duplicate
* archive that matches the original in most respects. Extracting the
* files from the duplicate will yield the same results as if they were
* extracted from the original, but the duplicate archive may differ
* in subtle ways (e.g. filename threads may be added, data may be
* recompressed).
*
* This demonstrates copying threads around, both with and without
* recompressing, between two archives that are open simultaneously. This
* also tests NufxLib's thread ordering and verifies that you can abort
* frequently with no adverse effects.
*
* NOTE: depending on the options you select, you have to have enough
* memory to hold the entire uncompressed contents of the original archive.
* The memory requirements are reduced if you use the "copy only" flag, and
* are virtually eliminated if you use "frequent flush".
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include "NufxLib.h"
#include "Common.h"
#define kTempFile "tmp-laundry"
#define kFlagCopyOnly (1)
#define kFlagReverseThreads (1 << 1)
#define kFlagFrequentFlush (1 << 2)
#define kFlagFrequentAbort (1 << 3) /* implies FrequentFlush */
#define kFlagUseTmp (1 << 4)
/*
* Globals.
*/
char gSentRecordWarning = false;
/*
* Copy a thread, expanding and recompressing it.
*
* This assumes the library is configured for compression (it defaults
* to LZW/2, so this is a reasonable assumption).
*/
NuError
CopyThreadRecompressed(NuArchive* pInArchive, NuArchive* pOutArchive,
long flags, const NuThread* pThread, long newRecordIdx)
{
NuError err = kNuErrNone;
NuDataSource* pDataSource = nil;
NuDataSink* pDataSink = nil;
uchar* buffer = nil;
/*
* Allocate a buffer large enough to hold all the uncompressed data, and
* wrap a data sink around it.
*
* If the thread is zero bytes long, we can skip this part.
*/
if (pThread->actualThreadEOF) {
buffer = malloc(pThread->actualThreadEOF);
if (buffer == nil) {
err = kNuErrMalloc;
goto bail;
}
err = NuCreateDataSinkForBuffer(true, kNuConvertOff, buffer,
pThread->actualThreadEOF, &pDataSink);
if (err != kNuErrNone) {
fprintf(stderr, "ERROR: unable to create data sink (err=%d)\n",
err);
goto bail;
}
/*
* Expand the data. For a pre-sized thread, this grabs only the
* interesting part of the buffer.
*/
err = NuExtractThread(pInArchive, pThread->threadIdx, pDataSink);
if (err != kNuErrNone) {
fprintf(stderr, "ERROR: unable to extract thread %ld (err=%d)\n",
pThread->threadIdx, err);
goto bail;
}
}
/*
* The expanded data is in the buffer, now create a data source that
* describes it.
*
* This is complicated by the existence of pre-sized threads, which
* require us to set "otherLen".
*
* We always use "actualThreadEOF" because "thThreadEOF" is broken
* for disk archives created by certain versions of ShrinkIt.
*
* It's okay to pass in a nil value for "buffer", so long as the
* amount of data in the buffer is also zero. The library will do
* the right thing.
*/
if (NuIsPresizedThreadID(NuGetThreadID(pThread))) {
err = NuCreateDataSourceForBuffer(kNuThreadFormatUncompressed, true,
pThread->thCompThreadEOF, buffer, 0,
pThread->actualThreadEOF, &pDataSource);
if (err != kNuErrNone) {
fprintf(stderr,
"ERROR: unable to create pre-sized data source (err=%d)\n",err);
goto bail;
}
} else {
err = NuCreateDataSourceForBuffer(kNuThreadFormatUncompressed, true,
0, buffer, 0,
pThread->actualThreadEOF, &pDataSource);
if (err != kNuErrNone) {
fprintf(stderr,
"ERROR: unable to create data source (err=%d)\n", err);
goto bail;
}
}
buffer = nil; /* doClose was set, so it's owned by the data source */
/*
* Schedule the data for addition to the record.
*/
err = NuAddThread(pOutArchive, newRecordIdx, NuGetThreadID(pThread),
pDataSource, nil);
if (err != kNuErrNone) {
fprintf(stderr, "ERROR: unable to add thread (err=%d)\n", err);
goto bail;
}
pDataSource = nil; /* library owns it now */
bail:
if (pDataSource != nil)
NuFreeDataSource(pDataSource);
if (pDataSink != nil)
NuFreeDataSink(pDataSink);
if (buffer != nil)
free(buffer);
return err;
}
/*
* Copy a thread from one archive to another without disturbing the
* compression.
*
* There is a much more efficient way to do this: create an FP
* data source using an offset within the archive file itself.
* Since pInArchive->archiveFp isn't exposed, we can't use that,
* but under most operating systems you aren't prevented from
* opening the same file twice in read-only mode. The file offset
* in pThread tells us where the data is.
*
* The method used below is less memory-efficient but more portable.
*
* This always extracts based on the compThreadEOF, which is
* reliable but extracts a little more than we need on pre-sized
* threads (filenames, comments).
*/
NuError
CopyThreadUncompressed(NuArchive* pInArchive, NuArchive* pOutArchive,
long flags, const NuThread* pThread, long newRecordIdx)
{
NuError err = kNuErrNone;
NuDataSource* pDataSource = nil;
NuDataSink* pDataSink = nil;
uchar* buffer = nil;
/*
* If we have some data files that were left uncompressed, perhaps
* because of GSHK's "don't compress anything smaller than 512 bytes"
* rule, NufxLib will try to compress them. We disable this
* behavior by disabling compression. That way, stuff that is
* already compressed will remain that way, and stuff that isn't
* compressed won't be. (We really only need to do this once, at
* the start of the program, but it's illustrative to do it here.)
*/
err = NuSetValue(pOutArchive, kNuValueDataCompression, kNuCompressNone);
if (err != kNuErrNone) {
fprintf(stderr, "ERROR: unable to set compression (err=%d)\n", err);
goto bail;
}
/*
* Allocate a buffer large enough to hold all the compressed data, and
* wrap a data sink around it.
*/
buffer = malloc(pThread->thCompThreadEOF);
if (buffer == nil) {
err = kNuErrMalloc;
goto bail;
}
err = NuCreateDataSinkForBuffer(false, kNuConvertOff, buffer,
pThread->thCompThreadEOF, &pDataSink);
if (err != kNuErrNone) {
fprintf(stderr, "ERROR: unable to create data sink (err=%d)\n",
err);
goto bail;
}
/*
* Get the compressed data. For a pre-sized thread, this grabs the
* entire contents of the buffer, including the padding.
*/
err = NuExtractThread(pInArchive, pThread->threadIdx, pDataSink);
if (err != kNuErrNone) {
fprintf(stderr, "ERROR: unable to extract thread %ld (err=%d)\n",
pThread->threadIdx, err);
goto bail;
}
/*
* The (perhaps compressed) data is in the buffer, now create a data
* source that describes it.
*
* This is complicated by the existence of pre-sized threads. There
* are two possibilities:
* 1. We have a certain amount of non-pre-sized data (thCompThreadEOF)
* that will expand out to a certain length (actualThreadEOF).
* 2. We have a certain amount of pre-sized data (actualThreadEOF)
* that will fit within a buffer (thCompThreadEOF).
* As you can see, the arguments need to be reversed for pre-sized
* threads.
*
* We always use "actualThreadEOF" because "thThreadEOF" is broken
* for disk archives created by certain versions of ShrinkIt.
*/
if (NuIsPresizedThreadID(NuGetThreadID(pThread))) {
err = NuCreateDataSourceForBuffer(pThread->thThreadFormat, true,
pThread->thCompThreadEOF, buffer, 0,
pThread->actualThreadEOF, &pDataSource);
if (err != kNuErrNone) {
fprintf(stderr,
"ERROR: unable to create pre-sized data source (err=%d)\n",err);
goto bail;
}
} else {
err = NuCreateDataSourceForBuffer(pThread->thThreadFormat, true,
pThread->actualThreadEOF, buffer, 0,
pThread->thCompThreadEOF, &pDataSource);
if (err != kNuErrNone) {
fprintf(stderr,
"ERROR: unable to create data source (err=%d)\n", err);
goto bail;
}
}
buffer = nil; /* doClose was set, so it's owned by the data source */
/* yes, this is a kluge... sigh */
err = NuDataSourceSetRawCrc(pDataSource, pThread->thThreadCRC);
if (err != kNuErrNone) {
fprintf(stderr, "ERROR: can't set source CRC (err=%d)\n", err);
goto bail;
}
/*
* Schedule the data for addition to the record.
*
* Note that NuAddThread makes a copy of the data source, and clears
* "doClose" on our copy, so we are free to dispose of pDataSource.
*/
err = NuAddThread(pOutArchive, newRecordIdx, NuGetThreadID(pThread),
pDataSource, nil);
if (err != kNuErrNone) {
fprintf(stderr, "ERROR: unable to add thread (err=%d)\n", err);
goto bail;
}
pDataSource = nil; /* library owns it now */
bail:
if (pDataSource != nil)
NuFreeDataSource(pDataSource);
if (pDataSink != nil)
NuFreeDataSink(pDataSink);
if (buffer != nil)
free(buffer);
return err;
}
/*
* Copy a thread from one archive to another.
*
* Depending on "flags", this will either copy it raw or uncompress and
* recompress.
*/
NuError
CopyThread(NuArchive* pInArchive, NuArchive* pOutArchive, long flags,
const NuThread* pThread, long newRecordIdx)
{
if (flags & kFlagCopyOnly) {
return CopyThreadUncompressed(pInArchive, pOutArchive, flags, pThread,
newRecordIdx);
} else {
return CopyThreadRecompressed(pInArchive, pOutArchive, flags, pThread,
newRecordIdx);
}
}
/*
* Copy a record from the input to the output.
*
* This runs through the list of threads and copies each one individually.
* It will copy them in the original order or in reverse order (the latter
* of which will not usually have any effect since NufxLib imposes a
* specific thread ordering on most common types) depending on "flags".
*/
NuError
CopyRecord(NuArchive* pInArchive, NuArchive* pOutArchive, long flags,
NuRecordIdx recordIdx)
{
NuError err = kNuErrNone;
const NuRecord* pRecord;
const NuThread* pThread;
NuFileDetails fileDetails;
NuRecordIdx newRecordIdx;
long numThreads;
int idx;
/*
* Grab the original record and see how many threads it has.
*/
err = NuGetRecord(pInArchive, recordIdx, &pRecord);
if (err != kNuErrNone) {
fprintf(stderr, "ERROR: unable to get recordIdx %ld\n", recordIdx);
goto bail;
}
/*
* Pre-v3 records didn't put CRCs in the thread headers. If we just
* copy the thread over without reprocessing the data, we won't compute
* a CRC for the thread, and we will get CRC failures.
*/
if (!gSentRecordWarning && (flags & kFlagCopyOnly) &&
pRecord->recVersionNumber < 3)
{
printf("WARNING: pre-v3 records that aren't recompressed may exhibit CRC failures\n");
gSentRecordWarning = true;
}
numThreads = NuRecordGetNumThreads(pRecord);
if (!numThreads) {
fprintf(stderr, "WARNING: recordIdx=%ld was empty\n", recordIdx);
goto bail;
}
/*
* Create a new record that looks just like the original.
*/
memset(&fileDetails, 0, sizeof(fileDetails));
fileDetails.storageName = pRecord->filename;
fileDetails.fileSysID = pRecord->recFileSysID;
fileDetails.fileSysInfo = pRecord->recFileSysInfo;
fileDetails.access = pRecord->recAccess;
fileDetails.fileType = pRecord->recFileType;
fileDetails.extraType = pRecord->recExtraType;
fileDetails.storageType = pRecord->recStorageType;
fileDetails.createWhen = pRecord->recCreateWhen;
fileDetails.modWhen = pRecord->recModWhen;
fileDetails.archiveWhen = pRecord->recArchiveWhen;
err = NuAddRecord(pOutArchive, &fileDetails, &newRecordIdx);
if (err != kNuErrNone) {
fprintf(stderr, "ERROR: NuAddRecord failed (err=%d)\n", err);
goto bail;
}
/*
* Copy the threads.
*/
if (flags & kFlagReverseThreads) {
for (idx = numThreads-1; idx >= 0; idx--) {
pThread = NuGetThread(pRecord, idx);
assert(pThread != nil);
err = CopyThread(pInArchive, pOutArchive, flags, pThread,
newRecordIdx);
if (err != kNuErrNone)
goto bail;
}
} else {
for (idx = 0; idx < numThreads; idx++) {
pThread = NuGetThread(pRecord, idx);
assert(pThread != nil);
err = CopyThread(pInArchive, pOutArchive, flags, pThread,
newRecordIdx);
if (err != kNuErrNone)
goto bail;
}
}
bail:
return err;
}
/*
* Launder an archive from inFile to outFile.
*
* Returns 0 on success, nonzero on failure.
*/
int
LaunderArchive(const char* inFile, const char* outFile, long flags)
{
NuError err = kNuErrNone;
NuArchive* pInArchive = nil;
NuArchive* pOutArchive = nil;
const NuMasterHeader* pMasterHeader;
NuRecordIdx recordIdx;
long idx, flushStatus;
err = NuOpenRO(inFile, &pInArchive);
if (err != kNuErrNone) {
fprintf(stderr, "ERROR: couldn't open input archive '%s' (err=%d)\n",
inFile, err);
goto bail;
}
err = NuOpenRW(outFile, kTempFile, kNuOpenCreat|kNuOpenExcl, &pOutArchive);
if (err != kNuErrNone) {
fprintf(stderr, "ERROR: couldn't open output archive '%s' (err=%d)\n",
outFile, err);
goto bail;
}
/* allow duplicates, in case the original archive has them */
err = NuSetValue(pOutArchive, kNuValueAllowDuplicates, true);
if (err != kNuErrNone) {
fprintf(stderr, "ERROR: couldn't allow duplicates (err=%d)\n", err);
goto bail;
}
if (flags & kFlagUseTmp) {
err = NuSetValue(pOutArchive, kNuValueModifyOrig, false);
if (err != kNuErrNone) {
fprintf(stderr,
"ERROR: couldn't disable modify orig (err=%d)\n", err);
goto bail;
}
}
err = NuGetMasterHeader(pInArchive, &pMasterHeader);
if (err != kNuErrNone) {
fprintf(stderr, "ERROR: couldn't get master header (err=%d)\n", err);
goto bail;
}
/*
* Iterate through the set of records.
*/
for (idx = 0; idx < (int)pMasterHeader->mhTotalRecords; idx++) {
err = NuGetRecordIdxByPosition(pInArchive, idx, &recordIdx);
if (err != kNuErrNone) {
fprintf(stderr, "ERROR: couldn't get record #%ld (err=%d)\n",
idx, err);
goto bail;
}
err = CopyRecord(pInArchive, pOutArchive, flags, recordIdx);
if (err != kNuErrNone)
goto bail;
/*
* If "frequent abort" is set, abort what we just did and redo it.
*/
if (flags & kFlagFrequentAbort) {
printf("(abort)\n");
err = NuAbort(pOutArchive);
if (err != kNuErrNone) {
fprintf(stderr, "ERROR: abort failed (err=%d)\n", err);
goto bail;
}
err = CopyRecord(pInArchive, pOutArchive, flags, recordIdx);
if (err != kNuErrNone)
goto bail;
}
/*
* If "frequent abort" or "frequent flush" is set, flush after
* each record is copied.
*/
if ((flags & kFlagFrequentAbort) || (flags & kFlagFrequentFlush)) {
printf("(flush)\n");
err = NuFlush(pOutArchive, &flushStatus);
if (err != kNuErrNone) {
fprintf(stderr,
"ERROR: flush failed (err=%d, status=0x%04lx)\n",
err, flushStatus);
goto bail;
}
}
}
/* first and only flush if frequent-flushing wasn't enabled */
err = NuFlush(pOutArchive, &flushStatus);
if (err != kNuErrNone) {
fprintf(stderr, "ERROR: flush failed (err=%d, status=0x%04lx)\n",
err, flushStatus);
goto bail;
}
bail:
if (pInArchive != nil)
NuClose(pInArchive);
if (pOutArchive != nil) {
if (err != kNuErrNone)
NuAbort(pOutArchive);
NuClose(pOutArchive); /* flush pending changes and close */
}
return (err != kNuErrNone);
}
/*
* Print usage info.
*/
void
Usage(const char* argv0)
{
fprintf(stderr, "Usage: %s [-crfat] infile.shk outfile.shk\n", argv0);
}
/*
* Grab the name of an archive to read.
*/
int
main(int argc, char** argv)
{
long major, minor, bug;
const char* pBuildDate;
long flags = 0;
char* cp = nil;
int cc;
(void) NuGetVersion(&major, &minor, &bug, &pBuildDate, nil);
printf("Using NuFX lib %ld.%ld.%ld built on or after %s\n",
major, minor, bug, pBuildDate);
if (argc < 3 || argc > 4) {
Usage(argv[0]);
exit(2);
}
if (argc == 4) {
cp = argv[1];
if (*cp++ != '-') {
Usage(argv[0]);
exit(2);
}
while (*cp != '\0') {
switch (*cp) {
case 'c': flags |= kFlagCopyOnly; break;
case 'r': flags |= kFlagReverseThreads; break;
case 'f': flags |= kFlagFrequentFlush; break;
case 'a': flags |= kFlagFrequentAbort; break;
case 't': flags |= kFlagUseTmp; break;
default:
Usage(argv[0]);
exit(2);
}
cp++;
}
argv++;
}
cc = LaunderArchive(argv[1], argv[2], flags);
if (cc == 0)
printf("Success!\n");
else
printf("Failed.\n");
exit(cc != 0);
}

View File

@ -0,0 +1,83 @@
#
# Makefile for nufxlib tests (should work with non-GNU make).
#
# This is normally invoked from the nufxlib makefile.
#
# If you invoke this directly, LIB_PRODUCT won't be defined, and it
# won't automatically detect changes to the library. However, any
# changes to the library should cause a re-build in here anyway if
# you're running "make" from the library directory.
#
SHELL = /bin/sh
CC = @CC@
AR = ar rcv
#OPT = @CFLAGS@ -DNDEBUG
OPT = @CFLAGS@
#OPT = @CFLAGS@ -DDEBUG_MSGS
#OPT = @CFLAGS@ -DDEBUG_VERBOSE
GCC_FLAGS = -Wall -Wwrite-strings -Wstrict-prototypes -Wpointer-arith -Wshadow
CFLAGS = @BUILD_FLAGS@ -I. -I.. @DEFS@
#ALL_SRCS = $(wildcard *.c *.cpp)
ALL_SRCS = Exerciser.c Launder.c ImgConv.c TestBasic.c TestExtract.c \
TestSimple.c
NUFXLIB = -L.. -lnufx @DMALLOC@
PRODUCTS = exerciser imgconv launder test-basic test-extract test-simple
#ifdef PURIFY_BUILD
# PURIFY = purify
# CFLAGS += -DPURIFY
#endif
#ifdef QUANTIFY_BUILD
# QUANTIFY = quantify
# CFLAGS += -DQUANTIFY
#endif
all: $(PRODUCTS)
@true
#quantify:
# -rm -f $(PRODUCT)
# @$(MAKE) QUANTIFY_BUILD=1
#
#purify:
# -rm -f $(PRODUCT)
# @$(MAKE) PURIFY_BUILD=1
exerciser: Exerciser.o $(LIB_PRODUCT)
$(PURIFY) $(QUANTIFY) $(CC) -o $@ Exerciser.o $(NUFXLIB)
imgconv: ImgConv.o $(LIB_PRODUCT)
$(PURIFY) $(QUANTIFY) $(CC) -o $@ ImgConv.o $(NUFXLIB)
launder: Launder.o $(LIB_PRODUCT)
$(PURIFY) $(QUANTIFY) $(CC) -o $@ Launder.o $(NUFXLIB)
test-basic: TestBasic.o $(LIB_PRODUCT)
$(PURIFY) $(QUANTIFY) $(CC) -o $@ TestBasic.o $(NUFXLIB)
test-simple: TestSimple.o $(LIB_PRODUCT)
$(PURIFY) $(QUANTIFY) $(CC) -o $@ TestSimple.o $(NUFXLIB)
test-extract: TestExtract.o $(LIB_PRODUCT)
$(PURIFY) $(QUANTIFY) $(CC) -o $@ TestExtract.o $(NUFXLIB)
tags::
ctags --totals -R ../*
@#ctags *.cpp ../*.c *.h ../*.h
clean:
-rm -f *.o core
-rm -f $(PRODUCTS)
distclean: clean
-rm -f tags
-rm -f Makefile Makefile.bak
depend:
makedepend -- $(CFLAGS) -I/usr/local/include -- $(ALL_SRCS)
# DO NOT DELETE THIS LINE -- make depend depends on it.

View File

@ -0,0 +1,71 @@
#
# Makefile for Microsoft C compilers. Tested against Visual C++ 6.0.
# Not pretty but it seems to work.
#
# Run with "nmake /f Makefile.msc". Expects NufxLib to have been built
# in "..".
#
# To build without debugging info, use "nmake nodebug=1".
#
# Windows magic
TARGETOS = BOTH
!include <ntwin32.mak>
NUFXSRCDIR = ..
LIB_PRODUCT = $(NUFXSRCDIR)\nufxlib.lib
!ifdef NODEBUG
#OPT = /D NDEBUG /ML /Ogityb2
OPT = /D DEBUG_MSGS /ML /Ogityb2
LIB_FLAGS = /nodefaultlib:libcd.lib /nologo libc.lib setargv.obj
!else
OPT = /D DEBUG_MSGS /MLd /Od
LIB_FLAGS = /nodefaultlib:libc.lib /nologo libcd.lib setargv.obj
!endif
BUILD_FLAGS = /W3 /GX /D "WIN32" /D "_CONSOLE" /I "$(NUFXSRCDIR)"
# how to compile sources
.c.obj:
@$(cc) $(cdebug) $(OPT) $(BUILD_FLAGS) $(cflags) $(cvars) -o $@ $<
PRODUCTS = exerciser.exe imgconv.exe launder.exe test-basic.exe test-extract.exe test-simple.exe
all: $(PRODUCTS)
exerciser.exe: Exerciser.obj $(LIB_PRODUCT)
$(link) $(ldebug) Exerciser.obj -out:$@ $(NUFXSRCDIR)\nufxlib.lib $(LIB_FLAGS)
imgconv.exe: ImgConv.obj $(LIB_PRODUCT)
$(link) $(ldebug) ImgConv.obj -out:$@ $(NUFXSRCDIR)\nufxlib.lib $(LIB_FLAGS)
launder.exe: Launder.obj $(LIB_PRODUCT)
$(link) $(ldebug) Launder.obj -out:$@ $(NUFXSRCDIR)\nufxlib.lib $(LIB_FLAGS)
test-basic.exe: TestBasic.obj $(LIB_PRODUCT)
$(link) $(ldebug) TestBasic.obj -out:$@ $(NUFXSRCDIR)\nufxlib.lib $(LIB_FLAGS)
test-simple.exe: TestSimple.obj $(LIB_PRODUCT)
$(link) $(ldebug) TestSimple.obj -out:$@ $(NUFXSRCDIR)\nufxlib.lib $(LIB_FLAGS)
test-extract.exe: TestExtract.obj $(LIB_PRODUCT)
$(link) $(ldebug) TestExtract.obj -out:$@ $(NUFXSRCDIR)\nufxlib.lib $(LIB_FLAGS)
clean:
del *.obj
del exerciser.exe
del imgconv.exe
del launder.exe
del test-basic.exe
del test-simple.exe
del test-extract.exe
Exerciser.obj: Exerciser.c Common.h $(NUFXSRCDIR)\NufxLib.h $(NUFXSRCDIR)\SysDefs.h
ImgConv.obj: ImgConv.c Common.h $(NUFXSRCDIR)\NufxLib.h $(NUFXSRCDIR)\SysDefs.h
Launder.obj: Launder.c Common.h $(NUFXSRCDIR)\NufxLib.h $(NUFXSRCDIR)\SysDefs.h
TestBasic.obj: TestBasic.c Common.h $(NUFXSRCDIR)\NufxLib.h $(NUFXSRCDIR)\SysDefs.h
TestSimple.obj: TestSimple.c Common.h $(NUFXSRCDIR)\NufxLib.h $(NUFXSRCDIR)\SysDefs.h
TestExtract.obj: TestExtract.c Common.h $(NUFXSRCDIR)\NufxLib.h $(NUFXSRCDIR)\SysDefs.h

View File

@ -0,0 +1,91 @@
NufxLib "samples" README
This directory contains some test programs and useful sample code.
test-basic
==========
Basic tests. Run this to verify that things are working.
exerciser
=========
This program allows you to exercise all of NufxLib's basic functions.
Run it without arguments and hit "?" for a list of commands.
If you think you have found a bug in NufxLib, you can use this to narrow
it down to a repeatable case.
imgconv
=======
A 2IMG disk image converter. You can convert ".2MG" files to ShrinkIt
disk archives, and ShrinkIt disk archives to 2IMG format. imgconv uses
a creator type of "NFXL".
You can use it like this:
% imgconv file.shk file.2mg
or
% imgconv file.2mg file.shk
It figures out what to do based on the filename. It will recognize ".sdk"
as a ShrinkIt archive.
Limitations: works for DOS-ordered and ProDOS-ordered 2MG images, but
not for raw nibble images. Converting from .shk only works if the first
record in the archive is a disk image; you don't get to pick the one you
want from an archive with several in it.
launder
=======
Run an archive through the laundry. This copies the entire contents of
an archive thread-by-thread, reconstructing it such that the data
matches the original even if the archive contents don't (e.g. records
are updated to version 3, files may be recompressed with LZW/2, option
lists are stripped out, etc).
The basic usage is:
% launder [-crfa] infile.shk outfile.shk
The flags are:
-c Just copy threads rather than recompressing them
-r Add threads in reverse order
-f Call NuFlush after every record
-a Call NuAbort after every record, then re-do the record and call NuFlush
-t Write to temp file, instead of writing directly into outfile.shk
If you use the "-c" flag with an archive created by P8 ShrinkIt or NuLib,
the laundered archive may have CRC failures. This is because "launder"
creates version 3 records, which are expected to have a valid CRC in the
thread header. The only way to compute the CRC is to uncompress the data,
which "launder" doesn't do when "-c" is set. The data itself is fine,
it's just the thread CRC that's wrong (if the data were hosed, the LZW/1
CRC would be bad too). "launder" will issue a warning when it detects
this situation.
If you find that you're running out of memory on very large archives, you
can reduce the memory requirements by specifying the "-f" flag.
test-simple
===========
Simple test program. Give it the name of an archive, and it will display
the contents.
test-extract
============
Simple test program. Give it the name of an archive, and it will write
all filename threads into "out.buf", "out.fp", and "out.file" using three
different kinds of NuDataSinks.

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,494 @@
/*
* NuFX archive manipulation library
* Copyright (C) 2000 by Andy McFadden, All Rights Reserved.
* This is free software; you can redistribute it and/or modify it under the
* terms of the GNU Library General Public License, see the file COPYING.LIB.
*
* Test extraction of individual threads in various ways. The net result
* of this is three files (out.file, out.fp, out.buf) that contain the
* result of writing all filenames in an archive to the same data sink.
*
* This gathers up information on the contents of the archive via a
* callback, and then emits all of the data at once.
*
* (This was originally written in C++, and converted to C after I repented.)
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include "NufxLib.h"
#include "Common.h"
#define false 0
#define true (!false)
#define kHappySize 2408
/*
* ===========================================================================
* ArchiveRecord
* ===========================================================================
*/
/*
* Track an archive record.
*/
typedef struct ArchiveRecord {
char* filename;
NuRecordIdx recordIdx;
long numThreads;
NuThread* pThreads;
struct ArchiveRecord* pNext;
} ArchiveRecord;
/*
* Alloc a new ArchiveRecord.
*/
ArchiveRecord*
ArchiveRecord_New(const NuRecord* pRecord)
{
ArchiveRecord* pArcRec = nil;
pArcRec = malloc(sizeof(*pArcRec));
if (pArcRec == nil)
return nil;
if (pRecord->filename == nil)
pArcRec->filename = strdup("<unknown>");
else
pArcRec->filename = strdup((char*)pRecord->filename);
pArcRec->recordIdx = pRecord->recordIdx;
pArcRec->numThreads = NuRecordGetNumThreads(pRecord);
(void) NuRecordCopyThreads(pRecord, &pArcRec->pThreads);
pArcRec->pNext = nil;
return pArcRec;
}
/*
* Free up an ArchiveRecord.
*/
void
ArchiveRecord_Free(ArchiveRecord* pArcRec)
{
if (pArcRec == nil)
return;
if (pArcRec->filename != nil)
free(pArcRec->filename);
if (pArcRec->pThreads != nil)
free(pArcRec->pThreads);
free(pArcRec);
}
/*
* Find a thread with a matching NuThreadID.
*/
const NuThread*
ArchiveRecord_FindThreadByID(const ArchiveRecord* pArcRec, NuThreadID threadID)
{
const NuThread* pThread;
int i;
for (i = 0; i < pArcRec->numThreads; i++) {
pThread = NuThreadGetByIdx(pArcRec->pThreads, i);
if (NuGetThreadID(pThread) == threadID)
return pThread;
}
return nil;
}
const char*
ArchiveRecord_GetFilename(const ArchiveRecord* pArcRec)
{
return pArcRec->filename;
}
NuRecordIdx
ArchiveRecord_GetRecordIdx(const ArchiveRecord* pArcRec)
{
return pArcRec->recordIdx;
}
long
ArchiveRecord_GetNumThreads(const ArchiveRecord* pArcRec)
{
return pArcRec->numThreads;
}
const NuThread*
ArchiveRecord_GetThread(const ArchiveRecord* pArcRec, int idx)
{
if (idx < 0 || idx >= pArcRec->numThreads)
return nil;
return NuThreadGetByIdx(pArcRec->pThreads, idx);
}
void
ArchiveRecord_SetNext(ArchiveRecord* pArcRec, ArchiveRecord* pNextRec)
{
pArcRec->pNext = pNextRec;
}
ArchiveRecord*
ArchiveRecord_GetNext(const ArchiveRecord* pArcRec)
{
return pArcRec->pNext;
}
/*
* ===========================================================================
* ArchiveData
* ===========================================================================
*/
/*
* A collection of records.
*/
typedef struct ArchiveData {
long numRecords;
ArchiveRecord* pRecordHead;
ArchiveRecord* pRecordTail;
} ArchiveData;
ArchiveData*
ArchiveData_New(void)
{
ArchiveData* pArcData;
pArcData = malloc(sizeof(*pArcData));
if (pArcData == nil)
return nil;
pArcData->numRecords = 0;
pArcData->pRecordHead = pArcData->pRecordTail = nil;
return pArcData;
}
void
ArchiveData_Free(ArchiveData* pArcData)
{
ArchiveRecord* pNext;
if (pArcData == nil)
return;
printf("*** Deleting %ld records!\n", pArcData->numRecords);
while (pArcData->pRecordHead != nil) {
pNext = ArchiveRecord_GetNext(pArcData->pRecordHead);
ArchiveRecord_Free(pArcData->pRecordHead);
pArcData->pRecordHead = pNext;
}
free(pArcData);
}
ArchiveRecord*
ArchiveData_GetRecordHead(const ArchiveData* pArcData)
{
return pArcData->pRecordHead;
}
/* add an ArchiveRecord to the list pointed at by ArchiveData */
void
ArchiveData_AddRecord(ArchiveData* pArcData, ArchiveRecord* pRecord)
{
assert(pRecord != nil);
assert((pArcData->pRecordHead == nil && pArcData->pRecordTail == nil) ||
(pArcData->pRecordHead != nil && pArcData->pRecordTail != nil));
if (pArcData->pRecordHead == nil) {
/* first */
pArcData->pRecordHead = pArcData->pRecordTail = pRecord;
} else {
/* not first, add to end */
ArchiveRecord_SetNext(pArcData->pRecordTail, pRecord);
pArcData->pRecordTail = pRecord;
}
pArcData->numRecords++;
}
/* dump the contents of the ArchiveData to stdout */
void
ArchiveData_DumpContents(const ArchiveData* pArcData)
{
ArchiveRecord* pArcRec;
pArcRec = pArcData->pRecordHead;
while (pArcRec != nil) {
const NuThread* pThread;
int i, count;
printf("%5ld '%s'\n",
ArchiveRecord_GetRecordIdx(pArcRec),
ArchiveRecord_GetFilename(pArcRec));
count = ArchiveRecord_GetNumThreads(pArcRec);
for (i = 0; i < count; i++) {
pThread = ArchiveRecord_GetThread(pArcRec, i);
printf(" %5ld 0x%04x 0x%04x\n", pThread->threadIdx,
pThread->thThreadClass, pThread->thThreadKind);
}
pArcRec = ArchiveRecord_GetNext(pArcRec);
}
}
/*
* ===========================================================================
* Main stuff
* ===========================================================================
*/
/*
* Callback function to collect archive information.
*/
NuResult
GatherContents(NuArchive* pArchive, void* vpRecord)
{
NuRecord* pRecord = (NuRecord*) vpRecord;
ArchiveData* pArchiveData = nil;
ArchiveRecord* pArchiveRecord = ArchiveRecord_New(pRecord);
NuGetExtraData(pArchive, (void**)&pArchiveData);
assert(pArchiveData != nil);
printf("*** Filename = '%s'\n",
pRecord->filename == nil ? "<unknown>":(const char*)pRecord->filename);
ArchiveData_AddRecord(pArchiveData, pArchiveRecord);
return kNuOK;
}
/*
* Copy the filename thread from every record to "pDataSink".
*/
NuError
ReadAllFilenameThreads(NuArchive* pArchive, ArchiveData* pArchiveData,
NuDataSink* pDataSink)
{
NuError err = kNuErrNone;
ArchiveRecord* pArchiveRecord;
const NuThread* pThread;
pArchiveRecord = ArchiveData_GetRecordHead(pArchiveData);
while (pArchiveRecord != nil) {
pThread = ArchiveRecord_FindThreadByID(pArchiveRecord,
kNuThreadIDFilename);
if (pThread != nil) {
err = NuExtractThread(pArchive, pThread->threadIdx, pDataSink);
if (err != kNuErrNone) {
fprintf(stderr, "*** Extract failed (%d)\n", err);
goto bail;
}
}
pArchiveRecord = ArchiveRecord_GetNext(pArchiveRecord);
}
bail:
return err;
}
/* extract every filename thread into a single file, overwriting each time */
NuError
ExtractToFile(NuArchive* pArchive, ArchiveData* pArchiveData)
{
NuError err;
NuDataSink* pDataSink = nil;
err = NuCreateDataSinkForFile(true, kNuConvertOff, "out.file", PATH_SEP,
&pDataSink);
if (err != kNuErrNone)
goto bail;
err = NuSetValue(pArchive, kNuValueHandleExisting, kNuAlwaysOverwrite);
if (err != kNuErrNone)
goto bail;
err = ReadAllFilenameThreads(pArchive, pArchiveData, pDataSink);
if (err != kNuErrNone)
goto bail;
bail:
(void) NuFreeDataSink(pDataSink);
if (err == kNuErrNone)
printf("*** File write complete\n");
return err;
}
/* extract every filename thread into a FILE*, appending */
NuError
ExtractToFP(NuArchive* pArchive, ArchiveData* pArchiveData)
{
NuError err;
FILE* fp = nil;
NuDataSink* pDataSink = nil;
if ((fp = fopen("out.fp", kNuFileOpenWriteTrunc)) == nil)
return kNuErrFileOpen;
err = NuCreateDataSinkForFP(true, kNuConvertOff, fp, &pDataSink);
if (err != kNuErrNone)
goto bail;
err = ReadAllFilenameThreads(pArchive, pArchiveData, pDataSink);
if (err != kNuErrNone)
goto bail;
bail:
(void) NuFreeDataSink(pDataSink);
if (fp != nil)
fclose(fp);
if (err == kNuErrNone)
printf("*** FP write complete\n");
return err;
}
/* extract every filename thread into a buffer, advancing as we go */
NuError
ExtractToBuffer(NuArchive* pArchive, ArchiveData* pArchiveData)
{
NuError err;
unsigned char buffer[kHappySize];
NuDataSink* pDataSink = nil;
unsigned long count;
err = NuCreateDataSinkForBuffer(true, kNuConvertOff, buffer, kHappySize,
&pDataSink);
if (err != kNuErrNone)
goto bail;
err = ReadAllFilenameThreads(pArchive, pArchiveData, pDataSink);
if (err != kNuErrNone) {
if (err == kNuErrBufferOverrun)
fprintf(stderr, "*** Hey, buffer wasn't big enough!\n");
goto bail;
}
/* write the buffer to a file */
(void) NuDataSinkGetOutCount(pDataSink, &count);
if (count > 0) {
FILE* fp;
if ((fp = fopen("out.buf", kNuFileOpenWriteTrunc)) != nil) {
printf("*** Writing %ld bytes\n", count);
if (fwrite(buffer, count, 1, fp) != 1)
err = kNuErrFileWrite;
fclose(fp);
}
} else {
printf("*** No data found!\n");
}
bail:
(void) NuFreeDataSink(pDataSink);
return err;
}
/*
* Do file stuff.
*/
int
DoFileStuff(const char* filename)
{
NuError err;
NuArchive* pArchive = nil;
NuMasterHeader* pMasterHeader = nil;
ArchiveData* pArchiveData = ArchiveData_New();
err = NuOpenRO(filename, &pArchive);
if (err != kNuErrNone)
goto bail;
NuSetExtraData(pArchive, pArchiveData);
printf("*** Gathering contents!\n");
err = NuContents(pArchive, GatherContents);
if (err != kNuErrNone)
goto bail;
printf("*** Dumping contents!\n");
ArchiveData_DumpContents(pArchiveData);
err = ExtractToFile(pArchive, pArchiveData);
if (err != kNuErrNone)
goto bail;
err = ExtractToFP(pArchive, pArchiveData);
if (err != kNuErrNone)
goto bail;
err = ExtractToBuffer(pArchive, pArchiveData);
if (err != kNuErrNone)
goto bail;
bail:
if (err != kNuErrNone)
fprintf(stderr, "*** ERROR: got error %d\n", err);
if (pArchive != nil) {
NuError err2 = NuClose(pArchive);
if (err == kNuErrNone && err2 != kNuErrNone)
err = err2;
}
if (pMasterHeader != nil)
free(pMasterHeader);
if (pArchiveData != nil)
free(pArchiveData);
return err;
}
/*
* Grab the name of an archive to read. If no name was provided, use stdin.
*/
int
main(int argc, char** argv)
{
long major, minor, bug;
const char* pBuildDate;
FILE* infp;
int cc;
(void) NuGetVersion(&major, &minor, &bug, &pBuildDate, nil);
printf("Using NuFX lib %ld.%ld.%ld built on or after %s\n",
major, minor, bug, pBuildDate);
if (argc == 2) {
infp = fopen(argv[1], kNuFileOpenReadOnly);
if (infp == nil) {
perror("fopen failed");
exit(1);
}
} else {
fprintf(stderr, "ERROR: you have to specify a filename\n");
exit(2);
}
cc = DoFileStuff(argv[1]);
exit(cc != 0);
}

View File

@ -0,0 +1,105 @@
/*
* NuFX archive manipulation library
* Copyright (C) 2000 by Andy McFadden, All Rights Reserved.
* This is free software; you can redistribute it and/or modify it under the
* terms of the GNU Library General Public License, see the file COPYING.LIB.
*
* Simple test program. Opens an archive, dumps the contents.
*
* If the first argument is "-", this will read from stdin. Otherwise,
* the first argument is taken to be an archive filename, and opened.
*/
#include <stdio.h>
#include "NufxLib.h"
#include "Common.h"
/*
* Callback function to display the contents of a single record.
*
* "pRecord->filename" is the record's filename, whether from the record
* header, a filename thread, or a default value ("UNKNOWN", stuffed in
* when a record has no filename at all).
*/
NuResult
ShowContents(NuArchive* pArchive, void* vpRecord)
{
const NuRecord* pRecord = (NuRecord*) vpRecord;
printf("*** Filename = '%s'\n", pRecord->filename);
return kNuOK;
}
/*
* Dump the contents from the streaming input.
*
* If we're not interested in handling an archive on stdin, we could just
* pass the filename in here and use NuOpenRO instead.
*/
int
DoStreamStuff(FILE* fp)
{
NuError err;
NuArchive* pArchive = nil;
err = NuStreamOpenRO(fp, &pArchive);
if (err != kNuErrNone) {
fprintf(stderr, "ERROR: unable to open stream archive (err=%d)\n", err);
goto bail;
}
printf("*** Streaming contents!\n");
err = NuContents(pArchive, ShowContents);
if (err != kNuErrNone) {
fprintf(stderr, "ERROR: NuContents failed (err=%d)\n", err);
goto bail;
}
bail:
if (pArchive != nil) {
NuError err2 = NuClose(pArchive);
if (err == kNuErrNone)
err = err2;
}
return err;
}
/*
* Grab the name of an archive to read. If "-" was given, use stdin.
*/
int
main(int argc, char** argv)
{
long major, minor, bug;
const char* pBuildDate;
FILE* infp = nil;
int cc;
(void) NuGetVersion(&major, &minor, &bug, &pBuildDate, nil);
printf("Using NuFX lib %ld.%ld.%ld built on or after %s\n",
major, minor, bug, pBuildDate);
if (argc != 2) {
fprintf(stderr, "Usage: %s (archive-name|-)\n", argv[0]);
exit(2);
}
if (strcmp(argv[1], "-") == 0)
infp = stdin;
else {
infp = fopen(argv[1], kNuFileOpenReadOnly);
if (infp == nil) {
fprintf(stderr, "ERROR: unable to open '%s'\n", argv[1]);
exit(1);
}
}
cc = DoStreamStuff(infp);
exit(cc != 0);
}

109
nulib2/Add.c Normal file
View File

@ -0,0 +1,109 @@
/*
* Nulib2
* Copyright (C) 2000 by Andy McFadden, All Rights Reserved.
* This is free software; you can redistribute it and/or modify it under the
* terms of the GNU General Public License, see the file COPYING.
*
* Add files to or update files in the archive.
*/
#include "Nulib2.h"
static NuError AddToArchive(NulibState* pState, NuArchive* pArchive);
/*
* Add the specified files to a new or existing archive.
*/
NuError
DoAdd(NulibState* pState)
{
NuError err;
NuArchive* pArchive = nil;
long flushStatus;
assert(pState != nil);
err = OpenArchiveReadWrite(pState);
if (err != kNuErrNone)
goto bail;
pArchive = NState_GetNuArchive(pState);
assert(pArchive != nil);
NState_SetMatchCount(pState, 0);
/* tell them about the list of files */
err = AddToArchive(pState, pArchive);
if (err != kNuErrNone)
goto bail;
/*(void)NuDebugDumpArchive(pArchive);*/
if (!NState_GetMatchCount(pState))
printf("%s: no records matched\n", gProgName);
bail:
if (pArchive != nil) {
NuError err2;
#if 0
if (err != kNuErrNone) {
printf("Attempting to flush changes in spite of errors...\n");
err = kNuErrNone;
}
#endif
if (err == kNuErrNone) {
err = NuFlush(pArchive, &flushStatus);
if (err != kNuErrNone && err == kNuErrNone) {
ReportError(err,
"Unable to flush archive changes (status=0x%04lx)",
flushStatus);
NuAbort(pArchive);
}
} else {
NuAbort(pArchive);
}
err2 = NuClose(pArchive);
assert(err2 == kNuErrNone);
}
return err;
}
/*
* Add the requested files to the specified archive.
*
* This just results in NuAddFile calls; the deferred write operation
* isn't initiated.
*/
static NuError
AddToArchive(NulibState* pState, NuArchive* pArchive)
{
NuError err = kNuErrNone;
char* const* pSpec;
ulong fileCount;
int i;
assert(pState != nil);
assert(pArchive != nil);
if (!NState_GetFilespecCount(pState)) {
err = kNuErrSyntax;
ReportError(err, "no files were specified");
}
fileCount = 0;
pSpec = NState_GetFilespecPointer(pState);
for (i = NState_GetFilespecCount(pState); i > 0; i--, pSpec++) {
err = AddFile(pState, pArchive, *pSpec);
if (err != kNuErrNone)
goto bail;
}
bail:
return err;
}

935
nulib2/ArcUtils.c Normal file
View File

@ -0,0 +1,935 @@
/*
* Nulib2
* Copyright (C) 2000 by Andy McFadden, All Rights Reserved.
* This is free software; you can redistribute it and/or modify it under the
* terms of the GNU General Public License, see the file COPYING.
*
* Common archive-related utility functions.
*/
#include "Nulib2.h"
/*
* ===========================================================================
* Output pathnames
* ===========================================================================
*/
/*
* General-purpose output pathname filter, invoked by nufxlib via a
* callback. Normalizes the pathname to match the current OS requirements.
*
* If we're extracting to stdout, this fills in the "newFp" field instead.
*
* The buffer we return to the archive library will be overwritten the
* next time this function gets called. This is expected.
*/
static NuResult
OutputPathnameFilter(NuArchive* pArchive, void* vproposal)
{
NuPathnameProposal* pathProposal = vproposal;
NulibState* pState;
const char* newPathname;
NuRecordIdx renameFromIdx;
char* renameToStr;
char* resultBuf;
assert(pArchive != nil);
(void) NuGetExtraData(pArchive, (void**) &pState);
assert(pState != nil);
/* handle extract-to-pipe */
if (NState_GetCommand(pState) == kCommandExtractToPipe) {
pathProposal->newDataSink = NState_GetPipeSink(pState);
NState_SetSuppressOutput(pState, true);
return kNuOK;
}
/*
* If they're trying to rename this file, do so now. We don't run
* the output through the normalizer; if the user typed it, assume
* that it's okay (and let the OS tell them if it isn't).
*/
renameToStr = NState_GetRenameToStr(pState);
if (renameToStr != nil) {
renameFromIdx = NState_GetRenameFromIdx(pState);
if (renameFromIdx == pathProposal->pRecord->recordIdx) {
/* right source file, proceed with the rename */
NState_SetTempPathnameLen(pState, strlen(renameToStr) +1);
resultBuf = NState_GetTempPathnameBuf(pState);
assert(resultBuf != nil);
strcpy(resultBuf, renameToStr);
pathProposal->newPathname = resultBuf;
}
/* free up renameToStr */
NState_SetRenameToStr(pState, nil);
goto bail;
}
/*
* Convert the pathname into something suitable for the current OS.
*/
newPathname = NormalizePath(pState, pathProposal);
if (newPathname == nil) {
ReportError(kNuErrNone, "unable to convert pathname");
return kNuAbort;
}
pathProposal->newPathname = newPathname;
bail:
return kNuOK;
}
/*
* ===========================================================================
* User input
* ===========================================================================
*/
/*
* Get a single character from the input.
*
* For portability, I'm just getting a line of input and keeping the
* first character. A fancier version would play with line disciplines
* so you wouldn't have to hit "return".
*/
static char
GetReplyChar(char defaultReply)
{
char tmpBuf[32];
if (fgets(tmpBuf, sizeof(tmpBuf), stdin) == nil)
return defaultReply;
if (tmpBuf[0] == '\n' || tmpBuf[0] == '\r')
return defaultReply;
return tmpBuf[0];
}
#define kMaxInputLen 128
/*
* Get a string back from the user.
*
* String returned should be freed by the caller.
*/
static char*
GetReplyString(const char* prompt)
{
char buf[kMaxInputLen];
char* result;
int len;
printf("%s", prompt);
fflush(stdout);
result = fgets(buf, sizeof(buf), stdin);
if (result == nil || feof(stdin) || ferror(stdin) || buf[0] == '\0' ||
buf[0] == '\n')
{
return nil;
}
/* nuke the terminating '\n', which is lots of fun in filenames */
len = strlen(buf);
if (buf[len-1] == '\n')
buf[len-1] = '\0';
return strdup(buf);
}
/*
* Get a one-line comment from the user, of at most "maxLen" bytes.
*
* If the user enters a blank line, return "nil".
*
* A pointer to a newly-allocated buffer is returned.
*/
char*
GetSimpleComment(NulibState* pState, const char* pathname, int maxLen)
{
char* buf = nil;
char* result;
int len;
buf = Malloc(maxLen);
if (buf == nil)
return nil;
printf("Enter one-line comment for '%s'\n: ", pathname);
fflush(stdout);
result = fgets(buf, maxLen, stdin);
if (result == nil || feof(stdin) || ferror(stdin) || buf[0] == '\0' ||
buf[0] == '\n')
{
Free(buf);
return nil;
}
/* nuke the terminating '\n', which we don't need */
len = strlen(buf);
if (buf[len-1] == '\n')
buf[len-1] = '\0';
return buf;
}
/*
* ===========================================================================
* Callbacks (progress updates, error handling, record selection)
* ===========================================================================
*/
#define kMaxDisplayLen 60
/*
* Returns "true" if the filespec in "spec" matches what's in "pRecord".
*
* (Someday "spec" might be a regexp.)
*/
static Boolean
SpecMatchesRecord(NulibState* pState, const char* spec, const NuRecord* pRecord)
{
if (NState_GetModRecurse(pState))
return (strncmp(spec, pRecord->filename, strlen(spec)) == 0);
else
return (strcmp(spec, pRecord->filename) == 0);
}
/*
* Determine whether the current record we're examining is described by
* the file specification given on the command line.
*
* If no filespec was provided, then all records are "specified".
*/
Boolean
IsSpecified(NulibState* pState, const NuRecord* pRecord)
{
char* const* pSpec;
int i;
if (!NState_GetFilespecCount(pState))
return true;
pSpec = NState_GetFilespecPointer(pState);
for (i = NState_GetFilespecCount(pState); i > 0; i--, pSpec++) {
if (SpecMatchesRecord(pState, *pSpec, pRecord))
return true;
}
return false;
}
/*
* General-purpose selection filter, invoked as a callback. Compares the
* selection proposal with the filenames in "filespec".
*/
NuResult
SelectionFilter(NuArchive* pArchive, void* vproposal)
{
const NuSelectionProposal* selProposal = vproposal;
NulibState* pState;
assert(pArchive != nil);
(void) NuGetExtraData(pArchive, (void**) &pState);
assert(pState != nil);
if (IsSpecified(pState, selProposal->pRecord)) {
NState_IncMatchCount(pState);
/* we don't get progress notifications for delete, so do it here */
if (NState_GetCommand(pState) == kCommandDelete)
printf("Deleting %s\n", selProposal->pRecord->filename);
return kNuOK;
} else
return kNuSkip;
}
/*
* Print a three-digit progress percentage; range is 0% to 100%.
*/
void
PrintPercentage(ulong total, ulong progress)
{
ulong perc;
if (!total) {
/*printf(" %%");*/
printf(" ");
return;
}
if (total < 21474836) {
perc = (progress * 100 + 50) / total;
if (perc > 100)
perc = 100;
} else {
perc = progress / (total / 100);
if (perc > 100)
perc = 100;
}
printf("%3ld%%", perc);
}
/*
* Show our progress, unless we're expanding to a pipe. Invoked as a
* callback by nufxlib.
*/
NuResult
ProgressUpdater(NuArchive* pArchive, void* vProgress)
{
const NuProgressData* pProgress = vProgress;
NulibState* pState;
const char* percStr;
const char* actionStr;
char nameBuf[kMaxDisplayLen+1];
Boolean showName, eolConv;
assert(pArchive != nil);
(void) NuGetExtraData(pArchive, (void**) &pState);
assert(pState != nil);
if (NState_GetSuppressOutput(pState))
return kNuOK;
percStr = nil;
showName = false;
eolConv = false;
switch (pProgress->operation) {
case kNuOpAdd:
switch (pProgress->state) {
case kNuProgressPreparing:
case kNuProgressOpening:
actionStr = "adding ";
showName = true;
break;
case kNuProgressCompressing:
actionStr = "compressing";
break;
case kNuProgressStoring:
actionStr = "storing ";
break;
default:
actionStr = "?????? ";
break;
}
break;
case kNuOpExtract:
switch (pProgress->state) {
case kNuProgressPreparing:
case kNuProgressOpening:
actionStr = "extracting";
showName = true;
break;
case kNuProgressExpanding:
actionStr = "expanding ";
break;
case kNuProgressCopying:
actionStr = "extracting";
break;
default:
actionStr = "?????? ";
break;
}
if (pProgress->expand.convertEOL == kNuConvertOn)
eolConv = true;
break;
case kNuOpTest:
switch (pProgress->state) {
case kNuProgressPreparing:
case kNuProgressOpening:
showName = true;
/* no break */
case kNuProgressExpanding:
case kNuProgressCopying:
actionStr = "verifying";
break;
default:
actionStr = "?????? ";
break;
}
break;
default:
assert(0);
actionStr = "????";
}
switch (pProgress->state) {
case kNuProgressDone:
actionStr = nil;
percStr = "DONE\n";
break;
case kNuProgressSkipped:
actionStr = nil;
percStr = "SKIP\n";
break;
case kNuProgressFailed:
actionStr = nil;
percStr = "FAIL\n";
break;
default:
break;
}
if (showName) {
/*
* Use "pathname" (whole thing) rather than "filename" (file part).
* Could also use "origPathname", but I like to show what they're
* getting instead of what they're giving.
*/
int len = strlen(pProgress->pathname);
if (len < sizeof(nameBuf)) {
strcpy(nameBuf, pProgress->pathname);
} else {
nameBuf[0] = nameBuf[1] = '.';
strncpy(nameBuf+2, pProgress->pathname + len - (sizeof(nameBuf)-3),
sizeof(nameBuf)-3);
nameBuf[sizeof(nameBuf)-1] = '\0';
}
}
if (actionStr == nil && percStr != nil) {
printf("\r%s", percStr);
} else if (actionStr != nil && percStr == nil) {
if (percStr == nil) {
putc('\r', stdout);
PrintPercentage(pProgress->uncompressedLength,
pProgress->uncompressedProgress);
if (showName)
printf(" %s%s %s", actionStr, eolConv ? "+" : " ", nameBuf);
else
printf(" %s%s", actionStr, eolConv ? "+" : " ");
}
} else {
assert(0);
printf("????\n");
}
fflush(stdout);
/*printf(" %ld \n", pProgress->uncompressedProgress);*/
/*usleep(250000);*/
return kNuOK;
}
/*
* Decide whether or not to replace an existing file (during extract)
* or record (during add).
*/
static NuResult
HandleReplaceExisting(NulibState* pState, NuArchive* pArchive,
const NuErrorStatus* pErrorStatus)
{
NuResult result = kNuOK;
char* renameName;
char reply;
assert(pState != nil);
assert(pErrorStatus != nil);
assert(pErrorStatus->pathname != nil);
assert(pErrorStatus->canOverwrite);
assert(pErrorStatus->canSkip);
assert(pErrorStatus->canAbort);
if (NState_GetInputUnavailable(pState)) {
putc('\n', stdout);
ReportError(pErrorStatus->err, "Giving up");
result = kNuAbort;
goto bail;
}
while (1) {
printf("\n Replace %s? [y]es, [n]o, [A]ll, [N]one",
pErrorStatus->pathname);
if (pErrorStatus->canRename) /* renaming records not allowed */
printf(", [r]ename: ");
else
printf(": ");
fflush(stdout);
reply = GetReplyChar('n');
switch (reply) {
case 'y':
result = kNuOverwrite;
goto bail;
case 'n':
result = kNuSkip;
goto bail;
case 'A':
(void) NuSetValue(pArchive, kNuValueHandleExisting,
kNuAlwaysOverwrite);
result = kNuOverwrite;
goto bail;
case 'N':
(void) NuSetValue(pArchive, kNuValueHandleExisting,
kNuNeverOverwrite);
result = kNuSkip;
goto bail;
case 'r':
if (!pErrorStatus->canRename) {
printf("Response not acceptable\n");
break; /* continue in "while" loop */
}
renameName = GetReplyString("New name: ");
if (renameName == nil)
break; /* continue in "while" loop */
if (pErrorStatus->pRecord == nil) {
ReportError(kNuErrNone, "Unexpected nil record");
break; /* continue in "while" loop */
}
NState_SetRenameFromIdx(pState,
pErrorStatus->pRecord->recordIdx);
NState_SetRenameToStr(pState, renameName);
result = kNuRename;
goto bail;
case 'q': /* stealth option to quit */
case 'Q':
result = kNuAbort;
goto bail;
default:
printf("Response not understood -- please use y/n/A/N/r\n");
break; /* continue in "while" loop */
}
}
bail:
return result;
}
/*
* Found a bad CRC... should we press onward?
*
* Note pErrorStatus->pathname may be nil if the error was found in the
* master header or in the record header.
*/
static NuResult
HandleBadCRC(NulibState* pState, NuArchive* pArchive,
const NuErrorStatus* pErrorStatus)
{
NuResult result = kNuOK;
char reply;
assert(pState != nil);
assert(pErrorStatus != nil);
if (NState_GetInputUnavailable(pState)) {
putc('\n', stderr);
ReportError(pErrorStatus->err, "Giving up");
result = kNuAbort;
goto bail;
}
while (1) {
if (pErrorStatus->pathname != nil)
fprintf(stderr, "\n Found a bad CRC in %s\n",
pErrorStatus->pathname);
else
fprintf(stderr, "\n Found a bad CRC in the archive\n");
fprintf(stderr,
" Archive may be damaged, continue anyway? [y]es, [n]o: ");
fflush(stderr);
reply = GetReplyChar('n');
switch (reply) {
case 'y':
result = kNuIgnore;
goto bail;
case 'n':
case 'N':
result = kNuAbort;
goto bail;
default:
fprintf(stderr, "Response not understood -- please use y/n\n");
break; /* continue in "while" loop */
}
}
bail:
return result;
}
#if 0
/*
* Tried to add a nonexistent file; continue?
*/
static NuResult
HandleAddNotFound(NulibState* pState, NuArchive* pArchive,
const NuErrorStatus* pErrorStatus)
{
NuResult result = kNuOK;
char reply;
assert(pState != nil);
assert(pErrorStatus != nil);
assert(pErrorStatus->pathname != nil);
if (NState_GetInputUnavailable(pState)) {
putc('\n', stdout);
ReportError(pErrorStatus->err, "Giving up");
result = kNuAbort;
goto bail;
}
while (1) {
fprintf("\n Couldn't find %s, continue? [y]es, [n]o: ",
pErrorStatus->pathname);
fflush(stderr);
reply = GetReplyChar('n');
switch (reply) {
case 'y':
result = kNuSkip;
goto bail;
case 'n':
case 'N':
result = kNuAbort;
goto bail;
default:
fprintf(stderr, "Response not understood -- please use y/n\n");
break; /* continue in "while" loop */
}
}
bail:
return result;
}
#endif
/*
* Something failed, and the user may want to choose how to handle it.
* Invoked as a callback.
*/
NuResult
ErrorHandler(NuArchive* pArchive, void* vErrorStatus)
{
const NuErrorStatus* pErrorStatus = vErrorStatus;
NulibState* pState;
NuResult result;
assert(pArchive != nil);
(void) NuGetExtraData(pArchive, (void**) &pState);
assert(pState != nil);
/* default action is to abort the current operation */
result = kNuAbort;
/*
* When extracting, the error handler callback gets invoked for several
* different problems because we might want to rename the file. Also,
* because extractions are done with "bulk" calls, returning an
* individual error message would be meaningless.
*
* When adding files, the NuAddFile and NuAddRecord calls can return
* immediate, specific results for a single add. The only reasons for
* calling here are to decide if an existing record should be replaced
* or not (without even an option to rename), or to decide what to do
* when the NuFlush call runs into a problem while adding a file.
*/
if (pErrorStatus->operation == kNuOpExtract) {
if (pErrorStatus->err == kNuErrFileExists) {
result = HandleReplaceExisting(pState, pArchive, pErrorStatus);
} else if (pErrorStatus->err == kNuErrNotNewer) {
/* if we were expecting this, it's okay */
if (NState_GetModFreshen(pState) || NState_GetModUpdate(pState)) {
printf("\rSKIP\n");
result = kNuSkip;
} else {
DBUG(("WEIRD one\n"));
}
} else if (pErrorStatus->err == kNuErrDuplicateNotFound) {
/* if we were expecting this, it's okay */
if (NState_GetModFreshen(pState)) {
printf("\rSKIP\n");
result = kNuSkip;
} else {
DBUG(("WEIRD two\n"));
}
}
} else if (pErrorStatus->operation == kNuOpAdd) {
if (pErrorStatus->err == kNuErrRecordExists) {
/* if they want to update or freshen, don't hassle them */
if (NState_GetModFreshen(pState) || NState_GetModUpdate(pState))
result = kNuOverwrite;
else
result = HandleReplaceExisting(pState, pArchive, pErrorStatus);
} else if (pErrorStatus->err == kNuErrFileNotFound) {
/*
* This should never happen, because NuLib2 verifies the
* presence of the files. (If you want to test this out,
* you have to "sabotage" AddFile, or remove a file from disk
* while NuFlush is running.)
*/
assert(0);
/*result = HandleAddNotFound(pState, pArchive, pErrorStatus);*/
}
} else if (pErrorStatus->operation == kNuOpTest) {
if (pErrorStatus->err == kNuErrBadMHCRC ||
pErrorStatus->err == kNuErrBadRHCRC ||
pErrorStatus->err == kNuErrBadThreadCRC ||
pErrorStatus->err == kNuErrBadDataCRC)
{
result = HandleBadCRC(pState, pArchive, pErrorStatus);
}
}
return result;
}
#if 0
/*
* Display an error message.
*
* (This was just a test to see if it worked... NufxLib's default behavior
* is fine for NuLib2.)
*/
NuResult
ErrorMessageHandler(NuArchive* pArchive, void* vErrorMessage)
{
const NuErrorMessage* pErrorMessage = (const NuErrorMessage*) vErrorMessage;
fprintf(stderr, "%s%d %3d %s:%d %s %s\n",
pArchive == nil ? "(GLOBAL)" : "",
pErrorMessage->isDebug, pErrorMessage->err, pErrorMessage->file,
pErrorMessage->line, pErrorMessage->function, pErrorMessage->message);
return kNuOK;
}
#endif
/*
* ===========================================================================
* Open an archive
* ===========================================================================
*/
/* an archive name of "-" indicates we want to use stdin */
static const char* kStdinArchive = "-";
/*
* Determine whether the access bits on the record make it a read-only
* file or not.
*
* Uses a simplified view of the access flags.
*/
Boolean
IsRecordReadOnly(const NuRecord* pRecord)
{
if (pRecord->recAccess == 0x21L || pRecord->recAccess == 0x01L)
return true;
else
return false;
}
/*
* Returns "true" if "archiveName" is the name we use to represent stdin.
*/
Boolean
IsFilenameStdin(const char* archiveName)
{
assert(archiveName != nil);
return (strcmp(archiveName, kStdinArchive) == 0);
}
#define BailError(err) { if (err != kNuErrNone) goto bail; }
/*
* Open the archive in read-only mode. We use "file mode" for a file, or
* "streaming mode" for stdin.
*/
NuError
OpenArchiveReadOnly(NulibState* pState)
{
NuError err;
NuArchive* pArchive;
assert(pState != nil);
if (IsFilenameStdin(NState_GetArchiveFilename(pState))) {
err = NuStreamOpenRO(stdin, &pArchive);
if (err != kNuErrNone) {
ReportError(err, "unable to open create stdin archive");
goto bail;
}
/*
* Since the archive is on stdin, we can't ask the user questions.
* On a UNIX system we could open /dev/tty, but that's not portable,
* and I don't think archives on stdin are going to be popular
* enough to make this worth doing.
*/
NState_SetInputUnavailable(pState, true);
} else {
err = NuOpenRO(NState_GetArchiveFilename(pState), &pArchive);
if (err != kNuErrNone) {
ReportError(err, "unable to open '%s'",
NState_GetArchiveFilename(pState));
goto bail;
}
}
/* introduce them */
NState_SetNuArchive(pState, pArchive);
err = NuSetExtraData(pArchive, pState);
err = NuSetSelectionFilter(pArchive, SelectionFilter);
err = NuSetOutputPathnameFilter(pArchive, OutputPathnameFilter);
err = NuSetProgressUpdater(pArchive, ProgressUpdater);
err = NuSetErrorHandler(pArchive, ErrorHandler);
/*err = NuSetErrorMessageHandler(pArchive, ErrorMessageHandler);*/
/* set the EOL conversion */
if (NState_GetModConvertAll(pState))
err = NuSetValue(pArchive, kNuValueConvertExtractedEOL, kNuConvertOn);
else if (NState_GetModConvertText(pState))
err = NuSetValue(pArchive, kNuValueConvertExtractedEOL, kNuConvertAuto);
else
err = NuSetValue(pArchive, kNuValueConvertExtractedEOL, kNuConvertOff);
BailError(err);
/* handle "-s" flag */
if (NState_GetModOverwriteExisting(pState)) {
err = NuSetValue(pArchive, kNuValueHandleExisting, kNuAlwaysOverwrite);
BailError(err);
}
/* handle "-f" and "-u" flags (this overrides "-s" during extraction) */
if (NState_GetModFreshen(pState) || NState_GetModUpdate(pState)) {
err = NuSetValue(pArchive, kNuValueOnlyUpdateOlder, true);
BailError(err);
}
if (NState_GetModFreshen(pState)) {
err = NuSetValue(pArchive, kNuValueHandleExisting, kNuMustOverwrite);
BailError(err);
}
DBUG(("--- enabling ShrinkIt compatibility mode\n"));
err = NuSetValue(pArchive, kNuValueMimicSHK, true);
BailError(err);
if (strcmp(SYSTEM_DEFAULT_EOL, "\r") == 0)
err = NuSetValue(pArchive, kNuValueEOL, kNuEOLCR);
else if (strcmp(SYSTEM_DEFAULT_EOL, "\n") == 0)
err = NuSetValue(pArchive, kNuValueEOL, kNuEOLLF);
else if (strcmp(SYSTEM_DEFAULT_EOL, "\r\n") == 0)
err = NuSetValue(pArchive, kNuValueEOL, kNuEOLCRLF);
else {
assert(0);
err = kNuErrInternal;
ReportError(err, "Unknown SYSTEM_DEFAULT_EOL '%s'", SYSTEM_DEFAULT_EOL);
goto bail;
}
BailError(err);
bail:
return err;
}
/*
* Open the archive in read-write mode, for purposes of adding, deleting,
* or updating files. We don't plan on extracting anything with this.
*
* "Streaming mode" isn't allowed.
*/
NuError
OpenArchiveReadWrite(NulibState* pState)
{
NuError err = kNuErrNone;
NuArchive* pArchive;
char* tempName = nil;
assert(pState != nil);
assert(IsFilenameStdin(NState_GetArchiveFilename(pState)) == false);
tempName = MakeTempArchiveName(pState);
if (tempName == nil)
goto bail;
DBUG(("TEMP NAME = '%s'\n", tempName));
err = NuOpenRW(NState_GetArchiveFilename(pState), tempName,
kNuOpenCreat, &pArchive);
if (err != kNuErrNone) {
ReportError(err, "unable to open '%s'",
NState_GetArchiveFilename(pState));
goto bail;
}
/* introduce them */
NState_SetNuArchive(pState, pArchive);
err = NuSetExtraData(pArchive, pState);
BailError(err);
err = NuSetSelectionFilter(pArchive, SelectionFilter);
BailError(err)
err = NuSetProgressUpdater(pArchive, ProgressUpdater);
BailError(err)
err = NuSetErrorHandler(pArchive, ErrorHandler);
BailError(err)
/*err = NuSetErrorMessageHandler(pArchive, ErrorMessageHandler);*/
/* handle "-0" flag */
if (NState_GetModNoCompression(pState)) {
err = NuSetValue(pArchive, kNuValueDataCompression, kNuCompressNone);
BailError(err);
}
/* handle "-f" and "-u" flags */
/* (BUG: if "-f" is set, creating a new archive is impossible) */
if (NState_GetModFreshen(pState) || NState_GetModUpdate(pState)) {
err = NuSetValue(pArchive, kNuValueOnlyUpdateOlder, true);
BailError(err);
}
if (NState_GetModFreshen(pState)) {
err = NuSetValue(pArchive, kNuValueHandleExisting, kNuMustOverwrite);
BailError(err);
}
DBUG(("--- enabling ShrinkIt compatibility mode\n"));
err = NuSetValue(pArchive, kNuValueMimicSHK, true);
BailError(err);
/* this probably isn't needed here, but set it anyway */
if (strcmp(SYSTEM_DEFAULT_EOL, "\r") == 0)
err = NuSetValue(pArchive, kNuValueEOL, kNuEOLCR);
else if (strcmp(SYSTEM_DEFAULT_EOL, "\n") == 0)
err = NuSetValue(pArchive, kNuValueEOL, kNuEOLLF);
else if (strcmp(SYSTEM_DEFAULT_EOL, "\r\n") == 0)
err = NuSetValue(pArchive, kNuValueEOL, kNuEOLCRLF);
else {
assert(0);
err = kNuErrInternal;
ReportError(err, "Unknown SYSTEM_DEFAULT_EOL '%s'", SYSTEM_DEFAULT_EOL);
goto bail;
}
BailError(err);
/*(void) NuSetValue(pArchive, kNuValueAllowDuplicates, true);*/
bail:
Free(tempName);
return err;
}

340
nulib2/COPYING Normal file
View File

@ -0,0 +1,340 @@
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
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) 19yy <name of author>
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
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) 19yy name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
<signature of Ty Coon>, 1 April 1989
Ty Coon, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Library General
Public License instead of this License.

123
nulib2/ChangeLog.txt Normal file
View File

@ -0,0 +1,123 @@
2000/05/18 ***** v1.0.0 shipped *****
2000/05/18 fadden
- added nulib2 to set of things stripped by "distbin"
- updated version information to indicate final release
2000/03/25 ***** v0.6.1 shipped *****
2000/03/25 fadden
- Sheppy says Mac OS X PPC v1.02 and v1.2 work with minor SysDefs tweak
2000/03/05 ***** v0.6.0 (beta) shipped *****
2000/03/05 fadden
- don't call mktemp(), just pass template into NuOpenRW
- removed DEBUG_MSGS from default CFLAGS
- updated version information to indicate beta release
2000/02/24 ***** v0.5.1 shipped *****
2000/02/20 changes from Scott Blackman
- portability fixes for DJGPP under Win95
2000/02/17 changes from Devin Reade
- portability fixes for BSD, AIX, and others
- added "distbin" target
2000/02/09 ***** v0.5.0 (alpha) shipped *****
2000/02/09 fadden
- changed the comparison used when extracting/deleting a list of files
from strcasecmp to strcmp, since NufxLib does case-sensitive compares.
- fixed the percentage for files and archives larger than 21MB
2000/02/08 fadden
- tweaked the BeOS/PPC config around a little
- deleted some commas to make "gcc -pedantic" happy
- changed version to x.y.z format here too
- generalized the "aux" handling to include all MS-DOS device names
2000/02/06 fadden
- include @CFLAGS@ in case somebody wants to override them
2000/02/06 ***** v0.4b shipped *****
2000/02/06 fadden
- added "install-shared" make target
- portability fixes for HP/UX
2000/02/06 ***** v0.4a shipped *****
2000/02/06 fadden
- massaged configure.in for BeOS, and added some type casts for mwerks
2000/02/06 ***** v0.4 shipped *****
2000/02/05 fadden
- added "mkinstalldirs" to install target
- added Win32 makefile
- made a few implicit typecasts explicit for Visual C++'s benefit
- change "aux" to "_aux", because FAT filesystems choke on it
2000/02/04 fadden
- added Win32 recursive directory descent
2000/02/02 fadden
- minor changes to get it working under Win32 (Visual C++ 6.0)
- added --enable-dmalloc to configuration
2000/02/01 fadden
- screen out leading "./", and junk the path if ".." shows up in path
- don't try to add comments to records we're skipping
- set kNuValueEOL appropriately for the current system
2000/01/29 ***** v0.3 shipped *****
2000/01/29 fadden
- added "make install" target, with the standard autoconf defines
- added some examples to the man page
2000/01/28 fadden
- merged "Kind" and "Type" columns in "v" output
- display a '+' when doing EOL conversions on an extracted file
2000/01/26 fadden
- added UI for allowing the user to ignore bad CRCs
- implemented "-j" (junk paths) for add and extract
- implemented "-c" (comments) for add and extract
- added totals to bottom of "v" output
2000/01/25 fadden
- when extracting without type preservation, append "_rsrc_" to
resource forks
2000/01/24 fadden
- added support for "-k" (add as disk image) flag
2000/01/24 ***** v0.2 shipped *****
2000/01/22 fadden
- added support for "-u" (update) and "-f" (freshen) flags
- set file dates in AddFile call
2000/01/20 fadden
- restructed the progress updater
2000/01/19 fadden
- normalized SysDefs.h, changing UNIX to UNIX_LIKE and defining for BeOS
- added "shared" target to makefile
- added BeOS stuff to autoconf setup
2000/01/17 fadden
- started recording locked/unlocked status
- some BeOS/Metrowerks "it's not gcc" changes from Eric Shepherd
- implemented "-s" (stomp existing) and "-0" (no compression) modifiers
2000/01/17 ***** v0.1 shipped *****
(much time passes)
mid-1998 fadden
- work begins

46
nulib2/Delete.c Normal file
View File

@ -0,0 +1,46 @@
/*
* Nulib2
* Copyright (C) 2000 by Andy McFadden, All Rights Reserved.
* This is free software; you can redistribute it and/or modify it under the
* terms of the GNU General Public License, see the file COPYING.
*
* Delete files from the archive.
*/
#include "Nulib2.h"
/*
* Delete the specified files.
*
* This uses the "bulk" delete call, allowing the SelectionFilter callback
* to do the matching against specified filenames.
*/
NuError
DoDelete(NulibState* pState)
{
NuError err;
NuArchive* pArchive = nil;
assert(pState != nil);
err = OpenArchiveReadWrite(pState);
if (err != kNuErrNone)
goto bail;
pArchive = NState_GetNuArchive(pState);
assert(pArchive != nil);
NState_SetMatchCount(pState, 0);
err = NuDelete(pArchive);
if (err != kNuErrNone)
goto bail;
if (!NState_GetMatchCount(pState))
printf("%s: no records matched\n", gProgName);
bail:
if (pArchive != nil)
(void) NuClose(pArchive);
return err;
}

172
nulib2/Extract.c Normal file
View File

@ -0,0 +1,172 @@
/*
* Nulib2
* Copyright (C) 2000 by Andy McFadden, All Rights Reserved.
* This is free software; you can redistribute it and/or modify it under the
* terms of the GNU General Public License, see the file COPYING.
*
* Extract files and test archives.
*/
#include "Nulib2.h"
/*
* Extract all of the records from the archive, pulling out and displaying
* comment threads.
*
* The "bulk extract" call doesn't deal with comments. Since we want to
* show them while we're extracting the files, we have to manually find
* and extract them.
*/
static NuError
ExtractAllRecords(NulibState* pState, NuArchive* pArchive)
{
NuError err;
const NuRecord* pRecord;
const NuThread* pThread;
NuRecordIdx recordIdx;
NuAttr numRecords;
int idx, threadIdx;
DBUG(("--- doing manual extract\n"));
assert(NState_GetCommand(pState) == kCommandExtract); /* no "-p" here */
err = NuGetAttr(pArchive, kNuAttrNumRecords, &numRecords);
for (idx = 0; idx < (int) numRecords; idx++) {
err = NuGetRecordIdxByPosition(pArchive, idx, &recordIdx);
if (err != kNuErrNone) {
fprintf(stderr, "ERROR: couldn't get record #%d (err=%d)\n",
idx, err);
goto bail;
}
err = NuGetRecord(pArchive, recordIdx, &pRecord);
if (err != kNuErrNone) {
fprintf(stderr, "ERROR: unable to get recordIdx %ld\n", recordIdx);
goto bail;
}
/* do we want to extract this record? */
if (!IsSpecified(pState, pRecord))
continue;
NState_IncMatchCount(pState);
/*
* Look for a comment thread.
*/
for (threadIdx = 0; (ulong)threadIdx < pRecord->recTotalThreads;
threadIdx++)
{
pThread = NuGetThread(pRecord, threadIdx);
assert(pThread != nil);
if (NuGetThreadID(pThread) == kNuThreadIDComment &&
pThread->actualThreadEOF > 0)
{
printf("----- '%s':\n", pRecord->filename);
err = NuExtractThread(pArchive, pThread->threadIdx,
NState_GetCommentSink(pState));
if (err != kNuErrNone) {
printf("[comment extraction failed, continuing\n");
} else {
printf("\n-----\n");
}
}
}
/* extract the record, using the usual mechanisms */
err = NuExtractRecord(pArchive, recordIdx);
if (err != kNuErrNone)
goto bail;
}
bail:
return err;
}
/*
* Extract the specified files.
*/
NuError
DoExtract(NulibState* pState)
{
NuError err;
NuArchive* pArchive = nil;
assert(pState != nil);
err = OpenArchiveReadOnly(pState);
if (err != kNuErrNone)
goto bail;
pArchive = NState_GetNuArchive(pState);
assert(pArchive != nil);
NState_SetMatchCount(pState, 0);
/*
* If we're not interested in comments, just use the "bulk" extract
* call. If we want comments, we need to do this one at a time.
*/
if (!NState_GetModComments(pState)) {
err = NuExtract(pArchive);
if (err != kNuErrNone)
goto bail;
} else {
err = ExtractAllRecords(pState, pArchive);
if (err != kNuErrNone)
goto bail;
}
if (!NState_GetMatchCount(pState))
printf("%s: no records match\n", gProgName);
bail:
if (pArchive != nil)
(void) NuClose(pArchive);
return err;
}
/*
* Extract the specified files to stdout.
*/
NuError
DoExtractToPipe(NulibState* pState)
{
/* we handle the "to pipe" part farther down */
return DoExtract(pState);
}
/*
* Do an integrity check on one or more records in the archive.
*/
NuError
DoTest(NulibState* pState)
{
NuError err;
NuArchive* pArchive = nil;
assert(pState != nil);
err = OpenArchiveReadOnly(pState);
if (err != kNuErrNone)
goto bail;
pArchive = NState_GetNuArchive(pState);
assert(pArchive != nil);
NState_SetMatchCount(pState, 0);
err = NuTest(pArchive);
if (err != kNuErrNone)
goto bail;
if (!NState_GetMatchCount(pState))
printf("%s: no records match\n", gProgName);
bail:
if (pArchive != nil)
(void) NuClose(pArchive);
return err;
}

660
nulib2/Filename.c Normal file
View File

@ -0,0 +1,660 @@
/*
* Nulib2
* Copyright (C) 2000 by Andy McFadden, All Rights Reserved.
* This is free software; you can redistribute it and/or modify it under the
* terms of the GNU General Public License, see the file COPYING.
*
* Filename manipulation, including file type preservation.
*/
#include "Nulib2.h"
#include <ctype.h>
/*
* ===========================================================================
* Common definitions
* ===========================================================================
*/
#define kPreserveIndic '#' /* use # rather than $ for hex indication */
#define kFilenameExtDelim '.' /* separates extension from filename */
#define kResourceFlag 'r'
#define kDiskImageFlag 'i'
#define kMaxExtLen 4 /* ".1234" */
#define kResourceStr "_rsrc_"
/* must be longer then strlen(kResourceStr)... no problem there */
#define kMaxPathGrowth (sizeof("#XXXXXXXXYYYYYYYYZ")-1 + kMaxExtLen+1)
/* ProDOS file type names; must be entirely in upper case */
static const char gFileTypeNames[256][4] = {
"NON", "BAD", "PCD", "PTX", "TXT", "PDA", "BIN", "FNT",
"FOT", "BA3", "DA3", "WPF", "SOS", "$0D", "$0E", "DIR",
"RPD", "RPI", "AFD", "AFM", "AFR", "SCL", "PFS", "$17",
"$18", "ADB", "AWP", "ASP", "$1C", "$1D", "$1E", "$1F",
"TDM", "$21", "$22", "$23", "$24", "$25", "$26", "$27",
"$28", "$29", "8SC", "8OB", "8IC", "8LD", "P8C", "$2F",
"$30", "$31", "$32", "$33", "$34", "$35", "$36", "$37",
"$38", "$39", "$3A", "$3B", "$3C", "$3D", "$3E", "$3F",
"DIC", "OCR", "FTD", "$43", "$44", "$45", "$46", "$47",
"$48", "$49", "$4A", "$4B", "$4C", "$4D", "$4E", "$4F",
"GWP", "GSS", "GDB", "DRW", "GDP", "HMD", "EDU", "STN",
"HLP", "COM", "CFG", "ANM", "MUM", "ENT", "DVU", "FIN",
"$60", "$61", "$62", "$63", "$64", "$65", "$66", "$67",
"$68", "$69", "$6A", "BIO", "$6C", "TDR", "PRE", "HDV",
"$70", "$71", "$72", "$73", "$74", "$75", "$76", "$77",
"$78", "$79", "$7A", "$7B", "$7C", "$7D", "$7E", "$7F",
"$80", "$81", "$82", "$83", "$84", "$85", "$86", "$87",
"$88", "$89", "$8A", "$8B", "$8C", "$8D", "$8E", "$8F",
"$90", "$91", "$92", "$93", "$94", "$95", "$96", "$97",
"$98", "$99", "$9A", "$9B", "$9C", "$9D", "$9E", "$9F",
"WP ", "$A1", "$A2", "$A3", "$A4", "$A5", "$A6", "$A7",
"$A8", "$A9", "$AA", "GSB", "TDF", "BDF", "$AE", "$AF",
"SRC", "OBJ", "LIB", "S16", "RTL", "EXE", "PIF", "TIF",
"NDA", "CDA", "TOL", "DVR", "LDF", "FST", "$BE", "DOC",
"PNT", "PIC", "ANI", "PAL", "$C4", "OOG", "SCR", "CDV",
"FON", "FND", "ICN", "$CB", "$CC", "$CD", "$CE", "$CF",
"$D0", "$D1", "$D2", "$D3", "$D4", "MUS", "INS", "MDI",
"SND", "$D9", "$DA", "DBM", "$DC", "DDD", "$DE", "$DF",
"LBR", "$E1", "ATK", "$E3", "$E4", "$E5", "$E6", "$E7",
"$E8", "$E9", "$EA", "$EB", "$EC", "$ED", "R16", "PAS",
"CMD", "$F1", "$F2", "$F3", "$F4", "$F5", "$F6", "$F7",
"$F8", "OS ", "INT", "IVR", "BAS", "VAR", "REL", "SYS"
};
/*
* Some file extensions we recognize. When adding files with "extended"
* preservation mode, we try to assign types to files that weren't
* explicitly preserved, but nevertheless have a recognizeable type.
*
* geoff@gwlink.net points out that this really ought to be in an external
* file rather than a hard-coded table. Ought to fix that someday.
*/
static const struct {
const char* label;
ushort fileType;
ulong auxType;
uchar flags;
} gRecognizedExtensions[] = {
{ "ASM", 0xb0, 0x0003, 0 }, /* APW assembly source */
{ "C", 0xb0, 0x000a, 0 }, /* APW C source */
{ "H", 0xb0, 0x000a, 0 }, /* APW C header */
{ "BNY", 0xe0, 0x8000, 0 }, /* Binary II lib */
{ "BQY", 0xe0, 0x8000, 0 }, /* Binary II lib, w/ compress */
{ "BXY", 0xe0, 0x8000, 0 }, /* Binary II wrap around SHK */
{ "BSE", 0xe0, 0x8000, 0 }, /* Binary II wrap around SEA */
{ "SEA", 0xb3, 0xdb07, 0 }, /* GSHK SEA */
{ "GIF", 0xc0, 0x8006, 0 }, /* GIF image */
{ "JPG", 0x06, 0x0000, 0 }, /* JPEG (nicer than 'NON') */
{ "JPEG", 0x06, 0x0000, 0 }, /* JPEG (nicer than 'NON') */
{ "SHK", 0xe0, 0x8002, 0 }, /* ShrinkIt archive */
};
/*
* Return a pointer to the three-letter representation of the file type name.
*/
const char*
GetFileTypeString(ulong fileType)
{
if (fileType < NELEM(gFileTypeNames))
return gFileTypeNames[fileType];
else
return "???";
}
/*
* ===========================================================================
* File type preservation
* ===========================================================================
*/
/*
* Add a preservation string.
*
* "pathBuf" is assumed to have enough space to hold the current path
* plus kMaxPathGrowth more. It will be modified in place.
*/
static void
AddPreservationString(NulibState* pState,
const NuPathnameProposal* pPathProposal, char* pathBuf)
{
char extBuf[kMaxPathGrowth +1];
const NuRecord* pRecord;
const NuThread* pThread;
NuThreadID threadID;
char* cp;
assert(pState != nil);
assert(pPathProposal != nil);
assert(pathBuf != nil);
assert(NState_GetModPreserveType(pState));
pRecord = pPathProposal->pRecord;
pThread = pPathProposal->pThread;
assert(pRecord != nil);
assert(pThread != nil);
cp = extBuf;
/*
* Cons up a preservation string. On some platforms "sprintf" doesn't
* return the #of characters written, so we add it up manually.
*/
if (pRecord->recFileType < 0x100 && pRecord->recExtraType < 0x10000) {
sprintf(cp, "%c%02lx%04lx", kPreserveIndic, pRecord->recFileType,
pRecord->recExtraType);
cp += 7;
} else {
sprintf(cp, "%c%08lx%08lx", kPreserveIndic, pRecord->recFileType,
pRecord->recExtraType);
cp += 17;
}
threadID = NuMakeThreadID(pThread->thThreadClass, pThread->thThreadKind);
if (threadID == kNuThreadIDRsrcFork)
*cp++ = kResourceFlag;
else if (threadID == kNuThreadIDDiskImage)
*cp++ = kDiskImageFlag;
/*
* If they've asked for "extended" type preservation, then we need
* to retain either the existing extension or append an extension
* based on the ProDOS file type.
*/
if (NState_GetModPreserveTypeExtended(pState)) {
const char* pExt;
char* end;
/*
* Find extension. Note FindExtension guarantees there's at least
* one char after '.'.
*
* It's hard to know when this is right and when it isn't. It's
* fairly likely that a text file really ought to end in ".txt",
* and it's fairly unlikely that a BIN file should be ".bin", but
* where do the rest fall in? We might want to force TXT files
* to be ".txt", and perhaps do something clever for some others.
*/
if (pRecord->recFileType == 0x04)
pExt = nil;
else
pExt = FindExtension(pState, pathBuf);
if (pExt != nil && strlen(pExt+1) <= kMaxExtLen) {
pExt++; /* skip past the '.' */
/* if it's strictly decimal-numeric, don't use it (.1, .2, etc) */
(void) strtoul(pExt, &end, 10);
if (*end == '\0') {
pExt = nil;
} else {
/* if '#' appears in it, don't use it -- it'll confuse us */
const char* ccp = pExt;
while (*ccp != '\0') {
if (*ccp == '#') {
pExt = nil;
break;
}
ccp++;
}
}
} else {
/*
* There's no extension on the filename. Use the standard
* ProDOS type, if one exists for this entry. We don't use
* the table if it's "NON" or a hex value.
*/
if (pRecord->recFileType) {
pExt = GetFileTypeString(pRecord->recFileType);
if (pExt[0] == '?' || pExt[0] == '$')
pExt = nil;
}
}
if (pExt != nil) {
*cp++ = kFilenameExtDelim;
strcpy(cp, pExt);
cp += strlen(pExt);
}
}
/* make sure it's terminated */
*cp = '\0';
assert(cp - extBuf <= kMaxPathGrowth);
strcat(pathBuf, extBuf);
}
/*
* Normalize a path for the conventions on the output filesystem. This
* adds optional file type preservation.
*
* The path from the archive is in "pPathProposal". Thew new pathname
* will be placed in the "new pathname" section of "pPathProposal".
*
* The new pathname may be shorter (because characters were removed) or
* longer (if we add a "#XXYYYYZ" extension or replace chars with '%' codes).
*
* This returns the new pathname, which is held in NState's temporary
* pathname buffer.
*/
const char*
NormalizePath(NulibState* pState, NuPathnameProposal* pPathProposal)
{
NuError err = kNuErrNone;
char* pathBuf;
const char* startp;
const char* endp;
char* dstp;
char localFssep;
assert(pState != nil);
assert(pPathProposal != nil);
assert(pPathProposal->pathname != nil);
localFssep = NState_GetSystemPathSeparator(pState);
/*
* Set up temporary buffer space. The maximum possible expansion
* requires converting all chars to '%' codes and adding the longest
* possible preservation string.
*/
NState_SetTempPathnameLen(pState,
strlen(pPathProposal->pathname)*3 + kMaxPathGrowth +1);
pathBuf = NState_GetTempPathnameBuf(pState);
assert(pathBuf != nil);
if (pathBuf == nil)
return nil;
startp = pPathProposal->pathname;
dstp = pathBuf;
while (*startp == pPathProposal->filenameSeparator) {
/* ignore leading path sep; always extract to current dir */
startp++;
}
/* normalize all directory components and the filename component */
while (startp != nil) {
endp = strchr(startp, pPathProposal->filenameSeparator);
if (endp != nil) {
/* normalize directory component */
err = NormalizeDirectoryName(pState, startp, endp - startp,
pPathProposal->filenameSeparator, &dstp,
NState_GetTempPathnameLen(pState));
if (err != kNuErrNone)
goto bail;
*dstp++ = localFssep;
startp = endp +1;
} else {
/* normalize filename */
err = NormalizeFileName(pState, startp, strlen(startp),
pPathProposal->filenameSeparator, &dstp,
NState_GetTempPathnameLen(pState));
if (err != kNuErrNone)
goto bail;
/* add/replace extension if necessary */
*dstp++ = '\0';
if (NState_GetModPreserveType(pState)) {
AddPreservationString(pState, pPathProposal, pathBuf);
} else if (NuGetThreadID(pPathProposal->pThread) == kNuThreadIDRsrcFork)
{
/* add this in lieu of the preservation extension */
strcat(pathBuf, kResourceStr);
}
startp = nil; /* we're done */
}
}
pPathProposal->newPathname = pathBuf;
pPathProposal->newFilenameSeparator = localFssep;
/* check for overflow */
assert(dstp - pathBuf <=
(int)(strlen(pPathProposal->pathname) + kMaxPathGrowth));
/*
* If "junk paths" is set, drop everything but the last component.
*/
if (NState_GetModJunkPaths(pState)) {
char* lastFssep;
lastFssep = strrchr(pathBuf, localFssep);
if (lastFssep != nil) {
assert(*(lastFssep+1) != '\0'); /* should already have been caught*/
memmove(pathBuf, lastFssep+1, strlen(lastFssep+1)+1);
}
}
bail:
if (err != kNuErrNone)
return nil;
return pathBuf;
}
/*
* ===========================================================================
* File type restoration
* ===========================================================================
*/
/*
* Try to figure out what file type is associated with a filename extension.
*
* This checks the standard list of ProDOS types (which should catch things
* like "TXT" and "BIN") and the separate list of recognized extensions.
*/
static void
LookupExtension(NulibState* pState, const char* ext, ulong* pFileType,
ulong* pAuxType)
{
char uext3[4];
int i, extLen;
extLen = strlen(ext);
assert(extLen > 0);
/*
* First step is to try to find it in the recognized types list.
*/
for (i = 0; i < NELEM(gRecognizedExtensions); i++) {
if (strcasecmp(ext, gRecognizedExtensions[i].label) == 0) {
*pFileType = gRecognizedExtensions[i].fileType;
*pAuxType = gRecognizedExtensions[i].auxType;
goto bail;
}
}
/*
* Second step is to try to find it in the ProDOS types list.
*
* The extension is converted to upper case and padded with spaces.
*
* [do we want to obstruct matching on things like '$f7' here?]
*/
if (extLen <= 3) {
for (i = 2; i >= extLen; i--)
uext3[i] = ' ';
for ( ; i >= 0; i--)
uext3[i] = toupper(ext[i]);
uext3[3] = '\0';
/*printf("### converted '%s' to '%s'\n", ext, uext3);*/
for (i = 0; i < NELEM(gFileTypeNames); i++) {
if (strcmp(uext3, gFileTypeNames[i]) == 0) {
*pFileType = i;
goto bail;
}
}
}
bail:
return;
}
/*
* Try to associate some meaning with the file extension.
*/
void
InterpretExtension(NulibState* pState, const char* pathName, ulong* pFileType,
ulong* pAuxType)
{
const char* pExt;
assert(pState != nil);
assert(pathName != nil);
assert(pFileType != nil);
assert(pAuxType != nil);
pExt = FindExtension(pState, pathName);
if (pExt != nil)
LookupExtension(pState, pExt+1, pFileType, pAuxType);
}
/*
* Check to see if there's a preservation string on the filename. If so,
* set the filetype and auxtype information, and trim the preservation
* string off.
*
* We have to be careful not to trip on false-positive occurrences of '#'
* in the filename.
*/
Boolean
ExtractPreservationString(NulibState* pState, char* pathname, ulong* pFileType,
ulong* pAuxType, NuThreadID* pThreadID)
{
char numBuf[9];
ulong fileType, auxType;
NuThreadID threadID;
char* pPreserve;
char* cp;
int digitCount;
assert(pState != nil);
assert(pathname != nil);
assert(pFileType != nil);
assert(pAuxType != nil);
assert(pThreadID != nil);
pPreserve = strrchr(pathname, kPreserveIndic);
if (pPreserve == nil)
return false;
/* count up the #of hex digits */
digitCount = 0;
for (cp = pPreserve+1; *cp != '\0' && isxdigit((int)*cp); cp++)
digitCount++;
/* extract the file and aux type */
switch (digitCount) {
case 6:
/* ProDOS 1-byte type and 2-byte aux */
memcpy(numBuf, pPreserve+1, 2);
numBuf[2] = 0;
fileType = strtoul(numBuf, &cp, 16);
assert(cp == numBuf + 2);
auxType = strtoul(pPreserve+3, &cp, 16);
assert(cp == pPreserve + 7);
break;
case 16:
/* HFS 4-byte type and 4-byte creator */
memcpy(numBuf, pPreserve+1, 8);
numBuf[8] = 0;
fileType = strtoul(numBuf, &cp, 16);
assert(cp == numBuf + 8);
auxType = strtoul(pPreserve+9, &cp, 16);
assert(cp == pPreserve + 17);
break;
default:
/* not valid */
return false;
}
/* check for a threadID specifier */
threadID = kNuThreadIDDataFork;
switch (*cp) {
case kResourceFlag:
threadID = kNuThreadIDRsrcFork;
cp++;
break;
case kDiskImageFlag:
threadID = kNuThreadIDDiskImage;
cp++;
break;
default:
/* do nothing... yet */
break;
}
/* make sure we were the very last component */
switch (*cp) {
case kFilenameExtDelim: /* redundant "-ee" extension */
case '\0': /* end of string! */
break;
default:
return false;
}
/* truncate the original string, and return what we got */
*pPreserve = '\0';
*pFileType = fileType;
*pAuxType = auxType;
*pThreadID = threadID;
return true;
}
/*
* Remove NuLib2's normalization magic (e.g. "%2f" for '/').
*
* This always results in the filename staying the same length or getting
* smaller, so we can do it in place in the buffer.
*/
void
DenormalizePath(NulibState* pState, char* pathBuf)
{
const char* srcp;
char* dstp;
char ch;
srcp = pathBuf;
dstp = pathBuf;
while (*srcp != '\0') {
if (*srcp == kForeignIndic) {
srcp++;
if (*srcp == kForeignIndic) {
*dstp++ = kForeignIndic;
srcp++;
} else if (isxdigit((int)*srcp)) {
ch = HexDigit(*srcp) << 4;
srcp++;
if (isxdigit((int)*srcp)) {
/* valid, output char */
ch += HexDigit(*srcp);
*dstp++ = ch;
srcp++;
} else {
/* bogus '%' with trailing hex digit found! */
*dstp++ = kForeignIndic;
*dstp++ = *(srcp-1);
}
} else {
/* bogus lone '%s' found! */
*dstp++ = kForeignIndic;
}
} else {
*dstp++ = *srcp++;
}
}
*dstp = '\0';
assert(dstp <= srcp);
}
/*
* ===========================================================================
* Misc utils
* ===========================================================================
*/
/*
* Find the filename component of a local pathname. Uses the fssep defined
* in pState.
*
* Always returns a pointer to a string; never returns nil.
*/
const char*
FilenameOnly(NulibState* pState, const char* pathname)
{
const char* retstr;
const char* pSlash;
char* tmpStr = nil;
assert(pState != nil);
assert(pathname != nil);
pSlash = strrchr(pathname, NState_GetSystemPathSeparator(pState));
if (pSlash == nil) {
retstr = pathname; /* whole thing is the filename */
goto bail;
}
pSlash++;
if (*pSlash == '\0') {
if (strlen(pathname) < 2) {
retstr = pathname; /* the pathname is just "/"? Whatever */
goto bail;
}
/* some bonehead put an fssep on the very end; back up before it */
/* (not efficient, but this should be rare, and I'm feeling lazy) */
tmpStr = strdup(pathname);
tmpStr[strlen(pathname)-1] = '\0';
pSlash = strrchr(tmpStr, NState_GetSystemPathSeparator(pState));
if (pSlash == nil) {
retstr = pathname; /* just a filename with a '/' after it */
goto bail;
}
pSlash++;
if (*pSlash == '\0') {
retstr = pathname; /* I give up! */
goto bail;
}
retstr = pathname + (pSlash - tmpStr);
} else {
retstr = pSlash;
}
bail:
Free(tmpStr);
return retstr;
}
/*
* Return the filename extension found in a full pathname.
*
* An extension is the stuff following the last '.' in the filename. If
* there is nothing following the last '.', then there is no extension.
*
* Returns a pointer to the '.' preceding the extension, or nil if no
* extension was found.
*/
const char*
FindExtension(NulibState* pState, const char* pathname)
{
const char* pFilename;
const char* pExt;
/*
* We have to isolate the filename so that we don't get excited
* about "/foo.bar/file".
*/
pFilename = FilenameOnly(pState, pathname);
assert(pFilename != nil);
pExt = strrchr(pFilename, kFilenameExtDelim);
/* also check for "/blah/foo.", which doesn't count */
if (pExt != nil && *(pExt+1) != '\0')
return pExt;
return nil;
}

183
nulib2/INSTALL Normal file
View File

@ -0,0 +1,183 @@
Basic Installation
==================
These are generic installation instructions.
The `configure' shell script attempts to guess correct values for
various system-dependent variables used during compilation. It uses
those values to create a `Makefile' in each directory of the package.
It may also create one or more `.h' files containing system-dependent
definitions. Finally, it creates a shell script `config.status' that
you can run in the future to recreate the current configuration, a file
`config.cache' that saves the results of its tests to speed up
reconfiguring, and a file `config.log' containing compiler output
(useful mainly for debugging `configure').
If you need to do unusual things to compile the package, please try
to figure out how `configure' could check whether to do them, and mail
diffs or instructions to the address given in the `README' so they can
be considered for the next release. If at some point `config.cache'
contains results you don't want to keep, you may remove or edit it.
The file `configure.in' is used to create `configure' by a program
called `autoconf'. You only need `configure.in' if you want to change
it or regenerate `configure' using a newer version of `autoconf'.
The simplest way to compile this package is:
1. `cd' to the directory containing the package's source code and type
`./configure' to configure the package for your system. If you're
using `csh' on an old version of System V, you might need to type
`sh ./configure' instead to prevent `csh' from trying to execute
`configure' itself.
Running `configure' takes awhile. While running, it prints some
messages telling which features it is checking for.
2. Type `make' to compile the package.
3. Optionally, type `make check' to run any self-tests that come with
the package.
4. Type `make install' to install the programs and any data files and
documentation.
5. You can remove the program binaries and object files from the
source code directory by typing `make clean'. To also remove the
files that `configure' created (so you can compile the package for
a different kind of computer), type `make distclean'. There is
also a `make maintainer-clean' target, but that is intended mainly
for the package's developers. If you use it, you may have to get
all sorts of other programs in order to regenerate files that came
with the distribution.
Compilers and Options
=====================
Some systems require unusual options for compilation or linking that
the `configure' script does not know about. You can give `configure'
initial values for variables by setting them in the environment. Using
a Bourne-compatible shell, you can do that on the command line like
this:
CC=c89 CFLAGS=-O2 LIBS=-lposix ./configure
Or on systems that have the `env' program, you can do it like this:
env CPPFLAGS=-I/usr/local/include LDFLAGS=-s ./configure
Compiling For Multiple Architectures
====================================
You can compile the package for more than one kind of computer at the
same time, by placing the object files for each architecture in their
own directory. To do this, you must use a version of `make' that
supports the `VPATH' variable, such as GNU `make'. `cd' to the
directory where you want the object files and executables to go and run
the `configure' script. `configure' automatically checks for the
source code in the directory that `configure' is in and in `..'.
If you have to use a `make' that does not supports the `VPATH'
variable, you have to compile the package for one architecture at a time
in the source code directory. After you have installed the package for
one architecture, use `make distclean' before reconfiguring for another
architecture.
Installation Names
==================
By default, `make install' will install the package's files in
`/usr/local/bin', `/usr/local/man', etc. You can specify an
installation prefix other than `/usr/local' by giving `configure' the
option `--prefix=PATH'.
You can specify separate installation prefixes for
architecture-specific files and architecture-independent files. If you
give `configure' the option `--exec-prefix=PATH', the package will use
PATH as the prefix for installing programs and libraries.
Documentation and other data files will still use the regular prefix.
In addition, if you use an unusual directory layout you can give
options like `--bindir=PATH' to specify different values for particular
kinds of files. Run `configure --help' for a list of the directories
you can set and what kinds of files go in them.
If the package supports it, you can cause programs to be installed
with an extra prefix or suffix on their names by giving `configure' the
option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'.
Optional Features
=================
Some packages pay attention to `--enable-FEATURE' options to
`configure', where FEATURE indicates an optional part of the package.
They may also pay attention to `--with-PACKAGE' options, where PACKAGE
is something like `gnu-as' or `x' (for the X Window System). The
`README' should mention any `--enable-' and `--with-' options that the
package recognizes.
For packages that use the X Window System, `configure' can usually
find the X include and library files automatically, but if it doesn't,
you can use the `configure' options `--x-includes=DIR' and
`--x-libraries=DIR' to specify their locations.
Specifying the System Type
==========================
There may be some features `configure' can not figure out
automatically, but needs to determine by the type of host the package
will run on. Usually `configure' can figure that out, but if it prints
a message saying it can not guess the host type, give it the
`--host=TYPE' option. TYPE can either be a short name for the system
type, such as `sun4', or a canonical name with three fields:
CPU-COMPANY-SYSTEM
See the file `config.sub' for the possible values of each field. If
`config.sub' isn't included in this package, then this package doesn't
need to know the host type.
If you are building compiler tools for cross-compiling, you can also
use the `--target=TYPE' option to select the type of system they will
produce code for and the `--build=TYPE' option to select the type of
system on which you are compiling the package.
Sharing Defaults
================
If you want to set default values for `configure' scripts to share,
you can create a site shell script called `config.site' that gives
default values for variables like `CC', `cache_file', and `prefix'.
`configure' looks for `PREFIX/share/config.site' if it exists, then
`PREFIX/etc/config.site' if it exists. Or, you can set the
`CONFIG_SITE' environment variable to the location of the site script.
A warning: not all `configure' scripts look for a site script.
Operation Controls
==================
`configure' recognizes the following options to control how it
operates.
`--cache-file=FILE'
Use and save the results of the tests in FILE instead of
`./config.cache'. Set FILE to `/dev/null' to disable caching, for
debugging `configure'.
`--help'
Print a summary of the options to `configure', and exit.
`--quiet'
`--silent'
`-q'
Do not print messages saying which checks are being made. To
suppress all normal output, redirect it to `/dev/null' (any error
messages will still be shown).
`--srcdir=DIR'
Look for the package's source code in directory DIR. Usually
`configure' can determine that directory automatically.
`--version'
Print the version of Autoconf used to generate the `configure'
script, and exit.
`configure' also accepts some other, not widely useful, options.

420
nulib2/List.c Normal file
View File

@ -0,0 +1,420 @@
/*
* Nulib2
* Copyright (C) 2000 by Andy McFadden, All Rights Reserved.
* This is free software; you can redistribute it and/or modify it under the
* terms of the GNU General Public License, see the file COPYING.
*
* List files in the archive.
*/
#include "Nulib2.h"
/* kinds of records */
enum RecordKind {
kRecordKindUnknown = 0,
kRecordKindDisk,
kRecordKindFile,
kRecordKindForkedFile
};
static const char* gShortFormatNames[] = {
"unc", "squ", "lz1", "lz2", "u12", "u16"
};
#if 0
/* days of the week */
static const char* gDayNames[] = {
"[ null ]",
"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"
};
#endif
/* months of the year */
static const char* gMonths[] = {
"Jan", "Feb", "Mar", "Apr", "May", "Jun",
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
};
#define kNuDateOutputLen 64
/*
* Compute a percentage.
*/
int
ComputePercent(ulong totalSize, ulong size)
{
int perc;
if (!totalSize && !size)
return 100; /* file is zero bytes long */
if (totalSize < 21474836)
perc = (totalSize * 100) / size;
else
perc = totalSize / (size/100);
/* don't say "0%" if it's not actually zero... it looks dumb */
if (!perc && size)
perc = 1;
return perc;
}
/*
* Convert a NuDateTime structure into something printable. This uses an
* abbreviated format, with date and time but not weekday or seconds.
*
* The buffer passed in must hold at least kNuDateOutputLen bytes.
*
* Returns "buffer" for the benefit of printf() calls.
*/
static char*
FormatDateShort(const NuDateTime* pDateTime, char* buffer)
{
/* is it valid? */
if (pDateTime->day > 30 || pDateTime->month > 11 || pDateTime->hour > 24 ||
pDateTime->minute > 59)
{
strcpy(buffer, " <invalid> ");
goto bail;
}
/* is it empty? */
if ((pDateTime->second | pDateTime->minute | pDateTime->hour |
pDateTime->year | pDateTime->day | pDateTime->month |
pDateTime->extra | pDateTime->weekDay) == 0)
{
strcpy(buffer, " [No Date] ");
goto bail;
}
sprintf(buffer, "%02d-%s-%02d %02d:%02d",
pDateTime->day+1, gMonths[pDateTime->month], pDateTime->year % 100,
pDateTime->hour, pDateTime->minute);
bail:
return buffer;
}
/*
* NuStream callback function. Displays the filename.
*/
static NuResult
ShowContentsShort(NuArchive* pArchive, void* vpRecord)
{
const NuRecord* pRecord = (NuRecord*) vpRecord;
NulibState* pState;
assert(pArchive != nil);
(void) NuGetExtraData(pArchive, (void**) &pState);
assert(pState != nil);
if (!IsSpecified(pState, pRecord))
goto bail;
printf("%s\n",
pRecord->filename == nil ? "<unknown>":(const char*)pRecord->filename);
bail:
return kNuOK;
}
/*
* Analyze the contents of a record to determine if it's a disk, file,
* or "other". Compute the total compressed and uncompressed lengths
* of all data threads. Return the "best" format.
*
* The "best format" and "record type" stuff assume that the entire
* record contains only a disk thread or a file thread, and that any
* format is interesting so long as it isn't "no compression". In
* general these will be true, because ShrinkIt and NuLib create files
* this way.
*
* You could, of course, create a single record with a data thread and
* a disk image thread, but it's a fair bet ShrinkIt would ignore one
* or the other.
*/
static NuError
AnalyzeRecord(const NuRecord* pRecord, enum RecordKind* pRecordKind,
ushort* pFormat, ulong* pTotalLen, ulong* pTotalCompLen)
{
const NuThread* pThread;
NuThreadID threadID;
ulong idx;
*pRecordKind = kRecordKindUnknown;
*pTotalLen = *pTotalCompLen = 0;
*pFormat = kNuThreadFormatUncompressed;
for (idx = 0; idx < pRecord->recTotalThreads; idx++) {
pThread = NuGetThread(pRecord, idx);
assert(pThread != nil);
if (pThread->thThreadClass == kNuThreadClassData) {
/* replace what's there if this might be more interesting */
if (*pFormat == kNuThreadFormatUncompressed)
*pFormat = pThread->thThreadFormat;
threadID = NuMakeThreadID(pThread->thThreadClass,
pThread->thThreadKind);
if (threadID == kNuThreadIDRsrcFork)
*pRecordKind = kRecordKindForkedFile;
else if (threadID == kNuThreadIDDiskImage)
*pRecordKind = kRecordKindDisk;
else if (threadID == kNuThreadIDDataFork &&
*pRecordKind == kRecordKindUnknown)
*pRecordKind = kRecordKindFile;
/* sum up, so we get both forks of forked files */
*pTotalLen += pThread->actualThreadEOF;
*pTotalCompLen += pThread->thCompThreadEOF;
}
}
return kNuErrNone;
}
/*
* NuStream callback function. Displays the filename and several attributes.
*
* This is intended to mimic the output of some old version of ProDOS 8
* ShrinkIt.
*/
static NuResult
ShowContentsVerbose(NuArchive* pArchive, void* vpRecord)
{
NuError err = kNuErrNone;
const NuRecord* pRecord = (NuRecord*) vpRecord;
enum RecordKind recordKind;
ulong totalLen, totalCompLen;
ushort format;
NulibState* pState;
char date1[kNuDateOutputLen];
char tmpbuf[16];
int len;
assert(pArchive != nil);
(void) NuGetExtraData(pArchive, (void**) &pState);
assert(pState != nil);
if (!IsSpecified(pState, pRecord))
goto bail;
err = AnalyzeRecord(pRecord, &recordKind, &format, &totalLen,
&totalCompLen);
if (err != kNuErrNone)
goto bail;
len = strlen(pRecord->filename);
if (len <= 27) {
printf("%c%-27.27s ", IsRecordReadOnly(pRecord) ? '+' : ' ',
pRecord->filename);
} else {
printf("%c..%-25.25s ", IsRecordReadOnly(pRecord) ? '+' : ' ',
pRecord->filename + len - 25);
}
switch (recordKind) {
case kRecordKindUnknown:
printf("??? $%04lX ",
/*GetFileTypeString(pRecord->recFileType),*/
pRecord->recExtraType);
break;
case kRecordKindDisk:
sprintf(tmpbuf, "%ldk", totalLen / 1024);
printf("Disk %-6s ", tmpbuf);
break;
case kRecordKindFile:
case kRecordKindForkedFile:
printf("%s%c $%04lX ",
GetFileTypeString(pRecord->recFileType),
recordKind == kRecordKindForkedFile ? '+' : ' ',
pRecord->recExtraType);
break;
default:
assert(0);
printf("ERROR ");
}
printf("%s ", FormatDateShort(&pRecord->recArchiveWhen, date1));
if (format >= NELEM(gShortFormatNames))
printf("??? ");
else
printf("%s ", gShortFormatNames[format]);
/* compute the percent size */
if ((!totalLen && totalCompLen) || (totalLen && !totalCompLen))
printf("--- "); /* weird */
else if (totalLen < totalCompLen)
printf(">100%% "); /* compression failed? */
else {
sprintf(tmpbuf, "%02d%%", ComputePercent(totalCompLen, totalLen));
printf("%4s ", tmpbuf);
}
if (!totalLen && totalCompLen)
printf(" ????"); /* weird */
else
printf("%8ld", totalLen);
printf("\n");
NState_AddToTotals(pState, totalLen, totalCompLen);
bail:
if (err != kNuErrNone) {
printf("(ERROR on '%s')\n", pRecord->filename == nil ?
"<unknown>" : (const char*)pRecord->filename);
}
return kNuOK;
}
/*
* Print a short listing of the contents of an archive.
*/
NuError
DoListShort(NulibState* pState)
{
NuError err;
NuArchive* pArchive = nil;
assert(pState != nil);
err = OpenArchiveReadOnly(pState);
if (err != kNuErrNone)
goto bail;
pArchive = NState_GetNuArchive(pState);
assert(pArchive != nil);
err = NuContents(pArchive, ShowContentsShort);
/* fall through with err */
bail:
if (pArchive != nil)
(void) NuClose(pArchive);
return err;
}
/*
* Print a more verbose listing of the contents of an archive.
*/
NuError
DoListVerbose(NulibState* pState)
{
NuError err;
NuArchive* pArchive = nil;
const NuMasterHeader* pHeader;
char date1[kNuDateOutputLen];
char date2[kNuDateOutputLen];
long totalLen, totalCompLen;
const char* cp;
assert(pState != nil);
err = OpenArchiveReadOnly(pState);
if (err != kNuErrNone)
goto bail;
pArchive = NState_GetNuArchive(pState);
assert(pArchive != nil);
/*
* Try to get just the filename.
*/
if (IsFilenameStdin(NState_GetArchiveFilename(pState)))
cp = "<stdin>";
else
cp = FilenameOnly(pState, NState_GetArchiveFilename(pState));
/* grab the master header block */
err = NuGetMasterHeader(pArchive, &pHeader);
if (err != kNuErrNone)
goto bail;
printf(" %-15.15s Created:%s Mod:%s Recs:%5lu\n\n",
cp,
FormatDateShort(&pHeader->mhArchiveCreateWhen, date1),
FormatDateShort(&pHeader->mhArchiveModWhen, date2),
pHeader->mhTotalRecords);
printf(" Name Type Auxtyp Archived"
" Fmat Size Un-Length\n");
printf("-------------------------------------------------"
"----------------------------\n");
err = NuContents(pArchive, ShowContentsVerbose);
if (err != kNuErrNone)
goto bail;
/*
* Show the totals. NuFX overhead can be as much as 25% for archives
* with lots of small files.
*/
NState_GetTotals(pState, &totalLen, &totalCompLen);
printf("-------------------------------------------------"
"----------------------------\n");
printf(" Uncomp: %ld Comp: %ld %%of orig: %d%%\n",
totalLen, totalCompLen,
totalLen == 0 ? 0 : ComputePercent(totalCompLen, totalLen));
#ifdef DEBUG_VERBOSE
printf(" Overhead: %ld (%d%%)\n",
pHeader->mhMasterEOF - totalCompLen,
ComputePercent(pHeader->mhMasterEOF - totalCompLen,
pHeader->mhMasterEOF));
#endif
/*(void) NuDebugDumpArchive(pArchive);*/
bail:
if (pArchive != nil)
(void) NuClose(pArchive);
return err;
}
/*
* Null callback, for those times when you don't really want to do anything.
*/
static NuResult
NullCallback(NuArchive* pArchive, void* vpRecord)
{
return kNuOK;
}
/*
* Print very detailed output, suitable for debugging (requires that
* debugging be enabled in nufxlib).
*/
NuError
DoListDebug(NulibState* pState)
{
NuError err;
NuArchive* pArchive = nil;
assert(pState != nil);
err = OpenArchiveReadOnly(pState);
if (err != kNuErrNone)
goto bail;
pArchive = NState_GetNuArchive(pState);
assert(pArchive != nil);
/* have to do something to force the library to scan the archive */
err = NuContents(pArchive, NullCallback);
if (err != kNuErrNone)
goto bail;
err = NuDebugDumpArchive(pArchive);
if (err != kNuErrNone)
fprintf(stderr, "ERROR: debugging not enabled in nufxlib\n");
/* fall through with err */
bail:
if (pArchive != nil)
(void) NuClose(pArchive);
return err;
}

409
nulib2/Main.c Normal file
View File

@ -0,0 +1,409 @@
/*
* Nulib2
* Copyright (C) 2000 by Andy McFadden, All Rights Reserved.
* This is free software; you can redistribute it and/or modify it under the
* terms of the GNU General Public License, see the file COPYING.
*
* Main entry point and shell command argument processing.
*/
#include "Nulib2.h"
#include <ctype.h>
/*
* Globals and constants.
*/
const char* gProgName = "Nulib2";
/*
* Which modifiers are valid with which commands?
*/
typedef struct ValidCombo {
Command cmd;
Boolean okayForPipe;
Boolean filespecRequired;
const char* modifiers;
} ValidCombo;
static const ValidCombo gValidCombos[] = {
{ kCommandAdd, false, true, "ufrj0cke" },
{ kCommandDelete, false, true, "r" },
{ kCommandExtract, true, false, "ufrjclse" },
{ kCommandExtractToPipe, true, false, "rl" },
{ kCommandListShort, true, false, "" },
{ kCommandListVerbose, true, false, "" },
{ kCommandListDebug, true, false, "" },
{ kCommandTest, true, false, "r" },
};
/*
* Find an entry in the gValidCombos table matching the specified command.
*
* Returns nil if not found.
*/
static const ValidCombo*
FindValidComboEntry(Command cmd)
{
int i;
for (i = 0; i < NELEM(gValidCombos); i++) {
if (gValidCombos[i].cmd == cmd)
return &gValidCombos[i];
}
return nil;
}
/*
* Determine whether the specified modifier is valid when used with the
* current command.
*/
static Boolean
IsValidModifier(Command cmd, char modifier)
{
const ValidCombo* pvc;
pvc = FindValidComboEntry(cmd);
if (pvc != nil) {
if (strchr(pvc->modifiers, modifier) == nil)
return false;
else
return true;
} else
return false;
}
/*
* Determine whether the specified command can be used with stdin as input.
*/
static Boolean
IsValidOnPipe(Command cmd)
{
const ValidCombo* pvc;
pvc = FindValidComboEntry(cmd);
if (pvc != nil) {
return pvc->okayForPipe;
} else
return false;
}
/*
* Determine whether the specified command can be used with stdin as input.
*/
static Boolean
IsFilespecRequired(Command cmd)
{
const ValidCombo* pvc;
pvc = FindValidComboEntry(cmd);
if (pvc != nil) {
return pvc->filespecRequired;
} else {
/* command not found? warn about it here... */
fprintf(stderr, "%s: Command %d not found in gValidCombos table\n",
gProgName, cmd);
return false;
}
}
/*
* Separate the program name out of argv[0].
*/
static const char*
GetProgName(const NulibState* pState, const char* argv0)
{
const char* result;
char sep;
/* use the appropriate system pathname separator */
sep = NState_GetSystemPathSeparator(pState);
result = strrchr(argv0, sep);
if (result == nil)
result = argv0;
else
result++; /* advance past the separator */
return result;
}
/*
* Print program usage.
*/
static void
Usage(const NulibState* pState)
{
long majorVersion, minorVersion, bugVersion;
const char* nufxLibDate;
const char* nufxLibFlags;
(void) NuGetVersion(&majorVersion, &minorVersion, &bugVersion,
&nufxLibDate, &nufxLibFlags);
printf("\nNulib2 v%s, linked with NufxLib v%ld.%ld.%ld [%s]\n",
NState_GetProgramVersion(pState),
majorVersion, minorVersion, bugVersion, nufxLibFlags);
printf("This software is distributed under terms of the GNU General Public License.\n");
printf("Written by Andy McFadden, http://www.nulib.com/.\n\n");
printf("Usage: %s -command[modifiers] archive [filename-list]\n\n",
gProgName);
printf(
" -a add files, create arc if needed -x extract files\n"
" -t list files (short) -v list files (verbose)\n"
" -p extract files to pipe, no msgs -i test archive integrity\n"
" -d delete files from archive\n"
"\n"
" modifiers:\n"
" -u update files (add + keep newest) -f freshen (update, no add)\n"
" -r recurse into subdirs -j junk (don't record) directory names\n"
" -0 don't use compression -c add one-line comments\n"
" -l auto-convert text files -ll auto-convert ALL files\n"
" -s stomp existing files w/o asking -k store files as disk images\n"
" -e preserve ProDOS file types -ee extend preserved names\n"
);
}
/*
* Process the command-line options. The results are placed into "pState".
*/
static int
ProcessOptions(NulibState* pState, int argc, char* const* argv)
{
const char* cp;
int idx;
/*
* Must have at least a command letter and an archive filename.
*/
if (argc < 3) {
Usage(pState);
return -1;
}
/*
* Argv[1] and any subsequent entries that have a leading hyphen
* are options. Anything after that is a filename. Parse until we
* think we've hit the filename.
*
* By UNIX convention, however, stdin is specified as a file called "-".
*/
for (idx = 1; idx < argc; idx++) {
cp = argv[idx];
if (idx > 1 && *cp != '-')
break;
if (*cp == '-')
cp++;
if (*cp == '\0') {
if (idx == 1) {
fprintf(stderr,
"%s: You must specify a command after the '-'\n",
gProgName);
goto fail;
} else {
/* they're using '-' for the filename */
break;
}
}
if (idx == 1) {
switch (tolower(*cp)) {
case 'a': NState_SetCommand(pState, kCommandAdd); break;
case 'x': NState_SetCommand(pState, kCommandExtract); break;
case 'p': NState_SetCommand(pState, kCommandExtractToPipe); break;
case 't': NState_SetCommand(pState, kCommandListShort); break;
case 'v': NState_SetCommand(pState, kCommandListVerbose); break;
case 'z': NState_SetCommand(pState, kCommandListDebug); break;
case 'i': NState_SetCommand(pState, kCommandTest); break;
case 'd': NState_SetCommand(pState, kCommandDelete); break;
default:
fprintf(stderr, "%s: Unknown command '%c'\n", gProgName, *cp);
goto fail;
}
cp++;
}
while (*cp != '\0') {
switch (tolower(*cp)) {
case 'u': NState_SetModUpdate(pState, true); break;
case 'f': NState_SetModFreshen(pState, true); break;
case 'r': NState_SetModRecurse(pState, true); break;
case 'j': NState_SetModJunkPaths(pState, true); break;
case '0': NState_SetModNoCompression(pState, true); break;
case 'c': NState_SetModComments(pState, true); break;
case 's': NState_SetModOverwriteExisting(pState, true); break;
case 'k': NState_SetModAddAsDisk(pState, true); break;
case 'e':
if (*(cp-1) == 'e') /* should never point at invalid */
NState_SetModPreserveTypeExtended(pState, true);
else
NState_SetModPreserveType(pState, true);
break;
case 'l':
if (*(cp-1) == 'l') /* should never point at invalid */
NState_SetModConvertAll(pState, true);
else
NState_SetModConvertText(pState, true);
break;
default:
fprintf(stderr, "%s: Unknown modifier '%c'\n", gProgName, *cp);
goto fail;
}
if (!IsValidModifier(NState_GetCommand(pState), (char)tolower(*cp)))
{
fprintf(stderr,
"%s: The '%c' modifier doesn't make sense here\n",
gProgName, tolower(*cp));
goto fail;
}
cp++;
}
}
/*
* See if we have an archive name. If it's "-", see if we allow that.
*/
assert(idx < argc);
NState_SetArchiveFilename(pState, argv[idx]);
if (IsFilenameStdin(argv[idx])) {
if (!IsValidOnPipe(NState_GetCommand(pState))) {
fprintf(stderr, "%s: You can't do that with a pipe\n",
gProgName);
goto fail;
}
}
idx++;
/*
* See if we have a file specification. Some of the commands require
* a filespec; others just perform the requested operation on all of
* the records in the archive if none is provided.
*/
if (idx < argc) {
/* got one or more */
NState_SetFilespecPointer(pState, &argv[idx]);
NState_SetFilespecCount(pState, argc - idx);
} else {
assert(idx == argc);
if (IsFilespecRequired(NState_GetCommand(pState))) {
fprintf(stderr, "%s: This command requires a list of files\n",
gProgName);
goto fail;
}
NState_SetFilespecPointer(pState, nil);
NState_SetFilespecCount(pState, 0);
}
#ifdef DEBUG_VERBOSE
NState_DebugDump(pState);
#endif
return 0;
fail:
fprintf(stderr,
"%s: (invoke without arguments to see usage information)\n",
gProgName);
return -1;
}
/*
* We have all of the parsed command line options in "pState". Now we just
* have to do something useful with it.
*
* Returns 0 on success, 1 on error.
*/
int
DoWork(NulibState* pState)
{
NuError err;
switch (NState_GetCommand(pState)) {
case kCommandAdd:
err = DoAdd(pState);
break;
case kCommandExtract:
err = DoExtract(pState);
break;
case kCommandExtractToPipe:
err = DoExtractToPipe(pState);
break;
case kCommandTest:
err = DoTest(pState);
break;
case kCommandListShort:
err = DoListShort(pState);
break;
case kCommandListVerbose:
err = DoListVerbose(pState);
break;
case kCommandListDebug:
err = DoListDebug(pState);
break;
case kCommandDelete:
err = DoDelete(pState);
break;
default:
fprintf(stderr, "ERROR: unexpected command %d\n",
NState_GetCommand(pState));
err = kNuErrInternal;
assert(0);
break;
}
return (err != kNuErrNone);
}
/*
* Entry point.
*/
int
main(int argc, char** argv)
{
NulibState* pState = nil;
int result = 0;
#if 0
extern NuResult ErrorMessageHandler(NuArchive* pArchive,
void* vErrorMessage);
NuSetGlobalErrorMessageHandler(ErrorMessageHandler);
#endif
if (NState_Init(&pState) != kNuErrNone) {
fprintf(stderr, "ERROR: unable to initialize globals\n");
exit(1);
}
gProgName = GetProgName(pState, argv[0]);
if (ProcessOptions(pState, argc, argv) < 0) {
result = 2;
goto bail;
}
if (NState_ExtraInit(pState) != kNuErrNone) {
fprintf(stderr, "ERROR: additional initialization failed\n");
exit(1);
}
result = DoWork(pState);
if (result)
printf("Failed.\n");
bail:
NState_Free(pState);
exit(result);
}

140
nulib2/Makefile.in Normal file
View File

@ -0,0 +1,140 @@
#
# Makefile for nulib2 stuff (should work with non-GNU make).
#
# You can use:
# make (builds nulib2 and checks for libnufx.a)
# make shared (builds nulib2 and checks for libnufx.so)
#
# This will try to link against the library in $(NUFXSRCDIR) first, then
# look for a copy in the standard system install location (usually
# /usr/local/lib).
#
# Note that this really wants to find $(NUFXLIB) for dependency checking.
# If you're building against a copy in /usr/local/lib, just put a '#' in
# front of the "NUFXLIB" line below.
#
# set this to where the NuFX library and ".h" file live
VERSION = 100
NUFXSRCDIR = ../nufxlib-$(VERSION)
NUFXLIB = $(NUFXSRCDIR)/$(LIB_PRODUCT)
# NuLib2 install location. The man page will go into $(mandir)/man1.
prefix = @prefix@
exec_prefix = @exec_prefix@
includedir = @includedir@
libdir = @libdir@
bindir = @bindir@
mandir = @mandir@
srcdir = @srcdir@
SHELL = @SHELL@
INSTALL = @INSTALL@
INSTALL_PROGRAM = @INSTALL_PROGRAM@
INSTALL_DATA = @INSTALL_DATA@
CC = @CC@
#OPT = @CFLAGS@ -DNDEBUG
OPT = @CFLAGS@
#OPT = @CFLAGS@ -DDEBUG_MSGS
#OPT = @CFLAGS@ -DDEBUG_VERBOSE
GCC_FLAGS = -Wall -Wwrite-strings -Wstrict-prototypes -Wpointer-arith -Wshadow
CFLAGS = @BUILD_FLAGS@ -I. -I$(NUFXSRCDIR) -I$(includedir) @DEFS@
#ifdef PURIFY_BUILD
# PURIFY = purify
# CFLAGS += -DPURIFY
#endif
#ifdef QUANTIFY_BUILD
# QUANTIFY = quantify
# CFLAGS += -DQUANTIFY
#endif
SRCS = Add.c ArcUtils.c Delete.c Extract.c Filename.c List.c Main.c \
MiscStuff.c MiscUtils.c State.c SysUtils.c
OBJS = Add.o ArcUtils.o Delete.o Extract.o Filename.o List.o Main.o \
MiscStuff.o MiscUtils.o State.o SysUtils.o
PRODUCT = nulib2
# this is used for dependency checking
LIB_PRODUCT = libnufx.a
#
# Build stuff
#
all: $(PRODUCT)
@true
install: $(PRODUCT)
$(srcdir)/mkinstalldirs $(bindir)
$(INSTALL_PROGRAM) $(PRODUCT) $(bindir)
$(srcdir)/mkinstalldirs $(mandir)/man1
$(INSTALL_DATA) nulib2.1 $(mandir)/man1/
install-shared:
LIB_PRODUCT="libnufx.so" $(MAKE) -e install
# Link against the shared version of libnufx. This is only needed so
# the dependency checking does the right thing.
shared::
LIB_PRODUCT="libnufx.so" $(MAKE) -e
quantify:
-rm -f $(PRODUCT)
@$(MAKE) QUANTIFY_BUILD=1
purify:
-rm -f $(PRODUCT)
@$(MAKE) PURIFY_BUILD=1
$(PRODUCT): $(OBJS) $(NUFXLIB)
$(PURIFY) $(QUANTIFY) $(CC) -o $@ $(OBJS) -L$(NUFXSRCDIR) -L$(libdir) -lnufx @DMALLOC@
clean:
-rm -f *.o core
-rm -f $(PRODUCT)
tags::
ctags -R --totals * $(NUFXSRCDIR)/*
@#ctags *.[ch] $(NUFXSRCDIR)/*.[ch]
distclean: clean
-rm -f Makefile Makefile.bak
-rm -f config.log config.cache config.status config.h
-rm -f tags
-rm -f nulib2-$(VERSION)-@host_alias@ nulib2-$(VERSION)-@host_alias@.tar.Z
# Copy all of the binaries into a directory and tar them up for distribution.
# All binaries except "nulib2" are stripped to reduce their size.
distbin: $(PRODUCT)
@ \
builddir="nulib2-$(VERSION)-@host_alias@"; \
samples=$(NUFXSRCDIR)/samples; \
echo "building $$builddir.tar.Z"; \
rm -rf $$builddir; \
mkdir -p $$builddir; \
cp -p $(PRODUCT) nulib2.1 README.txt COPYING $$samples/README-S.txt \
$$samples/exerciser $$samples/imgconv $$samples/launder \
$$samples/test-basic $$samples/test-extract \
$$samples/test-simple \
$$builddir; \
strip $$builddir/$(PRODUCT) \
$$builddir/exerciser $$builddir/imgconv $$builddir/launder \
$$builddir/test-basic $$builddir/test-extract \
$$builddir/test-simple; \
tar -cf - $$builddir | compress > $$builddir.tar.Z; \
rm -rf $$builddir
baktar:
@tar cvf nulib2.tar *.txt COPYING INSTALL nulib2.1 configure *.in Makefile \
Makefile.msc install-sh config.guess config.sub mkinstalldirs *.[ch]
@gzip -9 nulib2.tar
@mv -i nulib2.tar.gz /home/fadden/BAK/
depend:
makedepend -- $(CFLAGS) -- $(SRCS)
# DO NOT DELETE THIS LINE -- make depend depends on it.

61
nulib2/Makefile.msc Normal file
View File

@ -0,0 +1,61 @@
#
# Makefile for Microsoft C compilers. Tested against Visual C++ 6.0.
# Not pretty but it seems to work.
#
# Run with "nmake /f Makefile.msc". Expects NufxLib to have been built
# in "..\nufxlib-$(VERSION)".
#
# To build without debugging info, use "nmake nodebug=1".
#
# Windows magic
TARGETOS = BOTH
!include <ntwin32.mak>
VERSION=100
NUFXSRCDIR = ..\nufxlib-$(VERSION)
# object files
OBJS1 = Add.obj ArcUtils.obj Delete.obj Extract.obj Filename.obj List.obj
OBJS2 = Main.obj MiscStuff.obj MiscUtils.obj State.obj SysUtils.obj
OBJS = $(OBJS1) $(OBJS2)
!ifdef NODEBUG
#OPT = $(cdebug) /D NDEBUG /ML
OPT = $(cdebug) /ML
LIB_FLAGS = /nodefaultlib:libcd.lib /nologo libc.lib setargv.obj
!else
OPT = $(cdebug) /MLd
LIB_FLAGS = /nodefaultlib:libc.lib /nologo libcd.lib setargv.obj
!endif
BUILD_FLAGS = /W3 /GX /D "WIN32" /D "_CONSOLE" /I "$(NUFXSRCDIR)"
# how to compile sources
.c.obj:
@$(cc) $(cdebug) $(OPT) $(BUILD_FLAGS) $(cflags) $(cvars) -o $@ $<
PRODUCT = nulib2.exe
all: $(PRODUCT)
nulib2.exe: $(OBJS) $(NUFXSRCDIR)\nufxlib.lib
$(link) $(ldebug) $** -out:$@ $(NUFXSRCDIR)\nufxlib.lib $(LIB_FLAGS)
clean:
del *.obj
del $(PRODUCT)
Add.obj: Add.c NuLib2.h $(NUFXSRCDIR)\NufxLib.h SysDefs.h State.h MiscStuff.h
ArcUtils.obj: ArcUtils.c NuLib2.h $(NUFXSRCDIR)\NufxLib.h SysDefs.h State.h MiscStuff.h
Delete.obj: Delete.c NuLib2.h $(NUFXSRCDIR)\NufxLib.h SysDefs.h State.h MiscStuff.h
Extract.obj: Extract.c NuLib2.h $(NUFXSRCDIR)\NufxLib.h SysDefs.h State.h MiscStuff.h
Filename.obj: Filename.c NuLib2.h $(NUFXSRCDIR)\NufxLib.h SysDefs.h State.h MiscStuff.h
List.obj: List.c NuLib2.h $(NUFXSRCDIR)\NufxLib.h SysDefs.h State.h MiscStuff.h
Main.obj: Main.c NuLib2.h $(NUFXSRCDIR)\NufxLib.h SysDefs.h State.h MiscStuff.h
MiscStuff.obj: MiscStuff.c NuLib2.h $(NUFXSRCDIR)\NufxLib.h SysDefs.h State.h MiscStuff.h
MiscUtils.obj: MiscUtils.c NuLib2.h $(NUFXSRCDIR)\NufxLib.h SysDefs.h State.h MiscStuff.h
State.obj: State.c NuLib2.h $(NUFXSRCDIR)\NufxLib.h SysDefs.h State.h MiscStuff.h
SysUtils.obj: SysUtils.c NuLib2.h $(NUFXSRCDIR)\NufxLib.h SysDefs.h State.h MiscStuff.h

120
nulib2/MiscStuff.c Normal file
View File

@ -0,0 +1,120 @@
/*
* Copyright (C) 2000 by Andy McFadden, All Rights Reserved.
* This is free software; you can redistribute it and/or modify it under the
* terms of the GNU Library General Public License, see the file COPYING.LIB.
*
* Misc stuff (shared between nufxlib and nulib2). This is a collection
* of standard functions that aren't available in libc on this system.
*/
#include "SysDefs.h"
#include "MiscStuff.h"
#include <ctype.h>
#ifndef HAVE_STRERROR
/*
* Return a pointer to the appropriate string in the system table, or NULL
* if the value is out of bounds.
*/
const char*
Nu_strerror(int errnum)
{
extern int sys_nerr;
extern char *sys_errlist[];
if (errnum < 0 || errnum > sys_nerr)
return NULL;
return sys_errlist[errnum];
}
#endif
#ifndef HAVE_MEMMOVE
/*
* Move a block of memory. Unlike memcpy, this is expected to work
* correctly with overlapping blocks.
*
* This is a straightforward implementation. A much faster implementation,
* from BSD, is available in the PGP 2.6.2 distribution, but this should
* suffice for those few systems that don't have memmove.
*/
void*
Nu_memmove(void* dst, const void* src, size_t n)
{
void* retval = dst;
char* srcp = (char*)src;
char* dstp = (char*)dst;
/* you can normally get away with this if n==0 */
assert(dst != NULL);
assert(src != NULL);
if (dstp == srcp || !n) {
/* nothing to do */
} else if (dstp > srcp) {
/* start from the end */
(char*)dstp += n-1;
(char*)srcp += n-1;
while (n--)
*dstp-- = *srcp--;
} else {
/* start from the front */
while (n--)
*dstp++ = *srcp++;
}
return retval;
}
#endif
#ifndef HAVE_STRTOUL
/*
* Perform strtol, but on an unsigned long.
*
* On systems that have strtol but don't have strtoul, the strtol
* function doesn't clamp the return value, making it similar in
* function to strtoul. The comparison is not exact, however,
* because strtoul is expected to lots of fancy things (like set
* errno to ERANGE).
*
* For our purposes here, strtol does all we need it to. Someday
* we should replace this with a "real" version.
*/
unsigned long
Nu_strtoul(const char *nptr, char **endptr, int base)
{
return strtol(nptr, endptr, base);
}
#endif
#ifndef HAVE_STRCASECMP
/*
* Compare two strings, case-insensitive.
*/
int
Nu_strcasecmp(const char *str1, const char *str2)
{
while (*str1 && *str2 && toupper(*str1) == toupper(*str2))
str1++, str2++;
return (toupper(*str1) - toupper(*str2));
}
#endif
#ifndef HAVE_STRNCASECMP
/*
* Compare two strings, case-insensitive, stopping after "n" chars.
*/
int
Nu_strncasecmp(const char *str1, const char *str2, size_t n)
{
while (n && *str1 && *str2 && toupper(*str1) == toupper(*str2))
str1++, str2++, n--;
if (n)
return (toupper(*str1) - toupper(*str2));
else
return 0; /* no mismatch in first n chars */
}
#endif

108
nulib2/MiscStuff.h Normal file
View File

@ -0,0 +1,108 @@
/*
* Copyright (C) 2000 by Andy McFadden, All Rights Reserved.
* This is free software; you can redistribute it and/or modify it under the
* terms of the GNU Library General Public License, see the file COPYING.LIB.
*
* Misc stuff (shared between nufxlib and nulib2). This is a collection
* of miscellaneous types and macros that I find generally useful.
*/
#ifndef __MiscStuff__
#define __MiscStuff__
#include "SysDefs.h"
/*
* Use our versions of functions if they don't exist locally.
*/
#ifndef HAVE_STRERROR
#define strerror Nu_strerror
const char* Nu_strerror(int errnum);
#endif
#ifndef HAVE_MEMMOVE
#define memmove Nu_memmove
void* Nu_memmove(void *dest, const void *src, size_t n);
#endif
#ifndef HAVE_STRTOUL
#define strtoul Nu_strtoul
unsigned long Nu_strtoul(const char *nptr, char **endptr, int base);
#endif
#ifndef HAVE_STRCASECMP
#define strcasecmp Nu_strcasecmp
int Nu_strcasecmp(const char *s1, const char *s2);
#endif
#ifndef HAVE_STRNCASECMP
#define strncasecmp Nu_strncasecmp
int Nu_strncasecmp(const char *s1, const char *s2, size_t n);
#endif
/*
* Misc types.
*/
#include <sys/types.h>
#define nil NULL /* I can't seem to stop typing 'nil' now */
typedef uchar Boolean;
#define false (0)
#define true (!false)
/*
* Handy macros.
*/
/* compute #of elements in a static array */
#define NELEM(x) (sizeof(x) / sizeof((x)[0]))
/* convert single hex digit char to number */
#define HexDigit(x) ( !isxdigit((int)(x)) ? -1 : \
(x) <= '9' ? (x) - '0' : toupper(x) +10 - 'A' )
/* convert number from 0-15 to hex digit */
#define HexConv(x) ( ((uint)(x)) <= 15 ? \
( (x) <= 9 ? (x) + '0' : (x) -10 + 'A') : -1 )
/*
* Debug stuff.
*/
/*
* Redefine this if you want assertions to do something other than default.
* Changing the definition of assert is tough, because assert.h redefines
* it every time it's included. On a Solaris 2.7 system I was using, gcc
* pulled assert.h in with some of the system headers, and their definition
* resulted in corrupted core dumps.
*/
#define Assert assert
#if defined(DEBUG_VERBOSE)
/* quick debug printf macro */
#define DBUG(args) printf args
#else
#define DBUG(args) ((void)0)
#endif
#if defined(NDEBUG)
#define DebugFill(addr, len) ((void)0)
#define DebugAbort() ((void)0)
#else
/* when debugging, fill Malloc blocks with junk, unless we're using Purify */
#if !defined(PURIFY)
#define DebugFill(addr, len) memset(addr, 0xa3, len)
#else
#define DebugFill(addr, len) ((void)0)
#endif
#define DebugAbort() abort()
#endif
#define kInvalidFill (0xa3)
#define kInvalidPtr ((void*)0xa3a3a3a3)
#endif /*__MiscStuff__*/

110
nulib2/MiscUtils.c Normal file
View File

@ -0,0 +1,110 @@
/*
* Nulib2
* Copyright (C) 2000 by Andy McFadden, All Rights Reserved.
* This is free software; you can redistribute it and/or modify it under the
* terms of the GNU General Public License, see the file COPYING.
*
* Misc support functions.
*/
#define __MiscUtils_c__
#include "Nulib2.h"
/*
* Similar to perror(), but takes the error as an argument, and knows
* about NufxLib errors as well as system errors.
*
* If "format" is nil, just the error message itself is printed.
*/
void
ReportError(NuError err, const char* format, ...)
{
const char* msg;
va_list args;
assert(format != nil);
va_start(args, format);
/* print the message, if any */
if (format != nil) {
fprintf(stderr, "%s: ERROR: ", gProgName);
vfprintf(stderr, format, args);
}
/* print the error code data, if any */
if (err == kNuErrNone)
fprintf(stderr, "\n");
else {
if (format != nil)
fprintf(stderr, ": ");
msg = nil;
if (err >= 0)
msg = strerror(err);
if (msg == nil)
msg = NuStrError(err);
if (msg == nil)
fprintf(stderr, "(unknown err=%d)\n", err);
else
fprintf(stderr, "%s\n", msg);
}
va_end(args);
}
/*
* Memory allocation wrappers.
*
* Under gcc these would be macros, but not all compilers can handle that.
*/
#ifndef USE_DMALLOC
void*
Malloc(size_t size)
{
void* _result;
Assert(size > 0);
_result = malloc(size);
if (_result == nil) {
ReportError(kNuErrMalloc, "malloc(%u) failed", (uint) size);
DebugAbort(); /* leave a core dump if we're built for it */
}
DebugFill(_result, size);
return _result;
}
void*
Calloc(size_t size)
{
void* _cresult = Malloc(size);
memset(_cresult, 0, size);
return _cresult;
}
void*
Realloc(void* ptr, size_t size)
{
void* _result;
Assert(ptr != nil); /* disallow this usage */
Assert(size > 0); /* disallow this usage */
_result = realloc(ptr, size);
if (_result == nil) {
ReportError(kNuErrMalloc, "realloc(%u) failed", (uint) size);
DebugAbort(); /* leave a core dump if we're built for it */
}
return _result;
}
void
Free(void* ptr)
{
if (ptr != nil)
free(ptr);
}
#endif

97
nulib2/Nulib2.h Normal file
View File

@ -0,0 +1,97 @@
/*
* Nulib2
* Copyright (C) 2000 by Andy McFadden, All Rights Reserved.
* This is free software; you can redistribute it and/or modify it under the
* terms of the GNU General Public License, see the file COPYING.
*/
#ifndef __Nulib2__
#define __Nulib2__
#include "SysDefs.h" /* system-dependent defs; must come first */
#include <NufxLib.h>
#include "State.h"
#include "MiscStuff.h"
#ifdef USE_DMALLOC
/* enable with something like "dmalloc -l logfile -i 100 medium" */
# include "dmalloc.h"
#endif
/* replace unsupported chars with '%xx' */
#define kForeignIndic '%'
/* make our one-line comments this big */
#define kDefaultCommentLen 200
/*
* Function prototypes.
*/
/* Add.c */
NuError DoAdd(NulibState* pState);
/* ArcUtils.c */
char* GetSimpleComment(NulibState* pState, const char* pathname, int maxLen);
Boolean IsFilenameStdin(const char* archiveName);
Boolean IsSpecified(NulibState* pState, const NuRecord* pRecord);
NuError OpenArchiveReadOnly(NulibState* pState);
NuError OpenArchiveReadWrite(NulibState* pState);
const NuThread* GetThread(const NuRecord* pRecord, ulong idx);
Boolean IsRecordReadOnly(const NuRecord* pRecord);
/* Delete.c */
NuError DoDelete(NulibState* pState);
/* Extract.c */
NuError DoExtract(NulibState* pState);
NuError DoExtractToPipe(NulibState* pState);
NuError DoTest(NulibState* pState);
/* Filename.c */
const char* GetFileTypeString(ulong fileType);
const char* NormalizePath(NulibState* pState, NuPathnameProposal* pathProposal);
void InterpretExtension(NulibState* pState, const char* pathName,
ulong* pFileType, ulong* pAuxType);
Boolean ExtractPreservationString(NulibState* pState, char* pathname,
ulong* pFileType, ulong* pAuxType, NuThreadID* pThreadID);
void DenormalizePath(NulibState* pState, char* pathBuf);
const char* FilenameOnly(NulibState* pState, const char* pathname);
const char* FindExtension(NulibState* pState, const char* pathname);
/* List.c */
NuError DoListShort(NulibState* pState);
NuError DoListVerbose(NulibState* pState);
NuError DoListDebug(NulibState* pState);
/* Main.c */
extern const char* gProgName;
/* MiscUtils.c */
void ReportError(NuError err, const char* format, ...)
#if defined(__GNUC__)
__attribute__ ((format(printf, 2, 3)))
#endif
;
#ifdef USE_DMALLOC /* want file and line numbers for calls */
# define Malloc(size) malloc(size)
# define Calloc(size) calloc(1, size)
# define Realloc(ptr, size) realloc(ptr, size)
# define Free(ptr) (ptr != nil ? free(ptr) : (void)0)
#else
void* Malloc(size_t size);
void* Calloc(size_t size);
void* Realloc(void* ptr, size_t size);
void Free(void* ptr);
#endif
/* SysUtils.c */
NuError NormalizeFileName(NulibState* pState, const char* srcp, long srcLen,
char fssep, char** pDstp, long dstLen);
NuError NormalizeDirectoryName(NulibState* pState, const char* srcp,
long srcLen, char fssep, char** pDstp, long dstLen);
char* MakeTempArchiveName(NulibState* pState);
NuError AddFile(NulibState* pState, NuArchive* pArchive,
const char* pathname);
#endif /*__Nulib2__*/

122
nulib2/README.txt Normal file
View File

@ -0,0 +1,122 @@
NuLib2 README, updated 2000/05/18
http://www.nulib.com/
See "COPYING" for distribution restrictions.
To build NuLib2, you will also need a copy of NufxLib. This may have come
in the same .tar.gz file. Build the library first.
UNIX
====
Make sure that the "NUFXSRCDIR" define in Makefile.in points to the correct
directory, or that the library has been installed in a standard location
such as /usr/local/lib/. If you received NuLib2 and NufxLib in a single
".tar.gz" file, the variable is already set correctly. The makefile will
look in $(NUFXSRCDIR) first, /usr/local/lib second.
Run the "configure" script. Read through "INSTALL" if you haven't used
one of these before, especially if you want to use a specific compiler
or a particular set of compiler flags.
Run "make depend" if you have makedepend, and then type "make".
This should leave you with an executable called "nulib2". If you like,
"make install" will put things into your install directory, usually
/usr/local/bin/ and /usr/local/man/.
You may want to fiddle with the "OPT" setting in Makefile to enable or
disable optimizations and assertions. Because almost all of the hard
work is done by NufxLib, turning compiler optimizations on in NuLib2 has
little impact on performance.
A man page for nulib2 is in "nulib2.1", which you can format for viewing
with "nroff -man nulib2.1". A full manual for NuLib2 is available from
the www.nulib.com web site.
BeOS
====
This works just like the UNIX version, but certain defaults have been
changed. Running configure without arguments under BeOS is equivalent to:
./configure --prefix=/boot --includedir='${prefix}/develop/headers'
--libdir='${exec_prefix}/home/config/lib' --mandir='/tmp'
--bindir='${exec_prefix}/home/config/bin'
If you're using BeOS/PPC, it will also do:
CC=cc CFLAGS='-proc 603 -opt full'
Win32
=====
If you're using an environment that supports "configure" scripts, such as
DJGPP, follow the UNIX instructions.
NuLib2 has been tested with Microsoft Visual C++ 6.0. To build NuLib2,
start up a DOS shell and run vcvars32.bat to set your environment. Run:
nmake -f makefile.msc
to build with debugging info, or
nmake -f makefile.msc nodebug=1
to build optimized.
Other Notes
===========
All of the source code was formatted with four-space hard tabs.
Fun benchmark of the day:
Time to compress 1525 files, totaling 19942152 bytes, on an Apple IIgs
with an 8MHz ZipGS accelerator and Apple HS SCSI card, running System
6.0.1, from a 20MB ProDOS partition to a 13.9MB archive on an HFS volume,
with GS/ShrinkIt 1.1: about 40 minutes.
Time to compress the same files, on a 128MB 500MHz Pentium-III running
Red Hat Linux 6.0, with NuLib2 v0.3: about six seconds.
Here's a nifty way to evaluate GSHK vs NuLib2 (as well as Linux NuLib2
vs Win32 NuLib2):
- Archive a whole bunch of files from a ProDOS volume with GS/ShrinkIt.
I used a 20MB partition, which resulted in a 14MB archive. Transfer
the archive to a machine running NuLib2 (perhaps a Linux system).
- Create a new subdirectory, cd into it, and extract the entire archive
with "nulib2 xe ../foo.shk".
- Now create a new archive with all of the files, using
"nulib2 aer ../new.shk *".
- Change back to the directory above, and use "nulib2 v" to see what's
in them, e.g. "nulib2 v foo.shk > out.orig" and
"nulib2 v new.shk > out.new".
- Edit both of the "out" files with vi. Do a global search-and-replace
to change '/' to ':' in out.new (:%s/\//:/g) so the filename separator
doesn't mess up the comparison.
- Do a global search-and-replace in both files to set the file dates
to be the same. I used ":%s/..-...-.. ..:../01-Jan-00 00:00/". This
is necessary because, like ShrinkIt, NuLib displays the date on which
the files were archived, not when they were last modified.
- Sort both files, with ":%!sort". This is necessary because you
added the files with '*' up above, so the NuLib2-created archive
has the top-level files alphabetized.
- Quit out of vi. Diff the two files.
I did this for a 20MB hard drive partition with 1500 files on it.
The only discrepancies (accounting for a total difference of 116 bytes)
were a zero-byte "Kangaroo.data" file that GSHK stored improperly and some
semi-random GSHK behavior that I can't mimic. When the "Mimic ShrinkIt"
flag is disabled, the resulting archive is 13K smaller.
The largest archive I've tried had 4700 files for a total of 76MB
(compressed down to 47MB), and contained the entire contents of four 20MB
ProDOS hard drive partitions. NuLib2 under Linux handled it without
breaking a sweat.

499
nulib2/State.c Normal file
View File

@ -0,0 +1,499 @@
/*
* Nulib2
* Copyright (C) 2000 by Andy McFadden, All Rights Reserved.
* This is free software; you can redistribute it and/or modify it under the
* terms of the GNU General Public License, see the file COPYING.
*
* Global-ish state object.
*/
#include "Nulib2.h"
static const char* gProgramVersion = "1.0.0";
/*
* Allocate and initialize the semi-global Nulib2 state object.
*/
NuError
NState_Init(NulibState** ppState)
{
assert(ppState != nil);
*ppState = Calloc(sizeof(**ppState));
if (*ppState == nil)
return kNuErrMalloc;
/*
* Initialize the contents to default values.
*/
(*ppState)->systemPathSeparator = PATH_SEP;
(*ppState)->programVersion = gProgramVersion;
return kNuErrNone;
}
/*
* A little extra initialization, performed after arguments are parsed.
*/
NuError
NState_ExtraInit(NulibState* pState)
{
NuError err;
NuValue convertEOL;
/*
* Create a data sink for "stdout", in case we use the "-p" command.
* Set the EOL conversion according to the "-l" modifier.
*/
convertEOL = kNuConvertOff;
if (pState->modConvertText)
convertEOL = kNuConvertAuto;
if (pState->modConvertAll)
convertEOL = kNuConvertOn;
err = NuCreateDataSinkForFP(true, convertEOL, stdout, &pState->pPipeSink);
if (err != kNuErrNone)
return err;
/*
* Create a data sink for "stdout", in case we use the "-c" modifier.
* The EOL conversion is always on.
*/
err = NuCreateDataSinkForFP(true, kNuConvertOn, stdout,
&pState->pCommentSink);
return err;
}
/*
* Free up the state structure and its contents.
*/
void
NState_Free(NulibState* pState)
{
if (pState == nil)
return;
Free(pState->renameToStr); /* ?? */
Free(pState->tempPathnameBuf);
if (pState->pPipeSink != nil)
NuFreeDataSink(pState->pPipeSink);
if (pState->pCommentSink != nil)
NuFreeDataSink(pState->pCommentSink);
Free(pState);
}
#ifdef DEBUG_MSGS
void
NState_DebugDump(const NulibState* pState)
{
/* this will break when the code changes, but it's just for debugging */
static const char* kCommandNames[] = {
"<unknown>",
"add",
"delete",
"extract",
"extractToPipe",
"listShort",
"listVerbose",
"listDebug",
"test",
};
assert(pState != nil);
printf("NState:\n");
printf(" programVersion: '%s'\n", pState->programVersion);
printf(" systemPathSeparator: '%c'\n", pState->systemPathSeparator);
printf(" archiveFilename: '%s'\n", pState->archiveFilename);
printf(" filespec: %ld (%s ...)\n", pState->filespecCount,
!pState->filespecCount ? "<none>" : *pState->filespecPointer);
printf(" command: %d (%s); modifiers:\n", pState->command,
kCommandNames[pState->command]);
if (pState->modUpdate)
printf(" update\n");
if (pState->modFreshen)
printf(" freshen\n");
if (pState->modRecurse)
printf(" recurse\n");
if (pState->modJunkPaths)
printf(" junkPaths\n");
if (pState->modNoCompression)
printf(" noCompression\n");
if (pState->modComments)
printf(" comments\n");
if (pState->modConvertText)
printf(" convertText\n");
if (pState->modConvertAll)
printf(" convertAll\n");
if (pState->modOverwriteExisting)
printf(" overwriteExisting\n");
if (pState->modPreserveType)
printf(" preserveType\n");
if (pState->modPreserveTypeExtended)
printf(" preserveTypeExtended\n");
printf("\n");
}
#endif
/*
* ===========================================================================
* Simple set/get functions
* ===========================================================================
*/
char
NState_GetSystemPathSeparator(const NulibState* pState)
{
return pState->systemPathSeparator;
}
const char*
NState_GetProgramVersion(const NulibState* pState)
{
return pState->programVersion;
}
NuArchive*
NState_GetNuArchive(const NulibState* pState)
{
return pState->pArchive;
}
void
NState_SetNuArchive(NulibState* pState, NuArchive* pArchive)
{
pState->pArchive = pArchive;
}
Boolean
NState_GetSuppressOutput(const NulibState* pState)
{
return pState->suppressOutput;
}
void
NState_SetSuppressOutput(NulibState* pState, Boolean doSuppress)
{
pState->suppressOutput = doSuppress;
}
Boolean
NState_GetInputUnavailable(const NulibState* pState)
{
return pState->inputUnavailable;
}
void
NState_SetInputUnavailable(NulibState* pState, Boolean isUnavailable)
{
pState->inputUnavailable = isUnavailable;
}
NuRecordIdx
NState_GetRenameFromIdx(const NulibState* pState)
{
return pState->renameFromIdx;
}
void
NState_SetRenameFromIdx(NulibState* pState, NuRecordIdx recordIdx)
{
pState->renameFromIdx = recordIdx;
}
char*
NState_GetRenameToStr(const NulibState* pState)
{
return pState->renameToStr;
}
void
NState_SetRenameToStr(NulibState* pState, char* str)
{
Free(pState->renameToStr);
pState->renameToStr = str;
}
NuDataSink*
NState_GetPipeSink(const NulibState* pState)
{
return pState->pPipeSink;
}
NuDataSink*
NState_GetCommentSink(const NulibState* pState)
{
return pState->pCommentSink;
}
long
NState_GetMatchCount(const NulibState* pState)
{
return pState->matchCount;
}
void
NState_SetMatchCount(NulibState* pState, long count)
{
pState->matchCount = count;
}
void
NState_IncMatchCount(NulibState* pState)
{
pState->matchCount++;
}
void
NState_AddToTotals(NulibState* pState, long len, long compLen)
{
pState->totalLen += len;
pState->totalCompLen += compLen;
}
void
NState_GetTotals(NulibState* pState, long* pTotalLen, long* pTotalCompLen)
{
*pTotalLen = pState->totalLen;
*pTotalCompLen = pState->totalCompLen;
}
long
NState_GetTempPathnameLen(NulibState* pState)
{
return pState->tempPathnameAlloc;
}
void
NState_SetTempPathnameLen(NulibState* pState, long len)
{
char* newBuf;
len++; /* add one for the '\0' */
if (pState->tempPathnameAlloc < len) {
if (pState->tempPathnameBuf == nil)
newBuf = Malloc(len);
else
newBuf = Realloc(pState->tempPathnameBuf, len);
assert(newBuf != nil);
if (newBuf == nil) {
Free(pState->tempPathnameBuf);
pState->tempPathnameBuf = nil;
pState->tempPathnameAlloc = 0;
ReportError(kNuErrMalloc, "buf realloc failed (%ld)", len);
return;
}
pState->tempPathnameBuf = newBuf;
pState->tempPathnameAlloc = len;
}
}
char*
NState_GetTempPathnameBuf(NulibState* pState)
{
return pState->tempPathnameBuf;
}
Command
NState_GetCommand(const NulibState* pState)
{
return pState->command;
}
void
NState_SetCommand(NulibState* pState, Command cmd)
{
pState->command = cmd;
}
const char*
NState_GetArchiveFilename(const NulibState* pState)
{
return pState->archiveFilename;
}
void
NState_SetArchiveFilename(NulibState* pState, const char* archiveFilename)
{
pState->archiveFilename = archiveFilename;
}
char* const*
NState_GetFilespecPointer(const NulibState* pState)
{
return pState->filespecPointer;
}
void
NState_SetFilespecPointer(NulibState* pState, char* const* filespecPointer)
{
pState->filespecPointer = filespecPointer;
}
long
NState_GetFilespecCount(const NulibState* pState)
{
return pState->filespecCount;
}
void
NState_SetFilespecCount(NulibState* pState, long filespecCount)
{
pState->filespecCount = filespecCount;
}
Boolean
NState_GetModUpdate(const NulibState* pState)
{
return pState->modUpdate;
}
void
NState_SetModUpdate(NulibState* pState, Boolean val)
{
pState->modUpdate = val;
}
Boolean
NState_GetModFreshen(const NulibState* pState)
{
return pState->modFreshen;
}
void
NState_SetModFreshen(NulibState* pState, Boolean val)
{
pState->modFreshen = val;
}
Boolean
NState_GetModRecurse(const NulibState* pState)
{
return pState->modRecurse;
}
void
NState_SetModRecurse(NulibState* pState, Boolean val)
{
pState->modRecurse = val;
}
Boolean
NState_GetModJunkPaths(const NulibState* pState)
{
return pState->modJunkPaths;
}
void
NState_SetModJunkPaths(NulibState* pState, Boolean val)
{
pState->modJunkPaths = val;
}
Boolean
NState_GetModNoCompression(const NulibState* pState)
{
return pState->modNoCompression;
}
void
NState_SetModNoCompression(NulibState* pState, Boolean val)
{
pState->modNoCompression = val;
}
Boolean
NState_GetModComments(const NulibState* pState)
{
return pState->modComments;
}
void
NState_SetModComments(NulibState* pState, Boolean val)
{
pState->modComments = val;
}
Boolean
NState_GetModConvertText(const NulibState* pState)
{
return pState->modConvertText;
}
void
NState_SetModConvertText(NulibState* pState, Boolean val)
{
pState->modConvertText = val;
}
Boolean
NState_GetModConvertAll(const NulibState* pState)
{
return pState->modConvertAll;
}
void
NState_SetModConvertAll(NulibState* pState, Boolean val)
{
pState->modConvertAll = val;
}
Boolean
NState_GetModOverwriteExisting(const NulibState* pState)
{
return pState->modOverwriteExisting;
}
void
NState_SetModOverwriteExisting(NulibState* pState, Boolean val)
{
pState->modOverwriteExisting = val;
}
Boolean
NState_GetModAddAsDisk(const NulibState* pState)
{
return pState->modAddAsDisk;
}
void
NState_SetModAddAsDisk(NulibState* pState, Boolean val)
{
pState->modAddAsDisk = val;
}
Boolean
NState_GetModPreserveType(const NulibState* pState)
{
return pState->modPreserveType;
}
void
NState_SetModPreserveType(NulibState* pState, Boolean val)
{
pState->modPreserveType = val;
}
Boolean
NState_GetModPreserveTypeExtended(const NulibState* pState)
{
return pState->modPreserveTypeExtended;
}
void
NState_SetModPreserveTypeExtended(NulibState* pState, Boolean val)
{
pState->modPreserveTypeExtended = val;
}

145
nulib2/State.h Normal file
View File

@ -0,0 +1,145 @@
/*
* Nulib2
* Copyright (C) 2000 by Andy McFadden, All Rights Reserved.
* This is free software; you can redistribute it and/or modify it under the
* terms of the GNU General Public License, see the file COPYING.
*/
#ifndef __State__
#define __State__
#include "MiscStuff.h"
#include "NufxLib.h"
/*
* Things you can tell Nulib2 to do.
*
* (Some debug code in NState_DebugDump() is sensitive to the order here.)
*/
typedef enum Command {
kCommandUnknown = 0,
kCommandAdd,
kCommandDelete,
kCommandExtract,
kCommandExtractToPipe,
kCommandListShort,
kCommandListVerbose,
kCommandListDebug,
kCommandTest
} Command;
/*
* Program-wide state.
*/
typedef struct NulibState {
/* global goodness */
const char* programVersion;
/* system-specific values */
char systemPathSeparator;
/* pointer to archive we're working with */
NuArchive* pArchive;
/* misc state */
Boolean suppressOutput;
Boolean inputUnavailable;
NuRecordIdx renameFromIdx;
char* renameToStr;
NuDataSink* pPipeSink;
NuDataSink* pCommentSink;
long matchCount;
long totalLen;
long totalCompLen;
/* temp storage */
long tempPathnameAlloc;
char* tempPathnameBuf;
/* command-line options */
Command command;
Boolean modUpdate;
Boolean modFreshen;
Boolean modRecurse;
Boolean modJunkPaths;
Boolean modNoCompression;
Boolean modComments;
Boolean modConvertText;
Boolean modConvertAll;
Boolean modOverwriteExisting;
Boolean modAddAsDisk;
Boolean modPreserveType;
Boolean modPreserveTypeExtended;
const char* archiveFilename;
char* const* filespecPointer;
long filespecCount;
} NulibState;
NuError NState_Init(NulibState** ppState);
NuError NState_ExtraInit(NulibState* pState);
void NState_Free(NulibState* pState);
#ifdef DEBUG_MSGS
void NState_DebugDump(const NulibState* pState);
#endif
char NState_GetSystemPathSeparator(const NulibState* pState);
const char* NState_GetProgramVersion(const NulibState* pState);
NuArchive* NState_GetNuArchive(const NulibState* pState);
void NState_SetNuArchive(NulibState* pState, NuArchive* pArchive);
Boolean NState_GetSuppressOutput(const NulibState* pState);
void NState_SetSuppressOutput(NulibState* pState, Boolean doSuppress);
Boolean NState_GetInputUnavailable(const NulibState* pState);
void NState_SetInputUnavailable(NulibState* pState, Boolean isUnavailable);
NuRecordIdx NState_GetRenameFromIdx(const NulibState* pState);
void NState_SetRenameFromIdx(NulibState* pState, NuRecordIdx recordIdx);
char* NState_GetRenameToStr(const NulibState* pState);
void NState_SetRenameToStr(NulibState* pState, char* str);
NuDataSink* NState_GetPipeSink(const NulibState* pState);
NuDataSink* NState_GetCommentSink(const NulibState* pState);
long NState_GetMatchCount(const NulibState* pState);
void NState_SetMatchCount(NulibState* pState, long count);
void NState_IncMatchCount(NulibState* pState);
void NState_AddToTotals(NulibState* pState, long len, long compLen);
void NState_GetTotals(NulibState* pState, long* pTotalLen, long* pTotalCompLen);
long NState_GetTempPathnameLen(NulibState* pState);
void NState_SetTempPathnameLen(NulibState* pState, long len);
char* NState_GetTempPathnameBuf(NulibState* pState);
Command NState_GetCommand(const NulibState* pState);
void NState_SetCommand(NulibState* pState, Command cmd);
const char* NState_GetArchiveFilename(const NulibState* pState);
void NState_SetArchiveFilename(NulibState* pState, const char* archiveFilename);
char* const* NState_GetFilespecPointer(const NulibState* pState);
void NState_SetFilespecPointer(NulibState* pState, char* const* filespec);
long NState_GetFilespecCount(const NulibState* pState);
void NState_SetFilespecCount(NulibState* pState, long filespecCount);
Boolean NState_GetModUpdate(const NulibState* pState);
void NState_SetModUpdate(NulibState* pState, Boolean val);
Boolean NState_GetModFreshen(const NulibState* pState);
void NState_SetModFreshen(NulibState* pState, Boolean val);
Boolean NState_GetModRecurse(const NulibState* pState);
void NState_SetModRecurse(NulibState* pState, Boolean val);
Boolean NState_GetModJunkPaths(const NulibState* pState);
void NState_SetModJunkPaths(NulibState* pState, Boolean val);
Boolean NState_GetModNoCompression(const NulibState* pState);
void NState_SetModNoCompression(NulibState* pState, Boolean val);
Boolean NState_GetModComments(const NulibState* pState);
void NState_SetModComments(NulibState* pState, Boolean val);
Boolean NState_GetModConvertText(const NulibState* pState);
void NState_SetModConvertText(NulibState* pState, Boolean val);
Boolean NState_GetModConvertAll(const NulibState* pState);
void NState_SetModConvertAll(NulibState* pState, Boolean val);
Boolean NState_GetModOverwriteExisting(const NulibState* pState);
void NState_SetModOverwriteExisting(NulibState* pState, Boolean val);
Boolean NState_GetModAddAsDisk(const NulibState* pState);
void NState_SetModAddAsDisk(NulibState* pState, Boolean val);
Boolean NState_GetModPreserveType(const NulibState* pState);
void NState_SetModPreserveType(NulibState* pState, Boolean val);
Boolean NState_GetModPreserveTypeExtended(const NulibState* pState);
void NState_SetModPreserveTypeExtended(NulibState* pState, Boolean val);
#endif /*__State__*/

47
nulib2/SunOS4.h Normal file
View File

@ -0,0 +1,47 @@
/*
* NuFX archive manipulation library
* Copyright (C) 2000 by Andy McFadden, All Rights Reserved.
* This is free software; you can redistribute it and/or modify it under the
* terms of the GNU Library General Public License, see the file COPYING.LIB.
*
* This file was adapted from Devin Reade's "sunos4.h" in NuLib 3.2.5.
* It is provided for compilation under SunOS 4.x, when an ANSI compiler
* (such as gcc) is used. The system header files aren't quite sufficient
* to eliminate hordes of warnings.
*/
#ifndef __SunOS4__
#define __SunOS4__
#ifdef __GNUC__
extern int _flsbuf(int, FILE*);
extern int _filbuf(FILE*);
#endif
extern void bcopy(char*, char*, int);
extern int fclose(FILE*);
extern int fflush(FILE*);
extern int fprintf(FILE*, const char*, ...);
extern int fread(char*, int, int, FILE *);
extern int fseek(FILE*, long, int);
extern int ftruncate(int, off_t);
extern int fwrite(const char*, int, int, FILE*);
extern char* mktemp(char *template);
extern time_t mktime(struct tm*);
extern int perror(const char*);
extern int printf(const char*, ...);
extern int remove(const char*);
extern int rename(const char*, const char*);
extern int tolower(int);
extern int setvbuf(FILE*, char*, int, int);
extern int sscanf(char*, const char*, ...);
extern int strcasecmp(const char*, const char*);
extern int strncasecmp(const char*, const char*, size_t);
extern long strtol(const char *, char **, int);
extern int system(const char*);
extern time_t timelocal(struct tm*);
extern time_t time(time_t*);
extern int toupper(int);
extern int vfprintf(FILE*, const char *, va_list);
extern char* vsprintf(char *str, const char *format, va_list ap);
#endif /*__SunOS4__*/

213
nulib2/SysDefs.h Normal file
View File

@ -0,0 +1,213 @@
/*
* Nulib2
* Copyright (C) 2000 by Andy McFadden, All Rights Reserved.
* This is free software; you can redistribute it and/or modify it under the
* terms of the GNU General Public License, see the file COPYING.
*
* This was adapted from gzip's "tailor.h" and NuLib's "nudefs.h".
*/
#ifndef __SysDefs__
#define __SysDefs__
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#ifdef DEBUG_VERBOSE
# define DEBUG_MSGS
#endif
/* these should exist everywhere */
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include <memory.h>
#include <time.h>
#include <errno.h>
#include <assert.h>
/* basic Win32 stuff -- info-zip has much more complete defs */
#if defined(WIN32) || defined(MSDOS)
# define WINDOWS_LIKE
# ifndef HAVE_CONFIG_H
# define HAVE_FCNTL_H
# define HAVE_MALLOC_H
# define HAVE_STDLIB_H
# define HAVE_SYS_STAT_H
# undef HAVE_SYS_TIME_H
# define HAVE_SYS_TYPES_H
# undef HAVE_UNISTD_H
# undef HAVE_UTIME_H
# define HAVE_SYS_UTIME_H
# define HAVE_WINDOWS_H
# define HAVE_FDOPEN
# undef HAVE_FTRUNCATE
# define HAVE_MEMMOVE
# undef HAVE_MKSTEMP
# define HAVE_MKTIME
# define HAVE_SNPRINTF
# undef HAVE_STRCASECMP
# undef HAVE_STRNCASECMP
# define HAVE_STRERROR
# define HAVE_STRTOUL
# define HAVE_VSNPRINTF
# define SNPRINTF_DECLARED
# define VSNPRINTF_DECLARED
# define SPRINTF_RETURNS_INT
# define uchar unsigned char
# define ushort unsigned short
# define uint unsigned int
# define ulong unsigned long
# define inline /*Visual C++6.0 can't inline ".c" files*/
# define mode_t int
# endif
# include <io.h>
# define FOPEN_WANTS_B
# define HAVE_CHSIZE
# define snprintf _snprintf
# define vsnprintf _vsnprintf
#endif
#ifdef HAVE_MALLOC_H
# include <malloc.h>
#endif
#ifdef HAVE_STDLIB_H
# include <stdlib.h>
#endif
#ifdef HAVE_SYS_STAT_H
# include <sys/stat.h>
#endif
#ifdef HAVE_SYS_TIME_H
# include <sys/time.h>
#endif
#ifdef HAVE_SYS_TYPES_H
# include <sys/types.h>
#endif
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif
#if defined(WINDOWS_LIKE)
# ifndef F_OK
# define F_OK 02
# endif
# ifndef R_OK
# define R_OK 04
# endif
#endif
#if defined(__unix__) || defined(__unix) || defined(__BEOS__) || \
defined(__hpux) || defined(_AIX) || defined(__APPLE__)
# define UNIX_LIKE
#endif
#if defined(__MSDOS__) && !defined(MSDOS)
# define MSDOS
#endif
#if defined(__OS2__) && !defined(OS2)
# define OS2
#endif
#if defined(OS2) && defined(MSDOS) /* MS C under OS/2 */
# undef MSDOS
#endif
/* this ought to get trimmed down */
#ifdef MSDOS
# ifndef __GNUC__
# ifdef __TURBOC__
# define NO_OFF_T
# ifdef __BORLANDC__
# define DIRENT
# else
# define NO_UTIME
# endif
# else /* MSC */
# define HAVE_SYS_UTIME_H
# define NO_UTIME_H
# endif
# endif
# define PATH_SEP '\\'
# define PATH_SEP2 '/'
# define PATH_SEP3 ':'
# ifdef MAX_PATH
# define MAX_PATH_LEN MAX_PATH
# else
# define MAX_PATH_LEN 128
# endif
# define NO_MULTIPLE_DOTS
# define MAX_EXT_CHARS 3
# define NO_CHOWN
# define PROTO
# ifndef HAVE_CONFIG_H
# define STDC_HEADERS
# endif
# define NO_SIZE_CHECK
# define SYSTEM_DEFAULT_EOL "\r\n"
#endif
#ifdef WIN32 /* Windows 95/98/NT */
# define HAVE_SYS_UTIME_H
# define NO_UTIME_H
# define PATH_SEP '\\'
# define PATH_SEP2 '/'
# define PATH_SEP3 ':'
# ifdef MAX_PATH
# define MAX_PATH_LEN MAX_PATH
# else
# define MAX_PATH_LEN 260
# endif
# define PROTO
# define STDC_HEADERS
# define SYSTEM_DEFAULT_EOL "\r\n"
#endif
#ifdef MACOS
# define PATH_SEP ':'
# define NO_CHOWN
# define NO_UTIME
# define SYSTEM_DEFAULT_EOL "\r"
#endif
#if defined(APW) || defined(__ORCAC__)
# define __appleiigs__
# pragma lint -1
# pragma memorymodel 1
# pragma optimize 7
/*# pragma debug 25 */
# define PATH_SEP ':'
# define SYSTEM_DEFAULT_EOL "\r"
# ifdef GNO
# define HAS_DIRENT
# endif
#endif
#ifdef __GNUC__ /* this was missing from BeOS __MWERKS__, and probably others */
# define HAS__FUNCTION__
#endif
#if defined(__sun__) && !defined(__SVR4)
# include "SunOS4.h"
#endif
/* general defaults, mainly for UNIX */
#ifndef PATH_SEP
# define PATH_SEP '/'
#endif
#ifndef SYSTEM_DEFAULT_EOL
# define SYSTEM_DEFAULT_EOL "\n"
#endif
#ifndef MAX_PATH_LEN
# define MAX_PATH_LEN 1024
#endif
#endif /*__SysDefs__*/

1009
nulib2/SysUtils.c Normal file

File diff suppressed because it is too large Load Diff

1088
nulib2/config.guess vendored Normal file

File diff suppressed because it is too large Load Diff

109
nulib2/config.h.in Normal file
View File

@ -0,0 +1,109 @@
/* config.h.in. Generated automatically from configure.in by autoheader. */
/* Define to empty if the keyword does not work. */
#undef const
/* Define if utime(file, NULL) sets file's timestamp to the present. */
#undef HAVE_UTIME_NULL
/* Define to `int' if <sys/types.h> doesn't define. */
#undef mode_t
/* Define to `long' if <sys/types.h> doesn't define. */
#undef off_t
/* Define if the setvbuf function takes the buffering type as its second
argument and the buffer pointer as the third, as on System V
before release 3. */
#undef SETVBUF_REVERSED
/* Define to `unsigned' if <sys/types.h> doesn't define. */
#undef size_t
/* Define if you have the ANSI C header files. */
#undef STDC_HEADERS
/* Define if your <sys/time.h> declares struct tm. */
#undef TM_IN_SYS_TIME
/* Define to `unsigned char' if <sys/types.h> doesn't define. */
#undef uchar
/* Define to `unsigned short' if <sys/types.h> doesn't define. */
#undef ushort
/* Define to `unsigned int' if <sys/types.h> doesn't define. */
#undef uint
/* Define to `unsigned long' if <sys/types.h> doesn't define. */
#undef ulong
/* Define to `int' if <sys/types.h> doesn't define. */
#undef mode_t
/* Define to `long' if <sys/types.h> doesn't define. */
#undef off_t
/* Define to `unsigned' if <sys/types.h> doesn't define. */
#undef size_t
/* Define if you have the memmove function. */
#undef HAVE_MEMMOVE
/* Define if you have the mkdir function. */
#undef HAVE_MKDIR
/* Define if you have the strcasecmp function. */
#undef HAVE_STRCASECMP
/* Define if you have the strncasecmp function. */
#undef HAVE_STRNCASECMP
/* Define if you have the strerror function. */
#undef HAVE_STRERROR
/* Define if you have the strtoul function. */
#undef HAVE_STRTOUL
/* Define if you have the <dirent.h> header file. */
#undef HAVE_DIRENT_H
/* Define if you have the <fcntl.h> header file. */
#undef HAVE_FCNTL_H
/* Define if you have the <limits.h> header file. */
#undef HAVE_LIMITS_H
/* Define if you have the <malloc.h> header file. */
#undef HAVE_MALLOC_H
/* Define if you have the <stdlib.h> header file. */
#undef HAVE_STDLIB_H
/* Define if you have the <ndir.h> header file. */
#undef HAVE_NDIR_H
/* Define if you have the <strings.h> header file. */
#undef HAVE_STRINGS_H
/* Define if you have the <sys/dir.h> header file. */
#undef HAVE_SYS_DIR_H
/* Define if you have the <sys/ndir.h> header file. */
#undef HAVE_SYS_NDIR_H
/* Define if you have the <sys/stat.h> header file. */
#undef HAVE_SYS_STAT_H
/* Define if you have the <sys/time.h> header file. */
#undef HAVE_SYS_TIME_H
/* Define if you have the <sys/types.h> header file. */
#undef HAVE_SYS_TYPES_H
/* Define if you have the <unistd.h> header file. */
#undef HAVE_UNISTD_H
/* Define if we want to use the dmalloc library (--enable-dmalloc). */
#undef USE_DMALLOC

1220
nulib2/config.sub vendored Normal file

File diff suppressed because it is too large Load Diff

2069
nulib2/configure vendored Executable file

File diff suppressed because it is too large Load Diff

86
nulib2/configure.in Normal file
View File

@ -0,0 +1,86 @@
dnl Process this file with autoconf to produce a configure script.
AC_INIT(Main.c)
AC_CONFIG_HEADER(config.h)
dnl Checks for programs.
AC_CANONICAL_HOST
AC_PROG_CC
AC_PROG_INSTALL
dnl Checks for libraries.
dnl Replace `main' with a function in -lr:
dnl AC_CHECK_LIB(r, main)
dnl Checks for header files.
AC_HEADER_DIRENT
AC_HEADER_STDC
AC_CHECK_HEADERS(fcntl.h limits.h malloc.h stdlib.h strings.h sys/stat.h \
sys/time.h sys/types.h unistd.h)
dnl Checks for typedefs, structures, and compiler characteristics.
AC_C_CONST
AC_TYPE_MODE_T
AC_TYPE_OFF_T
AC_TYPE_SIZE_T
AC_STRUCT_TM
AC_CHECK_TYPE(uchar, unsigned char)
AC_CHECK_TYPE(ushort, unsigned short)
AC_CHECK_TYPE(uint, unsigned int)
AC_CHECK_TYPE(ulong, unsigned long)
dnl Checks for library functions.
dnl AC_FUNC_SETVBUF_REVERSED
AC_FUNC_UTIME_NULL
AC_CHECK_FUNCS(memmove mkdir strtoul strcasecmp strncasecmp strerror)
dnl BeOS doesn't like /usr/local/include, and gets feisty about it. If libdir
dnl and includedir are set to defaults, replace them with BeOS values. This
dnl might be going a little too far...
if test "$host_os" = "beos"; then
if test "$prefix" = "NONE" -a \
"$includedir" = '${prefix}/include' -a \
"$libdir" = '${exec_prefix}/lib' -a \
"$bindir" = '${exec_prefix}/bin' -a \
"$mandir" = '${prefix}/man'
then
echo replacing install locations with BeOS values
prefix=/boot
includedir='${prefix}/develop/headers'
libdir='${exec_prefix}/home/config/lib'
bindir='${exec_prefix}/home/config/bin'
mandir='/tmp'
AC_SUBST(prefix)
AC_SUBST(includedir)
AC_SUBST(libdir)
AC_SUBST(bindir)
AC_SUBST(mandir)
fi
fi
dnl Figure out what the build and link flags should be
if test "$host_cpu" = "powerpc" -a "$host_os" = "beos"; then
dnl BeOS/PPC with Metrowerks compiler
CC=cc
GCC=
CFLAGS='-proc 603 -opt full'
echo "forcing CC to \"$CC\" and CFLAGS to \"$CFLAGS\""
fi
dnl if we're using gcc, include gcc-specific warning flags
dnl (
if test -z "$GCC"; then
BUILD_FLAGS='$(OPT)'
else
BUILD_FLAGS='$(OPT) $(GCC_FLAGS)'
fi
AC_SUBST(BUILD_FLAGS)
DMALLOC=
AC_ARG_ENABLE(dmalloc, [ --enable-dmalloc: do dmalloc stuff], \
[ echo "--- enabling dmalloc";
DMALLOC="-L/usr/local/lib -ldmalloc"; AC_DEFINE(USE_DMALLOC) ])
AC_SUBST(DMALLOC)
AC_OUTPUT(Makefile)

251
nulib2/install-sh Executable file
View File

@ -0,0 +1,251 @@
#!/bin/sh
#
# install - install a program, script, or datafile
# This comes from X11R5 (mit/util/scripts/install.sh).
#
# Copyright 1991 by the Massachusetts Institute of Technology
#
# Permission to use, copy, modify, distribute, and sell this software and its
# documentation for any purpose is hereby granted without fee, provided that
# the above copyright notice appear in all copies and that both that
# copyright notice and this permission notice appear in supporting
# documentation, and that the name of M.I.T. not be used in advertising or
# publicity pertaining to distribution of the software without specific,
# written prior permission. M.I.T. makes no representations about the
# suitability of this software for any purpose. It is provided "as is"
# without express or implied warranty.
#
# Calling this script install-sh is preferred over install.sh, to prevent
# `make' implicit rules from creating a file called install from it
# when there is no Makefile.
#
# This script is compatible with the BSD install script, but was written
# from scratch. It can only install one file at a time, a restriction
# shared with many OS's install programs.
# set DOITPROG to echo to test this script
# Don't use :- since 4.3BSD and earlier shells don't like it.
doit="${DOITPROG-}"
# put in absolute paths if you don't have them in your path; or use env. vars.
mvprog="${MVPROG-mv}"
cpprog="${CPPROG-cp}"
chmodprog="${CHMODPROG-chmod}"
chownprog="${CHOWNPROG-chown}"
chgrpprog="${CHGRPPROG-chgrp}"
stripprog="${STRIPPROG-strip}"
rmprog="${RMPROG-rm}"
mkdirprog="${MKDIRPROG-mkdir}"
transformbasename=""
transform_arg=""
instcmd="$mvprog"
chmodcmd="$chmodprog 0755"
chowncmd=""
chgrpcmd=""
stripcmd=""
rmcmd="$rmprog -f"
mvcmd="$mvprog"
src=""
dst=""
dir_arg=""
while [ x"$1" != x ]; do
case $1 in
-c) instcmd="$cpprog"
shift
continue;;
-d) dir_arg=true
shift
continue;;
-m) chmodcmd="$chmodprog $2"
shift
shift
continue;;
-o) chowncmd="$chownprog $2"
shift
shift
continue;;
-g) chgrpcmd="$chgrpprog $2"
shift
shift
continue;;
-s) stripcmd="$stripprog"
shift
continue;;
-t=*) transformarg=`echo $1 | sed 's/-t=//'`
shift
continue;;
-b=*) transformbasename=`echo $1 | sed 's/-b=//'`
shift
continue;;
*) if [ x"$src" = x ]
then
src=$1
else
# this colon is to work around a 386BSD /bin/sh bug
:
dst=$1
fi
shift
continue;;
esac
done
if [ x"$src" = x ]
then
echo "install: no input file specified"
exit 1
else
true
fi
if [ x"$dir_arg" != x ]; then
dst=$src
src=""
if [ -d $dst ]; then
instcmd=:
chmodcmd=""
else
instcmd=mkdir
fi
else
# Waiting for this to be detected by the "$instcmd $src $dsttmp" command
# might cause directories to be created, which would be especially bad
# if $src (and thus $dsttmp) contains '*'.
if [ -f $src -o -d $src ]
then
true
else
echo "install: $src does not exist"
exit 1
fi
if [ x"$dst" = x ]
then
echo "install: no destination specified"
exit 1
else
true
fi
# If destination is a directory, append the input filename; if your system
# does not like double slashes in filenames, you may need to add some logic
if [ -d $dst ]
then
dst="$dst"/`basename $src`
else
true
fi
fi
## this sed command emulates the dirname command
dstdir=`echo $dst | sed -e 's,[^/]*$,,;s,/$,,;s,^$,.,'`
# Make sure that the destination directory exists.
# this part is taken from Noah Friedman's mkinstalldirs script
# Skip lots of stat calls in the usual case.
if [ ! -d "$dstdir" ]; then
defaultIFS='
'
IFS="${IFS-${defaultIFS}}"
oIFS="${IFS}"
# Some sh's can't handle IFS=/ for some reason.
IFS='%'
set - `echo ${dstdir} | sed -e 's@/@%@g' -e 's@^%@/@'`
IFS="${oIFS}"
pathcomp=''
while [ $# -ne 0 ] ; do
pathcomp="${pathcomp}${1}"
shift
if [ ! -d "${pathcomp}" ] ;
then
$mkdirprog "${pathcomp}"
else
true
fi
pathcomp="${pathcomp}/"
done
fi
if [ x"$dir_arg" != x ]
then
$doit $instcmd $dst &&
if [ x"$chowncmd" != x ]; then $doit $chowncmd $dst; else true ; fi &&
if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dst; else true ; fi &&
if [ x"$stripcmd" != x ]; then $doit $stripcmd $dst; else true ; fi &&
if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dst; else true ; fi
else
# If we're going to rename the final executable, determine the name now.
if [ x"$transformarg" = x ]
then
dstfile=`basename $dst`
else
dstfile=`basename $dst $transformbasename |
sed $transformarg`$transformbasename
fi
# don't allow the sed command to completely eliminate the filename
if [ x"$dstfile" = x ]
then
dstfile=`basename $dst`
else
true
fi
# Make a temp file name in the proper directory.
dsttmp=$dstdir/#inst.$$#
# Move or copy the file name to the temp name
$doit $instcmd $src $dsttmp &&
trap "rm -f ${dsttmp}" 0 &&
# and set any options; do chmod last to preserve setuid bits
# If any of these fail, we abort the whole thing. If we want to
# ignore errors from any of these, just make sure not to ignore
# errors from the above "$doit $instcmd $src $dsttmp" command.
if [ x"$chowncmd" != x ]; then $doit $chowncmd $dsttmp; else true;fi &&
if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dsttmp; else true;fi &&
if [ x"$stripcmd" != x ]; then $doit $stripcmd $dsttmp; else true;fi &&
if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dsttmp; else true;fi &&
# Now rename the file to the real destination.
$doit $rmcmd -f $dstdir/$dstfile &&
$doit $mvcmd $dsttmp $dstdir/$dstfile
fi &&
exit 0

34
nulib2/mkinstalldirs Executable file
View File

@ -0,0 +1,34 @@
#! /bin/sh
# mkinstalldirs --- make directory hierarchy
# Author: Noah Friedman <friedman@prep.ai.mit.edu>
# Created: 1993-05-16
# Last modified: 1995-03-05
# Public domain
errstatus=0
for file in ${1+"$@"} ; do
set fnord `echo ":$file" | sed -ne 's/^:\//#/;s/^://;s/\// /g;s/^#/\//;p'`
shift
pathcomp=
for d in ${1+"$@"} ; do
pathcomp="$pathcomp$d"
case "$pathcomp" in
-* ) pathcomp=./$pathcomp ;;
esac
if test ! -d "$pathcomp"; then
echo "mkdir $pathcomp" 1>&2
mkdir "$pathcomp" > /dev/null 2>&1 || lasterr=$?
fi
if test ! -d "$pathcomp"; then
errstatus=$lasterr
fi
pathcomp="$pathcomp/"
done
done
exit $errstatus

178
nulib2/nulib2.1 Normal file
View File

@ -0,0 +1,178 @@
.\" nulib2.1
.\" Copyright (C) 2000 by Andy McFadden. All Rights Reserved.
.\" This is free software; you can redistribute it and/or modify it under the
.\" terms of the GNU General Public License, see the file COPYING.
.\"
.\" The general structure of this man page was borrowed from "zip.1" in
.\" the Red Hat Linux 6.0 distribution.
.\"
.TH NULIB2 1L "17 Jan 2000"
.SH NAME
nulib2 \- package and compress (archive) files
.SH SYNOPSIS
.B nulib2
.RB \-command[modifiers]
.I archive
.I [ filenames ]
.SH DESCRIPTION
.I nulib2
is a disk and file archiver for NuFX (ShrinkIt) files. It can add files
to and extract files from
.IR .SHK ,
.IR .BXY ,
.IR .SEA
(as created by GS/ShrinkIt), and
.I .BSE
files.
.LP
When extracting, testing, or listing the contents of an archive, you can
specify "-" for the archive name. The archive will be read from stdin.
.LP
Filenames are considered case-sensitive.
.\" .LP
.\" The
.\" .I filenames
.\" will be compared in a case-sensitive fashion. While this would be
.\" inappropriate for most UNIX systems, it makes sense for Apple II archives,
.\" because most Apple II filesystems are case-insensitive.
.LP
This man page contains a summary of available options. For full
documentation and the latest versions, visit http://www.nulib.com/.
.SH "OPTIONS"
.TP
.B \-a
Add files to an archive. If the archive does not exist, a new one
will be created. The list of files to add must be given.
.TP
.B \-d
Delete files from an archive. The set of files to delete must be provided.
.TP
.B \-i
Integrity test. If no files are listed, all files in the archive are
tested.
.TP
.B \-p
Pipe extraction. All extracted files are written to stdout instead of
a file on disk. Normal archive progress messages are suppressed.
.TP
.B \-t
Table of contents. Provides a simple list of files in the archive, one
per line.
.TP
.B \-v
Verbose table of contents. Output similar to what ShrinkIt displays is
shown.
.TP
.B \-x
Extract files from an archive. If no files are listed, all files in
the archive are extracted.
.\" There's also a '-z' command that does a verbose archive dump, but it's
.\" only available if NufxLib was built with debugging enabled.
.SH "MODIFIERS"
.TP
.B \-0
Don't use compression. Files added will be stored without compression.
.TP
.B \-c
Comments. When extracting, comments will be displayed. When adding,
you will be prompted to enter a one-line comment for every file.
.TP
.B \-e
Preserve ProDOS file types. See the ProDOS File Type Preservation document
on http://www.nulib.com/ for details on how this works.
.TP
.B \-ee
Preserve file types, using extended names. A file extension is appended
to extracted files. Useful on operating systems like Windows, where
filename extensions are important. When adding files,
.I nulib2
will try to guess at correct file types by examining the filename extension.
.TP
.B \-f
Freshen files. When adding, files in the archive that are older than files
on disk are "freshened", meaning that no new files are added, and files
that are the same age or newer aren't touched. Works similarly when
extracting.
.TP
.B \-j
Junk directory names. Only the filename is kept; the rest of the pathname
is thrown away. Empty directories aren't stored. Works when adding or
extracting.
.TP
.B \-k
Store files as disk images. Files that are a multiple of 512 bytes will
be added as disk images rather than normal files. This does not override
the "-e" flag.
.TP
.B \-l
Auto-convert text files. A reasonably smart algorithm is used to identify
which files are text and which aren't during extraction. It then converts
whatever EOL
indicator is being used by the text file into something appropriate for
the current system.
.TP
.B \-ll
Auto-convert all files. All files being extracted are considered text,
and will be converted. Don't use this unless you're sure you need it.
.TP
.B \-r
Recurse into subdirectories. When adding, this causes
.I nulib2
to descend into subdirectories and store all of the files found. When
extracting, testing, or deleting, this causes the files listed to match
against all records whose prefix matches, allowing you to extract, test,
or delete entire subdirectories from the archive.
.TP
.B \-u
Update files. When adding, files in the archive that are older than files
on disk are updated. Files in the archive that are the same age or newer
aren't touched. New files will be added. Works similarly when extracting.
.SH "EXAMPLES"
A simple example:
.IP
\fCnulib2 a foo.shk *\fP
.LP
creates the archive
.I foo.shk
(assuming it doesn't exist) and stores all of the files in the current
directory in it, in compressed form.
.LP
If you wanted to add all the files in the current directory, as well as
all files in all subdirectories, you could use:
.IP
\fCnulib2 ar foo.shk *\fP
.LP
to recursively descend into the directory tree.
.LP
Using the command:
.IP
\fCnulib2 xe foo.shk\fP
.LP
would extract all files from
.I foo.shk,
preserving ProDOS file types. If you then used the command:
.IP
\fCnulib2 aer foo.shk *\fP
.LP
you would add the files,
preserving the file types of anything that was extracted with the
"-e" flag set.
.LP
A handy way to look at text documents is to use:
.IP
\fCnulib2 xeel foo.shk\fP
.LP
to convert end-of-line terminators (e.g. CRLF to LF) as the files are
being extracted. The "-ee" flag adds ".TXT" to all files with a ProDOS
file type of TXT ($04).
.SH "SEE ALSO"
compress(1),
tar(1),
zip(1L),
unzip(1L),
nulib(1L)
.SH BUGS
Probably.
.SH AUTHOR
Copyright (C) 2000 by Andy McFadden. All Rights Reserved.
.\" end of file