[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.22

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