/***********************************************************************
 *                                                                      *
 *               This software is part of the ast package               *
 *          Copyright (c) 1999-2011 AT&T Intellectual Property          *
 *                      and is licensed under the                       *
 *                 Eclipse Public License, Version 1.0                  *
 *                    by AT&T Intellectual Property                     *
 *                                                                      *
 *                A copy of the License is available at                 *
 *          http://www.eclipse.org/org/documents/epl-v10.html           *
 *         (with md5 checksum b35adb5213ca9657e911e9befb180842)         *
 *                                                                      *
 *              Information and Software Systems Research               *
 *                            AT&T Research                             *
 *                           Florham Park NJ                            *
 *                                                                      *
 *               Glenn Fowler <glenn.s.fowler@gmail.com>                *
 *                                                                      *
 ***********************************************************************/
#include "config_ast.h"  // IWYU pragma: keep

#include <signal.h>
#include <string.h>
#include <unistd.h>

#include "sfio.h"
#include "terror.h"

static int line;

#define SYNC line = __LINE__;

void alrmf(int sig) {
    UNUSED(sig);

    terror("blocked at line %d", line);
}

tmain() {
    UNUSED(argc);
    UNUSED(argv);
    int fd[2];
    Sfio_t *fr, *fw;
    char *s;
    int i, j, n, to;
    char buf[1024];

    to = argc > 1 ? 0 : 4;

    if (sfnew(sfstdout, buf, sizeof(buf), SF_UNBOUND, SF_STRING | SF_WRITE) != sfstdout) {
        terror("Reopen sfstdout");
    }

    if (pipe(fd) < 0) terror("Can't open pipe");
    fr = sfnew(NULL, NULL, (size_t)SF_UNBOUND, fd[0], SF_READ);
    fw = sfnew(NULL, NULL, (size_t)SF_UNBOUND, fd[1], SF_WRITE);
    if (!fr || !fw) terror("Can't open stream");
    if (to) {
        signal(SIGALRM, alrmf);
        alarm(4);
    }
    SYNC sfwrite(fw, "0123456789", 10);
    if (sfread(fr, buf, 10) != 10) terror("Can't read data from pipe");
    SYNC sfwrite(fw, "0123456789", 10);
    SYNC if (sfmove(fr, fw, (Sfoff_t)10, -1) != 10) terror("sfmove failed");
    if (to) alarm(0);
    sfpurge(fw);
    sfclose(fw);
    sfpurge(fr);
    sfclose(fr);

    if (pipe(fd) < 0) terror("Can't open pipe2");
    if (!(fr = sfnew(NULL, NULL, (size_t)SF_UNBOUND, fd[0], SF_READ)) ||
        !(fw = sfnew(NULL, NULL, (size_t)SF_UNBOUND, fd[1], SF_WRITE))) {
        terror("Can't open stream");
    }
    sfset(fr, SF_SHARE | SF_LINE, 1);
    sfset(fw, SF_SHARE, 1);

    if (to) alarm(4);
    SYNC if (sfwrite(fw, "1\n2\n3\n", 6) != 6) terror("sfwrite failed");
    i = j = -1;
    SYNC if (sfscanf(fr, "%d%d\n%n", &i, &j, &n) != 2 || i != 1 || j != 2 || n != 4) {
        terror("sfscanf failed");
    }
    SYNC if (sfscanf(fr, "%d\n%n", &i, &n) != 1 || i != 3 || n != 2) terror("sfscanf failed");
    if (to) alarm(0);

    if (to) alarm(4);
    SYNC if (sfwrite(fw, "123\n", 4) != 4) terror("sfwrite failed");
    SYNC if (!(s = sfreserve(fr, 4, 0))) terror("sfreserve failed");
    if (to) alarm(0);

    if (to) alarm(4);
    SYNC sfputr(fw, "abc", '\n');
    SYNC if (sfmove(fr, fw, (Sfoff_t)1, '\n') != 1) terror("sfmove failed");
    SYNC if (!(s = sfgetr(fr, '\n', 1)) || strcmp(s, "abc")) terror("sfgetr failed");
    if (to) alarm(0);

    if (to) alarm(4);
    SYNC if (sfwrite(fw, "111\n222\n333\n444\n", 16) != 16) terror("Bad write to pipe");
    SYNC if (!(s = sfgetr(fr, '\n', 1))) terror("sfgetr failed");
    if (to) alarm(0);
    if (strcmp(s, "111")) terror("sfgetr got wrong string");

    if (to) alarm(4);
    SYNC if (sfmove(fr, sfstdout, (Sfoff_t)2, '\n') != 2) terror("sfmove failed");
    SYNC sfputc(sfstdout, 0);
    SYNC if (strcmp("222\n333\n", buf)) terror("sfmove got wrong data");
    SYNC if (sfmove(fr, NULL, (Sfoff_t)1, '\n') != 1) terror("sfmove failed");
    if (to) alarm(0);

    if (to) alarm(4);
    SYNC if (sfwrite(fw, "0123456789", 11) != 11) terror("Bad write to pipe2");
    SYNC if (!(s = sfreserve(fr, 11, 0))) terror("Bad peek size %d, expect 11", sfvalue(fr));
    if (to) alarm(0);
    if (strncmp(s, "0123456789", 10)) terror("Bad peek str %s", s);

    /* test for handling pipe error */
    if (pipe(fd) < 0) terror("Can't create pipe");
    close(fd[0]);
    if (!(fw = sfnew(NULL, NULL, sizeof(buf), fd[1], SF_WRITE))) terror("Can't open stream");
    signal(SIGPIPE, SIG_IGN);

    for (i = 0; i < sizeof(buf); ++i) buf[i] = 'a';
    buf[sizeof(buf) - 1] = 0;
    for (i = 0; i < 3; ++i) {
        if (to) {
            signal(SIGALRM, alrmf); /* do this to avoid infinite loop */
            alarm(4);
        }
        sfprintf(fw, "%s\n", buf); /* this should not block */
        if (to) alarm(0);
    }

    texit(0);
}
