home *** CD-ROM | disk | FTP | other *** search
/ PC Extra Super CD 1998 January / PCPLUS131.iso / DJGPP / V2 / DJLSR201.ZIP / src / libc / compat / mntent / mntent.c < prev    next >
Encoding:
C/C++ Source or Header  |  1996-08-12  |  27.4 KB  |  846 lines

  1. /* Copyright (C) 1996 DJ Delorie, see COPYING.DJ for details */
  2. /* Copyright (C) 1995 DJ Delorie, see COPYING.DJ for details */
  3. /*
  4.  * This is implementation of getmntent() and friends for DJGPP v2.x.
  5.  *
  6.  * Copyright (c) 1995-96 Eli Zaretskii <eliz@is.elta.co.il>
  7.  *
  8.  * This software may be used freely so long as this copyright notice is
  9.  * left intact.  There is no warranty on this software.
  10.  *
  11.  * ---------------------------------------------------------------------
  12.  *
  13.  * The primary motivation for these functions was the GNU df program,
  14.  * which lists all the mounted filesystems with a summary of the disk
  15.  * space available on each one of them.  However, they are also useful
  16.  * on their own right.
  17.  *
  18.  * Unlike Unix, where all mountable filesystems can be found on special
  19.  * file (and thus implementing these function boils down to reading that
  20.  * file), with MS-DOS it's a mess.  Every type of drive has its own
  21.  * interface; there are JOINed and SUBSTed pseudo-drives and RAM disks;
  22.  * different network redirectors hook DOS in a plethora of incompatible
  23.  * ways; a single drive A: can be mapped to either A: or B:, etc.  That
  24.  * is why this implementation uses almost every trick in the book to get
  25.  * at the intimate details of every drive.  Some places where you might
  26.  * find these tricks are: ``Undocumented DOS, 2nd ed.'' by Schulman et al
  27.  * and Ralf Brown's Interrupt List.
  28.  *
  29.  */
  30. #include <libc/stubs.h>
  31. #include <stdio.h>
  32. #include <string.h>
  33. #include <ctype.h>
  34. #include <errno.h>
  35. #include <sys/stat.h>
  36. #include <mntent.h>
  37. #include <dir.h>
  38. #include <dos.h>
  39. #include <bios.h>
  40. #include <dpmi.h>
  41. #include <go32.h>
  42. #include <libc/farptrgs.h>
  43. #include <sys/movedata.h>
  44. #include <libc/unconst.h>
  45.  
  46. /* Macro to convert a segment and an offset to a "far offset" suitable
  47.    for _farxxx() functions of DJGPP.  */
  48. #ifndef MK_FOFF
  49. #define MK_FOFF(s,o) ((int)((((unsigned long)(s)) << 4) + (unsigned short)(o)))
  50. #endif
  51.  
  52. #define CDS_JOIN     0x2000
  53. #define CDS_VALID    0xc000
  54. #define REMOVABLE    0
  55. #define FIXED        1
  56.  
  57. /* Static variables.  */
  58.  
  59. static char          drive_number = -1;
  60. static char          skip_drive_b = 0;
  61. static char          drive_a_mapping = 0;
  62. static char          cds_drives   = 0;
  63. static unsigned long cds_address;
  64. static int           cds_elsize;
  65. static unsigned short dos_mem_base, our_mem_base;
  66. static struct mntent mntent;
  67. static unsigned char drive_string[128];
  68. static char          *mnt_type;
  69. static unsigned char mnt_dir[128];
  70. static unsigned char mnt_fsname[128];
  71. static          char dev_opts[] = "r ,dev=  ";
  72.  
  73. static char NAME_dblsp[] = "dblsp";
  74. static char NAME_stac[] = "stac";
  75. static char NAME_jam[] = "jam";
  76. static char NAME_ram[] = "ram";
  77. static char NAME_cdrom[] = "cdrom";
  78. static char NAME_net[] = "net";
  79. static char NAME_fd[] = "fd";
  80. static char NAME_hd[] = "hd";
  81. static char NAME_subst[] = "subst";
  82. static char NAME_join[] = "join";
  83. static char NAME_unknown[] = "???";
  84.  
  85. int _is_remote_drive(int);
  86.  
  87. /* Static helper functions.  */
  88.  
  89. /*
  90.  * Get the entry for this disk in the DOS Current Directory Structure
  91.  * (CDS).  In case of success, return this drive's attribute word; or
  92.  * 0 in case of failure.  Fill the buffer at CURRDIR with the current
  93.  * directory on that drive.
  94.  * The pointer to the CDS array and the size of the array element
  95.  * (which are DOS version-dependent) are computed when setmntent() is
  96.  * called.
  97.  */
  98. static int
  99. get_cds_entry(int drive_num, char *currdir)
  100. {
  101.   unsigned long  cds_entry_address;
  102.   if (!cds_address)
  103.     {
  104.       *currdir = '\0';
  105.       return 0;
  106.     }
  107.  
  108.   /* The address of the CDS element for this drive.  */
  109.   cds_entry_address = cds_address + (drive_num - 1)*cds_elsize;
  110.   
  111.   /* The current directory: 67-byte ASCIIZ string at the beginning
  112.      of the CDS structure for our drive.  */
  113.   movedata(dos_mem_base, (cds_entry_address & 0xfffff),
  114.            our_mem_base, (unsigned int)currdir, 0x43);
  115.  
  116.   /* The drive attribute word is at the offset 43h, right after the
  117.      current directory string.  */
  118.   return _farpeekw(dos_mem_base, cds_entry_address + 0x43);
  119. }
  120.  
  121. /*
  122.  * For a PC with a single floppy drive, that drive can be referenced
  123.  * as both A: and B:.  This function returns the logical drive number
  124.  * which was last used to reference a physical drive, or 0 if the
  125.  * drive has only one logical drive assigned to it (which means there
  126.  * are two floppies in this system).
  127.  */
  128. static int
  129. assigned_to(int drive_num)
  130. {
  131.   __dpmi_regs r;
  132.  
  133.   /* Issue Int 21h/AX=440Eh to get logical drive last used to
  134.      reference the physical drive DRIVE_NUM.  */
  135.   r.x.ax = 0x440e;
  136.   r.h.bl = drive_num;
  137.   __dpmi_int(0x21, &r);
  138.   if (r.x.flags & 1)
  139.     return 0;
  140.   return r.h.al;
  141. }
  142.  
  143. /*
  144.  * Check if the drive is compressed with DoubleSpace.  If it is,
  145.  * get the host drive on which the Compressed Volume File (CVF)
  146.  * resides, put the name of that CVF into MNT_FSNAME[] and return
  147.  * non-zero.
  148.  */
  149. static int
  150. get_doublespace_info(int drive_num)
  151. {
  152.   __dpmi_regs r;
  153.  
  154.   r.x.ax = 0x4a11;      /* DBLSPACE Int 2Fh API */
  155.   r.x.bx = 1;
  156.   r.h.dl = drive_num - 1; /* 0 = A: */
  157.   __dpmi_int(0x2f, &r);
  158.  
  159.   if (r.x.ax == 0 && r.h.bl & 0x80)
  160.     {
  161.       int host = r.h.bl & 0x7f;
  162.  
  163.       /* Compressed drive, get the host and sequence number of the CVF.  */
  164.       sprintf(mnt_fsname, "%c:\\DBLSPACE.%03u", 'A' + host, r.h.bh);
  165.       mnt_type = NAME_dblsp;
  166.       return 1;
  167.     }
  168.   else
  169.     /* Error, or uncompressed drive (might be a host, but don't care).  */
  170.     return 0;
  171. }
  172.  
  173. /*
  174.  * For a drive compressed with Stacker, get the name of the host
  175.  * of its compressed volume file, fill MNT_FSNAME[] with its
  176.  * name and return non-zero.  If this drive isn't controlled by
  177.  * Stacker, return 0.
  178.  */
  179. static int
  180. get_stacker_info(int drive_num)
  181. {
  182.   __dpmi_regs r;
  183.   unsigned long stac_driver_ptr;
  184.   unsigned char seq, host;
  185.  
  186.   /* Put a known DWORD into the Transfer Buffer.  If this drive
  187.      isn't compressed with Stacker, it will remain unchanged.  */
  188.   _farpokel(dos_mem_base, __tb, 0xbadabadaU);
  189.  
  190.   r.x.ax = 0x4404;      /* Stacker Get Driver Address function */
  191.   r.h.bl = drive_num;
  192.   r.x.cx = 4;
  193.   r.x.ds = __tb >> 4;
  194.   r.x.dx = __tb & 15;
  195.   __dpmi_int(0x21, &r);
  196.  
  197.   if ((stac_driver_ptr = _farpeekl(dos_mem_base, __tb)) == 0xbadabadaU)
  198.     return 0;
  199.  
  200.   /* This drive MIGHT be compressed with Stacker.  Construct a linear
  201.      address of the far pointer into the Stacker device driver.  */
  202.   stac_driver_ptr = ((stac_driver_ptr >> 12) & 0xffff0)
  203.                     + (stac_driver_ptr & 0xffff);
  204.  
  205.   /* Sanity check: real-mode addresses are only 20 bit-long, so we can
  206.      safely reject anything that's larger than FFFFFh, lest we get an
  207.      illegal address abort when we try to peek at the signature below.
  208.      Actually, it's enough to test for (FFFFFh - 55h - drive), because
  209.      we need to get the host drive number at that offset. */
  210.   if (stac_driver_ptr > 0x0000fffaa - drive_num)
  211.     return 0;
  212.  
  213.   /* Stacker Anywhere returns pointer to 1 byte before the A55Ah
  214.      signature (which is at offset 1Ah), while all other versions
  215.      of Stacker point to the signature itself.  */
  216.   if (_farpeekw(dos_mem_base,   stac_driver_ptr) != 0xa55a &&
  217.       _farpeekw(dos_mem_base, ++stac_driver_ptr) != 0xa55a)
  218.     return 0;
  219.   stac_driver_ptr -= 0x1a; /* start of the device driver */
  220.  
  221.   /* Check for the "SWAP" signature.  */                /*  P A W S  */
  222.   if (_farpeekl(dos_mem_base, stac_driver_ptr + 0x6c) != 0x50415753)
  223.     return 0;
  224.  
  225.   /* We have indeed Stacker device driver.  Get the volume
  226.      number (at offset 58h) and host drive (from the 26-byte
  227.      table beginning at offset 70h).  */
  228.   seq  = _farpeekb(dos_mem_base, stac_driver_ptr + 0x58);
  229.   host = _farpeekb(dos_mem_base, stac_driver_ptr + 0x70 + drive_num - 1);
  230.   sprintf(mnt_fsname, "%c:\\STACVOL.%03u", 'A' + host, seq);
  231.   mnt_type = NAME_stac;
  232.   return 1;
  233. }
  234.  
  235. /*
  236.  * For a drive compressed with Jam, get the full file name of
  237.  * the compressed archive file, fill MNT_FSNAME[] with its
  238.  * name and return non-zero.  If this drive isn't controlled by
  239.  * Jam, return 0.
  240.  *
  241.  * (JAM is a shareware disk compressor software.)
  242.  *
  243.  * Contributed by Markus F.X.J. Oberhumer <markus.oberhumer@jk.uni-linz.ac.at>
  244.  */
  245. static int
  246. get_jam_info(int drive_num)
  247. {
  248.   __dpmi_regs r;
  249.   unsigned jam_version, offset;
  250.  
  251.   r.x.ax = 0x5200;              /* Jam Get Version */
  252.   __dpmi_int(0x2f, &r);
  253.   if (r.h.ah != 0x80)           /* JAM.SYS is not installed */
  254.     return 0;
  255.   jam_version = r.x.bx;         /* v1.25 == 0x125 */
  256.   if (jam_version < 0x110)      /* version sanity check */
  257.     return 0;
  258.   /* Sanity check of the size of the JAMINFO structure.  */
  259.   if (r.x.cx < 0x115 || r.x.cx > _go32_info_block.size_of_transfer_buffer)
  260.     return 0;
  261.   if (jam_version >= 0x120 && r.x.cx < 0x124)
  262.     return 0;
  263.  
  264.   r.x.ax = 0x5201;              /* Jam Get Compressed Drive Information */
  265.   r.h.dl = drive_num;
  266.   r.x.ds = __tb >> 4;
  267.   r.x.bx = __tb & 15;
  268.   __dpmi_int(0x2f, &r);
  269.   if (r.h.ah != 0)              /* drive is not a Jam drive */
  270.     return 0;
  271.  
  272.   /* Check that the drive is mounted (attached).  */
  273.   r.h.ah = 0x32;                /* Get Device Parameter Block function */
  274.   r.h.dl = drive_num;
  275.   __dpmi_int(0x21, &r);
  276.   if (r.h.al != 0)              /* drive is not mounted (or other error) */
  277.     return 0;
  278.        
  279.   /* Copy the full name of the Jam archive file.  */
  280.   offset = (jam_version >= 0x120) ? 0x38 : 0x2a;
  281.   movedata(dos_mem_base, __tb + offset,
  282.            our_mem_base, (unsigned) mnt_fsname, 127);
  283.   mnt_fsname[127] = 0;
  284.  
  285.   mnt_type = NAME_jam;
  286.   return 1;
  287. }
  288.  
  289. /*
  290.  * Get the network name which corresponds to a drive DRIVE_NUM.
  291.  * Ideally, _truename() (Int 21h/AH=60h) should return the same
  292.  * string, but some network redirectors don't put a full UNC
  293.  * name into the CDS, and others bypass the CDS altogether.
  294.  */
  295. static int
  296. get_netredir_entry(int drive_num)
  297. {
  298.   __dpmi_regs r;
  299.   unsigned long tb = __tb & 0xfffff;
  300.   unsigned char devname[2];
  301.   int i = -1;
  302.  
  303.   r.x.ds = r.x.es = (tb >> 4);
  304.   r.x.si = tb & 15;
  305.   r.x.di = r.x.si + 16;
  306.  
  307.   /* Loop for all the valid list indices, comparing the local device
  308.      name with our disk drive letter, until we find one which matches.
  309.  
  310.      We could cache entries which we've seen, but (1) I never said
  311.      this will be the fastest function ever; and (2) I can't be sure
  312.      the network configuration won't change between two successive
  313.      calls to getmntent().  */
  314.   while (++i < 35)  /* at the maximum: 32 drives + 3 LPTn devices */
  315.     {
  316.       r.x.cx = 0;
  317.       r.x.bx = i;
  318.       r.x.ax = 0x5f02;
  319.       __dpmi_int(0x21, &r);
  320.  
  321.       if (r.x.flags & 1)
  322.         {
  323.           if (r.x.ax == 0x12) /* end of list--bail out */
  324.             return 0;
  325.  
  326.           else
  327.             continue;
  328.         }
  329.       else if ((r.h.bh == 0 || r.h.bh == 2) && r.h.bl == 4)
  330.         {
  331.           /* We have a valid device which is a disk drive (BL = 4).
  332.              Pull in the local device name and if that fits our
  333.              drive number, get its network name.  */
  334.           *(unsigned short *)devname = _farpeekw(dos_mem_base, tb);
  335.  
  336.           /* The local device name may or may not include the
  337.              colon (Ralf Brown's Interrupt List).  */
  338.           if (devname[0] == '@' + drive_num &&
  339.               (devname[1] == ':' || devname[1] == '\0'))
  340.             {
  341.               movedata(dos_mem_base, tb + 16,
  342.                        our_mem_base, (unsigned)mnt_fsname, 128);
  343.               return 1; /* found! */
  344.             }
  345.         }
  346.     }
  347.   return 0;
  348. }
  349.  
  350. /*
  351.  * Return 1 if this drive is a CD-ROM drive, 0 otherwise.  Works
  352.  * with MSCDEX 2.x, but what about other CD-ROM device drivers?
  353.  */
  354. static int
  355. is_cdrom_drive(int drive_num)
  356. {
  357.   __dpmi_regs r;
  358.  
  359.   r.x.ax = 0x150b;      /* CD-ROM Drive Check function */
  360.   r.x.cx = drive_num - 1; /* 0 = A: */
  361.   __dpmi_int(0x2f, &r);
  362.  
  363.   /* If MSCDEX installed, BX will hold ADADh; AX will be non-zero
  364.      if this drive is supported by MSCDEX.  */
  365.   if (r.x.bx == 0xadad && r.x.ax != 0)
  366.     return 1;
  367.   return 0;
  368. }
  369.  
  370. /*
  371.  * Return 1 if a CD-ROM drive DRIVE_NUM is ready, i.e. there is a
  372.  * disk in the drive and that disk is a data (not AUDIO) disk.
  373.  */
  374. static int
  375. cdrom_drive_ready(int drive_num)
  376. {
  377.   __dpmi_regs r;
  378.   int i = 2;
  379.  
  380.   /* Int 2Fh/AX=1505h (Read Volume Table Of Contents) will return
  381.      with error for empty drives or if the disk is an AUDIO disk.  */
  382.   r.x.es = __tb >> 4;
  383.   r.x.bx = __tb & 15;
  384.   r.x.cx = drive_num - 1;   /* 0 = A: */
  385.   r.x.dx = 0;   /* get the 1st descriptor (usually, the only one) */
  386.  
  387.   /* First time after the door is closed the call might fail.
  388.      Therefore try twice before giving up.  */
  389.   do
  390.     {
  391.       r.x.ax = 0x1505;
  392.       __dpmi_int(0x2f, &r);
  393.     } while (--i && (r.x.flags & 1));
  394.  
  395.   if ((r.x.flags & 1) == 0 && (r.x.ax == 0 || r.x.ax == 1))
  396.     return 1;
  397.   return 0;
  398. }
  399.  
  400. /*
  401.  * Detect a RAM disk.  We do this by checking if the number of FAT
  402.  * copies (in the Device Parameter Block) is 1, which is typical of
  403.  * RAM disks.  [This doesn't _have_ to be so, but if it's good
  404.  * enough for Andrew Schulman et al (Undocumented DOS, 2nd edition),
  405.  * we can use this as well.]
  406.  */
  407. static int
  408. is_ram_drive(int drive_num)
  409. {
  410.   __dpmi_regs r;
  411.  
  412.   r.h.ah = 0x32;        /* Get Device Parameter Block function */
  413.   r.h.dl = drive_num;
  414.   __dpmi_int(0x21, &r);
  415.  
  416.   if (r.h.al == 0)
  417.     {
  418.       /* The pointer to DPB is in DS:BX.  The number of FAT copies is at
  419.          offset 8 in the DPB.  */
  420.       char fat_copies = _farpeekb(dos_mem_base, MK_FOFF(r.x.ds, r.x.bx) + 8);
  421.  
  422.       return fat_copies == 1;
  423.     }
  424.   return 0;
  425. }
  426.  
  427. /*
  428.  * Check if the media in this disk drive is fixed or removable.
  429.  * Should only be called after we're sure this ain't CD-ROM or
  430.  * RAM disk, since these might fool you with this call.
  431.  */
  432. static int
  433. media_type(int drive_num)
  434. {
  435.   __dpmi_regs r;
  436.  
  437.   r.x.ax = 0x4408;
  438.   r.h.bl = drive_num;
  439.   __dpmi_int(0x21, &r);
  440.  
  441.   if (r.x.flags & 1)
  442.     return -1;
  443.   return r.x.ax;    /* returns 1 for fixed disks, 0 for removable */
  444. }
  445.  
  446. /* Exported library functions.  */
  447.  
  448. FILE *
  449. setmntent(const char *filename, const char *type)
  450. {
  451.   __dpmi_regs r;
  452.   int cds_address_offset;
  453.   /* Need TRUE DOS version. */
  454.   unsigned short true_dos_version = _get_dos_version(1);
  455.  
  456.   dos_mem_base = _go32_info_block.selector_for_linear_memory;
  457.   our_mem_base = _my_ds();
  458.  
  459.   drive_number = 0;
  460.   skip_drive_b = 0;
  461.  
  462.   /* Get and save the pointer to the CDS array and the size of
  463.      the array element.  This is version-dependent.  */
  464.   if (true_dos_version < 0x0300 /* CDS not supported for v2.x */
  465.       || true_dos_version > 0x1000) /* dos emulation (OS/2) ? */
  466.     {
  467.       cds_elsize  = -1;
  468.       cds_address = 0;
  469.       cds_address_offset = 0; /* pacify -Wall */
  470.     }
  471.   else if (true_dos_version == 0x0300)
  472.     {
  473.       cds_address_offset = 0x17;
  474.       cds_elsize = 0x51;
  475.     }
  476.   else
  477.     {
  478.       cds_address_offset = 0x16;
  479.       cds_elsize = (true_dos_version >= 0x0400) ? 0x58 : 0x51;
  480.     }
  481.  
  482.   if (cds_elsize > 0)
  483.     {
  484.       unsigned long cds_address_ptr;
  485.       
  486.       r.h.ah = 0x52;    /* DOS Get List of Lists call */
  487.       __dpmi_int(0x21, &r);
  488.  
  489.       /* The pointer to the List of Lists is in ES:BX.  Compute the
  490.          linear address of the pointer to the CDS, which is at version-
  491.          dependent offset from the LoL start.  */
  492.       cds_address_ptr = MK_FOFF(r.x.es, r.x.bx + cds_address_offset);
  493.  
  494.       /* Get the pointer and compute the linear address of the CDS array.  */
  495.       cds_address = MK_FOFF(_farpeekw(dos_mem_base, cds_address_ptr + 2),
  496.                             _farpeekw(dos_mem_base, cds_address_ptr));
  497.     }
  498.  
  499.   return (FILE *) 1;
  500. }
  501.  
  502. struct mntent *
  503. getmntent(FILE *filep)
  504. {
  505.   if (drive_number == -1)
  506.     return NULL;
  507.   if (filep != (FILE *)1)
  508.     {
  509.       errno = EBADF;    /* fake errno for invalid handle */
  510.       return NULL;
  511.     }
  512.  
  513.   /* The number of drives known to DOS is returned by the DOS
  514.      SetDisk (Int 21h/AH=0Eh) call.  Any drive beyond that
  515.      number can only come from some device driver which bypasses
  516.      the DOS network redirector interface and instead hooks Int 21h
  517.      directly (Novell NetWare 3.x is a good example).  Such drives
  518.      won't appear in the DOS Current Directory Structure (CDS).  */
  519.   cds_drives = setdisk(0xffff);
  520.  
  521.   /* There may be a maximum of 32 block devices.  Novell Netware indeed
  522.      allows for 32 disks (A-Z plus 6 more characters from '[' to '\'') */
  523.   while (drive_number < 32)
  524.     {
  525.       unsigned char *p, *q;
  526.       char *truename_result;
  527.       struct ffblk   mnt_ff;
  528.       unsigned char  cds_path[128];
  529.       unsigned short cds_flags = 0;
  530.       int            drvstr_len;
  531.       int            got_fsname = 0;
  532.  
  533.       drive_number++;
  534.       mnt_dir[0] = '\0';
  535.  
  536.       /* On PCs with only one floppy drive, it can be mapped to either
  537.          A: or B:, depending on how it was last referenced.  We will
  538.          skip and not report the drive if it is actually mapped to a
  539.          physical drive which we already reported.  This method both
  540.          insures more accurate report and avoids the annoying message
  541.          ``Insert disk...'' from the OS when _truename() below hits
  542.          the disk.
  543.  
  544.          I assume there is only one such drive in the system, and that
  545.          it is drive A:, otherwise the logic below will fail miserably.  */
  546.       if (drive_number == 2 && skip_drive_b)
  547.         continue;
  548.  
  549.       /* Work around a possible bug in QDPMI: _truename() would sometimes
  550.          crash the calling program when there is no disk in a (floppy)
  551.          drive.  To avoid this, we have to check if the drive is empty
  552.          with a BIOS call.  */
  553.       if (drive_number <= 2)
  554.         {
  555.           unsigned char buf[512];
  556.           int bios_status, count = 0;
  557.  
  558.           /* Int 13h/AH=02h returns 6 for disk changed, even if the
  559.              disk isn't readable (e.g., unformatted).  Retry the
  560.              operation after disk change, each time resetting the 
  561.              drive, until something other than 6 is returned or we
  562.              run out of our patience.  */
  563.           while (++count < 10 && (bios_status =
  564.                  biosdisk(2, drive_number - 1, 0, 0, 1, 1, buf)) == 6)
  565.             biosdisk(0, drive_number - 1, 0, 0, 0, 0, NULL);
  566.  
  567.           /* If the loop ends with nonzero status, fail.  */
  568.           if (bios_status != 0)
  569.             continue;
  570.         }
  571.  
  572.       /* See whether drive A: is mapped to B: and if it is, change
  573.          DRIVE_NUMBER on the fly and raise the flag to skip drive
  574.          number 2 next time we are called (meaning there is only
  575.          one floppy drive).  */
  576.       if (drive_number == 1 && (drive_a_mapping = assigned_to(1)) > 0)
  577.         {
  578.           skip_drive_b = 1;
  579.           drive_number = drive_a_mapping;
  580.         }
  581.  
  582.       drvstr_len = sprintf(drive_string, "%c:\\", '@' + drive_number);
  583.       mnt_type = NAME_unknown;
  584.  
  585.       /* For the ``File System Name'' we use one of the following:
  586.  
  587.          * X:\DBLSPACE.NNN or X:\STACVOL.NNN for drives compressed with
  588.        DblSpace or Stacker, where X: is the host drive of the
  589.        compressed volume and NNN is the volume sequence number;
  590.          * The full filename of the compressed volume file for 
  591.            a drive that is compressed with Jam;
  592.          * What _truename() returns for the root directory, in case
  593.            it isn't the usual ``X:\'';
  594.          * The name of the volume label;
  595.          * The string ``Drive X:'' (where X is the drive letter).
  596.  
  597.          These are used in the above order.  */
  598.  
  599.       /* See what 2160 can tell us.  If it fails, there ain't no such
  600.          drive, as far as DOS is concerned.  */
  601.       truename_result = _truename(drive_string, mnt_fsname);
  602.  
  603.       /* Get some info from the DOS Current Directory Structure (CDS).
  604.          We've already hit the disk with _truename(), so CDS now
  605.          contains valid and up to date data.  */
  606.       if (drive_number <= cds_drives)
  607.         cds_flags = get_cds_entry(drive_number, cds_path);
  608.  
  609.       if (truename_result != NULL)
  610.         {
  611.  
  612.           /* Determine the type of this drive, if not known already.
  613.  
  614.              We use one of the following types:
  615.  
  616.              fd     for floppy disks
  617.              hd     for hard disks
  618.              dblsp  for disks compressed with DoubleSpace
  619.              stac   for disks compressed with Stacker
  620.              jam    for disks compressed with Jam
  621.              cdrom  for CD-ROM drives
  622.              ram    for RAM disks
  623.              subst  for SUBSTed directories
  624.              join   for JOINed disks
  625.              net    for networked drives
  626.           */
  627.           if (mnt_type[0] == '?')
  628.             {
  629.               int disk_type = media_type(drive_number);
  630.  
  631.               if (is_ram_drive(drive_number))
  632.                 mnt_type = NAME_ram;
  633.               else if (is_cdrom_drive(drive_number))
  634.                 {
  635.                   /* Empty CD-ROM drives do NOT fail _truename(),
  636.                      so we must see if there is a disk in the drive.  */
  637.                   if (cdrom_drive_ready(drive_number))
  638.                     mnt_type = NAME_cdrom;
  639.                   else
  640.                     continue;           /* don't report this drive */
  641.                 }
  642.               /* _is_remote_drive() needs zero-based disk number  */
  643.               else if (_is_remote_drive(drive_number - 1) == 1)
  644.                 mnt_type = NAME_net;
  645.               else if (disk_type == REMOVABLE)
  646.                 mnt_type = NAME_fd;
  647.               else if (disk_type == FIXED)
  648.                 mnt_type = NAME_hd;
  649.             }
  650.  
  651.           /* If the canonicalized name isn't the boring ``X:\'' with X
  652.              the original drive letter, then it is either in the UNC
  653.              form (``\\MACHINE\PATH\'') or it's SUBSTed or JOINed drive,
  654.              and the canonicalized name might actually say something
  655.              interesting about this drive, so use it.  */
  656.           if (mnt_fsname[1] != ':')
  657.             got_fsname = 1;
  658.           else if (mnt_fsname[0] != drive_string[0])
  659.             {
  660.               /* Detect SUBSTed drives.  This could also be found by
  661.                  looking in the CDS, but we try to keep usage of
  662.                  undocumented features at the bare minimum.
  663.                  For SUBSTed drives, the root directory will be returned
  664.                  by _truename() as some directory on another drive.  */
  665.               mnt_type = NAME_subst;
  666.               got_fsname = 1;
  667.             }
  668.           else
  669.             {
  670.               /* Check for compressed drives (DoubleSpace, Stacker,
  671.          or Jam).  */
  672.               got_fsname = get_doublespace_info(drive_number)
  673.                || get_stacker_info(drive_number)
  674.                || get_jam_info(drive_number);
  675.             }
  676.         }
  677.       /* JOINed drives fail _truename().  I don't know how to check
  678.          for them without using the CDS.  We will only trust the CDS
  679.          data if bits 14 and 15 aren't both zero, and if the current
  680.          directory path in the CDS isn't empty (this might happen
  681.          after ``JOIN D: /d'').  */
  682.       else if ((cds_flags & CDS_VALID) &&
  683.                (cds_flags & CDS_JOIN)  &&
  684.                cds_path[0] != '\0')
  685.         {
  686.           mnt_type = NAME_join;
  687.           strcpy(drive_string, cds_path);
  688.           drvstr_len = strlen(drive_string);
  689.           strcat(drive_string, "\\");
  690.  
  691.           /* Don't set got_fsname, so that we get a chance to look for
  692.              a volume label below.  */
  693.         }
  694.       else
  695.         continue;   /* illegal (non-present) drive */
  696.  
  697.       /* Some network redirectors don't set a UNC path to be
  698.          returned by _truename().  See if 215F02 can help.  */
  699.       if ((!got_fsname || mnt_fsname[0] != '\\') &&
  700.            strcmp(mnt_type, "net") == 0)
  701.         if (get_netredir_entry(drive_number))
  702.           got_fsname = 1;
  703.       if (!got_fsname || strcmp(mnt_type, "cdrom") == 0)
  704.         {
  705.           /* Look for the volume label.  */
  706.           int e = errno;
  707.  
  708.           strcat(drive_string, "*.*");
  709.           errno = 0;
  710.  
  711.           if (!findfirst(drive_string, &mnt_ff, FA_LABEL))
  712.             {
  713.               errno = e;
  714.  
  715.               /* Got label.  Strip out extraneous '.' separator, if present. */
  716.               strcpy(mnt_fsname, mnt_ff.ff_name);
  717.               if (strlen(mnt_fsname) > 8 && mnt_fsname[8] == '.')
  718.                 {
  719.                   /* Overlapping copy, don't use strcpy() */
  720.                   p = mnt_fsname + 8;
  721.                   q = p + 1;
  722.                   while ((*p++ = *q++) != '\0');
  723.                 }
  724.  
  725.               got_fsname = 1;
  726.             }
  727.  
  728.           else if (errno == ENMFILE || errno == ENOENT)
  729.             {
  730.               /* Valid drive, but no label.  Construct default
  731.                  filesystem name.  If drive A: is mapped to B:,
  732.                  call it ``Drive A:''.  */
  733.               (void) strcpy(mnt_fsname, "Drive  :");
  734.               mnt_fsname[6] = (drive_number == 2 && drive_a_mapping == 2)
  735.                               ? 'A'
  736.                               : '@' + drive_number;
  737.               got_fsname = 1;
  738.             }
  739.         }
  740.  
  741.       if (got_fsname)
  742.         {
  743.           char xdrive[3];
  744.  
  745.           /* Remove the '*.*' from the drive string for use as mnt_dir */
  746.           drive_string[drvstr_len] = '\0';
  747.           if (mnt_dir[0] == '\0')
  748.             strcpy(mnt_dir, drive_string);
  749.  
  750.           /* Format mnt_dir[] for beauty.  */
  751.           for (p = mnt_dir; *p; p++)
  752.             {
  753.               if (*p == '\\')
  754.                 *p = '/';
  755.               else
  756.                 *p = tolower(*p);
  757.             }
  758.  
  759.           /* Should we convert ``\\HOST\PATH'' into ``HOST:PATH''?  */
  760.           mntent.mnt_fsname = mnt_fsname;
  761.           mntent.mnt_dir    = mnt_dir;
  762.           mntent.mnt_type   = mnt_type;
  763.           mntent.mnt_opts   = dev_opts;
  764.  
  765.           /* Make CD-ROM drives read-only, others read-write.  */
  766.           if (strcmp(mnt_type, "cdrom") == 0)
  767.             dev_opts[1] = 'o';
  768.           else
  769.             dev_opts[1] = 'w';
  770.  
  771.           /* Include "dev=XX" in the mnt_opts field, where XX should
  772.              be consistent with what stat() returns in st_dev.  */
  773.           sprintf(xdrive, "%02x", strcmp(mnt_type, "subst")
  774.                                   ? drive_number - 1
  775.                                   : mnt_fsname[0] - 'A');
  776.           dev_opts[7] = xdrive[0];
  777.           dev_opts[8] = xdrive[1];
  778.           mntent.mnt_freq   = -1;
  779.           mntent.mnt_passno = -1;
  780.           mntent.mnt_time   = -1;
  781.  
  782.           return &mntent;
  783.         }
  784.  
  785.       /* Go try next drive, if any left.  */
  786.     }
  787.   
  788.   return NULL;
  789. }
  790.  
  791. int
  792. addmntent(FILE *filep, const struct mntent *mnt)
  793. {
  794.   return 1;
  795. }
  796.  
  797. char *
  798. hasmntopt(const struct mntent *mnt, const char *opt)
  799. {
  800.   return strstr(mnt->mnt_opts, opt);
  801. }
  802.  
  803. int
  804. endmntent(FILE *filep)
  805. {
  806.   if (filep != (FILE *)1)
  807.     {
  808.       errno = EBADF;    /* fake errno for invalid handle */
  809.       return NULL;
  810.     }
  811.   drive_number = 0;
  812.   skip_drive_b = 0;
  813.   return 1;
  814. }
  815.  
  816. #ifdef  TEST
  817.  
  818. #if defined(MNT_MNTTAB)
  819. #define MNTTAB_FILE MNT_MNTTAB
  820. #elif defined(MNTTABNAME)
  821. #define MNTTAB_FILE MNTTABNAME
  822. #else
  823. #define MNTTAB_FILE "/etc/mnttab"
  824. #endif
  825.  
  826. int main(void)
  827. {
  828.   FILE *mp = setmntent(MNTTAB_FILE, "r");
  829.   struct mntent *me;
  830.   int i = 0;
  831.  
  832.   if (!mp)
  833.     {
  834.       fprintf(stderr, "setmntent() failed\n");
  835.       return 1;
  836.     }
  837.  
  838.   while ((me = getmntent(mp)) != NULL)
  839.     printf("%d:  %-35s %s\t%s\t%s\n", i++,
  840.            me->mnt_fsname, me->mnt_type, me->mnt_opts, me->mnt_dir);
  841.  
  842.   return 0;
  843. }
  844.  
  845. #endif
  846.