ratox

FIFO based tox client
git clone git://git.2f30.org/ratox.git
Log | Files | Refs | README | LICENSE

commit 280e85b7d36f8d2674293edcf94445441fc275f1
Author: sin <sin@2f30.org>
Date:   Sat, 13 Sep 2014 14:44:17 +0100

Initial commit

Diffstat:
queue.h | 648+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
ratatox.c | 541+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 1189 insertions(+), 0 deletions(-)

diff --git a/queue.h b/queue.h @@ -0,0 +1,648 @@ +/* $OpenBSD: queue.h,v 1.38 2013/07/03 15:05:21 fgsch Exp $ */ +/* $NetBSD: queue.h,v 1.11 1996/05/16 05:17:14 mycroft Exp $ */ + +/* + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)queue.h 8.5 (Berkeley) 8/20/94 + */ + +#ifndef _SYS_QUEUE_H_ +#define _SYS_QUEUE_H_ + +/* + * This file defines five types of data structures: singly-linked lists, + * lists, simple queues, tail queues, and circular queues. + * + * + * A singly-linked list is headed by a single forward pointer. The elements + * are singly linked for minimum space and pointer manipulation overhead at + * the expense of O(n) removal for arbitrary elements. New elements can be + * added to the list after an existing element or at the head of the list. + * Elements being removed from the head of the list should use the explicit + * macro for this purpose for optimum efficiency. A singly-linked list may + * only be traversed in the forward direction. Singly-linked lists are ideal + * for applications with large datasets and few or no removals or for + * implementing a LIFO queue. + * + * A list is headed by a single forward pointer (or an array of forward + * pointers for a hash table header). The elements are doubly linked + * so that an arbitrary element can be removed without a need to + * traverse the list. New elements can be added to the list before + * or after an existing element or at the head of the list. A list + * may only be traversed in the forward direction. + * + * A simple queue is headed by a pair of pointers, one the head of the + * list and the other to the tail of the list. The elements are singly + * linked to save space, so elements can only be removed from the + * head of the list. New elements can be added to the list before or after + * an existing element, at the head of the list, or at the end of the + * list. A simple queue may only be traversed in the forward direction. + * + * A tail queue is headed by a pair of pointers, one to the head of the + * list and the other to the tail of the list. The elements are doubly + * linked so that an arbitrary element can be removed without a need to + * traverse the list. New elements can be added to the list before or + * after an existing element, at the head of the list, or at the end of + * the list. A tail queue may be traversed in either direction. + * + * A circle queue is headed by a pair of pointers, one to the head of the + * list and the other to the tail of the list. The elements are doubly + * linked so that an arbitrary element can be removed without a need to + * traverse the list. New elements can be added to the list before or after + * an existing element, at the head of the list, or at the end of the list. + * A circle queue may be traversed in either direction, but has a more + * complex end of list detection. + * + * For details on the use of these macros, see the queue(3) manual page. + */ + +#if defined(QUEUE_MACRO_DEBUG) || (defined(_KERNEL) && defined(DIAGNOSTIC)) +#define _Q_INVALIDATE(a) (a) = ((void *)-1) +#else +#define _Q_INVALIDATE(a) +#endif + +/* + * Singly-linked List definitions. + */ +#define SLIST_HEAD(name, type) \ +struct name { \ + struct type *slh_first; /* first element */ \ +} + +#define SLIST_HEAD_INITIALIZER(head) \ + { NULL } + +#define SLIST_ENTRY(type) \ +struct { \ + struct type *sle_next; /* next element */ \ +} + +/* + * Singly-linked List access methods. + */ +#define SLIST_FIRST(head) ((head)->slh_first) +#define SLIST_END(head) NULL +#define SLIST_EMPTY(head) (SLIST_FIRST(head) == SLIST_END(head)) +#define SLIST_NEXT(elm, field) ((elm)->field.sle_next) + +#define SLIST_FOREACH(var, head, field) \ + for((var) = SLIST_FIRST(head); \ + (var) != SLIST_END(head); \ + (var) = SLIST_NEXT(var, field)) + +#define SLIST_FOREACH_SAFE(var, head, field, tvar) \ + for ((var) = SLIST_FIRST(head); \ + (var) && ((tvar) = SLIST_NEXT(var, field), 1); \ + (var) = (tvar)) + +/* + * Singly-linked List functions. + */ +#define SLIST_INIT(head) { \ + SLIST_FIRST(head) = SLIST_END(head); \ +} + +#define SLIST_INSERT_AFTER(slistelm, elm, field) do { \ + (elm)->field.sle_next = (slistelm)->field.sle_next; \ + (slistelm)->field.sle_next = (elm); \ +} while (0) + +#define SLIST_INSERT_HEAD(head, elm, field) do { \ + (elm)->field.sle_next = (head)->slh_first; \ + (head)->slh_first = (elm); \ +} while (0) + +#define SLIST_REMOVE_AFTER(elm, field) do { \ + (elm)->field.sle_next = (elm)->field.sle_next->field.sle_next; \ +} while (0) + +#define SLIST_REMOVE_HEAD(head, field) do { \ + (head)->slh_first = (head)->slh_first->field.sle_next; \ +} while (0) + +#define SLIST_REMOVE(head, elm, type, field) do { \ + if ((head)->slh_first == (elm)) { \ + SLIST_REMOVE_HEAD((head), field); \ + } else { \ + struct type *curelm = (head)->slh_first; \ + \ + while (curelm->field.sle_next != (elm)) \ + curelm = curelm->field.sle_next; \ + curelm->field.sle_next = \ + curelm->field.sle_next->field.sle_next; \ + _Q_INVALIDATE((elm)->field.sle_next); \ + } \ +} while (0) + +/* + * List definitions. + */ +#define LIST_HEAD(name, type) \ +struct name { \ + struct type *lh_first; /* first element */ \ +} + +#define LIST_HEAD_INITIALIZER(head) \ + { NULL } + +#define LIST_ENTRY(type) \ +struct { \ + struct type *le_next; /* next element */ \ + struct type **le_prev; /* address of previous next element */ \ +} + +/* + * List access methods + */ +#define LIST_FIRST(head) ((head)->lh_first) +#define LIST_END(head) NULL +#define LIST_EMPTY(head) (LIST_FIRST(head) == LIST_END(head)) +#define LIST_NEXT(elm, field) ((elm)->field.le_next) + +#define LIST_FOREACH(var, head, field) \ + for((var) = LIST_FIRST(head); \ + (var)!= LIST_END(head); \ + (var) = LIST_NEXT(var, field)) + +#define LIST_FOREACH_SAFE(var, head, field, tvar) \ + for ((var) = LIST_FIRST(head); \ + (var) && ((tvar) = LIST_NEXT(var, field), 1); \ + (var) = (tvar)) + +/* + * List functions. + */ +#define LIST_INIT(head) do { \ + LIST_FIRST(head) = LIST_END(head); \ +} while (0) + +#define LIST_INSERT_AFTER(listelm, elm, field) do { \ + if (((elm)->field.le_next = (listelm)->field.le_next) != NULL) \ + (listelm)->field.le_next->field.le_prev = \ + &(elm)->field.le_next; \ + (listelm)->field.le_next = (elm); \ + (elm)->field.le_prev = &(listelm)->field.le_next; \ +} while (0) + +#define LIST_INSERT_BEFORE(listelm, elm, field) do { \ + (elm)->field.le_prev = (listelm)->field.le_prev; \ + (elm)->field.le_next = (listelm); \ + *(listelm)->field.le_prev = (elm); \ + (listelm)->field.le_prev = &(elm)->field.le_next; \ +} while (0) + +#define LIST_INSERT_HEAD(head, elm, field) do { \ + if (((elm)->field.le_next = (head)->lh_first) != NULL) \ + (head)->lh_first->field.le_prev = &(elm)->field.le_next;\ + (head)->lh_first = (elm); \ + (elm)->field.le_prev = &(head)->lh_first; \ +} while (0) + +#define LIST_REMOVE(elm, field) do { \ + if ((elm)->field.le_next != NULL) \ + (elm)->field.le_next->field.le_prev = \ + (elm)->field.le_prev; \ + *(elm)->field.le_prev = (elm)->field.le_next; \ + _Q_INVALIDATE((elm)->field.le_prev); \ + _Q_INVALIDATE((elm)->field.le_next); \ +} while (0) + +#define LIST_REPLACE(elm, elm2, field) do { \ + if (((elm2)->field.le_next = (elm)->field.le_next) != NULL) \ + (elm2)->field.le_next->field.le_prev = \ + &(elm2)->field.le_next; \ + (elm2)->field.le_prev = (elm)->field.le_prev; \ + *(elm2)->field.le_prev = (elm2); \ + _Q_INVALIDATE((elm)->field.le_prev); \ + _Q_INVALIDATE((elm)->field.le_next); \ +} while (0) + +/* + * Simple queue definitions. + */ +#define SIMPLEQ_HEAD(name, type) \ +struct name { \ + struct type *sqh_first; /* first element */ \ + struct type **sqh_last; /* addr of last next element */ \ +} + +#define SIMPLEQ_HEAD_INITIALIZER(head) \ + { NULL, &(head).sqh_first } + +#define SIMPLEQ_ENTRY(type) \ +struct { \ + struct type *sqe_next; /* next element */ \ +} + +/* + * Simple queue access methods. + */ +#define SIMPLEQ_FIRST(head) ((head)->sqh_first) +#define SIMPLEQ_END(head) NULL +#define SIMPLEQ_EMPTY(head) (SIMPLEQ_FIRST(head) == SIMPLEQ_END(head)) +#define SIMPLEQ_NEXT(elm, field) ((elm)->field.sqe_next) + +#define SIMPLEQ_FOREACH(var, head, field) \ + for((var) = SIMPLEQ_FIRST(head); \ + (var) != SIMPLEQ_END(head); \ + (var) = SIMPLEQ_NEXT(var, field)) + +#define SIMPLEQ_FOREACH_SAFE(var, head, field, tvar) \ + for ((var) = SIMPLEQ_FIRST(head); \ + (var) && ((tvar) = SIMPLEQ_NEXT(var, field), 1); \ + (var) = (tvar)) + +/* + * Simple queue functions. + */ +#define SIMPLEQ_INIT(head) do { \ + (head)->sqh_first = NULL; \ + (head)->sqh_last = &(head)->sqh_first; \ +} while (0) + +#define SIMPLEQ_INSERT_HEAD(head, elm, field) do { \ + if (((elm)->field.sqe_next = (head)->sqh_first) == NULL) \ + (head)->sqh_last = &(elm)->field.sqe_next; \ + (head)->sqh_first = (elm); \ +} while (0) + +#define SIMPLEQ_INSERT_TAIL(head, elm, field) do { \ + (elm)->field.sqe_next = NULL; \ + *(head)->sqh_last = (elm); \ + (head)->sqh_last = &(elm)->field.sqe_next; \ +} while (0) + +#define SIMPLEQ_INSERT_AFTER(head, listelm, elm, field) do { \ + if (((elm)->field.sqe_next = (listelm)->field.sqe_next) == NULL)\ + (head)->sqh_last = &(elm)->field.sqe_next; \ + (listelm)->field.sqe_next = (elm); \ +} while (0) + +#define SIMPLEQ_REMOVE_HEAD(head, field) do { \ + if (((head)->sqh_first = (head)->sqh_first->field.sqe_next) == NULL) \ + (head)->sqh_last = &(head)->sqh_first; \ +} while (0) + +#define SIMPLEQ_REMOVE_AFTER(head, elm, field) do { \ + if (((elm)->field.sqe_next = (elm)->field.sqe_next->field.sqe_next) \ + == NULL) \ + (head)->sqh_last = &(elm)->field.sqe_next; \ +} while (0) + +/* + * XOR Simple queue definitions. + */ +#define XSIMPLEQ_HEAD(name, type) \ +struct name { \ + struct type *sqx_first; /* first element */ \ + struct type **sqx_last; /* addr of last next element */ \ + unsigned long sqx_cookie; \ +} + +#define XSIMPLEQ_ENTRY(type) \ +struct { \ + struct type *sqx_next; /* next element */ \ +} + +/* + * XOR Simple queue access methods. + */ +#define XSIMPLEQ_XOR(head, ptr) ((__typeof(ptr))((head)->sqx_cookie ^ \ + (unsigned long)(ptr))) +#define XSIMPLEQ_FIRST(head) XSIMPLEQ_XOR(head, ((head)->sqx_first)) +#define XSIMPLEQ_END(head) NULL +#define XSIMPLEQ_EMPTY(head) (XSIMPLEQ_FIRST(head) == XSIMPLEQ_END(head)) +#define XSIMPLEQ_NEXT(head, elm, field) XSIMPLEQ_XOR(head, ((elm)->field.sqx_next)) + + +#define XSIMPLEQ_FOREACH(var, head, field) \ + for ((var) = XSIMPLEQ_FIRST(head); \ + (var) != XSIMPLEQ_END(head); \ + (var) = XSIMPLEQ_NEXT(head, var, field)) + +#define XSIMPLEQ_FOREACH_SAFE(var, head, field, tvar) \ + for ((var) = XSIMPLEQ_FIRST(head); \ + (var) && ((tvar) = XSIMPLEQ_NEXT(head, var, field), 1); \ + (var) = (tvar)) + +/* + * XOR Simple queue functions. + */ +#define XSIMPLEQ_INIT(head) do { \ + arc4random_buf(&(head)->sqx_cookie, sizeof((head)->sqx_cookie)); \ + (head)->sqx_first = XSIMPLEQ_XOR(head, NULL); \ + (head)->sqx_last = XSIMPLEQ_XOR(head, &(head)->sqx_first); \ +} while (0) + +#define XSIMPLEQ_INSERT_HEAD(head, elm, field) do { \ + if (((elm)->field.sqx_next = (head)->sqx_first) == \ + XSIMPLEQ_XOR(head, NULL)) \ + (head)->sqx_last = XSIMPLEQ_XOR(head, &(elm)->field.sqx_next); \ + (head)->sqx_first = XSIMPLEQ_XOR(head, (elm)); \ +} while (0) + +#define XSIMPLEQ_INSERT_TAIL(head, elm, field) do { \ + (elm)->field.sqx_next = XSIMPLEQ_XOR(head, NULL); \ + *(XSIMPLEQ_XOR(head, (head)->sqx_last)) = XSIMPLEQ_XOR(head, (elm)); \ + (head)->sqx_last = XSIMPLEQ_XOR(head, &(elm)->field.sqx_next); \ +} while (0) + +#define XSIMPLEQ_INSERT_AFTER(head, listelm, elm, field) do { \ + if (((elm)->field.sqx_next = (listelm)->field.sqx_next) == \ + XSIMPLEQ_XOR(head, NULL)) \ + (head)->sqx_last = XSIMPLEQ_XOR(head, &(elm)->field.sqx_next); \ + (listelm)->field.sqx_next = XSIMPLEQ_XOR(head, (elm)); \ +} while (0) + +#define XSIMPLEQ_REMOVE_HEAD(head, field) do { \ + if (((head)->sqx_first = XSIMPLEQ_XOR(head, \ + (head)->sqx_first)->field.sqx_next) == XSIMPLEQ_XOR(head, NULL)) \ + (head)->sqx_last = XSIMPLEQ_XOR(head, &(head)->sqx_first); \ +} while (0) + +#define XSIMPLEQ_REMOVE_AFTER(head, elm, field) do { \ + if (((elm)->field.sqx_next = XSIMPLEQ_XOR(head, \ + (elm)->field.sqx_next)->field.sqx_next) \ + == XSIMPLEQ_XOR(head, NULL)) \ + (head)->sqx_last = \ + XSIMPLEQ_XOR(head, &(elm)->field.sqx_next); \ +} while (0) + + +/* + * Tail queue definitions. + */ +#define TAILQ_HEAD(name, type) \ +struct name { \ + struct type *tqh_first; /* first element */ \ + struct type **tqh_last; /* addr of last next element */ \ +} + +#define TAILQ_HEAD_INITIALIZER(head) \ + { NULL, &(head).tqh_first } + +#define TAILQ_ENTRY(type) \ +struct { \ + struct type *tqe_next; /* next element */ \ + struct type **tqe_prev; /* address of previous next element */ \ +} + +/* + * tail queue access methods + */ +#define TAILQ_FIRST(head) ((head)->tqh_first) +#define TAILQ_END(head) NULL +#define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next) +#define TAILQ_LAST(head, headname) \ + (*(((struct headname *)((head)->tqh_last))->tqh_last)) +/* XXX */ +#define TAILQ_PREV(elm, headname, field) \ + (*(((struct headname *)((elm)->field.tqe_prev))->tqh_last)) +#define TAILQ_EMPTY(head) \ + (TAILQ_FIRST(head) == TAILQ_END(head)) + +#define TAILQ_FOREACH(var, head, field) \ + for((var) = TAILQ_FIRST(head); \ + (var) != TAILQ_END(head); \ + (var) = TAILQ_NEXT(var, field)) + +#define TAILQ_FOREACH_SAFE(var, head, field, tvar) \ + for ((var) = TAILQ_FIRST(head); \ + (var) != TAILQ_END(head) && \ + ((tvar) = TAILQ_NEXT(var, field), 1); \ + (var) = (tvar)) + + +#define TAILQ_FOREACH_REVERSE(var, head, headname, field) \ + for((var) = TAILQ_LAST(head, headname); \ + (var) != TAILQ_END(head); \ + (var) = TAILQ_PREV(var, headname, field)) + +#define TAILQ_FOREACH_REVERSE_SAFE(var, head, headname, field, tvar) \ + for ((var) = TAILQ_LAST(head, headname); \ + (var) != TAILQ_END(head) && \ + ((tvar) = TAILQ_PREV(var, headname, field), 1); \ + (var) = (tvar)) + +/* + * Tail queue functions. + */ +#define TAILQ_INIT(head) do { \ + (head)->tqh_first = NULL; \ + (head)->tqh_last = &(head)->tqh_first; \ +} while (0) + +#define TAILQ_INSERT_HEAD(head, elm, field) do { \ + if (((elm)->field.tqe_next = (head)->tqh_first) != NULL) \ + (head)->tqh_first->field.tqe_prev = \ + &(elm)->field.tqe_next; \ + else \ + (head)->tqh_last = &(elm)->field.tqe_next; \ + (head)->tqh_first = (elm); \ + (elm)->field.tqe_prev = &(head)->tqh_first; \ +} while (0) + +#define TAILQ_INSERT_TAIL(head, elm, field) do { \ + (elm)->field.tqe_next = NULL; \ + (elm)->field.tqe_prev = (head)->tqh_last; \ + *(head)->tqh_last = (elm); \ + (head)->tqh_last = &(elm)->field.tqe_next; \ +} while (0) + +#define TAILQ_INSERT_AFTER(head, listelm, elm, field) do { \ + if (((elm)->field.tqe_next = (listelm)->field.tqe_next) != NULL)\ + (elm)->field.tqe_next->field.tqe_prev = \ + &(elm)->field.tqe_next; \ + else \ + (head)->tqh_last = &(elm)->field.tqe_next; \ + (listelm)->field.tqe_next = (elm); \ + (elm)->field.tqe_prev = &(listelm)->field.tqe_next; \ +} while (0) + +#define TAILQ_INSERT_BEFORE(listelm, elm, field) do { \ + (elm)->field.tqe_prev = (listelm)->field.tqe_prev; \ + (elm)->field.tqe_next = (listelm); \ + *(listelm)->field.tqe_prev = (elm); \ + (listelm)->field.tqe_prev = &(elm)->field.tqe_next; \ +} while (0) + +#define TAILQ_REMOVE(head, elm, field) do { \ + if (((elm)->field.tqe_next) != NULL) \ + (elm)->field.tqe_next->field.tqe_prev = \ + (elm)->field.tqe_prev; \ + else \ + (head)->tqh_last = (elm)->field.tqe_prev; \ + *(elm)->field.tqe_prev = (elm)->field.tqe_next; \ + _Q_INVALIDATE((elm)->field.tqe_prev); \ + _Q_INVALIDATE((elm)->field.tqe_next); \ +} while (0) + +#define TAILQ_REPLACE(head, elm, elm2, field) do { \ + if (((elm2)->field.tqe_next = (elm)->field.tqe_next) != NULL) \ + (elm2)->field.tqe_next->field.tqe_prev = \ + &(elm2)->field.tqe_next; \ + else \ + (head)->tqh_last = &(elm2)->field.tqe_next; \ + (elm2)->field.tqe_prev = (elm)->field.tqe_prev; \ + *(elm2)->field.tqe_prev = (elm2); \ + _Q_INVALIDATE((elm)->field.tqe_prev); \ + _Q_INVALIDATE((elm)->field.tqe_next); \ +} while (0) + +/* + * Circular queue definitions. + */ +#define CIRCLEQ_HEAD(name, type) \ +struct name { \ + struct type *cqh_first; /* first element */ \ + struct type *cqh_last; /* last element */ \ +} + +#define CIRCLEQ_HEAD_INITIALIZER(head) \ + { CIRCLEQ_END(&head), CIRCLEQ_END(&head) } + +#define CIRCLEQ_ENTRY(type) \ +struct { \ + struct type *cqe_next; /* next element */ \ + struct type *cqe_prev; /* previous element */ \ +} + +/* + * Circular queue access methods + */ +#define CIRCLEQ_FIRST(head) ((head)->cqh_first) +#define CIRCLEQ_LAST(head) ((head)->cqh_last) +#define CIRCLEQ_END(head) ((void *)(head)) +#define CIRCLEQ_NEXT(elm, field) ((elm)->field.cqe_next) +#define CIRCLEQ_PREV(elm, field) ((elm)->field.cqe_prev) +#define CIRCLEQ_EMPTY(head) \ + (CIRCLEQ_FIRST(head) == CIRCLEQ_END(head)) + +#define CIRCLEQ_FOREACH(var, head, field) \ + for((var) = CIRCLEQ_FIRST(head); \ + (var) != CIRCLEQ_END(head); \ + (var) = CIRCLEQ_NEXT(var, field)) + +#define CIRCLEQ_FOREACH_SAFE(var, head, field, tvar) \ + for ((var) = CIRCLEQ_FIRST(head); \ + (var) != CIRCLEQ_END(head) && \ + ((tvar) = CIRCLEQ_NEXT(var, field), 1); \ + (var) = (tvar)) + +#define CIRCLEQ_FOREACH_REVERSE(var, head, field) \ + for((var) = CIRCLEQ_LAST(head); \ + (var) != CIRCLEQ_END(head); \ + (var) = CIRCLEQ_PREV(var, field)) + +#define CIRCLEQ_FOREACH_REVERSE_SAFE(var, head, headname, field, tvar) \ + for ((var) = CIRCLEQ_LAST(head, headname); \ + (var) != CIRCLEQ_END(head) && \ + ((tvar) = CIRCLEQ_PREV(var, headname, field), 1); \ + (var) = (tvar)) + +/* + * Circular queue functions. + */ +#define CIRCLEQ_INIT(head) do { \ + (head)->cqh_first = CIRCLEQ_END(head); \ + (head)->cqh_last = CIRCLEQ_END(head); \ +} while (0) + +#define CIRCLEQ_INSERT_AFTER(head, listelm, elm, field) do { \ + (elm)->field.cqe_next = (listelm)->field.cqe_next; \ + (elm)->field.cqe_prev = (listelm); \ + if ((listelm)->field.cqe_next == CIRCLEQ_END(head)) \ + (head)->cqh_last = (elm); \ + else \ + (listelm)->field.cqe_next->field.cqe_prev = (elm); \ + (listelm)->field.cqe_next = (elm); \ +} while (0) + +#define CIRCLEQ_INSERT_BEFORE(head, listelm, elm, field) do { \ + (elm)->field.cqe_next = (listelm); \ + (elm)->field.cqe_prev = (listelm)->field.cqe_prev; \ + if ((listelm)->field.cqe_prev == CIRCLEQ_END(head)) \ + (head)->cqh_first = (elm); \ + else \ + (listelm)->field.cqe_prev->field.cqe_next = (elm); \ + (listelm)->field.cqe_prev = (elm); \ +} while (0) + +#define CIRCLEQ_INSERT_HEAD(head, elm, field) do { \ + (elm)->field.cqe_next = (head)->cqh_first; \ + (elm)->field.cqe_prev = CIRCLEQ_END(head); \ + if ((head)->cqh_last == CIRCLEQ_END(head)) \ + (head)->cqh_last = (elm); \ + else \ + (head)->cqh_first->field.cqe_prev = (elm); \ + (head)->cqh_first = (elm); \ +} while (0) + +#define CIRCLEQ_INSERT_TAIL(head, elm, field) do { \ + (elm)->field.cqe_next = CIRCLEQ_END(head); \ + (elm)->field.cqe_prev = (head)->cqh_last; \ + if ((head)->cqh_first == CIRCLEQ_END(head)) \ + (head)->cqh_first = (elm); \ + else \ + (head)->cqh_last->field.cqe_next = (elm); \ + (head)->cqh_last = (elm); \ +} while (0) + +#define CIRCLEQ_REMOVE(head, elm, field) do { \ + if ((elm)->field.cqe_next == CIRCLEQ_END(head)) \ + (head)->cqh_last = (elm)->field.cqe_prev; \ + else \ + (elm)->field.cqe_next->field.cqe_prev = \ + (elm)->field.cqe_prev; \ + if ((elm)->field.cqe_prev == CIRCLEQ_END(head)) \ + (head)->cqh_first = (elm)->field.cqe_next; \ + else \ + (elm)->field.cqe_prev->field.cqe_next = \ + (elm)->field.cqe_next; \ + _Q_INVALIDATE((elm)->field.cqe_prev); \ + _Q_INVALIDATE((elm)->field.cqe_next); \ +} while (0) + +#define CIRCLEQ_REPLACE(head, elm, elm2, field) do { \ + if (((elm2)->field.cqe_next = (elm)->field.cqe_next) == \ + CIRCLEQ_END(head)) \ + (head)->cqh_last = (elm2); \ + else \ + (elm2)->field.cqe_next->field.cqe_prev = (elm2); \ + if (((elm2)->field.cqe_prev = (elm)->field.cqe_prev) == \ + CIRCLEQ_END(head)) \ + (head)->cqh_first = (elm2); \ + else \ + (elm2)->field.cqe_prev->field.cqe_next = (elm2); \ + _Q_INVALIDATE((elm)->field.cqe_prev); \ + _Q_INVALIDATE((elm)->field.cqe_next); \ +} while (0) + +#endif /* !_SYS_QUEUE_H_ */ diff --git a/ratatox.c b/ratatox.c @@ -0,0 +1,541 @@ +#include <sys/select.h> +#include <sys/stat.h> +#include <sys/types.h> + +#include <errno.h> +#include <fcntl.h> +#include <limits.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <unistd.h> + +#include <tox/tox.h> + +#include "queue.h" + +#define LEN(x) (sizeof (x) / sizeof *(x)) + +static struct bootstrapnode { + char *addr; + uint16_t port; + uint8_t key[TOX_CLIENT_ID_SIZE]; +} bootstrapnodes[] = { + { + "95.85.13.245", + 33445, + { + 0x71, 0x87, 0x96, 0x9B, 0xB1, 0x0B, 0x54, 0xC9, 0x85, 0x38, 0xBA, 0xE9, 0x4C, 0x06, 0x9C, 0xE5, + 0xC8, 0x4E, 0x65, 0x0D, 0x54, 0xF7, 0xE5, 0x96, 0x54, 0x3D, 0x8F, 0xB1, 0xEC, 0xF4, 0xCF, 0x23 + } + }, + { + "37.187.46.132", + 33445, + { + 0xA9, 0xD9, 0x82, 0x12, 0xB3, 0xF9, 0x72, 0xBD, 0x11, 0xDA, 0x52, 0xBE, 0xB0, 0x65, 0x8C, 0x32, + 0x6F, 0xCC, 0xC1, 0xBF, 0xD4, 0x9F, 0x34, 0x7F, 0x9C, 0x2D, 0x3D, 0x8B, 0x61, 0xE1, 0xB9, 0x27 + } + }, + { + "144.76.60.215", + 33445, + { + 0x04, 0x11, 0x9E, 0x83, 0x5D, 0xF3, 0xE7, 0x8B, 0xAC, 0xF0, 0xF8, 0x42, 0x35, 0xB3, 0x00, 0x54, + 0x6A, 0xF8, 0xB9, 0x36, 0xF0, 0x35, 0x18, 0x5E, 0x2A, 0x8E, 0x9E, 0x0A, 0x67, 0xC8, 0x92, 0x4F + } + }, + { + "23.226.230.47", + 33445, + { + 0xA0, 0x91, 0x62, 0xD6, 0x86, 0x18, 0xE7, 0x42, 0xFF, 0xBC, 0xA1, 0xC2, 0xC7, 0x03, 0x85, 0xE6, + 0x67, 0x96, 0x04, 0xB2, 0xD8, 0x0E, 0xA6, 0xE8, 0x4A, 0xD0, 0x99, 0x6A, 0x1A, 0xC8, 0xA0, 0x74 + } + }, + { + "54.199.139.199", + 33445, + { + 0x7F, 0x9C, 0x31, 0xFE, 0x85, 0x0E, 0x97, 0xCE, 0xFD, 0x4C, 0x45, 0x91, 0xDF, 0x93, 0xFC, 0x75, + 0x7C, 0x7C, 0x12, 0x54, 0x9D, 0xDD, 0x55, 0xF8, 0xEE, 0xAE, 0xCC, 0x34, 0xFE, 0x76, 0xC0, 0x29 + } + }, + { + "109.169.46.133", + 33445, + { + 0x7F, 0x31, 0xBF, 0xC9, 0x3B, 0x8E, 0x40, 0x16, 0xA9, 0x02, 0x14, 0x4D, 0x0B, 0x11, 0x0C, 0x3E, + 0xA9, 0x7C, 0xB7, 0xD4, 0x3F, 0x1C, 0x4D, 0x21, 0xBC, 0xAE, 0x99, 0x8A, 0x7C, 0x83, 0x88, 0x21 + } + }, + { + "192.210.149.121", + 33445, + { + 0xF4, 0x04, 0xAB, 0xAA, 0x1C, 0x99, 0xA9, 0xD3, 0x7D, 0x61, 0xAB, 0x54, 0x89, 0x8F, 0x56, 0x79, + 0x3E, 0x1D, 0xEF, 0x8B, 0xD4, 0x6B, 0x10, 0x38, 0xB9, 0xD8, 0x22, 0xE8, 0x46, 0x0F, 0xAB, 0x67 + } + }, + { + "76.191.23.96", + 33445, + { + 0x4B, 0xA5, 0x76, 0x60, 0xDE, 0x3E, 0x85, 0x4C, 0x53, 0x0E, 0xED, 0x60, 0x1B, 0xF8, 0xD5, 0x4B, + 0x7E, 0xFA, 0xE9, 0x60, 0x52, 0x3B, 0x6C, 0xFC, 0x10, 0x21, 0x0C, 0xC0, 0x8E, 0x2C, 0xB8, 0x08 + } + }, +}; + +enum { + TEXT_IN_FIFO, + NR_FIFOS +}; + +static struct fifo { + const char *name; + mode_t mode; +} fifos[] = { + { "text_in", 0644 }, +}; + +struct friend { + /* null terminated name */ + uint8_t namestr[TOX_MAX_NAME_LENGTH + 1]; + int fid; + uint8_t id[TOX_CLIENT_ID_SIZE]; + /* null terminated id */ + uint8_t idstr[2 * TOX_CLIENT_ID_SIZE + 1]; + int fd[NR_FIFOS]; + TAILQ_ENTRY(friend) entry; +}; + +static TAILQ_HEAD(friendhead, friend) friendhead; + +static Tox *tox; +static void dataload(void); +static void datasave(void); +static void friendcreate(int32_t); + +static void +cb_conn_status(Tox *tox, int32_t fid, uint8_t status, void *udata) +{ + struct friend *f; + uint8_t name[TOX_MAX_NAME_LENGTH + 1]; + uint8_t *nick; + int n; + + n = tox_get_name(tox, fid, name); + if (n < 0) { + fprintf(stderr, "tox_get_name() on fid %d failed\n", fid); + exit(1); + } + name[n] = '\0'; + + if (n == 0) { + if (status == 0) + printf("Anonymous went offline\n"); + else + printf("Anonymous came online\n"); + } else { + if (status == 0) + printf("%s went offline\n", name); + else + printf("%s came online\n", name); + } + + TAILQ_FOREACH(f, &friendhead, entry) + if (f->fid == fid) + return; + + friendcreate(fid); +} + +static void +cb_friend_message(Tox *tox, int32_t fid, const uint8_t *data, uint16_t len, void *udata) +{ + FILE *fp; + struct friend *f; + uint8_t msg[len + 1]; + char path[PATH_MAX]; + + memcpy(msg, data, len); + msg[len] = '\0'; + + TAILQ_FOREACH(f, &friendhead, entry) { + if (f->fid == fid) { + snprintf(path, sizeof(path), "%s:%d/text_out", + f->namestr, f->fid); + fp = fopen(path, "a"); + if (!fp) { + perror("fopen"); + exit(1); + } + fputs(msg, fp); + fputc('\n', fp); + fclose(fp); + break; + } + } +} + +static void +cb_friend_request(Tox *tox, const uint8_t *id, const uint8_t *data, uint16_t len, void *udata) +{ + uint8_t msg[len + 1]; + + memcpy(msg, data, len); + msg[len] = '\0'; + tox_add_friend_norequest(tox, id); + if (len > 0) + printf("Accepted friend request with msg: %s\n", msg); + else + printf("Accepted friend request\n"); + datasave(); +} + +static void +cb_name_change(Tox *m, int32_t fid, const uint8_t *data, uint16_t len, void *user) +{ + FILE *fp; + char path[PATH_MAX]; + struct friend *f; + uint8_t name[len + 1]; + + memcpy(name, data, len); + name[len] = '\0'; + + TAILQ_FOREACH(f, &friendhead, entry) { + if (f->fid == fid) { + snprintf(path, sizeof(path), "%s/name", f->idstr); + fp = fopen(path, "w"); + if (!fp) { + perror("fopen"); + exit(1); + } + fputs(name, fp); + fputc('\n', fp); + fclose(fp); + if (memcmp(f->namestr, name, len + 1) == 0) + break; + if (f->namestr[0] == '\0') { + printf("%s -> %s\n", "Anonymous", name); + } else { + printf("%s -> %s\n", f->namestr, name); + } + memcpy(f->namestr, name, len + 1); + break; + } + } + datasave(); +} + +static void +cb_status_message(Tox *m, int32_t fid, const uint8_t *data, uint16_t len, void *udata) +{ + FILE *fp; + char path[PATH_MAX]; + struct friend *f; + uint8_t status[len + 1]; + + memcpy(status, data, len); + status[len] = '\0'; + TAILQ_FOREACH(f, &friendhead, entry) { + if (f->fid == fid) { + snprintf(path, sizeof(path), "%s/status", f->idstr); + fp = fopen(path, "w"); + if (!fp) { + perror("fopen"); + exit(1); + } + fputs(status, fp); + fputc('\n', fp); + fclose(fp); + printf("%s current status to %s\n", f->namestr, status); + break; + } + } + datasave(); +} + +static void +send_friend_text(struct friend *f) +{ + uint8_t buf[TOX_MAX_MESSAGE_LENGTH]; + ssize_t n; + +again: + n = read(f->fd[TEXT_IN_FIFO], buf, sizeof(buf)); + if (n < 0) { + if (errno == EINTR) + goto again; + perror("read"); + exit(1); + } + tox_send_message(tox, f->fid, buf, n); +} + +static void +dataload(void) +{ + FILE *fp; + size_t sz; + uint8_t *data; + + fp = fopen("toxcli.data", "r"); + if (!fp) + return; + + fseek(fp, 0, SEEK_END); + sz = ftell(fp); + rewind(fp); + + data = malloc(sz); + if (!data) { + perror("malloc"); + exit(1); + } + + if (fread(data, 1, sz, fp) != sz) { + fprintf(stderr, "failed to read toxcli.data\n"); + exit(1); + } + tox_load(tox, data, sz); + + free(data); + fclose(fp); +} + +static void +datasave(void) +{ + FILE *fp; + size_t sz; + uint8_t *data; + + fp = fopen("toxcli.data", "w"); + if (!fp) { + fprintf(stderr, "can't open toxcli.data for writing\n"); + exit(1); + } + + sz = tox_size(tox); + data = malloc(sz); + if (!data) { + perror("malloc"); + exit(1); + } + + tox_save(tox, data); + if (fwrite(data, 1, sz, fp) != sz) { + fprintf(stderr, "failed to write toxcli.data\n"); + exit(1); + } + + free(data); + fclose(fp); +} + +static void +toxrestore(void) +{ + dataload(); + datasave(); +} + +static int +toxinit(void) +{ + uint8_t address[TOX_FRIEND_ADDRESS_SIZE]; + int i; + + tox = tox_new(0); + toxrestore(); + tox_callback_connection_status(tox, cb_conn_status, NULL); + tox_callback_friend_message(tox, cb_friend_message, NULL); + tox_callback_friend_request(tox, cb_friend_request, NULL); + tox_callback_name_change(tox, cb_name_change, NULL); + tox_callback_status_message(tox, cb_status_message, NULL); + tox_set_name(tox, "TLH", strlen("TLH")); + tox_set_user_status(tox, TOX_USERSTATUS_NONE); + + tox_get_address(tox, address); + printf("ID: "); + for (i = 0; i < TOX_FRIEND_ADDRESS_SIZE; i++) + printf("%02x", address[i]); + putchar('\n'); + + + return 0; +} + +static int +toxconnect(void) +{ + struct bootstrapnode *bn; + size_t i; + + for (i = 0; i < LEN(bootstrapnodes); i++) { + bn = &bootstrapnodes[i]; + tox_bootstrap_from_address(tox, bn->addr, bn->port, bn->key); + } + return 0; +} + +static int +id2str(uint8_t *id, uint8_t *idstr) +{ + uint8_t hex[] = "0123456789abcdef"; + int i; + + for (i = 0; i < TOX_CLIENT_ID_SIZE; i++) { + *idstr++ = hex[(id[i] >> 4) & 0xf]; + *idstr++ = hex[id[i] & 0xf]; + } + *idstr = '\0'; +} + +static void +friendcreate(int32_t fid) +{ + struct friend *f; + char path[PATH_MAX]; + int i; + int r; + + f = calloc(1, sizeof(*f)); + if (!f) { + perror("malloc"); + exit(1); + } + + r = tox_get_name(tox, fid, f->namestr); + if (r < 0) { + fprintf(stderr, "tox_get_name() on fid %d failed\n", fid); + exit(1); + } + f->namestr[r] = '\0'; + + f->fid = fid; + tox_get_client_id(tox, f->fid, f->id); + id2str(f->id, f->idstr); + + r = mkdir(f->idstr, 0755); + if (r < 0 && errno != EEXIST) { + perror("mkdir"); + exit(1); + } + + for (i = 0; i < LEN(fifos); i++) { + snprintf(path, sizeof(path), "%s/%s", f->idstr, + fifos[i].name); + r = mkfifo(path, fifos[i].mode); + if (r < 0 && errno != EEXIST) { + perror("mkfifo"); + exit(1); + } + r = open(path, O_RDONLY | O_NONBLOCK, 0); + if (r < 0) { + perror("open"); + exit(1); + } + f->fd[i] = r; + } + TAILQ_INSERT_TAIL(&friendhead, f, entry); +} + +static void +friendload(void) +{ + int32_t *fids; + uint32_t sz; + uint32_t i, j; + int n; + char name[TOX_MAX_NAME_LENGTH + 1]; + + TAILQ_INIT(&friendhead); + + sz = tox_count_friendlist(tox); + fids = malloc(sz); + if (!fids) { + perror("malloc"); + exit(1); + } + + tox_get_friendlist(tox, fids, sz); + + for (i = 0; i < sz; i++) + friendcreate(fids[i]); +} + +static void +loop(void) +{ + struct friend *f; + time_t t0, t1; + int connected = 0; + int i, n; + int fdmax = 0; + fd_set rfds; + struct timeval tv; + + t0 = time(NULL); + toxconnect(); + while (1) { + if (tox_isconnected(tox) == 1) { + if (connected == 0) { + printf("Connected to DHT\n"); + connected = 1; + } + } else { + t1 = time(NULL); + if (t1 > t0 + 5) { + t0 = time(NULL); + printf("Connecting to DHT...\n"); + toxconnect(); + } + } + tox_do(tox); + FD_ZERO(&rfds); + TAILQ_FOREACH(f, &friendhead, entry) { + for (i = 0; i < NR_FIFOS; i++) { + if (f->fd[i] > fdmax) + fdmax = f->fd[i]; + FD_SET(f->fd[i], &rfds); + } + } + tv.tv_sec = 0; + tv.tv_usec = tox_do_interval(tox) * 1000; + n = select(fdmax + 1, &rfds, NULL, NULL, + &tv); + if (n < 0) { + if (errno == EINTR) + continue; + perror("select"); + exit(1); + } + TAILQ_FOREACH(f, &friendhead, entry) { + for (i = 0; i < NR_FIFOS; i++) { + if (FD_ISSET(f->fd[i], &rfds) == 0) + continue; + switch (i) { + case TEXT_IN_FIFO: + send_friend_text(f); + default: + fputs("Unhandled FIFO read\n", stderr); + } + } + } + } +} + +int +main(void) +{ + toxinit(); + friendload(); + loop(); + return 0; +}