Creating Temporary Files

This chapter will examine a number of ways that a temporary file can be created under UNIX. Each of these has its advantages and disadvantages. The tmpnam(3) function is discouraged and is covered only because you will encounter it in existing code. The remaining functions can be used in new software.

Using the tmpnam(3) Function

The tmpnam(3) function generates a pathname for a new temporary file but does not create the temporary file itself. Its function synopsis is as follows:

#include <stdio.h>

char *tmpnam(char *buf);  /* Discouraged */

This function generates a temporary pathname in the directory given by the macro name P_tmpdir (defined in <stdio.h>). The argument buf must be null or point to a character buffer of a minimum length of L_tmpnam bytes. When the argument buf is null, the function tmpnam(3) returns a pointer to an internal static buffer containing the name of the temporary file. When buf is not null, the buffer buf is populated with the pathname of the temporary file.

When it is successful, the function returns a valid pointer to buf or to an internal buffer. A null pointer is returned when the function fails, and errno contains the reason for the error.

Note

The function tmpnam(3) should not be used in new code. The disadvantages of this function include the fact that the temporary directory is hard-wired to the directory P_tmpdir and that filename generation is subject to race conditions on some UNIX platforms.


Using tmpnam(3) with a Null Argument

The argument to tmpnam(3) is a buffer pointer, which must be a minimum of L_tmpnam bytes in length. However, the argument can be specified as a null pointer, as is illustrated in the example program in Listing 8.1. Note, however, that when this is done, the pointer returned is valid only until the next call to tmpnam(3) is performed.

