Ejemplo n.º 1
0
    void initialize(vui_t const& elems) {

	this->data = elems;
        this->len = elems.size();
        this->repr.clear();

        DCERR("len: "<<this->len<<endl);

        const size_t ntables = log2(this->len) + 1;
        this->repr.resize(ntables);

        DCERR("ntables: "<<ntables<<endl);

        this->repr[0].resize(this->len);
        for (size_t i = 0; i < this->len; ++i) {
	    // This is the identity mapping, since the MAX element in
	    // a range of length 1 is the element itself.
            this->repr[0][i] = i;
        }

        for (size_t i = 1; i < ntables; ++i) {
	    /* The previous 'block size' */
            const uint_t pbs = 1<<(i-1);

            /* bs is the 'block size'. i.e. The number of elements
	     * from the data that are used to computed the max value
	     * and store it at repr[i][...].
	     */
            const uint_t bs = 1<<i;

	    /* The size of the vector at repr[i]. We need to resize it
	     * to this size.
	     */
            const size_t vsz = this->len - bs + 1;

	    DCERR("starting i: "<<i<<" bs: "<<bs<<endl);

            this->repr[i].resize(vsz);

            // cerr<<"i: "<<i<<", vsz: "<<vsz<<endl;

            vui_t& curr = this->repr[i];
            vui_t& prev = this->repr[i - 1];

            for (size_t j = 0; j < vsz; ++j) {
                // 'j' is the starting index of a block of size 'bs'
		const uint_t prev_elem1 = data[prev[j]];
		const uint_t prev_elem2 = data[prev[j+pbs]];
                if (prev_elem1 > prev_elem2) {
                    curr[j] = prev[j];
                }
                else {
                    curr[j] = prev[j+pbs];
                }
                // cerr<<"curr["<<j<<"] = "<<curr[j].first<<endl;
            }
            // cerr<<"done with i: "<<i<<endl;
        }
        // cerr<<"initialize() completed"<<endl;
    }
Ejemplo n.º 2
0
void
parse_options(int argc, char *argv[]) {
    int c;

    while (1) {
        int option_index = 0;
        static struct option long_options[] = {
            {"file", 1, 0, 'f'},
            {"port", 1, 0, 'p'},
            {"sorted", 0, 0, 's'},
            {"help", 0, 0, 'h'},
            {0, 0, 0, 0}
        };

        c = getopt_long(argc, argv, "f:p:sh",
                        long_options, &option_index);

        if (c == -1)
            break;

        switch (c) {
        case 0:
        case 'f':
            DCERR("File: "<<optarg<<endl);
            ac_file = optarg;
            break;

        case 'p':
            DCERR("Port: "<<optarg<<" ("<<atoi(optarg)<<")\n");
            port = optarg;
            break;

        case 's':
            DCERR("File is Sorted\n");
            ac_sorted = true;
            break;
          
        case 'h':
            opt_show_help = true;
            break;

        case '?':
            cerr<<"ERROR::Invalid option: "<<optopt<<endl;
            break;
        }
    }


}
Ejemplo n.º 3
0
 void
 on_snippet(const char *data, int len) {
     if (len && this->psnippet_proxy) {
         const char *base = this->mem_base + this->buff_offset + 
             (data - this->buff);
         DCERR("on_snippet::base: "<<(void*)base<<", len: "<<len<<"\n");
         this->psnippet_proxy->assign(base, len);
     }
 }
Ejemplo n.º 4
0
    // validate the smbios table entry point
    bool validateDMITableEntryPoint(
        const smbiosLowlevel::dmi_table_entry_point *tempTEP,
        bool strict,
        ParseExceptionImpl &parseException
        )
    {
        // This code checks for the following:
        //       entry point structure checksum : As per the specs
        //       anchor string : As per the specs
        //
        bool retval = true;

        u8 checksum = 0;
        const u8 *ptr = reinterpret_cast<const u8*>(tempTEP);
        // don't overrun tempTEP if BIOS is buggy... (note sizeof() test here)
        //      added especially to deal with buggy Intel BIOS.
        for( unsigned int i = 0; i < sizeof(*tempTEP); ++i )
        {
            // stupid stuff to avoid MVC++ .NET runtime exception check for cast to different size
            checksum = (checksum + ptr[i]) & 0xFF;
        }

        ostringstream oss;

        DCERR("_DMI_ anchor: " << tempTEP->anchor[0] << tempTEP->anchor[1] << tempTEP->anchor[2] << tempTEP->anchor[3] << tempTEP->anchor[4] << tempTEP->anchor[5] << endl);
        if(memcmp(tempTEP->anchor,"_DMI_",5)!=0) // Checking intermediate anchor string
        {
            oss << _("Intermediate anchor string does not match. anchor string: %(dmi_anchor)s") << endl;
            retval = false;  // validation failed
        }

        DCERR("_DMI_ checksum: " << (int)checksum << endl);
        if(checksum) // Checking entry point structure checksum
        {
            oss << _("Checksum check for table entry point should be zero. checksum: %(dmi_checksum)i ") << endl;
            retval = false;  // validation failed
        }

        parseException.setParameter("dmi_anchor", reinterpret_cast<const char *>(tempTEP->anchor));
        parseException.setParameter("dmi_checksum", static_cast<int>(checksum));

        return retval;
    }
