home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 October / usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso / misc / volume4 / curly < prev    next >
Internet Message Format  |  1989-02-03  |  23KB

  1. Path: xanth!mcnc!gatech!bloom-beacon!husc6!m2c!necntc!ncoast!allbery
  2. From: ksb@s.cc.purdue.edu (Kevin Braunsdorf)
  3. Newsgroups: comp.sources.misc
  4. Subject: v04i011: curly/uncurly:  fold and unfold file names into csh {} form
  5. Message-ID: <8807310008.AA16489@s.cc.purdue.edu>
  6. Date: 31 Jul 88 00:08:08 GMT
  7. Sender: allbery@ncoast.UUCP
  8. Reply-To: ksb@s.cc.purdue.edu (Kevin Braunsdorf)
  9. Lines: 896
  10. Approved: allbery@ncoast.UUCP
  11.  
  12. Posting-number: Volume 4, Issue 11
  13. Submitted-by: "Kevin Braunsdorf" <ksb@s.cc.purdue.edu>
  14. Archive-name: curly
  15.  
  16. #    This is a shell archive.
  17. #    Remove everything above and including the cut line.
  18. #    Then run the rest of the file through sh.
  19. #----cut here-----cut here-----cut here-----cut here----#
  20. #!/bin/sh
  21. # shar:    Shell Archiver
  22. #    Run the following text with /bin/sh to create:
  23. #    README
  24. #    curly.c
  25. #    uncurly.c
  26. # This archive created: Sat Jul 30 18:57:01 1988
  27. # By:    Kevin Braunsdorf (Purdue UNIX Group)
  28. sed 's/^K//' << \SHAR_EOF > README
  29. KHere are two programs I have found (recently) to be quite useful.
  30. KOne of them expands the C-Shell curly braces notation for file name
  31. Kbuilding, the other compresses a list of file names into one of
  32. Kthese expressions.  I called these programs `curly' and `uncurly'.
  33. K
  34. KThere is am example usage of {un,}curly in the comments in the code,
  35. Kcopied below.  I have used them to compress a dictionary (one initial
  36. Kletter at a time BTW), and to compress lists of files on tape.
  37. K
  38. KOne might pipe the output of a find to uncurly and compress to store
  39. Ka list of filenames as:
  40. K
  41. K    $ find . -type f -print | uncurly | compress > /tmp/files.u.Z
  42. K
  43. Kthen (later on) we need that list of files again...
  44. K
  45. K    $ zcat /tmp/files.u.Z | curly | xargs do_something
  46. K
  47. Kthis yields substantial compression over just compress alone, which it
  48. Kshouldn't.  (We know something quite special about the output of find
  49. Kon a `normal' find output that gives us an advantage here.)
  50. K
  51. KI always name the output from uncurly as files.u.  Here is some sample
  52. Kcompression data (for 567 handy filenames in my src directory):
  53. K
  54. K  15 -rw-r-----  1 ksb         14435 Jul 30 17:27 files        # 100%
  55. K   5 -rw-r-----  1 ksb          4754 Jul 30 17:27 files.Z    # 32.9%
  56. K   5 -rw-r-----  1 ksb          5074 Jul 30 17:26 files.u    # 35.2%
  57. K   3 -rw-r-----  1 ksb          2810 Jul 30 17:27 files.u.Z    # 17.3%
  58. K
  59. KI would like to be mailed any bug reports so I can fix my own copy.
  60. K
  61. KKnown bugs:  doesn't handle files with `{' or `}' in them well.
  62. K
  63. KEnjoy.
  64. Kkayessbee (Kevin S Braunsdorf, ksb@j.cc.purdue.edu, pur-ee!ksb)
  65. SHAR_EOF
  66. sed 's/^K//' << \SHAR_EOF > curly.c
  67. K/*
  68. K * curly -- expand {...} as csh(1)                    (ksb)
  69. K *
  70. K * Copyright 1988, All Rights Reserved
  71. K *    Kevin S Braunsdorf
  72. K *    ksb@j.cc.purdue.edu, pur-ee!ksb
  73. K *    Math/Sci Building, Purdue Univ
  74. K *    West Lafayette, IN
  75. K *
  76. K *  `You may redistibute this code as long as you don't claim to have
  77. K *   written it. -- ksb'
  78. K *
  79. K * We are limited to not eating backslash escapes because that would be
  80. K * very confusing to the user.  If you need a file name {a}.c don't call
  81. K * this routine.  Simple.  (If we did use \ as special, then \.c would
  82. K * need to be quoted from us... it never ends, so we let the shells worry
  83. K * about \ quoting for us.)
  84. K *
  85. K * We don't expand other globbing characters, because ksh will expand
  86. K * those for us when it reads our output in `quotes`.
  87. K *
  88. K * The command
  89. K *    $ curly c{1,2,3,4,5}.c
  90. K * outputs
  91. K *    c1.c c2.c c3.c c4.c c5.c
  92. K *
  93. K * So use you might use
  94. K *    $ tar xv `curly c{1,2,3,4,5}.c`
  95. K * to extract them from tape.
  96. K *
  97. K * If we are given no arguments we can read stdin for strings to glob.
  98. K * The READSTDIN switch controls this feature.
  99. K *
  100. K * Improvments:
  101. K *
  102. K * This code could be mixed with other globbing code to fully emulate
  103. K * csh(1) globbing in a few minutes,  I have no need of this (yet).
  104. K *
  105. K * We can avoid the malloc/strcpy/strcat in DoExpr if we build right
  106. K * context right to left in a large buffer; this buffer could limit the
  107. K * size of the glob expression, but other factors limit it already.
  108. K *
  109. K * $Compile: ${CC-cc} ${DEBUG--O} ${SYS--Dbsd} -DREADSTDIN %f -o %F
  110. K * $Compile: ${CC-cc} ${DEBUG--O} ${SYS--Dbsd} %f -o %F
  111. K * $Lint: lint -abhxp ${SYS--Dbsd} -DREADSTDIN %f
  112. K * $Lint: lint -abhxp ${SYS--Dbsd} %f
  113. K */
  114. K#include <stdio.h>
  115. K#include <sys/param.h>
  116. K#include <sys/types.h>
  117. K
  118. Kstatic char *progname =
  119. K    "$Id: curly.c,v 2.0 88/07/30 17:10:38 ksb Exp $";
  120. K
  121. K/*
  122. K * If your compiler doesn't allow `register' as a parameter storage class
  123. K * define PREG as empty, and don't worry about it.
  124. K */
  125. K#define PREG    register    /* make arguments faster access        */
  126. K/* #define PREG            /* no register arguments        */
  127. K
  128. K#if defined(bsd)
  129. K#define strrchr rindex        /* I must be on bsd, us rindex        */
  130. K#endif
  131. K
  132. K#if !defined(MAXPATHLEN)
  133. K#define MAXPATHLEN    1024
  134. K#endif
  135. K
  136. Kextern char *malloc(), *realloc(), *strcpy();
  137. K
  138. K/* static int iMatch = 0;     */
  139. Kstatic char acName[MAXPATHLEN];
  140. Kextern void DoExpr(), DoList();
  141. K
  142. K#if defined(READSTDIN)
  143. K#define FIRST_GUESS    8    /* be get on MAXPATHLEN * this        */
  144. K#define NEXT_GUESS    2    /* we hedge with MAXPATHLEN * this    */
  145. K#define GRAB        2    /* size chunk to read (<= NEXT_GUESS)    */
  146. K
  147. Kstatic char acNoMem[] = "%s: out of memory\n";
  148. K
  149. K/*
  150. K * Here we call gets() to read a glob expression to do.            (ksb)
  151. K * Repeat until end of file.
  152. K */
  153. Kvoid
  154. KDoStdin(pcAccum)
  155. KPREG char *pcAccum;
  156. K{
  157. K    extern char *strrchr();
  158. K    auto char acLine[MAXPATHLEN*GRAB];
  159. K    static char *pcLine = (char *)0;
  160. K    static unsigned uBufLen = 0;
  161. K    register unsigned uPos;
  162. K    register char *pcNewLine;
  163. K
  164. K    acLine[MAXPATHLEN*GRAB-1] = '\000';
  165. K    if ((char *)0 == pcLine) {
  166. K        uBufLen = MAXPATHLEN*FIRST_GUESS;
  167. K        pcLine = malloc(uBufLen);
  168. K        if ((char *)0 == pcLine) {
  169. K            fprintf(stderr, acNoMem, progname);
  170. K            exit(1);
  171. K        }
  172. K    }
  173. K    uPos = 0;
  174. K    while (NULL != fgets(acLine, MAXPATHLEN*GRAB-1, stdin)) {
  175. K        pcNewLine = strrchr(acLine, '\n');
  176. K        if (0 == uPos && (char *)0 != pcNewLine) {
  177. K            *pcNewLine = '\000';
  178. K            DoExpr(pcAccum, acLine, "\n");
  179. K            continue;
  180. K        }
  181. K        if ((char *)0 != pcNewLine) {
  182. K            *pcNewLine = '\000';
  183. K        }
  184. K        if (uPos + MAXPATHLEN*GRAB-1 > uBufLen) {
  185. K            uBufLen += MAXPATHLEN*NEXT_GUESS;
  186. K            pcLine = realloc(pcLine, uBufLen);
  187. K        }
  188. K        strcpy(pcLine+uPos, acLine);
  189. K        if ((char *)0 == pcNewLine) {    /* we got chars, no end yet */
  190. K            uPos += MAXPATHLEN*GRAB-2;
  191. K            continue;
  192. K        }
  193. K        /* we have a line */
  194. K        DoExpr(pcAccum, pcLine, "\n");
  195. K        uPos = 0;
  196. K    }
  197. K}
  198. K#endif    /* we can read stdin for a list of patterns */
  199. K
  200. K/*
  201. K * find a matching close char for the open we just ate, or (char *)0    (ksb)
  202. K *    pc = FindMatch("test(a,b))+f(d)", '(', ')', 1);
  203. K *                     ^ pc points here
  204. K */
  205. Kchar *
  206. KFindMatch(pcBuf, cOpen, cClose, iLevel)
  207. Kchar *pcBuf;
  208. Kchar cOpen, cClose;
  209. Kint iLevel;
  210. K{
  211. K    while ('\000' != *pcBuf) {
  212. K        if (cClose == *pcBuf) {
  213. K            --iLevel;
  214. K        } else if (cOpen == *pcBuf) {
  215. K            ++iLevel;
  216. K        }
  217. K        if (0 == iLevel)
  218. K            return pcBuf;
  219. K        ++pcBuf;
  220. K    }
  221. K    return (char *)0;
  222. K}
  223. K
  224. K/*
  225. K * if we can locate a curly expression in our expression if the form:    (ksb)
  226. K *         left { list } right
  227. K *    1) copy left side to pcAccum,
  228. K *    2) add right to our right context (malloc a new buffer if needed)
  229. K *    3) call DoList(pcAccum, list, right)
  230. K * or if we find no such curly expression
  231. K *    1) copy all nonspecial chars to pcAccum
  232. K *    2) recurse with DoExpr(pcAccum, pcRight, "")
  233. K */
  234. Kvoid
  235. KDoExpr(pcAccum, pcExpr, pcRight)
  236. KPREG char *pcAccum;
  237. Kchar *pcExpr, *pcRight;
  238. K{
  239. K    extern void DoList();
  240. K    extern char *malloc(), *strcat(), *strcpy();
  241. K    register char *pcClose;
  242. K    register char *pcComma;
  243. K    register char *pcTemp;
  244. K    register unsigned int uLen;
  245. K
  246. K    while ('{' != *pcExpr && '\000' != *pcExpr) {    /*}*/
  247. K        *pcAccum++ = *pcExpr++;
  248. K    }
  249. K
  250. K    switch (*pcExpr) {
  251. K    case '\000':
  252. K        if (*pcRight == '\000') {    /* no right context    */
  253. K            if (pcAccum != acName) {
  254. K                *pcAccum = '\000';
  255. K                fputs(acName, stdout);
  256. K                /* ++iMatch; */
  257. K            }
  258. K        } else {
  259. K            DoExpr(pcAccum, pcRight, "");
  260. K        }
  261. K        break;
  262. K    case '{':
  263. K        pcClose = FindMatch(pcExpr, '{', '}', 0);
  264. K        /*
  265. K         * if an open is unbalanced we ignore it.
  266. K         */
  267. K        if ((char *)0 == pcClose) {
  268. K            *pcAccum++ = *pcExpr++;
  269. K            DoExpr(pcAccum, pcExpr, pcRight);
  270. K            break;
  271. K        }
  272. K        *pcClose++ = '\000';
  273. K        pcComma = pcExpr+1;
  274. K
  275. K        /*
  276. K         * Now that the expr is cracked we can optimize if the
  277. K         * additional right context is empty.  If it is not we
  278. K         * have to compute a new right context.
  279. K         */
  280. K        uLen = strlen(pcClose);
  281. K        if (0 == uLen) {
  282. K            DoList(pcAccum, pcComma, pcRight);
  283. K        } else {
  284. K            uLen += strlen(pcRight);
  285. K            pcTemp = malloc(uLen+1);
  286. K            (void) strcpy(pcTemp, pcClose);
  287. K            (void) strcat(pcTemp, pcRight);
  288. K            DoList(pcAccum, pcComma, pcTemp);
  289. K            free(pcTemp);
  290. K        }
  291. K        *--pcClose = '}';
  292. K        break;
  293. K    }
  294. K}
  295. K
  296. K/*
  297. K * do a comma separated list of terms with known right context        (ksb)
  298. K *    1) loop through exprs at this level
  299. K *    2) call DoExpr(pcAccum, SubExpr, Right)
  300. K */
  301. Kvoid
  302. KDoList(pcAccum, pcList, pcRight)
  303. KPREG char *pcAccum;
  304. Kchar *pcList, *pcRight;
  305. K{
  306. K    extern void DoExpr();
  307. K    register char *pcThis;
  308. K    register int iLevel;
  309. K
  310. K    iLevel = 0;
  311. K
  312. K    for (pcThis = pcList; '\000' != *pcList; ++pcList) {
  313. K        switch (*pcList) {
  314. K        case '{':
  315. K            ++iLevel;
  316. K            break;
  317. K        case '}':
  318. K            --iLevel;
  319. K            break;
  320. K        default:
  321. K            break;
  322. K        case ',':
  323. K            if (0 == iLevel) {
  324. K                *pcList = '\000';
  325. K                DoExpr(pcAccum, pcThis, pcRight);
  326. K                *pcList = ',';
  327. K                pcThis = pcList+1;
  328. K            }
  329. K            break;
  330. K        }
  331. K    }
  332. K    DoExpr(pcAccum, pcThis, pcRight);
  333. K}
  334. K
  335. K/*
  336. K * Special case "{}" as csh(1) does for find (YUCK!)            (ksb)
  337. K * We take no options so that they won't conflict with anything.
  338. K * Count option exprs so we can output a blank line if we come up empty
  339. K * (I've forgotten why we do this...)
  340. K */
  341. Kint
  342. Kmain(argc, argv)
  343. Kint argc;
  344. Kchar **argv;
  345. K{
  346. K    register char *pcPat;
  347. K
  348. K    progname = *argv++;
  349. K    --argc;
  350. K
  351. K#if defined(READSTDIN)
  352. K    if (0 == argc) {
  353. K        DoStdin(acName);
  354. K    }
  355. K#endif
  356. K    while (argc > 0) {
  357. K        pcPat = *argv++;
  358. K        --argc;
  359. K        /*
  360. K         * this kludge keeps us more csh(1) compatible
  361. K         */
  362. K        if ('{' == pcPat[0] && '}' == pcPat[1] && '\000' == pcPat[2]) {
  363. K            fputs("{}\n", stdout);
  364. K            /* ++iMatch; */
  365. K            continue;
  366. K        }
  367. K        DoExpr(acName, pcPat, "\n");
  368. K    }
  369. K
  370. K    exit(0);
  371. K}
  372. SHAR_EOF
  373. sed 's/^K//' << \SHAR_EOF > uncurly.c
  374. K/*
  375. K * unculry -- uncurly expand a list of parameters            (ksb)
  376. K *
  377. K * Copyright 1988, All Rights Reserved
  378. K *    Kevin S Braunsdorf
  379. K *    ksb@j.cc.purdue.edu, pur-ee!ksb
  380. K *    Math/Sci Building, Purdue Univ
  381. K *    West Lafayette, IN
  382. K *
  383. K *  `You may redistibute this code as long as you don't claim to have
  384. K *   written it. -- ksb'
  385. K *
  386. K * The command
  387. K *    $ uncurly c1.c c2.c c3.c c4.c c5.c
  388. K * outputs
  389. K *    c{1,2,3,4,5}.c
  390. K *
  391. K * So one might pipe the ouptut of a find to uncurly to compress the filenames
  392. K * like:
  393. K *    $ find . -type f -print | uncurly | compress > /tmp/${USER}files.Z
  394. K *    # later on we need the list again...
  395. K *    $ zcat /tmp/${USER}files.Z | curly | xargs do_something
  396. K *
  397. K * Improvments:
  398. K *
  399. K * This code could be mixed with other globbing code to fully emulate
  400. K * an `arcglob' function, however this assumes the files exist in there
  401. K * present form and is therefore less useful (to me).
  402. K *
  403. K * We could free more memory, if we were more carefull with our bookkeeping.
  404. K *
  405. K * The READSTDIN flag could be stired with the code for main to get something
  406. K * that allocate less memory before UnCulry was called, free'd it and went
  407. K * back to reading... if you run out of memory you can try it and send me
  408. K * a patch :-).
  409. K *
  410. K * $Compile: ${CC-cc} ${DEBUG--O} ${SYS--Dbsd} -DREADSTDIN %f -o %F
  411. K * $Compile: ${CC-cc} ${DEBUG--O} ${SYS--Dbsd} %f -o %F
  412. K * $Lint: lint -abhxp ${SYS--Dbsd} -DREADSTDIN %f
  413. K * $Lint: lint -abhxp ${SYS--Dbsd} %f
  414. K */
  415. K#include <stdio.h>
  416. K#include <sys/param.h>
  417. K#include <sys/types.h>
  418. K
  419. Kstatic char *progname =
  420. K    "$Id: uncurly.c,v 2.0 88/07/30 17:10:50 ksb Exp $";
  421. K
  422. K/*
  423. K * If your compiler doesn't allow `register' as a parameter storage class
  424. K * define PREG as empty, and don't worry about it.
  425. K */
  426. K#define PREG    register    /* make arguments faster access        */
  427. K/* #define PREG            /* no register arguments        */
  428. K
  429. K#if defined(bsd)
  430. K#define strrchr rindex        /* I must be on bsd, us rindex        */
  431. K#endif
  432. K
  433. K#if !defined(MAXPATHLEN)
  434. K#define MAXPATHLEN    1024
  435. K#endif
  436. K
  437. Kextern char *malloc(), *calloc(), *strrchr(), *strcat();
  438. Kstatic char acNoMem[] = "%s: out of memory\n";
  439. K
  440. K/*
  441. K * find a matching close char for the open we just ate, or (char *)0    (ksb)
  442. K *    pc = FindMatch("test(a,b))+f(d)", '(', ')', 1);
  443. K *                     ^ pc points here
  444. K */
  445. Kchar *
  446. KFindMatch(pcBuf, cOpen, cClose, iLevel)
  447. KPREG char *pcBuf;
  448. Kchar cOpen, cClose;
  449. Kint iLevel;
  450. K{
  451. K    while ('\000' != *pcBuf) {
  452. K        if (cClose == *pcBuf) {
  453. K            --iLevel;
  454. K        } else if (cOpen == *pcBuf) {
  455. K            ++iLevel;
  456. K        }
  457. K        if (0 == iLevel)
  458. K            return pcBuf;
  459. K        ++pcBuf;
  460. K    }
  461. K    return (char *)0;
  462. K}
  463. K
  464. K/*
  465. K * save a string in malloc space                    (ksb)
  466. K */
  467. Kchar *
  468. Kstrsave(pc)
  469. Kchar *pc;
  470. K{
  471. K    extern char *strcpy();
  472. K    extern int strlen();
  473. K    register char *pcMem;
  474. K
  475. K    pcMem = malloc((unsigned int) strlen(pc)+1);
  476. K    if ((char *)0 == pcMem) {
  477. K        fprintf(stderr, acNoMem, progname);
  478. K        exit(1);
  479. K    }
  480. K    return strcpy(pcMem, pc);
  481. K}
  482. K
  483. K#if defined(READSTDIN)
  484. K#define FIRST_GUESS    8192    /* initial number of input files    */
  485. K#define NEXT_GUESS    2048    /* add this many if too few        */
  486. K
  487. K/*
  488. K * Joe wants us to turn a piped list of files into a big glob list    (ksb)
  489. K * we return the number of files (he gave us) and a vector of them.
  490. K */
  491. Kunsigned int
  492. KGetFiles(pppcArgv)
  493. Kchar ***pppcArgv;
  494. K{
  495. K    extern char *realloc();
  496. K    register unsigned int uCount, uLeft;
  497. K    register char **ppcVector;
  498. K    auto char acFile[MAXPATHLEN];
  499. K
  500. K    ppcVector = (char **) calloc(FIRST_GUESS, sizeof(char *));
  501. K    uCount = 0;
  502. K    uLeft = FIRST_GUESS;
  503. K    while (NULL != gets(acFile)) {
  504. K        if (0 == uLeft) {
  505. K            uLeft = (uCount+NEXT_GUESS) * sizeof(char *);
  506. K            ppcVector = (char **) realloc((char *)ppcVector, uLeft);
  507. K            uLeft = NEXT_GUESS;
  508. K        }
  509. K        ppcVector[uCount] = strsave(acFile);
  510. K        ++uCount;
  511. K        --uLeft;
  512. K    }
  513. K
  514. K    *pppcArgv = ppcVector;
  515. K    return uCount;
  516. K}
  517. K#endif    /* find files from stdin    */
  518. K
  519. K/*
  520. K * longest common prefix of more than one string            (ksb)
  521. K * Note that the prefix must have balanced '{'..'}' in it.
  522. K */
  523. Kint
  524. KPrefix(n, ppcList, puiLen)
  525. Kunsigned int n;
  526. Kchar **ppcList;
  527. Kunsigned *puiLen;
  528. K{
  529. K    register int cCmp, cCur, iBal;
  530. K    auto unsigned int j, i, uArea, uLen, uSpan, uCurlen;
  531. K
  532. K    *puiLen = 0;
  533. K
  534. K    iBal = 0;
  535. K    for (j = 0; j < n; ++j) {
  536. K        if ('\000' == ppcList[j][0]) {
  537. K            break;
  538. K        }
  539. K    }
  540. K
  541. K    /* trivial case either first or second sring is empty
  542. K     */
  543. K    if (j < 2) {
  544. K        return 0;
  545. K    }
  546. K
  547. K    uCurlen = uArea = uLen = uSpan = 0;
  548. K    while ('\000' != (cCur = ppcList[0][uCurlen])) {
  549. K        if ('{' == cCur)
  550. K            ++iBal;
  551. K        else if ('}' == cCur)
  552. K            --iBal;
  553. K        for (i = 1; i < j; ++i) {
  554. K            cCmp = ppcList[i][uCurlen];
  555. K            if ('\000' == cCmp || cCur != cCmp) {
  556. K                j = i;
  557. K                break;
  558. K            }
  559. K        }
  560. K        ++uCurlen;
  561. K        if (0 == iBal && uCurlen * j > uArea) {
  562. K            uArea = uCurlen*j;
  563. K            uLen = uCurlen;
  564. K            uSpan = j;
  565. K        }
  566. K    }
  567. K    *puiLen = uLen;
  568. K    return uSpan;
  569. K}
  570. K
  571. K/*
  572. K * longest common suffix of more than one string            (ksb)
  573. K *  1) find the ends of all the strings
  574. K *  2) back off until we find a non-match, but keep looking
  575. K *  3) return the one with most characters in it
  576. K * Note that the suffix must have balanced '{'..'}' in it.
  577. K */
  578. Kint
  579. KSuffix(n, ppcList, puiLen)
  580. Kunsigned int n;
  581. Kchar **ppcList;
  582. Kunsigned *puiLen;
  583. K{
  584. K    register char **ppcLast, *pcTemp;
  585. K    register unsigned int j, i, uCurlen;
  586. K    auto unsigned uArea, uLen, uSpan, iStopAt;
  587. K    auto int cCur, iBal;
  588. K
  589. K    *puiLen = 0;
  590. K
  591. K    ppcLast = (char **)calloc(n, sizeof(char *));
  592. K    if ((char **)0 == ppcLast) {
  593. K        fprintf(stderr, acNoMem, progname);
  594. K        exit(1);
  595. K    }
  596. K    for (j = 0; j < n; ++j) {
  597. K        ppcLast[j] = strrchr(ppcList[j], '\000');
  598. K        if (ppcLast[j] == ppcList[j]) {
  599. K            break;
  600. K        }
  601. K    }
  602. K
  603. K    iBal = uCurlen = uArea = uLen = uSpan = 0;
  604. K    while (ppcLast[0] != ppcList[0]) {
  605. K        cCur = ppcLast[0][-1];
  606. K        if ('{' == cCur)
  607. K            ++iBal;
  608. K        else if ('}' == cCur)
  609. K            --iBal;
  610. K        iStopAt = -1;
  611. K        for (i = 0; i < j; ++i) {
  612. K            pcTemp = --ppcLast[i];
  613. K            if (cCur != pcTemp[0]) {
  614. K                j = i;
  615. K                break;
  616. K            }
  617. K            if (ppcList[i] == pcTemp && -1 == iStopAt) {
  618. K                iStopAt = i;
  619. K            }
  620. K        }
  621. K        ++uCurlen;
  622. K        if (0 == iBal && uCurlen * j > uArea) {
  623. K            uArea = uCurlen*j;
  624. K            uLen = uCurlen;
  625. K            uSpan = j;
  626. K        }
  627. K        if (-1 != iStopAt) {
  628. K            j = iStopAt;
  629. K        }
  630. K    }
  631. K    *puiLen = uLen;
  632. K    free((char *)ppcLast);
  633. K    return uSpan;
  634. K}
  635. K
  636. K/*
  637. K * determine context for a list ppcList[0..n-1]                (ksb)
  638. K *    left { ... } right
  639. K *
  640. K * If the longest common prefix will eat more character then
  641. K * we should use that, else try the longest common suffix.
  642. K * If both are 0 chars just return the list (0).
  643. K */
  644. Kunsigned int
  645. KSplit(n, ppcList, ppcLeft, ppcRight)
  646. Kunsigned int n;
  647. Kchar **ppcList, **ppcLeft, **ppcRight;
  648. K{
  649. K    register unsigned int i, iLcs, iLcp;
  650. K    register char *pcEnd;
  651. K    auto unsigned int iLcsLen, iLcpLen;
  652. K    auto int cKeep;
  653. K
  654. K    *ppcLeft = (char *)0;
  655. K    *ppcRight = (char *)0;
  656. K    if (n == 1) {
  657. K        return 1 ;
  658. K    }
  659. K
  660. K    iLcp = Prefix(n, ppcList, & iLcpLen);
  661. K    if (iLcp * iLcpLen < 2 + iLcpLen) {
  662. K        iLcp = 0;
  663. K    }
  664. K
  665. K    iLcs = Suffix(n, ppcList, & iLcsLen);
  666. K    if (iLcs * iLcsLen < 2 + iLcsLen) {
  667. K        iLcs = 0;
  668. K    }
  669. K
  670. K    if (iLcp * iLcpLen < iLcs * iLcsLen) {
  671. K        pcEnd = strrchr(ppcList[0], '\000') - iLcsLen;
  672. K        *ppcRight = strsave(pcEnd);
  673. K        for (i = 0; i < iLcs; ++i) {
  674. K            pcEnd = strrchr(ppcList[i], '\000') - iLcsLen;
  675. K            *pcEnd = '\000';
  676. K        }
  677. K        iLcp = Prefix(iLcs, ppcList, & iLcpLen);
  678. K        if (iLcp == iLcs) {
  679. K            pcEnd = ppcList[0] + iLcpLen;
  680. K            cKeep = *pcEnd;
  681. K            *pcEnd = '\000';
  682. K            *ppcLeft = strsave(ppcList[0]);
  683. K            *pcEnd = cKeep;
  684. K            for (i = 0; i < iLcp; ++i) {
  685. K                ppcList[i] += iLcpLen;
  686. K            }
  687. K        }
  688. K        return iLcs;
  689. K    } else if (0 != iLcpLen && 0 != iLcp) {
  690. K        pcEnd = ppcList[0] + iLcpLen;
  691. K        cKeep = *pcEnd;
  692. K        *pcEnd = '\000';
  693. K        *ppcLeft = strsave(ppcList[0]);
  694. K        *pcEnd = cKeep;
  695. K        for (i = 0; i < iLcp; ++i) {
  696. K            ppcList[i] += iLcpLen;
  697. K        }
  698. K        iLcs = Suffix(iLcp, ppcList, & iLcsLen);
  699. K        if (iLcs == iLcp) {
  700. K            pcEnd = strrchr(ppcList[0], '\000') - iLcsLen;
  701. K            *ppcRight = strsave(pcEnd);
  702. K            for (i = 0; i < iLcs; ++i) {
  703. K                pcEnd = strrchr(ppcList[i], '\000') - iLcsLen;
  704. K                *pcEnd = '\000';
  705. K            }
  706. K        }
  707. K        return iLcp;
  708. K    }
  709. K    return 0;
  710. K}
  711. K/* If there are matched curlies around a
  712. K * member of the list we can remove them.
  713. K * uLen may be (a few chars) too big, who cares?
  714. K */
  715. Kvoid
  716. Kmcat(pcAccum, pcElement)
  717. KPREG char *pcAccum, *pcElement;
  718. K{
  719. K    extern int strlen();
  720. K    register char *pcMatch;
  721. K    register unsigned int uLen;
  722. K
  723. K    if ('{' == pcElement[0]) {
  724. K        uLen = strlen(pcElement)-1;
  725. K        pcMatch = FindMatch(pcElement, '{', '}', 0);
  726. K        if (pcMatch == & pcElement[uLen]) {
  727. K            *pcMatch = '\000';
  728. K            strcat(pcAccum, pcElement+1);
  729. K            *pcMatch = '}';
  730. K        } else {
  731. K            strcat(pcAccum, pcElement);
  732. K        }
  733. K    } else {
  734. K        strcat(pcAccum, pcElement);
  735. K    }
  736. K}
  737. K
  738. K/*
  739. K * undo what a {...} does in csh                    (ksb)
  740. K * We make passes over the list until we can make no more reductions.
  741. K * I think this works -- that is it does as good a job as I would.
  742. K */
  743. Kunsigned int
  744. KUnCurly(n, ppcWhole)
  745. Kunsigned int n;
  746. Kchar **ppcWhole;
  747. K{
  748. K    register unsigned int m, i;
  749. K    register char **ppcList;
  750. K    auto unsigned int uInside, uLen, uEnd, uSquish;
  751. K    auto char *pcLeft, *pcRight;
  752. K    auto char *pcTemp, *pcSep;
  753. K
  754. K    ppcList = ppcWhole;
  755. K    m = n;
  756. K    while (m > 0) {
  757. K        uInside = Split(m, ppcList, & pcLeft, & pcRight);
  758. K        switch (uInside) {
  759. K        case 0:
  760. K        case 1:
  761. K            /* skip boring files for next pass
  762. K             */
  763. K            --m;
  764. K            ++ppcList;
  765. K            break;
  766. K        default:
  767. K            /* Left "{" List[0] "," List[uInside-1] "}" Right
  768. K             */
  769. K            n -= m;
  770. K            uSquish = UnCurly(uInside, ppcList);
  771. K            uLen = 2;    /* close curly and "\000" */
  772. K            if ((char *)0 != pcLeft) {
  773. K                uLen += strlen(pcLeft);
  774. K            }
  775. K            for (i = 0; i < uSquish; ++i) {
  776. K                uLen += 1 + strlen(ppcList[i]);
  777. K            }
  778. K            if ((char *)0 != pcRight) {
  779. K                uLen += strlen(pcRight);
  780. K            }
  781. K            pcTemp = malloc(uLen);
  782. K            if ((char *)0 == pcTemp) {
  783. K                fprintf(stderr, acNoMem, progname);
  784. K                exit(1);
  785. K            }
  786. K
  787. K            pcTemp[0] = '\000';
  788. K            if ((char *)0 != pcLeft) {
  789. K                (void) strcat(pcTemp, pcLeft);
  790. K                free(pcLeft);
  791. K            }
  792. K            if (1 == uSquish) {
  793. K                mcat(pcTemp, ppcList[0]);
  794. K            } else {
  795. K                pcSep = "{";
  796. K                for (i = 0; i < uSquish; ++i) {
  797. K                    register char *pcMatch;
  798. K
  799. K                    strcat(pcTemp, pcSep);
  800. K
  801. K                    mcat(pcTemp, ppcList[i]);
  802. K                    pcSep = ",";
  803. K                }
  804. K                strcat(pcTemp, "}");
  805. K            }
  806. K            if ((char *)0 != pcRight) {
  807. K                (void) strcat(pcTemp, pcRight);
  808. K                free(pcRight);
  809. K            }
  810. K
  811. K            uEnd = UnCurly(m-uInside, ppcList+uInside);
  812. K            n += 1 + uEnd;
  813. K            ppcList[0] = pcTemp;
  814. K            for (i = 0 ; i < uEnd; /* update below */) {
  815. K                ppcList[++i] = ppcList[uInside++];
  816. K            }
  817. K            ppcList = ppcWhole;
  818. K            m = n;
  819. K            break;
  820. K        }
  821. K    }
  822. K    return n;
  823. K}
  824. K
  825. K/*
  826. K * do the opposite of csh(1) {...}                    (ksb)
  827. K * we cannot process files with a comma in them, but as a special
  828. K * case we will remove ",EXT" from the end of a list of files...
  829. K * and process those if it is the only comma in each of the files.
  830. K *  1) output UnCulry of files with no commas
  831. K *  2) output UnCulry of files with `,EXT' (only) on the end
  832. K *  3) output files with random commas in them (bletch)
  833. K *  4) loop until all files have been done
  834. K */
  835. Kint
  836. Kmain(argc, argv)
  837. Kunsigned int argc;
  838. Kchar **argv;
  839. K{
  840. K    register unsigned int i, uReplace, uCommon;
  841. K    register char *pcExt;
  842. K
  843. K    progname = *argv++;
  844. K    --argc;
  845. K
  846. K#if defined(READSTDIN)
  847. K    if (argc == 0) {
  848. K        argc = GetFiles(& argv);
  849. K    }
  850. K#endif
  851. K    while (0 < argc) {
  852. K        for (uCommon = 0; uCommon < argc; ++uCommon) {
  853. K            if ((char *)0 != strrchr(argv[uCommon], ',')) {
  854. K                break;
  855. K            }
  856. K        }
  857. K        if (0 != uCommon) {
  858. K            uReplace = UnCurly(uCommon, argv);
  859. K            argc -= uCommon;
  860. K            for (i = 0; i < uReplace; ++i) {
  861. K                puts(argv[i]);
  862. K            }
  863. K            argv += uCommon;
  864. K        }
  865. K        do {
  866. K            pcExt = (char *)0;
  867. K            for (uCommon = 0; uCommon < argc; ++uCommon) {
  868. K                register char *pcComma;
  869. K                if ((char *)0 == (pcComma = strrchr(argv[uCommon], ','))) {
  870. K                    break;
  871. K                }
  872. K                if ((char *)0 == pcExt) {
  873. K                    *pcComma ='\000';
  874. K                    pcExt = pcComma+1;
  875. K                } else if (0 != strcmp(pcExt, pcComma+1)) {
  876. K                    break;
  877. K                } else {
  878. K                    *pcComma = '\000';
  879. K                }
  880. K                if ((char *)0 != strrchr(argv[uCommon], ',')) {
  881. K                    *pcComma = ',';
  882. K                    break;
  883. K                }
  884. K            }
  885. K            if (0 != uCommon) {
  886. K                uReplace = UnCurly(uCommon, argv);
  887. K                argc -= uCommon;
  888. K                for (i = 0; i < uReplace; ++i) {
  889. K                    fputs(argv[i], stdout);
  890. K                    putchar(',');
  891. K                    puts(pcExt);
  892. K                }
  893. K                argv += uCommon;
  894. K            }
  895. K            if ((char *)0 != strrchr(argv[0], ',')) {
  896. K                puts(argv[0]);
  897. K                argc -= 1;
  898. K                argv += 1;
  899. K                uCommon = 1;
  900. K            }
  901. K        } while (0 != uCommon);
  902. K    }
  903. K    exit(0);
  904. K}
  905. SHAR_EOF
  906. #    End of shell archive
  907. exit 0
  908.