Task 310: .INFO File Format

Task 310: .INFO File Format

1. List of all the properties of this file format intrinsic to its file system

Based on the GNU Info file format specification, the properties (key components and structures) are:

  • Preamble: The initial header containing the magic byte (0x1F) and basic file identification, such as "Info: (filename)dir," followed by a separator.
  • Indirect table: A list of subfile names and byte offsets for large Info files split into multiple parts.
  • Tag table: A table listing node names and their byte offsets in the file for quick navigation.
  • Local variables: A section at the end of the file containing key-value pairs for configuration, such as "dircategory" or "direntry".
  • Regular nodes: The main content units, each starting with a header like "File: filename, Node: nodename, Up: upnode, Next: nextnode, Prev: prevnode", followed by the node body.
  • Menu: A list of entries within a node for navigation to other nodes, formatted as "* Menu:" followed by "* node:: description".
  • Image: Embedded image data within nodes, specified with tags like "* Image: [attributes]".
  • Printindex: Formatted index entries for printed output, using "* printindex cp" or similar for index generation.
  • Cross-reference: Links to other nodes or external files, formatted as "*note reference: (file)node".

3. Ghost blog embedded html javascript for drag n drop .INFO file to dump properties

.INFO File Parser
Drag and drop .INFO file here

4. Python class for .INFO file

class InfoFile:
    def __init__(self, filename):
        self.filename = filename
        self.data = None
        self.properties = {}

    def read(self):
        with open(self.filename, 'r') as f:
            self.data = f.read()
        self.decode()

    def decode(self):
        if not self.data:
            return
        # Preamble
        preamble_match = self.data[0: self.data.find('\x1F', 1)]
        self.properties['preamble'] = preamble_match if 'Info:' in preamble_match else None

        # Indirect table
        indirect_pos = self.data.find('\x1FIndirect\x1F')
        if indirect_pos != -1:
            end_pos = self.data.find('\x1F', indirect_pos + 10)
            self.properties['indirect_table'] = self.data[indirect_pos + 10:end_pos]

        # Tag table
        tag_pos = self.data.find('\x1FTag table:\x1F')
        if tag_pos != -1:
            end_pos = self.data.find('\x1F', tag_pos + 12)
            self.properties['tag_table'] = self.data[tag_pos + 12:end_pos]

        # Local variables
        local_pos = self.data.find('\x1FLocal Variables:\x1F')
        if local_pos != -1:
            end_pos = self.data.find('End:\x1F', local_pos)
            self.properties['local_variables'] = self.data[local_pos + 19:end_pos]

        # Regular nodes
        node_matches = []
        pos = 0
        while True:
            node_pos = self.data.find('\x1FFile: ', pos)
            if node_pos == -1:
                break
            end_pos = self.data.find('\x1F', node_pos + 1)
            node_matches.append(self.data[node_pos + 1:end_pos])
            pos = end_pos
        self.properties['regular_nodes'] = node_matches

        # Menu
        menu_pos = self.data.find('* Menu:')
        if menu_pos != -1:
            end_pos = self.data.find('\n\n', menu_pos)
            self.properties['menu'] = self.data[menu_pos:end_pos]

        # Image
        image_pos = self.data.find('* Image:')
        if image_pos != -1:
            end_pos = self.data.find('.', image_pos)
            self.properties['image'] = self.data[image_pos:end_pos]

        # Printindex
        printindex_pos = self.data.find('* printindex')
        if printindex_pos != -1:
            end_pos = self.data.find('\n', printindex_pos)
            self.properties['printindex'] = self.data[printindex_pos:end_pos]

        # Cross-reference
        xref_pos = self.data.find('*note ')
        if xref_pos != -1:
            end_pos = self.data.find('::', xref_pos)
            self.properties['cross_reference'] = self.data[xref_pos:end_pos]

    def print_properties(self):
        for key, value in self.properties.items():
            print(f"{key.upper()}: {value}")

    def write(self, new_filename):
        with open(new_filename, 'w') as f:
            f.write(self.data)