Ejemplo n.º 5
0
    bool SmbiosMemoryStrategy::getSmbiosTable(const u8 **smbiosBuffer, smbiosLowlevel::smbios_table_entry_point *table_header, bool strict)
    {
        bool ret = false;
        try
        {
            // allocates no mem
            DCERR("trying SmbiosMemoryStrategy" << endl);
            getSmbiosTableHeader(table_header, strict);

            // allocates mem, but frees on exception
            getSmbiosTableBuf(smbiosBuffer, *table_header);
            if(smbiosBuffer)
                    ret = true;
        }
        catch( MARK_UNUSED const exception &e)
        {
            DCERR("got Exception: " << e.what() << endl);
        }

        DCERR("  ret for SmbiosMemoryStrategy is: " << ret << endl);
        return ret;
    }
Ejemplo n.º 6
0
 void
 on_snippet(const char *data, int len) {
     if (len && this->psnippet_proxy) {
         const char *base = this->mem_base + this->buff_offset + 
             (data - this->buff);
         if (base < if_mmap_addr || base + len > if_mmap_addr + if_length) {
             fprintf(stderr, "base: %p, if_mmap_addr: %p, if_mmap_addr+if_length: %p\n", base, if_mmap_addr, if_mmap_addr + if_length);
             assert(base >= if_mmap_addr);
             assert(base <= if_mmap_addr + if_length);
             assert(base + len <= if_mmap_addr + if_length);
         }
         DCERR("on_snippet::base: "<<(void*)base<<", len: "<<len<<"\n");
         this->psnippet_proxy->assign(base, len);
     }
 }
