static void process_source_list(const char *source_list, const char *prefix, const char *suffix, bool no_backup, bool keep_mtime) { int from_stdin = strcmp(source_list, "-") == 0; FILE *p_file = from_stdin ? stdin : fopen(source_list, "r"); if (p_file == NULL) { LOG_FMT(LERR, "%s: fopen(%s) failed: %s (%d)\n", __func__, source_list, strerror(errno), errno); cpd.error_count++; return; } char linebuf[256]; char *fname; int line = 0; int len; while (fgets(linebuf, sizeof(linebuf), p_file) != NULL) { line++; fname = linebuf; len = strlen(fname); while ((len > 0) && unc_isspace(*fname)) { fname++; len--; } while ((len > 0) && unc_isspace(fname[len - 1])) { len--; } fname[len] = 0; while (len-- > 0) { if (fname[len] == '\\') { fname[len] = '/'; } } LOG_FMT(LFILELIST, "%3d] %s\n", line, fname); if (fname[0] != '#') { char outbuf[1024]; do_source_file(fname, make_output_filename(outbuf, sizeof(outbuf), fname, prefix, suffix), NULL, no_backup, keep_mtime); } } if (!from_stdin) { fclose(p_file); } }
/** * Called when we hit a backslash. * If there is nothing but whitespace until the newline, then this is a * backslash newline */ static bool parse_bs_newline(tok_ctx& ctx, chunk_t& pc) { ctx.save(); ctx.get(); /* skip the '\' */ int ch; while (ctx.more() && unc_isspace(ch = ctx.peek())) { ctx.get(); if ((ch == '\r') || (ch == '\n')) { if (ch == '\r') { ctx.expect('\n'); } pc.str = "\\"; pc.type = CT_NL_CONT; pc.nl_count = 1; return(true); } } ctx.restore(); return(false); }
/** * Checks for and updates the lead chars. * * @param line the comment line * @return 0=not present, >0=number of chars that are part of the lead */ static int cmt_parse_lead(const unc_text& line, int is_last) { int len = 0; while ((len < 32) && (len < line.size())) { if ((len > 0) && (line[len] == '/')) { /* ignore combined comments */ int tmp = len + 1; while ((tmp < line.size()) && unc_isspace(line[tmp])) { tmp++; } if ((tmp < line.size()) && (line[tmp] == '/')) { return 1; } break; } else if (strchr("*|\\#+", line[len]) == NULL) { break; } len++; } if (len > 30) { return 1; } if ((len > 0) && ((len >= line.size()) || unc_isspace(line[len]))) { return len; } if ((len == 1) && (line[0] == '*')) { return len; } if (is_last && (len > 0)) { return len; } return 0; }
/** * PAWN #define is different than C/C++. * #define PATTERN REPLACEMENT_TEXT * The PATTERN may not contain a space or '[' or ']'. * A generic whitespace check should be good enough. * Do not change the pattern. */ static void parse_pawn_pattern(tok_ctx& ctx, chunk_t& pc, c_token_t tt) { pc.str.clear(); pc.type = tt; while (!unc_isspace(ctx.peek())) { pc.str.append(ctx.get()); } }
void logmask_from_string(const char *str, log_mask_t &mask) { if (str == nullptr) { return; } logmask_set_all(mask, false); // Start with a clean mask // If the first character is 'a' or 'A', set all severities if (unc_toupper(*str) == 'A') { logmask_set_all(mask, true); str++; } char *ptmp; bool was_dash = false; int last_level = -1; while (*str != 0) // check string until termination character { if (unc_isspace(*str)) // ignore spaces and go on with next character { str++; continue; } if (unc_isdigit(*str)) { int level = strtoul(str, &ptmp, 10); str = ptmp; logmask_set_sev(mask, static_cast<log_sev_t>(level), true); if (was_dash) { for (int idx = last_level + 1; idx < level; idx++) { logmask_set_sev(mask, static_cast<log_sev_t>(idx), true); } was_dash = false; } last_level = level; } else if (*str == '-') // a dash marks all bits until the next number { was_dash = true; str++; } else // probably a comma { last_level = -1; was_dash = false; str++; } } } // logmask_from_string
/** * Count the number of whitespace characters. * * @param pc The structure to update, str is an input. * @return Whether whitespace was parsed */ static bool parse_whitespace(tok_ctx& ctx, chunk_t& pc) { int nl_count = 0; int ch = -2; /* REVISIT: use a better whitespace detector? */ while (ctx.more() && unc_isspace(ctx.peek())) { ch = ctx.get(); /* throw away the whitespace char */ switch (ch) { case '\r': if (ctx.expect('\n')) { /* CRLF ending */ cpd.le_counts[LE_CRLF]++; } else { /* CR ending */ cpd.le_counts[LE_CR]++; } nl_count++; pc.orig_prev_sp = 0; break; case '\n': /* LF ending */ cpd.le_counts[LE_LF]++; nl_count++; pc.orig_prev_sp = 0; break; case '\t': pc.orig_prev_sp += calc_next_tab_column(cpd.column, cpd.settings[UO_input_tab_size].n) - cpd.column; break; case ' ': pc.orig_prev_sp++; break; default: break; } } if (ch != -2) { pc.str.clear(); pc.nl_count = nl_count; pc.type = nl_count ? CT_NEWLINE : CT_WHITESPACE; pc.after_tab = (ctx.c.last_ch == '\t'); return(true); } return(false); } // parse_whitespace
/** * Count the number of characters to the end of the next chunk of text. * If it exceeds the limit, return true. */ static bool next_word_exceeds_limit(const unc_text& text, int idx) { int length = 0; /* Count any whitespace */ while ((idx < text.size()) && unc_isspace(text[idx])) { idx++; length++; } /* Count non-whitespace */ while ((idx < text.size()) && !unc_isspace(text[idx])) { idx++; length++; } return((cpd.column + length - 1) > cpd.settings[UO_cmt_width].n); }
/** * Count the number of whitespace characters. * * @param pc The structure to update, str is an input. * @return Whether whitespace was parsed */ static bool parse_whitespace(tok_ctx& ctx, chunk_t& pc) { int nl_count = 0; int ch = -2; /* REVISIT: use a better whitespace detector? */ while (ctx.more() && unc_isspace(ctx.peek())) { ch = ctx.get(); /* throw away the whitespace char */ switch (ch) { case '\r': if (ctx.peek() == '\n') { /* CRLF ending */ ctx.get(); /* throw away \n */ cpd.le_counts[LE_CRLF]++; } else { /* CR ending */ cpd.le_counts[LE_CR]++; } nl_count++; break; case '\n': /* LF ending */ cpd.le_counts[LE_LF]++; nl_count++; break; case '\t': case ' ': default: break; } } if (ch != -2) { pc.str.clear(); pc.nl_count = nl_count; pc.type = nl_count ? CT_NEWLINE : CT_WHITESPACE; pc.after_tab = (ctx.c.last_ch == '\t'); return(true); } return(false); }
static int next_up(const unc_text& text, int idx, unc_text& tag) { int offs = 0; while ((idx < text.size()) && unc_isspace(text[idx])) { idx++; offs++; } if (text.startswith(tag, idx)) { return(offs); } return(-1); }
/** * PAWN #define is different than C/C++. * #define PATTERN REPLACEMENT_TEXT * The PATTERN may not contain a space or '[' or ']'. * A generic whitespace check should be good enough. * Do not change the pattern. */ static void parse_pawn_pattern(tok_ctx &ctx, chunk_t &pc, c_token_t tt) { pc.str.clear(); pc.type = tt; while (!unc_isspace(ctx.peek())) { /* end the pattern on an escaped newline */ if (ctx.peek() == '\\') { int ch = ctx.peek(1); if ((ch == '\n') || (ch == '\r')) { break; } } pc.str.append(ctx.get()); } }
/** * Outputs a comment. The initial opening '//' may be included in the text. * Subsequent openings (if combining comments), should not be included. * The closing (for C/D comments) should not be included. * * TODO: * If reflowing text, the comment should be added one word (or line) at a time. * A newline should only be sent if a blank line is encountered or if the next * line is indented beyond the current line (optional?). * If the last char on a line is a ':' or '.', then the next line won't be * combined. */ static void add_comment_text(const unc_text& text, cmt_reflow& cmt, bool esc_close) { bool was_star = false; bool was_slash = false; bool was_dollar = false; bool in_word = false; int tmp; int len = text.size(); for (int idx = 0; idx < len; idx++) { if (!was_dollar && cmt.kw_subst && (text[idx] == '$') && (len > (idx + 3)) && (text[idx + 1] == '(')) { idx += add_comment_kw(text, idx, cmt); if (idx >= len) { break; } } /* Split the comment */ if (text[idx] == '\n') { in_word = false; add_char('\n'); cmt_output_indent(cmt.brace_col, cmt.base_col, cmt.column); if (cmt.xtra_indent) { add_char(' '); } /* hack to get escaped newlines to align and not dup the leading '//' */ tmp = next_up(text, idx + 1, cmt.cont_text); if (tmp < 0) { add_text(cmt.cont_text); } else { idx += tmp; } } else if (cmt.reflow && (text[idx] == ' ') && (cpd.settings[UO_cmt_width].n > 0) && ((cpd.column > cpd.settings[UO_cmt_width].n) || next_word_exceeds_limit(text, idx))) { in_word = false; add_char('\n'); cmt_output_indent(cmt.brace_col, cmt.base_col, cmt.column); if (cmt.xtra_indent) { add_char(' '); } add_text(cmt.cont_text); } else { /* Escape a C closure in a CPP comment */ if (esc_close && ((was_star && (text[idx] == '/')) || (was_slash && (text[idx] == '*')))) { add_char(' '); } if (!in_word && !unc_isspace(text[idx])) { cmt.word_count++; } in_word = !unc_isspace(text[idx]); add_char(text[idx]); was_star = (text[idx] == '*'); was_slash = (text[idx] == '/'); was_dollar = (text[idx] == '$'); } } }
/** * A multiline comment -- woopeee! * The only trick here is that we have to trim out whitespace characters * to get the comment to line up. */ static void output_comment_multi(chunk_t *pc) { int cmt_col; int cmt_idx; int ch; unc_text line; int line_count = 0; int ccol; /* the col of subsequent comment lines */ int col_diff = 0; bool nl_end = false; cmt_reflow cmt; //LOG_FMT(LSYS, "%s: line %d\n", __func__, pc->orig_line); output_cmt_start(cmt, pc); cmt.reflow = (cpd.settings[UO_cmt_reflow_mode].n != 1); cmt_col = cmt.base_col; col_diff = pc->orig_col - cmt.base_col; calculate_comment_body_indent(cmt, pc->str); cmt.cont_text = !cpd.settings[UO_cmt_indent_multi].b ? "" : (cpd.settings[UO_cmt_star_cont].b ? "* " : " "); LOG_CONTTEXT(); //LOG_FMT(LSYS, "Indenting1 line %d to col %d (orig=%d) col_diff=%d xtra=%d cont='%s'\n", // pc->orig_line, cmt_col, pc->orig_col, col_diff, cmt.xtra_indent, cmt.cont_text.c_str()); ccol = pc->column; cmt_idx = 0; line.clear(); while (cmt_idx < pc->len()) { ch = pc->str[cmt_idx++]; /* handle the CRLF and CR endings. convert both to LF */ if (ch == '\r') { ch = '\n'; if ((cmt_idx < pc->len()) && (pc->str[cmt_idx] == '\n')) { cmt_idx++; } } /* Find the start column */ if (line.size() == 0) { nl_end = false; if (ch == ' ') { ccol++; continue; } else if (ch == '\t') { ccol = calc_next_tab_column(ccol, cpd.settings[UO_input_tab_size].n); continue; } else { //LOG_FMT(LSYS, "%d] Text starts in col %d\n", line_count, ccol); } } /* * Now see if we need/must fold the next line with the current to enable * full reflow */ if ((cpd.settings[UO_cmt_reflow_mode].n == 2) && (ch == '\n') && (cmt_idx < pc->len())) { int nxt_len = 0; int next_nonempty_line = -1; int prev_nonempty_line = -1; int nwidx = line.size(); bool star_is_bullet = false; /* strip trailing whitespace from the line collected so far */ while (nwidx > 0) { nwidx--; if ((prev_nonempty_line < 0) && !unc_isspace(line[nwidx]) && (line[nwidx] != '*') && // block comment: skip '*' at end of line ((pc->flags & PCF_IN_PREPROC) ? (line[nwidx] != '\\') || ((line[nwidx + 1] != 'r') && (line[nwidx + 1] != '\n')) : true)) { prev_nonempty_line = nwidx; // last nonwhitespace char in the previous line } } int remaining = pc->len() - cmt_idx; for (nxt_len = 0; (nxt_len <= remaining) && (pc->str[nxt_len] != 'r') && (pc->str[nxt_len] != '\n'); nxt_len++) { if ((next_nonempty_line < 0) && !unc_isspace(pc->str[nxt_len]) && (pc->str[nxt_len] != '*') && ((nxt_len == remaining) || ((pc->flags & PCF_IN_PREPROC) ? (pc->str[nxt_len] != '\\') || ((pc->str[nxt_len + 1] != 'r') && (pc->str[nxt_len + 1] != '\n')) : true))) { next_nonempty_line = nxt_len; // first nonwhitespace char in the next line } } /* * see if we should fold up; usually that'd be a YES, but there are a few * situations where folding/reflowing by merging lines is frowned upon: * * - ASCII art in the comments (most often, these are drawings done in +-\/|.,*) * * - Doxygen/JavaDoc/etc. parameters: these often start with \ or @, at least * something clearly non-alphanumeric (you see where we're going with this?) * * - bullet lists that are closely spaced: bullets are always non-alphanumeric * characters, such as '-' or '+' (or, oh horor, '*' - that's bloody ambiguous * to parse :-( ... with or without '*' comment start prefix, that's the * question, then.) * * - semi-HTML formatted code, e.g. <pre>...</pre> comment sections (NDoc, etc.) * * - New lines which form a new paragraph without there having been added an * extra empty line between the last sentence and the new one. * A bit like this, really; so it is opportune to check if the last line ended * in a terminal (that would be the set '.:;!?') and the new line starts with * a capital. * Though new lines starting with comment delimiters, such as '(', should be * pulled up. * * So it bores down to this: the only folding (& reflowing) that's going to happen * is when the next line starts with an alphanumeric character AND the last * line didn't end with an non-alphanumeric character, except: ',' AND the next * line didn't start with a '*' all of a sudden while the previous one didn't * (the ambiguous '*'-for-bullet case!) */ if ((prev_nonempty_line >= 0) && (next_nonempty_line >= 0) && (((unc_isalnum(line[prev_nonempty_line]) || strchr(",)]", line[prev_nonempty_line])) && (unc_isalnum(pc->str[next_nonempty_line]) || strchr("([", pc->str[next_nonempty_line]))) || (('.' == line[prev_nonempty_line]) && // dot followed by non-capital is NOT a new sentence start unc_isupper(pc->str[next_nonempty_line]))) && !star_is_bullet) { // rewind the line to the last non-alpha: line.resize(prev_nonempty_line + 1); // roll the current line forward to the first non-alpha: cmt_idx += next_nonempty_line; // override the NL and make it a single whitespace: ch = ' '; } } line.append(ch); /* If we just hit an end of line OR we just hit end-of-comment... */ if ((ch == '\n') || (cmt_idx == pc->len())) { line_count++; /* strip trailing tabs and spaces before the newline */ if (ch == '\n') { nl_end = true; line.pop_back(); cmt_trim_whitespace(line, pc->flags & PCF_IN_PREPROC); } //LOG_FMT(LSYS, "[%3d]%s\n", ccol, line); if (line_count == 1) { /* this is the first line - add unchanged */ add_comment_text(line, cmt, false); if (nl_end) { add_char('\n'); } } else { /* This is not the first line, so we need to indent to the * correct column. Each line is indented 0 or more spaces. */ ccol -= col_diff; if (ccol < (cmt_col + 3)) { ccol = cmt_col + 3; } if (line.size() == 0) { /* Empty line - just a '\n' */ if (cpd.settings[UO_cmt_star_cont].b) { cmt.column = cmt_col + cpd.settings[UO_cmt_sp_before_star_cont].n; cmt_output_indent(cmt.brace_col, cmt.base_col, cmt.column); if (cmt.xtra_indent) { add_char(' '); } add_text(cmt.cont_text); } add_char('\n'); } else { /* If this doesn't start with a '*' or '|'. * '\name' is a common parameter documentation thing. */ if (cpd.settings[UO_cmt_indent_multi].b && (line[0] != '*') && (line[0] != '|') && (line[0] != '#') && ((line[0] != '\\') || unc_isalpha(line[1])) && (line[0] != '+')) { int start_col = cmt_col + cpd.settings[UO_cmt_sp_before_star_cont].n; if (cpd.settings[UO_cmt_star_cont].b) { cmt.column = start_col; cmt_output_indent(cmt.brace_col, cmt.base_col, cmt.column); if (cmt.xtra_indent) { add_char(' '); } add_text(cmt.cont_text); output_to_column(ccol + cpd.settings[UO_cmt_sp_after_star_cont].n, false); } else { cmt.column = ccol; cmt_output_indent(cmt.brace_col, cmt.base_col, cmt.column); } } else { cmt.column = cmt_col + cpd.settings[UO_cmt_sp_before_star_cont].n; cmt_output_indent(cmt.brace_col, cmt.base_col, cmt.column); if (cmt.xtra_indent) { add_char(' '); } int idx; idx = cmt_parse_lead(line, (cmt_idx == pc->len())); if (idx > 0) { cmt.cont_text.set(line, 0, idx); LOG_CONTTEXT(); if ((line.size() >= 2) && (line[0] == '*') && unc_isalnum(line[1])) { line.insert(1, ' '); } } else { add_text(cmt.cont_text); } } add_comment_text(line, cmt, false); if (nl_end) { add_text("\n"); } } } line.clear(); ccol = 1; } } }
/** * Outputs the CPP comment at pc. * CPP comment combining is done here * * @return the last chunk output'd */ static chunk_t *output_comment_cpp(chunk_t *first) { cmt_reflow cmt; unc_text tmp; output_cmt_start(cmt, first); cmt.reflow = (cpd.settings[UO_cmt_reflow_mode].n != 1); /* CPP comments can't be grouped unless they are converted to C comments */ if (!cpd.settings[UO_cmt_cpp_to_c].b) { cmt.cont_text = (cpd.settings[UO_sp_cmt_cpp_start].a & AV_REMOVE) ? "//" : "// "; LOG_CONTTEXT(); if (cpd.settings[UO_sp_cmt_cpp_start].a == AV_IGNORE) { add_comment_text(first->str, cmt, false); } else { unc_text tmp(first->str, 0, 2); add_comment_text(tmp, cmt, false); tmp.set(first->str, 2, first->len() - 2); if (cpd.settings[UO_sp_cmt_cpp_start].a & AV_REMOVE) { while ((tmp.size() > 0) && unc_isspace(tmp[0])) { tmp.pop_front(); } } if (tmp.size() > 0) { if (cpd.settings[UO_sp_cmt_cpp_start].a & AV_ADD) { if (!unc_isspace(tmp[0])) { add_comment_text(" ", cmt, false); } } add_comment_text(tmp, cmt, false); } } return(first); } /* We are going to convert the CPP comments to C comments */ cmt.cont_text = cpd.settings[UO_cmt_star_cont].b ? " * " : " "; LOG_CONTTEXT(); /* See if we can combine this comment with the next comment */ if (!cpd.settings[UO_cmt_cpp_group].b || !can_combine_comment(first, cmt)) { /* nothing to group: just output a single line */ add_text("/*"); if (!unc_isspace(first->str[2])) { add_char(' '); } tmp.set(first->str, 2, first->len() - 2); add_comment_text(tmp, cmt, true); add_text(" */"); return(first); } add_text("/*"); if (cpd.settings[UO_cmt_cpp_nl_start].b) { add_comment_text("\n", cmt, false); } else { add_text(" "); } chunk_t *pc = first; int offs; while (can_combine_comment(pc, cmt)) { offs = unc_isspace(pc->str[2]) ? 1 : 0; tmp.set(pc->str, 2 + offs, pc->len() - (2 + offs)); add_comment_text(tmp, cmt, true); add_comment_text("\n", cmt, false); pc = chunk_get_next(chunk_get_next(pc)); } offs = unc_isspace(pc->str[2]) ? 1 : 0; tmp.set(pc->str, 2 + offs, pc->len() - (2 + offs)); add_comment_text(tmp, cmt, true); if (cpd.settings[UO_cmt_cpp_nl_end].b) { cmt.cont_text = ""; LOG_CONTTEXT(); add_comment_text("\n", cmt, false); } add_comment_text(" */", cmt, false); return(pc); }