skroll

scroll a text to stdout
git clone git://iotek.org/skroll
Log | Files | Refs | README | LICENSE

skroll.c (4529B)


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
/*
 *            DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
 *                    Version 2, December 2004
 *
 * Copyright (C) 2004 Sam Hocevar <sam@hocevar.net>
 *
 * Everyone is permitted to copy and distribute verbatim or modified
 * copies of this license document, and changing it is allowed as long
 * as the name is changed.
 *
 *            DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
 *   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
 *
 *  0. You just DO WHAT THE FUCK YOU WANT TO.
 *
 */

#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <getopt.h>
#include <limits.h>

static bool newline = false;/* print a new line after each step */
static bool loop = false;   /* wether to loop text or not */
static float delay = 0.1;   /* scroll speed, in seconds */
static int number = 20;     /* number of chars to be shown at the same time */

/* return the len of an utf-8 character */
int utf8_len(unsigned char c)
{
    return c<192 ? 0 : c<224 ? 1 : c<240 ? 2 : 3;
}

/* scroll <input> to stdout */
void skroll (const char *input)
{
    int offset = 0;

    /* main loop. will loop forever if run with -l */
    do
    {
        /*
         * each step of the loop will print the buffer, one byte further after
         * each step. using a carriage return, it makes the text scroll out.
         * leading/ending spaces are here to make sure that the text goes from
         * far right, and goes all the way to far left
         */
        for (offset = 0; input[offset + number] != 0; offset++)
        {
            /* increase the message's length in case of utf-8 chars */
            number += utf8_len(input[offset + number - 1]);

            /* print out `number` characters from the buffer ! */
            write(1, input + offset, number);

            /* if we want a new line, do it here, otherwise, carriage return */
            putc(newline ? '\n' : '\r', stdout);

            /* flush stdout, and wait for the next step */
            fflush(stdout);

            /* decrease length when utf-8 chars disappear to the left */
            number -= utf8_len(input[offset]);
            offset += utf8_len(input[offset]);

            usleep(delay*1000000);
        }
    /* magnolia ? FOWEVA ! */
    } while(loop);

    putc('\b', stdout);

    return; /* void */
}

/* returns a char that contains the input bufferized */
const char *bufferize (FILE *stream)
{
    int len = 0;
    char *eol, *buf = NULL;

    /* allocate space to store the input */
    if (!(buf = calloc (LINE_MAX + 1, sizeof(char)))) { return NULL; }
    memset(buf, ' ', LINE_MAX);
    buf[LINE_MAX] = 0;

    /* OMG, NO MORE SPACE LEFT ON DEVICE (or no more input, in fact) */
    if (feof(stream) || !fgets(buf + number, LINE_MAX, stream))
    {
        free (buf);
        return NULL;
    }

    /*
     * we need to remove trailings \n and \0 from input string to sanitize output.
     * the buffer should now look like this:
     * [          my input          \0           \0] 
     *  |         |       |         |             `- last \0, to prevent segfaults
     *  |         |       |          `- remaining spaces (up to LINE_MAX)
     *  |         |        `- trailing spaces, to make the text croll to far left
     *  |          `- the input itself, with \n and \0 removed from it
     *   `- leading spaces, to make the text scroll from far right
     */

    /* get the size of the input (and thus, the position of the \0) */
    len = strnlen(buf, LINE_MAX);
    buf[len] = ' ';

    /* terminate the string a bit further */
    buf[len + number] = 0;

    /* remove those silly \n from the input */
    if ((eol = strchr(buf, '\n')) != NULL) {
        eol[0] = ' ';
    }

    return buf;
}

int main (int argc, char **argv)
{
    char ch;
    const char *buf = NULL;

    while ((ch = getopt(argc, argv, "hd:ln:r")) != -1)
    {
        switch (ch)
        {
            case 'h':
                printf("usage: %s [-hlr] [-d delay] [-n number]\n", argv[0]);
                exit(0);
                break;
            case 'd': delay = strtof(optarg, NULL); break;
            case 'n': number = strtoul(optarg, NULL, 10); break;
            case 'l': loop = true; break;
            case 'r': newline = true; break;
        }
    }

    /* SCROLL ALL THE TEXT! */
    while((buf = bufferize(stdin)) != NULL)
    {
        skroll(buf);
    }

    /* End with a new line, no matter what */
    putc('\n', stdout);

    return 0;
}