Add an RPN date manipulator
This commit is contained in:
parent
124df16711
commit
e59586b265
|
@ -25,9 +25,14 @@ then
|
|||
fi
|
||||
done
|
||||
|
||||
case "${TEST}" in
|
||||
obj/tests/manip) TEST_EXTRAS="-lm" ;;
|
||||
*) TEST_EXTRAS="" ;;
|
||||
esac
|
||||
|
||||
if [ ${MODIFIED} -ne 0 ]
|
||||
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}"
|
||||
else
|
||||
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