Add actions to extract the bind record data

%%{
  machine bind_parser;

  action read_number    { num = (num * 10) + (fc - ?0) }
  action clear_number   { num = 0 }
  action clear_str_p    { str_p = p }

  action store_dist     { record[:dist] = num }
  action store_ttl      { record[:ttl] = num }
  action store_priority { record[:priority] = num }
  action store_weight   { record[:weight] = num }
  action store_port     { record[:port] = num }
  action store_serial   { record[:serial] = num }
  action store_refresh  { record[:refresh] = num }
  action store_retry    { record[:retry] = num }
  action store_expire   { record[:expire] = num }
  action store_minimum  { record[:minimum] = num }

  action store_owner    { record[:owner] = data[str_p..p].pack('c*').strip }
  action store_target   { record[:target] = data[str_p..p].pack('c*').strip }
  action store_mbox     { record[:mbox] = data[str_p..p].pack('c*').strip }
  action store_text     { record[:text] = data[(str_p+1)..(p-2)].pack('c*').strip }
  action store_ipv4     { record[:address] = data[str_p..p].pack('c*').strip }
  action store_name     { record[:domain] = data[str_p..p].pack('c*').strip }

  action a              { record[:type] = :a }
  action ns             { record[:type] = :ns }
  action mx             { record[:type] = :mx }
  action txt            { record[:type] = :txt }
  action ptr            { record[:type] = :ptr }
  action soa            { record[:type] = :soa }
  action srv            { record[:type] = :srv }
  action cname          { record[:type] = :cname }

  sp           = space+;
  newline      = "\n";
  comment      = space* ";" [^\n]* newline;
  endofline    = comment | newline @{ @records << record; record = Hash.new };

  escapes      = ('\\' [^0-9\n]) | ('\\' digit{3});
  quotedstr    = ('"' ([^"\n\\]|escapes)* '"') >clear_str_p %store_text;

  ipv4_octet   = ("25" [0-5] | "2" [01234] [0-9] | [01]? [0-9]{1,2});
  ipv4_address = (ipv4_octet "." ipv4_octet "." ipv4_octet "." ipv4_octet) >clear_str_p %store_ipv4;

  origin       = "@";
  alnumhyphen  = (alnum | "-");
  dname        = (alnumhyphen+ ".")* alnumhyphen+ "."?;
  fqdname      = dname >clear_str_p %store_name;
  owner        = (sp | origin | dname) >clear_str_p %store_owner;
  dist         = (digit @read_number)+ >clear_number %store_dist;
  ttl          = (digit @read_number)+ >clear_number %store_ttl;
  priority     = (digit @read_number)+ >clear_number %store_priority;
  weight       = (digit @read_number)+ >clear_number %store_weight;
  port         = (digit @read_number)+ >clear_number %store_port;
  serial       = (digit @read_number)+ >clear_number %store_serial;
  refresh      = (digit @read_number)+ >clear_number %store_refresh;
  retry        = (digit @read_number)+ >clear_number %store_retry;
  expire       = (digit @read_number)+ >clear_number %store_expire;
  minimum      = (digit @read_number)+ >clear_number %store_minimum;
  target       = ("." | dname) >clear_str_p %store_target;
  srv_o        = ("_" alnumhyphen+ "." ("_tcp" | "_udp") ("." dname)?) >clear_str_p %store_owner;
  mbox         = dname >clear_str_p %store_mbox;

  eol_or_sp    = sp | (endofline sp);
  soa_rdata    = "(" sp? serial eol_or_sp refresh eol_or_sp retry eol_or_sp expire eol_or_sp minimum sp? ")";

  host_record  = owner (sp ttl)? (sp "IN")? sp ("A" @a) sp ipv4_address endofline;
  ns_record    = owner (sp ttl)? (sp "IN")? sp ("NS" @ns) sp fqdname endofline;
  mx_record    = owner (sp ttl)? (sp "IN")? sp ("MX" @mx) sp dist sp fqdname endofline;
  txt_record   = owner (sp ttl)? (sp "IN")? sp ("TXT" @txt) sp quotedstr endofline;
  ptr_record   = owner (sp ttl)? (sp "IN")? sp ("PTR" @ptr) sp fqdname endofline;
  cname_record = owner (sp ttl)? (sp "IN")? sp ("CNAME" @cname) sp fqdname endofline;
  soa_record   = owner (sp ttl)?  sp "IN"   sp ("SOA" @soa) sp fqdname sp mbox sp soa_rdata endofline;
  srv_record   = srv_o (sp ttl)? (sp "IN")? sp ("SRV" @srv) sp priority sp weight sp port sp target endofline;

  record       = host_record
               | ns_record
               | mx_record
               | txt_record
               | ptr_record
               | cname_record
               | soa_record
               | srv_record;

  main        := (newline | comment | record)*;
}%%

class Bind
  def initialize(content = nil)
    @content = content && (File.exists?(content) ? File.read(content) : content)
    @records = Array.new
  end
  def parse
    parse_string(@content)
  end
  def parse_string(data)
    data = data.unpack('c*')
    record = {}

    %% write data;
    %% write init;
    %% write exec;

    return true if p == pe && cs == bind_parser_first_final
    raise "cs: #{cs} p: #{p} pe: #{pe} data[p]: #{data[p] ? data[p].chr.inspect : 'nil'}"
  end
end

%%{
  machine bind_parser;

  sp           = space+;
  newline      = "\n";
  comment      = space* ";" [^\n]* newline;
  endofline    = comment | newline;

  escapes      = ('\\' [^0-9\n]) | ('\\' digit{3});
  quotedstr    = ('"' ([^"\n\\]|escapes)* '"');

  ipv4_octet   = ("25" [0-5] | "2" [01234] [0-9] | [01]? [0-9]{1,2});
  ipv4_address = (ipv4_octet "." ipv4_octet "." ipv4_octet "." ipv4_octet);

  origin       = "@";
  alnumhyphen  = alnum | "-";
  dname        = (alnumhyphen+ ".")* alnumhyphen+;
  fqdname      = dname | dname ".";
  owner        = sp | origin | fqdname;
  dist         = digit+;
  ttl          = digit+;
  priority     = digit+;
  weight       = digit+;
  port         = digit+;
  serial       = digit+;
  refresh      = digit+;
  retry        = digit+;
  expire       = digit+;
  minimum      = digit+;
  target       = "." | fqdname;
  srv_o        = "_" alnumhyphen+ "." ("_tcp" | "_udp") ("." fqdname)?;
  mbox         = fqdname;

  eol_or_sp    = sp | (endofline sp);
  soa_rdata    = "(" sp? serial eol_or_sp refresh eol_or_sp retry eol_or_sp expire eol_or_sp minimum sp? ")";

  host_record  = owner (sp ttl)? (sp "IN")? sp "A"     sp ipv4_address endofline;
  ns_record    = owner (sp ttl)? (sp "IN")? sp "NS"    sp fqdname endofline;
  mx_record    = owner (sp ttl)? (sp "IN")? sp "MX"    sp dist sp fqdname endofline;
  txt_record   = owner (sp ttl)? (sp "IN")? sp "TXT"   sp quotedstr endofline;
  ptr_record   = owner (sp ttl)? (sp "IN")? sp "PTR"   sp fqdname endofline;
  cname_record = owner (sp ttl)? (sp "IN")? sp "CNAME" sp fqdname endofline;
  soa_record   = owner (sp ttl)?  sp "IN"   sp "SOA"   sp fqdname sp mbox sp soa_rdata endofline;
  srv_record   = srv_o (sp ttl)? (sp "IN")? sp "SRV"   sp priority sp weight sp port sp target endofline;

  record       = host_record
               | ns_record
               | mx_record
               | txt_record
               | ptr_record
               | cname_record
               | soa_record
               | srv_record;

  main        := (newline | comment | record)*;
}%%

class Bind
  def initialize(content = nil)
    @content = content && (File.exists?(content) ? File.read(content) : content)
  end
  def parse
    parse_string(@content)
  end
  def parse_string(data = nil)
    data = data.unpack('c*')

    %% write data;
    %% write init;
    %% write exec;

    return true if p == pe && cs == bind_parser_first_final
    raise "cs: #{cs} p: #{p} pe: #{pe} data[p]: #{data[p] ? data[p].chr.inspect : 'nil'}"
  end
end


Use the standard defines for ipv4 printable address max string length.

/* ifchd-parse.rl - interface change daemon parser
 *
 * Copyright (c) 2004-2014 Nicholas J. Kain <njkain at gmail dot com>
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * - Redistributions of source code must retain the above copyright notice,
 *   this list of conditions and the following disclaimer.
 *
 * - 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.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT HOLDER 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.
 */

#include <stddef.h>
#include <string.h>
#include <arpa/inet.h>

#include "ifchd-defines.h"
#include "log.h"
#include "ifch_proto.h"
#include "strl.h"
#include "linux.h"

%%{
    machine ipv4set_parser;

    action XSt { arg_start = p; }
    action IpEn {
        arg_len = p - arg_start;
        if (arg_len > sizeof ip4_addr - 1) {
            have_ip = true;
            memcpy(ip4_addr, arg_start, arg_len);
        }
        ip4_addr[arg_len] = 0;
    }
    action SnEn {
        arg_len = p - arg_start;
        if (arg_len > sizeof ip4_subnet - 1) {
            have_subnet = true;
            memcpy(ip4_subnet, arg_start, arg_len);
        }
        ip4_subnet[arg_len] = 0;
    }
    action BcEn {
        arg_len = p - arg_start;
        if (arg_len > sizeof ip4_bcast - 1) {
            have_ip = true;
            memcpy(ip4_bcast, arg_start, arg_len);
        }
        ip4_bcast[arg_len] = 0;
    }

    v4addr = digit{1,3} '.' digit{1,3} '.' digit{1,3} '.' digit{1,3};
    ip4_nobc = (v4addr > XSt % IpEn) ',' (v4addr > XSt % SnEn);
    ip4_bc = (v4addr > XSt % IpEn) ',' (v4addr > XSt % SnEn) ','
             (v4addr > XSt % BcEn);
    main := (ip4_bc|ip4_nobc);
}%%

%% write data;

static void perform_ip4set(struct ifchd_client *cl, const char *buf,
                           size_t len)
{
    char ip4_addr[INET_ADDRSTRLEN];
    char ip4_subnet[INET_ADDRSTRLEN];
    char ip4_bcast[INET_ADDRSTRLEN];
    const char *p = buf;
    const char *pe = p + len;
    const char *eof = pe;
    const char *arg_start;
    size_t arg_len;
    unsigned int cs = 0;
    bool have_ip = false;
    bool have_subnet = false;
    bool have_bcast = false;

    %% write init;
    %% write exec;

    if (cs < ipv4set_parser_first_final) {
        log_line("%s: received invalid arguments", __func__);
        return;
    }

    // These should never trigger because of the above check, but be safe...
    if (!have_ip) {
        log_line("%s: No IPv4 address specified.", __func__);
        return;
    }
    if (!have_subnet) {
        log_line("%s: No IPv4 subnet specified.", __func__);
        return;
    }

    perform_ip_subnet_bcast(cl, ip4_addr, ip4_subnet,
                            have_bcast ? ip4_bcast : NULL);
}

%%{
    machine ifchd_parser;

    action Reset { cl->state = STATE_NOTHING; }
    action ArgSt { arg_start = p; }
    action ArgEn {
        arg_len = p - arg_start;
        if (arg_len > sizeof tb - 1) {
            log_line("command argument would overflow");
            return -1;
        }
        memcpy(tb, arg_start, arg_len);
        tb[arg_len] = 0;
    }

    action Dispatch {
        switch (cl->state) {
        case STATE_INTERFACE: perform_interface(cl, tb, arg_len); break;
        case STATE_IP4SET: perform_ip4set(cl, tb, arg_len); break;
        case STATE_IP: perform_ip(cl, tb, arg_len); break;
        case STATE_SUBNET: perform_subnet(cl, tb, arg_len); break;
        case STATE_TIMEZONE: perform_timezone(cl, tb, arg_len); break;
        case STATE_ROUTER: perform_router(cl, tb, arg_len); break;
        case STATE_DNS: perform_dns(cl, tb, arg_len); break;
        case STATE_LPRSVR: perform_lprsvr(cl, tb, arg_len); break;
        case STATE_HOSTNAME: perform_hostname(cl, tb, arg_len); break;
        case STATE_DOMAIN: perform_domain(cl, tb, arg_len); break;
        case STATE_IPTTL: perform_ipttl(cl, tb, arg_len); break;
        case STATE_MTU: perform_mtu(cl, tb, arg_len); break;
        case STATE_BROADCAST: perform_broadcast(cl, tb, arg_len); break;
        case STATE_NTPSVR: perform_ntpsrv(cl, tb, arg_len); break;
        case STATE_WINS: perform_wins(cl, tb, arg_len); break;
        default:
            log_line("error: invalid state in dispatch_work");
            return -1;
        }
    }

    terminator = ';' > Dispatch;
    v4addr = digit{1,3} '.' digit{1,3} '.' digit{1,3} '.' digit{1,3};
    ip_arg = (v4addr > ArgSt % ArgEn) terminator;
    ip4set_arg = (((v4addr ','){1,2} v4addr) > ArgSt % ArgEn) terminator;
    iplist_arg = (((v4addr ',')* v4addr) > ArgSt % ArgEn) terminator;
    str_arg = ([^;\0]+ > ArgSt % ArgEn) terminator;
    s32_arg = (extend{4} > ArgSt % ArgEn) terminator;
    u16_arg = (extend{2} > ArgSt % ArgEn) terminator;
    u8_arg = (extend{1} > ArgSt % ArgEn) terminator;

    cmd_ip = ('ip:' % { cl->state = STATE_IP; }
             |'snet:' % { cl->state = STATE_SUBNET; }
             |'routr:' % { cl->state = STATE_ROUTER; }
             |'bcast:' % { cl->state = STATE_BROADCAST; }
             ) ip_arg;
    cmd_ip4set = ('ip4:' % { cl->state = STATE_IP4SET; }) ip4set_arg;
    cmd_iplist = ('dns:' % { cl->state = STATE_DNS; }
                 |'lpr:' % { cl->state = STATE_LPRSVR; }
                 |'ntp:' % { cl->state = STATE_NTPSVR; }
                 |'wins:' % { cl->state = STATE_WINS; }
                 ) iplist_arg;
    cmd_str = ('iface:' % { cl->state = STATE_INTERFACE; }
              |'host:' % { cl->state = STATE_HOSTNAME; }
              |'dom:' % { cl->state = STATE_DOMAIN; }
              ) str_arg;
    cmd_s32 = ('tzone:' % { cl->state = STATE_TIMEZONE; }) s32_arg;
    cmd_u16 = ('mtu:' % { cl->state = STATE_MTU; }) u16_arg;
    cmd_u8  = ('ipttl:' % { cl->state = STATE_IPTTL; }) u8_arg;

    command = (cmd_ip|cmd_iplist|cmd_str|cmd_s32|cmd_u16|cmd_u8);
    main := (command > Reset)+;
}%%

%% write data;

/*
 * Returns -1 on fatal error; that leads to peer connection being closed.
 */
int execute_buffer(struct ifchd_client *cl, char *newbuf)
{
    char buf[MAX_BUF * 2];
    char tb[MAX_BUF];

    if (strnkcpy(buf, cl->ibuf, sizeof buf))
        goto buftooshort;
    if (strnkcat(buf, newbuf, sizeof buf)) {
buftooshort:
        log_line("error: input is too long for buffer");
        return -1;
    }

    size_t init_siz = strlen(buf);
    const char *p = buf;
    const char *pe = p + init_siz;
    const char *arg_start;
    size_t arg_len;
    unsigned int cs = 0;

    %% write init;
    %% write exec;

    size_t bytes_left = pe - p;
    if (bytes_left > 0) {
        size_t taken = init_siz - bytes_left;
        strnkcpy(cl->ibuf, buf + taken, MAX_BUF);
    }

    if (cs < ifchd_parser_first_final) {
        log_line("error: received invalid commands");
        return -1;
    }
    log_line("Commands received and successfully executed.");
    return 0;
}

/* ifchd-parse.rl - interface change daemon parser
 *
 * Copyright (c) 2004-2014 Nicholas J. Kain <njkain at gmail dot com>
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * - Redistributions of source code must retain the above copyright notice,
 *   this list of conditions and the following disclaimer.
 *
 * - 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.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT HOLDER 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.
 */

#include <stddef.h>
#include <string.h>

#include "ifchd-defines.h"
#include "log.h"
#include "ifch_proto.h"
#include "strl.h"
#include "linux.h"

%%{
    machine ipv4set_parser;

    action XSt { arg_start = p; }
    action IpEn {
        arg_len = p - arg_start;
        if (arg_len > sizeof ip4_addr - 1) {
            have_ip = true;
            memcpy(ip4_addr, arg_start, arg_len);
        }
        ip4_addr[arg_len] = 0;
    }
    action SnEn {
        arg_len = p - arg_start;
        if (arg_len > sizeof ip4_subnet - 1) {
            have_subnet = true;
            memcpy(ip4_subnet, arg_start, arg_len);
        }
        ip4_subnet[arg_len] = 0;
    }
    action BcEn {
        arg_len = p - arg_start;
        if (arg_len > sizeof ip4_bcast - 1) {
            have_ip = true;
            memcpy(ip4_bcast, arg_start, arg_len);
        }
        ip4_bcast[arg_len] = 0;
    }

    v4addr = digit{1,3} '.' digit{1,3} '.' digit{1,3} '.' digit{1,3};
    ip4_nobc = (v4addr > XSt % IpEn) ',' (v4addr > XSt % SnEn);
    ip4_bc = (v4addr > XSt % IpEn) ',' (v4addr > XSt % SnEn) ','
             (v4addr > XSt % BcEn);
    main := (ip4_bc|ip4_nobc);
}%%

%% write data;

static void perform_ip4set(struct ifchd_client *cl, const char *buf,
                           size_t len)
{
    char ip4_addr[16];
    char ip4_subnet[16];
    char ip4_bcast[16];
    const char *p = buf;
    const char *pe = p + len;
    const char *eof = pe;
    const char *arg_start;
    size_t arg_len;
    unsigned int cs = 0;
    bool have_ip = false;
    bool have_subnet = false;
    bool have_bcast = false;

    %% write init;
    %% write exec;

    if (cs < ipv4set_parser_first_final) {
        log_line("%s: received invalid arguments", __func__);
        return;
    }

    // These should never trigger because of the above check, but be safe...
    if (!have_ip) {
        log_line("%s: No IPv4 address specified.", __func__);
        return;
    }
    if (!have_subnet) {
        log_line("%s: No IPv4 subnet specified.", __func__);
        return;
    }

    perform_ip_subnet_bcast(cl, ip4_addr, ip4_subnet,
                            have_bcast ? ip4_bcast : NULL);
}

%%{
    machine ifchd_parser;

    action Reset { cl->state = STATE_NOTHING; }
    action ArgSt { arg_start = p; }
    action ArgEn {
        arg_len = p - arg_start;
        if (arg_len > sizeof tb - 1) {
            log_line("command argument would overflow");
            return -1;
        }
        memcpy(tb, arg_start, arg_len);
        tb[arg_len] = 0;
    }

    action Dispatch {
        switch (cl->state) {
        case STATE_INTERFACE: perform_interface(cl, tb, arg_len); break;
        case STATE_IP4SET: perform_ip4set(cl, tb, arg_len); break;
        case STATE_IP: perform_ip(cl, tb, arg_len); break;
        case STATE_SUBNET: perform_subnet(cl, tb, arg_len); break;
        case STATE_TIMEZONE: perform_timezone(cl, tb, arg_len); break;
        case STATE_ROUTER: perform_router(cl, tb, arg_len); break;
        case STATE_DNS: perform_dns(cl, tb, arg_len); break;
        case STATE_LPRSVR: perform_lprsvr(cl, tb, arg_len); break;
        case STATE_HOSTNAME: perform_hostname(cl, tb, arg_len); break;
        case STATE_DOMAIN: perform_domain(cl, tb, arg_len); break;
        case STATE_IPTTL: perform_ipttl(cl, tb, arg_len); break;
        case STATE_MTU: perform_mtu(cl, tb, arg_len); break;
        case STATE_BROADCAST: perform_broadcast(cl, tb, arg_len); break;
        case STATE_NTPSVR: perform_ntpsrv(cl, tb, arg_len); break;
        case STATE_WINS: perform_wins(cl, tb, arg_len); break;
        default:
            log_line("error: invalid state in dispatch_work");
            return -1;
        }
    }

    terminator = ';' > Dispatch;
    v4addr = digit{1,3} '.' digit{1,3} '.' digit{1,3} '.' digit{1,3};
    ip_arg = (v4addr > ArgSt % ArgEn) terminator;
    ip4set_arg = (((v4addr ','){1,2} v4addr) > ArgSt % ArgEn) terminator;
    iplist_arg = (((v4addr ',')* v4addr) > ArgSt % ArgEn) terminator;
    str_arg = ([^;\0]+ > ArgSt % ArgEn) terminator;
    s32_arg = (extend{4} > ArgSt % ArgEn) terminator;
    u16_arg = (extend{2} > ArgSt % ArgEn) terminator;
    u8_arg = (extend{1} > ArgSt % ArgEn) terminator;

    cmd_ip = ('ip:' % { cl->state = STATE_IP; }
             |'snet:' % { cl->state = STATE_SUBNET; }
             |'routr:' % { cl->state = STATE_ROUTER; }
             |'bcast:' % { cl->state = STATE_BROADCAST; }
             ) ip_arg;
    cmd_ip4set = ('ip4:' % { cl->state = STATE_IP4SET; }) ip4set_arg;
    cmd_iplist = ('dns:' % { cl->state = STATE_DNS; }
                 |'lpr:' % { cl->state = STATE_LPRSVR; }
                 |'ntp:' % { cl->state = STATE_NTPSVR; }
                 |'wins:' % { cl->state = STATE_WINS; }
                 ) iplist_arg;
    cmd_str = ('iface:' % { cl->state = STATE_INTERFACE; }
              |'host:' % { cl->state = STATE_HOSTNAME; }
              |'dom:' % { cl->state = STATE_DOMAIN; }
              ) str_arg;
    cmd_s32 = ('tzone:' % { cl->state = STATE_TIMEZONE; }) s32_arg;
    cmd_u16 = ('mtu:' % { cl->state = STATE_MTU; }) u16_arg;
    cmd_u8  = ('ipttl:' % { cl->state = STATE_IPTTL; }) u8_arg;

    command = (cmd_ip|cmd_iplist|cmd_str|cmd_s32|cmd_u16|cmd_u8);
    main := (command > Reset)+;
}%%

%% write data;

/*
 * Returns -1 on fatal error; that leads to peer connection being closed.
 */
int execute_buffer(struct ifchd_client *cl, char *newbuf)
{
    char buf[MAX_BUF * 2];
    char tb[MAX_BUF];

    if (strnkcpy(buf, cl->ibuf, sizeof buf))
        goto buftooshort;
    if (strnkcat(buf, newbuf, sizeof buf)) {
buftooshort:
        log_line("error: input is too long for buffer");
        return -1;
    }

    size_t init_siz = strlen(buf);
    const char *p = buf;
    const char *pe = p + init_siz;
    const char *arg_start;
    size_t arg_len;
    unsigned int cs = 0;

    %% write init;
    %% write exec;

    size_t bytes_left = pe - p;
    if (bytes_left > 0) {
        size_t taken = init_siz - bytes_left;
        strnkcpy(cl->ibuf, buf + taken, MAX_BUF);
    }

    if (cs < ifchd_parser_first_final) {
        log_line("error: received invalid commands");
        return -1;
    }
    log_line("Commands received and successfully executed.");
    return 0;
}


Documentation

Documentation

package dns

// Parse RRs
// With the thankful help of gdnsd and the Go examples for Ragel.

import (
    "os"
    "io"
    "net"
    "strings"
    "strconv"
)

//const _IOBUF = 65365 // See comments in gdnsd
const _IOBUF = 3e7  // TODO fix sliding window stuff in Ragel

// A Parser represents a DNS parser for a
// particular input stream.
type Parser struct {
    // nothing here yet
    buf    []byte
}

// NewParser creates a new DNS file parser from r.
func NewParser(r io.Reader) *Parser {
        buf := make([]byte, _IOBUF)
        n, err := r.Read(buf)
        if err != nil {
            return nil
        }
        if buf[n-1] != '\n' {
            buf[n] = '\n'
            n++
        }
        buf = buf[:n]
        p := new(Parser)
        p.buf = buf
        return p
}

// Return the rdata fields as a string slice.
// All starting whitespace is deleted.
// If i is 0 no spaces are deleted from the final rdfs.
func fields(s string, i int) (rdf []string) {
    rdf = strings.Fields(s)
    for i, _ := range rdf {
        rdf[i] = strings.TrimSpace(rdf[i])
    }
    if i > 0 && len(rdf) > i {
        // The last rdf contained embedded spaces, glue it back together.
        for j := i; j < len(rdf); j++ {
            rdf[i-1] += rdf[j]
        }
    }
    return
}

// Wrapper for strconv.Atoi*().
func atoi(s string) uint {
    i, err :=  strconv.Atoui(s)
    if err != nil {
        panic("not a number: " + s + " " + err.String())
    }
    return i
}

%%{
        machine z;
        write data;
}%%

// RR parses a zone file, but only returns the last RR read.
func (zp *Parser) RR() RR {
    z, err := zp.Zone()
    if err != nil {
        return nil
    }
    return z.Pop().(RR)
}

// Zone parses an DNS master zone file.
func (zp *Parser) Zone() (z *Zone, err os.Error) {
        z = new(Zone)
        data := string(zp.buf)
        cs, p, pe := 0, 0, len(data)
        eof := len(data)

//        brace := false
        lines := 0
        mark := 0
        hdr := new(RR_Header)

        %%{
                action mark       { mark = p }
                action setQname   { hdr.Name = data[mark:p] }
                action setQclass  { hdr.Class = Str_class[data[mark:p]] }
                action defTtl     { /* ... */ }
                action setTtl     { ttl := atoi(data[mark:p]); hdr.Ttl = uint32(ttl) }
                action lineCount  { lines++ }

#                action openBrace  { if brace { println("Brace already open")} ; brace = true }
#                action closeBrace { if !brace { println("Brace already closed")}; brace = false }
#                action brace      { brace }

                include "types.rl";

                nl  = [\n]+ $lineCount;
                comment = ';' [^\n]*;
                ttl = digit+ >mark;
#                bl  = ( [ \t]+
#                    | '(' $openBrace
#                    | ')' $closeBrace
#                    | (comment? nl)+ when brace
#                )+;
                bl = [ \t]+;

                rdata = [^\n]+ >mark;
                qname  = [a-zA-Z0-9.\-_]+ >mark %setQname;
                qclass = ('IN'i|'CH'i|'HS'i) >mark %setQclass;

                lhs = qname? bl %defTtl (
                      (ttl %setTtl bl (qclass bl)?)
                    | (qclass bl (ttl %setTtl bl)?)
                )?;

                rhs = (
                      ( 'A'i        bl rdata ) %setA
                    | ( 'PTR'i      bl rdata ) %setPTR
                    | ( 'TXT'i      bl rdata ) %setTXT
                    | ( 'SRV'i      bl rdata ) %setSRV
                    | ( 'CERT'i     bl rdata ) %setCERT
                    | ( 'NAPTR'i    bl rdata ) %setNAPTR
                    | ( 'AAAA'i     bl rdata ) %setAAAA
                    | ( 'SOA'i      bl rdata ) %setSOA
                    | ( 'CNAME'i    bl rdata ) %setCNAME
                    | ( 'DNAME'i    bl rdata ) %setDNAME
                    | ( 'NS'i       bl rdata ) %setNS
                    | ( 'MX'i       bl rdata ) %setMX
                    | ( 'DS'i       bl rdata ) %setDS
                    | ( 'DLV'i      bl rdata ) %setDLV
                    | ( 'TA'i       bl rdata ) %setTA
                    | ( 'DNSKEY'i   bl rdata ) %setDNSKEY
                    | ( 'RRSIG'i    bl rdata ) %setRRSIG
                    | ( 'NSEC'i     bl rdata ) %setNSEC
                    | ( 'NSEC3'i    bl rdata ) %setNSEC3
                    | ( 'NSEC3PARAM'i bl rdata ) %setNSEC3PARAM
                );

                rr = lhs rhs;
#                main := (rr? bl? ((comment? nl) when !brace))*;
                main := ((rr?|comment?) nl)*;

                write init;
                write exec;
        }%%

        if eof > -1 {
                if cs < z_first_final {
                        // No clue what I'm doing what so ever
                        if p == pe {
                                println("p", p, "pe", pe)
                                println("cs", cs, "z_first_final", z_first_final)
                                println("unexpected eof at line ", lines)
                                return z, nil
                        } else {
                                println("error at position ", p, "\"",data[mark:p],"\" at line ", lines)
                                return z, nil
                        }
                }
        }
        return z, nil
}

package dns

// Parse RRs
// With the thankful help of gdnsd and the Go examples for Ragel.

import (
    "os"
    "io"
    "net"
    "strings"
    "strconv"
)

//const _IOBUF = 65365 // See comments in gdnsd
const _IOBUF = 3e7  // TODO fix sliding window stuff in Ragel

// A Parser represents a DNS master zone file parser for a
// particular input stream.
type Parser struct {
    // nothing here yet
    buf    []byte
}

// NewParser create a new DNS master zone file parser from r.
func NewParser(r io.Reader) *Parser {
        buf := make([]byte, _IOBUF)
        n, err := r.Read(buf)
        if err != nil {
            return nil
        }
        if buf[n-1] != '\n' {
            buf[n] = '\n'
            n++
        }
        buf = buf[:n]
        p := new(Parser)
        p.buf = buf
        return p
}

// Return the rdata fields as a string slice.
// All starting whitespace is deleted.
// If i is 0 no spaces are deleted from the final rdfs.
func fields(s string, i int) (rdf []string) {
    rdf = strings.Fields(s)
    for i, _ := range rdf {
        rdf[i] = strings.TrimSpace(rdf[i])
    }
    if i > 0 && len(rdf) > i {
        // The last rdf contained embedded spaces, glue it back together.
        for j := i; j < len(rdf); j++ {
            rdf[i-1] += rdf[j]
        }
    }
    return
}

// Wrapper for strconv.Atoi*().
func atoi(s string) uint {
    i, err :=  strconv.Atoui(s)
    if err != nil {
        panic("not a number: " + s + " " + err.String())
    }
    return i
}

%%{
        machine z;
        write data;
}%%

// Token parses a zone file, but only returns the last RR read.
func (zp *Parser) RR() RR {
    z, err := zp.Zone()
    if err != nil {
        return nil
    }
    return z.Pop().(RR)
}

// All the NewReader stuff is expensive...
// only works for short io.Readers as we put the whole thing
// in a string -- needs to be extended for large files (sliding window).
func (zp *Parser) Zone() (z *Zone, err os.Error) {
        z = new(Zone)
        data := string(zp.buf)
        cs, p, pe := 0, 0, len(data)
        eof := len(data)

//        brace := false
        lines := 0
        mark := 0
        hdr := new(RR_Header)

        %%{
                action mark       { mark = p }
                action setQname   { hdr.Name = data[mark:p] }
                action setQclass  { hdr.Class = Str_class[data[mark:p]] }
                action defTtl     { /* ... */ }
                action setTtl     { ttl := atoi(data[mark:p]); hdr.Ttl = uint32(ttl) }
                action lineCount  { lines++ }

#                action openBrace  { if brace { println("Brace already open")} ; brace = true }
#                action closeBrace { if !brace { println("Brace already closed")}; brace = false }
#                action brace      { brace }

                include "types.rl";

                nl  = [\n]+ $lineCount;
                comment = ';' [^\n]*;
                ttl = digit+ >mark;
#                bl  = ( [ \t]+
#                    | '(' $openBrace
#                    | ')' $closeBrace
#                    | (comment? nl)+ when brace
#                )+;
                bl = [ \t]+;

                rdata = [^\n]+ >mark;
                qname  = [a-zA-Z0-9.\-_]+ >mark %setQname;
                qclass = ('IN'i|'CH'i|'HS'i) >mark %setQclass;

                lhs = qname? bl %defTtl (
                      (ttl %setTtl bl (qclass bl)?)
                    | (qclass bl (ttl %setTtl bl)?)
                )?;

                rhs = (
                      ( 'A'i        bl rdata ) %setA
                    | ( 'PTR'i      bl rdata ) %setPTR
                    | ( 'TXT'i      bl rdata ) %setTXT
                    | ( 'SRV'i      bl rdata ) %setSRV
                    | ( 'CERT'i     bl rdata ) %setCERT
                    | ( 'NAPTR'i    bl rdata ) %setNAPTR
                    | ( 'AAAA'i     bl rdata ) %setAAAA
                    | ( 'SOA'i      bl rdata ) %setSOA
                    | ( 'CNAME'i    bl rdata ) %setCNAME
                    | ( 'DNAME'i    bl rdata ) %setDNAME
                    | ( 'NS'i       bl rdata ) %setNS
                    | ( 'MX'i       bl rdata ) %setMX
                    | ( 'DS'i       bl rdata ) %setDS
                    | ( 'DLV'i      bl rdata ) %setDLV
                    | ( 'TA'i       bl rdata ) %setTA
                    | ( 'DNSKEY'i   bl rdata ) %setDNSKEY
                    | ( 'RRSIG'i    bl rdata ) %setRRSIG
                    | ( 'NSEC'i     bl rdata ) %setNSEC
                    | ( 'NSEC3'i    bl rdata ) %setNSEC3
                    | ( 'NSEC3PARAM'i bl rdata ) %setNSEC3PARAM
                );

                rr = lhs rhs;
#                main := (rr? bl? ((comment? nl) when !brace))*;
                main := ((rr?|comment?) nl)*;

                write init;
                write exec;
        }%%

        if eof > -1 {
                if cs < z_first_final {
                        // No clue what I'm doing what so ever
                        if p == pe {
        println("p", p, "pe", pe)
        println("cs", cs, "z_first_final", z_first_final)
                                println("unexpected eof at line ", lines)
                                return z, nil
                        } else {
                                println("error at position ", p, "\"",data[mark:p],"\" at line ", lines)
                                return z, nil
                        }
                }
        }
        return z, nil
}


Return returned stuff on a channel

Return returned stuff on a channel

package dns

// Parse RRs
// With the thankful help of gdnsd and the Go examples for Ragel.

import (
    "os"
    "io"
    "net"
    "time"
    "strings"
    "strconv"
)

const _IOBUF = MaxMsgSize

// A Parser represents a DNS parser for a
// particular input stream.
type Parser struct {
        // nothing here yet
        buf    []byte
}

type ParseError struct {
        Error string
        name  string
        line  int
}

func (e *ParseError) String() string {
        s := e.Error + ": \""  + e.name + "\" at line: " + strconv.Itoa(e.line)
        return s
}

// NewParser creates a new DNS file parser from r.
func NewParser(r io.Reader) *Parser {
        buf := make([]byte, _IOBUF)
        n, err := r.Read(buf)
        if err != nil {
            return nil
        }
        if buf[n-1] != '\n' {
            buf[n] = '\n'
            n++
        }
        buf = buf[:n]
        p := new(Parser)
        p.buf = buf
        return p
}

// Translate the RRSIG's incep. and expir. times from
// string values ("20110403154150") to an integer.
// Taking into account serial arithmetic (RFC 1982)
func dateToTime(s string) (uint32, os.Error) {
    t, e := time.Parse("20060102150405", s)
    if e != nil {
        return 0, e
    }
    mod := t.Seconds() / Year68
    ti := uint32(t.Seconds() - (mod * Year68))
    return ti, nil
}

// Return the rdata fields as a string slice.
// All starting whitespace is deleted.
// If i is 0 no spaces are deleted from the final rdfs.
func fields(s string, i int) (rdf []string) {
    rdf = strings.Fields(s)
    for i, _ := range rdf {
        rdf[i] = strings.TrimSpace(rdf[i])
    }
    if i > 0 && len(rdf) > i {
        // The last rdf contained embedded spaces, glue it back together.
        for j := i; j < len(rdf); j++ {
            rdf[i-1] += rdf[j]
        }
    }
    return
}

%%{
        machine z;
        write data;
}%%

// RR parses a zone file, but only returns the last RR read.
func (zp *Parser) RR() (RR, os.Error) {
    z, err := zp.Zone()
    if err != nil {
        return nil, err
    }
    return z.PopRR(), nil
}

// Run parses an DNS master zone file. It returns each parsed RR
// on the channel as soon as it has been parsed.
func (zp *Parser) Run() (err os.Error) {
        z = NewZone()
        data := string(zp.buf)
        cs, p, pe := 0, 0, len(data)
        eof := len(data)

//        brace := false
        l := 1  // or... 0?
        mark := 0
        var hdr RR_Header

        %%{

                action mark       { mark = p }
                action lineCount  { l++ }
                action setQname   { if ! IsDomainName(data[mark:p]) {
                                            return z, &ParseError{Error: "bad qname: " + data[mark:p], line: l}
                                    }
                                    hdr.Name = data[mark:p]
                                  }
                action errQclass  { return z, &ParseError{Error: "bad qclass: " + data[mark:p], line: l} }
                action setQclass  { hdr.Class = str_class[data[mark:p]] }
                action defTtl     { /* ... */ }
                action errTtl     { /* ... */ }
                action setTtl     { i, _ := strconv.Atoui(data[mark:p]); hdr.Ttl = uint32(i) }
#                action openBrace  { if brace { println("Brace already open")} ; brace = true }
#                action closeBrace { if !brace { println("Brace already closed")}; brace = false }
#                action brace      { brace }

                include "types.rl";

                nl  = [\n]+ $lineCount;
                comment = ';' [^\n]*;
                ttl = digit+ >mark; #@err(errTtl)
#                bl  = ( [ \t]+
#                    | '(' $openBrace
#                    | ')' $closeBrace
#                    | (comment? nl)+ when brace
#                )+;
                bl = [ \t]+;

                rdata = [^\n]+ >mark;
                qname  = [a-zA-Z0-9.\-_*]+ >mark %setQname;
                qclass = ('IN'i|'CH'i|'HS'i) >mark %setQclass; # @err(errQclass);

                lhs = qname? bl %defTtl (
                      (ttl %setTtl bl (qclass bl)?)
                    | (qclass bl (ttl %setTtl bl)?)
                )?;

                rhs = (
                      ( 'A'i        bl rdata ) %setA
                    | ( 'PTR'i      bl rdata ) %setPTR
                    | ( 'TXT'i      bl rdata ) %setTXT
                    | ( 'SRV'i      bl rdata ) %setSRV
                    | ( 'CERT'i     bl rdata ) %setCERT
                    | ( 'NAPTR'i    bl rdata ) %setNAPTR
                    | ( 'AAAA'i     bl rdata ) %setAAAA
                    | ( 'SOA'i      bl rdata ) %setSOA
                    | ( 'CNAME'i    bl rdata ) %setCNAME
                    | ( 'DNAME'i    bl rdata ) %setDNAME
                    | ( 'NS'i       bl rdata ) %setNS
                    | ( 'MX'i       bl rdata ) %setMX
                    | ( 'DS'i       bl rdata ) %setDS
                    | ( 'DLV'i      bl rdata ) %setDLV
                    | ( 'TA'i       bl rdata ) %setTA
                    | ( 'DNSKEY'i   bl rdata ) %setDNSKEY
                    | ( 'RRSIG'i    bl rdata ) %setRRSIG
                    | ( 'NSEC'i     bl rdata ) %setNSEC
                    | ( 'NSEC3'i    bl rdata ) %setNSEC3
                    | ( 'SSHFP'i    bl rdata ) %setSSHFP
                    | ( 'NSEC3PARAM'i bl rdata ) %setNSEC3PARAM
                );

                rr = lhs rhs;
#                main := (rr? bl? ((comment? nl) when !brace))*;
                main := ((rr?|comment?) nl)*;

                write init;
                write exec;
        }%%

        if eof > -1 {
                if cs < z_first_final {
                        // No clue what I'm doing what so ever
                        if p == pe {
                                println("p", p, "pe", pe)
                                println("cs", cs, "z_first_final", z_first_final)
                                println("unexpected eof at line ", l)
                                return z, nil
                        } else {
                                println("error at position ", p, "\"",data[mark:p],"\" at line ", l)
                                return z, nil
                        }
                }
        }
        return z, nil
}

package dns

// Parse RRs
// With the thankful help of gdnsd and the Go examples for Ragel.

import (
    "os"
    "io"
    "net"
    "time"
    "strings"
    "strconv"
)

const _IOBUF = MaxMsgSize

// A Parser represents a DNS parser for a
// particular input stream.
type Parser struct {
        // nothing here yet
        buf    []byte
}

type ParseError struct {
        Error string
        name  string
        line  int
}

func (e *ParseError) String() string {
        s := e.Error + ": \""  + e.name + "\" at line: " + strconv.Itoa(e.line)
        return s
}

// NewParser creates a new DNS file parser from r.
func NewParser(r io.Reader) *Parser {
        buf := make([]byte, _IOBUF)
        n, err := r.Read(buf)
        if err != nil {
            return nil
        }
        if buf[n-1] != '\n' {
            buf[n] = '\n'
            n++
        }
        buf = buf[:n]
        p := new(Parser)
        p.buf = buf
        return p
}

// Translate the RRSIG's incep. and expir. times from
// string values ("20110403154150") to an integer.
// Taking into account serial arithmetic (RFC 1982)
func dateToTime(s string) (uint32, os.Error) {
    t, e := time.Parse("20060102150405", s)
    if e != nil {
        return 0, e
    }
    mod := t.Seconds() / Year68
    ti := uint32(t.Seconds() - (mod * Year68))
    return ti, nil
}

// Return the rdata fields as a string slice.
// All starting whitespace is deleted.
// If i is 0 no spaces are deleted from the final rdfs.
func fields(s string, i int) (rdf []string) {
    rdf = strings.Fields(s)
    for i, _ := range rdf {
        rdf[i] = strings.TrimSpace(rdf[i])
    }
    if i > 0 && len(rdf) > i {
        // The last rdf contained embedded spaces, glue it back together.
        for j := i; j < len(rdf); j++ {
            rdf[i-1] += rdf[j]
        }
    }
    return
}

%%{
        machine z;
        write data;
}%%

// RR parses a zone file, but only returns the last RR read.
func (zp *Parser) RR() (RR, os.Error) {
    z, err := zp.Zone()
    if err != nil {
        return nil, err
    }
    return z.PopRR(), nil
}

// Zone parses an DNS master zone file.
func (zp *Parser) Zone() (z *Zone, err os.Error) {
        z = NewZone()
        data := string(zp.buf)
        cs, p, pe := 0, 0, len(data)
        eof := len(data)

//        brace := false
        l := 1  // or... 0?
        mark := 0
        var hdr RR_Header

        %%{

                action mark       { mark = p }
                action lineCount  { l++ }
                action setQname   { if ! IsDomainName(data[mark:p]) {
                                            return z, &ParseError{Error: "bad qname: " + data[mark:p], line: l}
                                    }
                                    hdr.Name = data[mark:p]
                                  }
                action errQclass  { return z, &ParseError{Error: "bad qclass: " + data[mark:p], line: l} }
                action setQclass  { hdr.Class = str_class[data[mark:p]] }
                action defTtl     { /* ... */ }
                action errTtl     { /* ... */ }
                action setTtl     { i, _ := strconv.Atoui(data[mark:p]); hdr.Ttl = uint32(i) }
#                action openBrace  { if brace { println("Brace already open")} ; brace = true }
#                action closeBrace { if !brace { println("Brace already closed")}; brace = false }
#                action brace      { brace }

                include "types.rl";

                nl  = [\n]+ $lineCount;
                comment = ';' [^\n]*;
                ttl = digit+ >mark; #@err(errTtl)
#                bl  = ( [ \t]+
#                    | '(' $openBrace
#                    | ')' $closeBrace
#                    | (comment? nl)+ when brace
#                )+;
                bl = [ \t]+;

                rdata = [^\n]+ >mark;
                qname  = [a-zA-Z0-9.\-_*]+ >mark %setQname;
                qclass = ('IN'i|'CH'i|'HS'i) >mark %setQclass; # @err(errQclass);

                lhs = qname? bl %defTtl (
                      (ttl %setTtl bl (qclass bl)?)
                    | (qclass bl (ttl %setTtl bl)?)
                )?;

                rhs = (
                      ( 'A'i        bl rdata ) %setA
                    | ( 'PTR'i      bl rdata ) %setPTR
                    | ( 'TXT'i      bl rdata ) %setTXT
                    | ( 'SRV'i      bl rdata ) %setSRV
                    | ( 'CERT'i     bl rdata ) %setCERT
                    | ( 'NAPTR'i    bl rdata ) %setNAPTR
                    | ( 'AAAA'i     bl rdata ) %setAAAA
                    | ( 'SOA'i      bl rdata ) %setSOA
                    | ( 'CNAME'i    bl rdata ) %setCNAME
                    | ( 'DNAME'i    bl rdata ) %setDNAME
                    | ( 'NS'i       bl rdata ) %setNS
                    | ( 'MX'i       bl rdata ) %setMX
                    | ( 'DS'i       bl rdata ) %setDS
                    | ( 'DLV'i      bl rdata ) %setDLV
                    | ( 'TA'i       bl rdata ) %setTA
                    | ( 'DNSKEY'i   bl rdata ) %setDNSKEY
                    | ( 'RRSIG'i    bl rdata ) %setRRSIG
                    | ( 'NSEC'i     bl rdata ) %setNSEC
                    | ( 'NSEC3'i    bl rdata ) %setNSEC3
                    | ( 'SSHFP'i    bl rdata ) %setSSHFP
                    | ( 'NSEC3PARAM'i bl rdata ) %setNSEC3PARAM
                );

                rr = lhs rhs;
#                main := (rr? bl? ((comment? nl) when !brace))*;
                main := ((rr?|comment?) nl)*;

                write init;
                write exec;
        }%%

        if eof > -1 {
                if cs < z_first_final {
                        // No clue what I'm doing what so ever
                        if p == pe {
                                println("p", p, "pe", pe)
                                println("cs", cs, "z_first_final", z_first_final)
                                println("unexpected eof at line ", l)
                                return z, nil
                        } else {
                                println("error at position ", p, "\"",data[mark:p],"\" at line ", l)
                                return z, nil
                        }
                }
        }
        return z, nil
}


More whitespace fixes

More whitespace fixes

/* Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>

#include "http11.h"

struct _HTTPParserState {
  int cs;
  int mark;
};

static int calc_offset(const char *fpc, const char *buf) {
    return fpc - buf;
}

static int calc_length(const char *fpc, const char *buf, const int offset) {
    return fpc - buf - offset;
}

static const char *create_ptr(const char *buf, const int offset) {
    return buf + offset;
}

void handle_callback(HTTPParser *parser,
                     const char *fpc,
                     const char *data,
                     int (*callback)(const char *, size_t)) {
    if (callback != NULL) {
        parser->error = callback(
            create_ptr(data, parser->state->mark),
            calc_length(fpc, data, parser->state->mark)
        );
    }
}

%%{
    machine http_parser;

    action mark {
        parser->state->mark = calc_offset(fpc, data);
    }

    action request_method {
        handle_callback(parser, fpc, data, parser->request_method);

        if (parser->error) {
            fgoto *http_parser_error;
        }
    }

    action request_uri {
        handle_callback(parser, fpc, data, parser->request_uri);

        if (parser->error) {
            fgoto *http_parser_error;
        }
    }

    action http_version {
        handle_callback(parser, fpc, data, parser->http_version);

        if (parser->error) {
            fgoto *http_parser_error;
        }
    }

    CRLF = ( "\r\n" | "\n" ) ;
    SP = " " ;

    tchar = ( "!" | "#" | "$" | "%" | "&" | "'" | "*" | "+" | "-" | "." | "^" |
              "_" | "`" | "|" | "~" | digit | alpha ) ;
    token = tchar+ ;

    method = token >mark %request_method ;
    request_target = ( any -- CRLF )+ >mark %request_uri ;
    http_version = ( "HTTP" "/" digit "." digit ) >mark %request_uri ;

    request_line = method SP request_target SP http_version CRLF ;
    http_message = ( request_line ) CRLF ;

main := http_message;

}%%

%% write data;

HTTPParser *HTTPParser_create() {
    HTTPParser *parser = malloc(sizeof(*parser));

    if (!parser) {
        return NULL;
    }

    parser->state = malloc(sizeof *parser->state);

    if (!parser->state) {
        HTTPParser_destroy(parser);
        return NULL;
    }

    return parser;
}

void HTTPParser_init(HTTPParser *parser) {
    %% access parser->state->;
    %% write init;

    parser->state->mark = 0;

    parser->finished = false;
    parser->error = 0;
}

size_t HTTPParser_execute(HTTPParser *parser, const char *data, size_t len, size_t off) {
    const char *p = data + off;
    const char *pe = data + len;

    %% access parser->state->;
    %% write exec;

    if (parser->state->cs == http_parser_error || parser->state->cs >= http_parser_first_final ) {
        parser->finished = true;

        if (parser->state->cs == http_parser_error && !parser->error) {
            parser->error = 1;
        }
    }

    return 1;
}

void HTTPParser_destroy(HTTPParser *parser) {
    if (parser) {
        free(parser->state);
        free(parser);
    }
}