[BACK]Return to cscope.c CVS log [TXT][DIR] Up to [local] / src / usr.bin / mg

Annotation of src/usr.bin/mg/cscope.c, Revision 1.20

1.20    ! lum         1: /*     $OpenBSD: cscope.c,v 1.19 2021/02/28 15:30:35 lum Exp $ */
1.2       jasper      2:
1.1       lum         3: /*
1.3       lum         4:  * This file is in the public domain.
1.1       lum         5:  *
1.15      sunil       6:  * Author: Sunil Nimmagadda <sunil@openbsd.org>
1.1       lum         7:  */
                      8:
1.9       bcallah     9: #include <sys/queue.h>
                     10: #include <sys/stat.h>
1.1       lum        11: #include <sys/types.h>
                     12: #include <ctype.h>
1.9       bcallah    13: #include <errno.h>
1.1       lum        14: #include <fcntl.h>
                     15: #include <fnmatch.h>
1.8       guenther   16: #include <limits.h>
1.9       bcallah    17: #include <signal.h>
1.1       lum        18: #include <stdio.h>
                     19: #include <stdlib.h>
                     20: #include <string.h>
1.9       bcallah    21: #include <unistd.h>
1.1       lum        22:
                     23: #include "def.h"
                     24:
                     25: #define CSSYMBOL      0
                     26: #define CSDEFINITION  1
                     27: #define CSCALLEDFUNCS 2
                     28: #define CSCALLERFUNCS 3
                     29: #define CSTEXT        4
                     30: #define CSEGREP       6
                     31: #define CSFINDFILE    7
                     32: #define CSINCLUDES    8
                     33:
                     34: struct cstokens {
                     35:        const char *fname;
                     36:        const char *function;
                     37:        const char *lineno;
                     38:        const char *pattern;
                     39: };
                     40:
                     41: struct csmatch {
                     42:        TAILQ_ENTRY(csmatch) entry;
                     43:        int lineno;
                     44: };
                     45:
                     46: struct csrecord {
                     47:        TAILQ_ENTRY(csrecord) entry;
                     48:        char *filename;
                     49:        TAILQ_HEAD(matches, csmatch) matches;
                     50: };
                     51:
                     52: static TAILQ_HEAD(csrecords, csrecord) csrecords = TAILQ_HEAD_INITIALIZER(csrecords);
                     53: static struct csrecord *addentryr;
                     54: static struct csrecord *currecord;
                     55: static struct csmatch  *curmatch;
                     56: static const char      *addentryfn;
                     57: static const char      *csprompt[] = {
                     58:        "Find this symbol: ",
                     59:        "Find this global definition: ",
                     60:        "Find functions called by this function: ",
                     61:        "Find functions calling this function: ",
                     62:        "Find this text string: ",
                     63:        "Change this text string: ",
                     64:        "Find this egrep pattern: ",
                     65:        "Find this file: ",
                     66:        "Find files #including this file: "
                     67: };
                     68:
                     69: static int  addentry(struct buffer *, char *);
                     70: static void csflush(void);
                     71: static int  do_cscope(int);
                     72: static int  csexists(const char *);
                     73: static int  getattr(char *, struct cstokens *);
                     74: static int  jumptomatch(void);
                     75: static void prettyprint(struct buffer *, struct cstokens *);
                     76: static const char *ltrim(const char *);
                     77:
                     78: /*
                     79:  * Find this symbol. Bound to C-c s s
                     80:  */
                     81: /* ARGSUSED */
                     82: int
                     83: cssymbol(int f, int n)
                     84: {
                     85:        return (do_cscope(CSSYMBOL));
                     86: }
                     87:
                     88: /*
                     89:  * Find this global definition. Bound to C-c s d
                     90:  */
                     91: /* ARGSUSED */int
                     92: csdefinition(int f, int n)
                     93: {
                     94:        return (do_cscope(CSDEFINITION));
                     95: }
                     96:
                     97: /*
                     98:  * Find functions called by this function. Bound to C-c s l
                     99:  */
                    100: /* ARGSUSED */
                    101: int
                    102: csfuncalled(int f, int n)
                    103: {
                    104:        return (do_cscope(CSCALLEDFUNCS));
                    105: }
                    106:
                    107: /*
                    108:  * Find functions calling this function. Bound to C-c s c
                    109:  */
                    110: /* ARGSUSED */
                    111: int
                    112: cscallerfuncs(int f, int n)
                    113: {
                    114:        return (do_cscope(CSCALLERFUNCS));
                    115: }
                    116:
                    117: /*
                    118:  * Find this text. Bound to C-c s t
                    119:  */
                    120: /* ARGSUSED */
                    121: int
                    122: csfindtext(int f, int n)
                    123: {
                    124:        return (do_cscope(CSTEXT));
                    125: }
                    126:
                    127: /*
                    128:  * Find this egrep pattern. Bound to C-c s e
                    129:  */
                    130: /* ARGSUSED */
                    131: int
                    132: csegrep(int f, int n)
                    133: {
                    134:        return (do_cscope(CSEGREP));
                    135: }
                    136:
                    137: /*
                    138:  * Find this file. Bound to C-c s f
                    139:  */
                    140: /* ARGSUSED */
                    141: int
                    142: csfindfile(int f, int n)
                    143: {
                    144:        return (do_cscope(CSFINDFILE));
                    145: }
                    146:
                    147: /*
                    148:  * Find files #including this file. Bound to C-c s i
                    149:  */
                    150: /* ARGSUSED */
                    151: int
                    152: csfindinc(int f, int n)
                    153: {
                    154:        return (do_cscope(CSINCLUDES));
                    155: }
                    156:
                    157: /*
                    158:  * Create list of files to index in the given directory
                    159:  * using cscope-indexer.
                    160:  */
                    161: /* ARGSUSED */
                    162: int
                    163: cscreatelist(int f, int n)
                    164: {
                    165:        struct buffer *bp;
                    166:        struct stat sb;
                    167:        FILE *fpipe;
                    168:        char dir[NFILEN], cmd[BUFSIZ], title[BUFSIZ], *line, *bufp;
1.17      florian   169:        size_t sz;
                    170:        ssize_t len;
1.1       lum       171:        int clen;
1.10      jasper    172:
1.17      florian   173:        line = NULL;
                    174:        sz = 0;
                    175:
1.1       lum       176:        if (getbufcwd(dir, sizeof(dir)) == FALSE)
                    177:                dir[0] = '\0';
1.10      jasper    178:
                    179:        bufp = eread("Index files in directory: ", dir,
1.1       lum       180:            sizeof(dir), EFCR | EFDEF | EFNEW | EFNUL);
1.10      jasper    181:
1.1       lum       182:        if (bufp == NULL)
                    183:                return (ABORT);
                    184:        else if (bufp[0] == '\0')
                    185:                return (FALSE);
1.10      jasper    186:
1.19      lum       187:        if (stat(dir, &sb) == -1)
                    188:                return(dobeep_msgs("stat: %s", strerror(errno)));
                    189:        else if (S_ISDIR(sb.st_mode) == 0)
                    190:                return(dobeep_msgs("%s: Not a directory", dir));
1.10      jasper    191:
1.19      lum       192:        if (csexists("cscope-indexer") == FALSE)
                    193:                return(dobeep_msg("no such file or directory, cscope-indexer"));
1.10      jasper    194:
1.1       lum       195:        clen = snprintf(cmd, sizeof(cmd), "cscope-indexer -v %s", dir);
                    196:        if (clen < 0 || clen >= sizeof(cmd))
                    197:                return (FALSE);
                    198:
1.19      lum       199:        if ((fpipe = popen(cmd, "r")) == NULL)
                    200:                return(dobeep_msg("problem opening pipe"));
1.10      jasper    201:
1.1       lum       202:        bp = bfind("*cscope*", TRUE);
1.4       jsg       203:        if (bclear(bp) != TRUE) {
                    204:                pclose(fpipe);
1.1       lum       205:                return (FALSE);
1.4       jsg       206:        }
1.1       lum       207:        bp->b_flag |= BFREADONLY;
                    208:
                    209:        clen = snprintf(title, sizeof(title), "%s%s",
                    210:            "Creating cscope file list 'cscope.files' in: ", dir);
1.6       jsg       211:        if (clen < 0 || clen >= sizeof(title)) {
                    212:                pclose(fpipe);
1.1       lum       213:                return (FALSE);
1.6       jsg       214:        }
1.1       lum       215:        addline(bp, title);
                    216:        addline(bp, "");
1.17      florian   217:        while ((len = getline(&line, &sz, fpipe)) != -1) {
1.20    ! lum       218:                if (line[len - 1] == *bp->b_nlchr)
1.17      florian   219:                        line[len - 1] = '\0';
1.1       lum       220:                addline(bp, line);
                    221:        }
1.17      florian   222:        free(line);
                    223:        if (ferror(fpipe))
                    224:                ewprintf("Problem reading pipe");
1.1       lum       225:        pclose(fpipe);
1.10      jasper    226:        return (popbuftop(bp, WNONE));
1.1       lum       227: }
                    228:
                    229: /*
                    230:  * Next Symbol. Bound to C-c s n
                    231:  */
                    232: /* ARGSUSED */
                    233: int
                    234: csnextmatch(int f, int n)
                    235: {
                    236:        struct csrecord *r;
                    237:        struct csmatch *m;
1.10      jasper    238:
1.1       lum       239:        if (curmatch == NULL) {
1.19      lum       240:                if ((r = TAILQ_FIRST(&csrecords)) == NULL)
                    241:                        return(dobeep_msg("The *cscope* buffer does "
                    242:                            "not exist yet"));
                    243:
1.1       lum       244:                currecord = r;
                    245:                curmatch = TAILQ_FIRST(&r->matches);
                    246:        } else {
                    247:                m = TAILQ_NEXT(curmatch, entry);
                    248:                if (m == NULL) {
                    249:                        r = TAILQ_NEXT(currecord, entry);
                    250:                        if (r == NULL) {
1.19      lum       251:                                return(dobeep_msg("The end of *cscope* buffer "
                    252:                                    "has been reached"));
1.1       lum       253:                        } else {
                    254:                                currecord = r;
                    255:                                curmatch = TAILQ_FIRST(&currecord->matches);
                    256:                        }
                    257:                } else
                    258:                        curmatch = m;
                    259:        }
                    260:        return (jumptomatch());
                    261: }
                    262:
                    263: /*
                    264:  * Previous Symbol. Bound to C-c s p
                    265:  */
                    266: /* ARGSUSED */
                    267: int
                    268: csprevmatch(int f, int n)
                    269: {
                    270:        struct csmatch *m;
                    271:        struct csrecord *r;
                    272:
                    273:        if (curmatch == NULL)
                    274:                return (FALSE);
                    275:        else {
                    276:                m  = TAILQ_PREV(curmatch, matches, entry);
                    277:                if (m)
                    278:                        curmatch = m;
                    279:                else {
                    280:                        r = TAILQ_PREV(currecord, csrecords, entry);
                    281:                        if (r == NULL) {
1.19      lum       282:                                return(dobeep_msg("The beginning of *cscope* "
                    283:                                    "buffer has been reached"));
1.1       lum       284:                        } else {
                    285:                                currecord = r;
                    286:                                curmatch = TAILQ_LAST(&currecord->matches,
                    287:                                    matches);
                    288:                        }
                    289:                }
                    290:        }
                    291:        return (jumptomatch());
                    292: }
                    293:
                    294: /*
                    295:  * Next file.
                    296:  */
                    297: int
                    298: csnextfile(int f, int n)
                    299: {
                    300:        struct csrecord *r;
1.10      jasper    301:
1.1       lum       302:        if (curmatch == NULL) {
1.19      lum       303:                if ((r = TAILQ_FIRST(&csrecords)) == NULL)
                    304:                        return(dobeep_msg("The *cscope* buffer does not "
                    305:                            "exist yet"));
1.1       lum       306:        } else {
1.19      lum       307:                if ((r = TAILQ_NEXT(currecord, entry)) == NULL)
                    308:                        return(dobeep_msg("The end of *cscope* buffer has "
                    309:                            "been reached"));
1.1       lum       310:        }
                    311:        currecord = r;
                    312:        curmatch = TAILQ_FIRST(&currecord->matches);
1.10      jasper    313:        return (jumptomatch());
1.1       lum       314: }
                    315:
                    316: /*
                    317:  * Previous file.
                    318:  */
                    319: int
                    320: csprevfile(int f, int n)
                    321: {
                    322:        struct csrecord *r;
1.10      jasper    323:
1.1       lum       324:        if (curmatch == NULL) {
1.19      lum       325:                if ((r = TAILQ_FIRST(&csrecords)) == NULL)
                    326:                        return(dobeep_msg("The *cscope* buffer does not"
                    327:                            "exist yet"));
1.1       lum       328:        } else {
1.19      lum       329:                if ((r = TAILQ_PREV(currecord, csrecords, entry)) == NULL)
                    330:                        return(dobeep_msg("The beginning of *cscope* buffer "
                    331:                            "has been reached"));
1.1       lum       332:        }
                    333:        currecord = r;
                    334:        curmatch = TAILQ_FIRST(&currecord->matches);
1.10      jasper    335:        return (jumptomatch());
1.1       lum       336: }
                    337:
                    338: /*
1.10      jasper    339:  * The current symbol location is extracted from currecord->filename and
                    340:  * curmatch->lineno. Load the file similar to filevisit and goto the
1.1       lum       341:  * lineno recorded.
                    342:  */
                    343: int
                    344: jumptomatch(void)
                    345: {
                    346:        struct buffer *bp;
                    347:        char *adjf;
1.10      jasper    348:
1.1       lum       349:        if (curmatch == NULL || currecord == NULL)
                    350:                return (FALSE);
                    351:        adjf = adjustname(currecord->filename, TRUE);
                    352:        if (adjf == NULL)
                    353:                return (FALSE);
                    354:        if ((bp = findbuffer(adjf)) == NULL)
                    355:                return (FALSE);
                    356:        curbp = bp;
                    357:        if (showbuffer(bp, curwp, WFFULL) != TRUE)
                    358:                return (FALSE);
                    359:        if (bp->b_fname[0] == '\0') {
                    360:                if (readin(adjf) != TRUE)
                    361:                        killbuffer(bp);
                    362:        }
                    363:        gotoline(FFARG, curmatch->lineno);
                    364:        return (TRUE);
                    365: }
                    366:
                    367: /*
                    368:  * Ask for the symbol, construct cscope commandline with the symbol
1.10      jasper    369:  * and passed in index. Popen cscope, read the output into *cscope*
1.1       lum       370:  * buffer and pop it.
                    371:  */
                    372: int
                    373: do_cscope(int i)
                    374: {
                    375:        struct buffer *bp;
                    376:        FILE *fpipe;
                    377:        char pattern[MAX_TOKEN], cmd[BUFSIZ], title[BUFSIZ];
                    378:        char *p, *buf;
                    379:        int clen, nores = 0;
1.17      florian   380:        size_t sz;
                    381:        ssize_t len;
                    382:
                    383:        buf = NULL;
                    384:        sz = 0;
1.1       lum       385:
                    386:        /* If current buffer isn't a source file just return */
1.19      lum       387:        if (fnmatch("*.[chy]", curbp->b_fname, 0) != 0)
                    388:                return(dobeep_msg("C-c s not defined"));
1.10      jasper    389:
1.1       lum       390:        if (curtoken(0, 1, pattern) == FALSE)
1.10      jasper    391:                return (FALSE);
1.11      guenther  392:        p = eread("%s", pattern, MAX_TOKEN, EFNEW | EFCR | EFDEF, csprompt[i]);
1.1       lum       393:        if (p == NULL)
                    394:                return (ABORT);
                    395:        else if (p[0] == '\0')
                    396:                return (FALSE);
                    397:
1.19      lum       398:        if (csexists("cscope") == FALSE)
                    399:                return(dobeep_msg("no such file or directory, cscope"));
1.10      jasper    400:
1.1       lum       401:        csflush();
                    402:        clen = snprintf(cmd, sizeof(cmd), "cscope -L -%d %s 2>/dev/null",
                    403:            i, pattern);
                    404:        if (clen < 0 || clen >= sizeof(cmd))
                    405:                return (FALSE);
                    406:
1.19      lum       407:        if ((fpipe = popen(cmd, "r")) == NULL)
                    408:                return(dobeep_msg("problem opening pipe"));
1.10      jasper    409:
1.1       lum       410:        bp = bfind("*cscope*", TRUE);
1.4       jsg       411:        if (bclear(bp) != TRUE) {
                    412:                pclose(fpipe);
1.1       lum       413:                return (FALSE);
1.4       jsg       414:        }
1.1       lum       415:        bp->b_flag |= BFREADONLY;
                    416:
                    417:        clen = snprintf(title, sizeof(title), "%s%s", csprompt[i], pattern);
1.6       jsg       418:        if (clen < 0 || clen >= sizeof(title)) {
                    419:                pclose(fpipe);
1.1       lum       420:                return (FALSE);
1.6       jsg       421:        }
1.1       lum       422:        addline(bp, title);
                    423:        addline(bp, "");
                    424:        addline(bp, "-------------------------------------------------------------------------------");
1.17      florian   425:        while ((len = getline(&buf, &sz, fpipe)) != -1) {
1.20    ! lum       426:                if (buf[len - 1] == *bp->b_nlchr)
1.17      florian   427:                        buf[len - 1] = '\0';
                    428:                if (addentry(bp, buf) != TRUE) {
                    429:                        free(buf);
1.1       lum       430:                        return (FALSE);
1.17      florian   431:                }
1.1       lum       432:                nores = 1;
1.17      florian   433:        }
                    434:        free(buf);
                    435:        if (ferror(fpipe))
                    436:                ewprintf("Problem reading pipe");
1.1       lum       437:        pclose(fpipe);
                    438:        addline(bp, "-------------------------------------------------------------------------------");
                    439:        if (nores == 0)
                    440:                ewprintf("No matches were found.");
                    441:        return (popbuftop(bp, WNONE));
                    442: }
                    443:
                    444: /*
                    445:  * For each line read from cscope output, extract the tokens,
                    446:  * add them to list and pretty print a line in *cscope* buffer.
                    447:  */
                    448: int
                    449: addentry(struct buffer *bp, char *csline)
                    450: {
                    451:        struct csrecord *r;
                    452:        struct csmatch *m;
                    453:        struct cstokens t;
                    454:        int lineno;
                    455:        char buf[BUFSIZ];
                    456:        const char *errstr;
                    457:
                    458:        r = NULL;
                    459:        if (getattr(csline, &t) == FALSE)
                    460:                return (FALSE);
                    461:
                    462:        lineno = strtonum(t.lineno, INT_MIN, INT_MAX, &errstr);
                    463:        if (errstr)
                    464:                return (FALSE);
1.10      jasper    465:
1.1       lum       466:        if (addentryfn == NULL || strcmp(addentryfn, t.fname) != 0) {
                    467:                if ((r = malloc(sizeof(struct csrecord))) == NULL)
                    468:                        return (FALSE);
                    469:                addentryr = r;
                    470:                if ((r->filename = strndup(t.fname, NFILEN)) == NULL)
                    471:                        goto cleanup;
                    472:                addentryfn = r->filename;
                    473:                TAILQ_INIT(&r->matches);
                    474:                if ((m = malloc(sizeof(struct csmatch))) == NULL)
                    475:                        goto cleanup;
                    476:                m->lineno = lineno;
                    477:                TAILQ_INSERT_TAIL(&r->matches, m, entry);
                    478:                TAILQ_INSERT_TAIL(&csrecords, r, entry);
                    479:                addline(bp, "");
                    480:                if (snprintf(buf, sizeof(buf), "*** %s", t.fname) < 0)
                    481:                        goto cleanup;
                    482:                addline(bp, buf);
                    483:        } else {
                    484:                if ((m = malloc(sizeof(struct csmatch))) == NULL)
                    485:                        goto cleanup;
                    486:                m->lineno = lineno;
                    487:                TAILQ_INSERT_TAIL(&addentryr->matches, m, entry);
                    488:        }
                    489:        prettyprint(bp, &t);
                    490:        return (TRUE);
                    491: cleanup:
                    492:        free(r);
                    493:        return (FALSE);
                    494: }
                    495:
                    496: /*
                    497:  * Cscope line: <filename> <function> <lineno> <pattern>
                    498:  */
                    499: int
                    500: getattr(char *line, struct cstokens *t)
                    501: {
                    502:        char *p;
                    503:
                    504:        if ((p = strchr(line, ' ')) == NULL)
                    505:                return (FALSE);
                    506:        *p++ = '\0';
                    507:        t->fname = line;
                    508:        line = p;
                    509:
                    510:        if ((p = strchr(line, ' ')) == NULL)
                    511:                return (FALSE);
                    512:        *p++ = '\0';
                    513:        t->function = line;
                    514:        line = p;
                    515:
                    516:        if ((p = strchr(line, ' ')) == NULL)
                    517:                return (FALSE);
                    518:        *p++ = '\0';
                    519:        t->lineno = line;
                    520:
                    521:        if (*p == '\0')
                    522:                return (FALSE);
                    523:        t->pattern = p;
                    524:
                    525:        return (TRUE);
                    526: }
                    527:
                    528: void
                    529: prettyprint(struct buffer *bp, struct cstokens *t)
                    530: {
                    531:        char buf[BUFSIZ];
                    532:
                    533:        if (snprintf(buf, sizeof(buf), "%s[%s]\t\t%s",
                    534:            t->function, t->lineno, ltrim(t->pattern)) < 0)
                    535:                return;
                    536:        addline(bp, buf);
                    537: }
                    538:
                    539: const char *
                    540: ltrim(const char *s)
                    541: {
1.7       guenther  542:        while (isblank((unsigned char)*s))
1.1       lum       543:                s++;
                    544:        return s;
                    545: }
                    546:
                    547: void
                    548: csflush(void)
                    549: {
                    550:        struct csrecord *r;
                    551:        struct csmatch *m;
1.10      jasper    552:
1.1       lum       553:        while ((r = TAILQ_FIRST(&csrecords)) != NULL) {
                    554:                free(r->filename);
                    555:                while ((m = TAILQ_FIRST(&r->matches)) != NULL) {
                    556:                        TAILQ_REMOVE(&r->matches, m, entry);
                    557:                        free(m);
                    558:                }
                    559:                TAILQ_REMOVE(&csrecords, r, entry);
                    560:                free(r);
                    561:        }
                    562:        addentryr = NULL;
                    563:        addentryfn = NULL;
                    564:        currecord = NULL;
                    565:        curmatch = NULL;
                    566: }
                    567:
                    568: /*
                    569:  * Check if the cmd exists in $PATH. Split on ":" and iterate through
                    570:  * all paths in $PATH.
                    571:  */
                    572: int
                    573: csexists(const char *cmd)
                    574: {
1.14      sunil     575:        char fname[NFILEN], *dir, *path, *pathc, *tmp;
                    576:        int  len, dlen;
1.1       lum       577:
1.14      sunil     578:        /* Special case if prog contains '/' */
                    579:        if (strchr(cmd, '/')) {
                    580:                if (access(cmd, F_OK) == -1)
                    581:                        return (FALSE);
                    582:                else
                    583:                        return (TRUE);
                    584:        }
                    585:        if ((tmp = getenv("PATH")) == NULL)
                    586:                return (FALSE);
1.19      lum       587:        if ((pathc = path = strndup(tmp, NFILEN)) == NULL)
                    588:                return(dobeep_msg("out of memory"));
                    589:
1.14      sunil     590:        while ((dir = strsep(&path, ":")) != NULL) {
                    591:                if (*dir == '\0')
1.12      sunil     592:                        continue;
1.1       lum       593:
1.14      sunil     594:                dlen = strlen(dir);
1.16      sunil     595:                while (dlen > 0 && dir[dlen-1] == '/')
1.14      sunil     596:                        dir[--dlen] = '\0';     /* strip trailing '/' */
                    597:
                    598:                len = snprintf(fname, sizeof(fname), "%s/%s", dir, cmd);
1.18      deraadt   599:                if (len < 0 || len >= sizeof(fname)) {
1.19      lum       600:                        (void)dobeep_msg("path too long");
1.14      sunil     601:                        goto cleanup;
                    602:                }
                    603:                if(access(fname, F_OK) == 0) {
                    604:                        free(pathc);
                    605:                        return (TRUE);
                    606:                }
                    607:        }
1.1       lum       608: cleanup:
                    609:        free(pathc);
                    610:        return (FALSE);
                    611: }