1N/A/***********************************************************************
1N/A* *
1N/A* This software is part of the ast package *
1N/A* Copyright (c) 1985-2011 AT&T Intellectual Property *
1N/A* and is licensed under the *
1N/A* Common Public License, Version 1.0 *
1N/A* by AT&T Intellectual Property *
1N/A* *
1N/A* A copy of the License is available at *
1N/A* http://www.opensource.org/licenses/cpl1.0.txt *
1N/A* (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) *
1N/A* *
1N/A* Information and Software Systems Research *
1N/A* AT&T Research *
1N/A* Florham Park NJ *
1N/A* *
1N/A* Glenn Fowler <gsf@research.att.com> *
1N/A* David Korn <dgk@research.att.com> *
1N/A* Phong Vo <kpv@research.att.com> *
1N/A* *
1N/A***********************************************************************/
1N/A#pragma prototyped
1N/A/*
1N/A * AT&T Research
1N/A *
1N/A * generate a temp file / name
1N/A *
1N/A * [<dir>/][<pfx>]<bas>.<suf>
1N/A *
1N/A * length(<pfx>)<=5
1N/A * length(<bas>)==3
1N/A * length(<suf>)==3
1N/A *
1N/A * pathtmp(a,b,c,d) pathtemp(a,L_tmpnam,b,c,0)
1N/A * tmpfile() char*p=pathtemp(0,0,0,"tf",&sp);
1N/A * remove(p);
1N/A * free(p)
1N/A * tmpnam(0) static char p[L_tmpnam];
1N/A * pathtemp(p,sizeof(p),0,"tn",0)
1N/A * tmpnam(p) pathtemp(p,L_tmpnam,0,"tn",0)
1N/A * tempnam(d,p) pathtemp(0,d,p,0)
1N/A * mktemp(p) pathtemp(0,0,p,0)
1N/A *
1N/A * if buf==0 then space is malloc'd
1N/A * buf size is size
1N/A * dir and pfx may be 0
1N/A * if pfx contains trailing X's then it is a mktemp(3) template
1N/A * otherwise only first 5 chars of pfx are used
1N/A * if fdp!=0 then the path is opened O_EXCL and *fdp is the open fd
1N/A * malloc'd space returned by successful pathtemp() calls
1N/A * must be freed by the caller
1N/A *
1N/A * generated names are pseudo-randomized to avoid both
1N/A * collisions and predictions (same alg in sfio/sftmp.c)
1N/A *
1N/A * / as first pfx char provides tmp file generation control
1N/A * 0 returned for unknown ops
1N/A *
1N/A * /cycle dir specifies TMPPATH cycle control
1N/A * automatic (default) cycled with each tmp file
1N/A * manual cycled by application with dir=(nil)
1N/A * (nil) cycle TMPPATH
1N/A * /prefix dir specifies the default prefix (default ast)
1N/A * /private private file/dir modes
1N/A * /public public file/dir modes
1N/A * /seed dir specifies pseudo-random generator seed
1N/A * 0 or "0" to re-initialize
1N/A * /TMPPATH dir overrides the env value
1N/A * /TMPDIR dir overrides the env value
1N/A */
1N/A
1N/A#include <ast.h>
1N/A#include <ls.h>
1N/A#include <tv.h>
1N/A#include <tm.h>
1N/A
1N/A#define ATTEMPT 10
1N/A
1N/A#define TMP_ENV "TMPDIR"
1N/A#define TMP_PATH_ENV "TMPPATH"
1N/A#define TMP1 "/tmp"
1N/A#define TMP2 "/usr/tmp"
1N/A
1N/A#define VALID(d) (*(d)&&!eaccess(d,W_OK|X_OK))
1N/A
1N/Astatic struct
1N/A{
1N/A mode_t mode;
1N/A char** vec;
1N/A char** dir;
1N/A uint32_t key;
1N/A uint32_t rng;
1N/A pid_t pid;
1N/A int manual;
1N/A int seed;
1N/A char* pfx;
1N/A char* tmpdir;
1N/A char* tmppath;
1N/A} tmp = { S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH };
1N/A
1N/Achar*
1N/Apathtemp(char* buf, size_t len, const char* dir, const char* pfx, int* fdp)
1N/A{
1N/A register char* d;
1N/A register char* b;
1N/A register char* s;
1N/A register char* x;
1N/A uint32_t key;
1N/A int m;
1N/A int n;
1N/A int l;
1N/A int r;
1N/A int z;
1N/A int attempt;
1N/A Tv_t tv;
1N/A char keybuf[16];
1N/A
1N/A if (pfx && *pfx == '/')
1N/A {
1N/A pfx++;
1N/A if (streq(pfx, "cycle"))
1N/A {
1N/A if (!dir)
1N/A {
1N/A tmp.manual = 1;
1N/A if (tmp.dir && !*tmp.dir++)
1N/A tmp.dir = tmp.vec;
1N/A }
1N/A else
1N/A tmp.manual = streq(dir, "manual");
1N/A return (char*)pfx;
1N/A }
1N/A else if (streq(pfx, "prefix"))
1N/A {
1N/A if (tmp.pfx)
1N/A free(tmp.pfx);
1N/A tmp.pfx = dir ? strdup(dir) : (char*)0;
1N/A return (char*)pfx;
1N/A }
1N/A else if (streq(pfx, "private"))
1N/A {
1N/A tmp.mode = S_IRUSR|S_IWUSR;
1N/A return (char*)pfx;
1N/A }
1N/A else if (streq(pfx, "public"))
1N/A {
1N/A tmp.mode = S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH;
1N/A return (char*)pfx;
1N/A }
1N/A else if (streq(pfx, "seed"))
1N/A {
1N/A tmp.key = (tmp.seed = (tmp.rng = dir ? (uint32_t)strtoul(dir, NiL, 0) : (uint32_t)1) != 0)? (uint32_t)0x63c63cd9L : 0;
1N/A return (char*)pfx;
1N/A }
1N/A else if (streq(pfx, TMP_ENV))
1N/A {
1N/A if (tmp.vec)
1N/A {
1N/A free(tmp.vec);
1N/A tmp.vec = 0;
1N/A }
1N/A if (tmp.tmpdir)
1N/A free(tmp.tmpdir);
1N/A tmp.tmpdir = dir ? strdup(dir) : (char*)0;
1N/A return (char*)pfx;
1N/A }
1N/A else if (streq(pfx, TMP_PATH_ENV))
1N/A {
1N/A if (tmp.vec)
1N/A {
1N/A free(tmp.vec);
1N/A tmp.vec = 0;
1N/A }
1N/A if (tmp.tmppath)
1N/A free(tmp.tmppath);
1N/A tmp.tmppath = dir ? strdup(dir) : (char*)0;
1N/A return (char*)pfx;
1N/A }
1N/A return 0;
1N/A }
1N/A if (tmp.seed)
1N/A tv.tv_nsec = 0;
1N/A else
1N/A tvgettime(&tv);
1N/A if (!(d = (char*)dir) || *d && eaccess(d, W_OK|X_OK))
1N/A {
1N/A if (!tmp.vec)
1N/A {
1N/A if ((x = tmp.tmppath) || (x = getenv(TMP_PATH_ENV)))
1N/A {
1N/A n = 2;
1N/A s = x;
1N/A while (s = strchr(s, ':'))
1N/A {
1N/A s++;
1N/A n++;
1N/A }
1N/A if (!(tmp.vec = newof(0, char*, n, strlen(x) + 1)))
1N/A return 0;
1N/A tmp.dir = tmp.vec;
1N/A x = strcpy((char*)(tmp.dir + n), x);
1N/A *tmp.dir++ = x;
1N/A while (x = strchr(x, ':'))
1N/A {
1N/A *x++ = 0;
1N/A if (!VALID(*(tmp.dir - 1)))
1N/A tmp.dir--;
1N/A *tmp.dir++ = x;
1N/A }
1N/A if (!VALID(*(tmp.dir - 1)))
1N/A tmp.dir--;
1N/A *tmp.dir = 0;
1N/A }
1N/A else
1N/A {
1N/A if (((d = tmp.tmpdir) || (d = getenv(TMP_ENV))) && !VALID(d))
1N/A d = 0;
1N/A if (!(tmp.vec = newof(0, char*, 2, d ? (strlen(d) + 1) : 0)))
1N/A return 0;
1N/A if (d)
1N/A *tmp.vec = strcpy((char*)(tmp.vec + 2), d);
1N/A }
1N/A tmp.dir = tmp.vec;
1N/A }
1N/A if (!(d = *tmp.dir++))
1N/A {
1N/A tmp.dir = tmp.vec;
1N/A d = *tmp.dir++;
1N/A }
1N/A 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))
1N/A return 0;
1N/A }
1N/A if (!len)
1N/A len = PATH_MAX;
1N/A len--;
1N/A if (!(b = buf) && !(b = newof(0, char, len, 1)))
1N/A return 0;
1N/A z = 0;
1N/A if (!pfx && !(pfx = tmp.pfx))
1N/A pfx = "ast";
1N/A m = strlen(pfx);
1N/A if (buf && dir && (buf == (char*)dir && (buf + strlen(buf) + 1) == (char*)pfx || buf == (char*)pfx && !*dir) && !strcmp((char*)pfx + m + 1, "XXXXX"))
1N/A {
1N/A d = (char*)dir;
1N/A len = m += strlen(d) + 8;
1N/A l = 3;
1N/A r = 3;
1N/A }
1N/A else if (*pfx && pfx[m - 1] == 'X')
1N/A {
1N/A for (l = m; l && pfx[l - 1] == 'X'; l--);
1N/A r = m - l;
1N/A m = l;
1N/A l = r / 2;
1N/A r -= l;
1N/A }
1N/A else if (strchr(pfx, '.'))
1N/A {
1N/A m = 5;
1N/A l = 3;
1N/A r = 3;
1N/A }
1N/A else
1N/A {
1N/A z = '.';
1N/A m = 5;
1N/A l = 2;
1N/A r = 3;
1N/A }
1N/A x = b + len;
1N/A s = b;
1N/A if (d)
1N/A {
1N/A while (s < x && (n = *d++))
1N/A *s++ = n;
1N/A if (s < x && s > b && *(s - 1) != '/')
1N/A *s++ = '/';
1N/A }
1N/A if ((x - s) > m)
1N/A x = s + m;
1N/A while (s < x && (n = *pfx++))
1N/A {
1N/A if (n == '/' || n == '\\' || n == z)
1N/A n = '_';
1N/A *s++ = n;
1N/A }
1N/A *s = 0;
1N/A len -= (s - b);
1N/A for (attempt = 0; attempt < ATTEMPT; attempt++)
1N/A {
1N/A if (!tmp.rng || !tmp.seed && (attempt || tmp.pid != getpid()))
1N/A {
1N/A register int r;
1N/A
1N/A /*
1N/A * get a quasi-random coefficient
1N/A */
1N/A
1N/A tmp.pid = getpid();
1N/A tmp.rng = (uint32_t)tmp.pid * ((uint32_t)time(NiL) ^ (((uint32_t)integralof(&attempt)) >> 3) ^ (((uint32_t)integralof(tmp.dir)) >> 3));
1N/A if (!tmp.key)
1N/A tmp.key = (tmp.rng >> 16) | ((tmp.rng & 0xffff) << 16);
1N/A tmp.rng ^= tmp.key;
1N/A
1N/A /*
1N/A * Knuth vol.2, page.16, Thm.A
1N/A */
1N/A
1N/A if ((r = (tmp.rng - 1) & 03))
1N/A tmp.rng += 4 - r;
1N/A }
1N/A
1N/A /*
1N/A * generate a pseudo-random name
1N/A */
1N/A
1N/A key = tmp.rng * tmp.key + tv.tv_nsec;
1N/A if (!tmp.seed)
1N/A tvgettime(&tv);
1N/A tmp.key = tmp.rng * key + tv.tv_nsec;
1N/A sfsprintf(keybuf, sizeof(keybuf), "%07.7.32I*u%07.7.32I*u", sizeof(key), key, sizeof(tmp.key), tmp.key);
1N/A sfsprintf(s, len, "%-.*s%s%-.*s", l, keybuf, z ? "." : "", r, keybuf + sizeof(keybuf) / 2);
1N/A if (fdp)
1N/A {
1N/A if ((n = open(b, O_CREAT|O_RDWR|O_EXCL|O_TEMPORARY, tmp.mode)) >= 0)
1N/A {
1N/A *fdp = n;
1N/A return b;
1N/A }
1N/A }
1N/A else if (access(b, F_OK))
1N/A return b;
1N/A }
1N/A if (!buf)
1N/A free(b);
1N/A return 0;
1N/A}