Ejemplo n.º 7
0
static void*
handle_suggest(enum mg_event event,
               struct mg_connection *conn,
               const struct mg_request_info *request_info) {

    ++nreq;
    if (building) {
        print_HTTP_response(conn, 412, "Busy");
        return (void*)"";
    }

    std::string q    = get_qs(request_info, "q");
    std::string sn   = get_qs(request_info, "n");
    std::string cb   = get_qs(request_info, "callback");
    std::string type = get_qs(request_info, "type");

    DCERR("handle_suggest::q:"<<q<<", sn:"<<sn<<", callback: "<<cb<<endl);

    unsigned int n = sn.empty() ? NMAX : atoi(sn.c_str());
    if (n > NMAX) {
        n = NMAX;
    }

    const bool has_cb = !cb.empty();
    if (has_cb && !is_valid_cb(cb)) {
        print_HTTP_response(conn, 400, "Invalid Request");
        return (void*)"";
    }

    print_HTTP_response(conn, 200, "OK");

    str_lowercase(q);
    vp_t results = suggest(pm, st, q, n);

    /*
      for (size_t i = 0; i < results.size(); ++i) {
      mg_printf(conn, "%s:%d\n", results[i].first.c_str(), results[i].second);
      }
    */
    if (has_cb) {
        mg_printf(conn, "%s(%s);\n", cb.c_str(), results_json(q, results, type).c_str());
    }
    else {
        mg_printf(conn, "%s\n", results_json(q, results, type).c_str());
    }

    return (void*)"";
}
Ejemplo n.º 8
0
static void handle_suggest(client_t *client, parsed_url_t &url) {
    ++nreq;
    std::string body;
    headers_t headers;
    headers["Cache-Control"] = "no-cache";

    if (building) {
        write_response(client, 412, "Busy", headers, body);
        return;
    }

    std::string q    = unescape_query(url.query["q"]);
    std::string sn   = url.query["n"];
    std::string cb   = unescape_query(url.query["callback"]);
    std::string type = unescape_query(url.query["type"]);

    DCERR("handle_suggest::q:"<<q<<", sn:"<<sn<<", callback: "<<cb<<endl);

    unsigned int n = sn.empty() ? NMAX : atoi(sn.c_str());
    if (n > NMAX) {
        n = NMAX;
    }
    if (n < 1) {
        n = 1;
    }

    const bool has_cb = !cb.empty();
    str_lowercase(q);
    vp_t results = suggest(pm, st, q, n);

    /*
      for (size_t i = 0; i < results.size(); ++i) {
      mg_printf(conn, "%s:%d\n", results[i].first.c_str(), results[i].second);
      }
    */
    headers["Content-Type"] = "text/plain; charset=UTF-8";
    if (has_cb) {
        body = cb + "(" + results_json(q, results, type) + ");\n";
    }
    else {
        body = results_json(q, results, type) + "\n";
    }

    write_response(client, 200, "OK", headers, body);
}
Ejemplo n.º 9
0
void serve_request(client_t *client) {
    parsed_url_t url;
    parse_URL(client->url, url);
    std::string &request_uri = url.path;
    DCERR("request_uri: " << request_uri << endl);

    if (request_uri == "/face/suggest/") {
        handle_suggest(client, url);
    }
    else if (request_uri == "/face/import/") {
        handle_import(client, url);
    }
    else if (request_uri == "/face/export/") {
        handle_export(client, url);
    }
    else if (request_uri == "/face/stats/") {
        handle_stats(client, url);
    }
    else {
        handle_invalid_request(client, url);
    }
}
Ejemplo n.º 10
0
int
do_import(std::string file, int sorted, uint_t limit, 
          int &rnadded, int &rnlines) {
#if defined USE_CXX_IO
    std::ifstream fin(file.c_str());
#else
    FILE *fin = fopen(file.c_str(), "r");
#endif

    int fd = open(file.c_str(), O_RDONLY);

    // Potential race condition + not checking for return value
    if_length = file_size(file.c_str());

    DCERR("handle_import::file:"<<file<<endl);

    if (!fin || !fd) {
        return -IMPORT_FILE_NOT_FOUND;
    }
    else {
        building = true;
        int nlines = 0;
        int foffset = 0;

        if (if_mmap_addr) {
            munmap(if_mmap_addr, if_length);
        }

        // mmap() the input file in
        if_mmap_addr = (char*)mmap(NULL, if_length, PROT_READ, MAP_SHARED, fd, 0);
        if (!if_mmap_addr) {
            fclose(fin);
            close(fd);
            return -IMPORT_FILE_NOT_FOUND;
        }

        pm.repr.clear();
        char buff[INPUT_LINE_SIZE];

        while (
#if defined USE_CXX_IO
               fin
#else
            !feof(fin)
#endif
               && limit--) {

            buff[0] = '\0';

#if defined USE_CXX_IO
            fin.getline(buff, INPUT_LINE_SIZE);
            const int llen = fin.gcount();
            buff[INPUT_LINE_SIZE - 1] = '\0';
#else
            char *got = fgets(buff, INPUT_LINE_SIZE, fin);
            if (!got) {
                break;
            }
            const int llen = strlen(buff);
            if (llen && buff[llen-1] == '\n') {
                buff[llen-1] = '\0';
            }
#endif

            ++nlines;

            int weight = 0;
            std::string phrase;
            StringProxy snippet;
            InputLineParser(if_mmap_addr, foffset, buff, &weight, &phrase, &snippet).start_parsing();

            foffset += llen;

            if (!phrase.empty()) {
                str_lowercase(phrase);
                DCERR("Adding: "<<weight<<", "<<phrase<<", "<<std::string(snippet)<<endl);
                pm.insert(weight, phrase, snippet);
            }
        }

        fclose(fin);
        pm.finalize(sorted);
        vui_t weights;
        for (size_t i = 0; i < pm.repr.size(); ++i) {
            weights.push_back(pm.repr[i].weight);
        }
        st.initialize(weights);

        rnadded = weights.size();
        rnlines = nlines;

        building = false;
    }

    return 0;
}
Ejemplo n.º 11
0
    void
    start_parsing() {
        int i = 0;
        int n = 0;
        const char *p_start = NULL, *s_start = NULL;
        int p_len = 0, s_len = 0;

        while (this->buff[i]) {
            char ch = this->buff[i];
            DCERR("State: "<<this->state<<", read: "<<ch<<"\n");

            switch (this->state) {
            case ILP_BEFORE_NON_WS:
                if (!isspace(ch)) {
                    this->state = ILP_WEIGHT;
                }
                else {
                    ++i;
                }
                break;

            case ILP_WEIGHT:
                if (isdigit(ch)) {
                    n *= 10;
                    n += (ch - '0');
                    ++i;
                }
                else {
                    this->state = ILP_BEFORE_PTAB;
                    on_weight(n);
                }
                break;

            case ILP_BEFORE_PTAB:
                if (ch == '\t') {
                    this->state = ILP_AFTER_PTAB;
                }
                ++i;
                break;

            case ILP_AFTER_PTAB:
                if (isspace(ch)) {
                    ++i;
                }
                else {
                    p_start = this->buff + i;
                    this->state = ILP_PHRASE;
                }
                break;

            case ILP_PHRASE:
                // DCERR("State: ILP_PHRASE: "<<buff[i]<<endl);
                if (ch != '\t') {
                    ++p_len;
                }
                else {
                    // Note: Skip to ILP_SNIPPET since the snippet may
                    // start with a white-space that we wish to
                    // preserve.
                    // 
                    // this->state = ILP_AFTER_STAB;
                    this->state = ILP_SNIPPET;
                    s_start = this->buff + i + 1;
                }
                ++i;
                break;

            case ILP_AFTER_STAB:
                if (isspace(ch)) {
                    this->state = ILP_SNIPPET;
                    s_start = this->buff + i;
                }
                else {
                    ++i;
                }
                break;

            case ILP_SNIPPET:
                ++i;
                ++s_len;
                break;

            };
        }
        on_phrase(p_start, p_len);
        on_snippet(s_start, s_len);
    }