# Example usage
# info = InfoFile('example.info')
# info.read()
# info.print_properties()
# info.write('new.info')

5. Java class for .INFO file

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class InfoFile {
    private String filename;
    private String data;
    private Map<String, String> properties = new HashMap<>();

    public InfoFile(String filename) {
        this.filename = filename;
    }

    public void read() throws IOException {
        StringBuilder sb = new StringBuilder();
        try (BufferedReader br = new BufferedReader(new FileReader(filename))) {
            String line;
            while (line = br.readLine() != null) {
                sb.append(line).append("\n");
            }
        }
        data = sb.toString();
        decode();
    }

    private void decode() {
        // Preamble
        Pattern preamblePattern = Pattern.compile("^\\u001FInfo: \\((.*)\\)dir,\\u001F");
        Matcher preambleMatcher = preamblePattern.matcher(data);
        if (preambleMatcher.find()) {
            properties.put("preamble", preambleMatcher.group(0));
        }

        // Indirect table
        Pattern indirectPattern = Pattern.compile("\\u001FIndirect\\u001F(.*?)\\u001F", Pattern.DOTALL);
        Matcher indirectMatcher = indirectPattern.matcher(data);
        if (indirectMatcher.find()) {
            properties.put("indirect_table", indirectMatcher.group(1));
        }

        // Tag table
        Pattern tagPattern = Pattern.compile("\\u001FTag table:\\u001F(.*?)\\u001F", Pattern.DOTALL);
        Matcher tagMatcher = tagPattern.matcher(data);
        if (tagMatcher.find()) {
            properties.put("tag_table", tagMatcher.group(1));
        }

        // Local variables
        Pattern localPattern = Pattern.compile("\\u001FLocal Variables:\\u001F(.*? )End:", Pattern.DOTALL);
        Matcher localMatcher = localPattern.matcher(data);
        if (localMatcher.find()) {
            properties.put("local_variables", localMatcher.group(1));
        }

        // Regular nodes
        Pattern nodePattern = Pattern.compile("\\u001FFile: (.*?),  Node: (.*?),  Up: (.*?),  Next: (.*?),  Prev: (.*?)\\n", Pattern.DOTALL);
        Matcher nodeMatcher = nodePattern.matcher(data);
        StringBuilder nodes = new StringBuilder();
        while (nodeMatcher.find()) {
            nodes.append(nodeMatcher.group(0)).append("\n");
        }
        if (nodes.length() > 0) {
            properties.put("regular_nodes", nodes.toString());
        }

        // Menu
        Pattern menuPattern = Pattern.compile("\\* Menu:(.*?)\\n\\n", Pattern.DOTALL);
        Matcher menuMatcher = menuPattern.matcher(data);
        if (menuMatcher.find()) {
            properties.put("menu", menuMatcher.group(1));
        }

        // Image
        Pattern imagePattern = Pattern.compile("\\* Image: (.*?)\\.");
        Matcher imageMatcher = imagePattern.matcher(data);
        if (imageMatcher.find()) {
            properties.put("image", imageMatcher.group(1));
        }

        // Printindex
        Pattern printindexPattern = Pattern.compile("\\* printindex (.*?)\\n");
        Matcher printindexMatcher = printindexPattern.matcher(data);
        if (printindexMatcher.find()) {
            properties.put("printindex", printindexMatcher.group(1));
        }

        // Cross-reference
        Pattern xrefPattern = Pattern.compile("\\*note (.*?)::");
        Matcher xrefMatcher = xrefPattern.matcher(data);
        if (xrefMatcher.find()) {
            properties.put("cross_reference", xrefMatcher.group(1));
        }
    }

    public void printProperties() {
        for (Map.Entry<String, String> entry : properties.entrySet()) {
            System.out.println(entry.getKey().toUpperCase() + ": " + entry.getValue());
        }
    }

    public void write(String newFilename) throws IOException {
        try (FileWriter fw = new FileWriter(newFilename)) {
            fw.write(data);
        }
    }

    // Example usage
    // public static void main(String[] args) throws IOException {
    //     InfoFile info = new InfoFile("example.info");
    //     info.read();
    //     info.printProperties();
    //     info.write("new.info");
    // }
}

