1/* Copyright (C) 2001, 2003, 2004 Free Software Foundation, Inc. 2 Written by Werner Lemberg (wl@gnu.org) 3 4This file is part of groff. 5 6groff is free software; you can redistribute it and/or modify it under 7the terms of the GNU General Public License as published by the Free 8Software Foundation; either version 2, or (at your option) any later 9version. 10 11groff is distributed in the hope that it will be useful, but WITHOUT ANY 12WARRANTY; without even the implied warranty of MERCHANTABILITY or 13FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14for more details. 15 16You should have received a copy of the GNU General Public License along 17with groff; see the file COPYING. If not, write to the Free Software 18Foundation, 51 Franklin St - Fifth Floor, Boston, MA 02110-1301, USA. */ 19 20 21/* This file is heavily based on the function __gen_tempname() in the 22 file tempname.c which is part of the fileutils package. */ 23 24 25#include "lib.h" 26 27#include <stddef.h> 28#include <stdlib.h> 29#include <errno.h> 30#include <time.h> 31 32#include "posix.h" 33#include "nonposix.h" 34 35#ifndef TMP_MAX 36# define TMP_MAX 238328 37#endif 38 39#if HAVE_SYS_TIME_H 40# include <sys/time.h> 41#endif 42 43#ifdef HAVE_GETTIMEOFDAY 44#ifdef NEED_DECLARATION_GETTIMEOFDAY 45extern "C" { 46 int gettimeofday(struct timeval *, void *); 47} 48#endif 49#endif 50 51#if HAVE_CC_INTTYPES_H 52# include <inttypes.h> 53#endif 54 55/* Use the widest available unsigned type if uint64_t is not 56 available. The algorithm below extracts a number less than 62**6 57 (approximately 2**35.725) from uint64_t, so ancient hosts where 58 uintmax_t is only 32 bits lose about 3.725 bits of randomness, 59 which is better than not having mkstemp at all. */ 60#if !defined UINT64_MAX && !defined uint64_t 61# define uint64_t uintmax_t 62#endif 63 64/* These are the characters used in temporary filenames. */ 65static const char letters[] = 66"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; 67 68int gen_tempname(char *tmpl, int dir) 69{ 70 static uint64_t value; 71 72 size_t len = strlen(tmpl); 73 if (len < 6 || strcmp(&tmpl[len - 6], "XXXXXX")) 74 return -1; /* EINVAL */ 75 76 /* This is where the Xs start. */ 77 char *XXXXXX = &tmpl[len - 6]; 78 79 /* Get some more or less random data. */ 80#if HAVE_GETTIMEOFDAY 81 timeval tv; 82 gettimeofday(&tv, NULL); 83 uint64_t random_time_bits = ((uint64_t)tv.tv_usec << 16) ^ tv.tv_sec; 84#else 85 uint64_t random_time_bits = time(NULL); 86#endif 87 value += random_time_bits ^ getpid(); 88 89 for (int count = 0; count < TMP_MAX; value += 7777, ++count) { 90 uint64_t v = value; 91 92 /* Fill in the random bits. */ 93 XXXXXX[0] = letters[v % 62]; 94 v /= 62; 95 XXXXXX[1] = letters[v % 62]; 96 v /= 62; 97 XXXXXX[2] = letters[v % 62]; 98 v /= 62; 99 XXXXXX[3] = letters[v % 62]; 100 v /= 62; 101 XXXXXX[4] = letters[v % 62]; 102 v /= 62; 103 XXXXXX[5] = letters[v % 62]; 104 105 int fd = dir ? mkdir(tmpl, S_IRUSR | S_IWUSR | S_IXUSR) 106 : open(tmpl, O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR); 107 108 if (fd >= 0) 109 return fd; 110 else if (errno != EEXIST) 111 return -1; 112 } 113 114 /* We got out of the loop because we ran out of combinations to try. */ 115 return -1; /* EEXIST */ 116} 117