1

I'm writing an implementation of Ex (hopefully Vi eventually) as a personal project. I need to somehow store an identifier for the file associated with each buffer. In terms of security/race conditions and performance (and convention, if there is one), should I store the name of the file as given by the user, the file descriptor as returned by open(), or the file pointer as returned by fopen()/fdopen()?

These are the methods I came up with, but I'm entirely open to any others.

Mac O'Brien
  • 113
  • 3
  • 1
    How would you desire to handle a hard link'ed file? `touch foo; ln foo bar; ex foo` and then in another window `ex bar`? –  Nov 05 '14 at 03:33
  • The method I'd considered was checking if the file had been written to externally before writing. So `:w` would cause the editor to check the last modification time and warn if there'd been an intermittent edit. – Mac O'Brien Nov 05 '14 at 03:41
  • 1
    Have you looked at [How to be notified of file/directory change in C/C++, ideally using POSIX](http://stackoverflow.com/q/61253/289086) (some interesting possibilities there... though this is getting more into an aside). Its more a question of "if you have the same file open with two different names in different buffers... is this being considered?" –  Nov 05 '14 at 03:47
  • Just looked at it. The handling of that situation would be different depending on whether it was two buffers in the same editor (in which case I could just check the inode) or two different instances, which would require some server-client stuff. – Mac O'Brien Nov 05 '14 at 04:00

1 Answers1

1

After further thought (and your comment), I realise there's a solid behavioural basis for choosing.

If I:

  • open an existing file in your editor
  • begin altering it
  • realise I want a backup and move (not copy) the original
  • save my modifications

should it alter the backup (the original inode with a new name)? Or write the altered file to the original name, leaving the backup alone?

I think the latter, which implies it shouldn't keep the original file open as it won't be able to use that file descriptor/FILE* safely anyway. You might decide you want different behaviour, though.


Well, what state do ex (or vi) need to track for each file?

Off the top of my head, vim needs:

  1. file name
  2. file descriptor or FILE*

    could this be -1 or NULL if it isn't open? (are files in the buffer list held open even when not displayed? I don't know, but it might be reasonable to close files and preserve an entry for them)

  3. swapfile - should the fd above refer to this? do we need to track the swapfile name as well?

  4. modification state
  5. last read stat of the original file (so you can tell if it changed before overwriting)

    or maybe just an "underlying file has changed" flag, if you want to use file change notifications

  6. whether it's read-only or read-write (you could just try to write and get an error, but ... ick)

  7. line-ending convention, encoding

Note that I can have multiple buffers looking at different positions in the same file, and possibly with different settings (syntax highlighting, line numbers etc.) so those can't be properties of the file itself.

Now, unless you can robustly infer all that from a single file descriptor, I think you need some struct storing all of that (and possibly more).

Useless
  • 12,380
  • 2
  • 34
  • 46
  • I currently have a `buffer_t` that has 1, 4, 5, and 6, and for the time being I'm enforcing `\n` and UTF-8 (obviously this is not a long-term solution). I was wondering specifically about 2 -- if I edit a file, would it be better to simply read in the data, close the file and open it later for writing, or to hold a `FILE *` / file descriptor the entire time? – Mac O'Brien Nov 05 '14 at 16:18
  • That's a perfect example. I could also hold onto the inode and use that to alert the user if the file name has changed or it's been deleted. – Mac O'Brien Nov 05 '14 at 16:42
  • Can you get back to the filename from the inode? You'd effectively have to iterate over all hard links to the inode, and I wasn't aware they were linked that way. – Useless Nov 05 '14 at 16:43
  • Good point. Probably better to just hold onto the `struct stat *` and check that things haven't changed. – Mac O'Brien Nov 05 '14 at 16:50