#if 0 ctime -begin ${0%.*}.ctm clang -g -fsanitize=address $0 -o ${0%.*} hmml.a ctime -end ${0%.*}.ctm exit #endif #include "hmmlib.h" #include #include #ifndef bool typedef unsigned int bool; #endif #define TRUE 1 #define FALSE 0 typedef struct { char *Location; char *Ptr; int Size; } buffer; int StringLength(char *String) { int i = 0; while(String[i]) { ++i; } return i; } bool StringsDiffer(char A[], char B[]) { char APtr, BPtr; while(*A) { APtr = *A++; BPtr = *B++; if(APtr >= 'A' && APtr <= 'Z') { APtr += 32; } if(BPtr >= 'A' && BPtr <= 'Z') { BPtr += 32; } if(APtr != BPtr) { return TRUE; } } return FALSE; } int main(int ArgC, char **Args) { if(ArgC < 2) { fprintf(stderr, "Usage: %s filename(s)\n", Args[0]); return 1; } FILE *InFile; for(int FileIndex = 1; FileIndex < ArgC; ++FileIndex) { if(!(InFile = fopen(Args[FileIndex], "r"))) { perror(Args[0]); return 1; } // Init MemoryArena int ArenaSize = 1024 * 32; char *MemoryArena; if(!(MemoryArena = calloc(ArenaSize, 1))) { perror(Args[0]); return 1; } int ClaimedMemory = 0; // Buffers and Pointers char *InPtr; // Associated buffer is allocated by hmml_parse_file() buffer Temp; // Temp.Ptr may be used independently of Temp.Location buffer Out; //printf("Reading %s\n", Args[FileIndex]); HMML_Output HMML = hmml_parse_file(InFile); fclose(InFile); if(HMML.well_formed) { Out.Location = MemoryArena + ClaimedMemory; Out.Size = 1024*16; ClaimedMemory += Out.Size; bool WritingChat = TRUE; char *Member = HMML.metadata.twitch ? HMML.metadata.twitch : HMML.metadata.member; for(int BuildPass = 0; BuildPass < 2; ++BuildPass) { if(WritingChat == FALSE) { printf("%s: %ld characters over budget. Shrinking...\n", Args[FileIndex], (Out.Ptr - Out.Location) - 5000); } Out.Ptr = Out.Location; for(int AnnotationIndex = 0; AnnotationIndex < HMML.annotation_count; ++AnnotationIndex) { if(HMML.annotations[AnnotationIndex].author && !WritingChat) { goto skip; } InPtr = HMML.annotations[AnnotationIndex].time; while(*InPtr) { *Out.Ptr++ = *InPtr++; } *Out.Ptr++ = ' '; InPtr = HMML.annotations[AnnotationIndex].text; // TODO(matt): Get this logic correct if(HMML.annotations[AnnotationIndex].author) { Temp.Location = "Chat comment: \""; Temp.Ptr = Temp.Location; while(*Temp.Ptr) { *Out.Ptr++ = *Temp.Ptr++; } if(*HMML.annotations[AnnotationIndex].text) { InPtr = HMML.annotations[AnnotationIndex].text; if(!StringsDiffer(Member, InPtr)) { InPtr += StringLength(Member); while(*InPtr && !(*InPtr >= '0' && *InPtr <= '9') && !(*InPtr >= 'A' && *InPtr <= 'Z') && !(*InPtr >= 'a' && *InPtr <= 'z')) { ++InPtr; } if(*InPtr && *InPtr >= 'a' && *InPtr <= 'z') { *InPtr -= 32; } } } } if(HMML.annotations[AnnotationIndex].reference_count) { for(int RefIndex = 0; RefIndex < HMML.annotations[AnnotationIndex].reference_count; ++RefIndex) { while(InPtr - HMML.annotations[AnnotationIndex].text < HMML.annotations[AnnotationIndex].references[RefIndex].offset) { *Out.Ptr++ = *InPtr++; } if(HMML.annotations[AnnotationIndex].references[RefIndex].offset == 0) { if(HMML.annotations[AnnotationIndex].references[RefIndex].page) { Temp.Ptr = HMML.annotations[AnnotationIndex].references[RefIndex].page; } else if(HMML.annotations[AnnotationIndex].references[RefIndex].site) { Temp.Ptr = HMML.annotations[AnnotationIndex].references[RefIndex].site; } else if(HMML.annotations[AnnotationIndex].references[RefIndex].title) { Temp.Ptr = HMML.annotations[AnnotationIndex].references[RefIndex].title; } if(Out.Ptr[-1] != '"' && Out.Ptr[-1] != ' ') { *Out.Ptr++ = ' '; } while(*Temp.Ptr) { *Out.Ptr++ = *Temp.Ptr++; } } if(HMML.annotations[AnnotationIndex].references[RefIndex].url) { Temp.Ptr = HMML.annotations[AnnotationIndex].references[RefIndex].url; if(HMML.annotations[AnnotationIndex].references[RefIndex].offset < StringLength(HMML.annotations[AnnotationIndex].text) || RefIndex < HMML.annotations[AnnotationIndex].reference_count-1) { if(Out.Ptr[-1] != ' ') { *Out.Ptr++ = ' '; } *Out.Ptr++ = '-'; *Out.Ptr++ = ' '; while(*Temp.Ptr) { *Out.Ptr++ = *Temp.Ptr++; } *Out.Ptr++ = ' '; *Out.Ptr++ = '-'; } else { if(Out.Ptr[-1] == ' ') { --Out.Ptr; } if(InPtr[-3] != ':') { *Out.Ptr++ = ':'; *Out.Ptr++ = ' '; } while(*Temp.Ptr) { *Out.Ptr++ = *Temp.Ptr++; } } } } } while(*InPtr) { *Out.Ptr++ = *InPtr++; } if(HMML.annotations[AnnotationIndex].author && WritingChat == TRUE) { *Out.Ptr++ = '\"'; } *Out.Ptr++ = '\n'; skip: {}; } Temp.Location = MemoryArena + ClaimedMemory; Temp.Size = 256; ClaimedMemory += Temp.Size; Temp.Ptr = Temp.Location; sprintf(Temp.Location, "\nAnnotated by %s - https://handmade.network/m/%s\n", HMML.metadata.annotator, HMML.metadata.annotator); while(*Temp.Ptr) { *Out.Ptr++ = *Temp.Ptr++; } *Temp.Location = '\0'; ClaimedMemory -= Temp.Size; if(Out.Ptr - Out.Location > 5000) { WritingChat = FALSE; } else { break; } } if(Out.Ptr - Out.Location > 5000) { fprintf(stderr, "%s: %ld characters over budget. Requires manual shrinking!\n", Args[FileIndex], (Out.Ptr - Out.Location) - 5000); } // NOTE(matt): At this point we should have filled a buffer with the stuff hmml_free(&HMML); // NOTE(matt): Open a file for writing out to Temp.Location = MemoryArena + ClaimedMemory; Temp.Size = StringLength(Args[FileIndex]) + 5; ClaimedMemory += Temp.Size; sprintf(Temp.Location, "%s.txt", Args[FileIndex]); //printf("Writing to %s\n", Temp.Location); FILE *OutFile; if(!(OutFile = fopen(Temp.Location, "w"))) { perror(Args[0]); free(MemoryArena); return 1; } *Temp.Location = '\0'; ClaimedMemory -= Temp.Size; 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); } free(MemoryArena); } return 0; }