views:

855

answers:

1

I have been programming an FTP server in my free time. But I have a problem with sending listings of a directory. I use the Unix format, also used by ls

drwxr-xr-x 28 kasper kasper 4096 2009-08-14 01:32 Music
drwxr-xr-x  4 kasper kasper 4096 2009-09-06 13:52 Pictures
drwxr-xr-x 14 kasper kasper 4096 2009-09-09 18:49 Source
drwxr-xr-x  2 kasper kasper 4096 2009-09-08 18:27 Videos

In my program, I stat the files they want and send the data over.
The fields of a stat struct are all special types:

struct stat {
 dev_t     st_dev;     /* ID of device containing file */
 ino_t     st_ino;     /* inode number */
 mode_t    st_mode;    /* protection */
 nlink_t   st_nlink;   /* number of hard links */
 uid_t     st_uid;     /* user ID of owner */
 gid_t     st_gid;     /* group ID of owner */
 dev_t     st_rdev;    /* device ID (if special file) */
 off_t     st_size;    /* total size, in bytes */
 blksize_t st_blksize; /* blocksize for file system I/O */
 blkcnt_t  st_blocks;  /* number of 512B blocks allocated */
 time_t    st_atime;   /* time of last access */
 time_t    st_mtime;   /* time of last modification */
 time_t    st_ctime;   /* time of last status change */
};

The relevant code for my question follows:

len = snprintf( statbuf, STAT_BUFFER_SIZE,
  "%crwxrwxrwx %lu %u %u %lld %s %s\r\n",
  S_ISDIR( filestats.st_mode ) ? 'd' : '-',
  (unsigned long ) filestats.st_nlink,
  filestats.st_uid,
  filestats.st_gid,
  (unsigned long long ) filestats.st_size,
  date,
  filename);

How do I portably and efficiently print these types? At first I did it without casts by guessing the correct format specifiers. Apart from being an annoying programming habit, this also meant my code wouldn't work on a 32 bit system. Now with the casts it seems to work, but on how many platforms?

Thanks in advance, Kasper.

+6  A: 

There isn't a fully portable way to do it, and it is a nuisance.

C99 provides a mechanism for built-in types like size_t with the %zu notation (and there are some extra, similar qualifiers).

It also provides the <inttypes.h> header with macros such as PRIX32 to define the correct qualifier for printing a 32-bit hexadecimal constant (in this case):

printf("32-bit integer: 0x%08" PRIX32 "\n", var_of_type_int32_t);

For the system-defined types (such as those defined by POSIX), AFAIK, there is no good way to handle them. So, what I do is take a flying guess at a 'safe' conversion and then print accordingly, including the cast, which is what you illustrate in the question. It is frustrating, but there is no better way that I know of. In case of doubt, and using C99, then conversion to 'unsigned long long' is pretty good; there could be a case for using a cast to uintmax_t and PRIXMAX or equivalent.

Jonathan Leffler
Couldn't have said it better. Using (u)intmax_t and PRI(d|u)MAX (or equivalent) may be the best solution for most problematic system-defined types.
Dan Moulding
I already knew those macro's but they looked so ugly to me. Thanks for the advice.
kmm
They're ugly, but they're the nearest thing to portable that I know of.
Jonathan Leffler
btw, the format specifier for `size_t` should be `%zu` as `size_t` is an unsigned type...
Christoph
@Christoph - yup: fixed.
Jonathan Leffler