Ejemplo n.º 12
0
    // validate the smbios table entry point
    bool validateSmbiosTableEntryPoint(
        const smbiosLowlevel::smbios_table_entry_point *tempTEP,
        bool strict,
        ParseExceptionImpl &parseException
        )
    {
        // This code checks for the following:
        //       entry point structure checksum : As per the specs
        //       smbios major version : As per the specs
        //       Intermediate anchor string : As per the specs
        //
        // This code does not check the following:
        //      intermediate checksum: the main checksum covers the
        //      entire area
        //          and should be sufficient, plus there is a
        //          possibility for
        //          BIOS bugs in this area.
        //
        //      minor version: according to the spec, this parser should
        //      work
        //          with any change in minor version. The spec says this
        //          parser
        //          will break if major version changes, so we check
        //          that.
        //

        bool retval = true;

        u8 checksum = 0;
        const u8 *ptr = reinterpret_cast<const u8*>(tempTEP);
        // don't overrun tempTEP if BIOS is buggy... (note sizeof() test here)
        //      added especially to deal with buggy Intel BIOS.
        for( unsigned int i = 0; (i < static_cast<unsigned int>(tempTEP->eps_length)) && (i < sizeof(*tempTEP)); ++i )
        {
            // stupid stuff to avoid MVC++ .NET runtime exception check for cast to different size
            checksum = (checksum + ptr[i]) & 0xFF;
        }

        ostringstream oss;
        oss << _("validation of table entry point failed") << endl;

        validateDMITableEntryPoint( &(tempTEP->dmi), strict, parseException );

        DCERR("strict table checking: " << strict << endl);


        DCERR("_SM_ checksum: " << (int)checksum << endl);

        if(checksum) // Checking entry point structure checksum
        {
            oss << _("Checksum check for table entry point should be zero. checksum: %(checksum)i ") << endl;
            retval = false;  // validation failed
        }

        DCERR("major_ver: " << (int)tempTEP->major_ver << endl);
        if(tempTEP->major_ver!=0x02)     // Checking smbios major version
        {
            oss << _("Major version of table entry point should be 2: %(major_version)i") << endl;
            retval = false;  // validation failed
        }

        // Entry Point Length field is at least 0x1f.
        DCERR("eps_length: " << (int)tempTEP->eps_length << endl);
        if(tempTEP->eps_length < 0x0f)
        {
            oss << _("Entry Point Length field is at least 0x1f : %(eps_length)i") << endl;
            retval = false;  // validation failed
        }

        parseException.setParameter("checksum", static_cast<int>(checksum));
        parseException.setParameter("major_version", static_cast<int>(tempTEP->major_ver));
        parseException.setParameter("eps_length", static_cast<int>(tempTEP->eps_length));
        parseException.setMessageString(oss.str());

        return retval;
    }
