e9b1c1bac6
git-svn-id: svn://kolibrios.org@6725 a494cfbc-eb01-0410-851d-a64ba20cac60
684 lines
19 KiB
C
684 lines
19 KiB
C
/*
|
|
Copyright (c) 1990-2005 Info-ZIP. All rights reserved.
|
|
|
|
See the accompanying file LICENSE, version 2000-Apr-09 or later
|
|
(the contents of which are also included in unzip.h) for terms of use.
|
|
If, for some reason, all these files are missing, the Info-ZIP license
|
|
also may be found at: ftp://ftp.info-zip.org/pub/infozip/license.html
|
|
*/
|
|
/*---------------------------------------------------------------------------
|
|
|
|
vmmvs.c (for both VM/CMS and MVS)
|
|
|
|
Contains: vmmvs_open_infile()
|
|
open_outfile()
|
|
close_outfile()
|
|
close_infile()
|
|
getVMMVSexfield()
|
|
do_wild()
|
|
mapattr()
|
|
mapname()
|
|
checkdir()
|
|
check_for_newer()
|
|
stat()
|
|
version()
|
|
|
|
---------------------------------------------------------------------------*/
|
|
|
|
|
|
#define __VMMVS_C /* identifies this source module */
|
|
#define UNZIP_INTERNAL
|
|
#include "unzip.h"
|
|
|
|
|
|
/********************************/
|
|
/* Function vmmvs_open_infile() */
|
|
/********************************/
|
|
|
|
FILE *vmmvs_open_infile(__G)
|
|
__GDEF
|
|
{
|
|
FILE *fzip;
|
|
|
|
G.tempfn = NULL;
|
|
|
|
fzip = fopen(G.zipfn, FOPR);
|
|
|
|
#if 0
|
|
/* Let's try it without the convert for a while -- RG Hartwig */
|
|
|
|
if ((fzip = fopen(G.zipfn,"rb,recfm=fb")) == NULL) {
|
|
size_t cnt;
|
|
char *buf;
|
|
FILE *in, *out;
|
|
|
|
if ((buf = (char *)malloc(32768)) == NULL) return NULL;
|
|
if ((G.tempfn = tmpnam(NULL)) == NULL) return NULL;
|
|
if ((in = fopen(G.zipfn,"rb")) != NULL &&
|
|
(out = fopen(G.tempfn,"wb,recfm=fb,lrecl=1")) != NULL) {
|
|
Trace((stdout,"Converting ZIP file to fixed record format...\n"));
|
|
while (!feof(in)) {
|
|
cnt = fread(buf,1,32768,in);
|
|
if (cnt) fwrite(buf,1,cnt,out);
|
|
}
|
|
}
|
|
else {
|
|
free(buf);
|
|
fclose(out);
|
|
fclose(in);
|
|
return NULL;
|
|
}
|
|
free(buf);
|
|
fclose(out);
|
|
fclose(in);
|
|
|
|
fzip = fopen(G.tempfn,"rb,recfm=fb");
|
|
if (fzip == NULL) return NULL;
|
|
|
|
/* Update the G.ziplen value since it might have changed after
|
|
the reformatting copy. */
|
|
fseek(fzip,0L,SEEK_SET);
|
|
fseek(fzip,0L,SEEK_END);
|
|
G.ziplen = ftell(fzip);
|
|
}
|
|
|
|
#endif
|
|
|
|
return fzip;
|
|
}
|
|
|
|
|
|
/***************************/
|
|
/* Function open_outfile() */
|
|
/***************************/
|
|
|
|
int open_outfile(__G) /* return 1 if fail */
|
|
__GDEF
|
|
{
|
|
char type[100];
|
|
char *mode = NULL;
|
|
#ifdef MVS
|
|
/* Check if the output file already exists and do not overwrite its DCB */
|
|
char basefilename[PATH_MAX], *p;
|
|
FILE *exists;
|
|
|
|
/* Get the base file name, without any member name */
|
|
strcpy(basefilename, G.filename);
|
|
if ((p = strchr(basefilename, '(')) != NULL) {
|
|
if (basefilename[0] == '\'')
|
|
*p++ = '\'';
|
|
*p = '\0';
|
|
}
|
|
exists = fopen(basefilename, FOPR);
|
|
if (exists) {
|
|
if (G.pInfo->textmode)
|
|
mode = FOPWTE; /* Text file, existing */
|
|
else
|
|
mode = FOPWE; /* Binary file, existing */
|
|
fclose(exists);
|
|
}
|
|
else /* continued on next line */
|
|
#endif /* MVS */
|
|
if (G.pInfo->textmode) {
|
|
if (mode == NULL)
|
|
mode = FOPWT;
|
|
} else if (G.lrec.extra_field_length > 0 && G.extra_field != NULL) {
|
|
unsigned lef_len = (unsigned)(G.lrec.extra_field_length);
|
|
uch *lef_buf = G.extra_field;
|
|
|
|
while (lef_len > EB_HEADSIZE) {
|
|
unsigned eb_id = makeword(&lef_buf[EB_ID]);
|
|
unsigned eb_dlen = makeword(&lef_buf[EB_LEN]);
|
|
|
|
if (eb_dlen > (lef_len - EB_HEADSIZE)) {
|
|
/* Discovered some extra field inconsistency! */
|
|
TTrace((stderr,
|
|
"open_outfile: block length %u > rest lef_size %u\n",
|
|
eb_dlen, lef_len - EB_HEADSIZE));
|
|
break;
|
|
}
|
|
|
|
if ((eb_id == EF_VMCMS || eb_id == EF_MVS) &&
|
|
(getVMMVSexfield(type, lef_buf, eb_dlen) > 0)) {
|
|
mode = type;
|
|
break;
|
|
}
|
|
|
|
/* Skip this extra field block */
|
|
lef_buf += (eb_dlen + EB_HEADSIZE);
|
|
lef_len -= (eb_dlen + EB_HEADSIZE);
|
|
}
|
|
}
|
|
if (mode == NULL) mode = FOPW;
|
|
|
|
Trace((stderr, "Output file='%s' opening with '%s'\n", G.filename, mode));
|
|
if ((G.outfile = fopen(G.filename, mode)) == NULL) {
|
|
Info(slide, 0x401, ((char *)slide, "\nerror: cannot create %s\n",
|
|
FnFilter1(G.filename)));
|
|
Trace((stderr, "error %d: '%s'\n", errno, strerror(errno)));
|
|
return 1;
|
|
}
|
|
return 0;
|
|
} /* end function open_outfile() */
|
|
|
|
|
|
/****************************/
|
|
/* Function close_outfile() */
|
|
/****************************/
|
|
|
|
void close_outfile(__G)
|
|
__GDEF
|
|
{
|
|
fclose(G.outfile);
|
|
} /* end function close_outfile() */
|
|
|
|
|
|
/***************************/
|
|
/* Function close_infile() */
|
|
/***************************/
|
|
|
|
void close_infile(__G)
|
|
__GDEF
|
|
{
|
|
fclose(G.zipfd);
|
|
|
|
/* If we're working from a temp file, erase it now */
|
|
if (G.tempfn)
|
|
remove(G.tempfn);
|
|
|
|
} /* end function close_infile() */
|
|
|
|
|
|
|
|
/******************************/
|
|
/* Function getVMMVSexfield() */
|
|
/******************************/
|
|
|
|
extent getVMMVSexfield(type, ef_block, datalen)
|
|
char *type;
|
|
uch *ef_block;
|
|
unsigned datalen;
|
|
{
|
|
fldata_t *fdata = (fldata_t *) &ef_block[4];
|
|
|
|
if (datalen < sizeof(fldata_t))
|
|
return 0;
|
|
|
|
strcpy(type, "w");
|
|
strcat(type, fdata->__openmode == __TEXT ? ""
|
|
:fdata->__openmode == __BINARY ? "b"
|
|
:fdata->__openmode == __RECORD ? "b,type=record"
|
|
: "");
|
|
strcat(type, ",recfm=");
|
|
strcat(type, fdata->__recfmF? "F"
|
|
:fdata->__recfmV? "V"
|
|
:fdata->__recfmU? "U"
|
|
: "?");
|
|
if (fdata->__recfmBlk) strcat(type, "B");
|
|
if (fdata->__recfmS) strcat(type, "S");
|
|
if (fdata->__recfmASA) strcat(type, "A");
|
|
if (fdata->__recfmM) strcat(type, "M");
|
|
sprintf(type+strlen(type), ",lrecl=%ld", fdata->__recfmV
|
|
? fdata->__maxreclen+4
|
|
: fdata->__maxreclen);
|
|
#ifdef VM_CMS
|
|
/* For CMS, use blocksize for FB files only */
|
|
if (fdata->__recfmBlk)
|
|
sprintf(type+strlen(type), ",blksize=%ld", fdata->__blksize);
|
|
#else
|
|
/* For MVS, always use blocksize */
|
|
sprintf(type+strlen(type), ",blksize=%ld", fdata->__blksize);
|
|
#endif
|
|
|
|
return strlen(type);
|
|
} /* end function getVMMVSexfield() */
|
|
|
|
|
|
|
|
#ifndef SFX
|
|
|
|
/**********************/
|
|
/* Function do_wild() */ /* for porting: dir separator; match(ignore_case) */
|
|
/**********************/
|
|
|
|
char *do_wild(__G__ wld)
|
|
__GDEF
|
|
ZCONST char *wld; /* only used first time on a given dir */
|
|
{
|
|
static int First = 0;
|
|
static char filename[256];
|
|
|
|
if (First == 0) {
|
|
First = 1;
|
|
strncpy(filename, wld, sizeof(filename));
|
|
filename[sizeof(filename)-1] = '\0';
|
|
return filename;
|
|
}
|
|
else
|
|
return (char *)NULL;
|
|
|
|
} /* end function do_wild() */
|
|
|
|
#endif /* !SFX */
|
|
|
|
|
|
|
|
/************************/
|
|
/* Function mapattr() */
|
|
/************************/
|
|
|
|
int mapattr(__G)
|
|
__GDEF
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
/************************/
|
|
/* Function mapname() */
|
|
/************************/
|
|
|
|
int mapname(__G__ renamed)
|
|
__GDEF
|
|
int renamed;
|
|
/*
|
|
* returns:
|
|
* MPN_OK - no problem detected
|
|
* MPN_INF_TRUNC - caution (truncated filename)
|
|
* MPN_INF_SKIP - info "skip entry" (dir doesn't exist)
|
|
* MPN_ERR_SKIP - error -> skip entry
|
|
* MPN_ERR_TOOLONG - error -> path is too long
|
|
* MPN_NOMEM - error (memory allocation failed) -> skip entry
|
|
* [also MPN_VOL_LABEL, MPN_CREATED_DIR]
|
|
*/
|
|
{
|
|
char newname[FILNAMSIZ], *lbar;
|
|
#ifdef MVS
|
|
char *pmember;
|
|
#endif
|
|
int name_changed = MPN_OK;
|
|
|
|
if (G.pInfo->vollabel)
|
|
return MPN_VOL_LABEL; /* can't set disk volume labels in CMS_MVS */
|
|
|
|
#ifdef MVS
|
|
/* Remove bad characters for MVS from the filename */
|
|
while ((lbar = strpbrk(G.filename, "_+-")) != NULL) {
|
|
/* Must use memmove() here because data overlaps. */
|
|
/* strcpy() gives undefined behavior in this case. */
|
|
memmove(lbar, lbar+1, strlen(lbar));
|
|
name_changed = MPN_INF_TRUNC;
|
|
}
|
|
#endif
|
|
|
|
/* Remove bad characters for MVS/CMS from the filename */
|
|
while ((lbar = strpbrk(G.filename, "()")) != NULL) {
|
|
memmove(lbar, lbar+1, strlen(lbar));
|
|
name_changed = MPN_INF_TRUNC;
|
|
}
|
|
|
|
#ifdef VM_CMS
|
|
if ((lbar = strrchr(G.filename, '/')) != NULL) {
|
|
strcpy(newname, lbar+1);
|
|
Trace((stderr, "File '%s' renamed to '%s'\n", G.filename, newname));
|
|
strcpy(G.filename, newname);
|
|
name_changed = MPN_INF_TRUNC;
|
|
}
|
|
#else /* MVS */
|
|
if ((pmember = strrchr(G.filename, '/')) == NULL)
|
|
pmember = G.filename;
|
|
else
|
|
pmember++;
|
|
|
|
/* search for extension in file name */
|
|
if ((lbar = strrchr(pmember, '.')) != NULL) {
|
|
*lbar++ = '\0';
|
|
strcpy(newname, pmember);
|
|
strcpy(pmember, lbar);
|
|
strcat(pmember, "(");
|
|
strcat(pmember, newname);
|
|
strcat(pmember, ")");
|
|
}
|
|
|
|
/* Remove all 'internal' dots '.', to prevent false consideration as
|
|
* MVS path delimiters! */
|
|
while ((lbar = strrchr(G.filename, '.')) != NULL) {
|
|
memmove(lbar, lbar+1, strlen(lbar));
|
|
name_changed = MPN_INF_TRUNC;
|
|
}
|
|
|
|
/* Finally, convert path delimiters from internal '/' to external '.' */
|
|
while ((lbar = strchr(G.filename, '/')) != NULL)
|
|
*lbar = '.';
|
|
#endif /* ?VM_CMS */
|
|
|
|
#ifndef MVS
|
|
if ((lbar = strchr(G.filename, '.')) == NULL) {
|
|
printf("WARNING: file '%s' has no extension - renamed to '%s.NONAME'\n"\
|
|
,G.filename, G.filename);
|
|
strcat(G.filename, ".NONAME");
|
|
name_changed = MPN_INF_TRUNC;
|
|
}
|
|
#endif
|
|
checkdir(__G__ G.filename, GETPATH);
|
|
|
|
return name_changed;
|
|
|
|
} /* end function mapname() */
|
|
|
|
|
|
int checkdir(__G__ pathcomp, flag)
|
|
__GDEF
|
|
char *pathcomp;
|
|
int flag;
|
|
/*
|
|
* returns:
|
|
* MPN_OK - no problem detected
|
|
* MPN_INF_TRUNC - (on APPEND_NAME) truncated filename
|
|
* MPN_INF_SKIP - path doesn't exist, not allowed to create
|
|
* MPN_ERR_SKIP - path doesn't exist, tried to create and failed; or path
|
|
* exists and is not a directory, but is supposed to be
|
|
* MPN_ERR_TOOLONG - path is too long
|
|
* MPN_NOMEM - can't allocate memory for filename buffers
|
|
*/
|
|
{
|
|
static int rootlen = 0; /* length of rootpath */
|
|
static char *rootpath; /* user's "extract-to" directory */
|
|
|
|
# define FN_MASK 7
|
|
# define FUNCTION (flag & FN_MASK)
|
|
|
|
|
|
/*---------------------------------------------------------------------------
|
|
ROOT: if appropriate, store the path in rootpath and create it if neces-
|
|
sary; else assume it's a zipfile member and return. This path segment
|
|
gets used in extracting all members from every zipfile specified on the
|
|
command line. Note that under OS/2 and MS-DOS, if a candidate extract-to
|
|
directory specification includes a drive letter (leading "x:"), it is
|
|
treated just as if it had a trailing '/'--that is, one directory level
|
|
will be created if the path doesn't exist, unless this is otherwise pro-
|
|
hibited (e.g., freshening).
|
|
---------------------------------------------------------------------------*/
|
|
|
|
#if (!defined(SFX) || defined(SFX_EXDIR))
|
|
if (FUNCTION == ROOT) {
|
|
Trace((stderr, "initializing root path to [%s]\n",
|
|
FnFilter1(pathcomp)));
|
|
if (pathcomp == (char *)NULL) {
|
|
rootlen = 0;
|
|
}
|
|
else if ((rootlen = strlen(pathcomp)) > 0) {
|
|
if ((rootpath = (char *)malloc(rootlen+1)) == NULL) {
|
|
rootlen = 0;
|
|
return MPN_NOMEM;
|
|
}
|
|
strcpy(rootpath, pathcomp);
|
|
Trace((stderr, "rootpath now = [%s]\n", rootpath));
|
|
}
|
|
return MPN_OK;
|
|
}
|
|
#endif /* !SFX || SFX_EXDIR */
|
|
|
|
/*---------------------------------------------------------------------------
|
|
GETPATH: copy full path to the string pointed at by pathcomp, and free
|
|
buildpath.
|
|
---------------------------------------------------------------------------*/
|
|
|
|
if (FUNCTION == GETPATH) {
|
|
if (rootlen > 0) {
|
|
#ifdef VM_CMS /* put the exdir after the filename */
|
|
strcat(pathcomp, "."); /* used as minidisk to be save on */
|
|
strcat(pathcomp, rootpath);
|
|
#else /* MVS */
|
|
char newfilename[PATH_MAX];
|
|
char *start_fname;
|
|
int quoted = 0;
|
|
|
|
strcpy(newfilename, rootpath);
|
|
if (newfilename[0] == '\'') {
|
|
quoted = strlen(newfilename) - 1;
|
|
if (newfilename[quoted] == '\'')
|
|
newfilename[quoted] = '\0';
|
|
else
|
|
quoted = 0;
|
|
}
|
|
if (strchr(pathcomp, '(') == NULL) {
|
|
if ((start_fname = strrchr(pathcomp, '.')) == NULL) {
|
|
start_fname = pathcomp;
|
|
}
|
|
else {
|
|
*start_fname++ = '\0';
|
|
strcat(newfilename, ".");
|
|
strcat(newfilename, pathcomp);
|
|
}
|
|
strcat(newfilename, "(");
|
|
strcat(newfilename, start_fname);
|
|
strcat(newfilename, ")");
|
|
}
|
|
else {
|
|
strcat(newfilename, ".");
|
|
strcat(newfilename, pathcomp);
|
|
}
|
|
if (quoted)
|
|
strcat(newfilename, "'");
|
|
Trace((stdout, "new dataset : %s\n", newfilename));
|
|
strcpy(pathcomp, newfilename);
|
|
#endif /* ?VM_CMS */
|
|
}
|
|
return MPN_OK;
|
|
}
|
|
|
|
/*---------------------------------------------------------------------------
|
|
END: free rootpath, immediately prior to program exit.
|
|
---------------------------------------------------------------------------*/
|
|
|
|
if (FUNCTION == END) {
|
|
Trace((stderr, "freeing rootpath\n"));
|
|
if (rootlen > 0) {
|
|
free(rootpath);
|
|
rootlen = 0;
|
|
}
|
|
return MPN_OK;
|
|
}
|
|
|
|
return MPN_INVALID; /* should never reach */
|
|
|
|
} /* end function checkdir() */
|
|
|
|
|
|
|
|
|
|
/******************************/
|
|
/* Function check_for_newer() */ /* used for overwriting/freshening/updating */
|
|
/******************************/
|
|
|
|
int check_for_newer(__G__ filename) /* return 1 if existing file is newer */
|
|
__GDEF /* or equal; 0 if older; -1 if doesn't */
|
|
char *filename; /* exist yet */
|
|
{
|
|
FILE *stream;
|
|
|
|
if ((stream = fopen(filename, FOPR)) != NULL) {
|
|
fclose(stream);
|
|
/* File exists, assume it is "newer" than archive entry. */
|
|
return EXISTS_AND_NEWER;
|
|
}
|
|
/* File does not exist. */
|
|
return DOES_NOT_EXIST;
|
|
} /* end function check_for_newer() */
|
|
|
|
|
|
/*********************/
|
|
/* Function stat() */
|
|
/*********************/
|
|
|
|
int stat(const char *path, struct stat *buf)
|
|
{
|
|
FILE *fp;
|
|
char fname[PATH_MAX];
|
|
time_t ltime;
|
|
|
|
if ((fp = fopen(path, FOPR)) != NULL) {
|
|
fldata_t fdata;
|
|
if (fldata( fp, fname, &fdata ) == 0) {
|
|
buf->st_dev = fdata.__device;
|
|
buf->st_mode = *(short *)(&fdata);
|
|
}
|
|
|
|
/* Determine file size by seeking to EOF */
|
|
fseek(fp,0L,SEEK_END);
|
|
buf->st_size = ftell(fp);
|
|
fclose(fp);
|
|
|
|
/* set time fields in stat buf to current time. */
|
|
time(<ime);
|
|
buf->st_atime =
|
|
buf->st_mtime =
|
|
buf->st_ctime = ltime;
|
|
|
|
/* File exists, return success */
|
|
return 0;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
|
|
|
|
#ifdef STAND_ALONE
|
|
/***************************/
|
|
/* Function main_vmmvs() */
|
|
/***************************/
|
|
|
|
/* This function is called as main() to parse arguments */
|
|
/* into argc and argv. This is required for stand-alone */
|
|
/* execution. This calls the "real" main() when done. */
|
|
|
|
int MAIN_VMMVS(void)
|
|
{
|
|
int argc=0;
|
|
char *argv[50];
|
|
|
|
int iArgLen;
|
|
char argstr[256];
|
|
char **pEPLIST, *pCmdStart, *pArgStart, *pArgEnd;
|
|
|
|
/* Get address of extended parameter list from S/370 Register 0 */
|
|
pEPLIST = (char **)__xregs(0);
|
|
|
|
/* Null-terminate the argument string */
|
|
pCmdStart = *(pEPLIST+0);
|
|
pArgStart = *(pEPLIST+1);
|
|
pArgEnd = *(pEPLIST+2);
|
|
iArgLen = pArgEnd - pCmdStart + 1;
|
|
|
|
/* Make a copy of the command string */
|
|
memcpy(argstr, pCmdStart, iArgLen);
|
|
argstr[iArgLen] = '\0'; /* Null-terminate */
|
|
|
|
/* Store first token (cmd) */
|
|
argv[argc++] = strtok(argstr, " ");
|
|
|
|
/* Store the rest (args) */
|
|
while (argv[argc-1])
|
|
argv[argc++] = strtok(NULL, " ");
|
|
argc--; /* Back off last NULL entry */
|
|
|
|
/* Call "real" main() function */
|
|
return MAIN(argc, argv);
|
|
}
|
|
#endif /* STAND_ALONE */
|
|
|
|
|
|
|
|
#ifndef SFX
|
|
|
|
/************************/
|
|
/* Function version() */
|
|
/************************/
|
|
|
|
void version(__G)
|
|
__GDEF
|
|
{
|
|
int len;
|
|
char liblvlmsg [50+1];
|
|
char *compiler = "?";
|
|
char *platform = "?";
|
|
char complevel[64];
|
|
|
|
/* Map the runtime library level information */
|
|
union {
|
|
unsigned int iVRM;
|
|
struct {
|
|
unsigned int pd:4; /* Product designation */
|
|
unsigned int vv:4; /* Version */
|
|
unsigned int rr:8; /* Release */
|
|
unsigned int mm:16; /* Modification level */
|
|
} xVRM;
|
|
} VRM;
|
|
|
|
|
|
/* Break down the runtime library level */
|
|
VRM.iVRM = __librel();
|
|
sprintf(liblvlmsg, "Using runtime library level %s V%dR%dM%d",
|
|
(VRM.xVRM.pd==1 ? "LE" : "CE"),
|
|
VRM.xVRM.vv, VRM.xVRM.rr, VRM.xVRM.mm);
|
|
/* Note: LE = Language Environment, CE = Common Env. (C/370). */
|
|
/* This refers ONLY to the current runtimes, not the compiler. */
|
|
|
|
|
|
#ifdef VM_CMS
|
|
platform = "VM/CMS";
|
|
#ifdef __IBMC__
|
|
compiler = "IBM C";
|
|
#else
|
|
compiler = "C/370";
|
|
#endif
|
|
#endif
|
|
|
|
#ifdef MVS
|
|
platform = "MVS";
|
|
#ifdef __IBMC__
|
|
compiler = "IBM C/C++";
|
|
#else
|
|
compiler = "C/370";
|
|
#endif
|
|
#endif
|
|
|
|
#ifdef __COMPILER_VER__
|
|
VRM.iVRM = __COMPILER_VER__;
|
|
sprintf(complevel," V%dR%dM%d",
|
|
VRM.xVRM.vv, VRM.xVRM.rr, VRM.xVRM.mm);
|
|
#else
|
|
#ifdef __IBMC__
|
|
sprintf(complevel," V%dR%d", __IBMC__ / 100, (__IBMC__ % 100)/10);
|
|
#else
|
|
complevel[0] = '\0';
|
|
#endif
|
|
#endif
|
|
|
|
|
|
/* Output is in the form "Compiled with %s%s for %s%s%s%s." */
|
|
len = sprintf((char *)slide, LoadFarString(CompiledWith),
|
|
|
|
/* Add compiler name and level */
|
|
compiler, complevel,
|
|
|
|
/* Add compile environment */
|
|
platform,
|
|
|
|
/* Add timestamp */
|
|
#ifdef __DATE__
|
|
" on " __DATE__
|
|
#ifdef __TIME__
|
|
" at " __TIME__
|
|
#endif
|
|
#endif
|
|
".\n", "",
|
|
liblvlmsg
|
|
);
|
|
|
|
(*G.message)((zvoid *)&G, slide, (ulg)len, 0);
|
|
|
|
} /* end function version() */
|
|
|
|
#endif /* !SFX */
|