#include <algorithm>
#include <cctype>
#include <iomanip>
#include <iostream>
#include <map>
#include <string>
#include <queue>





inline bool isWordLetter(char c)
{
    return std::isalnum(c) || c == '_';
}


inline void skipws(std::queue<char>& q)
{
    while (!( q.empty() || isWordLetter(q.front()) ))
        q.pop();
}


/* Extracts valid word.
 * Returns word or empty string if no word found till the end of queue
 */
std::string extractWord(std::queue<char>& q)
{
    std::string word;
    bool again = false;
    do {
        again = false;
        skipws(q);
        /* Extract all consequent word character */
        while( !q.empty() && isWordLetter(q.front()) ) {
            word += q.front();
            q.pop();
        }
        /* Check if word extracted is actually word */
        if (word.size() < 2 || count_if(word.begin(), word.end(), isalpha) == 0) {
            word.clear();
            if ( !q.empty() )
                again = true;
        }
    } while (again); //Repeat until we find word or exhaust our queue
    std::transform(word.begin(), word.end(), word.begin(), tolower);
    return word;
}


std::string getEnvString()
{
    extern char **environ; // needed to access your execution environment
    std::string envstr;
    int k = -1;
    while (environ[++k] != NULL) {
        envstr += environ[k];
        envstr += '\n';
    }
    return envstr;
}


int main()
{
    std::string envstr = getEnvString();
    std::cout << envstr << std::endl;;

    std::queue<char> line( {envstr.begin(), envstr.end()} );
    size_t wordCount = 0;
    std::map<std::string, unsigned> entries;
    std::string word;
    while((word = extractWord(line)) != "" ) {
        ++wordCount;
        ++entries[word];
    }
    std::cout << "There is " << wordCount << " words in envstring\n" <<
                 "Unique words and their frequency:\n";
    for(auto& p: entries) {
        std::cout << std::setw(30) << p.first << ' ' << p.second << '\n';
    }
}
