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