home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 9 Archive / 09-Archive.zip / zip22.zip / aosvs / aosvs.c next >
C/C++ Source or Header  |  1997-08-19  |  21KB  |  645 lines

  1. #include <dirent.h>
  2. #include <time.h>
  3.  
  4. #include "zip.h"
  5. #include <paru.h>                 /* parameter definitions */
  6. #include <sys_calls.h>            /* AOS/VS system call interface */
  7. #include <packets/filestatus.h>   /* AOS/VS ?FSTAT packet defs */
  8.  
  9. #ifndef UTIL            /* AOS/VS specific fileio code not needed for UTILs */
  10.  
  11. #define MATCH shmatch
  12. #define PAD 0
  13. #define PATH_END ':'
  14.  
  15. /*
  16.  * could probably avoid the union -
  17.  * all are same size & we're going to assume this
  18.  */
  19. typedef union zvsfstat_stru
  20. {
  21.      P_FSTAT        norm_fstat_packet;      /* normal fstat packet */
  22.      P_FSTAT_DIR    dir_fstat_packet;       /* DIR/CPD fstat packet */
  23.      P_FSTAT_UNIT   unit_fstat_packet;      /* unit (device) fstat packet */
  24.      P_FSTAT_IPC    ipc_fstat_packet;       /* IPC file fstat packet */
  25. } ZVSFSTAT_STRU;
  26.  
  27. typedef struct zextrafld
  28. {
  29.      char           extra_header_id[2]; /* set to VS - in theory, an int */
  30.      char           extra_data_size[2]; /* size of rest, in Intel little-endian order */
  31.      char           extra_sentinel[4];  /* set to FCI w/ trailing null */
  32.      unsigned char  extra_rev;          /* set to 10 for rev 1.0 */
  33.      ZVSFSTAT_STRU  fstat_packet;       /* the fstat packet */
  34.      char           aclbuf[$MXACL];     /* raw ACL, or link-resolution name */
  35. } ZEXTRAFLD;
  36.  
  37. #define ZEXTRA_HEADID   "VS"
  38. #define ZEXTRA_SENTINEL "FCI"
  39. #define ZEXTRA_REV      (unsigned char) 10
  40.  
  41. local ZEXTRAFLD   zzextrafld;         /* buffer for extra field containing
  42.                                          ?FSTAT packet & ACL buffer */
  43. local char        zlinkres[$MXPL];    /* buf for link resolution contents */
  44. local char        znamebuf[$MXPL];    /* buf for AOS/VS filename */
  45. static char     vsnamebuf[$MXPL];
  46. static char     uxnamebuf[FNMAX];
  47. static P_FSTAT  vsfstatbuf;
  48.  
  49. local ulg label_time = 0;
  50. local ulg label_mode = 0;
  51. local time_t label_utim = 0;
  52.  
  53. /* Local functions */
  54. local char *readd OF((DIR *));
  55.  
  56. char *readd(d)
  57. DIR *d;                 /* directory stream to read from */
  58. /* Return a pointer to the next name in the directory stream d, or NULL if
  59.    no more entries or an error occurs. */
  60. {
  61.   struct dirent *e;
  62.  
  63.   e = readdir(d);
  64.   return e == NULL ? (char *) NULL : e->d_name;
  65. }
  66.  
  67. int procname(n)
  68. char *n;                /* name to process */
  69. /* Process a name or sh expression to operate on (or exclude).  Return
  70.    an error code in the ZE_ class. */
  71. {
  72.   char *a;              /* path and name for recursion */
  73.   DIR *d;               /* directory stream from opendir() */
  74.   char *e;              /* pointer to name from readd() */
  75.   int m;                /* matched flag */
  76.   char *p;              /* path for recursion */
  77.   struct stat s;        /* result of stat() */
  78.   struct zlist far *z;  /* steps through zfiles list */
  79.  
  80.   if (strcmp(n, "-") == 0)   /* if compressing stdin */
  81.     return newname(n, 0);
  82.   else if (LSSTAT(n, &s))
  83.   {
  84.     /* Not a file or directory--search for shell expression in zip file */
  85.     p = ex2in(n, 0, (int *)NULL);       /* shouldn't affect matching chars */
  86.     m = 1;
  87.     for (z = zfiles; z != NULL; z = z->nxt) {
  88.       if (MATCH(p, z->iname))
  89.       {
  90.         z->mark = pcount ? filter(z->zname) : 1;
  91.         if (verbose)
  92.             fprintf(mesg, "zip diagnostic: %scluding %s\n",
  93.                z->mark ? "in" : "ex", z->name);
  94.         m = 0;
  95.       }
  96.     }
  97.     free((zvoid *)p);
  98.     return m ? ZE_MISS : ZE_OK;
  99.   }
  100.  
  101.   /* Live name--use if file, recurse if directory */
  102.   if ((s.st_mode & S_IFDIR) == 0)
  103.   {
  104.     /* add or remove name of file */
  105.     if ((m = newname(n, 0)) != ZE_OK)
  106.       return m;
  107.   } else {
  108.     /* Add trailing / to the directory name */
  109.     if ((p = malloc(strlen(n)+2)) == NULL)
  110.       return ZE_MEM;
  111.     if (strcmp(n, ".") == 0) {
  112.       *p = '\0';  /* avoid "./" prefix and do not create zip entry */
  113.     } else {
  114.       strcpy(p, n);
  115.       a = p + strlen(p);
  116.       if (a[-1] != '/')
  117.         strcpy(a, "/");
  118.       if (dirnames && (m = newname(p, 1)) != ZE_OK) {
  119.         free((zvoid *)p);
  120.         return m;
  121.       }
  122.     }
  123.     /* recurse into directory */
  124.     if (recurse && (d = opendir(n)) != NULL)
  125.     {
  126.       while ((e = readd(d)) != NULL) {
  127.         if (strcmp(e, ".") && strcmp(e, ".."))
  128.         {
  129.           if ((a = malloc(strlen(p) + strlen(e) + 1)) == NULL)
  130.           {
  131.             closedir(d);
  132.             free((zvoid *)p);
  133.             return ZE_MEM;
  134.           }
  135.           strcat(strcpy(a, p), e);
  136.           if ((m = procname(a)) != ZE_OK)   /* recurse on name */
  137.           {
  138.             if (m == ZE_MISS)
  139.               zipwarn("name not matched: ", a);
  140.             else
  141.               ziperr(m, a);
  142.           }
  143.           free((zvoid *)a);
  144.         }
  145.       }
  146.       closedir(d);
  147.     }
  148.     free((zvoid *)p);
  149.   } /* (s.st_mode & S_IFDIR) == 0) */
  150.   return ZE_OK;
  151. }
  152.  
  153. char *strlower(s)
  154. char *s;                /* string to convert */
  155. /* Convert all uppercase letters to lowercase in string s */
  156. {
  157.   char *p;              /* scans string */
  158.  
  159.   for (p = s; *p; p++)
  160.     if (*p >= 'A' && *p <= 'Z')
  161.       *p += 'a' - 'A';
  162.   return s;
  163. }
  164.  
  165. char *strupper(s)
  166. char *s;                /* string to convert */
  167. /* Convert all lowercase letters to uppercase in string s */
  168. {
  169.   char *p;              /* scans string */
  170.  
  171.   for (p = s; *p; p++)
  172.     if (*p >= 'a' && *p <= 'z')
  173.       *p -= 'a' - 'A';
  174.   return s;
  175. }
  176.  
  177. char *ex2in(x, isdir, pdosflag)
  178. char *x;                /* external file name */
  179. int isdir;              /* input: x is a directory */
  180. int *pdosflag;          /* output: force MSDOS file attributes? */
  181. /* Convert the external file name to a zip file name, returning the malloc'ed
  182.    string or NULL if not enough memory. */
  183. {
  184.   char *n;              /* internal file name (malloc'ed) */
  185.   char *t;              /* shortened name */
  186.   int dosflag;
  187.  
  188.   dosflag = dosify; /* default for non-DOS and non-OS/2 */
  189.  
  190.   /* Find starting point in name before doing malloc */
  191.   for (t = x; *t == '/'; t++)
  192.     ;
  193.  
  194.   if (*t == '=')                /* AOS/VS for ./ */
  195.     t++;
  196.   else if (*t == ':')           /* AOS/VS for / */
  197.     t++;
  198.  
  199.   if (!pathput)
  200.     t = last(t, PATH_END);
  201.  
  202.   if (*t == '^')        /* AOS/VS for ../ */
  203.   {
  204.     if ((n = malloc(strlen(t) + 3)) == NULL)
  205.       return NULL;
  206.     strcpy(n, "../");
  207.     strcpy(n + 3, t + 1);
  208.   }
  209.   else if (*t == '@')   /* AOS/VS for :PER:, kind of like /dev/ */
  210.   {
  211.     if ((n = malloc(strlen(t) + 5)) == NULL)
  212.       return NULL;
  213.     strcpy(n, "/PER/");
  214.     strcpy(n + 5, t + 1);
  215.   }
  216.   else
  217.   {
  218.     if ((n = malloc(strlen(t) + 1)) == NULL)
  219.       return NULL;
  220.     strcpy(n, t);
  221.   }
  222.   /* now turn AOS/VS dir separators (colons) into slashes */
  223.   for (t = n;  *t != '\0';  t++)
  224.     if (*t == ':')
  225.       *t = '/';
  226.   /*
  227.    * Convert filename to uppercase (for correct matching).
  228.    * (It may make more sense to patch the matching code, since
  229.    * we may want those filenames in uppercase on the target system,
  230.    * but this seems better at present.  If we're converting, uppercase
  231.    * also seems to make sense.)
  232.    */
  233.   strupper(n);
  234.  
  235.  
  236.   if (dosify)
  237.     msname(n);
  238.   /* Returned malloc'ed name */
  239.   if (pdosflag)
  240.     *pdosflag = dosflag;
  241.   return n;
  242. }
  243.  
  244.  
  245. char *in2ex(n)
  246. char *n;                /* internal file name */
  247. /* Convert the zip file name to an external file name, returning the malloc'ed
  248.    string or NULL if not enough memory. */
  249. {
  250.   char *x;              /* external file name */
  251.  
  252.   if ((x = malloc(strlen(n) + 1 + PAD)) == NULL)
  253.       return NULL;
  254.   strcpy(x, n);
  255.  
  256.   return x;
  257. }
  258.  
  259. void stamp(f, d)
  260. char *f;                /* name of file to change */
  261. ulg d;                  /* dos-style time to change it to */
  262. /* Set last updated and accessed time of file f to the DOS time d. */
  263. {
  264.   time_t u[2];          /* argument for utime() */
  265.  
  266.   /* Convert DOS time to time_t format in u */
  267.   u[0] = u[1] = dos2unixtime(d);
  268.   utime(f, u);
  269. }
  270.  
  271. ulg filetime(f, a, n, t)
  272. char *f;                /* name of file to get info on */
  273. ulg *a;                 /* return value: file attributes */
  274. long *n;                /* return value: file size */
  275. iztimes *t;             /* return value: access, modific. and creation times */
  276. /* If file *f does not exist, return 0.  Else, return the file's last
  277.    modified date and time as an MSDOS date and time.  The date and
  278.    time is returned in a long with the date most significant to allow
  279.    unsigned integer comparison of absolute times.  Also, if a is not
  280.    a NULL pointer, store the file attributes there, with the high two
  281.    bytes being the Unix attributes, and the low byte being a mapping
  282.    of that to DOS attributes.  If n is not NULL, store the file size
  283.    there.  If t is not NULL, the file's access, modification and creation
  284.    times are stored there as UNIX time_t values.
  285.    If f is "-", use standard input as the file. If f is a device, return
  286.    a file size of -1 */
  287. {
  288.   struct stat s;        /* results of stat() */
  289.   char name[FNMAX];
  290.   int len = strlen(f);
  291.  
  292.   if (f == label) {
  293.     if (a != NULL)
  294.       *a = label_mode;
  295.     if (n != NULL)
  296.       *n = -2L; /* convention for a label name */
  297.     if (t != NULL)
  298.       t->atime = t->mtime = t->ctime = label_utim;
  299.     return label_time;
  300.   }
  301.   strcpy(name, f);
  302.   if (name[len - 1] == '/')
  303.     name[len - 1] = '\0';
  304.   /* not all systems allow stat'ing a file with / appended */
  305.  
  306.   if (strcmp(f, "-") == 0) {
  307.     if (fstat(fileno(stdin), &s) != 0)
  308.       error("fstat(stdin)");
  309.   } else if (LSSTAT(name, &s) != 0)
  310.              /* Accept about any file kind including directories
  311.               * (stored with trailing / with -r option)
  312.               */
  313.     return 0;
  314.  
  315.   if (a != NULL) {
  316.     *a = ((ulg)s.st_mode << 16) | !(s.st_mode & S_IWRITE);
  317.     if ((s.st_mode & S_IFDIR) != 0) {
  318.       *a |= MSDOS_DIR_ATTR;
  319.     }
  320.   }
  321.   if (n != NULL)
  322.     *n = (s.st_mode & S_IFMT) == S_IFREG ? s.st_size : -1L;
  323.   if (t != NULL) {
  324.     t->atime = s.st_atime;
  325.     t->mtime = s.st_mtime;
  326.     t->ctime = s.st_ctime;
  327.   }
  328.  
  329.   return unix2dostime(&s.st_ctime);
  330. }
  331.  
  332. int deletedir(d)
  333. char *d;
  334. {
  335.    return rmdir(d);
  336. }
  337.  
  338. int set_extra_field(z, z_utim)
  339.   struct zlist far *z;
  340.   iztimes *z_utim;
  341.   /* create extra field and change z->att if desired */
  342.   /* NOTE: this AOS/VS version assumes the pathname in z->name is an
  343.    * AOS/VS pathname, not a unix-style one.  Since you can zip up using
  344.    * unix-style pathnames, this may create problems occasionally.
  345.    * We COULD add code to parse back to AOS/VS format ...
  346.    * (This might also fail for other reasons such as access denied, but
  347.    * that should already have occurred.)
  348.    * We set the central-dir extra fld pointer & length here to the same data.
  349.    */
  350. {
  351.   int             aclend = 0;
  352. /*
  353.  * use this to simplify because different calls depending on
  354.  * whether links are resolved
  355.  */
  356.   unsigned short  errc;
  357.  
  358.   z->ext = 0;               /* init to no extra field */
  359. /* get the ?FSTAT info & the acl - if no errors, get memory & store.
  360.  * (first, we have to cut off the trailing slash that was added if
  361.  * it's a dir, since AOS/VS doesn't accept that kind of thing)
  362.  */
  363.   strncpy(znamebuf, z->name, $MXPL);
  364.   znamebuf[$MXPL-1] = '\0';
  365.   if (znamebuf[strlen(znamebuf)-1] == '/')
  366.     znamebuf[strlen(znamebuf)-1] = '\0';
  367.   if (linkput)
  368.     errc = sys_fstat(znamebuf, BIT1, &(zzextrafld.fstat_packet));
  369.   else
  370.     errc = sys_fstat(znamebuf, 0, &(zzextrafld.fstat_packet));
  371.   if (errc)
  372.   {
  373.     fprintf(stderr,
  374.             "\n    Warning: can't get ?FSTAT info & acl of %s - error %d\n    ",
  375.             znamebuf, errc);
  376.     perror("sys_fstat()");
  377.   }
  378.   else
  379.   {
  380.     /* store the ACL - or, if a link (no ACL!), store the resolution name */
  381.     if (zzextrafld.fstat_packet.norm_fstat_packet.styp_type != $FLNK)
  382.     {
  383.       if ((errc = sys_gacl(znamebuf, zzextrafld.aclbuf)) != 0)
  384.       {
  385.         fprintf(stderr, "\n    Warning: can't get acl of %s - error %d\n    ",
  386.                 z->name, errc);
  387.         perror("sys_gacl()");
  388.       }
  389.       else
  390.       {
  391.         /* find length of ACL - ends with double-null */
  392.         while (aclend++ < $MXACL  &&
  393.                (zzextrafld.aclbuf[aclend - 1] != '\0'  ||
  394.                 zzextrafld.aclbuf[aclend] != '\0'))
  395.           /* EMPTY LOOP */ ;
  396.         if ((z->cextra = z->extra =
  397.              malloc(sizeof(ZEXTRAFLD) - $MXACL + aclend + 4)) != NULL)
  398.         {
  399.           strncpy(zzextrafld.extra_header_id, ZEXTRA_HEADID,
  400.                   sizeof(zzextrafld.extra_header_id));
  401.           strncpy(zzextrafld.extra_sentinel, ZEXTRA_SENTINEL,
  402.                   sizeof(zzextrafld.extra_sentinel));
  403.           zzextrafld.extra_rev = ZEXTRA_REV;    /* this is a char, no need
  404.                                                    to worry about byte order */
  405.           /* set size (Intel (little-endian)) 2-byte int, which we've set
  406.              as array to make it easier */
  407.           errc = (unsigned short) (sizeof(ZEXTRAFLD) - $MXACL + aclend + 4 -
  408.                                    sizeof(zzextrafld.extra_header_id) -
  409.                                    sizeof(zzextrafld.extra_data_size));
  410.           zzextrafld.extra_data_size[0] = errc & 0xFF;  /* low-order byte */
  411.           zzextrafld.extra_data_size[1] = errc >> 8;    /* high-order byte */
  412.           memcpy((char *) z->extra, (char *) &zzextrafld,
  413.                  sizeof(ZEXTRAFLD) - $MXACL + aclend + 4);
  414.           z->cext = z->ext = sizeof(ZEXTRAFLD) - $MXACL + aclend + 4;
  415.         }
  416.       }
  417.     }
  418.     else /* a link */
  419.     {
  420.       if ((errc = sys_glink(z->name, zzextrafld.aclbuf)) != 0)
  421.       {
  422.         fprintf(stderr,
  423.               "\n    Warning: can't get link-resolution of %s - error %d\n    ",
  424.                 z->name, errc);
  425.         perror("sys_glink()");
  426.       }
  427.       else
  428.       {
  429.         aclend = strlen(zzextrafld.aclbuf) + 1;
  430.         if ((z->extra = malloc(sizeof(ZEXTRAFLD) - $MXACL + aclend + 4))
  431.             != NULL)
  432.         {
  433.           strncpy(zzextrafld.extra_header_id, ZEXTRA_HEADID,
  434.                   sizeof(zzextrafld.extra_header_id));
  435.           strncpy(zzextrafld.extra_sentinel, ZEXTRA_SENTINEL,
  436.                   sizeof(zzextrafld.extra_sentinel));
  437.           zzextrafld.extra_rev = ZEXTRA_REV;    /* this is a char, no need
  438.                                                    to worry about byte order */
  439.           /* set size (Intel (little-endian)) 2-byte int, which we've set
  440.              as array to make it easier */
  441.           errc = (unsigned short) (sizeof(ZEXTRAFLD) - $MXACL + aclend + 4 -
  442.                                    sizeof(zzextrafld.extra_header_id) -
  443.                                    sizeof(zzextrafld.extra_data_size));
  444.           zzextrafld.extra_data_size[0] = errc & 0xFF;  /* low-order byte */
  445.           zzextrafld.extra_data_size[1] = errc >> 8;    /* high-order byte */
  446.           memcpy((char *) z->extra, (char *) &zzextrafld,
  447.                  sizeof(ZEXTRAFLD) - $MXACL + aclend + 4);
  448.           z->ext = sizeof(ZEXTRAFLD) - $MXACL + aclend + 4;
  449.         }
  450.       }
  451.     }
  452.   }
  453.   return ZE_OK;
  454. }
  455.  
  456. #endif /* !UTIL */
  457.  
  458. void version_local()
  459. {
  460.     printf("Compiled with %s under %s.\n",
  461.       "a C compiler",
  462.       "AOS/VS"
  463.     );
  464. }
  465.  
  466.  
  467. /*
  468.  * This file defines for AOS/VS two Unix functions relating to links;
  469.  * the calling code should have the following defines:
  470.  *
  471.  *    #define       lstat(path,buf)             zvs_lstat(path,buf)
  472.  *    #define       readlink(path,buf,nbytes)   zvs_readlink(path,buf,nbytes)
  473.  *
  474.  * For these functions, I'm going to define yet 2 MORE filename buffers
  475.  * and also insert code to change pathnames to Unix & back.  This is
  476.  * easier than changing all the other places this kind of thing happens to
  477.  * be efficient.  This is a kludge.  I'm also going to put the functions
  478.  * here for my immediate convenience rather than somewhere else for
  479.  * someone else's.
  480.  *
  481.  * WARNING: the use of static buffers means that you'd better get your
  482.  * data out of these buffers before the next call to any of these functions!
  483.  *
  484.  */
  485.  
  486. /* =========================================================================
  487.  * ZVS_LSTAT() - get (or simulate) stat information WITHOUT following symlinks
  488.  *      This is intended to look to the outside like the unix lstat()
  489.  *      function.  We do a quick-&-dirty filename conversion.
  490.  *
  491.  *      If the file is NOT a symbolic link, we can just do a stat() on it and
  492.  *      that should be fine.  But if it IS a link, we have to set the elements
  493.  *      of the stat struct ourselves, since AOS/VS doesn't have a built-in
  494.  *      lstat() function.
  495.  *
  496.  *      RETURNS: 0 on success, or -1 otherwise
  497.  *
  498.  */
  499.  
  500. int zvs_lstat(char *path, struct stat *buf)
  501. {
  502.     char        *cp_vs = vsnamebuf;
  503.     char        *cp_ux = path;
  504.     int         mm, dd, yy;
  505.  
  506.     /*
  507.      * Convert the Unix pathname to an AOS/VS pathname.
  508.      * This is quick & dirty; it won't handle (for instance) pathnames with
  509.      * ../ in the middle of them, and may choke on other Unixisms.  We hope
  510.      * they're unlikely.
  511.      */
  512.     if (!strncmp(cp_ux, "../", 3))
  513.     {
  514.         *cp_vs++ = '^';        /* AOS/VS for ../ */
  515.         cp_ux += 3;
  516.     }
  517.     else if (!strncmp(cp_ux, "./", 2))
  518.     {
  519.         *cp_vs++ = '=';        /* AOS/VS for ./ */
  520.         cp_ux += 2;
  521.     }
  522.  
  523.     do
  524.     {
  525.         if (*cp_ux == '/')
  526.         {
  527.             *cp_vs++ = ':';
  528.         }
  529.         else
  530.         {
  531.             *cp_vs++ = (char) toupper(*cp_ux);
  532.         }
  533.  
  534.     } while (*cp_ux++ != '\0'  &&  cp_vs - vsnamebuf < sizeof(vsnamebuf));
  535.  
  536.     /* If Unix name was too long for our buffer, return an error return */
  537.     if (cp_vs - vsnamebuf >= sizeof(vsnamebuf)  &&  *(cp_vs - 1) != '\0')
  538.         return (-1);     /* error */
  539.  
  540.     /* Make AOS/VS ?FSTAT call that won't follow links & see if we find
  541.      * anything.  If not, we return error.
  542.      */
  543.     if (sys_fstat(vsnamebuf,
  544.                   BIT1,                 /* BIT1 says to not resolve links */
  545.                   &vsfstatbuf))
  546.         return (-1);     /* error */
  547.  
  548.     /* If we DID find the file but it's not a link,
  549.      * call stat() and return its value.
  550.      */
  551.     if (vsfstatbuf.styp_type != $FLNK)
  552.         return (stat(path, buf));        /* call with Unix pathname ... */
  553.  
  554.     /* Otherwise, we have to kludge up values for the stat structure */
  555.     memset((char *) buf, 0, sizeof(*buf));   /* init to nulls (0 values) */
  556.     buf->st_mode = S_IFLNK | 0777;           /* link and rwxrwxrwx */
  557.     buf->st_uid = -1;                        /* this is what we get on AOS/VS
  558.                                                 anyway (maybe unless we set up
  559.                                                 a dummy password file?) */
  560.     buf->st_nlink = 1;
  561.     /* The DG date we've got is days since 12/31/67 and seconds/2.  So we
  562.      * need to subtract 732 days (if that's not negative), convert to seconds,
  563.      * and add adjusted seconds.
  564.      */
  565.     if (vsfstatbuf.stch.short_time[0] < 732)
  566.         buf->st_ctime = buf->st_mtime = buf->st_atime = 0L;
  567.     else
  568.     {
  569.         buf->st_ctime = buf->st_mtime = buf->st_atime =
  570.                 ((long) vsfstatbuf.stch.short_time[0] - 732L) * 24L * 3600L +
  571.                 2L * (long) vsfstatbuf.stch.short_time[1];
  572.     }
  573.  
  574.     /* And we need to get the filename linked to and use its length as
  575.      * the file size.  We'll use the Unix pathname buffer for this - hope
  576.      * it's big enough.  (We won't overwrite anything, but we could get a
  577.      * truncated path.)  If there's an error, here's our last chance to
  578.      * say anything.
  579.      */
  580.     if ((buf->st_size = zvs_readlink(vsnamebuf, uxnamebuf, FNMAX)) < 0)
  581.         return (-1);
  582.     else
  583.         return (0);
  584.  
  585. } /* end zvs_lstat() */
  586.  
  587. /* =========================================================================
  588.  * ZVS_READLINK() - get pathname pointed to by an AOS/VS link file
  589.  *      This is intended to look to the outside like the unix readlink()
  590.  *      function.  We do a quick-&-dirty filename conversion.
  591.  *
  592.  *      RETURNS: the length of the output path (in bytes), or -1 if an error
  593.  *
  594.  */
  595.  
  596. int zvs_readlink(char *path, char *buf, int nbytes)
  597. {
  598.     char    *cp_vs = vsnamebuf;
  599.     char    *cp_ux = buf;
  600.  
  601.     /* This is called with z->name, the filename the user gave, so we'll get
  602.      * the link-resolution name on the assumption that it's a valid AOS/VS
  603.      * name. We're also assuming a reasonable value (> 5) for nbytes.
  604.      */
  605.     if (sys_glink(path, vsnamebuf))
  606.         return (-1);     /* readlink() is supposed to return -1 on error */
  607.  
  608.     /* Now, convert the AOS/VS pathname to a Unix pathname.
  609.      * Note that sys_glink(), unlike readlink(), does add a null.
  610.      */
  611.     if (*cp_vs == '^')        /* AOS/VS for ../ */
  612.     {
  613.         strncpy(cp_ux, "../", 3);
  614.         cp_ux += 3;
  615.         cp_vs++;
  616.     }
  617.     else if (*cp_vs == '@')   /* AOS/VS for :PER:, kind of like /dev/ */
  618.     {
  619.         strncpy(cp_ux, "/PER/", 5);
  620.         cp_ux += 5;
  621.         cp_vs++;
  622.     }
  623.     else if (*cp_vs == '=')   /* AOS/VS for ./ */
  624.     {
  625.         strncpy(cp_ux, "./", 2);
  626.         cp_ux += 2;
  627.         cp_vs++;
  628.     }
  629.     while (*cp_vs != '\0'  &&  cp_ux - buf < nbytes)
  630.     {
  631.         if (*cp_vs == ':')
  632.         {
  633.             *cp_ux++ = '/';
  634.         }
  635.         else
  636.         {
  637.             *cp_ux++ = (char) toupper(*cp_vs);
  638.         }
  639.         cp_vs++;
  640.     }
  641.  
  642.     return (cp_ux - buf);   /* # characters in Unix path (no trailing null) */
  643.  
  644. } /* end zvs_readlink() */
  645.