int pruneT(Trie* st, Trie *newst, int e, int oldstCodes) { // if escape flag, start from 3 without checking if reserved. int newCount = (e) ? 3 : 259; // codeCount of new string table // index of old trie we are iterating thru to copy int oldindex = (e) ? 3 : 259; // copy the appearances of the first 256 non-reserved codes for(int i=3; i<newCount; i++){ (*newst)[i].appearances = (*st)[i].appearances/2; } while(oldindex < oldstCodes) { if(((*st)[oldindex].appearances/2)) { if(filledTable(newCount+1)){ expandT(newst, (newCount+1)*2); } addT(newst, (*st)[oldindex].prefix, (*st)[oldindex].suffix, newCount); (*newst)[newCount].appearances = (*st)[oldindex].appearances/2; for(int i=0; i<(*st)[oldindex].childCount; i++){ ((*st)[(*st)[oldindex].children[i]]).prefix = newCount; } newCount++; } oldindex++; } return newCount; }
/** * Execute macro primitives. * @param mb macro expansion state * @param negate should logic be inverted? * @param f beginning of field f * @param fn length of field f * @param g beginning of field g * @param gn length of field g */ static void doFoo(MacroBuf mb, int negate, const char * f, size_t fn, const char * g, size_t gn) { size_t blen = MACROBUFSIZ + fn + gn; char *buf = xmalloc(blen); char *b = NULL, *be; int c; buf[0] = '\0'; if (g != NULL) { strncpy(buf, g, gn); buf[gn] = '\0'; (void) expandU(mb, buf, blen); } if (STREQ("basename", f, fn)) { if ((b = strrchr(buf, '/')) == NULL) b = buf; else b++; #if NOTYET /* XXX watchout for conflict with %dir */ } else if (STREQ("dirname", f, fn)) { if ((b = strrchr(buf, '/')) != NULL) *b = '\0'; b = buf; #endif } else if (STREQ("suffix", f, fn)) { if ((b = strrchr(buf, '.')) != NULL) b++; } else if (STREQ("expand", f, fn)) { b = buf; } else if (STREQ("verbose", f, fn)) { if (negate) b = (rpmIsVerbose() ? NULL : buf); else b = (rpmIsVerbose() ? buf : NULL); } else if (STREQ("url2path", f, fn) || STREQ("u2p", f, fn)) { (void)urlPath(buf, (const char **)&b); if (*b == '\0') b = "/"; } else if (STREQ("uncompress", f, fn)) { rpmCompressedMagic compressed = COMPRESSED_OTHER; for (b = buf; (c = *b) && isblank(c);) b++; for (be = b; (c = *be) && !isblank(c);) be++; *be++ = '\0'; (void) rpmFileIsCompressed(b, &compressed); switch(compressed) { default: case COMPRESSED_NOT: sprintf(be, "%%__cat %s", b); break; case COMPRESSED_OTHER: sprintf(be, "%%__gzip -dc %s", b); break; case COMPRESSED_BZIP2: sprintf(be, "%%__bzip2 -dc %s", b); break; case COMPRESSED_ZIP: sprintf(be, "%%__unzip %s", b); break; case COMPRESSED_LZMA: sprintf(be, "%%__lzma -dc %s", b); break; } b = be; } else if (STREQ("getenv", f, fn)) { b = getenv(buf); } else if (STREQ("S", f, fn)) { for (b = buf; (c = *b) && risdigit(c);) b++; if (!c) { /* digit index */ b++; sprintf(b, "%%SOURCE%s", buf); } else b = buf; } else if (STREQ("P", f, fn)) { for (b = buf; (c = *b) && risdigit(c);) b++; if (!c) { /* digit index */ b++; sprintf(b, "%%PATCH%s", buf); } else b = buf; } else if (STREQ("F", f, fn)) { b = buf + strlen(buf) + 1; sprintf(b, "file%s.file", buf); } if (b) { (void) expandT(mb, b, strlen(b)); } free(buf); }
// encodes the input stream by taking advantage of lzw algorithm // also implements logic to prune the trie structure used to store the string // table, and escapes single character codes void encode(int e, int m, int p) { Trie st; createT(&st, e); int c = EMPTY; // same as (EMPTY, K), index of the prefix // int value of char k we are testing to see if c,k exists in the table int k; // number of codes you have inserted, 256 without escape flag... int codeCount = (e) ? 3 : 259; int bitCount = (e) ? 2 : 9; int maxbits = (m<=8 || m>20) ? 12 : m; int maxcodes = (1 << maxbits); int firstRead = false; // if first read of k when e flag is present int pruneCount = 0; printf("%02d:%d:%d:", maxbits, p, e); while((k = getchar())!= EOF) { st[c].appearances++; int ck = searchT(&st, c, k, e); // will increment c's appearance once // if ck is not in the table if(ck<0) { // if prune flag & reached maxcodes, do a prune before next insert // into the table, putBits 0 to indicate a prune has occurred // a prune should likewise happen in decode if(c!=EMPTY) { putBits(bitCount, c); } // add ck to the table as long as (e && c == EMPTY) is false // we will add (empty, k) to the table after this condition // !e and c==EMPTY will never happen, bc all chars will have been // added as children to empty // prune right before we reach maxcodes, we would have lost the next // code we insert anyways, now we won't lose k if(p&&(codeCount+1==maxcodes)) { putBits(bitCount, 0); pruneCount++; Trie newst; createT(&newst, e); int oldCodeCount = codeCount; codeCount=pruneT(&st, &newst, e, oldCodeCount); destroyT(&st, oldCodeCount); st = newst; bitCount=codeLength(codeCount); c=EMPTY; ungetc(k, stdin); continue; } // if(!e || c!=EMPTY) { if(codeCount<maxcodes) { if(tableFilled(codeCount+1)) { int newSize = (codeCount+1)*2; expandT(&st, newSize); bitCount++; } addT(&st, c, k, codeCount); codeCount++; } } // if escape flag is on and k is not yet added to the table if(e && searchT(&st, EMPTY, k, e) < 0) { putBits(bitCount, ESC); // 1 is the index of escape character putBits(8, k); if(codeCount<maxcodes) { if(codeLength(codeCount+1)-codeLength(codeCount)) { int newSize = (codeCount+1)*2; expandT(&st, newSize); bitCount++; } addT(&st, EMPTY, k, codeCount); codeCount++; } firstRead=true; // encode escaped something, don't unget(k) // if this happens } c = EMPTY; // make c empty again if(!firstRead) { ungetc(k, stdin); // put k back to start reading } // a new character else { firstRead = false; } } else { c=ck; // set c to index of next code } } if(c!=EMPTY) { putBits(bitCount, c); } putBits(bitCount, EOFILE); // puts EOF flushBits(); destroyT(&st, codeCount); }
/** * The main macro recursion loop. * @todo Dynamically reallocate target buffer. * @param mb macro expansion state * @return 0 on success, 1 on failure */ static int expandMacro(MacroBuf mb) { rpmMacroEntry *mep; rpmMacroEntry me; const char *s = mb->s, *se; const char *f, *fe; const char *g, *ge; size_t fn, gn; char *t = mb->t; /* save expansion pointer for printExpand */ int c; int rc = 0; int negate; const char * lastc; int chkexist; if (++mb->depth > max_macro_depth) { rpmlog(RPMLOG_ERR, _("Recursion depth(%d) greater than max(%d)\n"), mb->depth, max_macro_depth); mb->depth--; mb->expand_trace = 1; return 1; } while (rc == 0 && mb->nb > 0 && (c = *s) != '\0') { s++; /* Copy text until next macro */ switch(c) { case '%': if (*s) { /* Ensure not end-of-string. */ if (*s != '%') break; s++; /* skip first % in %% */ } default: SAVECHAR(mb, c); continue; break; } /* Expand next macro */ f = fe = NULL; g = ge = NULL; if (mb->depth > 1) /* XXX full expansion for outermost level */ t = mb->t; /* save expansion pointer for printExpand */ negate = 0; lastc = NULL; chkexist = 0; switch ((c = *s)) { default: /* %name substitution */ while (strchr("!?", *s) != NULL) { switch(*s++) { case '!': negate = ((negate + 1) % 2); break; case '?': chkexist++; break; } } f = se = s; if (*se == '-') se++; while((c = *se) && (risalnum(c) || c == '_')) se++; /* Recognize non-alnum macros too */ switch (*se) { case '*': se++; if (*se == '*') se++; break; case '#': se++; break; default: break; } fe = se; /* For "%name " macros ... */ if ((c = *fe) && isblank(c)) if ((lastc = strchr(fe,'\n')) == NULL) lastc = strchr(fe, '\0'); break; case '(': /* %(...) shell escape */ if ((se = matchchar(s, c, ')')) == NULL) { rpmlog(RPMLOG_ERR, _("Unterminated %c: %s\n"), (char)c, s); rc = 1; continue; } if (mb->macro_trace) printMacro(mb, s, se+1); s++; /* skip ( */ rc = doShellEscape(mb, s, (se - s)); se++; /* skip ) */ s = se; continue; break; case '{': /* %{...}/%{...:...} substitution */ if ((se = matchchar(s, c, '}')) == NULL) { rpmlog(RPMLOG_ERR, _("Unterminated %c: %s\n"), (char)c, s); rc = 1; continue; } f = s+1;/* skip { */ se++; /* skip } */ while (strchr("!?", *f) != NULL) { switch(*f++) { case '!': negate = ((negate + 1) % 2); break; case '?': chkexist++; break; } } for (fe = f; (c = *fe) && !strchr(" :}", c);) fe++; switch (c) { case ':': g = fe + 1; ge = se - 1; break; case ' ': lastc = se-1; break; default: break; } break; } /* XXX Everything below expects fe > f */ fn = (fe - f); gn = (ge - g); if ((fe - f) <= 0) { /* XXX Process % in unknown context */ c = '%'; /* XXX only need to save % */ SAVECHAR(mb, c); #if 0 rpmlog(RPMLOG_ERR, _("A %% is followed by an unparseable macro\n")); #endif s = se; continue; } if (mb->macro_trace) printMacro(mb, s, se); /* Expand builtin macros */ if (STREQ("global", f, fn)) { s = doDefine(mb, se, RMIL_GLOBAL, 1); continue; } if (STREQ("define", f, fn)) { s = doDefine(mb, se, mb->depth, 0); continue; } if (STREQ("undefine", f, fn)) { s = doUndefine(mb->mc, se); continue; } if (STREQ("echo", f, fn) || STREQ("warn", f, fn) || STREQ("error", f, fn)) { int waserror = 0; if (STREQ("error", f, fn)) waserror = 1; if (g != NULL && g < ge) doOutput(mb, waserror, g, gn); else doOutput(mb, waserror, f, fn); s = se; continue; } if (STREQ("trace", f, fn)) { /* XXX TODO restore expand_trace/macro_trace to 0 on return */ mb->expand_trace = mb->macro_trace = (negate ? 0 : mb->depth); if (mb->depth == 1) { print_macro_trace = mb->macro_trace; print_expand_trace = mb->expand_trace; } s = se; continue; } if (STREQ("dump", f, fn)) { rpmDumpMacroTable(mb->mc, NULL); while (iseol(*se)) se++; s = se; continue; } #ifdef WITH_LUA if (STREQ("lua", f, fn)) { rpmlua lua = NULL; /* Global state. */ const char *ls = s+sizeof("{lua:")-1; const char *lse = se-sizeof("}")+1; char *scriptbuf = (char *)xmalloc((lse-ls)+1); const char *printbuf; memcpy(scriptbuf, ls, lse-ls); scriptbuf[lse-ls] = '\0'; rpmluaSetPrintBuffer(lua, 1); if (rpmluaRunScript(lua, scriptbuf, NULL) == -1) rc = 1; printbuf = rpmluaGetPrintBuffer(lua); if (printbuf) { size_t len = strlen(printbuf); if (len > mb->nb) len = mb->nb; memcpy(mb->t, printbuf, len); mb->t += len; mb->nb -= len; } rpmluaSetPrintBuffer(lua, 0); free(scriptbuf); s = se; continue; } #endif /* XXX necessary but clunky */ if (STREQ("basename", f, fn) || STREQ("suffix", f, fn) || STREQ("expand", f, fn) || STREQ("verbose", f, fn) || STREQ("uncompress", f, fn) || STREQ("url2path", f, fn) || STREQ("u2p", f, fn) || STREQ("getenv", f, fn) || STREQ("S", f, fn) || STREQ("P", f, fn) || STREQ("F", f, fn)) { /* FIX: verbose may be set */ doFoo(mb, negate, f, fn, g, gn); s = se; continue; } /* Expand defined macros */ mep = findEntry(mb->mc, f, fn); me = (mep ? *mep : NULL); /* XXX Special processing for flags */ if (*f == '-') { if (me) me->used++; /* Mark macro as used */ if ((me == NULL && !negate) || /* Without -f, skip %{-f...} */ (me != NULL && negate)) { /* With -f, skip %{!-f...} */ s = se; continue; } if (g && g < ge) { /* Expand X in %{-f:X} */ rc = expandT(mb, g, gn); } else if (me && me->body && *me->body) {/* Expand %{-f}/%{-f*} */ rc = expandT(mb, me->body, strlen(me->body)); } s = se; continue; } /* XXX Special processing for macro existence */ if (chkexist) { if ((me == NULL && !negate) || /* Without -f, skip %{?f...} */ (me != NULL && negate)) { /* With -f, skip %{!?f...} */ s = se; continue; } if (g && g < ge) { /* Expand X in %{?f:X} */ rc = expandT(mb, g, gn); } else if (me && me->body && *me->body) { /* Expand %{?f}/%{?f*} */ rc = expandT(mb, me->body, strlen(me->body)); } s = se; continue; } if (me == NULL) { /* leave unknown %... as is */ #ifndef HACK #if DEAD /* XXX hack to skip over empty arg list */ if (fn == 1 && *f == '*') { s = se; continue; } #endif /* XXX hack to permit non-overloaded %foo to be passed */ c = '%'; /* XXX only need to save % */ SAVECHAR(mb, c); #else rpmlog(RPMLOG_ERR, _("Macro %%%.*s not found, skipping\n"), fn, f); s = se; #endif continue; } /* Setup args for "%name " macros with opts */ if (me && me->opts != NULL) { if (lastc != NULL) { se = grabArgs(mb, me, fe, lastc); } else { addMacro(mb->mc, "**", NULL, "", mb->depth); addMacro(mb->mc, "*", NULL, "", mb->depth); addMacro(mb->mc, "#", NULL, "0", mb->depth); addMacro(mb->mc, "0", NULL, me->name, mb->depth); } } /* Recursively expand body of macro */ if (me->body && *me->body) { mb->s = me->body; rc = expandMacro(mb); if (rc == 0) me->used++; /* Mark macro as used */ } /* Free args for "%name " macros with opts */ if (me->opts != NULL) freeArgs(mb); s = se; } *mb->t = '\0'; mb->s = s; mb->depth--; if (rc != 0 || mb->expand_trace) printExpansion(mb, t, mb->t); return rc; }
// decodes using lzw algorithm void decode() { int code, newcode; int maxbits = getFlags(2); int maxcodes = (1 << maxbits); int p = getFlags(1); int e = getFlags(1); Trie st; createT(&st, e); int bitCount = (e) ? 2 : 9; int codeCount = (e) ? 3 : 259; int oldc = EMPTY; bool kwk = false; char baseK; int pruneCount=0, kwkcount=0; while((newcode=code=getBits(bitCount))!=EOFILE) { // under these conditions, a valid prune can occur if(p && code==EMPTY) { pruneCount++; Trie newst; createT(&newst, e); int oldCodeCount=codeCount; codeCount=pruneT(&st, &newst, e, oldCodeCount); destroyT(&st, oldCodeCount); st=newst; bitCount=codeLength(codeCount); oldc=EMPTY; continue; } if(newcode>=codeCount+1) { ERROR("code impossible to decode\n"); } // read an escaped character else if(e && code==ESC) { if(tableFilled(codeCount+1)&&bitCount!=codeLength(codeCount+1)) { if(bitCount<maxbits) { bitCount++; } } code=getBits(8); baseK=(char)code; putchar(baseK); if(codeCount<maxcodes) { if(tableFilled(codeCount+1)) { expandT(&st, (codeCount)*2); } addT(&st, oldc, code, codeCount); codeCount++; // then we need to add the char k as it's own code if(oldc!=EMPTY) { if(tableFilled(codeCount+1)) { expandT(&st, (codeCount)*2); } addT(&st, EMPTY, code, codeCount); codeCount++; if(tableFilled(codeCount)&&bitCount!=codeLength(codeCount)) { if(bitCount<maxbits) { bitCount++; } } } } oldc=EMPTY; } else { // no escape character called, would read c and k normally if(newcode==codeCount) { kwk=true; code=oldc; // omega, need to print k after } baseK=outputCode(&st, code, true); if(kwk) { putchar(baseK); kwkcount++; } // oldc is empty on the first read, and when the e-flag is present // oldc is zero when the last character read was escaped if(oldc!=EMPTY) { if(codeCount<maxcodes) { if(tableFilled(codeCount+1)) { expandT(&st, (codeCount)*2); } addT(&st, oldc, (int)baseK, codeCount); codeCount++; if(kwk) { // we added kwk after seeing it once already in the prev // scan through so we should increase its number of apps st[newcode].appearances++; // this scenario means we have kkk, without w in between // so if(st[st[oldc].prefix].prefix==EMPTY&&kwkcount==1) { st[oldc].appearances--; } kwk=false; } if(tableFilled(codeCount+1)) { if(bitCount<maxbits&&bitCount!=codeLength(codeCount+1)) { bitCount++; } } } } else if(e) { // if e-flag & last char was excaped, increase bit count if // table is filled now if(tableFilled(codeCount+1)) { if(bitCount<maxbits) { bitCount++; } } } oldc = newcode; } } destroyT(&st, codeCount); }