0

I need the integer file descriptor from a temporary file to be used in mmap. This need may need to change if there's no simple way to do this while still being standards compliant.

I originally got a FILE stream using:

FILE *tmpfile();

and then used...

int fileno(FILE foo);

All was good until I noticed the following in the man page for fileno.

CONFORMING TO
   The functions clearerr(), feof(), and ferror() conform to C89 and C99.

ie fileno is not part of c99. I've had a look online for a simple way to get a temporary filename or it's file descriptor in a way that doesn't throw errors when using

-std=c99 -pedantic

and so far the best thing I've found is:

http://www.di-mgt.com.au/c_function_to_create_temp_file.html

I'd love to know what other people are doing to get around this and is there a man page that I've missed somewhere or something obvious I can use in c99 that I've overlooked?

Update: I wrote a small test program to see where I went wrong. Please forgive the lack of error checking, I'm trying to keep it short. I've run this using clang and gcc on a Debian box:

#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>
#include <assert.h>
#include <sys/mman.h>

int main(int argc, const char* const argv[]) 
{   
  const char *fname = argv[1];
  /*  
   FILE *stream  = fopen("foo", "wb+");
   -std=c99 -pedantic fails on fileno
   int fd = fileno(stream);
  */
  int fd ;
  if((fd = open(fname, O_RDWR| O_CREAT, 0644)) < 0) {
    fprintf(stderr, "fd=%i, failed: %s\n",fd, strerror (errno));
    exit(EXIT_FAILURE);
  }   
  size_t fsize = 27; 
  char *buf;
  lseek (fd, fsize  - 1, SEEK_SET);
  write (fd, "", 1); 
  lseek (fd, 0, SEEK_SET);
  buf = mmap(0, fsize, PROT_WRITE, MAP_SHARED, fd, 0); 
  size_t i = 0;
  for(i = 0; i < fsize; i++) {
    buf[i] = i % 26 + 97; 
  }   
  buf[26] = '\n';
  munmap(buf, fsize);
  close(fd);
  return argc;//Avoid warnings about unused variables
} 

The run it with either with clang or gcc using Beware the rm -f at the start of the command.

rm -f foo mmap; clang -Wall -Wextra -std=c99 -pedantic-errors -Wmissing-prototypes -Wstrict-prototypes -Wold-style-definition mmap.c -o mmap;./mmap foo;cat foo|wc -c

rm -f foo mmap; gcc   -Wall -Wextra -std=c99 -pedantic-errors -Wmissing-prototypes -Wstrict-prototypes -Wold-style-definition mmap.c -o mmap;./mmap foo;cat foo|wc -c

This works which means I'm missing something about the use of -std=c99 -pedantic because I would expect it to fail miserably when I tried to include any non standard header ie in this case I would have expected errors trying to include these...

#include <fcntl.h>
#include <unistd.h>
#include <sys/mman.h>

I'd be interested in knowing why the above programs compiles ie is something being set in the headers that's turning off warnings or am I abusing gcc?

Harry
  • 11,298
  • 1
  • 29
  • 43
  • 1
    The lower-level file descriptors are a POSIX thing. – Some programmer dude Sep 10 '14 at 07:33
  • 1
    You do realize that `mmap()` is not C99, it's POSIX? So there seems to be a contradiction here. You can't use `mmap()` if you only want to use things that are standard in C99. – unwind Sep 10 '14 at 07:35
  • This started with the following warning: warning: implicit declaration of function ‘fileno’. I'm not getting any warnings using mmap and I've got -std=c99 -pedantic so I've likely done something weird in my makefile. – Harry Sep 10 '14 at 07:49

2 Answers2

4

You can't achieve what you want.

The documentation for -pedantic says:

Issue all the warnings demanded by strict ISO C and ISO C++; reject all programs that use forbidden extensions, and some other programs that do not follow ISO C and ISO C++. For ISO C, follows the version of the ISO C standard specified by any -std option used.

Integer file descriptors are not part of the C standard. They are from POSIX. Also, there is no mmap in the C standard, it comes from POSIX too. So the -pedantic flag is designed to not allow you to do what you want to do. I just checked the header files and compiler configuration on my system and they explicitly exclude (many, but not all) POSIX functions if you're compiling with gcc -std=c99 -pedantic.

Are you sure you really want to use that flag?

To create a temporary file with standard C use tmpfile then instead of mmap you need to read and write the file with normal fread and fwrite.

Art
  • 19,807
  • 1
  • 34
  • 60
  • `-pedantic` doesn't prevent the standard headers from declaring the functions in question, it's `-std=c99`. Should work with a `#define _POSIX_C_SOURCE 1` before the first `#include` (even with `-std=c99 -pedantic-errors`). Additional library functions aren't "forbidden extensions" (e.g. converting from `void *` to a function pointer is). – mafso Sep 10 '14 at 08:56
  • Sure, you can work around `-pedantic`. But the question then still remains: "Are you sure you really want to use that flag?". Why create a problem and then solve it instead of not creating the problem in the first place? – Art Sep 10 '14 at 08:59
  • 1
    My point was, that `-pedantic` isn't the problem, but `-std=c99`. Either you use `-std=gnu99` (which predefines some feature test macros) or define the feature test macros yourself. – mafso Sep 10 '14 at 09:05
  • As a possible motivation: You may want to use POSIX extensions, but no GNU extensions. – mafso Sep 10 '14 at 09:09
0

Via sort-bed, here's one way that populates a fileName pointer with a temporary filename within path and returns a FILE*:

FILE *
createTmpFile(char const* path, char** fileName)
{
  FILE* fp;
  int fd;
  char* tmpl;

  if (path == NULL)
      {
          fileName = NULL;
          return tmpfile();
      }

  tmpl = (char*)malloc(1 + strlen(path) + L_tmpnam);
  strcpy(tmpl, path);
  strcpy(tmpl+strlen(path), "/sb.XXXXXX");
  fd = mkstemp(tmpl);
  if(fd == -1)
      {
          fprintf(stderr, "unable to create temp file!\n");
          return NULL;
      }
  fp = fdopen(fd, "wb+");
  *fileName = (char*)malloc(strlen(tmpl) + 1);
  strcpy(*fileName, tmpl);
  free(tmpl);
  return fp;
}
Alex Reynolds
  • 95,983
  • 54
  • 240
  • 345