Skip to main content
1 of 2
Loki Astari
  • 98.6k
  • 5
  • 127
  • 342

Unix WC Implementation

Code: https://github.com/Loki-Astari/Puzzle/tree/master/wc

Challenge: https://codingchallenges.fyi/challenges/challenge-wc/

wc.cpp

#include <cstddef>
#include <fstream>
#include <vector>
#include <string>
#include <iostream>

/*
 * Command line options.
 * If no options are specified then any is true and we print all values.
 */
struct Options
{
    bool    any         = true;
    bool    lines       = false;
    bool    words       = false;
    bool    chars       = false;
    bool    bytes       = false;
};

/*
 * Collect values from a file.
 */
struct Result
{
    std::size_t     lines   = 0;
    std::size_t     words   = 0;
    std::size_t     chars   = 0;
    std::size_t     bytes   = 0;
};

Result getData(std::istream& file)
{
    Result      result;
    int         c        = file.get();
    bool        newLine  = true;
    bool        inWord   = !std::isspace(c);

    for (; c != std::char_traits<char>::eof(); c = file.get()) {

        // A line must have at least one character on it.
        // The new line character counts as a character for this purpose.
        if (newLine == true) {
            newLine = false;
            result.lines    += 1;
        }
        if (c == '\n') {
            newLine = true;
        }

        // Words are "white space" separated.
        // Increment the counter when we hit a space when inside a word.
        bool isSpace = std::isspace(c);

        if (inWord && isSpace) {
            inWord = false;
            result.words    += 1;
        }
        else if (!inWord && !isSpace) {
            inWord = true;
        }

        // Ignore extra characters in multi byte character;
        if ((c & 0xC0) != 0x80) {
            result.chars    += 1;
        }

        // Increment for each char read from the stream
        result.bytes    += 1;
    }

    // We are in a word that has not been counted.
    if (inWord) {
        result.words += 1;
    }

    return result;
}

void display(std::istream& file, std::string const& fileName, Options const& options)
{
    Result data = getData(file);
    std::cout << "\t";
    if (options.any || options.lines) {
        std::cout << data.lines << "\t";
    }
    if (options.any || options.words) {
        std::cout << data.words << "\t";
    }
    if (options.any || options.chars) {
        std::cout << data.chars << "\t";
    }
    if (options.any || options.bytes) {
        std::cout << data.bytes << "\t";
    }
    std::cout << fileName << "\n";
}

int main(int argc, char* argv[])
{
    Options                     options;
    std::vector<std::string>    files;

    int loop = 1;
    for (; loop < argc; ++loop) {
        /*
         * If this is not a flag then we have reached the files.
         */
        if (argv[loop][0] != '-') {
            break;
        }

        /* Allow old style unix flags */
        for (int flag = 1; argv[loop][flag]; ++flag) {
            if (argv[loop][flag] == 'l') {
                options.any     = false;
                options.lines   = true;
            }
            else if (argv[loop][flag] == 'w') {
                options.any     = false;
                options.words   = true;
            }
            else if (argv[loop][flag] == 'm') {
                options.any     = false;
                options.chars   = true;
            }
            else if (argv[loop][flag] == 'c') {
                options.any     = false;
                options.bytes   = true;
            }
            else {
                std::cerr << "Usage: wc [-lwmc] <files>*\n";
                return 1;
            }
        }
    }

    /* Any remaining command line values are files */
    for (; loop < argc; ++loop) {
        files.emplace_back(argv[loop]);
    }

    /* If no files are explicitly set then use std::cin */
    if (files.size() == 0) {
        display(std::cin, "", options);
    }
    /* Loop over all the specified files */
    for (auto fileName: files) {
        std::ifstream   file(fileName);
        if (!file) {
            std::cout << "Unknown file: " << fileName << "\n";
        }
        else {
            display(file, fileName, options);
        }
    }
}
Loki Astari
  • 98.6k
  • 5
  • 127
  • 342