diff options
Diffstat (limited to 'parse.cc')
-rw-r--r-- | parse.cc | 582 |
1 files changed, 0 insertions, 582 deletions
diff --git a/parse.cc b/parse.cc deleted file mode 100644 index e4850973..00000000 --- a/parse.cc +++ /dev/null @@ -1,582 +0,0 @@ -#include "ledger.h" - -#include <fstream> -#include <cstring> -#include <ctime> -#include <cctype> - -#define TIMELOG_SUPPORT 1 - -namespace ledger { - -static inline char * skip_ws(char * ptr) -{ - while (std::isspace(*ptr)) - ptr++; - return ptr; -} - -static inline char * next_element(char * buf, bool variable = false) -{ - for (char * p = buf; *p; p++) { - if (! (*p == ' ' || *p == '\t')) - continue; - - if (! variable) { - *p = '\0'; - return skip_ws(p + 1); - } - else if (*p == '\t') { - *p = '\0'; - return skip_ws(p + 1); - } - else if (*(p + 1) == ' ') { - *p = '\0'; - return skip_ws(p + 2); - } - } - return NULL; -} - -static const char *formats[] = { - "%Y-%m-%d", - "%m-%d", - "%Y/%m/%d", - "%m/%d", - "%Y.%m.%d", - "%m.%d", - "%a", - "%A", - "%b", - "%B", - "%Y", - NULL -}; - -bool parse_date_mask(const char * date_str, struct std::tm * result) -{ - for (const char ** f = formats; *f; f++) { - memset(result, INT_MAX, sizeof(struct std::tm)); - if (strptime(date_str, *f, result)) - return true; - } - return false; -} - -bool parse_date(const char * date_str, std::time_t * result, const int year) -{ - struct std::tm when; - - if (! parse_date_mask(date_str, &when)) - return false; - - static std::time_t now = std::time(NULL); - static struct std::tm * now_tm = std::localtime(&now); - - when.tm_hour = 0; - when.tm_min = 0; - when.tm_sec = 0; - - if (when.tm_year == -1) - when.tm_year = ((year == -1) ? now_tm->tm_year : (year - 1900)); - - if (when.tm_mon == -1) - when.tm_mon = now_tm->tm_mon; - - if (when.tm_mday == -1) - when.tm_mday = now_tm->tm_mday; - - *result = std::mktime(&when); - - return true; -} - -void record_price(const std::string& symbol, amount * price, - std::time_t * date = NULL) -{ - commodity * comm = NULL; - commodities_map_iterator item = main_ledger->commodities.find(symbol); - if (item == main_ledger->commodities.end()) - comm = new commodity(symbol); - else - comm = (*item).second; - - assert(comm); - comm->set_price(price, date); -} - -void parse_price_setting(const std::string& setting) -{ - char buf[128]; - std::strcpy(buf, setting.c_str()); - - assert(setting.length() < 128); - - char * c = buf; - char * p = std::strchr(buf, '='); - if (! p) { - std::cerr << "Warning: Invalid price setting: " << setting << std::endl; - } else { - *p++ = '\0'; - record_price(c, create_amount(p)); - } -} - -#define MAX_LINE 1024 - - int linenum; -static bool do_compute; -static std::string account_prefix; - -transaction * parse_transaction_text(char * line, book * ledger) -{ - transaction * xact = new transaction(); - - // The call to `next_element' will skip past the account name, - // and return a pointer to the beginning of the amount. Once - // we know where the amount is, we can strip off any - // transaction note, and parse it. - - char * p = skip_ws(line); - char * cost_str = next_element(p, true); - char * note_str; - - // If there is no amount given, it is intended as an implicit - // amount; we must use the opposite of the value of the - // preceding transaction. - - if (! cost_str || ! *cost_str || *cost_str == ';') { - if (cost_str && *cost_str) { - while (*cost_str == ';' || std::isspace(*cost_str)) - cost_str++; - xact->note = cost_str; - } - - xact->cost = NULL; - } - else { - note_str = std::strchr(cost_str, ';'); - if (note_str) { - *note_str++ = '\0'; - xact->note = skip_ws(note_str); - } - - for (char * t = cost_str + (std::strlen(cost_str) - 1); - std::isspace(*t); - t--) - *t = '\0'; - - xact->cost = create_amount(cost_str); - } - - if (*p == '[' || *p == '(') { - xact->is_virtual = true; - xact->specified = true; - xact->must_balance = *p == '['; - p++; - - char * e = p + (std::strlen(p) - 1); - assert(*e == ')' || *e == ']'); - *e = '\0'; - } - - std::string name = account_prefix + p; - xact->acct = ledger->find_account(name.c_str()); - - if (do_compute && xact->cost) - xact->acct->balance.credit(xact->cost); - - return xact; -} - -transaction * parse_transaction(std::istream& in, book * ledger) -{ - static char line[MAX_LINE + 1]; - in.getline(line, MAX_LINE); - linenum++; - - return parse_transaction_text(line, ledger); -} - -entry * parse_entry(std::istream& in, book * ledger) -{ - entry * curr = new entry(ledger); - - static char line[MAX_LINE + 1]; - in.getline(line, MAX_LINE); - linenum++; - - // Parse the date - - char * next = next_element(line); - if (! parse_date(line, &curr->date, ledger->current_year)) { - std::cerr << "Error, line " << linenum - << ": Failed to parse date: " << line << std::endl; - return NULL; - } - - // Parse the optional cleared flag: * - - if (*next == '*') { - curr->cleared = true; - next = skip_ws(++next); - } - - // Parse the optional code: (TEXT) - - if (*next == '(') { - if (char * p = std::strchr(next++, ')')) { - *p++ = '\0'; - curr->code = next; - next = skip_ws(p); - } - } - - // Parse the description text - - curr->desc = next; - - // Parse all of the transactions associated with this entry - - while (! in.eof() && (in.peek() == ' ' || in.peek() == '\t')) - if (transaction * xact = parse_transaction(in, ledger)) - curr->xacts.push_back(xact); - - // If there were no transactions, throw away the entry - - if (curr->xacts.empty() || ! curr->finalize(do_compute)) { - delete curr; - return NULL; - } - - return curr; -} - -void parse_automated_transactions(std::istream& in, book * ledger) -{ - static char line[MAX_LINE + 1]; - - regexps_list * masks = NULL; - - while (! in.eof() && in.peek() == '=') { - in.getline(line, MAX_LINE); - linenum++; - - char * p = line + 1; - p = skip_ws(p); - - if (! masks) - masks = new regexps_list; - masks->push_back(mask(p)); - } - - std::list<transaction *> * xacts = NULL; - - while (! in.eof() && (in.peek() == ' ' || in.peek() == '\t')) { - if (transaction * xact = parse_transaction(in, ledger)) { - if (! xacts) - xacts = new std::list<transaction *>; - - if (! xact->cost) { - std::cerr << "Error, line " << (linenum - 1) - << ": All automated transactions must have a value." - << std::endl; - } else { - xacts->push_back(xact); - } - } - } - - if (masks && xacts) - ledger->virtual_mapping.insert(book::virtual_map_pair(masks, xacts)); - else if (masks) - delete masks; - else if (xacts) - delete xacts; -} - -////////////////////////////////////////////////////////////////////// -// -// Ledger parser -// - -#ifdef TIMELOG_SUPPORT -static std::time_t time_in; -static account * last_account; -static std::string last_desc; -#endif - -int parse_ledger(book * ledger, std::istream& in, - regexps_list& regexps, bool compute_balances, - const char * acct_prefix) -{ - static char line[MAX_LINE + 1]; - char c; - int count = 0; - std::string old_account_prefix = account_prefix; - - linenum = 1; - do_compute = compute_balances; - if (acct_prefix) { - account_prefix += acct_prefix; - account_prefix += ":"; - } - - while (! in.eof()) { - switch (in.peek()) { - case -1: // end of file - goto done; - - case '\n': - linenum++; - case '\r': // skip blank lines - in.get(c); - break; - -#ifdef TIMELOG_SUPPORT - case 'i': - case 'I': { - std::string date, time; - - in >> c; - in >> date; - in >> time; - date += " "; - date += time; - - in.getline(line, MAX_LINE); - linenum++; - - char * p = skip_ws(line); - char * n = next_element(p, true); - last_desc = n ? n : ""; - - static struct std::tm when; - if (strptime(date.c_str(), "%Y/%m/%d %H:%M:%S", &when)) { - time_in = std::mktime(&when); - last_account = ledger->find_account(p); - } else { - std::cerr << "Error, line " << (linenum - 1) - << ": Cannot parse timelog entry date." - << std::endl; - last_account = NULL; - } - break; - } - - case 'o': - case 'O': - if (last_account) { - std::string date, time; - - in >> c; - in >> date; - in >> time; - date += " "; - date += time; - - static struct std::tm when; - if (strptime(date.c_str(), "%Y/%m/%d %H:%M:%S", &when)) { - entry * curr = new entry(ledger); - - curr->date = std::mktime(&when); - - double diff = (curr->date - time_in) / 60.0 / 60.0; - char buf[128]; - std::sprintf(buf, "%fh", diff); - - curr->cleared = true; - curr->code = ""; - curr->desc = last_desc; - - std::string xact_line = "("; - xact_line += last_account->as_str(); - xact_line += ") "; - xact_line += buf; - - std::strcpy(buf, xact_line.c_str()); - - if (transaction * xact = parse_transaction_text(buf, ledger)) { - curr->xacts.push_back(xact); - - // Make sure numbers are reported only to 1 decimal place. - commodity * cmdty = xact->cost->commdty(); - cmdty->precision = 1; - } - - ledger->entries.push_back(curr); - count++; - } else { - std::cerr << "Error, line " << (linenum - 1) - << ": Cannot parse timelog entry date." - << std::endl; - } - - last_account = NULL; - } - break; -#endif // TIMELOG_SUPPORT - - case 'P': { // a pricing entry - in >> c; - - std::time_t date; - std::string symbol; - - in >> line; // the date - if (! parse_date(line, &date, ledger->current_year)) { - std::cerr << "Error, line " << linenum - << ": Failed to parse date: " << line << std::endl; - break; - } - - int hour, min, sec; - in >> hour; // the time - in >> c; - in >> min; - in >> c; - in >> sec; - date = std::time_t(((unsigned long) date) + - hour * 3600 + min * 60 + sec); - - in >> symbol; // the commodity - in >> line; // the price - - // Add this pricing entry to the history for the given - // commodity. - record_price(symbol, create_amount(line), &date); - break; - } - - case 'N': { // don't download prices - in >> c; - in >> line; // the symbol - - commodity * comm = NULL; - commodities_map_iterator item = main_ledger->commodities.find(line); - if (item == main_ledger->commodities.end()) - comm = new commodity(line); - else - comm = (*item).second; - - assert(comm); - if (comm) - comm->sought = true; - break; - } - - case 'C': { // a flat conversion - in >> c; - - std::string symbol; - in >> symbol; // the commodity - in >> line; // the price - - // Add this pricing entry to the given commodity - record_price(symbol, create_amount(line)); - break; - } - - case 'Y': // set the current year - in >> c; - in >> ledger->current_year; - break; - -#ifdef TIMELOG_SUPPORT - case 'h': - case 'b': -#endif - case ';': // a comment line - in.getline(line, MAX_LINE); - linenum++; - break; - - case '-': - case '+': // permanent regexps - in.getline(line, MAX_LINE); - linenum++; - - // Add the regexp to whatever masks currently exist - regexps.push_back(mask(line)); - break; - - case '=': // automated transactions - do_compute = false; - parse_automated_transactions(in, ledger); - do_compute = compute_balances; - break; - - case '!': // directive - in >> line; - if (std::string(line) == "!include") { - std::string path; - bool has_prefix = false; - - in >> path; - - if (in.peek() == ' ') { - has_prefix = true; - in.getline(line, MAX_LINE); - } - - int curr_linenum = linenum; - count += parse_ledger_file(ledger, path, regexps, compute_balances, - has_prefix ? skip_ws(line) : NULL); - linenum = curr_linenum; - } - break; - - default: - if (entry * ent = parse_entry(in, ledger)) { - ledger->entries.push_back(ent); - count++; - } - break; - } - } - - done: - account_prefix = old_account_prefix; - - return count; -} - -int parse_ledger_file(book * ledger, const std::string& file, - regexps_list& regexps, bool compute_balances, - const char * acct_prefix) -{ - std::ifstream stream(file.c_str()); - - // Parse the ledger - -#ifdef READ_GNUCASH - char buf[32]; - stream.get(buf, 31); - stream.seekg(0); - - if (std::strncmp(buf, "<?xml version=\"1.0\"?>", 21) == 0) - return parse_gnucash(ledger, stream, compute_balances); - else -#endif - return parse_ledger(ledger, stream, regexps, compute_balances, - acct_prefix); -} - -////////////////////////////////////////////////////////////////////// -// -// Read other kinds of data from files -// - -void read_regexps(const std::string& path, regexps_list& regexps) -{ - std::ifstream file(path.c_str()); - - while (! file.eof()) { - char buf[80]; - file.getline(buf, 79); - if (*buf && ! std::isspace(*buf)) - regexps.push_back(mask(buf)); - } -} - -} // namespace ledger |