"Fossies" - the Fresh Open Source Software Archive

Member "cvs-1.11.23/os2/filesubr.c" (4 Apr 2005, 22552 Bytes) of package /linux/misc/old/cvs-1.11.23.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "filesubr.c" see the Fossies "Dox" file reference documentation.

    1 /* filesubr.c --- subroutines for dealing with files under OS/2
    2    Jim Blandy <jimb@cyclic.com> and Karl Fogel <kfogel@cyclic.com>
    3 
    4    This file is part of GNU CVS.
    5 
    6    GNU CVS is free software; you can redistribute it and/or modify it
    7    under the terms of the GNU General Public License as published by the
    8    Free Software Foundation; either version 2, or (at your option) any
    9    later version.
   10 
   11    This program is distributed in the hope that it will be useful,
   12    but WITHOUT ANY WARRANTY; without even the implied warranty of
   13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   14    GNU General Public License for more details.  */
   15 
   16 /* These functions were moved out of subr.c because they need different
   17    definitions under operating systems (like, say, Windows NT) with different
   18    file system semantics.  */
   19 
   20 #include <io.h>
   21 
   22 #include "os2inc.h"
   23 #include "cvs.h"
   24 
   25 static int deep_remove_dir PROTO((const char *path));
   26 
   27 /*
   28  * Copies "from" to "to".
   29  */
   30 void
   31 copy_file (from, to)
   32     const char *from;
   33     const char *to;
   34 {
   35     struct stat sb;
   36     struct utimbuf t;
   37     int fdin, fdout;
   38 
   39     if (trace)
   40 #ifdef SERVER_SUPPORT
   41     (void) fprintf (stderr, "%c-> copy(%s,%s)\n",
   42             (server_active) ? 'S' : ' ', from, to);
   43 #else
   44     (void) fprintf (stderr, "-> copy(%s,%s)\n", from, to);
   45 #endif
   46     if (noexec)
   47     return;
   48 
   49     if ((fdin = open (from, O_RDONLY | O_BINARY)) < 0)
   50     error (1, errno, "cannot open %s for copying", from);
   51     if (fstat (fdin, &sb) < 0)
   52     error (1, errno, "cannot fstat %s", from);
   53     if ((fdout = open (to, O_CREAT | O_WRONLY | O_TRUNC | O_BINARY,
   54                        (int) sb.st_mode & 07777)) < 0)
   55     error (1, errno, "cannot create %s for copying", to);
   56     if (sb.st_size > 0)
   57     {
   58     char buf[BUFSIZ];
   59     int n;
   60 
   61     for (;;) 
   62     {
   63         n = read (fdin, buf, sizeof(buf));
   64         if (n == -1)
   65         {
   66 #ifdef EINTR
   67         if (errno == EINTR)
   68             continue;
   69 #endif
   70         error (1, errno, "cannot read file %s for copying", from);
   71         }
   72             else if (n == 0) 
   73         break;
   74   
   75         if (write(fdout, buf, n) != n) {
   76         error (1, errno, "cannot write file %s for copying", to);
   77         }
   78     }
   79 
   80 #ifdef HAVE_FSYNC
   81     if (fsync (fdout)) 
   82         error (1, errno, "cannot fsync file %s after copying", to);
   83 #endif
   84     }
   85 
   86     if (close (fdin) < 0) 
   87     error (0, errno, "cannot close %s", from);
   88     if (close (fdout) < 0)
   89     error (1, errno, "cannot close %s", to);
   90 
   91     /* now, set the times for the copied file to match those of the original */
   92     memset ((char *) &t, 0, sizeof (t));
   93     t.actime = sb.st_atime;
   94     t.modtime = sb.st_mtime;
   95     (void) utime ((char *)to, &t);
   96 }
   97 
   98 /* FIXME-krp: these functions would benefit from caching the char * &
   99    stat buf.  */
  100 
  101 /*
  102  * Returns non-zero if the argument file is a directory, or is a symbolic
  103  * link which points to a directory.
  104  */
  105 int
  106 isdir (file)
  107     const char *file;
  108 {
  109     struct stat sb;
  110 
  111     if (stat (file, &sb) < 0)
  112     return (0);
  113     return (S_ISDIR (sb.st_mode));
  114 }
  115 
  116 /*
  117  * Returns non-zero if the argument file is a symbolic link.
  118  */
  119 int
  120 islink (file)
  121     const char *file;
  122 {
  123 #ifdef S_ISLNK
  124     struct stat sb;
  125 
  126     if (lstat (file, &sb) < 0)
  127     return (0);
  128     return (S_ISLNK (sb.st_mode));
  129 #else
  130     return (0);
  131 #endif
  132 }
  133 
  134 /*
  135  * Returns non-zero if the argument file exists.
  136  */
  137 int
  138 isfile (file)
  139     const char *file;
  140 {
  141     struct stat sb;
  142 
  143     if (stat (file, &sb) < 0)
  144     return (0);
  145     return (1);
  146 }
  147 
  148 /*
  149  * Returns non-zero if the argument file is readable.
  150  * XXX - must be careful if "cvs" is ever made setuid!
  151  */
  152 int
  153 isreadable (file)
  154     const char *file;
  155 {
  156     return (access (file, R_OK) != -1);
  157 }
  158 
  159 /*
  160  * Returns non-zero if the argument file is writable
  161  * XXX - muct be careful if "cvs" is ever made setuid!
  162  */
  163 int
  164 iswritable (file)
  165     const char *file;
  166 {
  167     return (access (file, W_OK) != -1);
  168 }
  169 
  170 /*
  171  * Returns non-zero if the argument file is accessable according to
  172  * mode.  If compiled with SETXID_SUPPORT also works if cvs has setxid
  173  * bits set.
  174  */
  175 int
  176 isaccessible (file, mode)
  177     const char *file;
  178     const int mode;
  179 {
  180     return access(file, mode) == 0;
  181 }
  182 
  183 
  184 /*
  185  * Open a file and die if it fails
  186  */
  187 FILE *
  188 open_file (name, mode)
  189     const char *name;
  190     const char *mode;
  191 {
  192     FILE *fp;
  193 
  194     if ((fp = fopen (name, mode)) == NULL)
  195     error (1, errno, "cannot open %s", name);
  196     return (fp);
  197 }
  198 
  199 /*
  200  * Make a directory and die if it fails
  201  */
  202 void
  203 make_directory (name)
  204     const char *name;
  205 {
  206     struct stat buf;
  207 
  208     if (stat (name, &buf) == 0 && (!S_ISDIR (buf.st_mode)))
  209         error (0, 0, "%s already exists but is not a directory", name);
  210     if (!noexec && mkdir ((char *)name) < 0)
  211     error (1, errno, "cannot make directory %s", name);
  212 }
  213 
  214 /*
  215  * Make a path to the argument directory, printing a message if something
  216  * goes wrong.
  217  */
  218 void
  219 make_directories (name)
  220     const char *name;
  221 {
  222     char *cp;
  223 
  224     if (noexec)
  225     return;
  226 
  227     if (mkdir ((char *)name) == 0 || errno == EACCES)
  228     return;
  229     if (! existence_error (errno))
  230     {
  231     error (0, errno, "cannot make path to %s", name);
  232     return;
  233     }
  234     if ((cp = strrchr (name, '/')) == NULL)
  235     return;
  236     *cp = '\0';
  237     make_directories (name);
  238     *cp++ = '/';
  239     if (*cp == '\0')
  240     return;
  241     (void) mkdir ((char *)name);
  242 }
  243 
  244 /* Create directory NAME if it does not already exist; fatal error for
  245    other errors.  Returns 0 if directory was created; 1 if it already
  246    existed.  */
  247 int
  248 mkdir_if_needed (name)
  249     char *name;
  250 {
  251     if (mkdir (name) < 0)
  252     {
  253     /* Now, let me get this straight.  In IBM C/C++
  254        under OS/2, the error string for EEXIST is:
  255 
  256            "The file already exists",
  257 
  258            and the error string for EACCES is:
  259 
  260            "The file or directory specified is read-only".
  261 
  262            Nonetheless, mkdir() will set EACCES if the
  263        directory *exists*, according both to the
  264        documentation and its actual behavior.
  265 
  266        I'm sure that this made sense, to someone,
  267        somewhere, sometime.  Just not me, here, now.  */
  268     if (errno != EEXIST
  269 #ifdef EACCES
  270             && errno != EACCES
  271 #endif
  272         )
  273         error (1, errno, "cannot make directory %s", name);
  274     return 1;
  275     }
  276     return 0;
  277 }
  278 
  279 /*
  280  * Change the mode of a file, either adding write permissions, or removing
  281  * all write permissions.  Adding write permissions honors the current umask
  282  * setting.
  283  */
  284 void
  285 xchmod (fname, writable)
  286     char *fname;
  287     int writable;
  288 {
  289     char *attrib_cmd;
  290     char *attrib_option;
  291     char *whole_cmd;
  292     char *p;
  293     char *q;
  294 
  295     if (!isfile (fname))
  296     {
  297     error (0, 0, "cannot change mode of file %s; it does not exist",
  298            fname);
  299     return;
  300     }
  301 
  302     attrib_cmd = "attrib "; /* No, really? */
  303 
  304     if (writable)
  305         attrib_option = "-r ";  /* make writeable */
  306     else
  307         attrib_option = "+r ";  /* make read-only */
  308         
  309     whole_cmd = xmalloc (strlen (attrib_cmd)
  310                          + strlen (attrib_option)
  311                          + strlen (fname)
  312                          + 1);
  313 
  314     strcpy (whole_cmd, attrib_cmd);
  315     strcat (whole_cmd, attrib_option);
  316 
  317     /* Copy fname to the end of whole_cmd, translating / to \.
  318        Attrib doesn't take / but many parts of CVS rely
  319        on being able to use it.  */
  320     p = whole_cmd + strlen (whole_cmd);
  321     q = fname;
  322     while (*q)
  323     {
  324     if (*q == '/')
  325         *p++ = '\\';
  326     else
  327         *p++ = *q;
  328     ++q;
  329     }
  330     *p = '\0';
  331 
  332     system (whole_cmd);
  333     free (whole_cmd);
  334 }
  335 
  336 
  337 /* Read the value of a symbolic link.
  338    Under OS/2, this function always returns EINVAL.  */
  339 int
  340 readlink (char *path, char *buf, int buf_size)
  341 {
  342     errno = EINVAL;
  343     return -1;
  344 }
  345 
  346 /*
  347  * unlink a file, if possible.
  348  */
  349 int
  350 unlink_file (f)
  351     const char *f;
  352 {
  353     if (trace)
  354 #ifdef SERVER_SUPPORT
  355     (void) fprintf (stderr, "%c-> unlink(%s)\n",
  356             (server_active) ? 'S' : ' ', f);
  357 #else
  358     (void) fprintf (stderr, "-> unlink(%s)\n", f);
  359 #endif
  360     if (noexec)
  361     return (0);
  362 
  363    /* Win32 unlink is stupid - it fails if the file is read-only.
  364     * OS/2 is similarly stupid.  It does have a remove() function,
  365     * but the documentation does not make clear why remove() is or
  366     * isn't preferable to unlink().  I'll use unlink() because the
  367     * name is closer to our interface, what the heck.  Also, we know
  368     * unlink()'s error code when trying to remove a directory.
  369     */
  370     if (isfile (f))
  371     xchmod ((char *)f, 1);
  372     return (unlink (f));
  373 }
  374 
  375 /*
  376  * Unlink a file or dir, if possible.  If it is a directory do a deep
  377  * removal of all of the files in the directory.  Return -1 on error
  378  * (in which case errno is set).
  379  */
  380 int
  381 unlink_file_dir (f)
  382     const char *f;
  383 {
  384     if (trace)
  385 #ifdef SERVER_SUPPORT
  386     (void) fprintf (stderr, "%c-> unlink_file_dir(%s)\n",
  387             (server_active) ? 'S' : ' ', f);
  388 #else
  389     (void) fprintf (stderr, "-> unlink_file_dir(%s)\n", f);
  390 #endif
  391     if (noexec)
  392     return (0);
  393 
  394     if (unlink_file (f) != 0)
  395     {
  396         /* under OS/2, unlink returns EACCES if the path
  397        is a directory.  */
  398         if (errno == EACCES)
  399                 return deep_remove_dir (f);
  400         else
  401         /* The file wasn't a directory and some other
  402          * error occured
  403          */
  404                 return -1;
  405     }
  406     /* We were able to remove the file from the disk */
  407     return 0;
  408 }
  409 
  410 /* Remove a directory and everything it contains.  Returns 0 for
  411  * success, -1 for failure (in which case errno is set).
  412  */
  413 
  414 static int
  415 deep_remove_dir (path)
  416     const char *path;
  417 {
  418     DIR       *dirp;
  419     struct dirent *dp;
  420     char       buf[PATH_MAX];
  421 
  422     if (rmdir ((char *)path) != 0 && errno == EACCES)
  423     {
  424     if ((dirp = opendir ((char *)path)) == NULL)
  425         /* If unable to open the directory return
  426          * an error
  427          */
  428         return -1;
  429 
  430     while ((dp = readdir (dirp)) != NULL)
  431     {
  432         if (strcmp (dp->d_name, ".") == 0 ||
  433             strcmp (dp->d_name, "..") == 0)
  434         continue;
  435 
  436         sprintf (buf, "%s/%s", path, dp->d_name);
  437 
  438         if (unlink_file (buf) != 0 )
  439         {
  440         if (errno == EACCES)
  441         {
  442             if (deep_remove_dir (buf))
  443             {
  444             closedir (dirp);
  445             return -1;
  446             }
  447         }
  448         else
  449         {
  450             /* buf isn't a directory, or there are
  451              * some sort of permision problems
  452              */
  453             closedir (dirp);
  454             return -1;
  455         }
  456         }
  457     }
  458     closedir (dirp);
  459     return rmdir ((char *)path);
  460     }
  461     /* Was able to remove the directory return 0 */
  462     return 0;
  463 }
  464 
  465 
  466 /*
  467  * Rename a file and die if it fails
  468  */
  469 void
  470 rename_file (from, to)
  471     const char *from;
  472     const char *to;
  473 {
  474     if (trace)
  475 #ifdef SERVER_SUPPORT
  476     (void) fprintf (stderr, "%c-> rename(%s,%s)\n",
  477             (server_active) ? 'S' : ' ', from, to);
  478 #else
  479     (void) fprintf (stderr, "-> rename(%s,%s)\n", from, to);
  480 #endif
  481     if (noexec)
  482     return;
  483 
  484     unlink_file (to);
  485     if (rename (from, to) != 0)
  486     error (1, errno, "cannot rename file %s to %s", from, to);
  487 }
  488 
  489 
  490 /* Read NCHARS bytes from descriptor FD into BUF.
  491    Return the number of characters successfully read.
  492    The number returned is always NCHARS unless end-of-file or error.  */
  493 static size_t
  494 block_read (fd, buf, nchars)
  495     int fd;
  496     char *buf;
  497     size_t nchars;
  498 {
  499     char *bp = buf;
  500     size_t nread;
  501 
  502     do 
  503     {
  504     nread = read (fd, bp, nchars);
  505     if (nread == (size_t)-1)
  506     {
  507 #ifdef EINTR
  508         if (errno == EINTR)
  509         continue;
  510 #endif
  511         return (size_t)-1;
  512     }
  513 
  514     if (nread == 0)
  515         break; 
  516 
  517     bp += nread;
  518     nchars -= nread;
  519     } while (nchars != 0);
  520 
  521     return bp - buf;
  522 } 
  523 
  524     
  525 /*
  526  * Compare "file1" to "file2". Return non-zero if they don't compare exactly.
  527  */
  528 int
  529 xcmp (file1, file2)
  530     const char *file1;
  531     const char *file2;
  532 {
  533     char *buf1, *buf2;
  534     struct stat sb1, sb2;
  535     int fd1, fd2;
  536     int ret;
  537 
  538     if ((fd1 = open (file1, O_RDONLY | O_BINARY)) < 0)
  539     error (1, errno, "cannot open file %s for comparing", file1);
  540     if ((fd2 = open (file2, O_RDONLY | O_BINARY)) < 0)
  541     error (1, errno, "cannot open file %s for comparing", file2);
  542     if (fstat (fd1, &sb1) < 0)
  543     error (1, errno, "cannot fstat %s", file1);
  544     if (fstat (fd2, &sb2) < 0)
  545     error (1, errno, "cannot fstat %s", file2);
  546 
  547     /* A generic file compare routine might compare st_dev & st_ino here 
  548        to see if the two files being compared are actually the same file.
  549        But that won't happen in CVS, so we won't bother. */
  550 
  551     if (sb1.st_size != sb2.st_size)
  552     ret = 1;
  553     else if (sb1.st_size == 0)
  554     ret = 0;
  555     else
  556     {
  557     /* FIXME: compute the optimal buffer size by computing the least
  558        common multiple of the files st_blocks field */
  559     size_t buf_size = 8 * 1024;
  560     size_t read1;
  561     size_t read2;
  562 
  563     buf1 = xmalloc (buf_size);
  564     buf2 = xmalloc (buf_size);
  565 
  566     do 
  567     {
  568         read1 = block_read (fd1, buf1, buf_size);
  569         if (read1 == (size_t)-1)
  570         error (1, errno, "cannot read file %s for comparing", file1);
  571 
  572         read2 = block_read (fd2, buf2, buf_size);
  573         if (read2 == (size_t)-1)
  574         error (1, errno, "cannot read file %s for comparing", file2);
  575 
  576         /* assert (read1 == read2); */
  577 
  578         ret = memcmp(buf1, buf2, read1);
  579     } while (ret == 0 && read1 == buf_size);
  580 
  581     free (buf1);
  582     free (buf2);
  583     }
  584     
  585     (void) close (fd1);
  586     (void) close (fd2);
  587     return (ret);
  588 }
  589 
  590 
  591 /* The equivalence class mapping for filenames.
  592    OS/2 filenames are case-insensitive, but case-preserving.  Both /
  593    and \ are path element separators. 
  594    Thus, this table maps both upper and lower case to lower case, and
  595    both / and \ to /.  
  596 
  597    Much thanks to Jim Blandy, who already invented this wheel in the
  598    Windows NT port. */
  599 
  600 #if 0
  601 main ()
  602 {
  603   int c;
  604 
  605   for (c = 0; c < 256; c++)
  606     {
  607       int t;
  608 
  609       if (c == '\\')
  610         t = '/';
  611       else
  612         t = tolower (c);
  613       
  614       if ((c & 0x7) == 0x0)
  615          printf ("    ");
  616       printf ("0x%02x,", t);
  617       if ((c & 0x7) == 0x7)
  618          putchar ('\n');
  619       else if ((c & 0x7) == 0x3)
  620          putchar (' ');
  621     }
  622 }
  623 #endif
  624 
  625 
  626 unsigned char
  627 OS2_filename_classes[] =
  628 {
  629     0x00,0x01,0x02,0x03, 0x04,0x05,0x06,0x07,
  630     0x08,0x09,0x0a,0x0b, 0x0c,0x0d,0x0e,0x0f,
  631     0x10,0x11,0x12,0x13, 0x14,0x15,0x16,0x17,
  632     0x18,0x19,0x1a,0x1b, 0x1c,0x1d,0x1e,0x1f,
  633     0x20,0x21,0x22,0x23, 0x24,0x25,0x26,0x27,
  634     0x28,0x29,0x2a,0x2b, 0x2c,0x2d,0x2e,0x2f,
  635     0x30,0x31,0x32,0x33, 0x34,0x35,0x36,0x37,
  636     0x38,0x39,0x3a,0x3b, 0x3c,0x3d,0x3e,0x3f,
  637     0x40,0x61,0x62,0x63, 0x64,0x65,0x66,0x67,
  638     0x68,0x69,0x6a,0x6b, 0x6c,0x6d,0x6e,0x6f,
  639     0x70,0x71,0x72,0x73, 0x74,0x75,0x76,0x77,
  640     0x78,0x79,0x7a,0x5b, 0x2f,0x5d,0x5e,0x5f,
  641     0x60,0x61,0x62,0x63, 0x64,0x65,0x66,0x67,
  642     0x68,0x69,0x6a,0x6b, 0x6c,0x6d,0x6e,0x6f,
  643     0x70,0x71,0x72,0x73, 0x74,0x75,0x76,0x77,
  644     0x78,0x79,0x7a,0x7b, 0x7c,0x7d,0x7e,0x7f,
  645     0x80,0x81,0x82,0x83, 0x84,0x85,0x86,0x87,
  646     0x88,0x89,0x8a,0x8b, 0x8c,0x8d,0x8e,0x8f,
  647     0x90,0x91,0x92,0x93, 0x94,0x95,0x96,0x97,
  648     0x98,0x99,0x9a,0x9b, 0x9c,0x9d,0x9e,0x9f,
  649     0xa0,0xa1,0xa2,0xa3, 0xa4,0xa5,0xa6,0xa7,
  650     0xa8,0xa9,0xaa,0xab, 0xac,0xad,0xae,0xaf,
  651     0xb0,0xb1,0xb2,0xb3, 0xb4,0xb5,0xb6,0xb7,
  652     0xb8,0xb9,0xba,0xbb, 0xbc,0xbd,0xbe,0xbf,
  653     0xc0,0xc1,0xc2,0xc3, 0xc4,0xc5,0xc6,0xc7,
  654     0xc8,0xc9,0xca,0xcb, 0xcc,0xcd,0xce,0xcf,
  655     0xd0,0xd1,0xd2,0xd3, 0xd4,0xd5,0xd6,0xd7,
  656     0xd8,0xd9,0xda,0xdb, 0xdc,0xdd,0xde,0xdf,
  657     0xe0,0xe1,0xe2,0xe3, 0xe4,0xe5,0xe6,0xe7,
  658     0xe8,0xe9,0xea,0xeb, 0xec,0xed,0xee,0xef,
  659     0xf0,0xf1,0xf2,0xf3, 0xf4,0xf5,0xf6,0xf7,
  660     0xf8,0xf9,0xfa,0xfb, 0xfc,0xfd,0xfe,0xff,
  661 };
  662 
  663 /* Like strcmp, but with the appropriate tweaks for file names.
  664    Under OS/2, filenames are case-insensitive but case-preserving, and
  665    both \ and / are path element separators.  */ 
  666 int
  667 fncmp (const char *n1, const char *n2)
  668 {
  669     while (*n1 && *n2
  670            && (OS2_filename_classes[(unsigned char) *n1]
  671            == OS2_filename_classes[(unsigned char) *n2]))
  672         n1++, n2++;
  673     return (OS2_filename_classes[(unsigned char) *n1]
  674             - OS2_filename_classes[(unsigned char) *n2]);
  675 }
  676 
  677 /* Fold characters in FILENAME to their canonical forms.  
  678    If FOLD_FN_CHAR is not #defined, the system provides a default
  679    definition for this.  */
  680 void
  681 fnfold (char *filename)
  682 {
  683     while (*filename)
  684     {
  685         *filename = FOLD_FN_CHAR (*filename);
  686     filename++;
  687     }
  688 }
  689 
  690 
  691 /* Generate a unique temporary filename.  Returns a pointer to a newly
  692    malloc'd string containing the name.  Returns successfully or not at
  693    all.  */
  694 char *
  695 cvs_temp_name ()
  696 {
  697     char value[L_tmpnam + 1];
  698     char *retval;
  699 
  700     /* FIXME: Does OS/2 have some equivalent to TMPDIR?  */
  701     retval = tmpnam (value);
  702     if (retval == NULL)
  703     error (1, errno, "cannot generate temporary filename");
  704     return xstrdup (retval);
  705 }
  706 
  707 
  708 
  709 /* char *
  710  * xresolvepath ( const char *path )
  711  *
  712  * Like xreadlink(), but resolve all links in a path.
  713  *
  714  * INPUTS
  715  *  path    The original path.
  716  *
  717  * RETURNS
  718  *  The path with any symbolic links expanded.
  719  *
  720  * ERRORS
  721  *  This function exits with a fatal error if it fails to read the link for
  722  *  any reason.
  723  */
  724 char *
  725 xresolvepath ( path )
  726     const char *path;
  727 {
  728     char *hardpath;
  729     char *owd;
  730 
  731     /* assert ( isdir ( path ) ); */
  732 
  733     /* FIXME - If HAVE_READLINK is defined, we should probably walk the path
  734      * bit by bit calling xreadlink().
  735      */
  736 
  737     owd = xgetwd();
  738     if ( CVS_CHDIR ( path ) < 0)
  739     error ( 1, errno, "cannot chdir to %s", path );
  740     if ( ( hardpath = xgetwd() ) == NULL )
  741     error (1, errno, "cannot readlink %s", hardpath);
  742     if ( CVS_CHDIR ( owd ) < 0)
  743     error ( 1, errno, "cannot chdir to %s", owd );
  744     free (owd);
  745     return hardpath;
  746 }
  747 
  748 /* Return a pointer into PATH's last component.  */
  749 char *
  750 last_component (char *path)
  751 {
  752     char *scan;
  753     char *last = 0;
  754 
  755     for (scan = path; *scan; scan++)
  756         if (ISDIRSEP (*scan))
  757         last = scan;
  758 
  759     if (last && (last != path))
  760         return last + 1;
  761     else
  762         return path;
  763 }
  764 
  765 
  766 /* Return the home directory.  Returns a pointer to storage
  767    managed by this function or its callees (currently getenv).  */
  768 char *
  769 get_homedir ()
  770 {
  771     return getenv ("HOME");
  772 }
  773 
  774 /* See cvs.h for description.  */
  775 void
  776 expand_wild (argc, argv, pargc, pargv)
  777     int argc;
  778     char **argv;
  779     int *pargc;
  780     char ***pargv;
  781 {
  782     int i;
  783     int new_argc;
  784     char **new_argv;
  785     /* Allocated size of new_argv.  We arrange it so there is always room for
  786        one more element.  */
  787     int max_new_argc;
  788 
  789     new_argc = 0;
  790     /* Add one so this is never zero.  */
  791     max_new_argc = argc + 1;
  792     new_argv = (char **) xmalloc (max_new_argc * sizeof (char *));
  793     for (i = 0; i < argc; ++i)
  794     {
  795     HDIR          FindHandle = 0x0001;
  796     FILEFINDBUF3  FindBuffer;
  797     ULONG         FindCount = 1;
  798     APIRET        rc;          /* Return code */
  799 #define ALL_FILES (FILE_ARCHIVED|FILE_DIRECTORY|FILE_SYSTEM|FILE_HIDDEN|FILE_READONLY) 
  800 
  801     /* DosFindFirst, called with a string like 'dir/file' will return
  802      * *only* the file part. So what we have to do here is to save the
  803      * directory part, and add it later to the returned filename.
  804      */
  805 
  806     /* Path + name */
  807     char *PathName = argv [i];
  808 
  809     /* Path only, including slash */
  810     char *Path = NULL;
  811 
  812     /* Name without path */
  813     char *Name = last_component (PathName);
  814 
  815     if (Name > PathName)
  816     {
  817         /* We have a path component, save it */
  818         Path = xmalloc (Name - PathName + 1);
  819         memcpy (Path, PathName, Name - PathName);
  820         Path [Name - PathName] = '\0';
  821     }
  822 
  823     rc = DosFindFirst(PathName,          /* File pattern */
  824               &FindHandle,           /* Directory search handle */
  825               ALL_FILES,             /* Search attribute */
  826               (PVOID) &FindBuffer,   /* Result buffer */
  827               sizeof(FindBuffer),    /* Result buffer length */
  828               &FindCount,            /* Number of entries to find */
  829               FIL_STANDARD);     /* Return level 1 file info */
  830 
  831     if (rc != 0)
  832     {
  833         if (rc == ERROR_NO_MORE_FILES)
  834         {
  835         /* No match.  The file specified didn't contain a wildcard (in which case
  836            we clearly should return it unchanged), or it contained a wildcard which
  837            didn't match (in which case it might be better for it to be an error,
  838            but we don't try to do that).  */
  839         new_argv [new_argc++] = xstrdup (argv[i]);
  840         if (new_argc == max_new_argc)
  841         {
  842             max_new_argc *= 2;
  843             new_argv = xrealloc (new_argv, max_new_argc * sizeof (char *));
  844         }
  845         }
  846         else
  847         {
  848                 error (1, rc, "cannot find %s", PathName);
  849         }
  850     }
  851     else
  852     {
  853         while (1)
  854         {
  855         /*
  856          * Don't match ".", "..", and files starting with '.'
  857          * (unless pattern also starts with '.').  This is
  858          * (more or less) what standard Unix globbing does.
  859          */
  860         if ((strcmp(FindBuffer.achName, ".") != 0) &&
  861             (strcmp(FindBuffer.achName, "..") != 0) &&
  862             ((argv[i][0] == '.') || (FindBuffer.achName[0] != '.')))
  863         {
  864             /* Be sure to add the path if needed */
  865             char *NewArg;
  866             if (Path)
  867             {
  868             unsigned Len =
  869                 strlen (Path) + strlen (FindBuffer.achName) + 1;
  870             NewArg = xmalloc (Len);
  871             strcpy (NewArg, Path);
  872             strcat (NewArg, FindBuffer.achName);
  873             }
  874             else
  875             {
  876             NewArg = xstrdup (FindBuffer.achName);
  877             }
  878             new_argv [new_argc++] = NewArg;
  879             if (new_argc == max_new_argc)
  880             {
  881             max_new_argc *= 2;
  882             new_argv = xrealloc (new_argv, max_new_argc * sizeof (char *));
  883             }
  884         }
  885 
  886         rc = DosFindNext (FindHandle,
  887                   (PVOID) &FindBuffer,
  888                   sizeof(FindBuffer),
  889                   &FindCount);
  890         if (rc == ERROR_NO_MORE_FILES)
  891             break;
  892         else if (rc != NO_ERROR)
  893             error (1, rc, "cannot find %s", argv[i]);
  894         }
  895         rc = DosFindClose(FindHandle);
  896         if (rc != 0)
  897         error (1, rc, "cannot close %s", argv[i]);
  898     }
  899     if (Path != NULL)
  900         free (Path);
  901     }
  902     *pargc = new_argc;
  903     *pargv = new_argv;
  904 }
  905 
  906 /* Change drive and directory to path DIR.  */
  907 
  908 int
  909 os2_chdir (const char *Dir)
  910 {
  911     /* If the path includes a drive, change the current drive to the one
  912        given.  */
  913     if (strlen (Dir) >= 2 && Dir [1] == ':')
  914     {
  915     /* A drive is given in Dir. Extract the drive from the string, then
  916      * remove the drive from Dir by incrementing it.
  917      */
  918     int Drive = Dir [0];
  919     Dir += 2;
  920 
  921     /* Check if the given drive is valid, convert to a drive number
  922      * (A: == 1, B: == 2, etc.). The compare below assumes ascii, but
  923      * that is not a problem with OS/2.
  924      */
  925     if (Drive >= 'a' && Drive <= 'z')
  926     {
  927         Drive -= 'a' - 1;
  928     }
  929     else if (Drive >= 'A' && Drive <= 'Z')
  930     {
  931         Drive -= 'A' - 1;
  932     }
  933     else
  934     {
  935         /* An invalid drive letter. Set errno and return an error */
  936         errno = EACCES;
  937         return -1;
  938     }
  939 
  940     /* We have a valid drive given, so change the drive now */
  941     if (DosSetDefaultDisk (Drive) != 0)
  942     {
  943         /* We had an error. Assume that the drive does not exist */
  944 #ifdef ENODEV
  945         errno = ENODEV;
  946 #else
  947         /* IBM C/C++ Tools 2.01 seems to lack ENODEV.  */
  948         errno = ENOENT;
  949 #endif
  950         return -1;
  951     }
  952 
  953     }
  954 
  955     /* Now we have a path without a drive left. Make it the current dir */
  956     return chdir (Dir);
  957 }
  958 
  959 
  960