Task 483: .ORG File Format
Task 483: .ORG File Format
The .ORG file format is a plain-text markup format used by Emacs Org-mode for structured documents, notes, task management, and more. The full specifications are documented in the official Org Manual.
- Based on the format specifications, the following is a list of all intrinsic properties (built-in special and behavioral properties that can be defined, inherited, queried, or derived in .org files):
- ALLTAGS
- BLOCKED
- CATEGORY
- CLOCKSUM
- CLOCKSUM_T
- CLOSED
- DEADLINE
- FILE
- ITEM
- PRIORITY
- SCHEDULED
- TAGS
- TIMESTAMP
- TIMESTAMP_IA
- TODO
- ARCHIVE
- COLUMNS
- COOKIE_DATA
- LOGGING
- NOBLOCKING
- ORDERED
- STYLE
- Two direct download links for sample .org files (these are Org-mode examples, as Lotus Organizer .org files are rare and not readily available for direct download):
- https://raw.githubusercontent.com/elmarlee/org-mode-template/main/org-mode-template.org
- https://writequit.org/denver-emacs/presentations/files/example.org
- Here is a self-contained HTML snippet with embedded JavaScript that can be embedded in a Ghost blog post (or any HTML context). It creates a drag-and-drop zone for a .org file, reads it as text, parses it for headings and :PROPERTIES: drawers, extracts values for the properties from the list above (if present), and dumps them to the screen in a readable format.
Drag and drop a .org file here
- Here is a Python class that can open a .org file, read its content, decode/extract the properties from the list above (using a simple parser for :PROPERTIES: drawers under headings), write new content to the file, and print the extracted properties to console.
import re
PROPERTY_LIST = ['ALLTAGS', 'BLOCKED', 'CATEGORY', 'CLOCKSUM', 'CLOCKSUM_T', 'CLOSED', 'DEADLINE', 'FILE', 'ITEM', 'PRIORITY', 'SCHEDULED', 'TAGS', 'TIMESTAMP', 'TIMESTAMP_IA', 'TODO', 'ARCHIVE', 'COLUMNS', 'COOKIE_DATA', 'LOGGING', 'NOBLOCKING', 'ORDERED', 'STYLE']
class OrgFile:
def __init__(self, filename):
self.filename = filename
self.content = None
self.properties = {}
def open_and_read(self):
with open(self.filename, 'r', encoding='utf-8') as f:
self.content = f.read()
return self.content
def decode_properties(self):
if not self.content:
self.open_and_read()
lines = self.content.splitlines()
current_heading = None
in_properties_drawer = False
self.properties = {}
for line in lines:
trimmed = line.strip()
if re.match(r'^\*+', trimmed):
current_heading = trimmed
self.properties[current_heading] = {}
elif trimmed == ':PROPERTIES:':
in_properties_drawer = True
elif trimmed == ':END:':
in_properties_drawer = False
elif in_properties_drawer and trimmed.startswith(':'):
match = re.match(r'^:(\w+):\s*(.*)', trimmed)
if match:
key = match.group(1).upper() # Normalize for matching
value = match.group(2).strip()
if key in PROPERTY_LIST:
self.properties[current_heading][key] = value
def write(self, new_content):
with open(self.filename, 'w', encoding='utf-8') as f:
f.write(new_content)
def print_properties(self):
if not self.properties:
self.decode_properties()
for heading, props in self.properties.items():
print(heading)
for key, value in props.items():
print(f"{key}: {value}")
print() # Separator between headings
- Here is a Java class that can open a .org file, read its content, decode/extract the properties from the list above (using a simple parser for :PROPERTIES: drawers under headings), write new content to the file, and print the extracted properties to console.
import java.io.*;
import java.util.*;
import java.util.regex.*;
public class OrgFile {
private String filename;
private String content;
private Map<String, Map<String, String>> properties = new HashMap<>();
private static final Set<String> PROPERTY_LIST = new HashSet<>(Arrays.asList(
"ALLTAGS", "BLOCKED", "CATEGORY", "CLOCKSUM", "CLOCKSUM_T", "CLOSED", "DEADLINE", "FILE", "ITEM",
"PRIORITY", "SCHEDULED", "TAGS", "TIMESTAMP", "TIMESTAMP_IA", "TODO", "ARCHIVE", "COLUMNS",
"COOKIE_DATA", "LOGGING", "NOBLOCKING", "ORDERED", "STYLE"
));
public OrgFile(String filename) {
this.filename = filename;
}
public String openAndRead() throws IOException {
StringBuilder sb = new StringBuilder();
try (BufferedReader reader = new BufferedReader(new FileReader(filename))) {
String line;
while ((line = reader.readLine()) != null) {
sb.append(line).append("\n");
}
}
content = sb.toString();
return content;
}
public void decodeProperties() throws IOException {
if (content == null) {
openAndRead();
}
properties.clear();
String[] lines = content.split("\n");
String currentHeading = null;
boolean inPropertiesDrawer = false;
Pattern headingPattern = Pattern.compile("^\\*+ .*");
Pattern propertyPattern = Pattern.compile("^:(\\w+):\\s*(.*)");
for (String line : lines) {
String trimmed = line.trim();
if (headingPattern.matcher(trimmed).matches()) {
currentHeading = trimmed;
properties.put(currentHeading, new HashMap<>());
} else if (trimmed.equals(":PROPERTIES:")) {
inPropertiesDrawer = true;
} else if (trimmed.equals(":END:")) {
inPropertiesDrawer = false;
} else if (inPropertiesDrawer) {
Matcher matcher = propertyPattern.matcher(trimmed);
if (matcher.matches()) {
String key = matcher.group(1).toUpperCase();
String value = matcher.group(2).trim();
if (PROPERTY_LIST.contains(key) && currentHeading != null) {
properties.get(currentHeading).put(key, value);
}
}
}
}
}
public void write(String newContent) throws IOException {
try (BufferedWriter writer = new BufferedWriter(new FileWriter(filename))) {
writer.write(newContent);
}
}
public void printProperties() throws IOException {
if (properties.isEmpty()) {
decodeProperties();
}
for (Map.Entry<String, Map<String, String>> entry : properties.entrySet()) {
System.out.println(entry.getKey());
for (Map.Entry<String, String> prop : entry.getValue().entrySet()) {
System.out.println(prop.getKey() + ": " + prop.getValue());
}
System.out.println();
}
}
}
- Here is a JavaScript class that can open a .org file (assuming Node.js environment with fs module), read its content, decode/extract the properties from the list above (using a simple parser for :PROPERTIES: drawers under headings), write new content to the file, and print the extracted properties to console.
const fs = require('fs');
const PROPERTY_LIST = ['ALLTAGS', 'BLOCKED', 'CATEGORY', 'CLOCKSUM', 'CLOCKSUM_T', 'CLOSED', 'DEADLINE', 'FILE', 'ITEM', 'PRIORITY', 'SCHEDULED', 'TAGS', 'TIMESTAMP', 'TIMESTAMP_IA', 'TODO', 'ARCHIVE', 'COLUMNS', 'COOKIE_DATA', 'LOGGING', 'NOBLOCKING', 'ORDERED', 'STYLE'];
class OrgFile {
constructor(filename) {
this.filename = filename;
this.content = null;
this.properties = {};
}
openAndRead() {
this.content = fs.readFileSync(this.filename, 'utf8');
return this.content;
}
decodeProperties() {
if (!this.content) {
this.openAndRead();
}
const lines = this.content.split('\n');
let currentHeading = null;
let inPropertiesDrawer = false;
this.properties = {};
lines.forEach((line) => {
const trimmed = line.trim();
if (/^\*+ /.test(trimmed)) {
currentHeading = trimmed;
this.properties[currentHeading] = {};
} else if (trimmed === ':PROPERTIES:') {
inPropertiesDrawer = true;
} else if (trimmed === ':END:') {
inPropertiesDrawer = false;
} else if (inPropertiesDrawer && trimmed.startsWith(':')) {
const match = trimmed.match(/^:(\w+):\s*(.*)/);
if (match) {
const key = match[1].toUpperCase();
const value = match[2].trim();
if (PROPERTY_LIST.includes(key)) {
this.properties[currentHeading][key] = value;
}
}
}
});
}
write(newContent) {
fs.writeFileSync(this.filename, newContent, 'utf8');
}
printProperties() {
if (Object.keys(this.properties).length === 0) {
this.decodeProperties();
}
for (const heading in this.properties) {
console.log(heading);
for (const key in this.properties[heading]) {
console.log(`${key}: ${this.properties[heading][key]}`);
}
console.log('');
}
}
}
- Here is a C implementation (since C does not have classes, this uses a struct with associated functions) that can open a .org file, read its content, decode/extract the properties from the list above (using a simple parser for :PROPERTIES: drawers under headings), write new content to the file, and print the extracted properties to console. Note: This is basic and assumes small files (content loaded into memory); error handling is minimal for brevity.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#define MAX_LINE_LEN 1024
#define MAX_HEADINGS 100
#define MAX_PROPS_PER_HEADING 50
const char* PROPERTY_LIST[] = {
"ALLTAGS", "BLOCKED", "CATEGORY", "CLOCKSUM", "CLOCKSUM_T", "CLOSED", "DEADLINE", "FILE", "ITEM",
"PRIORITY", "SCHEDULED", "TAGS", "TIMESTAMP", "TIMESTAMP_IA", "TODO", "ARCHIVE", "COLUMNS",
"COOKIE_DATA", "LOGGING", "NOBLOCKING", "ORDERED", "STYLE"
};
const int NUM_PROPERTIES = sizeof(PROPERTY_LIST) / sizeof(PROPERTY_LIST[0]);
typedef struct {
char* key;
char* value;
} Prop;
typedef struct {
char* heading;
Prop props[MAX_PROPS_PER_HEADING];
int prop_count;
} HeadingProps;
typedef struct {
char* filename;
char* content;
HeadingProps headings[MAX_HEADINGS];
int heading_count;
} OrgFile;
OrgFile* orgfile_new(const char* filename) {
OrgFile* org = malloc(sizeof(OrgFile));
org->filename = strdup(filename);
org->content = NULL;
org->heading_count = 0;
return org;
}
void orgfile_free(OrgFile* org) {
free(org->filename);
if (org->content) free(org->content);
for (int i = 0; i < org->heading_count; i++) {
free(org->headings[i].heading);
for (int j = 0; j < org->headings[i].prop_count; j++) {
free(org->headings[i].props[j].key);
free(org->headings[i].props[j].value);
}
}
free(org);
}
char* orgfile_open_and_read(OrgFile* org) {
FILE* fp = fopen(org->filename, "r");
if (!fp) return NULL;
fseek(fp, 0, SEEK_END);
long size = ftell(fp);
fseek(fp, 0, SEEK_SET);
org->content = malloc(size + 1);
fread(org->content, 1, size, fp);
org->content[size] = '\0';
fclose(fp);
return org->content;
}
void orgfile_decode_properties(OrgFile* org) {
if (!org->content) orgfile_open_and_read(org);
char* content_copy = strdup(org->content);
char* line = strtok(content_copy, "\n");
char* current_heading = NULL;
int in_properties_drawer = 0;
org->heading_count = 0;
while (line) {
char* trimmed = line;
while (*trimmed == ' ' || *trimmed == '\t') trimmed++;
if (strncmp(trimmed, "*", 1) == 0) {
if (current_heading) free(current_heading);
current_heading = strdup(trimmed);
if (org->heading_count < MAX_HEADINGS) {
org->headings[org->heading_count].heading = strdup(current_heading);
org->headings[org->heading_count].prop_count = 0;
org->heading_count++;
}
} else if (strcmp(trimmed, ":PROPERTIES:") == 0) {
in_properties_drawer = 1;
} else if (strcmp(trimmed, ":END:") == 0) {
in_properties_drawer = 0;
} else if (in_properties_drawer && strncmp(trimmed, ":", 1) == 0) {
char* colon_pos = strchr(trimmed + 1, ':');
if (colon_pos) {
*colon_pos = '\0';
char* key = trimmed + 1;
char* value = colon_pos + 1;
while (*value == ' ') value++;
char upper_key[MAX_LINE_LEN];
strcpy(upper_key, key);
for (char* p = upper_key; *p; p++) *p = toupper(*p);
int is_intrinsic = 0;
for (int i = 0; i < NUM_PROPERTIES; i++) {
if (strcmp(upper_key, PROPERTY_LIST[i]) == 0) {
is_intrinsic = 1;
break;
}
}
if (is_intrinsic && org->heading_count > 0) {
HeadingProps* hp = &org->headings[org->heading_count - 1];
if (hp->prop_count < MAX_PROPS_PER_HEADING) {
hp->props[hp->prop_count].key = strdup(key);
hp->props[hp->prop_count].value = strdup(value);
hp->prop_count++;
}
}
}
}
line = strtok(NULL, "\n");
}
free(content_copy);
if (current_heading) free(current_heading);
}
void orgfile_write(OrgFile* org, const char* new_content) {
FILE* fp = fopen(org->filename, "w");
if (fp) {
fprintf(fp, "%s", new_content);
fclose(fp);
}
}
void orgfile_print_properties(OrgFile* org) {
if (org->heading_count == 0) {
orgfile_decode_properties(org);
}
for (int i = 0; i < org->heading_count; i++) {
printf("%s\n", org->headings[i].heading);
for (int j = 0; j < org->headings[i].prop_count; j++) {
printf("%s: %s\n", org->headings[i].props[j].key, org->headings[i].props[j].value);
}
printf("\n");
}
}