Welcome to little lamb

Code » slicd » master » tree

[master] / src / libslicd / slicd_add_job_from_cronline.c

/*
 * slicd - Copyright (C) 2016 Olivier Brunel
 *
 * slicd_add_job_from_cronline.c
 * Copyright (C) 2016 Olivier Brunel <jjk@jjacky.com>
 *
 * This file is part of slicd.
 *
 * slicd is free software: you can redistribute it and/or modify it under the
 * terms of the GNU General Public License as published by the Free Software
 * Foundation, either version 3 of the License, or (at your option) any later
 * version.
 *
 * slicd is distributed in the hope that it will be useful, but WITHOUT ANY
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE.
 * See the GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along with
 * slicd. If not, see http://www.gnu.org/licenses/
 */

#include <errno.h>
#include <slicd/slicd.h>
#include <slicd/parser.h>
#include <slicd/job.h>
#include <slicd/fields.h>
#include <slicd/err.h>


static const char *names[_SLICD_NB_FIELD] = {
    NULL,
    NULL,
    NULL,
    "JanFebMarAprMayJunJulAugSepOctNovDec",
    "SunMonTueWedThuFriSat",
};

#define is_blank(c) \
    (c == ' ' || c == '\t')
#define is_num(c) \
    (c >= '0' && c <= '9')
#define skip_blanks(s) \
    for ( ; *s != '\0' && is_blank (*s); ++s) ;
#define read_int(s,n) \
    do {                            \
        n = 0;                      \
        for (;;)                    \
        {                           \
            if (!is_num (*s))       \
                break;              \
            n *= 10;                \
            n += *s - '0';          \
            ++s;                    \
        }                           \
    } while (0)
#define in_interval(n,f,t) \
    (n >= f && n <= t)

static int
parse_field_names (slicd_field_t field, const char **s)
{
    const char *n = names[field];
    int max = _slicd_fields[field].max * 3;
    int i = 'A' - 'a';
    int p = 0;

    for (p = 0; p <= max; p += 3)
    {
        if ( ((*s)[0] == n[p] || (*s)[0] == n[p] - i)
                && ((*s)[1] == n[p + 1] || (*s)[1] == n[p + 1] + i)
                && ((*s)[2] == n[p + 2] || (*s)[2] == n[p + 2] + i) )
        {
            *s += 3;
            return (p / 3) + _slicd_fields[field].adjust;
        }
    }

    return -1;
}

static int
parse_interval (slicd_job_t *job, slicd_field_t field, const char **s)
{
    int min = _slicd_fields[field].adjust;
    int max = min + _slicd_fields[field].max;
    int from, to, step;
    int swap = 0;

    if (field == SLICD_HOURS)
    {
        slicd_dst_special_t dst = SLICD_DST_OFF;

        if (**s == '<')
        {
            dst |= SLICD_DST_ON_DEACTIVATION;
            ++*s;
        }
        if (**s == '>')
        {
            dst |= SLICD_DST_ON_ACTIVATION;
            ++*s;
        }
        slicd_job_set_dst_special (job, dst);
    }

    if (**s == '!')
    {
        swap = 1;
        ++*s;
    }

again:
    if (**s == '*')
    {
        from = min;
        to = max;
        ++*s;
    }
    else
    {
        if (is_num (**s))
            read_int (*s, from);
        else if (names[field])
        {
            from = parse_field_names (field, s);
            if (from < 0)
                return -SLICD_ERR_UNKNOWN_NAME_FIELD;
        }
        else
            return -SLICD_ERR_SYNTAX;

        if (!in_interval (from, min, max))
            return -SLICD_ERR_NOT_IN_RANGE;

        if (**s == '-')
        {
            ++*s;
            if (is_num (**s))
                read_int (*s, to);
            else if (names[field])
            {
                to = parse_field_names (field, s);
                if (to < 0)
                    return -SLICD_ERR_UNKNOWN_NAME_FIELD;
            }
            else
                return -SLICD_ERR_SYNTAX;

            if (!in_interval (to, from, max))
                return -SLICD_ERR_NOT_IN_RANGE;
        }
        else
            to = from;

        if (!is_blank (**s) && **s != '/' && **s != ',')
            return -SLICD_ERR_SYNTAX;
    }
    if (**s == '/')
    {
        ++*s;
        if (!is_num (**s))
            return -SLICD_ERR_SYNTAX;

        read_int (*s, step);
    }
    else
        step = 1;

    if (!is_blank (**s) && **s != ',')
        return -SLICD_ERR_SYNTAX;

    if (step == 1)
        slicd_job_set (job, field, from, to);
    else
        for ( ; from <= to; from += step)
            slicd_job_set (job, field, from, from);

    if (**s == ',')
    {
        ++*s;
        goto again;
    }
    skip_blanks (*s);

    if (swap)
        slicd_job_swap (job, field, _slicd_fields[field].adjust,
                _slicd_fields[field].adjust + _slicd_fields[field].max);

    return 0;
}

int
slicd_add_job_from_cronline (slicd_t     *slicd,
                             const char  *username,
                             const char  *cronline)
{
    slicd_job_t j = { 0, };
    const char *s = cronline;
    size_t len;
    int r;
    int i;

    if (!s)
        return -SLICD_ERR_SYNTAX;

    skip_blanks (s);
    if (*s == '\0' || *s == '#')
        return 0;

    for (i = 0; i < 5; ++i)
    {
        r = parse_interval (&j, i, &s);
        if (r < 0)
            return r;
    }

    r = slicd_job_ensure_valid (&j);
    if (r < 0)
        return r;

    if (username)
        i = strlen (username);
    else
    {
        username = s;
        for (i = 0; *s != '\0' && !is_blank (*s); ++s, ++i)
            ;
        skip_blanks (s);
    }
    if (i == 0)
        return -SLICD_ERR_NO_USERNAME;
    if (*s == '\0')
        return -SLICD_ERR_SYNTAX;

    for (len = strlen (s); is_blank (s[len - 1]); --len)
        ;

    if (!stralloc_readyplus (&slicd->str, i + 1 + len + 1)
            || !genalloc_readyplus (slicd_job_t, &slicd->jobs, 1))
        return -SLICD_ERR_MEMORY;

    j.offset = slicd->str.len;
    stralloc_catb (&slicd->str, username, i);
    stralloc_catb (&slicd->str, ":", 1);
    stralloc_catb (&slicd->str, s, len);
    stralloc_0 (&slicd->str);
    genalloc_append (slicd_job_t, &slicd->jobs, &j);

    return 1;
}