update changelog
[alioth/cvs.git] / windows-NT / filesubr.c
1 /* filesubr.c --- subroutines for dealing with files
2    Jim Blandy <jimb@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 <assert.h>
21 #include <io.h>
22 #include <sys/socket.h>  /* This does: #include <windows.h> */
23
24 #include "cvs.h"
25 #include "setenv.h"
26
27 #include "JmgStat.h"
28
29 #undef mkdir
30
31 static int deep_remove_dir( const char *path );
32
33 /* Copies "from" to "to".  Note that the functionality here is similar
34    to the win32 function CopyFile, but (1) we copy LastAccessTime and
35    CopyFile doesn't, (2) we set file attributes to the default set by
36    the C library and CopyFile copies them.  Neither #1 nor #2 was intentional
37    as far as I know, but changing them could be confusing, unless there
38    is some reason they should be changed (this would need more
39    investigation).  */
40 void
41 copy_file (from, to)
42     const char *from;
43     const char *to;
44 {
45     struct stat sb;
46     struct utimbuf t;
47     int fdin, fdout;
48
49     if (trace)
50 #ifdef SERVER_SUPPORT
51         (void) fprintf (stderr, "%c-> copy(%s,%s)\n",
52                         (server_active) ? 'S' : ' ', from, to);
53 #else
54         (void) fprintf (stderr, "-> copy(%s,%s)\n", from, to);
55 #endif
56     if (noexec)
57         return;
58
59     if ((fdin = open (from, O_RDONLY | O_BINARY)) < 0)
60         error (1, errno, "cannot open %s for copying", from);
61     if (fstat (fdin, &sb) < 0)
62         error (1, errno, "cannot fstat %s", from);
63     if ((fdout = open (to, O_CREAT | O_TRUNC | O_RDWR | O_BINARY,
64                        (int) sb.st_mode & 07777)) < 0)
65         error (1, errno, "cannot create %s for copying", to);
66     if (sb.st_size > 0)
67     {
68         char buf[BUFSIZ];
69         int n;
70
71         for (;;) 
72         {
73             n = read (fdin, buf, sizeof(buf));
74             if (n == -1)
75             {
76 #ifdef EINTR
77                 if (errno == EINTR)
78                     continue;
79 #endif
80                 error (1, errno, "cannot read file %s for copying", from);
81             }
82             else if (n == 0) 
83                 break;
84   
85             if (write(fdout, buf, n) != n) {
86                 error (1, errno, "cannot write file %s for copying", to);
87             }
88         }
89
90 #ifdef HAVE_FSYNC
91         if (fsync (fdout)) 
92             error (1, errno, "cannot fsync file %s after copying", to);
93 #endif
94     }
95
96     if (close (fdin) < 0) 
97         error (0, errno, "cannot close %s", from);
98     if (close (fdout) < 0)
99         error (1, errno, "cannot close %s", to);
100
101     /* now, set the times for the copied file to match those of the original */
102     memset ((char *) &t, 0, sizeof (t));
103     t.actime = sb.st_atime;
104     t.modtime = sb.st_mtime;
105     (void) utime (to, &t);
106 }
107
108
109 static char *tmpdir_env;
110 /*
111  * Return seperator (\) terminated path to system temporary directory.
112  */
113 const char *
114 get_system_temp_dir (void)
115 {
116         if (! tmpdir_env)
117         {
118                 DWORD dwBufferSize, dwReturn;
119
120                 dwReturn = 0;
121                 dwBufferSize = 64;
122                 do {
123                         if (dwReturn >= dwBufferSize)
124                         {
125                                 dwBufferSize = dwReturn + 4;
126                         }
127
128                         tmpdir_env = xrealloc (tmpdir_env, dwBufferSize);
129                         if (tmpdir_env)
130                         {
131                                 dwReturn = GetTempPath (dwBufferSize, tmpdir_env);
132                                 if (dwReturn <= 0)
133                                 {
134                                         free (tmpdir_env);
135                                         tmpdir_env = NULL;
136                                 }
137                         }
138                 } while (tmpdir_env && dwReturn >= dwBufferSize);
139         }
140
141         return tmpdir_env;
142 }
143
144
145 void
146 push_env_temp_dir (void)
147 {
148         const char *tmpdir = get_cvs_tmp_dir ();
149
150         if (tmpdir_env && strcmp (tmpdir_env, tmpdir))
151                 setenv ("TMP", tmpdir, 1);
152 }
153
154
155 /* FIXME-krp: these functions would benefit from caching the char * &
156    stat buf.  */
157
158 /*
159  * Returns non-zero if the argument file is a directory, or is a symbolic
160  * link which points to a directory.
161  */
162 int
163 isdir (file)
164     const char *file;
165 {
166     struct stat sb;
167
168     if (stat (file, &sb) < 0)
169         return (0);
170     return (S_ISDIR (sb.st_mode));
171 }
172
173 /*
174  * Returns non-zero if the argument file is a symbolic link.
175  */
176 int
177 islink (file)
178     const char *file;
179 {
180 #ifdef S_ISLNK
181     struct stat sb;
182
183     if (lstat (file, &sb) < 0)
184         return (0);
185     return (S_ISLNK (sb.st_mode));
186 #else
187     return (0);
188 #endif
189 }
190
191 /*
192  * Returns non-zero if the argument file exists.
193  */
194 int
195 isfile (file)
196     const char *file;
197 {
198     return isaccessible(file, F_OK);
199 }
200
201 /*
202  * Returns non-zero if the argument file is readable.
203  */
204 int
205 isreadable (file)
206     const char *file;
207 {
208     return isaccessible(file, R_OK);
209 }
210
211 /*
212  * Returns non-zero if the argument file is writable.
213  */
214 int
215 iswritable (file)
216     const char *file;
217 {
218     return isaccessible(file, W_OK);
219 }
220
221 /*
222  * Returns non-zero if the argument file is accessable according to
223  * mode.  If compiled with SETXID_SUPPORT also works if cvs has setxid
224  * bits set.
225  */
226 int
227 isaccessible (file, mode)
228     const char *file;
229     const int mode;
230 {
231 #ifdef SETXID_SUPPORT
232     struct stat sb;
233     int umask = 0;
234     int gmask = 0;
235     int omask = 0;
236     int uid;
237     
238     if (stat(file, &sb) == -1)
239         return 0;
240     if (mode == F_OK)
241         return 1;
242
243     uid = geteuid();
244     if (uid == 0)               /* superuser */
245     {
246         if (mode & X_OK)
247             return sb.st_mode & (S_IXUSR|S_IXGRP|S_IXOTH);
248         else
249             return 1;
250     }
251         
252     if (mode & R_OK)
253     {
254         umask |= S_IRUSR;
255         gmask |= S_IRGRP;
256         omask |= S_IROTH;
257     }
258     if (mode & W_OK)
259     {
260         umask |= S_IWUSR;
261         gmask |= S_IWGRP;
262         omask |= S_IWOTH;
263     }
264     if (mode & X_OK)
265     {
266         umask |= S_IXUSR;
267         gmask |= S_IXGRP;
268         omask |= S_IXOTH;
269     }
270
271     if (sb.st_uid == uid)
272         return (sb.st_mode & umask) == umask;
273     else if (sb.st_gid == getegid())
274         return (sb.st_mode & gmask) == gmask;
275     else
276         return (sb.st_mode & omask) == omask;
277 #else
278     return access(file, mode) == 0;
279 #endif
280 }
281
282
283
284 /*
285  * Make a directory and die if it fails
286  */
287 void
288 make_directory (name)
289     const char *name;
290 {
291     struct stat sb;
292
293     if (stat (name, &sb) == 0 && (!S_ISDIR (sb.st_mode)))
294             error (0, 0, "%s already exists but is not a directory", name);
295     if (!noexec && mkdir (name) < 0)
296         error (1, errno, "cannot make directory %s", name);
297 }
298
299 /*
300  * Make a path to the argument directory, printing a message if something
301  * goes wrong.
302  */
303 void
304 make_directories (name)
305     const char *name;
306 {
307     char *cp;
308
309     if (noexec)
310         return;
311
312     if (mkdir (name) == 0 || errno == EEXIST)
313         return;
314     if (errno != ENOENT)
315     {
316         error (0, errno, "cannot make path to %s", name);
317         return;
318     }
319     if ((cp = strrchr (name, '/')) == NULL)
320         return;
321     *cp = '\0';
322     make_directories (name);
323     *cp++ = '/';
324     if (*cp == '\0')
325         return;
326     (void) mkdir (name);
327 }
328
329 /* Create directory NAME if it does not already exist; fatal error for
330    other errors.  Returns 0 if directory was created; 1 if it already
331    existed.  */
332 int
333 mkdir_if_needed (name)
334     const char *name;
335 {
336     if (mkdir (name) < 0)
337     {
338         if (errno != EEXIST
339 #ifdef EACCESS
340             /* This was copied over from the OS/2 code; I would guess it
341                isn't needed here but that has not been verified.  */
342             && errno != EACCESS
343 #endif
344 #ifdef EACCES
345             /* This is said to be needed by NT on Alpha or PowerPC
346                (not sure what version) --August, 1996.  */
347             && errno != EACCES
348 #endif
349             )
350             error (1, errno, "cannot make directory %s", name);
351         return 1;
352     }
353     return 0;
354 }
355
356 /*
357  * Change the mode of a file, either adding write permissions, or removing
358  * all write permissions.  Adding write permissions honors the current umask
359  * setting.
360  */
361 void
362 xchmod (fname, writable)
363     const char *fname;
364     int writable;
365 {
366     struct stat sb;
367     mode_t mode, oumask;
368
369     if (stat (fname, &sb) < 0)
370     {
371         if (!noexec)
372             error (0, errno, "cannot stat %s", fname);
373         return;
374     }
375     if (writable)
376     {
377         oumask = umask (0);
378         (void) umask (oumask);
379         mode = sb.st_mode | ~oumask & (((sb.st_mode & S_IRUSR) ? S_IWUSR : 0) |
380                                        ((sb.st_mode & S_IRGRP) ? S_IWGRP : 0) |
381                                        ((sb.st_mode & S_IROTH) ? S_IWOTH : 0));
382     }
383     else
384     {
385         mode = sb.st_mode & ~(S_IWRITE | S_IWGRP | S_IWOTH);
386     }
387
388     if (trace)
389 #ifdef SERVER_SUPPORT
390         (void) fprintf (stderr, "%c-> chmod(%s,%o)\n",
391                         (server_active) ? 'S' : ' ', fname, mode);
392 #else
393         (void) fprintf (stderr, "-> chmod(%s,%o)\n", fname, mode);
394 #endif
395     if (noexec)
396         return;
397
398     if (chmod (fname, mode) < 0)
399         error (0, errno, "cannot change mode of file %s", fname);
400 }
401
402
403 /* Rename for NT which works for read only files.  Apparently if we are
404    accessing FROM and TO via a Novell network, this is an issue.  */
405 int
406 wnt_rename (from, to)
407     const char *from;
408     const char *to;
409 {
410     int result, save_errno;
411     int readonly = !iswritable (from);
412
413     if (readonly)
414     {
415         if (chmod (from, S_IWRITE) < 0)
416             return -1;
417     }
418     result = rename (from, to);
419     save_errno = errno;
420     if (readonly)
421     {
422         if (result == 0)
423         {
424             if (chmod (to, S_IREAD) < 0)
425                 return -1;
426         }
427         else
428         {
429             /* We have a choice of which error to report, if there is
430                one here too; report the one from rename ().  */
431             chmod (from, S_IREAD);
432         }
433         errno = save_errno;
434     }
435     return result;
436 }
437
438 /*
439  * Rename a file and die if it fails
440  */
441 void
442 rename_file (from, to)
443     const char *from;
444     const char *to;
445 {
446     if (trace)
447 #ifdef SERVER_SUPPORT
448         (void) fprintf (stderr, "%c-> rename(%s,%s)\n",
449                         (server_active) ? 'S' : ' ', from, to);
450 #else
451         (void) fprintf (stderr, "-> rename(%s,%s)\n", from, to);
452 #endif
453     if (noexec)
454         return;
455
456     /* Win32 unlink is stupid --- it fails if the file is read-only  */
457     chmod(to, S_IWRITE);
458     unlink(to);
459     if (CVS_RENAME (from, to) < 0)
460         error (1, errno, "cannot rename file %s to %s", from, to);
461 }
462
463 /*
464  * unlink a file, if possible.
465  */
466 int
467 unlink_file (f)
468     const char *f;
469 {
470     if (trace)
471 #ifdef SERVER_SUPPORT
472         (void) fprintf (stderr, "%c-> unlink(%s)\n",
473                         (server_active) ? 'S' : ' ', f);
474 #else
475         (void) fprintf (stderr, "-> unlink(%s)\n", f);
476 #endif
477     if (noexec)
478         return (0);
479
480     /* Win32 unlink is stupid - it fails if the file is read-only */
481     chmod (f, _S_IWRITE);
482     return (unlink (f));
483 }
484
485 /*
486  * Unlink a file or dir, if possible.  If it is a directory do a deep
487  * removal of all of the files in the directory.  Return -1 on error
488  * (in which case errno is set).
489  */
490 int
491 unlink_file_dir (f)
492     const char *f;
493 {
494     if (trace)
495 #ifdef SERVER_SUPPORT
496         (void) fprintf (stderr, "%c-> unlink_file_dir(%s)\n",
497                         (server_active) ? 'S' : ' ', f);
498 #else
499         (void) fprintf (stderr, "-> unlink_file_dir(%s)\n", f);
500 #endif
501     if (noexec)
502         return (0);
503
504     /* Win32 unlink is stupid - it fails if the file is read-only */
505     chmod (f, _S_IWRITE);
506     if (unlink (f) != 0)
507     {
508         /* Under Windows NT, unlink returns EACCES if the path
509            is a directory.  Under Windows 95, it returns ENOENT.
510            Under Windows XP, it can return ENOTEMPTY. */
511         if (errno == EISDIR || errno == EACCES || errno == ENOENT
512             || errno == ENOTEMPTY)
513                 return deep_remove_dir (f);
514         else
515                 /* The file wasn't a directory and some other
516                  * error occured
517                  */
518                 return -1;
519     }
520     /* We were able to remove the file from the disk */
521     return 0;
522 }
523
524 /* Remove a directory and everything it contains.  Returns 0 for
525  * success, -1 for failure (in which case errno is set).
526  */
527
528 static int
529 deep_remove_dir (path)
530     const char *path;
531 {
532     DIR           *dirp;
533     struct dirent *dp;
534     char           buf[PATH_MAX];
535
536     /* ENOTEMPTY for NT (obvious) but EACCES for Win95 (not obvious) */
537     if (rmdir (path) != 0)
538     {
539         if (errno == ENOTEMPTY || errno == EACCES)
540         {
541             if ((dirp = CVS_OPENDIR (path)) == NULL)
542                 /* If unable to open the directory return
543                  * an error
544                  */
545                 return -1;
546
547             while ((dp = CVS_READDIR (dirp)) != NULL)
548             {
549                 if (strcmp (dp->d_name, ".") == 0 ||
550                             strcmp (dp->d_name, "..") == 0)
551                     continue;
552
553                 sprintf (buf, "%s/%s", path, dp->d_name);
554
555                 /* Win32 unlink is stupid - it fails if the file is read-only */
556                 chmod (buf, _S_IWRITE);
557                 if (unlink (buf) != 0 )
558                 {
559                     /* Under Windows NT, unlink returns EACCES if the path
560                      * is a directory.  Under Windows 95, it returns ENOENT.
561                      * Under Windows XP, it can return ENOTEMPTY.  It
562                      * isn't really clear to me whether checking errno is
563                      * better or worse than using _stat to check for a
564                      * directory.
565                      * We aren't really trying to prevent race conditions here
566                      * (e.g. what if something changes between readdir and
567                      * unlink?)
568                      */
569                     if (errno == EISDIR || errno == EACCES || errno == ENOENT
570                         || errno == ENOTEMPTY)
571                     {
572                         if (deep_remove_dir (buf))
573                         {
574                             closedir (dirp);
575                             return -1;
576                         }
577                     }
578                     else
579                     {
580                         /* buf isn't a directory, or there are
581                          * some sort of permision problems
582                          */
583                         CVS_CLOSEDIR (dirp);
584                         return -1;
585                     }
586                 }
587             }
588             CVS_CLOSEDIR (dirp);
589             return rmdir (path);
590         }
591         else
592             return -1;
593     }
594     /* Was able to remove the directory return 0 */
595     return 0;
596 }
597
598 /* Read NCHARS bytes from descriptor FD into BUF.
599    Return the number of characters successfully read.
600    The number returned is always NCHARS unless end-of-file or error.  */
601 static size_t
602 block_read (fd, buf, nchars)
603     int fd;
604     char *buf;
605     size_t nchars;
606 {
607     char *bp = buf;
608     size_t nread;
609
610     do 
611     {
612         nread = read (fd, bp, nchars);
613         if (nread == (size_t)-1)
614         {
615 #ifdef EINTR
616             if (errno == EINTR)
617                 continue;
618 #endif
619             return (size_t)-1;
620         }
621
622         if (nread == 0)
623             break; 
624
625         bp += nread;
626         nchars -= nread;
627     } while (nchars != 0);
628
629     return bp - buf;
630
631
632     
633 /*
634  * Compare "file1" to "file2". Return non-zero if they don't compare exactly.
635  */
636 int
637 xcmp (file1, file2)
638     const char *file1;
639     const char *file2;
640 {
641     char *buf1, *buf2;
642     struct stat sb1, sb2;
643     int fd1, fd2;
644     int ret;
645
646     if ((fd1 = open (file1, O_RDONLY | O_BINARY)) < 0)
647         error (1, errno, "cannot open file %s for comparing", file1);
648     if ((fd2 = open (file2, O_RDONLY | O_BINARY)) < 0)
649         error (1, errno, "cannot open file %s for comparing", file2);
650     if (fstat (fd1, &sb1) < 0)
651         error (1, errno, "cannot fstat %s", file1);
652     if (fstat (fd2, &sb2) < 0)
653         error (1, errno, "cannot fstat %s", file2);
654
655     /* A generic file compare routine might compare st_dev & st_ino here 
656        to see if the two files being compared are actually the same file.
657        But that won't happen in CVS, so we won't bother. */
658
659     if (sb1.st_size != sb2.st_size)
660         ret = 1;
661     else if (sb1.st_size == 0)
662         ret = 0;
663     else
664     {
665         /* FIXME: compute the optimal buffer size by computing the least
666            common multiple of the files st_blocks field */
667         size_t buf_size = 8 * 1024;
668         size_t read1;
669         size_t read2;
670
671         buf1 = xmalloc (buf_size);
672         buf2 = xmalloc (buf_size);
673
674         do 
675         {
676             read1 = block_read (fd1, buf1, buf_size);
677             if (read1 == (size_t)-1)
678                 error (1, errno, "cannot read file %s for comparing", file1);
679
680             read2 = block_read (fd2, buf2, buf_size);
681             if (read2 == (size_t)-1)
682                 error (1, errno, "cannot read file %s for comparing", file2);
683
684             /* assert (read1 == read2); */
685
686             ret = memcmp(buf1, buf2, read1);
687         } while (ret == 0 && read1 == buf_size);
688
689         free (buf1);
690         free (buf2);
691     }
692         
693     (void) close (fd1);
694     (void) close (fd2);
695     return (ret);
696 }
697
698 /* Generate a unique temporary filename.  Returns a pointer to a newly
699  * malloc'd string containing the name.  Returns successfully or not at
700  * all.
701  *
702  *     THIS FUNCTION IS DEPRECATED!!!  USE cvs_temp_file INSTEAD!!!
703  *
704  * and yes, I know about the way the rcs commands use temp files.  I think
705  * they should be converted too but I don't have time to look into it right
706  * now.
707  */
708 char *
709 cvs_temp_name ()
710 {
711     char *fn;
712     FILE *fp;
713
714     fp = cvs_temp_file (&fn);
715     if (fp == NULL)
716         error (1, errno, "Failed to create temporary file");
717     if (fclose (fp) == EOF)
718         error (0, errno, "Failed to close temporary file %s", fn);
719     return fn;
720 }
721
722 /* Generate a unique temporary filename and return an open file stream
723  * to the truncated file by that name
724  *
725  *  INPUTS
726  *      filename        where to place the pointer to the newly allocated file
727  *                      name string
728  *
729  *  OUTPUTS
730  *      filename        dereferenced, will point to the newly allocated file
731  *                      name string.  This value is undefined if the function
732  *                      returns an error.
733  *
734  *  RETURNS
735  *      An open file pointer to a read/write mode empty temporary file with the
736  *      unique file name or NULL on failure.
737  *
738  *  ERRORS
739  *      on error, errno will be set to some value either by CVS_FOPEN or
740  *      whatever system function is called to generate the temporary file name
741  */
742 /* FIXME: This should use the mkstemp() function from the lib/mkstemp.c file
743  * from the GNULIB project.
744  */
745 FILE *cvs_temp_file (char ** filename)
746 {
747     char *fn;
748     FILE *fp;
749
750     /* FIXME - I'd like to be returning NULL here in noexec mode, but I think
751      * some of the rcs & diff functions which rely on a temp file run in
752      * noexec mode too.
753      */
754
755     /* assert (filename != NULL); */
756
757     fn = _tempnam (getenv("TEMP"), "cvs");
758     if (fn == NULL) fp = NULL;
759     else
760     if ((fp = CVS_FOPEN (fn, "w+")) == NULL)
761     {
762         free (fn);
763         fn = NULL;
764     }
765
766     /* tempnam returns a pointer to a newly malloc'd string, so there's
767      * no need for a xstrdup
768      */
769
770     *filename = fn;
771     return fp;
772 }
773
774
775
776 /* Return a pointer into PATH's last component.  */
777 const char *
778 last_component (const char *path)
779 {
780     const char *scan;
781     const char *last = 0;
782
783     for (scan = path; *scan; scan++)
784         if (ISSLASH (*scan))
785             last = scan;
786
787     if (last && (last != path))
788         return last + 1;
789     else
790         return path;
791 }
792
793
794 /* NT has two evironment variables, HOMEPATH and HOMEDRIVE, which,
795    when combined as ${HOMEDRIVE}${HOMEPATH}, give the unix equivalent
796    of HOME.  Some NT users are just too unixy, though, and set the
797    HOME variable themselves.  Therefore, we check for HOME first, and
798    then try to combine the other two if that fails.
799
800    Looking for HOME strikes me as bogus, particularly if the only reason
801    is to cater to "unixy users".  On the other hand, if the reasoning is
802    there should be a single variable, rather than requiring people to
803    set both HOMEDRIVE and HOMEPATH, then it starts to make a little more
804    sense.
805
806    Win95: The system doesn't set HOME, HOMEDRIVE, or HOMEPATH (at
807    least if you set it up as the "all users under one user ID" or
808    whatever the name of that option is).  Based on thing overheard on
809    the net, it seems that users of the pserver client have gotten in
810    the habit of setting HOME (if you don't use pserver, you can
811    probably get away without having a reasonable return from
812    get_homedir.  Of course you lose .cvsrc and .cvsignore, but many
813    users won't notice).  So it would seem that we should be somewhat
814    careful if we try to change the current behavior.
815
816    NT 3.51 or NT 4.0: I haven't checked this myself, but I am told
817    that HOME gets set, but not to the user's home directory.  It is
818    said to be set to c:\users\default by default.  */
819
820 char *
821 get_homedir (void)
822 {
823     char *homedir;
824
825     homedir = getenv ("HOME");
826
827     if (homedir == NULL)
828         homedir = woe32_home_dir ();
829
830     return homedir;
831 }
832
833 /* Compose a path to a file in the home directory.  This is necessary because
834  * of different behavior on UNIX, Windows, and VMS.  See more notes in
835  * vms/filesubr.c.
836  *
837  * A more clean solution would be something more along the lines of a
838  * "join a directory to a filename" kind of thing which was not specific to
839  * the homedir.  This should aid portability between UNIX, Mac, Windows, VMS,
840  * and possibly others.  This is already handled by Perl - it might be
841  * interesting to see how much of the code was written in C since Perl is under
842  * the GPL and the Artistic license - we might be able to use it.
843  */
844 char *
845 strcat_filename_onto_homedir (dir, file)
846     const char *dir;
847     const char *file;
848 {
849     char *path = xmalloc (strlen (dir) + 1 + strlen(file) + 1);
850     sprintf (path, "%s\\%s", dir, file);
851     return path;
852 }
853
854 /* See cvs.h for description.  */
855 void
856 expand_wild (argc, argv, pargc, pargv)
857     int argc;
858     char **argv;
859     int *pargc;
860     char ***pargv;
861 {
862     int i;
863     int new_argc;
864     char **new_argv;
865     /* Allocated size of new_argv.  We arrange it so there is always room for
866            one more element.  */
867     int max_new_argc;
868
869     new_argc = 0;
870     /* Add one so this is never zero.  */
871     max_new_argc = argc + 1;
872     new_argv = (char **) xmalloc (max_new_argc * sizeof (char *));
873     for (i = 0; i < argc; ++i)
874     {
875         HANDLE h;
876         WIN32_FIND_DATA fdata;
877
878         /* These variables help us extract the directory name from the
879            given pathname. */
880
881         char *last_forw_slash, *last_back_slash, *end_of_dirname;
882         int dirname_length = 0;
883
884         if ( strcmp( argv[i], "." ) == 0 )
885         {
886             new_argv[new_argc] = (char *) xmalloc ( 2 );
887             strcpy( new_argv[ new_argc++ ], "." );
888             continue;
889         }
890
891         /* FindFirstFile doesn't return pathnames, so we have to do
892            this ourselves.  Luckily, it's no big deal, since globbing
893            characters under Win32s can only occur in the last segment
894            of the path.  For example,
895                 /a/path/q*.h                      valid
896                 /w32/q*.dir/cant/do/this/q*.h     invalid */
897
898         /* Win32 can handle both forward and backward slashes as
899            filenames -- check for both. */
900              
901         last_forw_slash = strrchr (argv[i], '/');
902         last_back_slash = strrchr (argv[i], '\\');
903
904 #define cvs_max(x,y) ((x >= y) ? (x) : (y))
905
906         /* FIXME: this comparing a NULL pointer to a non-NULL one is
907            extremely ugly, and I strongly suspect *NOT* sanctioned by
908            ANSI C.  The code should just use last_component instead.  */
909         end_of_dirname = cvs_max (last_forw_slash, last_back_slash);
910
911         if (end_of_dirname == NULL)
912           dirname_length = 0;   /* no directory name */
913         else
914           dirname_length = end_of_dirname - argv[i] + 1; /* include slash */
915
916         h = FindFirstFile (argv[i], &fdata);
917         if (h == INVALID_HANDLE_VALUE)
918         {
919             if (GetLastError () == ENOENT)
920             {
921                 /* No match.  The file specified didn't contain a wildcard (in which case
922                    we clearly should return it unchanged), or it contained a wildcard which
923                    didn't match (in which case it might be better for it to be an error,
924                    but we don't try to do that).  */
925                 new_argv [new_argc++] = xstrdup (argv[i]);
926                 if (new_argc == max_new_argc)
927                 {
928                     max_new_argc *= 2;
929                     new_argv = xrealloc (new_argv, max_new_argc * sizeof (char *));
930                 }
931             }
932             else
933             {
934                 error (1, errno, "cannot find %s", argv[i]);
935             }
936         }
937         else
938         {
939             while (1)
940             {
941                 new_argv[new_argc] =
942                     (char *) xmalloc (strlen (fdata.cFileName) + 1
943                                       + dirname_length);
944
945                 /* Copy the directory name, if there is one. */
946
947                 if (dirname_length)
948                 {
949                     strncpy (new_argv[new_argc], argv[i], dirname_length);
950                     new_argv[new_argc][dirname_length] = '\0';
951                 }
952                 else
953                     new_argv[new_argc][0] = '\0';
954
955                 /* Copy the file name. */
956                 
957                 if (fncmp (argv[i] + dirname_length, fdata.cFileName) == 0)
958                     /* We didn't expand a wildcard; we just matched a filename.
959                        Use the file name as specified rather than the filename
960                        which exists in the directory (they may differ in case).
961                        This is needed to make cvs add on a directory consistently
962                        use the name specified on the command line, but it is
963                        probably a good idea in other contexts too.  */
964                     strcpy (new_argv[new_argc], argv[i]);
965                 else
966                     strcat (new_argv[new_argc], fdata.cFileName);
967
968                 new_argc++;
969
970                 if (new_argc == max_new_argc)
971                 {
972                     max_new_argc *= 2;
973                     new_argv = xrealloc (new_argv, max_new_argc * sizeof (char *));
974                 }
975                 if (!FindNextFile (h, &fdata))
976                 {
977                     if (GetLastError () == ERROR_NO_MORE_FILES)
978                         break;
979                     else
980                         error (1, errno, "cannot find %s", argv[i]);
981                 }
982             }
983             if (!FindClose (h))
984                 error (1, GetLastError (), "cannot close %s", argv[i]);
985         }
986     }
987     *pargc = new_argc;
988     *pargv = new_argv;
989 }
990
991 /* undo config.h stat macro */
992 #undef stat
993 extern int stat (const char *file, struct wnt_stat *sb);
994
995 /* see config.h stat macro */
996 int
997 wnt_stat (const char *file, struct wnt_stat *sb)
998 {
999     int retval;
1000
1001     retval = stat (file, sb);
1002     if (retval < 0)
1003                 return retval;
1004
1005         /* Win32 processes file times in a 64 bit format
1006        (see Win32 functions SetFileTime and GetFileTime).
1007        If the file time on a file doesn't fit into the
1008        32 bit time_t format, then stat will set that time
1009        to -1.  This would be OK, except that functions
1010        like ctime() don't check for validity.  So what we
1011        do here is to give a error on -1.  A cleaner solution
1012        might be to change CVS's interfaces to return a time
1013        in RCS format (for example), and then implement it
1014        on Win32 via GetFileTime, but that would be a lot of
1015        hair and I'm not sure there is much payoff.  */
1016     if (sb->st_mtime == (time_t) -1)
1017                 error (1, 0, "invalid modification time for %s", file);
1018     if (sb->st_ctime == (time_t) -1)
1019         /* I'm not sure what this means on windows.  It
1020            might be a creation time (unlike unix)....  */
1021                 error (1, 0, "invalid ctime for %s", file);
1022     if (sb->st_atime == (time_t) -1)
1023                 error (1, 0, "invalid access time for %s", file);
1024
1025     if (!GetUTCFileModTime (file, &sb->st_mtime))
1026                 error (1, 0, "Failed to retrieve modification time for %s", file);
1027
1028     return retval;
1029 }