Subversion Repositories Kolibri OS

Rev

Blame | Last modification | View Log | Download | RSS feed

  1. /*
  2.   Copyright (c) 1990-2005 Info-ZIP.  All rights reserved.
  3.  
  4.   See the accompanying file LICENSE, version 2000-Apr-09 or later
  5.   (the contents of which are also included in unzip.h) for terms of use.
  6.   If, for some reason, all these files are missing, the Info-ZIP license
  7.   also may be found at:  ftp://ftp.info-zip.org/pub/infozip/license.html
  8. */
  9. /*---------------------------------------------------------------------------
  10.  
  11.   vmmvs.c (for both VM/CMS and MVS)
  12.  
  13.   Contains:  vmmvs_open_infile()
  14.              open_outfile()
  15.              close_outfile()
  16.              close_infile()
  17.              getVMMVSexfield()
  18.              do_wild()
  19.              mapattr()
  20.              mapname()
  21.              checkdir()
  22.              check_for_newer()
  23.              stat()
  24.              version()
  25.  
  26.   ---------------------------------------------------------------------------*/
  27.  
  28.  
  29. #define __VMMVS_C       /* identifies this source module */
  30. #define UNZIP_INTERNAL
  31. #include "unzip.h"
  32.  
  33.  
  34. /********************************/
  35. /* Function vmmvs_open_infile() */
  36. /********************************/
  37.  
  38. FILE *vmmvs_open_infile(__G)
  39.    __GDEF
  40. {
  41.    FILE *fzip;
  42.  
  43.    G.tempfn = NULL;
  44.  
  45.    fzip = fopen(G.zipfn, FOPR);
  46.  
  47. #if 0
  48.    /* Let's try it without the convert for a while -- RG Hartwig */
  49.  
  50.    if ((fzip = fopen(G.zipfn,"rb,recfm=fb")) == NULL) {
  51.       size_t cnt;
  52.       char *buf;
  53.       FILE *in, *out;
  54.  
  55.       if ((buf = (char *)malloc(32768)) == NULL) return NULL;
  56.       if ((G.tempfn = tmpnam(NULL)) == NULL) return NULL;
  57.       if ((in = fopen(G.zipfn,"rb")) != NULL &&
  58.           (out = fopen(G.tempfn,"wb,recfm=fb,lrecl=1")) != NULL) {
  59.          Trace((stdout,"Converting ZIP file to fixed record format...\n"));
  60.          while (!feof(in)) {
  61.             cnt = fread(buf,1,32768,in);
  62.             if (cnt) fwrite(buf,1,cnt,out);
  63.          }
  64.       }
  65.       else {
  66.          free(buf);
  67.          fclose(out);
  68.          fclose(in);
  69.          return NULL;
  70.       }
  71.       free(buf);
  72.       fclose(out);
  73.       fclose(in);
  74.  
  75.       fzip = fopen(G.tempfn,"rb,recfm=fb");
  76.       if (fzip == NULL) return NULL;
  77.  
  78.       /* Update the G.ziplen value since it might have changed after
  79.          the reformatting copy. */
  80.       fseek(fzip,0L,SEEK_SET);
  81.       fseek(fzip,0L,SEEK_END);
  82.       G.ziplen = ftell(fzip);
  83.    }
  84.  
  85. #endif
  86.  
  87.    return fzip;
  88. }
  89.  
  90.  
  91. /***************************/
  92. /* Function open_outfile() */
  93. /***************************/
  94.  
  95. int open_outfile(__G)           /* return 1 if fail */
  96.     __GDEF
  97. {
  98.     char type[100];
  99.     char *mode = NULL;
  100. #ifdef MVS
  101.     /* Check if the output file already exists and do not overwrite its DCB */
  102.     char basefilename[PATH_MAX], *p;
  103.     FILE *exists;
  104.  
  105.     /* Get the base file name, without any member name */
  106.     strcpy(basefilename, G.filename);
  107.     if ((p = strchr(basefilename, '(')) != NULL) {
  108.        if (basefilename[0] == '\'')
  109.           *p++ = '\'';
  110.        *p = '\0';
  111.     }
  112.     exists = fopen(basefilename, FOPR);
  113.     if (exists) {
  114.        if (G.pInfo->textmode)
  115.            mode = FOPWTE;       /* Text file, existing */
  116.        else
  117.            mode = FOPWE;        /* Binary file, existing */
  118.        fclose(exists);
  119.     }
  120.     else   /* continued on next line */
  121. #endif /* MVS */
  122.     if (G.pInfo->textmode) {
  123.         if (mode == NULL)
  124.            mode = FOPWT;
  125.     } else if (G.lrec.extra_field_length > 0 && G.extra_field != NULL) {
  126.         unsigned lef_len = (unsigned)(G.lrec.extra_field_length);
  127.         uch *lef_buf = G.extra_field;
  128.  
  129.         while (lef_len > EB_HEADSIZE) {
  130.             unsigned eb_id = makeword(&lef_buf[EB_ID]);
  131.             unsigned eb_dlen = makeword(&lef_buf[EB_LEN]);
  132.  
  133.             if (eb_dlen > (lef_len - EB_HEADSIZE)) {
  134.                 /* Discovered some extra field inconsistency! */
  135.                 TTrace((stderr,
  136.                         "open_outfile: block length %u > rest lef_size %u\n",
  137.                         eb_dlen, lef_len - EB_HEADSIZE));
  138.                 break;
  139.             }
  140.  
  141.             if ((eb_id == EF_VMCMS || eb_id == EF_MVS) &&
  142.                 (getVMMVSexfield(type, lef_buf, eb_dlen) > 0)) {
  143.                 mode = type;
  144.                 break;
  145.             }
  146.  
  147.             /* Skip this extra field block */
  148.             lef_buf += (eb_dlen + EB_HEADSIZE);
  149.             lef_len -= (eb_dlen + EB_HEADSIZE);
  150.         }
  151.     }
  152.     if (mode == NULL) mode = FOPW;
  153.  
  154.     Trace((stderr, "Output file='%s' opening with '%s'\n", G.filename, mode));
  155.     if ((G.outfile = fopen(G.filename, mode)) == NULL) {
  156.         Info(slide, 0x401, ((char *)slide, "\nerror:  cannot create %s\n",
  157.              FnFilter1(G.filename)));
  158.         Trace((stderr, "error %d: '%s'\n", errno, strerror(errno)));
  159.         return 1;
  160.     }
  161.     return 0;
  162. } /* end function open_outfile() */
  163.  
  164.  
  165. /****************************/
  166. /* Function close_outfile() */
  167. /****************************/
  168.  
  169. void close_outfile(__G)
  170.    __GDEF
  171. {
  172.    fclose(G.outfile);
  173. } /* end function close_outfile() */
  174.  
  175.  
  176. /***************************/
  177. /* Function close_infile() */
  178. /***************************/
  179.  
  180. void close_infile(__G)
  181.    __GDEF
  182. {
  183.    fclose(G.zipfd);
  184.  
  185.    /* If we're working from a temp file, erase it now */
  186.    if (G.tempfn)
  187.       remove(G.tempfn);
  188.  
  189. } /* end function close_infile() */
  190.  
  191.  
  192.  
  193. /******************************/
  194. /* Function getVMMVSexfield() */
  195. /******************************/
  196.  
  197. extent getVMMVSexfield(type, ef_block, datalen)
  198.     char *type;
  199.     uch *ef_block;
  200.     unsigned datalen;
  201. {
  202.     fldata_t *fdata = (fldata_t *) &ef_block[4];
  203.  
  204.     if (datalen < sizeof(fldata_t))
  205.         return 0;
  206.  
  207.     strcpy(type, "w");
  208.     strcat(type,  fdata->__openmode == __TEXT   ? ""
  209.                  :fdata->__openmode == __BINARY ? "b"
  210.                  :fdata->__openmode == __RECORD ? "b,type=record"
  211.                  :                                "");
  212.     strcat(type, ",recfm=");
  213.     strcat(type,  fdata->__recfmF? "F"
  214.                  :fdata->__recfmV? "V"
  215.                  :fdata->__recfmU? "U"
  216.                  :                 "?");
  217.     if (fdata->__recfmBlk) strcat(type, "B");
  218.     if (fdata->__recfmS)   strcat(type, "S");
  219.     if (fdata->__recfmASA) strcat(type, "A");
  220.     if (fdata->__recfmM)   strcat(type, "M");
  221.     sprintf(type+strlen(type), ",lrecl=%ld", fdata->__recfmV
  222.                                               ? fdata->__maxreclen+4
  223.                                               : fdata->__maxreclen);
  224. #ifdef VM_CMS
  225.     /* For CMS, use blocksize for FB files only */
  226.     if (fdata->__recfmBlk)
  227.        sprintf(type+strlen(type), ",blksize=%ld", fdata->__blksize);
  228. #else
  229.     /* For MVS, always use blocksize */
  230.     sprintf(type+strlen(type), ",blksize=%ld", fdata->__blksize);
  231. #endif
  232.  
  233.     return strlen(type);
  234. } /* end function getVMMVSexfield() */
  235.  
  236.  
  237.  
  238. #ifndef SFX
  239.  
  240. /**********************/
  241. /* Function do_wild() */   /* for porting: dir separator; match(ignore_case) */
  242. /**********************/
  243.  
  244. char *do_wild(__G__ wld)
  245.     __GDEF
  246.     ZCONST char *wld;      /* only used first time on a given dir */
  247. {
  248.     static int First = 0;
  249.     static char filename[256];
  250.  
  251.     if (First == 0) {
  252.        First = 1;
  253.        strncpy(filename, wld, sizeof(filename));
  254.        filename[sizeof(filename)-1] = '\0';
  255.        return filename;
  256.     }
  257.     else
  258.        return (char *)NULL;
  259.  
  260. } /* end function do_wild() */
  261.  
  262. #endif /* !SFX */
  263.  
  264.  
  265.  
  266. /************************/
  267. /*  Function mapattr()  */
  268. /************************/
  269.  
  270. int mapattr(__G)
  271.      __GDEF
  272. {
  273.     return 0;
  274. }
  275.  
  276. /************************/
  277. /*  Function mapname()  */
  278. /************************/
  279.  
  280. int mapname(__G__ renamed)
  281.     __GDEF
  282.     int renamed;
  283. /*
  284.  * returns:
  285.  *  MPN_OK          - no problem detected
  286.  *  MPN_INF_TRUNC   - caution (truncated filename)
  287.  *  MPN_INF_SKIP    - info "skip entry" (dir doesn't exist)
  288.  *  MPN_ERR_SKIP    - error -> skip entry
  289.  *  MPN_ERR_TOOLONG - error -> path is too long
  290.  *  MPN_NOMEM       - error (memory allocation failed) -> skip entry
  291.  *  [also MPN_VOL_LABEL, MPN_CREATED_DIR]
  292.  */
  293. {
  294.     char newname[FILNAMSIZ], *lbar;
  295. #ifdef MVS
  296.     char *pmember;
  297. #endif
  298.     int name_changed = MPN_OK;
  299.  
  300.     if (G.pInfo->vollabel)
  301.         return MPN_VOL_LABEL;   /* can't set disk volume labels in CMS_MVS */
  302.  
  303. #ifdef MVS
  304.     /* Remove bad characters for MVS from the filename */
  305.     while ((lbar = strpbrk(G.filename, "_+-")) != NULL) {
  306.        /* Must use memmove() here because data overlaps.  */
  307.        /* strcpy() gives undefined behavior in this case. */
  308.        memmove(lbar, lbar+1, strlen(lbar));
  309.        name_changed = MPN_INF_TRUNC;
  310.     }
  311. #endif
  312.  
  313.     /* Remove bad characters for MVS/CMS from the filename */
  314.     while ((lbar = strpbrk(G.filename, "()")) != NULL) {
  315.        memmove(lbar, lbar+1, strlen(lbar));
  316.        name_changed = MPN_INF_TRUNC;
  317.     }
  318.  
  319. #ifdef VM_CMS
  320.     if ((lbar = strrchr(G.filename, '/')) != NULL) {
  321.         strcpy(newname, lbar+1);
  322.         Trace((stderr, "File '%s' renamed to '%s'\n", G.filename, newname));
  323.         strcpy(G.filename, newname);
  324.         name_changed = MPN_INF_TRUNC;
  325.     }
  326. #else /* MVS */
  327.     if ((pmember = strrchr(G.filename, '/')) == NULL)
  328.         pmember = G.filename;
  329.     else
  330.         pmember++;
  331.  
  332.     /* search for extension in file name */
  333.     if ((lbar = strrchr(pmember, '.')) != NULL) {
  334.         *lbar++ = '\0';
  335.         strcpy(newname, pmember);
  336.         strcpy(pmember, lbar);
  337.         strcat(pmember, "(");
  338.         strcat(pmember, newname);
  339.         strcat(pmember, ")");
  340.     }
  341.  
  342.     /* Remove all 'internal' dots '.', to prevent false consideration as
  343.      * MVS path delimiters! */
  344.     while ((lbar = strrchr(G.filename, '.')) != NULL) {
  345.         memmove(lbar, lbar+1, strlen(lbar));
  346.         name_changed = MPN_INF_TRUNC;
  347.     }
  348.  
  349.     /* Finally, convert path delimiters from internal '/' to external '.' */
  350.     while ((lbar = strchr(G.filename, '/')) != NULL)
  351.         *lbar = '.';
  352. #endif /* ?VM_CMS */
  353.  
  354. #ifndef MVS
  355.     if ((lbar = strchr(G.filename, '.')) == NULL) {
  356.         printf("WARNING: file '%s' has no extension - renamed to '%s.NONAME'\n"\
  357.               ,G.filename, G.filename);
  358.        strcat(G.filename, ".NONAME");
  359.        name_changed = MPN_INF_TRUNC;
  360.     }
  361. #endif
  362.     checkdir(__G__ G.filename, GETPATH);
  363.  
  364.     return name_changed;
  365.  
  366. } /* end function mapname() */
  367.  
  368.  
  369. int checkdir(__G__ pathcomp, flag)
  370.     __GDEF
  371.     char *pathcomp;
  372.     int flag;
  373. /*
  374.  * returns:
  375.  *  MPN_OK          - no problem detected
  376.  *  MPN_INF_TRUNC   - (on APPEND_NAME) truncated filename
  377.  *  MPN_INF_SKIP    - path doesn't exist, not allowed to create
  378.  *  MPN_ERR_SKIP    - path doesn't exist, tried to create and failed; or path
  379.  *                    exists and is not a directory, but is supposed to be
  380.  *  MPN_ERR_TOOLONG - path is too long
  381.  *  MPN_NOMEM       - can't allocate memory for filename buffers
  382.  */
  383. {
  384.     static int rootlen = 0;     /* length of rootpath */
  385.     static char *rootpath;      /* user's "extract-to" directory */
  386.  
  387. #   define FN_MASK   7
  388. #   define FUNCTION  (flag & FN_MASK)
  389.  
  390.  
  391. /*---------------------------------------------------------------------------
  392.     ROOT:  if appropriate, store the path in rootpath and create it if neces-
  393.     sary; else assume it's a zipfile member and return.  This path segment
  394.     gets used in extracting all members from every zipfile specified on the
  395.     command line.  Note that under OS/2 and MS-DOS, if a candidate extract-to
  396.     directory specification includes a drive letter (leading "x:"), it is
  397.     treated just as if it had a trailing '/'--that is, one directory level
  398.     will be created if the path doesn't exist, unless this is otherwise pro-
  399.     hibited (e.g., freshening).
  400.   ---------------------------------------------------------------------------*/
  401.  
  402. #if (!defined(SFX) || defined(SFX_EXDIR))
  403.     if (FUNCTION == ROOT) {
  404.         Trace((stderr, "initializing root path to [%s]\n",
  405.           FnFilter1(pathcomp)));
  406.         if (pathcomp == (char *)NULL) {
  407.             rootlen = 0;
  408.         }
  409.         else if ((rootlen = strlen(pathcomp)) > 0) {
  410.             if ((rootpath = (char *)malloc(rootlen+1)) == NULL) {
  411.                 rootlen = 0;
  412.                 return MPN_NOMEM;
  413.             }
  414.             strcpy(rootpath, pathcomp);
  415.             Trace((stderr, "rootpath now = [%s]\n", rootpath));
  416.         }
  417.         return MPN_OK;
  418.     }
  419. #endif /* !SFX || SFX_EXDIR */
  420.  
  421. /*---------------------------------------------------------------------------
  422.     GETPATH:  copy full path to the string pointed at by pathcomp, and free
  423.     buildpath.
  424.   ---------------------------------------------------------------------------*/
  425.  
  426.     if (FUNCTION == GETPATH) {
  427.         if (rootlen > 0) {
  428. #ifdef VM_CMS                     /* put the exdir after the filename */
  429.            strcat(pathcomp, ".");       /* used as minidisk to be save on  */
  430.            strcat(pathcomp, rootpath);
  431. #else /* MVS */
  432.            char newfilename[PATH_MAX];
  433.            char *start_fname;
  434.            int quoted = 0;
  435.  
  436.            strcpy(newfilename, rootpath);
  437.            if (newfilename[0] == '\'') {
  438.               quoted = strlen(newfilename) - 1;
  439.               if (newfilename[quoted] == '\'')
  440.                  newfilename[quoted] = '\0';
  441.               else
  442.                  quoted = 0;
  443.            }
  444.            if (strchr(pathcomp, '(') == NULL) {
  445.               if ((start_fname = strrchr(pathcomp, '.')) == NULL) {
  446.                  start_fname = pathcomp;
  447.               }
  448.               else {
  449.                  *start_fname++ = '\0';
  450.                  strcat(newfilename, ".");
  451.                  strcat(newfilename, pathcomp);
  452.               }
  453.               strcat(newfilename, "(");
  454.               strcat(newfilename, start_fname);
  455.               strcat(newfilename, ")");
  456.            }
  457.            else {
  458.               strcat(newfilename, ".");
  459.               strcat(newfilename, pathcomp);
  460.            }
  461.            if (quoted)
  462.               strcat(newfilename, "'");
  463.            Trace((stdout, "new dataset : %s\n", newfilename));
  464.            strcpy(pathcomp, newfilename);
  465. #endif /* ?VM_CMS */
  466.         }
  467.         return MPN_OK;
  468.     }
  469.  
  470. /*---------------------------------------------------------------------------
  471.     END:  free rootpath, immediately prior to program exit.
  472.   ---------------------------------------------------------------------------*/
  473.  
  474.     if (FUNCTION == END) {
  475.         Trace((stderr, "freeing rootpath\n"));
  476.         if (rootlen > 0) {
  477.             free(rootpath);
  478.             rootlen = 0;
  479.         }
  480.         return MPN_OK;
  481.     }
  482.  
  483.     return MPN_INVALID; /* should never reach */
  484.  
  485. } /* end function checkdir() */
  486.  
  487.  
  488.  
  489.  
  490. /******************************/
  491. /* Function check_for_newer() */  /* used for overwriting/freshening/updating */
  492. /******************************/
  493.  
  494. int check_for_newer(__G__ filename)  /* return 1 if existing file is newer */
  495.     __GDEF                           /*  or equal; 0 if older; -1 if doesn't */
  496.     char *filename;                  /*  exist yet */
  497. {
  498.     FILE *stream;
  499.  
  500.     if ((stream = fopen(filename, FOPR)) != NULL) {
  501.        fclose(stream);
  502.        /* File exists, assume it is "newer" than archive entry. */
  503.        return EXISTS_AND_NEWER;
  504.     }
  505.     /* File does not exist. */
  506.     return DOES_NOT_EXIST;
  507. } /* end function check_for_newer() */
  508.  
  509.  
  510. /*********************/
  511. /*  Function stat()  */
  512. /*********************/
  513.  
  514. int stat(const char *path, struct stat *buf)
  515. {
  516.    FILE *fp;
  517.    char fname[PATH_MAX];
  518.    time_t ltime;
  519.  
  520.    if ((fp = fopen(path, FOPR)) != NULL) {
  521.       fldata_t fdata;
  522.       if (fldata( fp, fname, &fdata ) == 0) {
  523.          buf->st_dev  = fdata.__device;
  524.          buf->st_mode = *(short *)(&fdata);
  525.       }
  526.  
  527.       /* Determine file size by seeking to EOF */
  528.       fseek(fp,0L,SEEK_END);
  529.       buf->st_size = ftell(fp);
  530.       fclose(fp);
  531.  
  532.       /* set time fields in stat buf to current time. */
  533.       time(&ltime);
  534.       buf->st_atime =
  535.       buf->st_mtime =
  536.       buf->st_ctime = ltime;
  537.  
  538.       /* File exists, return success */
  539.       return 0;
  540.    }
  541.    return 1;
  542. }
  543.  
  544.  
  545.  
  546. #ifdef STAND_ALONE
  547. /***************************/
  548. /*  Function main_vmmvs()  */
  549. /***************************/
  550.  
  551. /* This function is called as main() to parse arguments                */
  552. /* into argc and argv.  This is required for stand-alone               */
  553. /* execution.  This calls the "real" main() when done.                 */
  554.  
  555. int MAIN_VMMVS(void)
  556.    {
  557.     int  argc=0;
  558.     char *argv[50];
  559.  
  560.     int  iArgLen;
  561.     char argstr[256];
  562.     char **pEPLIST, *pCmdStart, *pArgStart, *pArgEnd;
  563.  
  564.    /* Get address of extended parameter list from S/370 Register 0 */
  565.    pEPLIST = (char **)__xregs(0);
  566.  
  567.    /* Null-terminate the argument string */
  568.    pCmdStart = *(pEPLIST+0);
  569.    pArgStart = *(pEPLIST+1);
  570.    pArgEnd   = *(pEPLIST+2);
  571.    iArgLen   = pArgEnd - pCmdStart + 1;
  572.  
  573.    /* Make a copy of the command string */
  574.    memcpy(argstr, pCmdStart, iArgLen);
  575.    argstr[iArgLen] = '\0';  /* Null-terminate */
  576.  
  577.    /* Store first token (cmd) */
  578.    argv[argc++] = strtok(argstr, " ");
  579.  
  580.    /* Store the rest (args) */
  581.    while (argv[argc-1])
  582.       argv[argc++] = strtok(NULL, " ");
  583.    argc--;  /* Back off last NULL entry */
  584.  
  585.    /* Call "real" main() function */
  586.    return MAIN(argc, argv);
  587. }
  588. #endif  /* STAND_ALONE */
  589.  
  590.  
  591.  
  592. #ifndef SFX
  593.  
  594. /************************/
  595. /*  Function version()  */
  596. /************************/
  597.  
  598. void version(__G)
  599.     __GDEF
  600. {
  601.     int len;
  602.     char liblvlmsg [50+1];
  603.     char *compiler = "?";
  604.     char *platform = "?";
  605.     char complevel[64];
  606.  
  607.     /* Map the runtime library level information */
  608.     union {
  609.        unsigned int iVRM;
  610.        struct {
  611.           unsigned int pd:4;    /* Product designation */
  612.           unsigned int vv:4;    /* Version             */
  613.           unsigned int rr:8;    /* Release             */
  614.           unsigned int mm:16;   /* Modification level  */
  615.        } xVRM;
  616.     } VRM;
  617.  
  618.  
  619.     /* Break down the runtime library level */
  620.     VRM.iVRM = __librel();
  621.     sprintf(liblvlmsg, "Using runtime library level %s V%dR%dM%d",
  622.             (VRM.xVRM.pd==1 ? "LE" : "CE"),
  623.             VRM.xVRM.vv, VRM.xVRM.rr, VRM.xVRM.mm);
  624.     /* Note:  LE = Language Environment, CE = Common Env. (C/370). */
  625.     /* This refers ONLY to the current runtimes, not the compiler. */
  626.  
  627.  
  628. #ifdef VM_CMS
  629.     platform = "VM/CMS";
  630.     #ifdef __IBMC__
  631.        compiler = "IBM C";
  632.     #else
  633.        compiler  = "C/370";
  634.     #endif
  635. #endif
  636.  
  637. #ifdef MVS
  638.     platform = "MVS";
  639.     #ifdef __IBMC__
  640.        compiler = "IBM C/C++";
  641.     #else
  642.        compiler = "C/370";
  643.     #endif
  644. #endif
  645.  
  646. #ifdef __COMPILER_VER__
  647.     VRM.iVRM = __COMPILER_VER__;
  648.     sprintf(complevel," V%dR%dM%d",
  649.             VRM.xVRM.vv, VRM.xVRM.rr, VRM.xVRM.mm);
  650. #else
  651. #ifdef __IBMC__
  652.     sprintf(complevel," V%dR%d", __IBMC__ / 100, (__IBMC__ % 100)/10);
  653. #else
  654.     complevel[0] = '\0';
  655. #endif
  656. #endif
  657.  
  658.  
  659.     /* Output is in the form "Compiled with %s%s for %s%s%s%s." */
  660.     len = sprintf((char *)slide, LoadFarString(CompiledWith),
  661.  
  662.     /* Add compiler name and level */
  663.     compiler, complevel,
  664.  
  665.     /* Add compile environment */
  666.     platform,
  667.  
  668.     /* Add timestamp */
  669. #ifdef __DATE__
  670.       " on " __DATE__
  671. #ifdef __TIME__
  672.       " at " __TIME__
  673. #endif
  674. #endif
  675.       ".\n", "",
  676.       liblvlmsg
  677.     );
  678.  
  679.     (*G.message)((zvoid *)&G, slide, (ulg)len, 0);
  680.  
  681. } /* end function version() */
  682.  
  683. #endif /* !SFX */
  684.