mirror of https://github.com/fadden/nulib2.git
Imported sources.
This commit is contained in:
commit
5615fa90d4
File diff suppressed because it is too large
Load Diff
|
@ -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;
|
||||
}
|
||||
|
|
@ -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!
|
|
@ -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
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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*/
|
File diff suppressed because it is too large
Load Diff
|
@ -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;
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
|
@ -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);
|
||||
}
|
||||
|
|
@ -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.
|
||||
|
File diff suppressed because it is too large
Load Diff
|
@ -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.
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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__*/
|
|
@ -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
|
||||
|
|
@ -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.
|
||||
|
|
@ -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__*/
|
|
@ -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__*/
|
|
@ -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.)
|
||||
|
File diff suppressed because it is too large
Load Diff
|
@ -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;
|
||||
}
|
||||
|
|
@ -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__*/
|
|
@ -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__*/
|
File diff suppressed because it is too large
Load Diff
|
@ -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;
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
|
@ -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
|
||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -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)
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
@ -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);
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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.
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
@ -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);
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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.
|
|
@ -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
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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.
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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.
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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__*/
|
|
@ -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
|
||||
|
|
@ -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__*/
|
|
@ -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.
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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__*/
|
|
@ -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__*/
|
|
@ -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__*/
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -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
|
||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -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)
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
Loading…
Reference in New Issue