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 #include "sfhdr.h"
234887Schin
244887Schin /* Create a temporary stream for read/write.
254887Schin ** The stream is originally created as a memory-resident stream.
264887Schin ** When this memory is exceeded, a real temp file will be created.
274887Schin ** The temp file creation sequence is somewhat convoluted so that
284887Schin ** pool/stack/discipline will work correctly.
294887Schin **
304887Schin ** Written by David Korn and Kiem-Phong Vo.
314887Schin */
324887Schin
334887Schin #if _tmp_rmfail
344887Schin
354887Schin /* File not removable while there is an open file descriptor.
364887Schin ** To ensure that temp files are properly removed, we need:
374887Schin ** 1. A discipline to remove a file when the corresponding stream is closed.
384887Schin ** Care must be taken to close the file descriptor before removing the
394887Schin ** file because systems such as NT do not allow file removal while
404887Schin ** there is an open file handle.
414887Schin ** 2. An atexit() function is set up to close temp files when process exits.
424887Schin ** 3. On systems with O_TEMPORARY (e.g., NT), this is used to further ensure
434887Schin ** that temp files will be removed after the last handle is closed.
444887Schin */
454887Schin
464887Schin typedef struct _file_s File_t;
474887Schin struct _file_s
484887Schin { File_t* next; /* link list */
494887Schin Sfio_t* f; /* associated stream */
504887Schin char name[1]; /* temp file name */
514887Schin };
524887Schin
534887Schin static File_t* File; /* list pf temp files */
544887Schin
554887Schin #if __STD_C
_tmprmfile(Sfio_t * f,int type,Void_t * val,Sfdisc_t * disc)564887Schin static int _tmprmfile(Sfio_t* f, int type, Void_t* val, Sfdisc_t* disc)
574887Schin #else
584887Schin static int _tmprmfile(f, type, val, disc)
594887Schin Sfio_t* f;
604887Schin int type;
614887Schin Void_t* val;
624887Schin Sfdisc_t* disc;
634887Schin #endif
644887Schin {
654887Schin reg File_t *ff, *last;
664887Schin
674887Schin NOTUSED(val);
684887Schin
694887Schin if(type == SF_DPOP) /* don't allow this to pop */
704887Schin return -1;
714887Schin
724887Schin if(type == SF_CLOSING)
734887Schin {
744887Schin (void)vtmtxlock(_Sfmutex);
754887Schin for(last = NIL(File_t*), ff = File; ff; last = ff, ff = ff->next)
764887Schin if(ff->f == f)
774887Schin break;
784887Schin if(ff)
794887Schin { if(!last)
804887Schin File = ff->next;
814887Schin else last->next = ff->next;
824887Schin
834887Schin if(_Sfnotify)
844887Schin (*_Sfnotify)(f,SF_CLOSING,f->file);
854887Schin CLOSE(f->file);
864887Schin f->file = -1;
874887Schin while(sysremovef(ff->name) < 0 && errno == EINTR)
884887Schin errno = 0;
894887Schin
904887Schin free((Void_t*)ff);
914887Schin }
924887Schin (void)vtmtxunlock(_Sfmutex);
934887Schin }
944887Schin
954887Schin return 0;
964887Schin }
974887Schin
984887Schin #if __STD_C
_rmfiles(void)994887Schin static void _rmfiles(void)
1004887Schin #else
1014887Schin static void _rmfiles()
1024887Schin #endif
1034887Schin { reg File_t *ff, *next;
1044887Schin
1054887Schin (void)vtmtxlock(_Sfmutex);
1064887Schin for(ff = File; ff; ff = next)
1074887Schin { next = ff->next;
1084887Schin _tmprmfile(ff->f, SF_CLOSING, NIL(Void_t*), ff->f->disc);
1094887Schin }
1104887Schin (void)vtmtxunlock(_Sfmutex);
1114887Schin }
1124887Schin
1134887Schin static Sfdisc_t Rmdisc =
1144887Schin { NIL(Sfread_f), NIL(Sfwrite_f), NIL(Sfseek_f), _tmprmfile, NIL(Sfdisc_t*) };
1154887Schin
1164887Schin #endif /*_tmp_rmfail*/
1174887Schin
1184887Schin #if __STD_C
_rmtmp(Sfio_t * f,char * file)1194887Schin static int _rmtmp(Sfio_t* f, char* file)
1204887Schin #else
1214887Schin static int _rmtmp(f, file)
1224887Schin Sfio_t* f;
1234887Schin char* file;
1244887Schin #endif
1254887Schin {
1264887Schin #if _tmp_rmfail /* remove only when stream is closed */
1274887Schin reg File_t* ff;
1284887Schin
1294887Schin if(!File)
1304887Schin atexit(_rmfiles);
1314887Schin
1324887Schin if(!(ff = (File_t*)malloc(sizeof(File_t)+strlen(file))) )
1334887Schin return -1;
1344887Schin (void)vtmtxlock(_Sfmutex);
1354887Schin ff->f = f;
1364887Schin strcpy(ff->name,file);
1374887Schin ff->next = File;
1384887Schin File = ff;
1394887Schin (void)vtmtxunlock(_Sfmutex);
1404887Schin
1414887Schin #else /* can remove now */
1424887Schin while(sysremovef(file) < 0 && errno == EINTR)
1434887Schin errno = 0;
1444887Schin #endif
1454887Schin
1464887Schin return 0;
1474887Schin }
1484887Schin
1494887Schin #if !_PACKAGE_ast
1504887Schin #define TMPDFLT "/tmp"
1514887Schin static char **Tmppath, **Tmpcur;
1524887Schin
1534887Schin #if __STD_C
_sfgetpath(char * path)1544887Schin char** _sfgetpath(char* path)
1554887Schin #else
1564887Schin char** _sfgetpath(path)
1574887Schin char* path;
1584887Schin #endif
1594887Schin { reg char *p, **dirs;
1604887Schin reg int n;
1614887Schin
1624887Schin if(!(path = getenv(path)) )
1634887Schin return NIL(char**);
1644887Schin
1654887Schin for(p = path, n = 0;;) /* count number of directories */
1664887Schin { while(*p == ':')
1674887Schin ++p;
1684887Schin if(*p == 0)
1694887Schin break;
1704887Schin n += 1;
1714887Schin while(*p && *p != ':') /* skip dir name */
1724887Schin ++p;
1734887Schin }
1744887Schin if(n == 0 || !(dirs = (char**)malloc((n+1)*sizeof(char*))) )
1754887Schin return NIL(char**);
1764887Schin if(!(p = (char*)malloc(strlen(path)+1)) )
1774887Schin { free(dirs);
1784887Schin return NIL(char**);
1794887Schin }
1804887Schin strcpy(p,path);
1814887Schin for(n = 0;; ++n)
1824887Schin { while(*p == ':')
1834887Schin ++p;
1844887Schin if(*p == 0)
1854887Schin break;
1864887Schin dirs[n] = p;
1874887Schin while(*p && *p != ':')
1884887Schin ++p;
1894887Schin if(*p == ':')
1904887Schin *p++ = 0;
1914887Schin }
1924887Schin dirs[n] = NIL(char*);
1934887Schin
1944887Schin return dirs;
1954887Schin }
1964887Schin
1974887Schin #endif /*!_PACKAGE_ast*/
1984887Schin
1994887Schin #if __STD_C
_tmpfd(Sfio_t * f)2004887Schin static int _tmpfd(Sfio_t* f)
2014887Schin #else
2024887Schin static int _tmpfd(f)
2034887Schin Sfio_t* f;
2044887Schin #endif
2054887Schin {
2064887Schin reg char* file;
2074887Schin int fd;
2084887Schin
2094887Schin #if _PACKAGE_ast
2104887Schin if(!(file = pathtemp(NiL,PATH_MAX,NiL,"sf",&fd)))
2114887Schin return -1;
2124887Schin _rmtmp(f, file);
2134887Schin free(file);
2144887Schin #else
2154887Schin int t;
2164887Schin
2174887Schin /* set up path of dirs to create temp files */
2184887Schin if(!Tmppath && !(Tmppath = _sfgetpath("TMPPATH")) )
2194887Schin { if(!(Tmppath = (char**)malloc(2*sizeof(char*))) )
2204887Schin return -1;
2214887Schin if(!(file = getenv("TMPDIR")) )
2224887Schin file = TMPDFLT;
2234887Schin if(!(Tmppath[0] = (char*)malloc(strlen(file)+1)) )
2244887Schin { free(Tmppath);
2254887Schin Tmppath = NIL(char**);
2264887Schin return -1;
2274887Schin }
2284887Schin strcpy(Tmppath[0],file);
2294887Schin Tmppath[1] = NIL(char*);
2304887Schin }
2314887Schin
2324887Schin /* set current directory to create this temp file */
2334887Schin if(Tmpcur)
2344887Schin Tmpcur += 1;
2354887Schin if(!Tmpcur || !Tmpcur[0])
2364887Schin Tmpcur = Tmppath;
2374887Schin
2384887Schin fd = -1;
2394887Schin for(t = 0; t < 10; ++t)
2404887Schin { /* compute a random name */
2414887Schin static ulong Key, A;
2424887Schin if(A == 0 || t > 0) /* get a quasi-random coefficient */
2434887Schin { reg int r;
2444887Schin A = (ulong)time(NIL(time_t*)) ^ (((ulong)(&t)) >> 3);
2454887Schin if(Key == 0)
2464887Schin Key = (A >> 16) | ((A&0xffff)<<16);
2474887Schin A ^= Key;
2484887Schin if((r = (A-1) & 03) != 0) /* Knuth vol.2, page.16, Thm.A */
2494887Schin A += 4-r;
2504887Schin }
2514887Schin
2524887Schin Key = A*Key + 987654321;
2534887Schin file = sfprints("%s/sf%3.3.32lu.%3.3.32lu",
2544887Schin Tmpcur[0], (Key>>15)&0x7fff, Key&0x7fff);
2554887Schin if(!file)
2564887Schin return -1;
2574887Schin #if _has_oflags
2584887Schin if((fd = sysopenf(file,O_RDWR|O_CREAT|O_EXCL|O_TEMPORARY,SF_CREATMODE)) >= 0)
2594887Schin break;
2604887Schin #else
2614887Schin if((fd = sysopenf(file,O_RDONLY)) >= 0)
2624887Schin { /* file already exists */
2634887Schin CLOSE(fd);
2644887Schin fd = -1;
2654887Schin }
2664887Schin else if((fd = syscreatf(file,SF_CREATMODE)) >= 0)
2674887Schin { /* reopen for read and write */
2684887Schin CLOSE(fd);
2694887Schin if((fd = sysopenf(file,O_RDWR)) >= 0)
2704887Schin break;
2714887Schin
2724887Schin /* don't know what happened but must remove file */
2734887Schin while(sysremovef(file) < 0 && errno == EINTR)
2744887Schin errno = 0;
2754887Schin }
2764887Schin #endif /* _has_oflags */
2774887Schin }
2784887Schin if(fd >= 0)
2794887Schin _rmtmp(f, file);
2804887Schin #endif /* _PACKAGE_ast */
2814887Schin return fd;
2824887Schin }
2834887Schin
2844887Schin #if __STD_C
_tmpexcept(Sfio_t * f,int type,Void_t * val,Sfdisc_t * disc)2854887Schin static int _tmpexcept(Sfio_t* f, int type, Void_t* val, Sfdisc_t* disc)
2864887Schin #else
2874887Schin static int _tmpexcept(f,type,val,disc)
2884887Schin Sfio_t* f;
2894887Schin int type;
2904887Schin Void_t* val;
2914887Schin Sfdisc_t* disc;
2924887Schin #endif
2934887Schin {
2944887Schin reg int fd, m;
2954887Schin reg Sfio_t* sf;
2964887Schin Sfio_t newf, savf;
2978462SApril.Chin@Sun.COM void (*notifyf)_ARG_((Sfio_t*, int, void*));
2984887Schin
2994887Schin NOTUSED(val);
3004887Schin
3014887Schin /* the discipline needs to change only under the following exceptions */
3024887Schin if(type != SF_WRITE && type != SF_SEEK &&
3034887Schin type != SF_DPUSH && type != SF_DPOP && type != SF_DBUFFER)
3044887Schin return 0;
3054887Schin
3064887Schin /* notify function */
3074887Schin notifyf = _Sfnotify;
3084887Schin
3094887Schin /* try to create the temp file */
3104887Schin SFCLEAR(&newf,NIL(Vtmutex_t*));
3114887Schin newf.flags = SF_STATIC;
3124887Schin newf.mode = SF_AVAIL;
3134887Schin
3144887Schin if((fd = _tmpfd(f)) < 0 )
3154887Schin return -1;
3164887Schin
3174887Schin /* make sure that the notify function won't be called here since
3184887Schin we are only interested in creating the file, not the stream */
3194887Schin _Sfnotify = 0;
3204887Schin sf = sfnew(&newf,NIL(Void_t*),(size_t)SF_UNBOUND,fd,SF_READ|SF_WRITE);
3214887Schin _Sfnotify = notifyf;
3224887Schin if(!sf)
3234887Schin return -1;
3244887Schin
3254887Schin if(newf.mutex) /* don't need a mutex for this stream */
3264887Schin { (void)vtmtxclrlock(newf.mutex);
3274887Schin (void)vtmtxclose(newf.mutex);
3284887Schin newf.mutex = NIL(Vtmutex_t*);
3294887Schin }
3304887Schin
3314887Schin /* make sure that new stream has the same mode */
3324887Schin if((m = f->flags&(SF_READ|SF_WRITE)) != (SF_READ|SF_WRITE))
3334887Schin sfset(sf, ((~m)&(SF_READ|SF_WRITE)), 0);
3344887Schin sfset(sf, (f->mode&(SF_READ|SF_WRITE)), 1);
3354887Schin
3364887Schin /* now remake the old stream into the new image */
3374887Schin memcpy((Void_t*)(&savf), (Void_t*)f, sizeof(Sfio_t));
3384887Schin memcpy((Void_t*)f, (Void_t*)sf, sizeof(Sfio_t));
3394887Schin f->push = savf.push;
3404887Schin f->pool = savf.pool;
3414887Schin f->rsrv = savf.rsrv;
3424887Schin f->proc = savf.proc;
3434887Schin f->mutex = savf.mutex;
3444887Schin f->stdio = savf.stdio;
3454887Schin
3464887Schin if(savf.data)
3474887Schin { SFSTRSIZE(&savf);
3484887Schin if(!(savf.flags&SF_MALLOC) )
3494887Schin (void)sfsetbuf(f,(Void_t*)savf.data,savf.size);
3504887Schin if(savf.extent > 0)
3514887Schin (void)sfwrite(f,(Void_t*)savf.data,(size_t)savf.extent);
3524887Schin (void)sfseek(f,(Sfoff_t)(savf.next - savf.data),SEEK_SET);
3534887Schin if((savf.flags&SF_MALLOC) )
3544887Schin free((Void_t*)savf.data);
3554887Schin }
3564887Schin
3574887Schin /* announce change of status */
3584887Schin if(notifyf)
3598462SApril.Chin@Sun.COM (*notifyf)(f, SF_NEW, (void*)((long)f->file));
3604887Schin
3614887Schin f->disc = disc->disc;
3624887Schin
3634887Schin /* erase all traces of newf */
3644887Schin newf.data = newf.endb = newf.endr = newf.endw = NIL(uchar*);
3654887Schin newf.file = -1;
3664887Schin sfclose(&newf);
3674887Schin
3684887Schin return 1;
3694887Schin }
3704887Schin
3714887Schin #if __STD_C
sftmp(size_t s)3728462SApril.Chin@Sun.COM Sfio_t* sftmp(size_t s)
3734887Schin #else
3744887Schin Sfio_t* sftmp(s)
3758462SApril.Chin@Sun.COM size_t s;
3764887Schin #endif
3774887Schin {
3788462SApril.Chin@Sun.COM Sfio_t* f;
3794887Schin static Sfdisc_t Tmpdisc =
3804887Schin { NIL(Sfread_f), NIL(Sfwrite_f), NIL(Sfseek_f), _tmpexcept,
3814887Schin #if _tmp_rmfail
3824887Schin &Rmdisc
3834887Schin #else
3844887Schin NIL(Sfdisc_t*)
3854887Schin #endif
3864887Schin };
3874887Schin
3884887Schin /* start with a memory resident stream */
3894887Schin if(!(f = sfnew(NIL(Sfio_t*),NIL(char*),s,-1,SF_STRING|SF_READ|SF_WRITE)) )
3904887Schin return NIL(Sfio_t*);
3914887Schin
3924887Schin if(s != (size_t)SF_UNBOUND) /* set up a discipline for out-of-bound, etc. */
3934887Schin f->disc = &Tmpdisc;
3944887Schin
3954887Schin /* make the file now */
3964887Schin if(s == 0 && _tmpexcept(f,SF_DPOP,NIL(Void_t*),f->disc) < 0)
3974887Schin { sfclose(f);
3984887Schin return NIL(Sfio_t*);
3994887Schin }
4004887Schin
4014887Schin return f;
4024887Schin }
403