1/***********************************************************************
2*                                                                      *
3*               This software is part of the ast package               *
4*          Copyright (c) 1985-2011 AT&T Intellectual Property          *
5*                      and is licensed under the                       *
6*                  Common Public License, Version 1.0                  *
7*                    by AT&T Intellectual Property                     *
8*                                                                      *
9*                A copy of the License is available at                 *
10*            http://www.opensource.org/licenses/cpl1.0.txt             *
11*         (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9)         *
12*                                                                      *
13*              Information and Software Systems Research               *
14*                            AT&T Research                             *
15*                           Florham Park NJ                            *
16*                                                                      *
17*                 Glenn Fowler <gsf@research.att.com>                  *
18*                  David Korn <dgk@research.att.com>                   *
19*                   Phong Vo <kpv@research.att.com>                    *
20*                                                                      *
21***********************************************************************/
22#pragma prototyped
23/*
24 * AT&T Research
25 *
26 * generate a temp file / name
27 *
28 *	[<dir>/][<pfx>]<bas>.<suf>
29 *
30 * length(<pfx>)<=5
31 * length(<bas>)==3
32 * length(<suf>)==3
33 *
34 *	pathtmp(a,b,c,d)	pathtemp(a,L_tmpnam,b,c,0)
35 *	tmpfile()		char*p=pathtemp(0,0,0,"tf",&sp);
36 *				remove(p);
37 *				free(p)
38 *	tmpnam(0)		static char p[L_tmpnam];
39 *				pathtemp(p,sizeof(p),0,"tn",0)
40 *	tmpnam(p)		pathtemp(p,L_tmpnam,0,"tn",0)
41 *	tempnam(d,p)		pathtemp(0,d,p,0)
42 *	mktemp(p)		pathtemp(0,0,p,0)
43 *
44 * if buf==0 then space is malloc'd
45 * buf size is size
46 * dir and pfx may be 0
47 * if pfx contains trailing X's then it is a mktemp(3) template
48 * otherwise only first 5 chars of pfx are used
49 * if fdp!=0 then the path is opened O_EXCL and *fdp is the open fd
50 * malloc'd space returned by successful pathtemp() calls
51 * must be freed by the caller
52 *
53 * generated names are pseudo-randomized to avoid both
54 * collisions and predictions (same alg in sfio/sftmp.c)
55 *
56 * / as first pfx char provides tmp file generation control
57 * 0 returned for unknown ops
58 *
59 *	/cycle		dir specifies TMPPATH cycle control
60 *		automatic	(default) cycled with each tmp file
61 *		manual		cycled by application with dir=(nil)
62 *		(nil)		cycle TMPPATH
63 *	/prefix		dir specifies the default prefix (default ast)
64 *	/private	private file/dir modes
65 *	/public		public file/dir modes
66 *	/seed		dir specifies pseudo-random generator seed
67 *			0 or "0" to re-initialize
68 *	/TMPPATH	dir overrides the env value
69 *	/TMPDIR		dir overrides the env value
70 */
71
72#include <ast.h>
73#include <ls.h>
74#include <tv.h>
75#include <tm.h>
76
77#define ATTEMPT		10
78
79#define TMP_ENV		"TMPDIR"
80#define TMP_PATH_ENV	"TMPPATH"
81#define TMP1		"/tmp"
82#define TMP2		"/usr/tmp"
83
84#define VALID(d)	(*(d)&&!eaccess(d,W_OK|X_OK))
85
86static struct
87{
88	mode_t		mode;
89	char**		vec;
90	char**		dir;
91	uint32_t	key;
92	uint32_t	rng;
93	pid_t		pid;
94	int		manual;
95	int		seed;
96	char*		pfx;
97	char*		tmpdir;
98	char*		tmppath;
99} tmp = { S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH };
100
101char*
102pathtemp(char* buf, size_t len, const char* dir, const char* pfx, int* fdp)
103{
104	register char*		d;
105	register char*		b;
106	register char*		s;
107	register char*		x;
108	uint32_t		key;
109	int			m;
110	int			n;
111	int			l;
112	int			r;
113	int			z;
114	int			attempt;
115	Tv_t			tv;
116	char			keybuf[16];
117
118	if (pfx && *pfx == '/')
119	{
120		pfx++;
121		if (streq(pfx, "cycle"))
122		{
123			if (!dir)
124			{
125				tmp.manual = 1;
126				if (tmp.dir && !*tmp.dir++)
127					tmp.dir = tmp.vec;
128			}
129			else
130				tmp.manual = streq(dir, "manual");
131			return (char*)pfx;
132		}
133		else if (streq(pfx, "prefix"))
134		{
135			if (tmp.pfx)
136				free(tmp.pfx);
137			tmp.pfx = dir ? strdup(dir) : (char*)0;
138			return (char*)pfx;
139		}
140		else if (streq(pfx, "private"))
141		{
142			tmp.mode = S_IRUSR|S_IWUSR;
143			return (char*)pfx;
144		}
145		else if (streq(pfx, "public"))
146		{
147			tmp.mode = S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH;
148			return (char*)pfx;
149		}
150		else if (streq(pfx, "seed"))
151		{
152			tmp.key = (tmp.seed = (tmp.rng = dir ? (uint32_t)strtoul(dir, NiL, 0) : (uint32_t)1) != 0)? (uint32_t)0x63c63cd9L : 0;
153			return (char*)pfx;
154		}
155		else if (streq(pfx, TMP_ENV))
156		{
157			if (tmp.vec)
158			{
159				free(tmp.vec);
160				tmp.vec = 0;
161			}
162			if (tmp.tmpdir)
163				free(tmp.tmpdir);
164			tmp.tmpdir = dir ? strdup(dir) : (char*)0;
165			return (char*)pfx;
166		}
167		else if (streq(pfx, TMP_PATH_ENV))
168		{
169			if (tmp.vec)
170			{
171				free(tmp.vec);
172				tmp.vec = 0;
173			}
174			if (tmp.tmppath)
175				free(tmp.tmppath);
176			tmp.tmppath = dir ? strdup(dir) : (char*)0;
177			return (char*)pfx;
178		}
179		return 0;
180	}
181	if (tmp.seed)
182		tv.tv_nsec = 0;
183	else
184		tvgettime(&tv);
185	if (!(d = (char*)dir) || *d && eaccess(d, W_OK|X_OK))
186	{
187		if (!tmp.vec)
188		{
189			if ((x = tmp.tmppath) || (x = getenv(TMP_PATH_ENV)))
190			{
191				n = 2;
192				s = x;
193				while (s = strchr(s, ':'))
194				{
195					s++;
196					n++;
197				}
198				if (!(tmp.vec = newof(0, char*, n, strlen(x) + 1)))
199					return 0;
200				tmp.dir = tmp.vec;
201				x = strcpy((char*)(tmp.dir + n), x);
202				*tmp.dir++ = x;
203				while (x = strchr(x, ':'))
204				{
205					*x++ = 0;
206					if (!VALID(*(tmp.dir - 1)))
207						tmp.dir--;
208					*tmp.dir++ = x;
209				}
210				if (!VALID(*(tmp.dir - 1)))
211					tmp.dir--;
212				*tmp.dir = 0;
213			}
214			else
215			{
216				if (((d = tmp.tmpdir) || (d = getenv(TMP_ENV))) && !VALID(d))
217					d = 0;
218				if (!(tmp.vec = newof(0, char*, 2, d ? (strlen(d) + 1) : 0)))
219					return 0;
220				if (d)
221					*tmp.vec = strcpy((char*)(tmp.vec + 2), d);
222			}
223			tmp.dir = tmp.vec;
224		}
225		if (!(d = *tmp.dir++))
226		{
227			tmp.dir = tmp.vec;
228			d = *tmp.dir++;
229		}
230		if (!d && (!*(d = astconf("TMP", NiL, NiL)) || eaccess(d, W_OK|X_OK)) && eaccess(d = TMP1, W_OK|X_OK) && eaccess(d = TMP2, W_OK|X_OK))
231			return 0;
232	}
233	if (!len)
234		len = PATH_MAX;
235	len--;
236	if (!(b = buf) && !(b = newof(0, char, len, 1)))
237		return 0;
238	z = 0;
239	if (!pfx && !(pfx = tmp.pfx))
240		pfx = "ast";
241	m = strlen(pfx);
242	if (buf && dir && (buf == (char*)dir && (buf + strlen(buf) + 1) == (char*)pfx || buf == (char*)pfx && !*dir) && !strcmp((char*)pfx + m + 1, "XXXXX"))
243	{
244		d = (char*)dir;
245		len = m += strlen(d) + 8;
246		l = 3;
247		r = 3;
248	}
249	else if (*pfx && pfx[m - 1] == 'X')
250	{
251		for (l = m; l && pfx[l - 1] == 'X'; l--);
252		r = m - l;
253		m = l;
254		l = r / 2;
255		r -= l;
256	}
257	else if (strchr(pfx, '.'))
258	{
259		m = 5;
260		l = 3;
261		r = 3;
262	}
263	else
264	{
265		z = '.';
266		m = 5;
267		l = 2;
268		r = 3;
269	}
270	x = b + len;
271	s = b;
272	if (d)
273	{
274		while (s < x && (n = *d++))
275			*s++ = n;
276		if (s < x && s > b && *(s - 1) != '/')
277			*s++ = '/';
278	}
279	if ((x - s) > m)
280		x = s + m;
281	while (s < x && (n = *pfx++))
282	{
283		if (n == '/' || n == '\\' || n == z)
284			n = '_';
285		*s++ = n;
286	}
287	*s = 0;
288	len -= (s - b);
289	for (attempt = 0; attempt < ATTEMPT; attempt++)
290	{
291		if (!tmp.rng || !tmp.seed && (attempt || tmp.pid != getpid()))
292		{
293			register int	r;
294
295			/*
296			 * get a quasi-random coefficient
297			 */
298
299			tmp.pid = getpid();
300			tmp.rng = (uint32_t)tmp.pid * ((uint32_t)time(NiL) ^ (((uint32_t)integralof(&attempt)) >> 3) ^ (((uint32_t)integralof(tmp.dir)) >> 3));
301			if (!tmp.key)
302				tmp.key = (tmp.rng >> 16) | ((tmp.rng & 0xffff) << 16);
303			tmp.rng ^= tmp.key;
304
305			/*
306			 * Knuth vol.2, page.16, Thm.A
307			 */
308
309			if ((r = (tmp.rng - 1) & 03))
310				tmp.rng += 4 - r;
311		}
312
313		/*
314		 * generate a pseudo-random name
315		 */
316
317		key = tmp.rng * tmp.key + tv.tv_nsec;
318		if (!tmp.seed)
319			tvgettime(&tv);
320		tmp.key = tmp.rng * key + tv.tv_nsec;
321		sfsprintf(keybuf, sizeof(keybuf), "%07.7.32I*u%07.7.32I*u", sizeof(key), key, sizeof(tmp.key), tmp.key);
322		sfsprintf(s, len, "%-.*s%s%-.*s", l, keybuf, z ? "." : "", r, keybuf + sizeof(keybuf) / 2);
323		if (fdp)
324		{
325			if ((n = open(b, O_CREAT|O_RDWR|O_EXCL|O_TEMPORARY, tmp.mode)) >= 0)
326			{
327				*fdp = n;
328				return b;
329			}
330		}
331		else if (access(b, F_OK))
332			return b;
333	}
334	if (!buf)
335		free(b);
336	return 0;
337}
338