Operating on a file

Updated: April 19, 2023

If we are handling a file, then we proceed to io_read_file(), which has changed slightly from the previous version:

...

// 1) odd inodes are GIF files, even inodes are text files
if (ocb -> base.attr -> base.inode & 1) {
  if (!ocb -> output) {
    ...
    // 2) allocate the input and output structures as before
    ...

    sprintf (string, "%0*d", optd, ocb -> base.attr -> count++);
    (*ocb -> render) (string, input, optx, opty);
    ocb -> size = ocb -> base.attr -> base.nbytes = encode_image (
      input, optx, opty, ocb -> output);

    // 3) note the changes to write_file()
    if (optS) {
      write_file (optS, ocb -> base.attr -> base.inode / 2 - 1, 
                 ocb -> base.attr -> count);
      }
  }
} else { // 4) even, so must be the text attribute
  int     tmp;  // limited scope

  ocb -> base.attr -> count = 
      attr_gif [ocb -> base.attr -> base.inode / 2 - 1].count;
  tmp = sprintf (string, "%0*d\n", optd, ocb -> base.attr -> count);
  if (ocb -> output) {
    free (ocb -> output);
  }
  ocb -> output = strdup (string);
  ocb -> size = tmp;
}

Notice a few things:

  1. We determine if we are dealing with the GIF-encoded file or the text file by looking at the inode member of the attributes structure. This is why we made the inodes odd for GIF-encoded and even for text, so that we could tell them apart easily.
  2. Code not shown for brevity, no change from previous.
  3. I've added an extra parameter to write_file(), namely the counter number. This lets write_file() seek into the correct spot in the persistent counter file and make a tiny write() rather than writing out the entire file.
  4. If we are dealing with the text file, then we need to get the count. However, the “real” value of the count is only maintained in the GIF-encoded file's attributes structure. Therefore, we need to use the inode as an index into the array of GIF-encoded attributes structures in order to find the correct one. This is why we made the inodes sequential, so that there's a direct mapping between the inode number and the index for the array of either attributes structure. Also notice that we check to see if we already have memory allocated for the string. If so, we free() it first.

What might appear to be “clever” use of inodes is in fact standard programming practice. When you think about it, a disk-based filesystem makes use of the inodes in a similar manner; it uses them to find the disk blocks on the media.