Ejemplo n.º 13
0
    // allocates no memory, constructs no objects.
    // can raise an exception
    void SmbiosMemoryStrategy::getSmbiosTableHeader(smbiosLowlevel::smbios_table_entry_point *table_header, bool strict)
    {
        memory::IMemory *mem = memory::MemoryFactory::getFactory()->getSingleton();

        unsigned long fp = E_BLOCK_START;
        if( offset )
            fp = offset;

        ParseExceptionImpl parseException;
        if( offset )
        {
            DCERR("SmbiosMemoryStrategy::getSmbiosTableHeader() using hardcoded offset: " << hex << offset << endl);
            parseException.setMessageString(_("SMBIOS Header not found at offset: %(offsetValue)i"));
            parseException.setParameter("offsetValue",offset);
        }
        else
        {
            DCERR("SmbiosMemoryStrategy::getSmbiosTableHeader() Memory scan for smbios table." << endl);
            parseException.setMessageString(_("SMBIOS Header not found in search."));
        }

        // tell the memory subsystem that it can optimize here and 
        // keep memory open while we scan rather than open/close/open/close/... 
        // for each fillBuffer() call
        // 
        // this would be safer if we used spiffy c++ raii technique here
        mem->decReopenHint();

        smbios_table_entry_point tempTEP;
        memset(&tempTEP, 0, sizeof(tempTEP));
        while ( (fp + sizeof(tempTEP)) < F_BLOCK_END)
        {
            mem->fillBuffer(
                reinterpret_cast<u8 *>(&tempTEP),
                fp,
                sizeof(tempTEP)
            );

            // search for promising looking headers
            // first, look for old-style DMI header
            if (memcmp (&tempTEP, "_DMI_", 5) == 0)
            {
                DCERR("Found _DMI_ anchor. Trying to parse legacy DMI structure." << endl);
                dmi_table_entry_point *dmiTEP = reinterpret_cast<dmi_table_entry_point *>(&tempTEP);
                memmove(&(tempTEP.dmi), &dmiTEP, sizeof(dmi_table_entry_point));
                // fake the rest of the smbios table entry point...
                tempTEP.major_ver=2;
                tempTEP.minor_ver=0;
                if(validateDMITableEntryPoint(dmiTEP, strict, parseException))
                {
                    DCERR("Found valid _DMI_ entry point at offset: " << fp << endl);
                    break;
                }
            }

            // then, look for new-style smbios header. This will always
            // occur before _DMI_ in memory
            if (offset || (memcmp (&tempTEP, "_SM_", 4) == 0))
            {
                // if we are passed a hardcoded offset (EFI?), it is possible
                // that machine doesnt have _SM_ anchor, but still has valid
                // table. Just try validating the table and skip _SM_ check.
                DCERR("Found _SM_ anchor or using hardcoded offset. Trying to parse Smbios Entry Point." << endl);
                if(validateSmbiosTableEntryPoint(&tempTEP, strict, parseException))
                {
                    DCERR("Found valid _SM_ entry point at offset: " << fp << endl);
                    break;
                }
            }

            // previous if() would have broken out if we have a valid
            // table header. if offset is set, then we are not supposed
            // to be scanning through memory. We didn't find a table,
            // so there is nothing to do but raise an exception.
            if (offset)
            {
                // dont need memory optimization anymore
                mem->incReopenHint();
                throw parseException; // previously set up.
            }

            fp += 16;
        }

        // dont need memory optimization anymore
        mem->incReopenHint();

        // bad stuff happened if we got to here and fp > 0xFFFFFL
        if ((fp + sizeof(tempTEP)) >= F_BLOCK_END)
            throw parseException; // previously set up.

        // found it. set offset for future reference (no need to search.)
        offset = fp;
        memcpy( const_cast<smbios_table_entry_point *>(table_header), &tempTEP, sizeof(*table_header) );
    }
