31 if (argv == NULL || name == NULL) {
34 for (index = 0; index < argc - 1; index++) {
35 if (
streq(argv[index], name)) {
36 return argv[index + 1];
79 char *next_text = NULL;
80 size_t next_capacity = 0;
84 if (needed_length + 1 <= buffer->
capacity) {
88 while (next_capacity <= needed_length) {
89 if (next_capacity > ((
size_t)-1) / 2) {
94 next_text = (
char *)realloc(buffer->
text, next_capacity);
95 if (next_text == NULL) {
98 buffer->
text = next_text;
100 if (buffer->
length == 0) {
101 buffer->
text[0] =
'\0';
107 size_t text_length = 0;
108 if (buffer == NULL || text == NULL) {
111 text_length = strlen(text);
115 memcpy(buffer->
text + buffer->
length, text, text_length + 1);
116 buffer->
length += text_length;
121 if (buffer == NULL) {
136 if (buffer == NULL || format == NULL) {
139 va_start(args, format);
140 va_copy(args_copy, args);
141 needed = vsnprintf(NULL, 0, format, args_copy);
149 buffer->
length += (size_t)needed;
155 if (buffer == NULL) {
158 if (buffer->
text == NULL) {
175 if (argc < 1 || argv == NULL || entries == NULL) {
178 for (index = 0; index < entry_count; index++) {
181 int command_match = 0;
191 }
else if (argc >= 2) {
194 if (!command_match) {
197 if (argc < entry->argv_offset) {
214 if (manifest_path == NULL || manifest_path[0] ==
'\0') {
244 size_t len = strlen(line);
245 if (
len >= 2 && line[0] ==
'"' && line[
len - 1] ==
'"') {
246 line[
len - 1] =
'\0';
247 fputs(line + 1, stdout);
257 return emitted ? 0 : 66;
288static int parse_ymd_runtime(
const char *date_value,
int *year_out,
int *month_out,
int *day_out);
295static void month_shift_runtime(
int year,
int month,
int shift,
int *out_year,
int *out_month);
341 if (doc_out != NULL) {
344 if (text == NULL || text[0] ==
'\0') {
352 if (doc_out != NULL) {
392 if (doc == NULL || path == NULL || path[0] ==
'\0') {
413 size_t bytes_read = 0;
414 unsigned char *buffer = NULL;
415 if (out_size != NULL) {
418 if (path == NULL || path[0] ==
'\0') {
421 file = fopen(path,
"rb");
425 if (fseek(file, 0, SEEK_END) != 0) {
429 file_size = ftell(file);
430 if (file_size < 0 || fseek(file, 0, SEEK_SET) != 0) {
434 buffer = (
unsigned char *)malloc((
size_t)file_size + 1);
435 if (buffer == NULL) {
440 bytes_read = fread(buffer, 1, (
size_t)file_size, file);
441 if (bytes_read != (
size_t)file_size) {
447 buffer[(size_t)file_size] =
'\0';
449 if (out_size != NULL) {
450 *out_size = (size_t)file_size;
465 if (path == NULL || path[0] ==
'\0' || data == NULL) {
474 file = fopen(path,
"wb");
478 if (length > 0 && fwrite(data, 1, length, file) != length) {
482 if (fclose(file) != 0) {
496static const unsigned char *
memmem_runtime(
const unsigned char *haystack,
size_t haystack_len,
const unsigned char *needle,
size_t needle_len) {
498 if (haystack == NULL || needle == NULL || needle_len == 0 || haystack_len < needle_len) {
501 for (index = 0; index + needle_len <= haystack_len; index++) {
502 if (memcmp(haystack + index, needle, needle_len) == 0) {
503 return haystack + index;
515 const char *boundary_start = NULL;
516 const char *boundary_end = NULL;
517 if (content_type == NULL) {
520 boundary_start = strstr(content_type,
"boundary=");
521 if (boundary_start == NULL) {
525 if (*boundary_start ==
'"') {
527 boundary_end = strchr(boundary_start,
'"');
529 boundary_end = boundary_start;
530 while (*boundary_end !=
'\0' && *boundary_end !=
';' && !isspace((
unsigned char)*boundary_end)) {
534 if (boundary_end == NULL || boundary_end <= boundary_start) {
537 return dup_range(boundary_start, (
size_t)(boundary_end - boundary_start));
548 const char *match = NULL;
549 const char *value_start = NULL;
550 const char *value_end = NULL;
551 if (headers_text == NULL || key == NULL || key[0] ==
'\0') {
554 snprintf(pattern,
sizeof(pattern),
"%s=\"", key);
555 match = strstr(headers_text, pattern);
559 value_start = match + strlen(pattern);
560 value_end = strchr(value_start,
'"');
561 if (value_end == NULL || value_end < value_start) {
564 return dup_range(value_start, (
size_t)(value_end - value_start));
580 const unsigned char *body,
582 const char *content_type,
583 const char *field_name,
586 const unsigned char **out_content_start,
587 size_t *out_content_len
589 char *boundary = NULL;
590 char *delimiter = NULL;
591 char *delimiter_crlf = NULL;
592 char *delimiter_lf = NULL;
593 const unsigned char *cursor = NULL;
594 size_t delimiter_len = 0;
596 if (out_text != NULL) *out_text = NULL;
597 if (out_filename != NULL) *out_filename = NULL;
598 if (out_content_start != NULL) *out_content_start = NULL;
599 if (out_content_len != NULL) *out_content_len = 0;
600 if (body == NULL || body_len == 0 || content_type == NULL || field_name == NULL || field_name[0] ==
'\0') {
604 if (boundary == NULL || boundary[0] ==
'\0') {
608 delimiter_len = strlen(boundary) + 2;
609 delimiter = (
char *)malloc(delimiter_len + 1);
610 delimiter_crlf = (
char *)malloc(delimiter_len + 3);
611 delimiter_lf = (
char *)malloc(delimiter_len + 2);
612 if (delimiter == NULL || delimiter_crlf == NULL || delimiter_lf == NULL) {
613 free(boundary); free(delimiter); free(delimiter_crlf); free(delimiter_lf);
616 snprintf(delimiter, delimiter_len + 1,
"--%s", boundary);
617 snprintf(delimiter_crlf, delimiter_len + 3,
"\r\n--%s", boundary);
618 snprintf(delimiter_lf, delimiter_len + 2,
"\n--%s", boundary);
619 cursor =
memmem_runtime(body, body_len, (
const unsigned char *)delimiter, delimiter_len);
620 while (cursor != NULL && (
size_t)(cursor - body) + delimiter_len <= body_len) {
621 const unsigned char *part_cursor = cursor + delimiter_len;
622 const unsigned char *header_end = NULL;
623 const unsigned char *content_start = NULL;
624 const unsigned char *next_crlf = NULL;
625 const unsigned char *next_lf = NULL;
626 const unsigned char *next_marker = NULL;
627 size_t header_sep_len = 0;
628 size_t headers_len = 0;
629 size_t content_len = 0;
630 char *headers_text = NULL;
631 char *part_name = NULL;
632 char *filename = NULL;
633 char *text_value = NULL;
634 if ((
size_t)(part_cursor - body) + 2 <= body_len && part_cursor[0] ==
'-' && part_cursor[1] ==
'-') {
637 if ((
size_t)(part_cursor - body) + 2 <= body_len && part_cursor[0] ==
'\r' && part_cursor[1] ==
'\n') {
639 }
else if ((
size_t)(part_cursor - body) + 1 <= body_len && part_cursor[0] ==
'\n') {
642 next_crlf =
memmem_runtime(part_cursor, body_len - (
size_t)(part_cursor - body), (
const unsigned char *)
"\r\n\r\n", 4);
643 next_lf =
memmem_runtime(part_cursor, body_len - (
size_t)(part_cursor - body), (
const unsigned char *)
"\n\n", 2);
644 if (next_crlf != NULL && (next_lf == NULL || next_crlf <= next_lf)) {
645 header_end = next_crlf;
647 }
else if (next_lf != NULL) {
648 header_end = next_lf;
653 headers_len = (size_t)(header_end - part_cursor);
654 headers_text =
dup_range((
const char *)part_cursor, headers_len);
657 content_start = header_end + header_sep_len;
658 next_crlf =
memmem_runtime(content_start, body_len - (
size_t)(content_start - body), (
const unsigned char *)delimiter_crlf, delimiter_len + 2);
659 next_lf =
memmem_runtime(content_start, body_len - (
size_t)(content_start - body), (
const unsigned char *)delimiter_lf, delimiter_len + 1);
660 if (next_crlf != NULL && (next_lf == NULL || next_crlf <= next_lf)) {
661 next_marker = next_crlf;
662 }
else if (next_lf != NULL) {
663 next_marker = next_lf;
665 next_marker = body + body_len;
667 content_len = (size_t)(next_marker - content_start);
668 if (part_name != NULL &&
streq(part_name, field_name)) {
669 if (out_text != NULL) {
670 text_value =
dup_range((
const char *)content_start, content_len);
671 *out_text = text_value;
673 if (out_filename != NULL && filename != NULL) {
674 *out_filename = strdup(filename);
676 if (out_content_start != NULL) {
677 *out_content_start = content_start;
679 if (out_content_len != NULL) {
680 *out_content_len = content_len;
691 if (next_marker >= body + body_len) {
694 cursor = next_marker + (next_marker == next_crlf ? 2 : 1);
695 cursor =
memmem_runtime(cursor, body_len - (
size_t)(cursor - body), (
const unsigned char *)delimiter, delimiter_len);
699 free(delimiter_crlf);
714 if (field_name == NULL || field_name[0] ==
'\0') {
717 if (content_type != NULL && strstr(content_type,
"multipart/form-data") != NULL && body_path != NULL && body_path[0] !=
'\0') {
718 unsigned char *body_bytes = NULL;
721 if (body_bytes != NULL) {
728 char *body_text = NULL;
729 const char *raw_form = form_body != NULL ? form_body :
"";
730 if (body_path != NULL && body_path[0] !=
'\0' &&
path_exists(body_path)) {
732 raw_form = body_text != NULL ? body_text : raw_form;
752 if (field_name == NULL || field_name[0] ==
'\0') {
757 fputs(value, stdout);
772 if (name == NULL || name[0] ==
'\0') {
777 fputs(value, stdout);
792 if (key == NULL || key[0] ==
'\0') {
797 fputs(value, stdout);
812 if (key == NULL || key[0] ==
'\0') {
817 fputs(value, stdout);
832 unsigned char *body_bytes = NULL;
834 char *filename = NULL;
835 if (field_name == NULL || field_name[0] ==
'\0' || content_type == NULL || body_path == NULL) {
839 if (body_bytes == NULL) {
843 if (filename != NULL) {
844 fputs(filename, stdout);
860 unsigned char *body_bytes = NULL;
863 if (field_name == NULL || field_name[0] ==
'\0' || content_type == NULL || body_path == NULL) {
867 if (body_bytes == NULL) {
889 unsigned char *body_bytes = NULL;
892 if (field_name == NULL || field_name[0] ==
'\0' || content_type == NULL || body_path == NULL || output_path == NULL) {
896 if (body_bytes == NULL) {
915 char *raw_lines = NULL;
918 long count_value = 0;
922 count_value = strtol(count_text, NULL, 10);
925 if (count_value > 0) {
927 for (index = 1; index <= count_value; index++) {
929 char *label_value = NULL;
930 char *duration_value = NULL;
931 char *cost_value = NULL;
932 snprintf(field_name,
sizeof(field_name),
"booking_option_label_%ld", index);
934 snprintf(field_name,
sizeof(field_name),
"booking_option_duration_%ld", index);
936 snprintf(field_name,
sizeof(field_name),
"booking_option_cost_%ld", index);
938 if ((label_value != NULL && label_value[0] !=
'\0') || (duration_value != NULL && duration_value[0] !=
'\0') || (cost_value != NULL && cost_value[0] !=
'\0')) {
946 free(duration_value);
953 free(duration_value);
958 if (raw_lines == NULL || raw_lines[0] ==
'\0') {
962 if (raw_lines == NULL || raw_lines[0] ==
'\0') {
969 if (doc == NULL || array == NULL) {
976 char *copy = raw_lines;
978 char *saveptr = NULL;
979 while ((line = strtok_r(copy,
"\n", &saveptr)) != NULL) {
980 char *parts[3] = { NULL, NULL, NULL };
984 while (part_index < 3) {
985 parts[part_index++] = cursor;
986 cursor = strchr(cursor,
'|');
987 if (cursor == NULL) {
993 if ((parts[0] != NULL && parts[0][0] !=
'\0') || (parts[1] != NULL && parts[1][0] !=
'\0') || (parts[2] != NULL && parts[2][0] !=
'\0')) {
1022 if (manifest_path == NULL || manifest_path[0] ==
'\0' || slice_name == NULL || slice_name[0] ==
'\0') {
1037 if (path != NULL &&
streq(path, slice_name)) {
1040 if (layer != NULL) {
1074 fputs(
"{}", stdout);
1103 if (field == NULL || field[0] ==
'\0') {
1113 fputs(copy, stdout);
1129 if (manifest_path == NULL || key == NULL || key[0] ==
'\0') {
1138 if (value == NULL) {
1140 if (fixtures != NULL) {
1144 if (value == NULL) {
1146 if (database != NULL) {
1153 fputs(copy, stdout);
1166 if (manifest_path == NULL) {
1176 fputs(copy, stdout);
1195 if (candidate_form_id == NULL || !
streq(candidate_form_id, form_id)) {
1196 free(candidate_form_id);
1199 free(candidate_form_id);
1206 int match = candidate_field_id != NULL &&
streq(candidate_field_id, field_id);
1207 free(candidate_field_id);
1224 if (fixture == NULL || form_id == NULL || field_id == NULL) {
1233 if (field != NULL) {
1237 return field != NULL ? code : 0;
1249 if (fixture == NULL || form_id == NULL) {
1264 if (candidate_form_id == NULL || !
streq(candidate_form_id, form_id)) {
1265 free(candidate_form_id);
1268 free(candidate_form_id);
1274 fputs(line, stdout);
1275 fputc(
'\n', stdout);
1285 return emitted ? 0 : 0;
1299 if (fixture == NULL || form_id == NULL || field_id == NULL) {
1312 if (value == NULL) {
1318 fputs(value, stdout);
1338 if (fixture == NULL || form_id == NULL || field_id == NULL) {
1352 if (value != NULL) {
1353 fprintf(stdout,
"<option value=\"%s\"%s>%s</option>", value, (selected != NULL &&
streq(value, selected)) ?
" selected" :
"", label != NULL ? label : value);
1369 if (field == NULL || field[0] ==
'\0') {
1375 fputs(copy, stdout);
1389 if (field == NULL || field[0] ==
'\0') {
1395 fprintf(stdout,
"%ld", value);
1407 if (field == NULL || field[0] ==
'\0') {
1416 fputs(copy, stdout);
1432 fprintf(stdout,
"%lu", (
unsigned long)count);
1446 fputs(
"false", stdout);
1457 fputs(ok ?
"true" :
"false", stdout);
1464 char *segment = NULL;
1465 char *saveptr = NULL;
1467 if (root == NULL || path == NULL || path[0] ==
'\0') {
1470 copy = strdup(path);
1474 segment = strtok_r(copy,
".", &saveptr);
1475 while (segment != NULL && current != NULL) {
1481 segment = strtok_r(NULL,
".", &saveptr);
1494 if (path == NULL || path[0] ==
'\0') {
1498 if (value == NULL) {
1499 fputs(
"null", stdout);
1515 if (path == NULL || path[0] ==
'\0') {
1522 fputs(copy, stdout);
1537 if (path == NULL || path[0] ==
'\0') {
1541 if (value != NULL) {
1546 fprintf(stdout,
"%ld", number);
1558 if (path == NULL || path[0] ==
'\0') {
1565 fputs(bool_value ?
"true" :
"false", stdout);
1577 if (path == NULL || path[0] ==
'\0') {
1584 fprintf(stdout,
"%lu", (
unsigned long)count);
1599 fputs(line, stdout);
1600 fputc(
'\n', stdout);
1622 fputs(line, stdout);
1626 fputc(
'\n', stdout);
1643 if (app_root == NULL) {
1647 fputs(
"[]", stdout);
1653 if (mut_doc == NULL || array == NULL) {
1664 if (value != NULL &&
len > 0 && value[0] ==
'/') {
1666 }
else if (value != NULL) {
1667 size_t needed = strlen(app_root) + 1 +
len + 1;
1668 char *joined = (
char *)malloc(needed);
1669 if (joined == NULL) {
1674 snprintf(joined, needed,
"%s/%s", app_root, value);
1692 if (file_path == NULL || file_path[0] ==
'\0') {
1701 fputs(
"{}", stdout);
1716 if (file_path == NULL || file_path[0] ==
'\0' || field == NULL || field[0] ==
'\0') {
1727 fputs(copy, stdout);
1741 if (file_path == NULL || file_path[0] ==
'\0' || field == NULL || field[0] ==
'\0') {
1752 fprintf(stdout,
"%ld", value);
1764 if (file_path == NULL || file_path[0] ==
'\0' || section == NULL || section[0] ==
'\0') {
1773 if (array_value != NULL) {
1776 fprintf(stdout,
"%lu", (
unsigned long)count);
1787 return key != NULL && (
1788 streq(key,
"db_path") ||
1789 streq(key,
"db_tls_root_cert") ||
1790 streq(key,
"db_tls_cert") ||
1791 streq(key,
"db_tls_key")
1803 if (raw_value == NULL) {
1809 return strdup(raw_value);
1819 if (conf_path == NULL || conf_path[0] ==
'\0' || key == NULL || key[0] ==
'\0' || !
path_exists(conf_path)) {
1833 char *raw_value = NULL;
1834 char *resolved_value = NULL;
1835 char *app_conf_path = NULL;
1836 if (key == NULL || key[0] ==
'\0') {
1840 if (raw_value != NULL) {
1843 return resolved_value;
1847 if (raw_value != NULL) {
1851 free(app_conf_path);
1852 return resolved_value;
1866 if (app_root == NULL || app_root[0] ==
'\0' || key == NULL || key[0] ==
'\0') {
1870 if (value != NULL) {
1871 fputs(value, stdout);
1886 char *backend = NULL;
1887 if (app_root == NULL || app_root[0] ==
'\0') {
1891 if (backend != NULL) {
1892 fputs(backend, stdout);
1908 char *sqlite_path = NULL;
1909 if (app_root == NULL || app_root[0] ==
'\0') {
1913 if (sqlite_path != NULL) {
1914 fputs(sqlite_path, stdout);
1929 if (safe_name == NULL) {
1932 fputs(safe_name, stdout);
1947 if (disk_path != NULL) {
1948 fputs(disk_path, stdout);
1991 char *result =
store_uploaded_media(artifact_dir, content_type, body_path, field_name, target_url_dir, fallback_filename);
1992 if (result != NULL) {
1993 fputs(result, stdout);
2006 char *path_copy = NULL;
2007 char *segment = NULL;
2009 const char *path_env = NULL;
2010 if (command == NULL || command[0] ==
'\0') {
2013 if (strchr(command,
'/') != NULL) {
2014 return access(command, X_OK) == 0 ? strdup(command) : NULL;
2016 path_env = getenv(
"PATH");
2017 if (path_env == NULL || path_env[0] ==
'\0') {
2020 path_copy = strdup(path_env);
2021 if (path_copy == NULL) {
2024 for (segment = strtok_r(path_copy,
":", &state); segment != NULL; segment = strtok_r(NULL,
":", &state)) {
2025 char *candidate =
path_join(segment, command);
2026 if (candidate != NULL && access(candidate, X_OK) == 0) {
2038 int present = resolved != NULL && resolved[0] !=
'\0';
2060 if (context == NULL || context->
buffer == NULL) {
2066 for (index = 0; index < column_count; index++) {
2067 const char *value = (column_values != NULL && column_values[index] != NULL) ? column_values[index] :
"";
2088 char *error_message = NULL;
2091 int sqlite_code = SQLITE_ERROR;
2093 if (output_out != NULL) {
2096 if (sqlite_path == NULL || sqlite_path[0] ==
'\0' || sql_text == NULL) {
2099 if (sqlite3_open(sqlite_path, &db) != SQLITE_OK || db == NULL) {
2105 capture.
buffer = &buffer;
2106 sqlite_code = sqlite3_exec(
2110 output_out != NULL ? &capture : NULL,
2113 if (sqlite_code == SQLITE_OK) {
2115 if (output_out != NULL) {
2117 if (*output_out == NULL) {
2118 *output_out = strdup(
"");
2120 if (*output_out != NULL) {
2127 sqlite3_free(error_message);
2134 char *output = NULL;
2143 if (buffer == NULL) {
2156 const char *cursor = value != NULL ? value :
"";
2157 while (*cursor !=
'\0') {
2168 char path_template[] =
"/tmp/pharos-runtime-XXXXXX";
2171 size_t text_len = text != NULL ? strlen(text) : 0;
2172 if (path_out == NULL) {
2176 fd = mkstemp(path_template);
2180 file = fdopen(fd,
"wb");
2183 remove(path_template);
2186 if (text_len > 0 && fwrite(text, 1, text_len, file) != text_len) {
2188 remove(path_template);
2191 if (fclose(file) != 0) {
2192 remove(path_template);
2195 *path_out = strdup(path_template);
2196 if (*path_out == NULL) {
2197 remove(path_template);
2204 char *resolved_path = NULL;
2207 char *output = NULL;
2208 if (code_out != NULL) {
2211 if (command == NULL || file_path == NULL) {
2215 if (resolved_path == NULL) {
2218 argv_list[argc++] = resolved_path;
2220 argv_list[argc++] = (
char *)arg1;
2223 argv_list[argc++] = (
char *)arg2;
2225 argv_list[argc++] = (
char *)file_path;
2226 argv_list[argc] = NULL;
2230 if (code_out != NULL) {
2234 free(resolved_path);
2239 static const struct {
2240 const char *command;
2243 } checksum_tools[] = {{
"sha256sum", NULL, NULL}, {
"shasum",
"-a",
"256"}};
2245 char *tmp_path = NULL;
2246 char *output = NULL;
2251 for (index = 0; index <
sizeof(checksum_tools) /
sizeof(checksum_tools[0]); index++) {
2260 if (code != 0 || output == NULL) {
2265 char *space = strchr(output,
' ');
2266 if (space != NULL) {
2274 char *tmp_path = NULL;
2275 char *output = NULL;
2276 char *signature = NULL;
2284 if (code != 0 || output == NULL) {
2289 char *first_space = strchr(output,
' ');
2290 char *length_text = NULL;
2291 if (first_space != NULL) {
2292 *first_space =
'\0';
2293 length_text = first_space + 1;
2294 while (*length_text ==
' ') {
2297 first_space = strchr(length_text,
' ');
2298 if (first_space != NULL) {
2299 *first_space =
'\0';
2301 signature = (
char *)malloc(strlen(output) + strlen(length_text) + 2);
2302 if (signature != NULL) {
2303 snprintf(signature, strlen(output) + strlen(length_text) + 2,
"%s:%s", output, length_text);
2312 char *sqlite_path = NULL;
2313 if (app_root == NULL || app_root[0] ==
'\0') {
2317 if (sqlite_path != NULL && sqlite_path[0] !=
'\0') {
2323 snprintf(buffer,
sizeof(buffer),
"/var/lib/pharos/%s.sqlite3", app_id != NULL && app_id[0] !=
'\0' ? app_id :
"app");
2324 return strdup(buffer);
2331 if (sqlite_path == NULL || app_root == NULL || list == NULL) {
2338 char *resolved_path = NULL;
2339 char *sql_text = NULL;
2344 sql_text = resolved_path != NULL ?
read_file_text(resolved_path) : NULL;
2345 free(resolved_path);
2346 if (sql_text == NULL) {
2369 if (config == NULL || app_root == NULL || list == NULL) {
2376 char *resolved_path = NULL;
2377 char *sql_text = NULL;
2382 sql_text = resolved_path != NULL ?
read_file_text(resolved_path) : NULL;
2383 free(resolved_path);
2384 if (sql_text == NULL) {
2404 char *forward_checksum = NULL;
2405 char *payload = NULL;
2406 char *migration_id = NULL;
2407 char *result = NULL;
2408 if (app_root == NULL || migration_record == NULL || backend == NULL) {
2416 char *resolved_path = NULL;
2417 char *file_text = NULL;
2423 if (resolved_path != NULL &&
path_exists(resolved_path)) {
2427 free(resolved_path);
2434 free(resolved_path);
2438 free(resolved_path);
2444 if (migration_id == NULL || forward_checksum == NULL) {
2446 free(forward_checksum);
2449 payload = (
char *)malloc(strlen(migration_id) + strlen(backend) + strlen(forward_checksum) + 64);
2450 if (payload != NULL) {
2451 snprintf(payload, strlen(migration_id) + strlen(backend) + strlen(forward_checksum) + 64,
"migration_id=%s\nbackend=%s\nforward_checksum=%s", migration_id, backend, forward_checksum);
2456 free(forward_checksum);
2466 return app_root != NULL ?
path_join(app_root,
"data/framework/runtime-state-export-sqlite.sql") : NULL;
2475 return app_root != NULL ?
path_join(app_root,
"data/framework/runtime-state-import-sqlite.sql") : NULL;
2484 return app_root != NULL ?
path_join(app_root,
"data/framework/runtime-state-export-postgresql.sql") : NULL;
2493 return app_root != NULL ?
path_join(app_root,
"data/framework/runtime-state-import-postgresql.sql") : NULL;
2516 if (config == NULL) {
2528 memset(config, 0,
sizeof(*config));
2539 if (app_root == NULL || app_root[0] ==
'\0' || config == NULL) {
2542 memset(config, 0,
sizeof(*config));
2554 config->
tls_mode = strdup(
"require");
2556 if (config->
host == NULL || config->
host[0] ==
'\0'
2557 || config->
port == NULL || config->
port[0] ==
'\0'
2559 || config->
user == NULL || config->
user[0] ==
'\0'
2578typedef struct pg_conn
PGconn;
2585 PGconn *(*PQconnectdb)(
const char *conninfo);
2592 char *(*PQgetvalue)(
const PGresult *res,
int row_number,
int column_number);
2597#define PHAROS_CONN_STATUS_OK_RUNTIME 0
2598#define PHAROS_PGRES_COMMAND_OK_RUNTIME 1
2599#define PHAROS_PGRES_TUPLES_OK_RUNTIME 2
2600#define PHAROS_PGRES_SINGLE_TUPLE_RUNTIME 9
2610 const char *cursor = text != NULL ? text :
"";
2611 while (*cursor !=
'\0') {
2612 if (escape_quotes && (*cursor ==
'\\' || *cursor ==
'\'')) {
2633 if (buffer == NULL || key == NULL || key[0] ==
'\0' || value == NULL || value[0] ==
'\0') {
2653 if (path == NULL || path[0] ==
'\0') {
2656 return dlopen(path, RTLD_NOW | RTLD_LOCAL);
2667 static const char *
const basenames[] = {
"libpq.5.dylib",
"libpq.dylib",
"libpq.so.5",
"libpq.so", NULL};
2668 const char *prefix = NULL;
2674 memset(api, 0,
sizeof(*api));
2678 prefix = getenv(
"PHAROS_LIBPQ_PREFIX");
2679 if (prefix != NULL && prefix[0] !=
'\0') {
2680 char *lib_dir =
path_join(prefix,
"lib");
2681 if (lib_dir != NULL) {
2682 for (index = 0; basenames[index] != NULL && api->
handle == NULL; index++) {
2683 char *candidate =
path_join(lib_dir, basenames[index]);
2684 if (candidate != NULL &&
path_exists(candidate)) {
2692 for (index = 0; basenames[index] != NULL && api->
handle == NULL; index++) {
2695 if (api->
handle == NULL) {
2712 memset(api, 0,
sizeof(*api));
2725 if (api != NULL && api->
handle != NULL) {
2730 memset(api, 0,
sizeof(*api));
2741 const char *password_value = NULL;
2742 if (config == NULL) {
2755 if (password_value != NULL && password_value[0] !=
'\0' && !
conninfo_append_kv_runtime(&buffer,
"password", password_value)) {
2779 int column_count = 0;
2781 int column_index = 0;
2782 if (output_out != NULL) {
2785 if (api == NULL || result == NULL || output_out == NULL) {
2790 for (row_index = 0; row_index < row_count; row_index++) {
2795 for (column_index = 0; column_index < column_count; column_index++) {
2796 const char *value = api->
PQgetvalue(result, row_index, column_index);
2808 if (*output_out == NULL) {
2825 PGconn *connection = NULL;
2827 char *conninfo = NULL;
2830 if (output_out != NULL) {
2833 if (config == NULL || sql_text == NULL) {
2840 connection = conninfo != NULL ? api.
PQconnectdb(conninfo) : NULL;
2844 result = api.
PQexec(connection, sql_text);
2845 if (result == NULL) {
2858 if (result != NULL) {
2861 if (connection != NULL) {
2876 const char *placeholder = NULL;
2877 size_t prefix_len = 0;
2878 size_t suffix_len = 0;
2879 char *sql_text = NULL;
2880 if (sql_template == NULL || escaped_state_json == NULL) {
2883 placeholder = strstr(sql_template,
"__STATE_JSON__");
2884 if (placeholder == NULL) {
2887 prefix_len = (size_t)(placeholder - sql_template);
2888 suffix_len = strlen(placeholder + strlen(
"__STATE_JSON__"));
2889 sql_text = (
char *)malloc(prefix_len + strlen(escaped_state_json) + suffix_len + 1);
2890 if (sql_text == NULL) {
2893 memcpy(sql_text, sql_template, prefix_len);
2894 memcpy(sql_text + prefix_len, escaped_state_json, strlen(escaped_state_json));
2895 memcpy(sql_text + prefix_len + strlen(escaped_state_json), placeholder + strlen(
"__STATE_JSON__"), suffix_len + 1);
2907 char *backend = NULL;
2908 char *sqlite_path = NULL;
2909 char *sql_path = NULL;
2910 char *sql_text = NULL;
2911 char *export_json = NULL;
2913 if (app_root == NULL || app_root[0] ==
'\0') {
2917 if (backend == NULL || backend[0] ==
'\0') {
2919 backend = strdup(
"sqlite");
2921 if (backend == NULL) {
2924 if (
streq(backend,
"sqlite")) {
2928 if (sqlite_path == NULL || sqlite_path[0] ==
'\0' || sql_text == NULL ||
run_sqlite_command_runtime(sqlite_path, sql_text, &export_json) != 0) {
2932 }
else if (
streq(backend,
"postgresql")) {
2958 char *backend = NULL;
2959 char *sqlite_path = NULL;
2960 char *table_present = NULL;
2961 char *count_text = NULL;
2963 if (app_root == NULL || app_root[0] ==
'\0') {
2967 if (backend == NULL || backend[0] ==
'\0') {
2969 backend = strdup(
"sqlite");
2971 if (backend == NULL) {
2974 if (
streq(backend,
"sqlite")) {
2976 if (sqlite_path != NULL && sqlite_path[0] !=
'\0') {
2977 table_present =
sqlite_query_value_runtime(sqlite_path,
"SELECT name FROM sqlite_master WHERE type='table' AND name='audit_event';");
2978 if (table_present == NULL || table_present[0] ==
'\0') {
2979 count_text = strdup(
"0");
2984 }
else if (
streq(backend,
"postgresql")) {
2986 char *table_exists = NULL;
2987 if (
run_postgresql_sql_capture_runtime(&pg,
"SELECT EXISTS (SELECT 1 FROM information_schema.tables WHERE table_schema = 'public' AND table_name = 'audit_event');", 1, &table_exists) == 0) {
2988 if (table_exists != NULL &&
streq(table_exists,
"t")) {
2992 count_text = strdup(
"0");
3000 free(table_present);
3015 char *backend = NULL;
3016 char *sqlite_path = NULL;
3017 char *current_audit_count = NULL;
3018 char *state_json = NULL;
3019 char *escaped_state_json = NULL;
3020 char *sql_template_path = NULL;
3021 char *sql_template = NULL;
3022 char *sql_text = NULL;
3025 if (app_root == NULL || app_root[0] ==
'\0' || state_path == NULL || state_path[0] ==
'\0') {
3029 if (backend == NULL || backend[0] ==
'\0') {
3031 backend = strdup(
"sqlite");
3033 if (backend == NULL) {
3036 if (expected_audit_count != NULL && expected_audit_count[0] !=
'\0') {
3038 if (current_audit_count == NULL || !
streq(current_audit_count, expected_audit_count)) {
3040 goto dynamic_relational_runtime_import_cleanup;
3045 if (
streq(backend,
"sqlite")) {
3048 sql_template = sql_template_path != NULL ?
read_file_text(sql_template_path) : NULL;
3050 if (sqlite_path == NULL || sqlite_path[0] ==
'\0' || sql_text == NULL) {
3051 goto dynamic_relational_runtime_import_cleanup;
3054 }
else if (
streq(backend,
"postgresql")) {
3056 goto dynamic_relational_runtime_import_cleanup;
3059 sql_template = sql_template_path != NULL ?
read_file_text(sql_template_path) : NULL;
3061 if (sql_text == NULL) {
3062 goto dynamic_relational_runtime_import_cleanup;
3066 dynamic_relational_runtime_import_cleanup:
3069 free(current_audit_count);
3071 free(escaped_state_json);
3072 free(sql_template_path);
3085 const unsigned char *cursor = (
const unsigned char *)text;
3086 if (text == NULL || text[0] ==
'\0') {
3089 while (*cursor !=
'\0') {
3090 if (!(isalnum(*cursor) || *cursor ==
'_')) {
3115 if (config == NULL || host == NULL || host[0] ==
'\0' || port == NULL || port[0] ==
'\0'
3116 || database == NULL || database[0] ==
'\0' || user == NULL || user[0] ==
'\0') {
3119 memset(config, 0,
sizeof(*config));
3120 config->
host = strdup(host);
3121 config->
port = strdup(port);
3122 config->
database = strdup(database);
3123 config->
user = strdup(user);
3124 config->
password_env_name = password_env != NULL && password_env[0] !=
'\0' ? strdup(password_env) : strdup(
"");
3125 config->
tls_mode = tls_mode != NULL && tls_mode[0] !=
'\0' ? strdup(tls_mode) : strdup(
"require");
3126 config->
tls_root_cert = tls_root_cert != NULL && tls_root_cert[0] !=
'\0' ? strdup(tls_root_cert) : strdup(
"");
3127 config->
tls_cert = tls_cert != NULL && tls_cert[0] !=
'\0' ? strdup(tls_cert) : strdup(
"");
3128 config->
tls_key = tls_key != NULL && tls_key[0] !=
'\0' ? strdup(tls_key) : strdup(
"");
3129 if (config->
host == NULL || config->
port == NULL || config->
database == NULL || config->
user == NULL
3157 if (source == NULL || database_name == NULL || database_name[0] ==
'\0' || copy_out == NULL) {
3160 memset(copy_out, 0,
sizeof(*copy_out));
3161 copy_out->
host = source->
host != NULL ? strdup(source->
host) : strdup(
"");
3162 copy_out->
port = source->
port != NULL ? strdup(source->
port) : strdup(
"");
3163 copy_out->
database = strdup(database_name);
3164 copy_out->
user = source->
user != NULL ? strdup(source->
user) : strdup(
"");
3170 if (copy_out->
host == NULL || copy_out->
port == NULL || copy_out->
database == NULL || copy_out->
user == NULL
3185 fputs(truthy ?
"1" :
"0", stdout);
3198 char *escaped_table = NULL;
3206 if (escaped_table == NULL) {
3209 snprintf(query,
sizeof(query),
"SELECT name FROM sqlite_master WHERE type = 'table' AND name = '%s' LIMIT 1;", escaped_table);
3211 exists = value != NULL &&
streq(value, table_name);
3212 free(escaped_table);
3231 snprintf(sql,
sizeof(sql),
"SELECT COUNT(*) FROM \"%s\";", table_name);
3233 fputs(value != NULL ? value :
"0", stdout);
3247 if (db_path == NULL || db_path[0] ==
'\0') {
3250 value =
sqlite_query_value_runtime(db_path,
"SELECT COUNT(*) FROM sqlite_master WHERE type = 'table' AND name NOT LIKE 'sqlite_%';");
3251 fputs(value != NULL ? value :
"0", stdout);
3266 char *escaped_id = NULL;
3267 char *escaped_backend = NULL;
3270 if (db_path == NULL || db_path[0] ==
'\0' || migration_id == NULL || backend == NULL) {
3275 if (escaped_id == NULL || escaped_backend == NULL) {
3277 free(escaped_backend);
3280 snprintf(query,
sizeof(query),
"SELECT COUNT(*) FROM pharos_schema_migration WHERE migration_id = '%s' AND backend = '%s';", escaped_id, escaped_backend);
3282 fputs(value != NULL ? value :
"0", stdout);
3284 free(escaped_backend);
3299 char *escaped_id = NULL;
3300 char *escaped_backend = NULL;
3303 if (db_path == NULL || db_path[0] ==
'\0' || migration_id == NULL || backend == NULL) {
3308 if (escaped_id == NULL || escaped_backend == NULL) {
3310 free(escaped_backend);
3313 snprintf(query,
sizeof(query),
"SELECT schema_version FROM pharos_schema_migration WHERE migration_id = '%s' AND backend = '%s' ORDER BY applied_at DESC LIMIT 1;", escaped_id, escaped_backend);
3315 fputs(value != NULL ? value :
"", stdout);
3317 free(escaped_backend);
3332 char *escaped_id = NULL;
3333 char *escaped_backend = NULL;
3336 if (db_path == NULL || db_path[0] ==
'\0' || migration_id == NULL || backend == NULL) {
3341 if (escaped_id == NULL || escaped_backend == NULL) {
3343 free(escaped_backend);
3346 snprintf(query,
sizeof(query),
"SELECT checksum_sha256 FROM pharos_schema_migration WHERE migration_id = '%s' AND backend = '%s' ORDER BY applied_at DESC LIMIT 1;", escaped_id, escaped_backend);
3348 fputs(value != NULL ? value :
"", stdout);
3350 free(escaped_backend);
3366 char *escaped_id = NULL;
3367 char *escaped_backend = NULL;
3368 char *escaped_checksum = NULL;
3370 if (db_path == NULL || db_path[0] ==
'\0' || migration_id == NULL || backend == NULL || checksum == NULL) {
3376 if (escaped_id == NULL || escaped_backend == NULL || escaped_checksum == NULL) {
3378 free(escaped_backend);
3379 free(escaped_checksum);
3382 snprintf(query,
sizeof(query),
"UPDATE pharos_schema_migration SET checksum_sha256 = '%s' WHERE migration_id = '%s' AND backend = '%s' AND (checksum_sha256 IS NULL OR TRIM(checksum_sha256) = '');", escaped_checksum, escaped_id, escaped_backend);
3384 free(escaped_backend);
3385 free(escaped_checksum);
3401 char *escaped_id = NULL;
3402 char *escaped_backend = NULL;
3403 char *escaped_checksum = NULL;
3405 if (db_path == NULL || db_path[0] ==
'\0' || migration_id == NULL || schema_version == NULL || backend == NULL || checksum == NULL) {
3411 if (escaped_id == NULL || escaped_backend == NULL || escaped_checksum == NULL) {
3413 free(escaped_backend);
3414 free(escaped_checksum);
3417 snprintf(query,
sizeof(query),
"INSERT INTO pharos_schema_migration (migration_id, schema_version, backend, checksum_sha256) VALUES ('%s', %s, '%s', '%s');", escaped_id, schema_version, escaped_backend, escaped_checksum);
3419 free(escaped_backend);
3420 free(escaped_checksum);
3432 char *table_present = NULL;
3433 char *count_text = NULL;
3434 if (db_path == NULL || db_path[0] ==
'\0') {
3437 table_present =
sqlite_query_value_runtime(db_path,
"SELECT name FROM sqlite_master WHERE type='table' AND name='audit_event';");
3438 if (table_present == NULL || table_present[0] ==
'\0') {
3442 fputs(count_text != NULL ? count_text :
"0", stdout);
3444 free(table_present);
3459 if (db_path == NULL || db_path[0] ==
'\0' || sql == NULL) {
3463 if (value != NULL) {
3464 fputs(value, stdout);
3479 if (db_path == NULL || db_path[0] ==
'\0' || sql == NULL) {
3496 if (db_path == NULL || db_path[0] ==
'\0' || file_path == NULL || file_path[0] ==
'\0') {
3518 char *escaped_database = NULL;
3530 if (escaped_database == NULL) {
3534 snprintf(query,
sizeof(query),
"SELECT 1 FROM pg_database WHERE datname = '%s' LIMIT 1;", escaped_database);
3540 free(escaped_database);
3557 char *escaped_database = NULL;
3558 char exists_query[2048];
3559 char create_query[2048];
3570 if (escaped_database == NULL) {
3574 snprintf(exists_query,
sizeof(exists_query),
"SELECT 1 FROM pg_database WHERE datname = '%s' LIMIT 1;", escaped_database);
3579 if (value != NULL &&
streq(value,
"1")) {
3583 snprintf(create_query,
sizeof(create_query),
"CREATE DATABASE \"%s\";", database_name);
3586 free(escaped_database);
3602 char *escaped_table = NULL;
3610 if (escaped_table == NULL) {
3614 snprintf(query,
sizeof(query),
"SELECT 1 FROM information_schema.tables WHERE table_schema = 'public' AND table_name = '%s' LIMIT 1;", escaped_table);
3620 free(escaped_table);
3641 snprintf(query,
sizeof(query),
"SELECT COUNT(*) FROM \"%s\";", table_name);
3644 fputs(value != NULL ? value :
"0", stdout);
3666 fputs(value != NULL ? value :
"0", stdout);
3683 char *escaped_id = NULL;
3684 char *escaped_backend = NULL;
3693 if (escaped_id == NULL || escaped_backend == NULL) {
3697 snprintf(query,
sizeof(query),
"SELECT COUNT(*) FROM pharos_schema_migration WHERE migration_id = '%s' AND backend = '%s';", escaped_id, escaped_backend);
3700 fputs(value != NULL ? value :
"0", stdout);
3704 free(escaped_backend);
3720 char *escaped_id = NULL;
3721 char *escaped_backend = NULL;
3730 if (escaped_id == NULL || escaped_backend == NULL) {
3734 snprintf(query,
sizeof(query),
"SELECT schema_version FROM pharos_schema_migration WHERE migration_id = '%s' AND backend = '%s' ORDER BY applied_at DESC LIMIT 1;", escaped_id, escaped_backend);
3737 fputs(value != NULL ? value :
"", stdout);
3741 free(escaped_backend);
3757 char *escaped_id = NULL;
3758 char *escaped_backend = NULL;
3767 if (escaped_id == NULL || escaped_backend == NULL) {
3771 snprintf(query,
sizeof(query),
"SELECT checksum_sha256 FROM pharos_schema_migration WHERE migration_id = '%s' AND backend = '%s' ORDER BY applied_at DESC LIMIT 1;", escaped_id, escaped_backend);
3774 fputs(value != NULL ? value :
"", stdout);
3778 free(escaped_backend);
3795 char *escaped_id = NULL;
3796 char *escaped_backend = NULL;
3797 char *escaped_checksum = NULL;
3806 if (escaped_id == NULL || escaped_backend == NULL || escaped_checksum == NULL) {
3810 snprintf(query,
sizeof(query),
"UPDATE pharos_schema_migration SET checksum_sha256 = '%s' WHERE migration_id = '%s' AND backend = '%s' AND (checksum_sha256 IS NULL OR BTRIM(checksum_sha256) = '');", escaped_checksum, escaped_id, escaped_backend);
3814 free(escaped_backend);
3815 free(escaped_checksum);
3828 char *table_exists = NULL;
3829 char *count_text = NULL;
3834 code =
run_postgresql_sql_capture_runtime(&config,
"SELECT EXISTS (SELECT 1 FROM information_schema.tables WHERE table_schema = 'public' AND table_name = 'audit_event');", 1, &table_exists);
3836 if (table_exists != NULL &&
streq(table_exists,
"t")) {
3839 fputs(count_text != NULL ? count_text :
"0", stdout);
3867 if (code == 0 && value != NULL) {
3868 fputs(value, stdout);
3926 unsigned char *bytes = NULL;
3929 unsigned long long hash = 1469598103934665603ULL;
3930 if (path == NULL || path[0] ==
'\0') {
3934 fputs(
"missing", stdout);
3938 if (bytes == NULL) {
3941 for (index = 0; index < length; index++) {
3942 hash ^= (
unsigned long long)bytes[index];
3943 hash *= 1099511628211ULL;
3945 fprintf(stdout,
"%016llx:%llu", hash, (
unsigned long long)length);
3960 char *export_json = NULL;
3961 if (app_root == NULL || app_root[0] ==
'\0') {
3965 if (export_json == NULL) {
3968 fputs(export_json, stdout);
3983 char *count_text = NULL;
3984 if (app_root == NULL || app_root[0] ==
'\0') {
3988 if (count_text == NULL) {
3991 fputs(count_text, stdout);
4021 if (app_root == NULL || app_root[0] ==
'\0') {
4037 if (app_root == NULL || app_root[0] ==
'\0') {
4053 if (app_root == NULL || app_root[0] ==
'\0') {
4063 if (app_root == NULL || app_root[0] ==
'\0') {
4109 if (file_path == NULL || backend_value == NULL || list_key == NULL || list_key[0] ==
'\0') {
4123 fputc(
'\n', stdout);
4137 if (file_path == NULL || backend_value == NULL || backend_value[0] ==
'\0') {
4146 if (backend != NULL) {
4163 if (file_path == NULL || backend_value == NULL || backend_value[0] ==
'\0') {
4180 if (migration_id != NULL) {
4181 fprintf(stdout,
"%s\t%ld\n", migration_id, schema_version);
4204 if (file_path == NULL || backend_value == NULL || migration_id == NULL || field_name == NULL || field_name[0] ==
'\0') {
4213 if (migration != NULL) {
4217 if (field_value != NULL) {
4226 if (backend_record != NULL) {
4227 if (
streq(field_name,
"forward_sql_files")) {
4229 }
else if (
streq(field_name,
"seed_sql_files")) {
4233 if (field_value != NULL) {
4239 fputs(
"[]", stdout);
4246 static const char *sections[] = {
4247 "users",
"sessions",
"tenants",
"tenant_memberships",
"password_resets",
"invites",
"rate_limit_events",
4248 "businesses",
"services",
"bookings",
"appointments",
"subscriptions",
"ratings",
"google_calendar_connections",
4249 "technicians",
"business_availability",
"technician_availability",
"email_verifications",
"idempotency_requests",
4250 "workflow_executions",
"workflow_execution_events",
"job_executions",
"job_execution_attempts",
"audit_log",
"webhook_receipts"
4258 if (file_path == NULL || file_path[0] ==
'\0') {
4276 for (i = 0; i <
sizeof(sections) /
sizeof(sections[0]); i++) {
4294 fputs(
"null", stdout);
4307 fputs(json, stdout);
4318 char *saveptr = NULL;
4322 if (text == NULL || prefix == NULL || prefix[0] ==
'\0') {
4325 copy = strdup(text);
4329 line = strtok_r(copy,
"&", &saveptr);
4330 while (line != NULL) {
4331 char *eq = strchr(line,
'=');
4333 size_t key_len = (size_t)(eq - line);
4334 const char *value = eq + 1;
4335 if (strncmp(line, prefix, strlen(prefix)) == 0) {
4336 size_t i = strlen(prefix);
4337 int key_ok = key_len > i;
4338 for (; i < key_len; i++) {
4339 if (!isdigit((
unsigned char)line[i])) {
4344 if (key_ok && value[0] !=
'\0') {
4345 char *endptr = NULL;
4346 long parsed = strtol(value, &endptr, 10);
4347 if (endptr != value && *endptr ==
'\0') {
4350 for (j = 0; j < count; j++) {
4351 if (values[j] == parsed) {
4356 if (!seen && count < (
sizeof(values) /
sizeof(values[0]))) {
4357 values[count++] = parsed;
4363 line = strtok_r(NULL,
"&", &saveptr);
4367 for (idx = 0; idx < count; idx++) {
4371 fprintf(stdout,
"%ld", values[idx]);
4380 if (start == NULL || trimmed_start == NULL || trimmed_len == NULL) {
4383 while (begin <
len && isspace((
unsigned char)start[begin])) {
4386 while (end > begin && isspace((
unsigned char)start[end - 1])) {
4389 *trimmed_start = start + begin;
4390 *trimmed_len = end - begin;
4396 if (text == NULL ||
len == 0) {
4399 for (i = 0; i <
len; i++) {
4400 if (!isdigit((
unsigned char)text[i])) {
4411 char *saveptr = NULL;
4418 copy = strdup(text);
4421 if (copy == NULL || mut_doc == NULL || array == NULL) {
4427 line = strtok_r(copy,
"\n", &saveptr);
4428 while (line != NULL) {
4429 size_t len = strlen(line);
4430 while (
len > 0 && line[
len - 1] ==
'\r') {
4434 char *first = strchr(line,
'|');
4435 char *second = first != NULL ? strchr(first + 1,
'|') : NULL;
4436 if (first != NULL && second != NULL) {
4437 const char *label_start = NULL;
4438 const char *duration_start = NULL;
4439 const char *cost_start = NULL;
4440 size_t label_len = 0;
4441 size_t duration_len = 0;
4442 size_t cost_len = 0;
4446 if (label_len > 0 && duration_len > 0 && cost_len > 0 &&
digits_only_runtime(duration_start, duration_len)) {
4447 char *label =
dup_range(label_start, label_len);
4448 char *duration_text =
dup_range(duration_start, duration_len);
4449 char *cost_value =
dup_range(cost_start, cost_len);
4451 long duration_value = duration_text != NULL ? strtol(duration_text, NULL, 10) : 0;
4452 if (label != NULL && duration_text != NULL && cost_value != NULL && obj != NULL &&
yyjson_mut_obj_add_strcpy(mut_doc, obj,
"label", label) &&
yyjson_mut_obj_add_int(mut_doc, obj,
"duration_minutes", duration_value) &&
yyjson_mut_obj_add_strcpy(mut_doc, obj,
"cost", cost_value) &&
yyjson_mut_arr_append(array, obj)) {
4455 free(duration_text);
4460 line = strtok_r(NULL,
"\n", &saveptr);
4481 if (base_json == NULL || overlay_json == NULL) {
4521 if (mut_doc == NULL || obj == NULL) {
4526 while (index < argc) {
4527 const char *option = argv[index];
4528 const char *key = NULL;
4529 const char *value = NULL;
4530 if (
streq(option,
"--string")) {
4531 if (index + 2 >= argc) {
4535 key = argv[index + 1];
4536 value = argv[index + 2];
4544 if (
streq(option,
"--number")) {
4545 char *endptr = NULL;
4547 if (index + 2 >= argc) {
4551 key = argv[index + 1];
4552 value = argv[index + 2];
4553 number = strtol(value != NULL ? value :
"0", &endptr, 10);
4554 if (value == NULL || endptr == value || *endptr !=
'\0' || !
yyjson_mut_obj_add_int(mut_doc, obj, key, number)) {
4561 if (
streq(option,
"--bool")) {
4563 if (index + 2 >= argc) {
4567 key = argv[index + 1];
4568 value = argv[index + 2];
4569 bool_value = value != NULL && (
streq(value,
"true") ||
streq(value,
"1") ||
streq(value,
"yes"));
4577 if (
streq(option,
"--null")) {
4578 if (index + 1 >= argc) {
4582 key = argv[index + 1];
4590 if (
streq(option,
"--json")) {
4593 if (index + 2 >= argc) {
4597 key = argv[index + 1];
4598 value = argv[index + 2];
4640 for (index = 0; text[index] !=
'\0'; index++) {
4641 unsigned char ch = (
unsigned char)text[index];
4642 if (ch < 0x20 || ch == 0x7f) {
4656 if (text == NULL || !isalpha((
unsigned char)text[0])) {
4659 for (index = 1; text[index] !=
'\0'; index++) {
4660 unsigned char ch = (
unsigned char)text[index];
4664 if (!(isalnum(ch) || ch ==
'+' || ch ==
'-' || ch ==
'.')) {
4680 return strncmp(text,
"https://", 8) == 0
4681 || strncmp(text,
"http://", 7) == 0
4682 || strncmp(text,
"mailto:", 7) == 0
4683 || strncmp(text,
"tel:", 4) == 0;
4695 if (text[0] ==
'\0') {
4716 if (text[0] ==
'\0') {
4741 if (buffer == NULL || value == NULL) {
4774 const char *colon = NULL;
4775 if (param_name != NULL) {
4776 *param_name = placeholder_name;
4778 if (placeholder_name == NULL) {
4781 colon = strchr(placeholder_name,
':');
4782 if (colon == NULL || colon == placeholder_name || colon[1] ==
'\0') {
4785 if (strncmp(placeholder_name,
"html:", 5) == 0) {
4786 if (param_name != NULL) {
4787 *param_name = placeholder_name + 5;
4791 if (strncmp(placeholder_name,
"path:", 5) == 0) {
4792 if (param_name != NULL) {
4793 *param_name = placeholder_name + 5;
4797 if (strncmp(placeholder_name,
"url:", 4) == 0) {
4798 if (param_name != NULL) {
4799 *param_name = placeholder_name + 4;
4816 if (template_text == NULL || params_root == NULL || !
yyjson_is_obj(params_root)) {
4819 while (template_text[i] !=
'\0') {
4820 if (template_text[i] ==
'{') {
4821 size_t start = i + 1;
4823 while (template_text[end] !=
'\0' && template_text[end] !=
'}') {
4826 if (template_text[end] ==
'}') {
4827 char *name =
dup_range(template_text + start, end - start);
4828 const char *param_name = NULL;
4832 kind = default_kind;
4855 if (rendered == NULL) {
4858 fputs(rendered, stdout);
4872 char *joined = NULL;
4874 if (base_path == NULL || base_path[0] ==
'\0' || relative_path == NULL || relative_path[0] ==
'\0') {
4877 needed = strlen(base_path) + 1 + strlen(relative_path) + 1;
4878 joined = (
char *)malloc(needed);
4879 if (joined != NULL) {
4880 snprintf(joined, needed,
"%s/%s", base_path, relative_path);
4886 char *template_text = NULL;
4887 char *rendered = NULL;
4888 if (template_file == NULL || template_file[0] ==
'\0' || params_root == NULL || !
yyjson_is_obj(params_root)) {
4892 if (template_text == NULL) {
4896 free(template_text);
4902 const char *base_name = NULL;
4903 if (media_url == NULL || media_url[0] ==
'\0') {
4906 if (business_slug != NULL && business_slug[0] !=
'\0') {
4907 char expected_prefix[512];
4908 snprintf(expected_prefix,
sizeof(expected_prefix),
"/media/businesses/%s/", business_slug);
4909 if (strncmp(media_url, expected_prefix, strlen(expected_prefix)) == 0) {
4910 return strdup(media_url);
4912 if (strncmp(media_url,
"/media/businesses/", 18) == 0) {
4913 base_name = strrchr(media_url,
'/');
4914 if (base_name != NULL) {
4915 size_t needed = strlen(
"/media/businesses/") + strlen(business_slug) + 1 + strlen(base_name + 1) + 1;
4916 out = (
char *)malloc(needed);
4918 snprintf(out, needed,
"/media/businesses/%s/%s", business_slug, base_name + 1);
4924 return strdup(media_url);
4929 const char *base_name = NULL;
4930 if (media_url == NULL || media_url[0] ==
'\0') {
4933 if (business_slug != NULL && business_slug[0] !=
'\0') {
4934 char expected_prefix[512];
4935 snprintf(expected_prefix,
sizeof(expected_prefix),
"/media/businesses/%s/services/", business_slug);
4936 if (strncmp(media_url, expected_prefix, strlen(expected_prefix)) == 0) {
4937 return strdup(media_url);
4939 if (strncmp(media_url,
"/media/businesses/", 18) == 0 && strstr(media_url,
"/services/") != NULL) {
4940 base_name = strrchr(media_url,
'/');
4941 if (base_name != NULL) {
4942 size_t needed = strlen(
"/media/businesses/") + strlen(business_slug) + strlen(
"/services/") + strlen(base_name + 1) + 1;
4943 out = (
char *)malloc(needed);
4945 snprintf(out, needed,
"/media/businesses/%s/services/%s", business_slug, base_name + 1);
4950 if (strncmp(media_url,
"/media/services/", 16) == 0) {
4951 base_name = strrchr(media_url,
'/');
4952 if (base_name != NULL) {
4953 size_t needed = strlen(
"/media/businesses/") + strlen(business_slug) + strlen(
"/services/") + strlen(base_name + 1) + 1;
4954 out = (
char *)malloc(needed);
4956 snprintf(out, needed,
"/media/businesses/%s/services/%s", business_slug, base_name + 1);
4962 return strdup(media_url);
4973 static const struct {
4974 const char *page_id;
4975 const char *relative_path;
4977 {
"account_created",
"templates/registration/account_created.page.json" },
4978 {
"login",
"templates/registration/login.page.json" },
4979 {
"register",
"templates/registration/register.page.json" },
4980 {
"password_reset_request",
"templates/registration/password_reset_request.page.json" },
4981 {
"password_reset_sent",
"templates/registration/password_reset_sent.page.json" },
4982 {
"password_reset_complete",
"templates/registration/password_reset_complete.page.json" },
4983 {
"password_reset_complete_done",
"templates/registration/password_reset_complete_done.page.json" },
4984 {
"invite_created",
"templates/registration/invite_created.page.json" },
4985 {
"invite_accept",
"templates/registration/invite_accept.page.json" },
4986 {
"invite_accept_done",
"templates/registration/invite_accept_done.page.json" },
4987 {
"profile",
"templates/profile/profile.page.json" },
4988 {
"appointments",
"templates/appointments/appointments.page.json" },
4989 {
"appointment_detail",
"templates/appointments/detail/appointment_detail.page.json" },
4990 {
"booking_detail",
"templates/bookings/booking_detail.page.json" },
4991 {
"calendar",
"templates/calendar/calendar.page.json" },
4992 {
"explore",
"templates/explore/explore.page.json" },
4993 {
"qr_upload",
"templates/qr/qr_upload.page.json" },
4994 {
"diagnostics",
"templates/appointments/diagnostics.page.json" },
4995 {
"business_landing",
"templates/businesses/business_landing.page.json" },
4996 {
"business_detail_admin",
"templates/businesses/business_detail_admin.page.json" },
4997 {
"business_dashboard",
"templates/businesses/business_dashboard.page.json" },
4998 {
"business_calendar",
"templates/businesses/business_calendar.page.json" },
4999 {
"business_availability_edit",
"templates/businesses/business_availability_edit.page.json" },
5000 {
"business_create",
"templates/businesses/business_create.page.json" },
5001 {
"business_edit",
"templates/businesses/business_edit.page.json" },
5002 {
"service_edit",
"templates/businesses/service_edit.page.json" },
5003 {
"technician_edit",
"templates/businesses/technician_edit.page.json" },
5004 {
"generated_admin_manage",
"templates/generated_admin/manage.page.json" },
5005 {
"generated_admin_edit",
"templates/generated_admin/edit.page.json" },
5006 {
"service_book_form",
"templates/businesses/service_book_form.page.json" },
5007 {
"booking_form",
"templates/bookings/booking_form.page.json" },
5008 {
"bookings",
"templates/bookings/bookings.page.json" }
5011 if (page_id == NULL || page_id[0] ==
'\0') {
5014 for (index = 0; index <
sizeof(entries) /
sizeof(entries[0]); index++) {
5015 if (
streq(page_id, entries[index].page_id)) {
5016 return entries[index].relative_path;
5023 static const char *patterns[] = {
5024 "templates/%s.page.json",
5025 "templates/%s/%s.page.json"
5028 const char *ucal_relative = NULL;
5029 if (app_root == NULL || app_root[0] ==
'\0' || page_id == NULL || page_id[0] ==
'\0') {
5032 for (index = 0; index <
sizeof(patterns) /
sizeof(patterns[0]); index++) {
5034 char *candidate = NULL;
5036 snprintf(relative,
sizeof(relative), patterns[index], page_id);
5038 snprintf(relative,
sizeof(relative), patterns[index], page_id, page_id);
5041 if (candidate != NULL &&
path_exists(candidate)) {
5043 return strdup(relative);
5048 return ucal_relative != NULL ? strdup(ucal_relative) : NULL;
5057 static const struct {
5058 const char *fragment_id;
5059 const char *relative_path;
5061 {
"business_logged_out_cta",
"templates/businesses/fragments/business_logged_out_cta.html" },
5062 {
"business_subscribed_cta",
"templates/businesses/fragments/business_subscribed_cta.html" },
5063 {
"business_subscribe_cta",
"templates/businesses/fragments/business_subscribe_cta.html" },
5064 {
"business_rating_form",
"templates/businesses/fragments/business_rating_form.html" },
5065 {
"business_service_card",
"templates/businesses/fragments/business_service_card.html" },
5066 {
"business_admin_service_card",
"templates/businesses/fragments/business_admin_service_card.html" },
5067 {
"business_admin_technician_card",
"templates/businesses/fragments/business_admin_technician_card.html" },
5068 {
"business_admin_availability_row",
"templates/businesses/fragments/business_admin_availability_row.html" },
5069 {
"business_admin_public_state_button",
"templates/businesses/fragments/business_admin_public_state_button.html" },
5070 {
"business_admin_empty_state",
"templates/businesses/fragments/business_admin_empty_state.html" },
5071 {
"business_dashboard_card",
"templates/businesses/fragments/business_dashboard_card.html" },
5072 {
"business_landing_image",
"templates/businesses/fragments/business_landing_image.html" },
5073 {
"business_rating_empty",
"templates/businesses/fragments/business_rating_empty.html" },
5074 {
"business_services_empty",
"templates/businesses/fragments/business_services_empty.html" },
5075 {
"business_rating_row",
"templates/businesses/fragments/business_rating_row.html" },
5076 {
"profile_subscription_row",
"templates/profile/fragments/subscription_row.html" },
5077 {
"profile_subscriptions_empty",
"templates/profile/fragments/subscriptions_empty.html" },
5078 {
"managed_appointment_row",
"templates/appointments/fragments/managed_appointment_row.html" },
5079 {
"customer_appointment_row",
"templates/appointments/fragments/customer_appointment_row.html" },
5080 {
"calendar_managed_appointment_row",
"templates/calendar/fragments/managed_appointment_row.html" },
5081 {
"calendar_customer_appointment_row",
"templates/calendar/fragments/customer_appointment_row.html" },
5082 {
"calendar_booking_row",
"templates/calendar/fragments/booking_row.html" },
5083 {
"explore_business_card",
"templates/explore/fragments/business_card.html" }
5086 if (fragment_id == NULL || fragment_id[0] ==
'\0') {
5089 for (index = 0; index <
sizeof(entries) /
sizeof(entries[0]); index++) {
5090 if (
streq(fragment_id, entries[index].fragment_id)) {
5091 return entries[index].relative_path;
5105 char *joined = NULL;
5106 if (app_root == NULL || app_root[0] ==
'\0' || relative_path == NULL || relative_path[0] ==
'\0') {
5109 needed = strlen(app_root) + 1 + strlen(relative_path) + 1;
5110 joined = (
char *)malloc(needed);
5111 if (joined == NULL) {
5114 snprintf(joined, needed,
"%s/%s", app_root, relative_path);
5130 char *spec_path = NULL;
5131 char *template_rel = NULL;
5132 char *template_path = NULL;
5134 char *layout_id = NULL;
5136 if (relative_path == NULL || app_root == NULL || app_root[0] ==
'\0') {
5137 free(relative_path);
5141 if (spec_path == NULL) {
5148 free(relative_path);
5159 if (out_doc == NULL || out_root == NULL
5172 free(relative_path);
5174 free(template_path);
5191 char *full_path = NULL;
5193 if (relative_path == NULL || app_root == NULL || app_root[0] ==
'\0') {
5197 if (full_path == NULL) {
5206 static const char *days[] = {
"Mon",
"Tue",
"Wed",
"Thu",
"Fri",
"Sat",
"Sun" };
5209 for (index = 0; index < (
sizeof(days) /
sizeof(days[0])); index++) {
5210 if (!
text_buffer_append_format_runtime(&buffer,
"<option value=\"%s\"%s>%s</option>", days[index], (selected_day != NULL &&
streq(selected_day, days[index])) ?
" selected" :
"", days[index])) {
5224 if (template_path == NULL || params_json == NULL) {
5234 if (rendered == NULL) {
5238 fputs(rendered, stdout);
5249 char *template_text = NULL;
5253 if (template_file == NULL || template_file[0] ==
'\0' || params_json == NULL) {
5257 if (template_text == NULL) {
5262 free(template_text);
5267 free(template_text);
5273 static const char *prefixes[] = {
"href=\"/",
"src=\"/",
"action=\"/",
"value=\"/"};
5279 if (base_path == NULL || base_path[0] ==
'\0' ||
streq(base_path,
"/")) {
5280 return strdup(html);
5282 while (html[index] !=
'\0') {
5283 size_t prefix_index = 0;
5285 for (prefix_index = 0; prefix_index <
sizeof(prefixes) /
sizeof(prefixes[0]); prefix_index++) {
5286 const char *prefix = prefixes[prefix_index];
5287 size_t prefix_length = strlen(prefix);
5288 if (strncmp(html + index, prefix, prefix_length) == 0) {
5295 index += prefix_length;
5315 char *result = NULL;
5321 if (result != NULL) {
5322 fputs(result, stdout);
5340 if (message == NULL || message[0] ==
'\0') {
5354 if (next_path == NULL || next_path[0] ==
'\0') {
5367 const char *error_html,
5368 const char *next_input,
5369 const char *account_type_options_html,
5370 const char *preview_html,
5371 const char *reset_post_path,
5372 const char *invite_email,
5373 const char *invite_path,
5374 const char *return_path,
5375 const char *invite_kind,
5376 const char *target_html,
5377 const char *form_action
5382 if (doc == NULL || root == NULL) {
5389 yyjson_mut_obj_add_strcpy(doc, root,
"account_type_options_html", account_type_options_html != NULL ? account_type_options_html :
"");
5404 static const char *
const language_codes[] = {
"en",
"ja",
"es",
"fr",
"de",
"pt"};
5405 static const char *
const language_labels[] = {
"English",
"日本語",
"Español",
"Français",
"Deutsch",
"Português"};
5408 for (index = 0; index <
sizeof(language_codes) /
sizeof(language_codes[0]); index++) {
5409 const char *selected =
streq(current_lang != NULL ? current_lang :
"en", language_codes[index]) ?
" selected" :
"";
5419 const char *app_root,
5420 const char *base_path,
5421 const char *layout_id,
5424 const char *current_lang,
5425 const char *current_request_target,
5427 int managed_business_count
5429 const char *effective_layout_id = layout_id != NULL && layout_id[0] !=
'\0' ? layout_id :
"default_shell";
5434 char *render_params_json = NULL;
5435 char *language_options = NULL;
5436 char *escaped_request_target = NULL;
5437 char *nav_language_form = NULL;
5438 const char *nav_home_path =
"/explore/";
5439 const char *nav_primary =
"";
5440 const char *nav_account =
"";
5443 char *layout_file = NULL;
5444 char *rendered = NULL;
5445 char *scoped = NULL;
5446 char *result = NULL;
5447 if (app_root == NULL || app_root[0] ==
'\0' || doc == NULL || root == NULL || title == NULL || body == NULL) {
5452 escaped_request_target =
html_escaped_dup_runtime(current_request_target != NULL && current_request_target[0] !=
'\0' ? current_request_target :
"/");
5453 if (language_options == NULL || escaped_request_target == NULL) {
5454 goto layout_page_cleanup;
5456 if (!
text_buffer_append_text_runtime(&language_form_buffer,
"<form action=\"/i18n/setlang/\" method=\"post\" class=\"language-form\"><input name=\"next\" type=\"hidden\" value=\"")
5458 || !
text_buffer_append_text_runtime(&language_form_buffer,
"\" /><label for=\"language-select\" class=\"sr-only\">Language</label><select id=\"language-select\" name=\"language\" onchange=\"this.form.submit()\" aria-label=\"Language\">")
5461 goto layout_page_cleanup;
5464 if (nav_language_form == NULL) {
5465 goto layout_page_cleanup;
5468 nav_home_path =
"/";
5469 if (managed_business_count > 0) {
5470 nav_primary =
"<a href=\"/appointments/\">Manage bookings</a> <a href=\"/businesses/\">My Businesses</a>";
5471 }
else if (
streq(effective_layout_id,
"account_shell")) {
5472 nav_primary =
"<a href=\"/appointments/\">Appointments</a>";
5473 nav_home_path =
"/explore/";
5474 }
else if (
streq(effective_layout_id,
"public_catalog_shell") ||
streq(effective_layout_id,
"public_business_shell")) {
5475 nav_primary =
"<a href=\"/appointments/\">Appointments</a>";
5476 nav_home_path =
"/explore/";
5479 || !
text_buffer_append_text_runtime(&nav_account_buffer,
" <a href=\"/profile/\">Profile</a><form method=\"post\" action=\"/accounts/logout/\" style=\"display:inline\"><button type=\"submit\" class=\"btn btn-secondary\">Log out</button></form>")) {
5480 goto layout_page_cleanup;
5482 nav_account = nav_account_buffer.
text != NULL ? nav_account_buffer.
text :
"";
5484 nav_home_path =
"/accounts/login/";
5485 nav_primary =
"<a href=\"/accounts/login/\">Log in</a> <a href=\"/accounts/register/\">Sign Up</a>";
5486 nav_account = nav_language_form;
5489 yyjson_mut_obj_add_strcpy(doc, root,
"current_lang", current_lang != NULL && current_lang[0] !=
'\0' ? current_lang :
"en");
5500 if (scoped != NULL) {
5501 result = strdup(scoped);
5506 free(language_options);
5507 free(escaped_request_target);
5508 free(nav_language_form);
5511 free(render_params_json);
5528 const char *effective_layout_id = layout_id != NULL && layout_id[0] !=
'\0' ? layout_id :
"default_shell";
5533 char *render_params_json = NULL;
5534 char *language_options = NULL;
5535 char *escaped_request_target = NULL;
5536 char *nav_language_form = NULL;
5537 const char *nav_home_path =
"/explore/";
5538 const char *nav_primary =
"";
5539 const char *nav_account =
"";
5542 char *layout_file = NULL;
5543 char *rendered = NULL;
5544 char *scoped = NULL;
5546 if (app_root == NULL || app_root[0] ==
'\0' || doc == NULL || root == NULL || title == NULL || body == NULL) {
5551 escaped_request_target =
html_escaped_dup_runtime(current_request_target != NULL && current_request_target[0] !=
'\0' ? current_request_target :
"/");
5552 if (language_options == NULL || escaped_request_target == NULL) {
5553 goto layout_page_html_cleanup;
5555 if (!
text_buffer_append_text_runtime(&language_form_buffer,
"<form action=\"/i18n/setlang/\" method=\"post\" class=\"language-form\"><input name=\"next\" type=\"hidden\" value=\"")
5557 || !
text_buffer_append_text_runtime(&language_form_buffer,
"\" /><label for=\"language-select\" class=\"sr-only\">Language</label><select id=\"language-select\" name=\"language\" onchange=\"this.form.submit()\" aria-label=\"Language\">")
5560 goto layout_page_html_cleanup;
5563 if (nav_language_form == NULL) {
5564 goto layout_page_html_cleanup;
5567 nav_home_path =
"/";
5568 if (managed_business_count > 0) {
5569 nav_primary =
"<a href=\"/appointments/\">Manage bookings</a> <a href=\"/businesses/\">My Businesses</a>";
5570 }
else if (
streq(effective_layout_id,
"account_shell")) {
5571 nav_primary =
"<a href=\"/appointments/\">Appointments</a>";
5572 nav_home_path =
"/explore/";
5573 }
else if (
streq(effective_layout_id,
"public_catalog_shell") ||
streq(effective_layout_id,
"public_business_shell")) {
5574 nav_primary =
"<a href=\"/appointments/\">Appointments</a>";
5575 nav_home_path =
"/explore/";
5578 || !
text_buffer_append_text_runtime(&nav_account_buffer,
" <a href=\"/profile/\">Profile</a><form method=\"post\" action=\"/accounts/logout/\" style=\"display:inline\"><button type=\"submit\" class=\"btn btn-secondary\">Log out</button></form>")) {
5579 goto layout_page_html_cleanup;
5581 nav_account = nav_account_buffer.
text != NULL ? nav_account_buffer.
text :
"";
5583 nav_home_path =
"/accounts/login/";
5584 nav_primary =
"<a href=\"/accounts/login/\">Log in</a> <a href=\"/accounts/register/\">Sign Up</a>";
5585 nav_account = nav_language_form;
5588 yyjson_mut_obj_add_strcpy(doc, root,
"current_lang", current_lang != NULL && current_lang[0] !=
'\0' ? current_lang :
"en");
5599 if (scoped != NULL) {
5600 fputs(scoped, stdout);
5603layout_page_html_cleanup:
5606 free(language_options);
5607 free(escaped_request_target);
5608 free(nav_language_form);
5611 free(render_params_json);
5626 const char *effective_layout_id = layout_id != NULL && layout_id[0] !=
'\0' ? layout_id :
"default_shell";
5629 char *language_options = NULL;
5630 char *escaped_request_target = NULL;
5631 char *nav_language_form = NULL;
5632 const char *nav_home_path =
"/explore/";
5633 const char *nav_primary =
"";
5634 const char *nav_account =
"";
5638 if (doc == NULL || root == NULL || title == NULL || body == NULL) {
5643 escaped_request_target =
html_escaped_dup_runtime(current_request_target != NULL && current_request_target[0] !=
'\0' ? current_request_target :
"/");
5644 if (language_options == NULL || escaped_request_target == NULL) {
5645 goto layout_page_cleanup;
5647 if (!
text_buffer_append_text_runtime(&language_form_buffer,
"<form action=\"/i18n/setlang/\" method=\"post\" class=\"language-form\"><input name=\"next\" type=\"hidden\" value=\"")
5649 || !
text_buffer_append_text_runtime(&language_form_buffer,
"\" /><label for=\"language-select\" class=\"sr-only\">Language</label><select id=\"language-select\" name=\"language\" onchange=\"this.form.submit()\" aria-label=\"Language\">")
5652 goto layout_page_cleanup;
5655 if (nav_language_form == NULL) {
5656 goto layout_page_cleanup;
5659 nav_home_path =
"/";
5660 if (managed_business_count > 0) {
5661 nav_primary =
"<a href=\"/appointments/\">Manage bookings</a> <a href=\"/businesses/\">My Businesses</a>";
5662 }
else if (
streq(effective_layout_id,
"account_shell")) {
5663 nav_primary =
"<a href=\"/appointments/\">Appointments</a>";
5664 nav_home_path =
"/explore/";
5665 }
else if (
streq(effective_layout_id,
"public_catalog_shell") ||
streq(effective_layout_id,
"public_business_shell")) {
5666 nav_primary =
"<a href=\"/appointments/\">Appointments</a>";
5667 nav_home_path =
"/explore/";
5670 || !
text_buffer_append_text_runtime(&nav_account_buffer,
" <a href=\"/profile/\">Profile</a><form method=\"post\" action=\"/accounts/logout/\" style=\"display:inline\"><button type=\"submit\" class=\"btn btn-secondary\">Log out</button></form>")) {
5671 goto layout_page_cleanup;
5673 nav_account = nav_account_buffer.
text != NULL ? nav_account_buffer.
text :
"";
5675 nav_home_path =
"/accounts/login/";
5676 nav_primary =
"<a href=\"/accounts/login/\">Log in</a> <a href=\"/accounts/register/\">Sign Up</a>";
5677 nav_account = nav_language_form;
5680 yyjson_mut_obj_add_strcpy(doc, root,
"current_lang", current_lang != NULL && current_lang[0] !=
'\0' ? current_lang :
"en");
5690 free(language_options);
5691 free(escaped_request_target);
5692 free(nav_language_form);
5702 int code = (error_html != NULL && next_input != NULL)
5703 ?
emit_template_page_params_json_runtime(error_html, next_input,
"",
"",
"",
"",
"",
"",
"",
"",
"")
5716 int code = (error_html != NULL && next_input != NULL)
5717 ?
emit_template_page_params_json_runtime(error_html, next_input, account_type_options_html != NULL ? account_type_options_html :
"",
"",
"",
"",
"",
"",
"",
"",
"")
5727 int code = error_html != NULL ?
emit_template_page_params_json_runtime(error_html,
"",
"",
"",
"",
"",
"",
"",
"",
"",
"") : 69;
5735 char *preview_html = NULL;
5737 if (preview_path == NULL || preview_path[0] ==
'\0') {
5738 return emit_template_page_params_json_runtime(
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"");
5749 code =
emit_template_page_params_json_runtime(
"",
"",
"", preview_html,
"",
"",
"",
"",
"",
"",
"");
5759 int code = (error_html != NULL && escaped_reset_post_path != NULL)
5760 ?
emit_template_page_params_json_runtime(error_html,
"",
"",
"", escaped_reset_post_path,
"",
"",
"",
"",
"",
"")
5763 free(escaped_reset_post_path);
5774 int code = (escaped_invite_email != NULL && escaped_invite_path != NULL && escaped_return_path != NULL)
5775 ?
emit_template_page_params_json_runtime(
"",
"",
"",
"",
"", escaped_invite_email, escaped_invite_path, escaped_return_path,
"",
"",
"")
5777 free(escaped_invite_email);
5778 free(escaped_invite_path);
5779 free(escaped_return_path);
5790 char *target_html = NULL;
5796 if (error_html == NULL || escaped_invite_email == NULL || escaped_invite_kind == NULL || escaped_form_action == NULL) {
5798 free(escaped_invite_email);
5799 free(escaped_invite_kind);
5800 free(escaped_form_action);
5803 if (target_business_name != NULL && target_business_name[0] !=
'\0') {
5809 free(escaped_invite_email);
5810 free(escaped_invite_kind);
5811 free(escaped_form_action);
5822 escaped_invite_email,
5825 escaped_invite_kind,
5826 target_html != NULL ? target_html :
"",
5830 free(escaped_invite_email);
5831 free(escaped_invite_kind);
5832 free(escaped_form_action);
5842 fprintf(stdout,
"{\"schema\":\"pharos.validation.report.v1\",\"report_id\":\"pharos.validation.report.v1\",\"form_id\":");
5844 fprintf(stdout,
",\"action_id\":");
5846 fprintf(stdout,
",\"field_errors\":[],\"form_errors\":[],\"actor_scope\":");
5848 fprintf(stdout,
",\"tenant_scope\":");
5854static int append_validation_error_runtime(
const char *report_json,
const char *array_name,
const char *field_id,
const char *rule_id,
const char *code_text,
const char *message) {
5862 if (root == NULL || !
yyjson_is_obj(root) || array_name == NULL) {
5875 if (errors == NULL || entry == NULL) {
5885 if (!
yyjson_mut_obj_add_strcpy(mut_doc, entry,
"rule_id", rule_id != NULL ? rule_id :
"") || !
yyjson_mut_obj_add_strcpy(mut_doc, entry,
"code", code_text != NULL ? code_text :
"") || !
yyjson_mut_obj_add_strcpy(mut_doc, entry,
"message", message != NULL ? message :
"") || !
yyjson_mut_arr_append(errors, entry)) {
5897 return append_validation_error_runtime(
json_option_value_runtime(argc, argv,
"--report-json"),
"field_errors",
json_option_value_runtime(argc, argv,
"--field-id"),
json_option_value_runtime(argc, argv,
"--rule-id"),
json_option_value_runtime(argc, argv,
"--code"),
json_option_value_runtime(argc, argv,
"--message"));
5901 return append_validation_error_runtime(
json_option_value_runtime(argc, argv,
"--report-json"),
"form_errors", NULL,
json_option_value_runtime(argc, argv,
"--rule-id"),
json_option_value_runtime(argc, argv,
"--code"),
json_option_value_runtime(argc, argv,
"--message"));
5917 fputs(count > 0 ?
"true" :
"false", stdout);
5918 return count > 0 ? 0 : 1;
5938 if (message != NULL && message[0] !=
'\0') {
5942 fprintf(stdout,
"[%s] %s", rule_id != NULL ? rule_id :
"", message);
5954 if (message != NULL && message[0] !=
'\0') {
5958 fprintf(stdout,
"[%s] %s", rule_id != NULL ? rule_id :
"", message);
5986 fprintf(stdout,
"<li><code>%s</code> · <strong>%s</strong> · %s</li>", rule_id != NULL ? rule_id :
"", field_id != NULL ? field_id :
"", message != NULL ? message :
"");
5997 fprintf(stdout,
"<li><code>%s</code> · %s</li>", rule_id != NULL ? rule_id :
"", message != NULL ? message :
"");
6013 fprintf(stdout,
"{\"schema\":\"pharos.error.v1\",\"ok\":false,\"error\":{\"type\":");
6015 fprintf(stdout,
",\"code\":");
6017 fprintf(stdout,
",\"message\":");
6019 fprintf(stdout,
",\"actor_scope\":");
6021 fprintf(stdout,
",\"tenant_scope\":");
6023 fprintf(stdout,
",\"details\":%s}}", (details_json != NULL && details_json[0] !=
'\0') ? details_json :
"null");
6031 char *actor_scope = NULL;
6032 char *tenant_scope = NULL;
6041 fprintf(stdout,
"{\"schema\":\"pharos.error.v1\",\"ok\":false,\"error\":{\"type\":\"validation_error\",\"code\":\"validation_failed\",\"message\":\"Validation failed.\",\"actor_scope\":");
6043 fprintf(stdout,
",\"tenant_scope\":");
6045 fprintf(stdout,
",\"details\":%s}}", report_json != NULL ? report_json :
"null");
6052 int64_t signed_value = 0;
6053 uint64_t unsigned_value = 0;
6054 double real_value = 0.0;
6055 if (value == NULL || out == NULL || !
yyjson_is_num(value)) {
6060 *out = (long)signed_value;
6065 *out = (long)unsigned_value;
6072 if ((
double)((
long)real_value) != real_value) {
6075 *out = (long)real_value;
6083 if (array_value == NULL || field_name == NULL || expected_value == NULL || !
yyjson_is_arr(array_value)) {
6102 long actual_value = 0;
6128 if (doc == NULL || obj == NULL || field_name == NULL || field_name[0] ==
'\0') {
6135 if (array_value != NULL) {
6139 if (array_value == NULL) {
6166 if (value == NULL) {
6171 fputs(text, stdout);
6182 if (array_value == NULL || field_name == NULL || !
yyjson_is_arr(array_value)) {
6250 unsigned char left_ch = 0;
6251 unsigned char right_ch = 0;
6252 if (left == NULL || right == NULL) {
6255 while (*left !=
'\0' && *right !=
'\0') {
6256 left_ch = (
unsigned char)*left;
6257 right_ch = (
unsigned char)*right;
6258 if (tolower(left_ch) != tolower(right_ch)) {
6264 return *left ==
'\0' && *right ==
'\0';
6275 if (users == NULL || email == NULL || email[0] ==
'\0' || !
yyjson_arr_iter_init(users, &iter)) {
6280 char *candidate_email = NULL;
6291 free(candidate_email);
6399 long business_id = 0;
6400 if (business == NULL) {
6424 long business_id = 0;
6425 if (service == NULL) {
6434 long business_id = 0;
6435 if (technician == NULL) {
6443 if (value == NULL) {
6450 fprintf(stdout,
"%ld", value);
6458 if (array_value == NULL || field_name == NULL || !
yyjson_mut_is_arr(array_value)) {
6493 fputs(default_value != NULL ? default_value :
"", stdout);
6498 fputs(copy, stdout);
6500 }
else if (default_value != NULL) {
6501 fputs(default_value, stdout);
6511 if (service != NULL) {
6515 long technician_id = 0;
6519 fprintf(stdout,
"%ld\n", technician_id);
6578 size_t next_capacity = 0;
6579 if (list == NULL || service == NULL) {
6585 if (items == NULL) {
6588 list->
items = items;
6626 long service_id = 0;
6627 long sort_order = 0;
6633 if (sort_order <= 0) {
6634 sort_order = service_id;
6640 if (list->
count > 1) {
6659 long business_id = 0;
6662 if (business == NULL) {
6685 ratings_array = mut_doc != NULL ?
yyjson_mut_arr(mut_doc) : NULL;
6686 services_array = mut_doc != NULL ?
yyjson_mut_arr(mut_doc) : NULL;
6687 if (mut_doc == NULL || page_obj == NULL || business_obj == NULL || ratings_array == NULL || services_array == NULL) {
6698 if (business_obj == NULL
6710 free(business_slug);
6718 free(business_slug);
6724 char *comment = NULL;
6732 if (rating_obj == NULL
6746 for (index = 0; index < service_list.
count; index++) {
6756 if (image_url == NULL || image_url[0] ==
'\0') {
6760 if (duration_minutes <= 0) {
6761 duration_minutes = 45;
6763 if (service_obj == NULL
6802 fputs(
"[]", stdout);
6806 long service_id = 0;
6807 long sort_order = 0;
6813 if (sort_order <= 0) {
6814 sort_order = service_id;
6821 if (list.
count > 1) {
6826 if (mut_doc == NULL || array == NULL) {
6832 for (index = 0; index < list.
count; index++) {
6846 char *booking_options_summary = NULL;
6847 char *technician_names = NULL;
6849 long order_index = (long)index + 1;
6850 int first_booking_option = 1;
6851 int first_technician = 1;
6852 if (image_url_raw == NULL || image_url_raw[0] ==
'\0') {
6853 free(image_url_raw);
6861 char fallback_label[64];
6862 size_t current_len = booking_options_summary != NULL ? strlen(booking_options_summary) : 0;
6863 const char *label_text = label;
6864 if (label_text == NULL || label_text[0] ==
'\0') {
6865 snprintf(fallback_label,
sizeof(fallback_label),
"%ld min", option_duration);
6866 label_text = fallback_label;
6869 const char *cost_text = cost != NULL ? cost :
"";
6870 size_t needed = current_len + (first_booking_option ? 0 : 2) + strlen(label_text) + 2 + strlen(cost_text) + 1;
6871 char *next = (
char *)realloc(booking_options_summary, needed);
6877 free(service_description);
6878 free(image_url_raw);
6879 free(booking_options_summary);
6880 free(technician_names);
6885 booking_options_summary = next;
6886 if (first_booking_option) {
6887 booking_options_summary[0] =
'\0';
6889 strcat(booking_options_summary,
", ");
6891 strcat(booking_options_summary, label_text);
6892 strcat(booking_options_summary,
" $");
6893 strcat(booking_options_summary, cost_text);
6894 first_booking_option = 0;
6902 char *technician_name = NULL;
6903 long technician_id = 0;
6913 long assigned_id = 0;
6926 if (technician_name != NULL && technician_name[0] !=
'\0') {
6927 size_t current_len = technician_names != NULL ? strlen(technician_names) : 0;
6928 size_t needed = current_len + (first_technician ? 0 : 2) + strlen(technician_name) + 1;
6929 char *next = (
char *)realloc(technician_names, needed);
6931 free(technician_name);
6934 free(service_description);
6935 free(image_url_raw);
6936 free(booking_options_summary);
6937 free(technician_names);
6942 technician_names = next;
6943 if (first_technician) {
6944 technician_names[0] =
'\0';
6946 strcat(technician_names,
", ");
6948 strcat(technician_names, technician_name);
6949 first_technician = 0;
6951 free(technician_name);
6954 if (duration_minutes <= 0) {
6955 duration_minutes = 45;
6964 || !
yyjson_mut_obj_add_strcpy(mut_doc, obj,
"booking_options_summary", booking_options_summary != NULL ? booking_options_summary :
"")
6972 free(service_description);
6973 free(image_url_raw);
6974 free(booking_options_summary);
6975 free(technician_names);
6982 free(service_description);
6983 free(image_url_raw);
6984 free(booking_options_summary);
6985 free(technician_names);
7002 fputs(
"[]", stdout);
7007 if (mut_doc == NULL || array == NULL) {
7016 long technician_id = 0;
7019 char *summary_html = NULL;
7020 int first_entry = 1;
7027 if (technician_availability != NULL &&
yyjson_arr_iter_init(technician_availability, &availability_iter)) {
7033 size_t current_len = summary_html != NULL ? strlen(summary_html) : 0;
7044 if (start == NULL) {
7050 needed = strlen(day != NULL ? day :
"") + strlen(start != NULL ? start :
"") + strlen(end != NULL ? end :
"") + 4;
7051 entry = (
char *)malloc(needed);
7052 if (entry == NULL) {
7062 snprintf(entry, needed,
"%s %s–%s", day != NULL ? day :
"", start != NULL ? start :
"", end != NULL ? end :
"");
7064 size_t total = current_len + (first_entry ? 0 : 4) + strlen(entry) + 1;
7065 char *next = (
char *)realloc(summary_html, total);
7077 summary_html = next;
7079 summary_html[0] =
'\0';
7081 strcat(summary_html,
"<br>");
7083 strcat(summary_html, entry);
7092 if (summary_html == NULL) {
7093 summary_html =
dup_range(
"No technician availability set.", strlen(
"No technician availability set."));
7128 long business_id = 0;
7129 long first_service_id = 0;
7131 if (business == NULL) {
7137 if (first_service != NULL) {
7144 availability_array = mut_doc != NULL ?
yyjson_mut_arr(mut_doc) : NULL;
7145 appointments_array = mut_doc != NULL ?
yyjson_mut_arr(mut_doc) : NULL;
7146 if (mut_doc == NULL || page_obj == NULL || business_obj == NULL || availability_array == NULL || appointments_array == NULL) {
7154 if (business_obj == NULL
7163 free(business_slug);
7168 free(business_slug);
7173 char *day_of_week = NULL;
7174 char *start_time = NULL;
7175 char *end_time = NULL;
7183 if (availability_obj == NULL
7204 long service_id = 0;
7205 long technician_id = 0;
7208 char *status = NULL;
7209 char *service_name = NULL;
7210 char *technician_name = NULL;
7224 if (appointment_obj == NULL
7230 || !
yyjson_mut_obj_add_strcpy(mut_doc, appointment_obj,
"technician_name", technician_name != NULL ? technician_name :
"Unassigned")
7236 free(technician_name);
7244 free(technician_name);
7264 char *business_name = NULL;
7265 char *business_description = NULL;
7266 char *raw_business_image = NULL;
7267 char *business_image_url = NULL;
7268 char *business_image_html = NULL;
7269 char *rating_form_html = NULL;
7270 char *cta_html = NULL;
7271 char *empty_html = NULL;
7272 long business_id = 0;
7275 if (business == NULL || app_root == NULL || app_root[0] ==
'\0') {
7293 if (business_image_url != NULL && business_image_url[0] !=
'\0') {
7296 if (doc == NULL || params == NULL) {
7298 goto business_public_cleanup;
7305 if (business_image_html == NULL) {
7306 goto business_public_cleanup;
7313 char *comment = NULL;
7323 if (doc == NULL || params == NULL) {
7326 goto business_public_cleanup;
7336 goto business_public_cleanup;
7341 if (rating_rows.
length == 0) {
7344 if (doc == NULL || params == NULL) {
7346 goto business_public_cleanup;
7353 goto business_public_cleanup;
7359 goto business_public_cleanup;
7361 if (service_list.
count > 0) {
7363 for (index = 0; index < service_list.
count; index++) {
7371 char *service_image_url = NULL;
7372 char *visual_html = NULL;
7373 char *card_html = NULL;
7376 if (doc == NULL || params == NULL) {
7378 free(service_name); free(service_cost); free(service_description); free(image_url_raw);
7379 goto business_public_cleanup;
7381 if (image_url_raw == NULL || image_url_raw[0] ==
'\0') {
7382 free(image_url_raw);
7387 if (duration_minutes <= 0) {
7388 duration_minutes = 45;
7400 free(service_name); free(service_cost); free(service_description); free(image_url_raw); free(service_image_url); free(visual_html);
7403 goto business_public_cleanup;
7408 if (service_cards.
length == 0) {
7411 if (doc == NULL || params == NULL) {
7413 goto business_public_cleanup;
7420 goto business_public_cleanup;
7428 if (doc == NULL || params == NULL) {
7430 goto business_public_cleanup;
7436 if (rating_form_html == NULL) {
7437 goto business_public_cleanup;
7443 const char *fragment_path = NULL;
7444 if (doc == NULL || params == NULL) {
7446 goto business_public_cleanup;
7452 fragment_path =
"templates/businesses/fragments/business_logged_out_cta.html";
7453 }
else if (subscribed) {
7454 fragment_path =
"templates/businesses/fragments/business_subscribed_cta.html";
7456 fragment_path =
"templates/businesses/fragments/business_subscribe_cta.html";
7460 if (cta_html == NULL) {
7461 goto business_public_cleanup;
7466 if (page_doc == NULL || page_obj == NULL) {
7467 goto business_public_cleanup;
7471 yyjson_mut_obj_add_strcpy(page_doc, page_obj,
"business_image_html", business_image_html != NULL ? business_image_html :
"");
7472 yyjson_mut_obj_add_strcpy(page_doc, page_obj,
"business_description", business_description != NULL ? business_description :
"");
7479business_public_cleanup:
7483 free(business_name);
7484 free(business_description);
7485 free(raw_business_image);
7486 free(business_image_url);
7487 free(business_image_html);
7488 free(rating_form_html);
7506 char *business_name = NULL;
7507 char *landing_path = NULL;
7508 char *manage_path = NULL;
7509 char *prev_month_path = NULL;
7510 char *next_month_path = NULL;
7511 char *selected_date = NULL;
7512 long business_id = 0;
7520 int first_offset = 0;
7521 int day_cell_count = 0;
7524 time_t now = time(NULL);
7525 struct tm *local = localtime(&now);
7526 if (business == NULL || app_root == NULL || app_root[0] ==
'\0') {
7531 year = (selected_year_text != NULL && selected_year_text[0] !=
'\0') ? (
int)strtol(selected_year_text, NULL, 10) : (local != NULL ? local->tm_year + 1900 : 1970);
7532 month = (selected_month_text != NULL && selected_month_text[0] !=
'\0') ? (
int)strtol(selected_month_text, NULL, 10) : (local != NULL ? local->tm_mon + 1 : 1);
7533 if (year < 1970) year = 1970;
7534 if (month < 1) month = 1;
7535 if (month > 12) month = 12;
7536 if (selected_date_text != NULL && selected_date_text[0] !=
'\0') {
7537 selected_date = strdup(selected_date_text);
7542 goto business_calendar_render_cleanup;
7550 while (day_cell_count < first_offset) {
7552 goto business_calendar_render_cleanup;
7556 while (day_number <= month_days) {
7558 const char *day_name = NULL;
7559 int available_count = 0;
7560 int appointment_count = 0;
7561 char *date_text = NULL;
7565 goto business_calendar_render_cleanup;
7571 char *row_day = NULL;
7576 if (row_day != NULL &&
streq(row_day, day_name)) {
7584 char *row_date = NULL;
7585 char *status = NULL;
7591 if (row_date != NULL &&
streq(row_date, date_text) && !(status != NULL &&
streq(status,
"cancelled"))) {
7592 appointment_count++;
7598 selected = selected_date != NULL &&
streq(selected_date, date_text);
7599 if (!
text_buffer_append_format_runtime(&week_row,
"<td class=\"day%s%s\" style=\"vertical-align:top;padding:0.5rem;min-width:88px;\">", available_count == 0 ?
" unavailable" :
"", selected ?
" today" :
"") ||
7600 !
text_buffer_append_format_runtime(&week_row,
"<a href=\"/business/%s/calendar/?year=%04d&month=%02d&date=%s\" class=\"btn calendar-day-chip\" style=\"display:inline-block;min-width:2.25rem;text-align:center;%s\">%d</a>", slug != NULL ? slug :
"", year, month, date_text, selected ?
"background:var(--primary);color:#fff;" :
"background:var(--gray-200);color:var(--gray-700);", day_number)) {
7602 goto business_calendar_render_cleanup;
7604 if (appointment_count > 0 && !
text_buffer_append_format_runtime(&week_row,
"<div style=\"margin-top:0.4rem;font-size:0.75rem;color:var(--gray-700);\">%d booked</div>", appointment_count)) {
7606 goto business_calendar_render_cleanup;
7608 if (available_count > 0) {
7609 if (!
text_buffer_append_format_runtime(&week_row,
"<div style=\"margin-top:0.2rem;font-size:0.75rem;color:var(--primary);\">%d slots</div>", available_count)) {
7611 goto business_calendar_render_cleanup;
7615 goto business_calendar_render_cleanup;
7619 goto business_calendar_render_cleanup;
7623 if ((day_cell_count % 7) == 0) {
7625 goto business_calendar_render_cleanup;
7631 while ((day_cell_count % 7) != 0) {
7633 goto business_calendar_render_cleanup;
7637 if (week_row.
length > 0) {
7639 goto business_calendar_render_cleanup;
7643 int selected_year = 0;
7644 int selected_month = 0;
7645 int selected_day = 0;
7646 if (selected_date == NULL || sscanf(selected_date,
"%d-%d-%d", &selected_year, &selected_month, &selected_day) != 3) {
7647 selected_year = year;
7648 selected_month = month;
7653 int schedule_found = 0;
7656 char *row_day = NULL;
7665 if (row_day != NULL &&
streq(row_day, selected_day_name)) {
7667 free(row_day); free(start); free(end);
7668 goto business_calendar_render_cleanup;
7676 free(row_day); free(start); free(end);
7677 goto business_calendar_render_cleanup;
7680 free(row_day); free(start); free(end);
7683 if (!schedule_found) {
7685 goto business_calendar_render_cleanup;
7690 goto business_calendar_render_cleanup;
7699 char *row_date = NULL;
7700 char *status = NULL;
7701 char *service_name = NULL;
7703 long appointment_id = 0;
7704 long service_id = 0;
7709 if (row_date == NULL || !
streq(row_date, selected_date != NULL ? selected_date :
"")) {
7720 if (doc == NULL || params == NULL) {
7722 goto business_calendar_render_cleanup;
7735 char action_path[256];
7736 snprintf(action_path,
sizeof(action_path),
"/appointments/%ld/complete/", appointment_id);
7738 snprintf(action_path,
sizeof(action_path),
"/appointments/%ld/cancel/", appointment_id);
7743 free(row_date); free(status); free(service_name);
7746 goto business_calendar_render_cleanup;
7752 goto business_calendar_render_cleanup;
7755 size_t needed = strlen(
"/b//") + strlen(slug != NULL ? slug :
"") + 1;
7756 landing_path = (
char *)malloc(needed);
7757 if (landing_path == NULL) {
7758 goto business_calendar_render_cleanup;
7760 snprintf(landing_path, needed,
"/b/%s/", slug != NULL ? slug :
"");
7763 size_t needed = strlen(
"/businesses//") + strlen(slug != NULL ? slug :
"") + 1;
7764 manage_path = (
char *)malloc(needed);
7765 if (manage_path == NULL) {
7766 goto business_calendar_render_cleanup;
7768 snprintf(manage_path, needed,
"/businesses/%s/", slug != NULL ? slug :
"");
7772 snprintf(buffer,
sizeof(buffer),
"/business/%s/calendar/?year=%04d&month=%02d", slug != NULL ? slug :
"", prev_year, prev_month);
7773 prev_month_path = strdup(buffer);
7774 snprintf(buffer,
sizeof(buffer),
"/business/%s/calendar/?year=%04d&month=%02d", slug != NULL ? slug :
"", next_year, next_month);
7775 next_month_path = strdup(buffer);
7776 if (prev_month_path == NULL || next_month_path == NULL) {
7777 goto business_calendar_render_cleanup;
7782 if (page_doc == NULL || page_obj == NULL) {
7783 goto business_calendar_render_cleanup;
7799business_calendar_render_cleanup:
7804 free(business_name);
7807 free(prev_month_path);
7808 free(next_month_path);
7809 free(selected_date);
7815 const char *state_path,
7816 const char *business_slug,
7817 const char *app_root,
7818 const char *selected_year_text,
7819 const char *selected_month_text,
7820 const char *selected_date_text
7835 char *business_name = NULL;
7836 char *landing_path = NULL;
7837 char *manage_path = NULL;
7838 char *prev_month_path = NULL;
7839 char *next_month_path = NULL;
7840 char *selected_date = NULL;
7841 char *result = NULL;
7842 long business_id = 0;
7850 int first_offset = 0;
7851 int day_cell_count = 0;
7853 time_t now = time(NULL);
7854 struct tm *local = localtime(&now);
7856 if (state_path == NULL || state_path[0] ==
'\0'
7857 || business_slug == NULL || business_slug[0] ==
'\0'
7858 || app_root == NULL || app_root[0] ==
'\0') {
7865 availability_rows = root != NULL ?
state_array_runtime(root,
"business_availability") : NULL;
7867 if (business == NULL) {
7874 year = (selected_year_text != NULL && selected_year_text[0] !=
'\0') ? (
int)strtol(selected_year_text, NULL, 10) : (local != NULL ? local->tm_year + 1900 : 1970);
7875 month = (selected_month_text != NULL && selected_month_text[0] !=
'\0') ? (
int)strtol(selected_month_text, NULL, 10) : (local != NULL ? local->tm_mon + 1 : 1);
7876 if (year < 1970) year = 1970;
7877 if (month < 1) month = 1;
7878 if (month > 12) month = 12;
7879 if (selected_date_text != NULL && selected_date_text[0] !=
'\0') {
7880 selected_date = strdup(selected_date_text);
7885 goto business_calendar_params_cleanup;
7893 while (day_cell_count < first_offset) {
7895 goto business_calendar_params_cleanup;
7899 while (day_number <= month_days) {
7901 const char *day_name = NULL;
7902 int available_count = 0;
7903 int appointment_count = 0;
7904 char *date_text = NULL;
7908 goto business_calendar_params_cleanup;
7914 char *row_day = NULL;
7919 if (row_day != NULL &&
streq(row_day, day_name)) {
7927 char *row_date = NULL;
7928 char *status = NULL;
7934 if (row_date != NULL &&
streq(row_date, date_text) && !(status != NULL &&
streq(status,
"cancelled"))) {
7935 appointment_count++;
7941 selected = selected_date != NULL &&
streq(selected_date, date_text);
7942 if (!
text_buffer_append_format_runtime(&week_row,
"<td class=\"day%s%s\" style=\"vertical-align:top;padding:0.5rem;min-width:88px;\">", available_count == 0 ?
" unavailable" :
"", selected ?
" today" :
"") ||
7943 !
text_buffer_append_format_runtime(&week_row,
"<a href=\"/business/%s/calendar/?year=%04d&month=%02d&date=%s\" class=\"btn calendar-day-chip\" style=\"display:inline-block;min-width:2.25rem;text-align:center;%s\">%d</a>", business_slug, year, month, date_text, selected ?
"background:var(--primary);color:#fff;" :
"background:var(--gray-200);color:var(--gray-700);", day_number)) {
7945 goto business_calendar_params_cleanup;
7947 if (appointment_count > 0 && !
text_buffer_append_format_runtime(&week_row,
"<div style=\"margin-top:0.4rem;font-size:0.75rem;color:var(--gray-700);\">%d booked</div>", appointment_count)) {
7949 goto business_calendar_params_cleanup;
7951 if (available_count > 0) {
7952 if (!
text_buffer_append_format_runtime(&week_row,
"<div style=\"margin-top:0.2rem;font-size:0.75rem;color:var(--primary);\">%d slots</div>", available_count)) {
7954 goto business_calendar_params_cleanup;
7958 goto business_calendar_params_cleanup;
7962 goto business_calendar_params_cleanup;
7966 if ((day_cell_count % 7) == 0) {
7968 goto business_calendar_params_cleanup;
7974 while ((day_cell_count % 7) != 0) {
7976 goto business_calendar_params_cleanup;
7980 if (week_row.
length > 0) {
7982 goto business_calendar_params_cleanup;
7986 int selected_year = 0;
7987 int selected_month = 0;
7988 int selected_day = 0;
7989 if (selected_date == NULL || sscanf(selected_date,
"%d-%d-%d", &selected_year, &selected_month, &selected_day) != 3) {
7990 selected_year = year;
7991 selected_month = month;
7996 int schedule_found = 0;
7999 char *row_day = NULL;
8008 if (row_day != NULL &&
streq(row_day, selected_day_name)) {
8010 free(row_day); free(start); free(end);
8011 goto business_calendar_params_cleanup;
8019 free(row_day); free(start); free(end);
8020 goto business_calendar_params_cleanup;
8023 free(row_day); free(start); free(end);
8026 if (!schedule_found) {
8028 goto business_calendar_params_cleanup;
8033 goto business_calendar_params_cleanup;
8042 char *row_date = NULL;
8043 char *status = NULL;
8044 char *service_name = NULL;
8046 long appointment_id = 0;
8047 long service_id = 0;
8052 if (row_date == NULL || !
streq(row_date, selected_date != NULL ? selected_date :
"")) {
8063 if (doc == NULL || params == NULL) {
8065 goto business_calendar_params_cleanup;
8078 char action_path[256];
8079 snprintf(action_path,
sizeof(action_path),
"/appointments/%ld/complete/", appointment_id);
8081 snprintf(action_path,
sizeof(action_path),
"/appointments/%ld/cancel/", appointment_id);
8086 free(row_date); free(status); free(service_name);
8089 goto business_calendar_params_cleanup;
8095 goto business_calendar_params_cleanup;
8098 char path_buffer[256];
8099 snprintf(path_buffer,
sizeof(path_buffer),
"/b/%s/", business_slug);
8100 landing_path = strdup(path_buffer);
8101 snprintf(path_buffer,
sizeof(path_buffer),
"/businesses/%s/", business_slug);
8102 manage_path = strdup(path_buffer);
8103 snprintf(path_buffer,
sizeof(path_buffer),
"/business/%s/calendar/?year=%04d&month=%02d", business_slug, prev_year, prev_month);
8104 prev_month_path = strdup(path_buffer);
8105 snprintf(path_buffer,
sizeof(path_buffer),
"/business/%s/calendar/?year=%04d&month=%02d", business_slug, next_year, next_month);
8106 next_month_path = strdup(path_buffer);
8107 if (landing_path == NULL || manage_path == NULL || prev_month_path == NULL || next_month_path == NULL) {
8108 goto business_calendar_params_cleanup;
8113 if (page_doc == NULL || page_obj == NULL) {
8114 goto business_calendar_params_cleanup;
8130business_calendar_params_cleanup:
8135 free(business_name);
8138 free(prev_month_path);
8139 free(next_month_path);
8140 free(selected_date);
8150 if (rows == NULL || day_name == NULL || day_name[0] ==
'\0' || !
yyjson_arr_iter_init(rows, &iter)) {
8154 char *row_day = NULL;
8155 char *start_time = NULL;
8156 char *end_time = NULL;
8161 if (row_day == NULL || !
streq(row_day, day_name)) {
8167 if (start_time != NULL && end_time != NULL) {
8168 fprintf(stdout,
"%s|%s\n", start_time, end_time);
8181 if (rows == NULL || day_name == NULL || day_name[0] ==
'\0' || !
yyjson_arr_iter_init(rows, &iter)) {
8185 char *row_day = NULL;
8186 char *start_time = NULL;
8187 char *end_time = NULL;
8192 if (row_day == NULL || !
streq(row_day, day_name)) {
8198 if (start_time != NULL && end_time != NULL) {
8199 fprintf(stdout,
"%s|%s\n", start_time, end_time);
8212 if (rows == NULL || date_value == NULL || date_value[0] ==
'\0' || !
yyjson_arr_iter_init(rows, &iter)) {
8216 char *row_date = NULL;
8217 char *time_value = NULL;
8218 char *status_value = NULL;
8219 long service_id = 0;
8224 if (row_date == NULL || !
streq(row_date, date_value)) {
8231 if (time_value != NULL && status_value != NULL) {
8232 fprintf(stdout,
"%s|%ld|%s\n", time_value, service_id, status_value);
8268 fputs(
"[]", stdout);
8273 char *membership_status = NULL;
8279 if (membership_status != NULL && membership_status[0] !=
'\0' && !
streq(membership_status,
"active")) {
8280 free(membership_status);
8283 free(membership_status);
8288 fprintf(stdout,
"%ld", tenant_id);
8322 fputs(
"[]", stdout);
8327 if (mut_doc == NULL || array == NULL) {
8335 char *description = NULL;
8337 long business_id = 0;
8374 char *rendered = NULL;
8375 if (template_file == NULL) {
8379 free(template_file);
8384 char *params_json = NULL;
8387 char *rendered = NULL;
8392 if (params_json == NULL) {
8410 if (doc == NULL || params == NULL) {
8422 char letters[4] = {0};
8427 return strdup(
"XX");
8429 for (index = 0; name[index] !=
'\0' && count < 3; index++) {
8430 unsigned char ch = (
unsigned char)name[index];
8433 letters[count++] = (char)toupper(ch);
8442 for (index = 0; name[index] !=
'\0' && count < 2; index++) {
8443 unsigned char ch = (
unsigned char)name[index];
8445 letters[count++] = (char)toupper(ch);
8450 letters[count++] =
'X';
8452 letters[count] =
'\0';
8453 return strdup(letters);
8457 static const char *colors[] = {
"#2563eb",
"#7c3aed",
"#0f766e",
"#b45309",
"#be123c",
"#0369a1" };
8458 unsigned long checksum = 0;
8461 for (index = 0; name[index] !=
'\0'; index++) {
8462 checksum = (checksum * 33u) + (
unsigned char)name[index];
8465 return colors[checksum % (
sizeof(colors) /
sizeof(colors[0]))];
8483 char *service_name = NULL;
8484 char *service_description = NULL;
8485 char *service_image_raw = NULL;
8486 char *service_image_url = NULL;
8487 char *service_visual_html = NULL;
8488 char *error_html = NULL;
8489 char *save_path = NULL;
8490 const char *time_grid_columns =
"1fr 1fr 1fr";
8491 const char *time_period_html =
"<select name=\"time_period\" required><option value=\"AM\" selected>AM</option><option value=\"PM\">PM</option></select>";
8492 const char *time_format =
"am_pm";
8493 long business_id = 0;
8494 long service_duration = 45;
8495 long booking_option_count = 0;
8497 if (business == NULL || service == NULL || app_root == NULL || app_root[0] ==
'\0') {
8507 if (service_image_raw == NULL || service_image_raw[0] ==
'\0') {
8508 free(service_image_raw);
8513 if (service_visual_html == NULL) {
8514 goto service_book_form_cleanup;
8521 if (service_duration <= 0) {
8522 service_duration = 45;
8524 if (error_message != NULL && error_message[0] !=
'\0') {
8530 goto service_book_form_cleanup;
8540 long duration = service_duration;
8544 goto service_book_form_cleanup;
8546 booking_option_count = 1;
8552 if (duration <= 0) {
8555 if ((label != NULL && label[0] !=
'\0') || duration > 0 || (cost != NULL && cost[0] !=
'\0')) {
8561 goto service_book_form_cleanup;
8563 booking_option_count++;
8570 if (booking_option_count > 1) {
8571 if (!
text_buffer_append_text_runtime(&booking_option_select,
"<div class=\"form-group\"><label for=\"booking-option-index\" class=\"sr-only\">Booking option</label><select id=\"booking-option-index\" name=\"booking_option_index\" class=\"form-control\" required style=\"display:block;width:100%;min-height:2.75rem;appearance:none;-webkit-appearance:none;background-color:#fff;color:#111827;border:1px solid #d1d5db;border-radius:6px;padding:0.5rem 2.75rem 0.5rem 0.75rem;background-image:linear-gradient(45deg, transparent 50%, #374151 50%),linear-gradient(135deg, #374151 50%, transparent 50%);background-position:calc(100% - 18px) calc(50% - 3px),calc(100% - 12px) calc(50% - 3px);background-size:6px 6px,6px 6px;background-repeat:no-repeat;font-weight:500;\">")
8574 goto service_book_form_cleanup;
8577 if (
streq(time_format,
"military")) {
8579 time_grid_columns =
"1fr 1fr";
8580 time_period_html =
"";
8582 goto service_book_form_cleanup;
8584 for (hour = 0; hour <= 23; hour++) {
8586 goto service_book_form_cleanup;
8592 goto service_book_form_cleanup;
8594 for (hour = 1; hour <= 12; hour++) {
8596 goto service_book_form_cleanup;
8601 goto service_book_form_cleanup;
8604 const int minute_values[] = {0, 10, 20, 30, 40, 50};
8605 size_t minute_index = 0;
8606 for (minute_index = 0; minute_index < (
sizeof(minute_values) /
sizeof(minute_values[0])); minute_index++) {
8607 if (!
text_buffer_append_format_runtime(&time_minute_options,
"<option value=\"%02d\">%02d</option>", minute_values[minute_index], minute_values[minute_index])) {
8608 goto service_book_form_cleanup;
8613 size_t needed = strlen(
"/business//book//") + strlen(slug != NULL ? slug :
"") + 32;
8614 save_path = (
char *)malloc(needed);
8615 if (save_path == NULL) {
8616 goto service_book_form_cleanup;
8618 snprintf(save_path, needed,
"/business/%s/book/%ld/", slug != NULL ? slug :
"", service_id);
8622 if (page_doc == NULL || page_obj == NULL) {
8623 goto service_book_form_cleanup;
8628 yyjson_mut_obj_add_strcpy(page_doc, page_obj,
"service_visual_html", service_visual_html != NULL ? service_visual_html :
"");
8629 yyjson_mut_obj_add_strcpy(page_doc, page_obj,
"service_description", service_description != NULL ? service_description :
"");
8639service_book_form_cleanup:
8645 free(service_description);
8646 free(service_image_raw);
8647 free(service_image_url);
8648 free(service_visual_html);
8656 const char *state_path,
8657 const char *business_slug,
8659 const char *app_root,
8660 const char *error_message
8674 char *service_name = NULL;
8675 char *service_description = NULL;
8676 char *service_image_raw = NULL;
8677 char *service_image_url = NULL;
8678 char *service_visual_html = NULL;
8679 char *error_html = NULL;
8680 char *save_path = NULL;
8681 char *result = NULL;
8682 const char *time_grid_columns =
"1fr 1fr 1fr";
8683 const char *time_period_html =
"<select name=\"time_period\" required><option value=\"AM\" selected>AM</option><option value=\"PM\">PM</option></select>";
8684 const char *time_format =
"am_pm";
8685 long business_id = 0;
8686 long service_duration = 45;
8687 long booking_option_count = 0;
8689 if (state_path == NULL || state_path[0] ==
'\0' || business_slug == NULL || business_slug[0] ==
'\0' || app_root == NULL || app_root[0] ==
'\0') {
8697 if (business == NULL || service == NULL) {
8711 if (service_image_raw == NULL || service_image_raw[0] ==
'\0') {
8712 free(service_image_raw);
8717 if (service_visual_html == NULL) {
8718 goto service_book_form_params_cleanup;
8726 if (service_duration <= 0) {
8727 service_duration = 45;
8730 if (error_message != NULL && error_message[0] !=
'\0') {
8736 goto service_book_form_params_cleanup;
8747 long duration = service_duration;
8751 goto service_book_form_params_cleanup;
8753 booking_option_count = 1;
8759 if (duration <= 0) {
8762 if ((label != NULL && label[0] !=
'\0') || duration > 0 || (cost != NULL && cost[0] !=
'\0')) {
8768 goto service_book_form_params_cleanup;
8770 booking_option_count++;
8778 if (booking_option_count > 1) {
8779 if (!
text_buffer_append_text_runtime(&booking_option_select,
"<div class=\"form-group\"><label for=\"booking-option-index\" class=\"sr-only\">Booking option</label><select id=\"booking-option-index\" name=\"booking_option_index\" class=\"form-control\" required style=\"display:block;width:100%;min-height:2.75rem;appearance:none;-webkit-appearance:none;background-color:#fff;color:#111827;border:1px solid #d1d5db;border-radius:6px;padding:0.5rem 2.75rem 0.5rem 0.75rem;background-image:linear-gradient(45deg, transparent 50%, #374151 50%),linear-gradient(135deg, #374151 50%, transparent 50%);background-position:calc(100% - 18px) calc(50% - 3px),calc(100% - 12px) calc(50% - 3px);background-size:6px 6px,6px 6px;background-repeat:no-repeat;font-weight:500;\">")
8782 goto service_book_form_params_cleanup;
8786 if (
streq(time_format,
"military")) {
8788 time_grid_columns =
"1fr 1fr";
8789 time_period_html =
"";
8791 goto service_book_form_params_cleanup;
8793 for (hour = 0; hour <= 23; hour++) {
8795 goto service_book_form_params_cleanup;
8801 goto service_book_form_params_cleanup;
8803 for (hour = 1; hour <= 12; hour++) {
8805 goto service_book_form_params_cleanup;
8811 goto service_book_form_params_cleanup;
8814 const int minute_values[] = {0, 10, 20, 30, 40, 50};
8815 size_t minute_index = 0;
8816 for (minute_index = 0; minute_index < (
sizeof(minute_values) /
sizeof(minute_values[0])); minute_index++) {
8817 if (!
text_buffer_append_format_runtime(&time_minute_options,
"<option value=\"%02d\">%02d</option>", minute_values[minute_index], minute_values[minute_index])) {
8818 goto service_book_form_params_cleanup;
8824 size_t needed = strlen(
"/business//book//") + strlen(business_slug) + 32;
8825 save_path = (
char *)malloc(needed);
8826 if (save_path == NULL) {
8827 goto service_book_form_params_cleanup;
8829 snprintf(save_path, needed,
"/business/%s/book/%ld/", business_slug, service_id);
8834 if (page_doc == NULL || page_obj == NULL) {
8835 goto service_book_form_params_cleanup;
8840 yyjson_mut_obj_add_strcpy(page_doc, page_obj,
"service_visual_html", service_visual_html != NULL ? service_visual_html :
"");
8841 yyjson_mut_obj_add_strcpy(page_doc, page_obj,
"service_description", service_description != NULL ? service_description :
"");
8851service_book_form_params_cleanup:
8857 free(service_description);
8858 free(service_image_raw);
8859 free(service_image_url);
8860 free(service_visual_html);
8871 char *rendered = NULL;
8872 char *initials = NULL;
8873 if (app_root == NULL || app_root[0] ==
'\0') {
8878 if (doc == NULL || params == NULL) {
8883 if (image_url != NULL && image_url[0] !=
'\0' && !
streq(image_url,
"null")) {
8903 long service_count = 0;
8905 long current_order = 1;
8906 if (app_root == NULL || app_root[0] ==
'\0') {
8916 max_order = include_append ? (service_count + 1) : service_count;
8917 if (max_order < 1) {
8920 if (selected_order <= 0 || selected_order > max_order) {
8921 selected_order = max_order;
8923 for (current_order = 1; current_order <= max_order; current_order++) {
8927 if (doc == NULL || params == NULL) {
8955 char *editor_html = NULL;
8956 long option_count = 0;
8957 if (app_root == NULL || app_root[0] ==
'\0') {
8964 char *row_html = NULL;
8969 if (row_doc == NULL || row_params == NULL) {
8977 if (duration <= 0) {
8999 if (doc == NULL || params == NULL) {
9021 if (app_root == NULL || app_root[0] ==
'\0') {
9031 long technician_id = 0;
9042 if (doc == NULL || params == NULL) {
9084 if (minutes_out != NULL) {
9087 if (value == NULL || sscanf(value,
"%d:%d", &hour, &minute) != 2 || hour < 0 || hour > 23 || minute < 0 || minute > 59) {
9090 if (minutes_out != NULL) {
9091 *minutes_out = (hour * 60) + minute;
9100 if (year_out != NULL) *year_out = 0;
9101 if (month_out != NULL) *month_out = 0;
9102 if (day_out != NULL) *day_out = 0;
9103 if (date_value == NULL || sscanf(date_value,
"%d-%d-%d", &year, &month, &day) != 3 || month < 1 || month > 12 || day < 1 || day > 31) {
9106 if (year_out != NULL) *year_out = year;
9107 if (month_out != NULL) *month_out = month;
9108 if (day_out != NULL) *day_out = day;
9113 int end_a_minutes = start_a_minutes + duration_a;
9114 int end_b_minutes = start_b_minutes + duration_b;
9115 return start_a_minutes < end_b_minutes && start_b_minutes < end_a_minutes;
9127 const char *day_name = NULL;
9132 slot_end = slot_start + (int)duration_minutes;
9135 char *row_day = NULL;
9136 char *start_text = NULL;
9137 char *end_text = NULL;
9138 int start_minutes = 0;
9139 int end_minutes = 0;
9169 char *existing_date = NULL;
9170 char *existing_time = NULL;
9171 char *existing_status = NULL;
9172 long service_id = 0;
9174 int existing_start = 0;
9175 int existing_duration = 45;
9183 if (existing_status != NULL &&
streq(existing_status,
"cancelled")) {
9184 free(existing_date);
9185 free(existing_time);
9186 free(existing_status);
9191 if (service != NULL) {
9195 free(existing_date);
9196 free(existing_time);
9197 free(existing_status);
9217 int duration_minutes = 45;
9218 const char *day_name = NULL;
9219 if (service != NULL) {
9222 if (duration_minutes <= 0) {
9223 duration_minutes = 45;
9229 slot_end = slot_start + duration_minutes;
9232 long technician_id = 0;
9240 char *row_day = NULL;
9241 char *start_text = NULL;
9242 char *end_text = NULL;
9243 int start_minutes = 0;
9244 int end_minutes = 0;
9257 return technician_id;
9268 case 1:
case 3:
case 5:
case 7:
case 8:
case 10:
case 12:
9270 case 4:
case 6:
case 9:
case 11:
9273 return ((year % 400) == 0 || (((year % 4) == 0) && ((year % 100) != 0))) ? 29 : 28;
9280 static const char *names[] = {
"January",
"February",
"March",
"April",
"May",
"June",
"July",
"August",
"September",
"October",
"November",
"December" };
9281 if (month < 1 || month > 12) {
9284 return names[month - 1];
9288 int total = (year * 12) + (month - 1) + shift;
9289 if (out_year != NULL) {
9290 *out_year = total / 12;
9292 if (out_month != NULL) {
9293 *out_month = (total % 12) + 1;
9300 memset(&value, 0,
sizeof(value));
9301 value.tm_year = year - 1900;
9302 value.tm_mon = month - 1;
9303 value.tm_mday = day;
9305 stamp = mktime(&value);
9306 if (stamp == (time_t)-1) {
9309 return value.tm_wday == 0 ? 7 : value.tm_wday;
9313 static const char *days[] = {
"Sun",
"Mon",
"Tue",
"Wed",
"Thu",
"Fri",
"Sat" };
9316 memset(&value, 0,
sizeof(value));
9317 value.tm_year = year - 1900;
9318 value.tm_mon = month - 1;
9319 value.tm_mday = day;
9321 stamp = mktime(&value);
9322 if (stamp == (time_t)-1 || value.tm_wday < 0 || value.tm_wday > 6) {
9325 return days[value.tm_wday];
9342 char *description = NULL;
9345 long business_id = 0;
9360 if (doc == NULL || params == NULL) {
9369 free(name); free(description); free(slug);
9378 if (buffer.
length == 0) {
9379 return strdup(
"<article class=\"card\"><p>No businesses yet.</p></article>");
9387 char *cards_html = NULL;
9390 if (cards_html == NULL) {
9395 if (doc == NULL || obj == NULL) {
9411 const char *app_root,
9412 const char *invite_account_type_options_html
9428 char *business_name = NULL;
9429 char *connection_status = NULL;
9430 char *public_state_button_html = NULL;
9431 char *new_service_sort_order_options_html = NULL;
9432 char *new_service_booking_options_editor_html = NULL;
9433 char *service_technician_checkboxes_html = NULL;
9434 char *availability_day_options_html = NULL;
9435 char *empty_html = NULL;
9436 long business_id = 0;
9437 long appointment_count = 0;
9438 long unassigned_count = 0;
9439 long technician_count = 0;
9440 int stripe_connected = 0;
9443 if (business == NULL || app_root == NULL || app_root[0] ==
'\0') {
9452 if (connection_status == NULL || connection_status[0] ==
'\0') {
9453 free(connection_status);
9454 connection_status = strdup(
"Disconnected");
9459 if (doc == NULL || params == NULL) {
9460 goto business_admin_page_cleanup;
9468 char *toggle_path = NULL;
9469 size_t needed = strlen(
"/businesses//toggle-public/") + strlen(slug != NULL ? slug :
"") + 1;
9470 toggle_path = (
char *)malloc(needed);
9471 if (toggle_path == NULL) {
9473 goto business_admin_page_cleanup;
9475 snprintf(toggle_path, needed,
"/businesses/%s/toggle-public/", slug != NULL ? slug :
"");
9481 if (public_state_button_html == NULL) {
9482 goto business_admin_page_cleanup;
9486 goto business_admin_page_cleanup;
9492 if (new_service_sort_order_options_html == NULL || new_service_booking_options_editor_html == NULL || service_technician_checkboxes_html == NULL || availability_day_options_html == NULL) {
9493 goto business_admin_page_cleanup;
9495 if (service_list.
count > 0) {
9497 for (index = 0; index < service_list.
count; index++) {
9511 char *service_image_url = NULL;
9512 char *booking_summary = NULL;
9513 char *technician_names = NULL;
9514 char *image_html = NULL;
9515 char *description_html = NULL;
9516 char *booking_html = NULL;
9517 char *technicians_html = NULL;
9518 char *service_summary = NULL;
9519 char *fragment_html = NULL;
9522 long order_index = (long)index + 1;
9523 int first_booking_option = 1;
9524 int first_technician = 1;
9525 if (doc == NULL || params == NULL) {
9527 free(service_name); free(service_cost); free(service_description); free(image_url_raw);
9528 goto business_admin_page_cleanup;
9531 if (image_url_raw == NULL || image_url_raw[0] ==
'\0') {
9532 free(image_url_raw);
9536 if (service_image_url != NULL && service_image_url[0] !=
'\0') {
9541 free(service_name); free(service_cost); free(service_description); free(image_url_raw); free(service_image_url);
9542 goto business_admin_page_cleanup;
9546 if (service_description != NULL && service_description[0] !=
'\0') {
9551 free(service_name); free(service_cost); free(service_description); free(image_url_raw); free(service_image_url); free(image_html);
9552 goto business_admin_page_cleanup;
9561 char fallback_label[64];
9562 const char *label_text = label;
9563 if (label_text == NULL || label_text[0] ==
'\0') {
9564 snprintf(fallback_label,
sizeof(fallback_label),
"%ld min", option_duration);
9565 label_text = fallback_label;
9568 size_t current_len = booking_summary != NULL ? strlen(booking_summary) : 0;
9569 const char *cost_text = cost != NULL ? cost :
"";
9570 size_t needed = current_len + (first_booking_option ? 0 : 2) + strlen(label_text) + 2 + strlen(cost_text) + 1;
9571 char *next = (
char *)realloc(booking_summary, needed);
9573 free(label); free(cost);
yyjson_mut_doc_free(doc); free(service_name); free(service_cost); free(service_description); free(image_url_raw); free(service_image_url); free(image_html); free(description_html); free(booking_summary);
9574 goto business_admin_page_cleanup;
9576 booking_summary = next;
9577 if (first_booking_option) {
9578 booking_summary[0] =
'\0';
9580 strcat(booking_summary,
", ");
9582 strcat(booking_summary, label_text);
9583 strcat(booking_summary,
" $");
9584 strcat(booking_summary, cost_text);
9585 first_booking_option = 0;
9591 if (booking_summary != NULL && booking_summary[0] !=
'\0') {
9596 free(service_name); free(service_cost); free(service_description); free(image_url_raw); free(service_image_url); free(image_html); free(description_html); free(booking_summary);
9597 goto business_admin_page_cleanup;
9603 char *technician_name = NULL;
9604 long technician_id = 0;
9614 long assigned_id = 0;
9627 if (technician_name != NULL && technician_name[0] !=
'\0') {
9628 size_t current_len = technician_names != NULL ? strlen(technician_names) : 0;
9629 size_t needed = current_len + (first_technician ? 0 : 2) + strlen(technician_name) + 1;
9630 char *next = (
char *)realloc(technician_names, needed);
9632 free(technician_name);
yyjson_mut_doc_free(doc); free(service_name); free(service_cost); free(service_description); free(image_url_raw); free(service_image_url); free(image_html); free(description_html); free(booking_summary); free(booking_html); free(technician_names);
9633 goto business_admin_page_cleanup;
9635 technician_names = next;
9636 if (first_technician) technician_names[0] =
'\0';
else strcat(technician_names,
", ");
9637 strcat(technician_names, technician_name);
9638 first_technician = 0;
9640 free(technician_name);
9643 if (technician_names != NULL && technician_names[0] !=
'\0') {
9646 text_buffer_free_runtime(&tmp);
yyjson_mut_doc_free(doc); free(service_name); free(service_cost); free(service_description); free(image_url_raw); free(service_image_url); free(image_html); free(description_html); free(booking_summary); free(booking_html); free(technician_names);
goto business_admin_page_cleanup;
9650 technicians_html = strdup(
"<p class=\"muted\" style=\"margin-top:0.5rem;\">Technicians: none assigned</p>");
9652 if (duration_minutes <= 0) duration_minutes = 45;
9656 text_buffer_free_runtime(&tmp);
yyjson_mut_doc_free(doc); free(service_name); free(service_cost); free(service_description); free(image_url_raw); free(service_image_url); free(image_html); free(description_html); free(booking_summary); free(booking_html); free(technician_names); free(technicians_html);
goto business_admin_page_cleanup;
9664 snprintf(text,
sizeof(text),
"/businesses/%s/services/%ld/move-up/", slug != NULL ? slug :
"", service_id);
9666 snprintf(text,
sizeof(text),
"/businesses/%s/services/%ld/move-down/", slug != NULL ? slug :
"", service_id);
9668 snprintf(text,
sizeof(text),
"/businesses/%s/services/%ld/edit/", slug != NULL ? slug :
"", service_id);
9670 snprintf(text,
sizeof(text),
"/businesses/%s/services/%ld/delete/", slug != NULL ? slug :
"", service_id);
9681 free(service_name); free(service_cost); free(service_description); free(image_url_raw); free(service_image_url); free(image_html); free(description_html); free(booking_summary); free(booking_html); free(technician_names); free(technicians_html); free(service_summary);
9683 free(fragment_html);
9684 goto business_admin_page_cleanup;
9686 free(fragment_html);
9689 if (service_rows.
length == 0) {
9693 goto business_admin_page_cleanup;
9704 char *availability_summary = NULL;
9705 char *fragment_html = NULL;
9706 char *day_options_html = NULL;
9707 long technician_id = 0;
9713 if (availability_rows != NULL) {
9716 int first_entry = 1;
9719 char *day = NULL;
char *start = NULL;
char *end = NULL;
char *entry = NULL;
9720 size_t current_len = availability_summary != NULL ? strlen(availability_summary) : 0;
9726 if (day != NULL && start != NULL && end != NULL) {
9727 needed = strlen(day) + strlen(start) + strlen(end) + 4;
9728 entry = (
char *)malloc(needed);
9729 if (entry != NULL) snprintf(entry, needed,
"%s %s–%s", day, start, end);
9731 if (entry != NULL) {
9732 size_t total = current_len + (first_entry ? 0 : 4) + strlen(entry) + 1;
9733 char *next = (
char *)realloc(availability_summary, total);
9734 if (next == NULL) { free(day); free(start); free(end); free(entry); free(name); free(email); free(availability_summary);
goto business_admin_page_cleanup; }
9735 availability_summary = next;
9736 if (first_entry) availability_summary[0] =
'\0';
else strcat(availability_summary,
"<br>");
9737 strcat(availability_summary, entry);
9740 free(day); free(start); free(end); free(entry);
9747 if (doc == NULL || params == NULL) {
yyjson_mut_doc_free(doc); free(name); free(email); free(availability_summary); free(day_options_html);
goto business_admin_page_cleanup; }
9751 yyjson_mut_obj_add_strcpy(doc, params,
"technician_availability_summary_html", availability_summary != NULL && availability_summary[0] !=
'\0' ? availability_summary :
"none set");
9752 yyjson_mut_obj_add_strcpy(doc, params,
"availability_day_options_html", day_options_html != NULL ? day_options_html :
"");
9755 snprintf(text,
sizeof(text),
"/businesses/%s/technicians/%ld/edit/", slug != NULL ? slug :
"", technician_id);
9757 snprintf(text,
sizeof(text),
"/businesses/%s/technicians/%ld/delete/", slug != NULL ? slug :
"", technician_id);
9759 snprintf(text,
sizeof(text),
"/businesses/technicians/%ld/availability/new/", technician_id);
9764 free(name); free(email); free(availability_summary); free(day_options_html);
9766 free(fragment_html);
9767 goto business_admin_page_cleanup;
9769 free(fragment_html);
9772 if (technician_rows.
length == 0) {
9785 char *time_range = NULL;
9786 char *fragment_html = NULL;
9787 long availability_id = 0;
9793 if (day != NULL && start != NULL && end != NULL) {
9794 size_t needed = strlen(start) + strlen(end) + 4;
9795 time_range = (
char *)malloc(needed);
9796 if (time_range != NULL) snprintf(time_range, needed,
"%s–%s", start, end);
9800 if (doc == NULL || params == NULL) {
yyjson_mut_doc_free(doc); free(day); free(start); free(end); free(time_range);
goto business_admin_page_cleanup; }
9806 snprintf(text,
sizeof(text),
"/businesses/%s/availability/%ld/delete/", slug != NULL ? slug :
"", availability_id);
9811 free(day); free(start); free(end); free(time_range);
9812 if (fragment_html == NULL || !
text_buffer_append_text_runtime(&availability_html, fragment_html)) { free(fragment_html);
goto business_admin_page_cleanup; }
9813 free(fragment_html);
9816 if (availability_html.
length == 0) {
9825 appointment_count++;
9833 if (page_doc == NULL || page_obj == NULL) {
9834 goto business_admin_page_cleanup;
9841 yyjson_mut_obj_add_strcpy(page_doc, page_obj,
"public_state_button_html", public_state_button_html != NULL ? public_state_button_html :
"");
9847 yyjson_mut_obj_add_strcpy(page_doc, page_obj,
"connection_status", connection_status != NULL ? connection_status :
"Disconnected");
9853 yyjson_mut_obj_add_strcpy(page_doc, page_obj,
"invite_account_type_options_html", invite_account_type_options_html != NULL ? invite_account_type_options_html :
"");
9857 yyjson_mut_obj_add_strcpy(page_doc, page_obj,
"new_service_sort_order_options_html", new_service_sort_order_options_html != NULL ? new_service_sort_order_options_html :
"");
9858 yyjson_mut_obj_add_strcpy(page_doc, page_obj,
"new_service_booking_options_editor_html", new_service_booking_options_editor_html != NULL ? new_service_booking_options_editor_html :
"");
9859 yyjson_mut_obj_add_strcpy(page_doc, page_obj,
"service_technician_checkboxes_html", service_technician_checkboxes_html != NULL ? service_technician_checkboxes_html :
"");
9866 yyjson_mut_obj_add_strcpy(page_doc, page_obj,
"availability_day_options_html", availability_day_options_html != NULL ? availability_day_options_html :
"");
9868 char path_text[512];
9869 snprintf(path_text,
sizeof(path_text),
"/b/%s/", slug != NULL ? slug :
"");
9872 snprintf(path_text,
sizeof(path_text),
"/businesses/%s/edit/", slug != NULL ? slug :
"");
9874 snprintf(path_text,
sizeof(path_text),
"/business/%s/calendar/", slug != NULL ? slug :
"");
9876 snprintf(path_text,
sizeof(path_text),
"/businesses/%s/delete/", slug != NULL ? slug :
"");
9878 snprintf(path_text,
sizeof(path_text),
"/businesses/%s/connect-stripe/", slug != NULL ? slug :
"");
9880 snprintf(path_text,
sizeof(path_text),
"/businesses/%s/", slug != NULL ? slug :
"");
9882 snprintf(path_text,
sizeof(path_text),
"/businesses/%s/invites/new/", slug != NULL ? slug :
"");
9884 snprintf(path_text,
sizeof(path_text),
"/businesses/%s/services/", slug != NULL ? slug :
"");
9886 snprintf(path_text,
sizeof(path_text),
"/businesses/%s/services/new/", slug != NULL ? slug :
"");
9888 snprintf(path_text,
sizeof(path_text),
"/businesses/%s/technicians/", slug != NULL ? slug :
"");
9890 snprintf(path_text,
sizeof(path_text),
"/businesses/%s/technicians/new/", slug != NULL ? slug :
"");
9892 snprintf(path_text,
sizeof(path_text),
"/businesses/%s/availability/", slug != NULL ? slug :
"");
9894 snprintf(path_text,
sizeof(path_text),
"/businesses/%s/availability/new/", slug != NULL ? slug :
"");
9898business_admin_page_cleanup:
9900 free(business_name);
9901 free(connection_status);
9902 free(public_state_button_html);
9903 free(new_service_sort_order_options_html);
9904 free(new_service_booking_options_editor_html);
9905 free(service_technician_checkboxes_html);
9906 free(availability_day_options_html);
9929 if (app_root == NULL || app_root[0] ==
'\0') {
9939 char *description = NULL;
9940 char *raw_image_url = NULL;
9941 char *business_image_url = NULL;
9951 if (slug == NULL || slug[0] ==
'\0') {
9961 if (doc == NULL || params == NULL) {
9962 free(slug); free(name); free(description); free(raw_image_url); free(business_image_url);
9974 free(slug); free(name); free(description); free(raw_image_url); free(business_image_url);
9989 if (page_doc == NULL || page_obj == NULL
10017 char *subscriptions_block = NULL;
10018 char *full_name = NULL;
10019 char *email = NULL;
10020 char *phone_number = NULL;
10021 char *address = NULL;
10023 if (app_root == NULL || app_root[0] ==
'\0') {
10028 long business_id = 0;
10032 char *business_name = NULL;
10033 char *business_slug = NULL;
10044 if (doc == NULL || params == NULL) {
10045 free(business_name); free(business_slug);
10055 free(business_name); free(business_slug);
10064 if (subscription_rows.
length > 0) {
10077 if (doc == NULL || params == NULL) {
10092 if (subscriptions_block == NULL || page_doc == NULL || page_obj == NULL
10097 || !
yyjson_mut_obj_add_strcpy(page_doc, page_obj,
"payment_preference_options_html", payment_preference_options_html != NULL ? payment_preference_options_html :
"")
10100 free(full_name); free(email); free(phone_number); free(address); free(subscriptions_block);
10106 free(full_name); free(email); free(phone_number); free(address); free(subscriptions_block);
10116 long tenant_id = 0;
10117 if (root == NULL || user_id <= 0 || business_id <= 0) {
10121 if (tenant_id <= 0) {
10129 const char *membership_status = NULL;
10140 if (membership_status != NULL && membership_status[0] !=
'\0' && !
streq(membership_status,
"active")) {
10149 const char *state_path,
10151 const char *app_root,
10152 const char *payment_preference_options_html
10163 char *subscriptions_block = NULL;
10164 char *full_name = NULL;
10165 char *email = NULL;
10166 char *phone_number = NULL;
10167 char *address = NULL;
10168 char *result = NULL;
10170 if (state_path == NULL || state_path[0] ==
'\0' || app_root == NULL || app_root[0] ==
'\0') {
10176 if (root == NULL) {
10185 long business_id = 0;
10189 char *business_name = NULL;
10190 char *business_slug = NULL;
10202 if (doc == NULL || params == NULL) {
10203 free(business_name);
10204 free(business_slug);
10215 free(business_name);
10216 free(business_slug);
10227 if (subscription_rows.
length > 0) {
10241 if (doc == NULL || params == NULL) {
10258 if (subscriptions_block == NULL || page_doc == NULL || page_obj == NULL
10263 || !
yyjson_mut_obj_add_strcpy(page_doc, page_obj,
"payment_preference_options_html", payment_preference_options_html != NULL ? payment_preference_options_html :
"")
10268 free(phone_number);
10270 free(subscriptions_block);
10282 free(phone_number);
10284 free(subscriptions_block);
10309 int managed_mode = tenant_ids != NULL &&
yyjson_arr_size(tenant_ids) > 0;
10310 if (app_root == NULL || app_root[0] ==
'\0') {
10319 long appointment_id = 0;
10320 long business_id = 0;
10321 long service_id = 0;
10322 long tenant_id = 0;
10323 char *business_name = NULL;
10324 char *service_name = NULL;
10325 char *date_value = NULL;
10326 char *time_value = NULL;
10327 char *status_value = NULL;
10328 char *fragment_path = NULL;
10336 if (managed_mode) {
10353 if (doc == NULL || params == NULL) {
10354 free(business_name); free(service_name); free(date_value); free(time_value); free(status_value);
10365 if (managed_mode) {
10366 char complete_path[128];
10367 char cancel_path[128];
10368 snprintf(complete_path,
sizeof(complete_path),
"/appointments/%ld/complete/", appointment_id);
10369 snprintf(cancel_path,
sizeof(cancel_path),
"/appointments/%ld/cancel/", appointment_id);
10372 fragment_path =
"templates/calendar/fragments/managed_appointment_row.html";
10374 char request_cancel_path[128];
10375 snprintf(request_cancel_path,
sizeof(request_cancel_path),
"/appointments/%ld/request-cancel/", appointment_id);
10377 fragment_path =
"templates/calendar/fragments/customer_appointment_row.html";
10381 free(business_name); free(service_name); free(date_value); free(time_value); free(status_value);
10394 char *booking_title = NULL;
10395 char *date_value = NULL;
10396 char *time_value = NULL;
10397 char *status_value = NULL;
10408 if (doc == NULL || params == NULL) {
10409 free(booking_title); free(date_value); free(time_value); free(status_value);
10421 free(booking_title); free(date_value); free(time_value); free(status_value);
10440 if (page_doc == NULL || page_obj == NULL
10461 const char *heading =
"Appointments";
10462 const char *tips_block =
"";
10463 int managed_mode = tenant_ids != NULL &&
yyjson_arr_size(tenant_ids) > 0;
10465 if (app_root == NULL || app_root[0] ==
'\0') {
10468 if (managed_mode) {
10469 heading =
"Manage Bookings";
10479 long appointment_id = 0;
10480 long business_id = 0;
10481 long service_id = 0;
10482 long customer_id = 0;
10483 long technician_id = 0;
10484 long tenant_id = 0;
10485 char *business_name = NULL;
10486 char *service_name = NULL;
10487 char *customer_name = NULL;
10488 char *technician_name = NULL;
10489 char *date_value = NULL;
10490 char *time_value = NULL;
10491 char *status_value = NULL;
10492 char *technician_html = NULL;
10495 const char *fragment_path = NULL;
10504 if (managed_mode) {
10509 }
else if (customer_id != user_id) {
10523 if (technician_name != NULL && technician_name[0] !=
'\0') {
10524 if (managed_mode) {
10528 free(business_name); free(service_name); free(customer_name); free(technician_name); free(date_value); free(time_value); free(status_value);
10535 free(business_name); free(service_name); free(customer_name); free(technician_name); free(date_value); free(time_value); free(status_value);
10544 if (doc == NULL || params == NULL) {
10545 free(business_name); free(service_name); free(customer_name); free(technician_name); free(date_value); free(time_value); free(status_value); free(technician_html);
10556 if (managed_mode) {
10557 char complete_path[128];
10558 char cancel_path[128];
10560 snprintf(complete_path,
sizeof(complete_path),
"/appointments/%ld/complete/", appointment_id);
10561 snprintf(cancel_path,
sizeof(cancel_path),
"/appointments/%ld/cancel/", appointment_id);
10564 fragment_path =
"templates/appointments/fragments/managed_appointment_row.html";
10566 char detail_path[128];
10567 char request_cancel_path[128];
10568 snprintf(detail_path,
sizeof(detail_path),
"/appointments/%ld/", appointment_id);
10569 snprintf(request_cancel_path,
sizeof(request_cancel_path),
"/appointments/%ld/request-cancel/", appointment_id);
10572 fragment_path =
"templates/appointments/fragments/customer_appointment_row.html";
10576 free(business_name); free(service_name); free(customer_name); free(technician_name); free(date_value); free(time_value); free(status_value); free(technician_html);
10585 if (appointment_items.
length == 0) {
10591 if (!managed_mode) {
10592 tips_block =
"<div class=\"card\" style=\"margin-top:2rem;background:var(--gray-50);\"><h3>Tips</h3><ul style=\"margin:0.5rem 0;padding-left:1.5rem;\"><li>Use this page to review your active appointments and request cancellations</li><li>Contact the business owner directly using the contact details shown in appointment detail</li><li>If you need to cancel, do so as early as possible</li></ul></div>";
10596 if (page_doc == NULL || page_obj == NULL
10615 char *description = NULL;
10616 char *address = NULL;
10617 char *raw_image = NULL;
10618 char *image_url = NULL;
10619 char *current_logo_html = NULL;
10620 char *public_options_html = NULL;
10621 char *time_format_options_html = NULL;
10622 char *time_format = NULL;
10626 if (business == NULL || slug == NULL || slug[0] ==
'\0') {
10636 if (image_url != NULL && image_url[0] !=
'\0') {
10641 || !
text_buffer_append_text_runtime(&buffer,
"\" style=\"max-width:180px;max-height:180px;border-radius:12px;border:1px solid #d0d7de;display:block;\">")) {
10642 goto business_edit_cleanup;
10645 goto business_edit_cleanup;
10648 if (current_logo_html == NULL) {
10649 goto business_edit_cleanup;
10651 public_options_html = strdup(is_public ?
"<option value=\"true\" selected>Public</option><option value=\"false\">Private</option>" :
"<option value=\"true\">Public</option><option value=\"false\" selected>Private</option>");
10652 time_format_options_html = strdup(time_format != NULL &&
streq(time_format,
"military") ?
"<option value=\"am_pm\">12-hour (AM/PM)</option><option value=\"military\" selected>24-hour (Military)</option>" :
"<option value=\"am_pm\" selected>12-hour (AM/PM)</option><option value=\"military\">24-hour (Military)</option>");
10655 if (public_options_html == NULL || time_format_options_html == NULL || doc == NULL || page == NULL) {
10656 goto business_edit_cleanup;
10660 char save_path[256];
10661 char cancel_path[256];
10662 snprintf(save_path,
sizeof(save_path),
"/businesses/%s/edit/", slug);
10663 snprintf(cancel_path,
sizeof(cancel_path),
"/businesses/%s/", slug);
10670 || !
yyjson_mut_obj_add_strcpy(doc, page,
"public_options_html", public_options_html != NULL ? public_options_html :
"")
10671 || !
yyjson_mut_obj_add_strcpy(doc, page,
"time_format_options_html", time_format_options_html != NULL ? time_format_options_html :
"")) {
10672 goto business_edit_cleanup;
10676business_edit_cleanup:
10678 free(name); free(description); free(address); free(raw_image); free(image_url); free(current_logo_html); free(public_options_html); free(time_format_options_html); free(time_format);
10684 const char *state_path,
10685 const char *app_root
10695 char *result = NULL;
10697 if (state_path == NULL || state_path[0] ==
'\0' || app_root == NULL || app_root[0] ==
'\0') {
10703 if (root == NULL) {
10716 char *description = NULL;
10717 char *raw_image_url = NULL;
10718 char *business_image_url = NULL;
10729 if (slug == NULL || slug[0] ==
'\0') {
10739 if (doc == NULL || params == NULL) {
10743 free(raw_image_url);
10744 free(business_image_url);
10760 free(raw_image_url);
10761 free(business_image_url);
10772 if (cards.
length == 0) {
10782 if (page_doc == NULL || page_obj == NULL
10799 const char *state_path,
10800 const char *business_slug,
10802 const char *app_root
10817 char *business_name = NULL;
10818 char *business_description = NULL;
10819 char *raw_business_image = NULL;
10820 char *business_image_url = NULL;
10821 char *business_image_html = NULL;
10822 char *rating_form_html = NULL;
10823 char *cta_html = NULL;
10824 char *empty_html = NULL;
10825 char *result = NULL;
10826 long business_id = 0;
10827 int subscribed = 0;
10829 if (state_path == NULL || state_path[0] ==
'\0'
10830 || business_slug == NULL || business_slug[0] ==
'\0'
10831 || app_root == NULL || app_root[0] ==
'\0') {
10838 if (root == NULL || business == NULL) {
10863 if (business_image_url != NULL && business_image_url[0] !=
'\0') {
10866 if (doc == NULL || params == NULL) {
10868 goto business_landing_cleanup;
10875 if (business_image_html == NULL) {
10876 goto business_landing_cleanup;
10884 char *comment = NULL;
10894 if (doc == NULL || params == NULL) {
10897 goto business_landing_cleanup;
10907 goto business_landing_cleanup;
10913 if (rating_rows.
length == 0) {
10916 if (doc == NULL || params == NULL) {
10918 goto business_landing_cleanup;
10925 goto business_landing_cleanup;
10932 goto business_landing_cleanup;
10934 if (service_list.
count > 0) {
10936 for (index = 0; index < service_list.
count; index++) {
10944 char *service_image_url = NULL;
10945 char *visual_html = NULL;
10946 char *card_html = NULL;
10949 if (doc == NULL || params == NULL) {
10951 free(service_name);
10952 free(service_cost);
10953 free(service_description);
10954 free(image_url_raw);
10955 goto business_landing_cleanup;
10957 if (image_url_raw == NULL || image_url_raw[0] ==
'\0') {
10958 free(image_url_raw);
10963 if (duration_minutes <= 0) {
10964 duration_minutes = 45;
10976 free(service_name);
10977 free(service_cost);
10978 free(service_description);
10979 free(image_url_raw);
10980 free(service_image_url);
10984 goto business_landing_cleanup;
10990 if (service_cards.
length == 0) {
10993 if (doc == NULL || params == NULL) {
10995 goto business_landing_cleanup;
11002 goto business_landing_cleanup;
11011 if (doc == NULL || params == NULL) {
11013 goto business_landing_cleanup;
11019 if (rating_form_html == NULL) {
11020 goto business_landing_cleanup;
11027 const char *fragment_path = NULL;
11028 if (doc == NULL || params == NULL) {
11030 goto business_landing_cleanup;
11035 if (user_id <= 0) {
11036 fragment_path =
"templates/businesses/fragments/business_logged_out_cta.html";
11037 }
else if (subscribed) {
11038 fragment_path =
"templates/businesses/fragments/business_subscribed_cta.html";
11040 fragment_path =
"templates/businesses/fragments/business_subscribe_cta.html";
11044 if (cta_html == NULL) {
11045 goto business_landing_cleanup;
11051 if (page_doc == NULL || page_obj == NULL) {
11052 goto business_landing_cleanup;
11056 yyjson_mut_obj_add_strcpy(page_doc, page_obj,
"business_image_html", business_image_html != NULL ? business_image_html :
"");
11057 yyjson_mut_obj_add_strcpy(page_doc, page_obj,
"business_description", business_description != NULL ? business_description :
"");
11064business_landing_cleanup:
11068 free(business_name);
11069 free(business_description);
11070 free(raw_business_image);
11071 free(business_image_url);
11072 free(business_image_html);
11073 free(rating_form_html);
11081 const char *state_path,
11083 const char *app_root
11093 const char *heading =
"Appointments";
11094 const char *tips_block =
"";
11095 int managed_mode = 0;
11096 char *result = NULL;
11097 if (state_path == NULL || state_path[0] ==
'\0' || app_root == NULL || app_root[0] ==
'\0') {
11102 if (root == NULL) {
11120 if (managed_mode) {
11121 heading =
"Manage Bookings";
11132 long appointment_id = 0;
11133 long business_id = 0;
11134 long service_id = 0;
11135 long customer_id = 0;
11136 long technician_id = 0;
11137 char *business_name = NULL;
11138 char *service_name = NULL;
11139 char *customer_name = NULL;
11140 char *technician_name = NULL;
11141 char *date_value = NULL;
11142 char *time_value = NULL;
11143 char *status_value = NULL;
11144 char *technician_html = NULL;
11147 const char *fragment_path = NULL;
11156 if (managed_mode) {
11160 }
else if (customer_id != user_id) {
11174 if (technician_name != NULL && technician_name[0] !=
'\0') {
11175 if (managed_mode) {
11179 free(business_name); free(service_name); free(customer_name); free(technician_name); free(date_value); free(time_value); free(status_value);
11187 free(business_name); free(service_name); free(customer_name); free(technician_name); free(date_value); free(time_value); free(status_value);
11197 if (doc == NULL || params == NULL) {
11198 free(business_name); free(service_name); free(customer_name); free(technician_name); free(date_value); free(time_value); free(status_value); free(technician_html);
11210 if (managed_mode) {
11211 char complete_path[128];
11212 char cancel_path[128];
11214 snprintf(complete_path,
sizeof(complete_path),
"/appointments/%ld/complete/", appointment_id);
11215 snprintf(cancel_path,
sizeof(cancel_path),
"/appointments/%ld/cancel/", appointment_id);
11218 fragment_path =
"templates/appointments/fragments/managed_appointment_row.html";
11220 char detail_path[128];
11221 char request_cancel_path[128];
11222 snprintf(detail_path,
sizeof(detail_path),
"/appointments/%ld/", appointment_id);
11223 snprintf(request_cancel_path,
sizeof(request_cancel_path),
"/appointments/%ld/request-cancel/", appointment_id);
11226 fragment_path =
"templates/appointments/fragments/customer_appointment_row.html";
11230 free(business_name); free(service_name); free(customer_name); free(technician_name); free(date_value); free(time_value); free(status_value); free(technician_html);
11240 if (appointment_items.
length == 0) {
11247 if (!managed_mode) {
11248 tips_block =
"<div class=\"card\" style=\"margin-top:2rem;background:var(--gray-50);\"><h3>Tips</h3><ul style=\"margin:0.5rem 0;padding-left:1.5rem;\"><li>Use this page to review your active appointments and request cancellations</li><li>Contact the business owner directly using the contact details shown in appointment detail</li><li>If you need to cancel, do so as early as possible</li></ul></div>";
11252 if (page_doc == NULL || page_obj == NULL
11273 } RuntimeStatusCount;
11288 RuntimeStatusCount statuses[32] = {0};
11289 size_t status_count = 0;
11290 size_t audit_total = 0;
11291 size_t audit_start = 0;
11292 long appointment_count = 0;
11293 long booking_count = 0;
11294 long subscription_count = 0;
11295 long rating_count = 0;
11296 long connection_count = 0;
11297 long unassigned_count = 0;
11298 long cancel_requested_count = 0;
11299 char *result = NULL;
11302 if (state_path == NULL || state_path[0] ==
'\0') {
11308 if (root == NULL) {
11319 appointment_count = appointments != NULL ? (long)
yyjson_arr_size(appointments) : 0;
11320 booking_count = bookings != NULL ? (long)
yyjson_arr_size(bookings) : 0;
11321 subscription_count = subscriptions != NULL ? (long)
yyjson_arr_size(subscriptions) : 0;
11323 connection_count = connections != NULL ? (long)
yyjson_arr_size(connections) : 0;
11327 char *status = NULL;
11328 long technician_id = 0;
11336 if (technician_id <= 0) {
11337 unassigned_count++;
11339 if (status != NULL &&
streq(status,
"cancel_requested")) {
11340 cancel_requested_count++;
11342 for (slot = 0; slot < status_count; slot++) {
11343 if (statuses[slot].name != NULL && status != NULL &&
streq(statuses[slot].name, status)) {
11344 statuses[slot].count++;
11349 if (!found && status != NULL && status_count < (
sizeof(statuses) /
sizeof(statuses[0]))) {
11350 statuses[status_count].name = status;
11351 statuses[status_count].count = 1;
11359 if (status_count == 0) {
11361 goto diagnostics_render_cleanup;
11364 for (index = 0; index < status_count; index++) {
11368 goto diagnostics_render_cleanup;
11374 audit_start = audit_total > 5 ? audit_total - 5 : 0;
11378 char *
event = NULL;
11379 char *resource_id = NULL;
11384 if (event == NULL) {
11390 || ((resource_id != NULL && resource_id[0] !=
'\0')
11396 goto diagnostics_render_cleanup;
11403 goto diagnostics_render_cleanup;
11408 if (page_doc == NULL || page_obj == NULL) {
11409 goto diagnostics_render_cleanup;
11423 diagnostics_render_cleanup:
11424 for (index = 0; index < status_count; index++) {
11425 free(statuses[index].name);
11435 const char *state_path,
11437 const char *app_root
11447 char *result = NULL;
11448 if (state_path == NULL || state_path[0] ==
'\0' || app_root == NULL || app_root[0] ==
'\0') {
11453 if (root == NULL) {
11462 long business_id = 0;
11464 char *description = NULL;
11477 if (doc == NULL || params == NULL) {
11504 if (page_doc == NULL || page_obj == NULL
11520 const char *state_path,
11521 const char *business_slug,
11522 const char *app_root
11524 static const char *invite_account_type_options_html =
11525 "<option value=\"business_customer\" selected>Customer</option>"
11526 "<option value=\"business_manager\">Manager</option>"
11527 "<option value=\"business_technician\">Technician</option>";
11541 char *business_name = NULL;
11542 char *new_service_sort_order_options_html = NULL;
11543 char *new_service_booking_options_editor_html = NULL;
11544 char *service_technician_checkboxes_html = NULL;
11545 char *availability_day_options_html = NULL;
11546 char *result = NULL;
11547 long business_id = 0;
11548 long service_count = 0;
11549 long technician_count = 0;
11550 long appointment_count = 0;
11551 long unassigned_count = 0;
11552 int stripe_connected = 0;
11553 if (state_path == NULL || state_path[0] ==
'\0' || business_slug == NULL || business_slug[0] ==
'\0' || app_root == NULL || app_root[0] ==
'\0') {
11559 if (root == NULL || business == NULL) {
11571 long row_business_id = 0;
11572 long service_id = 0;
11577 if (row_business_id != business_id) {
11584 if (!
text_buffer_append_format_runtime(&service_rows,
"<div class=\"card\"><strong>%s</strong><br><span class=\"muted\">%s</span><br><a href=\"/businesses/%s/services/%ld/edit/\">Edit</a></div>", name != NULL ? name :
"", cost != NULL ? cost :
"", business_slug, service_id)) {
11585 free(name); free(cost);
goto business_detail_admin_cleanup;
11587 free(name); free(cost);
11594 char *email = NULL;
11595 long row_business_id = 0;
11596 long technician_id = 0;
11601 if (row_business_id != business_id) {
11604 technician_count++;
11608 if (!
text_buffer_append_format_runtime(&technician_rows,
"<div class=\"card\"><strong>%s</strong><br><span class=\"muted\">%s</span><br><a href=\"/businesses/%s/technicians/%ld/edit/\">Edit</a></div>", name != NULL ? name :
"", email != NULL ? email :
"", business_slug, technician_id)) {
11609 free(name); free(email);
goto business_detail_admin_cleanup;
11611 free(name); free(email);
11618 char *start_time = NULL;
11619 char *end_time = NULL;
11620 long row_business_id = 0;
11621 long availability_id = 0;
11626 if (row_business_id != business_id) {
11633 if (!
text_buffer_append_format_runtime(&availability_html,
"<div class=\"card\"><strong>%s</strong> %s-%s<br><a href=\"/businesses/%s/availability/%ld/edit/\">Edit</a></div>", day != NULL ? day :
"", start_time != NULL ? start_time :
"", end_time != NULL ? end_time :
"", business_slug, availability_id)) {
11634 free(day); free(start_time); free(end_time);
goto business_detail_admin_cleanup;
11636 free(day); free(start_time); free(end_time);
11643 long row_business_id = 0;
11648 if (row_business_id == business_id) {
11649 appointment_count++;
11651 unassigned_count++;
11663 if (page_doc == NULL || page_obj == NULL || new_service_sort_order_options_html == NULL || new_service_booking_options_editor_html == NULL || service_technician_checkboxes_html == NULL || availability_day_options_html == NULL) {
11664 goto business_detail_admin_cleanup;
11687 yyjson_mut_obj_add_strcpy(page_doc, page_obj,
"new_service_sort_order_options_html", new_service_sort_order_options_html);
11688 yyjson_mut_obj_add_strcpy(page_doc, page_obj,
"new_service_booking_options_editor_html", new_service_booking_options_editor_html);
11689 yyjson_mut_obj_add_strcpy(page_doc, page_obj,
"service_technician_checkboxes_html", service_technician_checkboxes_html);
11698 char path_text[512];
11699 snprintf(path_text,
sizeof(path_text),
"/b/%s/", business_slug);
11702 snprintf(path_text,
sizeof(path_text),
"/businesses/%s/edit/", business_slug);
11704 snprintf(path_text,
sizeof(path_text),
"/business/%s/calendar/", business_slug);
11706 snprintf(path_text,
sizeof(path_text),
"/businesses/%s/delete/", business_slug);
11708 snprintf(path_text,
sizeof(path_text),
"/businesses/%s/connect-stripe/", business_slug);
11710 snprintf(path_text,
sizeof(path_text),
"/businesses/%s/", business_slug);
11712 snprintf(path_text,
sizeof(path_text),
"/businesses/%s/invites/new/", business_slug);
11714 snprintf(path_text,
sizeof(path_text),
"/businesses/%s/services/", business_slug);
11716 snprintf(path_text,
sizeof(path_text),
"/businesses/%s/services/new/", business_slug);
11718 snprintf(path_text,
sizeof(path_text),
"/businesses/%s/technicians/", business_slug);
11720 snprintf(path_text,
sizeof(path_text),
"/businesses/%s/technicians/new/", business_slug);
11722 snprintf(path_text,
sizeof(path_text),
"/businesses/%s/availability/", business_slug);
11724 snprintf(path_text,
sizeof(path_text),
"/businesses/%s/availability/new/", business_slug);
11729business_detail_admin_cleanup:
11733 free(business_name);
11734 free(new_service_sort_order_options_html);
11735 free(new_service_booking_options_editor_html);
11736 free(service_technician_checkboxes_html);
11737 free(availability_day_options_html);
11744 const char *state_path,
11745 const char *business_slug
11753 char *description = NULL;
11754 char *address = NULL;
11755 char *raw_image = NULL;
11756 char *image_url = NULL;
11757 char *current_logo_html = NULL;
11758 char *public_options_html = NULL;
11759 char *time_format_options_html = NULL;
11760 char *time_format = NULL;
11761 char *result = NULL;
11765 if (state_path == NULL || state_path[0] ==
'\0' || business_slug == NULL || business_slug[0] ==
'\0') {
11771 if (business == NULL) {
11782 if (image_url != NULL && image_url[0] !=
'\0') {
11787 || !
text_buffer_append_text_runtime(&buffer,
"\" style=\"max-width:180px;max-height:180px;border-radius:12px;border:1px solid #d0d7de;display:block;\">")) {
11788 goto business_edit_params_cleanup;
11791 goto business_edit_params_cleanup;
11794 if (current_logo_html == NULL) {
11795 goto business_edit_params_cleanup;
11797 public_options_html = strdup(is_public ?
"<option value=\"true\" selected>Public</option><option value=\"false\">Private</option>" :
"<option value=\"true\">Public</option><option value=\"false\" selected>Private</option>");
11798 time_format_options_html = strdup(time_format != NULL &&
streq(time_format,
"military") ?
"<option value=\"am_pm\">12-hour (AM/PM)</option><option value=\"military\" selected>24-hour (Military)</option>" :
"<option value=\"am_pm\" selected>12-hour (AM/PM)</option><option value=\"military\">24-hour (Military)</option>");
11801 if (public_options_html == NULL || time_format_options_html == NULL || doc == NULL || page == NULL) {
11802 goto business_edit_params_cleanup;
11806 char save_path[256];
11807 char cancel_path[256];
11808 snprintf(save_path,
sizeof(save_path),
"/businesses/%s/edit/", business_slug);
11809 snprintf(cancel_path,
sizeof(cancel_path),
"/businesses/%s/", business_slug);
11816 || !
yyjson_mut_obj_add_strcpy(doc, page,
"public_options_html", public_options_html != NULL ? public_options_html :
"")
11817 || !
yyjson_mut_obj_add_strcpy(doc, page,
"time_format_options_html", time_format_options_html != NULL ? time_format_options_html :
"")) {
11818 goto business_edit_params_cleanup;
11823business_edit_params_cleanup:
11825 free(name); free(description); free(address); free(raw_image); free(image_url); free(current_logo_html); free(public_options_html); free(time_format_options_html); free(time_format);
11837 char *description = NULL;
11839 char *image_raw = NULL;
11840 char *image_url = NULL;
11841 char *service_visual_html = NULL;
11842 char *service_sort_order_options_html = NULL;
11843 char *booking_options_editor_html = NULL;
11844 char *technician_checkboxes_html = NULL;
11845 long business_id = 0;
11846 long service_duration = 45;
11847 long service_sort_order = 0;
11849 if (business == NULL || service == NULL || slug == NULL || slug[0] ==
'\0') {
11860 if (image_raw == NULL || image_raw[0] ==
'\0') {
11867 if (service_duration <= 0) {
11868 service_duration = 45;
11870 if (service_sort_order <= 0) {
11879 if (service_visual_html == NULL || service_sort_order_options_html == NULL || booking_options_editor_html == NULL || technician_checkboxes_html == NULL || doc == NULL || page == NULL) {
11880 goto service_edit_cleanup;
11884 char save_path[256];
11885 char cancel_path[256];
11886 char duration_buffer[32];
11887 snprintf(save_path,
sizeof(save_path),
"/businesses/%s/services/%ld/edit/", slug, service_id);
11888 snprintf(cancel_path,
sizeof(cancel_path),
"/businesses/%s/", slug);
11889 snprintf(duration_buffer,
sizeof(duration_buffer),
"%ld", service_duration);
11900 goto service_edit_cleanup;
11904service_edit_cleanup:
11905 free(name); free(description); free(cost); free(image_raw); free(image_url); free(service_visual_html); free(service_sort_order_options_html); free(booking_options_editor_html); free(technician_checkboxes_html);
11916 char *email = NULL;
11918 if (business == NULL || technician == NULL || slug == NULL || slug[0] ==
'\0') {
11928 if (doc == NULL || page == NULL) {
11929 goto technician_edit_cleanup;
11933 char save_path[256];
11934 char cancel_path[256];
11935 snprintf(save_path,
sizeof(save_path),
"/businesses/%s/technicians/%ld/edit/", slug, technician_id);
11936 snprintf(cancel_path,
sizeof(cancel_path),
"/businesses/%s/", slug);
11941 goto technician_edit_cleanup;
11945technician_edit_cleanup:
11946 free(name); free(email);
11959 fputs(
"[]", stdout);
11964 if (mut_doc == NULL || array == NULL) {
11974 char *description = NULL;
11975 char *image_url = NULL;
11976 long business_id = 0;
11985 if (slug == NULL || slug[0] ==
'\0') {
12026 fputs(
"[]", stdout);
12031 if (mut_doc == NULL || array == NULL) {
12041 long business_id = 0;
12042 long service_id = 0;
12043 long technician_id = 0;
12044 char *business_name = NULL;
12045 char *business_slug = NULL;
12046 char *service_name = NULL;
12047 char *technician_name = NULL;
12050 char *status = NULL;
12079 free(business_name);
12080 free(business_slug);
12081 free(service_name);
12082 free(technician_name);
12089 free(business_name);
12090 free(business_slug);
12091 free(service_name);
12092 free(technician_name);
12110 fputs(
"[]", stdout);
12115 if (mut_doc == NULL || array == NULL) {
12126 long business_id = 0;
12127 long tenant_id = 0;
12128 long service_id = 0;
12130 long technician_id = 0;
12131 char *business_name = NULL;
12132 char *service_name = NULL;
12133 char *customer_name = NULL;
12134 char *technician_name = NULL;
12137 char *status = NULL;
12172 free(business_name);
12173 free(service_name);
12174 free(customer_name);
12175 free(technician_name);
12182 free(business_name);
12183 free(service_name);
12184 free(customer_name);
12185 free(technician_name);
12207 if (mut_doc == NULL || page_obj == NULL || subs_array == NULL) {
12225 || !
yyjson_mut_obj_add_strcpy(mut_doc, page_obj,
"payment_preference", payment_preference != NULL && payment_preference[0] !=
'\0' ? payment_preference :
"pay_on_site")
12230 free(phone_number);
12232 free(payment_preference);
12239 free(phone_number);
12241 free(payment_preference);
12247 long business_id = 0;
12248 char *business_name = NULL;
12249 char *business_slug = NULL;
12263 free(business_name);
12264 free(business_slug);
12268 free(business_name);
12269 free(business_slug);
12282 if (array_value == NULL || token_hash == NULL || token_hash[0] ==
'\0' || now_iso == NULL || now_iso[0] ==
'\0' || !
yyjson_is_arr(array_value)) {
12290 char *expires_at = NULL;
12291 char *claimed_at = NULL;
12303 if (match && (claimed_at == NULL || claimed_at[0] ==
'\0') && expires_at != NULL && strcmp(expires_at, now_iso) > 0) {
12321 if (list != NULL) {
12323 list->
items = NULL;
12331 size_t next_capacity = 0;
12332 if (list == NULL || item == NULL) {
12341 if (next_items == NULL) {
12344 list->
items = next_items;
12355 if (lhs_id < rhs_id) {
12358 if (lhs_id > rhs_id) {
12366 if (value == NULL) {
12370 if (text == NULL) {
12373 fputs(text, stdout);
12374 fputc(
'\n', stdout);
12396 for (i = 0; i < list.
count; i++) {
12409 for (i = 0; i + 1 < count; i += 2) {
12410 if (values[i] == first && values[i + 1] == second) {
12418 if (section_name == NULL || section_name[0] ==
'\0') {
12421 if (
streq(section_name,
"users") ||
streq(section_name,
"sessions") ||
streq(section_name,
"businesses") ||
streq(section_name,
"technicians") ||
streq(section_name,
"business_availability") ||
streq(section_name,
"technician_availability") ||
streq(section_name,
"services") ||
streq(section_name,
"bookings") ||
streq(section_name,
"appointments") ||
streq(section_name,
"email_verifications") ||
streq(section_name,
"password_resets") ||
streq(section_name,
"invites") ||
streq(section_name,
"rate_limit_events") ||
streq(section_name,
"workflow_executions") ||
streq(section_name,
"workflow_execution_events") ||
streq(section_name,
"job_executions") ||
streq(section_name,
"job_execution_attempts")) {
12424 if (
streq(section_name,
"tenants")) {
12445 for (i = 0; i < list.
count; i++) {
12452 if (mut_doc == NULL || obj == NULL || !
yyjson_mut_obj_add_int(mut_doc, obj,
"id",
id) || !
yyjson_mut_obj_add_strcpy(mut_doc, obj,
"tenant_kind",
"business") || !
yyjson_mut_obj_add_int(mut_doc, obj,
"parent_tenant_id", 0) || !
yyjson_mut_obj_add_strcpy(mut_doc, obj,
"slug", slug != NULL ? slug :
"") || !
yyjson_mut_obj_add_strcpy(mut_doc, obj,
"display_name", name != NULL ? name :
"") || !
yyjson_mut_obj_add_bool(mut_doc, obj,
"is_active", 1) || !
yyjson_mut_obj_add_int(mut_doc, obj,
"owner_user_id", owner_id) || !
yyjson_mut_obj_add_int(mut_doc, obj,
"source_business_id",
id)) {
12468 if (
streq(section_name,
"tenant_memberships")) {
12489 for (i = 0; i < list.
count; i++) {
12494 if (mut_doc == NULL || obj == NULL || !
yyjson_mut_obj_add_int(mut_doc, obj,
"id", (
long)(i + 1)) || !
yyjson_mut_obj_add_int(mut_doc, obj,
"tenant_id", tenant_id) || !
yyjson_mut_obj_add_int(mut_doc, obj,
"user_id", user_id) || !
yyjson_mut_obj_add_strcpy(mut_doc, obj,
"role_id",
"business_owner") || !
yyjson_mut_obj_add_strcpy(mut_doc, obj,
"membership_status",
"active") || !
yyjson_mut_obj_add_int(mut_doc, obj,
"invited_by_user_id", 0)) {
12506 if (
streq(section_name,
"subscriptions") ||
streq(section_name,
"ratings")) {
12511 long seen_pairs[2048];
12512 size_t seen_count = 0;
12523 for (idx = list.
count; idx > 0; idx--) {
12527 if (seen_count + 2 <=
sizeof(seen_pairs) /
sizeof(seen_pairs[0]) && !
seen_long_pair_runtime(seen_pairs, seen_count, user_id, business_id)) {
12528 seen_pairs[seen_count++] = user_id;
12529 seen_pairs[seen_count++] = business_id;
12530 list.
items[idx - 1] = candidate;
12532 list.
items[idx - 1] = NULL;
12536 for (idx = 0; idx < list.
count; idx++) {
12537 if (list.
items[idx] != NULL) {
12544 if (
streq(section_name,
"google_calendar_connections")) {
12559 for (idx = list.
count; idx > 0; idx--) {
12566 for (j = idx; j < list.
count; j++) {
12568 char *other_mode = NULL;
12569 if (other == NULL) {
12583 list.
items[idx - 1] = NULL;
12587 for (idx = 0; idx < list.
count; idx++) {
12588 if (list.
items[idx] != NULL) {
12595 if (
streq(section_name,
"audit_log")) {
12639 memset(row, 0,
sizeof(*row));
12644 if (list == NULL) {
12647 for (index = 0; index < list->
count; index++) {
12651 list->
items = NULL;
12658 size_t next_capacity = 0;
12659 if (list == NULL || row == NULL) {
12665 if (next_items == NULL) {
12668 list->
items = next_items;
12672 memset(row, 0,
sizeof(*row));
12677 const char *source = text;
12678 if (source == NULL || source[0] ==
'\0') {
12679 source = fallback != NULL ? fallback :
"";
12681 return strdup(source);
12689 for (index = 0; index < field_count; index++) {
12704 for (index = 0; index < field_count; index++) {
12706 if (value != NULL && value[0] !=
'\0') {
12721 long actual_value = 0;
12730 const char *colon = NULL;
12731 if (resource_id == NULL || resource_id[0] ==
'\0') {
12734 colon = strchr(resource_id,
':');
12735 if (colon == NULL || colon == resource_id) {
12738 return dup_range(resource_id, (
size_t)(colon - resource_id));
12742 char *resource_id = NULL;
12743 char *entity_type = NULL;
12749 if (resource_id != NULL && resource_id[0] !=
'\0') {
12750 return resource_id;
12755 if (entity_type != NULL && entity_type[0] !=
'\0' && entity_id_value != NULL && !
yyjson_is_null(entity_id_value)) {
12757 char *joined = NULL;
12758 if (entity_id_json != NULL) {
12759 size_t needed = strlen(entity_type) + strlen(entity_id_json) + 2;
12760 joined = (
char *)malloc(needed);
12761 if (joined != NULL) {
12762 snprintf(joined, needed,
"%s:%s", entity_type, entity_id_json);
12766 free(entity_id_json);
12774 const char *fields[] = {
"tenant_id",
"target_tenant_id"};
12776 if (tenant_id != 0) {
12779 if (business_id != 0) {
12786 if (doc == NULL || obj == NULL || key == NULL || key[0] ==
'\0') {
12789 if (value == NULL) {
12800 const char *lhs_source = lhs != NULL && lhs->
source != NULL ? lhs->
source :
"";
12801 const char *rhs_source = rhs != NULL && rhs->
source != NULL ? rhs->
source :
"";
12802 int cmp = strcmp(lhs_created_at, rhs_created_at);
12806 cmp = strcmp(lhs_source, rhs_source);
12820 char *scope_kind = NULL;
12821 long subscription_user_id = 0;
12824 if (subscription_scope == NULL || !
yyjson_is_obj(subscription_scope)) {
12828 if (scope_kind == NULL || scope_kind[0] ==
'\0' ||
streq(scope_kind,
"all")) {
12835 allowed = (subscription_user_id != 0 && event_user_id == subscription_user_id) || (event_tenant_id != 0 &&
long_in_array_runtime(allowed_tenant_ids, event_tenant_id));
12840 char *source_filter = NULL;
12841 char *event_kind_filter = NULL;
12842 char *resource_id_filter = NULL;
12843 char *trace_id_filter = NULL;
12845 if (filters == NULL || !
yyjson_is_obj(filters) || row == NULL) {
12852 if (source_filter != NULL && source_filter[0] !=
'\0' && !
streq(source_filter, row->
source != NULL ? row->
source :
"")) {
12855 if (matches && event_kind_filter != NULL && event_kind_filter[0] !=
'\0' && !
streq(event_kind_filter, row->
event_kind != NULL ? row->
event_kind :
"")) {
12858 if (matches && resource_id_filter != NULL && resource_id_filter[0] !=
'\0' && !
streq(resource_id_filter, row->
resource_id != NULL ? row->
resource_id :
"")) {
12864 free(source_filter);
12865 free(event_kind_filter);
12866 free(resource_id_filter);
12867 free(trace_id_filter);
12871static int collect_event_stream_row_runtime(
event_stream_row_list_runtime *rows,
yyjson_val *root,
const char *source,
const char *event_kind_default,
yyjson_val *payload,
const char *created_at_field_fallback,
long native_order,
yyjson_val *subscription_scope,
yyjson_val *filters,
const char *created_at_override,
const char *event_kind_override) {
12872 static const char *user_fields[] = {
"user_id",
"actor_user_id",
"invited_by_user_id",
"owner_user_id"};
12873 static const char *business_fields[] = {
"business_id",
"source_business_id"};
12874 static const char *tenant_fields[] = {
"tenant_id",
"target_tenant_id"};
12875 static const char *trace_fields[] = {
"replay_trace_id",
"trace_id"};
12877 char *created_at = NULL;
12878 char *event_kind = NULL;
12879 long business_id = 0;
12880 memset(&row, 0,
sizeof(row));
12881 if (rows == NULL || root == NULL || source == NULL || payload == NULL || !
yyjson_is_obj(payload)) {
12885 event_kind = event_kind_override != NULL ? strdup(event_kind_override) : NULL;
12886 if (event_kind == NULL || event_kind[0] ==
'\0') {
12890 if (event_kind == NULL || event_kind[0] ==
'\0') {
12898 created_at = created_at_override != NULL ? strdup(created_at_override) : NULL;
12899 if (created_at == NULL || created_at[0] ==
'\0') {
12939 size_t emitted = 0;
12940 if (limit_value <= 0) {
12945 long native_order = (long)(++index);
12948 int ok =
collect_event_stream_row_runtime(&rows, root,
"audit_log",
"audit_event", item, NULL, native_order, subscription_scope, filters, created_at, event_kind);
12963 int ok =
collect_event_stream_row_runtime(&rows, root,
"workflow_execution_event",
"workflow_execution_event", payload != NULL &&
yyjson_is_obj(payload) ? payload : item, NULL, native_order, subscription_scope, filters, created_at, event_kind);
12977 if (created_at == NULL || created_at[0] ==
'\0') {
12981 if (!
collect_event_stream_row_runtime(&rows, root,
"job_execution",
"job_execution", item, NULL, native_order, subscription_scope, filters, created_at, event_kind)) {
12996 if (created_at == NULL || created_at[0] ==
'\0') {
13000 if (created_at == NULL || created_at[0] ==
'\0') {
13004 if (!
collect_event_stream_row_runtime(&rows, root,
"job_execution_attempt",
"job_execution_attempt", item, NULL, native_order, subscription_scope, filters, created_at, event_kind)) {
13015 for (index = 0; index < rows.
count; index++) {
13018 long stream_id = (long)index + 1;
13020 if (stream_id <= since_id) {
13023 if (emitted >= (
size_t)limit_value) {
13028 if (doc == NULL || obj == NULL || !
yyjson_mut_obj_add_int(doc, obj,
"stream_id", stream_id) || !
yyjson_mut_obj_add_strcpy(doc, obj,
"source", rows.
items[index].
source != NULL ? rows.
items[index].
source :
"") || !
yyjson_mut_obj_add_strcpy(doc, obj,
"event_kind", rows.
items[index].
event_kind != NULL ? rows.
items[index].
event_kind :
"message") || !
add_json_string_or_null_runtime(doc, obj,
"created_at", rows.
items[index].
created_at) || !
yyjson_mut_obj_add_int(doc, obj,
"user_id", rows.
items[index].
user_id) || !
yyjson_mut_obj_add_int(doc, obj,
"business_id", rows.
items[index].
business_id) || !
yyjson_mut_obj_add_int(doc, obj,
"tenant_id", rows.
items[index].
tenant_id) || !
add_json_string_or_null_runtime(doc, obj,
"resource_kind", rows.
items[index].
resource_kind) || !
add_json_string_or_null_runtime(doc, obj,
"resource_id", rows.
items[index].
resource_id) || !
add_json_string_or_null_runtime(doc, obj,
"replay_trace_id", rows.
items[index].
replay_trace_id) || !
yyjson_mut_obj_put(obj,
yyjson_mut_strcpy(doc,
"payload_json"),
yyjson_val_mut_copy(doc, rows.
items[index].
payload_json))) {
13040 fputc(
'\n', stdout);
13048 const char *name = NULL;
13052 if (argc < 1 || argv == NULL || state_path == NULL || state_path[0] ==
'\0') {
13065 if (
streq(name,
"ucal-import-row-json-lines")) {
13071 if (
streq(name,
"business-json-by-slug")) {
13077 if (
streq(name,
"business-id-by-slug")) {
13084 if (
streq(name,
"tenant-id-for-business-slug")) {
13090 if (
streq(name,
"tenant-id-for-service-id")) {
13096 if (
streq(name,
"tenant-id-for-technician-id")) {
13102 if (
streq(name,
"appointment-json-by-id")) {
13108 if (
streq(name,
"service-duration-minutes")) {
13115 if (
streq(name,
"service-assigned-technician-ids-lines")) {
13122 if (
streq(name,
"business-technician-ids-lines")) {
13128 if (
streq(name,
"business-availability-lines")) {
13135 if (
streq(name,
"technician-availability-lines")) {
13142 if (
streq(name,
"technician-appointments-lines")) {
13149 if (
streq(name,
"business-open-for-slot")) {
13158 if (
streq(name,
"technician-booked-for-slot")) {
13167 if (
streq(name,
"assign-technician-for-slot")) {
13176 if (
streq(name,
"active-tenant-ids-lines")) {
13181 if (
streq(name,
"user-membership-tenant-ids-json")) {
13187 if (
streq(name,
"manageable-business-cards-json")) {
13196 if (
streq(name,
"business-dashboard-page-json")) {
13206 if (
streq(name,
"explore-page-json")) {
13212 if (
streq(name,
"public-business-cards-json")) {
13217 if (
streq(name,
"managed-appointments-json")) {
13226 if (
streq(name,
"user-appointments-json")) {
13232 if (
streq(name,
"calendar-page-json")) {
13243 if (
streq(name,
"profile-page-json")) {
13249 if (
streq(name,
"appointments-page-json")) {
13260 if (
streq(name,
"profile-render-page-json")) {
13263 const char *payment_preference_options_html =
json_option_value_runtime(argc, argv,
"--payment-preference-options-html");
13268 if (
streq(name,
"user-json-by-username")) {
13274 if (
streq(name,
"user-json-by-email")) {
13280 if (
streq(name,
"user-json-by-id")) {
13286 if (
streq(name,
"service-json-by-id")) {
13292 if (
streq(name,
"service-json-for-business")) {
13299 if (
streq(name,
"business-availability-json-for-business")) {
13306 if (
streq(name,
"first-service-json-for-business")) {
13312 if (
streq(name,
"service-image-url-for-business")) {
13319 if (
streq(name,
"service-sort-order-for-business")) {
13328 if (
streq(name,
"business-name-by-id")) {
13334 if (
streq(name,
"business-slug-by-id")) {
13340 if (
streq(name,
"business-time-format-by-id")) {
13346 if (
streq(name,
"business-admin-service-rows-json")) {
13352 if (
streq(name,
"business-admin-technician-rows-json")) {
13358 if (
streq(name,
"business-public-page-json")) {
13365 if (
streq(name,
"business-edit-page-json")) {
13371 if (
streq(name,
"service-edit-page-json")) {
13379 if (
streq(name,
"technician-edit-page-json")) {
13386 if (
streq(name,
"business-public-render-page-json")) {
13394 if (
streq(name,
"business-calendar-page-json")) {
13400 if (
streq(name,
"business-calendar-render-page-json")) {
13410 if (
streq(name,
"service-book-form-page-json")) {
13419 if (
streq(name,
"service-visual-html")) {
13424 int code = html != NULL ? 0 : 69;
13425 if (html != NULL) {
13426 fputs(html, stdout);
13432 if (
streq(name,
"service-sort-order-options-html")) {
13438 int code = html != NULL ? 0 : 69;
13439 if (html != NULL) {
13440 fputs(html, stdout);
13446 if (
streq(name,
"service-booking-options-editor-html")) {
13450 int code = html != NULL ? 0 : 69;
13451 if (html != NULL) {
13452 fputs(html, stdout);
13458 if (
streq(name,
"service-technician-checkboxes-html")) {
13464 int code = html != NULL ? 0 : 69;
13465 if (html != NULL) {
13466 fputs(html, stdout);
13472 if (
streq(name,
"business-technician-checkboxes-html")) {
13477 int code = html != NULL ? 0 : 69;
13478 if (html != NULL) {
13479 fputs(html, stdout);
13485 if (
streq(name,
"business-admin-page-json")) {
13488 const char *invite_account_type_options_html =
json_option_value_runtime(argc, argv,
"--invite-account-type-options-html");
13493 if (
streq(name,
"business-image-url-by-slug")) {
13499 if (
streq(name,
"tenant-id-for-business-id")) {
13505 if (
streq(name,
"google-calendar-connection-for-business")) {
13511 if (
streq(name,
"booking-json-for-user")) {
13518 if (
streq(name,
"technician-json-for-business")) {
13525 if (
streq(name,
"service-booking-options-array-json")) {
13529 if (options == NULL) {
13530 fputs(
"[]", stdout);
13540 if (
streq(name,
"service-booking-options-json")) {
13543 if (service == NULL) {
13544 fputs(
"[]", stdout);
13553 fprintf(stdout,
"[{\"label\":");
13554 if (name_copy != NULL) {
13558 fprintf(stdout,
"%s", name_json != NULL ? name_json :
"\"\"");
13562 fputs(
"\"\"", stdout);
13564 fprintf(stdout,
",\"duration_minutes\":%ld,\"cost\":", duration);
13565 if (cost_copy != NULL) {
13569 fprintf(stdout,
"%s", cost_json != NULL ? cost_json :
"\"\"");
13573 fputs(
"\"\"", stdout);
13575 fputs(
"}", stdout);
13576 if (booking_options != NULL) {
13582 if (text != NULL) {
13583 fputc(
',', stdout);
13584 fputs(text, stdout);
13590 fputs(
"]", stdout);
13597 if (
streq(name,
"service-selected-booking-option-json")) {
13602 if (service == NULL) {
13603 fputs(
"{}", stdout);
13607 if (booking_option_index <= 0) {
13608 booking_option_index = 0;
13610 if (booking_options != NULL &&
yyjson_arr_size(booking_options) > 0) {
13612 if (item == NULL) {
13615 if (item != NULL) {
13621 fputs(
"{}", stdout);
13625 if (
streq(name,
"event-stream-row-json-lines")) {
13632 yyjson_val *subscription_scope_root =
load_root_from_text_runtime(subscription_scope_json != NULL ? subscription_scope_json :
"{\"scope_kind\":\"all\"}", &subscription_scope_doc);
13634 long since_id = strtol(since_id_text != NULL ? since_id_text :
"0", NULL, 10);
13635 long limit_value = strtol(limit_text != NULL ? limit_text :
"25", NULL, 10);
13637 if (limit_value <= 0) {
13640 if (limit_value > 100) {
13649 if (
streq(name,
"next-id")) {
13652 if (section_name == NULL || section_name[0] ==
'\0') {
13660 if (
streq(name,
"append-record")) {
13669 if (section_name == NULL || section_name[0] ==
'\0' || record_json == NULL || record_json[0] ==
'\0') {
13700 if (
streq(name,
"update-record-by-id")) {
13710 long record_id = strtol(record_id_text != NULL ? record_id_text :
"0", NULL, 10);
13712 if (section_name == NULL || section_name[0] ==
'\0' || record_id <= 0 || patch_json == NULL || patch_json[0] ==
'\0') {
13736 return record == NULL ? 66 : 69;
13744 if (
streq(name,
"remove-record-by-id")) {
13753 long record_id = strtol(record_id_text != NULL ? record_id_text :
"0", NULL, 10);
13755 if (section_name == NULL || section_name[0] ==
'\0' || record_id <= 0) {
13780 if (
streq(name,
"prune-rate-limit-events")) {
13787 if (cutoff_iso == NULL || cutoff_iso[0] ==
'\0') {
13799 if (events == NULL) {
13806 char *created_at = NULL;
13811 if (created_at == NULL || created_at[0] ==
'\0' || strcmp(created_at, cutoff_iso) < 0) {
13821 if (
streq(name,
"rate-limit-event-count")) {
13830 if (policy_key == NULL || bucket_key == NULL || window_start == NULL) {
13836 char *created_at = NULL;
13837 char *event_ip = NULL;
13847 ip_match = ((ip_address == NULL || ip_address[0] ==
'\0') && (event_ip == NULL || event_ip[0] ==
'\0')) || ((ip_address != NULL && ip_address[0] !=
'\0') && event_ip != NULL &&
streq(event_ip, ip_address));
13848 if (ip_match && created_at != NULL && strcmp(created_at, window_start) >= 0) {
13858 if (
streq(name,
"record-rate-limit-event")) {
13870 if (policy_key == NULL || policy_key[0] ==
'\0' || bucket_key == NULL || bucket_key[0] ==
'\0' || outcome == NULL || outcome[0] ==
'\0' || created_at == NULL || created_at[0] ==
'\0') {
13883 if (events == NULL || event_obj == NULL || !
yyjson_mut_obj_add_int(mut_doc, event_obj,
"id", event_id) || !
yyjson_mut_obj_add_strcpy(mut_doc, event_obj,
"policy_key", policy_key) || !
yyjson_mut_obj_add_strcpy(mut_doc, event_obj,
"bucket_key", bucket_key) || !
yyjson_mut_obj_add_strcpy(mut_doc, event_obj,
"outcome", outcome) || !
yyjson_mut_obj_add_strcpy(mut_doc, event_obj,
"created_at", created_at)) {
13888 if (ip_address != NULL && ip_address[0] !=
'\0') {
13909 if (
streq(name,
"append-audit-event")) {
13918 if (event_kind == NULL || event_kind[0] ==
'\0' || created_at == NULL || created_at[0] ==
'\0') {
13948 if (
streq(name,
"append-workflow-event")) {
13958 if (event_kind == NULL || event_kind[0] ==
'\0' || created_at == NULL || created_at[0] ==
'\0') {
13990 if (
streq(name,
"issue-browser-session")) {
14001 long user_id = strtol(user_id_text != NULL ? user_id_text :
"0", NULL, 10);
14004 if (user_id <= 0 || session_token_hash == NULL || session_token_hash[0] ==
'\0' || created_at == NULL || created_at[0] ==
'\0' || expires_at == NULL || expires_at[0] ==
'\0') {
14017 if (sessions == NULL || session_obj == NULL || !
yyjson_mut_obj_add_int(mut_doc, session_obj,
"id", session_id) || !
yyjson_mut_obj_add_int(mut_doc, session_obj,
"user_id", user_id) || !
yyjson_mut_obj_add_strcpy(mut_doc, session_obj,
"session_token_hash", session_token_hash) || !
yyjson_mut_obj_add_strcpy(mut_doc, session_obj,
"session_kind",
"browser_session") || !
yyjson_mut_obj_put(session_obj,
yyjson_mut_strcpy(mut_doc,
"current_tenant_id"),
yyjson_mut_null(mut_doc)) || !
yyjson_mut_obj_add_strcpy(mut_doc, session_obj,
"expires_at", expires_at) || !
yyjson_mut_obj_put(session_obj,
yyjson_mut_strcpy(mut_doc,
"revoked_at"),
yyjson_mut_null(mut_doc)) || !
yyjson_mut_obj_add_strcpy(mut_doc, session_obj,
"created_at", created_at)) {
14022 if (user_agent != NULL && user_agent[0] !=
'\0') {
14033 if (ip_address != NULL && ip_address[0] !=
'\0') {
14054 if (
streq(name,
"revoke-session-hash")) {
14064 if (session_token_hash == NULL || session_token_hash[0] ==
'\0' || revoked_at == NULL || revoked_at[0] ==
'\0') {
14097 if (
streq(name,
"issue-email-verification")) {
14106 long user_id = strtol(user_id_text != NULL ? user_id_text :
"0", NULL, 10);
14110 if (user_id <= 0 || token_hash == NULL || token_hash[0] ==
'\0' || created_at == NULL || created_at[0] ==
'\0' || expires_at == NULL || expires_at[0] ==
'\0') {
14122 if (verifications == NULL) {
14139 if (verification_obj == NULL || !
yyjson_mut_obj_add_int(mut_doc, verification_obj,
"id", verification_id) || !
yyjson_mut_obj_add_int(mut_doc, verification_obj,
"user_id", user_id) || !
yyjson_mut_obj_add_strcpy(mut_doc, verification_obj,
"token_hash", token_hash) || !
yyjson_mut_obj_add_strcpy(mut_doc, verification_obj,
"expires_at", expires_at) || !
yyjson_mut_obj_put(verification_obj,
yyjson_mut_strcpy(mut_doc,
"claimed_at"),
yyjson_mut_null(mut_doc)) || !
yyjson_mut_obj_add_strcpy(mut_doc, verification_obj,
"created_at", created_at) || !
yyjson_mut_arr_append(verifications, verification_obj)) {
14149 if (
streq(name,
"active-email-verification-json")) {
14156 if (
streq(name,
"claim-email-verification")) {
14168 long verification_id = strtol(verification_id_text != NULL ? verification_id_text :
"0", NULL, 10);
14169 long user_id = strtol(user_id_text != NULL ? user_id_text :
"0", NULL, 10);
14171 if (verification_id <= 0 || user_id <= 0 || claimed_at == NULL || claimed_at[0] ==
'\0') {
14184 if (users == NULL || verifications == NULL) {
14215 fprintf(stdout,
"{\"verification_id\":%ld,\"user_id\":%ld,\"claimed_at\":", verification_id, user_id);
14219 fputs(claimed_at_json != NULL ? claimed_at_json :
"\"\"", stdout);
14220 free(claimed_at_json);
14222 fputc(
'}', stdout);
14228 if (
streq(name,
"issue-password-reset")) {
14237 long user_id = strtol(user_id_text != NULL ? user_id_text :
"0", NULL, 10);
14241 if (user_id <= 0 || token_hash == NULL || token_hash[0] ==
'\0' || created_at == NULL || created_at[0] ==
'\0' || expires_at == NULL || expires_at[0] ==
'\0') {
14253 if (resets == NULL) {
14270 if (reset_obj == NULL || !
yyjson_mut_obj_add_int(mut_doc, reset_obj,
"id", reset_id) || !
yyjson_mut_obj_add_int(mut_doc, reset_obj,
"user_id", user_id) || !
yyjson_mut_obj_add_strcpy(mut_doc, reset_obj,
"token_hash", token_hash) || !
yyjson_mut_obj_add_strcpy(mut_doc, reset_obj,
"expires_at", expires_at) || !
yyjson_mut_obj_put(reset_obj,
yyjson_mut_strcpy(mut_doc,
"claimed_at"),
yyjson_mut_null(mut_doc)) || !
yyjson_mut_obj_add_strcpy(mut_doc, reset_obj,
"created_at", created_at) || !
yyjson_mut_arr_append(resets, reset_obj)) {
14280 if (
streq(name,
"active-password-reset-json")) {
14287 if (
streq(name,
"apply-password-reset")) {
14302 long reset_id = strtol(reset_id_text != NULL ? reset_id_text :
"0", NULL, 10);
14303 long user_id = strtol(user_id_text != NULL ? user_id_text :
"0", NULL, 10);
14305 if (reset_id <= 0 || user_id <= 0 || password_hash == NULL || password_hash[0] ==
'\0' || claimed_at == NULL || claimed_at[0] ==
'\0') {
14319 if (users == NULL || resets == NULL || sessions == NULL) {
14367 fprintf(stdout,
"{\"reset_id\":%ld,\"user_id\":%ld,\"claimed_at\":", reset_id, user_id);
14371 fputs(claimed_at_json != NULL ? claimed_at_json :
"\"\"", stdout);
14372 free(claimed_at_json);
14374 fputc(
'}', stdout);
14380 if (
streq(name,
"issue-invite")) {
14395 long invited_by_user_id = strtol(invited_by_user_id_text != NULL ? invited_by_user_id_text :
"0", NULL, 10);
14396 long target_tenant_id = strtol(target_tenant_id_text != NULL ? target_tenant_id_text :
"0", NULL, 10);
14399 if (email_value == NULL || email_value[0] ==
'\0' || account_type_value == NULL || account_type_value[0] ==
'\0' || invite_kind_value == NULL || invite_kind_value[0] ==
'\0' || token_hash == NULL || token_hash[0] ==
'\0' || expires_at == NULL || expires_at[0] ==
'\0' || created_at == NULL || created_at[0] ==
'\0') {
14411 if (invites == NULL) {
14419 char *item_email = NULL;
14420 char *item_kind = NULL;
14436 if (invite_obj == NULL || !
yyjson_mut_obj_add_int(mut_doc, invite_obj,
"id", invite_id) || !
yyjson_mut_obj_add_strcpy(mut_doc, invite_obj,
"email", email_value) || !
yyjson_mut_obj_add_strcpy(mut_doc, invite_obj,
"account_type", account_type_value) || !
yyjson_mut_obj_add_strcpy(mut_doc, invite_obj,
"invite_kind", invite_kind_value) || !
yyjson_mut_obj_add_strcpy(mut_doc, invite_obj,
"token_hash", token_hash) || !
yyjson_mut_obj_add_strcpy(mut_doc, invite_obj,
"expires_at", expires_at) || !
yyjson_mut_obj_put(invite_obj,
yyjson_mut_strcpy(mut_doc,
"claimed_at"),
yyjson_mut_null(mut_doc)) || !
yyjson_mut_obj_add_strcpy(mut_doc, invite_obj,
"created_at", created_at)) {
14441 if (!
yyjson_mut_obj_put(invite_obj,
yyjson_mut_strcpy(mut_doc,
"target_tenant_id"), target_tenant_id > 0 ?
yyjson_mut_int(mut_doc, target_tenant_id) :
yyjson_mut_null(mut_doc)) || !
yyjson_mut_obj_put(invite_obj,
yyjson_mut_strcpy(mut_doc,
"role_hint"), (role_hint_value != NULL && role_hint_value[0] !=
'\0') ?
yyjson_mut_strcpy(mut_doc, role_hint_value) :
yyjson_mut_null(mut_doc)) || !
yyjson_mut_obj_put(invite_obj,
yyjson_mut_strcpy(mut_doc,
"invited_by_user_id"), invited_by_user_id > 0 ?
yyjson_mut_int(mut_doc, invited_by_user_id) :
yyjson_mut_null(mut_doc)) || !
yyjson_mut_obj_put(invite_obj,
yyjson_mut_strcpy(mut_doc,
"accepted_user_id"),
yyjson_mut_null(mut_doc)) || !
yyjson_mut_arr_append(invites, invite_obj)) {
14451 if (
streq(name,
"active-invite-json")) {
14458 if (
streq(name,
"claim-invite")) {
14468 long invite_id = strtol(invite_id_text != NULL ? invite_id_text :
"0", NULL, 10);
14469 long accepted_user_id = strtol(accepted_user_id_text != NULL ? accepted_user_id_text :
"0", NULL, 10);
14471 if (invite_id <= 0 || accepted_user_id <= 0 || claimed_at == NULL || claimed_at[0] ==
'\0') {
14483 if (invites == NULL) {
14502 fprintf(stdout,
"{\"invite_id\":%ld,\"accepted_user_id\":%ld,\"claimed_at\":", invite_id, accepted_user_id);
14506 fputs(claimed_at_json != NULL ? claimed_at_json :
"\"\"", stdout);
14507 free(claimed_at_json);
14509 fputc(
'}', stdout);
14515 if (
streq(name,
"authenticate-user-json")) {
14518 if (user != NULL) {
14531 if (
streq(name,
"active-session-json")) {
14540 char *expires_at = NULL;
14541 char *revoked_at = NULL;
14549 if (match && (revoked_at == NULL || revoked_at[0] ==
'\0') && expires_at != NULL && strcmp(expires_at, now_iso) > 0) {
14565 if (
streq(name,
"current-user-json")) {
14575 char *expires_at = NULL;
14576 char *revoked_at = NULL;
14584 if (match && (revoked_at == NULL || revoked_at[0] ==
'\0') && expires_at != NULL && strcmp(expires_at, now_iso) > 0) {
14594 if (user_id != 0) {
14596 if (user != NULL) {
14614 size_t in_index = 0;
14615 size_t out_index = 0;
14617 if (text == NULL) {
14620 len = strlen(text);
14621 out = (
char *)malloc(
len + 1);
14625 while (in_index <
len) {
14626 char ch = text[in_index];
14628 out[out_index++] =
' ';
14632 if (ch ==
'%' && in_index + 2 <
len && isxdigit((
unsigned char)text[in_index + 1]) && isxdigit((
unsigned char)text[in_index + 2])) {
14634 unsigned int value = 0;
14635 hex_text[0] = text[in_index + 1];
14636 hex_text[1] = text[in_index + 2];
14637 hex_text[2] =
'\0';
14638 sscanf(hex_text,
"%x", &value);
14639 out[out_index++] = (char)value;
14643 out[out_index++] = ch;
14646 out[out_index] =
'\0';
14653 char *saveptr = NULL;
14654 size_t key_len = 0;
14655 if (raw_query == NULL || key == NULL || key[0] ==
'\0') {
14658 copy = strdup(raw_query);
14659 if (copy == NULL) {
14662 key_len = strlen(key);
14663 part = strtok_r(copy,
"&", &saveptr);
14664 while (part != NULL) {
14665 char *eq = strchr(part,
'=');
14666 if (eq != NULL && (
size_t)(eq - part) == key_len && strncmp(part, key, key_len) == 0) {
14671 part = strtok_r(NULL,
"&", &saveptr);
14678 char *template_copy = NULL;
14679 char *request_copy = NULL;
14680 char *template_save = NULL;
14681 char *request_save = NULL;
14682 char *template_part = NULL;
14683 char *request_part = NULL;
14685 if (out_params != NULL) {
14686 *out_params = NULL;
14688 if (doc == NULL || template_path == NULL || request_path == NULL || out_params == NULL) {
14691 template_copy = strdup(template_path);
14692 request_copy = strdup(request_path);
14694 if (template_copy == NULL || request_copy == NULL || params == NULL) {
14695 free(template_copy);
14696 free(request_copy);
14699 template_part = strtok_r(template_copy,
"/", &template_save);
14700 request_part = strtok_r(request_copy,
"/", &request_save);
14701 while (template_part != NULL || request_part != NULL) {
14702 size_t template_len = template_part != NULL ? strlen(template_part) : 0;
14703 if (template_part == NULL || request_part == NULL) {
14704 free(template_copy);
14705 free(request_copy);
14708 if (template_len >= 2 && template_part[0] ==
'{' && template_part[template_len - 1] ==
'}') {
14709 char *name =
dup_range(template_part + 1, template_len - 2);
14712 if (name == NULL) {
14713 free(template_copy);
14714 free(request_copy);
14719 if (key_val == NULL || value_val == NULL || !
yyjson_mut_obj_put(params, key_val, value_val)) {
14721 free(template_copy);
14722 free(request_copy);
14726 }
else if (!
streq(template_part, request_part)) {
14727 free(template_copy);
14728 free(request_copy);
14731 template_part = strtok_r(NULL,
"/", &template_save);
14732 request_part = strtok_r(NULL,
"/", &request_save);
14734 free(template_copy);
14735 free(request_copy);
14736 *out_params = params;
14749 if (file_path == NULL || method == NULL || request_path == NULL) {
14764 if (surface_kind == NULL || (!
streq(surface_kind,
"generated_api") && !
streq(surface_kind,
"generated_admin"))) {
14765 free(surface_kind);
14768 free(surface_kind);
14775 char *operation_name = NULL;
14776 char *operation_method = NULL;
14777 char *operation_path = NULL;
14788 if (operation_method == NULL || operation_method[0] ==
'\0') {
14789 free(operation_method);
14790 operation_method = strdup(
"GET");
14792 if (operation_name == NULL || operation_method == NULL || operation_path == NULL || operation_path[0] ==
'\0' || !
streq(operation_method, method)) {
14793 free(operation_name);
14794 free(operation_method);
14795 free(operation_path);
14800 if (out_doc == NULL || out_root == NULL || !
route_template_match_into_mut_obj_runtime(out_doc, operation_path, request_path, ¶ms) || !
yyjson_mut_obj_put(out_root,
yyjson_mut_strcpy(out_doc,
"surface"),
yyjson_val_mut_copy(out_doc, surface)) || !
yyjson_mut_obj_add_strcpy(out_doc, out_root,
"operation", operation_name) || !
yyjson_mut_obj_put(out_root,
yyjson_mut_strcpy(out_doc,
"operation_config"),
yyjson_val_mut_copy(out_doc, op_value)) || !
yyjson_mut_obj_put(out_root,
yyjson_mut_strcpy(out_doc,
"params"), params)) {
14801 free(operation_name);
14802 free(operation_method);
14803 free(operation_path);
14809 free(operation_name);
14810 free(operation_method);
14811 free(operation_path);
14833 char *actual = NULL;
14835 if (expected == NULL) {
14839 match = actual != NULL &&
streq(actual, expected);
14846 if (text == NULL || text[0] ==
'\0') {
14849 for (index = 0; text[index] !=
'\0'; index++) {
14850 if (!isdigit((
unsigned char)text[index])) {
14858 size_t hay_len = 0;
14859 size_t needle_len = 0;
14861 if (haystack == NULL || needle == NULL) {
14864 hay_len = strlen(haystack);
14865 needle_len = strlen(needle);
14866 if (needle_len == 0) {
14869 if (needle_len > hay_len) {
14872 for (start = 0; start + needle_len <= hay_len; start++) {
14875 for (idx = 0; idx < needle_len; idx++) {
14876 if (tolower((
unsigned char)haystack[start + idx]) != tolower((
unsigned char)needle[idx])) {
14905 if (value == NULL) {
14908 if (
streq(value,
"Mon"))
return 1;
14909 if (
streq(value,
"Tue"))
return 2;
14910 if (
streq(value,
"Wed"))
return 3;
14911 if (
streq(value,
"Thu"))
return 4;
14912 if (
streq(value,
"Fri"))
return 5;
14913 if (
streq(value,
"Sat"))
return 6;
14914 if (
streq(value,
"Sun"))
return 7;
14952 if (rows == NULL || field_name == NULL || expected_value == NULL || !
yyjson_arr_iter_init(rows, &iter)) {
14967 if (scope_field == NULL || scope_field[0] ==
'\0' || context_id_value == NULL ||
yyjson_is_null(context_id_value)) {
14977 int match = left != NULL && right != NULL &&
streq(left, right);
14996 if (value == NULL || value[0] ==
'\0') {
15002 if (field == NULL || field[0] ==
'\0') {
15009 if (match_kind != NULL &&
streq(match_kind,
"contains")) {
15013 }
else if (match_kind != NULL &&
streq(match_kind,
"boolean")) {
15021 }
else if (match_kind != NULL &&
streq(match_kind,
"array_contains_integer")) {
15029 wanted = strtol(value, NULL, 10);
15042 matched = item_text != NULL &&
streq(item_text, value);
15058 if (field_name == NULL || field_name[0] ==
'\0') {
15063 if (type_name != NULL &&
streq(type_name,
"number")) {
15064 long left_number = 0;
15065 long right_number = 0;
15068 if (left_number < right_number)
return -1;
15069 if (left_number > right_number)
return 1;
15072 if (type_name != NULL &&
streq(type_name,
"weekday")) {
15079 if (left_rank < right_rank)
return -1;
15080 if (left_rank > right_rank)
return 1;
15087 if (left_text == NULL) left_text = strdup(
"");
15088 if (right_text == NULL) right_text = strdup(
"");
15089 cmp = strcasecmp(left_text, right_text);
15092 if (cmp < 0)
return -1;
15093 if (cmp > 0)
return 1;
15100 char *field_name = NULL;
15101 char *direction = NULL;
15102 char *type_name = NULL;
15103 char *secondary_field = NULL;
15104 char *secondary_type = NULL;
15105 if (list == NULL || list->
count < 2 || sort_config == NULL || !
yyjson_is_obj(sort_config)) {
15113 if (field_name == NULL || field_name[0] ==
'\0') {
15117 free(secondary_field);
15118 free(secondary_type);
15121 for (i = 1; i < list->
count; i++) {
15125 if (cmp == 0 && secondary_field != NULL && secondary_field[0] !=
'\0') {
15131 if (left_id < right_id) cmp = -1;
15132 else if (left_id > right_id) cmp = 1;
15143 list->
items[j] = tmp;
15151 free(secondary_field);
15152 free(secondary_type);
15158 char *label = NULL;
15159 if (field_name == NULL) {
15160 return strdup(
"value");
15162 len = strlen(field_name);
15163 label = (
char *)malloc(
len + 1);
15164 if (label == NULL) {
15167 for (i = 0; i <
len; i++) {
15168 label[i] = field_name[i] ==
'_' ?
' ' : field_name[i];
15182 if (errors == NULL || entry == NULL) {
15213 if (configured_strategy != NULL && configured_strategy[0] !=
'\0') {
15214 if (
streq(configured_strategy,
"soft") ||
streq(configured_strategy,
"hard")) {
15215 return configured_strategy;
15217 free(configured_strategy);
15218 return strdup(
"unsupported");
15220 free(configured_strategy);
15223 if (soft_delete_field != NULL && soft_delete_field[0] !=
'\0') {
15224 free(soft_delete_field);
15225 return strdup(
"soft");
15227 free(soft_delete_field);
15229 return strdup(
"unsupported");
15257 size_t capacity = 0;
15262 if (sort_field == NULL || sort_field[0] ==
'\0' || collection_name == NULL || collection_name[0] ==
'\0' || scope_field == NULL || scope_field[0] ==
'\0') {
15264 free(collection_name);
15266 free(identity_field);
15269 if (identity_field == NULL || identity_field[0] ==
'\0') {
15270 free(identity_field);
15271 identity_field = strdup(
"id");
15276 free(collection_name);
15278 free(identity_field);
15282 long sort_value = 0;
15283 long record_id = 0;
15292 sort_value = record_id;
15294 if (sort_value <= 0) {
15295 sort_value = record_id;
15297 if (count == capacity) {
15298 capacity = capacity == 0 ? 8 : capacity * 2;
15300 if (next == NULL) {
15306 items[count].
row = row;
15307 items[count].
sort_key = sort_value;
15311 if (ok && count > 1) {
15315 for (idx = 0; idx < count; idx++) {
15324 free(collection_name);
15326 free(identity_field);
15335 char *event_name = NULL;
15342 if (entity_id == NULL || entity_id[0] ==
'\0') {
15344 entity_id = strdup(
"entity");
15346 if (surface_id == NULL || surface_id[0] ==
'\0') {
15348 surface_id = strdup(
"surface");
15350 if (identity_field == NULL || identity_field[0] ==
'\0') {
15351 free(identity_field);
15352 identity_field = strdup(
"id");
15358 if (event_name == NULL || event_name[0] ==
'\0') {
15360 event_name = (
char *)malloc(strlen(operation_name != NULL ? operation_name :
"operation") + 19);
15361 if (event_name != NULL) {
15362 snprintf(event_name, strlen(operation_name != NULL ? operation_name :
"operation") + 19,
"generated_surface_%s", operation_name != NULL ? operation_name :
"operation");
15367 entity_record_id = item_root != NULL ?
native_json_obj_get(item_root, identity_field) : NULL;
15369 actor_value =
yyjson_mut_int(mut_doc, strtol(actor_id_text, NULL, 10));
15373 if (tenant_id_text != NULL && tenant_id_text[0] !=
'\0' && !
streq(tenant_id_text,
"0") && !
streq(tenant_id_text,
"null") &&
digits_only_text_runtime(tenant_id_text)) {
15374 tenant_value =
yyjson_mut_int(mut_doc, strtol(tenant_id_text, NULL, 10));
15378 if (audit_log == NULL || event_obj == NULL || event_name == NULL || !
yyjson_mut_obj_add_strcpy(mut_doc, event_obj,
"event", event_name) || !
yyjson_mut_obj_add_strcpy(mut_doc, event_obj,
"created_at", created_at != NULL ? created_at :
"") || !
yyjson_mut_obj_add_strcpy(mut_doc, event_obj,
"surface_id", surface_id) || !
yyjson_mut_obj_add_strcpy(mut_doc, event_obj,
"entity_id", entity_id) || !
yyjson_mut_obj_add_strcpy(mut_doc, event_obj,
"operation", operation_name != NULL ? operation_name :
"") || !
yyjson_mut_obj_put(event_obj,
yyjson_mut_strcpy(mut_doc,
"actor_user_id"), actor_value) || !
yyjson_mut_obj_put(event_obj,
yyjson_mut_strcpy(mut_doc,
"tenant_id"), tenant_value) || !
yyjson_mut_obj_put(event_obj,
yyjson_mut_strcpy(mut_doc,
"entity_record_id"), entity_record_id != NULL ?
yyjson_val_mut_copy(mut_doc, entity_record_id) :
yyjson_mut_null(mut_doc)) || !
yyjson_mut_arr_append(audit_log, event_obj)) {
15382 free(identity_field);
15383 free(actor_id_text);
15384 free(tenant_id_text);
15390 free(identity_field);
15391 free(actor_id_text);
15392 free(tenant_id_text);
15415 if (text == NULL || text[0] ==
'\0') {
15418 for (index = 0; text[index] !=
'\0'; index++) {
15419 if (text[index] ==
'.') {
15421 if (dot_count > 1 || index == 0 || text[index + 1] ==
'\0') {
15426 if (!isdigit((
unsigned char)text[index])) {
15429 if (dot_count == 1) {
15431 if (decimals > 2) {
15440 const char *at = NULL;
15441 const char *dot = NULL;
15442 if (text == NULL || text[0] ==
'\0') {
15445 at = strchr(text,
'@');
15446 if (at == NULL || at == text || at[1] ==
'\0') {
15449 dot = strrchr(at + 1,
'.');
15450 if (dot == NULL || dot == at + 1 || dot[1] ==
'\0' || strlen(dot + 1) < 2) {
15457 if (text == NULL || strlen(text) != 5 || text[2] !=
':' || !isdigit((
unsigned char)text[0]) || !isdigit((
unsigned char)text[1]) || !isdigit((
unsigned char)text[3]) || !isdigit((
unsigned char)text[4])) {
15461 int hour = (text[0] -
'0') * 10 + (text[1] -
'0');
15462 int minute = (text[3] -
'0') * 10 + (text[4] -
'0');
15463 return hour >= 0 && hour <= 23 && minute >= 0 && minute <= 59;
15469 if (text == NULL || strlen(text) != 10 || text[4] !=
'-' || text[7] !=
'-') {
15472 for (i = 0; i < 10; i++) {
15473 if (i == 4 || i == 7) {
15476 if (!isdigit((
unsigned char)text[i])) {
15485 char *default_sort = NULL;
15489 if (doc == NULL || surface_root == NULL || !
yyjson_is_obj(surface_root)) {
15493 if (sort_key == NULL || sort_key[0] ==
'\0') {
15496 sort_key = default_sort;
15498 if (sort_key == NULL || sort_key[0] ==
'\0') {
15524 if (doc == NULL || array == NULL || filters == NULL || !
yyjson_arr_iter_init(filters, &iter)) {
15530 if ((value == NULL || value[0] ==
'\0') &&
native_json_obj_get(item,
"default") != NULL) {
15534 if (value != NULL && value[0] !=
'\0') {
15559 size_t emitted = 0;
15560 if (surface_root == NULL || !
yyjson_is_obj(surface_root)) {
15574 if (out_doc == NULL || out_array == NULL) {
15584 char *label = NULL;
15585 if (field_name == NULL ||
streq(field_name,
"id") ||
streq(field_name,
"business_id")) {
15588 if (emitted >= 4) {
15617 char *context_param = NULL;
15618 char *context_value = NULL;
15619 char *identity_param = NULL;
15632 if (out_doc == NULL || out_root == NULL) {
15633 goto generated_surface_params_cleanup;
15636 if (context_param != NULL && context_param[0] !=
'\0' && context_value != NULL && context_value[0] !=
'\0') {
15639 if (identity_param != NULL && identity_param[0] !=
'\0' && identity_value != NULL && identity_value[0] !=
'\0') {
15643 generated_surface_params_cleanup:
15644 free(context_param);
15645 free(context_value);
15646 free(identity_param);
15657 if (item_root == NULL || !
yyjson_is_obj(item_root) || field_name == NULL || field_name[0] ==
'\0') {
15671 if (text != NULL) {
15700 if (item_root == NULL || !
yyjson_is_obj(item_root) || field_name == NULL || field_name[0] ==
'\0') {
15705 if (text != NULL) {
15706 fputs(text, stdout);
15718 if (text == NULL) {
15721 for (index = 0; text[index] !=
'\0'; index++) {
15722 switch (text[index]) {
15723 case '&': needed += 5;
break;
15725 case '>': needed += 4;
break;
15726 case '"': needed += 6;
break;
15727 case '\'': needed += 5;
break;
15728 default: needed += 1;
break;
15731 out = (
char *)malloc(needed);
15735 for (index = 0; text[index] !=
'\0'; index++) {
15736 const char *replacement = NULL;
15737 switch (text[index]) {
15738 case '&': replacement =
"&";
break;
15739 case '<': replacement =
"<";
break;
15740 case '>': replacement =
">";
break;
15741 case '"': replacement =
""";
break;
15742 case '\'': replacement =
"'";
break;
15743 default: replacement = NULL;
break;
15745 if (replacement != NULL) {
15746 size_t repl_len = strlen(replacement);
15747 memcpy(out + cursor, replacement, repl_len);
15748 cursor += repl_len;
15750 out[cursor++] = text[index];
15753 out[cursor] =
'\0';
15768 int selected_found = 0;
15769 if (buffer == NULL || field_root == NULL || !
yyjson_is_obj(field_root)) {
15777 int is_selected = 0;
15778 if (option_value == NULL || option_value[0] ==
'\0') {
15779 free(option_value);
15782 if (option_value == NULL || option_value[0] ==
'\0') {
15783 free(option_value);
15786 if (option_label == NULL || option_label[0] ==
'\0') {
15787 free(option_label);
15790 if ((option_label == NULL || option_label[0] ==
'\0') && option_value != NULL) {
15791 free(option_label);
15792 option_label = strdup(option_value);
15794 is_selected = option_value != NULL && selected_value != NULL &&
streq(option_value, selected_value);
15795 if (option_value != NULL && option_label != NULL) {
15801 free(option_value);
15802 free(option_label);
15806 selected_found = 1;
15809 free(option_value);
15810 free(option_label);
15813 if (!selected_found && selected_value != NULL && selected_value[0] !=
'\0') {
15830 char *relation_field = NULL;
15831 char *related_collection = NULL;
15832 char *related_scope_field = NULL;
15833 char *label_field = NULL;
15834 char *checkbox_prefix = NULL;
15835 char *relation_label = NULL;
15836 char *relation_help_text = NULL;
15837 char *empty_options_text = NULL;
15851 if (label_field == NULL || label_field[0] ==
'\0') {
15853 label_field = strdup(
"name");
15855 if (checkbox_prefix == NULL || checkbox_prefix[0] ==
'\0') {
15856 size_t needed = strlen(relation_field != NULL ? relation_field :
"field") + 3;
15857 free(checkbox_prefix);
15858 checkbox_prefix = (
char *)malloc(needed);
15859 if (checkbox_prefix != NULL) {
15860 snprintf(checkbox_prefix, needed,
"%s__", relation_field != NULL ? relation_field :
"field");
15863 if (relation_label == NULL || relation_label[0] ==
'\0') {
15864 free(relation_label);
15865 relation_label = strdup(relation_field != NULL ? relation_field :
"field");
15867 if (empty_options_text == NULL || empty_options_text[0] ==
'\0') {
15868 free(empty_options_text);
15869 empty_options_text = strdup(
"No related records available.");
15871 rows = related_collection != NULL ?
state_array_runtime(state_root, related_collection) : NULL;
15874 if (ok && relation_label != NULL) {
15877 if (ok && relation_help_text != NULL && relation_help_text[0] !=
'\0') {
15886 char *row_label = NULL;
15891 if (row_label == NULL || row_label[0] ==
'\0') {
15908 if (ok && !emitted && empty_options_text != NULL) {
15914 free(relation_field);
15915 free(related_collection);
15916 free(related_scope_field);
15918 free(checkbox_prefix);
15919 free(relation_label);
15920 free(relation_help_text);
15921 free(empty_options_text);
15932 char *form_id = NULL;
15947 char *value = NULL;
15951 if (field_name == NULL || field_name[0] ==
'\0') {
15955 free(field_help_text);
15956 free(field_placeholder);
15960 if (widget == NULL || widget[0] ==
'\0') {
15962 widget = strdup(
"text");
15964 if (field_label == NULL || field_label[0] ==
'\0') {
15976 free(field_help_text);
15977 free(field_placeholder);
15983 if (widget != NULL &&
streq(widget,
"textarea")) {
15994 }
else if (widget != NULL &&
streq(widget,
"select") && options.
length > 0) {
16005 const char *input_type =
"text";
16006 if (widget != NULL && (
streq(widget,
"number") ||
streq(widget,
"email") ||
streq(widget,
"time") ||
streq(widget,
"date") ||
streq(widget,
"datetime-local") ||
streq(widget,
"password"))) {
16007 input_type = widget;
16022 if (ok && field_help_text != NULL && field_help_text[0] !=
'\0') {
16028 free(field_help_text);
16029 free(field_placeholder);
16039 if (state_root != NULL && relation_inputs != NULL &&
yyjson_arr_iter_init(relation_inputs, &iter)) {
16044 free(relation_field);
16071 if ((filter_label == NULL || filter_label[0] ==
'\0') && field_name != NULL && field_name[0] !=
'\0') {
16072 free(filter_label);
16073 filter_label = strdup(field_name);
16075 if ((filter_label == NULL || filter_label[0] ==
'\0') && query_name != NULL && query_name[0] !=
'\0') {
16076 free(filter_label);
16077 filter_label = strdup(query_name);
16079 if (match_kind == NULL || match_kind[0] ==
'\0') {
16081 match_kind = strdup(
"exact");
16083 if ((filter_value == NULL || filter_value[0] ==
'\0') && default_value != NULL && default_value[0] !=
'\0') {
16084 free(filter_value);
16085 filter_value = strdup(default_value);
16087 if (match_kind != NULL &&
streq(match_kind,
"boolean")) {
16088 const char *selected_true =
"";
16089 const char *selected_false =
"";
16090 const char *selected_all =
"";
16091 if (filter_value != NULL &&
streq(filter_value,
"false")) {
16092 selected_false =
" selected";
16093 }
else if (filter_value != NULL &&
streq(filter_value,
"all")) {
16094 selected_all =
" selected";
16096 selected_true =
" selected";
16098 ok =
text_buffer_append_format_runtime(&options,
"<option value=\"true\"%s>Active only</option><option value=\"false\"%s>Inactive only</option><option value=\"all\"%s>All</option>", selected_true, selected_false, selected_all) &&
16104 const char *input_type = match_kind != NULL &&
streq(match_kind,
"array_contains_integer") ?
"number" :
"text";
16109 free(filter_label);
16112 free(filter_value);
16113 free(default_value);
16129 if ((current_sort == NULL || current_sort[0] ==
'\0') && surface_root != NULL &&
yyjson_is_obj(surface_root)) {
16130 free(current_sort);
16134 free(current_sort);
16141 if ((sort_label == NULL || sort_label[0] ==
'\0') && sort_key != NULL) {
16143 sort_label = strdup(sort_key);
16145 if (sort_key != NULL && sort_key[0] !=
'\0') {
16155 free(current_sort);
16160 free(current_sort);
16172 int selected_found = 0;
16182 if ((option_value == NULL || option_value[0] ==
'\0')) {
16183 free(option_value);
16186 if ((option_value == NULL || option_value[0] ==
'\0')) {
16187 free(option_value);
16190 if ((option_label == NULL || option_label[0] ==
'\0')) {
16191 free(option_label);
16194 if ((option_label == NULL || option_label[0] ==
'\0') && option_value != NULL) {
16195 option_label = strdup(option_value);
16197 if (option_value != NULL && option_label != NULL) {
16198 int is_selected = selected_value != NULL &&
streq(option_value, selected_value);
16201 if (escaped_value != NULL && escaped_label != NULL) {
16202 fprintf(stdout,
"<option value=\"%s\"%s>%s</option>", escaped_value, is_selected ?
" selected" :
"", escaped_label);
16204 selected_found = 1;
16207 free(escaped_value);
16208 free(escaped_label);
16210 free(option_value);
16211 free(option_label);
16214 if (!selected_found && selected_value != NULL && selected_value[0] !=
'\0') {
16216 if (escaped_value != NULL) {
16217 fprintf(stdout,
"<option value=\"%s\" selected>%s</option>", escaped_value, escaped_value);
16218 free(escaped_value);
16242 char *relation_field = NULL;
16243 char *related_collection = NULL;
16244 char *related_scope_field = NULL;
16245 char *label_field = NULL;
16246 char *checkbox_prefix = NULL;
16247 char *relation_label = NULL;
16248 char *relation_help_text = NULL;
16249 char *empty_options_text = NULL;
16251 if (state_path == NULL || relation_root == NULL || context_root == NULL || !
yyjson_is_obj(relation_root) || !
yyjson_is_obj(context_root)) {
16274 if (label_field == NULL || label_field[0] ==
'\0') {
16276 label_field = strdup(
"name");
16278 if (checkbox_prefix == NULL || checkbox_prefix[0] ==
'\0') {
16279 size_t needed = strlen(relation_field != NULL ? relation_field :
"field") + 3;
16280 free(checkbox_prefix);
16281 checkbox_prefix = (
char *)malloc(needed);
16282 if (checkbox_prefix != NULL) {
16283 snprintf(checkbox_prefix, needed,
"%s__", relation_field != NULL ? relation_field :
"field");
16286 if (relation_label == NULL || relation_label[0] ==
'\0') {
16287 free(relation_label);
16288 relation_label = strdup(relation_field != NULL ? relation_field :
"field");
16290 if (empty_options_text == NULL || empty_options_text[0] ==
'\0') {
16291 free(empty_options_text);
16292 empty_options_text = strdup(
"No related records available.");
16294 rows = related_collection != NULL ?
state_array_runtime(state_root, related_collection) : NULL;
16296 if (relation_label != NULL) {
16298 if (escaped != NULL) {
16299 fprintf(stdout,
"<div class=\"form-group\"><label>%s</label>", escaped);
16303 if (relation_help_text != NULL && relation_help_text[0] !=
'\0') {
16305 if (escaped != NULL) {
16306 fprintf(stdout,
"<p class=\"muted\">%s</p>", escaped);
16310 fprintf(stdout,
"<div class=\"stack\">");
16314 char *row_label = NULL;
16315 char *escaped_name = NULL;
16316 char *escaped_label = NULL;
16321 if (row_label == NULL || row_label[0] ==
'\0') {
16328 if (escaped_name != NULL && escaped_label != NULL) {
16329 fprintf(stdout,
"<label><input type=\"checkbox\" name=\"%s%ld\" value=\"%ld\" checked> %s</label>", escaped_name, row_id, row_id, escaped_label);
16332 free(escaped_name);
16333 free(escaped_label);
16337 if (escaped_name != NULL && escaped_label != NULL) {
16338 fprintf(stdout,
"<label><input type=\"checkbox\" name=\"%s%ld\" value=\"%ld\"> %s</label>", escaped_name, row_id, row_id, escaped_label);
16341 free(escaped_name);
16342 free(escaped_label);
16347 if (!emitted && empty_options_text != NULL) {
16349 if (escaped_empty != NULL) {
16350 fprintf(stdout,
"<p class=\"muted\">%s</p>", escaped_empty);
16351 free(escaped_empty);
16354 fprintf(stdout,
"</div></div>");
16355 free(relation_field);
16356 free(related_collection);
16357 free(related_scope_field);
16359 free(checkbox_prefix);
16360 free(relation_label);
16361 free(relation_help_text);
16362 free(empty_options_text);
16372 char *configured_label = NULL;
16373 char *delete_strategy = NULL;
16374 if (surface_root == NULL || !
yyjson_is_obj(surface_root)) {
16375 return strdup(
"Delete");
16379 if (configured_label != NULL && configured_label[0] !=
'\0') {
16380 return configured_label;
16382 free(configured_label);
16384 if (delete_strategy != NULL &&
streq(delete_strategy,
"soft")) {
16385 free(delete_strategy);
16386 return strdup(
"Deactivate");
16388 free(delete_strategy);
16389 return strdup(
"Delete");
16395 const char *template_path = NULL;
16399 char *context_param = NULL;
16400 char *context_value = NULL;
16401 char *identity_param = NULL;
16402 char *rendered = NULL;
16403 if (surface_root == NULL || context_root == NULL || operation_name == NULL || operation_name[0] ==
'\0' || !
yyjson_is_obj(surface_root) || !
yyjson_is_obj(context_root)) {
16409 if (template_path == NULL || template_path[0] ==
'\0') {
16413 params_root = params_doc != NULL ?
yyjson_mut_obj(params_doc) : NULL;
16414 if (params_doc == NULL || params_root == NULL) {
16423 if (context_param != NULL && context_param[0] !=
'\0' && context_value != NULL && context_value[0] !=
'\0') {
16426 if (identity_param != NULL && identity_param[0] !=
'\0' && identity_value != NULL && identity_value[0] !=
'\0') {
16430 free(context_param);
16431 free(context_value);
16432 free(identity_param);
16456 char *columns_json = NULL;
16457 char *delete_button_label = NULL;
16458 char *identity_field = NULL;
16459 char *empty_state_text = NULL;
16460 size_t column_count = 1;
16463 goto generated_surface_manage_table_cleanup;
16467 free(columns_json);
16468 columns_json = NULL;
16477 size_t emitted = 0;
16486 if (tmp_doc == NULL || tmp_array == NULL) {
16488 goto generated_surface_manage_table_cleanup;
16495 char *label = NULL;
16496 if (field_name == NULL ||
streq(field_name,
"id") ||
streq(field_name,
"business_id")) {
16499 if (emitted >= 4) {
16516 if (columns_root == NULL || !
yyjson_is_arr(columns_root)) {
16517 goto generated_surface_manage_table_cleanup;
16522 if (identity_field == NULL || identity_field[0] ==
'\0') {
16523 free(identity_field);
16524 identity_field = strdup(
"id");
16526 if (empty_state_text == NULL || empty_state_text[0] ==
'\0') {
16527 free(empty_state_text);
16528 empty_state_text = strdup(
"No records found.");
16535 if ((column_label == NULL || column_label[0] ==
'\0') && field_name != NULL && field_name[0] !=
'\0') {
16536 free(column_label);
16537 column_label = strdup(field_name);
16539 if (column_label == NULL || column_label[0] ==
'\0') {
16540 free(column_label);
16541 column_label = strdup(
"value");
16544 free(column_label);
16545 goto generated_surface_manage_table_cleanup;
16547 free(column_label);
16555 char *item_id = NULL;
16556 char *edit_path = NULL;
16557 char *delete_path = NULL;
16565 free(item_id); free(edit_path); free(delete_path);
goto generated_surface_manage_table_cleanup;
16575 free(item_id); free(edit_path); free(delete_path);
goto generated_surface_manage_table_cleanup;
16580 free(item_id); free(edit_path); free(delete_path);
goto generated_surface_manage_table_cleanup;
16582 if (edit_path != NULL && edit_path[0] !=
'\0') {
16584 free(item_id); free(edit_path); free(delete_path);
goto generated_surface_manage_table_cleanup;
16587 if (delete_path != NULL && delete_path[0] !=
'\0') {
16589 free(item_id); free(edit_path); free(delete_path);
goto generated_surface_manage_table_cleanup;
16593 free(item_id); free(edit_path); free(delete_path);
goto generated_surface_manage_table_cleanup;
16602 goto generated_surface_manage_table_cleanup;
16607 if (out_doc == NULL || out_root == NULL) {
16608 goto generated_surface_manage_table_cleanup;
16615generated_surface_manage_table_cleanup:
16616 free(columns_json);
16617 free(delete_button_label);
16618 free(identity_field);
16619 free(empty_state_text);
16648 if (state_path == NULL || fixture_path == NULL || surface_json == NULL || context_json == NULL) {
16660 goto generated_surface_form_fields_cleanup;
16663 if (html == NULL) {
16665 goto generated_surface_form_fields_cleanup;
16667 fputs(html, stdout);
16669generated_surface_form_fields_cleanup:
16686 if (surface_root == NULL || !
yyjson_is_obj(surface_root)) {
16691 if (html == NULL) {
16694 fputs(html, stdout);
16709 if (surface_root == NULL || !
yyjson_is_obj(surface_root)) {
16714 if (html == NULL) {
16717 fputs(html, stdout);
16752 char *collection_name = NULL;
16753 char *scope_field = NULL;
16754 char *sort_key = NULL;
16755 char *manage_path = NULL;
16756 char *create_path = NULL;
16757 char *back_path_template = NULL;
16758 char *back_path = NULL;
16759 char *manage_table_json = NULL;
16762 char *header_html = NULL;
16763 char *row_html = NULL;
16764 char *form_fields_html = NULL;
16765 char *title = NULL;
16766 char *intro = NULL;
16767 char *filter_title = NULL;
16768 char *records_title = NULL;
16769 char *create_title = NULL;
16770 char *create_button_label = NULL;
16771 char *filter_controls_html = NULL;
16772 char *sort_options_html = NULL;
16773 char *current_page_size = NULL;
16774 char *page_size_text = NULL;
16775 char *pagination_summary = NULL;
16776 long page_default = 25;
16777 long page_max = 100;
16778 long page_value = 1;
16779 long page_size_value = 25;
16780 size_t total_items = 0;
16781 size_t start_index = 0;
16782 size_t end_index = 0;
16784 if (state_path == NULL || fixture_path == NULL || surface_json == NULL || context_json == NULL) {
16795 goto generated_surface_manage_page_cleanup;
16800 items_array = collection_name != NULL ?
state_array_runtime(state_root, collection_name) : NULL;
16807 goto generated_surface_manage_page_cleanup;
16813 if (out_doc == NULL || out_root == NULL) {
16814 goto generated_surface_manage_page_cleanup;
16820 if (items_out == NULL || pagination == NULL || filters == NULL || sort_config == NULL) {
16821 goto generated_surface_manage_page_cleanup;
16830 page_value = strtol(page_text, NULL, 10);
16833 page_size_value = strtol(page_size_text, NULL, 10);
16835 page_size_value = page_default;
16837 if (page_size_value > page_max) {
16838 page_size_value = page_max;
16845 for (idx = 0; idx < filtered.
count; idx++) {
16850 filtered = visible;
16853 total_items = filtered.
count;
16854 start_index = (size_t)((page_value - 1) * page_size_value);
16855 if (start_index > total_items) {
16856 start_index = total_items;
16858 end_index = start_index + (size_t)page_size_value;
16859 if (end_index > total_items) {
16860 end_index = total_items;
16865 for (idx = start_index; idx < end_index; idx++) {
16883 if (entry != NULL) {
16892 yyjson_mut_obj_add_int(out_doc, pagination,
"total_pages", page_size_value > 0 ? (
long)((total_items + (
size_t)page_size_value - 1) / (
size_t)page_size_value) : 1);
16898 if (back_path_template != NULL && back_path_template[0] !=
'\0') {
16904 if (params_doc != NULL && params_root != NULL) {
16906 if (context_param != NULL && context_param[0] !=
'\0' && context_value != NULL && context_value[0] !=
'\0') {
16911 free(context_param);
16912 free(context_value);
16915 if (back_path == NULL || back_path[0] ==
'\0') {
16917 back_path = manage_path != NULL ? strdup(manage_path) : strdup(
"");
16921 manage_table_json = list_result_json != NULL ? NULL : NULL;
16922 if (list_result_json == NULL) {
16923 goto generated_surface_manage_page_cleanup;
16937 char *columns_json = NULL;
16938 char *delete_button_label = NULL;
16939 char *identity_field = NULL;
16940 char *empty_state_text = NULL;
16941 size_t column_count = 1;
16942 if (surface_root2 == NULL || context_root2 == NULL || list_root2 == NULL) {
16943 free(list_result_json);
16947 goto generated_surface_manage_page_cleanup;
16959 size_t emitted = 0;
16963 if (tmp_doc == NULL || tmp_array == NULL) {
16964 free(list_result_json);
16969 goto generated_surface_manage_page_cleanup;
16976 char *label = NULL;
16977 if (field_name == NULL ||
streq(field_name,
"id") ||
streq(field_name,
"business_id")) {
16980 if (emitted >= 4) {
16999 if (identity_field == NULL || identity_field[0] ==
'\0') {
17000 free(identity_field);
17001 identity_field = strdup(
"id");
17003 if (empty_state_text == NULL || empty_state_text[0] ==
'\0') {
17004 free(empty_state_text);
17005 empty_state_text = strdup(
"No records found.");
17013 if ((column_label == NULL || column_label[0] ==
'\0') && field_name != NULL && field_name[0] !=
'\0') {
17014 free(column_label);
17015 column_label = strdup(field_name);
17017 if (column_label == NULL || column_label[0] ==
'\0') {
17018 free(column_label);
17019 column_label = strdup(
"value");
17022 free(column_label);
17026 free(list_result_json);
17027 free(columns_json);
17028 free(delete_button_label);
17029 free(identity_field);
17030 free(empty_state_text);
17036 goto generated_surface_manage_page_cleanup;
17038 free(column_label);
17048 char *item_id = NULL;
17049 char *edit_path = NULL;
17050 char *delete_path = NULL;
17058 free(item_id); free(edit_path); free(delete_path);
17060 free(list_result_json); free(columns_json); free(delete_button_label); free(identity_field); free(empty_state_text);
17062 goto generated_surface_manage_page_cleanup;
17072 free(item_id); free(edit_path); free(delete_path);
17074 free(list_result_json); free(columns_json); free(delete_button_label); free(identity_field); free(empty_state_text);
17076 goto generated_surface_manage_page_cleanup;
17081 free(item_id); free(edit_path); free(delete_path);
17083 free(list_result_json); free(columns_json); free(delete_button_label); free(identity_field); free(empty_state_text);
17085 goto generated_surface_manage_page_cleanup;
17087 if (edit_path != NULL && edit_path[0] !=
'\0') {
17089 free(item_id); free(edit_path); free(delete_path);
17091 free(list_result_json); free(columns_json); free(delete_button_label); free(identity_field); free(empty_state_text);
17093 goto generated_surface_manage_page_cleanup;
17096 if (delete_path != NULL && delete_path[0] !=
'\0') {
17098 free(item_id); free(edit_path); free(delete_path);
17100 free(list_result_json); free(columns_json); free(delete_button_label); free(identity_field); free(empty_state_text);
17102 goto generated_surface_manage_page_cleanup;
17106 free(item_id); free(edit_path); free(delete_path);
17108 free(list_result_json); free(columns_json); free(delete_button_label); free(identity_field); free(empty_state_text);
17110 goto generated_surface_manage_page_cleanup;
17120 free(list_result_json); free(columns_json); free(delete_button_label); free(identity_field); free(empty_state_text);
17122 goto generated_surface_manage_page_cleanup;
17127 free(list_result_json);
17128 free(columns_json);
17129 free(delete_button_label);
17130 free(identity_field);
17131 free(empty_state_text);
17142 if (title == NULL || title[0] ==
'\0') {
17146 if (title == NULL || title[0] ==
'\0') {
17148 title = strdup(
"Generated Admin");
17151 if (intro == NULL || intro[0] ==
'\0') { free(intro); intro = strdup(
"Framework-owned generated admin surface."); }
17153 if (filter_title == NULL || filter_title[0] ==
'\0') { free(filter_title); filter_title = strdup(
"Filter"); }
17155 if (records_title == NULL || records_title[0] ==
'\0') { free(records_title); records_title = strdup(
"Records"); }
17157 if (create_title == NULL || create_title[0] ==
'\0') { free(create_title); create_title = strdup(
"Create"); }
17159 if (create_button_label == NULL || create_button_label[0] ==
'\0') { free(create_button_label); create_button_label = strdup(
"Create"); }
17162 if (page_size_text != NULL && page_size_text[0] !=
'\0') {
17163 current_page_size = strdup(page_size_text);
17166 snprintf(tmp,
sizeof(tmp),
"%ld", page_default == 0 ? 25 : page_default);
17167 current_page_size = strdup(tmp);
17171 snprintf(tmp,
sizeof(tmp),
"Page %ld of %ld · %ld total", page_value, page_size_value > 0 ? (
long)((total_items + (
size_t)page_size_value - 1) / (
size_t)page_size_value) : 1, (
long)total_items);
17172 pagination_summary = strdup(tmp);
17174 if (form_fields_html == NULL || filter_controls_html == NULL || sort_options_html == NULL || current_page_size == NULL || pagination_summary == NULL || header_html == NULL || row_html == NULL) {
17175 goto generated_surface_manage_page_cleanup;
17183 yyjson_mut_obj_add_strcpy(out_doc, out_root,
"filter_controls_html", filter_controls_html != NULL ? filter_controls_html :
"");
17185 yyjson_mut_obj_add_strcpy(out_doc, out_root,
"current_page_size", current_page_size != NULL ? current_page_size :
"25");
17186 yyjson_mut_obj_add_strcpy(out_doc, out_root,
"pagination_summary", pagination_summary != NULL ? pagination_summary :
"");
17193 yyjson_mut_obj_add_strcpy(out_doc, out_root,
"create_button_label", create_button_label != NULL ? create_button_label :
"Create");
17196generated_surface_manage_page_cleanup:
17197 free(collection_name);
17202 free(back_path_template);
17204 free(manage_table_json);
17208 free(form_fields_html);
17211 free(filter_title);
17212 free(records_title);
17213 free(create_title);
17214 free(create_button_label);
17215 free(filter_controls_html);
17216 free(sort_options_html);
17217 free(current_page_size);
17218 free(page_size_text);
17219 free(pagination_summary);
17248 char *identity_field = NULL;
17249 char *item_id = NULL;
17250 char *update_path = NULL;
17251 char *delete_path = NULL;
17252 char *manage_path = NULL;
17253 char *form_fields_html = NULL;
17254 char *save_button_label = NULL;
17255 char *delete_button_label = NULL;
17256 char *title_field = NULL;
17257 char *title_value = NULL;
17258 char *title = NULL;
17259 char *delete_form_html = NULL;
17261 if (state_path == NULL || fixture_path == NULL || surface_json == NULL || context_json == NULL) {
17273 goto generated_surface_edit_page_cleanup;
17276 if (identity_field == NULL || identity_field[0] ==
'\0') { free(identity_field); identity_field = strdup(
"id"); }
17283 if (save_button_label == NULL || save_button_label[0] ==
'\0') { free(save_button_label); save_button_label = strdup(
"Save"); }
17286 if (title_field == NULL || title_field[0] ==
'\0') { free(title_field); title_field = strdup(
"name"); }
17288 if (title_value == NULL || title_value[0] ==
'\0') {
17292 snprintf(tmp,
sizeof(tmp),
"Record %s", item_id != NULL ? item_id :
"");
17293 title_value = strdup(tmp);
17297 size_t needed = strlen(
"Edit ") + strlen(title_value != NULL ? title_value :
"") + 1;
17298 title = (
char *)malloc(needed);
17299 if (title != NULL) {
17300 snprintf(title, needed,
"Edit %s", title_value != NULL ? title_value :
"");
17303 if (delete_path != NULL && delete_path[0] !=
'\0') {
17311 goto generated_surface_edit_page_cleanup;
17317 if (out_doc == NULL || out_root == NULL || form_fields_html == NULL || title == NULL) {
17318 goto generated_surface_edit_page_cleanup;
17325 yyjson_mut_obj_add_strcpy(out_doc, out_root,
"save_button_label", save_button_label != NULL ? save_button_label :
"Save");
17329generated_surface_edit_page_cleanup:
17330 free(identity_field);
17335 free(form_fields_html);
17336 free(save_button_label);
17337 free(delete_button_label);
17341 free(delete_form_html);
17366 char *state_collection = NULL;
17367 char *param_name = NULL;
17368 char *match_field = NULL;
17369 char *context_label_field = NULL;
17370 char *tenant_collection = NULL;
17371 char *tenant_match_field = NULL;
17372 char *tenant_match_from_context_field = NULL;
17373 char *context_value = NULL;
17374 char *context_id_field = NULL;
17375 char *context_label = NULL;
17376 long tenant_id = 0;
17378 if (state_path == NULL || surface_json == NULL || params_json == NULL) {
17400 if (context_id_field == NULL || context_id_field[0] ==
'\0') {
17401 free(context_id_field);
17402 context_id_field = strdup(
"id");
17406 if (out_doc == NULL || out_root == NULL) {
17407 goto generated_surface_context_cleanup;
17409 if (state_collection == NULL || state_collection[0] ==
'\0' || param_name == NULL || param_name[0] ==
'\0' || match_field == NULL || match_field[0] ==
'\0') {
17420 goto generated_surface_context_cleanup;
17423 if (context_value == NULL || context_value[0] ==
'\0') {
17429 goto generated_surface_context_cleanup;
17432 if (record == NULL) {
17438 goto generated_surface_context_cleanup;
17441 if (tenant_collection != NULL && tenant_collection[0] !=
'\0' && tenant_match_field != NULL && tenant_match_field[0] !=
'\0' && tenant_match_from_context_field != NULL && tenant_match_from_context_field[0] !=
'\0') {
17444 if (tenant_record != NULL) {
17447 free(tenant_match_value);
17449 context_label = context_label_field != NULL && context_label_field[0] !=
'\0' ?
native_json_obj_get_string_dup(record, context_label_field) : strdup(
"");
17452 if (tenant_id != 0) {
17453 char tenant_scope_text[64];
17454 snprintf(tenant_scope_text,
sizeof(tenant_scope_text),
"tenant:%ld", tenant_id);
17466generated_surface_context_cleanup:
17467 free(state_collection);
17470 free(context_id_field);
17471 free(context_label_field);
17472 free(tenant_collection);
17473 free(tenant_match_field);
17474 free(tenant_match_from_context_field);
17475 free(context_value);
17476 free(context_label);
17518 if (candidate_value == NULL || type_text == NULL) {
17522 if (
streq(type_text,
"integer")) {
17528 }
else if (
streq(type_text,
"boolean")) {
17537 if (new_array != NULL) {
17542 }
else if (entry != NULL) {
17574 char *form_id = NULL;
17576 if (surface_root == NULL || !
yyjson_is_obj(surface_root)) {
17584 if (out_doc == NULL || out_root == NULL) {
17585 goto generated_surface_submitted_cleanup;
17587 if (content_type != NULL && strncasecmp(content_type,
"application/json", 16) == 0) {
17588 char *body_text = NULL;
17590 body_text = (request_body_path != NULL && request_body_path[0] !=
'\0') ?
read_file_text(request_body_path) : strdup(form_body != NULL ? form_body :
"{}");
17602 if (field_id != NULL && value != NULL) {
17612 if (field_name != NULL && value != NULL) {
17621 payload_doc = NULL;
17632 if (field_id != NULL && value != NULL) {
17644 char *body_copy = strdup(form_body != NULL ? form_body :
"");
17646 char *saveptr = NULL;
17647 if (field_name != NULL && (checkbox_prefix == NULL || checkbox_prefix[0] ==
'\0')) {
17648 size_t needed = strlen(field_name) + 3;
17649 free(checkbox_prefix);
17650 checkbox_prefix = (
char *)malloc(needed);
17651 if (checkbox_prefix != NULL) {
17652 snprintf(checkbox_prefix, needed,
"%s__", field_name);
17655 if (array != NULL && body_copy != NULL && checkbox_prefix != NULL) {
17656 part = strtok_r(body_copy,
"&", &saveptr);
17657 while (part != NULL) {
17658 char *eq = strchr(part,
'=');
17659 if (eq != NULL && strncmp(part, checkbox_prefix, strlen(checkbox_prefix)) == 0) {
17666 part = strtok_r(NULL,
"&", &saveptr);
17668 if (field_name != NULL) {
17673 free(checkbox_prefix);
17680generated_surface_submitted_cleanup:
17711 char *collection_name = NULL;
17712 char *scope_field = NULL;
17713 char *sort_key = NULL;
17714 long page_default = 25;
17715 long page_max = 100;
17716 long page_value = 1;
17717 long page_size_value = 25;
17718 size_t total_items = 0;
17719 size_t start_index = 0;
17720 size_t end_index = 0;
17722 if (state_path == NULL || surface_json == NULL || context_json == NULL) {
17731 goto generated_surface_list_cleanup;
17736 items_array = collection_name != NULL ?
state_array_runtime(state_root, collection_name) : NULL;
17738 items_array = NULL;
17749 goto generated_surface_list_cleanup;
17759 if (out_doc == NULL || out_root == NULL || items_out == NULL || pagination == NULL || filters == NULL || sort_config == NULL) {
17761 goto generated_surface_list_cleanup;
17770 page_value = strtol(page_text, NULL, 10);
17773 page_size_value = strtol(page_size_text, NULL, 10);
17775 page_size_value = page_default;
17777 if (page_size_value > page_max) {
17778 page_size_value = page_max;
17781 free(page_size_text);
17786 for (idx = 0; idx < filtered.
count; idx++) {
17791 filtered = visible;
17794 total_items = filtered.
count;
17795 start_index = (size_t)((page_value - 1) * page_size_value);
17796 if (start_index > total_items) {
17797 start_index = total_items;
17799 end_index = start_index + (size_t)page_size_value;
17800 if (end_index > total_items) {
17801 end_index = total_items;
17806 for (idx = start_index; idx < end_index; idx++) {
17824 if (entry != NULL) {
17833 yyjson_mut_obj_add_int(out_doc, pagination,
"total_pages", page_size_value > 0 ? (
long)((total_items + (
size_t)page_size_value - 1) / (
size_t)page_size_value) : 1);
17838generated_surface_list_cleanup:
17839 free(collection_name);
17868 char *scope_field = NULL;
17869 char *auto_sequence_field = NULL;
17870 char *entity_collection = NULL;
17872 if (state_path == NULL || surface_json == NULL || context_json == NULL) {
17881 goto generated_surface_default_cleanup;
17885 if (mut_doc == NULL || mut_root == NULL) {
17887 goto generated_surface_default_cleanup;
17894 if (scope_field != NULL && scope_field[0] !=
'\0') {
17902 if ((existing_item_json == NULL || existing_item_json[0] ==
'\0') && auto_sequence_field != NULL && auto_sequence_field[0] !=
'\0' && entity_collection != NULL && entity_collection[0] !=
'\0') {
17905 if (current_text == NULL || current_text[0] ==
'\0') {
17909 long next_value = 1;
17913 long candidate_value = 0;
17921 if (candidate_value >= next_value) {
17922 next_value = candidate_value + 1;
17928 free(current_text);
17931 char *candidate_text = NULL;
17933 if (candidate_text != NULL) {
17934 char *argv_local[] = {
"--surface-json", (
char *)surface_json,
"--candidate-json", candidate_text};
17936 free(candidate_text);
17937 goto generated_surface_default_cleanup;
17941 generated_surface_default_cleanup:
17943 free(auto_sequence_field);
17944 free(entity_collection);
17976 long current_id = 0;
17977 int has_current_id = 0;
17979 if (state_path == NULL || surface_json == NULL || context_json == NULL || candidate_json == NULL || report_json == NULL) {
17990 goto generated_surface_validation_cleanup;
17993 current_id = strtol(current_id_text, NULL, 10);
17994 has_current_id = 1;
18000 goto generated_surface_validation_cleanup;
18011 if (rule_id == NULL) {
18012 rule_id = strdup(
"validation_rule");
18014 if (field_name != NULL && constraint_kind != NULL && field_text != NULL && field_text[0] !=
'\0' && label != NULL) {
18016 if (
streq(constraint_kind,
"positive_integer")) {
18018 snprintf(message,
sizeof(message),
"%s must be a positive whole number.", label);
18021 }
else if (
streq(constraint_kind,
"decimal")) {
18023 snprintf(message,
sizeof(message),
"%s must be a valid decimal amount.", label);
18026 }
else if (
streq(constraint_kind,
"email")) {
18028 snprintf(message,
sizeof(message),
"%s must be a valid email address.", label);
18031 }
else if (
streq(constraint_kind,
"enum")) {
18034 snprintf(message,
sizeof(message),
"%s has an invalid value.", label);
18037 }
else if (
streq(constraint_kind,
"time")) {
18039 snprintf(message,
sizeof(message),
"%s must be a valid time.", label);
18042 }
else if (
streq(constraint_kind,
"date")) {
18044 snprintf(message,
sizeof(message),
"%s must be a valid date.", label);
18050 free(constraint_kind);
18067 if (invalid_rule_id == NULL) invalid_rule_id = strdup(
"invalid_relation");
18068 if (invalid_scope_rule_id == NULL) invalid_scope_rule_id = strdup(
"invalid_relation_scope");
18069 if (invalid_message == NULL) invalid_message = strdup(
"Values must be an array of integer ids.");
18070 if (invalid_scope_message == NULL) invalid_scope_message = strdup(
"Values must belong to the current tenant context.");
18071 if (relation_field != NULL) {
18074 }
else if (related_collection != NULL && related_collection[0] !=
'\0' && related_scope_field != NULL && related_scope_field[0] !=
'\0') {
18079 int invalid_scope = 0;
18085 int found_in_scope = 0;
18095 found_in_scope = 1;
18099 if (!found_in_scope) {
18104 if (invalid_scope) {
18109 free(relation_field);
18110 free(related_collection);
18111 free(related_scope_field);
18112 free(invalid_rule_id);
18113 free(invalid_scope_rule_id);
18114 free(invalid_message);
18115 free(invalid_scope_message);
18125 char *first_field = NULL;
18130 int duplicate_found = 0;
18131 if (rule_id == NULL) rule_id = strdup(
"unique_constraint");
18132 if (message == NULL) message = strdup(
"Unique constraint violated.");
18137 first_field = strdup(
"value");
18143 int matches_all = 1;
18144 long record_id = 0;
18154 char *candidate_text = NULL;
18155 char *record_text = NULL;
18156 if (field_name == NULL) {
18162 if (candidate_text != NULL) {
18163 size_t i = 0;
for (i = 0; candidate_text[i] !=
'\0'; i++) candidate_text[i] = (
char)tolower((
unsigned char)candidate_text[i]);
18165 if (record_text != NULL) {
18166 size_t i = 0;
for (i = 0; record_text[i] !=
'\0'; i++) record_text[i] = (
char)tolower((
unsigned char)record_text[i]);
18169 if ((candidate_text == NULL ?
"" : candidate_text)[0] !=
'\0' || (record_text == NULL ?
"" : record_text)[0] !=
'\0') {
18170 if (!
streq(candidate_text != NULL ? candidate_text :
"", record_text != NULL ? record_text :
"")) {
18173 }
else if (!
streq(candidate_text != NULL ? candidate_text :
"", record_text != NULL ? record_text :
"")) {
18176 free(candidate_text);
18178 if (!matches_all) {
18184 duplicate_found = 1;
18188 if (duplicate_found) {
18194 free(collection_name);
18199 generated_surface_validation_cleanup:
18235 char *collection_name = NULL;
18236 char *identity_field = NULL;
18237 char *delete_strategy = NULL;
18238 char *soft_delete_field = NULL;
18239 long record_id = 0;
18240 int normalize_after = 0;
18242 if (state_path == NULL || surface_json == NULL || context_json == NULL || operation_name == NULL || operation_name[0] ==
'\0') {
18248 if (user_json != NULL && user_json[0] !=
'\0') {
18251 if (candidate_json != NULL && candidate_json[0] !=
'\0') {
18254 if (existing_item_json != NULL && existing_item_json[0] !=
'\0') {
18260 goto generated_surface_mutate_cleanup;
18264 if (identity_field == NULL || identity_field[0] ==
'\0') {
18265 free(identity_field);
18266 identity_field = strdup(
"id");
18268 if (collection_name == NULL || collection_name[0] ==
'\0') {
18270 goto generated_surface_mutate_cleanup;
18276 goto generated_surface_mutate_cleanup;
18281 goto generated_surface_mutate_cleanup;
18283 if (
streq(operation_name,
"create")) {
18284 if (candidate_root == NULL || !
yyjson_is_obj(candidate_root)) {
18286 goto generated_surface_mutate_cleanup;
18291 goto generated_surface_mutate_cleanup;
18296 goto generated_surface_mutate_cleanup;
18298 normalize_after = 1;
18299 }
else if (
streq(operation_name,
"update")) {
18300 if (candidate_root == NULL || !
yyjson_is_obj(candidate_root)) {
18302 goto generated_surface_mutate_cleanup;
18305 if (record_id <= 0) {
18307 goto generated_surface_mutate_cleanup;
18311 code = record_mut == NULL ? 66 : 69;
18312 goto generated_surface_mutate_cleanup;
18314 item_mut = record_mut;
18315 normalize_after = 1;
18316 }
else if (
streq(operation_name,
"delete")) {
18317 if (existing_root == NULL || !
yyjson_is_obj(existing_root)) {
18319 goto generated_surface_mutate_cleanup;
18322 if (record_id <= 0) {
18324 goto generated_surface_mutate_cleanup;
18327 if (delete_strategy == NULL ||
streq(delete_strategy,
"unsupported")) {
18329 goto generated_surface_mutate_cleanup;
18331 if (
streq(delete_strategy,
"soft")) {
18335 code = record_mut == NULL ? 66 : 69;
18336 goto generated_surface_mutate_cleanup;
18338 item_mut = record_mut;
18354 goto generated_surface_mutate_cleanup;
18356 normalize_after = 1;
18360 goto generated_surface_mutate_cleanup;
18364 goto generated_surface_mutate_cleanup;
18368 goto generated_surface_mutate_cleanup;
18372 goto generated_surface_mutate_cleanup;
18375 generated_surface_mutate_cleanup:
18376 free(collection_name);
18377 free(identity_field);
18378 free(delete_strategy);
18379 free(soft_delete_field);
18522 if (argc < 1 || argv == NULL) {
18529 sizeof(command_entries) /
sizeof(command_entries[0])
char * app_local_conf_path_native(const char *app_root)
Builds the path to an app-local runtime configuration file.
char * resolve_conf_relative_path_native(const char *conf_path, const char *raw_value)
Resolves a config value relative to the directory that owns a config file.
Declares app-local runtime configuration helpers and the app-local configuration record.
char * native_dynamic_config_merged_value_dup(const char *app_root, const char *runtime_conf_path, const char *key)
Loads one merged dynamic config value with runtime-conf precedence over app-local config.
Declares Pharos-owned dynamic runtime config resolution helpers.
int native_dynamic_multipart_extract_file(const unsigned char *body, size_t body_len, const char *content_type, const char *field_name, const char *output_path)
Extracts file content from a multipart file part and writes it to disk.
char * native_dynamic_multipart_extract_text(const unsigned char *body, size_t body_len, const char *content_type, const char *field_name)
Extracts a text value from a multipart form field.
char * native_dynamic_multipart_filename(const unsigned char *body, size_t body_len, const char *content_type, const char *field_name)
Extracts the uploaded filename for a named multipart file part.
Pharos-owned multipart form-data parsing for in-process dynamic handling.
#define PHAROS_PGRES_TUPLES_OK_RUNTIME
int native_dynamic_relational_runtime_import_state(const char *app_root, const char *runtime_conf_path, const char *app_id, const char *state_path, const char *expected_audit_count)
Imports canonical runtime state JSON into the configured relational backend.
int native_dynamic_relational_postgresql_runtime_ready(const char *app_root, const char *runtime_conf_path)
Returns whether PostgreSQL runtime prerequisites are satisfied.
int native_dynamic_relational_postgresql_runtime_prepare(const char *app_root, const char *runtime_conf_path)
Prepares the PostgreSQL schema, seed data, and migration ledger for one dynamic app.
int native_dynamic_relational_sqlite_runtime_prepare(const char *app_root, const char *runtime_conf_path, const char *app_id)
Prepares the sqlite schema, seed data, and migration ledger for one dynamic app.
#define PHAROS_PGRES_COMMAND_OK_RUNTIME
char * native_dynamic_relational_runtime_audit_count_dup(const char *app_root, const char *runtime_conf_path, const char *app_id)
Returns the current audit-event count from the configured relational backend.
int native_dynamic_relational_sqlite_runtime_ready(const char *app_root, const char *runtime_conf_path, const char *app_id)
Returns whether sqlite runtime prerequisites are satisfied.
char * native_dynamic_relational_runtime_export_json_dup(const char *app_root, const char *runtime_conf_path, const char *app_id)
Exports canonical runtime state JSON from the configured relational backend.
#define PHAROS_CONN_STATUS_OK_RUNTIME
char * native_dynamic_relational_backend_dup(const char *app_root, const char *runtime_conf_path)
Returns the configured relational backend when it is recognized.
struct pg_result PGresult
char * native_dynamic_relational_sqlite_path_dup(const char *app_root, const char *runtime_conf_path, const char *app_id)
Returns the effective sqlite database path for a dynamic app.
#define PHAROS_PGRES_SINGLE_TUPLE_RUNTIME
Declares Pharos-owned native relational runtime helpers for dynamic apps.
char * native_dynamic_form_value(const char *body, const char *key)
Extracts a value from a URL-encoded form body.
char * native_dynamic_query_value(const char *query, const char *key)
Extracts a parameter value from a query string.
char * native_dynamic_cookie_value(const char *cookie_header, const char *name)
Extracts a cookie value from a Cookie header string.
Request field extraction helpers for in-process dynamic handling.
Declares template rendering functions for the in-process dynamic runtime.
int ensure_directory(const char *path)
Creates a directory path and any missing parent directories.
int write_text_file(const char *path, const char *content)
Writes a text buffer to a file path.
Declares native filesystem helper routines.
char * conf_lookup_value(const char *path, const char *key)
Reads a Pharos conf file and looks up one key.
Declares lightweight JSON and conf value lookup helpers.
static int parse_ymd_runtime(const char *date_value, int *year_out, int *month_out, int *day_out)
static int json_value_list_append_runtime(json_value_list_runtime *list, yyjson_val *item)
static int handle_template_apply_base_path_command(int argc, char **argv)
static int digits_only_text_runtime(const char *text)
static char * resolve_dynamic_config_value_runtime(const char *conf_path, const char *key, const char *raw_value)
Resolves one raw dynamic config value against its owning config file when needed.
static int handle_dynamic_multipart_extract_text_command(int argc, char **argv)
Extracts a text value from a multipart form field.
static char * render_business_dashboard_cards_html_dup_runtime(yyjson_val *root, yyjson_val *tenant_ids, const char *app_root)
static long payload_first_long_runtime(yyjson_val *payload, const char **fields, size_t field_count)
static int weekday_monday_index_runtime(int year, int month, int day)
static char * generated_surface_label_from_field_runtime(const char *field_name)
static int handle_state_event_stream_row_json_lines_command(yyjson_val *root, yyjson_val *subscription_scope, yyjson_val *filters, long since_id, long limit_value)
static int add_json_string_or_null_runtime(yyjson_mut_doc *doc, yyjson_mut_val *obj, const char *key, const char *value)
static char * template_value_text_dup_runtime(yyjson_val *value)
static int handle_db_postgresql_migration_applied_count_command(int argc, char **argv)
Emits one PostgreSQL migration applied count.
static int emit_business_admin_service_rows_json_runtime(yyjson_val *root, long business_id)
static int handle_validation_field_list_html_command(int argc, char **argv)
static int handle_object_path_string_command(int argc, char **argv)
static void month_shift_runtime(int year, int month, int shift, int *out_year, int *out_month)
static int append_escaped_sql_char_runtime(TextBufferRuntime *buffer, char value)
static char * dynamic_sqlite_path_dup_runtime(const char *app_root, const char *runtime_conf_path, const char *app_id)
static char * runtime_state_placeholder_sql_dup_runtime(const char *sql_template, const char *escaped_state_json)
Replaces the canonical runtime-state placeholder inside one declarative SQL template.
static int handle_object_path_number_command(int argc, char **argv)
static int handle_file_compact_object_command(int argc, char **argv)
char * business_calendar_render_page_params_json_dup_runtime(const char *state_path, const char *business_slug, const char *app_root, const char *selected_year_text, const char *selected_month_text, const char *selected_date_text)
static int template_reference_contains_unsafe_char_runtime(const char *text)
Checks whether a reference candidate contains unsafe control bytes.
static int handle_object_field_number_command(int argc, char **argv)
static const char * json_option_value_runtime(int argc, char **argv, const char *name)
char * business_dashboard_render_page_params_json_dup_runtime(const char *state_path, long user_id, const char *app_root)
Builds rendered business dashboard page params JSON from runtime state.
static int load_cli_postgresql_runtime_config(int argc, char **argv, DynamicPostgresqlRuntimeConfig *config)
Loads PostgreSQL connection settings from explicit JSON command options.
static int event_stream_allowed_by_scope_runtime(yyjson_val *subscription_scope, long event_user_id, long event_tenant_id)
static char * request_form_field_value_dup_runtime(const char *content_type, const char *form_body, const char *body_path, const char *field_name)
Resolves one request form field from urlencoded or multipart request inputs.
static char * json_value_tostring_dup_runtime(yyjson_val *value)
static int handle_request_query_value_command(int argc, char **argv)
Extracts a parameter value from a query string.
static char * dynamic_relational_runtime_audit_count_dup_runtime(const char *app_root, const char *runtime_conf_path, const char *app_id)
Returns the current audit-event count for one dynamic relational runtime using the configured backend...
static int trim_ascii_whitespace_range_runtime(const char *start, size_t len, const char **trimmed_start, size_t *trimmed_len)
unsigned char * read_file_bytes_dup_runtime(const char *path, size_t *out_size)
Reads one file into an owned binary buffer and appends a trailing NUL byte for text helpers.
static int handle_object_build_command(int argc, char **argv)
static int emit_explore_page_json_runtime(yyjson_val *root, const char *app_root)
Emits rendered explore-page template params using declarative business card fragments.
static int generated_surface_array_contains_string_runtime(yyjson_val *array, const char *wanted)
static int command_exists_in_path_runtime(const char *command)
static int handle_file_generated_surface_match_command(int argc, char **argv)
static int dispatch_native_json_command_entries(int argc, char **argv, const NativeJsonCommandDispatchEntry *entries, size_t entry_count)
char * profile_render_page_params_json_dup_runtime(const char *state_path, long user_id, const char *app_root, const char *payment_preference_options_html)
Builds rendered profile page params JSON from runtime state.
char * render_file_template_dup_runtime(const char *template_file, yyjson_val *params_root)
Reads a template file and renders it with JSON parameter substitution.
static int json_value_equals_text_runtime(yyjson_val *value, const char *expected)
static int handle_generated_surface_select_options_html_command(int argc, char **argv)
static int handle_db_sqlite_migration_applied_count_command(int argc, char **argv)
Emits one sqlite migration applied count.
static char * url_decode_dup_runtime(const char *text)
char * error_alert_html_dup_runtime(const char *message)
Renders an error alert.
static int duplicate_postgresql_runtime_config_with_database_runtime(const DynamicPostgresqlRuntimeConfig *source, const char *database_name, DynamicPostgresqlRuntimeConfig *copy_out)
Duplicates one PostgreSQL runtime configuration while replacing the database name.
static char * sqlite_query_value_runtime(const char *sqlite_path, const char *sql_text)
static int handle_template_login_page_json_command(int argc, char **argv)
static yyjson_val * state_find_user_by_id_runtime(yyjson_val *root, long user_id)
static TemplatePlaceholderKindRuntime template_placeholder_kind_runtime(const char *placeholder_name, const char **param_name)
Parses one placeholder token into its rendering mode and JSON key name.
static int handle_db_sqlite_migration_applied_schema_version_command(int argc, char **argv)
Emits one sqlite migration applied schema version.
static int handle_dynamic_sqlite_runtime_ready_command(int argc, char **argv)
Emits whether the sqlite dynamic runtime prerequisites are currently satisfied.
static int sqlite_apply_file_list_runtime(const char *sqlite_path, const char *app_root, yyjson_val *list)
static int text_buffer_reserve_runtime(TextBufferRuntime *buffer, size_t needed_length)
static int emit_json_string_runtime(const char *text)
static yyjson_val * find_active_token_record_runtime(yyjson_val *array_value, const char *token_hash, const char *now_iso)
static int streq_casefold_runtime(const char *left, const char *right)
char * join_base_relative_path_dup_runtime(const char *base_path, const char *relative_path)
Joins a base path and a relative path into one string.
static char * render_template_text_with_default_kind_dup_runtime(const char *template_text, yyjson_val *params_root, TemplatePlaceholderKindRuntime default_kind)
Renders one template string using one caller-selected default placeholder mode.
static void free_event_stream_row_list_runtime(event_stream_row_list_runtime *list)
static int handle_request_lines_to_booking_options_json_command(int argc, char **argv)
static int handle_db_sqlite_exec_command(int argc, char **argv)
Executes one sqlite SQL text payload.
static int generated_surface_text_is_email_runtime(const char *text)
static int handle_file_migration_backend_files_json_command(int argc, char **argv)
static const char * day_abbrev_for_ymd_runtime(int year, int month, int day)
static int handle_file_migration_rows_lines_command(int argc, char **argv)
static char * build_generated_surface_sort_options_html_runtime(yyjson_val *surface_root, const char *raw_query)
static long max_id_in_state_section_runtime(yyjson_val *root, const char *section_name)
static int handle_object_relative_path_array_to_absolute_json_command(int argc, char **argv)
static int handle_form_options_csv_command(int argc, char **argv)
static char * resource_kind_from_resource_id_runtime(const char *resource_id)
static void surface_sort_items_runtime(json_value_list_runtime *list, yyjson_val *sort_config)
static int write_temp_text_file_runtime(const char *text, char **path_out)
static int handle_dynamic_multipart_filename_command(int argc, char **argv)
Extracts the uploaded filename from a multipart file part.
static int emit_technician_edit_page_json_runtime(yyjson_val *root, const char *slug, long technician_id)
char * business_detail_admin_render_page_params_json_dup_runtime(const char *state_path, const char *business_slug, const char *app_root)
Builds rendered business admin page params JSON from runtime state.
static int handle_dynamic_sqlite_runtime_prepare_command(int argc, char **argv)
static int handle_dynamic_remove_media_url_file_command(int argc, char **argv)
Removes one managed business media file from an app artifact when it exists.
static int handle_generated_surface_list_result_command(int argc, char **argv)
static int handle_dynamic_relational_sqlite_path_command(int argc, char **argv)
Emits the effective sqlite path for a dynamic app.
static int json_array_contains_long_runtime(yyjson_val *array_value, long wanted)
static int handle_dynamic_postgresql_runtime_prepare_command(int argc, char **argv)
Prepares the PostgreSQL dynamic runtime schema, seed data, and migration ledger.
static char * ucal_runtime_state_export_sqlite_path_dup_runtime(const char *app_root)
Resolves the declarative sqlite export query path for UCAL runtime-state bridge export.
static int technician_booked_for_slot_runtime(yyjson_val *root, long technician_id, const char *date_value, const char *time_value, long duration_minutes)
char * business_landing_render_page_params_json_dup_runtime(const char *state_path, const char *business_slug, long user_id, const char *app_root)
static int days_in_month_runtime(int year, int month)
static int template_reference_has_scheme_runtime(const char *text)
Tests whether a reference begins with a URI scheme prefix.
static long state_tenant_id_for_business_id_runtime(yyjson_val *root, long business_id)
static int handle_db_sqlite_table_row_count_command(int argc, char **argv)
Emits one sqlite table row count.
static yyjson_val * object_value_at_path_runtime(yyjson_val *root, const char *path)
static char * render_business_admin_empty_state_dup_runtime(const char *app_root, const char *message)
static int handle_template_declared_page_contract_command(int argc, char **argv)
Emits one UCAL declared page contract as JSON.
static int seen_long_pair_runtime(long *values, size_t count, long first, long second)
char * appointments_render_page_params_json_dup_runtime(const char *state_path, long user_id, const char *app_root)
Builds rendered appointments page params JSON from runtime state.
static int handle_generated_surface_list_columns_json_command(int argc, char **argv)
static char * app_root_relative_path_dup_runtime(const char *app_root, const char *relative_path)
Joins an app root with one relative template path.
static char * dynamic_merged_config_value_runtime(const char *app_root, const char *runtime_conf_path, const char *key)
Loads one merged dynamic config value with runtime-conf precedence over app-local config.
static yyjson_mut_val * surface_build_active_filters_runtime(yyjson_mut_doc *doc, yyjson_val *surface_root, const char *raw_query)
static char * generated_surface_delete_button_label_dup_runtime(yyjson_val *surface_root)
static int emit_long_runtime(long value)
static int parse_hhmm_minutes_runtime(const char *value, int *minutes_out)
static int handle_generated_surface_relation_checkboxes_html_command(int argc, char **argv)
static int emit_business_technician_ids_lines_runtime(yyjson_val *root, long business_id)
static int emit_digit_bool_runtime(int truthy)
Emits one digit boolean result for shell callers.
static char * text_buffer_take_runtime(TextBufferRuntime *buffer)
static int handle_db_postgresql_public_table_count_command(int argc, char **argv)
Emits the current PostgreSQL public table count.
static int handle_file_field_number_command(int argc, char **argv)
static int emit_calendar_page_json_runtime(yyjson_val *root, long user_id, yyjson_val *tenant_ids, const char *app_root)
Emits rendered calendar-page template params using declarative booking and appointment fragments.
static int handle_db_sqlite_record_migration_command(int argc, char **argv)
Inserts one sqlite migration ledger record.
static int handle_db_postgresql_query_value_command(int argc, char **argv)
Emits the first-line output of one PostgreSQL query.
static int emit_compact_json_value_runtime(yyjson_val *value)
static char * migration_expected_checksum_dup_runtime(const char *app_root, yyjson_val *migration_record, const char *backend)
static int emit_sorted_section_lines_runtime(yyjson_val *root, const char *section_name)
static int emit_text_line_runtime(const char *text)
static int compare_item_id_runtime(const void *lhs_ptr, const void *rhs_ptr)
static yyjson_val * state_find_first_service_for_business_runtime(yyjson_val *root, long business_id)
static int surface_context_match_runtime(yyjson_val *item, const char *scope_field, yyjson_val *context_id_value)
static int handle_db_postgresql_exec_command(int argc, char **argv)
Executes one PostgreSQL SQL text payload.
static int emit_compact_json_mut_value_runtime(yyjson_mut_val *value)
static yyjson_val * surface_find_record_by_string_field_runtime(yyjson_val *root, const char *collection_name, const char *field_name, const char *expected_value)
char * render_path_template_text_dup_runtime(const char *template_text, yyjson_val *params_root)
Renders one path-oriented template text string with JSON substitution.
char * render_template_text_dup_runtime(const char *template_text, yyjson_val *params_root)
Renders one HTML template text string with JSON parameter substitution.
static yyjson_mut_val * find_mut_array_item_by_long_field_runtime(yyjson_mut_val *array_value, const char *field_name, long expected_value)
static int multipart_find_part_runtime(const unsigned char *body, size_t body_len, const char *content_type, const char *field_name, char **out_text, char **out_filename, const unsigned char **out_content_start, size_t *out_content_len)
Finds one multipart part by field name and optionally returns its filename and content range.
static int handle_dynamic_relational_runtime_audit_count_command(int argc, char **argv)
Emits the current audit-event count for a sqlite-backed dynamic relational runtime.
static long state_tenant_id_for_business_slug_runtime(yyjson_val *root, const char *slug)
static yyjson_val * state_find_technician_for_business_runtime(yyjson_val *root, long technician_id, long business_id)
static int digits_only_runtime(const char *text, size_t len)
static int handle_dynamic_config_value_command(int argc, char **argv)
Emits one merged dynamic config value.
static char * capture_checksum_output_runtime(const char *command, const char *arg1, const char *arg2, const char *file_path, int *code_out)
static int emit_template_page_params_json_runtime(const char *error_html, const char *next_input, const char *account_type_options_html, const char *preview_html, const char *reset_post_path, const char *invite_email, const char *invite_path, const char *return_path, const char *invite_kind, const char *target_html, const char *form_action)
static int template_url_reference_allowed_runtime(const char *text)
Tests whether one candidate URL reference is safe for HTML URL attributes.
static yyjson_val * find_array_item_by_long_field_runtime(yyjson_val *array_value, const char *field_name, long expected_value)
char * explore_render_page_params_json_dup_runtime(const char *state_path, const char *app_root)
Builds rendered appointments page params JSON from runtime state.
static char * render_technician_checkboxes_html_dup_runtime(yyjson_val *root, long business_id, long service_id, const char *empty_message, int select_all, const char *app_root)
static int handle_object_merge_command(int argc, char **argv)
static char * day_of_week_options_html_dup_runtime(const char *selected_day)
static int handle_generated_surface_sort_options_html_command(int argc, char **argv)
static int value_long_equals_runtime(yyjson_val *value, long expected_value)
static int emit_business_public_render_page_json_runtime(yyjson_val *root, const char *slug, long user_id, const char *app_root)
static int handle_db_sqlite_table_count_command(int argc, char **argv)
Emits one sqlite non-system table count.
static int merge_patch_into_mut_object_runtime(yyjson_mut_doc *mut_doc, yyjson_mut_val *target_obj, yyjson_val *patch_root)
static int handle_validation_add_field_error_command(int argc, char **argv)
char * html_escaped_dup_runtime(const char *text)
Escapes one plain-text string for safe HTML insertion.
static int surface_filter_item_runtime(yyjson_val *item, yyjson_val *filters)
static int handle_dynamic_relational_backend_command(int argc, char **argv)
Emits the configured dynamic relational backend when it is recognized.
static yyjson_val * migration_backend_record_runtime(yyjson_val *root, const char *backend_value)
static int dynamic_relational_runtime_import_state_runtime(const char *app_root, const char *runtime_conf_path, const char *app_id, const char *state_path, const char *expected_audit_count)
Imports canonical UCAL relational runtime state using the configured backend contract.
static char * ucal_runtime_state_export_postgresql_path_dup_runtime(const char *app_root)
Resolves the declarative postgresql export query path for UCAL runtime-state bridge export.
char * declared_page_relative_path_dup_runtime(const char *app_root, const char *page_id)
Resolves one UCAL declared page id to its app-relative page-spec path.
static int event_stream_matches_filters_runtime(yyjson_val *filters, const event_stream_row_runtime *row)
static int collect_business_service_list_runtime(yyjson_val *services, long business_id, business_admin_service_list_runtime *list)
static int run_sqlite_command_runtime(const char *sqlite_path, const char *sql_text, char **output_out)
Executes one sqlite SQL batch through the native sqlite3 C API.
static int handle_generated_surface_mutate_command(int argc, char **argv)
static int emit_business_public_page_json_runtime(yyjson_val *root, const char *slug, long user_id)
static int business_open_for_slot_runtime(yyjson_val *root, long business_id, const char *date_value, const char *time_value, long duration_minutes)
static int handle_template_password_reset_request_page_json_command(int argc, char **argv)
static int handle_generated_surface_manage_table_json_command(int argc, char **argv)
char * language_options_html_dup_runtime(const char *current_lang)
static char * service_media_url_dup_runtime(const char *business_slug, const char *media_url)
static int handle_db_sqlite_exec_file_command(int argc, char **argv)
Executes one sqlite SQL file.
static char * render_service_sort_order_options_html_dup_runtime(yyjson_val *root, long business_id, long selected_order, int include_append, const char *app_root)
static void free_business_admin_service_list_runtime(business_admin_service_list_runtime *list)
static int handle_template_register_page_json_command(int argc, char **argv)
char * business_edit_page_params_json_dup_runtime(const char *state_path, const char *business_slug)
Builds rendered business-edit page params JSON from runtime state.
static char * render_service_visual_html_dup_runtime(const char *app_root, const char *image_url, const char *service_name)
static int handle_state_named_command(int argc, char **argv)
static yyjson_val * state_find_business_by_slug_runtime(yyjson_val *root, const char *slug)
static char * sql_literal_escape_dup_runtime(const char *value)
static int handle_generated_surface_filter_controls_html_command(int argc, char **argv)
char * service_book_form_page_params_json_dup_runtime(const char *state_path, const char *business_slug, long service_id, const char *app_root, const char *error_message)
Builds rendered service-book-form page params JSON from runtime state.
static int handle_state_ucal_import_row_json_lines_command(yyjson_val *root, const char *section_name)
static char * cksum_signature_runtime(const char *value)
static char * resolve_command_path_runtime(const char *command)
Tests whether an executable command name resolves through PATH.
static int handle_dynamic_safe_upload_filename_command(int argc, char **argv)
Emits the safe Pharos filename for an uploaded media candidate.
static int handle_template_invite_created_page_json_command(int argc, char **argv)
static int handle_template_layout_page_json_command(int argc, char **argv)
static char * render_business_fragment_dup_runtime(const char *app_root, const char *fragment_name, yyjson_val *params_root)
static int handle_object_path_bool_command(int argc, char **argv)
static int handle_template_layout_page_html_command(int argc, char **argv)
static int generated_surface_relation_array_all_integers_runtime(yyjson_val *array)
static int handle_generated_surface_params_json_command(int argc, char **argv)
static const char * ucal_declared_page_relative_path_runtime(const char *page_id)
Resolves one UCAL declared page id to its app-relative page-spec path.
static int emit_business_dashboard_page_json_runtime(yyjson_val *root, yyjson_val *tenant_ids, const char *app_root)
static int text_buffer_append_html_escaped_runtime(TextBufferRuntime *buffer, const char *text)
static int handle_validation_error_envelope_command(int argc, char **argv)
static int emit_business_calendar_page_json_runtime(yyjson_val *root, const char *slug)
static char * generated_surface_display_value_dup_runtime(yyjson_val *item_root, const char *field_name)
static yyjson_val * state_find_google_calendar_connection_for_business_runtime(yyjson_val *root, long business_id)
static int handle_db_postgresql_exec_file_command(int argc, char **argv)
Executes one PostgreSQL SQL file.
static int compare_business_admin_service_runtime(const void *left_ptr, const void *right_ptr)
static yyjson_val * find_array_item_by_string_field_runtime(yyjson_val *array_value, const char *field_name, const char *expected_value)
static char * multipart_boundary_dup_runtime(const char *content_type)
Extracts the multipart boundary token from a content type value.
static int handle_generated_surface_edit_page_json_command(int argc, char **argv)
static int emit_service_edit_page_json_runtime(yyjson_val *root, const char *slug, long service_id, const char *app_root)
static int emit_technician_appointments_lines_runtime(yyjson_val *root, long technician_id, const char *date_value)
static int handle_db_postgresql_table_exists_command(int argc, char **argv)
Emits whether one PostgreSQL table exists in the public schema.
static int handle_dynamic_ensure_business_media_tree_command(int argc, char **argv)
Ensures the managed business media subtree exists under an app artifact.
static long state_tenant_id_for_technician_id_runtime(yyjson_val *root, long technician_id)
static const unsigned char * memmem_runtime(const unsigned char *haystack, size_t haystack_len, const unsigned char *needle, size_t needle_len)
Finds the first binary needle occurrence within a byte range.
static char * dynamic_relational_runtime_export_json_dup_runtime(const char *app_root, const char *runtime_conf_path, const char *app_id)
Exports UCAL relational runtime state into canonical JSON using the configured backend contract.
static int handle_db_postgresql_audit_event_count_command(int argc, char **argv)
Emits the current PostgreSQL audit event count.
static int handle_template_password_reset_sent_page_json_command(int argc, char **argv)
static int handle_file_field_string_command(int argc, char **argv)
static int handle_validation_add_form_error_command(int argc, char **argv)
static int handle_dynamic_store_uploaded_media_command(int argc, char **argv)
Extracts one multipart upload into the app artifact media tree and returns the resulting media URL.
static int json_value_truthy_runtime(yyjson_val *value)
static yyjson_mut_val * ensure_mut_array_field_runtime(yyjson_mut_doc *doc, yyjson_mut_val *obj, const char *field_name)
static int load_native_libpq_api_runtime(NativeLibpqApi *api)
Loads the native libpq client ABI from cached image prefixes or standard library names.
static int generated_surface_text_is_decimal_runtime(const char *text)
static char * checksum_string_runtime(const char *value)
static int handle_generated_surface_form_fields_html_command(int argc, char **argv)
static int handle_db_sqlite_migration_applied_checksum_command(int argc, char **argv)
Emits one sqlite migration applied checksum.
static char * service_abbreviation_dup_runtime(const char *name)
static int append_generated_surface_relation_checkboxes_html_runtime(TextBufferRuntime *buffer, yyjson_val *state_root, yyjson_val *relation_root, yyjson_val *context_root, yyjson_val *selected_root)
static int handle_dynamic_postgresql_runtime_ready_command(int argc, char **argv)
Prepares the sqlite dynamic runtime schema, seed data, and migration ledger.
static int handle_generated_surface_manage_page_json_command(int argc, char **argv)
static int handle_db_sqlite_audit_event_count_command(int argc, char **argv)
Emits the current sqlite audit event count.
static yyjson_val * state_find_appointment_by_id_runtime(yyjson_val *root, long appointment_id)
static int emit_business_admin_technician_rows_json_runtime(yyjson_val *root, long business_id)
static int append_business_admin_service_runtime(business_admin_service_list_runtime *list, yyjson_val *service, long sort_order, long service_id)
static int emit_technician_availability_lines_runtime(yyjson_val *root, long technician_id, const char *day_name)
static int handle_dynamic_relational_runtime_export_json_command(int argc, char **argv)
Emits canonical exported JSON for a sqlite-backed dynamic relational runtime.
static int generated_surface_report_add_field_error_runtime(yyjson_mut_doc *doc, yyjson_mut_val *report_root, const char *field_id, const char *rule_id, const char *code_text, const char *message)
static int handle_validation_has_errors_command(int argc, char **argv)
static int handle_error_envelope_command(int argc, char **argv)
static int append_generated_surface_select_options_html_runtime(TextBufferRuntime *buffer, yyjson_val *field_root, const char *selected_value)
static char * dynamic_conf_lookup_runtime(const char *conf_path, const char *key)
Loads one raw config value from a path when the file exists.
static int handle_request_field_command(int argc, char **argv)
static int handle_template_invite_accept_page_json_command(int argc, char **argv)
static char * render_service_booking_options_editor_html_dup_runtime(yyjson_val *root, long service_id, const char *app_root)
static int route_template_match_into_mut_obj_runtime(yyjson_mut_doc *doc, const char *template_path, const char *request_path, yyjson_mut_val **out_params)
static int long_from_value_runtime(yyjson_val *value, long *out)
int handle_native_json_command(int argc, char **argv)
static yyjson_val * find_form_field_value_runtime(yyjson_val *root, const char *form_id, const char *field_id)
char * hidden_next_input_html_dup_runtime(const char *next_path)
Renders a hidden next-path <input> HTML, or an empty string.
static char * build_generated_surface_filter_controls_html_runtime(yyjson_val *surface_root, const char *raw_query)
static int template_path_reference_allowed_runtime(const char *text)
Tests whether one candidate path reference is safe for HTML URL attributes.
static int emit_managed_appointments_json_runtime(yyjson_val *root, yyjson_val *tenant_ids)
static int handle_object_path_array_length_command(int argc, char **argv)
static int emit_template_text_runtime(const char *template_text, yyjson_val *params_root)
static int emit_json_line_runtime(yyjson_val *value)
static int handle_generated_surface_coerce_candidate_command(int argc, char **argv)
char * apply_base_path_html_dup_runtime(const char *html, const char *base_path)
Rewrites relative URLs in HTML to use a base path.
static char * postgresql_conninfo_dup_runtime(const DynamicPostgresqlRuntimeConfig *config)
Builds one libpq conninfo string from merged runtime config values.
static int handle_template_render_file_command(int argc, char **argv)
static int handle_request_compact_json_command(int argc, char **argv)
static void * open_libpq_handle_candidate_runtime(const char *path)
Attempts to open one libpq shared library path.
static int emit_business_edit_page_json_runtime(yyjson_val *root, const char *slug)
static char * multipart_header_param_dup_runtime(const char *headers_text, const char *key)
Extracts one quoted Content-Disposition parameter from multipart headers.
static void free_native_libpq_api_runtime(NativeLibpqApi *api)
Releases one resolved native libpq ABI loader handle.
static yyjson_val * state_array_runtime(yyjson_val *root, const char *key)
static int emit_profile_page_json_runtime(yyjson_val *root, long user_id)
static int handle_generated_surface_validation_report_command(int argc, char **argv)
static int text_buffer_append_template_value_runtime(TextBufferRuntime *buffer, yyjson_val *value, TemplatePlaceholderKindRuntime kind)
Appends one template placeholder value using the requested safety mode.
static int handle_template_declared_fragment_path_command(int argc, char **argv)
Emits one UCAL declared fragment template path as text.
static int generated_surface_append_audit_event_mut_runtime(yyjson_mut_doc *mut_doc, yyjson_mut_val *mut_root, yyjson_val *surface_root, const char *operation_name, yyjson_val *user_root, yyjson_val *context_root, yyjson_val *item_root, const char *created_at)
static int handle_request_compact_object_strict_command(int argc, char **argv)
static int emit_user_appointments_json_runtime(yyjson_val *root, long user_id)
static int handle_manifest_app_root_command(int argc, char **argv)
static int handle_generated_surface_default_candidate_command(int argc, char **argv)
static int handle_bundle_manifest_layer_for_slice_command(int argc, char **argv)
static int emit_business_availability_lines_runtime(yyjson_val *root, long business_id, const char *day_name)
static int emit_compact_mutable_json_doc_runtime(yyjson_mut_doc *doc)
static int handle_bundle_manifest_slice_order_command(int argc, char **argv)
static int surface_compare_sort_values_runtime(yyjson_val *left_item, yyjson_val *right_item, const char *field_name, const char *type_name)
static yyjson_val * migration_record_by_id_runtime(yyjson_val *root, const char *migration_id)
static int weekday_rank_runtime(const char *value)
static yyjson_val * state_find_user_by_email_runtime(yyjson_val *root, const char *email)
static int postgresql_apply_file_list_runtime(const DynamicPostgresqlRuntimeConfig *config, const char *app_root, yyjson_val *list)
Applies one declarative SQL file list through the native PostgreSQL driver.
static char * generated_surface_operation_path_dup_runtime(yyjson_val *surface_root, yyjson_val *context_root, const char *operation_name, const char *identity_value)
static int handle_db_postgresql_reconcile_migration_checksum_command(int argc, char **argv)
Updates one PostgreSQL migration checksum when the ledger row is missing one.
static int emit_object_string_field_runtime(yyjson_val *obj, const char *field_name, const char *default_value)
static int capture_postgresql_result_output_runtime(const NativeLibpqApi *api, PGresult *result, char **output_out)
Captures one tuple-producing PostgreSQL result into CLI-compatible line output.
static char * generated_surface_delete_strategy_runtime(yyjson_val *surface_root)
static int handle_template_render_path_command(int argc, char **argv)
static long event_stream_tenant_id_runtime(yyjson_val *root, yyjson_val *payload, long business_id)
static yyjson_val * state_find_user_by_username_runtime(yyjson_val *root, const char *username)
static int handle_object_field_string_command(int argc, char **argv)
static yyjson_mut_val * surface_build_sort_config_runtime(yyjson_mut_doc *doc, yyjson_val *surface_root, const char *raw_query)
static int handle_object_field_text_command(int argc, char **argv)
static int text_buffer_append_text_runtime(TextBufferRuntime *buffer, const char *text)
static int emit_profile_render_page_json_runtime(yyjson_val *root, long user_id, const char *app_root, const char *payment_preference_options_html)
Emits rendered profile-page template params using declarative subscription fragments.
static int emit_appointments_page_json_runtime(yyjson_val *root, long user_id, yyjson_val *tenant_ids, const char *app_root)
static int run_postgresql_sql_capture_runtime(const DynamicPostgresqlRuntimeConfig *config, const char *sql_text, int at_output, char **output_out)
Runs one PostgreSQL SQL text payload through the native libpq driver.
static int handle_dynamic_multipart_extract_file_command(int argc, char **argv)
Extracts a file from a multipart upload and writes it to disk.
static char * ucal_runtime_state_import_postgresql_path_dup_runtime(const char *app_root)
Resolves the declarative postgresql import query path for UCAL runtime-state bridge import.
static int emit_user_membership_tenant_ids_json_runtime(yyjson_val *root, long user_id)
static const char * service_badge_color_runtime(const char *name)
static int handle_file_normalize_ucal_state_command(int argc, char **argv)
static const char * ucal_declared_fragment_relative_path_runtime(const char *fragment_id)
Resolves one UCAL declared fragment id to its app-relative template path.
static int time_ranges_overlap_runtime(int start_a_minutes, int duration_a, int start_b_minutes, int duration_b)
static int handle_dynamic_media_disk_path_command(int argc, char **argv)
Emits the on-disk artifact path for a managed media URL.
static int handle_template_password_reset_complete_page_json_command(int argc, char **argv)
static int handle_db_sqlite_table_exists_command(int argc, char **argv)
Emits whether one sqlite table exists.
static int template_reference_has_allowed_url_scheme_runtime(const char *text)
Tests whether a reference uses one permitted external URL scheme.
static int conninfo_append_kv_runtime(TextBufferRuntime *buffer, const char *key, const char *value)
Appends one libpq conninfo key value pair.
static yyjson_val * state_find_service_by_id_runtime(yyjson_val *root, long service_id)
static char * build_generated_surface_form_fields_html_runtime(yyjson_val *state_root, yyjson_val *fixture_root, yyjson_val *surface_root, yyjson_val *context_root, yyjson_val *item_root)
static int handle_object_path_json_command(int argc, char **argv)
static void json_value_list_free_runtime(json_value_list_runtime *list)
static int handle_form_field_json_command(int argc, char **argv)
TemplatePlaceholderKindRuntime
@ TEMPLATE_PLACEHOLDER_PATH_RUNTIME
@ TEMPLATE_PLACEHOLDER_URL_RUNTIME
@ TEMPLATE_PLACEHOLDER_HTML_RUNTIME
@ TEMPLATE_PLACEHOLDER_TEXT_RUNTIME
static int handle_request_form_field_command(int argc, char **argv)
Returns one request form field value from urlencoded or multipart request inputs.
static int emit_business_admin_page_json_runtime(yyjson_val *root, const char *slug, const char *app_root, const char *invite_account_type_options_html)
static int long_in_array_runtime(yyjson_val *array_value, long expected_value)
static char * query_value_decoded_dup_runtime(const char *raw_query, const char *key)
static int handle_validation_summary_text_command(int argc, char **argv)
static int handle_validation_empty_report_command(int argc, char **argv)
static yyjson_val * state_find_business_availability_for_business_runtime(yyjson_val *root, long availability_id, long business_id)
static yyjson_val * state_find_service_for_business_runtime(yyjson_val *root, long service_id, long business_id)
static long tenant_id_for_business_runtime(yyjson_val *root, long business_id)
static char * dup_or_default_runtime(const char *text, const char *fallback)
static int handle_generated_surface_display_value_command(int argc, char **argv)
static yyjson_val * surface_find_by_id_runtime(yyjson_val *root, const char *surface_id)
static int handle_file_migration_backend_record_json_command(int argc, char **argv)
static yyjson_val * state_find_business_by_id_runtime(yyjson_val *root, long business_id)
static int handle_form_field_json_lines_command(int argc, char **argv)
static int handle_generated_surface_submitted_command(int argc, char **argv)
static int emit_service_assigned_technician_ids_lines_runtime(yyjson_val *root, yyjson_val *service, long business_id)
static int handle_form_options_html_command(int argc, char **argv)
static int text_buffer_append_char_runtime(TextBufferRuntime *buffer, char value)
static int handle_dynamic_file_signature_command(int argc, char **argv)
Emits a stable file signature for change detection.
static int handle_file_migration_backend_list_lines_command(int argc, char **argv)
static int generated_surface_text_is_date_runtime(const char *text)
static char * payload_first_string_dup_runtime(yyjson_val *payload, const char **fields, size_t field_count)
static char * html_escape_dup_runtime(const char *text)
static int append_event_stream_row_runtime(event_stream_row_list_runtime *list, event_stream_row_runtime *row)
static int handle_db_postgresql_table_row_count_command(int argc, char **argv)
Emits one PostgreSQL public table row count.
static int handle_db_postgresql_create_database_if_missing_command(int argc, char **argv)
Creates one PostgreSQL database when it is missing.
static int compare_event_stream_rows_runtime(const void *lhs_ptr, const void *rhs_ptr)
static int handle_request_booking_options_json_from_form_command(int argc, char **argv)
Extracts booking option rows directly from repeated request form fields.
static int contains_casefold_runtime(const char *haystack, const char *needle)
static int handle_object_array_item_json_lines_command(int argc, char **argv)
char * render_page_html_dup_runtime(const char *app_root, const char *base_path, const char *layout_id, const char *title, const char *body, const char *current_lang, const char *current_request_target, int has_user, int managed_business_count)
Builds the nav context and renders a full layout page.
static int emit_json_or_empty_runtime(yyjson_val *value)
static int load_dynamic_postgresql_runtime_config(const char *app_root, const char *runtime_conf_path, DynamicPostgresqlRuntimeConfig *config)
Loads merged PostgreSQL runtime connection settings for one dynamic app.
static int conninfo_append_text_runtime(TextBufferRuntime *buffer, const char *text, int escape_quotes)
Appends one libpq conninfo key or value fragment with native escaping.
static char * ucal_runtime_state_import_sqlite_path_dup_runtime(const char *app_root)
Resolves the declarative sqlite import query path for UCAL runtime-state bridge import.
static int handle_request_compact_object_command(int argc, char **argv)
static long assign_technician_for_slot_runtime(yyjson_val *root, long business_id, long service_id, const char *date_value, const char *time_value)
static char * render_business_fragment_from_doc_dup_runtime(const char *app_root, const char *fragment_name, yyjson_mut_doc *doc)
static yyjson_val * surface_find_form_runtime(yyjson_val *root, const char *form_id)
static int emit_service_book_form_page_json_runtime(yyjson_val *root, const char *slug, long service_id, const char *app_root, const char *error_message)
static int handle_db_postgresql_migration_applied_checksum_command(int argc, char **argv)
Emits one PostgreSQL migration applied checksum.
static int handle_request_checkbox_id_array_json_command(int argc, char **argv)
static int handle_db_postgresql_database_exists_command(int argc, char **argv)
Emits whether one PostgreSQL database exists.
static void free_dynamic_postgresql_runtime_config(DynamicPostgresqlRuntimeConfig *config)
Releases owned PostgreSQL runtime connection settings.
static int sqlite_capture_callback_runtime(void *context_void, int column_count, char **column_values, char **column_names)
Captures sqlite result rows into the shared text buffer using CLI-compatible separators.
static int collect_event_stream_row_runtime(event_stream_row_list_runtime *rows, yyjson_val *root, const char *source, const char *event_kind_default, yyjson_val *payload, const char *created_at_field_fallback, long native_order, yyjson_val *subscription_scope, yyjson_val *filters, const char *created_at_override, const char *event_kind_override)
static int text_buffer_append_format_runtime(TextBufferRuntime *buffer, const char *format,...)
static yyjson_val * state_find_technician_by_id_runtime(yyjson_val *root, long technician_id)
static int handle_object_array_all_integers_command(int argc, char **argv)
static int user_manages_business_runtime(yyjson_val *root, long user_id, long business_id)
static int dynamic_config_key_is_path_runtime(const char *key)
Returns non-zero when a dynamic config key should resolve relative to its owning config file.
static int handle_db_sqlite_reconcile_migration_checksum_command(int argc, char **argv)
Updates a sqlite migration checksum when the ledger row is missing one.
static int emit_active_tenant_ids_lines_runtime(yyjson_val *root)
static int generated_surface_normalize_order_mut_runtime(yyjson_mut_doc *mut_doc, yyjson_mut_val *mut_root, yyjson_val *surface_root, yyjson_val *context_root)
static int identifier_safe_runtime(const char *text)
Tests whether one identifier contains only letters, digits, and underscores.
static int handle_request_form_value_command(int argc, char **argv)
Extracts a value from a URL-encoded form body.
int(* NativeJsonCommandHandlerFn)(int argc, char **argv)
static int compare_generated_surface_order_item_runtime(const void *left_ptr, const void *right_ptr)
static int append_date_runtime(TextBufferRuntime *buffer, int year, int month, int day)
static char * business_media_url_dup_runtime(const char *business_slug, const char *media_url)
int write_binary_file_runtime(const char *path, const unsigned char *data, size_t length)
Writes one binary byte range to a file path, creating parent directories as needed.
static int handle_generated_surface_context_command(int argc, char **argv)
static int emit_business_calendar_render_page_json_runtime(yyjson_val *root, const char *slug, const char *app_root, const char *selected_year_text, const char *selected_month_text, const char *selected_date_text)
static long state_tenant_id_for_service_id_runtime(yyjson_val *root, long service_id)
static yyjson_mut_val * normalized_payload_object_mut_runtime(yyjson_mut_doc *doc, const char *payload_json)
static yyjson_val * state_find_booking_for_user_runtime(yyjson_val *root, long booking_id, long user_id)
static int write_mutable_json_doc_runtime(yyjson_mut_doc *doc, const char *path)
static void free_event_stream_row_runtime(event_stream_row_runtime *row)
static int handle_db_postgresql_migration_applied_schema_version_command(int argc, char **argv)
Emits one PostgreSQL migration applied schema version.
static int handle_file_section_length_command(int argc, char **argv)
static char * event_stream_resource_id_dup_runtime(yyjson_val *payload)
static int emit_public_business_cards_json_runtime(yyjson_val *root)
static int handle_db_sqlite_query_value_command(int argc, char **argv)
Emits the first-line output of one sqlite query.
char * diagnostics_render_page_params_json_dup_runtime(const char *state_path)
static int append_validation_error_runtime(const char *report_json, const char *array_name, const char *field_id, const char *rule_id, const char *code_text, const char *message)
static int handle_manifest_fixture_path_raw_command(int argc, char **argv)
yyjson_val * load_root_from_text_runtime(const char *text, yyjson_doc **doc_out)
Parses JSON text into a yyjson_val root.
static int emit_manageable_business_cards_json_runtime(yyjson_val *root, yyjson_val *tenant_ids)
static void text_buffer_free_runtime(TextBufferRuntime *buffer)
static const char * month_name_runtime(int month)
static int handle_object_array_lines_command(int argc, char **argv)
static int handle_object_array_length_command(int argc, char **argv)
static int generated_surface_text_is_time_runtime(const char *text)
static int handle_request_cookie_value_command(int argc, char **argv)
Extracts a cookie value from a Cookie header string.
static int handle_dynamic_relational_runtime_import_state_command(int argc, char **argv)
Imports canonical state into a sqlite-backed dynamic relational runtime.
int capture_execv_output_native(char *const argv[], char **output, size_t *output_len)
Executes a process and captures its combined output.
Declares child-process capture helpers.
char * path_join(const char *left, const char *right)
Joins two path segments using the native path separator.
void trim_in_place(char *text)
Trims leading and trailing ASCII whitespace from a string in place.
char * dup_range(const char *start, size_t len)
Duplicates a byte range into a newly allocated NUL-terminated string.
int streq(const char *a, const char *b)
Tests whether two strings are exactly equal.
char * path_dirname(const char *path)
Returns the parent directory component of a path.
char * resolve_relative_to(const char *base_dir, const char *value)
Resolves a path value relative to a base directory when needed.
char * read_file_text(const char *path)
Reads an entire file into a newly allocated text buffer.
int path_exists(const char *path)
Tests whether a filesystem path currently exists.
Declares shared low-level support helpers used across the Pharos native runtime.
yyjson_doc * native_json_doc_load_file(const char *path)
Parses a JSON file into an owned yyjson document.
long native_json_obj_get_long_default(yyjson_val *obj, const char *key, long default_value, int *found_out)
Reads a numeric object member or returns a fallback value.
void native_json_doc_free(yyjson_doc *doc)
Releases a document returned by the native yyjson helpers.
yyjson_doc * native_json_doc_load_text(const char *text)
Parses JSON text into an owned yyjson document.
char * native_json_obj_get_string_dup(yyjson_val *obj, const char *key)
Duplicates a string-valued key from a JSON object.
yyjson_val * native_json_obj_get_array(yyjson_val *obj, const char *key)
Looks up an array-valued key from a JSON object value.
yyjson_val * native_json_obj_get(yyjson_val *obj, const char *key)
Looks up a key from a JSON object value.
char * native_json_serialize_compact_dup(yyjson_val *value)
Serializes a JSON value to compact JSON text.
Declares shared helper wrappers around vendored yyjson parsing and serialization APIs.
Carries resolved PostgreSQL runtime connection settings for native relational bridge commands.
NativeJsonCommandHandlerFn handler
const char * command_name
char *(* PQerrorMessage)(const PGconn *conn)
PGconn *(* PQconnectdb)(const char *conninfo)
char *(* PQgetvalue)(const PGresult *res, int row_number, int column_number)
int(* PQresultStatus)(const PGresult *res)
int(* PQntuples)(const PGresult *res)
int(* PQstatus)(const PGconn *conn)
void(* PQclear)(PGresult *res)
void(* PQfinish)(PGconn *conn)
PGresult *(* PQexec)(PGconn *conn, const char *query)
int(* PQnfields)(const PGresult *res)
TextBufferRuntime * buffer
business_admin_service_runtime * items
event_stream_row_runtime * items
yyjson_val * payload_json
void yyjson_mut_doc_free(yyjson_mut_doc *doc)
yyjson_mut_doc * yyjson_mut_doc_new(const yyjson_alc *alc)
yyjson_mut_doc * yyjson_doc_mut_copy(const yyjson_doc *doc, const yyjson_alc *alc)
yyjson_mut_val * yyjson_val_mut_copy(yyjson_mut_doc *m_doc, const yyjson_val *i_vals)
yyjson_api_inline size_t yyjson_mut_arr_size(const yyjson_mut_val *arr)
yyjson_api_inline yyjson_mut_val * yyjson_mut_obj_remove_key(yyjson_mut_val *obj, const char *key)
yyjson_api_inline bool yyjson_is_str(const yyjson_val *val)
yyjson_api_inline yyjson_mut_val * yyjson_mut_strncpy(yyjson_mut_doc *doc, const char *str, size_t len)
yyjson_api_inline bool yyjson_is_obj(const yyjson_val *val)
yyjson_api_inline bool yyjson_mut_obj_add_val(yyjson_mut_doc *doc, yyjson_mut_val *obj, const char *key, yyjson_mut_val *val)
#define yyjson_mut_arr_foreach(arr, idx, max, val)
yyjson_api_inline yyjson_mut_val * yyjson_mut_arr_remove(yyjson_mut_val *arr, size_t idx)
yyjson_api_inline bool yyjson_is_sint(const yyjson_val *val)
yyjson_api_inline yyjson_mut_val * yyjson_mut_obj_get(const yyjson_mut_val *obj, const char *key)
yyjson_api_inline uint64_t yyjson_get_uint(const yyjson_val *val)
yyjson_api_inline yyjson_val * yyjson_obj_iter_get_val(yyjson_val *key)
yyjson_api_inline int64_t yyjson_get_sint(const yyjson_val *val)
yyjson_api_inline yyjson_val * yyjson_arr_get_first(const yyjson_val *arr)
yyjson_api_inline yyjson_val * yyjson_arr_get(const yyjson_val *arr, size_t idx)
yyjson_api_inline bool yyjson_mut_obj_add_int(yyjson_mut_doc *doc, yyjson_mut_val *obj, const char *key, int64_t val)
yyjson_api_inline yyjson_mut_val * yyjson_mut_bool(yyjson_mut_doc *doc, bool val)
yyjson_api_inline yyjson_val * yyjson_obj_iter_next(yyjson_obj_iter *iter)
yyjson_api_inline yyjson_val * yyjson_doc_get_root(const yyjson_doc *doc)
yyjson_api_inline bool yyjson_mut_obj_add_null(yyjson_mut_doc *doc, yyjson_mut_val *obj, const char *key)
yyjson_api_inline bool yyjson_is_arr(const yyjson_val *val)
yyjson_api_inline char * yyjson_mut_val_write(const yyjson_mut_val *val, yyjson_write_flag flg, size_t *len)
yyjson_api_inline yyjson_mut_val * yyjson_mut_obj(yyjson_mut_doc *doc)
yyjson_api_inline yyjson_mut_val * yyjson_mut_null(yyjson_mut_doc *doc)
yyjson_api_inline bool yyjson_is_int(const yyjson_val *val)
yyjson_api_inline bool yyjson_is_real(const yyjson_val *val)
yyjson_api_inline bool yyjson_obj_iter_init(const yyjson_val *obj, yyjson_obj_iter *iter)
yyjson_api_inline char * yyjson_mut_write(const yyjson_mut_doc *doc, yyjson_write_flag flg, size_t *len)
yyjson_api_inline void yyjson_mut_doc_set_root(yyjson_mut_doc *doc, yyjson_mut_val *root)
yyjson_api_inline yyjson_mut_val * yyjson_mut_int(yyjson_mut_doc *doc, int64_t num)
yyjson_api_inline yyjson_mut_val * yyjson_mut_strcpy(yyjson_mut_doc *doc, const char *str)
yyjson_api_inline bool yyjson_equals_str(const yyjson_val *val, const char *str)
yyjson_api_inline bool yyjson_mut_is_obj(const yyjson_mut_val *val)
yyjson_api_inline yyjson_mut_val * yyjson_mut_doc_get_root(yyjson_mut_doc *doc)
yyjson_api_inline bool yyjson_is_uint(const yyjson_val *val)
yyjson_api_inline yyjson_val * yyjson_arr_iter_next(yyjson_arr_iter *iter)
yyjson_api_inline bool yyjson_is_num(const yyjson_val *val)
yyjson_api_inline bool yyjson_is_null(const yyjson_val *val)
yyjson_api_inline yyjson_mut_val * yyjson_mut_arr_get(const yyjson_mut_val *arr, size_t idx)
yyjson_api_inline size_t yyjson_get_len(const yyjson_val *val)
yyjson_api_inline bool yyjson_mut_obj_add_bool(yyjson_mut_doc *doc, yyjson_mut_val *obj, const char *key, bool val)
yyjson_api_inline bool yyjson_mut_obj_add_strcpy(yyjson_mut_doc *doc, yyjson_mut_val *obj, const char *key, const char *val)
yyjson_api_inline bool yyjson_mut_obj_add(yyjson_mut_val *obj, yyjson_mut_val *key, yyjson_mut_val *val)
yyjson_api_inline size_t yyjson_arr_size(const yyjson_val *arr)
yyjson_api_inline bool yyjson_mut_obj_put(yyjson_mut_val *obj, yyjson_mut_val *key, yyjson_mut_val *val)
static const yyjson_write_flag YYJSON_WRITE_NOFLAG
yyjson_api_inline const char * yyjson_get_str(const yyjson_val *val)
yyjson_api_inline double yyjson_get_real(const yyjson_val *val)
yyjson_api_inline bool yyjson_mut_is_arr(const yyjson_mut_val *val)
yyjson_api_inline yyjson_mut_val * yyjson_mut_arr(yyjson_mut_doc *doc)
yyjson_api_inline bool yyjson_mut_arr_append(yyjson_mut_val *arr, yyjson_mut_val *val)
yyjson_api_inline bool yyjson_arr_iter_init(const yyjson_val *arr, yyjson_arr_iter *iter)
yyjson_api_inline bool yyjson_get_bool(const yyjson_val *val)
yyjson_api_inline bool yyjson_is_bool(const yyjson_val *val)