kolibrios/programs/develop/open watcom/trunk/clib/streamio/tmpfl.c
Sergey Semyonov (Serge) 3be3768ce0 update CLib
git-svn-id: svn://kolibrios.org@614 a494cfbc-eb01-0410-851d-a64ba20cac60
2007-08-28 10:31:46 +00:00

172 lines
6.3 KiB
C

/****************************************************************************
*
* Open Watcom Project
*
* Portions Copyright (c) 1983-2002 Sybase, Inc. All Rights Reserved.
*
* ========================================================================
*
* This file contains Original Code and/or Modifications of Original
* Code as defined in and that are subject to the Sybase Open Watcom
* Public License version 1.0 (the 'License'). You may not use this file
* except in compliance with the License. BY USING THIS FILE YOU AGREE TO
* ALL TERMS AND CONDITIONS OF THE LICENSE. A copy of the License is
* provided with the Original Code and Modifications, and is also
* available at www.sybase.com/developer/opensource.
*
* The Original Code and all software distributed under the License are
* distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
* EXPRESS OR IMPLIED, AND SYBASE AND ALL CONTRIBUTORS HEREBY DISCLAIM
* ALL SUCH WARRANTIES, INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR
* NON-INFRINGEMENT. Please see the License for the specific language
* governing rights and limitations under the License.
*
* ========================================================================
*
* Description: Platform independent tmpfile() implementation.
*
****************************************************************************/
#include "variety.h"
#include "rtinit.h"
#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
#include <fcntl.h>
#include <direct.h>
#include <string.h>
#include <process.h>
#include <unistd.h>
#include <errno.h>
#include "rtdata.h"
#include "tmpfname.h"
#include "seterrno.h"
#include "openmode.h"
#define OPEN_MODE (O_RDWR | O_CREAT | O_BINARY)
#define PMODE (S_IREAD | S_IWRITE)
/* Netware doesn't define these */
/* Symbolic constants for the access() function */
#if !defined( F_OK )
#define R_OK 4 /* Test for read permission */
#define W_OK 2 /* Test for write permission */
#define X_OK 1 /* Test for execute permission */
#define F_OK 0 /* Test for existence of file */
#endif
extern void __MkTmpFile( char *buf, int num );
extern void __RmTmpFile( FILE *fp );
extern void (*__RmTmpFileFn)( FILE *fp );
char __tmpfnext = _TMP_INIT_CHAR;
_WCRTLINK FILE *tmpfile( void ) /* create a temporary file */
{
int hdl;
int old_errno;
int our_errno;
char suffix1;
char suffix2;
FILE *fp;
char name1[PATH_MAX + _TMPFNAME_LENGTH + 1];
char name2[PATH_MAX + _TMPFNAME_LENGTH + 1];
old_errno = _RWD_errno;
suffix1 = 0;
for( ;; ) {
// Part I
for( ;; ) {
__MkTmpFile( name1, suffix1 );
// if a file by this name does not exist
if( access( name1, F_OK ) != 0 ) {
// then let's try to create it
hdl = sopen( name1, OPEN_MODE, OPENMODE_DENY_COMPAT, PMODE );
// if we created it then continue with part II
if( hdl != -1 ) break;
__set_errno( EAGAIN );
}
suffix1++;
// give up after _TMP_INIT_CHAR tries JBS 99/10/26
if( suffix1 >= _TMP_INIT_CHAR ) return NULL;
}
close( hdl );
// Part II
/* we now have a empty file. Let's try to rename it
rename should be an atomic operation in the operating system
so if it succeeds we can be sure no one else has this file.
Consider the following sequence:
task1: access x.y => file does not exist
task2: access x.y => file does not exist
task1: fopen x.y => succeeds
task2: fopen x.y => succeeds (now have both tasks with x.y open)
task1: rename x.y to y.y => succeeds, can use this file
task2: rename x.y to y.y => fails (because x.y no longer exists)
task2: start over again to get a new file name
task2: succeeds second time around since no more race condition
with task1.
*/
suffix2 = _RWD_tmpfnext; // only one of these per process
for( ;; ) {
if( suffix2 == suffix1 ) {
suffix2++;
}
__MkTmpFile( name2, suffix2 );
if( rename( name1, name2 ) == 0 ) { // if rename worked
// The file is now ours. Let's try to open it.
fp = fopen( name2, "wb+" );
if( fp != NULL ) {
fp->_flag |= _TMPFIL;
_FP_TMPFCHAR(fp) = suffix2;
__set_errno( old_errno );
return( fp );
}
// We couldn't open it, probably because we have run out of handles.
// Remove the renamed file.
our_errno = errno;
remove( name2 );
__set_errno( our_errno );
return( NULL );
}
// The rename didn't work or we couldn't open the renamed file.
// One of two possibilities:
// (1) The "to" name already exists.
// (2) Another process renamed it away from us.
// Check for case (2).
// Quit if "from" file is gone and start over.
if( access( name1, F_OK ) != 0 ) break;
// Must be case (1). Try another "to" name.
++suffix2;
if( suffix2 == 0 ) {
suffix2 = _TMP_INIT_CHAR;
}
_RWD_tmpfnext = suffix2; // update for all processes
}
}
}
/* tmpfil() pulls in a lot of overhead that many programs do not need. But */
/* since temp files are removed on program shutdown, the code to remove */
/* them would always get linked in even if the program never heard of temp */
/* files. Since we know that temporary files can _only_ be created through */
/* tmpfile(), we can have a dummy __RmTmpFile() by default and use the */
/* real thing only if tmpfil() was called. */
void __Init_Tmpfl( void )
{
// Just assign the function address
__RmTmpFileFn = __RmTmpFile;
}
AXI( __Init_Tmpfl, INIT_PRIORITY_RUNTIME )