2017-03-10 14:19:25 +00:00
#if 0
ctime - begin $ { 0 % . * } . ctm
2017-08-23 20:30:08 +00:00
gcc - g - fsanitize = address - Wall - std = c99 - pipe $ 0 - o $ { 0 % . * } hmml . a - lcurl
2017-10-18 21:07:29 +00:00
# gcc -Wall -std=c99 -pipe $0 -o ${0%.*} hmml.a -lcurl
2017-03-10 14:19:25 +00:00
ctime - end $ { 0 % . * } . ctm
exit
# endif
2017-11-11 00:34:47 +00:00
typedef struct
{
unsigned int Major , Minor , Patch ;
} version ;
version CINERA_APP_VERSION = {
. Major = 0 ,
. Minor = 5 ,
2017-12-18 15:10:02 +00:00
. Patch = 13
2017-11-11 00:34:47 +00:00
} ;
2017-12-12 23:24:10 +00:00
# define CINERA_DB_VERSION 2
2017-10-18 21:07:29 +00:00
2017-05-22 21:37:00 +00:00
# define DEBUG 0
2017-10-18 21:07:29 +00:00
# define DEBUG_MEM 0
2017-05-21 06:35:16 +00:00
2017-03-23 00:34:59 +00:00
typedef unsigned int bool ;
2017-03-10 14:19:25 +00:00
# define TRUE 1
# define FALSE 0
2017-04-13 00:21:04 +00:00
# include <stdarg.h> // NOTE(matt): varargs
# include <stdio.h> // NOTE(matt): printf, sprintf, vsprintf, fprintf, perror
# include <stdlib.h> // NOTE(matt): calloc, malloc, free
2017-03-23 00:34:59 +00:00
# include "hmmlib.h"
2017-06-25 18:05:58 +00:00
# include <getopt.h> // NOTE(matt): getopts
2017-05-25 20:28:52 +00:00
//#include "config.h" // TODO(matt): Implement config.h
2017-08-09 00:57:09 +00:00
# include <curl/curl.h>
2017-08-10 01:05:41 +00:00
# include <time.h>
# include <sys/stat.h>
2017-08-23 20:30:08 +00:00
# include <sys/types.h>
# include <dirent.h>
2017-09-07 21:41:08 +00:00
# include <string.h> // NOTE(matt): strerror
# include <errno.h> //NOTE(matt): errno
2017-09-15 01:11:39 +00:00
# include <sys/inotify.h> // NOTE(matt): inotify
# include <unistd.h> // NOTE(matt): sleep()
2017-03-23 00:34:59 +00:00
2017-05-24 21:56:36 +00:00
# define Kilobytes(Bytes) Bytes << 10
# define Megabytes(Bytes) Bytes << 20
2017-06-10 16:47:47 +00:00
enum
{
2017-08-19 21:41:51 +00:00
EDITION_SINGLE ,
EDITION_PROJECT ,
EDITION_NETWORK
2017-08-29 20:35:28 +00:00
} editions ;
2017-09-07 21:41:08 +00:00
enum
{
// NOTE(matt): https://tools.ietf.org/html/rfc5424#section-6.2.1
LOG_EMERGENCY ,
LOG_ALERT ,
LOG_CRITICAL ,
LOG_ERROR ,
LOG_WARNING ,
LOG_NOTICE ,
LOG_INFORMATIONAL ,
LOG_DEBUG
} log_levels ;
2017-08-29 20:35:28 +00:00
enum
{
2017-12-18 15:10:02 +00:00
MODE_BARE = 1 < < 0 ,
MODE_INTEGRATE = 1 < < 1 ,
MODE_ONESHOT = 1 < < 2
2017-08-29 20:35:28 +00:00
} modes ;
2017-09-07 21:41:08 +00:00
enum
{
RC_ARENA_FULL ,
RC_ERROR_DIRECTORY ,
RC_ERROR_FATAL ,
RC_ERROR_FILE ,
RC_ERROR_HMML ,
RC_ERROR_MAX_REFS ,
RC_ERROR_MEMORY ,
2017-11-11 00:34:47 +00:00
RC_ERROR_PROJECT ,
2017-09-07 21:41:08 +00:00
RC_ERROR_QUOTE ,
2017-11-11 00:34:47 +00:00
RC_ERROR_SEEK ,
2017-09-07 21:41:08 +00:00
RC_FOUND ,
RC_UNFOUND ,
RC_INVALID_REFERENCE ,
2017-11-11 00:34:47 +00:00
RC_INVALID_TEMPLATE ,
2017-09-07 21:41:08 +00:00
RC_NOOP ,
RC_RIP ,
RC_SUCCESS
} returns ;
2017-08-29 20:35:28 +00:00
typedef struct
{
2017-10-18 21:07:29 +00:00
int Edition ;
int LogLevel ;
int Mode ;
int UpdateInterval ;
bool ForceIntegration ;
2017-11-11 00:34:47 +00:00
char * RootDir ; // Absolute
char * RootURL ;
char * CSSDir ; // Relative to RootDir and RootURL
char * ImagesDir ; // Relative to RootDir and RootURL
char * JSDir ; // Relative to RootDir and RootURL
2017-12-12 23:24:10 +00:00
char * ProjectID ;
char * BaseDir ; // Absolute
char * BaseURL ;
char * IndexLocation ; // Relative to BaseDir and BaseURL
char * PlayerLocation ; // Relative to BaseDir and BaseURL
2017-11-11 00:34:47 +00:00
char * TemplateIndexLocation ; // Relative to RootDir and RootURL
char * TemplatePlayerLocation ; // Relative to RootDir and RootURL
2017-12-07 01:15:13 +00:00
char * Theme ;
2017-11-11 00:34:47 +00:00
char CacheDir [ 256 ] ;
2017-08-29 20:35:28 +00:00
char * DefaultMedium ;
char * OutLocation ;
char * OutIntegratedLocation ;
char * ProjectDir ;
2017-12-12 23:24:10 +00:00
char * PlayerURLPrefix ; /* NOTE(matt): This will become a full blown customisable output URL.
2017-12-07 21:07:36 +00:00
For now it simply replaces the ProjectID */
2017-08-29 20:35:28 +00:00
} config ;
2017-06-10 16:47:47 +00:00
2017-09-07 21:41:08 +00:00
typedef struct
{
void * Location ;
void * Ptr ;
char * ID ;
int Size ;
} arena ;
2017-10-18 21:07:29 +00:00
// NOTE(matt): Globals
config Config ;
arena MemoryArena ;
2017-03-10 14:19:25 +00:00
typedef struct
{
char * Location ;
char * Ptr ;
2017-05-21 06:35:16 +00:00
char * ID ;
2017-03-10 14:19:25 +00:00
int Size ;
} buffer ;
2017-10-18 21:07:29 +00:00
typedef struct
{
buffer Buffer ;
FILE * Handle ;
2017-11-11 00:34:47 +00:00
char Path [ 256 ] ;
2017-10-18 21:07:29 +00:00
int FileSize ;
} file_buffer ;
2017-09-07 21:41:08 +00:00
enum
{
2017-10-18 21:07:29 +00:00
// Contents Page
2017-09-07 21:41:08 +00:00
TAG_INDEX ,
2017-10-18 21:07:29 +00:00
// Player Page
2017-09-07 21:41:08 +00:00
TAG_INCLUDES ,
TAG_MENUS ,
TAG_PLAYER ,
TAG_SCRIPT ,
2017-10-18 21:07:29 +00:00
// Anywhere
2017-12-07 01:15:13 +00:00
TAG_PROJECT ,
2017-12-12 23:24:10 +00:00
TAG_TITLE ,
TAG_URL ,
TAG_VIDEO_ID ,
2017-09-07 21:41:08 +00:00
} template_tags ;
typedef struct
{
int Code ; // template_tags
char * Tag ;
} tag ;
tag Tags [ ] = {
{ TAG_INDEX , " __CINERA_INDEX__ " } ,
{ TAG_INCLUDES , " __CINERA_INCLUDES__ " } ,
{ TAG_MENUS , " __CINERA_MENUS__ " } ,
{ TAG_PLAYER , " __CINERA_PLAYER__ " } ,
{ TAG_SCRIPT , " __CINERA_SCRIPT__ " } ,
2017-12-07 01:15:13 +00:00
{ TAG_PROJECT , " __CINERA_PROJECT__ " } ,
2017-09-07 21:41:08 +00:00
{ TAG_TITLE , " __CINERA_TITLE__ " } ,
2017-12-12 23:24:10 +00:00
{ TAG_URL , " __CINERA_URL__ " } ,
{ TAG_VIDEO_ID , " __CINERA_VIDEO_ID__ " } ,
2017-09-07 21:41:08 +00:00
} ;
typedef struct
{
int Offset ;
int TagCode ;
} tag_offset ;
typedef struct
{
2017-11-11 00:34:47 +00:00
char Filename [ 256 ] ;
2017-09-07 21:41:08 +00:00
tag_offset Tag [ 16 ] ;
int Validity ; // NOTE(matt): Bitmask describing which page the template is valid for, i.e. contents and / or player page
int TagCount ;
2017-10-18 21:07:29 +00:00
} template_metadata ;
typedef struct
{
template_metadata Metadata ;
buffer Buffer ;
2017-09-07 21:41:08 +00:00
} template ;
2017-08-29 20:35:28 +00:00
typedef struct
{
buffer IncludesIndex ;
2017-11-11 00:34:47 +00:00
buffer Search ;
2017-12-07 01:15:13 +00:00
buffer Index ; // NOTE(matt): This buffer is malloc'd separately, rather than claimed from the memory_arena
2017-11-11 00:34:47 +00:00
buffer ScriptIndex ;
2017-08-29 20:35:28 +00:00
buffer IncludesPlayer ;
buffer Menus ;
buffer Player ;
2017-11-11 00:34:47 +00:00
buffer ScriptPlayer ;
2017-12-07 01:15:13 +00:00
char ProjectName [ 32 ] ;
2017-12-12 23:24:10 +00:00
char Title [ 256 ] ;
char URLIndex [ 2048 ] ;
char URLPlayer [ 2048 ] ;
char VideoID [ 16 ] ;
2017-08-29 20:35:28 +00:00
} buffers ;
2017-05-21 06:35:16 +00:00
// TODO(matt): Consider putting the ref_info and quote_info into linked lists on the heap, just to avoid all the hardcoded sizes
typedef struct
{
char Date [ 32 ] ;
char Text [ 512 ] ;
} quote_info ;
2017-03-19 01:23:44 +00:00
typedef struct
{
2017-03-30 02:57:04 +00:00
char Timecode [ 8 ] ;
int Identifier ;
} identifier ;
2017-07-29 23:01:39 +00:00
# define REF_MAX_IDENTIFIER 64
2017-05-13 15:38:10 +00:00
2017-03-30 02:57:04 +00:00
typedef struct
{
2017-04-13 00:21:04 +00:00
char RefTitle [ 620 ] ;
char ID [ 512 ] ;
char URL [ 512 ] ;
char Source [ 256 ] ;
2017-05-13 15:38:10 +00:00
identifier Identifier [ REF_MAX_IDENTIFIER ] ;
2017-03-30 02:57:04 +00:00
int IdentifierCount ;
2017-03-19 01:23:44 +00:00
} ref_info ;
2017-04-23 02:47:42 +00:00
typedef struct
{
2017-05-24 21:56:36 +00:00
char Marker [ 32 ] ;
char WrittenText [ 32 ] ;
2017-05-21 06:35:16 +00:00
} category_info ;
2017-04-23 02:47:42 +00:00
2017-08-19 21:41:51 +00:00
typedef struct
{
category_info Category [ 64 ] ;
int Count ;
} categories ;
2017-05-25 20:28:52 +00:00
// TODO(matt): Parse this stuff out of a config file
2017-08-18 22:46:58 +00:00
typedef struct
{
char * Username ;
char * CreditedName ;
char * HomepageURL ;
char * SupportIcon ;
char * SupportURL ;
} credential_info ;
credential_info Credentials [ ] =
2017-05-25 20:28:52 +00:00
{
2017-11-18 00:27:33 +00:00
{ " /a_waterman " , " Andrew Waterman " , " https://www.linkedin.com/in/andrew-waterman-76805788 " , " " , " " } ,
{ " /y_lee " , " Yunsup Lee " , " https://www.linkedin.com/in/yunsup-lee-385b692b/ " , " " , " " } ,
2017-12-03 00:47:27 +00:00
{ " AndrewJDR " , " Andrew Johnson " , " " , " " , " " } ,
2017-12-07 01:15:13 +00:00
{ " AsafGartner " , " Asaf Gartner " , " " , " " , " " } ,
2017-11-18 00:27:33 +00:00
{ " BretHudson " , " Bret Hudson " , " http://www.brethudson.com/ " , " cinera_sprite_patreon.png " , " https://www.patreon.com/indieFunction " } ,
{ " ChronalDragon " , " Andrew Chronister " , " http://chronal.net/ " , " " , " " } ,
{ " Kelimion " , " Jeroen van Rijn " , " https://handmade.network/home " , " " , " " } ,
2017-12-03 00:47:27 +00:00
{ " Mannilie " , " Emmanuel Vaccaro " , " http://emmanuelvaccaro.com/ " , " " , " " } ,
2017-12-17 20:44:00 +00:00
{ " Miblo " , " Matt Mascarenhas " , " http://miblodelcarpio.co.uk " , " " , " " } ,
{ " Mr4thDimention " , " Allen Webster " , " http://www.4coder.net/ " , " " , " " } ,
{ " Quel_Solaar " , " Eskil Steenberg " , " http://quelsolaar.com/ " , " " , " " } ,
2017-12-03 00:47:27 +00:00
{ " ZedZull " , " Jay Waggle " , " " , " " , " " } ,
2017-12-17 20:44:00 +00:00
{ " abnercoimbre " , " Abner Coimbre " , " https://handmade.network/m/abnercoimbre " , " " , " " } ,
2017-11-18 00:27:33 +00:00
{ " brianwill " , " Brian Will " , " http://brianwill.net/blog/ " , " " , " " } ,
2017-12-03 00:55:01 +00:00
{ " cbloom " , " Charles Bloom " , " http://cbloomrants.blogspot.co.uk/ " , " " , " " } ,
2017-12-10 17:29:00 +00:00
{ " cmuratori " , " Casey Muratori " , " https://handmadehero.org " , " cinera_sprite_sendowl.png " , " https://handmadehero.org/patreon.html " } ,
2017-12-03 18:25:20 +00:00
{ " csnover " , " Colin Snover " , " https://zetafleet.com/ " , " " , " " } ,
2017-08-18 22:46:58 +00:00
{ " debiatan " , " Miguel Lechón " , " http://blog.debiatan.net/ " , " " , " " } ,
2017-12-03 00:47:27 +00:00
{ " dspecht " , " Dustin Specht " , " " , " " , " " } ,
{ " effect0r " , " Cory Henderlite " , " " , " " , " " } ,
{ " ffsjs " , " ffsjs " , " " , " " , " " } ,
2017-11-18 00:27:33 +00:00
{ " fierydrake " , " Mike Tunnicliffe " , " " , " " , " " } ,
{ " garlandobloom " , " Matthew VanDevander " , " https://lowtideproductions.com/ " , " cinera_sprite_patreon.png " , " https://www.patreon.com/mv " } ,
2017-12-03 00:47:27 +00:00
{ " ikerms " , " Iker Murga " , " " , " " , " " } ,
{ " insofaras " , " Alex Baines " , " https://abaines.me.uk/ " , " " , " " } ,
{ " jacebennett " , " Jace Bennett " , " " , " " , " " } ,
2017-11-18 00:27:33 +00:00
{ " jon " , " Jonathan Blow " , " http://the-witness.net/news/ " , " " , " " } ,
2017-12-07 01:15:13 +00:00
{ " jpike " , " Jacob Pike " , " " , " " , " " } ,
2017-12-03 00:47:27 +00:00
{ " martincohen " , " Martin Cohen " , " http://blog.coh.io/ " , " " , " " } ,
2017-11-18 00:27:33 +00:00
{ " miotatsu " , " Mio Iwakura " , " http://riscy.tv/ " , " cinera_sprite_patreon.png " , " https://patreon.com/miotatsu " } ,
{ " nothings " , " Sean Barrett " , " https://nothings.org/ " , " " , " " } ,
{ " philipbuuck " , " Philip Buuck " , " http://philipbuuck.com/ " , " " , " " } ,
2017-12-03 18:14:19 +00:00
{ " powerc9000 " , " Clay Murray " , " http://claymurray.website/ " , " " , " " } ,
2017-11-18 00:27:33 +00:00
{ " rygorous " , " Fabian Giesen " , " https://fgiesen.wordpress.com/ " , " " , " " } ,
2017-12-03 00:47:27 +00:00
{ " schme " , " Kasper Sauramo " , " " , " " , " " } ,
2017-11-18 00:27:33 +00:00
{ " sssmcgrath " , " Shawn McGrath " , " http://www.dyadgame.com/ " , " " , " " } ,
{ " thehappiecat " , " Anne " , " https://www.youtube.com/c/TheHappieCat " , " cinera_sprite_patreon.png " , " https://www.patreon.com/thehappiecat " } ,
2017-12-07 01:15:13 +00:00
{ " theinternetftw " , " Ben Craddock " , " " , " " , " " } ,
2017-12-03 00:47:27 +00:00
{ " wheatdog " , " Tim Liou " , " http://stringbulbs.com/ " , " " , " " } ,
2017-11-18 00:27:33 +00:00
{ " williamchyr " , " William Chyr " , " http://williamchyr.com/ " , " " , " " } ,
{ " wonchun " , " Won Chun " , " https://twitter.com/won3d " , " " , " " } ,
2017-08-18 22:46:58 +00:00
} ;
typedef struct
{
char * Medium ;
char * Icon ;
char * WrittenName ;
} category_medium ;
category_medium CategoryMedium [ ] =
{
// medium icon written name
2017-11-28 23:02:00 +00:00
{ " admin " , " 🗹 " , " Administrivia " } ,
2017-12-03 00:55:01 +00:00
{ " afk " , " … " , " Away from Keyboard " } ,
2017-08-18 22:46:58 +00:00
{ " authored " , " 🗪 " , " Chat Comment " } , // TODO(matt): Conditionally handle Chat vs Guest Comments
{ " blackboard " , " 🖌 " , " Blackboard " } ,
{ " experience " , " 🍷 " , " Experience " } ,
2017-11-28 23:02:00 +00:00
{ " hat " , " 🎩 " , " Hat " } ,
{ " multimedia " , " 🎬 " , " Media Clip " } ,
2017-08-18 22:46:58 +00:00
{ " owl " , " 🦉 " , " Owl of Shame " } ,
{ " programming " , " 🖮 " , " Programming " } , // TODO(matt): Potentially make this configurable per project
{ " rant " , " 💢 " , " Rant " } ,
{ " research " , " 📖 " , " Research " } ,
{ " run " , " 🏃 " , " In-Game " } , // TODO(matt): Potentially make this configurable per project
2017-10-18 21:07:29 +00:00
{ " speech " , " 🗩 " , " Speech " } ,
2017-08-18 22:46:58 +00:00
{ " trivia " , " 🎲 " , " Trivia " } ,
2017-05-25 20:28:52 +00:00
} ;
2017-11-11 00:34:47 +00:00
enum
{
NS_CALENDRICAL ,
NS_LINEAR ,
NS_SEASONAL ,
} numbering_schemes ;
typedef struct
{
char * ProjectID ;
char * FullName ;
char * Unit ; // e.g. Day, Episode, Session
2017-12-07 21:07:36 +00:00
int NumberingScheme ; // numbering_schemes
char * Medium ;
char * AltURLPrefix ; // NOTE(matt): This currently just straight up replaces the ProjectID in the player pages' output directories
2017-11-11 00:34:47 +00:00
} project_info ;
project_info ProjectInfo [ ] =
{
2017-12-07 21:07:36 +00:00
{ " book " , " Book Club " , " Day " , NS_LINEAR , " research " , " " } ,
{ " pcalc " , " pcalc " , " Day " , NS_LINEAR , " programming " , " " } ,
{ " riscy " , " RISCY BUSINESS " , " Day " , NS_LINEAR , " programming " , " " } ,
2017-12-07 01:15:13 +00:00
2017-12-07 21:07:36 +00:00
{ " chat " , " Handmade Chat " , " Day " , NS_LINEAR , " speech " , " " } ,
{ " code " , " Handmade Hero " , " Day " , NS_LINEAR , " programming " , " day " } ,
{ " intro-to-c " , " Intro to C on Windows " , " Day " , NS_LINEAR , " programming " , " day " } ,
{ " misc " , " Handmade Miscellany " , " " , NS_LINEAR , " admin " , " " } ,
{ " ray " , " Handmade Ray " , " Day " , NS_LINEAR , " programming " , " " } ,
2017-12-07 01:15:13 +00:00
2017-12-07 21:07:36 +00:00
{ " hmdshow " , " HandmadeDev Show " , " " , NS_SEASONAL , " speech " , " " } ,
2017-12-07 01:15:13 +00:00
2017-12-07 21:07:36 +00:00
{ " obbg " , " Open Block Building Game " , " Episode " , NS_LINEAR , " programming " , " " } ,
2017-12-07 01:15:13 +00:00
2017-12-07 21:07:36 +00:00
{ " sysadmin " , " SysAdmin " , " Session " , NS_LINEAR , " admin " , " " } ,
2017-11-11 00:34:47 +00:00
} ;
2017-03-25 02:07:55 +00:00
# define ArrayCount(A) sizeof(A) / sizeof(*(A))
2017-08-20 21:23:15 +00:00
__attribute__ ( ( format ( printf , 2 , 3 ) ) )
2017-11-11 00:34:47 +00:00
int
2017-08-20 21:23:15 +00:00
CopyString ( char Dest [ ] , char * Format , . . . )
2017-05-21 06:35:16 +00:00
{
2017-11-11 00:34:47 +00:00
int Length = 0 ;
2017-08-20 21:23:15 +00:00
va_list Args ;
va_start ( Args , Format ) ;
2017-11-11 00:34:47 +00:00
Length = vsprintf ( Dest , Format , Args ) ;
2017-08-20 21:23:15 +00:00
va_end ( Args ) ;
2017-11-11 00:34:47 +00:00
return Length ;
2017-03-16 00:56:58 +00:00
}
2017-03-10 14:19:25 +00:00
int
2017-08-20 21:23:15 +00:00
StringLength ( char * String )
2017-03-10 14:19:25 +00:00
{
2017-08-20 21:23:15 +00:00
int i = 0 ;
while ( String [ i ] )
2017-03-10 14:19:25 +00:00
{
2017-08-20 21:23:15 +00:00
+ + i ;
2017-03-10 14:19:25 +00:00
}
2017-08-20 21:23:15 +00:00
return i ;
2017-03-10 14:19:25 +00:00
}
2017-03-16 00:56:58 +00:00
void
2017-03-30 02:57:04 +00:00
CopyBuffer ( buffer * Dest , buffer * Src )
2017-03-16 00:56:58 +00:00
{
Src - > Ptr = Src - > Location ;
while ( * Src - > Ptr )
{
2017-05-21 06:35:16 +00:00
// TODO(matt)
{
if ( Dest - > Ptr - Dest - > Location > = Dest - > Size )
{
2017-05-24 21:56:36 +00:00
fprintf ( stderr , " CopyBuffer: %s cannot accommodate %s \n " , Dest - > ID , Src - > ID ) ;
2017-05-21 06:35:16 +00:00
__asm__ ( " int3 " ) ;
}
}
2017-03-16 00:56:58 +00:00
* Dest - > Ptr + + = * Src - > Ptr + + ;
}
2017-08-19 21:41:51 +00:00
* Dest - > Ptr = ' \0 ' ;
2017-03-16 00:56:58 +00:00
}
2017-06-13 22:13:03 +00:00
int
2017-06-07 23:47:47 +00:00
CopyStringNoFormat ( char * Dest , char * String )
{
2017-06-13 22:13:03 +00:00
int Length = 0 ;
2017-06-07 23:47:47 +00:00
while ( * String )
{
* Dest + + = * String + + ;
2017-06-13 22:13:03 +00:00
+ + Length ;
2017-06-07 23:47:47 +00:00
}
2017-06-15 22:10:36 +00:00
* Dest = ' \0 ' ;
2017-06-13 22:13:03 +00:00
return Length ;
2017-06-07 23:47:47 +00:00
}
2017-08-29 20:35:28 +00:00
// TODO(matt): Maybe do a version of this that takes a string as a Terminator
2017-08-18 22:46:58 +00:00
int
CopyStringNoFormatT ( char * Dest , char * String , char Terminator )
{
int Length = 0 ;
while ( * String ! = Terminator )
{
* Dest + + = * String + + ;
+ + Length ;
}
* Dest = ' \0 ' ;
return Length ;
}
2017-03-29 03:38:12 +00:00
__attribute__ ( ( format ( printf , 2 , 3 ) ) )
2017-03-16 00:56:58 +00:00
void
2017-03-23 00:34:59 +00:00
CopyStringToBuffer ( buffer * Dest , char * Format , . . . )
2017-03-16 00:56:58 +00:00
{
2017-03-23 00:34:59 +00:00
va_list Args ;
va_start ( Args , Format ) ;
2017-06-03 01:32:18 +00:00
int Length = vsnprintf ( Dest - > Ptr , Dest - > Size - ( Dest - > Ptr - Dest - > Location ) , Format , Args ) ;
2017-03-23 00:34:59 +00:00
va_end ( Args ) ;
2017-11-11 00:34:47 +00:00
// TODO(matt)
2017-05-21 06:35:16 +00:00
{
if ( Length + ( Dest - > Ptr - Dest - > Location ) > = Dest - > Size )
{
2017-06-21 23:16:09 +00:00
fprintf ( stderr , " CopyStringToBuffer: %s cannot accommodate %d-character string: \n "
" \n "
2017-06-23 14:47:48 +00:00
" %s \n " , Dest - > ID , Length , Format ) ;
2017-05-21 06:35:16 +00:00
__asm__ ( " int3 " ) ;
}
}
2017-03-23 00:34:59 +00:00
Dest - > Ptr + = Length ;
2017-03-16 00:56:58 +00:00
}
2017-06-07 23:47:47 +00:00
void
CopyStringToBufferHTMLSafe ( buffer * Dest , char * String )
{
while ( * String )
{
if ( Dest - > Ptr - Dest - > Location > = Dest - > Size )
{
fprintf ( stderr , " CopyStringToBufferHTMLSafe: %s cannot accommodate %d-character string \n " , Dest - > ID , StringLength ( String ) ) ;
__asm__ ( " int3 " ) ;
}
switch ( * String )
{
case ' < ' :
2017-06-25 18:05:58 +00:00
CopyStringToBuffer ( Dest , " < " ) ;
String + + ;
break ;
2017-06-07 23:47:47 +00:00
case ' > ' :
2017-06-25 18:05:58 +00:00
CopyStringToBuffer ( Dest , " > " ) ;
String + + ;
break ;
2017-06-07 23:47:47 +00:00
case ' & ' :
2017-06-25 18:05:58 +00:00
CopyStringToBuffer ( Dest , " & " ) ;
String + + ;
break ;
2017-06-07 23:47:47 +00:00
case ' \" ' :
2017-06-25 18:05:58 +00:00
CopyStringToBuffer ( Dest , " " " ) ;
String + + ;
break ;
2017-06-07 23:47:47 +00:00
case ' \' ' :
2017-06-25 18:05:58 +00:00
CopyStringToBuffer ( Dest , " ' " ) ;
String + + ;
break ;
2017-06-07 23:47:47 +00:00
default :
2017-06-25 18:05:58 +00:00
* Dest - > Ptr + + = * String + + ;
break ;
2017-06-07 23:47:47 +00:00
}
}
}
2017-11-11 00:34:47 +00:00
void
CopyStringToBufferJSONSafe ( buffer * Dest , char * String )
{
while ( * String )
{
if ( Dest - > Ptr - Dest - > Location > = Dest - > Size )
{
fprintf ( stderr , " CopyStringToBufferHTMLSafe: %s cannot accommodate %d-character string \n " , Dest - > ID , StringLength ( String ) ) ;
__asm__ ( " int3 " ) ;
}
switch ( * String )
{
case ' \\ ' :
case ' \" ' :
* Dest - > Ptr + + = ' \\ ' ;
default :
* Dest - > Ptr + + = * String + + ;
break ;
}
}
}
2017-03-17 01:45:16 +00:00
int
2017-06-13 22:13:03 +00:00
StringsDiffer ( char * A , char * B ) // NOTE(matt): Two null-terminated strings
2017-03-17 01:45:16 +00:00
{
2017-03-21 02:38:18 +00:00
while ( * A & & * B & & * A = = * B )
2017-03-17 01:45:16 +00:00
{
+ + A , + + B ;
}
return * A - * B ;
}
2017-04-23 00:30:37 +00:00
bool
2017-06-13 22:13:03 +00:00
StringsDifferT ( char * A , // NOTE(matt): Null-terminated string
char * B , // NOTE(matt): Not null-terminated string (e.g. one mid-buffer)
2017-06-16 07:55:59 +00:00
char Terminator // NOTE(matt): Caller definable terminator. Pass 0 to only match on the extent of A
2017-06-13 22:13:03 +00:00
)
2017-04-23 00:30:37 +00:00
{
2017-06-13 22:13:03 +00:00
int ALength = StringLength ( A ) ;
2017-04-23 00:30:37 +00:00
int i = 0 ;
2017-06-13 22:13:03 +00:00
while ( i < ALength & & A [ i ] & & A [ i ] = = B [ i ] )
2017-04-23 00:30:37 +00:00
{
+ + i ;
}
2017-06-13 22:13:03 +00:00
if ( ( ! Terminator & & ! A [ i ] & & ALength = = i ) | |
2017-06-25 18:05:58 +00:00
( ! A [ i ] & & ALength = = i & & ( B [ i ] = = Terminator ) ) )
2017-04-23 00:30:37 +00:00
{
return FALSE ;
}
else
{
return TRUE ;
}
}
2017-08-20 21:23:15 +00:00
int
MakeDir ( char * Path )
{
2017-09-07 21:41:08 +00:00
// TODO(matt): Correctly check for permissions
2017-08-20 21:23:15 +00:00
int i = StringLength ( Path ) ;
int Ancestors = 0 ;
while ( mkdir ( Path , 00755 ) = = - 1 )
{
2017-09-07 21:41:08 +00:00
if ( errno = = EACCES )
{
return RC_ERROR_DIRECTORY ;
}
2017-08-20 21:23:15 +00:00
while ( Path [ i ] ! = ' / ' & & i > 0 )
{
- - i ;
}
+ + Ancestors ;
Path [ i ] = ' \0 ' ;
2017-09-07 21:41:08 +00:00
if ( i = = 0 ) { return RC_ERROR_DIRECTORY ; }
2017-08-20 21:23:15 +00:00
}
while ( Ancestors > 0 )
{
while ( Path [ i ] ! = ' \0 ' )
{
+ + i ;
}
Path [ i ] = ' / ' ;
- - Ancestors ;
if ( ( mkdir ( Path , 00755 ) ) = = - 1 )
{
2017-09-07 21:41:08 +00:00
return RC_ERROR_DIRECTORY ;
2017-08-20 21:23:15 +00:00
}
}
2017-09-07 21:41:08 +00:00
return RC_SUCCESS ;
}
void
2017-10-18 21:07:29 +00:00
LogUsage ( buffer * Buffer )
2017-09-07 21:41:08 +00:00
{
2017-09-15 01:11:39 +00:00
# if DEBUG
2017-11-11 00:34:47 +00:00
char LogPath [ 256 ] ;
2017-10-18 21:07:29 +00:00
CopyString ( LogPath , " %s/%s " , Config . CacheDir , " buffers.log " ) ;
2017-09-07 21:41:08 +00:00
FILE * LogFile ;
if ( ! ( LogFile = fopen ( LogPath , " a+ " ) ) )
{
MakeDir ( CacheDir ) ;
if ( ! ( LogFile = fopen ( LogPath , " a+ " ) ) )
{
perror ( " LogUsage " ) ;
return ;
}
}
fprintf ( LogFile , " %s,%ld,%d \n " ,
2017-10-18 21:07:29 +00:00
Buffer - > ID ,
Buffer - > Ptr - Buffer - > Location ,
Buffer - > Size ) ;
2017-09-07 21:41:08 +00:00
fclose ( LogFile ) ;
2017-09-15 01:11:39 +00:00
# endif
2017-09-07 21:41:08 +00:00
}
2017-10-18 21:07:29 +00:00
__attribute__ ( ( format ( printf , 2 , 3 ) ) )
2017-09-07 21:41:08 +00:00
void
2017-10-18 21:07:29 +00:00
LogError ( int LogLevel , char * Format , . . . )
2017-09-07 21:41:08 +00:00
{
if ( Config . LogLevel > = LogLevel )
{
2017-11-11 00:34:47 +00:00
char LogPath [ 256 ] ;
2017-09-07 21:41:08 +00:00
CopyString ( LogPath , " %s/%s " , Config . CacheDir , " errors.log " ) ;
FILE * LogFile ;
if ( ! ( LogFile = fopen ( LogPath , " a+ " ) ) )
{
MakeDir ( Config . CacheDir ) ;
if ( ! ( LogFile = fopen ( LogPath , " a+ " ) ) )
{
perror ( " LogUsage " ) ;
return ;
}
}
va_list Args ;
va_start ( Args , Format ) ;
vfprintf ( LogFile , Format , Args ) ;
va_end ( Args ) ;
// TODO(matt): Include the LogLevel "string" and the current wall time
fprintf ( LogFile , " \n " ) ;
fclose ( LogFile ) ;
}
2017-08-20 21:23:15 +00:00
}
void
FreeBuffer ( buffer * Buffer )
{
free ( Buffer - > Location ) ;
2017-11-11 00:34:47 +00:00
Buffer - > Location = ' \0 ' ;
2017-10-18 21:07:29 +00:00
# if DEBUG_MEM
FILE * MemLog = fopen ( " /home/matt/cinera_mem " , " a+ " ) ;
fprintf ( MemLog , " Freed %s \n " , Buffer - > ID ) ;
fclose ( MemLog ) ;
printf ( " Freed %s \n " , Buffer - > ID ) ;
# endif
2017-08-20 21:23:15 +00:00
}
2017-08-29 20:35:28 +00:00
#if 0
# define ClaimBuffer(MemoryArena, Buffer, ID, Size) if(__ClaimBuffer(MemoryArena, Buffer, ID, Size))\
2017-08-20 21:23:15 +00:00
{ \
fprintf ( stderr , " %s:%d: MemoryArena cannot contain %s of size %d \n " , __FILE__ , __LINE__ , ID , Size ) ; \
hmml_free ( & HMML ) ; \
FreeBuffer ( MemoryArena ) ; \
return 1 ; \
} ;
2017-08-29 20:35:28 +00:00
# endif
2017-08-20 21:23:15 +00:00
int
2017-10-18 21:07:29 +00:00
ClaimBuffer ( buffer * Buffer , char * ID , int Size )
2017-08-20 21:23:15 +00:00
{
2017-10-18 21:07:29 +00:00
if ( MemoryArena . Ptr - MemoryArena . Location + Size > MemoryArena . Size )
2017-08-20 21:23:15 +00:00
{
2017-09-07 21:41:08 +00:00
return RC_ARENA_FULL ;
2017-08-20 21:23:15 +00:00
}
2017-10-18 21:07:29 +00:00
Buffer - > Location = ( char * ) MemoryArena . Ptr ;
2017-08-20 21:23:15 +00:00
Buffer - > Size = Size ;
Buffer - > ID = ID ;
2017-10-18 21:07:29 +00:00
MemoryArena . Ptr + = Buffer - > Size ;
2017-08-20 21:23:15 +00:00
* Buffer - > Location = ' \0 ' ;
Buffer - > Ptr = Buffer - > Location ;
# if DEBUG
2017-10-18 21:07:29 +00:00
float PercentageUsed = ( float ) ( MemoryArena - > Ptr - MemoryArena - > Location ) / MemoryArena - > Size * 100 ;
printf ( " ClaimBuffer(%s): %d \n "
" Total ClaimedMemory: %ld (%.2f%%, leaving %ld free) \n \n " , Buffer - > ID , Buffer - > Size , MemoryArena - > Ptr - MemoryArena - > Location , PercentageUsed , MemoryArena - > Size - ( MemoryArena - > Ptr - MemoryArena - > Location ) ) ;
2017-08-20 21:23:15 +00:00
# endif
2017-09-07 21:41:08 +00:00
return RC_SUCCESS ;
2017-08-20 21:23:15 +00:00
}
void
2017-10-18 21:07:29 +00:00
DeclaimBuffer ( buffer * Buffer )
2017-08-20 21:23:15 +00:00
{
* Buffer - > Location = ' \0 ' ;
2017-10-18 21:07:29 +00:00
MemoryArena . Ptr - = Buffer - > Size ;
2017-08-20 21:23:15 +00:00
float PercentageUsed = ( float ) ( Buffer - > Ptr - Buffer - > Location ) / Buffer - > Size * 100 ;
# if DEBUG
2017-10-18 21:07:29 +00:00
printf ( " DeclaimBuffer(%s) \n "
2017-08-20 21:23:15 +00:00
" Used: %ld / %d (%.2f%%) \n "
" \n "
2017-08-29 20:35:28 +00:00
" Total ClaimedMemory: %ld \n \n " ,
2017-08-20 21:23:15 +00:00
Buffer - > ID ,
Buffer - > Ptr - Buffer - > Location ,
Buffer - > Size ,
PercentageUsed ,
2017-10-18 21:07:29 +00:00
MemoryArena . Ptr - MemoryArena . Location ) ;
2017-08-20 21:23:15 +00:00
# endif
2017-10-18 21:07:29 +00:00
LogUsage ( Buffer ) ;
2017-08-20 21:23:15 +00:00
if ( PercentageUsed > = 80.0f )
{
// TODO(matt): Implement either dynamically growing buffers, or phoning home to matt@handmadedev.org
2017-10-18 21:07:29 +00:00
LogError ( LOG_ERROR , " %s used %.2f%% of its allotted memory \n " , Buffer - > ID , PercentageUsed ) ;
2017-08-20 21:23:15 +00:00
fprintf ( stderr , " Warning: %s used %.2f%% of its allotted memory \n " , Buffer - > ID , PercentageUsed ) ;
}
2017-11-11 00:34:47 +00:00
Buffer - > Size = 0 ;
2017-08-20 21:23:15 +00:00
}
2017-10-18 21:07:29 +00:00
void RewindBuffer ( buffer * Buffer )
{
# if DEBUG
float PercentageUsed = ( float ) ( Buffer - > Ptr - Buffer - > Location ) / Buffer - > Size * 100 ;
printf ( " Rewinding %s \n "
" Used: %ld / %d (%.2f%%) \n \n " ,
Buffer - > ID ,
Buffer - > Ptr - Buffer - > Location ,
Buffer - > Size ,
PercentageUsed ) ;
# endif
Buffer - > Ptr = Buffer - > Location ;
}
2017-09-07 21:41:08 +00:00
int
2017-10-18 21:07:29 +00:00
ClaimTemplate ( template * * Template , char * Location )
2017-09-07 21:41:08 +00:00
{
2017-10-18 21:07:29 +00:00
* Template = ( template * ) MemoryArena . Ptr ;
( * Template ) - > Buffer . Location = MemoryArena . Ptr + sizeof ( template ) ;
( * Template ) - > Buffer . Ptr = ( * Template ) - > Buffer . Location ;
( * Template ) - > Buffer . ID = Location ;
2017-12-07 01:15:13 +00:00
if ( Location [ 0 ] ! = ' / ' )
{
CopyString ( ( * Template ) - > Metadata . Filename , " %s/%s " , Config . RootDir , Location ) ;
}
else
{
CopyString ( ( * Template ) - > Metadata . Filename , " %s " , Location ) ;
}
2017-10-18 21:07:29 +00:00
FILE * File ;
if ( ! ( File = fopen ( ( * Template ) - > Metadata . Filename , " r " ) ) )
{
LogError ( LOG_ERROR , " Unable to open template %s: %s " , ( * Template ) - > Metadata . Filename , strerror ( errno ) ) ;
fprintf ( stderr , " Unable to open template %s: %s \n " , ( * Template ) - > Metadata . Filename , strerror ( errno ) ) ;
return RC_ERROR_FILE ;
}
fseek ( File , 0 , SEEK_END ) ;
( * Template ) - > Buffer . Size = ftell ( File ) ;
if ( MemoryArena . Ptr - MemoryArena . Location + sizeof ( template ) + ( * Template ) - > Buffer . Size > MemoryArena . Size )
2017-09-07 21:41:08 +00:00
{
return RC_ARENA_FULL ;
}
2017-10-18 21:07:29 +00:00
MemoryArena . Ptr + = sizeof ( template ) + ( * Template ) - > Buffer . Size ;
fseek ( File , 0 , SEEK_SET ) ;
fread ( ( * Template ) - > Buffer . Location , ( * Template ) - > Buffer . Size , 1 , File ) ;
fclose ( File ) ;
2017-09-07 21:41:08 +00:00
# if DEBUG
2017-10-18 21:07:29 +00:00
printf ( " ClaimTemplate(%s): %ld \n "
" Total ClaimedMemory: %ld \n \n " , ( * Template ) - > Metadata . Filename , sizeof ( template ) + ( * Template ) - > Buffer . Size , MemoryArena - > Ptr - MemoryArena - > Location ) ;
2017-09-07 21:41:08 +00:00
# endif
return RC_SUCCESS ;
}
int
2017-10-18 21:07:29 +00:00
DeclaimTemplate ( template * Template )
2017-09-07 21:41:08 +00:00
{
2017-10-18 21:07:29 +00:00
MemoryArena . Ptr - = ( sizeof ( template ) + ( * Template ) . Buffer . Size ) ;
2017-09-07 21:41:08 +00:00
# if DEBUG
2017-10-18 21:07:29 +00:00
printf ( " DeclaimTemplate(%s) \n "
2017-09-07 21:41:08 +00:00
" Total ClaimedMemory: %ld \n \n " ,
2017-10-18 21:07:29 +00:00
( * Template ) . Metadata . Filename ,
MemoryArena . Ptr - MemoryArena . Location ) ;
2017-09-07 21:41:08 +00:00
# endif
return RC_SUCCESS ;
}
2017-08-20 21:23:15 +00:00
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 ] ;
}
2017-04-13 23:46:21 +00:00
typedef struct
{
2017-04-21 01:25:40 +00:00
unsigned int Hue : 16 ;
unsigned int Saturation : 8 ;
unsigned int Lightness : 8 ;
2017-04-13 23:46:21 +00:00
} hsl_colour ;
hsl_colour
2017-03-18 02:04:13 +00:00
CharToColour ( char Char )
{
2017-04-13 23:46:21 +00:00
hsl_colour Colour ;
2017-03-18 02:04:13 +00:00
if ( Char > = ' a ' & & Char < = ' z ' )
{
2017-04-19 01:10:45 +00:00
Colour . Hue = ( ( ( float ) Char - ' a ' ) / ( ' z ' - ' a ' ) * 360 ) ;
Colour . Saturation = ( ( ( float ) Char - ' a ' ) / ( ' z ' - ' a ' ) * 26 + 74 ) ;
2017-03-18 02:04:13 +00:00
}
else if ( Char > = ' A ' & & Char < = ' Z ' )
{
2017-04-19 01:10:45 +00:00
Colour . Hue = ( ( ( float ) Char - ' A ' ) / ( ' Z ' - ' A ' ) * 360 ) ;
Colour . Saturation = ( ( ( float ) Char - ' A ' ) / ( ' Z ' - ' A ' ) * 26 + 74 ) ;
2017-03-18 02:04:13 +00:00
}
else if ( Char > = ' 0 ' & & Char < = ' 9 ' )
{
2017-04-19 01:10:45 +00:00
Colour . Hue = ( ( ( float ) Char - ' 0 ' ) / ( ' 9 ' - ' 0 ' ) * 360 ) ;
Colour . Saturation = ( ( ( float ) Char - ' 0 ' ) / ( ' 9 ' - ' 0 ' ) * 26 + 74 ) ;
2017-03-18 02:04:13 +00:00
}
else
{
2017-04-13 23:46:21 +00:00
Colour . Hue = 180 ;
Colour . Saturation = 50 ;
2017-03-18 02:04:13 +00:00
}
2017-06-25 18:22:54 +00:00
2017-04-13 23:46:21 +00:00
return Colour ;
2017-03-18 02:04:13 +00:00
}
2017-06-09 22:04:07 +00:00
hsl_colour *
StringToColourHash ( hsl_colour * Colour , char * String )
2017-03-18 02:04:13 +00:00
{
2017-06-09 22:04:07 +00:00
Colour - > Hue = 0 ;
Colour - > Saturation = 0 ;
2017-11-11 00:34:47 +00:00
Colour - > Lightness = 74 ;
2017-06-25 18:22:54 +00:00
2017-03-18 02:04:13 +00:00
int i ;
for ( i = 0 ; String [ i ] ; + + i )
{
2017-06-09 22:04:07 +00:00
Colour - > Hue + = CharToColour ( String [ i ] ) . Hue ;
Colour - > Saturation + = CharToColour ( String [ i ] ) . Saturation ;
2017-03-18 02:04:13 +00:00
}
2017-06-25 18:22:54 +00:00
2017-06-09 22:04:07 +00:00
Colour - > Hue = Colour - > Hue % 360 ;
Colour - > Saturation = Colour - > Saturation % 26 + 74 ;
return ( Colour ) ;
2017-03-18 02:04:13 +00:00
}
2017-03-25 02:07:55 +00:00
char *
SanitisePunctuation ( char * String )
{
char * Ptr = String ;
while ( * Ptr )
{
if ( * Ptr = = ' ' )
{
* Ptr = ' _ ' ;
}
if ( ( * Ptr < ' 0 ' | | * Ptr > ' 9 ' ) & &
2017-06-25 18:05:58 +00:00
( * Ptr < ' a ' | | * Ptr > ' z ' ) & &
( * Ptr < ' A ' | | * Ptr > ' Z ' ) )
2017-03-25 02:07:55 +00:00
{
* Ptr = ' - ' ;
}
+ + Ptr ;
}
return String ;
}
2017-12-07 21:07:36 +00:00
enum
{
INCLUDE_CSS ,
INCLUDE_Images ,
INCLUDE_JS ,
} include_types ;
enum
{
PAGE_PLAYER = 1 < < 0 ,
PAGE_INDEX = 1 < < 1
} pages ;
void
ConstructURLPrefix ( buffer * URLPrefix , int IncludeType , int PageType )
{
2017-12-12 23:24:10 +00:00
RewindBuffer ( URLPrefix ) ;
2017-12-07 21:07:36 +00:00
if ( StringsDiffer ( Config . RootURL , " " ) )
{
URLPrefix - > Ptr + = CopyString ( URLPrefix - > Ptr , " %s/ " , Config . RootURL ) ;
}
else
{
if ( Config . Edition = = EDITION_PROJECT )
{
if ( PageType = = PAGE_PLAYER )
{
URLPrefix - > Ptr + = CopyString ( URLPrefix - > Ptr , " ../ " ) ;
}
URLPrefix - > Ptr + = CopyString ( URLPrefix - > Ptr , " ../ " ) ;
}
}
switch ( IncludeType )
{
case INCLUDE_CSS :
if ( StringsDiffer ( Config . CSSDir , " " ) )
{
URLPrefix - > Ptr + = CopyString ( URLPrefix - > Ptr , " %s/ " , Config . CSSDir ) ;
}
break ;
case INCLUDE_Images :
if ( StringsDiffer ( Config . ImagesDir , " " ) )
{
URLPrefix - > Ptr + = CopyString ( URLPrefix - > Ptr , " %s/ " , Config . ImagesDir ) ;
}
break ;
case INCLUDE_JS :
if ( StringsDiffer ( Config . JSDir , " " ) )
{
URLPrefix - > Ptr + = CopyString ( URLPrefix - > Ptr , " %s/ " , Config . JSDir ) ;
}
break ;
}
}
2017-08-18 22:46:58 +00:00
enum
2017-05-25 20:28:52 +00:00
{
2017-08-19 21:41:51 +00:00
CreditsError_NoHost ,
CreditsError_NoAnnotator ,
CreditsError_NoCredentials
2017-11-11 00:34:47 +00:00
} credits_errors ;
2017-06-25 18:22:54 +00:00
2017-08-18 22:46:58 +00:00
int
2017-10-18 21:07:29 +00:00
SearchCredentials ( buffer * CreditsMenu , bool * HasCreditsMenu , char * Person , char * Role )
2017-08-18 22:46:58 +00:00
{
bool Found = FALSE ;
2017-05-25 20:28:52 +00:00
for ( int CredentialIndex = 0 ; CredentialIndex < ArrayCount ( Credentials ) ; + + CredentialIndex )
{
2017-08-18 22:46:58 +00:00
if ( ! StringsDiffer ( Person , Credentials [ CredentialIndex ] . Username ) )
2017-05-25 20:28:52 +00:00
{
2017-08-18 22:46:58 +00:00
Found = TRUE ;
if ( * HasCreditsMenu = = FALSE )
{
CopyStringToBuffer ( CreditsMenu ,
2017-11-11 00:34:47 +00:00
" <div class= \" menu credits \" > \n "
" <div class= \" mouse_catcher \" ></div> \n "
" <span>Credits</span> \n "
" <div class= \" credits_container \" > \n " ) ;
2017-08-18 22:46:58 +00:00
* HasCreditsMenu = TRUE ;
}
CopyStringToBuffer ( CreditsMenu ,
" <span class= \" credit \" > \n " ) ;
if ( * Credentials [ CredentialIndex ] . HomepageURL )
{
CopyStringToBuffer ( CreditsMenu ,
" <a class= \" person \" href= \" %s \" target= \" _blank \" > \n "
" <div class= \" role \" >%s</div> \n "
" <div class= \" name \" >%s</div> \n "
" </a> \n " ,
Credentials [ CredentialIndex ] . HomepageURL ,
Role ,
Credentials [ CredentialIndex ] . CreditedName ) ;
2017-05-25 20:28:52 +00:00
}
else
{
2017-08-18 22:46:58 +00:00
CopyStringToBuffer ( CreditsMenu ,
" <div class= \" person \" > \n "
" <div class= \" role \" >%s</div> \n "
" <div class= \" name \" >%s</div> \n "
" </div> \n " ,
Role ,
Credentials [ CredentialIndex ] . CreditedName ) ;
2017-05-25 20:28:52 +00:00
}
2017-06-25 18:22:54 +00:00
2017-08-18 22:46:58 +00:00
if ( * Credentials [ CredentialIndex ] . SupportIcon & & * Credentials [ CredentialIndex ] . SupportURL )
2017-05-25 20:28:52 +00:00
{
2017-12-07 21:07:36 +00:00
buffer URLPrefix ;
2017-12-12 23:24:10 +00:00
ClaimBuffer ( & URLPrefix , " URLPrefix " , 1024 ) ;
2017-12-18 00:15:03 +00:00
ConstructURLPrefix ( & URLPrefix , INCLUDE_Images , PAGE_PLAYER ) ;
2017-11-11 00:34:47 +00:00
CopyStringToBuffer ( CreditsMenu ,
" <a class= \" support \" href= \" %s \" target= \" _blank \" ><div class= \" support_icon \" style= \" background-image: url(%s%s); \" ></div></a> \n " ,
Credentials [ CredentialIndex ] . SupportURL ,
2017-12-07 21:07:36 +00:00
URLPrefix . Location ,
2017-11-11 00:34:47 +00:00
Credentials [ CredentialIndex ] . SupportIcon ) ;
2017-12-07 21:07:36 +00:00
DeclaimBuffer ( & URLPrefix ) ;
2017-05-25 20:28:52 +00:00
}
2017-06-25 18:22:54 +00:00
2017-08-18 22:46:58 +00:00
CopyStringToBuffer ( CreditsMenu ,
" </span> \n " ) ;
2017-05-25 20:28:52 +00:00
}
2017-08-18 22:46:58 +00:00
}
return Found ? 0 : CreditsError_NoCredentials ;
}
2017-06-25 18:22:54 +00:00
2017-08-18 22:46:58 +00:00
int
2017-10-18 21:07:29 +00:00
BuildCredits ( buffer * CreditsMenu , bool * HasCreditsMenu , HMML_VideoMetaData * Metadata )
2017-11-11 00:34:47 +00:00
// TODO(matt): Make this take the Credentials, once we are parsing them from a config
2017-08-18 22:46:58 +00:00
{
2017-10-18 21:07:29 +00:00
if ( Metadata - > member )
2017-08-18 22:46:58 +00:00
{
2017-10-18 21:07:29 +00:00
if ( SearchCredentials ( CreditsMenu , HasCreditsMenu , Metadata - > member , " Host " ) )
2017-05-25 20:28:52 +00:00
{
2017-08-18 22:46:58 +00:00
printf ( " No credentials for %s. Please contact matt@handmadedev.org with their: \n "
" Full name \n "
" Homepage URL (optional) \n "
2017-10-18 21:07:29 +00:00
" Financial support info, e.g. Patreon URL (optional) \n " , Metadata - > member ) ;
2017-08-18 22:46:58 +00:00
}
}
else
{
if ( * HasCreditsMenu = = TRUE )
{
CopyStringToBuffer ( CreditsMenu ,
" </div> \n "
" </div> \n " ) ;
}
return CreditsError_NoHost ;
}
2017-10-18 21:07:29 +00:00
if ( Metadata - > co_host_count > 0 )
2017-08-18 22:46:58 +00:00
{
2017-10-18 21:07:29 +00:00
for ( int i = 0 ; i < Metadata - > co_host_count ; + + i )
2017-08-18 22:46:58 +00:00
{
2017-10-18 21:07:29 +00:00
if ( SearchCredentials ( CreditsMenu , HasCreditsMenu , Metadata - > co_hosts [ i ] , " Co-host " ) )
2017-05-25 20:28:52 +00:00
{
2017-08-18 22:46:58 +00:00
printf ( " No credentials for %s. Please contact matt@handmadedev.org with their: \n "
" Full name \n "
" Homepage URL (optional) \n "
2017-10-18 21:07:29 +00:00
" Financial support info, e.g. Patreon URL (optional) \n " , Metadata - > co_hosts [ i ] ) ;
2017-05-25 20:28:52 +00:00
}
2017-08-18 22:46:58 +00:00
}
}
2017-06-25 18:22:54 +00:00
2017-10-18 21:07:29 +00:00
if ( Metadata - > guest_count > 0 )
2017-08-18 22:46:58 +00:00
{
2017-10-18 21:07:29 +00:00
for ( int i = 0 ; i < Metadata - > guest_count ; + + i )
2017-08-18 22:46:58 +00:00
{
2017-10-18 21:07:29 +00:00
if ( SearchCredentials ( CreditsMenu , HasCreditsMenu , Metadata - > guests [ i ] , " Guest " ) )
2017-05-25 20:28:52 +00:00
{
2017-08-18 22:46:58 +00:00
printf ( " No credentials for %s. Please contact matt@handmadedev.org with their: \n "
" Full name \n "
" Homepage URL (optional) \n "
2017-10-18 21:07:29 +00:00
" Financial support info, e.g. Patreon URL (optional) \n " , Metadata - > guests [ i ] ) ;
2017-05-25 20:28:52 +00:00
}
}
}
2017-06-25 18:22:54 +00:00
2017-10-18 21:07:29 +00:00
if ( Metadata - > annotator_count > 0 )
2017-05-25 20:28:52 +00:00
{
2017-10-18 21:07:29 +00:00
for ( int i = 0 ; i < Metadata - > annotator_count ; + + i )
2017-05-25 20:28:52 +00:00
{
2017-10-18 21:07:29 +00:00
if ( SearchCredentials ( CreditsMenu , HasCreditsMenu , Metadata - > annotators [ i ] , " Annotator " ) )
2017-08-18 22:46:58 +00:00
{
printf ( " No credentials for %s. Please contact matt@handmadedev.org with their: \n "
" Full name \n "
" Homepage URL (optional) \n "
2017-10-18 21:07:29 +00:00
" Financial support info, e.g. Patreon URL (optional) \n " , Metadata - > annotators [ i ] ) ;
2017-08-18 22:46:58 +00:00
}
2017-05-25 20:28:52 +00:00
}
2017-08-18 22:46:58 +00:00
}
else
{
if ( * HasCreditsMenu = = TRUE )
2017-05-25 20:28:52 +00:00
{
2017-08-18 22:46:58 +00:00
CopyStringToBuffer ( CreditsMenu ,
" </div> \n "
" </div> \n " ) ;
2017-05-25 20:28:52 +00:00
}
2017-08-18 22:46:58 +00:00
return CreditsError_NoAnnotator ;
2017-05-25 20:28:52 +00:00
}
2017-08-18 22:46:58 +00:00
if ( * HasCreditsMenu = = TRUE )
2017-05-25 20:28:52 +00:00
{
2017-08-18 22:46:58 +00:00
CopyStringToBuffer ( CreditsMenu ,
" </div> \n "
" </div> \n " ) ;
2017-05-25 20:28:52 +00:00
}
2017-06-25 18:22:54 +00:00
2017-05-25 20:28:52 +00:00
return 0 ;
}
2017-04-21 01:25:40 +00:00
int
2017-10-18 21:07:29 +00:00
BuildReference ( ref_info * ReferencesArray , int RefIdentifier , int UniqueRefs , HMML_Reference * Ref , HMML_Annotation * Anno )
2017-04-19 01:10:45 +00:00
{
2017-07-29 23:01:39 +00:00
# define REF_SITE (1 << 0)
# define REF_PAGE (1 << 1)
# define REF_URL (1 << 2)
# define REF_TITLE (1 << 3)
# define REF_ARTICLE (1 << 4)
# define REF_AUTHOR (1 << 5)
# define REF_EDITOR (1 << 6)
# define REF_PUBLISHER (1 << 7)
# define REF_ISBN (1 << 8)
int Mask = 0 ;
2017-10-18 21:07:29 +00:00
if ( Ref - > site ) { Mask | = REF_SITE ; }
if ( Ref - > page ) { Mask | = REF_PAGE ; }
if ( Ref - > url ) { Mask | = REF_URL ; }
if ( Ref - > title ) { Mask | = REF_TITLE ; }
if ( Ref - > article ) { Mask | = REF_ARTICLE ; }
if ( Ref - > author ) { Mask | = REF_AUTHOR ; }
if ( Ref - > editor ) { Mask | = REF_EDITOR ; }
if ( Ref - > publisher ) { Mask | = REF_PUBLISHER ; }
if ( Ref - > isbn ) { Mask | = REF_ISBN ; }
2017-07-29 23:01:39 +00:00
if ( ( REF_URL | REF_TITLE | REF_AUTHOR | REF_PUBLISHER | REF_ISBN ) = = Mask )
2017-04-21 01:25:40 +00:00
{
2017-10-18 21:07:29 +00:00
CopyString ( ReferencesArray [ UniqueRefs ] . ID , Ref - > isbn ) ;
CopyString ( ReferencesArray [ UniqueRefs ] . Source , " %s (%s) " , Ref - > author , Ref - > publisher ) ;
CopyStringNoFormat ( ReferencesArray [ UniqueRefs ] . RefTitle , Ref - > title ) ;
CopyStringNoFormat ( ReferencesArray [ UniqueRefs ] . URL , Ref - > url ) ;
2017-04-21 01:25:40 +00:00
}
2017-07-29 23:01:39 +00:00
else if ( ( REF_AUTHOR | REF_SITE | REF_PAGE | REF_URL ) = = Mask )
{
2017-10-18 21:07:29 +00:00
CopyStringNoFormat ( ReferencesArray [ UniqueRefs ] . ID , Ref - > url ) ;
CopyStringNoFormat ( ReferencesArray [ UniqueRefs ] . Source , Ref - > site ) ;
CopyString ( ReferencesArray [ UniqueRefs ] . RefTitle , " %s: \" %s \" " , Ref - > author , Ref - > page ) ;
CopyStringNoFormat ( ReferencesArray [ UniqueRefs ] . URL , Ref - > url ) ;
2017-07-29 23:01:39 +00:00
}
else if ( ( REF_PAGE | REF_URL | REF_TITLE ) = = Mask )
2017-04-21 01:25:40 +00:00
{
2017-10-18 21:07:29 +00:00
CopyStringNoFormat ( ReferencesArray [ UniqueRefs ] . ID , Ref - > url ) ;
CopyStringNoFormat ( ReferencesArray [ UniqueRefs ] . Source , Ref - > title ) ;
CopyStringNoFormat ( ReferencesArray [ UniqueRefs ] . RefTitle , Ref - > page ) ;
CopyStringNoFormat ( ReferencesArray [ UniqueRefs ] . URL , Ref - > url ) ;
2017-04-21 01:25:40 +00:00
}
2017-07-29 23:01:39 +00:00
else if ( ( REF_SITE | REF_PAGE | REF_URL ) = = Mask )
2017-04-21 01:25:40 +00:00
{
2017-10-18 21:07:29 +00:00
CopyStringNoFormat ( ReferencesArray [ UniqueRefs ] . ID , Ref - > url ) ;
CopyStringNoFormat ( ReferencesArray [ UniqueRefs ] . Source , Ref - > site ) ;
CopyStringNoFormat ( ReferencesArray [ UniqueRefs ] . RefTitle , Ref - > page ) ;
CopyStringNoFormat ( ReferencesArray [ UniqueRefs ] . URL , Ref - > url ) ;
2017-04-21 01:25:40 +00:00
}
2017-07-29 23:01:39 +00:00
else if ( ( REF_SITE | REF_URL | REF_TITLE ) = = Mask )
2017-04-21 01:25:40 +00:00
{
2017-10-18 21:07:29 +00:00
CopyStringNoFormat ( ReferencesArray [ UniqueRefs ] . ID , Ref - > url ) ;
CopyStringNoFormat ( ReferencesArray [ UniqueRefs ] . Source , Ref - > site ) ;
CopyStringNoFormat ( ReferencesArray [ UniqueRefs ] . RefTitle , Ref - > title ) ;
CopyStringNoFormat ( ReferencesArray [ UniqueRefs ] . URL , Ref - > url ) ;
2017-04-21 01:25:40 +00:00
}
2017-07-29 23:01:39 +00:00
else if ( ( REF_TITLE | REF_AUTHOR | REF_ISBN ) = = Mask )
2017-04-19 01:10:45 +00:00
{
2017-10-18 21:07:29 +00:00
CopyString ( ReferencesArray [ UniqueRefs ] . ID , Ref - > isbn ) ;
CopyString ( ReferencesArray [ UniqueRefs ] . Source , Ref - > author ) ;
CopyStringNoFormat ( ReferencesArray [ UniqueRefs ] . RefTitle , Ref - > title ) ;
CopyString ( ReferencesArray [ UniqueRefs ] . URL , " http://www.openisbn.com/isbn/%s " , Ref - > isbn ) ;
2017-04-19 01:10:45 +00:00
}
2017-07-29 23:01:39 +00:00
else if ( ( REF_URL | REF_ARTICLE | REF_AUTHOR ) = = Mask )
2017-04-19 01:10:45 +00:00
{
2017-10-18 21:07:29 +00:00
CopyStringNoFormat ( ReferencesArray [ UniqueRefs ] . ID , Ref - > url ) ;
CopyString ( ReferencesArray [ UniqueRefs ] . Source , Ref - > author ) ;
CopyStringNoFormat ( ReferencesArray [ UniqueRefs ] . RefTitle , Ref - > article ) ;
CopyStringNoFormat ( ReferencesArray [ UniqueRefs ] . URL , Ref - > url ) ;
2017-04-19 01:10:45 +00:00
}
2017-07-29 23:01:39 +00:00
else if ( ( REF_URL | REF_TITLE | REF_AUTHOR ) = = Mask )
2017-04-21 01:25:40 +00:00
{
2017-10-18 21:07:29 +00:00
CopyStringNoFormat ( ReferencesArray [ UniqueRefs ] . ID , Ref - > url ) ;
CopyString ( ReferencesArray [ UniqueRefs ] . Source , Ref - > author ) ;
CopyStringNoFormat ( ReferencesArray [ UniqueRefs ] . RefTitle , Ref - > title ) ;
CopyStringNoFormat ( ReferencesArray [ UniqueRefs ] . URL , Ref - > url ) ;
2017-04-21 01:25:40 +00:00
}
2017-07-29 23:01:39 +00:00
else if ( ( REF_URL | REF_TITLE ) = = Mask )
2017-04-23 00:30:37 +00:00
{
2017-10-18 21:07:29 +00:00
CopyStringNoFormat ( ReferencesArray [ UniqueRefs ] . ID , Ref - > url ) ;
CopyStringNoFormat ( ReferencesArray [ UniqueRefs ] . RefTitle , Ref - > title ) ;
CopyStringNoFormat ( ReferencesArray [ UniqueRefs ] . URL , Ref - > url ) ;
2017-04-23 00:30:37 +00:00
}
2017-07-29 23:01:39 +00:00
else if ( ( REF_SITE | REF_URL ) = = Mask )
2017-04-23 00:30:37 +00:00
{
2017-10-18 21:07:29 +00:00
CopyStringNoFormat ( ReferencesArray [ UniqueRefs ] . ID , Ref - > url ) ;
CopyStringNoFormat ( ReferencesArray [ UniqueRefs ] . RefTitle , Ref - > site ) ;
CopyStringNoFormat ( ReferencesArray [ UniqueRefs ] . URL , Ref - > url ) ;
2017-04-23 00:30:37 +00:00
}
2017-04-21 01:25:40 +00:00
else
{
return 1 ;
}
2017-06-25 18:22:54 +00:00
2017-10-18 21:07:29 +00:00
CopyString ( ReferencesArray [ UniqueRefs ] . Identifier [ ReferencesArray [ UniqueRefs ] . IdentifierCount ] . Timecode , Anno - > time ) ;
2017-04-19 01:10:45 +00:00
ReferencesArray [ UniqueRefs ] . Identifier [ ReferencesArray [ UniqueRefs ] . IdentifierCount ] . Identifier = RefIdentifier ;
2017-04-21 01:25:40 +00:00
return 0 ;
2017-04-19 01:10:45 +00:00
}
2017-05-21 06:35:16 +00:00
void
2017-10-18 21:07:29 +00:00
InsertCategory ( categories * GlobalTopics , categories * LocalTopics , categories * GlobalMedia , categories * LocalMedia , char * Marker )
2017-05-21 06:35:16 +00:00
{
2017-05-24 21:56:36 +00:00
bool IsMedium = FALSE ;
2017-06-25 18:22:54 +00:00
2017-08-19 21:41:51 +00:00
int CategoryMediumIndex ;
for ( CategoryMediumIndex = 0 ; CategoryMediumIndex < ArrayCount ( CategoryMedium ) ; + + CategoryMediumIndex )
2017-05-21 06:35:16 +00:00
{
2017-08-19 21:41:51 +00:00
if ( ! StringsDiffer ( CategoryMedium [ CategoryMediumIndex ] . Medium , Marker ) )
2017-05-21 06:35:16 +00:00
{
2017-05-24 21:56:36 +00:00
IsMedium = TRUE ;
break ;
2017-05-21 06:35:16 +00:00
}
2017-05-24 21:56:36 +00:00
}
2017-06-25 18:22:54 +00:00
2017-08-19 21:41:51 +00:00
int CategoryIndex ;
2017-05-24 21:56:36 +00:00
if ( IsMedium )
{
2017-10-18 21:07:29 +00:00
for ( CategoryIndex = 0 ; CategoryIndex < LocalMedia - > Count ; + + CategoryIndex )
2017-05-22 21:37:00 +00:00
{
2017-10-18 21:07:29 +00:00
if ( ! StringsDiffer ( CategoryMedium [ CategoryMediumIndex ] . Medium , LocalMedia - > Category [ CategoryIndex ] . Marker ) )
2017-05-22 21:37:00 +00:00
{
2017-05-24 21:56:36 +00:00
return ;
2017-05-22 21:37:00 +00:00
}
2017-10-18 21:07:29 +00:00
if ( ( StringsDiffer ( CategoryMedium [ CategoryMediumIndex ] . WrittenName , LocalMedia - > Category [ CategoryIndex ] . WrittenText ) ) < 0 )
2017-05-24 21:56:36 +00:00
{
2017-08-19 21:41:51 +00:00
int CategoryCount ;
2017-10-18 21:07:29 +00:00
for ( CategoryCount = LocalMedia - > Count ; CategoryCount > CategoryIndex ; - - CategoryCount )
2017-05-24 21:56:36 +00:00
{
2017-10-18 21:07:29 +00:00
CopyString ( LocalMedia - > Category [ CategoryCount ] . Marker , LocalMedia - > Category [ CategoryCount - 1 ] . Marker ) ;
CopyString ( LocalMedia - > Category [ CategoryCount ] . WrittenText , LocalMedia - > Category [ CategoryCount - 1 ] . WrittenText ) ;
2017-05-24 21:56:36 +00:00
}
2017-06-25 18:22:54 +00:00
2017-10-18 21:07:29 +00:00
CopyString ( LocalMedia - > Category [ CategoryCount ] . Marker , CategoryMedium [ CategoryMediumIndex ] . Medium ) ;
CopyString ( LocalMedia - > Category [ CategoryCount ] . WrittenText , CategoryMedium [ CategoryMediumIndex ] . WrittenName ) ;
2017-05-24 21:56:36 +00:00
break ;
}
2017-05-22 21:37:00 +00:00
}
2017-06-25 18:22:54 +00:00
2017-10-18 21:07:29 +00:00
if ( CategoryIndex = = LocalMedia - > Count )
2017-05-24 21:56:36 +00:00
{
2017-10-18 21:07:29 +00:00
CopyString ( LocalMedia - > Category [ CategoryIndex ] . Marker , CategoryMedium [ CategoryMediumIndex ] . Medium ) ;
CopyString ( LocalMedia - > Category [ CategoryIndex ] . WrittenText , CategoryMedium [ CategoryMediumIndex ] . WrittenName ) ;
2017-05-24 21:56:36 +00:00
}
2017-06-25 18:22:54 +00:00
2017-10-18 21:07:29 +00:00
+ + LocalMedia - > Count ;
for ( CategoryIndex = 0 ; CategoryIndex < GlobalMedia - > Count ; + + CategoryIndex )
{
if ( ! StringsDiffer ( CategoryMedium [ CategoryMediumIndex ] . Medium , GlobalMedia - > Category [ CategoryIndex ] . Marker ) )
{
return ;
}
if ( ( StringsDiffer ( CategoryMedium [ CategoryMediumIndex ] . WrittenName , GlobalMedia - > Category [ CategoryIndex ] . WrittenText ) ) < 0 )
{
int CategoryCount ;
for ( CategoryCount = GlobalMedia - > Count ; CategoryCount > CategoryIndex ; - - CategoryCount )
{
CopyString ( GlobalMedia - > Category [ CategoryCount ] . Marker , GlobalMedia - > Category [ CategoryCount - 1 ] . Marker ) ;
CopyString ( GlobalMedia - > Category [ CategoryCount ] . WrittenText , GlobalMedia - > Category [ CategoryCount - 1 ] . WrittenText ) ;
}
CopyString ( GlobalMedia - > Category [ CategoryCount ] . Marker , CategoryMedium [ CategoryMediumIndex ] . Medium ) ;
CopyString ( GlobalMedia - > Category [ CategoryCount ] . WrittenText , CategoryMedium [ CategoryMediumIndex ] . WrittenName ) ;
break ;
}
}
if ( CategoryIndex = = GlobalMedia - > Count )
{
CopyString ( GlobalMedia - > Category [ CategoryIndex ] . Marker , CategoryMedium [ CategoryMediumIndex ] . Medium ) ;
CopyString ( GlobalMedia - > Category [ CategoryIndex ] . WrittenText , CategoryMedium [ CategoryMediumIndex ] . WrittenName ) ;
}
+ + GlobalMedia - > Count ;
2017-05-21 06:35:16 +00:00
}
2017-05-24 21:56:36 +00:00
else
{
2017-10-18 21:07:29 +00:00
for ( CategoryIndex = 0 ; CategoryIndex < LocalTopics - > Count ; + + CategoryIndex )
2017-05-24 21:56:36 +00:00
{
2017-10-18 21:07:29 +00:00
if ( ! StringsDiffer ( Marker , LocalTopics - > Category [ CategoryIndex ] . Marker ) )
2017-05-24 21:56:36 +00:00
{
return ;
}
2017-10-18 21:07:29 +00:00
if ( ( StringsDiffer ( Marker , LocalTopics - > Category [ CategoryIndex ] . Marker ) ) < 0 )
2017-05-24 21:56:36 +00:00
{
2017-08-19 21:41:51 +00:00
int CategoryCount ;
2017-10-18 21:07:29 +00:00
for ( CategoryCount = LocalTopics - > Count ; CategoryCount > CategoryIndex ; - - CategoryCount )
2017-05-24 21:56:36 +00:00
{
2017-10-18 21:07:29 +00:00
CopyString ( LocalTopics - > Category [ CategoryCount ] . Marker , LocalTopics - > Category [ CategoryCount - 1 ] . Marker ) ;
2017-05-24 21:56:36 +00:00
}
2017-06-25 18:22:54 +00:00
2017-10-18 21:07:29 +00:00
CopyString ( LocalTopics - > Category [ CategoryCount ] . Marker , Marker ) ;
2017-05-24 21:56:36 +00:00
break ;
}
}
2017-06-25 18:22:54 +00:00
2017-10-18 21:07:29 +00:00
if ( CategoryIndex = = LocalTopics - > Count )
2017-05-21 06:35:16 +00:00
{
2017-10-18 21:07:29 +00:00
CopyString ( LocalTopics - > Category [ CategoryIndex ] . Marker , Marker ) ;
2017-05-21 06:35:16 +00:00
}
2017-06-25 18:22:54 +00:00
2017-10-18 21:07:29 +00:00
+ + LocalTopics - > Count ;
for ( CategoryIndex = 0 ; CategoryIndex < GlobalTopics - > Count ; + + CategoryIndex )
{
if ( ! StringsDiffer ( Marker , GlobalTopics - > Category [ CategoryIndex ] . Marker ) )
{
return ;
}
if ( ( StringsDiffer ( Marker , GlobalTopics - > Category [ CategoryIndex ] . Marker ) ) < 0 )
{
int CategoryCount ;
for ( CategoryCount = GlobalTopics - > Count ; CategoryCount > CategoryIndex ; - - CategoryCount )
{
CopyString ( GlobalTopics - > Category [ CategoryCount ] . Marker , GlobalTopics - > Category [ CategoryCount - 1 ] . Marker ) ;
}
CopyString ( GlobalTopics - > Category [ CategoryCount ] . Marker , Marker ) ;
break ;
}
}
if ( CategoryIndex = = GlobalTopics - > Count )
{
CopyString ( GlobalTopics - > Category [ CategoryIndex ] . Marker , Marker ) ;
}
+ + GlobalTopics - > Count ;
2017-05-21 06:35:16 +00:00
}
2017-08-19 21:41:51 +00:00
return ;
2017-05-21 06:35:16 +00:00
}
2017-03-25 03:10:15 +00:00
void
2017-10-18 21:07:29 +00:00
BuildCategories ( buffer * AnnotationClass , buffer * TopicDots , categories * LocalTopics , categories * LocalMedia , int * MarkerIndex )
2017-03-25 03:10:15 +00:00
{
2017-10-18 21:07:29 +00:00
if ( LocalTopics - > Count > 0 )
2017-03-25 03:10:15 +00:00
{
2017-08-19 21:41:51 +00:00
CopyStringToBuffer ( TopicDots , " <span class= \" categories \" > " ) ;
2017-10-18 21:07:29 +00:00
for ( int i = 0 ; i < LocalTopics - > Count ; + + i )
2017-03-25 03:10:15 +00:00
{
2017-08-19 21:41:51 +00:00
CopyStringToBuffer ( TopicDots , " <div title= \" %s \" class= \" category %s \" ></div> " ,
2017-10-18 21:07:29 +00:00
SanitisePunctuation ( LocalTopics - > Category [ i ] . Marker ) ,
SanitisePunctuation ( LocalTopics - > Category [ i ] . Marker ) ) ;
2017-06-25 18:22:54 +00:00
2017-08-19 21:41:51 +00:00
CopyStringToBuffer ( AnnotationClass , " cat_%s " ,
2017-10-18 21:07:29 +00:00
SanitisePunctuation ( LocalTopics - > Category [ i ] . Marker ) ) ;
2017-06-11 22:49:04 +00:00
}
2017-08-19 21:41:51 +00:00
CopyStringToBuffer ( TopicDots , " </span> " ) ;
2017-06-11 22:49:04 +00:00
}
2017-06-25 18:22:54 +00:00
2017-10-18 21:07:29 +00:00
for ( int i = 0 ; i < LocalMedia - > Count ; + + i )
2017-06-11 22:49:04 +00:00
{
2017-11-11 00:34:47 +00:00
if ( ! StringsDiffer ( LocalMedia - > Category [ i ] . Marker , " afk " ) ) // TODO(matt): Initially hidden config
{
CopyStringToBuffer ( AnnotationClass , " off_%s skip " , SanitisePunctuation ( LocalMedia - > Category [ i ] . Marker ) ) ; // TODO(matt): Bulletproof this?
}
else
{
2017-10-18 21:07:29 +00:00
CopyStringToBuffer ( AnnotationClass , " %s " , SanitisePunctuation ( LocalMedia - > Category [ i ] . Marker ) ) ;
2017-11-11 00:34:47 +00:00
}
2017-06-11 22:49:04 +00:00
}
2017-06-25 18:22:54 +00:00
2017-08-19 21:41:51 +00:00
CopyStringToBuffer ( AnnotationClass , " \" " ) ;
2017-03-25 03:10:15 +00:00
}
2017-04-23 02:47:42 +00:00
int
StringToInt ( char * String )
{
int Result = 0 ;
while ( * String )
{
Result = Result * 10 + ( * String - ' 0 ' ) ;
+ + String ;
}
return Result ;
}
2017-08-18 22:46:58 +00:00
size_t
CurlIntoBuffer ( char * InPtr , size_t CharLength , size_t Chars , char * * OutputPtr )
{
int Length = CharLength * Chars ;
int i ;
for ( i = 0 ; InPtr [ i ] & & i < Length ; + + i )
{
* ( ( * OutputPtr ) + + ) = InPtr [ i ] ;
}
* * OutputPtr = ' \0 ' ;
return Length ;
} ;
2017-08-11 22:41:52 +00:00
void
CurlQuotes ( buffer * QuoteStaging , char * QuotesURL )
{
CURL * curl = curl_easy_init ( ) ;
if ( curl ) {
CURLcode res ;
curl_easy_setopt ( curl , CURLOPT_WRITEDATA , & QuoteStaging - > Ptr ) ;
curl_easy_setopt ( curl , CURLOPT_WRITEFUNCTION , CurlIntoBuffer ) ;
curl_easy_setopt ( curl , CURLOPT_URL , QuotesURL ) ;
if ( ( res = curl_easy_perform ( curl ) ) )
{
fprintf ( stderr , " %s " , curl_easy_strerror ( res ) ) ;
}
curl_easy_cleanup ( curl ) ;
}
}
2017-08-18 22:46:58 +00:00
int
2017-10-18 21:07:29 +00:00
SearchQuotes ( buffer * QuoteStaging , int CacheSize , quote_info * Info , int ID )
2017-08-18 22:46:58 +00:00
{
2017-10-18 21:07:29 +00:00
QuoteStaging - > Ptr = QuoteStaging - > Location ;
while ( QuoteStaging - > Ptr - QuoteStaging - > Location < CacheSize )
2017-08-18 22:46:58 +00:00
{
char InID [ 4 ] = { 0 } ;
char InTime [ 16 ] = { 0 } ;
char * OutPtr = InID ;
2017-10-18 21:07:29 +00:00
QuoteStaging - > Ptr + = CopyStringNoFormatT ( OutPtr , QuoteStaging - > Ptr , ' , ' ) ;
2017-08-18 22:46:58 +00:00
if ( StringToInt ( InID ) = = ID )
{
2017-10-18 21:07:29 +00:00
QuoteStaging - > Ptr + = 1 ;
2017-08-18 22:46:58 +00:00
OutPtr = InTime ;
2017-10-18 21:07:29 +00:00
QuoteStaging - > Ptr + = CopyStringNoFormatT ( OutPtr , QuoteStaging - > Ptr , ' , ' ) ;
2017-08-18 22:46:58 +00:00
long int Time = StringToInt ( InTime ) ;
char DayString [ 3 ] ;
strftime ( DayString , 3 , " %d " , gmtime ( & Time ) ) ;
int Day = StringToInt ( DayString ) ;
char DaySuffix [ 3 ] ; if ( DayString [ 1 ] = = ' 1 ' & & Day ! = 11 ) { CopyString ( DaySuffix , " st " ) ; }
else if ( DayString [ 1 ] = = ' 2 ' & & Day ! = 12 ) { CopyString ( DaySuffix , " nd " ) ; }
else if ( DayString [ 1 ] = = ' 3 ' & & Day ! = 13 ) { CopyString ( DaySuffix , " rd " ) ; }
else { CopyString ( DaySuffix , " th " ) ; }
char MonthYear [ 32 ] ;
strftime ( MonthYear , 32 , " %B, %Y " , gmtime ( & Time ) ) ;
CopyString ( Info - > Date , " %d%s %s " , Day , DaySuffix , MonthYear ) ;
2017-10-18 21:07:29 +00:00
QuoteStaging - > Ptr + = 1 ;
2017-08-18 22:46:58 +00:00
OutPtr = Info - > Text ;
2017-10-18 21:07:29 +00:00
QuoteStaging - > Ptr + = CopyStringNoFormatT ( OutPtr , QuoteStaging - > Ptr , ' \n ' ) ;
2017-08-18 22:46:58 +00:00
2017-10-18 21:07:29 +00:00
FreeBuffer ( QuoteStaging ) ;
2017-09-07 21:41:08 +00:00
return RC_FOUND ;
2017-08-18 22:46:58 +00:00
}
else
{
2017-10-18 21:07:29 +00:00
while ( * QuoteStaging - > Ptr ! = ' \n ' )
2017-08-18 22:46:58 +00:00
{
2017-10-18 21:07:29 +00:00
+ + QuoteStaging - > Ptr ;
2017-08-18 22:46:58 +00:00
}
2017-10-18 21:07:29 +00:00
+ + QuoteStaging - > Ptr ;
2017-08-18 22:46:58 +00:00
}
}
2017-09-07 21:41:08 +00:00
return RC_UNFOUND ;
2017-08-18 22:46:58 +00:00
}
2017-08-10 01:05:41 +00:00
int
2017-10-18 21:07:29 +00:00
BuildQuote ( quote_info * Info , char * Speaker , int ID )
2017-08-10 01:05:41 +00:00
{
// TODO(matt): Rebuild cache option
2017-11-11 00:34:47 +00:00
char QuoteCacheDir [ 256 ] ;
2017-10-18 21:07:29 +00:00
CopyString ( QuoteCacheDir , " %s/quotes " , Config . CacheDir ) ;
2017-11-11 00:34:47 +00:00
char QuoteCachePath [ 256 ] ;
2017-08-10 01:05:41 +00:00
CopyString ( QuoteCachePath , " %s/%s " , QuoteCacheDir , Speaker ) ;
FILE * QuoteCache ;
2017-08-11 22:41:52 +00:00
char QuotesURL [ 256 ] ;
2017-08-29 20:35:28 +00:00
// TODO(matt): Make the URL configurable
2017-08-11 22:41:52 +00:00
CopyString ( QuotesURL , " https://dev.abaines.me.uk/quotes/%s.raw " , Speaker ) ;
bool CacheAvailable = FALSE ;
2017-08-10 01:05:41 +00:00
2017-08-11 22:41:52 +00:00
if ( ! ( QuoteCache = fopen ( QuoteCachePath , " a+ " ) ) )
2017-08-10 01:05:41 +00:00
{
2017-09-07 21:41:08 +00:00
if ( MakeDir ( QuoteCacheDir ) = = RC_SUCCESS )
2017-08-10 01:05:41 +00:00
{
2017-08-11 22:41:52 +00:00
CacheAvailable = TRUE ;
} ;
if ( ! ( QuoteCache = fopen ( QuoteCachePath , " a+ " ) ) )
{
2017-09-07 21:41:08 +00:00
fprintf ( stderr , " Unable to open quote cache %s: %s \n " , QuoteCachePath , strerror ( errno ) ) ;
2017-08-11 22:41:52 +00:00
}
else
{
CacheAvailable = TRUE ;
2017-08-10 01:05:41 +00:00
}
}
2017-08-11 22:41:52 +00:00
else
{
CacheAvailable = TRUE ;
}
2017-08-10 01:05:41 +00:00
buffer QuoteStaging ;
QuoteStaging . ID = " QuoteStaging " ;
QuoteStaging . Size = Kilobytes ( 256 ) ;
if ( ! ( QuoteStaging . Location = malloc ( QuoteStaging . Size ) ) )
{
2017-09-07 21:41:08 +00:00
fclose ( QuoteCache ) ;
return RC_ERROR_MEMORY ;
2017-08-10 01:05:41 +00:00
}
2017-10-18 21:07:29 +00:00
# if DEBUG_MEM
FILE * MemLog = fopen ( " /home/matt/cinera_mem " , " a+ " ) ;
fprintf ( MemLog , " Allocated QuoteStaging (%d) \n " , QuoteStaging . Size ) ;
fclose ( MemLog ) ;
printf ( " Allocated QuoteStaging (%d) \n " , QuoteStaging . Size ) ;
# endif
2017-08-10 01:05:41 +00:00
QuoteStaging . Ptr = QuoteStaging . Location ;
2017-08-11 22:41:52 +00:00
if ( CacheAvailable )
2017-08-10 01:05:41 +00:00
{
2017-08-11 22:41:52 +00:00
fseek ( QuoteCache , 0 , SEEK_END ) ;
int FileSize = ftell ( QuoteCache ) ;
fseek ( QuoteCache , 0 , SEEK_SET ) ;
fread ( QuoteStaging . Location , FileSize , 1 , QuoteCache ) ;
fclose ( QuoteCache ) ;
2017-08-10 01:05:41 +00:00
2017-10-18 21:07:29 +00:00
if ( SearchQuotes ( & QuoteStaging , FileSize , Info , ID ) = = RC_UNFOUND )
2017-08-11 22:41:52 +00:00
{
CurlQuotes ( & QuoteStaging , QuotesURL ) ;
if ( ! ( QuoteCache = fopen ( QuoteCachePath , " w " ) ) )
2017-08-10 01:05:41 +00:00
{
2017-08-11 22:41:52 +00:00
perror ( QuoteCachePath ) ;
2017-08-10 01:05:41 +00:00
}
2017-08-11 22:41:52 +00:00
fwrite ( QuoteStaging . Location , QuoteStaging . Ptr - QuoteStaging . Location , 1 , QuoteCache ) ;
fclose ( QuoteCache ) ;
2017-08-10 01:05:41 +00:00
2017-08-11 22:41:52 +00:00
int CacheSize = QuoteStaging . Ptr - QuoteStaging . Location ;
QuoteStaging . Ptr = QuoteStaging . Location ;
2017-10-18 21:07:29 +00:00
if ( SearchQuotes ( & QuoteStaging , CacheSize , Info , ID ) = = 1 )
2017-08-11 22:41:52 +00:00
{
FreeBuffer ( & QuoteStaging ) ;
return 1 ;
}
2017-08-10 01:05:41 +00:00
}
2017-08-11 22:41:52 +00:00
}
else
{
CurlQuotes ( & QuoteStaging , QuotesURL ) ;
2017-08-10 01:05:41 +00:00
int CacheSize = QuoteStaging . Ptr - QuoteStaging . Location ;
QuoteStaging . Ptr = QuoteStaging . Location ;
2017-10-18 21:07:29 +00:00
if ( SearchQuotes ( & QuoteStaging , CacheSize , Info , ID ) = = 1 )
2017-08-10 01:05:41 +00:00
{
FreeBuffer ( & QuoteStaging ) ;
return 1 ;
}
}
2017-08-11 22:41:52 +00:00
2017-08-10 01:05:41 +00:00
return 0 ;
}
2017-09-07 21:41:08 +00:00
int
2017-10-18 21:07:29 +00:00
GenerateTopicColours ( char * Topic )
2017-03-31 00:56:50 +00:00
{
for ( int i = 0 ; i < ArrayCount ( CategoryMedium ) ; + + i )
{
2017-08-18 22:46:58 +00:00
if ( ! StringsDiffer ( Topic , CategoryMedium [ i ] . Medium ) )
2017-03-31 00:56:50 +00:00
{
2017-09-07 21:41:08 +00:00
return RC_NOOP ;
2017-03-31 00:56:50 +00:00
}
}
2017-06-25 18:22:54 +00:00
2017-10-18 21:07:29 +00:00
file_buffer Topics ;
Topics . Buffer . ID = " Topics " ;
2017-11-11 00:34:47 +00:00
if ( StringsDiffer ( Config . CSSDir , " " ) )
{
CopyString ( Topics . Path , " %s/%s/cinera_topics.css " , Config . RootDir , Config . CSSDir ) ;
}
else
{
CopyString ( Topics . Path , " %s/cinera_topics.css " , Config . RootDir ) ;
}
2017-10-18 21:07:29 +00:00
if ( ( Topics . Handle = fopen ( Topics . Path , " a+ " ) ) )
2017-03-31 00:56:50 +00:00
{
2017-10-18 21:07:29 +00:00
fseek ( Topics . Handle , 0 , SEEK_END ) ;
Topics . FileSize = ftell ( Topics . Handle ) ;
Topics . Buffer . Size = Topics . FileSize ;
fseek ( Topics . Handle , 0 , SEEK_SET ) ;
2017-06-25 18:22:54 +00:00
2017-10-18 21:07:29 +00:00
if ( ! ( Topics . Buffer . Location = malloc ( Topics . Buffer . Size ) ) )
2017-03-31 00:56:50 +00:00
{
2017-09-07 21:41:08 +00:00
return RC_ERROR_MEMORY ;
2017-03-31 00:56:50 +00:00
}
2017-06-25 18:22:54 +00:00
2017-10-18 21:07:29 +00:00
# if DEBUG_MEM
2017-11-11 00:34:47 +00:00
FILE * MemLog = fopen ( " /home/matt/cinera_mem " , " a+ " ) ;
fprintf ( MemLog , " Allocated Topics (%d) \n " , Topics . Size ) ;
fclose ( MemLog ) ;
printf ( " Allocated Topics (%d) \n " , Topics . Size ) ;
2017-10-18 21:07:29 +00:00
# endif
2017-06-25 18:22:54 +00:00
2017-10-18 21:07:29 +00:00
Topics . Buffer . Ptr = Topics . Buffer . Location ;
fread ( Topics . Buffer . Location , Topics . Buffer . Size , 1 , Topics . Handle ) ;
2017-06-25 18:22:54 +00:00
2017-10-18 21:07:29 +00:00
while ( Topics . Buffer . Ptr - Topics . Buffer . Location < Topics . Buffer . Size )
2017-03-31 00:56:50 +00:00
{
2017-10-18 21:07:29 +00:00
Topics . Buffer . Ptr + = StringLength ( " .category. " ) ;
if ( ! StringsDifferT ( SanitisePunctuation ( Topic ) , Topics . Buffer . Ptr , ' ' ) )
2017-03-31 00:56:50 +00:00
{
2017-10-18 21:07:29 +00:00
FreeBuffer ( & Topics . Buffer ) ;
fclose ( Topics . Handle ) ;
2017-09-07 21:41:08 +00:00
return RC_NOOP ;
2017-03-31 00:56:50 +00:00
}
2017-10-18 21:07:29 +00:00
while ( Topics . Buffer . Ptr - Topics . Buffer . Location < Topics . Buffer . Size & & * Topics . Buffer . Ptr ! = ' \n ' )
2017-03-31 00:56:50 +00:00
{
2017-10-18 21:07:29 +00:00
+ + Topics . Buffer . Ptr ;
2017-03-31 00:56:50 +00:00
}
2017-10-18 21:07:29 +00:00
+ + Topics . Buffer . Ptr ;
2017-03-31 00:56:50 +00:00
}
2017-06-25 18:22:54 +00:00
2017-06-09 22:04:07 +00:00
hsl_colour Colour ;
StringToColourHash ( & Colour , Topic ) ;
2017-10-18 21:07:29 +00:00
fprintf ( Topics . Handle , " .category.%s { border: 1px solid hsl(%d, %d%%, %d%%); background: hsl(%d, %d%%, %d%%); } \n " ,
2017-06-25 18:05:58 +00:00
SanitisePunctuation ( Topic ) , Colour . Hue , Colour . Saturation , Colour . Lightness , Colour . Hue , Colour . Saturation , Colour . Lightness ) ;
2017-06-25 18:22:54 +00:00
2017-10-18 21:07:29 +00:00
fclose ( Topics . Handle ) ;
FreeBuffer ( & Topics . Buffer ) ;
2017-09-07 21:41:08 +00:00
return RC_SUCCESS ;
2017-03-31 00:56:50 +00:00
}
else
{
2017-09-07 21:41:08 +00:00
return RC_ERROR_FILE ;
2017-03-31 00:56:50 +00:00
}
}
2017-08-29 20:35:28 +00:00
void
2017-10-18 21:07:29 +00:00
PrintUsage ( char * BinaryLocation , config * DefaultConfig )
2017-08-29 20:35:28 +00:00
{
fprintf ( stderr , " Usage: %s [option(s)] filename(s) \n "
" \n "
" Options: \n "
2017-10-18 21:07:29 +00:00
" Paths: \n "
" -r <root directory> \n "
" Override default root directory ( \" %s \" ) \n "
2017-12-12 23:24:10 +00:00
" -R <root URL> \n "
2017-11-11 00:34:47 +00:00
" Override default root URL ( \" %s \" ) \n "
2017-10-18 21:07:29 +00:00
" -c <CSS directory path> \n "
" Override default CSS directory ( \" %s \" ), relative to root \n "
" -i <images directory path> \n "
" Override default images directory ( \" %s \" ), relative to root \n "
" -j <JS directory path> \n "
" Override default JS directory ( \" %s \" ), relative to root \n "
2017-12-12 23:24:10 +00:00
" \n "
" -b <base output directory> \n "
" Override project's default base output directory ( \" %s \" ) \n "
" -B <base URL> \n "
" Override default base URL ( \" %s \" ) \n "
" NOTE: This must be set, if -n or -a are used \n "
" -n <index location> \n "
" Override default index location ( \" %s \" ), relative to base \n "
" -a <player location> \n "
" Override default player location ( \" %s \" ), relative to base \n "
" \n "
2017-10-18 21:07:29 +00:00
" -t <player template location> \n "
2017-12-12 23:24:10 +00:00
" Override default player template location ( \" %s \" ), either \n "
" absolute or relative to root, and enable integration \n "
2017-10-18 21:07:29 +00:00
" -x <index template location> \n "
2017-12-12 23:24:10 +00:00
" Override default index template location ( \" %s \" ), either \n "
" absolute or relative to root, and enable integration \n "
2017-10-18 21:07:29 +00:00
" \n "
2017-11-11 00:34:47 +00:00
" -d <project directory> \n "
2017-10-18 21:07:29 +00:00
" Override default project directory ( \" %s \" ) \n "
2017-12-12 23:24:10 +00:00
" -o <output location> \n "
" Override default output player location for SINGLE_EDITION ( \" %s \" ) \n "
2017-10-18 21:07:29 +00:00
" \n "
2017-11-11 00:34:47 +00:00
" -p <project ID> \n "
2017-10-18 21:07:29 +00:00
" Set the project ID, corresponding to the \" project \" field in the HMML files \n "
2017-12-18 14:31:05 +00:00
" -q \n "
" Quit after syncing with annotation files in project input directory \n "
" (Only affects PROJECT edition) \n "
2017-12-07 01:15:13 +00:00
" -s <style> \n "
" Set the style / theme, corresponding to a cinera__*.css file \n "
" This is equal to the \" project \" field in the HMML files by default \n "
2017-08-29 20:35:28 +00:00
" -m <default medium> \n "
" Override default default medium ( \" %s \" ) \n "
2017-12-12 23:24:10 +00:00
" -f \n "
" Force integration with an incomplete template \n "
" \n "
" -l <n> \n "
" Override default log level (%d), where n is from 0 (terse) to 7 (verbose) \n "
2017-11-11 00:34:47 +00:00
" -U <seconds> \n "
2017-10-18 21:07:29 +00:00
" Override default update interval ( \" %d \" ) \n "
2017-08-29 20:35:28 +00:00
//" -c config location\n"
2017-11-18 00:27:33 +00:00
" -v \n "
" display version and exit \n "
2017-08-29 20:35:28 +00:00
" -h \n "
" display this help \n "
" \n "
" Environment Variables: \n "
" CINERA_MODE \n "
" =INTEGRATE \n "
" Enable integration \n "
" \n "
" Template: \n "
2017-12-07 01:15:13 +00:00
" A complete index template shall contain exactly one each of the following tags: \n "
2017-12-12 23:24:10 +00:00
" <!-- __CINERA_INCLUDES__ --> \n "
" to put inside your own <head></head> \n "
2017-12-07 01:15:13 +00:00
" <!-- __CINERA_INDEX__ --> \n "
" \n "
" A complete player template shall contain exactly one each of the following tags: \n "
2017-12-12 23:24:10 +00:00
" <!-- __CINERA_INCLUDES__ --> \n "
" to put inside your own <head></head> \n "
2017-08-29 20:35:28 +00:00
" <!-- __CINERA_MENUS__ --> \n "
" <!-- __CINERA_PLAYER__ --> \n "
2017-12-12 23:24:10 +00:00
" <!-- __CINERA_SCRIPT__ --> \n "
" must come after <!-- __CINERA_MENUS__ --> and <!-- __CINERA_PLAYER__ --> \n "
2017-12-07 01:15:13 +00:00
" \n "
" Other available tags: \n "
" <!-- __CINERA_PROJECT__ --> \n "
2017-08-29 20:35:28 +00:00
" <!-- __CINERA_TITLE__ --> \n "
2017-12-12 23:24:10 +00:00
" <!-- __CINERA_URL__ --> \n "
" Only really usable if Base URL is set (-B) \n "
" <!-- __CINERA_VIDEO_ID__ --> \n "
" Intended only for your player template \n "
2017-08-29 20:35:28 +00:00
" \n "
" HMML Specification: \n "
" https://git.handmade.network/Annotation-Pushers/Annotation-System/wikis/hmmlspec \n " ,
2017-12-12 23:24:10 +00:00
BinaryLocation , DefaultConfig - > RootDir , DefaultConfig - > RootURL , DefaultConfig - > CSSDir , DefaultConfig - > ImagesDir , DefaultConfig - > JSDir , DefaultConfig - > BaseDir , DefaultConfig - > BaseURL , DefaultConfig - > IndexLocation , DefaultConfig - > PlayerLocation , DefaultConfig - > TemplatePlayerLocation , DefaultConfig - > TemplateIndexLocation , DefaultConfig - > ProjectDir , DefaultConfig - > OutLocation , DefaultConfig - > DefaultMedium , DefaultConfig - > LogLevel , DefaultConfig - > UpdateInterval ) ;
2017-08-29 20:35:28 +00:00
}
2017-09-07 21:41:08 +00:00
void
DepartComment ( buffer * Template )
2017-05-13 15:38:10 +00:00
{
2017-09-07 21:41:08 +00:00
while ( Template - > Ptr - Template - > Location < Template - > Size )
{
if ( ! StringsDifferT ( " --> " , Template - > Ptr , 0 ) )
{
Template - > Ptr + = StringLength ( " --> " ) ;
break ;
}
+ + Template - > Ptr ;
}
}
2017-05-13 15:38:10 +00:00
2017-08-29 20:35:28 +00:00
int
2017-10-18 21:07:29 +00:00
ValidateTemplate ( buffer * Errors , template * * Template , int PageType )
2017-08-29 20:35:28 +00:00
{
2017-10-18 21:07:29 +00:00
if ( ClaimTemplate ( Template ,
PageType = = PAGE_INDEX ? Config . TemplateIndexLocation : Config . TemplatePlayerLocation ) = = RC_ARENA_FULL ) { return RC_ARENA_FULL ; } ;
2017-09-07 21:41:08 +00:00
2017-10-18 21:07:29 +00:00
RewindBuffer ( Errors ) ;
bool HaveErrors = FALSE ;
2017-06-25 18:22:54 +00:00
2017-08-29 20:35:28 +00:00
bool FoundIncludes = FALSE ;
bool FoundMenus = FALSE ;
bool FoundPlayer = FALSE ;
bool FoundScript = FALSE ;
2017-09-07 21:41:08 +00:00
bool FoundIndex = FALSE ;
2017-06-25 18:22:54 +00:00
2017-10-18 21:07:29 +00:00
char * Previous = ( * Template ) - > Buffer . Location ;
2017-09-07 21:41:08 +00:00
2017-10-18 21:07:29 +00:00
while ( ( * Template ) - > Buffer . Ptr - ( * Template ) - > Buffer . Location < ( * Template ) - > Buffer . Size )
2017-05-13 15:38:10 +00:00
{
2017-09-07 21:41:08 +00:00
Here :
2017-10-18 21:07:29 +00:00
if ( * ( * Template ) - > Buffer . Ptr = = ' ! ' & & ( ( * Template ) - > Buffer . Ptr > ( * Template ) - > Buffer . Location & & ! StringsDifferT ( " <!-- " , & ( * Template ) - > Buffer . Ptr [ - 1 ] , 0 ) ) )
2017-05-13 15:38:10 +00:00
{
2017-10-18 21:07:29 +00:00
char * CommentStart = & ( * Template ) - > Buffer . Ptr [ - 1 ] ;
while ( ( * Template ) - > Buffer . Ptr - ( * Template ) - > Buffer . Location < ( * Template ) - > Buffer . Size & & StringsDifferT ( " --> " , ( * Template ) - > Buffer . Ptr , 0 ) )
2017-06-25 18:05:58 +00:00
{
2017-09-07 21:41:08 +00:00
for ( int i = 0 ; i < ArrayCount ( Tags ) ; + + i )
2017-05-13 15:38:10 +00:00
{
2017-10-18 21:07:29 +00:00
if ( ! ( StringsDifferT ( Tags [ i ] . Tag , ( * Template ) - > Buffer . Ptr , 0 ) ) )
2017-05-13 15:38:10 +00:00
{
2017-09-07 21:41:08 +00:00
// TODO(matt): Pack up this data for BuffersToHTML() to use
/*
* Potential ways to compress these cases
*
* bool Found [ TAG_COUNT ]
* - Asaf
*
* int * flags [ ] = { [ TAG_INCLUDES ] = & FoundIncludes , [ TAG_MENUS ] = & FoundMenus } flags [ Tags [ i ] . Code ] = true ;
* - insofaras
*
*/
//printf("Switching on the tags\n");
switch ( Tags [ i ] . Code )
2017-08-29 20:35:28 +00:00
{
2017-09-07 21:41:08 +00:00
case TAG_INDEX :
2017-10-18 21:07:29 +00:00
( * Template ) - > Metadata . Tag [ ( * Template ) - > Metadata . TagCount ] . Offset = CommentStart - Previous ;
( * Template ) - > Metadata . Tag [ ( * Template ) - > Metadata . TagCount ] . TagCode = TAG_INDEX ;
( * Template ) - > Metadata . TagCount + + ;
DepartComment ( & ( * Template ) - > Buffer ) ;
Previous = ( * Template ) - > Buffer . Ptr ;
2017-09-07 21:41:08 +00:00
FoundIndex = TRUE ;
goto Here ;
case TAG_INCLUDES :
if ( ! Config . ForceIntegration & & FoundIncludes = = TRUE )
{
2017-10-18 21:07:29 +00:00
CopyStringToBuffer ( Errors , " (*Template) contains more than one <!-- %s --> tag \n " , Tags [ i ] . Tag ) ;
2017-09-07 21:41:08 +00:00
HaveErrors = TRUE ;
}
2017-10-18 21:07:29 +00:00
( * Template ) - > Metadata . Tag [ ( * Template ) - > Metadata . TagCount ] . Offset = CommentStart - Previous ;
( * Template ) - > Metadata . Tag [ ( * Template ) - > Metadata . TagCount ] . TagCode = TAG_INCLUDES ;
( * Template ) - > Metadata . TagCount + + ;
DepartComment ( & ( * Template ) - > Buffer ) ;
Previous = ( * Template ) - > Buffer . Ptr ;
2017-09-07 21:41:08 +00:00
FoundIncludes = TRUE ;
goto Here ;
case TAG_MENUS :
if ( ! Config . ForceIntegration & & FoundMenus = = TRUE )
{
2017-10-18 21:07:29 +00:00
CopyStringToBuffer ( Errors , " (*Template) contains more than one <!-- %s --> tag \n " , Tags [ i ] . Tag ) ;
2017-09-07 21:41:08 +00:00
HaveErrors = TRUE ;
}
2017-10-18 21:07:29 +00:00
( * Template ) - > Metadata . Tag [ ( * Template ) - > Metadata . TagCount ] . Offset = CommentStart - Previous ;
( * Template ) - > Metadata . Tag [ ( * Template ) - > Metadata . TagCount ] . TagCode = TAG_MENUS ;
( * Template ) - > Metadata . TagCount + + ;
DepartComment ( & ( * Template ) - > Buffer ) ;
Previous = ( * Template ) - > Buffer . Ptr ;
2017-09-07 21:41:08 +00:00
FoundMenus = TRUE ;
goto Here ;
case TAG_PLAYER :
if ( ! Config . ForceIntegration & & FoundPlayer = = TRUE )
{
2017-10-18 21:07:29 +00:00
CopyStringToBuffer ( Errors , " (*Template) contains more than one <!-- %s --> tag \n " , Tags [ i ] . Tag ) ;
2017-09-07 21:41:08 +00:00
HaveErrors = TRUE ;
}
2017-10-18 21:07:29 +00:00
( * Template ) - > Metadata . Tag [ ( * Template ) - > Metadata . TagCount ] . Offset = CommentStart - Previous ;
( * Template ) - > Metadata . Tag [ ( * Template ) - > Metadata . TagCount ] . TagCode = TAG_PLAYER ;
( * Template ) - > Metadata . TagCount + + ;
DepartComment ( & ( * Template ) - > Buffer ) ;
Previous = ( * Template ) - > Buffer . Ptr ;
2017-09-07 21:41:08 +00:00
FoundPlayer = TRUE ;
goto Here ;
case TAG_SCRIPT :
if ( ! Config . ForceIntegration & & FoundPlayer = = FALSE )
{
CopyStringToBuffer ( Errors , " <!-- %s --> must come after <!-- __CINERA_PLAYER__ --> \n " , Tags [ i ] . Tag ) ;
HaveErrors = TRUE ;
}
if ( ! Config . ForceIntegration & & FoundScript = = TRUE )
{
2017-10-18 21:07:29 +00:00
CopyStringToBuffer ( Errors , " (*Template) contains more than one <!-- %s --> tag \n " , Tags [ i ] . Tag ) ;
2017-09-07 21:41:08 +00:00
HaveErrors = TRUE ;
}
2017-10-18 21:07:29 +00:00
( * Template ) - > Metadata . Tag [ ( * Template ) - > Metadata . TagCount ] . Offset = CommentStart - Previous ;
( * Template ) - > Metadata . Tag [ ( * Template ) - > Metadata . TagCount ] . TagCode = TAG_SCRIPT ;
( * Template ) - > Metadata . TagCount + + ;
DepartComment ( & ( * Template ) - > Buffer ) ;
Previous = ( * Template ) - > Buffer . Ptr ;
2017-09-07 21:41:08 +00:00
FoundScript = TRUE ;
goto Here ;
2017-12-07 01:15:13 +00:00
case TAG_PROJECT :
( * Template ) - > Metadata . Tag [ ( * Template ) - > Metadata . TagCount ] . Offset = CommentStart - Previous ;
( * Template ) - > Metadata . Tag [ ( * Template ) - > Metadata . TagCount ] . TagCode = TAG_PROJECT ;
( * Template ) - > Metadata . TagCount + + ;
DepartComment ( & ( * Template ) - > Buffer ) ;
Previous = ( * Template ) - > Buffer . Ptr ;
goto Here ;
2017-09-07 21:41:08 +00:00
case TAG_TITLE :
2017-10-18 21:07:29 +00:00
( * Template ) - > Metadata . Tag [ ( * Template ) - > Metadata . TagCount ] . Offset = CommentStart - Previous ;
( * Template ) - > Metadata . Tag [ ( * Template ) - > Metadata . TagCount ] . TagCode = TAG_TITLE ;
( * Template ) - > Metadata . TagCount + + ;
DepartComment ( & ( * Template ) - > Buffer ) ;
Previous = ( * Template ) - > Buffer . Ptr ;
2017-09-07 21:41:08 +00:00
goto Here ;
2017-12-12 23:24:10 +00:00
case TAG_URL :
( * Template ) - > Metadata . Tag [ ( * Template ) - > Metadata . TagCount ] . Offset = CommentStart - Previous ;
( * Template ) - > Metadata . Tag [ ( * Template ) - > Metadata . TagCount ] . TagCode = TAG_URL ;
( * Template ) - > Metadata . TagCount + + ;
DepartComment ( & ( * Template ) - > Buffer ) ;
Previous = ( * Template ) - > Buffer . Ptr ;
goto Here ;
case TAG_VIDEO_ID :
( * Template ) - > Metadata . Tag [ ( * Template ) - > Metadata . TagCount ] . Offset = CommentStart - Previous ;
( * Template ) - > Metadata . Tag [ ( * Template ) - > Metadata . TagCount ] . TagCode = TAG_VIDEO_ID ;
( * Template ) - > Metadata . TagCount + + ;
DepartComment ( & ( * Template ) - > Buffer ) ;
Previous = ( * Template ) - > Buffer . Ptr ;
goto Here ;
2017-09-07 21:41:08 +00:00
} ;
2017-05-13 15:38:10 +00:00
}
}
2017-10-18 21:07:29 +00:00
+ + ( * Template ) - > Buffer . Ptr ;
2017-08-29 20:35:28 +00:00
}
}
else
{
2017-10-18 21:07:29 +00:00
+ + ( * Template ) - > Buffer . Ptr ;
2017-08-29 20:35:28 +00:00
}
}
2017-09-07 21:41:08 +00:00
if ( FoundIndex )
{
2017-10-18 21:07:29 +00:00
( * Template ) - > Metadata . Validity | = PAGE_INDEX ;
2017-09-07 21:41:08 +00:00
}
2017-08-29 20:35:28 +00:00
if ( ! HaveErrors & & FoundIncludes & & FoundMenus & & FoundPlayer & & FoundScript )
{
2017-10-18 21:07:29 +00:00
( * Template ) - > Metadata . Validity | = PAGE_PLAYER ;
2017-08-29 20:35:28 +00:00
}
2017-09-07 21:41:08 +00:00
if ( ! Config . ForceIntegration )
2017-08-29 20:35:28 +00:00
{
2017-10-18 21:07:29 +00:00
if ( PageType = = PAGE_INDEX & & ! ( ( * Template ) - > Metadata . Validity & PAGE_INDEX ) )
2017-09-07 21:41:08 +00:00
{
2017-10-18 21:07:29 +00:00
CopyStringToBuffer ( Errors , " Index template %s must include one <!-- __CINERA_INDEX__ --> tag \n " , ( * Template ) - > Metadata . Filename ) ;
2017-09-07 21:41:08 +00:00
fprintf ( stderr , " %s " , Errors - > Location ) ;
return RC_INVALID_TEMPLATE ;
}
2017-10-18 21:07:29 +00:00
else if ( PageType = = PAGE_PLAYER & & ! ( ( * Template ) - > Metadata . Validity & PAGE_PLAYER ) )
2017-08-29 20:35:28 +00:00
{
2017-10-18 21:07:29 +00:00
if ( ! FoundIncludes ) { CopyStringToBuffer ( Errors , " Player template %s must include one <!-- __CINERA_INCLUDES__ --> tag \n " , ( * Template ) - > Metadata . Filename ) ; } ;
if ( ! FoundMenus ) { CopyStringToBuffer ( Errors , " Player template %s must include one <!-- __CINERA_MENUS__ --> tag \n " , ( * Template ) - > Metadata . Filename ) ; } ;
if ( ! FoundPlayer ) { CopyStringToBuffer ( Errors , " Player template %s must include one <!-- __CINERA_PLAYER__ --> tag \n " , ( * Template ) - > Metadata . Filename ) ; } ;
if ( ! FoundScript ) { CopyStringToBuffer ( Errors , " Player template %s must include one <!-- __CINERA_SCRIPT__ --> tag \n " , ( * Template ) - > Metadata . Filename ) ; } ;
2017-09-07 21:41:08 +00:00
fprintf ( stderr , " %s " , Errors - > Location ) ;
return RC_INVALID_TEMPLATE ;
2017-08-29 20:35:28 +00:00
}
}
2017-09-07 21:41:08 +00:00
return RC_SUCCESS ;
}
2017-12-12 23:24:10 +00:00
void
ConstructIndexURL ( buffer * IndexURL )
{
RewindBuffer ( IndexURL ) ;
if ( StringsDiffer ( Config . BaseURL , " " ) )
{
CopyStringToBuffer ( IndexURL , " %s/ " , Config . BaseURL ) ;
if ( StringsDiffer ( Config . IndexLocation , " " ) )
{
CopyStringToBuffer ( IndexURL , " %s/ " , Config . IndexLocation ) ;
}
}
}
void
ConstructPlayerURL ( buffer * PlayerURL , char * BaseFilename )
{
RewindBuffer ( PlayerURL ) ;
if ( StringsDiffer ( Config . BaseURL , " " ) )
{
CopyStringToBuffer ( PlayerURL , " %s/ " , Config . BaseURL ) ;
if ( StringsDiffer ( Config . PlayerLocation , " " ) )
{
CopyStringToBuffer ( PlayerURL , " %s/ " , Config . PlayerLocation ) ;
}
}
if ( StringsDiffer ( BaseFilename , " " ) )
{
if ( StringsDiffer ( Config . PlayerURLPrefix , " " ) )
{
char * Ptr = BaseFilename + StringLength ( Config . ProjectID ) ;
CopyStringToBuffer ( PlayerURL , " %s%s " , Config . PlayerURLPrefix , Ptr ) ;
}
else
{
CopyStringToBuffer ( PlayerURL , " %s " , BaseFilename ) ;
}
}
}
2017-08-29 20:35:28 +00:00
int
2017-10-18 21:07:29 +00:00
HMMLToBuffers ( buffers * CollationBuffers , char * Filename )
2017-08-29 20:35:28 +00:00
{
2017-09-07 21:41:08 +00:00
RewindBuffer ( & CollationBuffers - > IncludesPlayer ) ;
RewindBuffer ( & CollationBuffers - > Menus ) ;
RewindBuffer ( & CollationBuffers - > Player ) ;
2017-11-11 00:34:47 +00:00
RewindBuffer ( & CollationBuffers - > ScriptPlayer ) ;
2017-09-07 21:41:08 +00:00
RewindBuffer ( & CollationBuffers - > IncludesIndex ) ;
2017-11-11 00:34:47 +00:00
RewindBuffer ( & CollationBuffers - > ScriptIndex ) ;
2017-08-29 20:35:28 +00:00
2017-11-11 00:34:47 +00:00
char Filepath [ 256 ] ;
2017-08-29 20:35:28 +00:00
if ( Config . Edition = = EDITION_PROJECT )
{
CopyString ( Filepath , " %s/%s " , Config . ProjectDir , Filename ) ;
}
else
{
CopyString ( Filepath , " %s " , Filename ) ;
}
FILE * InFile ;
if ( ! ( InFile = fopen ( Filepath , " r " ) ) )
{
2017-10-18 21:07:29 +00:00
LogError ( LOG_ERROR , " Unable to open (annotations file) %s: %s " , Filename , strerror ( errno ) ) ;
2017-09-07 21:41:08 +00:00
fprintf ( stderr , " Unable to open (annotations file) %s: %s \n " , Filename , strerror ( errno ) ) ;
return RC_ERROR_FILE ;
2017-08-29 20:35:28 +00:00
}
HMML_Output HMML = hmml_parse_file ( InFile ) ;
fclose ( InFile ) ;
if ( HMML . well_formed )
{
2017-12-12 23:24:10 +00:00
for ( int ProjectIndex = 0 ; ProjectIndex < ArrayCount ( ProjectInfo ) ; + + ProjectIndex )
2017-12-07 01:15:13 +00:00
{
if ( ! StringsDiffer ( ProjectInfo [ ProjectIndex ] . ProjectID , Config . ProjectID ) )
{
CopyString ( CollationBuffers - > ProjectName , ProjectInfo [ ProjectIndex ] . FullName ) ;
break ;
}
}
2017-12-12 23:24:10 +00:00
CopyString ( CollationBuffers - > Title , HMML . metadata . title ) ;
buffer URLPlayer ;
ClaimBuffer ( & URLPlayer , " URLPlayer " , 2048 ) ;
char * Ptr = Filename ;
Ptr + = ( StringLength ( Filename ) - StringLength ( " .hmml " ) ) ;
if ( ! ( StringsDiffer ( Ptr , " .hmml " ) ) )
{
* Ptr = ' \0 ' ;
ConstructPlayerURL ( & URLPlayer , Filename ) ;
* Ptr = ' . ' ;
}
CopyString ( CollationBuffers - > URLPlayer , URLPlayer . Location ) ;
CopyString ( CollationBuffers - > VideoID , HMML . metadata . id ) ;
2017-12-07 01:15:13 +00:00
2017-08-29 20:35:28 +00:00
# if DEBUG
printf (
" ================================================================================ \n "
" %s \n "
" ================================================================================ \n " ,
Filename ) ;
# endif
// NOTE(matt): Tree structure of "global" buffer dependencies
// Master
// IncludesPlayer
// Menus
// QuoteMenu
// ReferenceMenu
// FilterMenu
// FilterTopics
// FilterMedia
// CreditsMenu
// Player
// Script
// FilterState
buffer QuoteMenu ;
buffer ReferenceMenu ;
buffer FilterMenu ;
buffer FilterTopics ;
buffer FilterMedia ;
buffer CreditsMenu ;
buffer Annotation ;
buffer AnnotationHeader ;
buffer AnnotationClass ;
buffer AnnotationData ;
buffer Text ;
buffer TopicDots ;
buffer FilterState ;
2017-12-07 01:15:13 +00:00
if ( ClaimBuffer ( & QuoteMenu , " QuoteMenu " , Kilobytes ( 32 ) ) = = RC_ARENA_FULL ) { hmml_free ( & HMML ) ; return RC_ARENA_FULL ; } ;
if ( ClaimBuffer ( & ReferenceMenu , " ReferenceMenu " , Kilobytes ( 32 ) ) = = RC_ARENA_FULL ) { hmml_free ( & HMML ) ; return RC_ARENA_FULL ; } ;
2017-10-18 21:07:29 +00:00
if ( ClaimBuffer ( & FilterMenu , " FilterMenu " , Kilobytes ( 16 ) ) = = RC_ARENA_FULL ) { hmml_free ( & HMML ) ; return RC_ARENA_FULL ; } ;
if ( ClaimBuffer ( & FilterTopics , " FilterTopics " , Kilobytes ( 8 ) ) = = RC_ARENA_FULL ) { hmml_free ( & HMML ) ; return RC_ARENA_FULL ; } ;
if ( ClaimBuffer ( & FilterMedia , " FilterMedia " , Kilobytes ( 8 ) ) = = RC_ARENA_FULL ) { hmml_free ( & HMML ) ; return RC_ARENA_FULL ; } ;
if ( ClaimBuffer ( & CreditsMenu , " CreditsMenu " , Kilobytes ( 8 ) ) = = RC_ARENA_FULL ) { hmml_free ( & HMML ) ; return RC_ARENA_FULL ; } ;
if ( ClaimBuffer ( & FilterState , " FilterState " , Kilobytes ( 4 ) ) = = RC_ARENA_FULL ) { hmml_free ( & HMML ) ; return RC_ARENA_FULL ; } ;
2017-08-29 20:35:28 +00:00
ref_info ReferencesArray [ 200 ] = { 0 } ;
categories Topics = { 0 } ;
categories Media = { 0 } ;
bool HasQuoteMenu = FALSE ;
bool HasReferenceMenu = FALSE ;
bool HasFilterMenu = FALSE ;
bool HasCreditsMenu = FALSE ;
int QuoteIdentifier = 0x3b1 ;
int RefIdentifier = 1 ;
int UniqueRefs = 0 ;
CopyStringToBuffer ( & CollationBuffers - > Menus ,
2017-12-10 00:17:19 +00:00
" <div class= \" cineraMenus %s \" > \n "
2017-12-07 01:15:13 +00:00
" <span class= \" episode_name \" > " , StringsDiffer ( Config . Theme , " " ) ? Config . Theme : HMML . metadata . project ) ;
2017-08-29 20:35:28 +00:00
CopyStringToBufferHTMLSafe ( & CollationBuffers - > Menus , HMML . metadata . title ) ;
CopyStringToBuffer ( & CollationBuffers - > Menus , " </span> \n "
" <span id= \" focus-warn \" >⚠ Click here to regain focus ⚠</span> \n " ) ;
CopyStringToBuffer ( & CollationBuffers - > Player ,
2017-12-10 00:17:19 +00:00
" <div class= \" cineraPlayerContainer \" > \n "
2017-08-29 20:35:28 +00:00
" <div class= \" video_container \" data-videoId= \" %s \" ></div> \n "
2017-12-07 01:15:13 +00:00
" <div class= \" markers_container %s \" > \n " , HMML . metadata . id , StringsDiffer ( Config . Theme , " " ) ? Config . Theme : HMML . metadata . project ) ;
2017-08-29 20:35:28 +00:00
2017-10-18 21:07:29 +00:00
int CreditsErrorCode = BuildCredits ( & CreditsMenu , & HasCreditsMenu , & HMML . metadata ) ;
2017-08-29 20:35:28 +00:00
if ( CreditsErrorCode )
{
switch ( CreditsErrorCode )
{
case CreditsError_NoHost :
fprintf ( stderr , " %s: Missing \" member \" in the [video] node. Skipping... \n " , Filename ) ;
goto Cleanup ;
break ;
case CreditsError_NoAnnotator :
fprintf ( stderr , " %s: Missing \" annotator \" in the [video] node. Skipping... \n " , Filename ) ;
goto Cleanup ;
break ;
default :
break ;
2017-06-25 18:05:58 +00:00
}
2017-08-29 20:35:28 +00:00
}
# if DEBUG
printf ( " \n \n --- Entering Annotations Loop --- \n \n \n \n " ) ;
2017-06-25 18:05:58 +00:00
# endif
2017-11-11 00:34:47 +00:00
RewindBuffer ( & CollationBuffers - > Search ) ;
char * FilenamePtr = Filename ;
FilenamePtr + = StringLength ( Filename ) - StringLength ( " .hmml " ) ;
* FilenamePtr = ' \0 ' ;
CopyStringToBuffer ( & CollationBuffers - > Search , " name: \" %s \" \n "
" title: \" " , Filename ) ;
* FilenamePtr = ' . ' ;
CopyStringToBufferJSONSafe ( & CollationBuffers - > Search , HMML . metadata . title ) ;
CopyStringToBuffer ( & CollationBuffers - > Search , " \" \n "
" markers: \n " ) ;
2017-08-29 20:35:28 +00:00
for ( int AnnotationIndex = 0 ; AnnotationIndex < HMML . annotation_count ; + + AnnotationIndex )
{
# if DEBUG
printf ( " %d \n " , AnnotationIndex ) ;
# endif
2017-11-11 00:34:47 +00:00
2017-08-29 20:35:28 +00:00
HMML_Annotation * Anno = HMML . annotations + AnnotationIndex ;
2017-11-11 00:34:47 +00:00
CopyStringToBuffer ( & CollationBuffers - > Search , " \" %d \" : \" " , TimecodeToSeconds ( Anno - > time ) ) ;
CopyStringToBufferJSONSafe ( & CollationBuffers - > Search , Anno - > text ) ;
CopyStringToBuffer ( & CollationBuffers - > Search , " \" \n " ) ;
2017-08-29 20:35:28 +00:00
categories LocalTopics = { 0 } ;
categories LocalMedia = { 0 } ;
bool HasQuote = FALSE ;
bool HasReference = FALSE ;
quote_info QuoteInfo = { 0 } ;
// NOTE(matt): Tree structure of "annotation local" buffer dependencies
// Annotation
// AnnotationHeader
// AnnotationClass
// AnnotationData
// Text
// TopicDots
2017-10-18 21:07:29 +00:00
if ( ClaimBuffer ( & Annotation , " Annotation " , Kilobytes ( 8 ) ) = = RC_ARENA_FULL ) { hmml_free ( & HMML ) ; return RC_ARENA_FULL ; } ;
if ( ClaimBuffer ( & AnnotationHeader , " AnnotationHeader " , 512 ) = = RC_ARENA_FULL ) { hmml_free ( & HMML ) ; return RC_ARENA_FULL ; } ;
if ( ClaimBuffer ( & AnnotationClass , " AnnotationClass " , 256 ) = = RC_ARENA_FULL ) { hmml_free ( & HMML ) ; return RC_ARENA_FULL ; } ;
if ( ClaimBuffer ( & AnnotationData , " AnnotationData " , 512 ) = = RC_ARENA_FULL ) { hmml_free ( & HMML ) ; return RC_ARENA_FULL ; } ;
if ( ClaimBuffer ( & Text , " Text " , Kilobytes ( 4 ) ) = = RC_ARENA_FULL ) { hmml_free ( & HMML ) ; return RC_ARENA_FULL ; } ;
if ( ClaimBuffer ( & TopicDots , " TopicDots " , 512 ) = = RC_ARENA_FULL ) { hmml_free ( & HMML ) ; return RC_ARENA_FULL ; } ;
2017-08-29 20:35:28 +00:00
CopyStringToBuffer ( & AnnotationHeader ,
" <div data-timestamp= \" %d \" " ,
TimecodeToSeconds ( Anno - > time ) ) ;
CopyStringToBuffer ( & AnnotationClass ,
" class= \" marker " ) ;
if ( Anno - > author )
{
if ( ! HasFilterMenu )
2017-06-25 18:05:58 +00:00
{
2017-08-29 20:35:28 +00:00
HasFilterMenu = TRUE ;
2017-06-25 18:05:58 +00:00
}
2017-10-18 21:07:29 +00:00
InsertCategory ( & Topics , & LocalTopics , & Media , & LocalMedia , " authored " ) ;
2017-08-29 20:35:28 +00:00
hsl_colour AuthorColour ;
StringToColourHash ( & AuthorColour , Anno - > author ) ;
if ( Config . Edition = = EDITION_NETWORK )
2017-06-25 18:05:58 +00:00
{
2017-08-29 20:35:28 +00:00
fprintf ( stderr , " %s:%d - TODO(matt): Implement author hoverbox \n " , __FILE__ , __LINE__ ) ;
// NOTE(matt): We should get instructions on how to get this info in the config
CopyStringToBuffer ( & Text ,
" <a class= \" author \" href= \" https://handmade.network/m/%s \" target= \" blank \" style= \" color: hsl(%d, %d%%, %d%%); text-decoration: none \" data-hue= \" %d \" data-saturation= \" %d%% \" >%s</a> " ,
Anno - > author ,
AuthorColour . Hue , AuthorColour . Saturation , AuthorColour . Lightness ,
AuthorColour . Hue , AuthorColour . Saturation ,
Anno - > author ) ;
2017-06-25 18:05:58 +00:00
}
else
{
2017-08-29 20:35:28 +00:00
CopyStringToBuffer ( & Text ,
" <span class= \" author \" style= \" color: hsl(%d, %d%%, %d%%); \" data-hue= \" %d \" data-saturation= \" %d%% \" >%s</span> " ,
AuthorColour . Hue , AuthorColour . Saturation , AuthorColour . Lightness ,
AuthorColour . Hue , AuthorColour . Saturation ,
Anno - > author ) ;
2017-06-25 18:05:58 +00:00
}
2017-08-29 20:35:28 +00:00
2017-06-25 18:05:58 +00:00
}
2017-08-29 20:35:28 +00:00
char * InPtr = Anno - > text ;
int MarkerIndex = 0 , RefIndex = 0 ;
while ( * InPtr | | RefIndex < Anno - > reference_count )
2017-06-25 18:05:58 +00:00
{
2017-08-29 20:35:28 +00:00
if ( MarkerIndex < Anno - > marker_count & &
InPtr - Anno - > text = = Anno - > markers [ MarkerIndex ] . offset )
2017-05-13 15:38:10 +00:00
{
2017-08-29 20:35:28 +00:00
char * Readable = Anno - > markers [ MarkerIndex ] . parameter
? Anno - > markers [ MarkerIndex ] . parameter
: Anno - > markers [ MarkerIndex ] . marker ;
if ( Anno - > markers [ MarkerIndex ] . type = = HMML_MEMBER )
2017-05-13 15:38:10 +00:00
{
2017-08-29 20:35:28 +00:00
hsl_colour MemberColour ;
StringToColourHash ( & MemberColour , Anno - > markers [ MarkerIndex ] . marker ) ;
if ( Config . Edition = = EDITION_NETWORK )
2017-05-13 15:38:10 +00:00
{
2017-08-29 20:35:28 +00:00
fprintf ( stderr , " %s:%d - TODO(matt): Implement member hoverbox \n " , __FILE__ , __LINE__ ) ;
// NOTE(matt): We should get instructions on how to get this info in the config
CopyStringToBuffer ( & Text ,
" <a class= \" member \" href= \" https://handmade.network/m/%s \" target= \" blank \" style= \" color: hsl(%d, %d%%, %d%%); text-decoration: none \" data-hue= \" %d \" data-saturation= \" %d%% \" >%.*s</a> " ,
Anno - > markers [ MarkerIndex ] . marker ,
MemberColour . Hue , MemberColour . Saturation , MemberColour . Lightness ,
MemberColour . Hue , MemberColour . Saturation ,
StringLength ( Readable ) , InPtr ) ;
2017-05-13 15:38:10 +00:00
}
2017-08-29 20:35:28 +00:00
else
{
CopyStringToBuffer ( & Text ,
" <span class= \" member \" style= \" color: hsl(%d, %d%%, %d%%); \" data-hue= \" %d \" data-saturation= \" %d%% \" >%.*s</span> " ,
MemberColour . Hue , MemberColour . Saturation , MemberColour . Lightness ,
MemberColour . Hue , MemberColour . Saturation ,
StringLength ( Readable ) , InPtr ) ;
}
InPtr + = StringLength ( Readable ) ;
+ + MarkerIndex ;
}
else if ( Anno - > markers [ MarkerIndex ] . type = = HMML_PROJECT )
{
hsl_colour ProjectColour ;
StringToColourHash ( & ProjectColour , Anno - > markers [ MarkerIndex ] . marker ) ;
if ( Config . Edition = = EDITION_NETWORK )
2017-05-13 15:38:10 +00:00
{
2017-08-29 20:35:28 +00:00
fprintf ( stderr , " %s:%d - TODO(matt): Implement project hoverbox \n " , __FILE__ , __LINE__ ) ;
// NOTE(matt): We should get instructions on how to get this info in the config
CopyStringToBuffer ( & Text ,
" <a class= \" project \" href= \" https://%s.handmade.network/ \" target= \" blank \" style= \" color: hsl(%d, %d%%, %d%%); text-decoration: none \" data-hue= \" %d \" data-saturation= \" %d%% \" >%s</a> " ,
Anno - > markers [ MarkerIndex ] . marker ,
ProjectColour . Hue , ProjectColour . Saturation , ProjectColour . Lightness ,
ProjectColour . Hue , ProjectColour . Saturation ,
Readable ) ;
2017-05-13 15:38:10 +00:00
}
2017-08-29 20:35:28 +00:00
else
2017-05-13 15:38:10 +00:00
{
2017-08-29 20:35:28 +00:00
CopyStringToBuffer ( & Text ,
" <span class= \" project \" style= \" color: hsl(%d, %d%%, %d%%); \" data-hue= \" %d \" data-saturation= \" %d%% \" >%s</span> " ,
ProjectColour . Hue , ProjectColour . Saturation , ProjectColour . Lightness ,
ProjectColour . Hue , ProjectColour . Saturation ,
Readable ) ;
2017-05-13 15:38:10 +00:00
}
2017-08-29 20:35:28 +00:00
InPtr + = StringLength ( Readable ) ;
+ + MarkerIndex ;
}
else if ( Anno - > markers [ MarkerIndex ] . type = = HMML_CATEGORY )
{
2017-10-18 21:07:29 +00:00
switch ( GenerateTopicColours ( Anno - > markers [ MarkerIndex ] . marker ) )
2017-09-07 21:41:08 +00:00
{
case RC_SUCCESS :
case RC_NOOP :
break ;
case RC_ERROR_FILE :
case RC_ERROR_MEMORY :
2017-12-18 14:31:05 +00:00
hmml_free ( & HMML ) ;
2017-09-07 21:41:08 +00:00
return RC_ERROR_FATAL ;
} ;
2017-08-29 20:35:28 +00:00
if ( ! HasFilterMenu )
2017-05-13 15:38:10 +00:00
{
2017-08-29 20:35:28 +00:00
HasFilterMenu = TRUE ;
2017-05-13 15:38:10 +00:00
}
2017-10-18 21:07:29 +00:00
InsertCategory ( & Topics , & LocalTopics , & Media , & LocalMedia , Anno - > markers [ MarkerIndex ] . marker ) ;
2017-05-13 15:38:10 +00:00
}
}
2017-06-25 18:22:54 +00:00
2017-08-29 20:35:28 +00:00
while ( RefIndex < Anno - > reference_count & &
InPtr - Anno - > text = = Anno - > references [ RefIndex ] . offset )
2017-06-25 18:05:58 +00:00
{
2017-08-29 20:35:28 +00:00
HMML_Reference * CurrentRef = Anno - > references + RefIndex ;
if ( ! HasReferenceMenu )
{
CopyStringToBuffer ( & ReferenceMenu ,
" <div class= \" menu references \" > \n "
" <span>References ▼</span> \n "
" <div class= \" mouse_catcher \" ></div> \n "
" <div class= \" refs references_container \" > \n " ) ;
2017-06-25 18:22:54 +00:00
2017-10-18 21:07:29 +00:00
if ( BuildReference ( ReferencesArray , RefIdentifier , UniqueRefs , CurrentRef , Anno ) = = 1 )
2017-08-29 20:35:28 +00:00
{
2017-10-18 21:07:29 +00:00
LogError ( LOG_ERROR , " Reference combination processing failed: %s:%d " , Filename , Anno - > line ) ;
2017-08-29 20:35:28 +00:00
fprintf ( stderr , " %s:%d: Cannot process new combination of reference info \n "
" \n "
" Either tweak your annotation, or contact matt@handmadedev.org \n "
" mentioning the ref node you want to write and how you want it to \n "
" appear in the references menu \n " , Filename , Anno - > line ) ;
hmml_free ( & HMML ) ;
2017-09-07 21:41:08 +00:00
return RC_INVALID_REFERENCE ;
2017-08-29 20:35:28 +00:00
}
+ + ReferencesArray [ RefIdentifier - 1 ] . IdentifierCount ;
+ + UniqueRefs ;
2017-06-25 18:22:54 +00:00
2017-08-29 20:35:28 +00:00
HasReferenceMenu = TRUE ;
}
else
2017-08-08 19:26:54 +00:00
{
2017-08-29 20:35:28 +00:00
for ( int i = 0 ; i < UniqueRefs ; + + i )
2017-08-08 19:26:54 +00:00
{
2017-08-29 20:35:28 +00:00
if ( ReferencesArray [ i ] . IdentifierCount = = REF_MAX_IDENTIFIER )
2017-08-08 19:26:54 +00:00
{
2017-10-18 21:07:29 +00:00
LogError ( LOG_EMERGENCY , " REF_MAX_IDENTIFIER (%d) reached. Contact matt@handmadedev.org " , REF_MAX_IDENTIFIER ) ;
2017-08-29 20:35:28 +00:00
fprintf ( stderr , " %s:%d: Too many timecodes associated with one reference (increase REF_MAX_IDENTIFIER) \n " , Filename , Anno - > line ) ;
hmml_free ( & HMML ) ;
2017-09-07 21:41:08 +00:00
return RC_ERROR_MAX_REFS ;
2017-08-08 19:26:54 +00:00
}
2017-08-29 20:35:28 +00:00
if ( CurrentRef - > isbn )
2017-08-08 19:26:54 +00:00
{
2017-08-29 20:35:28 +00:00
if ( ! StringsDiffer ( CurrentRef - > isbn , ReferencesArray [ i ] . ID ) )
2017-08-08 19:26:54 +00:00
{
2017-08-29 20:35:28 +00:00
CopyString ( ReferencesArray [ i ] . Identifier [ ReferencesArray [ i ] . IdentifierCount ] . Timecode , Anno - > time ) ;
ReferencesArray [ i ] . Identifier [ ReferencesArray [ i ] . IdentifierCount ] . Identifier = RefIdentifier ;
+ + ReferencesArray [ i ] . IdentifierCount ;
goto AppendedIdentifier ;
2017-08-08 19:26:54 +00:00
}
}
2017-08-29 20:35:28 +00:00
else if ( CurrentRef - > url )
2017-08-08 19:26:54 +00:00
{
2017-08-29 20:35:28 +00:00
if ( ! StringsDiffer ( CurrentRef - > url , ReferencesArray [ i ] . ID ) )
2017-08-08 19:26:54 +00:00
{
2017-08-29 20:35:28 +00:00
CopyString ( ReferencesArray [ i ] . Identifier [ ReferencesArray [ i ] . IdentifierCount ] . Timecode , Anno - > time ) ;
ReferencesArray [ i ] . Identifier [ ReferencesArray [ i ] . IdentifierCount ] . Identifier = RefIdentifier ;
+ + ReferencesArray [ i ] . IdentifierCount ;
goto AppendedIdentifier ;
2017-08-08 19:26:54 +00:00
}
}
2017-08-29 20:35:28 +00:00
else
2017-08-08 19:26:54 +00:00
{
2017-10-18 21:07:29 +00:00
LogError ( LOG_ERROR , " Reference missing ISBN or URL: %s:%d " , Filename , Anno - > line ) ;
2017-08-29 20:35:28 +00:00
fprintf ( stderr , " %s:%d: Reference must have an ISBN or URL \n " , Filename , Anno - > line ) ;
hmml_free ( & HMML ) ;
2017-09-07 21:41:08 +00:00
return RC_INVALID_REFERENCE ;
2017-08-08 19:26:54 +00:00
}
}
2017-08-29 20:35:28 +00:00
2017-10-18 21:07:29 +00:00
if ( BuildReference ( ReferencesArray , RefIdentifier , UniqueRefs , CurrentRef , Anno ) = = 1 )
2017-08-08 19:26:54 +00:00
{
2017-10-18 21:07:29 +00:00
LogError ( LOG_ERROR , " Reference combination processing failed: %s:%d " , Filename , Anno - > line ) ;
2017-08-29 20:35:28 +00:00
fprintf ( stderr , " %s:%d: Cannot process new combination of reference info \n "
" \n "
" Either tweak your annotation, or contact matt@handmadedev.org \n "
" mentioning the ref node you want to write and how you want it to \n "
" appear in the references menu \n " , Filename , Anno - > line ) ;
hmml_free ( & HMML ) ;
2017-09-07 21:41:08 +00:00
return RC_INVALID_REFERENCE ;
2017-08-08 19:26:54 +00:00
}
2017-08-29 20:35:28 +00:00
+ + ReferencesArray [ UniqueRefs ] . IdentifierCount ;
+ + UniqueRefs ;
2017-08-08 19:26:54 +00:00
}
2017-08-29 20:35:28 +00:00
AppendedIdentifier :
if ( ! HasReference )
2017-03-25 02:07:55 +00:00
{
2017-08-29 20:35:28 +00:00
if ( CurrentRef - > isbn )
2017-03-25 02:07:55 +00:00
{
2017-08-29 20:35:28 +00:00
CopyStringToBuffer ( & AnnotationData , " data-ref= \" %s " , CurrentRef - > isbn ) ;
2017-03-25 02:07:55 +00:00
}
2017-08-29 20:35:28 +00:00
else if ( CurrentRef - > url )
2017-03-29 03:38:12 +00:00
{
2017-08-29 20:35:28 +00:00
CopyStringToBuffer ( & AnnotationData , " data-ref= \" %s " , CurrentRef - > url ) ;
2017-03-29 03:38:12 +00:00
}
2017-03-30 02:57:04 +00:00
else
{
2017-10-18 21:07:29 +00:00
LogError ( LOG_ERROR , " Reference missing ISBN or URL: %s:%d " , Filename , Anno - > line ) ;
2017-08-29 20:35:28 +00:00
fprintf ( stderr , " %s:%d: Reference must have an ISBN or URL \n " , Filename , Anno - > line ) ;
hmml_free ( & HMML ) ;
2017-09-07 21:41:08 +00:00
return RC_INVALID_REFERENCE ;
2017-03-30 02:57:04 +00:00
}
2017-06-25 18:22:54 +00:00
2017-08-29 20:35:28 +00:00
HasReference = TRUE ;
}
else
{
if ( CurrentRef - > isbn )
2017-03-29 03:38:12 +00:00
{
2017-08-29 20:35:28 +00:00
CopyStringToBuffer ( & AnnotationData , " ,%s " , CurrentRef - > isbn ) ;
2017-03-29 03:38:12 +00:00
}
2017-08-29 20:35:28 +00:00
else if ( CurrentRef - > url )
2017-05-24 21:56:36 +00:00
{
2017-08-29 20:35:28 +00:00
CopyStringToBuffer ( & AnnotationData , " ,%s " , CurrentRef - > url ) ;
2017-05-24 21:56:36 +00:00
}
else
{
2017-10-18 21:07:29 +00:00
LogError ( LOG_ERROR , " Reference missing ISBN or URL: %s:%d " , Filename , Anno - > line ) ;
2017-08-29 20:35:28 +00:00
fprintf ( stderr , " %s:%d: Reference must have an ISBN or URL " , Filename , Anno - > line ) ;
hmml_free ( & HMML ) ;
2017-09-07 21:41:08 +00:00
return RC_INVALID_REFERENCE ;
2017-05-24 21:56:36 +00:00
}
2017-08-29 20:35:28 +00:00
}
2017-06-25 18:22:54 +00:00
2017-08-29 20:35:28 +00:00
if ( Anno - > references [ RefIndex ] . offset = = Anno - > references [ RefIndex - 1 ] . offset )
{
CopyStringToBuffer ( & Text , " <sup>,%d</sup> " , RefIdentifier ) ;
2017-03-25 02:07:55 +00:00
}
2017-08-29 20:35:28 +00:00
else
{
CopyStringToBuffer ( & Text , " <sup>%d</sup> " , RefIdentifier ) ;
}
+ + RefIndex ;
+ + RefIdentifier ;
}
2017-06-25 18:22:54 +00:00
2017-08-29 20:35:28 +00:00
if ( * InPtr )
{
switch ( * InPtr )
2017-03-25 02:07:55 +00:00
{
2017-08-29 20:35:28 +00:00
case ' < ' :
2017-06-25 18:05:58 +00:00
CopyStringToBuffer ( & Text , " < " ) ;
InPtr + + ;
break ;
2017-08-29 20:35:28 +00:00
case ' > ' :
2017-06-25 18:05:58 +00:00
CopyStringToBuffer ( & Text , " > " ) ;
InPtr + + ;
break ;
2017-08-29 20:35:28 +00:00
case ' & ' :
2017-06-25 18:05:58 +00:00
CopyStringToBuffer ( & Text , " & " ) ;
InPtr + + ;
break ;
2017-08-29 20:35:28 +00:00
case ' \" ' :
2017-06-25 18:05:58 +00:00
CopyStringToBuffer ( & Text , " " " ) ;
InPtr + + ;
break ;
2017-08-29 20:35:28 +00:00
case ' \' ' :
2017-06-25 18:05:58 +00:00
CopyStringToBuffer ( & Text , " ' " ) ;
InPtr + + ;
break ;
2017-08-29 20:35:28 +00:00
default :
2017-06-25 18:05:58 +00:00
* Text . Ptr + + = * InPtr + + ;
2017-08-19 21:41:51 +00:00
* Text . Ptr = ' \0 ' ;
2017-06-25 18:05:58 +00:00
break ;
2017-03-25 02:07:55 +00:00
}
}
2017-08-29 20:35:28 +00:00
}
2017-06-25 18:22:54 +00:00
2017-08-29 20:35:28 +00:00
if ( Anno - > is_quote )
{
if ( ! HasQuoteMenu )
2017-03-29 03:38:12 +00:00
{
2017-08-29 20:35:28 +00:00
CopyStringToBuffer ( & QuoteMenu ,
" <div class= \" menu quotes \" > \n "
" <span>Quotes ▼</span> \n "
" <div class= \" mouse_catcher \" ></div> \n "
" <div class= \" refs quotes_container \" > \n " ) ;
2017-06-25 18:22:54 +00:00
2017-08-29 20:35:28 +00:00
HasQuoteMenu = TRUE ;
}
2017-06-25 18:22:54 +00:00
2017-08-29 20:35:28 +00:00
if ( ! HasReference )
{
CopyStringToBuffer ( & AnnotationData , " data-ref= \" &#%d; " , QuoteIdentifier ) ;
}
else
{
CopyStringToBuffer ( & AnnotationData , " ,&#%d; " , QuoteIdentifier ) ;
}
2017-06-25 18:22:54 +00:00
2017-08-29 20:35:28 +00:00
HasQuote = TRUE ;
2017-06-25 18:22:54 +00:00
2017-08-29 20:35:28 +00:00
char * Speaker = Anno - > quote . author ? Anno - > quote . author : HMML . metadata . stream_username ? HMML . metadata . stream_username : HMML . metadata . member ;
if ( BuildQuote ( & QuoteInfo ,
Speaker ,
2017-10-18 21:07:29 +00:00
Anno - > quote . id ) = = 1 )
2017-08-29 20:35:28 +00:00
{
2017-10-18 21:07:29 +00:00
LogError ( LOG_ERROR , " Quote #%s %d not found: %s:%d " , Speaker , Anno - > quote . id , Filename , Anno - > line ) ;
2017-09-07 21:41:08 +00:00
fprintf ( stderr , " %s:%d: Quote #%s %d not found. Skipping this file... \n " ,
2017-08-29 20:35:28 +00:00
Filename ,
Anno - > line ,
Speaker ,
Anno - > quote . id ) ;
hmml_free ( & HMML ) ;
2017-09-07 21:41:08 +00:00
return RC_ERROR_QUOTE ;
2017-03-29 03:38:12 +00:00
}
2017-06-25 18:22:54 +00:00
2017-08-29 20:35:28 +00:00
CopyStringToBuffer ( & QuoteMenu ,
" <a target= \" _blank \" class= \" ref \" href= \" https://dev.abaines.me.uk/quotes/%s/%d \" > \n "
" <span data-id= \" &#%d; \" > \n "
" <span class= \" ref_content \" > \n "
" <div class= \" source \" >Quote %d</div> \n "
" <div class= \" ref_title \" > " ,
Speaker ,
Anno - > quote . id ,
QuoteIdentifier ,
Anno - > quote . id ) ;
CopyStringToBufferHTMLSafe ( & QuoteMenu , QuoteInfo . Text ) ;
CopyStringToBuffer ( & QuoteMenu , " </div> \n "
" <div class= \" quote_byline \" >—%s, %s</div> \n "
" </span> \n "
" <div class= \" ref_indices \" > \n "
" <span data-timestamp= \" %d \" class= \" timecode \" ><span class= \" ref_index \" >[&#%d;]</span><span class= \" time \" >%s</span></span> \n "
" </div> \n "
" </span> \n "
" </a> \n " ,
Speaker ,
QuoteInfo . Date ,
TimecodeToSeconds ( Anno - > time ) ,
QuoteIdentifier ,
Anno - > time ) ;
if ( ! Anno - > text [ 0 ] )
2017-03-25 02:07:55 +00:00
{
2017-08-29 20:35:28 +00:00
CopyStringToBuffer ( & Text , " “ " ) ;
CopyStringToBufferHTMLSafe ( & Text , QuoteInfo . Text ) ;
CopyStringToBuffer ( & Text , " ” " ) ;
2017-05-22 21:37:00 +00:00
}
2017-08-29 20:35:28 +00:00
CopyStringToBuffer ( & Text , " <sup>&#%d;</sup> " , QuoteIdentifier ) ;
+ + QuoteIdentifier ;
}
2017-06-25 18:22:54 +00:00
2017-08-29 20:35:28 +00:00
while ( MarkerIndex < Anno - > marker_count )
{
2017-10-18 21:07:29 +00:00
switch ( GenerateTopicColours ( Anno - > markers [ MarkerIndex ] . marker ) )
2017-09-07 21:41:08 +00:00
{
case RC_SUCCESS :
case RC_NOOP :
break ;
case RC_ERROR_FILE :
case RC_ERROR_MEMORY :
2017-12-18 14:31:05 +00:00
hmml_free ( & HMML ) ;
2017-09-07 21:41:08 +00:00
return RC_ERROR_FATAL ;
} ;
2017-08-29 20:35:28 +00:00
if ( ! HasFilterMenu )
2017-05-22 21:37:00 +00:00
{
2017-08-29 20:35:28 +00:00
HasFilterMenu = TRUE ;
2017-03-25 02:07:55 +00:00
}
2017-10-18 21:07:29 +00:00
InsertCategory ( & Topics , & LocalTopics , & Media , & LocalMedia , Anno - > markers [ MarkerIndex ] . marker ) ;
2017-08-29 20:35:28 +00:00
+ + MarkerIndex ;
}
2017-06-25 18:22:54 +00:00
2017-08-29 20:35:28 +00:00
if ( LocalMedia . Count = = 0 )
{
2017-10-18 21:07:29 +00:00
InsertCategory ( & Topics , & LocalTopics , & Media , & LocalMedia , Config . DefaultMedium ) ;
2017-08-29 20:35:28 +00:00
}
2017-06-25 18:22:54 +00:00
2017-10-18 21:07:29 +00:00
BuildCategories ( & AnnotationClass , & TopicDots , & LocalTopics , & LocalMedia , & MarkerIndex ) ;
2017-08-29 20:35:28 +00:00
CopyBuffer ( & AnnotationHeader , & AnnotationClass ) ;
2017-06-25 18:22:54 +00:00
2017-08-29 20:35:28 +00:00
if ( HasQuote | | HasReference )
{
CopyStringToBuffer ( & AnnotationData , " \" " ) ;
CopyBuffer ( & AnnotationHeader , & AnnotationData ) ;
}
CopyStringToBuffer ( & AnnotationHeader , " > \n " ) ;
CopyBuffer ( & Annotation , & AnnotationHeader ) ;
CopyStringToBuffer ( & Annotation ,
2017-12-10 17:05:46 +00:00
" <div class= \" cineraContent \" ><span class= \" timecode \" >%s</span> " ,
2017-08-29 20:35:28 +00:00
Anno - > time ) ;
CopyBuffer ( & Annotation , & Text ) ;
2017-06-25 18:22:54 +00:00
2017-08-29 20:35:28 +00:00
if ( LocalTopics . Count > 0 )
{
CopyBuffer ( & Annotation , & TopicDots ) ;
2017-03-22 02:18:31 +00:00
}
2017-06-25 18:22:54 +00:00
2017-08-29 20:35:28 +00:00
CopyStringToBuffer ( & Annotation , " </div> \n "
" <div class= \" progress faded \" > \n "
2017-12-10 17:05:46 +00:00
" <div class= \" cineraContent \" ><span class= \" timecode \" >%s</span> " ,
2017-08-29 20:35:28 +00:00
Anno - > time ) ;
CopyBuffer ( & Annotation , & Text ) ;
2017-11-11 00:34:47 +00:00
if ( LocalTopics . Count > 0 )
{
CopyBuffer ( & Annotation , & TopicDots ) ;
}
2017-08-29 20:35:28 +00:00
CopyStringToBuffer ( & Annotation , " </div> \n "
" </div> \n "
" <div class= \" progress main \" > \n "
2017-12-10 17:05:46 +00:00
" <div class= \" cineraContent \" ><span class= \" timecode \" >%s</span> " ,
2017-08-29 20:35:28 +00:00
Anno - > time ) ;
CopyBuffer ( & Annotation , & Text ) ;
2017-11-11 00:34:47 +00:00
if ( LocalTopics . Count > 0 )
{
CopyBuffer ( & Annotation , & TopicDots ) ;
}
2017-08-29 20:35:28 +00:00
CopyStringToBuffer ( & Annotation , " </div> \n "
" </div> \n "
" </div> \n " ) ;
CopyBuffer ( & CollationBuffers - > Player , & Annotation ) ;
// NOTE(matt): Tree structure of "annotation local" buffer dependencies
// TopicDots
// Text
// AnnotationData
// AnnotationClass
// AnnotationHeader
// Annotation
2017-10-18 21:07:29 +00:00
DeclaimBuffer ( & TopicDots ) ;
DeclaimBuffer ( & Text ) ;
DeclaimBuffer ( & AnnotationData ) ;
DeclaimBuffer ( & AnnotationClass ) ;
DeclaimBuffer ( & AnnotationHeader ) ;
DeclaimBuffer ( & Annotation ) ;
2017-08-29 20:35:28 +00:00
}
2017-11-11 00:34:47 +00:00
CopyStringToBuffer ( & CollationBuffers - > Search , " --- \n " ) ;
2017-08-29 20:35:28 +00:00
2017-05-21 06:35:16 +00:00
# if DEBUG
2017-08-29 20:35:28 +00:00
printf ( " \n \n --- End of Annotations Loop --- \n \n \n \n " ) ;
2017-05-21 06:35:16 +00:00
# endif
2017-08-29 20:35:28 +00:00
if ( HasQuoteMenu )
{
CopyStringToBuffer ( & QuoteMenu ,
" </div> \n "
" </div> \n " ) ;
CopyBuffer ( & CollationBuffers - > Menus , & QuoteMenu ) ;
}
2017-06-25 18:22:54 +00:00
2017-08-29 20:35:28 +00:00
if ( HasReferenceMenu )
{
for ( int i = 0 ; i < UniqueRefs ; + + i )
2017-03-29 03:38:12 +00:00
{
2017-08-29 20:35:28 +00:00
CopyStringToBuffer ( & ReferenceMenu ,
" <a data-id= \" %s \" href= \" %s \" target= \" _blank \" class= \" ref \" > \n "
" <span class= \" ref_content \" > \n " ,
ReferencesArray [ i ] . ID ,
ReferencesArray [ i ] . URL ) ;
if ( * ReferencesArray [ i ] . Source )
2017-03-30 02:57:04 +00:00
{
2017-06-25 18:05:58 +00:00
CopyStringToBuffer ( & ReferenceMenu ,
2017-08-29 20:35:28 +00:00
" <div class= \" source \" > " ) ;
CopyStringToBufferHTMLSafe ( & ReferenceMenu , ReferencesArray [ i ] . Source ) ;
CopyStringToBuffer ( & ReferenceMenu , " </div> \n "
" <div class= \" ref_title \" > " ) ;
CopyStringToBufferHTMLSafe ( & ReferenceMenu , ReferencesArray [ i ] . RefTitle ) ;
CopyStringToBuffer ( & ReferenceMenu , " </div> \n " ) ;
}
else
{
2017-06-25 18:05:58 +00:00
CopyStringToBuffer ( & ReferenceMenu ,
2017-08-29 20:35:28 +00:00
" <div class= \" ref_title \" > " ) ;
CopyStringToBufferHTMLSafe ( & ReferenceMenu , ReferencesArray [ i ] . RefTitle ) ;
CopyStringToBuffer ( & ReferenceMenu , " </div> \n " ) ;
}
CopyStringToBuffer ( & ReferenceMenu ,
" </span> \n " ) ;
2017-06-25 18:22:54 +00:00
2017-08-29 20:35:28 +00:00
for ( int j = 0 ; j < ReferencesArray [ i ] . IdentifierCount ; )
{
CopyStringToBuffer ( & ReferenceMenu ,
" <div class= \" ref_indices \" > \n " ) ;
for ( int k = 0 ; k < 3 & & j < ReferencesArray [ i ] . IdentifierCount ; + + k , + + j )
2017-06-25 18:05:58 +00:00
{
CopyStringToBuffer ( & ReferenceMenu ,
2017-08-29 20:35:28 +00:00
" <span data-timestamp= \" %d \" class= \" timecode \" ><span class= \" ref_index \" >[%d]</span><span class= \" time \" >%s</span></span> " ,
TimecodeToSeconds ( ReferencesArray [ i ] . Identifier [ j ] . Timecode ) ,
ReferencesArray [ i ] . Identifier [ j ] . Identifier ,
ReferencesArray [ i ] . Identifier [ j ] . Timecode ) ;
2017-06-25 18:05:58 +00:00
}
2017-08-29 20:35:28 +00:00
CopyStringToBuffer ( & ReferenceMenu , " \n "
" </div> \n " ) ;
2017-03-30 02:57:04 +00:00
}
2017-06-25 18:22:54 +00:00
2017-03-29 03:38:12 +00:00
CopyStringToBuffer ( & ReferenceMenu ,
2017-08-29 20:35:28 +00:00
" </a> \n " ) ;
}
CopyStringToBuffer ( & ReferenceMenu ,
" </div> \n "
" </div> \n " ) ;
CopyBuffer ( & CollationBuffers - > Menus , & ReferenceMenu ) ;
}
if ( HasFilterMenu )
{
2017-11-11 00:34:47 +00:00
CopyStringToBuffer ( & FilterState , " <script> \n "
" var filterInitState = { \n " ) ;
for ( int i = 0 ; i < Topics . Count ; + + i )
{
CopyStringToBuffer ( & FilterState , " \" %s \" : \t { \" type \" : \" %s \" , \t \" off \" : false }, \n " ,
Topics . Category [ i ] . Marker , " topic " ) ;
}
for ( int i = 0 ; i < Media . Count ; + + i )
{
if ( ! StringsDiffer ( Media . Category [ i ] . Marker , " afk " ) ) // TODO(matt): Make this configurable?
{
CopyStringToBuffer ( & FilterState , " \" %s \" : \t { \" type \" : \" %s \" , \t \" off \" : true }, \n " ,
Media . Category [ i ] . Marker , " medium " ) ;
}
else
{
CopyStringToBuffer ( & FilterState , " \" %s \" : \t { \" type \" : \" %s \" , \t \" off \" : false }, \n " ,
Media . Category [ i ] . Marker , " medium " ) ;
}
}
2017-08-29 20:35:28 +00:00
CopyStringToBuffer ( & FilterState ,
2017-11-11 00:34:47 +00:00
" }; \n "
" \n "
2017-08-29 20:35:28 +00:00
" var filterState = { \n " ) ;
for ( int i = 0 ; i < Topics . Count ; + + i )
{
CopyStringToBuffer ( & FilterState , " \" %s \" : \t { \" type \" : \" %s \" , \t \" off \" : false }, \n " ,
Topics . Category [ i ] . Marker , " topic " ) ;
2017-03-29 03:38:12 +00:00
}
2017-08-29 20:35:28 +00:00
for ( int i = 0 ; i < Media . Count ; + + i )
2017-05-21 06:35:16 +00:00
{
2017-11-11 00:34:47 +00:00
if ( ! StringsDiffer ( Media . Category [ i ] . Marker , " afk " ) ) // TODO(matt): Make this configurable?
{
CopyStringToBuffer ( & FilterState , " \" %s \" : \t { \" type \" : \" %s \" , \t \" off \" : true }, \n " ,
Media . Category [ i ] . Marker , " medium " ) ;
}
else
{
CopyStringToBuffer ( & FilterState , " \" %s \" : \t { \" type \" : \" %s \" , \t \" off \" : false }, \n " ,
Media . Category [ i ] . Marker , " medium " ) ;
}
2017-08-29 20:35:28 +00:00
}
2017-11-11 00:34:47 +00:00
2017-08-29 20:35:28 +00:00
CopyStringToBuffer ( & FilterState ,
2017-11-11 00:34:47 +00:00
" }; \n "
" </script> \n " ) ;
2017-06-25 18:22:54 +00:00
2017-12-07 21:07:36 +00:00
buffer URLPrefix ;
2017-12-12 23:24:10 +00:00
ClaimBuffer ( & URLPrefix , " URLPrefix " , 1024 ) ;
2017-12-18 00:15:03 +00:00
ConstructURLPrefix ( & URLPrefix , INCLUDE_Images , PAGE_PLAYER ) ;
2017-11-11 00:34:47 +00:00
CopyStringToBuffer ( & FilterMenu ,
" <div class= \" menu filter \" > \n "
" <span><img src= \" %scinera_icon_filter.png \" ></span> \n "
" <div class= \" filter_container \" > \n "
" <div class= \" filter_mode inclusive \" >Filter mode: </div> \n "
" <div class= \" filters \" > \n " ,
2017-12-07 21:07:36 +00:00
URLPrefix . Location ) ;
DeclaimBuffer ( & URLPrefix ) ;
2017-11-11 00:34:47 +00:00
2017-08-29 20:35:28 +00:00
if ( Topics . Count > 0 )
{
CopyStringToBuffer ( & FilterMenu ,
" <div class= \" filter_topics \" > \n "
" <div class= \" filter_title \" >Topics</div> \n " ) ;
for ( int i = 0 ; i < Topics . Count ; + + i )
2017-08-19 22:35:07 +00:00
{
CopyStringToBuffer ( & FilterTopics ,
2017-08-29 20:35:28 +00:00
" <div class= \" filter_content %s \" > \n "
" <span class= \" icon category %s \" ></span><span class= \" text \" >%s</span> \n "
" </div> \n " ,
Topics . Category [ i ] . Marker ,
Topics . Category [ i ] . Marker ,
Topics . Category [ i ] . Marker ) ;
2017-08-19 22:35:07 +00:00
}
2017-08-29 20:35:28 +00:00
CopyStringToBuffer ( & FilterTopics ,
" </div> \n " ) ;
CopyBuffer ( & FilterMenu , & FilterTopics ) ;
}
2017-06-25 18:22:54 +00:00
2017-08-29 20:35:28 +00:00
if ( Media . Count > 0 )
{
CopyStringToBuffer ( & FilterMedia ,
" <div class= \" filter_media \" > \n "
" <div class= \" filter_title \" >Media</div> \n " ) ;
for ( int i = 0 ; i < Media . Count ; + + i )
2017-08-19 22:35:07 +00:00
{
2017-08-29 20:35:28 +00:00
int j ;
for ( j = 0 ; j < ArrayCount ( CategoryMedium ) ; + + j )
2017-05-24 21:56:36 +00:00
{
2017-08-29 20:35:28 +00:00
if ( ! StringsDiffer ( Media . Category [ i ] . Marker , CategoryMedium [ j ] . Medium ) )
2017-05-24 21:56:36 +00:00
{
2017-08-29 20:35:28 +00:00
break ;
2017-05-24 21:56:36 +00:00
}
2017-05-21 06:35:16 +00:00
}
2017-08-29 20:35:28 +00:00
2017-11-11 00:34:47 +00:00
if ( ! StringsDiffer ( Media . Category [ i ] . Marker , " afk " ) ) // TODO(matt): Initially hidden config
{
CopyStringToBuffer ( & FilterMedia ,
" <div class= \" filter_content %s off \" > \n "
" <span class= \" icon \" >%s</span><span class= \" text \" >%s</span> \n "
" </div> \n " ,
Media . Category [ i ] . Marker ,
CategoryMedium [ j ] . Icon ,
CategoryMedium [ j ] . WrittenName
) ;
}
else
{
CopyStringToBuffer ( & FilterMedia ,
" <div class= \" filter_content %s \" > \n "
" <span class= \" icon \" >%s</span><span class= \" text \" >%s</span> \n "
" </div> \n " ,
Media . Category [ i ] . Marker ,
CategoryMedium [ j ] . Icon ,
CategoryMedium [ j ] . WrittenName
) ;
}
2017-05-21 06:35:16 +00:00
}
2017-08-29 20:35:28 +00:00
CopyStringToBuffer ( & FilterMedia ,
" </div> \n " ) ;
CopyBuffer ( & FilterMenu , & FilterMedia ) ;
}
2017-06-25 18:22:54 +00:00
2017-08-29 20:35:28 +00:00
CopyStringToBuffer ( & FilterMenu ,
" </div> \n "
" </div> \n "
" </div> \n " ) ;
2017-08-19 22:35:07 +00:00
2017-08-29 20:35:28 +00:00
CopyBuffer ( & CollationBuffers - > Menus , & FilterMenu ) ;
2017-06-25 18:22:54 +00:00
2017-08-29 20:35:28 +00:00
}
2017-06-25 18:22:54 +00:00
2017-08-29 20:35:28 +00:00
if ( HasCreditsMenu )
{
CopyBuffer ( & CollationBuffers - > Menus , & CreditsMenu ) ;
}
2017-06-25 18:22:54 +00:00
2017-08-29 20:35:28 +00:00
CopyStringToBuffer ( & CollationBuffers - > Menus ,
" <div class= \" help \" > \n "
" <span>?</span> \n "
" <div class= \" help_container \" > \n "
" <span class= \" help_key \" >?</span><h1>Keyboard Navigation</h1> \n "
" \n "
" <h2>Global Keys</h2> \n "
" <span class= \" help_key \" >W</span>, <span class= \" help_key \" >A</span>, <span class= \" help_key \" >P</span> / <span class= \" help_key \" >S</span>, <span class= \" help_key \" >D</span>, <span class= \" help_key \" >N</span> <span class= \" help_text \" >Jump to previous / next marker</span><br> \n " ) ;
if ( HasFilterMenu )
{
CopyStringToBuffer ( & CollationBuffers - > Menus ,
" <span class= \" help_key \" >z</span> <span class= \" help_text \" >Toggle filter mode</span> <span class= \" help_key \" >V</span> <span class= \" help_text \" >Revert filter to original state</span> \n " ) ;
}
else
{
CopyStringToBuffer ( & CollationBuffers - > Menus ,
" <span class= \" help_key unavailable \" >z</span> <span class= \" help_text unavailable \" >Toggle filter mode</span> <span class= \" help_key unavailable \" >V</span> <span class= \" help_text unavailable \" >Revert filter to original state</span> \n " ) ;
}
2017-06-25 18:22:54 +00:00
2017-08-29 20:35:28 +00:00
CopyStringToBuffer ( & CollationBuffers - > Menus ,
" \n "
" <h2>Menu toggling</h2> \n " ) ;
2017-06-25 18:22:54 +00:00
2017-08-29 20:35:28 +00:00
if ( HasQuoteMenu )
{
CopyStringToBuffer ( & CollationBuffers - > Menus ,
" <span class= \" help_key \" >q</span> <span class= \" help_text \" >Quotes</span> \n " ) ;
}
else
{
CopyStringToBuffer ( & CollationBuffers - > Menus ,
" <span class= \" help_key unavailable \" >q</span> <span class= \" help_text unavailable \" >Quotes</span> \n " ) ;
}
if ( HasReferenceMenu )
{
CopyStringToBuffer ( & CollationBuffers - > Menus ,
" <span class= \" help_key \" >r</span> <span class= \" help_text \" >References</span> \n " ) ;
}
else
{
CopyStringToBuffer ( & CollationBuffers - > Menus ,
" <span class= \" help_key unavailable \" >r</span> <span class= \" help_text unavailable \" >References</span> \n " ) ;
}
if ( HasFilterMenu )
{
CopyStringToBuffer ( & CollationBuffers - > Menus ,
" <span class= \" help_key \" >f</span> <span class= \" help_text \" >Filter</span> \n " ) ;
}
else
{
CopyStringToBuffer ( & CollationBuffers - > Menus ,
" <span class= \" help_key unavailable \" >f</span> <span class= \" help_text unavailable \" >Filter</span> \n " ) ;
}
2017-06-25 18:22:54 +00:00
2017-08-29 20:35:28 +00:00
if ( HasCreditsMenu )
{
CopyStringToBuffer ( & CollationBuffers - > Menus ,
" <span class= \" help_key \" >c</span> <span class= \" help_text \" >Credits</span> \n " ) ;
}
else
{
CopyStringToBuffer ( & CollationBuffers - > Menus ,
" <span class= \" help_key unavailable \" >c</span> <span class= \" help_text unavailable \" >Credits</span> \n " ) ;
}
CopyStringToBuffer ( & CollationBuffers - > Menus ,
" \n "
" <h2>Movement</h2> \n "
" <div class= \" help_paragraph \" > \n "
" <div class= \" key_block \" > \n "
" <div class= \" key_column \" style= \" flex-grow: 1 \" > \n "
" <span class= \" help_key \" >a</span> \n "
" </div> \n "
" <div class= \" key_column \" style= \" flex-grow: 2 \" > \n "
" <span class= \" help_key \" >w</span><br> \n "
" <span class= \" help_key \" >s</span> \n "
" </div> \n "
" <div class= \" key_column \" style= \" flex-grow: 1 \" > \n "
" <span class= \" help_key \" >d</span> \n "
" </div> \n "
" </div> \n "
" <div class= \" key_block \" > \n "
" <span class= \" help_key \" >h</span> \n "
" <span class= \" help_key \" >j</span> \n "
" <span class= \" help_key \" >k</span> \n "
" <span class= \" help_key \" >l</span> \n "
" </div> \n "
" <div class= \" key_block \" > \n "
" <div style= \" flex-grow: 1 \" > \n "
" <span class= \" help_key \" >←</span> \n "
" </div> \n "
" <div style= \" flex-grow: 2 \" > \n "
" <span class= \" help_key \" >↑</span><br> \n "
" <span class= \" help_key \" >↓</span> \n "
" </div> \n "
" <div style= \" flex-grow: 1 \" > \n "
" <span class= \" help_key \" >→</span> \n "
" </div> \n "
" </div> \n "
" </div> \n "
" <br> \n " ) ;
if ( HasQuoteMenu )
{
CopyStringToBuffer ( & CollationBuffers - > Menus ,
" <h2>Quotes " ) ;
2017-06-03 01:32:18 +00:00
if ( HasReferenceMenu )
{
2017-08-29 20:35:28 +00:00
CopyStringToBuffer ( & CollationBuffers - > Menus , " and References Menus</h2> \n " ) ;
2017-06-03 01:32:18 +00:00
}
else
{
2017-08-29 20:35:28 +00:00
CopyStringToBuffer ( & CollationBuffers - > Menus , " <span class= \" unavailable \" >and References</span> Menus</h2> \n " ) ;
2017-06-03 01:32:18 +00:00
}
2017-08-29 20:35:28 +00:00
}
else
{
CopyStringToBuffer ( & CollationBuffers - > Menus ,
" <h2><span class= \" unavailable \" >Quotes " ) ;
if ( HasReferenceMenu )
2017-06-03 01:32:18 +00:00
{
2017-08-29 20:35:28 +00:00
CopyStringToBuffer ( & CollationBuffers - > Menus , " and</span> References Menus</h2> \n " ) ;
2017-06-03 01:32:18 +00:00
}
else
{
2017-08-29 20:35:28 +00:00
CopyStringToBuffer ( & CollationBuffers - > Menus , " and References Menus</span></h2> \n " ) ;
2017-06-03 01:32:18 +00:00
}
2017-08-29 20:35:28 +00:00
}
if ( HasQuoteMenu | | HasReferenceMenu )
{
CopyStringToBuffer ( & CollationBuffers - > Menus ,
" <span style= \" width: auto \" class= \" help_key \" >Enter</span> <span class= \" help_text \" >Jump to timecode</span><br> \n " ) ;
}
else
{
CopyStringToBuffer ( & CollationBuffers - > Menus ,
" <span style= \" width: auto \" class= \" help_key unavailable \" >Enter</span> <span class= \" help_text unavailable \" >Jump to timecode</span><br> \n " ) ;
}
CopyStringToBuffer ( & CollationBuffers - > Menus , " \n " ) ;
2017-06-25 18:22:54 +00:00
2017-08-29 20:35:28 +00:00
if ( HasQuoteMenu )
{
CopyStringToBuffer ( & CollationBuffers - > Menus ,
" <h2>Quotes " ) ;
if ( HasReferenceMenu )
2017-06-03 01:32:18 +00:00
{
2017-08-29 20:35:28 +00:00
CopyStringToBuffer ( & CollationBuffers - > Menus , " , References " ) ;
if ( HasCreditsMenu )
{
CopyStringToBuffer ( & CollationBuffers - > Menus , " and Credits Menus</h2> " ) ;
}
else
{
CopyStringToBuffer ( & CollationBuffers - > Menus , " <span class= \" unavailable \" >and Credits</span> Menus</h2> " ) ;
}
2017-06-03 01:32:18 +00:00
}
else
{
2017-08-29 20:35:28 +00:00
CopyStringToBuffer ( & CollationBuffers - > Menus , " <span class= \" unavailable \" >, References " ) ;
if ( HasCreditsMenu )
{
CopyStringToBuffer ( & CollationBuffers - > Menus , " </span>and Credits Menus</h2> " ) ;
}
else
{
CopyStringToBuffer ( & CollationBuffers - > Menus , " and Credits</span> Menus</h2> " ) ;
}
2017-06-03 01:32:18 +00:00
}
2017-08-29 20:35:28 +00:00
}
else
{
CopyStringToBuffer ( & CollationBuffers - > Menus ,
" <h2><span class= \" unavailable \" >Quotes " ) ;
if ( HasReferenceMenu )
2017-06-03 01:32:18 +00:00
{
2017-08-29 20:35:28 +00:00
CopyStringToBuffer ( & CollationBuffers - > Menus , " , </span>References " ) ;
if ( HasCreditsMenu )
2017-06-03 01:32:18 +00:00
{
2017-08-29 20:35:28 +00:00
CopyStringToBuffer ( & CollationBuffers - > Menus , " and Credits Menus</h2> " ) ;
2017-06-03 01:32:18 +00:00
}
else
{
2017-08-29 20:35:28 +00:00
CopyStringToBuffer ( & CollationBuffers - > Menus , " <span class= \" unavailable \" >and Credits</span> Menus</h2> " ) ;
2017-06-03 01:32:18 +00:00
}
}
else
{
2017-08-29 20:35:28 +00:00
CopyStringToBuffer ( & CollationBuffers - > Menus , " , References " ) ;
if ( HasCreditsMenu )
2017-06-03 01:32:18 +00:00
{
2017-08-29 20:35:28 +00:00
CopyStringToBuffer ( & CollationBuffers - > Menus , " </span>and Credits Menus</h2> " ) ;
2017-06-03 01:32:18 +00:00
}
else
{
2017-08-29 20:35:28 +00:00
CopyStringToBuffer ( & CollationBuffers - > Menus , " and Credits Menus</h2></span> " ) ;
2017-06-03 01:32:18 +00:00
}
}
2017-08-29 20:35:28 +00:00
}
CopyStringToBuffer ( & CollationBuffers - > Menus , " \n " ) ;
if ( HasQuoteMenu | | HasReferenceMenu | | HasCreditsMenu )
{
CopyStringToBuffer ( & CollationBuffers - > Menus ,
" <span class= \" help_key \" >o</span> <span class= \" help_text \" >Open URL (in new tab)</span> \n " ) ;
}
else
{
CopyStringToBuffer ( & CollationBuffers - > Menus ,
" <span class= \" help_key unavailable \" >o</span> <span class= \" help_text unavailable \" >Open URL (in new tab)</span> \n " ) ;
}
CopyStringToBuffer ( & CollationBuffers - > Menus ,
" \n " ) ;
if ( HasFilterMenu )
{
CopyStringToBuffer ( & CollationBuffers - > Menus ,
" <h2>Filter Menu</h2> \n "
" <span class= \" help_key \" >x</span>, <span style= \" width: auto \" class= \" help_key \" >Space</span> <span class= \" help_text \" >Toggle category and focus next</span><br> \n "
" <span class= \" help_key \" >X</span>, <span style= \" width: auto; margin-right: 0px \" class= \" help_key \" >Shift</span><span style= \" width: auto \" class= \" help_key \" >Space</span> <span class= \" help_text \" >Toggle category and focus previous</span><br> \n "
" <span class= \" help_key \" >v</span> <span class= \" help_text \" >Invert topics / media as per focus</span> \n " ) ;
}
else
{
CopyStringToBuffer ( & CollationBuffers - > Menus ,
" <h2><span class= \" unavailable \" >Filter Menu</span></h2> \n "
" <span class= \" help_key unavailable \" >x</span>, <span style= \" width: auto \" class= \" help_key unavailable \" >Space</span> <span class= \" help_text unavailable \" >Toggle category and focus next</span><br> \n "
" <span class= \" help_key unavailable \" >X</span>, <span style= \" width: auto; margin-right: 0px \" class= \" help_key unavailable \" >Shift</span><span style= \" width: auto \" class= \" help_key unavailable \" >Space</span> <span class= \" help_text unavailable \" >Toggle category and focus previous</span><br> \n "
" <span class= \" help_key unavailable \" >v</span> <span class= \" help_text unavailable \" >Invert topics / media as per focus</span> \n " ) ;
}
CopyStringToBuffer ( & CollationBuffers - > Menus , " \n " ) ;
if ( HasCreditsMenu )
{
CopyStringToBuffer ( & CollationBuffers - > Menus ,
" <h2>Credits Menu</h2> \n "
" <span style= \" width: auto \" class= \" help_key \" >Enter</span> <span class= \" help_text \" >Open URL (in new tab)</span><br> \n " ) ;
}
else
{
CopyStringToBuffer ( & CollationBuffers - > Menus ,
" <h2><span class= \" unavailable \" >Credits Menu</span></h2> \n "
" <span style= \" width: auto \" class= \" help_key unavailable \" >Enter</span> <span class= \" help_text unavailable \" >Open URL (in new tab)</span><br> \n " ) ;
}
CopyStringToBuffer ( & CollationBuffers - > Menus ,
" </div> \n "
" </div> \n "
" </div> " ) ;
CopyStringToBuffer ( & CollationBuffers - > Player ,
" </div> \n "
" </div> " ) ;
// TODO(matt): Maybe do something about indentation levels
2017-12-12 23:24:10 +00:00
buffer URLIndex ;
ClaimBuffer ( & URLIndex , " URLPlayer " , 2048 ) ;
ConstructIndexURL ( & URLIndex ) ;
CopyString ( CollationBuffers - > URLIndex , URLIndex . Location ) ;
DeclaimBuffer ( & URLIndex ) ;
2017-12-07 21:07:36 +00:00
buffer URLPrefix ;
2017-12-12 23:24:10 +00:00
ClaimBuffer ( & URLPrefix , " URLPrefix " , 1024 ) ;
2017-12-07 21:07:36 +00:00
ConstructURLPrefix ( & URLPrefix , INCLUDE_CSS , PAGE_INDEX ) ;
2017-11-11 00:34:47 +00:00
CopyStringToBuffer ( & CollationBuffers - > IncludesIndex ,
" <link rel= \" stylesheet \" type= \" text/css \" href= \" %scinera.css \" > \n "
" <link rel= \" stylesheet \" type= \" text/css \" href= \" %scinera__%s.css \" > \n "
" <link rel= \" stylesheet \" type= \" text/css \" href= \" %scinera_topics.css \" > \n "
" <style>body { overflow-y: scroll; }</style> \n "
2017-08-29 20:35:28 +00:00
" \n "
" <meta charset= \" UTF-8 \" > \n "
2017-11-11 00:34:47 +00:00
" <meta name= \" generator \" content= \" Cinera %d.%d.%d \" > \n " ,
2017-12-07 21:07:36 +00:00
URLPrefix . Location ,
URLPrefix . Location , StringsDiffer ( Config . Theme , " " ) ? Config . Theme : HMML . metadata . project ,
URLPrefix . Location ,
2017-11-11 00:34:47 +00:00
CINERA_APP_VERSION . Major ,
CINERA_APP_VERSION . Minor ,
CINERA_APP_VERSION . Patch ) ;
2017-12-07 21:07:36 +00:00
ConstructURLPrefix ( & URLPrefix , INCLUDE_CSS , PAGE_PLAYER ) ;
2017-11-11 00:34:47 +00:00
CopyStringToBuffer ( & CollationBuffers - > IncludesPlayer ,
" <link rel= \" stylesheet \" type= \" text/css \" href= \" %scinera.css \" > \n "
" <link rel= \" stylesheet \" type= \" text/css \" href= \" %scinera__%s.css \" > \n "
" <link rel= \" stylesheet \" type= \" text/css \" href= \" %scinera_topics.css \" > \n "
" \n "
" <meta charset= \" UTF-8 \" > \n "
" <meta name= \" generator \" content= \" Cinera %d.%d.%d \" > \n " ,
2017-12-07 21:07:36 +00:00
URLPrefix . Location ,
URLPrefix . Location , StringsDiffer ( Config . Theme , " " ) ? Config . Theme : HMML . metadata . project ,
URLPrefix . Location ,
2017-11-11 00:34:47 +00:00
CINERA_APP_VERSION . Major ,
CINERA_APP_VERSION . Minor ,
CINERA_APP_VERSION . Patch ) ;
2017-08-29 20:35:28 +00:00
if ( Topics . Count | | Media . Count )
{
CopyStringToBuffer ( & CollationBuffers - > IncludesPlayer ,
" <meta name= \" keywords \" content= \" " ) ;
2017-06-25 18:22:54 +00:00
2017-08-29 20:35:28 +00:00
if ( Topics . Count > 0 )
2017-06-03 01:32:18 +00:00
{
2017-08-29 20:35:28 +00:00
for ( int i = 0 ; i < Topics . Count ; + + i )
{
CopyStringToBuffer ( & CollationBuffers - > IncludesPlayer , " %s, " , Topics . Category [ i ] . Marker ) ;
}
2017-06-03 01:32:18 +00:00
}
2017-08-29 20:35:28 +00:00
if ( Media . Count > 0 )
2017-06-03 01:32:18 +00:00
{
2017-08-29 20:35:28 +00:00
for ( int i = 0 ; i < Media . Count ; + + i )
{
CopyStringToBuffer ( & CollationBuffers - > IncludesPlayer , " %s, " , Media . Category [ i ] . WrittenText ) ;
}
2017-06-03 01:32:18 +00:00
}
2017-06-25 18:22:54 +00:00
2017-08-29 20:35:28 +00:00
CollationBuffers - > IncludesPlayer . Ptr - = 2 ;
CopyStringToBuffer ( & CollationBuffers - > IncludesPlayer , " \" > \n \n " ) ;
}
2017-12-07 21:07:36 +00:00
ConstructURLPrefix ( & URLPrefix , INCLUDE_JS , PAGE_PLAYER ) ;
2017-11-11 00:34:47 +00:00
CopyStringToBuffer ( & CollationBuffers - > IncludesPlayer ,
" <script type= \" text/javascript \" src= \" %scinera_player_pre.js \" ></script> \n " ,
2017-12-07 21:07:36 +00:00
URLPrefix . Location ) ;
2017-08-29 20:35:28 +00:00
2017-11-11 00:34:47 +00:00
CopyStringToBuffer ( & CollationBuffers - > ScriptPlayer ,
" <script type= \" text/javascript \" src= \" %scinera_player_post.js \" ></script> \n " ,
2017-12-07 21:07:36 +00:00
URLPrefix . Location ) ;
DeclaimBuffer ( & URLPrefix ) ;
2017-08-29 20:35:28 +00:00
if ( HasFilterMenu )
{
2017-11-11 00:34:47 +00:00
CopyBuffer ( & CollationBuffers - > ScriptPlayer , & FilterState ) ;
2017-08-29 20:35:28 +00:00
}
// NOTE(matt): Tree structure of "global" buffer dependencies
// FilterState
// CreditsMenu
// FilterMedia
// FilterTopics
// FilterMenu
// ReferenceMenu
// QuoteMenu
Cleanup :
2017-10-18 21:07:29 +00:00
DeclaimBuffer ( & FilterState ) ;
2017-08-29 20:35:28 +00:00
2017-10-18 21:07:29 +00:00
DeclaimBuffer ( & CreditsMenu ) ;
DeclaimBuffer ( & FilterMedia ) ;
DeclaimBuffer ( & FilterTopics ) ;
DeclaimBuffer ( & FilterMenu ) ;
DeclaimBuffer ( & ReferenceMenu ) ;
DeclaimBuffer ( & QuoteMenu ) ;
2017-08-29 20:35:28 +00:00
}
else
{
2017-10-18 21:07:29 +00:00
LogError ( LOG_ERROR , " %s:%d: %s " , Filename , HMML . error . line , HMML . error . message ) ;
2017-08-29 20:35:28 +00:00
fprintf ( stderr , " %s:%d: %s \n " , Filename , HMML . error . line , HMML . error . message ) ;
2017-09-07 21:41:08 +00:00
hmml_free ( & HMML ) ;
return RC_ERROR_HMML ;
2017-08-29 20:35:28 +00:00
}
hmml_free ( & HMML ) ;
2017-09-07 21:41:08 +00:00
return RC_SUCCESS ;
2017-08-29 20:35:28 +00:00
}
2017-09-07 21:41:08 +00:00
int
2017-10-18 21:07:29 +00:00
BuffersToHTML ( buffers * CollationBuffers , template * Template , char * OutputPath , int PageType )
2017-08-29 20:35:28 +00:00
{
# if DEBUG
2017-09-07 21:41:08 +00:00
printf ( " \n \n --- Buffer Collation --- \n "
2017-10-18 21:07:29 +00:00
" %s \n \n \n " , OutputPath ? OutputPath : Config - > OutLocation ) ;
# endif
# if DEBUG_MEM
FILE * MemLog = fopen ( " /home/matt/cinera_mem " , " a+ " ) ;
fprintf ( MemLog , " \n Entered BuffersToHTML(%s) \n " , OutputPath ? OutputPath : Config - > OutLocation ) ;
fclose ( MemLog ) ;
2017-08-29 20:35:28 +00:00
# endif
2017-12-18 15:10:02 +00:00
if ( Config . Mode & MODE_INTEGRATE )
2017-08-29 20:35:28 +00:00
{
2017-10-18 21:07:29 +00:00
if ( Template - > Metadata . Validity & PageType )
2017-08-29 20:35:28 +00:00
{
2017-09-07 21:41:08 +00:00
buffer Output ;
2017-10-18 21:07:29 +00:00
Output . Size = Template - > Buffer . Size + ( Kilobytes ( 512 ) ) ;
2017-09-07 21:41:08 +00:00
Output . ID = " Output " ;
if ( ! ( Output . Location = malloc ( Output . Size ) ) )
{
2017-10-18 21:07:29 +00:00
LogError ( LOG_ERROR , " BuffersToHTML(): %s " ,
2017-09-07 21:41:08 +00:00
strerror ( errno ) ) ;
return RC_ERROR_MEMORY ;
}
2017-10-18 21:07:29 +00:00
# if DEBUG_MEM
2017-11-11 00:34:47 +00:00
MemLog = fopen ( " /home/matt/cinera_mem " , " a+ " ) ;
fprintf ( MemLog , " Allocated Output (%d) \n " , Output . Size ) ;
fclose ( MemLog ) ;
printf ( " Allocated Output (%d) \n " , Output . Size ) ;
2017-10-18 21:07:29 +00:00
# endif
2017-09-07 21:41:08 +00:00
Output . Ptr = Output . Location ;
2017-08-29 20:35:28 +00:00
2017-10-18 21:07:29 +00:00
Template - > Buffer . Ptr = Template - > Buffer . Location ;
for ( int i = 0 ; i < Template - > Metadata . TagCount ; + + i )
2017-06-03 01:32:18 +00:00
{
2017-09-07 21:41:08 +00:00
int j = 0 ;
2017-10-18 21:07:29 +00:00
while ( Template - > Metadata . Tag [ i ] . Offset > j )
2017-06-03 01:32:18 +00:00
{
2017-10-18 21:07:29 +00:00
* Output . Ptr + + = * Template - > Buffer . Ptr + + ;
2017-09-07 21:41:08 +00:00
+ + j ;
}
2017-08-29 20:35:28 +00:00
2017-10-18 21:07:29 +00:00
switch ( Template - > Metadata . Tag [ i ] . TagCode )
2017-09-07 21:41:08 +00:00
{
2017-12-12 23:24:10 +00:00
case TAG_PROJECT :
CopyStringToBuffer ( & Output , CollationBuffers - > ProjectName ) ;
break ;
2017-09-07 21:41:08 +00:00
case TAG_TITLE :
2017-10-18 21:07:29 +00:00
CopyStringToBuffer ( & Output , CollationBuffers - > Title ) ;
2017-09-07 21:41:08 +00:00
break ;
2017-12-12 23:24:10 +00:00
case TAG_URL :
CopyStringToBuffer ( & Output , PageType = = PAGE_PLAYER ? CollationBuffers - > URLPlayer : CollationBuffers - > URLIndex ) ;
break ;
case TAG_VIDEO_ID :
CopyStringToBuffer ( & Output , CollationBuffers - > VideoID ) ;
2017-12-07 01:15:13 +00:00
break ;
2017-09-07 21:41:08 +00:00
case TAG_INDEX :
2017-10-18 21:07:29 +00:00
CopyBuffer ( & Output , & CollationBuffers - > Index ) ;
2017-09-07 21:41:08 +00:00
break ;
case TAG_INCLUDES :
2017-10-18 21:07:29 +00:00
CopyBuffer ( & Output , PageType = = PAGE_PLAYER ? & CollationBuffers - > IncludesPlayer : & CollationBuffers - > IncludesIndex ) ;
2017-08-29 20:35:28 +00:00
break ;
2017-09-07 21:41:08 +00:00
case TAG_MENUS :
2017-10-18 21:07:29 +00:00
CopyBuffer ( & Output , & CollationBuffers - > Menus ) ;
2017-09-07 21:41:08 +00:00
break ;
case TAG_PLAYER :
2017-10-18 21:07:29 +00:00
CopyBuffer ( & Output , & CollationBuffers - > Player ) ;
2017-08-29 20:35:28 +00:00
break ;
2017-09-07 21:41:08 +00:00
case TAG_SCRIPT :
2017-11-11 00:34:47 +00:00
CopyBuffer ( & Output , & CollationBuffers - > ScriptPlayer ) ;
2017-08-29 20:35:28 +00:00
break ;
2017-06-03 01:32:18 +00:00
}
2017-10-18 21:07:29 +00:00
DepartComment ( & Template - > Buffer ) ;
2017-06-03 01:32:18 +00:00
}
2017-10-18 21:07:29 +00:00
while ( Template - > Buffer . Ptr - Template - > Buffer . Location < Template - > Buffer . Size )
2017-06-03 01:32:18 +00:00
{
2017-10-18 21:07:29 +00:00
* Output . Ptr + + = * Template - > Buffer . Ptr + + ;
2017-06-03 01:32:18 +00:00
}
2017-08-29 20:35:28 +00:00
2017-09-07 21:41:08 +00:00
FILE * OutFile ;
if ( ! ( OutFile = fopen ( Config . Edition = = EDITION_PROJECT ? OutputPath : Config . OutIntegratedLocation , " w " ) ) )
{
2017-10-18 21:07:29 +00:00
LogError ( LOG_ERROR , " Unable to open output file %s: %s " , Config . Edition = = EDITION_PROJECT ? OutputPath : Config . OutIntegratedLocation , strerror ( errno ) ) ;
2017-09-07 21:41:08 +00:00
free ( Output . Location ) ;
2017-10-18 21:07:29 +00:00
# if DEBUG_MEM
2017-11-11 00:34:47 +00:00
MemLog = fopen ( " /home/matt/cinera_mem " , " a+ " ) ;
fprintf ( MemLog , " Freed Output \n " ) ;
fclose ( MemLog ) ;
printf ( " Freed Output \n " ) ;
2017-10-18 21:07:29 +00:00
# endif
2017-09-07 21:41:08 +00:00
return RC_ERROR_FILE ;
}
fwrite ( Output . Location , Output . Ptr - Output . Location , 1 , OutFile ) ;
fclose ( OutFile ) ;
free ( Output . Location ) ;
2017-10-18 21:07:29 +00:00
# if DEBUG_MEM
2017-11-11 00:34:47 +00:00
MemLog = fopen ( " /home/matt/cinera_mem " , " a+ " ) ;
fprintf ( MemLog , " Freed Output \n " ) ;
fclose ( MemLog ) ;
printf ( " Freed Output \n " ) ;
2017-10-18 21:07:29 +00:00
# endif
2017-09-07 21:41:08 +00:00
return RC_SUCCESS ;
}
else
2017-08-29 20:35:28 +00:00
{
2017-09-07 21:41:08 +00:00
return RC_INVALID_TEMPLATE ;
2017-08-29 20:35:28 +00:00
}
}
else
{
2017-09-07 21:41:08 +00:00
buffer Master ;
2017-10-18 21:07:29 +00:00
if ( ClaimBuffer ( & Master , " Master " , Kilobytes ( 512 ) ) = = RC_ARENA_FULL ) { return RC_ARENA_FULL ; } ;
2017-09-07 21:41:08 +00:00
2017-08-29 20:35:28 +00:00
CopyStringToBuffer ( & Master ,
" <html> \n "
" <head> \n " ) ;
2017-10-18 21:07:29 +00:00
CopyBuffer ( & Master , PageType = = PAGE_PLAYER ? & CollationBuffers - > IncludesPlayer : & CollationBuffers - > IncludesIndex ) ;
2017-09-07 21:41:08 +00:00
CopyStringToBuffer ( & Master , " \n " ) ;
2017-08-29 20:35:28 +00:00
CopyStringToBuffer ( & Master ,
" </head> \n "
" <body> \n " ) ;
2017-09-07 21:41:08 +00:00
if ( PageType = = PAGE_PLAYER )
{
2017-10-18 21:07:29 +00:00
CopyBuffer ( & Master , & CollationBuffers - > Menus ) ;
2017-09-07 21:41:08 +00:00
CopyStringToBuffer ( & Master , " \n " ) ;
2017-10-18 21:07:29 +00:00
CopyBuffer ( & Master , & CollationBuffers - > Player ) ;
2017-09-07 21:41:08 +00:00
CopyStringToBuffer ( & Master , " \n " ) ;
2017-11-11 00:34:47 +00:00
CopyBuffer ( & Master , & CollationBuffers - > ScriptPlayer ) ;
2017-09-07 21:41:08 +00:00
CopyStringToBuffer ( & Master , " \n " ) ;
}
else
{
2017-10-18 21:07:29 +00:00
CopyBuffer ( & Master , & CollationBuffers - > Index ) ;
2017-09-07 21:41:08 +00:00
}
2017-08-29 20:35:28 +00:00
CopyStringToBuffer ( & Master ,
" </body> \n "
" </html> \n " ) ;
FILE * OutFile ;
if ( ! ( OutFile = fopen ( Config . Edition = = EDITION_PROJECT ? OutputPath : Config . OutLocation , " w " ) ) )
{
2017-10-18 21:07:29 +00:00
LogError ( LOG_ERROR , " Unable to open output file %s: %s " , Config . Edition = = EDITION_PROJECT ? OutputPath : Config . OutLocation , strerror ( errno ) ) ;
DeclaimBuffer ( & Master ) ;
2017-09-07 21:41:08 +00:00
return RC_ERROR_FILE ;
2017-08-29 20:35:28 +00:00
}
fwrite ( Master . Location , Master . Ptr - Master . Location , 1 , OutFile ) ;
fclose ( OutFile ) ;
2017-10-18 21:07:29 +00:00
DeclaimBuffer ( & Master ) ;
2017-09-07 21:41:08 +00:00
return RC_SUCCESS ;
2017-08-29 20:35:28 +00:00
}
}
int
2017-10-18 21:07:29 +00:00
ReadFileIntoBuffer ( file_buffer * File , int BufferPadding )
2017-08-29 20:35:28 +00:00
{
2017-10-18 21:07:29 +00:00
if ( ! ( File - > Handle = fopen ( File - > Path , " r " ) ) )
2017-08-29 20:35:28 +00:00
{
2017-09-07 21:41:08 +00:00
return RC_ERROR_FILE ;
2017-08-29 20:35:28 +00:00
}
2017-10-18 21:07:29 +00:00
fseek ( File - > Handle , 0 , SEEK_END ) ;
File - > FileSize = ftell ( File - > Handle ) ;
File - > Buffer . Size = File - > FileSize + 1 + BufferPadding ; // NOTE(matt): +1 to accommodate a NULL terminator
fseek ( File - > Handle , 0 , SEEK_SET ) ;
2017-06-25 18:22:54 +00:00
2017-10-18 21:07:29 +00:00
// TODO(matt): Consider using the MemoryArena? Maybe have separate ReadFileIntoMemory() and ReadFileIntoArena()
if ( ! ( File - > Buffer . Location = malloc ( File - > Buffer . Size ) ) )
2017-08-29 20:35:28 +00:00
{
2017-10-18 21:07:29 +00:00
fclose ( File - > Handle ) ;
2017-09-07 21:41:08 +00:00
return RC_ERROR_MEMORY ;
2017-08-29 20:35:28 +00:00
}
2017-10-18 21:07:29 +00:00
File - > Buffer . Ptr = File - > Buffer . Location ;
2017-08-29 20:35:28 +00:00
2017-10-18 21:07:29 +00:00
fread ( File - > Buffer . Location , File - > FileSize , 1 , File - > Handle ) ;
File - > Buffer . Location [ File - > FileSize ] = ' \0 ' ;
fclose ( File - > Handle ) ;
return RC_SUCCESS ;
}
2017-06-25 18:22:54 +00:00
2017-10-18 21:07:29 +00:00
// NOTE(matt): Currently unused
int
WriteBufferToFile ( file_buffer * File , buffer * Buffer , int BytesToWrite , bool KeepFileHandleOpen )
{
if ( ! ( File - > Handle = fopen ( File - > Path , " w " ) ) )
2017-08-29 20:35:28 +00:00
{
2017-10-18 21:07:29 +00:00
return RC_ERROR_FILE ;
}
fwrite ( Buffer - > Location , BytesToWrite , 1 , File - > Handle ) ;
if ( ! KeepFileHandleOpen )
{
fclose ( File - > Handle ) ;
}
return RC_SUCCESS ;
}
2017-11-11 00:34:47 +00:00
enum
{
C_SEEK_FORWARDS ,
C_SEEK_BACKWARDS
} seek_directions ;
enum
{
C_SEEK_START , // First character of string
C_SEEK_BEFORE , // Character before first character
C_SEEK_END , // Last character of string
C_SEEK_AFTER // Character after last character
} seek_positions ;
int
SeekBufferForString ( buffer * Buffer , char * String ,
int Direction , /* seek_directions */
int Position /* seek_positions */ )
{
// TODO(matt): Optimise? Some means of analysing the String to increment
// the pointer in bigger strides
// Perhaps count up runs of consecutive chars and seek for the char with
// the longest run, in strides of that run-length
char * InitialLocation = Buffer - > Ptr ;
if ( Direction = = C_SEEK_FORWARDS )
{
while ( Buffer - > Ptr - Buffer - > Location < Buffer - > Size - StringLength ( String )
& & StringsDifferT ( String , Buffer - > Ptr , 0 ) )
{
+ + Buffer - > Ptr ;
}
}
else
{
while ( Buffer - > Ptr > Buffer - > Location
& & StringsDifferT ( String , Buffer - > Ptr , 0 ) )
{
- - Buffer - > Ptr ;
}
}
if ( StringsDifferT ( String , Buffer - > Ptr , 0 ) )
{
Buffer - > Ptr = InitialLocation ;
return RC_UNFOUND ;
}
switch ( Position )
{
case C_SEEK_START :
break ;
case C_SEEK_BEFORE :
if ( Buffer - > Ptr > Buffer - > Location )
{
- - Buffer - > Ptr ;
break ;
}
else
{
return RC_ERROR_SEEK ; // Ptr remains at string start
}
case C_SEEK_END :
Buffer - > Ptr + = StringLength ( String ) - 1 ;
break ;
case C_SEEK_AFTER :
if ( Buffer - > Size > = Buffer - > Ptr - Buffer - > Location + StringLength ( String ) )
{
Buffer - > Ptr + = StringLength ( String ) ;
break ;
}
else
{
return RC_ERROR_SEEK ; // Ptr remains at string start
// NOTE(matt): Should it, however, be left at the end of the string?
}
}
return RC_SUCCESS ;
}
// TODO(matt): Increment CINERA_DB_VERSION!
typedef struct
{
unsigned int DBVersion ; // NOTE(matt): Put this first to aid reliability
version AppVersion ;
version HMMLVersion ;
unsigned int EntryCount ;
2017-12-12 23:24:10 +00:00
char IndexLocation [ 32 ] ;
char PlayerLocation [ 32 ] ;
2017-11-11 00:34:47 +00:00
} index_header ;
typedef struct
{
int Size ;
char BaseFilename [ 32 ] ;
} index_metadata ;
typedef struct
{
file_buffer File ;
file_buffer Metadata ;
index_header Header ;
index_metadata Entry ;
} index ;
// TODO(matt): Increment CINERA_DB_VERSION!
2017-10-18 21:07:29 +00:00
int
InsertIntoIndex ( buffers * CollationBuffers , char * BaseFilename )
{
2017-11-11 00:34:47 +00:00
// NOTE(matt): The index will be stored in two files:
// 1) ProjectID.index
// 2) ProjectID.metadata
//
// .index is all of the stuff needed for the search
// .metadata is: int DBVersion
// version AppVersion
// version HMMLVersion
// int EntryCount
// for each Entry
// int Size from start of "name:" to end of "---\n")
// char BaseFilename[32] (0-padded)
//
index Index = { 0 } ;
Index . Metadata . Buffer . ID = " IndexMetadata " ;
CopyString ( Index . Metadata . Path , " %s/%s.metadata " , Config . BaseDir , Config . ProjectID ) ;
int IndexMetadataFileReadCode = ReadFileIntoBuffer ( & Index . Metadata , 0 ) ;
switch ( IndexMetadataFileReadCode )
2017-10-18 21:07:29 +00:00
{
case RC_ERROR_MEMORY :
return RC_ERROR_MEMORY ;
case RC_ERROR_FILE :
case RC_SUCCESS :
2017-08-29 20:35:28 +00:00
break ;
2017-10-18 21:07:29 +00:00
}
2017-12-07 01:15:13 +00:00
Index . File . Buffer . ID = " IndexFile " ;
2017-11-11 00:34:47 +00:00
CopyString ( Index . File . Path , " %s/%s.index " , Config . BaseDir , Config . ProjectID ) ;
int IndexFileReadCode = ReadFileIntoBuffer ( & Index . File , 0 ) ;
switch ( IndexFileReadCode )
{
case RC_ERROR_MEMORY :
return RC_ERROR_MEMORY ;
case RC_ERROR_FILE :
case RC_SUCCESS :
break ;
}
2017-10-18 21:07:29 +00:00
2017-11-11 00:34:47 +00:00
int MetadataInsertionOffset = - 1 ;
int IndexEntryInsertionStart = - 1 ;
int IndexEntryInsertionEnd = - 1 ;
Index . Header . EntryCount = 0 ;
char * IndexEntryStart ;
bool Found = FALSE ;
char InputFile [ StringLength ( BaseFilename ) + StringLength ( " .hmml " ) ] ;
2017-10-18 21:07:29 +00:00
CopyString ( InputFile , " %s.hmml " , BaseFilename ) ;
HMMLToBuffers ( CollationBuffers , InputFile ) ;
2017-11-11 00:34:47 +00:00
Index . Entry . Size = CollationBuffers - > Search . Ptr - CollationBuffers - > Search . Location ;
for ( int i = 0 ; i < ArrayCount ( Index . Entry . BaseFilename ) ; + + i )
2017-10-18 21:07:29 +00:00
{
2017-11-11 00:34:47 +00:00
Index . Entry . BaseFilename [ i ] = ' \0 ' ;
}
CopyString ( Index . Entry . BaseFilename , BaseFilename ) ;
2017-10-18 21:07:29 +00:00
2017-11-11 00:34:47 +00:00
int EntryIndex ;
if ( IndexMetadataFileReadCode = = RC_SUCCESS & & IndexFileReadCode = = RC_SUCCESS )
{
// TODO(matt): Index validation?
// Maybe at least if(!StringsDiffer(..., "name: \"");
// and check that we won't crash through the end of the file when skipping to the next entry
Index . Header = * ( index_header * ) Index . Metadata . Buffer . Ptr ;
Index . Metadata . Buffer . Ptr + = sizeof ( Index . Header ) ;
Index . File . Buffer . Ptr + = StringLength ( " --- \n " ) ;
IndexEntryStart = Index . File . Buffer . Ptr ;
for ( EntryIndex = 0 ; EntryIndex < Index . Header . EntryCount ; + + EntryIndex )
2017-09-07 21:41:08 +00:00
{
2017-11-11 00:34:47 +00:00
index_metadata This = * ( index_metadata * ) Index . Metadata . Buffer . Ptr ;
if ( ! StringsDiffer ( This . BaseFilename , BaseFilename ) )
2017-09-07 21:41:08 +00:00
{
2017-11-11 00:34:47 +00:00
// Reinsert
MetadataInsertionOffset = Index . Metadata . Buffer . Ptr - Index . Metadata . Buffer . Location ;
IndexEntryInsertionStart = IndexEntryStart - Index . File . Buffer . Location ;
IndexEntryInsertionEnd = IndexEntryInsertionStart + Index . Entry . Size ;
Found = TRUE ;
break ;
2017-09-07 21:41:08 +00:00
}
2017-11-11 00:34:47 +00:00
else if ( StringsDiffer ( This . BaseFilename , BaseFilename ) > 0 )
2017-09-07 21:41:08 +00:00
{
2017-11-11 00:34:47 +00:00
// Insert
MetadataInsertionOffset = Index . Metadata . Buffer . Ptr - Index . Metadata . Buffer . Location ;
IndexEntryInsertionStart = IndexEntryStart - Index . File . Buffer . Location ;
2017-10-18 21:07:29 +00:00
break ;
2017-09-07 21:41:08 +00:00
}
2017-10-18 21:07:29 +00:00
else
2017-09-07 21:41:08 +00:00
{
2017-11-11 00:34:47 +00:00
Index . Metadata . Buffer . Ptr + = sizeof ( Index . Entry ) ;
IndexEntryStart + = This . Size ;
Index . File . Buffer . Ptr = IndexEntryStart ;
2017-09-07 21:41:08 +00:00
}
2017-10-18 21:07:29 +00:00
}
}
2017-11-11 00:34:47 +00:00
else
2017-10-18 21:07:29 +00:00
{
2017-11-11 00:34:47 +00:00
// NOTE(matt): Initialising new index_header
Index . Header . DBVersion = CINERA_DB_VERSION ;
Index . Header . AppVersion = CINERA_APP_VERSION ;
Index . Header . HMMLVersion . Major = hmml_version . Major ;
Index . Header . HMMLVersion . Minor = hmml_version . Minor ;
Index . Header . HMMLVersion . Patch = hmml_version . Patch ;
2017-12-12 23:24:10 +00:00
CopyString ( Index . Header . IndexLocation , Config . IndexLocation ) ;
CopyString ( Index . Header . PlayerLocation , Config . PlayerLocation ) ;
2017-11-11 00:34:47 +00:00
DIR * OutputDirectoryHandle ;
if ( ! ( OutputDirectoryHandle = opendir ( Config . BaseDir ) ) )
2017-10-18 21:07:29 +00:00
{
2017-11-11 00:34:47 +00:00
if ( MakeDir ( Config . BaseDir ) = = RC_ERROR_DIRECTORY )
{
LogError ( LOG_ERROR , " Unable to create directory %s: %s " , Config . BaseDir , strerror ( errno ) ) ;
fprintf ( stderr , " Unable to create directory %s: %s \n " , Config . BaseDir , strerror ( errno ) ) ;
return RC_ERROR_DIRECTORY ;
} ;
2017-10-18 21:07:29 +00:00
}
2017-11-11 00:34:47 +00:00
closedir ( OutputDirectoryHandle ) ;
2017-10-18 21:07:29 +00:00
}
2017-09-07 21:41:08 +00:00
2017-11-11 00:34:47 +00:00
if ( ! ( Index . Metadata . Handle = fopen ( Index . Metadata . Path , " w " ) ) ) { FreeBuffer ( & Index . Metadata . Buffer ) ; return RC_ERROR_FILE ; }
if ( ! ( Index . File . Handle = fopen ( Index . File . Path , " w " ) ) ) { FreeBuffer ( & Index . File . Buffer ) ; return RC_ERROR_FILE ; }
2017-09-07 21:41:08 +00:00
2017-11-11 00:34:47 +00:00
if ( ! Found ) { + + Index . Header . EntryCount ; }
2017-09-07 21:41:08 +00:00
2017-11-11 00:34:47 +00:00
fwrite ( & Index . Header , sizeof ( Index . Header ) , 1 , Index . Metadata . Handle ) ;
if ( IndexMetadataFileReadCode = = RC_SUCCESS )
2017-10-18 21:07:29 +00:00
{
2017-11-11 00:34:47 +00:00
Index . Metadata . Buffer . Ptr = Index . Metadata . Buffer . Location + sizeof ( Index . Header ) ;
2017-10-18 21:07:29 +00:00
}
2017-11-11 00:34:47 +00:00
if ( Found )
2017-10-18 21:07:29 +00:00
{
2017-11-11 00:34:47 +00:00
// NOTE(matt): We hit this during the start-up sync and when copying in a .hmml file over an already existing one, but
// would need to fool about with the inotify event processing to get to this branch in the case that saving
// a file triggers an IN_DELETE followed by an IN_CLOSE_WRITE event
// Reinsert
fwrite ( Index . Metadata . Buffer . Ptr , MetadataInsertionOffset - sizeof ( Index . Header ) , 1 , Index . Metadata . Handle ) ;
fwrite ( & Index . Entry , sizeof ( Index . Entry ) , 1 , Index . Metadata . Handle ) ;
fwrite ( Index . Metadata . Buffer . Ptr - sizeof ( Index . Header ) + MetadataInsertionOffset + sizeof ( Index . Entry ) , Index . Metadata . FileSize - MetadataInsertionOffset - sizeof ( Index . Entry ) , 1 , Index . Metadata . Handle ) ;
fwrite ( Index . File . Buffer . Location , IndexEntryInsertionStart , 1 , Index . File . Handle ) ;
fwrite ( CollationBuffers - > Search . Location , Index . Entry . Size , 1 , Index . File . Handle ) ;
fwrite ( Index . File . Buffer . Location + IndexEntryInsertionEnd , Index . File . FileSize - IndexEntryInsertionEnd , 1 , Index . File . Handle ) ;
LogError ( LOG_NOTICE , " Reinserted %s - %s " , BaseFilename , CollationBuffers - > Title ) ;
fprintf ( stderr , " \ e[1;33mReinserted \ e[0m %s - %s \n " , BaseFilename , CollationBuffers - > Title ) ;
}
else if ( MetadataInsertionOffset > = 0 & & IndexEntryInsertionStart > = 0 )
{
// Insert new
fwrite ( Index . Metadata . Buffer . Ptr , MetadataInsertionOffset - sizeof ( Index . Header ) , 1 , Index . Metadata . Handle ) ;
fwrite ( & Index . Entry , sizeof ( Index . Entry ) , 1 , Index . Metadata . Handle ) ;
fwrite ( Index . Metadata . Buffer . Ptr - sizeof ( Index . Header ) + MetadataInsertionOffset , Index . Metadata . FileSize - MetadataInsertionOffset , 1 , Index . Metadata . Handle ) ;
fwrite ( Index . File . Buffer . Location , IndexEntryInsertionStart , 1 , Index . File . Handle ) ;
fwrite ( CollationBuffers - > Search . Location , Index . Entry . Size , 1 , Index . File . Handle ) ;
fwrite ( Index . File . Buffer . Location + IndexEntryInsertionStart , Index . File . FileSize - IndexEntryInsertionStart , 1 , Index . File . Handle ) ;
LogError ( LOG_NOTICE , " Inserted %s - %s " , BaseFilename , CollationBuffers - > Title ) ;
fprintf ( stderr , " \ e[1;32mInserted \ e[0m %s - %s \n " , BaseFilename , CollationBuffers - > Title ) ;
2017-10-18 21:07:29 +00:00
}
else
{
2017-11-11 00:34:47 +00:00
// Append new
if ( IndexMetadataFileReadCode = = RC_SUCCESS )
2017-10-18 21:07:29 +00:00
{
2017-11-11 00:34:47 +00:00
fwrite ( Index . Metadata . Buffer . Ptr , Index . Metadata . FileSize - sizeof ( Index . Header ) , 1 , Index . Metadata . Handle ) ;
fwrite ( Index . File . Buffer . Location , Index . File . FileSize , 1 , Index . File . Handle ) ;
2017-09-07 21:41:08 +00:00
}
2017-11-11 00:34:47 +00:00
else
{
fprintf ( Index . File . Handle , " --- \n " ) ;
}
fwrite ( & Index . Entry , sizeof ( Index . Entry ) , 1 , Index . Metadata . Handle ) ;
fwrite ( CollationBuffers - > Search . Location , Index . Entry . Size , 1 , Index . File . Handle ) ;
LogError ( LOG_NOTICE , " Appended %s - %s " , BaseFilename , CollationBuffers - > Title ) ;
fprintf ( stderr , " \ e[1;32mAppended \ e[0m %s - %s \n " , BaseFilename , CollationBuffers - > Title ) ;
2017-10-18 21:07:29 +00:00
}
2017-11-11 00:34:47 +00:00
fclose ( Index . Metadata . Handle ) ;
fclose ( Index . File . Handle ) ;
FreeBuffer ( & Index . Metadata . Buffer ) ;
FreeBuffer ( & Index . File . Buffer ) ;
2017-10-18 21:07:29 +00:00
return RC_SUCCESS ;
}
2017-12-12 23:24:10 +00:00
void
ConstructDirectoryPath ( buffer * DirectoryPath , int PageType , char * PageLocation , char * BaseFilename )
{
RewindBuffer ( DirectoryPath ) ;
CopyStringToBuffer ( DirectoryPath , Config . BaseDir ) ;
switch ( PageType )
{
case PAGE_INDEX :
if ( StringsDiffer ( PageLocation , " " ) )
{
CopyStringToBuffer ( DirectoryPath , " /%s " , PageLocation ) ;
}
break ;
case PAGE_PLAYER :
if ( StringsDiffer ( PageLocation , " " ) )
{
CopyStringToBuffer ( DirectoryPath , " /%s " , PageLocation ) ;
}
if ( StringsDiffer ( Config . PlayerURLPrefix , " " ) )
{
char * Ptr = BaseFilename + StringLength ( Config . ProjectID ) ;
CopyStringToBuffer ( DirectoryPath , " /%s%s " , Config . PlayerURLPrefix , Ptr ) ;
}
else
{
CopyStringToBuffer ( DirectoryPath , " /%s " , BaseFilename ) ;
}
break ;
}
}
2017-10-18 21:07:29 +00:00
int
DeleteFromIndex ( char * BaseFilename )
{
// TODO(matt): LogError()
2017-11-11 00:34:47 +00:00
index Index ;
2017-10-18 21:07:29 +00:00
2017-11-11 00:34:47 +00:00
Index . Metadata . Buffer . ID = " IndexMetadata " ;
CopyString ( Index . Metadata . Path , " %s/%s.metadata " , Config . BaseDir , Config . ProjectID ) ;
switch ( ReadFileIntoBuffer ( & Index . Metadata , 0 ) )
2017-10-18 21:07:29 +00:00
{
case RC_ERROR_FILE :
return RC_ERROR_FILE ;
case RC_ERROR_MEMORY :
LogError ( LOG_ERROR , " DeleteFromIndex(): %s " , strerror ( errno ) ) ;
return RC_ERROR_MEMORY ;
2017-11-11 00:34:47 +00:00
case RC_SUCCESS :
2017-10-18 21:07:29 +00:00
break ;
2017-11-11 00:34:47 +00:00
}
Index . File . Buffer . ID = " Index " ;
CopyString ( Index . File . Path , " %s/%s.index " , Config . BaseDir , Config . ProjectID ) ;
switch ( ReadFileIntoBuffer ( & Index . File , 0 ) )
{
case RC_ERROR_FILE :
return RC_ERROR_FILE ;
case RC_ERROR_MEMORY :
LogError ( LOG_ERROR , " DeleteFromIndex(): %s " , strerror ( errno ) ) ;
return RC_ERROR_MEMORY ;
2017-10-18 21:07:29 +00:00
case RC_SUCCESS :
break ;
}
2017-11-11 00:34:47 +00:00
Index . Header = * ( index_header * ) Index . Metadata . Buffer . Ptr ;
Index . Metadata . Buffer . Ptr + = sizeof ( Index . Header ) ;
2017-10-18 21:07:29 +00:00
bool Found = FALSE ;
2017-11-11 00:34:47 +00:00
int DeleteMetadataFrom = - 1 ;
int DeleteFileFrom = - 1 ;
int DeleteFileTo = - 1 ;
int SizeAcc = 0 ;
2017-10-18 21:07:29 +00:00
2017-11-11 00:34:47 +00:00
for ( int EntryIndex = 0 ; EntryIndex < Index . Header . EntryCount ; + + EntryIndex , Index . Metadata . Buffer . Ptr + = sizeof ( index_metadata ) )
2017-10-18 21:07:29 +00:00
{
2017-11-11 00:34:47 +00:00
index_metadata This = * ( index_metadata * ) Index . Metadata . Buffer . Ptr ;
if ( ! StringsDiffer ( This . BaseFilename , BaseFilename ) )
2017-08-29 20:35:28 +00:00
{
2017-10-18 21:07:29 +00:00
Found = TRUE ;
2017-11-11 00:34:47 +00:00
- - Index . Header . EntryCount ;
DeleteMetadataFrom = Index . Metadata . Buffer . Ptr - Index . Metadata . Buffer . Location ;
DeleteFileFrom = StringLength ( " --- \n " ) + SizeAcc ;
DeleteFileTo = DeleteFileFrom + This . Size ;
2017-10-18 21:07:29 +00:00
break ;
2017-08-29 20:35:28 +00:00
}
2017-11-11 00:34:47 +00:00
SizeAcc + = This . Size ;
2017-08-29 20:35:28 +00:00
}
2017-10-18 21:07:29 +00:00
if ( Found )
2017-08-29 20:35:28 +00:00
{
2017-11-11 00:34:47 +00:00
if ( Index . Header . EntryCount = = 0 )
2017-10-18 21:07:29 +00:00
{
2017-12-12 23:24:10 +00:00
buffer IndexDirectory ;
ClaimBuffer ( & IndexDirectory , " IndexDirectory " , 1024 ) ;
ConstructDirectoryPath ( & IndexDirectory , PAGE_INDEX , Config . IndexLocation , " " ) ;
char IndexPagePath [ 1024 ] ;
CopyString ( IndexPagePath , " %s/index.html " , IndexDirectory . Location ) ;
remove ( IndexPagePath ) ;
remove ( IndexDirectory . Location ) ;
DeclaimBuffer ( & IndexDirectory ) ;
2017-11-11 00:34:47 +00:00
remove ( Index . Metadata . Path ) ;
remove ( Index . File . Path ) ;
2017-10-18 21:07:29 +00:00
}
else
2017-09-07 21:41:08 +00:00
{
2017-11-11 00:34:47 +00:00
if ( ! ( Index . Metadata . Handle = fopen ( Index . Metadata . Path , " w " ) ) ) { FreeBuffer ( & Index . Metadata . Buffer ) ; return RC_ERROR_FILE ; }
if ( ! ( Index . File . Handle = fopen ( Index . File . Path , " w " ) ) ) { FreeBuffer ( & Index . File . Buffer ) ; return RC_ERROR_FILE ; }
fwrite ( & Index . Header , sizeof ( Index . Header ) , 1 , Index . Metadata . Handle ) ;
Index . Metadata . Buffer . Ptr = Index . Metadata . Buffer . Location + sizeof ( Index . Header ) ;
fwrite ( Index . Metadata . Buffer . Ptr , DeleteMetadataFrom - sizeof ( Index . Header ) , 1 , Index . Metadata . Handle ) ;
fwrite ( Index . Metadata . Buffer . Ptr + DeleteMetadataFrom - sizeof ( Index . Header ) + sizeof ( Index . Entry ) , Index . Metadata . FileSize - DeleteMetadataFrom - sizeof ( Index . Entry ) , 1 , Index . Metadata . Handle ) ;
fclose ( Index . Metadata . Handle ) ;
fwrite ( Index . File . Buffer . Location , DeleteFileFrom , 1 , Index . File . Handle ) ;
fwrite ( Index . File . Buffer . Location + DeleteFileTo , Index . File . FileSize - DeleteFileTo , 1 , Index . File . Handle ) ;
fclose ( Index . File . Handle ) ;
2017-09-07 21:41:08 +00:00
}
2017-10-18 21:07:29 +00:00
}
2017-11-11 00:34:47 +00:00
FreeBuffer ( & Index . Metadata . Buffer ) ;
FreeBuffer ( & Index . File . Buffer ) ;
2017-10-18 21:07:29 +00:00
return Found ? RC_SUCCESS : RC_NOOP ;
}
int
IndexToBuffer ( buffers * CollationBuffers )
{
// TODO(matt): Consider parsing the index into a linked list, or do something to save us having to iterate through the index
// file multiple times
2017-11-11 00:34:47 +00:00
index Index ;
Index . Metadata . Buffer . ID = " IndexMetadata " ;
CopyString ( Index . Metadata . Path , " %s/%s.metadata " , Config . BaseDir , Config . ProjectID ) ;
int IndexMetadataFileReadCode = ReadFileIntoBuffer ( & Index . Metadata , 0 ) ;
2017-10-18 21:07:29 +00:00
2017-12-07 01:15:13 +00:00
Index . File . Buffer . ID = " IndexFile " ;
2017-11-11 00:34:47 +00:00
CopyString ( Index . File . Path , " %s/%s.index " , Config . BaseDir , Config . ProjectID ) ;
int IndexFileReadCode = ReadFileIntoBuffer ( & Index . File , 0 ) ;
2017-10-18 21:07:29 +00:00
2017-11-11 00:34:47 +00:00
if ( IndexMetadataFileReadCode = = RC_SUCCESS & & IndexFileReadCode = = RC_SUCCESS )
2017-10-18 21:07:29 +00:00
{
2017-11-11 00:34:47 +00:00
Index . Header = * ( index_header * ) Index . Metadata . Buffer . Ptr ;
Index . Metadata . Buffer . Ptr + = sizeof ( Index . Header ) ;
Index . File . Buffer . Ptr + = StringLength ( " --- \n " ) ;
char * IndexEntryStart = Index . File . Buffer . Ptr ;
2017-10-18 21:07:29 +00:00
2017-12-07 01:15:13 +00:00
bool ProjectFound = FALSE ;
int ProjectIndex ;
for ( ProjectIndex = 0 ; ProjectIndex < ArrayCount ( ProjectInfo ) ; + + ProjectIndex )
{
if ( ! StringsDiffer ( ProjectInfo [ ProjectIndex ] . ProjectID , Config . ProjectID ) )
{
ProjectFound = TRUE ;
break ;
}
}
if ( ! ProjectFound )
{
fprintf ( stderr , " Missing Project Info for %s \n " , Config . ProjectID ) ;
2017-12-18 14:31:05 +00:00
FreeBuffer ( & Index . Metadata . Buffer ) ;
FreeBuffer ( & Index . File . Buffer ) ;
2017-12-07 01:15:13 +00:00
return RC_ERROR_PROJECT ;
}
2017-12-10 00:17:19 +00:00
int ThemeStringLength = StringsDiffer ( Config . Theme , " " ) ? ( StringLength ( Config . Theme ) * 2 ) : ( StringLength ( Config . ProjectID ) * 2 ) ;
char queryContainer [ 512 + ThemeStringLength ] ;
2017-12-07 01:15:13 +00:00
CopyString ( queryContainer ,
2017-12-10 00:17:19 +00:00
" <div class= \" cineraQueryContainer %s \" > \n "
2017-11-11 00:34:47 +00:00
" <label for= \" query \" >Query:</label> \n "
" <div class= \" inputContainer \" > \n "
" <input type= \" text \" id= \" query \" autofocus= \" \" > \n "
" <div class= \" spinner \" > \n "
" Downloading data... \n "
" </div> \n "
" </div> \n "
" </div> \n "
2017-12-10 00:17:19 +00:00
" <div id= \" cineraResultsSummary \" >Found: 0 episodes, 0 markers, 0h 0m 0s total.</div> \n "
" <div id= \" cineraResults \" ></div> \n "
2017-11-11 00:34:47 +00:00
" \n "
2017-12-07 01:15:13 +00:00
" <dl id= \" cineraIndex \" class= \" %s \" > \n " ,
StringsDiffer ( Config . Theme , " " ) ? Config . Theme : Config . ProjectID ,
StringsDiffer ( Config . Theme , " " ) ? Config . Theme : Config . ProjectID ) ;
2017-10-18 21:07:29 +00:00
2017-12-07 21:07:36 +00:00
buffer URLPrefix ;
ClaimBuffer ( & URLPrefix , " URLPrefix " , 1024 ) ;
ConstructURLPrefix ( & URLPrefix , INCLUDE_JS , PAGE_INDEX ) ;
2017-10-18 21:07:29 +00:00
2017-12-12 23:24:10 +00:00
buffer PlayerURL ;
ClaimBuffer ( & PlayerURL , " PlayerURL " , 4096 ) ;
ConstructPlayerURL ( & PlayerURL , " " ) ;
2017-12-07 21:07:36 +00:00
char Script [ 512 + StringLength ( URLPrefix . Location ) + ( StringLength ( Config . ProjectID ) * 2 ) ] ;
2017-12-07 01:15:13 +00:00
CopyString ( Script , " </dl> \n "
" <script type= \" text/javascript \" > \n "
" var projectID = \" %s \" ; \n "
2017-12-07 21:07:36 +00:00
" var theme = \" %s \" ; \n "
2017-12-12 23:24:10 +00:00
" var baseURL = \" %s \" ; \n "
" var playerLocation = \" %s \" ; \n "
2017-12-07 21:07:36 +00:00
" var outputURLPrefix = \" %s \" ; \n "
2017-12-12 23:24:10 +00:00
// TODO(matt): PlayerURL
2017-12-07 01:15:13 +00:00
" </script> \n "
" <script type= \" text/javascript \" src= \" %scinera_search.js \" ></script> \n " ,
Config . ProjectID ,
StringsDiffer ( Config . Theme , " " ) ? Config . Theme : Config . ProjectID ,
2017-12-12 23:24:10 +00:00
Config . BaseURL ,
Config . PlayerLocation ,
StringsDiffer ( Config . PlayerURLPrefix , " " ) ? Config . PlayerURLPrefix : Config . ProjectID ,
2017-12-07 21:07:36 +00:00
URLPrefix . Location ) ;
DeclaimBuffer ( & URLPrefix ) ;
2017-12-07 01:15:13 +00:00
int EntryLength = 32 + StringLength ( ProjectInfo [ ProjectIndex ] . Unit ) + 16 + 256 ;
CollationBuffers - > Index . Size = StringLength ( queryContainer ) + ( Index . Header . EntryCount * EntryLength ) + StringLength ( Script ) ;
if ( ! ( CollationBuffers - > Index . Location = malloc ( CollationBuffers - > Index . Size ) ) )
2017-11-11 00:34:47 +00:00
{
2017-12-18 14:31:05 +00:00
FreeBuffer ( & Index . Metadata . Buffer ) ;
FreeBuffer ( & Index . File . Buffer ) ;
2017-12-07 01:15:13 +00:00
return ( RC_ERROR_MEMORY ) ;
2017-11-11 00:34:47 +00:00
}
2017-12-07 01:15:13 +00:00
CollationBuffers - > Index . Ptr = CollationBuffers - > Index . Location ;
CopyStringToBuffer ( & CollationBuffers - > Index , queryContainer ) ;
2017-11-11 00:34:47 +00:00
for ( int EntryIndex = 0 ; EntryIndex < Index . Header . EntryCount ; + + EntryIndex )
{
index_metadata This = * ( index_metadata * ) Index . Metadata . Buffer . Ptr ;
char Number [ 16 ] ;
CopyString ( Number , This . BaseFilename + StringLength ( Config . ProjectID ) ) ;
if ( ProjectInfo [ ProjectIndex ] . NumberingScheme = = NS_LINEAR )
{
for ( int i = 0 ; Number [ i ] ; + + i )
{
if ( Number [ i ] = = ' _ ' )
{
Number [ i ] = ' . ' ;
}
}
}
SeekBufferForString ( & Index . File . Buffer , " title: \" " , C_SEEK_FORWARDS , C_SEEK_AFTER ) ;
char Title [ 256 ] ;
CopyStringNoFormatT ( Title , Index . File . Buffer . Ptr , ' \n ' ) ;
Title [ StringLength ( Title ) - 1 ] = ' \0 ' ;
2017-12-12 23:24:10 +00:00
ConstructPlayerURL ( & PlayerURL , This . BaseFilename ) ;
2017-12-07 21:07:36 +00:00
2017-12-07 01:15:13 +00:00
if ( StringsDiffer ( ProjectInfo [ ProjectIndex ] . Unit , " " ) )
{
CopyStringToBuffer ( & CollationBuffers - > Index ,
" <dt> \n "
2017-12-12 23:24:10 +00:00
" <a href= \" %s \" > " , PlayerURL . Location ) ;
2017-12-07 21:07:36 +00:00
char Text [ 1024 ] ; // NOTE(matt): Surely this will be big enough
CopyString ( Text , " %s %s: %s " ,
2017-12-07 01:15:13 +00:00
ProjectInfo [ ProjectIndex ] . Unit , // TODO(matt): Do we need to special-case the various numbering schemes?
Number ,
Title ) ;
2017-12-07 21:07:36 +00:00
CopyStringToBufferHTMLSafe ( & CollationBuffers - > Index , Text ) ;
CopyStringToBuffer ( & CollationBuffers - > Index ,
" </a> \n "
" </dt> \n " ) ;
2017-12-07 01:15:13 +00:00
}
else
{
CopyStringToBuffer ( & CollationBuffers - > Index ,
" <dt> \n "
" <a href= \" %s \" >%s</a> \n "
" </dt> \n " ,
2017-12-12 23:24:10 +00:00
PlayerURL . Location ,
2017-12-07 01:15:13 +00:00
Title ) ;
}
2017-11-11 00:34:47 +00:00
Index . Metadata . Buffer . Ptr + = sizeof ( Index . Entry ) ;
IndexEntryStart + = This . Size ;
Index . File . Buffer . Ptr = IndexEntryStart ;
}
2017-12-12 23:24:10 +00:00
DeclaimBuffer ( & PlayerURL ) ;
2017-12-07 01:15:13 +00:00
CopyStringToBuffer ( & CollationBuffers - > Index , Script ) ;
2017-11-11 00:34:47 +00:00
FreeBuffer ( & Index . Metadata . Buffer ) ;
2017-12-18 14:31:05 +00:00
FreeBuffer ( & Index . File . Buffer ) ;
2017-11-11 00:34:47 +00:00
return RC_SUCCESS ;
}
else
{
return RC_ERROR_FILE ;
}
2017-10-18 21:07:29 +00:00
}
2017-09-07 21:41:08 +00:00
2017-12-12 23:24:10 +00:00
char *
StripTrailingSlash ( char * String ) // NOTE(matt): For absolute paths
2017-10-18 21:07:29 +00:00
{
2017-12-12 23:24:10 +00:00
int Length = StringLength ( String ) ;
while ( Length > 0 & & String [ Length - 1 ] = = ' / ' )
2017-12-07 21:07:36 +00:00
{
2017-12-12 23:24:10 +00:00
String [ Length - 1 ] = ' \0 ' ;
- - Length ;
2017-12-07 21:07:36 +00:00
}
2017-12-12 23:24:10 +00:00
return String ;
}
char *
StripSurroundingSlashes ( char * String ) // NOTE(matt): For relative paths
{
int Length = StringLength ( String ) ;
if ( Length > 0 )
2017-12-07 21:07:36 +00:00
{
2017-12-12 23:24:10 +00:00
while ( ( String [ 0 ] ) = = ' / ' )
{
+ + String ;
- - Length ;
}
while ( String [ Length - 1 ] = = ' / ' )
{
String [ Length - 1 ] = ' \0 ' ;
- - Length ;
}
2017-12-07 21:07:36 +00:00
}
2017-12-12 23:24:10 +00:00
return String ;
}
int
GeneratePlayerPage ( buffers * CollationBuffers , template * PlayerTemplate , char * BaseFilename )
{
buffer OutputDirectoryPath ;
ClaimBuffer ( & OutputDirectoryPath , " OutputDirectoryPath " , 1024 ) ;
ConstructDirectoryPath ( & OutputDirectoryPath , PAGE_PLAYER , Config . PlayerLocation , BaseFilename ) ;
2017-09-15 01:11:39 +00:00
2017-10-18 21:07:29 +00:00
DIR * OutputDirectoryHandle ;
2017-12-12 23:24:10 +00:00
if ( ! ( OutputDirectoryHandle = opendir ( OutputDirectoryPath . Location ) ) ) // TODO(matt): open()
2017-10-18 21:07:29 +00:00
{
2017-12-12 23:24:10 +00:00
if ( MakeDir ( OutputDirectoryPath . Location ) = = RC_ERROR_DIRECTORY )
2017-09-07 21:41:08 +00:00
{
2017-12-12 23:24:10 +00:00
LogError ( LOG_ERROR , " Unable to create directory %s: %s " , OutputDirectoryPath . Location , strerror ( errno ) ) ;
fprintf ( stderr , " Unable to create directory %s: %s \n " , OutputDirectoryPath . Location , strerror ( errno ) ) ;
2017-10-18 21:07:29 +00:00
return RC_ERROR_DIRECTORY ;
} ;
}
closedir ( OutputDirectoryHandle ) ;
2017-12-12 23:24:10 +00:00
char PlayerPagePath [ 1024 ] ;
CopyString ( PlayerPagePath , " %s/index.html " , OutputDirectoryPath . Location ) ;
DeclaimBuffer ( & OutputDirectoryPath ) ;
2017-10-18 21:07:29 +00:00
BuffersToHTML ( CollationBuffers , PlayerTemplate , PlayerPagePath , PAGE_PLAYER ) ;
return RC_SUCCESS ;
}
int
GenerateIndexPage ( buffers * CollationBuffers , template * IndexTemplate )
{
2017-12-12 23:24:10 +00:00
buffer OutputDirectoryPath ;
ClaimBuffer ( & OutputDirectoryPath , " OutputDirectoryPath " , 1024 ) ;
ConstructDirectoryPath ( & OutputDirectoryPath , PAGE_INDEX , Config . IndexLocation , 0 ) ;
DIR * OutputDirectoryHandle ;
if ( ! ( OutputDirectoryHandle = opendir ( OutputDirectoryPath . Location ) ) ) // TODO(matt): open()
{
if ( MakeDir ( OutputDirectoryPath . Location ) = = RC_ERROR_DIRECTORY )
{
LogError ( LOG_ERROR , " Unable to create directory %s: %s " , OutputDirectoryPath . Location , strerror ( errno ) ) ;
fprintf ( stderr , " Unable to create directory %s: %s \n " , OutputDirectoryPath . Location , strerror ( errno ) ) ;
return RC_ERROR_DIRECTORY ;
} ;
}
closedir ( OutputDirectoryHandle ) ;
char IndexPagePath [ 1024 ] ;
CopyString ( IndexPagePath , " %s/index.html " , OutputDirectoryPath . Location ) ;
DeclaimBuffer ( & OutputDirectoryPath ) ;
2017-10-18 21:07:29 +00:00
IndexToBuffer ( CollationBuffers ) ;
BuffersToHTML ( CollationBuffers , IndexTemplate , IndexPagePath , PAGE_INDEX ) ;
2017-12-07 01:15:13 +00:00
FreeBuffer ( & CollationBuffers - > Index ) ;
2017-10-18 21:07:29 +00:00
return RC_SUCCESS ;
}
int
2017-12-12 23:24:10 +00:00
DeletePlayerPageFromFilesystem ( char * BaseFilename , char * PlayerLocation , bool Relocating )
2017-10-18 21:07:29 +00:00
{
// NOTE(matt): Once we have the notion of an output filename format, we'll need to use that here
2017-12-12 23:24:10 +00:00
buffer OutputDirectoryPath ;
ClaimBuffer ( & OutputDirectoryPath , " OutputDirectoryPath " , 1024 ) ;
ConstructDirectoryPath ( & OutputDirectoryPath , PAGE_PLAYER , PlayerLocation , BaseFilename ) ;
2017-10-18 21:07:29 +00:00
DIR * PlayerDir ;
2017-12-12 23:24:10 +00:00
if ( ( PlayerDir = opendir ( OutputDirectoryPath . Location ) ) ) // There is a directory for the Player, which there probably should be if not for manual intervention
2017-10-18 21:07:29 +00:00
{
2017-11-11 00:34:47 +00:00
char PlayerPagePath [ 256 ] ;
2017-12-12 23:24:10 +00:00
CopyString ( PlayerPagePath , " %s/index.html " , OutputDirectoryPath . Location ) ;
2017-10-18 21:07:29 +00:00
FILE * PlayerPage ;
if ( ( PlayerPage = fopen ( PlayerPagePath , " r " ) ) )
{
fclose ( PlayerPage ) ;
remove ( PlayerPagePath ) ;
}
closedir ( PlayerDir ) ;
2017-12-12 23:24:10 +00:00
if ( ( remove ( OutputDirectoryPath . Location ) = = - 1 ) )
2017-10-18 21:07:29 +00:00
{
2017-12-12 23:24:10 +00:00
LogError ( LOG_NOTICE , " Mostly deleted %s. Unable to remove directory %s: %s " , BaseFilename , OutputDirectoryPath . Location , strerror ( errno ) ) ;
fprintf ( stderr , " \ e[1;30mMostly deleted \ e[0m %s. \ e[1;31mUnable to remove directory \ e[0m %s: %s " , BaseFilename , OutputDirectoryPath . Location , strerror ( errno ) ) ;
2017-10-18 21:07:29 +00:00
}
else
{
2017-12-12 23:24:10 +00:00
if ( ! Relocating )
{
LogError ( LOG_INFORMATIONAL , " Deleted %s " , BaseFilename ) ;
fprintf ( stderr , " \ e[1;30mDeleted \ e[0m %s \n " , BaseFilename ) ;
}
2017-10-18 21:07:29 +00:00
}
}
2017-12-12 23:24:10 +00:00
DeclaimBuffer ( & OutputDirectoryPath ) ;
2017-10-18 21:07:29 +00:00
return RC_SUCCESS ;
}
2017-08-29 20:35:28 +00:00
2017-10-18 21:07:29 +00:00
void
DeleteEntry ( char * BaseFilename )
{
if ( DeleteFromIndex ( BaseFilename ) = = RC_SUCCESS )
{
2017-12-12 23:24:10 +00:00
DeletePlayerPageFromFilesystem ( BaseFilename , Config . PlayerLocation , FALSE ) ;
2017-10-18 21:07:29 +00:00
}
}
2017-08-29 20:35:28 +00:00
2017-10-18 21:07:29 +00:00
int
MonitorDirectory ( buffers * CollationBuffers , template * IndexTemplate , template * PlayerTemplate , int inotifyInstance , int WatchDescriptor )
{
# if DEBUG_MEM
FILE * MemLog = fopen ( " /home/matt/cinera_mem " , " a+ " ) ;
fprintf ( MemLog , " \n Called MonitorDirectory() \n " ) ;
fclose ( MemLog ) ;
# endif
// TODO(matt): Maybe straight up store the IndexPath in the Config to save us having to derive it near / at the usage site
buffer Events ;
2017-11-11 00:34:47 +00:00
if ( ClaimBuffer ( & Events , " inotify Events " , Kilobytes ( 32 ) ) = = RC_ARENA_FULL ) { return RC_ARENA_FULL ; } ;
2017-10-18 21:07:29 +00:00
struct inotify_event * Event ;
int BytesRead ;
while ( ( BytesRead = read ( inotifyInstance , Events . Location , Events . Size ) ) ! = - 1 & & errno = = EAGAIN & & BytesRead > 0 )
{
for ( Events . Ptr = Events . Location ;
Events . Ptr < Events . Location + BytesRead & & Events . Ptr - Events . Location < Events . Size ;
Events . Ptr + = sizeof ( struct inotify_event ) + Event - > len )
{
Event = ( struct inotify_event * ) Events . Ptr ;
2017-09-15 01:11:39 +00:00
2017-10-18 21:07:29 +00:00
char * Ptr ;
Ptr = Event - > name ;
Ptr + = ( StringLength ( Event - > name ) - StringLength ( " .hmml " ) ) ;
if ( ! ( StringsDiffer ( Ptr , " .hmml " ) ) )
{
* Ptr = ' \0 ' ;
2017-11-11 00:34:47 +00:00
char BaseFilename [ 256 ] ;
2017-10-18 21:07:29 +00:00
CopyString ( BaseFilename , Event - > name ) ;
* Ptr = ' . ' ;
2017-06-25 18:22:54 +00:00
2017-11-11 00:34:47 +00:00
// TODO(matt): Maybe handle IN_ALL_EVENTS
2017-10-18 21:07:29 +00:00
if ( Event - > mask & IN_DELETE | | Event - > mask & IN_MOVED_FROM )
{
DeleteEntry ( BaseFilename ) ;
}
else
{
2017-11-11 00:34:47 +00:00
InsertIntoIndex ( CollationBuffers , BaseFilename ) ;
GeneratePlayerPage ( CollationBuffers , PlayerTemplate , BaseFilename ) ;
2017-10-18 21:07:29 +00:00
}
2017-11-11 00:34:47 +00:00
GenerateIndexPage ( CollationBuffers , IndexTemplate ) ;
2017-10-18 21:07:29 +00:00
}
2017-09-07 21:41:08 +00:00
}
}
2017-08-19 21:41:51 +00:00
2017-10-18 21:07:29 +00:00
DeclaimBuffer ( & Events ) ;
return RC_NOOP ;
2017-09-07 21:41:08 +00:00
}
2017-06-25 18:22:54 +00:00
2017-12-12 23:24:10 +00:00
int
RemoveDirectory ( char * Path )
{
if ( ( remove ( Path ) = = - 1 ) )
{
LogError ( LOG_NOTICE , " Unable to remove directory %s: %s " , Path , strerror ( errno ) ) ;
fprintf ( stderr , " \ e[1;30mUnable to remove directory \ e[0m %s: %s " , Path , strerror ( errno ) ) ;
return RC_ERROR_DIRECTORY ;
}
else
{
LogError ( LOG_INFORMATIONAL , " Deleted %s " , Path ) ;
//fprintf(stderr, "\e[1;30mDeleted\e[0m %s\n", Path);
return RC_SUCCESS ;
}
}
int
RemoveDirectoryRecursively ( char * Path )
{
if ( RemoveDirectory ( Path ) = = RC_ERROR_DIRECTORY ) { return RC_ERROR_DIRECTORY ; }
char * Ptr = Path + StringLength ( Path ) - 1 ;
while ( Ptr > Path )
{
if ( * Ptr = = ' / ' )
{
* Ptr = ' \0 ' ;
if ( RemoveDirectory ( Path ) = = RC_ERROR_DIRECTORY ) { return RC_ERROR_DIRECTORY ; }
}
- - Ptr ;
}
return RC_SUCCESS ;
}
void
Clear ( char * String , int Size )
{
for ( int i = 0 ; i < Size ; + + i )
{
String [ i ] = 0 ;
}
}
int
UpgradeDB ( index * Index )
{
int DBVersion = Index - > Header . DBVersion ;
switch ( DBVersion )
{
case 1 :
{
typedef struct
{
unsigned int DBVersion ;
version AppVersion ;
version HMMLVersion ;
unsigned int EntryCount ;
} index_header1 ;
typedef struct
{
int Size ;
char BaseFilename [ 32 ] ;
} index_metadata1 ;
typedef struct
{
file_buffer File ;
file_buffer Metadata ;
index_header1 Header ;
index_metadata1 Entry ;
} index1 ;
index1 OldIndex = { 0 } ;
OldIndex . Header = * ( index_header1 * ) Index - > Metadata . Buffer . Ptr ;
Index - > Metadata . Buffer . Ptr = Index - > Metadata . Buffer . Location + sizeof ( OldIndex . Header ) ;
Index - > Header . DBVersion = CINERA_DB_VERSION ;
Index - > Header . AppVersion = CINERA_APP_VERSION ;
Index - > Header . HMMLVersion . Major = hmml_version . Major ;
Index - > Header . HMMLVersion . Minor = hmml_version . Minor ;
Index - > Header . HMMLVersion . Patch = hmml_version . Patch ;
Index - > Header . EntryCount = OldIndex . Header . EntryCount ;
Clear ( Index - > Header . IndexLocation , sizeof ( Index - > Header . IndexLocation ) ) ;
Clear ( Index - > Header . PlayerLocation , sizeof ( Index - > Header . PlayerLocation ) ) ;
if ( ! ( Index - > Metadata . Handle = fopen ( Index - > Metadata . Path , " w " ) ) ) { FreeBuffer ( & Index - > Metadata . Buffer ) ; return RC_ERROR_FILE ; }
fwrite ( & Index - > Header , sizeof ( Index - > Header ) , 1 , Index - > Metadata . Handle ) ;
Index - > Metadata . Buffer . Ptr = Index - > Metadata . Buffer . Location + sizeof ( OldIndex . Header ) ;
fwrite ( Index - > Metadata . Buffer . Ptr , Index - > Metadata . FileSize - sizeof ( OldIndex . Header ) , 1 , Index - > Metadata . Handle ) ;
Index - > Metadata . Buffer . Ptr = Index - > Metadata . Buffer . Location ;
fclose ( Index - > Metadata . Handle ) ;
FreeBuffer ( & Index - > Metadata . Buffer ) ;
if ( ReadFileIntoBuffer ( & Index - > Metadata , 0 ) = = RC_ERROR_FILE )
{
return RC_ERROR_FILE ;
}
}
break ;
}
2017-12-17 20:44:00 +00:00
fprintf ( stderr , " \n \ e[1;32mUpgraded Cinera DB from %d to %d! \ e[0m \n \n " , DBVersion , Index - > Header . DBVersion ) ;
2017-12-12 23:24:10 +00:00
return RC_SUCCESS ;
}
2017-10-18 21:07:29 +00:00
typedef struct
{
bool Present ;
char ID [ 32 ] ;
2017-11-11 00:34:47 +00:00
} index_entry ; // Metadata, unless we actually want to bolster this?
2017-10-18 21:07:29 +00:00
2017-09-07 21:41:08 +00:00
int
2017-10-18 21:07:29 +00:00
DeleteDeadIndexEntries ( )
2017-09-07 21:41:08 +00:00
{
2017-11-11 00:34:47 +00:00
// TODO(matt): More rigorously figure out who we should delete
// Maybe compare the output directory and the input HMML names
2017-12-12 23:24:10 +00:00
index Index = { 0 } ;
2017-11-11 00:34:47 +00:00
Index . Metadata . Buffer . ID = " IndexMetadata " ;
CopyString ( Index . Metadata . Path , " %s/%s.metadata " , Config . BaseDir , Config . ProjectID ) ;
if ( ReadFileIntoBuffer ( & Index . Metadata , 0 ) = = RC_ERROR_FILE )
2017-10-18 21:07:29 +00:00
{
return RC_ERROR_FILE ;
}
2017-11-11 00:34:47 +00:00
Index . Header = * ( index_header * ) Index . Metadata . Buffer . Ptr ;
if ( Index . Header . DBVersion ! = CINERA_DB_VERSION )
{
2017-12-12 23:24:10 +00:00
if ( CINERA_DB_VERSION = = 3 )
{
fprintf ( stderr , " \n \ e[1;31mHandle conversion from CINERA_DB_VERSION %d to %d! \ e[0m \n \n " , Index . Header . DBVersion , CINERA_DB_VERSION ) ;
exit ( RC_ERROR_FATAL ) ;
}
UpgradeDB ( & Index ) ;
2017-11-11 00:34:47 +00:00
}
2017-10-18 21:07:29 +00:00
2017-12-12 23:24:10 +00:00
if ( StringsDiffer ( Index . Header . PlayerLocation , Config . PlayerLocation ) )
{
buffer PlayerDirectory ;
ClaimBuffer ( & PlayerDirectory , " PlayerDirectory " , 1024 ) ;
printf ( " \ e[1;33mRelocating Player Page%s from %s to %s \ e[0m \n " ,
Index . Header . EntryCount > 1 ? " s " : " " ,
( StringsDiffer ( Index . Header . PlayerLocation , " " ) ? Index . Header . PlayerLocation : ( StringsDiffer ( Config . BaseDir , " . " ) ? Config . BaseDir : " \" Base Directory \" " ) ) ,
( StringsDiffer ( Config . PlayerLocation , " " ) ? Config . PlayerLocation : ( StringsDiffer ( Config . BaseDir , " . " ) ? Config . BaseDir : " \" Base Directory \" " ) ) ) ;
Index . Metadata . Buffer . Ptr = Index . Metadata . Buffer . Location + sizeof ( Index . Header ) ;
for ( int EntryIndex = 0 ; EntryIndex < Index . Header . EntryCount ; + + EntryIndex )
{
index_metadata This = * ( index_metadata * ) Index . Metadata . Buffer . Ptr ;
ConstructDirectoryPath ( & PlayerDirectory , PAGE_PLAYER , Index . Header . PlayerLocation , This . BaseFilename ) ;
DeletePlayerPageFromFilesystem ( This . BaseFilename , Index . Header . PlayerLocation , TRUE ) ;
Index . Metadata . Buffer . Ptr + = sizeof ( This ) ;
}
DeclaimBuffer ( & PlayerDirectory ) ;
RemoveDirectoryRecursively ( Index . Header . PlayerLocation ) ;
Clear ( Index . Header . PlayerLocation , sizeof ( Index . Header . PlayerLocation ) ) ;
CopyString ( Index . Header . PlayerLocation , Config . PlayerLocation ) ;
if ( ! ( Index . Metadata . Handle = fopen ( Index . Metadata . Path , " w " ) ) ) { FreeBuffer ( & Index . Metadata . Buffer ) ; return RC_ERROR_FILE ; }
fwrite ( & Index . Header , sizeof ( Index . Header ) , 1 , Index . Metadata . Handle ) ;
Index . Metadata . Buffer . Ptr = Index . Metadata . Buffer . Location + sizeof ( Index . Header ) ;
fwrite ( Index . Metadata . Buffer . Ptr , Index . Metadata . FileSize - sizeof ( Index . Header ) , 1 , Index . Metadata . Handle ) ;
Index . Metadata . Buffer . Ptr = Index . Metadata . Buffer . Location ;
fclose ( Index . Metadata . Handle ) ;
}
if ( StringsDiffer ( Index . Header . IndexLocation , Config . IndexLocation ) )
{
printf ( " \ e[1;33mRelocating Index Page from %s to %s \ e[0m \n " ,
( StringsDiffer ( Index . Header . IndexLocation , " " ) ? Index . Header . IndexLocation : ( StringsDiffer ( Config . BaseDir , " . " ) ? Config . BaseDir : " \" Base Directory \" " ) ) ,
( StringsDiffer ( Config . IndexLocation , " " ) ? Config . IndexLocation : ( StringsDiffer ( Config . BaseDir , " . " ) ? Config . BaseDir : " \" Base Directory \" " ) ) ) ;
buffer IndexDirectory ;
ClaimBuffer ( & IndexDirectory , " IndexDirectory " , 1024 ) ;
ConstructDirectoryPath ( & IndexDirectory , PAGE_INDEX , Index . Header . IndexLocation , " " ) ;
char IndexPagePath [ 2048 ] = { 0 } ;
CopyString ( IndexPagePath , " %s/index.html " , IndexDirectory . Location ) ;
remove ( IndexPagePath ) ;
RemoveDirectoryRecursively ( IndexDirectory . Location ) ;
DeclaimBuffer ( & IndexDirectory ) ;
Clear ( Index . Header . IndexLocation , sizeof ( Index . Header . IndexLocation ) ) ;
CopyString ( Index . Header . IndexLocation , Config . IndexLocation ) ;
if ( ! ( Index . Metadata . Handle = fopen ( Index . Metadata . Path , " w " ) ) ) { FreeBuffer ( & Index . Metadata . Buffer ) ; return RC_ERROR_FILE ; }
fwrite ( & Index . Header , sizeof ( Index . Header ) , 1 , Index . Metadata . Handle ) ;
Index . Metadata . Buffer . Ptr = Index . Metadata . Buffer . Location + sizeof ( Index . Header ) ;
fwrite ( Index . Metadata . Buffer . Ptr , Index . Metadata . FileSize - sizeof ( Index . Header ) , 1 , Index . Metadata . Handle ) ;
Index . Metadata . Buffer . Ptr = Index . Metadata . Buffer . Location ;
fclose ( Index . Metadata . Handle ) ;
}
Index . Metadata . Buffer . Ptr = Index . Metadata . Buffer . Location + sizeof ( Index . Header ) ;
2017-10-18 21:07:29 +00:00
2017-11-11 00:34:47 +00:00
index_entry Entries [ Index . Header . EntryCount ] ;
for ( int EntryIndex = 0 ; EntryIndex < Index . Header . EntryCount ; + + EntryIndex )
2017-10-18 21:07:29 +00:00
{
2017-11-11 00:34:47 +00:00
index_metadata This = * ( index_metadata * ) Index . Metadata . Buffer . Ptr ;
CopyStringNoFormat ( Entries [ EntryIndex ] . ID , This . BaseFilename ) ;
Entries [ EntryIndex ] . Present = FALSE ;
2017-12-12 23:24:10 +00:00
Index . Metadata . Buffer . Ptr + = sizeof ( This ) ;
2017-10-18 21:07:29 +00:00
}
2017-09-07 21:41:08 +00:00
DIR * ProjectDirHandle ;
if ( ! ( ProjectDirHandle = opendir ( Config . ProjectDir ) ) )
{
2017-10-18 21:07:29 +00:00
LogError ( LOG_ERROR , " Unable to scan project directory %s: %s " , Config . ProjectDir , strerror ( errno ) ) ;
2017-09-07 21:41:08 +00:00
fprintf ( stderr , " Unable to scan project directory %s: %s \n " , Config . ProjectDir , strerror ( errno ) ) ;
return RC_ERROR_DIRECTORY ;
}
2017-08-29 20:35:28 +00:00
2017-09-07 21:41:08 +00:00
struct dirent * ProjectFiles ;
2017-08-29 20:35:28 +00:00
2017-09-07 21:41:08 +00:00
while ( ( ProjectFiles = readdir ( ProjectDirHandle ) ) )
{
char * Ptr ;
Ptr = ProjectFiles - > d_name ;
Ptr + = ( StringLength ( ProjectFiles - > d_name ) - StringLength ( " .hmml " ) ) ;
if ( ! ( StringsDiffer ( Ptr , " .hmml " ) ) )
{
* Ptr = ' \0 ' ;
2017-11-11 00:34:47 +00:00
for ( int i = 0 ; i < Index . Header . EntryCount ; + + i )
2017-08-19 21:41:51 +00:00
{
2017-10-18 21:07:29 +00:00
if ( ! StringsDiffer ( Entries [ i ] . ID , ProjectFiles - > d_name ) )
{
Entries [ i ] . Present = TRUE ;
2017-09-07 21:41:08 +00:00
break ;
2017-10-18 21:07:29 +00:00
}
2017-08-29 20:35:28 +00:00
}
}
}
2017-09-07 21:41:08 +00:00
closedir ( ProjectDirHandle ) ;
2017-10-18 21:07:29 +00:00
bool Deleted = FALSE ;
2017-11-11 00:34:47 +00:00
for ( int i = 0 ; i < Index . Header . EntryCount ; + + i )
2017-10-18 21:07:29 +00:00
{
if ( Entries [ i ] . Present = = FALSE )
{
Deleted = TRUE ;
DeleteEntry ( Entries [ i ] . ID ) ;
}
}
2017-11-11 00:34:47 +00:00
FreeBuffer ( & Index . Metadata . Buffer ) ;
2017-10-18 21:07:29 +00:00
return Deleted ? RC_SUCCESS : RC_NOOP ;
2017-08-29 20:35:28 +00:00
}
2017-08-19 21:41:51 +00:00
2017-09-15 01:11:39 +00:00
int
2017-10-18 21:07:29 +00:00
SyncIndexWithInput ( buffers * CollationBuffers , template * IndexTemplate , template * PlayerTemplate )
2017-09-15 01:11:39 +00:00
{
2017-11-11 00:34:47 +00:00
bool Deleted = FALSE ;
if ( DeleteDeadIndexEntries ( ) = = RC_SUCCESS )
{
Deleted = TRUE ;
}
2017-09-15 01:11:39 +00:00
2017-10-18 21:07:29 +00:00
DIR * ProjectDirHandle ;
if ( ! ( ProjectDirHandle = opendir ( Config . ProjectDir ) ) )
{
LogError ( LOG_ERROR , " Unable to scan project directory %s: %s " , Config . ProjectDir , strerror ( errno ) ) ;
fprintf ( stderr , " Unable to scan project directory %s: %s \n " , Config . ProjectDir , strerror ( errno ) ) ;
return RC_ERROR_DIRECTORY ;
}
2017-09-15 01:11:39 +00:00
2017-10-18 21:07:29 +00:00
struct dirent * ProjectFiles ;
bool Inserted = FALSE ;
while ( ( ProjectFiles = readdir ( ProjectDirHandle ) ) )
2017-09-15 01:11:39 +00:00
{
2017-10-18 21:07:29 +00:00
char * Ptr = ProjectFiles - > d_name ;
Ptr + = ( StringLength ( ProjectFiles - > d_name ) - StringLength ( " .hmml " ) ) ;
if ( ! ( StringsDiffer ( Ptr , " .hmml " ) ) )
2017-09-15 01:11:39 +00:00
{
2017-10-18 21:07:29 +00:00
* Ptr = ' \0 ' ;
2017-11-11 00:34:47 +00:00
InsertIntoIndex ( CollationBuffers , ProjectFiles - > d_name ) ;
GeneratePlayerPage ( CollationBuffers , PlayerTemplate , ProjectFiles - > d_name ) ;
Inserted = TRUE ;
2017-09-15 01:11:39 +00:00
}
}
2017-10-18 21:07:29 +00:00
closedir ( ProjectDirHandle ) ;
2017-09-15 01:11:39 +00:00
2017-11-11 00:34:47 +00:00
if ( Deleted | | Inserted )
2017-10-18 21:07:29 +00:00
{
GenerateIndexPage ( CollationBuffers , IndexTemplate ) ;
}
2017-09-15 01:11:39 +00:00
return RC_SUCCESS ;
}
2017-11-18 00:27:33 +00:00
void
PrintVersions ( )
{
curl_version_info_data * CurlVersion = curl_version_info ( CURLVERSION_NOW ) ;
printf ( " Cinera: %d.%d.%d \n "
" Cinera DB: %d \n "
" hmmlib: %d.%d.%d \n "
" libcurl: %s \n " ,
CINERA_APP_VERSION . Major , CINERA_APP_VERSION . Minor , CINERA_APP_VERSION . Patch ,
CINERA_DB_VERSION ,
hmml_version . Major , hmml_version . Minor , hmml_version . Patch ,
CurlVersion - > version ) ;
}
2017-08-29 20:35:28 +00:00
int
main ( int ArgC , char * * Args )
{
// TODO(matt): Read all defaults from the config
config DefaultConfig = {
2017-12-12 23:24:10 +00:00
. RootDir = " . " ,
. RootURL = " " ,
2017-11-11 00:34:47 +00:00
. CSSDir = " " ,
. ImagesDir = " " ,
. JSDir = " " ,
2017-12-12 23:24:10 +00:00
. BaseDir = " . " ,
. BaseURL = " " ,
. IndexLocation = " " ,
. PlayerLocation = " " , // Should default to the ProjectID
. Edition = EDITION_SINGLE ,
2017-10-18 21:07:29 +00:00
. LogLevel = LOG_EMERGENCY ,
2017-08-29 20:35:28 +00:00
. DefaultMedium = " programming " ,
. Mode = getenv ( " CINERA_MODE " ) ? MODE_INTEGRATE : MODE_BARE ,
. OutLocation = " out.html " ,
. OutIntegratedLocation = " out_integrated.html " ,
. ForceIntegration = FALSE ,
. ProjectDir = " . " ,
2017-10-18 21:07:29 +00:00
. ProjectID = " " ,
2017-12-07 01:15:13 +00:00
. Theme = " " ,
2017-09-07 21:41:08 +00:00
. TemplatePlayerLocation = " template_player.html " ,
2017-10-18 21:07:29 +00:00
. TemplateIndexLocation = " template_index.html " ,
2017-12-07 21:07:36 +00:00
. UpdateInterval = 4 ,
2017-12-12 23:24:10 +00:00
. PlayerURLPrefix = " "
2017-08-29 20:35:28 +00:00
} ;
2017-08-19 21:41:51 +00:00
2017-08-29 20:35:28 +00:00
if ( getenv ( " XDG_CACHE_HOME " ) )
{
CopyString ( DefaultConfig . CacheDir , " %s/cinera " , getenv ( " XDG_CACHE_HOME " ) ) ;
}
else
{
CopyString ( DefaultConfig . CacheDir , " %s/.cache/cinera " , getenv ( " HOME " ) ) ;
}
2017-08-19 21:41:51 +00:00
2017-10-18 21:07:29 +00:00
Config = DefaultConfig ;
2017-06-25 18:22:54 +00:00
2017-08-29 20:35:28 +00:00
if ( ArgC < 2 )
{
2017-10-18 21:07:29 +00:00
PrintUsage ( Args [ 0 ] , & DefaultConfig ) ;
2017-09-07 21:41:08 +00:00
return RC_RIP ;
2017-08-29 20:35:28 +00:00
}
2017-06-25 18:22:54 +00:00
2017-08-29 20:35:28 +00:00
char CommandLineArg ;
2017-12-18 14:31:05 +00:00
while ( ( CommandLineArg = getopt ( ArgC , Args , " a:b:B:c:d:fhi:j:l:m:n:o:p:qr:R:s:t:U:vx: " ) ) ! = - 1 )
2017-08-29 20:35:28 +00:00
{
switch ( CommandLineArg )
{
2017-12-12 23:24:10 +00:00
case ' a ' :
Config . PlayerLocation = StripSurroundingSlashes ( optarg ) ;
break ;
2017-08-29 20:35:28 +00:00
case ' b ' :
2017-11-11 00:34:47 +00:00
Config . BaseDir = StripTrailingSlash ( optarg ) ;
2017-08-29 20:35:28 +00:00
break ;
2017-12-12 23:24:10 +00:00
case ' B ' :
Config . BaseURL = StripTrailingSlash ( optarg ) ;
break ;
2017-08-29 20:35:28 +00:00
case ' c ' :
2017-12-12 23:24:10 +00:00
Config . CSSDir = StripSurroundingSlashes ( optarg ) ;
2017-11-11 00:34:47 +00:00
break ;
case ' d ' :
Config . ProjectDir = StripTrailingSlash ( optarg ) ;
2017-08-29 20:35:28 +00:00
break ;
case ' f ' :
Config . ForceIntegration = TRUE ;
break ;
case ' i ' :
2017-12-12 23:24:10 +00:00
Config . ImagesDir = StripSurroundingSlashes ( optarg ) ;
2017-10-18 21:07:29 +00:00
break ;
2017-08-29 20:35:28 +00:00
case ' j ' :
2017-12-12 23:24:10 +00:00
Config . JSDir = StripSurroundingSlashes ( optarg ) ;
2017-08-29 20:35:28 +00:00
break ;
2017-09-07 21:41:08 +00:00
case ' l ' :
// TODO(matt): Make this actually take a string, rather than requiring the LogLevel number
Config . LogLevel = StringToInt ( optarg ) ;
break ;
2017-08-29 20:35:28 +00:00
case ' m ' :
Config . DefaultMedium = optarg ;
break ;
2017-12-12 23:24:10 +00:00
case ' n ' :
Config . IndexLocation = StripSurroundingSlashes ( optarg ) ;
break ;
2017-08-29 20:35:28 +00:00
case ' o ' :
Config . OutLocation = optarg ;
Config . OutIntegratedLocation = optarg ;
break ;
case ' p ' :
2017-11-11 00:34:47 +00:00
Config . ProjectID = optarg ;
2017-08-29 20:35:28 +00:00
break ;
2017-12-18 14:31:05 +00:00
case ' q ' :
2017-12-18 15:10:02 +00:00
Config . Mode | = MODE_ONESHOT ;
2017-12-18 14:31:05 +00:00
break ;
2017-10-18 21:07:29 +00:00
case ' r ' :
2017-11-11 00:34:47 +00:00
Config . RootDir = StripTrailingSlash ( optarg ) ;
2017-10-18 21:07:29 +00:00
break ;
2017-12-12 23:24:10 +00:00
case ' R ' :
Config . RootURL = StripTrailingSlash ( optarg ) ;
break ;
2017-12-07 01:15:13 +00:00
case ' s ' :
Config . Theme = optarg ;
break ;
2017-08-29 20:35:28 +00:00
case ' t ' :
2017-09-07 21:41:08 +00:00
Config . TemplatePlayerLocation = optarg ;
2017-12-18 15:10:02 +00:00
Config . Mode | = MODE_INTEGRATE ;
2017-09-07 21:41:08 +00:00
break ;
2017-11-11 00:34:47 +00:00
case ' U ' :
2017-10-18 21:07:29 +00:00
Config . UpdateInterval = StringToInt ( optarg ) ;
break ;
2017-11-18 00:27:33 +00:00
case ' v ' :
PrintVersions ( ) ;
return RC_SUCCESS ;
2017-09-07 21:41:08 +00:00
case ' x ' :
Config . TemplateIndexLocation = optarg ;
2017-12-18 15:10:02 +00:00
Config . Mode | = MODE_INTEGRATE ;
2017-08-29 20:35:28 +00:00
break ;
case ' h ' :
default :
2017-10-18 21:07:29 +00:00
PrintUsage ( Args [ 0 ] , & DefaultConfig ) ;
2017-11-18 00:27:33 +00:00
return RC_SUCCESS ;
2017-08-29 20:35:28 +00:00
}
}
2017-11-11 00:34:47 +00:00
if ( StringsDiffer ( Config . ProjectID , " " ) )
2017-08-29 20:35:28 +00:00
{
2017-11-11 00:34:47 +00:00
Config . Edition = EDITION_PROJECT ;
2017-12-07 21:07:36 +00:00
for ( int ProjectInfoIndex = 0 ; ProjectInfoIndex < ArrayCount ( ProjectInfo ) ; + + ProjectInfoIndex )
{
if ( ! StringsDiffer ( Config . ProjectID , ProjectInfo [ ProjectInfoIndex ] . ProjectID ) )
{
if ( StringsDiffer ( ProjectInfo [ ProjectInfoIndex ] . Medium , " " ) )
{
Config . DefaultMedium = ProjectInfo [ ProjectInfoIndex ] . Medium ;
}
if ( StringsDiffer ( ProjectInfo [ ProjectInfoIndex ] . AltURLPrefix , " " ) )
{
2017-12-12 23:24:10 +00:00
Config . PlayerURLPrefix = ProjectInfo [ ProjectInfoIndex ] . AltURLPrefix ;
2017-12-07 21:07:36 +00:00
}
break ;
}
}
2017-08-29 20:35:28 +00:00
}
2017-06-25 18:22:54 +00:00
2017-08-29 20:35:28 +00:00
bool ValidDefaultMedium = FALSE ;
for ( int i = 0 ; i < ArrayCount ( CategoryMedium ) ; + + i )
{
if ( ! StringsDiffer ( Config . DefaultMedium , CategoryMedium [ i ] . Medium ) )
{
ValidDefaultMedium = TRUE ;
break ;
}
}
if ( ! ValidDefaultMedium )
{
fprintf ( stderr , " Specified default medium \" %s \" not available. Valid media are: \n " , Config . DefaultMedium ) ;
for ( int i = 0 ; i < ArrayCount ( CategoryMedium ) ; + + i )
{
fprintf ( stderr , " %s \n " , CategoryMedium [ i ] . Medium ) ;
}
fprintf ( stderr , " To have \" %s \" added to the list, contact matt@handmadedev.org \n " , Config . DefaultMedium ) ;
return 1 ;
}
2017-06-25 18:22:54 +00:00
2017-10-18 21:07:29 +00:00
// NOTE(matt): Init MemoryArena (it is global)
2017-08-29 20:35:28 +00:00
MemoryArena . Size = Megabytes ( 4 ) ;
if ( ! ( MemoryArena . Location = calloc ( MemoryArena . Size , 1 ) ) )
{
2017-10-18 21:07:29 +00:00
LogError ( LOG_EMERGENCY , " %s: %s " , Args [ 0 ] , strerror ( errno ) ) ;
2017-09-07 21:41:08 +00:00
return RC_RIP ;
2017-08-29 20:35:28 +00:00
}
MemoryArena . Ptr = MemoryArena . Location ;
2017-06-25 18:22:54 +00:00
2017-10-18 21:07:29 +00:00
# if DEBUG_MEM
FILE * MemLog = fopen ( " /home/matt/cinera_mem " , " a+ " ) ;
fprintf ( MemLog , " Allocated MemoryArena (%d) \n " , MemoryArena . Size ) ;
fclose ( MemLog ) ;
printf ( " Allocated MemoryArena (%d) \n " , MemoryArena . Size ) ;
# endif
2017-09-07 21:41:08 +00:00
2017-10-18 21:07:29 +00:00
# if DEBUG
printf ( " Allocated MemoryArena: %d \n \n " , MemoryArena . Size ) ;
# endif
buffer Errors ;
if ( ClaimBuffer ( & Errors , " Errors " , Kilobytes ( 1 ) ) = = RC_ARENA_FULL ) { goto RIP ; } ;
2017-06-25 18:22:54 +00:00
2017-08-29 20:35:28 +00:00
// NOTE(matt): Tree structure of buffer dependencies
// IncludesPlayer
// Menus
// Player
2017-11-11 00:34:47 +00:00
// ScriptPlayer
2017-10-18 21:07:29 +00:00
//
// IncludesIndex
// Index
2017-06-25 18:22:54 +00:00
2017-08-29 20:35:28 +00:00
buffers CollationBuffers ;
2017-10-18 21:07:29 +00:00
if ( ClaimBuffer ( & CollationBuffers . IncludesPlayer , " IncludesPlayer " , Kilobytes ( 1 ) ) = = RC_ARENA_FULL ) { goto RIP ; } ;
2017-11-18 00:27:33 +00:00
if ( ClaimBuffer ( & CollationBuffers . Menus , " Menus " , Kilobytes ( 32 ) ) = = RC_ARENA_FULL ) { goto RIP ; } ;
2017-10-18 21:07:29 +00:00
if ( ClaimBuffer ( & CollationBuffers . Player , " Player " , Kilobytes ( 256 ) ) = = RC_ARENA_FULL ) { goto RIP ; } ;
2017-11-11 00:34:47 +00:00
if ( ClaimBuffer ( & CollationBuffers . ScriptPlayer , " ScriptPlayer " , Kilobytes ( 8 ) ) = = RC_ARENA_FULL ) { goto RIP ; } ;
2017-06-25 18:22:54 +00:00
2017-10-18 21:07:29 +00:00
if ( ClaimBuffer ( & CollationBuffers . IncludesIndex , " IncludesIndex " , Kilobytes ( 1 ) ) = = RC_ARENA_FULL ) { goto RIP ; } ;
2017-11-11 00:34:47 +00:00
if ( ClaimBuffer ( & CollationBuffers . Search , " Search " , Kilobytes ( 32 ) ) = = RC_ARENA_FULL ) { goto RIP ; } ;
if ( ClaimBuffer ( & CollationBuffers . ScriptIndex , " ScriptIndex " , 256 ) = = RC_ARENA_FULL ) { goto RIP ; } ;
2017-08-29 20:35:28 +00:00
* CollationBuffers . Title = ' \0 ' ;
2017-06-25 18:22:54 +00:00
2017-11-18 00:27:33 +00:00
// NOTE(matt): Templating
//
// Config will contain paths of multiple templates
// App is running all the time, and picking up changes to the config as we go
// If we find a new template, we first of all validate it
2017-12-18 15:10:02 +00:00
// In our case here, we just want to straight up validate a template if Config.Mode & MODE_INTEGRATE
2017-11-18 00:27:33 +00:00
// And, in that same state, we gotta keep a Template buffer around
2017-10-18 21:07:29 +00:00
template * PlayerTemplate ;
template * IndexTemplate ;
2017-06-25 18:22:54 +00:00
2017-12-18 15:10:02 +00:00
if ( Config . Mode & MODE_INTEGRATE )
2017-08-29 20:35:28 +00:00
{
2017-10-18 21:07:29 +00:00
switch ( ValidateTemplate ( & Errors , & PlayerTemplate , PAGE_PLAYER ) )
2017-09-07 21:41:08 +00:00
{
case RC_INVALID_TEMPLATE : // Invalid template
case RC_ERROR_FILE : // Could not load template
case RC_ERROR_MEMORY : // Could not allocate memory for template
goto RIP ;
case RC_SUCCESS :
break ;
}
if ( Config . Edition = = EDITION_PROJECT )
{
2017-10-18 21:07:29 +00:00
switch ( ValidateTemplate ( & Errors , & IndexTemplate , PAGE_INDEX ) )
2017-09-07 21:41:08 +00:00
{
case RC_INVALID_TEMPLATE : // Invalid template
case RC_ERROR_MEMORY : // Could not allocate memory for template
case RC_ERROR_FILE : // Could not load template
goto RIP ;
case RC_SUCCESS :
break ;
}
}
2017-08-29 20:35:28 +00:00
}
2017-06-25 18:22:54 +00:00
2017-08-29 20:35:28 +00:00
// NOTE(matt)
//
// Single Edition == Loop over Args[FileIndex]
// Project Edition == Loop over Config.ProjectDir
//
// Integrating or not
2017-06-25 18:22:54 +00:00
2017-08-29 20:35:28 +00:00
if ( Config . Edition = = EDITION_PROJECT )
{
2017-10-18 21:07:29 +00:00
# if DEBUG_MEM
FILE * MemLog = fopen ( " /home/matt/cinera_mem " , " w+ " ) ;
fprintf ( MemLog , " Entered Project Edition \n " ) ;
fclose ( MemLog ) ;
# endif
2017-11-11 00:34:47 +00:00
// TODO(matt): Also log these startup messages?
2017-11-18 00:27:33 +00:00
PrintVersions ( ) ;
printf ( " \n "
2017-11-11 00:34:47 +00:00
" Globals \n "
2017-12-12 23:24:10 +00:00
" Cache Directory: \ e[1;30m(XDG_CACHE_HOME) \ e[0m \t %s \n "
2017-10-18 21:07:29 +00:00
" \n "
2017-11-11 00:34:47 +00:00
" Root \n "
2017-12-12 23:24:10 +00:00
" Directory: \ e[1;30m(-r) \ e[0m \t \t \t %s \n "
" URL: \ e[1;30m(-R) \ e[0m \t \t \t %s \n "
2017-11-11 00:34:47 +00:00
" Paths relative to root \n "
2017-12-12 23:24:10 +00:00
" CSS: \ e[1;30m(-c) \ e[0m \t \t \t %s \n "
" Images: \ e[1;30m(-i) \ e[0m \t \t \t %s \n "
" JS: \ e[1;30m(-j) \ e[0m \t \t \t %s \n " ,
2017-11-11 00:34:47 +00:00
Config . CacheDir ,
Config . RootDir ,
StringsDiffer ( Config . RootURL , " " ) ? Config . RootURL : " [empty] " ,
StringsDiffer ( Config . CSSDir , " " ) ? Config . CSSDir : " (same as root) " ,
StringsDiffer ( Config . ImagesDir , " " ) ? Config . ImagesDir : " (same as root) " ,
StringsDiffer ( Config . JSDir , " " ) ? Config . JSDir : " (same as root) " ) ;
2017-12-18 15:10:02 +00:00
if ( Config . Mode & MODE_INTEGRATE )
2017-11-11 00:34:47 +00:00
{
2017-12-12 23:24:10 +00:00
printf ( " Index Template: \ e[1;30m(-x) \ e[0m \t \t %s \n "
" Player Template: \ e[1;30m(-t) \ e[0m \t \t %s \n " ,
2017-11-11 00:34:47 +00:00
StringsDiffer ( Config . TemplateIndexLocation , " " ) ? Config . TemplateIndexLocation : " (same as root) " ,
StringsDiffer ( Config . TemplatePlayerLocation , " " ) ? Config . TemplatePlayerLocation : " (same as root) " ) ;
}
printf ( " \n "
" Project \n "
2017-12-12 23:24:10 +00:00
" Base \n "
" Directory: \ e[1;30m(-b) \ e[0m \t \t \t %s \n "
" URL: \ e[1;30m(-B) \ e[0m \t \t \t %s \n "
" Paths relative to base \n "
" Index Page: \ e[1;30m(-n) \ e[0m \t \t \t %s \n "
/* NOTE(matt): Here, I think, is where we'll split into sub-projects */
" Player Page(s): \ e[1;30m(-a) \ e[0m \t \t %s \n "
" ID: \ e[1;30m(-p) \ e[0m \t \t \t \t %s \n "
" Input Directory: \ e[1;30m(-d) \ e[0m \t \t %s \n "
" Player Page URL Prefix: \ e[1;30m(hardcoded) \ e[0m \t %s \n "
" Default Medium: \ e[1;30m(-m) \ e[0m \t \t %s \n "
" Style / Theme: \ e[1;30m(-s) \ e[0m \t \t \t %s \n "
" \n " ,
2017-11-11 00:34:47 +00:00
2017-12-12 23:24:10 +00:00
Config . BaseDir ,
StringsDiffer ( Config . BaseURL , " " ) ? Config . BaseURL : " [empty] " ,
StringsDiffer ( Config . IndexLocation , " " ) ? Config . IndexLocation : " (same as base) " ,
StringsDiffer ( Config . PlayerLocation , " " ) ? Config . PlayerLocation : " (directly descended from base) " ,
2017-11-11 00:34:47 +00:00
Config . ProjectID ,
Config . ProjectDir ,
2017-12-12 23:24:10 +00:00
StringsDiffer ( Config . PlayerURLPrefix , " " ) ? Config . PlayerURLPrefix : Config . ProjectID ,
2017-12-07 01:15:13 +00:00
Config . DefaultMedium ,
StringsDiffer ( Config . Theme , " " ) ? Config . Theme : Config . ProjectID ) ;
2017-10-18 21:07:29 +00:00
2017-12-12 23:24:10 +00:00
if ( ( StringsDiffer ( Config . IndexLocation , " " ) | | StringsDiffer ( Config . PlayerLocation , " " ) )
& & StringLength ( Config . BaseURL ) = = 0 )
{
printf ( " \ e[1;33mPlease set a Project Base URL (-B) so we can output the Index / Player pages to \n "
" locations other than the defaults \ e[0m \n " ) ;
return ( RC_SUCCESS ) ;
}
printf ( " ┌╼ Synchronising with annotation files in Project Input Directory ╾┐ \n " ) ;
2017-10-18 21:07:29 +00:00
SyncIndexWithInput ( & CollationBuffers , IndexTemplate , PlayerTemplate ) ;
2017-12-18 15:10:02 +00:00
if ( Config . Mode & MODE_ONESHOT )
2017-12-18 14:31:05 +00:00
{
goto RIP ;
}
2017-10-18 21:07:29 +00:00
2017-11-11 00:34:47 +00:00
printf ( " \n ┌╼ Monitoring Project Directory for \ e[1;32mnew \ e[0m, \ e[1;33medited \ e[0m and \ e[1;30mdeleted \ e[0m .hmml files ╾┐ \n " ) ;
2017-09-15 01:11:39 +00:00
int inotifyInstance = inotify_init1 ( IN_NONBLOCK ) ;
2017-10-18 21:07:29 +00:00
// NOTE(matt): Do we want to also watch IN_DELETE_SELF events?
int WatchDescriptor = inotify_add_watch ( inotifyInstance , Config . ProjectDir , IN_CLOSE_WRITE | IN_DELETE | IN_MOVED_FROM | IN_MOVED_TO ) ;
2017-09-15 01:11:39 +00:00
2017-10-18 21:07:29 +00:00
while ( MonitorDirectory ( & CollationBuffers , IndexTemplate , PlayerTemplate , inotifyInstance , WatchDescriptor ) ! = RC_ERROR_FATAL )
2017-09-15 01:11:39 +00:00
{
2017-10-18 21:07:29 +00:00
sleep ( Config . UpdateInterval ) ;
2017-09-15 01:11:39 +00:00
}
2017-09-07 21:41:08 +00:00
}
else
{
if ( optind = = ArgC )
2017-08-29 20:35:28 +00:00
{
fprintf ( stderr , " %s: requires at least one input .hmml file \n " , Args [ 0 ] ) ;
2017-10-18 21:07:29 +00:00
PrintUsage ( Args [ 0 ] , & DefaultConfig ) ;
2017-09-07 21:41:08 +00:00
goto RIP ;
2017-03-22 02:18:31 +00:00
}
2017-09-07 21:41:08 +00:00
NextFile :
2017-08-29 20:35:28 +00:00
for ( int FileIndex = optind ; FileIndex < ArgC ; + + FileIndex )
2017-03-22 02:18:31 +00:00
{
2017-10-18 21:07:29 +00:00
switch ( HMMLToBuffers ( & CollationBuffers , Args [ FileIndex ] ) )
2017-09-07 21:41:08 +00:00
{
// TODO(matt): Actually sort out the fatality of these cases, once we are always-on
case RC_ERROR_FILE :
case RC_ERROR_FATAL :
goto RIP ;
case RC_ERROR_HMML :
case RC_ERROR_MAX_REFS :
case RC_ERROR_QUOTE :
case RC_INVALID_REFERENCE :
2017-09-15 01:11:39 +00:00
if ( FileIndex < ( ArgC - 1 ) ) { goto NextFile ; }
else { goto RIP ; }
2017-09-07 21:41:08 +00:00
case RC_SUCCESS :
break ;
} ;
2017-10-18 21:07:29 +00:00
switch ( BuffersToHTML ( & CollationBuffers , PlayerTemplate , 0 , PAGE_PLAYER ) )
2017-09-07 21:41:08 +00:00
{
// TODO(matt): Actually sort out the fatality of these cases, once we are always-on
case RC_INVALID_TEMPLATE :
2017-10-18 21:07:29 +00:00
LogError ( LOG_ERROR , " Invalid player template: %s " , PlayerTemplate - > Metadata . Filename ) ;
2017-09-07 21:41:08 +00:00
case RC_ERROR_MEMORY :
case RC_ERROR_FILE :
case RC_ARENA_FULL :
goto RIP ;
case RC_SUCCESS :
break ;
} ;
2017-03-22 02:18:31 +00:00
}
}
2017-08-29 20:35:28 +00:00
2017-12-18 15:10:02 +00:00
if ( Config . Mode & MODE_INTEGRATE )
2017-08-29 20:35:28 +00:00
{
2017-10-18 21:07:29 +00:00
DeclaimTemplate ( PlayerTemplate ) ;
2017-09-07 21:41:08 +00:00
if ( Config . Edition = = EDITION_PROJECT )
{
2017-10-18 21:07:29 +00:00
DeclaimTemplate ( IndexTemplate ) ;
2017-09-07 21:41:08 +00:00
}
2017-08-29 20:35:28 +00:00
}
2017-11-11 00:34:47 +00:00
DeclaimBuffer ( & CollationBuffers . ScriptIndex ) ;
DeclaimBuffer ( & CollationBuffers . Search ) ;
2017-10-18 21:07:29 +00:00
DeclaimBuffer ( & CollationBuffers . IncludesIndex ) ;
2017-11-11 00:34:47 +00:00
DeclaimBuffer ( & CollationBuffers . ScriptPlayer ) ;
2017-10-18 21:07:29 +00:00
DeclaimBuffer ( & CollationBuffers . Player ) ;
DeclaimBuffer ( & CollationBuffers . Menus ) ;
DeclaimBuffer ( & CollationBuffers . IncludesPlayer ) ;
DeclaimBuffer ( & Errors ) ;
2017-09-07 21:41:08 +00:00
RIP :
2017-07-29 00:36:14 +00:00
free ( MemoryArena . Location ) ;
2017-10-18 21:07:29 +00:00
# if DEBUG_MEM
MemLog = fopen ( " /home/matt/cinera_mem " , " a+ " ) ;
fprintf ( MemLog , " Freed MemoryArena \n " ) ;
fclose ( MemLog ) ;
printf ( " Freed MemoryArena \n " ) ;
# endif
2017-03-10 14:19:25 +00:00
}