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