6. Javascript class for .INFO file

class InfoFile {
  constructor(filename) {
    this.filename = filename;
    this.data = null;
    this.properties = {};
  }

  async read() {
    // Assuming node.js with fs
    const fs = require('fs');
    this.data = fs.readFileSync(this.filename, 'utf8');
    this.decode();
  }

  decode() {
    if (!this.data) return;
    // Preamble
    const preambleMatch = this.data.match(/^\x1FInfo: \((.*)\)dir,\x1F/);
    this.properties.preamble = preambleMatch ? preambleMatch[0] : null;

    // Indirect table
    const indirectMatch = this.data.match(/\x1FIndirect\x1F(.*?)\x1F/gs);
    this.properties.indirect_table = indirectMatch ? indirectMatch[0] : null;

    // Tag table
    const tagMatch = this.data.match(/\x1FTag table:\x1F(.*?)\x1F/gs);
    this.properties.tag_table = tagMatch ? tagMatch[0] : null;

    // Local variables
    const localMatch = this.data.match(/\x1FLocal Variables:\x1F(.*? )End:/gs);
    this.properties.local_variables = localMatch ? localMatch[0] : null;

    // Regular nodes
    const nodeMatch = this.data.match(/\x1FFile: (.*?),  Node: (.*?),  Up: (.*?),  Next: (.*?),  Prev: (.*?)\n/gs);
    this.properties.regular_nodes = nodeMatch ? nodeMatch : null;

    // Menu
    const menuMatch = this.data.match(/\* Menu:(.*?)\n\n/gs);
    this.properties.menu = menuMatch ? menuMatch[0] : null;

    // Image
    const imageMatch = this.data.match(/\* Image: (.*?)\./gs);
    this.properties.image = imageMatch ? imageMatch[0] : null;

    // Printindex
    the printindexMatch = this.data.match(/\* printindex (.*?)\n/gs);
    this.properties.printindex = printindexMatch ? printindexMatch[0] : null;

    // Cross-reference
    const xrefMatch = this.data.match(/\*note (.*?)::/gs);
    this.properties.cross_reference = xrefMatch ? xrefMatch[0] : null;
  }

  printProperties() {
    for (const [key, value] of Object.entries(this.properties)) {
      console.log(`${key.toUpperCase()}: ${value}`);
    }
  }

  write(newFilename) {
    const fs = require('fs');
    fs.writeFileSync(newFilename, this.data);
  }
}

// Example usage
// const info = new InfoFile('example.info');
 // await info.read();
// info.printProperties();
// info.write('new.info');

7. C class for .INFO file

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

typedef struct {
  char *data;
  long size;
  char *preamble;
  char *indirect_table;
  char *tag_table;
  char *local_variables;
  char **regular_nodes;
  int node_count;
  char *menu;
  char *image;
  char *printindex;
  char *cross_reference;
} InfoFile;

InfoFile *info_file_create(const char *filename) {
  InfoFile *info = malloc(sizeof(InfoFile));
  FILE *f = fopen(filename, "r");
  if (!f) return NULL;
  fseek(f, 0, SEEK_END);
  info->size = ftell(f);
  fseek(f, 0, SEEK_SET);
  info->data = malloc(info->size + 1);
  fread(info->data, 1, info->size, f);
  info->data[info->size] = 0;
  fclose(f);
  return info;
}

