/* The output function for an ELF hash table. The hash table is an * array of 32-bit values. The first two array elements indicate the * length of the following subsections. Line breaks are inserted to * indicate where the subsections start and end. */ static void outhashtable(void const *ptr, long size, int ndx) { Elf64_Word const *entries = ptr; long count = size / sizeof *entries; long i; (void)ndx; beginblock(TRUE); if (count <= 2 || (long)(entries[0] + entries[1] + 2) != count) { for (i = 0 ; i < count ; ++i) outdec(entries[i]); } else { outdec(entries[0]); outdec(entries[1]); linebreak(); i = 2; if (entries[0] > 0) { for ( ; i < (long)(2 + entries[0]) ; ++i) outdec(entries[i]); linebreak(); } for ( ; i < count ; ++i) outdec(entries[i]); } endblock(); }
/* * NAME: cmdbuf->shift() * DESCRIPTION: shift a range of lines left or right */ static int cb_shift(cmdbuf *cb) { cb_do(cb, cb->first); startblock(cb); cb->lineno = cb->first - 1; cb->flags |= CB_CHANGE; eb_range(cb->edbuf, cb->first, cb->last, shift, FALSE); endblock(cb); return RET_FLAGS; }
/* * NAME: shift() * DESCRIPTION: shift a line left or right */ static void shift(const char *text) { cmdbuf *cb; int idx; cb = ccb; /* first determine the number of leading spaces */ idx = 0; while (*text == ' ' || *text == HT) { if (*text++ == ' ') { idx++; } else { idx = (idx + 8) & ~7; } } if (*text == '\0') { /* don't shift lines with ws only */ addblock(cb, text); cb->lineno++; } else { idx += cb->shift; if (idx < MAX_LINE_SIZE) { char buffer[MAX_LINE_SIZE]; char *p; p = buffer; /* fill with leading ws */ while (idx >= 8) { *p++ = HT; idx -= 8; } while (idx > 0) { *p++ = ' '; --idx; } if (p - buffer + strlen(text) < MAX_LINE_SIZE) { strcpy(p, text); addblock(cb, buffer); cb->lineno++; return; } } /* Error: line too long. Finish block of lines already shifted. */ cb->last = cb->lineno; endblock(cb); error("Result of shift would be too long"); } }
/* The output function for pieces of type P_UNCLAIMED, P_SECTION, * P_BYTES, or P_STRINGS. The contents are output either as a literal * string, an array of character values, or an array of hexadecimal * byte values. The last will be used if the contents contain an * excess of non-graphic, non-ASCII characters. Otherwise, one of the * first two representations will be selected based on whether or not * the contents appear to be null-terminated. */ static void outbytes(void const *ptr, long size, int ndx) { unsigned char const *bytes = ptr; long zeroes, n; long i; (void)ndx; for (zeroes = 0 ; zeroes < size && !bytes[size - zeroes - 1] ; ++zeroes) ; if (zeroes == size) { out("{ 0 }"); return; } if (zeroes < 255 || zeroes * 4 < size) zeroes = 0; zeroes &= ~7; n = outstringsize((signed char const*)bytes, size); if (n * 2 > size * 3) { beginblock(TRUE); for (i = 0 ; i < size - zeroes ; ++i) outf("0x%02X", bytes[i]); if (zeroes) outcomment(strf("0x00 x %d", zeroes)); endblock(); } else if (zeroes || bytes[size - 1]) { beginblock(TRUE); for (i = 0 ; i < size - zeroes ; ++i) outchar(bytes[i]); if (zeroes) outcomment(strf("0x00 x %ld", zeroes)); endblock(); } else { outstring((char const*)bytes, size - 1); } }
/* * NAME: sub() * DESCRIPTION: add a string to the current substitute buffer */ static void sub(cmdbuf *cb, const char *text, unsigned int size) { char *p; const char *q; unsigned int i; i = size; if (cb->buflen + i >= MAX_LINE_SIZE) { if (cb->flags & CB_CURRENTBLK) { /* finish already processed block */ endblock(cb); } cb->cthis = cb->othis = cb->lineno; error("Line overflow in substitute"); } p = cb->buffer + cb->buflen; q = text; if (cb->flags & CB_TLOWER) { /* lowercase one letter */ *p++ = tolower(*q); q++; cb->flags &= ~CB_TLOWER; --i; } else if (cb->flags & CB_TUPPER) { /* uppercase one letter */ *p++ = toupper(*q); q++; cb->flags &= ~CB_TUPPER; --i; } if (cb->flags & CB_LOWER) { /* lowercase string */ while (i > 0) { *p++ = tolower(*q); q++; --i; } } else if (cb->flags & CB_UPPER) { /* uppercase string */ while (i > 0) { *p++ = toupper(*q); q++; --i; } } else if (i > 0) { /* don't change case */ memcpy(p, q, i); } cb->buflen += size; }
/* The GNU hash table is an array of 32-bit values. (Although in a * 64-bit ELF file the mask subpart consists of 64-bit values. But an * array cannot change its type midway through, so in this case the * mask values are split across two array entries.) */ static void outgnuhash(void const *ptr, long size, int ndx) { Elf64_Word const *entries = ptr; long count = size / sizeof *entries; int buckets, masks; int i, n; (void)ndx; if (count < 4) { outwords(ptr, size, ndx); return; } buckets = entries[0]; masks = entries[2]; if (iself64()) masks *= 2; if (4 + masks + buckets > count) { outwords(ptr, size, ndx); return; } beginblock(TRUE); for (i = 0 ; i < 4 ; ++i) outdec(entries[i]); n = i; if (masks) { linebreak(); for (i = 0 ; i < masks ; ++i) outf("0x%08lX", entries[n + i]); n += i; } if (buckets) { linebreak(); for (i = 0 ; i < buckets ; ++i) outdec(entries[n + i]); n += i; } if (n < count) { linebreak(); for (i = n ; i < count ; ++i) outf("0x%08lX", entries[i]); } endblock(); }
/* The output function for an array of 64-bit values. */ static void outxwords(void const *ptr, long size, int ndx) { Elf64_Xword const *xwords = ptr; long count = size / sizeof *xwords; char fmtbuf[16]; char const *fmt = NULL; unsigned long max; long zeroes, i; (void)ndx; for (zeroes = 0 ; zeroes < count ; ++zeroes) if (xwords[count - zeroes - 1]) break; if (zeroes == count) { out("{ 0 }"); return; } if (zeroes < 255 || zeroes * 4 < count) zeroes = 0; zeroes &= ~3; count -= zeroes; max = 0; for (i = 0 ; i < count ; ++i) { if (max < xwords[i]) max = xwords[i]; } fmt = max > 0xFFFFFFFF ? "0x%016lX" : max > 0x0000FFFF ? "0x%08lX" : max > 0x000000FF ? "0x%04lX" : max > 99 ? "0x%02lX" : "%lu"; beginblock(TRUE); for (i = 0 ; i < count ; ++i) outf(fmt, xwords[i]); if (zeroes) { sprintf(fmtbuf, "%s x %%ld", fmt); outcomment(strf(fmtbuf, 0, zeroes)); } endblock(); }
/* The output function for note sections. The format of a note section * varies with the type of note, the only constant part being that it * starts with an Elf*_Nhdr struct, and is usually followed by a * string. However, each subpart is guaranteed to be a multiple of 4 * bytes, so notes are displayed as arrays of 32-bit values. Line * breaks are inserted to indicate the beginning of a note header. * Note sections in core files are rather different, so in that case a * completely different function is called instead. */ void outnote(void const *ptr, long size, int ndx) { Elf64_Word const *words = ptr; long count = size / sizeof *words; long i; int namesize, descsize, tag; (void)ndx; if (iscorefile()) { iself64() ? outnote64(ptr, size, ndx) : outnote32(ptr, size, ndx); return; } beginblock(TRUE); i = 0; while (i < count) { namesize = (words[i] + 3) / 4; descsize = (words[i + 1] + 3) / 4; linebreak(); outdec(words[i++]); outdec(words[i++]); tag = words[i++]; outdefint(tag, "NT_GNU_"); while (namesize-- && i < count) outhex(words[i++]); if (descsize == 0) continue; #ifdef NT_GNU_ABI_TAG if (tag == NT_GNU_ABI_TAG) { outdefint(words[i++], "ELF_NOTE_OS_"); --descsize; } #endif while (descsize-- && i < count) outhex(words[i++]); } endblock(); }
/* The output function for an array of 16-bit values. The values are * printed in hexadecimal unless they are all relatively small. */ static void outhalves(void const *ptr, long size, int ndx) { Elf64_Half const *halves = ptr; long count = size / sizeof *halves; char fmtbuf[16]; char const *fmt = NULL; unsigned int max; long zeroes, i; (void)ndx; for (zeroes = 0 ; zeroes < count ; ++zeroes) if (halves[count - zeroes - 1]) break; if (zeroes == count) { out("{ 0 }"); return; } if (zeroes < 255 || zeroes * 4 < count) zeroes = 0; zeroes &= ~3; count -= zeroes; max = 0; for (i = 0 ; i < count ; ++i) { if (max < halves[i]) max = halves[i]; } fmt = max > 0xFF ? "0x%04X" : max > 99 ? "0x%02X" : "%u"; beginblock(TRUE); for (i = 0 ; i < count ; ++i) outf(fmt, halves[i]); if (zeroes) { sprintf(fmtbuf, "%s x %%ld", fmt); outcomment(strf(fmtbuf, 0, zeroes)); } endblock(); }
/* * NAME: cmdbuf->indent() * DESCRIPTION: indent a range of lines */ int cb_indent(cmdbuf *cb) { char s[STACKSZ]; int i[STACKSZ]; /* setup stacks */ cb->stackbot = s; cb->stack = s + STACKSZ - 1; cb->stack[0] = EOT; cb->ind = i + STACKSZ - 1; cb->ind[0] = 0; cb->quote = '\0'; cb_do(cb, cb->first); startblock(cb); cb->lineno = cb->first - 1; cb->flags |= CB_CHANGE; cb->flags &= ~(CB_PPCONTROL | CB_COMMENT | CB_JSKEYWORD); eb_range(cb->edbuf, cb->first, cb->last, indent, FALSE); endblock(cb); return 0; }
/* * NAME: cmdbuf->join() * DESCRIPTION: join a range of lines in the edit buffer */ int cb_join(cmdbuf *cb) { char buf[MAX_LINE_SIZE + 1]; Int *m; if (cb->edbuf->lines == 0) { error("No lines in buffer"); } if (cb->first < 0) { cb->first = cb->cthis; } if (cb->last < 0) { cb->last = (cb->first == cb->edbuf->lines) ? cb->first : cb->first + 1; } cb_do(cb, cb->first); cb->cthis = cb->othis = cb->first; buf[0] = '\0'; cb->buffer = buf; cb->buflen = 0; eb_range(cb->edbuf, cb->first, cb->last, join, FALSE); /* erase marks for joined lines */ for (m = cb->mark; m < &cb->mark[26]; m++) { if (*m > cb->first && *m <= cb->last) { *m = 0; } } cb->flags |= CB_CHANGE; startblock(cb); addblock(cb, buf); endblock(cb); return RET_FLAGS; }
/* * NAME: subst() * DESCRIPTION: do substitutions in a line. If something is substituted on line * N, and the next substitution happens on line N + 2, line N + 1 * is joined in the new block also. */ static void subst(const char *text) { char line[MAX_LINE_SIZE]; cmdbuf *cb; int idx, size; char *p; Int *k, *l; Int newlines; bool found; cb = ccb; found = FALSE; newlines = 0; idx = 0; /* * Because the write buffer might be flushed, and the text would * not remain in memory, use a local copy. */ text = strcpy(line, text); while (rx_exec(cb->regexp, text, idx, IGNORECASE(cb->vars)) > 0) { if (cb->flags & CB_SKIPPED) { /* * add the previous line, in which nothing was substituted, to * the block. Has to be done here, before the contents of the buffer * are changed. */ addblock(cb, cb->buffer); cb->flags &= ~CB_SKIPPED; /* * check if there were newlines in the last substitution. If there * are, marks on the previous line (without substitutions) will * also have to be changed. */ if (cb->offset > 0) { for (k = cb->mark, l = cb->moffset; k < &cb->mark[26]; k++, l++) { if (*k == cb->lineno - 1 && *l == 0) { *l = *k + cb->offset; } } } } found = TRUE; cb->flags &= ~(CB_UPPER | CB_LOWER | CB_TUPPER | CB_TLOWER); size = cb->regexp->start - text - idx; if (size > 0) { /* copy first unchanged part of line to buffer */ sub(cb, text + idx, size); } p = cb->replace; while (*p != '\0') { switch (*p) { case '&': /* insert matching string */ sub(cb, cb->regexp->start, cb->regexp->size); break; case '\\': /* special substitute characters */ switch (*++p) { case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': /* insert subexpression between \( \) */ if (cb->regexp->se[*p - '1'].start != (char*) NULL) { sub(cb, cb->regexp->se[*p - '1'].start, cb->regexp->se[*p - '1'].size); break; } /* if no subexpression, fall though */ default: sub(cb, p, 1); /* ignore preceding backslash */ break; case 'n': cb->buffer[cb->buflen++] = '\0'; newlines++; /* insert newline */ break; case 'U': /* convert string to uppercase */ cb->flags |= CB_UPPER; cb->flags &= ~(CB_LOWER | CB_TUPPER | CB_TLOWER); break; case 'L': /* convert string to lowercase */ cb->flags |= CB_LOWER; cb->flags &= ~(CB_UPPER | CB_TUPPER | CB_TLOWER); break; case 'e': case 'E': /* end case conversion */ cb->flags &= ~(CB_UPPER | CB_LOWER | CB_TUPPER | CB_TLOWER); break; case 'u': /* convert char to uppercase */ cb->flags |= CB_TUPPER; cb->flags &= ~CB_TLOWER; break; case 'l': /* convert char to lowercase */ cb->flags &= ~CB_TUPPER; cb->flags |= CB_TLOWER; break; case '\0': /* sigh */ continue; } break; default: /* normal char */ sub(cb, p, 1); break; } p++; } idx = cb->regexp->start + cb->regexp->size - text; if (!(cb->flags & CB_GLOBSUBST) || text[idx] == '\0' || (cb->regexp->size == 0 && text[++idx] == '\0')) { break; } } if (found) { if (text[idx] != '\0') { /* concatenate unchanged part of line after found pattern */ cb->flags &= ~(CB_UPPER | CB_LOWER | CB_TUPPER | CB_TLOWER); sub(cb, text + idx, strlen(text + idx)); } if (!(cb->flags & CB_CURRENTBLK)) { /* start a new block of lines with substitutions in them */ cb->flags |= CB_CHANGE; cb->first = cb->lineno; startblock(cb); cb->flags |= CB_CURRENTBLK; } /* add this changed line to block */ cb->buffer[cb->buflen] = '\0'; if (newlines == 0) { addblock(cb, cb->buffer); } else { /* * There were newlines in the substituted string. Add all * lines to the current block, and save the marks in range. */ p = cb->buffer; do { addblock(cb, p); p += strlen(p) + 1; } while (p <= cb->buffer + cb->buflen); for (k = cb->mark, l = cb->moffset; k < &cb->mark[26]; k++, l++) { if (*k == cb->lineno && *l == 0) { *l = *k + cb->offset; } } cb->offset += newlines; } cb->buflen = 0; cb->last = cb->lineno; } else { if (cb->flags & CB_SKIPPED) { /* two lines without substitutions now. Finish previous block. */ endblock(cb); cb->lineno += cb->offset; cb->offset = 0; cb->flags &= ~(CB_CURRENTBLK | CB_SKIPPED); } else if (cb->flags & CB_CURRENTBLK) { /* * no substitution on this line, but there was one on the previous * line. mark this line as skipped, so it can still be added to * the block of changed lines if the next line has substitutions. */ strcpy(cb->buffer, text); cb->flags |= CB_SKIPPED; } } cb->lineno++; }
/* * NAME: indent() * DESCRIPTION: Parse and indent a line of text. This isn't perfect, as * keywords could be defined as macros, comments are very hard to * handle properly, (, [ and ({ will match any of ), ] and }), * and last but not least everyone has his own taste of * indentation. */ static void indent(const char *text) { static char f[] = { 7, 1, 7, 1, 2, 1, 6, 4, 2, 6, 7, 2, 0, }; static char g[] = { 2, 2, 1, 7, 1, 5, 1, 3, 6, 2, 2, 2, 0, }; char ident[MAX_LINE_SIZE]; char line[MAX_LINE_SIZE]; cmdbuf *cb; const char *p; char *sp; int *ip, idx; int top, token; const char *start; bool do_indent; cb = ccb; do_indent = FALSE; idx = 0; p = text = strcpy(line, text); /* process status vars */ if (cb->quote != '\0') { cb->shift = 0; /* in case a comment starts on this line */ noshift(cb, p); } else if ((cb->flags & CB_PPCONTROL) || *p == '#') { noshift(cb, p); while (*p != '\0') { if (*p == '\\' && *++p == '\0') { cb->flags |= CB_PPCONTROL; return; } p++; } cb->flags &= ~CB_PPCONTROL; return; } else { /* count leading ws */ while (*p == ' ' || *p == HT) { if (*p++ == ' ') { idx++; } else { idx = (idx + 8) & ~7; } } if (*p == '\0') { noshift(cb, p); return; } else if (cb->flags & CB_COMMENT) { shift(text); /* use previous shift */ } else { do_indent = TRUE; } } /* process this line */ start = p; while (*p != '\0') { /* lexical scanning: find the next token */ ident[0] = '\0'; if (cb->flags & CB_COMMENT) { /* comment */ while (*p != '*') { if (*p == '\0') { return; } p++; } while (*p == '*') { p++; } if (*p == '/') { cb->flags &= ~CB_COMMENT; p++; } continue; } else if (cb->quote != '\0') { /* string or character constant */ for (;;) { if (*p == cb->quote) { cb->quote = '\0'; p++; break; } else if (*p == '\0') { cb->last = cb->lineno; endblock(cb); error("Unterminated string"); } else if (*p == '\\' && *++p == '\0') { break; } p++; } token = TOKEN; } else { switch (*p++) { case ' ': /* white space */ case HT: continue; case '\'': /* start of string */ case '"': cb->quote = p[-1]; continue; case '/': if (*p == '*') { /* start of comment */ cb->flags |= CB_COMMENT; if (do_indent) { /* this line hasn't been indented yet */ cb->shift = cb->ind[0] - idx; shift(text); do_indent = FALSE; } else { const char *q; int idx2; /* * find how much the comment has shifted, so the same * shift can be used if the comment continues on the * next line */ idx2 = cb->ind[0]; for (q = start; q < p - 1;) { if (*q++ == HT) { idx = (idx + 8) & ~7; idx2 = (idx2 + 8) & ~7; } else { idx++; idx2++; } } cb->shift = idx2 - idx; } p++; continue; } token = TOKEN; break; case '{': token = LBRACKET; break; case '(': if (cb->flags & CB_JSKEYWORD) { /* * LOPERATOR & ROPERATOR are a kludge. The operator * precedence parser that is used could not work if * parenthesis after keywords was not treated specially. */ token = LOPERATOR; break; } if (*p == '{') { p++; /* ({ is one token */ } case '[': token = LHOOK; break; case '}': if (*p != ')') { token = RBRACKET; break; } p++; /* }) is one token; fall through */ case ')': case ']': token = RHOOK; break; case ';': token = SEMICOLON; break; default: if (isalpha(*--p) || *p == '_') { char *q; /* Identifier. See if it's a keyword. */ q = ident; do { *q++ = *p++; } while (isalnum(*p) || *p == '_'); *q = '\0'; if (strcmp(ident, "if") == 0) token = IF; else if (strcmp(ident, "else") == 0) token = ELSE; else if (strcmp(ident, "for") == 0 || strcmp(ident, "while") == 0 || strcmp(ident, "rlimits") == 0) token = FOR; else if (strcmp(ident, "do") == 0) token = DO; else /* not a keyword */ token = TOKEN; } else { /* anything else is a "token" */ p++; token = TOKEN; } break; } } /* parse */ sp = cb->stack; ip = cb->ind; for (;;) { top = *sp; if (top == LOPERATOR && token == RHOOK) { /* ) after LOPERATOR is ROPERATOR */ token = ROPERATOR; } if (f[top] <= g[token]) { /* shift the token on the stack */ int i; if (sp == cb->stackbot) { /* out of stack. Finish already indented block. */ cb->last = cb->lineno; endblock(cb); error("Nesting too deep"); } /* handle indentation */ i = *ip; /* if needed, reduce indentation prior to shift */ if ((token == LBRACKET && (*sp == ROPERATOR || *sp == ELSE || *sp == DO)) || token == RBRACKET || (token == IF && *sp == ELSE)) { /* back up */ i -= SHIFTWIDTH(cb->vars); } /* shift the current line, if appropriate */ if (do_indent) { cb->shift = i - idx; if (i > 0 && token != RHOOK && (*sp == LOPERATOR || *sp == LHOOK)) { /* half indent after ( [ ({ (HACK!) */ cb->shift += SHIFTWIDTH(cb->vars) / 2; } else if (token == TOKEN && *sp == LBRACKET && (strcmp(ident, "case") == 0 || strcmp(ident, "default") == 0)) { /* back up if this is a switch label */ cb->shift -= SHIFTWIDTH(cb->vars); } shift(text); do_indent = FALSE; } /* change indentation after current token */ if (token == LBRACKET || token == ROPERATOR || token == ELSE || token == DO) { /* add indentation */ i += SHIFTWIDTH(cb->vars); } else if (token == SEMICOLON && (*sp == ROPERATOR || *sp == ELSE)) { /* in case it is followed by a comment */ i -= SHIFTWIDTH(cb->vars); } *--sp = token; *--ip = i; break; } /* reduce handle */ do { top = *sp++; ip++; } while (f[*sp] >= g[top]); } cb->stack = sp; cb->ind = ip; if (token >= IF) { /* but not ELSE */ cb->flags |= CB_JSKEYWORD; } else { cb->flags &= ~CB_JSKEYWORD; } } }
/* * NAME: cmdbuf->substitute() * DESCRIPTION: do substitutions on a range of lines */ int cb_subst(cmdbuf *cb) { char buf[MAX_LINE_SIZE], delim; Int m[26]; Int edit; const char *p; Int *k, *l; delim = cb->cmd[0]; if (delim == '\0' || strchr("0123456789gpl#-+", delim) != (char*) NULL) { /* no search pattern & replace string specified */ if (cb->search[0] == '\0') { error("No previous substitute to repeat"); } } else if (!isalpha(delim)) { char *q; /* get search pattern */ p = pattern(cb->cmd + 1, delim, cb->search); /* get replace string */ q = cb->replace; while (*p != '\0') { if (*p == delim) { p++; break; } if (q == cb->replace + STRINGSZ - 1) { cb->search[0] = '\0'; error("Replace string too large"); } if ((*q++ = *p++) == '\\' && *p != '\0') { *q++ = *p++; } } *q = '\0'; cb->cmd = p; } else { /* cause error */ cb->search[0] = '\0'; } if (cb->search[0] == '\0') { error("Missing regular expression for substitute"); } /* compile regexp */ p = rx_comp(cb->regexp, cb->search); if (p != (char *) NULL) { error(p); } cb_count(cb); /* get count */ /* handle global flag */ if (cb->cmd[0] == 'g') { cb->flags |= CB_GLOBSUBST; cb->cmd++; } else { cb->flags &= ~CB_GLOBSUBST; } /* make a blank mark table */ cb->moffset = m; for (l = m; l < &m[26]; ) { *l++ = 0; } cb->offset = 0; /* do substitutions */ cb_do(cb, cb->first); cb->lineno = cb->first; edit = cb->edit; cb->buffer = buf; cb->buflen = 0; cb->flags &= ~(CB_CURRENTBLK | CB_SKIPPED); eb_range(cb->edbuf, cb->first, cb->last, subst, FALSE); if (cb->flags & CB_CURRENTBLK) { /* finish current block, if needed */ endblock(cb); } cb->othis = cb->uthis; if (edit != cb->edit) { /* some marks may have been messed up. fix them */ for (l = m, k = cb->mark; l < &m[26]; l++, k++) { if (*l != 0) { *k = *l; } } } else if (!(cb->flags & CB_GLOBAL)) { error("Substitute pattern match failed"); } return RET_FLAGS; }