#if 0 ctime -begin ${0%.*}.ctm clang -g -Wall -Wno-unused-variable -fsanitize=address -std=c99 $0 -o ${0%.*} hmml.a ctime -end ${0%.*}.ctm exit #endif typedef unsigned int bool; #define TRUE 1 #define FALSE 0 #include #include #include #include "hmmlib.h" typedef struct { char *Location; char *Ptr; int Size; } buffer; typedef struct { char *Source; char *RefTitle; } ref_info; void ClaimBuffer(char *MemoryArena, int *ClaimedMemory, buffer *Buffer, int Size) { Buffer->Location = MemoryArena + *ClaimedMemory; Buffer->Size = Size; *ClaimedMemory += Buffer->Size; Buffer->Ptr = Buffer->Location; } ref_info ParseRef(HMML_Reference RefInput) { ref_info Info; if(RefInput.author) { Info.Source = RefInput.author; Info.RefTitle = RefInput.title; return Info; } else if(RefInput.page) { Info.Source = RefInput.site; Info.RefTitle = RefInput.page; return Info; } else { Info.Source = ""; Info.RefTitle = RefInput.site; return Info; } } int TimecodeToSeconds(char *Timecode) { int HMS[3] = { 0, 0, 0 }; // 0 == Seconds; 1 == Minutes; 2 == Hours int Colons = 0; while(*Timecode) { if((*Timecode < '0' || *Timecode > '9') && *Timecode != ':') { return FALSE; } if(*Timecode == ':') { ++Colons; if(Colons > 2) { return FALSE; } for(int i = 0; i < Colons; ++i) { HMS[Colons - i] = HMS[Colons - (i + 1)]; } HMS[0] = 0; } else { HMS[0] = HMS[0] * 10 + *Timecode - '0'; } ++Timecode; } if(HMS[0] > 59 || HMS[1] > 59 || Timecode[-1] == ':') { return FALSE; } return HMS[2] * 60 * 60 + HMS[1] * 60 + HMS[0]; } void CopyBuffer(buffer *Src, buffer *Dest) { Src->Ptr = Src->Location; while(*Src->Ptr) { *Dest->Ptr++ = *Src->Ptr++; } } void CopyStringToBuffer(buffer *Dest, char *Format, ...) { va_list Args; va_start(Args, Format); int Length = vsprintf(Dest->Ptr, Format, Args); va_end(Args); Dest->Ptr += Length; } int StringsDiffer(char *A, char *B) { while(*A && *B && *A == *B) { ++A, ++B; } return *A - *B; } int CharToColour(char Char) { if(Char >= 'a' && Char <= 'z') { return (((float)Char - 'a') / ('z' - 'a') * 0xFFFFFF); } else if(Char >= 'A' && Char <= 'Z') { return (((float)Char - 'A') / ('Z' - 'A') * 0xFFFFFF); } else if(Char >= '0' && Char <= '9') { return (((float)Char - '0') / ('9' - '0') * 0xFFFFFF); } else { return 0x777777; } } int StringToColourHash(char *String) { int Result = 0; int i; for(i = 0; String[i]; ++i) { Result += CharToColour(String[i]); } return Result / i; } int main(int ArgC, char **Args) { if(ArgC < 2) { fprintf(stderr, "Usage: %s filename(s)\n", Args[0]); return 1; } // NOTE(matt): Init MemoryArena char *MemoryArena; int ArenaSize = 1024 * 1024; if(!(MemoryArena = calloc(ArenaSize, 1))) { perror(Args[0]); return 1; } int ClaimedMemory = 0; // NOTE(matt): Setup buffers and ptrs #if 1 char *InPtr; buffer Title; buffer QuoteMenu; buffer ReferenceMenu; buffer Player; buffer Annotation; buffer AnnotationHeader; buffer AnnotationClass; buffer AnnotationData; buffer Text; buffer Category; buffer Master; for(int FileIndex = 1; FileIndex < ArgC; ++FileIndex) { FILE *InFile; if(!(InFile = fopen(Args[FileIndex], "r"))) { perror(Args[0]); free(MemoryArena); return 1; } HMML_Output HMML = hmml_parse_file(InFile); fclose(InFile); if(HMML.well_formed) { ClaimBuffer(MemoryArena, &ClaimedMemory, &Title, 1024 * 16); ClaimBuffer(MemoryArena, &ClaimedMemory, &Player, 1024 * 256); CopyStringToBuffer(&Title, "
\n" " %s\n", HMML.metadata.title); CopyStringToBuffer(&Player, "
\n" "
\n" "
\n", HMML.metadata.id); for(int AnnotationIndex = 0; AnnotationIndex < HMML.annotation_count; ++AnnotationIndex) { ClaimBuffer(MemoryArena, &ClaimedMemory, &AnnotationHeader, 256); ClaimBuffer(MemoryArena, &ClaimedMemory, &AnnotationClass, 128); ClaimBuffer(MemoryArena, &ClaimedMemory, &Text, 1024 * 4); CopyStringToBuffer(&AnnotationHeader, "
%s ", StringToColourHash(HMML.annotations[AnnotationIndex].author), HMML.annotations[AnnotationIndex].author); } #endif //TODO(matt): Replace this CopyStringToBuffer() with real stuff! CopyStringToBuffer(&Text, HMML.annotations[AnnotationIndex].text); CopyStringToBuffer(&AnnotationClass, "\""); CopyBuffer(&AnnotationClass, &AnnotationHeader); CopyStringToBuffer(&AnnotationHeader, ">\n"); ClaimBuffer(MemoryArena, &ClaimedMemory, &Annotation, 1024 * 4); CopyBuffer(&AnnotationHeader, &Annotation); CopyStringToBuffer(&Annotation, "
%s", HMML.annotations[AnnotationIndex].time); CopyBuffer(&Text, &Annotation); CopyStringToBuffer(&Annotation, "
\n" "
\n" "
%s", HMML.annotations[AnnotationIndex].time); CopyBuffer(&Text, &Annotation); CopyStringToBuffer(&Annotation, "
\n" "
\n" "
\n" "
%s", HMML.annotations[AnnotationIndex].time); CopyBuffer(&Text, &Annotation); CopyStringToBuffer(&Annotation, "
\n" "
\n" "
\n"); CopyBuffer(&Annotation, &Player); ClaimedMemory -= Text.Size; ClaimedMemory -= AnnotationHeader.Size; ClaimedMemory -= AnnotationClass.Size; ClaimedMemory -= Annotation.Size; } CopyStringToBuffer(&Title, " Annotator: %s\n" "
\n", HMML.metadata.annotator); CopyStringToBuffer(&Player, "
\n" "
\n"); //NOTE(matt): Collate the buffers! ClaimBuffer(MemoryArena, &ClaimedMemory, &Master, 1024 * 512); CopyStringToBuffer(&Master, "\n" " \n" " \n" "\n" " \n" " \n" " \n" " \n" " \n"); //NOTE(matt): Here is where we do all our CopyBuffer() calls CopyBuffer(&Title, &Master); CopyBuffer(&Player, &Master); // CopyStringToBuffer(&Master, " \n" " \n" "\n"); FILE *OutFile; if(!(OutFile = fopen("out.html", "w"))) { perror(Args[0]); return 1; } fwrite(Master.Location, Master.Ptr - Master.Location, 1, OutFile); fclose(OutFile); ClaimedMemory -= Title.Size; ClaimedMemory -= Master.Size; } else { fprintf(stderr, "%s:%d: %s\n", Args[FileIndex], HMML.error.line, HMML.error.message); } hmml_free(&HMML); } #else char *InPtr; buffer Working; buffer Text; buffer Out; ClaimBuffer(MemoryArena, &ClaimedMemory, &Out, 1024 * 512); for(int FileIndex = 1; FileIndex < ArgC; ++FileIndex) { FILE *InFile; if(!(InFile = fopen(Args[FileIndex], "r"))) { perror(Args[0]); free(MemoryArena); return 1; } HMML_Output HMML = hmml_parse_file(InFile); fclose(InFile); if(HMML.well_formed) { ClaimBuffer(MemoryArena, &ClaimedMemory, &Working, 1024 * 4); sprintf(Working.Location, "\n" " \n" " \n" "\n" " \n" " \n" " \n" " \n" " \n" "
\n" " %s\n", HMML.metadata.title); CopyBuffer(&Working, &Out); int AnnotationIndex = 0; int ReferenceIndex = 1; while(AnnotationIndex < HMML.annotation_count) { if(HMML.annotations[AnnotationIndex].reference_count) { sprintf(Working.Location, "
\n" " References ▼\n" "
\n" "
\n"); CopyBuffer(&Working, &Out); while(AnnotationIndex < HMML.annotation_count) { for(int i = 0; i < HMML.annotations[AnnotationIndex].reference_count; ++i) { HMML_Reference *CurrentRef = HMML.annotations[AnnotationIndex].references + i; // NOTE(matt): Consider removing the ref_index class if it ain't needed sprintf(Working.Location, " \n" " \n" "
%s
\n" "
%s
\n" "
\n" // TODO(matt): Fill the div class="ref_indices" with <= 3 span // class="ref_index" and ensure to put these <=3 spans on the same line without // a space between them "
\n" " [%d]%s\n" "
\n" "
\n", AnnotationIndex, CurrentRef->url, ParseRef(*CurrentRef).Source, ParseRef(*CurrentRef).RefTitle, TimecodeToSeconds(HMML.annotations[AnnotationIndex].time), ReferenceIndex, HMML.annotations[AnnotationIndex].time); CopyBuffer(&Working, &Out); ++ReferenceIndex; } ++AnnotationIndex; } sprintf(Working.Location, "
\n" "
\n"); CopyBuffer(&Working, &Out); } ++AnnotationIndex; } sprintf(Working.Location, " Annotator: %s\n" "
\n" "
\n" "
\n" "
\n", HMML.metadata.annotator, HMML.metadata.id); CopyBuffer(&Working, &Out); int DataRef = 1; for(int AnnotationIndex = 0; AnnotationIndex < HMML.annotation_count; ++AnnotationIndex) { sprintf(Working.Location, "
%s ", StringToColourHash(HMML.annotations[AnnotationIndex].author), HMML.annotations[AnnotationIndex].author); Text.Ptr += Inc; } if(HMML.annotations[AnnotationIndex].marker_count) { for(int MarkerIndex = 0; MarkerIndex < HMML.annotations[AnnotationIndex].marker_count; ++MarkerIndex) { if(!StringsDiffer("blackboard", HMML.annotations[AnnotationIndex].markers[MarkerIndex].marker) && HMML.annotations[AnnotationIndex].markers[MarkerIndex].type == HMML_CATEGORY) { sprintf(Working.Location, " blackboard"); CopyBuffer(&Working, &Out); } } } sprintf(Working.Location, "\" data-timestamp=\"%d\"", TimecodeToSeconds(HMML.annotations[AnnotationIndex].time)); CopyBuffer(&Working, &Out); InPtr = HMML.annotations[AnnotationIndex].text; if(HMML.annotations[AnnotationIndex].reference_count) // || HMML.annotations[AnnotationIndex].is_quote) { sprintf(Working.Location, " data-ref=\"%d\"", AnnotationIndex); CopyBuffer(&Working, &Out); for(int RefLocalIndex = 0; RefLocalIndex < HMML.annotations[AnnotationIndex].reference_count; ++RefLocalIndex) { while(InPtr - HMML.annotations[AnnotationIndex].text < HMML.annotations[AnnotationIndex].references[RefLocalIndex].offset) { *Text.Ptr++ = *InPtr++; } if(HMML.annotations[AnnotationIndex].references[RefLocalIndex].offset <= 2) { if(HMML.annotations[AnnotationIndex].references[RefLocalIndex].page) { Inc = sprintf(Text.Ptr, "%s", HMML.annotations[AnnotationIndex].references[RefLocalIndex].page); } else if(HMML.annotations[AnnotationIndex].references[RefLocalIndex].site) { Inc = sprintf(Text.Ptr, "%s", HMML.annotations[AnnotationIndex].references[RefLocalIndex].site); } else if(HMML.annotations[AnnotationIndex].references[RefLocalIndex].title) { Inc = sprintf(Text.Ptr, "%s", HMML.annotations[AnnotationIndex].references[RefLocalIndex].title); } else { fprintf(stderr, "%s: Potentially incomplete reference at %s:%d\n", Args[0], Args[FileIndex], HMML.annotations[AnnotationIndex].line); } Text.Ptr += Inc; } if(HMML.annotations[AnnotationIndex].references[RefLocalIndex].offset <= 2 && Text.Ptr[-1] == ' ') { --Text.Ptr; } Inc = sprintf(Text.Ptr, "%d", DataRef); Text.Ptr += Inc; ++DataRef; } } *Out.Ptr++ = '>'; *Out.Ptr++ = '\n'; CopyStringToBuffer(&Text, InPtr); *Text.Ptr = '\0'; sprintf(Working.Location, "
%s%s
\n" "
\n" "
%s%s
\n" "
\n" "
\n" "
%s%s
\n" "
\n" "
\n", HMML.annotations[AnnotationIndex].time, Text.Location, HMML.annotations[AnnotationIndex].time, Text.Location, HMML.annotations[AnnotationIndex].time, Text.Location); CopyBuffer(&Working, &Out); ClaimedMemory -= Text.Size; } sprintf(Working.Location, "
\n" "
\n" " \n" " \n" "\n"); CopyBuffer(&Working, &Out); FILE *OutFile; //char *OutFilename; //sprintf(OutFilename, "%s.html", Args[FileIndex]); if(!(OutFile = fopen("out.html", "w"))) { perror(Args[0]); return 1; } fwrite(Out.Location, Out.Ptr - Out.Location, 1, OutFile); fclose(OutFile); } else { fprintf(stderr, "%s:%d: %s\n", Args[FileIndex], HMML.error.line, HMML.error.message); } hmml_free(&HMML); } #endif free(MemoryArena); }