14887Schin /***********************************************************************
24887Schin * *
34887Schin * This software is part of the ast package *
4*12068SRoger.Faulkner@Oracle.COM * Copyright (c) 1985-2010 AT&T Intellectual Property *
54887Schin * and is licensed under the *
64887Schin * Common Public License, Version 1.0 *
78462SApril.Chin@Sun.COM * by AT&T Intellectual Property *
84887Schin * *
94887Schin * A copy of the License is available at *
104887Schin * http://www.opensource.org/licenses/cpl1.0.txt *
114887Schin * (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) *
124887Schin * *
134887Schin * Information and Software Systems Research *
144887Schin * AT&T Research *
154887Schin * Florham Park NJ *
164887Schin * *
174887Schin * Glenn Fowler <gsf@research.att.com> *
184887Schin * David Korn <dgk@research.att.com> *
194887Schin * Phong Vo <kpv@research.att.com> *
204887Schin * *
214887Schin ***********************************************************************/
224887Schin #pragma prototyped
234887Schin /*
244887Schin * AT&T Research
254887Schin *
264887Schin * generate a temp file / name
274887Schin *
284887Schin * [<dir>/][<pfx>]<bas>.<suf>
294887Schin *
304887Schin * length(<pfx>)<=5
314887Schin * length(<bas>)==3
324887Schin * length(<suf>)==3
334887Schin *
344887Schin * pathtmp(a,b,c,d) pathtemp(a,L_tmpnam,b,c,0)
354887Schin * tmpfile() char*p=pathtemp(0,0,0,"tf",&sp);
364887Schin * remove(p);
374887Schin * free(p)
384887Schin * tmpnam(0) static char p[L_tmpnam];
394887Schin * pathtemp(p,sizeof(p),0,"tn",0)
404887Schin * tmpnam(p) pathtemp(p,L_tmpnam,0,"tn",0)
414887Schin * tempnam(d,p) pathtemp(0,d,p,0)
42*12068SRoger.Faulkner@Oracle.COM * mktemp(p) pathtemp(0,0,p,0)
434887Schin *
444887Schin * if buf==0 then space is malloc'd
454887Schin * buf size is size
464887Schin * dir and pfx may be 0
47*12068SRoger.Faulkner@Oracle.COM * if pfx contains trailing X's then it is a mktemp(3) template
48*12068SRoger.Faulkner@Oracle.COM * otherwise only first 5 chars of pfx are used
494887Schin * if fdp!=0 then the path is opened O_EXCL and *fdp is the open fd
504887Schin * malloc'd space returned by successful pathtemp() calls
514887Schin * must be freed by the caller
524887Schin *
534887Schin * generated names are pseudo-randomized to avoid both
544887Schin * collisions and predictions (same alg in sfio/sftmp.c)
554887Schin *
564887Schin * / as first pfx char provides tmp file generation control
574887Schin * 0 returned for unknown ops
584887Schin *
594887Schin * /cycle dir specifies TMPPATH cycle control
604887Schin * automatic (default) cycled with each tmp file
614887Schin * manual cycled by application with dir=(nil)
624887Schin * (nil) cycle TMPPATH
634887Schin * /prefix dir specifies the default prefix (default ast)
64*12068SRoger.Faulkner@Oracle.COM * /private private file/dir modes
65*12068SRoger.Faulkner@Oracle.COM * /public public file/dir modes
66*12068SRoger.Faulkner@Oracle.COM * /seed dir specifies pseudo-random generator seed
67*12068SRoger.Faulkner@Oracle.COM * 0 or "0" to re-initialize
684887Schin * /TMPPATH dir overrides the env value
694887Schin * /TMPDIR dir overrides the env value
704887Schin */
714887Schin
724887Schin #include <ast.h>
734887Schin #include <ls.h>
74*12068SRoger.Faulkner@Oracle.COM #include <tv.h>
754887Schin #include <tm.h>
764887Schin
774887Schin #define ATTEMPT 10
784887Schin
794887Schin #define TMP_ENV "TMPDIR"
804887Schin #define TMP_PATH_ENV "TMPPATH"
814887Schin #define TMP1 "/tmp"
824887Schin #define TMP2 "/usr/tmp"
834887Schin
844887Schin #define VALID(d) (*(d)&&!eaccess(d,W_OK|X_OK))
854887Schin
864887Schin static struct
874887Schin {
884887Schin mode_t mode;
894887Schin char** vec;
904887Schin char** dir;
91*12068SRoger.Faulkner@Oracle.COM uint32_t key;
92*12068SRoger.Faulkner@Oracle.COM uint32_t rng;
934887Schin pid_t pid;
944887Schin int manual;
95*12068SRoger.Faulkner@Oracle.COM int seed;
964887Schin char* pfx;
974887Schin char* tmpdir;
984887Schin char* tmppath;
994887Schin } tmp = { S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH };
1004887Schin
1014887Schin char*
pathtemp(char * buf,size_t len,const char * dir,const char * pfx,int * fdp)1024887Schin pathtemp(char* buf, size_t len, const char* dir, const char* pfx, int* fdp)
1034887Schin {
1044887Schin register char* d;
1054887Schin register char* b;
1064887Schin register char* s;
1074887Schin register char* x;
108*12068SRoger.Faulkner@Oracle.COM uint32_t key;
1094887Schin int m;
1104887Schin int n;
111*12068SRoger.Faulkner@Oracle.COM int l;
112*12068SRoger.Faulkner@Oracle.COM int r;
1134887Schin int z;
1144887Schin int attempt;
115*12068SRoger.Faulkner@Oracle.COM Tv_t tv;
116*12068SRoger.Faulkner@Oracle.COM char keybuf[16];
1174887Schin
1184887Schin if (pfx && *pfx == '/')
1194887Schin {
1204887Schin pfx++;
1214887Schin if (streq(pfx, "cycle"))
1224887Schin {
1234887Schin if (!dir)
1244887Schin {
1254887Schin tmp.manual = 1;
1264887Schin if (tmp.dir && !*tmp.dir++)
1274887Schin tmp.dir = tmp.vec;
1284887Schin }
1294887Schin else
1304887Schin tmp.manual = streq(dir, "manual");
1314887Schin return (char*)pfx;
1324887Schin }
1334887Schin else if (streq(pfx, "prefix"))
1344887Schin {
1354887Schin if (tmp.pfx)
1364887Schin free(tmp.pfx);
1374887Schin tmp.pfx = dir ? strdup(dir) : (char*)0;
1384887Schin return (char*)pfx;
1394887Schin }
1404887Schin else if (streq(pfx, "private"))
141*12068SRoger.Faulkner@Oracle.COM {
1424887Schin tmp.mode = S_IRUSR|S_IWUSR;
143*12068SRoger.Faulkner@Oracle.COM return (char*)pfx;
144*12068SRoger.Faulkner@Oracle.COM }
1454887Schin else if (streq(pfx, "public"))
146*12068SRoger.Faulkner@Oracle.COM {
1474887Schin tmp.mode = S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH;
148*12068SRoger.Faulkner@Oracle.COM return (char*)pfx;
149*12068SRoger.Faulkner@Oracle.COM }
150*12068SRoger.Faulkner@Oracle.COM else if (streq(pfx, "seed"))
151*12068SRoger.Faulkner@Oracle.COM {
152*12068SRoger.Faulkner@Oracle.COM tmp.key = (tmp.seed = (tmp.rng = dir ? (uint32_t)strtoul(dir, NiL, 0) : (uint32_t)1) != 0)? (uint32_t)0x63c63cd9L : 0;
153*12068SRoger.Faulkner@Oracle.COM return (char*)pfx;
154*12068SRoger.Faulkner@Oracle.COM }
1554887Schin else if (streq(pfx, TMP_ENV))
1564887Schin {
1574887Schin if (tmp.vec)
1584887Schin {
1594887Schin free(tmp.vec);
1604887Schin tmp.vec = 0;
1614887Schin }
1624887Schin if (tmp.tmpdir)
1634887Schin free(tmp.tmpdir);
1644887Schin tmp.tmpdir = dir ? strdup(dir) : (char*)0;
1654887Schin return (char*)pfx;
1664887Schin }
1674887Schin else if (streq(pfx, TMP_PATH_ENV))
1684887Schin {
1694887Schin if (tmp.vec)
1704887Schin {
1714887Schin free(tmp.vec);
1724887Schin tmp.vec = 0;
1734887Schin }
1744887Schin if (tmp.tmppath)
1754887Schin free(tmp.tmppath);
1764887Schin tmp.tmppath = dir ? strdup(dir) : (char*)0;
1774887Schin return (char*)pfx;
1784887Schin }
1794887Schin return 0;
1804887Schin }
181*12068SRoger.Faulkner@Oracle.COM if (tmp.seed)
182*12068SRoger.Faulkner@Oracle.COM tv.tv_nsec = 0;
183*12068SRoger.Faulkner@Oracle.COM else
184*12068SRoger.Faulkner@Oracle.COM tvgettime(&tv);
1854887Schin if (!(d = (char*)dir) || *d && eaccess(d, W_OK|X_OK))
1864887Schin {
1874887Schin if (!tmp.vec)
1884887Schin {
1894887Schin if ((x = tmp.tmppath) || (x = getenv(TMP_PATH_ENV)))
1904887Schin {
1914887Schin n = 2;
1924887Schin s = x;
1934887Schin while (s = strchr(s, ':'))
1944887Schin {
1954887Schin s++;
1964887Schin n++;
1974887Schin }
1984887Schin if (!(tmp.vec = newof(0, char*, n, strlen(x) + 1)))
1994887Schin return 0;
2004887Schin tmp.dir = tmp.vec;
2014887Schin x = strcpy((char*)(tmp.dir + n), x);
2024887Schin *tmp.dir++ = x;
2034887Schin while (x = strchr(x, ':'))
2044887Schin {
2054887Schin *x++ = 0;
2064887Schin if (!VALID(*(tmp.dir - 1)))
2074887Schin tmp.dir--;
2084887Schin *tmp.dir++ = x;
2094887Schin }
2104887Schin if (!VALID(*(tmp.dir - 1)))
2114887Schin tmp.dir--;
2124887Schin *tmp.dir = 0;
2134887Schin }
2144887Schin else
2154887Schin {
2164887Schin if (((d = tmp.tmpdir) || (d = getenv(TMP_ENV))) && !VALID(d))
2174887Schin d = 0;
2184887Schin if (!(tmp.vec = newof(0, char*, 2, d ? (strlen(d) + 1) : 0)))
2194887Schin return 0;
2204887Schin if (d)
2214887Schin *tmp.vec = strcpy((char*)(tmp.vec + 2), d);
2224887Schin }
2234887Schin tmp.dir = tmp.vec;
2244887Schin }
2254887Schin if (!(d = *tmp.dir++))
2264887Schin {
2274887Schin tmp.dir = tmp.vec;
2284887Schin d = *tmp.dir++;
2294887Schin }
2304887Schin 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))
2314887Schin return 0;
2324887Schin }
2334887Schin if (!len)
2344887Schin len = PATH_MAX;
2354887Schin len--;
2364887Schin if (!(b = buf) && !(b = newof(0, char, len, 1)))
2374887Schin return 0;
238*12068SRoger.Faulkner@Oracle.COM z = 0;
239*12068SRoger.Faulkner@Oracle.COM if (!pfx && !(pfx = tmp.pfx))
240*12068SRoger.Faulkner@Oracle.COM pfx = "ast";
241*12068SRoger.Faulkner@Oracle.COM m = strlen(pfx);
242*12068SRoger.Faulkner@Oracle.COM if (buf && dir && (buf == (char*)dir && (buf + strlen(buf) + 1) == (char*)pfx || buf == (char*)pfx && !*dir) && !strcmp((char*)pfx + m + 1, "XXXXX"))
2434887Schin {
2444887Schin d = (char*)dir;
245*12068SRoger.Faulkner@Oracle.COM len = m += strlen(d) + 8;
246*12068SRoger.Faulkner@Oracle.COM l = 3;
247*12068SRoger.Faulkner@Oracle.COM r = 3;
248*12068SRoger.Faulkner@Oracle.COM }
249*12068SRoger.Faulkner@Oracle.COM else if (*pfx && pfx[m - 1] == 'X')
250*12068SRoger.Faulkner@Oracle.COM {
251*12068SRoger.Faulkner@Oracle.COM for (l = m; l && pfx[l - 1] == 'X'; l--);
252*12068SRoger.Faulkner@Oracle.COM r = m - l;
253*12068SRoger.Faulkner@Oracle.COM m = l;
254*12068SRoger.Faulkner@Oracle.COM l = r / 2;
255*12068SRoger.Faulkner@Oracle.COM r -= l;
256*12068SRoger.Faulkner@Oracle.COM }
257*12068SRoger.Faulkner@Oracle.COM else if (strchr(pfx, '.'))
258*12068SRoger.Faulkner@Oracle.COM {
259*12068SRoger.Faulkner@Oracle.COM m = 5;
260*12068SRoger.Faulkner@Oracle.COM l = 3;
261*12068SRoger.Faulkner@Oracle.COM r = 3;
2624887Schin }
2634887Schin else
2644887Schin {
2654887Schin z = '.';
2664887Schin m = 5;
267*12068SRoger.Faulkner@Oracle.COM l = 2;
268*12068SRoger.Faulkner@Oracle.COM r = 3;
2694887Schin }
2704887Schin x = b + len;
2714887Schin s = b;
2724887Schin if (d)
2734887Schin {
2744887Schin while (s < x && (n = *d++))
2754887Schin *s++ = n;
2764887Schin if (s < x && s > b && *(s - 1) != '/')
2774887Schin *s++ = '/';
2784887Schin }
2794887Schin if ((x - s) > m)
2804887Schin x = s + m;
2814887Schin while (s < x && (n = *pfx++))
2824887Schin {
2834887Schin if (n == '/' || n == '\\' || n == z)
2844887Schin n = '_';
2854887Schin *s++ = n;
2864887Schin }
2874887Schin *s = 0;
2884887Schin len -= (s - b);
2894887Schin for (attempt = 0; attempt < ATTEMPT; attempt++)
2904887Schin {
291*12068SRoger.Faulkner@Oracle.COM if (!tmp.rng || !tmp.seed && (attempt || tmp.pid != getpid()))
2924887Schin {
2934887Schin register int r;
2944887Schin
2954887Schin /*
2964887Schin * get a quasi-random coefficient
2974887Schin */
2984887Schin
2994887Schin tmp.pid = getpid();
300*12068SRoger.Faulkner@Oracle.COM tmp.rng = (uintptr_t)tmp.pid * ((uintptr_t)time(NiL) ^ (((uintptr_t)(&attempt)) >> 3) ^ (((uintptr_t)tmp.dir) >> 3));
3014887Schin if (!tmp.key)
3024887Schin tmp.key = (tmp.rng >> 16) | ((tmp.rng & 0xffff) << 16);
3034887Schin tmp.rng ^= tmp.key;
3044887Schin
3054887Schin /*
3064887Schin * Knuth vol.2, page.16, Thm.A
3074887Schin */
3084887Schin
3094887Schin if ((r = (tmp.rng - 1) & 03))
3104887Schin tmp.rng += 4 - r;
3114887Schin }
3124887Schin
3134887Schin /*
3144887Schin * generate a pseudo-random name
3154887Schin */
3164887Schin
317*12068SRoger.Faulkner@Oracle.COM key = tmp.rng * tmp.key + tv.tv_nsec;
318*12068SRoger.Faulkner@Oracle.COM if (!tmp.seed)
319*12068SRoger.Faulkner@Oracle.COM tvgettime(&tv);
320*12068SRoger.Faulkner@Oracle.COM tmp.key = tmp.rng * key + tv.tv_nsec;
321*12068SRoger.Faulkner@Oracle.COM sfsprintf(keybuf, sizeof(keybuf), "%07.7.32I*u%07.7.32I*u", sizeof(key), key, sizeof(tmp.key), tmp.key);
322*12068SRoger.Faulkner@Oracle.COM sfsprintf(s, len, "%-.*s%s%-.*s", l, keybuf, z ? "." : "", r, keybuf + sizeof(keybuf) / 2);
3234887Schin if (fdp)
3244887Schin {
3254887Schin if ((n = open(b, O_CREAT|O_RDWR|O_EXCL|O_TEMPORARY, tmp.mode)) >= 0)
3264887Schin {
3274887Schin *fdp = n;
3284887Schin return b;
3294887Schin }
3304887Schin }
3314887Schin else if (access(b, F_OK))
3324887Schin return b;
3334887Schin }
3344887Schin if (!buf)
3354887Schin free(b);
3364887Schin return 0;
3374887Schin }
338