Code Listing 8.1. tmpnam.c—A Program Using tmpnam(3) with a Null Argument
1:   /* tmpnam.c */
2:
3:   #include <stdio.h>
4:   #include <stdlib.h>
5:   #include <unistd.h>
6:   #include <string.h>
7:   #include <errno.h>
8:
9:   int
10:  main(int argc,char *argv[]) {
11:      char *tmp_pathname;     /* Temp. File Pathname */
12:      FILE *tmpf = 0;         /* Opened temp. file */
13:      char cmd[256];
14:
15:      if ( !(tmp_pathname = tmpnam(NULL)) ) {
16:          fprintf(stderr,"%s: tmpnam(3)
",strerror(errno));
17:          abort();
18:      }
19:
20:      printf("Using temp file: %s
",tmp_pathname);
21:
22:      if ( !(tmpf = fopen(tmp_pathname,"w")) ) {
23:          fprintf(stderr,"%s: creating temp %s
",
24:              strerror(errno),tmp_pathname);
25:          abort();
26:      }
27:
28:      sprintf(cmd,"ls -l %s",tmp_pathname);
29:      system(cmd);
30:
31:      fclose(tmpf);           /* Close the temp file */
32:      unlink(tmp_pathname);   /* Release the temp file */
33:
34:      return 0;
35:  }
							

This program generates a temporary pathname in lines 15–18. Then the temporary file is created by calling fopen(3) in line 22. In lines 28–29, the temporary file is listed by a system(3) command, which invokes the ls(1) command. Finally, the temporary file is released in line 32 before the program exits.

Compiling and invoking the program yields the following results on a FreeBSD system:

$ make tmpnam
cc -c -D_POSIX_C_SOURCE=199309L -D_POSIX_SOURCE -Wall tmpnam.c
cc tmpnam.o -o tmpnam
$ ./tmpnam
Using temp file: /var/tmp/tmp.0.H49596
-rw-r--r--  1 me   mygrp  0 May  1 21:22 /var/tmp/tmp.0.H49596
$ ./tmpnam
Using temp file: /var/tmp/tmp.0.U49599
-rw-r--r--  1 me   mygrp  0 May  1 21:22 /var/tmp/tmp.0.U49599
$

The program ./tmpnam was invoked twice to demonstrate the differences in the generated temporary filename. Note that the pathname generated for your temporary filename will differ for different UNIX platforms.

Using tmpnam() with a Buffer

An improved way to use the tmpnam(3) function is to supply a buffer to the function, so that the generated pathname can be stored there indefinitely. When the argument to tmpnam(3) is null, the returned pathname string is only valid until the next call to the function. Listing 8.2 shows an example program that supplies its own buffer.

Code Listing 8.2. tmpnam2.c—A Program Using tmpnam(3) with a Supplied Buffer
1:   /* tmpnam2.c */
2:
3:   #include <stdio.h>
4:   #include <stdlib.h>
5:   #include <unistd.h>
6:   #include <string.h>
7:   #include <errno.h>
8:
9:   int
10:  main(int argc,char *argv[]) {
11:      char tmp_pathname[L_tmpnam]; /* Temp. pathname */
12:      FILE *tmpf = 0;         /* Opened temp. file */
13:      char cmd[256];
14:
15:      if ( !tmpnam(tmp_pathname) ) {
16:          fprintf(stderr,"%s: tmpnam(3)
",strerror(errno));
17:          abort();
18:      }
19:
20:      printf("Using temp file: %s
",tmp_pathname);
21:
22:      if ( !(tmpf = fopen(tmp_pathname,"w")) ) {
23:          fprintf(stderr,"%s: creating temp %s
",
24:              strerror(errno),tmp_pathname);
25:          abort();
26:      }
27:
28:      sprintf(cmd,"ls -l %s",tmp_pathname);
29:      system(cmd);
30:
31:      fclose(tmpf);           /* Close the temp file */
32:      unlink(tmp_pathname);   /* Release the temp file */
33:
34:      return 0;
35:  }

The program shown in Listing 8.2 is almost identical to the program shown in Listing 8.1. However, this time the buffer is declared in line 11 as an array with a length of L_tmpnam bytes and provided as an argument to the tmpnam(3) function in line 15.

Compiling and running the program yields the same result as before:

$ make tmpnam2
cc -c -D_POSIX_C_SOURCE=199309L -D_POSIX_SOURCE -Wall tmpnam2.c
cc tmpnam2.o -o tmpnam2
$ ./tmpnam2
Using temp file: /var/tmp/tmp.0.E49652
-rw-r--r--  1 wwg  wheel  0 May  1 21:37 /var/tmp/tmp.0.E49652
$

Using the mktemp(3) Function

Another function that is available for generating temporary filenames is the mktemp(3) function. Its synopsis is as follows:

#include <unistd.h>    /* <== Use for FreeBSD */
#include <stdlib.h>    /* <== Use for Solaris, AIX, Linux, HPUX, UnixWare 7 */
#include <stdio.h>     /* <== Use for SGI IRIX 6.5 */

char *mktemp(char *template);

The mktemp(3) function accepts as input a C string that acts as a pathname template. The last characters are specified as the character X and are replaced to generate a unique pathname. For this reason, never pass a C string constant as an argument to the function. For example, the argument template may contain the string "/tmp/temp.XXXX", allowing the last four X characters to be replaced to generate a unique filename.

The following example code shows how a temporary filename can be generated and displayed:

char template[256];            /* Holding buffer for the template */

strcpy(template,"/var/tmp/tmp.XXXX");
printf("A temp file is '%s'
",mktemp(template));

The pointer value returned is the same pointer template that was passed as an argument if the call is successful. Otherwise, a null pointer is returned and errno is set.

Warning

The X characters must be at the end of the string. Placing them in other positions will not work. For example, the string "/tmp/XXXX.tmp" will not work.


Using the mkstemp(3) Function

The mkstemp(3) function goes one step further than mktemp(3). It not only generates a temporary filename from the template given, but it creates and opens the temporary file. The function synopsis is as follows:

#include <unistd.h>    /* <== Use for FreeBSD */
#include <stdlib.h>    /* <== Use for Solaris, AIX, Linux, HPUX, UnixWare 7 */
#include <stdio.h>     /* <== Use for SGI IRIX 6.5 */

int mkstemp(char *template);

The rules for the template string are the same as the function mktemp(3). The function returns an open file descriptor when it is successful or -1 and an error code in errno if it fails.

The temporary file is created with read (S_IRUSR) and write (S_IWUSR) permissions for the owner only. The final permissions assigned are determined by the umask(2) value currently in effect, however. The following code shows how a temporary filename can be generated, created, and opened:

char template[256];            /* Holding buffer for the template */
int tmpf;                      /* Open temp. file descriptor */

strcpy(template,"/var/tmp/tmp.XXXX");
tmpf = mkstemp(template);      /* Create and open the temp. file */

Listing 8.3 demonstrates how the mkstemp(3) function can be used with the standard I/O functions.

Code Listing 8.3. mkstemp.c—A Program Using mkstemp(3) to Create a Temporary File
1:   /* mkstemp.c */
2:
3:   #include <stdio.h>
4:   #include <stdlib.h>
5:   #include <unistd.h>
6:   #include <string.h>
7:   #include <errno.h>
8:
9:   extern int mkstemp(char *template);
10:
11:  int
12:  main(int argc,char *argv[]) {
13:      char tf_path[64];       /* Temp. File Pathname */
14:      int tfd = -1;           /* File Descriptor */
15:      FILE *tmpf = 0;         /* Opened temp FILE */
16:
17:      /*
18:       * Initialize the temp. file template :
19:       */
20:      strcpy(tf_path,"/var/tmp/tmp.XXXXXX");
21:
22:      /*
23:       * Generate temp file pathname, create and open
24:       * the temporary file on file unit tfd :
25:       */
26:      if ( (tfd = mkstemp(tf_path)) < 0 ) {
27:          fprintf(stderr,"%s: generating a temp file name.
",
28:              strerror(errno));
29:          abort();
30:      }
31:
32:      printf("Using temp file: %s
",tf_path);
33:
34:      /*
35:       * Use standard I/O on temp. file :
36:       */
37:      tmpf = fdopen(tfd,"w+");
38:      fprintf(tmpf,"Written by PID=%ld
",(long)getpid());
39:      fclose(tmpf);
40:
41:      unlink(tf_path);    /* Release the temp. file */
42:
43:      return 0;
44:  }

The program shown in Listing 8.3 initializes the template in line 20 and then creates and opens the temporary file in line 26, where mkstemp(3) is called. To allow the standard I/O routines to be used, the function fdopen(3) is called in line 37 with the open file descriptor tfd. Then a write to the temporary file is performed in line 38 using fprintf(3).

Compiling and running the program under FreeBSD yields the following result:

$ make mkstemp
cc -c -D_POSIX_C_SOURCE=199309L -D_POSIX_SOURCE -Wall mkstemp.c
cc mkstemp.o -o mkstemp
$ ./mkstemp
Using temp file: /var/tmp/tmp.m49798
$

The temporary file generated and used for this run was the file /var/tmp/tmp.m49798, which agrees with the template used in line 20 of the program.

The program in Listing 8.3 used a temporary filename template, as shown below:

strcpy(tf_path,"/var/tmp/tmp.XXXXXX");

The characters preceding the Xs may be modified to allow more than one temporary file in your program. For example, the first and second temporary files might use the following templates instead:

strcpy(template01,"/var/tmp/01-XXXXXX");
strcpy(template02,"/var/tmp/02-XXXXXX");

This technique is not absolutely necessary for using multiple temporary files, but it can be helpful when debugging your program. When you see temporary files named in this fashion in the /var/tmp directory, you will know that the temporary file starting with 01- is the first temporary file that the application created and that 02- indicates the second.

Using the mkstemps(3) Function

FreeBSD supports the mkstemps(3) function, which permits a suffix to be appended to the temporary filename. In all other ways, it is similar to mkstemp(3). The synopsis for it is as follows:

#include <unistd.h>

int mkstemps(char *template, int suffixlen);

The template argument is the same as the template argument for mkstemp(3), except that the X characters no longer need to be at the end of the string. The argument suffixlen indicates how many characters at the end of the string represent the suffix. The following code illustrates:

char template[256];            /* Holding buffer for the template */
int tmpf;                      /* Open temp. file descriptor */

strcpy(template,"/var/tmp/XXXX.tmp");
tmpf = mkstemps(template,4);   /* Create and open the temp. file */

In this example, the last four characters form the suffix. The X characters can now be at the start or middle of the temporary file's basename.

Warning

The function mkstemps(3) is not universally available. For this reason, it is not recommended for portable code.


Using the tmpfile(3) Function

The tmpfile(3) function creates and opens a temporary file, returning a FILE stream pointer instead of a file descriptor. The following is its synopsis:

#include <stdio.h>

FILE *tmpfile(void);

Listing 8.4 shows a short program that creates a temporary file, writes one line to it, and then reads back one line from it.

Code Listing 8.4. tmpfile.c—A Program Using the tmpfile(3) Function
1:   /* tmpfile.c */
2:
3:   #include <stdio.h>
4:   #include <unistd.h>
5:   #include <string.h>
6:   #include <errno.h>
7:
8:   int
9:   main(int argc,char *argv[]) {
10:      FILE *tmpf = 0;     /* Opened temp. file */
11:      char buf[128];      /* Input buffer */
12:
13:      if ( !(tmpf = tmpfile()) ) {
14:          fprintf(stderr,"%s: generating a temp file name.
",
15:              strerror(errno));
16:          abort();
17:      }
18:
19:      fprintf(tmpf,"PID %ld was here.
",(long)getpid());
20:      fflush(tmpf);
21:
22:      rewind(tmpf);
23:      fgets(buf,sizeof buf,tmpf);
24:
25:      printf("Read back: %s
",buf);
26:
27:      fclose(tmpf);
28:
29:      return 0;
30:  }
						

The program does not show a pathname for the temporary file, nor does it call unlink(2) to remove it later. This is because the file has already been deleted. Even so, it remains available to you as long as the file remains open. The disk space is automatically reclaimed by the UNIX kernel when the file is closed. This saves you from having to make sure that it is deleted later.

Compiling and running this program under FreeBSD looks like this:

$ make tmpfile
cc -c -D_POSIX_C_SOURCE=199309L -D_POSIX_SOURCE -Wall tmpfile.c
cc tmpfile.o -o tmpfile
$ ./tmpfile
Read back: PID 10058 was here.

$

Notice the extra line feed displayed following the line starting with Read back:. This is due to the line feed written in line 19 and then included in the buffer from the fgets(3) call in line 23.

Using the tempnam(3) Function

The last temporary file function that will be covered in this chapter is the tempnam(3) function. Its function synopsis is as follows:

#include <stdio.h>

char *tempnam(const char *dir, const char *prefix);

This function accepts two arguments. The second argument, prefix, is optional and may be supplied with a null pointer. However, when it is not null, it points to a C string that specifies up to five characters that can be used as a prefix to the temporary filename generated.

The first argument, dir, is more complicated. It can be specified as a null pointer, or it may point to a string specifying a directory that the programmer has chosen. Whether dir is null or not, the following procedure determines the final directory chosen for the temporary filename:

  1. Attempt to obtain exported environment variable TMPDIR. If this variable is defined and it specifies a directory that is writable to the current process, then this directory will be used. In effect, the TMPDIR variable overrides the program's choice of directory.

  2. When step 1 fails, the dir argument of the tempnam(3) call is examined. If this argument is not a null pointer, then this directory will be used if the specified directory exists.

  3. When step 2 is not satisfied, the directory specified by the stdio.h macro P_tmpdir is tried.

  4. As a last resort, the directory /tmp will be used.

Normally, step 1 or 2 specifies the directory. Steps 3 and 4 represent fallback directory names.

The returned pointer is to a dynamically allocated pathname string, or a null pointer if it fails. Be certain to free this returned pointer later, when your program is finished using this pathname. Note that no file is created; only the temporary pathname is created by tempnam(3).

Listing 8.5 shows a short program that uses the tempnam(3) function.

Code Listing 8.5. tempnam.c—A Program Using the tempnam(3) Function
1:   /* tempnam.c */
2:
3:   #include <stdio.h>
4:   #include <stdlib.h>
5:   #include <unistd.h>
6:   #include <string.h>
7:   #include <errno.h>
8:
9:   extern char *tempnam(const char *tmpdir, const char *prefix);
10:
11:  int
12:  main(int argc,char *argv[]) {
13:      char *tf_path = NULL;       /* Temp. File Pathname */
14:      FILE *tmpf = 0;             /* Temp. File stream */
15:
16:      if ( !(tf_path = tempnam("./my_tmp","tmp-")) ) {
17:          fprintf(stderr,"%s: generating a temp file name.
",
18:              strerror(errno));
19:          abort();
20:      }
21:
22:      printf("Temp. file name is %s
",tf_path);
23:
24:      if ( !(tmpf = fopen(tf_path,"w+")) ) {
25:          fprintf(stderr,"%s: opening %s for I/O
",
26:              strerror(errno),tf_path);
27:          abort();
28:      }
29:
30:      fprintf(tmpf,"PID %ld was here.
",(long)getpid());
31:      fclose(tmpf);
32:
33:      unlink(tf_path);            /* Release the temp file */
34:      free(tf_path);              /* Free allocated string */
35:
36:      return 0;
37:  }

In line 16 this program uses tempnam(3) to generate a pathname to be used for a temporary file. The temporary file is created and opened in line 24. Notice that the pathname string must be freed, since it is dynamically allocated (see line 34).

To test the TMPDIR environment variable, the program can be run and tested as follows:

$ make tempnam
cc -c -D_POSIX_C_SOURCE=199309L -D_POSIX_SOURCE -Wall tempnam.c
cc tempnam.o -o tempnam
$ TMPDIR=/tmp ./tempnam
Temp. file name is /tmp/tmp-g50054
$

Note that the pathname generated uses the directory /tmp as was given in the TMPDIR environment variable. If you look at line 16, the program would normally create the temporary file in subdirectory ./my_tmp. However, the TMPDIR environment variable successfully overrode that choice.

Now run the same program without TMPDIR defined:

$ unset TMPDIR
$ ./tempnam
Temp. file name is ./my_tmp/tmp-D50059
No such file or directory: opening ./my_tmp/tmp-D50059 for I/O
Abort trap - core dumped
$

In this case, the fopen(3) call failed because the subdirectory ./my_tmp does not exist yet. If you create it now and repeat the test, you will obtain the following result:

$ mkdir ./my_tmp
$ ./tempnam
Temp. file name is ./my_tmp/tmp-a50061
$

This time, the program ./tempnam is successful at creating a temporary file in the subdirectory ./my_tmp. This comes from the specification in line 16 of Listing 8.5.

If you remove the permissions on your ./my_tmp directory, you can test the fallback plans for tempnam(3):

$ chmod 0 my_tmp
$ ./tempnam
Temp. file name is /var/tmp/tmp-w50063
$ ls -l my_tmp
ls: my_tmp: Permission denied
$

The chmod(1) command takes all permissions away from the subdirectory my_tmp. When the program is run, the directory /var/tmp is used instead for the temporary filename. This agrees with FreeBSD's P_tmpdir macro value.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.147.238.70