3
votes

I've got some code that was working on a server until it was moved to a different system. The problem seems to be here:

given a structure I've defined elsewhere:

user1_type user1;              /* structure containing user data */
user1_type *user1_ptr=&user1;

this routine appends the record to the end of the file

if ((dfile=fopen(filename,"ab+"))==NULL)  
  error_message("Unable to open file for append.",filename,1);
 else { /* append data */
 user1.recid=ftell(dfile);               /* update file position */

 fwrite(user1_ptr,sizeof(user1),1,dfile);
 fflush(dfile);
 fclose(dfile);

I can confirm the data gets appended in the file, but the value of user1.recid always returns 0 - any ideas why?

UPDATE: Looks like the issue is not all implementations require a fseek() after fopen(). I obviously need an fseek(dfile,0,SEEK_END); before I do a ftell() when appending. But if I want to read from the beginning of a text or binary file, is it also customary to place a fseek() right after fopen? Does it vary depending upon the file type?

3
does ftell return a long or a 64. How is user1 defined? - cleblanc
@cleblanc long int ftell(FILE *stream) - chux - Reinstate Monica
typedef struct { long recid; char dataid[5]; /* USER1 */ .. } user1_type; - Trent Three
From the docs for ftell() that I use: "Note that when a file is opened for appending data, the current file position is determined by the last I/O operation, not by where the next write would occur." You must call fseek() first. - Hans Passant
apparently on some implementations the file pos for a+b is set at EOF without a seek being needed (FreeBSD) - Trent Three

3 Answers

3
votes

From MSDN's documentation of ftell

The position returned by ftell() is expressed as an offset relative to the beginning of the stream

If no I/O operation has yet occurred on a file opened for appending, the file position is the beginning of the file.

This gives you an offset of 0 relative to the beginning.

So when you call user1.recid=ftell(dfile); no I/O operation has occurred on the stream yet so ftell() returns 0 indicating the file pointer position is at the begining.

3
votes

This behavior of ftell(dfile) here would be implementation defined. From C11 7.21.3 (similar wording in previous C standards):

If a file can support positioning requests (such as a disk file, as opposed to a terminal), then a file position indicator associated with the stream is positioned at the start (character number zero) of the file, unless the file is opened with append mode in which case it is implementation-defined whether the file position indicator is initially positioned at the beginning or the end of the file.

2
votes

...so (in addition to bkVnet's answer) you must use fseek(dfile,0,SEEK_END) to seek to the end, ask ftell the position and divide that by sizeof(user1_type) to get the record id (i.e. the number of records already in the file, so plus 1 for the new record).