Add an RPN date manipulator
This commit is contained in:
parent
124df16711
commit
e59586b265
|
@ -25,9 +25,14 @@ then
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
|
case "${TEST}" in
|
||||||
|
obj/tests/manip) TEST_EXTRAS="-lm" ;;
|
||||||
|
*) TEST_EXTRAS="" ;;
|
||||||
|
esac
|
||||||
|
|
||||||
if [ ${MODIFIED} -ne 0 ]
|
if [ ${MODIFIED} -ne 0 ]
|
||||||
then
|
then
|
||||||
do_cmd ${CC} -Iobj ${CFLAGS} -o ${TEST} ${SRC} ${LIBS} ${EXTRAS} || return 1
|
do_cmd ${CC} -Iobj ${CFLAGS} -o ${TEST} ${SRC} ${LIBS} ${EXTRAS} ${TEST_EXTRAS} || return 1
|
||||||
print_success "Built ${TEST}"
|
print_success "Built ${TEST}"
|
||||||
else
|
else
|
||||||
print_success "${TEST} is up to date"
|
print_success "${TEST} is up to date"
|
||||||
|
|
|
@ -0,0 +1,299 @@
|
||||||
|
/* libiso8601/src/tests/manip.c
|
||||||
|
*
|
||||||
|
* (c)2007, Laurence Withers, <l@lwithers.me.uk>.
|
||||||
|
* Released under the GNU GPLv2. See file COPYING or
|
||||||
|
* http://www.gnu.org/copyleft/gpl.html for details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "iso8601.h"
|
||||||
|
|
||||||
|
#include <math.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* safe_strtod()
|
||||||
|
* Converts `str' into a double, which it will store in `out'. If an error occurs, returns -1,
|
||||||
|
* else 0 on success.
|
||||||
|
*/
|
||||||
|
int safe_strtod(const char* str, double* out)
|
||||||
|
{
|
||||||
|
char* endp = 0;
|
||||||
|
|
||||||
|
errno = 0;
|
||||||
|
*out = strtod(str, &endp);
|
||||||
|
if(errno || !endp || *endp) return -1;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* date_stack
|
||||||
|
* We use a stack of dates and/or periods to implement effectively an RPN manipulator.
|
||||||
|
*/
|
||||||
|
enum date_stack_type {
|
||||||
|
date_stack_type_date,
|
||||||
|
date_stack_type_per
|
||||||
|
};
|
||||||
|
|
||||||
|
struct date_stack {
|
||||||
|
enum date_stack_type type;
|
||||||
|
|
||||||
|
union {
|
||||||
|
struct {
|
||||||
|
struct iso8601_date d;
|
||||||
|
struct iso8601_details det;
|
||||||
|
}date;
|
||||||
|
|
||||||
|
struct iso8601_elapsed per;
|
||||||
|
}u;
|
||||||
|
|
||||||
|
struct date_stack* next;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct date_stack* date_stack_top = 0;
|
||||||
|
int date_stack_size = 0;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* date_stack_push_*()
|
||||||
|
* Routines for pushing an object onto the date stack.
|
||||||
|
*/
|
||||||
|
void date_stack_push_date(const struct iso8601_date* d, const struct iso8601_details* det)
|
||||||
|
{
|
||||||
|
struct date_stack* s;
|
||||||
|
|
||||||
|
s = malloc(sizeof(struct date_stack));
|
||||||
|
s->type = date_stack_type_date;
|
||||||
|
s->u.date.d = *d;
|
||||||
|
s->u.date.det = *det;
|
||||||
|
s->next = date_stack_top;
|
||||||
|
|
||||||
|
date_stack_top = s;
|
||||||
|
++date_stack_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
void date_stack_push_elapsed(const struct iso8601_elapsed* per)
|
||||||
|
{
|
||||||
|
struct date_stack* s;
|
||||||
|
|
||||||
|
s = malloc(sizeof(struct date_stack));
|
||||||
|
s->type = date_stack_type_per;
|
||||||
|
s->u.per = *per;
|
||||||
|
s->next = date_stack_top;
|
||||||
|
|
||||||
|
date_stack_top = s;
|
||||||
|
++date_stack_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* date_stack_pop_date()
|
||||||
|
* Pops a date from the stack, returning non-0 on error (underflow or type mismatch).
|
||||||
|
*/
|
||||||
|
int date_stack_pop_date(struct iso8601_date* d, struct iso8601_details* det)
|
||||||
|
{
|
||||||
|
struct date_stack* next;
|
||||||
|
|
||||||
|
if(!date_stack_top) {
|
||||||
|
fputs("Stack underflow.\n", stderr);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if(date_stack_top->type != date_stack_type_date) {
|
||||||
|
fputs("Type mismatch.\n", stderr);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
*d = date_stack_top->u.date.d;
|
||||||
|
*det = date_stack_top->u.date.det;
|
||||||
|
|
||||||
|
--date_stack_size;
|
||||||
|
next = date_stack_top->next;
|
||||||
|
free(date_stack_top);
|
||||||
|
date_stack_top = next;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* date_stack_pop_elapsed()
|
||||||
|
* Pops an elapsed period from the stack, returning non-0 on error (underflow or type mismatch).
|
||||||
|
*/
|
||||||
|
int date_stack_pop_elapsed(struct iso8601_elapsed* per)
|
||||||
|
{
|
||||||
|
struct date_stack* next;
|
||||||
|
|
||||||
|
if(!date_stack_top) {
|
||||||
|
fputs("Stack underflow.\n", stderr);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if(date_stack_top->type != date_stack_type_per) {
|
||||||
|
fputs("Type mismatch.\n", stderr);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
*per = date_stack_top->u.per;
|
||||||
|
|
||||||
|
--date_stack_size;
|
||||||
|
next = date_stack_top->next;
|
||||||
|
free(date_stack_top);
|
||||||
|
date_stack_top = next;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* usage()
|
||||||
|
* Displays help.
|
||||||
|
*/
|
||||||
|
void usage(void)
|
||||||
|
{
|
||||||
|
fputs("Push a date or elapsed time onto the stack by entering it, e.g.:\n"
|
||||||
|
" 2007-07-29 12345.67\n"
|
||||||
|
"Operators:\n"
|
||||||
|
" + (date, period) Advance date by elapsed time, push date.\n"
|
||||||
|
" - (date, period) Regress date by elapsed time, push date.\n"
|
||||||
|
" < (date, date) Prints 1 if first date less than second.\n"
|
||||||
|
" <= (date, date) Prints 1 if first date less than or equal to second.\n"
|
||||||
|
" == (date, date) Prints 1 if dates equal.\n"
|
||||||
|
" p (date) Prints a date.\n"
|
||||||
|
"\n"
|
||||||
|
"Type help for this screen at any point.\n"
|
||||||
|
"\n", stdout);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
int parse_command(char* cmd)
|
||||||
|
{
|
||||||
|
struct iso8601_date date, date2;
|
||||||
|
struct iso8601_details details, details2;
|
||||||
|
struct iso8601_elapsed period;
|
||||||
|
double period_d;
|
||||||
|
char date_str[40];
|
||||||
|
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
cmd = strtok(cmd, " \n");
|
||||||
|
while(cmd) {
|
||||||
|
if(!iso8601_parse(cmd, &date, 0, &details)) {
|
||||||
|
date_stack_push_date(&date, &details);
|
||||||
|
|
||||||
|
} else if(!safe_strtod(cmd, &period_d) && period_d >= 0) {
|
||||||
|
period.sec = trunc(period_d);
|
||||||
|
period.nsec = trunc((period_d - period.sec) * 1e9);
|
||||||
|
date_stack_push_elapsed(&period);
|
||||||
|
|
||||||
|
} else if(!strcmp(cmd, "+")) {
|
||||||
|
if(date_stack_pop_elapsed(&period) || date_stack_pop_date(&date, &details)) {
|
||||||
|
ret = 1;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
iso8601_add_elapsed(&date, &period);
|
||||||
|
date_stack_push_date(&date, &details);
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if(!strcmp(cmd, "-")) {
|
||||||
|
if(date_stack_pop_elapsed(&period) || date_stack_pop_date(&date, &details)) {
|
||||||
|
ret = 1;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
iso8601_subtract_elapsed(&date, &period);
|
||||||
|
date_stack_push_date(&date, &details);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if(!strcmp(cmd, "<")) {
|
||||||
|
if(date_stack_pop_date(&date, &details) || date_stack_pop_date(&date2, &details2)) {
|
||||||
|
ret = 1;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
printf("%d\n", iso8601_lt(&date2, &date));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if(!strcmp(cmd, "<=")) {
|
||||||
|
if(date_stack_pop_date(&date, &details) || date_stack_pop_date(&date2, &details2)) {
|
||||||
|
ret = 1;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
printf("%d\n", iso8601_lte(&date2, &date));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if(!strcmp(cmd, "==")) {
|
||||||
|
if(date_stack_pop_date(&date, &details) || date_stack_pop_date(&date2, &details2)) {
|
||||||
|
ret = 1;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
printf("%d\n", iso8601_eq(&date2, &date));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if(!strcmp(cmd, "p")) {
|
||||||
|
if(date_stack_pop_date(&date, &details)) {
|
||||||
|
ret = 1;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
iso8601_print(date_str, sizeof(date_str), &date, &details);
|
||||||
|
fputs(date_str, stdout);
|
||||||
|
putc('\n', stdout);
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
fputs("Unrecognised command or argument.\n", stderr);
|
||||||
|
ret = 1;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd = strtok(0, " \n");
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
int main(int argc, char* argv[])
|
||||||
|
{
|
||||||
|
int ret = 0;
|
||||||
|
char* buf = 0;
|
||||||
|
size_t buf_len = 0;
|
||||||
|
|
||||||
|
if(argc == 2 && !strcmp(argv[1], "--print-summary")) {
|
||||||
|
printf("Interactive date manipulator. Also evaluates commandline.\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* commandline -- evaluate arguments as though interactive */
|
||||||
|
if(argc > 1) {
|
||||||
|
for(ret = 1; ret < argc; ++ret) {
|
||||||
|
if(parse_command(argv[ret])) return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
usage();
|
||||||
|
while(!feof(stdin)) {
|
||||||
|
printf("%d >> ", date_stack_size);
|
||||||
|
ret = getline(&buf, &buf_len, stdin);
|
||||||
|
if(ret == -1) break;
|
||||||
|
|
||||||
|
parse_command(buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* options for text editors
|
||||||
|
kate: replace-trailing-space-save true; space-indent true; tab-width 4;
|
||||||
|
vim: expandtab:ts=4:sw=4
|
||||||
|
*/
|
Loading…
Reference in New Issue