URI: 
       tInitial commit - ratox - FIFO based tox client
   DIR Log
   DIR Files
   DIR Refs
   DIR README
   DIR LICENSE
       ---
   DIR commit 280e85b7d36f8d2674293edcf94445441fc275f1
  HTML Author: sin <sin@2f30.org>
       Date:   Sat, 13 Sep 2014 14:44:17 +0100
       
       Initial commit
       
       Diffstat:
         A queue.h                             |     648 ++++++++++++++++++++++++++++++
         A ratatox.c                           |     541 +++++++++++++++++++++++++++++++
       
       2 files changed, 1189 insertions(+), 0 deletions(-)
       ---
   DIR diff --git a/queue.h b/queue.h
       t@@ -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_ */
   DIR diff --git a/ratatox.c b/ratatox.c
       t@@ -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;
       +}