1656 lines
44 KiB
C
1656 lines
44 KiB
C
#include "include/conf.h"
|
|
|
|
#include "include/path-builtin.h"
|
|
|
|
#include <vessel/def.h>
|
|
#include <vessel/switch.h>
|
|
|
|
#include "include/path.h"
|
|
|
|
#include <ctype.h>
|
|
|
|
typedef struct {
|
|
char *pat;
|
|
size_t len;
|
|
} vw_DateTimeState;
|
|
|
|
static inline int
|
|
vw_DateTime_matchn_parse_digits(const char *str, size_t *pos, size_t max, size_t count) {
|
|
int val = 0;
|
|
|
|
for (size_t idx = 0; idx < count; ++idx) {
|
|
if (*pos >= max || str[*pos] < '0' || str[*pos] > '9') {
|
|
return -1;
|
|
}
|
|
|
|
val = val * 10 + (str[(*pos)++] - '0');
|
|
}
|
|
|
|
return val;
|
|
}
|
|
|
|
static inline bool vw_DateTime_matchn_is_leap_year(const uint16_t year) {
|
|
return (year % 4 == 0 && (year % 100 != 0 || year % 400 == 0));
|
|
}
|
|
|
|
static inline uint16_t vw_find_century(const uint16_t year) {
|
|
return (uint16_t)((year % 100 != 0) + (year / 100));
|
|
}
|
|
|
|
bool vw_DateTime_print(const vw_DateTime *dt) {
|
|
if (!dt) {
|
|
return false;
|
|
}
|
|
|
|
static const char *weekdays[] = { "Sunday", "Monday", "Tuesday", "Wednesday",
|
|
"Thursday", "Friday", "Saturday" };
|
|
|
|
if (dt->set & VW_DATE_TIME_SET_YEAR) {
|
|
printf("%04u", dt->year);
|
|
if (dt->set & VW_DATE_TIME_SET_MONTH) {
|
|
printf("-%02u", dt->month);
|
|
if (dt->set & VW_DATE_TIME_SET_DAY) {
|
|
printf("-%02u", dt->day);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (dt->set & VW_DATE_TIME_SET_HOUR) {
|
|
printf(" %02u", dt->hour);
|
|
if (dt->set & VW_DATE_TIME_SET_MINUTE) {
|
|
printf(":%02u", dt->minute);
|
|
if (dt->set & VW_DATE_TIME_SET_SECOND) {
|
|
printf(":%02u", dt->second);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (dt->set & VW_DATE_TIME_SET_WEEKDAY) {
|
|
printf(", %s", weekdays[dt->weekday]);
|
|
if (dt->set & VW_DATE_TIME_SET_YEARWEEK) {
|
|
printf(" (week %u)", dt->yearweek);
|
|
}
|
|
if (dt->set & VW_DATE_TIME_SET_YEARDAY) {
|
|
printf(", day %u of the year", dt->yearday);
|
|
}
|
|
}
|
|
|
|
if (dt->set & VW_DATE_TIME_SET_OFFSET) {
|
|
printf(" UTC");
|
|
if (dt->offset >= 0) {
|
|
printf("+");
|
|
}
|
|
printf("%.2f", dt->offset);
|
|
}
|
|
|
|
putchar('\n');
|
|
return true;
|
|
}
|
|
|
|
size_t vw_DateTime_matchn(
|
|
vw_DateTime *dt, const char *pat, size_t pat_len, const char *src, size_t src_len) {
|
|
if (!dt || !pat || !*pat || pat_len == 0 || !src || !*src || src_len == 0) {
|
|
return 0;
|
|
}
|
|
|
|
size_t p = 0;
|
|
size_t s = 0;
|
|
size_t matched = 0;
|
|
|
|
time_t seconds = time(NULL);
|
|
struct tm *current_time = localtime(&seconds);
|
|
|
|
dt->year = (uint16_t)(current_time->tm_year > 1900 ? (current_time->tm_year + 1900) : 0);
|
|
dt->set = VW_DATE_TIME_SET_NONE;
|
|
|
|
while (p < pat_len && s < src_len) {
|
|
/* Static match */
|
|
if (pat[p] != '%') {
|
|
if (src[s++] != pat[p++]) {
|
|
return 0;
|
|
}
|
|
matched = s;
|
|
continue;
|
|
}
|
|
|
|
/* Skip past % */
|
|
if (++p >= pat_len) {
|
|
return 0;
|
|
}
|
|
|
|
/* Parse fmt */
|
|
switch (pat[p++]) {
|
|
case 'Y': {
|
|
const int year = vw_DateTime_matchn_parse_digits(src, &s, src_len, 4);
|
|
if (year < 0) {
|
|
return 0;
|
|
}
|
|
dt->year = (uint16_t)year;
|
|
dt->set |= VW_DATE_TIME_SET_YEAR;
|
|
break;
|
|
}
|
|
|
|
case 'y': {
|
|
const int yy = vw_DateTime_matchn_parse_digits(src, &s, src_len, 2);
|
|
if (yy < 0) {
|
|
return 0;
|
|
}
|
|
dt->year = (uint16_t)(vw_find_century(dt->year) + yy);
|
|
dt->set |= VW_DATE_TIME_SET_YEAR;
|
|
break;
|
|
}
|
|
|
|
case 'm': {
|
|
const int month = vw_DateTime_matchn_parse_digits(src, &s, src_len, 2);
|
|
if (month < 1 || month > 12) {
|
|
return 0;
|
|
}
|
|
dt->month = (uint8_t)month;
|
|
dt->set |= VW_DATE_TIME_SET_MONTH;
|
|
break;
|
|
}
|
|
|
|
case 'b': {
|
|
if (s + 2 >= src_len) {
|
|
return 0;
|
|
}
|
|
|
|
const uint64_t hash = vs_switch_hashn(&src[s], 3);
|
|
|
|
switch (hash) {
|
|
case 4939817477974586218ULL /* Jan */:
|
|
dt->month = 1;
|
|
break;
|
|
case 15709302032656021367ULL /* Feb */:
|
|
dt->month = 2;
|
|
break;
|
|
case 13369147897303923553ULL /* Mar */:
|
|
dt->month = 3;
|
|
break;
|
|
case 1455433102601615117ULL /* Apr */:
|
|
dt->month = 4;
|
|
break;
|
|
case 214815404126853896ULL /* May */:
|
|
dt->month = 5;
|
|
break;
|
|
case 3581390445273919101ULL /* Jun */:
|
|
dt->month = 6;
|
|
break;
|
|
case 15353982735725600812ULL /* Jul */:
|
|
dt->month = 7;
|
|
break;
|
|
case 15401104073652460215ULL /* Aug */:
|
|
dt->month = 8;
|
|
break;
|
|
case 3369370446682186658ULL /* Sep */:
|
|
dt->month = 9;
|
|
break;
|
|
case 1911355218825294899ULL /* Oct */:
|
|
dt->month = 10;
|
|
break;
|
|
case 9172265116556117536ULL /* Nov */:
|
|
dt->month = 11;
|
|
break;
|
|
case 5696553264589341849ULL /* Dec */:
|
|
dt->month = 12;
|
|
break;
|
|
default:
|
|
return 0;
|
|
}
|
|
|
|
s += 3;
|
|
dt->set |= VW_DATE_TIME_SET_MONTH;
|
|
|
|
break;
|
|
}
|
|
|
|
case 'B': {
|
|
const size_t start = s;
|
|
|
|
if (!(src[s] >= 'A' && src[s] <= 'Z')) {
|
|
return false;
|
|
}
|
|
++s;
|
|
|
|
while (src[s] >= 'a' && src[s] <= 'z') {
|
|
++s;
|
|
}
|
|
|
|
const size_t len = s - start;
|
|
|
|
if (len < 3) {
|
|
return false;
|
|
}
|
|
|
|
const uint64_t hash = vs_switch_hashn(&src[start], len);
|
|
|
|
switch (hash) {
|
|
case 17964601829990767649ULL /* January */:
|
|
dt->month = 1;
|
|
break;
|
|
case 7255334670142472140ULL /* February */:
|
|
dt->month = 2;
|
|
break;
|
|
case 9305720913207313840ULL /* March */:
|
|
dt->month = 3;
|
|
break;
|
|
case 11074245164197147555ULL /* April */:
|
|
dt->month = 4;
|
|
break;
|
|
case 214815404126853896ULL /* May */:
|
|
dt->month = 5;
|
|
break;
|
|
case 15085057628823179632ULL /* June */:
|
|
dt->month = 6;
|
|
break;
|
|
case 3727642979271583586ULL /* July */:
|
|
dt->month = 7;
|
|
break;
|
|
case 5444754080439213179ULL /* August */:
|
|
dt->month = 8;
|
|
break;
|
|
case 7373379154950709813ULL /* September */:
|
|
dt->month = 9;
|
|
break;
|
|
case 7166752048728274669ULL /* October */:
|
|
dt->month = 10;
|
|
break;
|
|
case 12975793898007606520ULL /* November */:
|
|
dt->month = 11;
|
|
break;
|
|
case 17027227474939830354ULL /* December */:
|
|
dt->month = 12;
|
|
break;
|
|
default:
|
|
return 0;
|
|
}
|
|
|
|
s += len - 1;
|
|
|
|
dt->set |= VW_DATE_TIME_SET_MONTH;
|
|
break;
|
|
}
|
|
|
|
case 'd': {
|
|
const int day = vw_DateTime_matchn_parse_digits(src, &s, src_len, 2);
|
|
if (day < 1 || day > 31) {
|
|
return 0;
|
|
}
|
|
dt->day = (uint8_t)day;
|
|
dt->set |= VW_DATE_TIME_SET_DAY;
|
|
break;
|
|
}
|
|
|
|
case 'j': {
|
|
const int yearday = vw_DateTime_matchn_parse_digits(src, &s, src_len, 3);
|
|
if (yearday < 1 || yearday > 366) {
|
|
return 0;
|
|
}
|
|
dt->yearday = (uint16_t)yearday;
|
|
dt->set |= VW_DATE_TIME_SET_YEARDAY;
|
|
break;
|
|
}
|
|
|
|
case 'H': {
|
|
const int hour = vw_DateTime_matchn_parse_digits(src, &s, src_len, 2);
|
|
if (hour < 0 || hour > 23) {
|
|
return 0;
|
|
}
|
|
dt->hour = (uint8_t)hour;
|
|
dt->set |= VW_DATE_TIME_SET_HOUR;
|
|
break;
|
|
}
|
|
|
|
case 'I': {
|
|
const int hour = vw_DateTime_matchn_parse_digits(src, &s, src_len, 2);
|
|
if (hour < 1 || hour > 12) {
|
|
return 0;
|
|
}
|
|
dt->hour = (uint8_t)hour;
|
|
dt->set |= VW_DATE_TIME_SET_HOUR;
|
|
break;
|
|
}
|
|
|
|
case 'M': {
|
|
const int mins = vw_DateTime_matchn_parse_digits(src, &s, src_len, 2);
|
|
if (mins < 0 || mins > 59) {
|
|
return 0;
|
|
}
|
|
dt->minute = (uint8_t)mins;
|
|
dt->set |= VW_DATE_TIME_SET_MINUTE;
|
|
break;
|
|
}
|
|
|
|
case 'S': {
|
|
const int sec = vw_DateTime_matchn_parse_digits(src, &s, src_len, 2);
|
|
if (sec < 0 || sec > 59) {
|
|
return 0;
|
|
}
|
|
dt->second = (uint8_t)sec;
|
|
dt->set |= VW_DATE_TIME_SET_SECOND;
|
|
break;
|
|
}
|
|
|
|
case 'p': {
|
|
if (s + 1 >= src_len) {
|
|
return 0;
|
|
}
|
|
if ((src[s] == 'P' || src[s] == 'p') && (src[s + 1] == 'M' || src[s + 1] == 'm')) {
|
|
if (dt->hour < 12) {
|
|
dt->hour += 12;
|
|
}
|
|
dt->set |= VW_DATE_TIME_SET_HOUR;
|
|
s += 2;
|
|
} else if ((src[s] == 'A' || src[s] == 'a') &&
|
|
(src[s + 1] == 'M' || src[s + 1] == 'm')) {
|
|
if (dt->hour == 12) {
|
|
dt->hour = 0;
|
|
}
|
|
dt->set |= VW_DATE_TIME_SET_HOUR;
|
|
s += 2;
|
|
} else {
|
|
return 0;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case 'A': {
|
|
const size_t start = s;
|
|
|
|
if (!(src[s] >= 'A' && src[s] <= 'Z')) {
|
|
return false;
|
|
}
|
|
++s;
|
|
|
|
while (src[s] >= 'a' && src[s] <= 'z') {
|
|
++s;
|
|
}
|
|
|
|
const size_t len = s - start;
|
|
|
|
if (len < 3) {
|
|
return false;
|
|
}
|
|
|
|
const uint64_t hash = vs_switch_hashn(&src[start], len);
|
|
|
|
switch (hash) {
|
|
case 1499209936175797929ULL /* Sunday */:
|
|
dt->weekday = 0;
|
|
break;
|
|
case 15343497047085022806ULL /* Monday */:
|
|
dt->weekday = 1;
|
|
break;
|
|
case 682670563137842932ULL /* Tuesday */:
|
|
dt->weekday = 2;
|
|
break;
|
|
case 12516549914688717708ULL /* Wednesday */:
|
|
dt->weekday = 3;
|
|
break;
|
|
case 13714863946278208848ULL /* Thursday */:
|
|
dt->weekday = 4;
|
|
break;
|
|
case 18143703299807957456ULL /* Friday */:
|
|
dt->weekday = 5;
|
|
break;
|
|
case 15689643484499948403ULL /* Saturday */:
|
|
dt->weekday = 6;
|
|
break;
|
|
default:
|
|
return 0;
|
|
}
|
|
|
|
dt->set |= VW_DATE_TIME_SET_WEEKDAY;
|
|
break;
|
|
}
|
|
|
|
case 'a': {
|
|
if (s + 2 >= src_len) {
|
|
return 0;
|
|
}
|
|
|
|
const uint64_t hash = vs_switch_hashn(&src[s], 3);
|
|
|
|
switch (hash) {
|
|
case 16849390285903832232ULL /* Sun */:
|
|
dt->weekday = 0;
|
|
break;
|
|
case 10205521733537287787ULL /* Mon */:
|
|
dt->weekday = 1;
|
|
break;
|
|
case 6157980093028800402ULL /* Tue */:
|
|
dt->weekday = 2;
|
|
break;
|
|
case 14244903640496851306ULL /* Wed */:
|
|
dt->weekday = 3;
|
|
break;
|
|
case 2309337100958552554ULL /* Thu */:
|
|
dt->weekday = 4;
|
|
break;
|
|
case 9175942417219430151ULL /* Fri */:
|
|
dt->weekday = 5;
|
|
break;
|
|
case 14299747937899091273ULL /* Sat */:
|
|
dt->weekday = 6;
|
|
break;
|
|
default:
|
|
return 0;
|
|
}
|
|
|
|
dt->set |= VW_DATE_TIME_SET_WEEKDAY;
|
|
s += 3;
|
|
|
|
break;
|
|
}
|
|
|
|
case 'w': {
|
|
const int weekday = vw_DateTime_matchn_parse_digits(src, &s, src_len, 1);
|
|
if (weekday < 0 || weekday > 6) {
|
|
return 0;
|
|
}
|
|
dt->weekday = (uint8_t)weekday;
|
|
dt->set |= VW_DATE_TIME_SET_WEEKDAY;
|
|
break;
|
|
}
|
|
|
|
case 'W': {
|
|
const int yearweek = vw_DateTime_matchn_parse_digits(src, &s, src_len, 2);
|
|
if (yearweek < 0 || yearweek > 53) {
|
|
return 0;
|
|
}
|
|
dt->yearweek = (uint8_t)yearweek;
|
|
dt->set |= VW_DATE_TIME_SET_YEARWEEK;
|
|
break;
|
|
}
|
|
|
|
case 'Z': {
|
|
if (s >= src_len) {
|
|
return 0;
|
|
}
|
|
|
|
const size_t start = s;
|
|
while (s - start < 4 && src[s] >= 'A' && src[s] <= 'Z') {
|
|
++s;
|
|
}
|
|
const size_t len = s - start;
|
|
|
|
const char *tz = &src[s - len];
|
|
const uint64_t hash = vs_switch_hashn(tz, len);
|
|
|
|
switch (hash) {
|
|
case 5400647078792755728ULL /* ACDT */:
|
|
dt->offset = 10.5;
|
|
break;
|
|
case 4766677935472667375ULL /* ACST */:
|
|
dt->offset = 9.5;
|
|
break;
|
|
case 2103978029288782776ULL /* ACT */:
|
|
dt->offset = -5.0;
|
|
break;
|
|
case 7370160732139184342ULL /* AEDT */:
|
|
dt->offset = 11.0;
|
|
break;
|
|
case 9967661272838146170ULL /* AEST */:
|
|
dt->offset = 10.0;
|
|
break;
|
|
case 10106460371877471795ULL /* AKDT */:
|
|
dt->offset = -8.0;
|
|
break;
|
|
case 2628000161117006295ULL /* AKST */:
|
|
dt->offset = -9.0;
|
|
break;
|
|
case 13964713882942732942ULL /* ART */:
|
|
dt->offset = -3.0;
|
|
break;
|
|
case 6186762422267593169ULL /* AST */:
|
|
dt->offset = +3.0;
|
|
break;
|
|
case 18265226512131121940ULL /* AWST */:
|
|
dt->offset = 8.0;
|
|
break;
|
|
case 777586831009781693ULL /* BST */:
|
|
dt->offset = 1.0;
|
|
break;
|
|
case 11147673027240901128ULL /* CDT */:
|
|
dt->offset = -5.0;
|
|
break;
|
|
case 10256117318433937095ULL /* CET */:
|
|
dt->offset = 1.0;
|
|
break;
|
|
case 4107056610611744234ULL /* CEST */:
|
|
dt->offset = 2.0;
|
|
break;
|
|
case 13777264060303488607ULL /* CST */:
|
|
dt->offset = -6.0;
|
|
break;
|
|
case 6856334708055307450ULL /* EET */:
|
|
dt->offset = 2.0;
|
|
break;
|
|
case 18305502196204186102ULL /* EST */:
|
|
dt->offset = -5.0;
|
|
break;
|
|
case 5811218158441837705ULL /* EEST */:
|
|
dt->offset = +3.0;
|
|
break;
|
|
case 17094731644321730344ULL /* GMT */:
|
|
dt->offset = 0.0;
|
|
break;
|
|
case 13438807085359815380ULL /* HKT */:
|
|
dt->offset = 8.0;
|
|
break;
|
|
case 13009103050995326270ULL /* IST */:
|
|
dt->offset = 5.5;
|
|
break;
|
|
case 6807014158321330431ULL /* JST */:
|
|
dt->offset = 9.0;
|
|
break;
|
|
case 9256148920970304684ULL /* MDT */:
|
|
dt->offset = -6.0;
|
|
break;
|
|
case 685932710232085380ULL /* MST */:
|
|
dt->offset = 8.0;
|
|
break;
|
|
case 4660469000854508473ULL /* PDT */:
|
|
dt->offset = -7.0;
|
|
break;
|
|
case 10135139478756120134ULL /* PST */:
|
|
dt->offset = -8.0;
|
|
break;
|
|
case 12852326507233345576ULL /* SGT */:
|
|
dt->offset = 8.0;
|
|
break;
|
|
case 12512111609108849081ULL /* WET */:
|
|
dt->offset = 0.0;
|
|
break;
|
|
default:
|
|
return 0;
|
|
}
|
|
|
|
dt->set |= VW_DATE_TIME_SET_OFFSET;
|
|
break;
|
|
}
|
|
|
|
case 'z': {
|
|
if (s >= src_len) {
|
|
return 0;
|
|
}
|
|
const char sign = src[s++];
|
|
if (sign != '+' && sign != '-') {
|
|
return 0;
|
|
}
|
|
|
|
const int hrs = vw_DateTime_matchn_parse_digits(src, &s, src_len, 2);
|
|
const int mins = vw_DateTime_matchn_parse_digits(src, &s, src_len, 2);
|
|
|
|
if (hrs < 0 || hrs > 23 || mins < 0 || hrs > 59) {
|
|
return 0;
|
|
}
|
|
|
|
dt->offset = (sign == '+' ? 1 : -1) * ((float)(hrs + mins) / 60.0F);
|
|
|
|
dt->set |= VW_DATE_TIME_SET_OFFSET;
|
|
break;
|
|
}
|
|
|
|
case '%': {
|
|
if (src[s++] != '%') {
|
|
return 0;
|
|
}
|
|
break;
|
|
}
|
|
|
|
default:
|
|
return 0;
|
|
}
|
|
|
|
matched = s;
|
|
}
|
|
|
|
if ((dt->set | VW_DATE_TIME_SET_YEARDAY) && dt->yearday == 366 &&
|
|
(dt->set | VW_DATE_TIME_SET_YEAR) && !vw_DateTime_matchn_is_leap_year(dt->year)) {
|
|
return false;
|
|
}
|
|
|
|
return (p == pat_len) ? matched : 0;
|
|
}
|
|
|
|
static inline uint8_t vw_DateTime_adjust_offset_days_in_month(const uint8_t month,
|
|
const uint16_t year) {
|
|
static const uint8_t days[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
|
|
if (month < 1 || month > 12) {
|
|
return 0;
|
|
}
|
|
return (uint8_t)(days[month - 1] + (month == 2 && vw_DateTime_matchn_is_leap_year(year)));
|
|
}
|
|
|
|
static inline void vw_DateTime_adjust_offset_adjust_date(vw_DateTime *dt, ssize_t days) {
|
|
if (days == 0) {
|
|
return;
|
|
}
|
|
|
|
ssize_t dir = days > 0 ? 1 : -1;
|
|
days = days > 0 ? days : -days;
|
|
|
|
while (days--) {
|
|
if (dir == 1) {
|
|
if (++dt->day > vw_DateTime_adjust_offset_days_in_month(dt->month, dt->year)) {
|
|
dt->day = 1;
|
|
if (++dt->month > 12) {
|
|
dt->month = 1;
|
|
dt->year++;
|
|
}
|
|
}
|
|
} else {
|
|
if (--dt->day < 1) {
|
|
if (--dt->month < 1) {
|
|
dt->month = 12;
|
|
dt->year--;
|
|
}
|
|
dt->day = vw_DateTime_adjust_offset_days_in_month(dt->month, dt->year);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
bool vw_DateTime_adjust_offset(vw_DateTime *dt, const float new_offset) {
|
|
if (new_offset < -14.0F || new_offset > 14.0F) {
|
|
return false;
|
|
}
|
|
|
|
const float delta = new_offset - dt->offset;
|
|
const ssize_t delta_seconds = (ssize_t)(delta * 3600.0F);
|
|
|
|
ssize_t total_sec = (dt->hour * 3600) + (dt->minute * 60) + dt->second;
|
|
total_sec += delta_seconds;
|
|
|
|
ssize_t days_delta = total_sec / 86400;
|
|
ssize_t remaining = total_sec % 86400;
|
|
if (remaining < 0) {
|
|
remaining += 86400;
|
|
--days_delta;
|
|
}
|
|
|
|
dt->second = (uint8_t)(remaining % 60);
|
|
remaining /= 60;
|
|
dt->minute = (uint8_t)(remaining % 60);
|
|
dt->hour = (uint8_t)(remaining / 60);
|
|
|
|
if (days_delta != 0) {
|
|
vw_DateTime_adjust_offset_adjust_date(dt, days_delta);
|
|
}
|
|
|
|
dt->offset = new_offset;
|
|
return true;
|
|
}
|
|
|
|
static inline bool
|
|
vw_IntRange_parse_int_only(vw_IntRange *out, const char *end, const char **pptr) {
|
|
const char *ptr = *pptr;
|
|
|
|
if (!*ptr) {
|
|
return false;
|
|
}
|
|
|
|
int8_t sign = 1;
|
|
|
|
if (*ptr == '-') {
|
|
sign = -1;
|
|
++ptr;
|
|
} else if (*ptr == '+') {
|
|
++ptr;
|
|
}
|
|
|
|
intmax_t value = 0;
|
|
bool has_digits = false;
|
|
|
|
while (ptr < end && *ptr >= '0' && *ptr <= '9') {
|
|
value = value * 10 + (*ptr - '0');
|
|
++ptr;
|
|
has_digits = true;
|
|
}
|
|
|
|
if (!has_digits) {
|
|
return false; /* Invalid number */
|
|
}
|
|
|
|
value *= sign;
|
|
out->a = out->b = value;
|
|
out->set |= VW_INT_RANGE_A | VW_INT_RANGE_B;
|
|
|
|
*pptr = ptr;
|
|
return ptr == end; /* We should be done */
|
|
}
|
|
|
|
static inline bool vw_IntRange_parse_step(vw_IntRange *out, const char *end, const char **pptr) {
|
|
const char *ptr = *pptr;
|
|
|
|
if (*ptr != '/') {
|
|
return true;
|
|
}
|
|
++ptr;
|
|
if (ptr >= end) {
|
|
return false; /* Invalid step */
|
|
}
|
|
|
|
/* Spaces after / */
|
|
if (!*ptr) {
|
|
return false;
|
|
}
|
|
|
|
if (*ptr == '+') {
|
|
++ptr;
|
|
}
|
|
if (ptr >= end) {
|
|
return false; /* Invalid number */
|
|
}
|
|
|
|
intmax_t step = 0;
|
|
bool has_digits = false;
|
|
|
|
while (ptr < end && *ptr >= '0' && *ptr <= '9') {
|
|
step = step * 10 + (*ptr - '0');
|
|
++ptr;
|
|
has_digits = true;
|
|
}
|
|
|
|
if (!has_digits || step == 0) {
|
|
return false; /* Invalid step */
|
|
}
|
|
|
|
out->step = step;
|
|
out->set |= VW_INT_RANGE_STEP;
|
|
|
|
*pptr = ptr;
|
|
return ptr <= end;
|
|
}
|
|
|
|
static inline bool vw_IntRange_parse_a(vw_IntRange *out, const char *end, const char **pptr) {
|
|
const char *ptr = *pptr;
|
|
|
|
/* Check if not :b */
|
|
if (*ptr == ':') {
|
|
return true;
|
|
}
|
|
|
|
int8_t sign = 1;
|
|
if (*ptr == '-') {
|
|
sign = -1;
|
|
++ptr;
|
|
} else if (*ptr == '+') {
|
|
++ptr;
|
|
}
|
|
|
|
if (ptr >= end) {
|
|
return false; /* Invalid number */
|
|
}
|
|
|
|
intmax_t a = 0;
|
|
bool has_digits = false;
|
|
|
|
while (ptr < end && *ptr >= '0' && *ptr <= '9') {
|
|
a = a * 10 + (*ptr - '0');
|
|
++ptr;
|
|
has_digits = true;
|
|
}
|
|
|
|
if (!has_digits) {
|
|
return false; /* Invalid a */
|
|
}
|
|
|
|
out->a = sign * a;
|
|
out->set |= VW_INT_RANGE_A;
|
|
|
|
*pptr = ptr;
|
|
return ptr <= end;
|
|
}
|
|
|
|
static inline bool vw_IntRange_parse_b(vw_IntRange *out, const char *end, const char **pptr) {
|
|
const char *ptr = *pptr;
|
|
|
|
if (ptr > end || !*ptr || *ptr == '/') {
|
|
return true;
|
|
}
|
|
|
|
int8_t sign = 1;
|
|
if (*ptr == '-') {
|
|
sign = -1;
|
|
++ptr;
|
|
} else if (*ptr == '+') {
|
|
++ptr;
|
|
}
|
|
|
|
if (ptr >= end) {
|
|
return false; /* Invalid number */
|
|
}
|
|
|
|
intmax_t b = 0;
|
|
bool has_digits = false;
|
|
|
|
while (ptr < end && *ptr >= '0' && *ptr <= '9') {
|
|
b = b * 10 + (*ptr - '0');
|
|
++ptr;
|
|
has_digits = true;
|
|
}
|
|
|
|
if (!has_digits) {
|
|
return false; /* Invalid b */
|
|
}
|
|
|
|
if (out->set & VW_INT_RANGE_A) {
|
|
if (out->a > b) {
|
|
return false; /* Invalid range: a cannot be larger than b */
|
|
}
|
|
|
|
if ((out->set & VW_INT_RANGE_STEP) && out->a % out->step != 0) {
|
|
const intmax_t n = (out->a + (out->step - (out->a % out->step)));
|
|
|
|
if (n > b) {
|
|
return false; /* Invalid range: No such multiple in range */
|
|
}
|
|
}
|
|
}
|
|
|
|
out->b = sign * b;
|
|
out->set |= VW_INT_RANGE_B;
|
|
|
|
*pptr = ptr;
|
|
return ptr <= end;
|
|
}
|
|
|
|
bool vw_IntRange_parse(vw_IntRange *out, const char *range, size_t len) {
|
|
if (!range || !out) {
|
|
return false;
|
|
}
|
|
|
|
/* Values for : */
|
|
out->a = 0;
|
|
out->b = 0;
|
|
out->step = 1;
|
|
out->set = VW_INT_RANGE_NONE;
|
|
|
|
if (!*range || len == 0) {
|
|
return true;
|
|
}
|
|
|
|
const char *ptr = range;
|
|
const char *end = ptr + len;
|
|
|
|
if (ptr >= end) {
|
|
return true;
|
|
}
|
|
|
|
/* Check if the string is just an integer */
|
|
if (vs_strnintonly(ptr, (size_t)(end - ptr)) && !vw_IntRange_parse_int_only(out, end, &ptr)) {
|
|
return false;
|
|
}
|
|
if (ptr >= end) {
|
|
return true;
|
|
}
|
|
|
|
/* /step (only) */
|
|
if (ptr + 1 < end && *ptr == '/' && vs_strnintonly(ptr + 1, (size_t)(end - ptr - 1)) &&
|
|
!vw_IntRange_parse_step(out, end, &ptr)) {
|
|
return false;
|
|
}
|
|
if (ptr >= end) {
|
|
return true;
|
|
}
|
|
|
|
/* a */
|
|
if (!vw_IntRange_parse_a(out, end, &ptr)) {
|
|
return false;
|
|
}
|
|
if (ptr >= end) {
|
|
return true;
|
|
}
|
|
|
|
/* : */
|
|
if (*ptr == ':') {
|
|
ptr += 1;
|
|
} else {
|
|
return false; /* Invalid format */
|
|
}
|
|
if (ptr >= end) {
|
|
return true;
|
|
}
|
|
|
|
/* b */
|
|
if (!vw_IntRange_parse_b(out, end, &ptr)) {
|
|
return false;
|
|
}
|
|
if (ptr >= end) {
|
|
return true;
|
|
}
|
|
|
|
/* /step (after a:b) */
|
|
if (ptr + 1 < end && vs_strnintonly(ptr + 1, (size_t)(end - ptr - 1)) &&
|
|
!vw_IntRange_parse_step(out, end, &ptr)) {
|
|
return false;
|
|
}
|
|
|
|
/* We should be done... */
|
|
return ptr == end;
|
|
}
|
|
|
|
static inline void vw_IntRange_print_raw(const vw_IntRange *range) {
|
|
const bool a_set = range->set & VW_INT_RANGE_A;
|
|
const bool b_set = range->set & VW_INT_RANGE_B;
|
|
const bool step_set = range->set & VW_INT_RANGE_STEP;
|
|
|
|
/* Handle special case for "" */
|
|
if (!a_set && !b_set && !step_set) {
|
|
return;
|
|
}
|
|
|
|
/* Handle special case for "/step" (instead of ":/step") */
|
|
if (step_set && !a_set && !b_set) {
|
|
printf("/%ju", (uintmax_t)range->step);
|
|
return;
|
|
}
|
|
|
|
/* Print a/b components */
|
|
if (a_set && b_set) {
|
|
if (range->a == range->b) {
|
|
printf("%jd", range->a);
|
|
} else {
|
|
printf("%jd:%jd", range->a, range->b);
|
|
}
|
|
} else if (a_set) {
|
|
printf("%jd:", range->a);
|
|
} else if (b_set) {
|
|
printf(":%jd", range->b);
|
|
} else {
|
|
fputs(":", stdout);
|
|
}
|
|
|
|
/* Add step if present */
|
|
if (step_set) {
|
|
printf("/%ju", (uintmax_t)range->step);
|
|
}
|
|
}
|
|
|
|
bool vw_IntRange_print(const vw_IntRange *range) {
|
|
if (!range) {
|
|
return false;
|
|
}
|
|
|
|
vw_IntRange_print_raw(range);
|
|
putchar('\n');
|
|
|
|
return true;
|
|
}
|
|
|
|
bool vw_IntRange_inrange(const vw_IntRange *range, const intmax_t n) {
|
|
if (!range) {
|
|
return false;
|
|
}
|
|
|
|
/* a: (from a) */
|
|
if ((range->set & VW_INT_RANGE_A) && n < range->a) {
|
|
return false;
|
|
}
|
|
|
|
/* :b (up to b) */
|
|
if ((range->set & VW_INT_RANGE_B) && n > range->b) {
|
|
return false;
|
|
}
|
|
|
|
/* /step (on step) */
|
|
if ((range->set & VW_INT_RANGE_STEP)) {
|
|
if ((range->set & VW_INT_RANGE_A)) {
|
|
/* Ensure n is of the form a + k * step */
|
|
if ((n - range->a) % range->step != 0) {
|
|
return false;
|
|
}
|
|
} else {
|
|
/* If no start is defined, just check divisibility */
|
|
if (n % range->step != 0) {
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool vw_IntRange_indrange(const vw_IntRange *range, const double n) {
|
|
if (!range) {
|
|
return false;
|
|
}
|
|
|
|
/* a: (from a) */
|
|
if ((range->set & VW_INT_RANGE_A) && n < (double)range->a) {
|
|
return false;
|
|
}
|
|
|
|
/* :b (up to b) */
|
|
if ((range->set & VW_INT_RANGE_B) && n > (double)range->b) {
|
|
return false;
|
|
}
|
|
|
|
/* /step (on step) */
|
|
if ((range->set & VW_INT_RANGE_STEP)) {
|
|
if ((range->set & VW_INT_RANGE_A)) {
|
|
/* Ensure n is of the form a + k * step */
|
|
if (vs_double_mod((double)(n - (double)range->a), (double)range->step) != 0) {
|
|
return false;
|
|
}
|
|
} else {
|
|
/* If no start is defined, just check divisibility */
|
|
if (vs_double_mod((double)n, (double)range->step) != 0) {
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool vw_IntRange_expand(const vw_IntRange *range, intmax_t *min, intmax_t *max, intmax_t *step) {
|
|
if (!range) {
|
|
return false;
|
|
}
|
|
|
|
if (min) {
|
|
*min = (range->set & VW_INT_RANGE_A) ? range->a : INTMAX_MIN;
|
|
}
|
|
|
|
if (max) {
|
|
*max = (range->set & VW_INT_RANGE_B) ? range->b : INTMAX_MAX;
|
|
}
|
|
|
|
if (step) {
|
|
*step = (range->set & VW_INT_RANGE_STEP) ? range->step : 1;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool vw_BoolList_init(vw_BoolList *list) {
|
|
if (!list) {
|
|
return false;
|
|
}
|
|
return vs_HMap_init_unsafe(&list->bools);
|
|
}
|
|
|
|
static inline bool vw_BoolList_valid_char(const char c) {
|
|
/* a-z, 0-9, -, _, @ */
|
|
return (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || c == '-' || c == '_' || c == '@';
|
|
}
|
|
|
|
bool vw_BoolList_parse(vw_BoolList *out, const char *list, size_t len) {
|
|
if (!out || !list || !*list || len == 0) {
|
|
return false;
|
|
}
|
|
|
|
bool seen_slash = false;
|
|
bool is_truthy = true;
|
|
|
|
bool in_token = false;
|
|
size_t token_start = 0;
|
|
size_t token_len = 0;
|
|
|
|
size_t idx = 0;
|
|
|
|
for (idx = 0; idx <= len; ++idx) {
|
|
const char c = list[idx];
|
|
|
|
if (!(c == ' ' || c == '/' || c == '\0') && !vw_BoolList_valid_char(c)) {
|
|
return false;
|
|
}
|
|
|
|
if (c == ' ' || c == '/' || c == '\0') {
|
|
if (in_token) {
|
|
if (vs_HMap_findn(&out->bools, &list[token_start], token_len) != NULL) {
|
|
return false;
|
|
}
|
|
|
|
vs_HMap_insertn(
|
|
&out->bools, &list[token_start], token_len, is_truthy ? (void *)1 : (void *)0);
|
|
|
|
in_token = false;
|
|
}
|
|
|
|
if (c == '/') {
|
|
if (seen_slash) {
|
|
return false;
|
|
}
|
|
|
|
seen_slash = true;
|
|
is_truthy = false;
|
|
}
|
|
} else {
|
|
if (!in_token) {
|
|
token_start = idx;
|
|
token_len = 0;
|
|
in_token = true;
|
|
}
|
|
|
|
++token_len;
|
|
}
|
|
|
|
if (c == '\0') {
|
|
break;
|
|
}
|
|
}
|
|
|
|
return idx == len;
|
|
}
|
|
|
|
vw_BoolStatus vw_BoolList_find(const vw_BoolList *list, const char *str, size_t len) {
|
|
if (!list || !str || !*str || len == 0) {
|
|
return vw_BoolStatus_not_bool;
|
|
}
|
|
|
|
const vs_HMapEntry *b = vs_HMap_findn_unsafe(&list->bools, str, len);
|
|
|
|
if (!b) {
|
|
return vw_BoolStatus_not_bool;
|
|
}
|
|
|
|
switch ((uintptr_t)b->value) {
|
|
case 0:
|
|
return vw_BoolStatus_false;
|
|
case 1:
|
|
return vw_BoolStatus_true;
|
|
default:
|
|
return vw_BoolStatus_not_bool;
|
|
}
|
|
}
|
|
|
|
bool vw_BoolList_destroy(vw_BoolList *list) { return list && vs_HMap_destroy(&list->bools); }
|
|
|
|
static inline void vw_BoolList_print_raw(const vw_BoolList *list) {
|
|
bool slash_printed = false;
|
|
|
|
for (size_t idx = 0; idx < list->bools.size; ++idx) {
|
|
const vs_HMapEntry *b = list->bools.occupied_ents[idx];
|
|
|
|
if (!slash_printed && (uintptr_t)b->value == 0) {
|
|
fputs("/ ", stdout);
|
|
slash_printed = true;
|
|
}
|
|
|
|
if (idx + 1 == list->bools.size) {
|
|
printf("%s", (const char *)b->key);
|
|
} else {
|
|
printf("%s ", (const char *)b->key);
|
|
}
|
|
}
|
|
}
|
|
|
|
bool vw_BoolList_print(const vw_BoolList *list) {
|
|
if (!list) {
|
|
return false;
|
|
}
|
|
|
|
vw_BoolList_print_raw(list);
|
|
putchar('\n');
|
|
|
|
return true;
|
|
}
|
|
|
|
bool vw_PathType_builtin_int_consume(const vw_PathSource *src, size_t *size, size_t *reqb) {
|
|
size_t idx = 0;
|
|
while (idx < src->len && src->src[idx] >= '0' && src->src[idx] <= '9') {
|
|
++idx;
|
|
}
|
|
|
|
*size = idx;
|
|
*reqb = sizeof(intmax_t);
|
|
if (src->full && idx != src->len) {
|
|
return false;
|
|
}
|
|
|
|
if (src->seg->compiled) {
|
|
const intmax_t n = vs_strn2max(src->src, idx);
|
|
if (!vw_IntRange_inrange(src->seg->compiled, n)) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool vw_PathType_builtin_int_convert(const vw_PathSource *src, size_t reqb, void *out) {
|
|
if (reqb != sizeof(intmax_t)) {
|
|
return false;
|
|
}
|
|
|
|
intmax_t *final = (intmax_t *)out;
|
|
*final = vs_strn2max(src->src, src->len);
|
|
return true;
|
|
}
|
|
|
|
bool vw_PathType_builtin_int_compile(const vw_ArgSource *arg, void **comp) {
|
|
vw_IntRange *range = VS_MALLOC(sizeof(*range));
|
|
|
|
if (!range) {
|
|
return false;
|
|
}
|
|
|
|
if (!vw_IntRange_parse(range, arg->src, arg->len)) {
|
|
VS_FREE(range);
|
|
return false;
|
|
}
|
|
|
|
*comp = range;
|
|
return true;
|
|
}
|
|
|
|
bool vw_PathType_builtin_int_cleanup(void *comp) {
|
|
VS_FREE(comp);
|
|
return true;
|
|
}
|
|
|
|
bool vw_PathType_builtin_int_print(const void *comp) {
|
|
vw_IntRange_print_raw((const vw_IntRange *)comp);
|
|
return true;
|
|
}
|
|
|
|
bool vw_PathType_builtin_str_compile(const vw_ArgSource *arg, void **comp) {
|
|
vw_IntRange *range = VS_MALLOC(sizeof(*range));
|
|
|
|
if (!range) {
|
|
return false;
|
|
}
|
|
|
|
if (!vw_IntRange_parse(range, arg->src, arg->len) || vw_IntRange_inrange(range, 0)) {
|
|
VS_FREE(range);
|
|
return false;
|
|
}
|
|
|
|
*comp = range;
|
|
return true;
|
|
}
|
|
|
|
bool vw_PathType_builtin_str_consume(const vw_PathSource *src, size_t *size, size_t *reqb) {
|
|
size_t idx = 0;
|
|
while (idx < src->len && src->src[idx] != '\0' && src->src[idx] != '/') {
|
|
++idx;
|
|
}
|
|
|
|
*size = idx;
|
|
*reqb = idx;
|
|
if (src->full && idx != src->len) {
|
|
return false;
|
|
}
|
|
|
|
if (src->seg->compiled) {
|
|
if (!vw_IntRange_inrange(src->seg->compiled, (intmax_t)idx)) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool vw_PathType_builtin_str_cleanup(void *comp) {
|
|
VS_FREE(comp);
|
|
return true;
|
|
}
|
|
|
|
bool vw_PathType_builtin_str_print(const void *comp) {
|
|
vw_IntRange_print_raw((const vw_IntRange *)comp);
|
|
return true;
|
|
}
|
|
|
|
bool vw_PathType_builtin_path_consume(const vw_PathSource *src, size_t *size, size_t *reqb) {
|
|
const vw_IntRange *length = src->seg->compiled;
|
|
|
|
*size = src->len;
|
|
*reqb = src->len;
|
|
|
|
if (length && !vw_IntRange_inrange(length, (intmax_t)src->len)) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool vw_PathType_builtin_float_consume(const vw_PathSource *src, size_t *size, size_t *reqb) {
|
|
size_t idx = 0;
|
|
while (idx < src->len && src->src[idx] >= '0' && src->src[idx] <= '9') {
|
|
++idx;
|
|
}
|
|
if (idx < src->len && src->src[idx] == '.') {
|
|
++idx;
|
|
}
|
|
while (idx < src->len && src->src[idx] >= '0' && src->src[idx] <= '9') {
|
|
++idx;
|
|
}
|
|
|
|
*size = idx;
|
|
*reqb = sizeof(double);
|
|
if (src->full && idx != src->len) {
|
|
return false;
|
|
}
|
|
|
|
if (src->seg->compiled) {
|
|
const double n = vs_strn2double(src->src, idx, false);
|
|
if (!vw_IntRange_indrange(src->seg->compiled, n)) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool vw_PathType_builtin_float_convert(const vw_PathSource *src, size_t reqb, void *out) {
|
|
if (reqb != sizeof(double)) {
|
|
return false;
|
|
}
|
|
|
|
const double final = vs_strn2double(src->src, src->len, false);
|
|
|
|
if (final < VS_DNULL && *src->src != '0') {
|
|
return false;
|
|
}
|
|
|
|
*(double *)out = final;
|
|
return true;
|
|
}
|
|
|
|
bool vw_PathType_builtin_double_consume(const vw_PathSource *src, size_t *size, size_t *reqb) {
|
|
size_t idx = 0;
|
|
while (idx < src->len && src->src[idx] >= '0' && src->src[idx] <= '9') {
|
|
++idx;
|
|
}
|
|
if (idx >= src->len || src->src[idx] != '.') {
|
|
*size = idx;
|
|
return false;
|
|
}
|
|
++idx;
|
|
while (idx < src->len && src->src[idx] >= '0' && src->src[idx] <= '9') {
|
|
++idx;
|
|
}
|
|
|
|
*size = idx;
|
|
*reqb = sizeof(double);
|
|
|
|
if (src->full && idx != src->len) {
|
|
return false;
|
|
}
|
|
|
|
if (src->seg->compiled) {
|
|
const double n = vs_strn2double(src->src, idx, true);
|
|
if (!vw_IntRange_indrange(src->seg->compiled, n)) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool vw_PathType_builtin_double_convert(const vw_PathSource *src, size_t reqb, void *out) {
|
|
if (reqb != sizeof(double)) {
|
|
return false;
|
|
}
|
|
|
|
const double final = vs_strn2double(src->src, src->len, true);
|
|
|
|
if (final < VS_DNULL && *src->src != '0') {
|
|
return false;
|
|
}
|
|
|
|
*(double *)out = final;
|
|
return true;
|
|
}
|
|
|
|
bool vw_PathType_builtin_bool_compile(const vw_ArgSource *arg, void **comp) {
|
|
vw_BoolList *bl = VS_CALLOC(1, sizeof(*bl));
|
|
|
|
if (!bl) {
|
|
return false;
|
|
}
|
|
|
|
if (!vw_BoolList_init(bl)) {
|
|
VS_FREE(bl);
|
|
return false;
|
|
}
|
|
|
|
if (arg->src && arg->len) {
|
|
if (!vw_BoolList_parse(bl, arg->src, arg->len)) {
|
|
vw_BoolList_destroy(bl);
|
|
VS_FREE(bl);
|
|
return false;
|
|
}
|
|
} else {
|
|
static const char default_bools[] = "true 1 yes up / false 0 no down";
|
|
|
|
if (!vw_BoolList_parse(bl, default_bools, VS_ARRLEN(default_bools) - 1)) {
|
|
vw_BoolList_destroy(bl);
|
|
VS_FREE(bl);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
*comp = bl;
|
|
return true;
|
|
}
|
|
|
|
bool vw_PathType_builtin_bool_consume(const vw_PathSource *src, size_t *size, size_t *reqb) {
|
|
size_t idx = 0;
|
|
|
|
while (idx < src->len && src->src[idx] != '\0' && src->src[idx] != '/' &&
|
|
vw_BoolList_valid_char(src->src[idx])) {
|
|
++idx;
|
|
}
|
|
|
|
*size = idx;
|
|
*reqb = sizeof(bool);
|
|
|
|
if (src->full && idx != src->len) {
|
|
return false;
|
|
}
|
|
|
|
return vw_BoolList_find(src->seg->compiled, src->src, idx) != vw_BoolStatus_not_bool;
|
|
}
|
|
|
|
bool vw_PathType_builtin_bool_convert(const vw_PathSource *src, size_t reqb, void *out) {
|
|
if (reqb != sizeof(bool)) {
|
|
return false;
|
|
}
|
|
|
|
*(bool *)out = (vw_BoolList_find(src->seg->compiled, src->src, src->len) == vw_BoolStatus_true);
|
|
|
|
return true;
|
|
}
|
|
|
|
bool vw_PathType_builtin_bool_cleanup(void *comp) {
|
|
vw_BoolList_destroy(comp);
|
|
VS_FREE(comp);
|
|
return false;
|
|
}
|
|
|
|
bool vw_PathType_builtin_bool_print(const void *comp) {
|
|
vw_BoolList_print_raw(comp);
|
|
return true;
|
|
}
|
|
|
|
bool vw_PathType_builtin_date_consume(const vw_PathSource *src, size_t *size, size_t *reqb) {
|
|
vw_DateTime dt = { 0 };
|
|
const vw_DateTimeState *dts = src->seg->compiled;
|
|
|
|
const size_t matched = vw_DateTime_matchn(&dt, dts->pat, dts->len, src->src, src->len);
|
|
|
|
*size = matched;
|
|
*reqb = sizeof(vw_DateTime);
|
|
|
|
if (src->full && matched != src->len) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool vw_PathType_builtin_date_convert(const vw_PathSource *src, size_t reqb, void *out) {
|
|
if (reqb != sizeof(vw_DateTime)) {
|
|
return false;
|
|
}
|
|
|
|
vw_DateTimeState *dts = src->seg->compiled;
|
|
return vw_DateTime_matchn(out, dts->pat, dts->len, src->src, src->len) == src->len;
|
|
}
|
|
|
|
bool vw_PathType_builtin_date_compile(const vw_ArgSource *arg, void **comp) {
|
|
vw_DateTimeState *dts = VS_CALLOC(1, sizeof(*dts));
|
|
if (!dts) {
|
|
return false;
|
|
}
|
|
|
|
if (arg->len && arg->src) {
|
|
dts->pat = VS_CALLOC(1, arg->len + 1);
|
|
if (!dts->pat) {
|
|
VS_FREE(dts);
|
|
return false;
|
|
}
|
|
|
|
dts->len = arg->len;
|
|
strncpy(dts->pat, arg->src, arg->len);
|
|
} else {
|
|
static const char default_date[] = "%Y-%m-%d";
|
|
|
|
dts->len = VS_ARRLEN(default_date) - 1;
|
|
|
|
dts->pat = VS_CALLOC(1, dts->len + 1);
|
|
if (!dts->pat) {
|
|
VS_FREE(dts);
|
|
return false;
|
|
}
|
|
|
|
strncpy(dts->pat, default_date, dts->len);
|
|
}
|
|
|
|
*comp = dts;
|
|
return true;
|
|
}
|
|
|
|
bool vw_PathType_builtin_date_cleanup(void *comp) {
|
|
vw_DateTimeState *dts = (vw_DateTimeState *)comp;
|
|
|
|
VS_FREE(dts->pat);
|
|
VS_FREE(dts);
|
|
|
|
return true;
|
|
}
|
|
|
|
bool vw_PathType_builtin_date_print(const void *comp) {
|
|
const vw_DateTimeState *dts = (const vw_DateTimeState *)comp;
|
|
printf("%*s", (unsigned)dts->len, dts->pat);
|
|
return true;
|
|
}
|
|
|
|
bool vw_PathType_builtin_uuid_consume(const vw_PathSource *src, size_t *size, size_t *reqb) {
|
|
static const uint8_t uuid_segment_lengths[5] = { 8, 4, 4, 4, 12 };
|
|
|
|
size_t idx = 0;
|
|
|
|
size_t segment_idx = 0;
|
|
size_t segment_length = uuid_segment_lengths[segment_idx];
|
|
|
|
if (src->full && src->len != 36) {
|
|
return false;
|
|
}
|
|
|
|
while (idx < src->len && src->src[idx] != '\0' && src->src[idx] != '/') {
|
|
if (segment_length == 0) {
|
|
if (src->src[idx] != '-') {
|
|
return false;
|
|
}
|
|
|
|
segment_length = uuid_segment_lengths[++segment_idx];
|
|
} else {
|
|
if (!((src->src[idx] >= '0' && src->src[idx] <= '9') ||
|
|
(src->src[idx] >= 'a' && src->src[idx] <= 'f') ||
|
|
(src->src[idx] >= 'A' && src->src[idx] <= 'F'))) {
|
|
return false;
|
|
}
|
|
|
|
--segment_length;
|
|
}
|
|
|
|
++idx;
|
|
}
|
|
|
|
*size = idx;
|
|
*reqb = idx;
|
|
|
|
if (src->full && idx != src->len) {
|
|
return false;
|
|
}
|
|
|
|
if (segment_idx != 4 || segment_length != 0 || idx != 36) {
|
|
return false;
|
|
}
|
|
|
|
if (src->seg->compiled) {
|
|
const char v = (char)(uintptr_t)src->seg->compiled;
|
|
|
|
if (src->src[14] != v) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool vw_PathType_builtin_uuid_compile(const vw_ArgSource *arg, void **comp) {
|
|
if (arg->src && arg->len <= 2) {
|
|
char c = 0;
|
|
|
|
if (arg->len == 1) {
|
|
c = arg->src[0];
|
|
} else if (*arg->src == 'v' && arg->len == 2) {
|
|
c = arg->src[1];
|
|
} else {
|
|
return false;
|
|
}
|
|
|
|
if (c < '1' || c > '8') {
|
|
return false;
|
|
}
|
|
|
|
*comp = VS_MALLOC(sizeof(char));
|
|
|
|
if (!*comp) {
|
|
return false;
|
|
}
|
|
|
|
*comp = (void *)(uintptr_t)c;
|
|
|
|
if (c) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool vw_PathType_builtin_uuid_print(const void *comp) {
|
|
putchar((char)(uintptr_t)comp);
|
|
return true;
|
|
}
|
|
|
|
bool vw_PathType_builtin_hex_consume(const vw_PathSource *src, size_t *size, size_t *reqb) {
|
|
size_t idx = 0;
|
|
|
|
while (idx < src->len && src->src[idx] != '\0' && isxdigit(src->src[idx])) {
|
|
++idx;
|
|
}
|
|
|
|
*size = idx;
|
|
*reqb = idx;
|
|
|
|
if (src->full && idx != src->len) {
|
|
return false;
|
|
}
|
|
|
|
if (src->seg->compiled) {
|
|
if (!vw_IntRange_inrange(src->seg->compiled, (intmax_t)idx)) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|