1da2e3ebdSchin /***********************************************************************
2da2e3ebdSchin *                                                                      *
3da2e3ebdSchin *               This software is part of the ast package               *
4*b30d1939SAndy Fiddaman *          Copyright (c) 1985-2011 AT&T Intellectual Property          *
5da2e3ebdSchin *                      and is licensed under the                       *
6*b30d1939SAndy Fiddaman *                 Eclipse Public License, Version 1.0                  *
77c2fbfb3SApril Chin *                    by AT&T Intellectual Property                     *
8da2e3ebdSchin *                                                                      *
9da2e3ebdSchin *                A copy of the License is available at                 *
10*b30d1939SAndy Fiddaman *          http://www.eclipse.org/org/documents/epl-v10.html           *
11*b30d1939SAndy Fiddaman *         (with md5 checksum b35adb5213ca9657e911e9befb180842)         *
12da2e3ebdSchin *                                                                      *
13da2e3ebdSchin *              Information and Software Systems Research               *
14da2e3ebdSchin *                            AT&T Research                             *
15da2e3ebdSchin *                           Florham Park NJ                            *
16da2e3ebdSchin *                                                                      *
17da2e3ebdSchin *                 Glenn Fowler <gsf@research.att.com>                  *
18da2e3ebdSchin *                  David Korn <dgk@research.att.com>                   *
19da2e3ebdSchin *                   Phong Vo <kpv@research.att.com>                    *
20da2e3ebdSchin *                                                                      *
21da2e3ebdSchin ***********************************************************************/
22da2e3ebdSchin #pragma prototyped
23da2e3ebdSchin /*
24da2e3ebdSchin  * AT&T Research
25da2e3ebdSchin  *
26da2e3ebdSchin  * generate a temp file / name
27da2e3ebdSchin  *
28da2e3ebdSchin  *	[<dir>/][<pfx>]<bas>.<suf>
29da2e3ebdSchin  *
30da2e3ebdSchin  * length(<pfx>)<=5
31da2e3ebdSchin  * length(<bas>)==3
32da2e3ebdSchin  * length(<suf>)==3
33da2e3ebdSchin  *
34da2e3ebdSchin  *	pathtmp(a,b,c,d)	pathtemp(a,L_tmpnam,b,c,0)
35da2e3ebdSchin  *	tmpfile()		char*p=pathtemp(0,0,0,"tf",&sp);
36da2e3ebdSchin  *				remove(p);
37da2e3ebdSchin  *				free(p)
38da2e3ebdSchin  *	tmpnam(0)		static char p[L_tmpnam];
39da2e3ebdSchin  *				pathtemp(p,sizeof(p),0,"tn",0)
40da2e3ebdSchin  *	tmpnam(p)		pathtemp(p,L_tmpnam,0,"tn",0)
41da2e3ebdSchin  *	tempnam(d,p)		pathtemp(0,d,p,0)
423e14f97fSRoger A. Faulkner  *	mktemp(p)		pathtemp(0,0,p,0)
43da2e3ebdSchin  *
44da2e3ebdSchin  * if buf==0 then space is malloc'd
45da2e3ebdSchin  * buf size is size
46da2e3ebdSchin  * dir and pfx may be 0
473e14f97fSRoger A. Faulkner  * if pfx contains trailing X's then it is a mktemp(3) template
483e14f97fSRoger A. Faulkner  * otherwise only first 5 chars of pfx are used
49da2e3ebdSchin  * if fdp!=0 then the path is opened O_EXCL and *fdp is the open fd
50da2e3ebdSchin  * malloc'd space returned by successful pathtemp() calls
51da2e3ebdSchin  * must be freed by the caller
52da2e3ebdSchin  *
53da2e3ebdSchin  * generated names are pseudo-randomized to avoid both
54da2e3ebdSchin  * collisions and predictions (same alg in sfio/sftmp.c)
55da2e3ebdSchin  *
56da2e3ebdSchin  * / as first pfx char provides tmp file generation control
57da2e3ebdSchin  * 0 returned for unknown ops
58da2e3ebdSchin  *
59da2e3ebdSchin  *	/cycle		dir specifies TMPPATH cycle control
60da2e3ebdSchin  *		automatic	(default) cycled with each tmp file
61da2e3ebdSchin  *		manual		cycled by application with dir=(nil)
62da2e3ebdSchin  *		(nil)		cycle TMPPATH
63da2e3ebdSchin  *	/prefix		dir specifies the default prefix (default ast)
643e14f97fSRoger A. Faulkner  *	/private	private file/dir modes
653e14f97fSRoger A. Faulkner  *	/public		public file/dir modes
663e14f97fSRoger A. Faulkner  *	/seed		dir specifies pseudo-random generator seed
673e14f97fSRoger A. Faulkner  *			0 or "0" to re-initialize
68da2e3ebdSchin  *	/TMPPATH	dir overrides the env value
69da2e3ebdSchin  *	/TMPDIR		dir overrides the env value
70da2e3ebdSchin  */
71da2e3ebdSchin 
72da2e3ebdSchin #include <ast.h>
73da2e3ebdSchin #include <ls.h>
743e14f97fSRoger A. Faulkner #include <tv.h>
75da2e3ebdSchin #include <tm.h>
76da2e3ebdSchin 
77da2e3ebdSchin #define ATTEMPT		10
78da2e3ebdSchin 
79da2e3ebdSchin #define TMP_ENV		"TMPDIR"
80da2e3ebdSchin #define TMP_PATH_ENV	"TMPPATH"
81da2e3ebdSchin #define TMP1		"/tmp"
82da2e3ebdSchin #define TMP2		"/usr/tmp"
83da2e3ebdSchin 
84da2e3ebdSchin #define VALID(d)	(*(d)&&!eaccess(d,W_OK|X_OK))
85da2e3ebdSchin 
86da2e3ebdSchin static struct
87da2e3ebdSchin {
88da2e3ebdSchin 	mode_t		mode;
89da2e3ebdSchin 	char**		vec;
90da2e3ebdSchin 	char**		dir;
913e14f97fSRoger A. Faulkner 	uint32_t	key;
923e14f97fSRoger A. Faulkner 	uint32_t	rng;
93da2e3ebdSchin 	pid_t		pid;
94da2e3ebdSchin 	int		manual;
953e14f97fSRoger A. Faulkner 	int		seed;
96da2e3ebdSchin 	char*		pfx;
97da2e3ebdSchin 	char*		tmpdir;
98da2e3ebdSchin 	char*		tmppath;
99da2e3ebdSchin } tmp = { S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH };
100da2e3ebdSchin 
101da2e3ebdSchin char*
pathtemp(char * buf,size_t len,const char * dir,const char * pfx,int * fdp)102da2e3ebdSchin pathtemp(char* buf, size_t len, const char* dir, const char* pfx, int* fdp)
103da2e3ebdSchin {
104da2e3ebdSchin 	register char*		d;
105da2e3ebdSchin 	register char*		b;
106da2e3ebdSchin 	register char*		s;
107da2e3ebdSchin 	register char*		x;
1083e14f97fSRoger A. Faulkner 	uint32_t		key;
109da2e3ebdSchin 	int			m;
110da2e3ebdSchin 	int			n;
1113e14f97fSRoger A. Faulkner 	int			l;
1123e14f97fSRoger A. Faulkner 	int			r;
113da2e3ebdSchin 	int			z;
114da2e3ebdSchin 	int			attempt;
1153e14f97fSRoger A. Faulkner 	Tv_t			tv;
1163e14f97fSRoger A. Faulkner 	char			keybuf[16];
117da2e3ebdSchin 
118da2e3ebdSchin 	if (pfx && *pfx == '/')
119da2e3ebdSchin 	{
120da2e3ebdSchin 		pfx++;
121da2e3ebdSchin 		if (streq(pfx, "cycle"))
122da2e3ebdSchin 		{
123da2e3ebdSchin 			if (!dir)
124da2e3ebdSchin 			{
125da2e3ebdSchin 				tmp.manual = 1;
126da2e3ebdSchin 				if (tmp.dir && !*tmp.dir++)
127da2e3ebdSchin 					tmp.dir = tmp.vec;
128da2e3ebdSchin 			}
129da2e3ebdSchin 			else
130da2e3ebdSchin 				tmp.manual = streq(dir, "manual");
131da2e3ebdSchin 			return (char*)pfx;
132da2e3ebdSchin 		}
133da2e3ebdSchin 		else if (streq(pfx, "prefix"))
134da2e3ebdSchin 		{
135da2e3ebdSchin 			if (tmp.pfx)
136da2e3ebdSchin 				free(tmp.pfx);
137da2e3ebdSchin 			tmp.pfx = dir ? strdup(dir) : (char*)0;
138da2e3ebdSchin 			return (char*)pfx;
139da2e3ebdSchin 		}
140da2e3ebdSchin 		else if (streq(pfx, "private"))
1413e14f97fSRoger A. Faulkner 		{
142da2e3ebdSchin 			tmp.mode = S_IRUSR|S_IWUSR;
1433e14f97fSRoger A. Faulkner 			return (char*)pfx;
1443e14f97fSRoger A. Faulkner 		}
145da2e3ebdSchin 		else if (streq(pfx, "public"))
1463e14f97fSRoger A. Faulkner 		{
147da2e3ebdSchin 			tmp.mode = S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH;
1483e14f97fSRoger A. Faulkner 			return (char*)pfx;
1493e14f97fSRoger A. Faulkner 		}
1503e14f97fSRoger A. Faulkner 		else if (streq(pfx, "seed"))
1513e14f97fSRoger A. Faulkner 		{
1523e14f97fSRoger A. Faulkner 			tmp.key = (tmp.seed = (tmp.rng = dir ? (uint32_t)strtoul(dir, NiL, 0) : (uint32_t)1) != 0)? (uint32_t)0x63c63cd9L : 0;
1533e14f97fSRoger A. Faulkner 			return (char*)pfx;
1543e14f97fSRoger A. Faulkner 		}
155da2e3ebdSchin 		else if (streq(pfx, TMP_ENV))
156da2e3ebdSchin 		{
157da2e3ebdSchin 			if (tmp.vec)
158da2e3ebdSchin 			{
159da2e3ebdSchin 				free(tmp.vec);
160da2e3ebdSchin 				tmp.vec = 0;
161da2e3ebdSchin 			}
162da2e3ebdSchin 			if (tmp.tmpdir)
163da2e3ebdSchin 				free(tmp.tmpdir);
164da2e3ebdSchin 			tmp.tmpdir = dir ? strdup(dir) : (char*)0;
165da2e3ebdSchin 			return (char*)pfx;
166da2e3ebdSchin 		}
167da2e3ebdSchin 		else if (streq(pfx, TMP_PATH_ENV))
168da2e3ebdSchin 		{
169da2e3ebdSchin 			if (tmp.vec)
170da2e3ebdSchin 			{
171da2e3ebdSchin 				free(tmp.vec);
172da2e3ebdSchin 				tmp.vec = 0;
173da2e3ebdSchin 			}
174da2e3ebdSchin 			if (tmp.tmppath)
175da2e3ebdSchin 				free(tmp.tmppath);
176da2e3ebdSchin 			tmp.tmppath = dir ? strdup(dir) : (char*)0;
177da2e3ebdSchin 			return (char*)pfx;
178da2e3ebdSchin 		}
179da2e3ebdSchin 		return 0;
180da2e3ebdSchin 	}
1813e14f97fSRoger A. Faulkner 	if (tmp.seed)
1823e14f97fSRoger A. Faulkner 		tv.tv_nsec = 0;
1833e14f97fSRoger A. Faulkner 	else
1843e14f97fSRoger A. Faulkner 		tvgettime(&tv);
185da2e3ebdSchin 	if (!(d = (char*)dir) || *d && eaccess(d, W_OK|X_OK))
186da2e3ebdSchin 	{
187da2e3ebdSchin 		if (!tmp.vec)
188da2e3ebdSchin 		{
189da2e3ebdSchin 			if ((x = tmp.tmppath) || (x = getenv(TMP_PATH_ENV)))
190da2e3ebdSchin 			{
191da2e3ebdSchin 				n = 2;
192da2e3ebdSchin 				s = x;
193da2e3ebdSchin 				while (s = strchr(s, ':'))
194da2e3ebdSchin 				{
195da2e3ebdSchin 					s++;
196da2e3ebdSchin 					n++;
197da2e3ebdSchin 				}
198da2e3ebdSchin 				if (!(tmp.vec = newof(0, char*, n, strlen(x) + 1)))
199da2e3ebdSchin 					return 0;
200da2e3ebdSchin 				tmp.dir = tmp.vec;
201da2e3ebdSchin 				x = strcpy((char*)(tmp.dir + n), x);
202da2e3ebdSchin 				*tmp.dir++ = x;
203da2e3ebdSchin 				while (x = strchr(x, ':'))
204da2e3ebdSchin 				{
205da2e3ebdSchin 					*x++ = 0;
206da2e3ebdSchin 					if (!VALID(*(tmp.dir - 1)))
207da2e3ebdSchin 						tmp.dir--;
208da2e3ebdSchin 					*tmp.dir++ = x;
209da2e3ebdSchin 				}
210da2e3ebdSchin 				if (!VALID(*(tmp.dir - 1)))
211da2e3ebdSchin 					tmp.dir--;
212da2e3ebdSchin 				*tmp.dir = 0;
213da2e3ebdSchin 			}
214da2e3ebdSchin 			else
215da2e3ebdSchin 			{
216da2e3ebdSchin 				if (((d = tmp.tmpdir) || (d = getenv(TMP_ENV))) && !VALID(d))
217da2e3ebdSchin 					d = 0;
218da2e3ebdSchin 				if (!(tmp.vec = newof(0, char*, 2, d ? (strlen(d) + 1) : 0)))
219da2e3ebdSchin 					return 0;
220da2e3ebdSchin 				if (d)
221da2e3ebdSchin 					*tmp.vec = strcpy((char*)(tmp.vec + 2), d);
222da2e3ebdSchin 			}
223da2e3ebdSchin 			tmp.dir = tmp.vec;
224da2e3ebdSchin 		}
225da2e3ebdSchin 		if (!(d = *tmp.dir++))
226da2e3ebdSchin 		{
227da2e3ebdSchin 			tmp.dir = tmp.vec;
228da2e3ebdSchin 			d = *tmp.dir++;
229da2e3ebdSchin 		}
230da2e3ebdSchin 		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))
231da2e3ebdSchin 			return 0;
232da2e3ebdSchin 	}
233da2e3ebdSchin 	if (!len)
234da2e3ebdSchin 		len = PATH_MAX;
235da2e3ebdSchin 	len--;
236da2e3ebdSchin 	if (!(b = buf) && !(b = newof(0, char, len, 1)))
237da2e3ebdSchin 		return 0;
2383e14f97fSRoger A. Faulkner 	z = 0;
2393e14f97fSRoger A. Faulkner 	if (!pfx && !(pfx = tmp.pfx))
2403e14f97fSRoger A. Faulkner 		pfx = "ast";
2413e14f97fSRoger A. Faulkner 	m = strlen(pfx);
2423e14f97fSRoger A. Faulkner 	if (buf && dir && (buf == (char*)dir && (buf + strlen(buf) + 1) == (char*)pfx || buf == (char*)pfx && !*dir) && !strcmp((char*)pfx + m + 1, "XXXXX"))
243da2e3ebdSchin 	{
244da2e3ebdSchin 		d = (char*)dir;
2453e14f97fSRoger A. Faulkner 		len = m += strlen(d) + 8;
2463e14f97fSRoger A. Faulkner 		l = 3;
2473e14f97fSRoger A. Faulkner 		r = 3;
2483e14f97fSRoger A. Faulkner 	}
2493e14f97fSRoger A. Faulkner 	else if (*pfx && pfx[m - 1] == 'X')
2503e14f97fSRoger A. Faulkner 	{
2513e14f97fSRoger A. Faulkner 		for (l = m; l && pfx[l - 1] == 'X'; l--);
2523e14f97fSRoger A. Faulkner 		r = m - l;
2533e14f97fSRoger A. Faulkner 		m = l;
2543e14f97fSRoger A. Faulkner 		l = r / 2;
2553e14f97fSRoger A. Faulkner 		r -= l;
2563e14f97fSRoger A. Faulkner 	}
2573e14f97fSRoger A. Faulkner 	else if (strchr(pfx, '.'))
2583e14f97fSRoger A. Faulkner 	{
2593e14f97fSRoger A. Faulkner 		m = 5;
2603e14f97fSRoger A. Faulkner 		l = 3;
2613e14f97fSRoger A. Faulkner 		r = 3;
262da2e3ebdSchin 	}
263da2e3ebdSchin 	else
264da2e3ebdSchin 	{
265da2e3ebdSchin 		z = '.';
266da2e3ebdSchin 		m = 5;
2673e14f97fSRoger A. Faulkner 		l = 2;
2683e14f97fSRoger A. Faulkner 		r = 3;
269da2e3ebdSchin 	}
270da2e3ebdSchin 	x = b + len;
271da2e3ebdSchin 	s = b;
272da2e3ebdSchin 	if (d)
273da2e3ebdSchin 	{
274da2e3ebdSchin 		while (s < x && (n = *d++))
275da2e3ebdSchin 			*s++ = n;
276da2e3ebdSchin 		if (s < x && s > b && *(s - 1) != '/')
277da2e3ebdSchin 			*s++ = '/';
278da2e3ebdSchin 	}
279da2e3ebdSchin 	if ((x - s) > m)
280da2e3ebdSchin 		x = s + m;
281da2e3ebdSchin 	while (s < x && (n = *pfx++))
282da2e3ebdSchin 	{
283da2e3ebdSchin 		if (n == '/' || n == '\\' || n == z)
284da2e3ebdSchin 			n = '_';
285da2e3ebdSchin 		*s++ = n;
286da2e3ebdSchin 	}
287da2e3ebdSchin 	*s = 0;
288da2e3ebdSchin 	len -= (s - b);
289da2e3ebdSchin 	for (attempt = 0; attempt < ATTEMPT; attempt++)
290da2e3ebdSchin 	{
2913e14f97fSRoger A. Faulkner 		if (!tmp.rng || !tmp.seed && (attempt || tmp.pid != getpid()))
292da2e3ebdSchin 		{
293da2e3ebdSchin 			register int	r;
294da2e3ebdSchin 
295da2e3ebdSchin 			/*
296da2e3ebdSchin 			 * get a quasi-random coefficient
297da2e3ebdSchin 			 */
298da2e3ebdSchin 
299da2e3ebdSchin 			tmp.pid = getpid();
300*b30d1939SAndy Fiddaman 			tmp.rng = (uint32_t)tmp.pid * ((uint32_t)time(NiL) ^ (((uint32_t)integralof(&attempt)) >> 3) ^ (((uint32_t)integralof(tmp.dir)) >> 3));
301da2e3ebdSchin 			if (!tmp.key)
302da2e3ebdSchin 				tmp.key = (tmp.rng >> 16) | ((tmp.rng & 0xffff) << 16);
303da2e3ebdSchin 			tmp.rng ^= tmp.key;
304da2e3ebdSchin 
305da2e3ebdSchin 			/*
306da2e3ebdSchin 			 * Knuth vol.2, page.16, Thm.A
307da2e3ebdSchin 			 */
308da2e3ebdSchin 
309da2e3ebdSchin 			if ((r = (tmp.rng - 1) & 03))
310da2e3ebdSchin 				tmp.rng += 4 - r;
311da2e3ebdSchin 		}
312da2e3ebdSchin 
313da2e3ebdSchin 		/*
314da2e3ebdSchin 		 * generate a pseudo-random name
315da2e3ebdSchin 		 */
316da2e3ebdSchin 
3173e14f97fSRoger A. Faulkner 		key = tmp.rng * tmp.key + tv.tv_nsec;
3183e14f97fSRoger A. Faulkner 		if (!tmp.seed)
3193e14f97fSRoger A. Faulkner 			tvgettime(&tv);
3203e14f97fSRoger A. Faulkner 		tmp.key = tmp.rng * key + tv.tv_nsec;
3213e14f97fSRoger A. Faulkner 		sfsprintf(keybuf, sizeof(keybuf), "%07.7.32I*u%07.7.32I*u", sizeof(key), key, sizeof(tmp.key), tmp.key);
3223e14f97fSRoger A. Faulkner 		sfsprintf(s, len, "%-.*s%s%-.*s", l, keybuf, z ? "." : "", r, keybuf + sizeof(keybuf) / 2);
323da2e3ebdSchin 		if (fdp)
324da2e3ebdSchin 		{
325da2e3ebdSchin 			if ((n = open(b, O_CREAT|O_RDWR|O_EXCL|O_TEMPORARY, tmp.mode)) >= 0)
326da2e3ebdSchin 			{
327da2e3ebdSchin 				*fdp = n;
328da2e3ebdSchin 				return b;
329da2e3ebdSchin 			}
330da2e3ebdSchin 		}
331da2e3ebdSchin 		else if (access(b, F_OK))
332da2e3ebdSchin 			return b;
333da2e3ebdSchin 	}
334da2e3ebdSchin 	if (!buf)
335da2e3ebdSchin 		free(b);
336da2e3ebdSchin 	return 0;
337da2e3ebdSchin }
338