Merge branch 'ef/msvc-noreturn'
[git] / compat / mkstemps.c
1 #include "../git-compat-util.h"
2
3 /* Adapted from libiberty's mkstemp.c. */
4
5 #undef TMP_MAX
6 #define TMP_MAX 16384
7
8 int gitmkstemps(char *pattern, int suffix_len)
9 {
10         static const char letters[] =
11                 "abcdefghijklmnopqrstuvwxyz"
12                 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
13                 "0123456789";
14         static const int num_letters = 62;
15         uint64_t value;
16         struct timeval tv;
17         char *template;
18         size_t len;
19         int fd, count;
20
21         len = strlen(pattern);
22
23         if (len < 6 + suffix_len) {
24                 errno = EINVAL;
25                 return -1;
26         }
27
28         if (strncmp(&pattern[len - 6 - suffix_len], "XXXXXX", 6)) {
29                 errno = EINVAL;
30                 return -1;
31         }
32
33         /*
34          * Replace pattern's XXXXXX characters with randomness.
35          * Try TMP_MAX different filenames.
36          */
37         gettimeofday(&tv, NULL);
38         value = ((size_t)(tv.tv_usec << 16)) ^ tv.tv_sec ^ getpid();
39         template = &pattern[len - 6 - suffix_len];
40         for (count = 0; count < TMP_MAX; ++count) {
41                 uint64_t v = value;
42                 /* Fill in the random bits. */
43                 template[0] = letters[v % num_letters]; v /= num_letters;
44                 template[1] = letters[v % num_letters]; v /= num_letters;
45                 template[2] = letters[v % num_letters]; v /= num_letters;
46                 template[3] = letters[v % num_letters]; v /= num_letters;
47                 template[4] = letters[v % num_letters]; v /= num_letters;
48                 template[5] = letters[v % num_letters]; v /= num_letters;
49
50                 fd = open(pattern, O_CREAT | O_EXCL | O_RDWR, 0600);
51                 if (fd > 0)
52                         return fd;
53                 /*
54                  * Fatal error (EPERM, ENOSPC etc).
55                  * It doesn't make sense to loop.
56                  */
57                 if (errno != EEXIST)
58                         break;
59                 /*
60                  * This is a random value.  It is only necessary that
61                  * the next TMP_MAX values generated by adding 7777 to
62                  * VALUE are different with (module 2^32).
63                  */
64                 value += 7777;
65         }
66         /* We return the null string if we can't find a unique file name.  */
67         pattern[0] = '\0';
68         errno = EINVAL;
69         return -1;
70 }