2

I need to store some logs in a file that can grow with every execution. A logical way would be to use a+ option when opening because using w+ would truncate the file. However, with the a+ option (Tcl 8.4) I cannot write anywhere in the file. seek works fine. I can verify that the pointer was moved using tell. But the output is always done at the tail end of the file.

Is there any way to resolve this? I.e. having the ability to seek and write in any place and also preserve the old file at the open.

ilya1725
  • 4,496
  • 7
  • 43
  • 68
  • Note that `open` supports another way of telling it the access mode , for instance you could call it like this: `open $fname {RDWR CREAT}` to get what you want. But really `r+` is the way to go as @slebtman suggested. – kostix Nov 06 '12 at 08:01

3 Answers3

4

In Tcl 8.5, the behavior of Tcl on Unix was changed so that the O_APPEND flag is passed to the open() system call. This causes the OS to always append the data to the file, and is inherited when the FD is passed to subprocesses; for logs, it is exactly the right thing. (In 8.4 and before, and in all versions on Windows, the behavior is simulated inside Tcl's file channel implementation, which will internally seek() to the end immediately before the write(); that obviously is subject to potential problems with race conditions when there are multiple processes logging to the same file and is definitely unsafe when the FD is passed to subprocesses.) You can manage truncation of the opened file with chan truncate (new in 8.5), which works just fine on a+-opened files.

If you do not want the seek-to-end behavior, you should not use a+ (or a). Try r+ or some combination of flags, like this:

set f [open $filename {RDWR CREAT}]

For comparison, the a+ option is now exactly the same as the flags RDWR CREAT APPEND, and not all combinations of longer flags can be described by short form flag specifiers. If you're not specifying APPEND, you'll need to do the seek $f 0 end yourself (and watch out for problems with multiple processes if you're appending to logs; that's when APPEND becomes required and exceptionally hard to correctly simulate any other way).

Donal Fellows
  • 133,037
  • 18
  • 149
  • 215
  • I've just found the (really ill-documented) `FILE_APPEND_DATA` flag to `CreateFile` might allow for proper appending on Windows. Or maybe not; it's really quite tricky to figure out what some things in the Win32 API actually do from its documentation… – Donal Fellows Nov 06 '12 at 11:32
3

Open with r+ - it opens in read mode (thus not turncating the file) but allows writing as well.

See the documentation of open for more info: http://www.tcl.tk/man/tcl8.5/TclCmd/open.htm

slebetman
  • 109,858
  • 19
  • 140
  • 171
0

I have verified that using the a+ option allow me to read/write anywhere in the file. However, by writing in the middle (or at the beginning) of a file, I overwrite the data there, not inserting. The following code illustrate that point:

#!/usr/bin/env tclsh

# Open the file, with truncation
set f [open foo w]
puts $f "one"
puts $f "two"
close $f

# Open again, with a+ ==> read/write/append
set f [open foo a+]
puts $f "three" ;# This goes to the end of the file
seek $f 4       ;# Seek to the beginning of the word "two"
puts $f "2.0"   ;# Overwrite the word "two"
close $f

# Open and verify the contents
set f [open foo r]
puts [read $f]
close $f

Output:

one
2.0
three

If you are looking to insert in the middle of the file, you might want to look at the fileutil package, which contains the ::fileutil::insertIntoFile command.

Hai Vu
  • 37,849
  • 11
  • 66
  • 93