Ejemplo n.º 14
0
int
do_import(std::string file, uint_t limit, 
          int &rnadded, int &rnlines) {
    bool is_input_sorted = true;
#if defined USE_CXX_IO
    std::ifstream fin(file.c_str());
#else
    FILE *fin = fopen(file.c_str(), "r");
#endif

    int fd = open(file.c_str(), O_RDONLY);

    DCERR("handle_import::file:" << file << "[fin: " << (!!fin) << ", fd: " << fd << "]" << endl);

    if (!fin || fd == -1) {
        perror("fopen");
        return -IMPORT_FILE_NOT_FOUND;
    }
    else {
        building = true;
        int nlines = 0;
        int foffset = 0;

        if (if_mmap_addr) {
            int r = munmap(if_mmap_addr, if_length);
            if (r < 0) {
                perror("munmap");
                building = false;
                return -IMPORT_MUNMAP_FAILED;
            }
        }

        // Potential race condition + not checking for return value
        if_length = file_size(file.c_str());

        // mmap() the input file in
        if_mmap_addr = (char*)mmap(NULL, if_length, PROT_READ, MAP_SHARED, fd, 0);
        if (if_mmap_addr == MAP_FAILED) {
            fprintf(stderr, "length: %llu, fd: %d\n", if_length, fd);
            perror("mmap");
            if (fin) { fclose(fin); }
            if (fd != -1) { close(fd); }
            building = false;
            return -IMPORT_MMAP_FAILED;
        }

        pm.repr.clear();
        char buff[INPUT_LINE_SIZE];
        std::string prev_phrase;

        while (!is_EOF(fin) && limit--) {
            buff[0] = '\0';

            int llen = -1;
            get_line(fin, buff, INPUT_LINE_SIZE, llen);
            if (llen == -1) {
                break;
            }

            ++nlines;

            int weight = 0;
            std::string phrase;
            StringProxy snippet;
            InputLineParser(if_mmap_addr, foffset, buff, &weight, &phrase, &snippet).start_parsing();

            foffset += llen;

            if (!phrase.empty()) {
                str_lowercase(phrase);
                DCERR("Adding: " << weight << ", " << phrase << ", " << std::string(snippet) << endl);
                pm.insert(weight, phrase, snippet);
            }
            if (is_input_sorted && prev_phrase <= phrase) {
                prev_phrase.swap(phrase);
            } else if (is_input_sorted) {
                is_input_sorted = false;
            }
        }

        DCERR("Creating PhraseMap::Input is " << (!is_input_sorted ? "NOT " : "") << "sorted\n");

        fclose(fin);
        pm.finalize(is_input_sorted);
        vui_t weights;
        for (size_t i = 0; i < pm.repr.size(); ++i) {
            weights.push_back(pm.repr[i].weight);
        }
        st.initialize(weights);

        rnadded = weights.size();
        rnlines = nlines;

        building = false;
    }

    return 0;
}
Ejemplo n.º 15
0
    void
    start_parsing() {
        int i = 0;                  // The current record byte-offset.
        int n = 0;                  // Temporary buffer for numeric (integer) fields.
        const char *p_start = NULL; // Beginning of the phrase.
        const char *s_start = NULL; // Beginning of the snippet.
        int p_len = 0;              // Phrase Length.
        int s_len = 0;              // Snippet length.

        while (this->buff[i]) {
            char ch = this->buff[i];
            DCERR("["<<this->state<<":"<<ch<<"]");

            switch (this->state) {
            case ILP_BEFORE_NON_WS:
                if (!isspace(ch)) {
                    this->state = ILP_WEIGHT;
                }
                else {
                    ++i;
                }
                break;

            case ILP_WEIGHT:
                if (isdigit(ch)) {
                    n *= 10;
                    n += (ch - '0');
                    ++i;
                }
                else {
                    this->state = ILP_BEFORE_PTAB;
                    on_weight(n);
                }
                break;

            case ILP_BEFORE_PTAB:
                if (ch == '\t') {
                    this->state = ILP_AFTER_PTAB;
                }
                ++i;
                break;

            case ILP_AFTER_PTAB:
                if (isspace(ch)) {
                    ++i;
                }
                else {
                    p_start = this->buff + i;
                    this->state = ILP_PHRASE;
                }
                break;

            case ILP_PHRASE:
                // DCERR("State: ILP_PHRASE: "<<buff[i]<<endl);
                if (ch != '\t') {
                    ++p_len;
                }
                else {
                    // Note: Skip to ILP_SNIPPET since the snippet may
                    // start with a white-space that we wish to
                    // preserve.
                    // 
                    this->state = ILP_SNIPPET;
                    s_start = this->buff + i + 1;
                }
                ++i;
                break;

            case ILP_SNIPPET:
                ++i;
                ++s_len;
                break;

            };
        }
        DCERR("\n");
        on_phrase(p_start, p_len);
        on_snippet(s_start, s_len);
    }