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#include "sfhdr.h"
1N/A
1N/A/* Create a temporary stream for read/write.
1N/A** The stream is originally created as a memory-resident stream.
1N/A** When this memory is exceeded, a real temp file will be created.
1N/A** The temp file creation sequence is somewhat convoluted so that
1N/A** pool/stack/discipline will work correctly.
1N/A**
1N/A** Written by David Korn and Kiem-Phong Vo.
1N/A*/
1N/A
1N/A#if _tmp_rmfail
1N/A
1N/A/* File not removable while there is an open file descriptor.
1N/A** To ensure that temp files are properly removed, we need:
1N/A** 1. A discipline to remove a file when the corresponding stream is closed.
1N/A** Care must be taken to close the file descriptor before removing the
1N/A** file because systems such as NT do not allow file removal while
1N/A** there is an open file handle.
1N/A** 2. An atexit() function is set up to close temp files when process exits.
1N/A** 3. On systems with O_TEMPORARY (e.g., NT), this is used to further ensure
1N/A** that temp files will be removed after the last handle is closed.
1N/A*/
1N/A
1N/Atypedef struct _file_s File_t;
1N/Astruct _file_s
1N/A{ File_t* next; /* link list */
1N/A Sfio_t* f; /* associated stream */
1N/A char name[1]; /* temp file name */
1N/A};
1N/A
1N/Astatic File_t* File; /* list pf temp files */
1N/A
1N/A#if __STD_C
1N/Astatic int _tmprmfile(Sfio_t* f, int type, Void_t* val, Sfdisc_t* disc)
1N/A#else
1N/Astatic int _tmprmfile(f, type, val, disc)
1N/ASfio_t* f;
1N/Aint type;
1N/AVoid_t* val;
1N/ASfdisc_t* disc;
1N/A#endif
1N/A{
1N/A reg File_t *ff, *last;
1N/A
1N/A NOTUSED(val);
1N/A
1N/A if(type == SF_DPOP) /* don't allow this to pop */
1N/A return -1;
1N/A
1N/A if(type == SF_CLOSING)
1N/A {
1N/A (void)vtmtxlock(_Sfmutex);
1N/A for(last = NIL(File_t*), ff = File; ff; last = ff, ff = ff->next)
1N/A if(ff->f == f)
1N/A break;
1N/A if(ff)
1N/A { if(!last)
1N/A File = ff->next;
1N/A else last->next = ff->next;
1N/A
1N/A if(_Sfnotify)
1N/A (*_Sfnotify)(f,SF_CLOSING,f->file);
1N/A CLOSE(f->file);
1N/A f->file = -1;
1N/A while(sysremovef(ff->name) < 0 && errno == EINTR)
1N/A errno = 0;
1N/A
1N/A free((Void_t*)ff);
1N/A }
1N/A (void)vtmtxunlock(_Sfmutex);
1N/A }
1N/A
1N/A return 0;
1N/A}
1N/A
1N/A#if __STD_C
1N/Astatic void _rmfiles(void)
1N/A#else
1N/Astatic void _rmfiles()
1N/A#endif
1N/A{ reg File_t *ff, *next;
1N/A
1N/A (void)vtmtxlock(_Sfmutex);
1N/A for(ff = File; ff; ff = next)
1N/A { next = ff->next;
1N/A _tmprmfile(ff->f, SF_CLOSING, NIL(Void_t*), ff->f->disc);
1N/A }
1N/A (void)vtmtxunlock(_Sfmutex);
1N/A}
1N/A
1N/Astatic Sfdisc_t Rmdisc =
1N/A { NIL(Sfread_f), NIL(Sfwrite_f), NIL(Sfseek_f), _tmprmfile, NIL(Sfdisc_t*) };
1N/A
1N/A#endif /*_tmp_rmfail*/
1N/A
1N/A#if __STD_C
1N/Astatic int _rmtmp(Sfio_t* f, char* file)
1N/A#else
1N/Astatic int _rmtmp(f, file)
1N/ASfio_t* f;
1N/Achar* file;
1N/A#endif
1N/A{
1N/A#if _tmp_rmfail /* remove only when stream is closed */
1N/A reg File_t* ff;
1N/A
1N/A if(!File)
1N/A atexit(_rmfiles);
1N/A
1N/A if(!(ff = (File_t*)malloc(sizeof(File_t)+strlen(file))) )
1N/A return -1;
1N/A (void)vtmtxlock(_Sfmutex);
1N/A ff->f = f;
1N/A strcpy(ff->name,file);
1N/A ff->next = File;
1N/A File = ff;
1N/A (void)vtmtxunlock(_Sfmutex);
1N/A
1N/A#else /* can remove now */
1N/A while(sysremovef(file) < 0 && errno == EINTR)
1N/A errno = 0;
1N/A#endif
1N/A
1N/A return 0;
1N/A}
1N/A
1N/A#if !_PACKAGE_ast
1N/A#define TMPDFLT "/tmp"
1N/Astatic char **Tmppath, **Tmpcur;
1N/A
1N/A#if __STD_C
1N/Achar** _sfgetpath(char* path)
1N/A#else
1N/Achar** _sfgetpath(path)
1N/Achar* path;
1N/A#endif
1N/A{ reg char *p, **dirs;
1N/A reg int n;
1N/A
1N/A if(!(path = getenv(path)) )
1N/A return NIL(char**);
1N/A
1N/A for(p = path, n = 0;;) /* count number of directories */
1N/A { while(*p == ':')
1N/A ++p;
1N/A if(*p == 0)
1N/A break;
1N/A n += 1;
1N/A while(*p && *p != ':') /* skip dir name */
1N/A ++p;
1N/A }
1N/A if(n == 0 || !(dirs = (char**)malloc((n+1)*sizeof(char*))) )
1N/A return NIL(char**);
1N/A if(!(p = (char*)malloc(strlen(path)+1)) )
1N/A { free(dirs);
1N/A return NIL(char**);
1N/A }
1N/A strcpy(p,path);
1N/A for(n = 0;; ++n)
1N/A { while(*p == ':')
1N/A ++p;
1N/A if(*p == 0)
1N/A break;
1N/A dirs[n] = p;
1N/A while(*p && *p != ':')
1N/A ++p;
1N/A if(*p == ':')
1N/A *p++ = 0;
1N/A }
1N/A dirs[n] = NIL(char*);
1N/A
1N/A return dirs;
1N/A}
1N/A
1N/A#endif /*!_PACKAGE_ast*/
1N/A
1N/A#if __STD_C
1N/Astatic int _tmpfd(Sfio_t* f)
1N/A#else
1N/Astatic int _tmpfd(f)
1N/ASfio_t* f;
1N/A#endif
1N/A{
1N/A reg char* file;
1N/A int fd;
1N/A
1N/A#if _PACKAGE_ast
1N/A if(!(file = pathtemp(NiL,PATH_MAX,NiL,"sf",&fd)))
1N/A return -1;
1N/A _rmtmp(f, file);
1N/A free(file);
1N/A#else
1N/A int t;
1N/A
1N/A /* set up path of dirs to create temp files */
1N/A if(!Tmppath && !(Tmppath = _sfgetpath("TMPPATH")) )
1N/A { if(!(Tmppath = (char**)malloc(2*sizeof(char*))) )
1N/A return -1;
1N/A if(!(file = getenv("TMPDIR")) )
1N/A file = TMPDFLT;
1N/A if(!(Tmppath[0] = (char*)malloc(strlen(file)+1)) )
1N/A { free(Tmppath);
1N/A Tmppath = NIL(char**);
1N/A return -1;
1N/A }
1N/A strcpy(Tmppath[0],file);
1N/A Tmppath[1] = NIL(char*);
1N/A }
1N/A
1N/A /* set current directory to create this temp file */
1N/A if(Tmpcur)
1N/A Tmpcur += 1;
1N/A if(!Tmpcur || !Tmpcur[0])
1N/A Tmpcur = Tmppath;
1N/A
1N/A fd = -1;
1N/A for(t = 0; t < 10; ++t)
1N/A { /* compute a random name */
1N/A static ulong Key, A;
1N/A if(A == 0 || t > 0) /* get a quasi-random coefficient */
1N/A { reg int r;
1N/A A = (ulong)time(NIL(time_t*)) ^ (((ulong)(&t)) >> 3);
1N/A if(Key == 0)
1N/A Key = (A >> 16) | ((A&0xffff)<<16);
1N/A A ^= Key;
1N/A if((r = (A-1) & 03) != 0) /* Knuth vol.2, page.16, Thm.A */
1N/A A += 4-r;
1N/A }
1N/A
1N/A Key = A*Key + 987654321;
1N/A file = sfprints("%s/sf%3.3.32lu.%3.3.32lu",
1N/A Tmpcur[0], (Key>>15)&0x7fff, Key&0x7fff);
1N/A if(!file)
1N/A return -1;
1N/A#if _has_oflags
1N/A if((fd = sysopenf(file,O_RDWR|O_CREAT|O_EXCL|O_TEMPORARY,SF_CREATMODE)) >= 0)
1N/A break;
1N/A#else
1N/A if((fd = sysopenf(file,O_RDONLY)) >= 0)
1N/A { /* file already exists */
1N/A CLOSE(fd);
1N/A fd = -1;
1N/A }
1N/A else if((fd = syscreatf(file,SF_CREATMODE)) >= 0)
1N/A { /* reopen for read and write */
1N/A CLOSE(fd);
1N/A if((fd = sysopenf(file,O_RDWR)) >= 0)
1N/A break;
1N/A
1N/A /* don't know what happened but must remove file */
1N/A while(sysremovef(file) < 0 && errno == EINTR)
1N/A errno = 0;
1N/A }
1N/A#endif /* _has_oflags */
1N/A }
1N/A if(fd >= 0)
1N/A _rmtmp(f, file);
1N/A#endif /* _PACKAGE_ast */
1N/A return fd;
1N/A}
1N/A
1N/A#if __STD_C
1N/Astatic int _tmpexcept(Sfio_t* f, int type, Void_t* val, Sfdisc_t* disc)
1N/A#else
1N/Astatic int _tmpexcept(f,type,val,disc)
1N/ASfio_t* f;
1N/Aint type;
1N/AVoid_t* val;
1N/ASfdisc_t* disc;
1N/A#endif
1N/A{
1N/A reg int fd, m;
1N/A reg Sfio_t* sf;
1N/A Sfio_t newf, savf;
1N/A void (*notifyf)_ARG_((Sfio_t*, int, void*));
1N/A
1N/A NOTUSED(val);
1N/A
1N/A /* the discipline needs to change only under the following exceptions */
1N/A if(type != SF_WRITE && type != SF_SEEK &&
1N/A type != SF_DPUSH && type != SF_DPOP && type != SF_DBUFFER)
1N/A return 0;
1N/A
1N/A /* notify function */
1N/A notifyf = _Sfnotify;
1N/A
1N/A /* try to create the temp file */
1N/A SFCLEAR(&newf,NIL(Vtmutex_t*));
1N/A newf.flags = SF_STATIC;
1N/A newf.mode = SF_AVAIL;
1N/A
1N/A if((fd = _tmpfd(f)) < 0 )
1N/A return -1;
1N/A
1N/A /* make sure that the notify function won't be called here since
1N/A we are only interested in creating the file, not the stream */
1N/A _Sfnotify = 0;
1N/A sf = sfnew(&newf,NIL(Void_t*),(size_t)SF_UNBOUND,fd,SF_READ|SF_WRITE);
1N/A _Sfnotify = notifyf;
1N/A if(!sf)
1N/A return -1;
1N/A
1N/A if(newf.mutex) /* don't need a mutex for this stream */
1N/A { (void)vtmtxclrlock(newf.mutex);
1N/A (void)vtmtxclose(newf.mutex);
1N/A newf.mutex = NIL(Vtmutex_t*);
1N/A }
1N/A
1N/A /* make sure that new stream has the same mode */
1N/A if((m = f->flags&(SF_READ|SF_WRITE)) != (SF_READ|SF_WRITE))
1N/A sfset(sf, ((~m)&(SF_READ|SF_WRITE)), 0);
1N/A sfset(sf, (f->mode&(SF_READ|SF_WRITE)), 1);
1N/A
1N/A /* now remake the old stream into the new image */
1N/A memcpy((Void_t*)(&savf), (Void_t*)f, sizeof(Sfio_t));
1N/A memcpy((Void_t*)f, (Void_t*)sf, sizeof(Sfio_t));
1N/A f->push = savf.push;
1N/A f->pool = savf.pool;
1N/A f->rsrv = savf.rsrv;
1N/A f->proc = savf.proc;
1N/A f->mutex = savf.mutex;
1N/A f->stdio = savf.stdio;
1N/A
1N/A if(savf.data)
1N/A { SFSTRSIZE(&savf);
1N/A if(!(savf.flags&SF_MALLOC) )
1N/A (void)sfsetbuf(f,(Void_t*)savf.data,savf.size);
1N/A if(savf.extent > 0)
1N/A (void)sfwrite(f,(Void_t*)savf.data,(size_t)savf.extent);
1N/A (void)sfseek(f,(Sfoff_t)(savf.next - savf.data),SEEK_SET);
1N/A if((savf.flags&SF_MALLOC) )
1N/A free((Void_t*)savf.data);
1N/A }
1N/A
1N/A /* announce change of status */
1N/A if(notifyf)
1N/A (*notifyf)(f, SF_NEW, (void*)((long)f->file));
1N/A
1N/A f->disc = disc->disc;
1N/A
1N/A /* erase all traces of newf */
1N/A newf.data = newf.endb = newf.endr = newf.endw = NIL(uchar*);
1N/A newf.file = -1;
1N/A sfclose(&newf);
1N/A
1N/A return 1;
1N/A}
1N/A
1N/A#if __STD_C
1N/ASfio_t* sftmp(size_t s)
1N/A#else
1N/ASfio_t* sftmp(s)
1N/Asize_t s;
1N/A#endif
1N/A{
1N/A Sfio_t* f;
1N/A static Sfdisc_t Tmpdisc =
1N/A { NIL(Sfread_f), NIL(Sfwrite_f), NIL(Sfseek_f), _tmpexcept,
1N/A#if _tmp_rmfail
1N/A &Rmdisc
1N/A#else
1N/A NIL(Sfdisc_t*)
1N/A#endif
1N/A };
1N/A
1N/A /* start with a memory resident stream */
1N/A if(!(f = sfnew(NIL(Sfio_t*),NIL(char*),s,-1,SF_STRING|SF_READ|SF_WRITE)) )
1N/A return NIL(Sfio_t*);
1N/A
1N/A if(s != (size_t)SF_UNBOUND) /* set up a discipline for out-of-bound, etc. */
1N/A f->disc = &Tmpdisc;
1N/A
1N/A /* make the file now */
1N/A if(s == 0 && _tmpexcept(f,SF_DPOP,NIL(Void_t*),f->disc) < 0)
1N/A { sfclose(f);
1N/A return NIL(Sfio_t*);
1N/A }
1N/A
1N/A return f;
1N/A}