void info_file_decode(InfoFile *info) {
  // Preamble
  char *preamble_end = strchr(info->data + 1, 31);
  if (preamble_end) {
    info->preamble = strndup(info->data + 1, preamble_end - (info->data + 1));
  }

  // Indirect table
  char *indirect_start = strstr(info->data, "\x1FIndirect\x1F");
  if (indirect_start) {
    indirect_start += 11;
    char *end = strchr(indirect_start, 31);
    info->indirect_table = strndup(indirect_start, end - indirect_start);
  }

  // Tag table
  char *tag_start = strstr(info->data, "\x1FTag table:\x1F");
  if (tag_start) {
    tag_start += 12;
    char *end = strchr(tag_start, 31);
    info->tag_table = strndup(tag_start, end - tag_start);
  }

  // Local variables
  char *local_start = strstr(info->data, "\x1FLocal Variables:\x1F");
  if (local_start) {
    local_start += 19;
    char *end = strstr(local_start, "End:");
    info->local_variables = strndup(local_start, end - local_start);
  }

  // Regular nodes (simple count)
  char *pos = info->data;
  info->node_count = 0;
  while ((pos = strstr(pos, "\x1FFile: ")) != NULL) {
    info->node_count++;
    pos += 8;
  }
  info->regular_nodes = malloc(info->node_count * sizeof(char*));
  pos = info->data;
  int i = 0;
  while ((pos = strstr(pos, "\x1FFile: ")) != NULL) {
    pos += 1;
    char *end = strchr(pos, 31);
    info->regular_nodes[i++] = strndup(pos, end - pos);
    pos = end;
  }

  // Menu
  char *menu_start = strstr(info->data, "* Menu:");
  if (menu_start) {
    char *end = strstr(menu_start, "\n\n");
    info->menu = strndup(menu_start, end - menu_start);
  }

  // Image
  char *image_start = strstr(info->data, "* Image:");
  if (image_start) {
    char *end = strchr(image_start, '.');
    info->image = strndup(image_start, end - image_start + 1);
  }

  // Printindex
  char *printindex_start = strstr(info->data, "* printindex");
  if (printindex_start) {
    char *end = strchr(printindex_start, '\n');
    info->printindex = strndup(printindex_start, end - printindex_start);
  }

  // Cross-reference
  char *xref_start = strstr(info->data, "*note ");
  if (xref_start) {
    char *end = strstr(xref_start, "::");
    info->cross_reference = strndup(xref_start, end - xref_start + 2);
  }
}

void info_file_print_properties(InfoFile *info) {
  printf("PREAMBLE: %s\n", info->preamble ? info->preamble : "Not found");
  printf("INDIRECT_TABLE: %s\n", info->indirect_table ? info->indirect_table : "Not found");
  printf("TAG_TABLE: %s\n", info->tag_table ? info->tag_table : "Not found");
  printf("LOCAL_VARIABLES: %s\n", info->local_variables ? info->local_variables : "Not found");
  printf("REGULAR_NODES:\n");
  for (int i = 0; i < info->node_count; i++) {
    printf("Node %d: %s\n", i + 1, info->regular_nodes[i]);
  }
  printf("MENU: %s\n", info->menu ? info->menu : "Not found");
  printf("IMAGE: %s\n", info->image ? info->image : "Not found");
  printf("PRINTINDEX: %s\n", info->printindex ? info->printindex : "Not found");
  printf("CROSS_REFERENCE: %s\n", info->cross_reference ? info->cross_reference : "Not found");
}

void info_file_write(InfoFile *info, const char *new_filename) {
  FILE *f = fopen(new_filename, "w");
  if (f) {
    fwrite(info->data, 1, info->size, f);
    fclose(f);
  }
}

void info_file_destroy(InfoFile *info) {
  free(info->data);
  free(info->preamble);
  free(info->indirect_table);
  free(info->tag_table);
  free(info->local_variables);
  for (int i = 0; i < info->node_count; i++) {
    free(info->regular_nodes[i]);
  }
  free(info->regular_nodes);
  free(info->menu);
  free(info->image);
  free(info->printindex);
  free(info->cross_reference);
  free(info);
}

// Example usage
// int main() {
  // InfoFile *info = info_file_create("example.info");
  // info_file_decode(info);
  // info_file_print_properties(info);
  // info_file_write(info, "new.info");
  // info_file_destroy(info);
  // return 0;
// }