home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Spezial / SPEZIAL2_97.zip / SPEZIAL2_97.iso / ANWEND / EDITOR / NVI179B / NVI179B.ZIP / ex / ex_tag.c < prev    next >
C/C++ Source or Header  |  1996-09-15  |  30KB  |  1,325 lines

  1. /*-
  2.  * Copyright (c) 1992, 1993, 1994
  3.  *    The Regents of the University of California.  All rights reserved.
  4.  * Copyright (c) 1992, 1993, 1994, 1995, 1996
  5.  *    Keith Bostic.  All rights reserved.
  6.  *
  7.  * This code is derived from software contributed to Berkeley by
  8.  * David Hitz of Auspex Systems, Inc.
  9.  *
  10.  * See the LICENSE file for redistribution information.
  11.  */
  12.  
  13. #include "config.h"
  14.  
  15. #ifndef lint
  16. static const char sccsid[] = "@(#)ex_tag.c    10.36 (Berkeley) 9/15/96";
  17. #endif /* not lint */
  18.  
  19. #include <sys/param.h>
  20. #include <sys/types.h>        /* XXX: param.h may not have included types.h */
  21.  
  22. #ifdef HAVE_SYS_MMAN_H
  23. #include <sys/mman.h>
  24. #endif
  25.  
  26. #include <sys/queue.h>
  27. #include <sys/stat.h>
  28. #include <sys/time.h>
  29.  
  30. #include <bitstring.h>
  31. #include <ctype.h>
  32. #include <errno.h>
  33. #include <fcntl.h>
  34. #include <limits.h>
  35. #include <stddef.h>
  36. #include <stdio.h>
  37. #include <stdlib.h>
  38. #include <string.h>
  39. #include <unistd.h>
  40.  
  41. #include "../common/common.h"
  42. #include "../vi/vi.h"
  43. #include "tag.h"
  44.  
  45. static char    *binary_search __P((char *, char *, char *));
  46. static int     compare __P((char *, char *, char *));
  47. static void     ctag_file __P((SCR *, TAGF *, char *, char **, size_t *));
  48. static int     ctag_search __P((SCR *, char *, size_t, char *));
  49. static int     ctag_sfile __P((SCR *, TAGF *, TAGQ *, char *));
  50. static TAGQ    *ctag_slist __P((SCR *, char *));
  51. static char    *linear_search __P((char *, char *, char *));
  52. static int     tag_copy __P((SCR *, TAG *, TAG **));
  53. static int     tag_pop __P((SCR *, TAGQ *, int));
  54. static int     tagf_copy __P((SCR *, TAGF *, TAGF **));
  55. static int     tagf_free __P((SCR *, TAGF *));
  56. static int     tagq_copy __P((SCR *, TAGQ *, TAGQ **));
  57.  
  58. /*
  59.  * ex_tag_first --
  60.  *    The tag code can be entered from main, e.g., "vi -t tag".
  61.  *
  62.  * PUBLIC: int ex_tag_first __P((SCR *, char *));
  63.  */
  64. int
  65. ex_tag_first(sp, tagarg)
  66.     SCR *sp;
  67.     char *tagarg;
  68. {
  69.     ARGS *ap[2], a;
  70.     EXCMD cmd;
  71.  
  72.     /* Build an argument for the ex :tag command. */
  73.     ex_cinit(&cmd, C_TAG, 0, OOBLNO, OOBLNO, 0, ap);
  74.     ex_cadd(&cmd, &a, tagarg, strlen(tagarg));
  75.  
  76.     /*
  77.      * XXX
  78.      * Historic vi went ahead and created a temporary file when it failed
  79.      * to find the tag.  We match historic practice, but don't distinguish
  80.      * between real error and failure to find the tag.
  81.      */
  82.     if (ex_tag_push(sp, &cmd))
  83.         return (0);
  84.  
  85.     /* Display tags in the center of the screen. */
  86.     F_CLR(sp, SC_SCR_TOP);
  87.     F_SET(sp, SC_SCR_CENTER);
  88.  
  89.     return (0);
  90. }
  91.  
  92. /*
  93.  * ex_tag_push -- ^]
  94.  *          :tag[!] [string]
  95.  *
  96.  * Enter a new TAGQ context based on a ctag string.
  97.  *
  98.  * PUBLIC: int ex_tag_push __P((SCR *, EXCMD *));
  99.  */
  100. int
  101. ex_tag_push(sp, cmdp)
  102.     SCR *sp;
  103.     EXCMD *cmdp;
  104. {
  105.     EX_PRIVATE *exp;
  106.     FREF *frp;
  107.     TAG *rtp;
  108.     TAGQ *rtqp, *tqp;
  109.     recno_t lno;
  110.     size_t cno;
  111.     long tl;
  112.     int force, istmp;
  113.  
  114.     exp = EXP(sp);
  115.     switch (cmdp->argc) {
  116.     case 1:
  117.         if (exp->tag_last != NULL)
  118.             free(exp->tag_last);
  119.  
  120.         if ((exp->tag_last = strdup(cmdp->argv[0]->bp)) == NULL) {
  121.             msgq(sp, M_SYSERR, NULL);
  122.             return (1);
  123.         }
  124.  
  125.         /* Taglength may limit the number of characters. */
  126.         if ((tl =
  127.             O_VAL(sp, O_TAGLENGTH)) != 0 && strlen(exp->tag_last) > tl)
  128.             exp->tag_last[tl] = '\0';
  129.         break;
  130.     case 0:
  131.         if (exp->tag_last == NULL) {
  132.             msgq(sp, M_ERR, "158|No previous tag entered");
  133.             return (1);
  134.         }
  135.         break;
  136.     default:
  137.         abort();
  138.     }
  139.  
  140.     /* Get the tag information. */
  141.     if ((tqp = ctag_slist(sp, exp->tag_last)) == NULL)
  142.         return (1);
  143.  
  144.     /*
  145.      * Allocate all necessary memory before swapping screens.  Initialize
  146.      * flags so we know what to free.
  147.      */
  148.     rtp = NULL;
  149.     rtqp = NULL;
  150.     if (exp->tq.cqh_first == (void *)&exp->tq) {
  151.         /* Initialize the `local context' tag queue structure. */
  152.         CALLOC_GOTO(sp, rtqp, TAGQ *, 1, sizeof(TAGQ));
  153.         CIRCLEQ_INIT(&rtqp->tagq);
  154.  
  155.         /* Initialize and link in its tag structure. */
  156.         CALLOC_GOTO(sp, rtp, TAG *, 1, sizeof(TAG));
  157.         CIRCLEQ_INSERT_HEAD(&rtqp->tagq, rtp, q);
  158.         rtqp->current = rtp;
  159.     }
  160.  
  161.     /*
  162.      * Stick the current context information in a convenient place, we're
  163.      * about to lose it.  Note, if we're called on editor startup, there
  164.      * will be no FREF structure.
  165.      */
  166.     frp = sp->frp;
  167.     lno = sp->lno;
  168.     cno = sp->cno;
  169.     istmp = frp == NULL ||
  170.         F_ISSET(frp, FR_TMPFILE) && !F_ISSET(cmdp, E_NEWSCREEN);
  171.  
  172.     /* Try to switch to the tag. */
  173.     force = FL_ISSET(cmdp->iflags, E_C_FORCE);
  174.     if (F_ISSET(cmdp, E_NEWSCREEN)) {
  175.         if (ex_tag_Nswitch(sp, tqp->tagq.cqh_first, force))
  176.             goto err;
  177.  
  178.         /* Everything else gets done in the new screen. */
  179.         sp = sp->nextdisp;
  180.         exp = EXP(sp);
  181.     } else
  182.         if (ex_tag_nswitch(sp, tqp->tagq.cqh_first, force))
  183.             goto err;
  184.  
  185.     /*
  186.      * If this is the first tag, put a `current location' queue entry
  187.      * in place, so we can pop all the way back to the current mark.
  188.      * Note, it doesn't point to much of anything, it's a placeholder.
  189.      */
  190.     if (exp->tq.cqh_first == (void *)&exp->tq) {
  191.         CIRCLEQ_INSERT_HEAD(&exp->tq, rtqp, q);
  192.     } else
  193.         rtqp = exp->tq.cqh_first;
  194.  
  195.     /* Link the new TAGQ structure into place. */
  196.     CIRCLEQ_INSERT_HEAD(&exp->tq, tqp, q);
  197.  
  198.     (void)ctag_search(sp,
  199.         tqp->current->search, tqp->current->slen, tqp->tag);
  200.  
  201.     /*
  202.      * Move the current context from the temporary save area into the
  203.      * right structure.
  204.      *
  205.      * If we were in a temporary file, we don't have a context to which
  206.      * we can return, so just make it be the same as what we're moving
  207.      * to.  It will be a little odd that ^T doesn't change anything, but
  208.      * I don't think it's a big deal.
  209.      */
  210.     if (istmp) {
  211.         rtqp->current->frp = sp->frp;
  212.         rtqp->current->lno = sp->lno;
  213.         rtqp->current->cno = sp->cno;
  214.     } else {
  215.         rtqp->current->frp = frp;
  216.         rtqp->current->lno = lno;
  217.         rtqp->current->cno = cno;
  218.     }
  219.     return (0);
  220.  
  221. err:
  222. alloc_err:
  223.     if (rtqp != NULL)
  224.         free(rtqp);
  225.     if (rtp != NULL)
  226.         free(rtp);
  227.     tagq_free(sp, tqp);
  228.     return (1);
  229. }
  230.  
  231. /* 
  232.  * ex_tag_next --
  233.  *    Switch context to the next TAG.
  234.  *
  235.  * PUBLIC: int ex_tag_next __P((SCR *, EXCMD *));
  236.  */
  237. int
  238. ex_tag_next(sp, cmdp)
  239.     SCR *sp;
  240.     EXCMD *cmdp;
  241. {
  242.     EX_PRIVATE *exp;
  243.     TAG *tp;
  244.     TAGQ *tqp;
  245.  
  246.     exp = EXP(sp);
  247.     if ((tqp = exp->tq.cqh_first) == (void *)&exp->tq) {
  248.         tag_msg(sp, TAG_EMPTY, NULL);
  249.         return (1);
  250.     }
  251.     if ((tp = tqp->current->q.cqe_next) == (void *)&tqp->tagq) {
  252.         msgq(sp, M_ERR, "282|Already at the last tag of this group");
  253.         return (1);
  254.     }
  255.     if (ex_tag_nswitch(sp, tp, FL_ISSET(cmdp->iflags, E_C_FORCE)))
  256.         return (1);
  257.     tqp->current = tp;
  258.  
  259.     if (F_ISSET(tqp, TAG_CSCOPE))
  260.         (void)cscope_search(sp, tqp, tp);
  261.     else
  262.         (void)ctag_search(sp, tp->search, tp->slen, tqp->tag);
  263.     return (0);
  264. }
  265.  
  266. /* 
  267.  * ex_tag_prev --
  268.  *    Switch context to the next TAG.
  269.  *
  270.  * PUBLIC: int ex_tag_prev __P((SCR *, EXCMD *));
  271.  */
  272. int
  273. ex_tag_prev(sp, cmdp)
  274.     SCR *sp;
  275.     EXCMD *cmdp;
  276. {
  277.     EX_PRIVATE *exp;
  278.     TAG *tp;
  279.     TAGQ *tqp;
  280.  
  281.     exp = EXP(sp);
  282.     if ((tqp = exp->tq.cqh_first) == (void *)&exp->tq) {
  283.         tag_msg(sp, TAG_EMPTY, NULL);
  284.         return (0);
  285.     }
  286.     if ((tp = tqp->current->q.cqe_prev) == (void *)&tqp->tagq) {
  287.         msgq(sp, M_ERR, "255|Already at the first tag of this group");
  288.         return (1);
  289.     }
  290.     if (ex_tag_nswitch(sp, tp, FL_ISSET(cmdp->iflags, E_C_FORCE)))
  291.         return (1);
  292.     tqp->current = tp;
  293.  
  294.     if (F_ISSET(tqp, TAG_CSCOPE))
  295.         (void)cscope_search(sp, tqp, tp);
  296.     else
  297.         (void)ctag_search(sp, tp->search, tp->slen, tqp->tag);
  298.     return (0);
  299. }
  300.  
  301. /*
  302.  * ex_tag_nswitch --
  303.  *    Switch context to the specified TAG.
  304.  *
  305.  * PUBLIC: int ex_tag_nswitch __P((SCR *, TAG *, int));
  306.  */
  307. int
  308. ex_tag_nswitch(sp, tp, force)
  309.     SCR *sp;
  310.     TAG *tp;
  311.     int force;
  312. {
  313.     /* Get a file structure. */
  314.     if (tp->frp == NULL && (tp->frp = file_add(sp, tp->fname)) == NULL)
  315.         return (1);
  316.  
  317.     /* If not changing files, return, we're done. */
  318.     if (tp->frp == sp->frp)
  319.         return (0);
  320.  
  321.     /* Check for permission to leave. */
  322.     if (file_m1(sp, force, FS_ALL | FS_POSSIBLE))
  323.         return (1);
  324.  
  325.     /* Initialize the new file. */
  326.     if (file_init(sp, tp->frp, NULL, FS_SETALT))
  327.         return (1);
  328.  
  329.     /* Display tags in the center of the screen. */
  330.     F_CLR(sp, SC_SCR_TOP);
  331.     F_SET(sp, SC_SCR_CENTER);
  332.  
  333.     /* Switch. */
  334.     F_SET(sp, SC_FSWITCH);
  335.     return (0);
  336. }
  337.  
  338. /*
  339.  * ex_tag_Nswitch --
  340.  *    Switch context to the specified TAG in a new screen.
  341.  *
  342.  * PUBLIC: int ex_tag_Nswitch __P((SCR *, TAG *, int));
  343.  */
  344. int
  345. ex_tag_Nswitch(sp, tp, force)
  346.     SCR *sp;
  347.     TAG *tp;
  348.     int force;
  349. {
  350.     SCR *new;
  351.  
  352.     /* Get a file structure. */
  353.     if (tp->frp == NULL && (tp->frp = file_add(sp, tp->fname)) == NULL)
  354.         return (1);
  355.  
  356.     /* Get a new screen. */
  357.     if (screen_init(sp->gp, sp, &new))
  358.         return (1);
  359.     if (vs_split(sp, new, 0)) {
  360.         (void)file_end(new, new->ep, 1);
  361.         (void)screen_end(new);
  362.         return (1);
  363.     }
  364.  
  365.     /* Get a backing file. */
  366.     if (tp->frp == sp->frp) {
  367.         /* Copy file state. */
  368.         new->ep = sp->ep;
  369.         ++new->ep->refcnt;
  370.  
  371.         new->frp = tp->frp;
  372.         new->frp->flags = sp->frp->flags;
  373.     } else if (file_init(new, tp->frp, NULL, force)) {
  374.         (void)vs_discard(new, NULL);
  375.         (void)screen_end(new);
  376.         return (1);
  377.     }
  378.  
  379.     /* Create the argument list. */
  380.     new->cargv = new->argv = ex_buildargv(sp, NULL, tp->frp->name);
  381.  
  382.     /* Display tags in the center of the screen. */
  383.     F_CLR(new, SC_SCR_TOP);
  384.     F_SET(new, SC_SCR_CENTER);
  385.  
  386.     /* Switch. */
  387.     sp->nextdisp = new;
  388.     F_SET(sp, SC_SSWITCH);
  389.  
  390.     return (0);
  391. }
  392.  
  393. /*
  394.  * ex_tag_pop -- ^T
  395.  *         :tagp[op][!] [number | file]
  396.  *
  397.  *    Pop to a previous TAGQ context.
  398.  *
  399.  * PUBLIC: int ex_tag_pop __P((SCR *, EXCMD *));
  400.  */
  401. int
  402. ex_tag_pop(sp, cmdp)
  403.     SCR *sp;
  404.     EXCMD *cmdp;
  405. {
  406.     EX_PRIVATE *exp;
  407.     TAGQ *tqp, *dtqp;
  408.     size_t arglen;
  409.     long off;
  410.     char *arg, *p, *t;
  411.  
  412.     /* Check for an empty stack. */
  413.     exp = EXP(sp);
  414.     if (exp->tq.cqh_first == (void *)&exp->tq) {
  415.         tag_msg(sp, TAG_EMPTY, NULL);
  416.         return (1);
  417.     }
  418.  
  419.     /* Find the last TAG structure that we're going to DISCARD! */
  420.     switch (cmdp->argc) {
  421.     case 0:                /* Pop one tag. */
  422.         dtqp = exp->tq.cqh_first;
  423.         break;
  424.     case 1:                /* Name or number. */
  425.         arg = cmdp->argv[0]->bp;
  426.         off = strtol(arg, &p, 10);
  427.         if (*p != '\0')
  428.             goto filearg;
  429.  
  430.         /* Number: pop that many queue entries. */
  431.         if (off < 1)
  432.             return (0);
  433.         for (tqp = exp->tq.cqh_first;
  434.             tqp != (void *)&exp->tq && --off > 1;
  435.             tqp = tqp->q.cqe_next);
  436.         if (tqp == (void *)&exp->tq) {
  437.             msgq(sp, M_ERR,
  438.     "159|Less than %s entries on the tags stack; use :display t[ags]",
  439.                 arg);
  440.             return (1);
  441.         }
  442.         dtqp = tqp;
  443.         break;
  444.  
  445.         /* File argument: pop to that queue entry. */
  446. filearg:    arglen = strlen(arg);
  447.         for (tqp = exp->tq.cqh_first;
  448.             tqp != (void *)&exp->tq;
  449.             dtqp = tqp, tqp = tqp->q.cqe_next) {
  450.             /* Don't pop to the current file. */
  451.             if (tqp == exp->tq.cqh_first)
  452.                 continue;
  453.             p = tqp->current->frp->name;
  454.             if ((t = strrchr(p, '/')) == NULL)
  455.                 t = p;
  456.             else
  457.                 ++t;
  458.             if (!strncmp(arg, t, arglen))
  459.                 break;
  460.         }
  461.         if (tqp == (void *)&exp->tq) {
  462.             msgq_str(sp, M_ERR, arg,
  463.     "160|No file %s on the tags stack to return to; use :display t[ags]");
  464.             return (1);
  465.         }
  466.         if (tqp == exp->tq.cqh_first)
  467.             return (0);
  468.         break;
  469.     default:
  470.         abort();
  471.     }
  472.  
  473.     return (tag_pop(sp, dtqp, FL_ISSET(cmdp->iflags, E_C_FORCE)));
  474. }
  475.  
  476. /*
  477.  * ex_tag_top -- :tagt[op][!]
  478.  *    Clear the tag stack.
  479.  *
  480.  * PUBLIC: int ex_tag_top __P((SCR *, EXCMD *));
  481.  */
  482. int
  483. ex_tag_top(sp, cmdp)
  484.     SCR *sp;
  485.     EXCMD *cmdp;
  486. {
  487.     EX_PRIVATE *exp;
  488.  
  489.     exp = EXP(sp);
  490.  
  491.     /* Check for an empty stack. */
  492.     if (exp->tq.cqh_first == (void *)&exp->tq) {
  493.         tag_msg(sp, TAG_EMPTY, NULL);
  494.         return (1);
  495.     }
  496.  
  497.     /* Return to the oldest information. */
  498.     return (tag_pop(sp,
  499.         exp->tq.cqh_last->q.cqe_prev, FL_ISSET(cmdp->iflags, E_C_FORCE)));
  500. }
  501.  
  502. /*
  503.  * tag_pop --
  504.  *    Pop up to and including the specified TAGQ context.
  505.  */
  506. static int
  507. tag_pop(sp, dtqp, force)
  508.     SCR *sp;
  509.     TAGQ *dtqp;
  510.     int force;
  511. {
  512.     EX_PRIVATE *exp;
  513.     TAG *tp;
  514.     TAGQ *tqp;
  515.  
  516.     exp = EXP(sp);
  517.  
  518.     /*
  519.      * Update the cursor from the saved TAG information of the TAG
  520.      * structure we're moving to.
  521.      */
  522.     tp = dtqp->q.cqe_next->current;
  523.     if (tp->frp == sp->frp) {
  524.         sp->lno = tp->lno;
  525.         sp->cno = tp->cno;
  526.     } else {
  527.         if (file_m1(sp, force, FS_ALL | FS_POSSIBLE))
  528.             return (1);
  529.  
  530.         tp->frp->lno = tp->lno;
  531.         tp->frp->cno = tp->cno;
  532.         F_SET(sp->frp, FR_CURSORSET);
  533.         if (file_init(sp, tp->frp, NULL, FS_SETALT))
  534.             return (1);
  535.  
  536.         F_SET(sp, SC_FSWITCH);
  537.     }
  538.  
  539.     /* Pop entries off the queue up to and including dtqp. */
  540.     do {
  541.         tqp = exp->tq.cqh_first;
  542.         if (tagq_free(sp, tqp))
  543.             return (0);
  544.     } while (tqp != dtqp);
  545.  
  546.     /*
  547.      * If only a single tag left, we've returned to the first tag point,
  548.      * and the stack is now empty.
  549.      */
  550.     if (exp->tq.cqh_first->q.cqe_next == (void *)&exp->tq)
  551.         tagq_free(sp, exp->tq.cqh_first);
  552.  
  553.     return (0);
  554. }
  555.  
  556. /*
  557.  * ex_tag_display --
  558.  *    Display the list of tags.
  559.  *
  560.  * PUBLIC: int ex_tag_display __P((SCR *));
  561.  */
  562. int
  563. ex_tag_display(sp)
  564.     SCR *sp;
  565. {
  566.     EX_PRIVATE *exp;
  567.     TAG *tp;
  568.     TAGQ *tqp;
  569.     int cnt;
  570.     size_t len;
  571.     char *p, *sep;
  572.  
  573.     exp = EXP(sp);
  574.     if ((tqp = exp->tq.cqh_first) == (void *)&exp->tq) {
  575.         tag_msg(sp, TAG_EMPTY, NULL);
  576.         return (0);
  577.     }
  578.  
  579.     /*
  580.      * We give the file name 20 columns and the search string the rest.
  581.      * If there's not enough room, we don't do anything special, it's
  582.      * not worth the effort, it just makes the display more confusing.
  583.      *
  584.      * We also assume that characters in file names map 1-1 to printing
  585.      * characters.  This might not be true, but I don't think it's worth
  586.      * fixing.  (The obvious fix is to pass the filenames through the
  587.      * msg_print function.)
  588.      */
  589. #define    L_NAME    30        /* Name. */
  590. #define    L_SLOP     4        /* Leading number plus trailing *. */
  591. #define    L_SPACE     5        /* Spaces after name, before tag. */
  592. #define    L_TAG    20        /* Tag. */
  593.     if (sp->cols <= L_NAME + L_SLOP) {
  594.         msgq(sp, M_ERR, "292|Display too small.");
  595.         return (0);
  596.     }
  597.  
  598.     /*
  599.      * Display the list of tags for each queue entry.  The first entry
  600.      * is numbered, and the current tag entry has an asterisk appended.
  601.      */
  602.     for (cnt = 1, tqp = exp->tq.cqh_first; !INTERRUPTED(sp) &&
  603.         tqp != (void *)&exp->tq; ++cnt, tqp = tqp->q.cqe_next)
  604.         for (tp = tqp->tagq.cqh_first;
  605.             tp != (void *)&tqp->tagq; tp = tp->q.cqe_next) {
  606.             if (tp == tqp->tagq.cqh_first)
  607.                 (void)ex_printf(sp, "%2d ", cnt);
  608.             else
  609.                 (void)ex_printf(sp, "   ");
  610.             p = tp->frp == NULL ? tp->fname : tp->frp->name;
  611.             if ((len = strlen(p)) > L_NAME) {
  612.                 len = len - (L_NAME - 4);
  613.                 (void)ex_printf(sp, "   ... %*.*s",
  614.                     L_NAME - 4, L_NAME - 4, p + len);
  615.             } else
  616.                 (void)ex_printf(sp,
  617.                     "   %*.*s", L_NAME, L_NAME, p);
  618.             if (tqp->current == tp)
  619.                 (void)ex_printf(sp, "*");
  620.  
  621.             if (tp == tqp->tagq.cqh_first && tqp->tag != NULL &&
  622.                 (sp->cols - L_NAME) >= L_TAG + L_SPACE) {
  623.                 len = strlen(tqp->tag);
  624.                 if (len > sp->cols - (L_NAME + L_SPACE))
  625.                     len = sp->cols - (L_NAME + L_SPACE);
  626.                 (void)ex_printf(sp, "%s%.*s",
  627.                     tqp->current == tp ? "    " : "     ",
  628.                     (int)len, tqp->tag);
  629.             }
  630.             (void)ex_printf(sp, "\n");
  631.         }
  632.     return (0);
  633. }
  634.  
  635. /*
  636.  * ex_tag_copy --
  637.  *    Copy a screen's tag structures.
  638.  *
  639.  * PUBLIC: int ex_tag_copy __P((SCR *, SCR *));
  640.  */
  641. int
  642. ex_tag_copy(orig, sp)
  643.     SCR *orig, *sp;
  644. {
  645.     EX_PRIVATE *oexp, *nexp;
  646.     TAGQ *aqp, *tqp;
  647.     TAG *ap, *tp;
  648.     TAGF *atfp, *tfp;
  649.  
  650.     oexp = EXP(orig);
  651.     nexp = EXP(sp);
  652.  
  653.     /* Copy tag queue and tags stack. */
  654.     for (aqp = oexp->tq.cqh_first;
  655.         aqp != (void *)&oexp->tq; aqp = aqp->q.cqe_next) {
  656.         if (tagq_copy(sp, aqp, &tqp))
  657.             return (1);
  658.         for (ap = aqp->tagq.cqh_first;
  659.             ap != (void *)&aqp->tagq; ap = ap->q.cqe_next) {
  660.             if (tag_copy(sp, ap, &tp))
  661.                 return (1);
  662.             /* Set the current pointer. */
  663.             if (aqp->current == ap)
  664.                 tqp->current = tp;
  665.             CIRCLEQ_INSERT_TAIL(&tqp->tagq, tp, q);
  666.         }
  667.         CIRCLEQ_INSERT_TAIL(&nexp->tq, tqp, q);
  668.     }
  669.  
  670.     /* Copy list of tag files. */
  671.     for (atfp = oexp->tagfq.tqh_first;
  672.         atfp != NULL; atfp = atfp->q.tqe_next) {
  673.         if (tagf_copy(sp, atfp, &tfp))
  674.             return (1);
  675.         TAILQ_INSERT_TAIL(&nexp->tagfq, tfp, q);
  676.     }
  677.  
  678.     /* Copy the last tag. */
  679.     if (oexp->tag_last != NULL &&
  680.         (nexp->tag_last = strdup(oexp->tag_last)) == NULL) {
  681.         msgq(sp, M_SYSERR, NULL);
  682.         return (1);
  683.     }
  684.     return (0);
  685. }
  686.  
  687. /*
  688.  * tagf_copy --
  689.  *    Copy a TAGF structure and return it in new memory.
  690.  */
  691. static int
  692. tagf_copy(sp, otfp, tfpp)
  693.     SCR *sp;
  694.     TAGF *otfp, **tfpp;
  695. {
  696.     TAGF *tfp;
  697.  
  698.     MALLOC_RET(sp, tfp, TAGF *, sizeof(TAGF));
  699.     *tfp = *otfp;
  700.  
  701.     /* XXX: Allocate as part of the TAGF structure!!! */
  702.     if ((tfp->name = strdup(otfp->name)) == NULL)
  703.         return (1);
  704.  
  705.     *tfpp = tfp;
  706.     return (0);
  707. }
  708.  
  709. /*
  710.  * tagq_copy --
  711.  *    Copy a TAGQ structure and return it in new memory.
  712.  */
  713. static int
  714. tagq_copy(sp, otqp, tqpp)
  715.     SCR *sp;
  716.     TAGQ *otqp, **tqpp;
  717. {
  718.     TAGQ *tqp;
  719.     size_t len;
  720.  
  721.     len = sizeof(TAGQ);
  722.     if (otqp->tag != NULL)
  723.         len += otqp->tlen + 1;
  724.     MALLOC_RET(sp, tqp, TAGQ *, len);
  725.     memcpy(tqp, otqp, len);
  726.  
  727.     CIRCLEQ_INIT(&tqp->tagq);
  728.     tqp->current = NULL;
  729.     if (otqp->tag != NULL)
  730.         tqp->tag = tqp->buf;
  731.  
  732.     *tqpp = tqp;
  733.     return (0);
  734. }
  735.  
  736. /*
  737.  * tag_copy --
  738.  *    Copy a TAG structure and return it in new memory.
  739.  */
  740. static int
  741. tag_copy(sp, otp, tpp)
  742.     SCR *sp;
  743.     TAG *otp, **tpp;
  744. {
  745.     TAG *tp;
  746.     size_t len;
  747.  
  748.     len = sizeof(TAG);
  749.     if (otp->fname != NULL)
  750.         len += otp->fnlen + 1;
  751.     if (otp->search != NULL)
  752.         len += otp->slen + 1;
  753.     MALLOC_RET(sp, tp, TAG *, len);
  754.     memcpy(tp, otp, len);
  755.  
  756.     if (otp->fname != NULL)
  757.         tp->fname = tp->buf;
  758.     if (otp->search != NULL)
  759.         tp->search = tp->fname + otp->fnlen + 1;
  760.  
  761.     *tpp = tp;
  762.     return (0);
  763. }
  764.  
  765. /*
  766.  * tagf_free --
  767.  *    Free a TAGF structure.
  768.  */
  769. static int
  770. tagf_free(sp, tfp)
  771.     SCR *sp;
  772.     TAGF *tfp;
  773. {
  774.     EX_PRIVATE *exp;
  775.  
  776.     exp = EXP(sp);
  777.     TAILQ_REMOVE(&exp->tagfq, tfp, q);
  778.     free(tfp->name);
  779.     free(tfp);
  780.     return (0);
  781. }
  782.  
  783. /*
  784.  * tagq_free --
  785.  *    Free a TAGQ structure (and associated TAG structures).
  786.  *
  787.  * PUBLIC: int tagq_free __P((SCR *, TAGQ *));
  788.  */
  789. int
  790. tagq_free(sp, tqp)
  791.     SCR *sp;
  792.     TAGQ *tqp;
  793. {
  794.     EX_PRIVATE *exp;
  795.     TAG *tp;
  796.  
  797.     exp = EXP(sp);
  798.     while ((tp = tqp->tagq.cqh_first) != (void *)&tqp->tagq) {
  799.         CIRCLEQ_REMOVE(&tqp->tagq, tp, q);
  800.         free(tp);
  801.     }
  802.     /*
  803.      * !!!
  804.      * If allocated and then the user failed to switch files, the TAGQ
  805.      * structure was never attached to any list.
  806.      */
  807.     if (tqp->q.cqe_next != NULL)
  808.         CIRCLEQ_REMOVE(&exp->tq, tqp, q);
  809.     free(tqp);
  810.     return (0);
  811. }
  812.  
  813. /*
  814.  * tag_msg
  815.  *    A few common messages.
  816.  *
  817.  * PUBLIC: void tag_msg __P((SCR *, tagmsg_t, char *));
  818.  */
  819. void
  820. tag_msg(sp, msg, tag)
  821.     SCR *sp;
  822.     tagmsg_t msg;
  823.     char *tag;
  824. {
  825.     switch (msg) {
  826.     case TAG_BADLNO:
  827.         msgq_str(sp, M_ERR, tag,
  828.         "164|%s: the tag's line number is past the end of the file");
  829.         break;
  830.     case TAG_EMPTY:
  831.         msgq(sp, M_INFO, "165|The tags stack is empty");
  832.         break;
  833.     case TAG_SEARCH:
  834.         msgq_str(sp, M_ERR, tag, "166|%s: search pattern not found");
  835.         break;
  836.     default:
  837.         abort();
  838.     }
  839. }
  840.  
  841. /*
  842.  * ex_tagf_alloc --
  843.  *    Create a new list of ctag files.
  844.  *
  845.  * PUBLIC: int ex_tagf_alloc __P((SCR *, char *));
  846.  */
  847. int
  848. ex_tagf_alloc(sp, str)
  849.     SCR *sp;
  850.     char *str;
  851. {
  852.     EX_PRIVATE *exp;
  853.     TAGF *tfp;
  854.     size_t len;
  855.     char *p, *t;
  856.  
  857.     /* Free current queue. */
  858.     exp = EXP(sp);
  859.     while ((tfp = exp->tagfq.tqh_first) != NULL)
  860.         tagf_free(sp, tfp);
  861.  
  862.     /* Create new queue. */
  863.     for (p = t = str;; ++p) {
  864.         if (*p == '\0' || isblank(*p)) {
  865.             if ((len = p - t) > 1) {
  866.                 MALLOC_RET(sp, tfp, TAGF *, sizeof(TAGF));
  867.                 MALLOC(sp, tfp->name, char *, len + 1);
  868.                 if (tfp->name == NULL) {
  869.                     free(tfp);
  870.                     return (1);
  871.                 }
  872.                 memcpy(tfp->name, t, len);
  873.                 tfp->name[len] = '\0';
  874.                 tfp->flags = 0;
  875.                 TAILQ_INSERT_TAIL(&exp->tagfq, tfp, q);
  876.             }
  877.             t = p + 1;
  878.         }
  879.         if (*p == '\0')
  880.              break;
  881.     }
  882.     return (0);
  883. }
  884.                         /* Free previous queue. */
  885. /*
  886.  * ex_tag_free --
  887.  *    Free the ex tag information.
  888.  *
  889.  * PUBLIC: int ex_tag_free __P((SCR *));
  890.  */
  891. int
  892. ex_tag_free(sp)
  893.     SCR *sp;
  894. {
  895.     EX_PRIVATE *exp;
  896.     TAGF *tfp;
  897.     TAGQ *tqp;
  898.  
  899.     /* Free up tag information. */
  900.     exp = EXP(sp);
  901.     while ((tqp = exp->tq.cqh_first) != (void *)&exp->tq)
  902.         tagq_free(sp, tqp);
  903.     while ((tfp = exp->tagfq.tqh_first) != NULL)
  904.         tagf_free(sp, tfp);
  905.     if (exp->tag_last != NULL)
  906.         free(exp->tag_last);
  907.     return (0);
  908. }
  909.  
  910. /*
  911.  * ctag_search --
  912.  *    Search a file for a tag.
  913.  */
  914. static int
  915. ctag_search(sp, search, slen, tag)
  916.     SCR *sp;
  917.     char *search, *tag;
  918.     size_t slen;
  919. {
  920.     MARK m;
  921.     char *p;
  922.  
  923.     /*
  924.      * !!!
  925.      * The historic tags file format (from a long, long time ago...)
  926.      * used a line number, not a search string.  I got complaints, so
  927.      * people are still using the format.  POSIX 1003.2 permits it.
  928.      */
  929.     if (isdigit(search[0])) {
  930.         m.lno = atoi(search);
  931.         if (!db_exist(sp, m.lno)) {
  932.             tag_msg(sp, TAG_BADLNO, tag);
  933.             return (1);
  934.         }
  935.     } else {
  936.         /*
  937.          * Search for the tag; cheap fallback for C functions
  938.          * if the name is the same but the arguments have changed.
  939.          */
  940.         m.lno = 1;
  941.         m.cno = 0;
  942.         if (f_search(sp, &m, &m,
  943.             search, slen, NULL, SEARCH_FILE | SEARCH_TAG))
  944.             if ((p = strrchr(search, '(')) != NULL) {
  945.                 slen = p - search;
  946.                 if (f_search(sp, &m, &m, search, slen,
  947.                     NULL, SEARCH_FILE | SEARCH_TAG))
  948.                     goto notfound;
  949.             } else {
  950. notfound:            tag_msg(sp, TAG_SEARCH, tag);
  951.                 return (1);
  952.             }
  953.         /*
  954.          * !!!
  955.          * Historically, tags set the search direction if it wasn't
  956.          * already set.
  957.          */
  958.         if (sp->searchdir == NOTSET)
  959.             sp->searchdir = FORWARD;
  960.     }
  961.  
  962.     /*
  963.      * !!!
  964.      * Tags move to the first non-blank, NOT the search pattern start.
  965.      */
  966.     sp->lno = m.lno;
  967.     sp->cno = 0;
  968.     (void)nonblank(sp, sp->lno, &sp->cno);
  969.     return (0);
  970. }
  971.  
  972. /*
  973.  * ctag_slist --
  974.  *    Search the list of tags files for a tag, and return tag queue.
  975.  */
  976. static TAGQ *
  977. ctag_slist(sp, tag)
  978.     SCR *sp;
  979.     char *tag;
  980. {
  981.     EX_PRIVATE *exp;
  982.     TAGF *tfp;
  983.     TAGQ *tqp;
  984.     size_t len;
  985.     int echk;
  986.  
  987.     exp = EXP(sp);
  988.  
  989.     /* Allocate and initialize the tag queue structure. */
  990.     len = strlen(tag);
  991.     CALLOC_GOTO(sp, tqp, TAGQ *, 1, sizeof(TAGQ) + len + 1);
  992.     CIRCLEQ_INIT(&tqp->tagq);
  993.     tqp->tag = tqp->buf;
  994.     memcpy(tqp->tag, tag, (tqp->tlen = len) + 1);
  995.  
  996.     /*
  997.      * Find the tag, only display missing file messages once, and
  998.      * then only if we didn't find the tag.
  999.      */
  1000.     for (echk = 0,
  1001.         tfp = exp->tagfq.tqh_first; tfp != NULL; tfp = tfp->q.tqe_next)
  1002.         if (ctag_sfile(sp, tfp, tqp, tag)) {
  1003.             echk = 1;
  1004.             F_SET(tfp, TAGF_ERR);
  1005.         } else
  1006.             F_CLR(tfp, TAGF_ERR | TAGF_ERR_WARN);
  1007.  
  1008.     /* Check to see if we found anything. */
  1009.     if (tqp->tagq.cqh_first == (void *)&tqp->tagq) {
  1010.         msgq_str(sp, M_ERR, tag, "162|%s: tag not found");
  1011.         if (echk)
  1012.             for (tfp = exp->tagfq.tqh_first;
  1013.                 tfp != NULL; tfp = tfp->q.tqe_next)
  1014.                 if (F_ISSET(tfp, TAGF_ERR) &&
  1015.                     !F_ISSET(tfp, TAGF_ERR_WARN)) {
  1016.                     errno = tfp->errnum;
  1017.                     msgq_str(sp, M_SYSERR, tfp->name, "%s");
  1018.                     F_SET(tfp, TAGF_ERR_WARN);
  1019.                 }
  1020.         free(tqp);
  1021.         return (NULL);
  1022.     }
  1023.  
  1024.     tqp->current = tqp->tagq.cqh_first;
  1025.     return (tqp);
  1026.  
  1027. alloc_err:
  1028.     return (NULL);
  1029. }
  1030.  
  1031. /*
  1032.  * ctag_sfile --
  1033.  *    Search a tags file for a tag, adding any found to the tag queue.
  1034.  */
  1035. static int
  1036. ctag_sfile(sp, tfp, tqp, tname)
  1037.     SCR *sp;
  1038.     TAGF *tfp;
  1039.     TAGQ *tqp;
  1040.     char *tname;
  1041. {
  1042.     struct stat sb;
  1043.     TAG *tp;
  1044.     size_t dlen, nlen, slen;
  1045.     int fd, i, nf1, nf2;
  1046.     char *back, *cname, *dname, *front, *map, *name, *p, *search, *t;
  1047.  
  1048.     if ((fd = open(tfp->name, O_RDONLY, 0)) < 0) {
  1049.         tfp->errnum = errno;
  1050.         return (1);
  1051.     }
  1052.  
  1053.     /*
  1054.      * XXX
  1055.      * Some old BSD systems require MAP_FILE as an argument when mapping
  1056.      * regular files.
  1057.      */
  1058. #ifndef MAP_FILE
  1059. #define    MAP_FILE    0
  1060. #endif
  1061.     /*
  1062.      * XXX
  1063.      * We'd like to test if the file is too big to mmap.  Since we don't
  1064.      * know what size or type off_t's or size_t's are, what the largest
  1065.      * unsigned integral type is, or what random insanity the local C
  1066.      * compiler will perpetrate, doing the comparison in a portable way
  1067.      * is flatly impossible.  Hope mmap fails if the file is too large.
  1068.      */
  1069.     if (fstat(fd, &sb) != 0 ||
  1070.         (map = mmap(NULL, (size_t)sb.st_size, PROT_READ | PROT_WRITE,
  1071.         MAP_FILE | MAP_PRIVATE, fd, (off_t)0)) == (caddr_t)-1) {
  1072.         tfp->errnum = errno;
  1073.         (void)close(fd);
  1074.         return (1);
  1075.     }
  1076.  
  1077.     front = map;
  1078.     back = front + sb.st_size;
  1079.     front = binary_search(tname, front, back);
  1080.     front = linear_search(tname, front, back);
  1081.     if (front == NULL)
  1082.         goto done;
  1083.  
  1084.     /*
  1085.      * Initialize and link in the tag structure(s).  The historic ctags
  1086.      * file format only permitted a single tag location per tag.  The
  1087.      * obvious extension to permit multiple tags locations per tag is to
  1088.      * output multiple records in the standard format.  Unfortunately,
  1089.      * this won't work correctly with historic ex/vi implementations,
  1090.      * because their binary search assumes that there's only one record
  1091.      * per tag, and so will use a random tag entry if there si more than
  1092.      * one.  This code handles either format.
  1093.      *
  1094.      * The tags file is in the following format:
  1095.      *
  1096.      *    <tag> <filename> <line number> | <pattern>
  1097.      *
  1098.      * Figure out how long everything is so we can allocate in one swell
  1099.      * foop, but discard anything that looks wrong.
  1100.      */
  1101.     for (;;) {
  1102.         /* Nul-terminate the end of the line. */
  1103.         for (p = front; p < back && *p != '\n'; ++p);
  1104.         if (p == back || *p != '\n')
  1105.             break;
  1106.         *p = '\0';
  1107.  
  1108.         /* Update the pointers for the next time. */
  1109.         t = p + 1;
  1110.         p = front;
  1111.         front = t;
  1112.  
  1113.         /* Break the line into tokens. */
  1114.         for (i = 0; i < 2 && (t = strsep(&p, "\t ")) != NULL; ++i)
  1115.             switch (i) {
  1116.             case 0:            /* Tag. */
  1117.                 cname = t;
  1118.                 break;
  1119.             case 1:            /* Filename. */
  1120.                 name = t;
  1121.                 nlen = strlen(name);
  1122.                 break;
  1123.             }
  1124.  
  1125.         /* Check for corruption. */
  1126.         if (i != 2 || p == NULL || t == NULL)
  1127.             goto corrupt;
  1128.  
  1129.         /* The rest of the string is the search pattern. */
  1130.         search = p;
  1131.         if ((slen = strlen(p)) == 0) {
  1132. corrupt:        p = msg_print(sp, tname, &nf1);
  1133.             t = msg_print(sp, tfp->name, &nf2);
  1134.             msgq(sp, M_ERR, "163|%s: corrupted tag in %s", p, t);
  1135.             if (nf1)
  1136.                 FREE_SPACE(sp, p, 0);
  1137.             if (nf2)
  1138.                 FREE_SPACE(sp, t, 0);
  1139.             continue;
  1140.         }
  1141.  
  1142.         /* Check for passing the last entry. */
  1143.         if (strcmp(tname, cname))
  1144.             break;
  1145.  
  1146.         /* Resolve the file name. */
  1147.         ctag_file(sp, tfp, name, &dname, &dlen);
  1148.  
  1149.         CALLOC_GOTO(sp, tp,
  1150.             TAG *, 1, sizeof(TAG) + dlen + 2 + nlen + 1 + slen + 1);
  1151.         tp->fname = tp->buf;
  1152.         if (dlen != 0) {
  1153.             memcpy(tp->fname, dname, dlen);
  1154.             tp->fname[dlen] = '/';
  1155.             ++dlen;
  1156.         }
  1157.         memcpy(tp->fname + dlen, name, nlen + 1);
  1158.         tp->fnlen = dlen + nlen;
  1159.         tp->search = tp->fname + tp->fnlen + 1;
  1160.         memcpy(tp->search, search, (tp->slen = slen) + 1);
  1161.         CIRCLEQ_INSERT_TAIL(&tqp->tagq, tp, q);
  1162.     }
  1163.  
  1164. alloc_err:
  1165. done:    if (munmap(map, (size_t)sb.st_size))
  1166.         msgq(sp, M_SYSERR, "munmap");
  1167.     if (close(fd))
  1168.         msgq(sp, M_SYSERR, "close");
  1169.     return (0);
  1170. }
  1171.  
  1172. /*
  1173.  * ctag_file --
  1174.  *    Search for the right path to this file.
  1175.  */
  1176. static void
  1177. ctag_file(sp, tfp, name, dirp, dlenp)
  1178.     SCR *sp;
  1179.     TAGF *tfp;
  1180.     char *name, **dirp;
  1181.     size_t *dlenp;
  1182. {
  1183.     struct stat sb;
  1184.     size_t len;
  1185.     char *p, buf[MAXPATHLEN];
  1186.  
  1187.     /*
  1188.      * !!!
  1189.      * If the tag file path is a relative path, see if it exists.  If it
  1190.      * doesn't, look relative to the tags file path.  It's okay for a tag
  1191.      * file to not exist, and historically, vi simply displayed a "new"
  1192.      * file.  However, if the path exists relative to the tag file, it's
  1193.      * pretty clear what's happening, so we may as well get it right.
  1194.      */
  1195.     *dlenp = 0;
  1196.     if (name[0] != '/' &&
  1197.         stat(name, &sb) && (p = strrchr(tfp->name, '/')) != NULL) {
  1198.         *p = '\0';
  1199.         len = snprintf(buf, sizeof(buf), "%s/%s", tfp->name, name);
  1200.         *p = '/';
  1201.         if (stat(buf, &sb) == 0) {
  1202.             *dirp = tfp->name;
  1203.             *dlenp = strlen(*dirp);
  1204.         }
  1205.     }
  1206. }
  1207.  
  1208. /*
  1209.  * Binary search for "string" in memory between "front" and "back".
  1210.  *
  1211.  * This routine is expected to return a pointer to the start of a line at
  1212.  * *or before* the first word matching "string".  Relaxing the constraint
  1213.  * this way simplifies the algorithm.
  1214.  *
  1215.  * Invariants:
  1216.  *     front points to the beginning of a line at or before the first
  1217.  *    matching string.
  1218.  *
  1219.  *     back points to the beginning of a line at or after the first
  1220.  *    matching line.
  1221.  *
  1222.  * Base of the Invariants.
  1223.  *     front = NULL;
  1224.  *    back = EOF;
  1225.  *
  1226.  * Advancing the Invariants:
  1227.  *
  1228.  *     p = first newline after halfway point from front to back.
  1229.  *
  1230.  *     If the string at "p" is not greater than the string to match,
  1231.  *    p is the new front.  Otherwise it is the new back.
  1232.  *
  1233.  * Termination:
  1234.  *
  1235.  *     The definition of the routine allows it return at any point,
  1236.  *    since front is always at or before the line to print.
  1237.  *
  1238.  *     In fact, it returns when the chosen "p" equals "back".  This
  1239.  *    implies that there exists a string is least half as long as
  1240.  *    (back - front), which in turn implies that a linear search will
  1241.  *    be no more expensive than the cost of simply printing a string or two.
  1242.  *
  1243.  *     Trying to continue with binary search at this point would be
  1244.  *    more trouble than it's worth.
  1245.  */
  1246. #define    EQUAL        0
  1247. #define    GREATER        1
  1248. #define    LESS        (-1)
  1249.  
  1250. #define    SKIP_PAST_NEWLINE(p, back)    while (p < back && *p++ != '\n');
  1251.  
  1252. static char *
  1253. binary_search(string, front, back)
  1254.     register char *string, *front, *back;
  1255. {
  1256.     register char *p;
  1257.  
  1258.     p = front + (back - front) / 2;
  1259.     SKIP_PAST_NEWLINE(p, back);
  1260.  
  1261.     while (p != back) {
  1262.         if (compare(string, p, back) == GREATER)
  1263.             front = p;
  1264.         else
  1265.             back = p;
  1266.         p = front + (back - front) / 2;
  1267.         SKIP_PAST_NEWLINE(p, back);
  1268.     }
  1269.     return (front);
  1270. }
  1271.  
  1272. /*
  1273.  * Find the first line that starts with string, linearly searching from front
  1274.  * to back.
  1275.  *
  1276.  * Return NULL for no such line.
  1277.  *
  1278.  * This routine assumes:
  1279.  *
  1280.  *     o front points at the first character in a line.
  1281.  *    o front is before or at the first line to be printed.
  1282.  */
  1283. static char *
  1284. linear_search(string, front, back)
  1285.     char *string, *front, *back;
  1286. {
  1287.     while (front < back) {
  1288.         switch (compare(string, front, back)) {
  1289.         case EQUAL:        /* Found it. */
  1290.             return (front);
  1291.         case LESS:        /* No such string. */
  1292.             return (NULL);
  1293.         case GREATER:        /* Keep going. */
  1294.             break;
  1295.         }
  1296.         SKIP_PAST_NEWLINE(front, back);
  1297.     }
  1298.     return (NULL);
  1299. }
  1300.  
  1301. /*
  1302.  * Return LESS, GREATER, or EQUAL depending on how the string1 compares
  1303.  * with string2 (s1 ??? s2).
  1304.  *
  1305.  *     o Matches up to len(s1) are EQUAL.
  1306.  *    o Matches up to len(s2) are GREATER.
  1307.  *
  1308.  * The string "s1" is null terminated.  The string s2 is '\t', space, (or
  1309.  * "back") terminated.
  1310.  *
  1311.  * !!!
  1312.  * Reasonably modern ctags programs use tabs as separators, not spaces.
  1313.  * However, historic programs did use spaces, and, I got complaints.
  1314.  */
  1315. static int
  1316. compare(s1, s2, back)
  1317.     register char *s1, *s2, *back;
  1318. {
  1319.     for (; *s1 && s2 < back && (*s2 != '\t' && *s2 != ' '); ++s1, ++s2)
  1320.         if (*s1 != *s2)
  1321.             return (*s1 < *s2 ? LESS : GREATER);
  1322.     return (*s1 ? GREATER : s2 < back &&
  1323.         (*s2 != '\t' && *s2 != ' ') ? LESS : EQUAL);
  1324. }
  1325.