1919#include <ctype.h>
2020#include <time.h>
2121
22- #if defined(_WIN32 )
23- #include <winsock2.h>
24- #else
25- #include <unistd.h>
26- #endif
27-
28-
29- // Parses a string like: "key": "value"
30- static bool match_key_value (const char * * ptr , const char * key , const char * value ) {
31- const char * p = * ptr ;
32- while (isspace (* p )) p ++ ;
33- size_t klen = strlen (key );
34- if (strncmp (p , "\"" , 1 ) != 0 || strncmp (p + 1 , key , klen ) != 0 || strncmp (p + 1 + klen , "\":" , 2 ) != 0 )
35- return false;
36- p += klen + 3 ;
37- while (isspace (* p )) p ++ ;
38- size_t vlen = strlen (value );
39- if (strncmp (p , "\"" , 1 ) != 0 || strncmp (p + 1 , value , vlen ) != 0 || strncmp (p + 1 + vlen , "\"" , 1 ) != 0 )
40- return false;
41- * ptr = p + vlen + 2 ;
42- return true;
43- }
44-
45- static bool skip_key (const char * * ptr , const char * key ) {
46- const char * p = * ptr ;
47- while (isspace (* p )) p ++ ;
48- size_t klen = strlen (key );
49- if (strncmp (p , "\"" , 1 ) != 0 || strncmp (p + 1 , key , klen ) != 0 || strncmp (p + 1 + klen , "\":" , 2 ) != 0 )
50- return false;
51- * ptr = p + klen + 3 ;
52- return true;
53- }
54-
55- static bool skip_symbol (const char * * ptr , char symbol ) {
56- const char * p = * ptr ;
57- while (isspace (* p )) p ++ ;
58- if (* p != symbol )
59- return false;
60- * ptr = p + 1 ;
61- return true;
62- }
63-
64- static bool skip_comma (const char * * ptr ) {
65- const char * p = * ptr ;
66- while (isspace (* p )) p ++ ;
67- if (* p == ',' ) {
68- * ptr = p + 1 ;
69- return true;
70- }
71- return false;
72- }
73-
74- static bool parse_string_field (const char * * ptr , const char * key , char * out , size_t max ) {
75- if (!skip_key (ptr , key )) return false;
76- if (!skip_symbol (ptr , '"' )) return false;
77-
78- const char * p = * ptr ;
79- size_t i = 0 ;
80-
81- while (* p && * p != '"' && i < max - 1 ) {
82- if (* p == '\\' && * (p + 1 )) p ++ ; // skip escape
83- out [i ++ ] = * p ++ ;
84- }
85-
86- if (* p != '"' ) return false;
87- out [i ] = '\0' ;
88- * ptr = p + 1 ;
89- return true;
90- }
91-
92- static bool parse_number_field (const char * * ptr , const char * key , double * out_d , uint64_t * out_u64 , int * out_i , uint32_t * out_u32 ) {
93- if (!skip_key (ptr , key )) return false;
94-
95- char * end ;
96- const char * p = * ptr ;
97-
98- while (isspace (* p )) p ++ ;
99-
100- if (out_d ) {
101- * out_d = strtod (p , & end );
102- } else if (out_u64 ) {
103- * out_u64 = strtoull (p , & end , 10 );
104- } else if (out_i ) {
105- * out_i = strtol (p , & end , 10 );
106- } else if (out_u32 ) {
107- * out_u32 = strtoul (p , & end , 10 );
108- } else {
109- return false;
110- }
111-
112- if (end == p ) return false;
113- * ptr = end ;
114- return true;
115- }
116-
11722// HASH Algorithm magic
11823
11924#if defined(_WIN32 ) || defined(_WIN64 )
@@ -134,27 +39,33 @@ uint64_t get_time_microseconds(void) {
13439#endif
13540
13641static uint64_t get_device_salt (void ) {
42+ // FNV-1a 64-bit base offset
13743 uint64_t hash = 0xcbf29ce484222325ULL ;
138- const char * user = getenv ("USER" );
139- const char * host = getenv ("HOSTNAME" );
140- char hostname [256 ];
141-
142- if (!host ) {
143- gethostname (hostname , sizeof (hostname ));
144- host = hostname ;
145- }
14644
147- if (user ) {
148- for (size_t i = 0 ; i < strlen (user ); ++ i ) {
149- hash ^= user [i ];
150- hash *= 0x100000001b3ULL ;
151- }
152- }
45+ // Cross-platform user and home detection
46+ #if defined(_WIN32 ) || defined(_WIN64 )
47+ const char * vars [] = {
48+ getenv ("USERNAME" ),
49+ getenv ("USERPROFILE" ),
50+ getenv ("COMPUTERNAME" )
51+ };
52+ #else
53+ const char * vars [] = {
54+ getenv ("USER" ),
55+ getenv ("HOME" ),
56+ getenv ("SHELL" ),
57+ getenv ("HOSTNAME" )
58+ };
59+ #endif
15360
154- if (host ) {
155- for (size_t i = 0 ; i < strlen (host ); ++ i ) {
156- hash ^= host [i ];
157- hash *= 0x100000001b3ULL ;
61+ // Mix in each variable if it exists
62+ for (size_t v = 0 ; v < sizeof (vars ) / sizeof (vars [0 ]); ++ v ) {
63+ const char * val = vars [v ];
64+ if (val ) {
65+ for (size_t i = 0 ; val [i ]; ++ i ) {
66+ hash ^= (uint8_t )val [i ];
67+ hash *= 0x100000001b3ULL ;
68+ }
15869 }
15970 }
16071
@@ -762,6 +673,94 @@ bool fossil_jellyfish_verify_chain(const fossil_jellyfish_chain* chain) {
762673 return true;
763674}
764675
676+ // Parses a string like: "key": "value"
677+ static bool match_key_value (const char * * ptr , const char * key , const char * value ) {
678+ const char * p = * ptr ;
679+ while (isspace (* p )) p ++ ;
680+ size_t klen = strlen (key );
681+ if (strncmp (p , "\"" , 1 ) != 0 || strncmp (p + 1 , key , klen ) != 0 || strncmp (p + 1 + klen , "\":" , 2 ) != 0 )
682+ return false;
683+ p += klen + 3 ;
684+ while (isspace (* p )) p ++ ;
685+ size_t vlen = strlen (value );
686+ if (strncmp (p , "\"" , 1 ) != 0 || strncmp (p + 1 , value , vlen ) != 0 || strncmp (p + 1 + vlen , "\"" , 1 ) != 0 )
687+ return false;
688+ * ptr = p + vlen + 2 ;
689+ return true;
690+ }
691+
692+ static bool skip_key (const char * * ptr , const char * key ) {
693+ const char * p = * ptr ;
694+ while (isspace (* p )) p ++ ;
695+ size_t klen = strlen (key );
696+ if (strncmp (p , "\"" , 1 ) != 0 || strncmp (p + 1 , key , klen ) != 0 || strncmp (p + 1 + klen , "\":" , 2 ) != 0 )
697+ return false;
698+ * ptr = p + klen + 3 ;
699+ return true;
700+ }
701+
702+ static bool skip_symbol (const char * * ptr , char symbol ) {
703+ const char * p = * ptr ;
704+ while (isspace (* p )) p ++ ;
705+ if (* p != symbol )
706+ return false;
707+ * ptr = p + 1 ;
708+ return true;
709+ }
710+
711+ static bool skip_comma (const char * * ptr ) {
712+ const char * p = * ptr ;
713+ while (isspace (* p )) p ++ ;
714+ if (* p == ',' ) {
715+ * ptr = p + 1 ;
716+ return true;
717+ }
718+ return false;
719+ }
720+
721+ static bool parse_string_field (const char * * ptr , const char * key , char * out , size_t max ) {
722+ if (!skip_key (ptr , key )) return false;
723+ if (!skip_symbol (ptr , '"' )) return false;
724+
725+ const char * p = * ptr ;
726+ size_t i = 0 ;
727+
728+ while (* p && * p != '"' && i < max - 1 ) {
729+ if (* p == '\\' && * (p + 1 )) p ++ ; // skip escape
730+ out [i ++ ] = * p ++ ;
731+ }
732+
733+ if (* p != '"' ) return false;
734+ out [i ] = '\0' ;
735+ * ptr = p + 1 ;
736+ return true;
737+ }
738+
739+ static bool parse_number_field (const char * * ptr , const char * key , double * out_d , uint64_t * out_u64 , int * out_i , uint32_t * out_u32 ) {
740+ if (!skip_key (ptr , key )) return false;
741+
742+ char * end ;
743+ const char * p = * ptr ;
744+
745+ while (isspace (* p )) p ++ ;
746+
747+ if (out_d ) {
748+ * out_d = strtod (p , & end );
749+ } else if (out_u64 ) {
750+ * out_u64 = strtoull (p , & end , 10 );
751+ } else if (out_i ) {
752+ * out_i = strtol (p , & end , 10 );
753+ } else if (out_u32 ) {
754+ * out_u32 = strtoul (p , & end , 10 );
755+ } else {
756+ return false;
757+ }
758+
759+ if (end == p ) return false;
760+ * ptr = end ;
761+ return true;
762+ }
763+
765764/**
766765 * Parses a .jellyfish (JellyDSL) file with Meson-like syntax and extracts models.
767766 *
0 commit comments