home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 October / usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso / unix / volume16 / uniqbib < prev    next >
Text File  |  1988-11-01  |  25KB  |  951 lines

  1. Subject:  v16i052:  Remove duplicates from a "bib" database
  2. Newsgroups: comp.sources.unix
  3. Sender: sources
  4. Approved: rsalz@uunet.UU.NET
  5.  
  6. Submitted-by: Brain in Neutral <bin@rhesus.primate.wisc.edu>
  7. Posting-number: Volume 16, Issue 52
  8. Archive-name: uniqbib
  9.  
  10. Uniqbib is used in conjunction with the suite of programs comprising the
  11. refer system.  One problem with this system is that there is no provision
  12. for removal of duplicates from bibliographic databases.  (Duplicates may
  13. occur, for instance, if the output from two or more lookbib queries is
  14. combined, where entries satisfy the keywords of more than one search.)
  15. Uniqbib is used to remove such duplicates.  It reads its input and writes
  16. all the unique entries therein to the standard output.  The entries in the
  17. input do not have to be sorted (i.e., you do not have to run sortbib
  18. first), nor do the entries produced by uniqbib come out in any particular
  19. order.
  20.  
  21. #!/bin/sh
  22. # shar:    Shell Archiver
  23. #    Run the following text with /bin/sh to create:
  24. #    uniqbib.c
  25. #    bibinfo.h
  26. #    BibAlloc.c
  27. #    BibCmp.c
  28. #    BibRead.c
  29. #    BibWrite.c
  30. #    panic.c
  31. #    uniqbib.1
  32. #    Makefile
  33. #    bad1
  34. #    bad2
  35. #    bad3
  36. echo shar: extracting uniqbib.c '(7434 characters)'
  37. sed 's/^XX//' << \SHAR_EOF > uniqbib.c
  38. XX/*
  39. XX    uniqbib - uniq a bibliographic database to eliminate duplicates.
  40. XX
  41. XX    syntax: uniqbib [ -v ] [ file ]
  42. XX
  43. XX    -v means verbose: prints feedback.
  44. XX
  45. XX    Only one file may be explicitly named (use cat f1 f2 ... | uniqbib
  46. XX    for > 1 file).
  47. XX
  48. XX    Strategy:
  49. XX    Read input file, writing file position and summary information
  50. XX    for each entry.  Sort on summary info.  For each set of entries
  51. XX    having identical summary information, compare directly, and write
  52. XX    out those that are different.  (For those entries that have unique
  53. XX    summary info, the entry is unique, of course.)
  54. XX
  55. XX    The summary information is trivial:  the sum of the characters in
  56. XX    the text of the entry.  This is invariant with respect to order of
  57. XX    fields (records that are identical except for order of fields get
  58. XX    identical checksums this way).
  59. XX
  60. XX    7 April 1988    Paul DuBois    dubois@rhesus.primate.wisc.edu
  61. XX
  62. XX    Change History:
  63. XX
  64. XX    08/10/88 Fixed failure to check for citations that are too long.
  65. XX        Added -v flag.  Put better error messages in BERead
  66. XX        (prints text of citation seen so far, input line number
  67. XX        where error occurred).  Removed inability to uniq from
  68. XX        a pipe or redirected input (citations are copied to
  69. XX        holding file during reading phase 1).
  70. XX*/
  71. XX
  72. XX# include    <stdio.h>
  73. XX# include    <signal.h>
  74. XX# include    "bibinfo.h"
  75. XX
  76. XX
  77. XXint    count = 0;
  78. XXint    verbose = 0;
  79. XX
  80. XXint    cleanup ();
  81. XXint    onintr ();
  82. XX
  83. XXchar    keyFile[BUFSIZ];
  84. XXchar    holdFile[BUFSIZ];
  85. XX
  86. XX
  87. XXmain (argc, argv)
  88. XXint    argc;
  89. XXchar    **argv;
  90. XX{
  91. XXBibEnt    *be;
  92. XXBibFld    *bf;
  93. XXFILE    *kf, *hf;
  94. XXchar    buf[BUFSIZ];
  95. XXchar    *cp;
  96. XXlong    sum;
  97. XXint    len;
  98. XXint    result;
  99. XXint    empty = 1;
  100. XXint    useHold = 0;
  101. XXlong    holdPos = 0;
  102. XX
  103. XX/*
  104. XX    Arrange for cleanup of temp files in
  105. XX    the event of abnormal termination.
  106. XX*/
  107. XX
  108. XX    setpanichook (cleanup);
  109. XX    signal (SIGINT, onintr);
  110. XX    signal (SIGHUP, onintr);
  111. XX    signal (SIGQUIT, onintr);
  112. XX    signal (SIGTERM, onintr);
  113. XX
  114. XX/*
  115. XX    Process arguments and set up files.  If a file is named, use
  116. XX    that for input, else read whatever's on stdin and arrange to
  117. XX    hold a copy in a seekable temp file.
  118. XX*/
  119. XX
  120. XX    --argc;
  121. XX    ++argv;
  122. XX    if (argc > 0 && strcmp (*argv, "-v") == 0)
  123. XX    {
  124. XX        ++verbose;
  125. XX        --argc;
  126. XX        ++argv;
  127. XX    }
  128. XX    if (argc == 0)        /* reading from pipe or redirection */
  129. XX    {
  130. XX        sprintf (holdFile, "/tmp/ubh%05d", getpid ());
  131. XX        if ((hf = fopen (holdFile, "w")) == NULL)
  132. XX            panic ("Can't open temporary hold file");
  133. XX        ++useHold;
  134. XX    }
  135. XX    else if (argc == 1)
  136. XX    {
  137. XX        if (freopen (argv[0], "r", stdin) == NULL)
  138. XX            panic ("Can't open: %s", argv[0]);
  139. XX    }
  140. XX    else
  141. XX        panic ("Usage: uniqbib [-v] file");
  142. XX
  143. XX    sprintf (keyFile, "/tmp/ub%05d", getpid ());
  144. XX    if ((kf = fopen (keyFile, "w")) == NULL)
  145. XX        panic ("Can't open temporary key file.");
  146. XX
  147. XX/*
  148. XX    Ready to make first pass through input.  Read each citation,
  149. XX    compute the key and write out the key and file position of the
  150. XX    citation to the key file.  If reading a pipe or redirected input
  151. XX    write out the citation to a holding file so can re-read it.  In
  152. XX    that case, the file position must be fixed, since the position
  153. XX    in the holding file may well be different (e.g., if there are
  154. XX    multiple blank lines between citations in the original input).
  155. XX*/
  156. XX
  157. XX    if ((be = BEAlloc ()) == NULL)
  158. XX        panic ("Out of memory.");
  159. XX    if (verbose)
  160. XX        fprintf (stderr, "Reading citations\n");
  161. XX    while ((result = BERead (stdin, be)) > 0)
  162. XX    {
  163. XX        ++count;
  164. XX        if (verbose)
  165. XX            fprintf (stderr, ".");
  166. XX        empty = 0;
  167. XX        sum = 0;
  168. XX        cp = BEText (be);
  169. XX        len = BELen (be);
  170. XX        if (useHold)
  171. XX        {
  172. XX            BEWrite (hf, be);
  173. XX            BEFilPos (be) = holdPos;
  174. XX            holdPos += len + 1;    /* +1 for preceding newline */
  175. XX        }
  176. XX        while (len-- > 0)
  177. XX            sum += *cp++;
  178. XX        fprintf (kf, "%D %D\n", sum, BEFilPos (be));
  179. XX    }
  180. XX    fclose (kf);
  181. XX    if (useHold)    /* if using hold file, close and attach to stdin */
  182. XX    {
  183. XX        fclose (hf);
  184. XX        if (freopen (holdFile, "r", stdin) == NULL)
  185. XX            panic ("Can't reopen hold file.");
  186. XX    }
  187. XX    if (result < 0 || empty)
  188. XX    {
  189. XX        cleanup ();
  190. XX        exit (0);
  191. XX    }
  192. XX    if (verbose)
  193. XX        fprintf (stderr, "\nPass 1 done (%d citations)\n", count);
  194. XX
  195. XX/*
  196. XX    Pass two.  Sort the keys so duplicates will cluster, and uniq
  197. XX    them.
  198. XX*/
  199. XX
  200. XX    if (verbose)
  201. XX        fprintf (stderr, "Sorting keys\n");
  202. XX    sprintf (buf, "exec sort -o %s %s", keyFile, keyFile);
  203. XX    system (buf);
  204. XX    if (verbose)
  205. XX        fprintf (stderr, "Sort done\n");
  206. XX    if ((kf = fopen (keyFile, "r")) == NULL)
  207. XX        panic ("Can't reopen key file.");
  208. XX    UniqBib (kf);
  209. XX    fclose (kf);
  210. XX    if (verbose)
  211. XX        fprintf (stderr, "\nDone\n");
  212. XX    cleanup ();
  213. XX    exit (0);
  214. XX}
  215. XX
  216. XX
  217. XX
  218. XX/*
  219. XX    The ugly heart of the program.
  220. XX
  221. XX    Read checksum/file-position pairs from f.  It's sorted on checksum,
  222. XX    so that all groups of identical checksums will cluster.  The
  223. XX    bib entries within each cluster may or *may not* be content-identical,
  224. XX    so the algorithm hangs onto each entry until it either knows it's
  225. XX    unique or that it's a dup, as follows:
  226. XX
  227. XX    First read one line and hold onto it for a reference.  Then read rest
  228. XX    of lines.  If checksum is different, flush the reference and restart
  229. XX    with the next line after the reference as the new reference.  If the
  230. XX    checksum is the same, then do a direct compare of the bib entries
  231. XX    themselves.  If they're the same, skip the reference and restart with
  232. XX    the next line after the reference as the new reference.  If they are
  233. XX    different, just keep reading.  (Eventually one will be found that's
  234. XX    either a different checksum or identical, or EOF will be reached, so
  235. XX    the reference can be either flushed or skipped.)
  236. XX
  237. XX    When restarting so that the reference is bounced to the next
  238. XX    line in the summary file, check first whether the comparison
  239. XX    is that line.  If so, don't bother to reread that line or to
  240. XX    fetch the bibliographic entry itself.  Since except in perverse
  241. XX    cases the comparison almost always becomes the next reference, this
  242. XX    is a big win.
  243. XX
  244. XX*/
  245. XX
  246. XXUniqBib (f)
  247. XXFILE    *f;
  248. XX{
  249. XXlong    refPos, comPos;        /* reference and comparison seek positions */
  250. XXlong    refCkSum, comCkSum;    /* reference and comparison checksums */
  251. XXint    getNextRef = 1;        /* non-zero if need to read ref sum, pos */
  252. XXlong    refOff = 0, comOff;    /* offset of line after reference, comparison */
  253. XXint    nCompares = 0;        /* number of comparisons made with ref */
  254. XXBibEnt    b1, *beRef = &b1;
  255. XXBibEnt    b2, *beCom = &b2;
  256. XXBibEnt    *beTmp;
  257. XXint    nondups = 0;
  258. XX
  259. XX    if (verbose)
  260. XX        fprintf (stderr, "Comparing keys\n");
  261. XX    for (;;)
  262. XX    {
  263. XX        if (verbose)
  264. XX            fprintf (stderr, ".");
  265. XX        if (getNextRef)
  266. XX        {
  267. XX            getNextRef = 0;
  268. XX            if (nCompares == 1)    /* make comparison next ref */
  269. XX            {
  270. XX                refCkSum = comCkSum;
  271. XX                refPos = comPos;
  272. XX                refOff = comOff;
  273. XX                beTmp = beRef;
  274. XX                beRef = beCom;
  275. XX                beCom = beTmp;
  276. XX            }
  277. XX            else    /* seek to correct spot, read summary */
  278. XX            {    /* info and entry from bib file */
  279. XX                fseek (f, refOff, 0);
  280. XX                if (fscanf (f, "%D %D\n", &refCkSum, &refPos)
  281. XX                        != 2)
  282. XX                    break;    /* no more refs in file */
  283. XX                refOff = ftell (f);
  284. XX                fseek (stdin, refPos, 0);
  285. XX                if (!BERead (stdin, beRef))
  286. XX                    panic ("Can't read reference entry.");
  287. XX            }
  288. XX            nCompares = 0;
  289. XX        }
  290. XX        if (fscanf (f, "%D %D\n", &comCkSum, &comPos) != 2)
  291. XX        {
  292. XX            BEWrite (stdout, beRef);    /* flush reference */
  293. XX            ++nondups;
  294. XX            ++getNextRef;
  295. XX            continue;
  296. XX        }
  297. XX        comOff = ftell (f);
  298. XX        fseek (stdin, comPos, 0);
  299. XX        if (!BERead (stdin, beCom))
  300. XX            panic ("Can't read comparison entry.");
  301. XX        ++nCompares;
  302. XX        if (refCkSum != comCkSum)        /* different - flush */
  303. XX        {
  304. XX            BEWrite (stdout, beRef);    /* current reference */
  305. XX            ++nondups;
  306. XX        }
  307. XX        else if (BECmp (beRef, beCom))        /* compare directly, */
  308. XX            continue;            /* skip ref if diff */
  309. XX        ++getNextRef;
  310. XX    }
  311. XX    fprintf (stderr, "%d citations (%d + %d duplicates)\n",
  312. XX            count, nondups, count-nondups);
  313. XX}
  314. XX
  315. XX
  316. XXcleanup ()
  317. XX{
  318. XX    (void) unlink (keyFile);
  319. XX    (void) unlink (holdFile);
  320. XX}
  321. XX
  322. XXonintr () { panic ("\nInterrupted..."); }
  323. SHAR_EOF
  324. if test 7434 -ne "`wc -c uniqbib.c`"
  325. then
  326. echo shar: error transmitting uniqbib.c '(should have been 7434 characters)'
  327. fi
  328. echo shar: extracting bibinfo.h '(2596 characters)'
  329. sed 's/^XX//' << \SHAR_EOF > bibinfo.h
  330. XX/*
  331. XX    bibinfo.h - constants, types and variables to support operations
  332. XX    on bibliography entries.
  333. XX
  334. XX    The BibEnt type is the basic unit of bibliographic information.
  335. XX    It contains a buffer holding the text of the entry (unmodified),
  336. XX    and structures which are set up when the entry is read in from
  337. XX    a file.  These structures allow easy access to the various fields
  338. XX    of the entry.  They also allow quick checks as to whether a field
  339. XX    is present in the entry, how many authors there are, and so forth.
  340. XX
  341. XX    Macros and functions are used to access parts of records.  This
  342. XX    allows the underlying implementation to change while preserving
  343. XX    the programming interface.  Currently the implementation is
  344. XX    as follows:
  345. XX
  346. XX    Lines of a bibliography entry are read one after the other
  347. XX    into a buffer.  As each is read, the lines are categorized and
  348. XX    information set up to allow each field to be accessed easily.
  349. XX    For each field (except authors), one BibFld struct is set up.
  350. XX    bfStart is the offset into the text buffer of the beginning of the
  351. XX    field (starts at the % itself), and bfLen is the length of the
  352. XX    field (includes length of all continuation lines).  Authors are
  353. XX    anomalous, since there may be many of them; BibFld's for these are
  354. XX    stored at the end of the regular field array, and there is a count
  355. XX    variable.
  356. XX*/
  357. XX
  358. XX# ifndef    NULL
  359. XX# define    NULL    (0)
  360. XX# endif
  361. XX
  362. XX
  363. XX# define    beMaxBuf    1024    /* max size of entry's text */
  364. XX# define    beMaxAuth    20    /* max authors allowed */
  365. XX
  366. XX
  367. XXtypedef struct
  368. XX{
  369. XX    int    bfStart;    /* index of start of bib field text */
  370. XX    int    bfLen;        /* length of bib field text */
  371. XX} BibFld;
  372. XX
  373. XX
  374. XXtypedef struct
  375. XX{
  376. XX    int    beTextLen;        /* length of entry text */
  377. XX    char    beText[beMaxBuf];    /* entry text */
  378. XX    BibFld    beFld[26+beMaxAuth];    /* field information */
  379. XX    int    beAuthCnt;        /* number of authors */
  380. XX    long    beFilPos;        /* starting position in file */
  381. XX} BibEnt;
  382. XX
  383. XX
  384. XX/*
  385. XX    Macros for accessing various pieces of the bibliographic
  386. XX    entry.  Most of these can be used as lvalues.  Arguments
  387. XX    should be as follows:
  388. XX
  389. XX    BibEnt    *be
  390. XX    BibFld    *bf
  391. XX    char    f    ('B'..'Z')
  392. XX    int    n    (0..BEAuthCnt(be)-1)
  393. XX
  394. XX*/
  395. XX
  396. XX# define    BEText(be)        (be->beText)
  397. XX# define    BELen(be)        (be->beTextLen)
  398. XX# define    BEFilPos(be)        (be->beFilPos)
  399. XX
  400. XX# define    BEHaveFld(be,f)        (BEFldLen(be,f) != 0)
  401. XX# define    BEFldPtr(be,f)        (&(be->beFld[(f)-'A']))
  402. XX
  403. XX# define    BEFldStart(bf)        (bf->bfStart)
  404. XX# define    BEFldLen(bf)        (bf->bfLen)
  405. XX# define    BEFldText(be,bf)    (&(BEText(be)[BEFldStart(bf)]))
  406. XX
  407. XX# define    BEAuthPtr(be,n)        (BEFldPtr(be,'Z'+n+1))
  408. XX# define    BEAuthLen(be,n)        (BEFldLen(be,'Z'+n+1))
  409. XX# define    BEAuthText(be,n)    (BEFldText(be,'Z'+n+1))
  410. XX
  411. XX# define    BEAuthCnt(be)        (be->beAuthCnt)
  412. XX
  413. XX
  414. XXBibEnt *BEAlloc ();
  415. SHAR_EOF
  416. if test 2596 -ne "`wc -c bibinfo.h`"
  417. then
  418. echo shar: error transmitting bibinfo.h '(should have been 2596 characters)'
  419. fi
  420. echo shar: extracting BibAlloc.c '(205 characters)'
  421. sed 's/^XX//' << \SHAR_EOF > BibAlloc.c
  422. XX/*
  423. XX    BibEnt structure allocation and disposal
  424. XX*/
  425. XX
  426. XX# include    "bibinfo.h"
  427. XX
  428. XXBibEnt *BEAlloc ()
  429. XX{
  430. XXchar    *calloc ();
  431. XX
  432. XX    return ((BibEnt *) calloc (1, sizeof (BibEnt)));
  433. XX}
  434. XX
  435. XX
  436. XXBEFree (bp)
  437. XXBibEnt    *bp;
  438. XX{
  439. XX    free (bp);
  440. XX}
  441. SHAR_EOF
  442. if test 205 -ne "`wc -c BibAlloc.c`"
  443. then
  444. echo shar: error transmitting BibAlloc.c '(should have been 205 characters)'
  445. fi
  446. echo shar: extracting BibCmp.c '(1623 characters)'
  447. sed 's/^XX//' << \SHAR_EOF > BibCmp.c
  448. XX/*
  449. XX    Compare two bibliographic entries for equality.  They do
  450. XX    not need to have the fields in the same order (except that
  451. XX    authors must be in the same order), just the same fields
  452. XX    present, and the same information in corresponding fields.
  453. XX
  454. XX    Returns:
  455. XX
  456. XX    0    b1 = b2
  457. XX    !0    b1 != b2
  458. XX
  459. XX    This should be extended to allow comparison to return ordering
  460. XX    information by passing in sort ordering information.
  461. XX
  462. XX    First compare the number of authors.
  463. XX    If equal, compare the authors by length and text.
  464. XX    If equal, see if the same fields are present and have the same length
  465. XX    and the same text.
  466. XX    If equal, they're equal.
  467. XX    Otherwise, not.
  468. XX*/
  469. XX
  470. XX# include    <stdio.h>
  471. XX# include    "bibinfo.h"
  472. XX
  473. XX# undef    DEBUG
  474. XX# ifdef        DEBUG
  475. XX# define    RETURN(x,y)    return (printf ("%s\n", x) ? y : y)
  476. XX# else
  477. XX# define    RETURN(x,y)    return (y)
  478. XX# endif
  479. XX
  480. XXBECmp (b1, b2)
  481. XXBibEnt    *b1, *b2;
  482. XX{
  483. XXint    i, len, n;
  484. XXBibFld    *bfp1, *bfp2;
  485. XX
  486. XX    if ((n = BEAuthCnt (b1)) != BEAuthCnt (b2))
  487. XX        RETURN ("author count", 1);
  488. XX    for (i = 0; i < n; i++)
  489. XX    {
  490. XX        bfp1 = BEAuthPtr (b1, i);
  491. XX        bfp2 = BEAuthPtr (b2, i);
  492. XX        if ((len = BEFldLen (bfp1)) != BEFldLen (bfp2))
  493. XX            RETURN ("author length", 1);
  494. XX        if (strncmp (BEFldText (b1, bfp1), BEFldText (b2, bfp2), len))
  495. XX            RETURN ("author text", 1);
  496. XX    }
  497. XX    for (i = 'B'; i <= 'Z'; i++)
  498. XX    {
  499. XX/*
  500. XX    Don't have to check whether fields are present or not, since
  501. XX    missing ones have length zero and will compare properly.
  502. XX*/
  503. XX        bfp1 = BEFldPtr (b1, i);
  504. XX        bfp2 = BEFldPtr (b2, i);
  505. XX        if ((len = BEFldLen (bfp1)) != BEFldLen (bfp2))
  506. XX            RETURN ("field length", 1);
  507. XX        if (strncmp (BEFldText (b1, bfp1), BEFldText (b2, bfp2), len))
  508. XX            RETURN ("field text", 1);
  509. XX    }
  510. XX    RETURN ("same", 0);
  511. XX}
  512. SHAR_EOF
  513. if test 1623 -ne "`wc -c BibCmp.c`"
  514. then
  515. echo shar: error transmitting BibCmp.c '(should have been 1623 characters)'
  516. fi
  517. echo shar: extracting BibRead.c '(2701 characters)'
  518. sed 's/^XX//' << \SHAR_EOF > BibRead.c
  519. XX/*
  520. XX    Read a bibliographic entry from a file.
  521. XX
  522. XX    Read until have a non-blank line, then read until a blank line or
  523. XX    EOF.  Return number of lines in citation.  (Zero means EOF, -1 error).
  524. XX
  525. XX    Error are returned for various conditions:
  526. XX
  527. XX    text of citation too long
  528. XX    more than beMaxAuth authors
  529. XX    first line does not begin with %
  530. XX    % lines with non-capital letter following %
  531. XX*/
  532. XX
  533. XX# include    <stdio.h>
  534. XX# include    <ctype.h>
  535. XX# include    <varargs.h>
  536. XX# include    "bibinfo.h"
  537. XX
  538. XX
  539. XXstatic    long    line = 0;
  540. XXstatic    long    citation = 0;
  541. XX
  542. XX
  543. XXBERead (f, bi)
  544. XXFILE    *f;
  545. XXBibEnt    *bi;
  546. XX{
  547. XXBibFld    *bf = NULL;
  548. XXchar    *bp = BEText (bi);
  549. XXint    left = sizeof (BEText (bi));
  550. XXint    read = 1;
  551. XXint    nLines = 0;
  552. XXint    i, len;
  553. XXint    c;
  554. XX
  555. XX/*
  556. XX    Initialize entry to "nothing"
  557. XX*/
  558. XX
  559. XX    BELen (bi) = 0;            /* no text */
  560. XX    BEAuthCnt (bi) = 0;            /* no authors */
  561. XX    for (i = 'A'; i <= 'Z'; i++)        /* no other fields, either */
  562. XX        BEFldLen (BEFldPtr (bi, i)) = 0;
  563. XX
  564. XX    ++citation;
  565. XX
  566. XX    while (read)
  567. XX    {
  568. XX        ++line;
  569. XX        if ((c = getc (f)) == EOF)
  570. XX            break;
  571. XX        if (c == '\n')            /* blank line */
  572. XX        {
  573. XX            read = (nLines == 0);    /* if haven't seen ref, keep */
  574. XX            continue;        /* reading, else skip line */
  575. XX        }
  576. XX        if (++nLines == 1)        /* set pos on first line */
  577. XX        {
  578. XX            BEFilPos (bi) = ftell (f) - 1;
  579. XX            if (c != '%')        /* make sure not cont. line */
  580. XX            {
  581. XX                BRError (bi, "First line in citation does not begin with '%%':\n%s\n", bp);
  582. XX                return (-1);
  583. XX            }
  584. XX        }
  585. XX        for (len = 0; left-- > 0; )    /* read line into buffer */
  586. XX        {
  587. XX            bp[len++] = c;
  588. XX            if (c == '\n')        /* have full line now */
  589. XX                break;
  590. XX            if ((c = getc (f)) == EOF)
  591. XX            {
  592. XX                read = 0;    /* done reading */
  593. XX                break;
  594. XX            }
  595. XX        }
  596. XX        BELen (bi) += len;
  597. XX        if (left < 0)
  598. XX        {
  599. XX            BRError (bi, "Citation too long, exceeds %d characters\n", sizeof (BEText (bi)));
  600. XX            return (-1);
  601. XX        }
  602. XX        if (*bp == '%')            /* start of new field */
  603. XX        {
  604. XX            if (!isupper (bp[1]))
  605. XX            {
  606. XX                BRError (bi, "Bad key line\n");
  607. XX                return (-1);
  608. XX            }
  609. XX            /*bp += 3;    /* skip to beginning of text */
  610. XX            /*len -= 3;    /* adjust length of rest of line */
  611. XX            if (bp[1] != 'A')
  612. XX                bf = BEFldPtr (bi, bp[1]);
  613. XX            else if (BEAuthCnt (bi) >= beMaxAuth)
  614. XX            {
  615. XX                BRError (bi, "Too many authors, only %d allowed.\n", beMaxAuth);
  616. XX                return (-1);
  617. XX            }
  618. XX            else
  619. XX                bf = BEAuthPtr (bi, BEAuthCnt (bi)++);
  620. XX
  621. XX            BEFldStart (bf) = bp - BEText (bi);
  622. XX            BEFldLen (bf) = 0;
  623. XX        }
  624. XX        BEFldLen (bf) += len;
  625. XX        bp += len;
  626. XX    }
  627. XX
  628. XX    return (nLines);
  629. XX}
  630. XX
  631. XX
  632. XX
  633. XXstatic BRError (va_alist)
  634. XXva_dcl
  635. XX{
  636. XXva_list    args;
  637. XXBibEnt    *bi;
  638. XXchar    *fmt;
  639. XX
  640. XX    va_start (args);
  641. XX    bi = va_arg (args, BibEnt *);
  642. XX    fmt = va_arg (args, char *);
  643. XX    fprintf (stderr, "Error at line %D (in citation %D)\n", line, citation);
  644. XX    vfprintf (stderr, fmt, args);
  645. XX    if (BELen (bi) > 0)
  646. XX    {
  647. XX        fprintf (stderr, "Text of entry up to error:\n");
  648. XX        BEWrite (stderr, bi);
  649. XX    }
  650. XX    va_end (args);
  651. XX}
  652. SHAR_EOF
  653. if test 2701 -ne "`wc -c BibRead.c`"
  654. then
  655. echo shar: error transmitting BibRead.c '(should have been 2701 characters)'
  656. fi
  657. echo shar: extracting BibWrite.c '(265 characters)'
  658. sed 's/^XX//' << \SHAR_EOF > BibWrite.c
  659. XX/*
  660. XX    Write a bibliographic entry to a file.  This is simple; just
  661. XX    write a newline followed by the text of the entry.
  662. XX*/
  663. XX
  664. XX# include    <stdio.h>
  665. XX# include    "bibinfo.h"
  666. XX
  667. XX
  668. XXBEWrite (f, be)
  669. XXFILE    *f;
  670. XXBibEnt    *be;
  671. XX{
  672. XX    fputc ('\n', f);
  673. XX    fwrite (BEText (be), 1, BELen (be), f);
  674. XX}
  675. SHAR_EOF
  676. if test 265 -ne "`wc -c BibWrite.c`"
  677. then
  678. echo shar: error transmitting BibWrite.c '(should have been 265 characters)'
  679. fi
  680. echo shar: extracting panic.c '(670 characters)'
  681. sed 's/^XX//' << \SHAR_EOF > panic.c
  682. XX/*
  683. XX    panic - print message and die with status 1.  Uses vprintf
  684. XX    so that panic can take variable argument lists.
  685. XX
  686. XX    setpanichook - install function to be called with no arguments
  687. XX    after printing panic message and before exiting.  Can be used
  688. XX    for additional cleanup, like removing temp files, etc.
  689. XX*/
  690. XX
  691. XX# include    <stdio.h>
  692. XX# include    <varargs.h>
  693. XX
  694. XX
  695. XXstatic    int    (*panichook) () = NULL;
  696. XX
  697. XXvoid setpanichook (p)
  698. XXint    (*p) ();
  699. XX{
  700. XX    panichook = p;
  701. XX}
  702. XX
  703. XX
  704. XXvoid
  705. XXpanic (va_alist)
  706. XXva_dcl
  707. XX{
  708. XXva_list    args;
  709. XXchar    *fmt;
  710. XX
  711. XX    va_start (args);
  712. XX    fmt = va_arg (args, char *);
  713. XX    vfprintf (stderr, fmt, args);
  714. XX    va_end (args);
  715. XX    fprintf (stderr, "\n");
  716. XX    if (panichook != NULL)
  717. XX        (*panichook) ();
  718. XX    exit (1);
  719. XX}
  720. SHAR_EOF
  721. if test 670 -ne "`wc -c panic.c`"
  722. then
  723. echo shar: error transmitting panic.c '(should have been 670 characters)'
  724. fi
  725. echo shar: extracting uniqbib.1 '(2518 characters)'
  726. sed 's/^XX//' << \SHAR_EOF > uniqbib.1
  727. XX.\" xroff -man.new % | lpr
  728. XX.TH uniqbib 1
  729. XX.UC 4
  730. XX.SH NAME
  731. XXuniqbib \- remove duplicates from bibliographic database
  732. XX.SH SYNTAX
  733. XX.B uniqbib
  734. XX[
  735. XX.B \-v
  736. XX] [
  737. XX.I file
  738. XX]
  739. XX.SH DESCRIPTION
  740. XX.I Uniqbib
  741. XXis used in conjunction with the suite of programs comprising the
  742. XX.I refer
  743. XXsystem.
  744. XXOne problem with this system is that there is no provision for removal
  745. XXof duplicates from bibliographic databases.
  746. XX(Duplicates may occur, for instance, if the output from two or more
  747. XX.I lookbib
  748. XXqueries is combined, where entries
  749. XXsatisfy the keywords of more than one search.)
  750. XX.I Uniqbib
  751. XXis used to remove such duplicates.
  752. XXIt reads its input and writes all the unique entries therein to the
  753. XXstandard output.
  754. XXThe entries in the input do not have to be sorted (i.e., you do not have to
  755. XXrun
  756. XX.I sortbib
  757. XXfirst), nor do the entries produced by
  758. XX.I uniqbib
  759. XXcome out in any particular order.
  760. XX.PP
  761. XXIf a file is named,
  762. XX.I uniqbib
  763. XXreads it; otherwise it reads its standard input.
  764. XXThis means that
  765. XX.I uniqbib
  766. XXcan be used in pipelines.
  767. XXIf more than one file is to be processed, use
  768. XX.PP
  769. XX.nf
  770. XX.in +.5i
  771. XXcat input1 input2 ... input\f2n\f1 | uniqbib > output
  772. XX.in -.5i
  773. XX.fi
  774. XX.PP
  775. XXEntries are considered duplicates if
  776. XX(i) they contain the same authors, in the same order; and (ii)
  777. XXthey contain the same non-author fields, and the content of corresponding
  778. XXfields is identical.
  779. XX.PP
  780. XXFields (except for authors) do
  781. XX.I not
  782. XXhave to appear in the same order for two entries to be considered identical.
  783. XXFor example, the following two entries are the same as far as
  784. XX.I uniqbib
  785. XXis concerned, because within the entries, the authors appear in the same
  786. XXorder, and the other fields are identical with respect to content, even
  787. XXthough they appear in different orders.
  788. XX.PP
  789. XX.nf
  790. XX.in +.5i
  791. XX.ta 2.5i
  792. XX%A First Author        %T My title
  793. XX%T My title        %J Some journal
  794. XX%A Second Author    %A First Author
  795. XX%D 1988    %A Second Author
  796. XX%N 4    %D 1988
  797. XX%J Some journal    %V 198
  798. XX%V 198    %N 4
  799. XX.in -.5i
  800. XX.fi
  801. XX.PP
  802. XXThe following entries are
  803. XX.I not
  804. XXidentical:
  805. XX.PP
  806. XX.nf
  807. XX.in +.5i
  808. XX.ta 2.5i
  809. XX%A First Author        %A Second author
  810. XX%A Second Author    %A First Author
  811. XX%T My title        %T My title
  812. XX%J Some journal    %J Some journal
  813. XX%V 198    %V 198
  814. XX%N 4    %N 4
  815. XX%D 1988    %D 1988
  816. XX.in -.5i
  817. XX.fi
  818. XX.PP
  819. XXIf the
  820. XX.B \-v
  821. XXflag is given,
  822. XX.I uniqbib
  823. XXgoes into verbose mode;
  824. XXit announces the beginning and end of each phase of analysis, and prints
  825. XXa ``.'' as each citation is read during the initial and comparison phases.
  826. XX.SH "SEE ALSO"
  827. XXlookbib(1), refer(1), sortbib(1)
  828. XX.SH "WHO-TO-BLAME"
  829. XXPaul DuBois, dubois@rhesus.primate.wisc.edu
  830. XX.SH BUGS
  831. XXShould probably be named ``snail.''
  832. SHAR_EOF
  833. if test 2518 -ne "`wc -c uniqbib.1`"
  834. then
  835. echo shar: error transmitting uniqbib.1 '(should have been 2518 characters)'
  836. fi
  837. echo shar: extracting Makefile '(841 characters)'
  838. sed 's/^XX//' << \SHAR_EOF > Makefile
  839. XXBINDIR=/usr/local
  840. XXLIBS=biblib
  841. XXINSTALL= -c -m 755 -o bin -g system
  842. XXTROFF=xroff
  843. XXMANMACROS=man.new
  844. XXLOBJS=\
  845. XX    BibAlloc.o\
  846. XX    BibCmp.o\
  847. XX    BibRead.o\
  848. XX    BibWrite.o\
  849. XX    panic.o
  850. XX
  851. XXall: uniqbib biblib
  852. XXinstall: iuniqbib ibiblib
  853. XXclean:
  854. XX    rm -f *.o uniqbib biblib
  855. XX
  856. XXuniqbib: uniqbib.o biblib
  857. XX    cc uniqbib.o ${LIBS} -o uniqbib
  858. XXiuniqbib: uniqbib
  859. XX    install ${INSTALL} uniqbib ${BINDIR}
  860. XX
  861. XXuniqbib.o: bibinfo.h
  862. XX${LOBJS}: bibinfo.h
  863. XX
  864. XXbiblib: ${LOBJS}
  865. XX    -rm -f biblib
  866. XX    ar r biblib ${LOBJS}
  867. XX    ranlib biblib
  868. XXibiblib:
  869. XX
  870. XXman: uniqbib.1
  871. XX    ${TROFF} -{MANMACROS} uniqbib.1 | lpr
  872. XX
  873. XXshar:
  874. XX    cshar -a uniqbib.c bibinfo.h BibAlloc.c BibCmp.c BibRead.c \
  875. XX        BibWrite.c panic.c uniqbib.1 Makefile \
  876. XX        bad1 bad2 bad3 > uniqbib.sha
  877. XX
  878. XXtest:
  879. XX    @echo ERROR IS: too many authors
  880. XX    -uniqbib bad1
  881. XX    @echo ERROR IS: citation too long
  882. XX    -uniqbib bad2
  883. XX    @echo ERROR IS: first line is non-key line
  884. XX    -uniqbib bad3
  885. SHAR_EOF
  886. if test 841 -ne "`wc -c Makefile`"
  887. then
  888. echo shar: error transmitting Makefile '(should have been 841 characters)'
  889. fi
  890. echo shar: extracting bad1 '(223 characters)'
  891. sed 's/^XX//' << \SHAR_EOF > bad1
  892. XX
  893. XX%A auth 1
  894. XX%A auth 2
  895. XX%A auth 3
  896. XX%A auth 4
  897. XX%A auth 5
  898. XX%A auth 6
  899. XX%A auth 7
  900. XX%A auth 8
  901. XX%A auth 9
  902. XX%A auth 10
  903. XX%A auth 11
  904. XX%A auth 12
  905. XX%A auth 13
  906. XX%A auth 14
  907. XX%A auth 15
  908. XX%A auth 16
  909. XX%A auth 17
  910. XX%A auth 18
  911. XX%A auth 19
  912. XX%A auth 20
  913. XX%A auth 21
  914. SHAR_EOF
  915. if test 223 -ne "`wc -c bad1`"
  916. then
  917. echo shar: error transmitting bad1 '(should have been 223 characters)'
  918. fi
  919. echo shar: extracting bad2 '(1025 characters)'
  920. sed 's/^XX//' << \SHAR_EOF > bad2
  921. XX%A aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
  922. XX%T ttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttt
  923. XX%V vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
  924. XX%N nnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnn
  925. XX%P PPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPP
  926. XX%C ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
  927. XX%I iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii
  928. XX%D ddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd
  929. XX%K kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk
  930. XX%O ooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo
  931. XX%E eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee
  932. XX%B bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
  933. XX%X xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
  934. SHAR_EOF
  935. if test 1025 -ne "`wc -c bad2`"
  936. then
  937. echo shar: error transmitting bad2 '(should have been 1025 characters)'
  938. fi
  939. echo shar: extracting bad3 '(3 characters)'
  940. sed 's/^XX//' << \SHAR_EOF > bad3
  941. XX
  942. XXt
  943. SHAR_EOF
  944. if test 3 -ne "`wc -c bad3`"
  945. then
  946. echo shar: error transmitting bad3 '(should have been 3 characters)'
  947. fi
  948. #    End of shell archive
  949. exit 0
  950.  
  951.