ksb@s.cc.purdue.edu (Kevin Braunsdorf) (07/31/88)
Posting-number: Volume 4, Issue 11 Submitted-by: "Kevin Braunsdorf" <ksb@s.cc.purdue.edu> Archive-name: curly # This is a shell archive. # Remove everything above and including the cut line. # Then run the rest of the file through sh. #----cut here-----cut here-----cut here-----cut here----# #!/bin/sh # shar: Shell Archiver # Run the following text with /bin/sh to create: # README # curly.c # uncurly.c # This archive created: Sat Jul 30 18:57:01 1988 # By: Kevin Braunsdorf (Purdue UNIX Group) sed 's/^K//' << \SHAR_EOF > README KHere are two programs I have found (recently) to be quite useful. KOne of them expands the C-Shell curly braces notation for file name Kbuilding, the other compresses a list of file names into one of Kthese expressions. I called these programs `curly' and `uncurly'. K KThere is am example usage of {un,}curly in the comments in the code, Kcopied below. I have used them to compress a dictionary (one initial Kletter at a time BTW), and to compress lists of files on tape. K KOne might pipe the output of a find to uncurly and compress to store Ka list of filenames as: K K $ find . -type f -print | uncurly | compress > /tmp/files.u.Z K Kthen (later on) we need that list of files again... K K $ zcat /tmp/files.u.Z | curly | xargs do_something K Kthis yields substantial compression over just compress alone, which it Kshouldn't. (We know something quite special about the output of find Kon a `normal' find output that gives us an advantage here.) K KI always name the output from uncurly as files.u. Here is some sample Kcompression data (for 567 handy filenames in my src directory): K K 15 -rw-r----- 1 ksb 14435 Jul 30 17:27 files # 100% K 5 -rw-r----- 1 ksb 4754 Jul 30 17:27 files.Z # 32.9% K 5 -rw-r----- 1 ksb 5074 Jul 30 17:26 files.u # 35.2% K 3 -rw-r----- 1 ksb 2810 Jul 30 17:27 files.u.Z # 17.3% K KI would like to be mailed any bug reports so I can fix my own copy. K KKnown bugs: doesn't handle files with `{' or `}' in them well. K KEnjoy. Kkayessbee (Kevin S Braunsdorf, ksb@j.cc.purdue.edu, pur-ee!ksb) SHAR_EOF sed 's/^K//' << \SHAR_EOF > curly.c K/* K * curly -- expand {...} as csh(1) (ksb) K * K * Copyright 1988, All Rights Reserved K * Kevin S Braunsdorf K * ksb@j.cc.purdue.edu, pur-ee!ksb K * Math/Sci Building, Purdue Univ K * West Lafayette, IN K * K * `You may redistibute this code as long as you don't claim to have K * written it. -- ksb' K * K * We are limited to not eating backslash escapes because that would be K * very confusing to the user. If you need a file name {a}.c don't call K * this routine. Simple. (If we did use \ as special, then \.c would K * need to be quoted from us... it never ends, so we let the shells worry K * about \ quoting for us.) K * K * We don't expand other globbing characters, because ksh will expand K * those for us when it reads our output in `quotes`. K * K * The command K * $ curly c{1,2,3,4,5}.c K * outputs K * c1.c c2.c c3.c c4.c c5.c K * K * So use you might use K * $ tar xv `curly c{1,2,3,4,5}.c` K * to extract them from tape. K * K * If we are given no arguments we can read stdin for strings to glob. K * The READSTDIN switch controls this feature. K * K * Improvments: K * K * This code could be mixed with other globbing code to fully emulate K * csh(1) globbing in a few minutes, I have no need of this (yet). K * K * We can avoid the malloc/strcpy/strcat in DoExpr if we build right K * context right to left in a large buffer; this buffer could limit the K * size of the glob expression, but other factors limit it already. K * K * $Compile: ${CC-cc} ${DEBUG--O} ${SYS--Dbsd} -DREADSTDIN %f -o %F K * $Compile: ${CC-cc} ${DEBUG--O} ${SYS--Dbsd} %f -o %F K * $Lint: lint -abhxp ${SYS--Dbsd} -DREADSTDIN %f K * $Lint: lint -abhxp ${SYS--Dbsd} %f K */ K#include <stdio.h> K#include <sys/param.h> K#include <sys/types.h> K Kstatic char *progname = K "$Id: curly.c,v 2.0 88/07/30 17:10:38 ksb Exp $"; K K/* K * If your compiler doesn't allow `register' as a parameter storage class K * define PREG as empty, and don't worry about it. K */ K#define PREG register /* make arguments faster access */ K/* #define PREG /* no register arguments */ K K#if defined(bsd) K#define strrchr rindex /* I must be on bsd, us rindex */ K#endif K K#if !defined(MAXPATHLEN) K#define MAXPATHLEN 1024 K#endif K Kextern char *malloc(), *realloc(), *strcpy(); K K/* static int iMatch = 0; */ Kstatic char acName[MAXPATHLEN]; Kextern void DoExpr(), DoList(); K K#if defined(READSTDIN) K#define FIRST_GUESS 8 /* be get on MAXPATHLEN * this */ K#define NEXT_GUESS 2 /* we hedge with MAXPATHLEN * this */ K#define GRAB 2 /* size chunk to read (<= NEXT_GUESS) */ K Kstatic char acNoMem[] = "%s: out of memory\n"; K K/* K * Here we call gets() to read a glob expression to do. (ksb) K * Repeat until end of file. K */ Kvoid KDoStdin(pcAccum) KPREG char *pcAccum; K{ K extern char *strrchr(); K auto char acLine[MAXPATHLEN*GRAB]; K static char *pcLine = (char *)0; K static unsigned uBufLen = 0; K register unsigned uPos; K register char *pcNewLine; K K acLine[MAXPATHLEN*GRAB-1] = '\000'; K if ((char *)0 == pcLine) { K uBufLen = MAXPATHLEN*FIRST_GUESS; K pcLine = malloc(uBufLen); K if ((char *)0 == pcLine) { K fprintf(stderr, acNoMem, progname); K exit(1); K } K } K uPos = 0; K while (NULL != fgets(acLine, MAXPATHLEN*GRAB-1, stdin)) { K pcNewLine = strrchr(acLine, '\n'); K if (0 == uPos && (char *)0 != pcNewLine) { K *pcNewLine = '\000'; K DoExpr(pcAccum, acLine, "\n"); K continue; K } K if ((char *)0 != pcNewLine) { K *pcNewLine = '\000'; K } K if (uPos + MAXPATHLEN*GRAB-1 > uBufLen) { K uBufLen += MAXPATHLEN*NEXT_GUESS; K pcLine = realloc(pcLine, uBufLen); K } K strcpy(pcLine+uPos, acLine); K if ((char *)0 == pcNewLine) { /* we got chars, no end yet */ K uPos += MAXPATHLEN*GRAB-2; K continue; K } K /* we have a line */ K DoExpr(pcAccum, pcLine, "\n"); K uPos = 0; K } K} K#endif /* we can read stdin for a list of patterns */ K K/* K * find a matching close char for the open we just ate, or (char *)0 (ksb) K * pc = FindMatch("test(a,b))+f(d)", '(', ')', 1); K * ^ pc points here K */ Kchar * KFindMatch(pcBuf, cOpen, cClose, iLevel) Kchar *pcBuf; Kchar cOpen, cClose; Kint iLevel; K{ K while ('\000' != *pcBuf) { K if (cClose == *pcBuf) { K --iLevel; K } else if (cOpen == *pcBuf) { K ++iLevel; K } K if (0 == iLevel) K return pcBuf; K ++pcBuf; K } K return (char *)0; K} K K/* K * if we can locate a curly expression in our expression if the form: (ksb) K * left { list } right K * 1) copy left side to pcAccum, K * 2) add right to our right context (malloc a new buffer if needed) K * 3) call DoList(pcAccum, list, right) K * or if we find no such curly expression K * 1) copy all nonspecial chars to pcAccum K * 2) recurse with DoExpr(pcAccum, pcRight, "") K */ Kvoid KDoExpr(pcAccum, pcExpr, pcRight) KPREG char *pcAccum; Kchar *pcExpr, *pcRight; K{ K extern void DoList(); K extern char *malloc(), *strcat(), *strcpy(); K register char *pcClose; K register char *pcComma; K register char *pcTemp; K register unsigned int uLen; K K while ('{' != *pcExpr && '\000' != *pcExpr) { /*}*/ K *pcAccum++ = *pcExpr++; K } K K switch (*pcExpr) { K case '\000': K if (*pcRight == '\000') { /* no right context */ K if (pcAccum != acName) { K *pcAccum = '\000'; K fputs(acName, stdout); K /* ++iMatch; */ K } K } else { K DoExpr(pcAccum, pcRight, ""); K } K break; K case '{': K pcClose = FindMatch(pcExpr, '{', '}', 0); K /* K * if an open is unbalanced we ignore it. K */ K if ((char *)0 == pcClose) { K *pcAccum++ = *pcExpr++; K DoExpr(pcAccum, pcExpr, pcRight); K break; K } K *pcClose++ = '\000'; K pcComma = pcExpr+1; K K /* K * Now that the expr is cracked we can optimize if the K * additional right context is empty. If it is not we K * have to compute a new right context. K */ K uLen = strlen(pcClose); K if (0 == uLen) { K DoList(pcAccum, pcComma, pcRight); K } else { K uLen += strlen(pcRight); K pcTemp = malloc(uLen+1); K (void) strcpy(pcTemp, pcClose); K (void) strcat(pcTemp, pcRight); K DoList(pcAccum, pcComma, pcTemp); K free(pcTemp); K } K *--pcClose = '}'; K break; K } K} K K/* K * do a comma separated list of terms with known right context (ksb) K * 1) loop through exprs at this level K * 2) call DoExpr(pcAccum, SubExpr, Right) K */ Kvoid KDoList(pcAccum, pcList, pcRight) KPREG char *pcAccum; Kchar *pcList, *pcRight; K{ K extern void DoExpr(); K register char *pcThis; K register int iLevel; K K iLevel = 0; K K for (pcThis = pcList; '\000' != *pcList; ++pcList) { K switch (*pcList) { K case '{': K ++iLevel; K break; K case '}': K --iLevel; K break; K default: K break; K case ',': K if (0 == iLevel) { K *pcList = '\000'; K DoExpr(pcAccum, pcThis, pcRight); K *pcList = ','; K pcThis = pcList+1; K } K break; K } K } K DoExpr(pcAccum, pcThis, pcRight); K} K K/* K * Special case "{}" as csh(1) does for find (YUCK!) (ksb) K * We take no options so that they won't conflict with anything. K * Count option exprs so we can output a blank line if we come up empty K * (I've forgotten why we do this...) K */ Kint Kmain(argc, argv) Kint argc; Kchar **argv; K{ K register char *pcPat; K K progname = *argv++; K --argc; K K#if defined(READSTDIN) K if (0 == argc) { K DoStdin(acName); K } K#endif K while (argc > 0) { K pcPat = *argv++; K --argc; K /* K * this kludge keeps us more csh(1) compatible K */ K if ('{' == pcPat[0] && '}' == pcPat[1] && '\000' == pcPat[2]) { K fputs("{}\n", stdout); K /* ++iMatch; */ K continue; K } K DoExpr(acName, pcPat, "\n"); K } K K exit(0); K} SHAR_EOF sed 's/^K//' << \SHAR_EOF > uncurly.c K/* K * unculry -- uncurly expand a list of parameters (ksb) K * K * Copyright 1988, All Rights Reserved K * Kevin S Braunsdorf K * ksb@j.cc.purdue.edu, pur-ee!ksb K * Math/Sci Building, Purdue Univ K * West Lafayette, IN K * K * `You may redistibute this code as long as you don't claim to have K * written it. -- ksb' K * K * The command K * $ uncurly c1.c c2.c c3.c c4.c c5.c K * outputs K * c{1,2,3,4,5}.c K * K * So one might pipe the ouptut of a find to uncurly to compress the filenames K * like: K * $ find . -type f -print | uncurly | compress > /tmp/${USER}files.Z K * # later on we need the list again... K * $ zcat /tmp/${USER}files.Z | curly | xargs do_something K * K * Improvments: K * K * This code could be mixed with other globbing code to fully emulate K * an `arcglob' function, however this assumes the files exist in there K * present form and is therefore less useful (to me). K * K * We could free more memory, if we were more carefull with our bookkeeping. K * K * The READSTDIN flag could be stired with the code for main to get something K * that allocate less memory before UnCulry was called, free'd it and went K * back to reading... if you run out of memory you can try it and send me K * a patch :-). K * K * $Compile: ${CC-cc} ${DEBUG--O} ${SYS--Dbsd} -DREADSTDIN %f -o %F K * $Compile: ${CC-cc} ${DEBUG--O} ${SYS--Dbsd} %f -o %F K * $Lint: lint -abhxp ${SYS--Dbsd} -DREADSTDIN %f K * $Lint: lint -abhxp ${SYS--Dbsd} %f K */ K#include <stdio.h> K#include <sys/param.h> K#include <sys/types.h> K Kstatic char *progname = K "$Id: uncurly.c,v 2.0 88/07/30 17:10:50 ksb Exp $"; K K/* K * If your compiler doesn't allow `register' as a parameter storage class K * define PREG as empty, and don't worry about it. K */ K#define PREG register /* make arguments faster access */ K/* #define PREG /* no register arguments */ K K#if defined(bsd) K#define strrchr rindex /* I must be on bsd, us rindex */ K#endif K K#if !defined(MAXPATHLEN) K#define MAXPATHLEN 1024 K#endif K Kextern char *malloc(), *calloc(), *strrchr(), *strcat(); Kstatic char acNoMem[] = "%s: out of memory\n"; K K/* K * find a matching close char for the open we just ate, or (char *)0 (ksb) K * pc = FindMatch("test(a,b))+f(d)", '(', ')', 1); K * ^ pc points here K */ Kchar * KFindMatch(pcBuf, cOpen, cClose, iLevel) KPREG char *pcBuf; Kchar cOpen, cClose; Kint iLevel; K{ K while ('\000' != *pcBuf) { K if (cClose == *pcBuf) { K --iLevel; K } else if (cOpen == *pcBuf) { K ++iLevel; K } K if (0 == iLevel) K return pcBuf; K ++pcBuf; K } K return (char *)0; K} K K/* K * save a string in malloc space (ksb) K */ Kchar * Kstrsave(pc) Kchar *pc; K{ K extern char *strcpy(); K extern int strlen(); K register char *pcMem; K K pcMem = malloc((unsigned int) strlen(pc)+1); K if ((char *)0 == pcMem) { K fprintf(stderr, acNoMem, progname); K exit(1); K } K return strcpy(pcMem, pc); K} K K#if defined(READSTDIN) K#define FIRST_GUESS 8192 /* initial number of input files */ K#define NEXT_GUESS 2048 /* add this many if too few */ K K/* K * Joe wants us to turn a piped list of files into a big glob list (ksb) K * we return the number of files (he gave us) and a vector of them. K */ Kunsigned int KGetFiles(pppcArgv) Kchar ***pppcArgv; K{ K extern char *realloc(); K register unsigned int uCount, uLeft; K register char **ppcVector; K auto char acFile[MAXPATHLEN]; K K ppcVector = (char **) calloc(FIRST_GUESS, sizeof(char *)); K uCount = 0; K uLeft = FIRST_GUESS; K while (NULL != gets(acFile)) { K if (0 == uLeft) { K uLeft = (uCount+NEXT_GUESS) * sizeof(char *); K ppcVector = (char **) realloc((char *)ppcVector, uLeft); K uLeft = NEXT_GUESS; K } K ppcVector[uCount] = strsave(acFile); K ++uCount; K --uLeft; K } K K *pppcArgv = ppcVector; K return uCount; K} K#endif /* find files from stdin */ K K/* K * longest common prefix of more than one string (ksb) K * Note that the prefix must have balanced '{'..'}' in it. K */ Kint KPrefix(n, ppcList, puiLen) Kunsigned int n; Kchar **ppcList; Kunsigned *puiLen; K{ K register int cCmp, cCur, iBal; K auto unsigned int j, i, uArea, uLen, uSpan, uCurlen; K K *puiLen = 0; K K iBal = 0; K for (j = 0; j < n; ++j) { K if ('\000' == ppcList[j][0]) { K break; K } K } K K /* trivial case either first or second sring is empty K */ K if (j < 2) { K return 0; K } K K uCurlen = uArea = uLen = uSpan = 0; K while ('\000' != (cCur = ppcList[0][uCurlen])) { K if ('{' == cCur) K ++iBal; K else if ('}' == cCur) K --iBal; K for (i = 1; i < j; ++i) { K cCmp = ppcList[i][uCurlen]; K if ('\000' == cCmp || cCur != cCmp) { K j = i; K break; K } K } K ++uCurlen; K if (0 == iBal && uCurlen * j > uArea) { K uArea = uCurlen*j; K uLen = uCurlen; K uSpan = j; K } K } K *puiLen = uLen; K return uSpan; K} K K/* K * longest common suffix of more than one string (ksb) K * 1) find the ends of all the strings K * 2) back off until we find a non-match, but keep looking K * 3) return the one with most characters in it K * Note that the suffix must have balanced '{'..'}' in it. K */ Kint KSuffix(n, ppcList, puiLen) Kunsigned int n; Kchar **ppcList; Kunsigned *puiLen; K{ K register char **ppcLast, *pcTemp; K register unsigned int j, i, uCurlen; K auto unsigned uArea, uLen, uSpan, iStopAt; K auto int cCur, iBal; K K *puiLen = 0; K K ppcLast = (char **)calloc(n, sizeof(char *)); K if ((char **)0 == ppcLast) { K fprintf(stderr, acNoMem, progname); K exit(1); K } K for (j = 0; j < n; ++j) { K ppcLast[j] = strrchr(ppcList[j], '\000'); K if (ppcLast[j] == ppcList[j]) { K break; K } K } K K iBal = uCurlen = uArea = uLen = uSpan = 0; K while (ppcLast[0] != ppcList[0]) { K cCur = ppcLast[0][-1]; K if ('{' == cCur) K ++iBal; K else if ('}' == cCur) K --iBal; K iStopAt = -1; K for (i = 0; i < j; ++i) { K pcTemp = --ppcLast[i]; K if (cCur != pcTemp[0]) { K j = i; K break; K } K if (ppcList[i] == pcTemp && -1 == iStopAt) { K iStopAt = i; K } K } K ++uCurlen; K if (0 == iBal && uCurlen * j > uArea) { K uArea = uCurlen*j; K uLen = uCurlen; K uSpan = j; K } K if (-1 != iStopAt) { K j = iStopAt; K } K } K *puiLen = uLen; K free((char *)ppcLast); K return uSpan; K} K K/* K * determine context for a list ppcList[0..n-1] (ksb) K * left { ... } right K * K * If the longest common prefix will eat more character then K * we should use that, else try the longest common suffix. K * If both are 0 chars just return the list (0). K */ Kunsigned int KSplit(n, ppcList, ppcLeft, ppcRight) Kunsigned int n; Kchar **ppcList, **ppcLeft, **ppcRight; K{ K register unsigned int i, iLcs, iLcp; K register char *pcEnd; K auto unsigned int iLcsLen, iLcpLen; K auto int cKeep; K K *ppcLeft = (char *)0; K *ppcRight = (char *)0; K if (n == 1) { K return 1 ; K } K K iLcp = Prefix(n, ppcList, & iLcpLen); K if (iLcp * iLcpLen < 2 + iLcpLen) { K iLcp = 0; K } K K iLcs = Suffix(n, ppcList, & iLcsLen); K if (iLcs * iLcsLen < 2 + iLcsLen) { K iLcs = 0; K } K K if (iLcp * iLcpLen < iLcs * iLcsLen) { K pcEnd = strrchr(ppcList[0], '\000') - iLcsLen; K *ppcRight = strsave(pcEnd); K for (i = 0; i < iLcs; ++i) { K pcEnd = strrchr(ppcList[i], '\000') - iLcsLen; K *pcEnd = '\000'; K } K iLcp = Prefix(iLcs, ppcList, & iLcpLen); K if (iLcp == iLcs) { K pcEnd = ppcList[0] + iLcpLen; K cKeep = *pcEnd; K *pcEnd = '\000'; K *ppcLeft = strsave(ppcList[0]); K *pcEnd = cKeep; K for (i = 0; i < iLcp; ++i) { K ppcList[i] += iLcpLen; K } K } K return iLcs; K } else if (0 != iLcpLen && 0 != iLcp) { K pcEnd = ppcList[0] + iLcpLen; K cKeep = *pcEnd; K *pcEnd = '\000'; K *ppcLeft = strsave(ppcList[0]); K *pcEnd = cKeep; K for (i = 0; i < iLcp; ++i) { K ppcList[i] += iLcpLen; K } K iLcs = Suffix(iLcp, ppcList, & iLcsLen); K if (iLcs == iLcp) { K pcEnd = strrchr(ppcList[0], '\000') - iLcsLen; K *ppcRight = strsave(pcEnd); K for (i = 0; i < iLcs; ++i) { K pcEnd = strrchr(ppcList[i], '\000') - iLcsLen; K *pcEnd = '\000'; K } K } K return iLcp; K } K return 0; K} K/* If there are matched curlies around a K * member of the list we can remove them. K * uLen may be (a few chars) too big, who cares? K */ Kvoid Kmcat(pcAccum, pcElement) KPREG char *pcAccum, *pcElement; K{ K extern int strlen(); K register char *pcMatch; K register unsigned int uLen; K K if ('{' == pcElement[0]) { K uLen = strlen(pcElement)-1; K pcMatch = FindMatch(pcElement, '{', '}', 0); K if (pcMatch == & pcElement[uLen]) { K *pcMatch = '\000'; K strcat(pcAccum, pcElement+1); K *pcMatch = '}'; K } else { K strcat(pcAccum, pcElement); K } K } else { K strcat(pcAccum, pcElement); K } K} K K/* K * undo what a {...} does in csh (ksb) K * We make passes over the list until we can make no more reductions. K * I think this works -- that is it does as good a job as I would. K */ Kunsigned int KUnCurly(n, ppcWhole) Kunsigned int n; Kchar **ppcWhole; K{ K register unsigned int m, i; K register char **ppcList; K auto unsigned int uInside, uLen, uEnd, uSquish; K auto char *pcLeft, *pcRight; K auto char *pcTemp, *pcSep; K K ppcList = ppcWhole; K m = n; K while (m > 0) { K uInside = Split(m, ppcList, & pcLeft, & pcRight); K switch (uInside) { K case 0: K case 1: K /* skip boring files for next pass K */ K --m; K ++ppcList; K break; K default: K /* Left "{" List[0] "," List[uInside-1] "}" Right K */ K n -= m; K uSquish = UnCurly(uInside, ppcList); K uLen = 2; /* close curly and "\000" */ K if ((char *)0 != pcLeft) { K uLen += strlen(pcLeft); K } K for (i = 0; i < uSquish; ++i) { K uLen += 1 + strlen(ppcList[i]); K } K if ((char *)0 != pcRight) { K uLen += strlen(pcRight); K } K pcTemp = malloc(uLen); K if ((char *)0 == pcTemp) { K fprintf(stderr, acNoMem, progname); K exit(1); K } K K pcTemp[0] = '\000'; K if ((char *)0 != pcLeft) { K (void) strcat(pcTemp, pcLeft); K free(pcLeft); K } K if (1 == uSquish) { K mcat(pcTemp, ppcList[0]); K } else { K pcSep = "{"; K for (i = 0; i < uSquish; ++i) { K register char *pcMatch; K K strcat(pcTemp, pcSep); K K mcat(pcTemp, ppcList[i]); K pcSep = ","; K } K strcat(pcTemp, "}"); K } K if ((char *)0 != pcRight) { K (void) strcat(pcTemp, pcRight); K free(pcRight); K } K K uEnd = UnCurly(m-uInside, ppcList+uInside); K n += 1 + uEnd; K ppcList[0] = pcTemp; K for (i = 0 ; i < uEnd; /* update below */) { K ppcList[++i] = ppcList[uInside++]; K } K ppcList = ppcWhole; K m = n; K break; K } K } K return n; K} K K/* K * do the opposite of csh(1) {...} (ksb) K * we cannot process files with a comma in them, but as a special K * case we will remove ",EXT" from the end of a list of files... K * and process those if it is the only comma in each of the files. K * 1) output UnCulry of files with no commas K * 2) output UnCulry of files with `,EXT' (only) on the end K * 3) output files with random commas in them (bletch) K * 4) loop until all files have been done K */ Kint Kmain(argc, argv) Kunsigned int argc; Kchar **argv; K{ K register unsigned int i, uReplace, uCommon; K register char *pcExt; K K progname = *argv++; K --argc; K K#if defined(READSTDIN) K if (argc == 0) { K argc = GetFiles(& argv); K } K#endif K while (0 < argc) { K for (uCommon = 0; uCommon < argc; ++uCommon) { K if ((char *)0 != strrchr(argv[uCommon], ',')) { K break; K } K } K if (0 != uCommon) { K uReplace = UnCurly(uCommon, argv); K argc -= uCommon; K for (i = 0; i < uReplace; ++i) { K puts(argv[i]); K } K argv += uCommon; K } K do { K pcExt = (char *)0; K for (uCommon = 0; uCommon < argc; ++uCommon) { K register char *pcComma; K if ((char *)0 == (pcComma = strrchr(argv[uCommon], ','))) { K break; K } K if ((char *)0 == pcExt) { K *pcComma ='\000'; K pcExt = pcComma+1; K } else if (0 != strcmp(pcExt, pcComma+1)) { K break; K } else { K *pcComma = '\000'; K } K if ((char *)0 != strrchr(argv[uCommon], ',')) { K *pcComma = ','; K break; K } K } K if (0 != uCommon) { K uReplace = UnCurly(uCommon, argv); K argc -= uCommon; K for (i = 0; i < uReplace; ++i) { K fputs(argv[i], stdout); K putchar(','); K puts(pcExt); K } K argv += uCommon; K } K if ((char *)0 != strrchr(argv[0], ',')) { K puts(argv[0]); K argc -= 1; K argv += 1; K uCommon = 1; K } K } while (0 != uCommon); K } K exit(0); K} SHAR_EOF # End of shell archive exit 0