diff --git a/CHANGES b/CHANGES index 4a2a3f1..d97da7b 100644 --- a/CHANGES +++ b/CHANGES @@ -3,6 +3,9 @@ add API for integer multiply with overflow detection change length counters from int to long for 64-bit platforms add array API from http://cr.yp.to/lib/array.html + oops, had a declaration and man page for taia_addsec but not the + function itself + add buffer functions to write strerror(errno) 0.15: man page update (document stralloc return values) diff --git a/array.h b/array.h index 0afbdc9..9241524 100644 --- a/array.h +++ b/array.h @@ -32,6 +32,6 @@ void array_cat0(array* to); void array_cate(array* to,const array* const from,int64 pos,int64 stop); #define array_failed(x) (array_bytes(x)==-1) -#define array_allocated(x) (array_bytes(x)==0) +#define array_unallocated(x) (array_bytes(x)==0) #endif diff --git a/buffer.h b/buffer.h index 04721e2..f0f7a0e 100644 --- a/buffer.h +++ b/buffer.h @@ -15,18 +15,18 @@ typedef struct buffer { #define BUFFER_INSIZE 8192 #define BUFFER_OUTSIZE 8192 -extern void buffer_init(buffer* b,int (*op)(),int fd,char* y,unsigned int ylen); +void buffer_init(buffer* b,int (*op)(),int fd,char* y,unsigned int ylen); -extern int buffer_flush(buffer* b); -extern int buffer_put(buffer* b,const char* x,unsigned int len); -extern int buffer_putalign(buffer* b,const char* x,unsigned int len); -extern int buffer_putflush(buffer* b,const char* x,unsigned int len); -extern int buffer_puts(buffer* b,const char* x); -extern int buffer_putsalign(buffer* b,const char* x); -extern int buffer_putsflush(buffer* b,const char* x); +int buffer_flush(buffer* b); +int buffer_put(buffer* b,const char* x,unsigned int len); +int buffer_putalign(buffer* b,const char* x,unsigned int len); +int buffer_putflush(buffer* b,const char* x,unsigned int len); +int buffer_puts(buffer* b,const char* x); +int buffer_putsalign(buffer* b,const char* x); +int buffer_putsflush(buffer* b,const char* x); -extern int buffer_putspace(buffer* b); -extern int buffer_putnlflush(buffer* b); /* put \n and flush */ +int buffer_putspace(buffer* b); +int buffer_putnlflush(buffer* b); /* put \n and flush */ #define buffer_PUTC(s,c) \ ( ((s)->a != (s)->p) \ @@ -34,16 +34,16 @@ extern int buffer_putnlflush(buffer* b); /* put \n and flush */ : buffer_put((s),&(c),1) \ ) -extern int buffer_get(buffer* b,char* x,unsigned int len); -extern int buffer_feed(buffer* b); -extern int buffer_getc(buffer* b,char* x); -extern int buffer_getn(buffer* b,char* x,unsigned int len); +int buffer_get(buffer* b,char* x,unsigned int len); +int buffer_feed(buffer* b); +int buffer_getc(buffer* b,char* x); +int buffer_getn(buffer* b,char* x,unsigned int len); /* read bytes until the destination buffer is full (len bytes), end of * file is reached or the read char is in charset (setlen bytes). An * empty line when looking for \n will write '\n' to x and return 0. If * EOF is reached, \0 is written to the buffer */ -extern int buffer_get_token(buffer* b,char* x,unsigned int len,const char* charset,unsigned int setlen); +int buffer_get_token(buffer* b,char* x,unsigned int len,const char* charset,unsigned int setlen); #define buffer_getline(b,x,len) buffer_get_token((b),(x),(len),"\n",1) /* this predicate is given the string as currently read from the buffer @@ -51,10 +51,10 @@ extern int buffer_get_token(buffer* b,char* x,unsigned int len,const char* chars typedef int (*string_predicate)(const char* x,unsigned int len); /* like buffer_get_token but the token ends when your predicate says so */ -extern int buffer_get_token_pred(buffer* b,char* x,unsigned int len,string_predicate p); +int buffer_get_token_pred(buffer* b,char* x,unsigned int len,string_predicate p); -extern char *buffer_peek(buffer* b); -extern void buffer_seek(buffer* b,unsigned int len); +char *buffer_peek(buffer* b); +void buffer_seek(buffer* b,unsigned int len); #define buffer_PEEK(s) ( (s)->x + (s)->p ) #define buffer_SEEK(s,len) ( (s)->p += (len) ) @@ -65,22 +65,28 @@ extern void buffer_seek(buffer* b,unsigned int len); : buffer_get((s),(c),1) \ ) -extern int buffer_copy(buffer* out,buffer* in); +int buffer_copy(buffer* out,buffer* in); -extern int buffer_putulong(buffer *b,unsigned long l); -extern int buffer_put8long(buffer *b,unsigned long l); -extern int buffer_putxlong(buffer *b,unsigned long l); -extern int buffer_putlong(buffer *b,unsigned long l); +int buffer_putulong(buffer *b,unsigned long l); +int buffer_put8long(buffer *b,unsigned long l); +int buffer_putxlong(buffer *b,unsigned long l); +int buffer_putlong(buffer *b,signed long l); -extern buffer *buffer_0; -extern buffer *buffer_0small; -extern buffer *buffer_1; -extern buffer *buffer_1small; -extern buffer *buffer_2; +int buffer_putlonglong(buffer* b,signed long long l); +int buffer_putulonglong(buffer* b,unsigned long long l); + +int buffer_puterror(buffer* b); +int buffer_puterror2(buffer* b, int errnum); + +buffer *buffer_0; +buffer *buffer_0small; +buffer *buffer_1; +buffer *buffer_1small; +buffer *buffer_2; #ifdef STRALLOC_H /* write stralloc to buffer */ -extern int buffer_putsa(buffer* b,stralloc* sa); +int buffer_putsa(buffer* b,stralloc* sa); /* these "read token" functions return 0 if the token was complete or * EOF was hit or -1 on error. In contrast to the non-stralloc token @@ -94,14 +100,14 @@ extern int buffer_putsa(buffer* b,stralloc* sa); * data is available. */ /* read token from buffer to stralloc */ -extern int buffer_get_token_sa(buffer* b,stralloc* sa,const char* charset,unsigned int setlen); +int buffer_get_token_sa(buffer* b,stralloc* sa,const char* charset,unsigned int setlen); /* read line from buffer to stralloc */ -extern int buffer_getline_sa(buffer* b,stralloc* sa); +int buffer_getline_sa(buffer* b,stralloc* sa); typedef int (*sa_predicate)(stralloc* sa); /* like buffer_get_token_sa but the token ends when your predicate says so */ -extern int buffer_get_token_sa_pred(buffer* b,stralloc* sa,sa_predicate p); +int buffer_get_token_sa_pred(buffer* b,stralloc* sa,sa_predicate p); /* make a buffer from a stralloc. * Do not change the stralloc after this! */ diff --git a/buffer/buffer_puterror.3 b/buffer/buffer_puterror.3 new file mode 100644 index 0000000..a679537 --- /dev/null +++ b/buffer/buffer_puterror.3 @@ -0,0 +1,11 @@ +.TH buffer_puterror 3 +.SH NAME +buffer_puterror \- write error string to buffer and flush +.SH SYNTAX +.B #include + +int \fBbuffer_puterror\fP(buffer* \fIb\fR); +.SH DESCRIPTION +buffer_puterror is equivalent to calling buffer_puterror2 with errno. +.SH "SEE ALSO" +buffer_puterror2(3), buffer_puts(3), buffer_put(3), buffer_flush(3), buffer(3) diff --git a/buffer/buffer_puterror.c b/buffer/buffer_puterror.c new file mode 100644 index 0000000..35384ea --- /dev/null +++ b/buffer/buffer_puterror.c @@ -0,0 +1,7 @@ +#include "buffer.h" +#include +#include + +int buffer_puterror(buffer* b) { + return buffer_puts(b,strerror(errno)); +} diff --git a/buffer/buffer_puterror2.3 b/buffer/buffer_puterror2.3 new file mode 100644 index 0000000..3e0996d --- /dev/null +++ b/buffer/buffer_puterror2.3 @@ -0,0 +1,13 @@ +.TH buffer_puterror2 3 +.SH NAME +buffer_puterror2 \- write error string to buffer and flush +.SH SYNTAX +.B #include + +int \fBbuffer_puterror2\fP(buffer* \fIb\fR,int \fIerrnum\fR); +.SH DESCRIPTION +buffer_puterror2 writes the error message corresponding to the error +number in \fIerrnum\fR to the buffer (e.g. "No such file or directory" +for ENOENT). +.SH "SEE ALSO" +buffer_puterror(3), buffer_puts(3), buffer_put(3), buffer_flush(3), buffer(3) diff --git a/buffer/buffer_puterror2.c b/buffer/buffer_puterror2.c new file mode 100644 index 0000000..b8fb956 --- /dev/null +++ b/buffer/buffer_puterror2.c @@ -0,0 +1,6 @@ +#include "buffer.h" +#include + +int buffer_puterror2(buffer* b,int errnum) { + return buffer_puts(b,strerror(errnum)); +} diff --git a/buffer/buffer_putlong.3 b/buffer/buffer_putlong.3 index a4b7b9d..9e2e688 100644 --- a/buffer/buffer_putlong.3 +++ b/buffer/buffer_putlong.3 @@ -5,7 +5,7 @@ long integer to buffer .SH SYNTAX .B #include -int \fBbuffer_putlong\fP(buffer* \fIb\fR,unsigned long \fIx\fR); +int \fBbuffer_putlong\fP(buffer* \fIb\fR,signed long \fIx\fR); .SH DESCRIPTION buffer_putlong is similar to passing the result of fmt_long to buffer_put. diff --git a/buffer/buffer_putlong.c b/buffer/buffer_putlong.c index 1d11527..c622f34 100644 --- a/buffer/buffer_putlong.c +++ b/buffer/buffer_putlong.c @@ -1,8 +1,8 @@ #include "buffer.h" #include "fmt.h" -int buffer_putlong(buffer *b,unsigned long l) { - char buf[FMT_ULONG]; +int buffer_putlong(buffer *b,signed long l) { + char buf[FMT_LONG]; return buffer_put(b,buf,fmt_long(buf,l)); } diff --git a/buffer/buffer_putlonglong.3 b/buffer/buffer_putlonglong.3 new file mode 100644 index 0000000..d0fff3b --- /dev/null +++ b/buffer/buffer_putlonglong.3 @@ -0,0 +1,13 @@ +.TH buffer_putlonglong 3 +.SH NAME +buffer_putlonglong \- write a decimal ASCII representation of a signed +long integer to buffer +.SH SYNTAX +.B #include + +int \fBbuffer_putlonglong\fP(buffer* \fIb\fR,signed long long \fIx\fR); +.SH DESCRIPTION +buffer_putlonglong is equivalent to passing the result of fmt_longlong to +buffer_put. +.SH "SEE ALSO" +fmt_longlong(3), buffer_put(3), buffer_flush(3), buffer(3) diff --git a/buffer/buffer_putlonglong.c b/buffer/buffer_putlonglong.c new file mode 100644 index 0000000..efbcbfe --- /dev/null +++ b/buffer/buffer_putlonglong.c @@ -0,0 +1,8 @@ +#include "buffer.h" +#include "fmt.h" + +int buffer_putlonglong(buffer *b,signed long long l) { + char buf[FMT_LONG]; + return buffer_put(b,buf,fmt_longlong(buf,l)); +} + diff --git a/buffer/buffer_putulonglong.3 b/buffer/buffer_putulonglong.3 new file mode 100644 index 0000000..3c7c387 --- /dev/null +++ b/buffer/buffer_putulonglong.3 @@ -0,0 +1,13 @@ +.TH buffer_putulonglong 3 +.SH NAME +buffer_putulonglong \- write a decimal ASCII representation of a signed +long integer to buffer +.SH SYNTAX +.B #include + +int \fBbuffer_putulonglong\fP(buffer* \fIb\fR,unsigned long long \fIx\fR); +.SH DESCRIPTION +buffer_putulonglong is equivalent to passing the result of fmt_ulonglong to +buffer_put. +.SH "SEE ALSO" +fmt_ulonglong(3), buffer_put(3), buffer_flush(3), buffer(3) diff --git a/buffer/buffer_putulonglong.c b/buffer/buffer_putulonglong.c new file mode 100644 index 0000000..861acf5 --- /dev/null +++ b/buffer/buffer_putulonglong.c @@ -0,0 +1,8 @@ +#include "buffer.h" +#include "fmt.h" + +int buffer_putulonglong(buffer *b,unsigned long long l) { + char buf[FMT_ULONG]; + return buffer_put(b,buf,fmt_ulonglong(buf,l)); +} + diff --git a/io.h b/io.h index 327fe8a..0d96388 100644 --- a/io.h +++ b/io.h @@ -26,7 +26,7 @@ int64 io_trywrite(int64 d,const char* buf,int64 len); int64 io_waitwrite(int64 d,const char* buf,int64 len); /* modify timeout attribute of file descriptor */ -void io_timeout(int64 d,struct taia t); +void io_timeout(int64 d,tai6464 t); /* like io_tryread but will return -2,errno=ETIMEDOUT if d has a timeout * associated and it is passed without input being there */ @@ -42,7 +42,7 @@ void io_dontwantread(int64 d); void io_dontwantwrite(int64 d); void io_wait(); -void io_waituntil(struct taia t); +void io_waituntil(tai6464 t); void io_check(); /* return next descriptor from io_wait that can be read from */ diff --git a/io/io_timeout.c b/io/io_timeout.c new file mode 100644 index 0000000..b1415f8 --- /dev/null +++ b/io/io_timeout.c @@ -0,0 +1,7 @@ +#include "io_internal.h" + +void io_timeout(int64 d,tai6464 t) { + io_entry* e=array_get(&io_fds,sizeof(io_entry),d); + if (e) + e->timeout=t; +} diff --git a/io/io_tryreadtimeout.c b/io/io_tryreadtimeout.c new file mode 100644 index 0000000..5c41bcb --- /dev/null +++ b/io/io_tryreadtimeout.c @@ -0,0 +1,19 @@ +#include +#include +#include +#include +#include "io_internal.h" + +int64 io_tryreadtimeout(int64 d,char* buf,int64 len) { + int64 r=io_tryread(d,buf,len); + if (r==-1) { + tai6464 x; + io_entry* e=array_get(&io_fds,sizeof(io_entry),d); + taia_now(&x); + if (taia_less(&x,&e->timeout)) { + errno=ETIMEDOUT; + r=-2; + } + } + return r; +} diff --git a/io/io_trywritetimeout.c b/io/io_trywritetimeout.c new file mode 100644 index 0000000..75b4894 --- /dev/null +++ b/io/io_trywritetimeout.c @@ -0,0 +1,19 @@ +#include +#include +#include +#include +#include "io_internal.h" + +int64 io_trywritetimeout(int64 d,const char* buf,int64 len) { + int64 r=io_trywrite(d,buf,len); + if (r==-1) { + tai6464 x; + io_entry* e=array_get(&io_fds,sizeof(io_entry),d); + taia_now(&x); + if (taia_less(&x,&e->timeout)) { + errno=ETIMEDOUT; + r=-2; + } + } + return r; +} diff --git a/io/io_waitread.c b/io/io_waitread.c new file mode 100644 index 0000000..f89eb6f --- /dev/null +++ b/io/io_waitread.c @@ -0,0 +1,27 @@ +#include +#include +#include +#include "io_internal.h" + +int64 io_waitread(int64 d,char* buf,int64 len) { + long r; + struct pollfd p; + io_entry* e=array_get(&io_fds,sizeof(io_entry),d); + if (!e) { errno=EBADF; return -3; } + if (e->nonblock) { +again: + p.fd=d; + if (p.fd != d) { errno=EBADF; return -3; } /* catch overflow */ + p.events=POLLIN; + switch (poll(&p,1,-1)) { + case -1: if (errno==EAGAIN) goto again; return -3; + } + } + r=read(d,buf,len); + if (r==-1) { + if (errno==EAGAIN) + goto again; + r=-3; + } + return r; +} diff --git a/io/io_waitwrite.c b/io/io_waitwrite.c new file mode 100644 index 0000000..6c434c6 --- /dev/null +++ b/io/io_waitwrite.c @@ -0,0 +1,27 @@ +#include +#include +#include +#include "io_internal.h" + +int64 io_waitwrite(int64 d,const char* buf,int64 len) { + long r; + struct pollfd p; + io_entry* e=array_get(&io_fds,sizeof(io_entry),d); + if (!e) { errno=EBADF; return -3; } + if (e->nonblock) { +again: + p.fd=d; + if (p.fd != d) { errno=EBADF; return -3; } /* catch overflow */ + p.events=POLLOUT; + switch (poll(&p,1,-1)) { + case -1: if (errno==EAGAIN) goto again; return -3; + } + } + r=write(d,buf,len); + if (r==-1) { + if (errno==EAGAIN) + goto again; + r=-3; + } + return r; +} diff --git a/io_internal.h b/io_internal.h index 768e97e..89babfc 100644 --- a/io_internal.h +++ b/io_internal.h @@ -8,6 +8,7 @@ typedef struct { unsigned int canwrite:1; unsigned int nonblock:1; unsigned int inuse:1; + tai6464 timeout; } io_entry; array io_fds; diff --git a/tai.h b/tai.h index 7c07387..17c8279 100644 --- a/tai.h +++ b/tai.h @@ -17,9 +17,9 @@ * difference between two TAI64 labels. * See http://cr.yp.to/libtai/tai64.html */ -struct tai { +typedef struct tai { uint64 x; -} ; +} tai64; #define tai_unix(t,u) ((void) ((t)->x = 4611686018427387914ULL + (uint64) (u))) diff --git a/taia.h b/taia.h index fc0e98d..3c14a0b 100644 --- a/taia.h +++ b/taia.h @@ -9,37 +9,37 @@ * exclusive. The number is a multiple of 10^-18. The format of struct * taia is designed to speed up common operations; applications should * not look inside struct taia. */ -struct taia { +typedef struct taia { struct tai sec; unsigned long nano; /* 0...999999999 */ unsigned long atto; /* 0...999999999 */ -}; +} tai6464; /* extract seconds */ -extern void taia_tai(const struct taia *source,struct tai *dest); +void taia_tai(const tai6464 *source,tai64 *dest); /* get current time */ -extern void taia_now(struct taia *); +void taia_now(struct taia *); /* return double-precision approximation; always nonnegative */ -extern double taia_approx(const struct taia *); +double taia_approx(const tai6464 *); /* return double-precision approximation of the fraction part; * always nonnegative */ -extern double taia_frac(const struct taia *); +double taia_frac(const tai6464 *); /* add source1 to source2 modulo 2^64 and put the result in dest. * The inputs and output may overlap */ -extern void taia_add(struct taia *dest,const struct taia *source1,const struct taia *source2); +void taia_add(tai6464 *dest,const tai6464 *source1,const tai6464 *source2); /* add secs seconds to source modulo 2^64 and put the result in dest. */ -extern void taia_addsec(struct taia *dest,const struct taia *source,int secs); +void taia_addsec(tai6464 *dest,const tai6464 *source,long secs); /* subtract source2 from source1 modulo 2^64 and put the result in dest. * The inputs and output may overlap */ -extern void taia_sub(struct taia *,const struct taia *,const struct taia *); +void taia_sub(tai6464 *,const tai6464 *,const tai6464 *); /* divide source by 2, rouding down to a multiple of 10^-18, and put the * result into dest. The input and output may overlap */ -extern void taia_half(struct taia *dest,const struct taia *source); +void taia_half(tai6464 *dest,const tai6464 *source); /* return 1 if a is less than b, 0 otherwise */ -extern int taia_less(const struct taia *a,const struct taia *b); +int taia_less(const tai6464 *a,const tai6464 *b); #define TAIA_PACK 16 /* char buf[TAIA_PACK] can be used to store a TAI64NA label in external @@ -49,10 +49,10 @@ extern int taia_less(const struct taia *a,const struct taia *b); /* convert a TAI64NA label from internal format in src to external * TAI64NA format in buf. */ -extern void taia_pack(char *buf,const struct taia *src); +void taia_pack(char *buf,const tai6464 *src); /* convert a TAI64NA label from external TAI64NA format in buf to * internal format in dest. */ -extern void taia_unpack(const char *buf,struct taia *dest); +void taia_unpack(const char *buf,tai6464 *dest); #define TAIA_FMTFRAC 19 /* print the 18-digit fraction part of t in decimal, without a decimal @@ -60,9 +60,9 @@ extern void taia_unpack(const char *buf,struct taia *dest); * terminating \0. It returns 18, the number of characters written. s * may be zero; then taia_fmtfrac returns 18 without printing anything. * */ -extern unsigned int taia_fmtfrac(char *s,const struct taia *t); +unsigned int taia_fmtfrac(char *s,const tai6464 *t); /* initialize t to secs seconds. */ -extern void taia_uint(struct taia *t,unsigned int secs); +void taia_uint(tai6464 *t,unsigned int secs); #endif diff --git a/taia/taia_addsec.c b/taia/taia_addsec.c new file mode 100644 index 0000000..03a04b5 --- /dev/null +++ b/taia/taia_addsec.c @@ -0,0 +1,10 @@ +#include "taia.h" + +/* XXX: breaks tai encapsulation */ + +void taia_addsec(struct taia *t,const struct taia *u,long secs) +{ + t->sec.x = u->sec.x + secs; + t->nano = u->nano; + t->atto = u->atto; +} diff --git a/test/io3.c b/test/io3.c new file mode 100644 index 0000000..1343b8e --- /dev/null +++ b/test/io3.c @@ -0,0 +1,39 @@ +#include +#include "buffer.h" +#include "io.h" + +main() { + int64 pfd[2]; + char buf[20480]; + int i; + if (!io_pipe(pfd)) return 111; + io_nonblock(pfd[1]); + if (!io_fd(pfd[1])) return 111; + switch (fork()) { + case -1: return 111; + case 0: /* child */ + io_close(pfd[1]); + sleep(1); + for (;;) { + switch (io_waitread(pfd[0],buf,sizeof buf)) { + case -1: buffer_putsflush(buffer_2,"io_waitread returned -1!\n"); return 111; + case -3: buffer_puts(buffer_2,"io_waitread: "); + buffer_puterror(buffer_2); + buffer_putnlflush(buffer_2); + return 111; + case 0: io_close(pfd[0]); + return 0; + } + } + } + io_close(pfd[0]); + for (i=0; i