GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.bin/mg/cscope.c Lines: 0 260 0.0 %
Date: 2016-12-06 Branches: 0 144 0.0 %

Line Branch Exec Source
1
/*	$OpenBSD: cscope.c,v 1.16 2016/01/19 14:51:00 sunil Exp $	*/
2
3
/*
4
 * This file is in the public domain.
5
 *
6
 * Author: Sunil Nimmagadda <sunil@openbsd.org>
7
 */
8
9
#include <sys/queue.h>
10
#include <sys/stat.h>
11
#include <sys/types.h>
12
#include <ctype.h>
13
#include <errno.h>
14
#include <fcntl.h>
15
#include <fnmatch.h>
16
#include <limits.h>
17
#include <signal.h>
18
#include <stdio.h>
19
#include <stdlib.h>
20
#include <string.h>
21
#include <unistd.h>
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;
169
	size_t len;
170
	int clen;
171
172
	if (getbufcwd(dir, sizeof(dir)) == FALSE)
173
		dir[0] = '\0';
174
175
	bufp = eread("Index files in directory: ", dir,
176
	    sizeof(dir), EFCR | EFDEF | EFNEW | EFNUL);
177
178
	if (bufp == NULL)
179
		return (ABORT);
180
	else if (bufp[0] == '\0')
181
		return (FALSE);
182
183
	if (stat(dir, &sb) == -1) {
184
		dobeep();
185
		ewprintf("stat: %s", strerror(errno));
186
		return (FALSE);
187
	} else if (S_ISDIR(sb.st_mode) == 0) {
188
		dobeep();
189
		ewprintf("%s: Not a directory", dir);
190
		return (FALSE);
191
	}
192
193
	if (csexists("cscope-indexer") == FALSE) {
194
		dobeep();
195
		ewprintf("no such file or directory, cscope-indexer");
196
		return (FALSE);
197
	}
198
199
	clen = snprintf(cmd, sizeof(cmd), "cscope-indexer -v %s", dir);
200
	if (clen < 0 || clen >= sizeof(cmd))
201
		return (FALSE);
202
203
	if ((fpipe = popen(cmd, "r")) == NULL) {
204
		dobeep();
205
		ewprintf("problem opening pipe");
206
		return (FALSE);
207
	}
208
209
	bp = bfind("*cscope*", TRUE);
210
	if (bclear(bp) != TRUE) {
211
		pclose(fpipe);
212
		return (FALSE);
213
	}
214
	bp->b_flag |= BFREADONLY;
215
216
	clen = snprintf(title, sizeof(title), "%s%s",
217
	    "Creating cscope file list 'cscope.files' in: ", dir);
218
	if (clen < 0 || clen >= sizeof(title)) {
219
		pclose(fpipe);
220
		return (FALSE);
221
	}
222
	addline(bp, title);
223
	addline(bp, "");
224
	/* All lines are NUL terminated */
225
	while ((line = fgetln(fpipe, &len)) != NULL) {
226
		line[len - 1] = '\0';
227
		addline(bp, line);
228
	}
229
	pclose(fpipe);
230
	return (popbuftop(bp, WNONE));
231
}
232
233
/*
234
 * Next Symbol. Bound to C-c s n
235
 */
236
/* ARGSUSED */
237
int
238
csnextmatch(int f, int n)
239
{
240
	struct csrecord *r;
241
	struct csmatch *m;
242
243
	if (curmatch == NULL) {
244
		if ((r = TAILQ_FIRST(&csrecords)) == NULL) {
245
			dobeep();
246
			ewprintf("The *cscope* buffer does not exist yet");
247
			return (FALSE);
248
		}
249
		currecord = r;
250
		curmatch = TAILQ_FIRST(&r->matches);
251
	} else {
252
		m = TAILQ_NEXT(curmatch, entry);
253
		if (m == NULL) {
254
			r = TAILQ_NEXT(currecord, entry);
255
			if (r == NULL) {
256
				dobeep();
257
				ewprintf("The end of *cscope* buffer has been"
258
				    " reached");
259
				return (FALSE);
260
			} else {
261
				currecord = r;
262
				curmatch = TAILQ_FIRST(&currecord->matches);
263
			}
264
		} else
265
			curmatch = m;
266
	}
267
	return (jumptomatch());
268
}
269
270
/*
271
 * Previous Symbol. Bound to C-c s p
272
 */
273
/* ARGSUSED */
274
int
275
csprevmatch(int f, int n)
276
{
277
	struct csmatch *m;
278
	struct csrecord *r;
279
280
	if (curmatch == NULL)
281
		return (FALSE);
282
	else {
283
		m  = TAILQ_PREV(curmatch, matches, entry);
284
		if (m)
285
			curmatch = m;
286
		else {
287
			r = TAILQ_PREV(currecord, csrecords, entry);
288
			if (r == NULL) {
289
				dobeep();
290
				ewprintf("The beginning of *cscope* buffer has"
291
				    " been reached");
292
				return (FALSE);
293
			} else {
294
				currecord = r;
295
				curmatch = TAILQ_LAST(&currecord->matches,
296
				    matches);
297
			}
298
		}
299
	}
300
	return (jumptomatch());
301
}
302
303
/*
304
 * Next file.
305
 */
306
int
307
csnextfile(int f, int n)
308
{
309
	struct csrecord *r;
310
311
	if (curmatch == NULL) {
312
		if ((r = TAILQ_FIRST(&csrecords)) == NULL) {
313
			dobeep();
314
			ewprintf("The *cscope* buffer does not exist yet");
315
			return (FALSE);
316
		}
317
318
	} else {
319
		if ((r = TAILQ_NEXT(currecord, entry)) == NULL) {
320
			dobeep();
321
			ewprintf("The end of *cscope* buffer has been reached");
322
			return (FALSE);
323
		}
324
	}
325
	currecord = r;
326
	curmatch = TAILQ_FIRST(&currecord->matches);
327
	return (jumptomatch());
328
}
329
330
/*
331
 * Previous file.
332
 */
333
int
334
csprevfile(int f, int n)
335
{
336
	struct csrecord *r;
337
338
	if (curmatch == NULL) {
339
		if ((r = TAILQ_FIRST(&csrecords)) == NULL) {
340
			dobeep();
341
			ewprintf("The *cscope* buffer does not exist yet");
342
			return (FALSE);
343
		}
344
345
	} else {
346
		if ((r = TAILQ_PREV(currecord, csrecords, entry)) == NULL) {
347
			dobeep();
348
			ewprintf("The beginning of *cscope* buffer has been"
349
			    " reached");
350
			return (FALSE);
351
		}
352
	}
353
	currecord = r;
354
	curmatch = TAILQ_FIRST(&currecord->matches);
355
	return (jumptomatch());
356
}
357
358
/*
359
 * The current symbol location is extracted from currecord->filename and
360
 * curmatch->lineno. Load the file similar to filevisit and goto the
361
 * lineno recorded.
362
 */
363
int
364
jumptomatch(void)
365
{
366
	struct buffer *bp;
367
	char *adjf;
368
369
	if (curmatch == NULL || currecord == NULL)
370
		return (FALSE);
371
	adjf = adjustname(currecord->filename, TRUE);
372
	if (adjf == NULL)
373
		return (FALSE);
374
	if ((bp = findbuffer(adjf)) == NULL)
375
		return (FALSE);
376
	curbp = bp;
377
	if (showbuffer(bp, curwp, WFFULL) != TRUE)
378
		return (FALSE);
379
	if (bp->b_fname[0] == '\0') {
380
		if (readin(adjf) != TRUE)
381
			killbuffer(bp);
382
	}
383
	gotoline(FFARG, curmatch->lineno);
384
	return (TRUE);
385
}
386
387
/*
388
 * Ask for the symbol, construct cscope commandline with the symbol
389
 * and passed in index. Popen cscope, read the output into *cscope*
390
 * buffer and pop it.
391
 */
392
int
393
do_cscope(int i)
394
{
395
	struct buffer *bp;
396
	FILE *fpipe;
397
	char pattern[MAX_TOKEN], cmd[BUFSIZ], title[BUFSIZ];
398
	char *p, *buf;
399
	int clen, nores = 0;
400
	size_t len;
401
402
	/* If current buffer isn't a source file just return */
403
	if (fnmatch("*.[chy]", curbp->b_fname, 0) != 0) {
404
		dobeep();
405
		ewprintf("C-c s not defined");
406
		return (FALSE);
407
	}
408
409
	if (curtoken(0, 1, pattern) == FALSE)
410
		return (FALSE);
411
	p = eread("%s", pattern, MAX_TOKEN, EFNEW | EFCR | EFDEF, csprompt[i]);
412
	if (p == NULL)
413
		return (ABORT);
414
	else if (p[0] == '\0')
415
		return (FALSE);
416
417
	if (csexists("cscope") == FALSE) {
418
		dobeep();
419
		ewprintf("no such file or directory, cscope");
420
		return (FALSE);
421
	}
422
423
	csflush();
424
	clen = snprintf(cmd, sizeof(cmd), "cscope -L -%d %s 2>/dev/null",
425
	    i, pattern);
426
	if (clen < 0 || clen >= sizeof(cmd))
427
		return (FALSE);
428
429
	if ((fpipe = popen(cmd, "r")) == NULL) {
430
		dobeep();
431
		ewprintf("problem opening pipe");
432
		return (FALSE);
433
	}
434
435
	bp = bfind("*cscope*", TRUE);
436
	if (bclear(bp) != TRUE) {
437
		pclose(fpipe);
438
		return (FALSE);
439
	}
440
	bp->b_flag |= BFREADONLY;
441
442
	clen = snprintf(title, sizeof(title), "%s%s", csprompt[i], pattern);
443
	if (clen < 0 || clen >= sizeof(title)) {
444
		pclose(fpipe);
445
		return (FALSE);
446
	}
447
	addline(bp, title);
448
	addline(bp, "");
449
	addline(bp, "-------------------------------------------------------------------------------");
450
	/* All lines are NUL terminated */
451
	while ((buf = fgetln(fpipe, &len)) != NULL) {
452
		buf[len - 1] = '\0';
453
		if (addentry(bp, buf) != TRUE)
454
			return (FALSE);
455
		nores = 1;
456
	};
457
	pclose(fpipe);
458
	addline(bp, "-------------------------------------------------------------------------------");
459
	if (nores == 0)
460
		ewprintf("No matches were found.");
461
	return (popbuftop(bp, WNONE));
462
}
463
464
/*
465
 * For each line read from cscope output, extract the tokens,
466
 * add them to list and pretty print a line in *cscope* buffer.
467
 */
468
int
469
addentry(struct buffer *bp, char *csline)
470
{
471
	struct csrecord *r;
472
	struct csmatch *m;
473
	struct cstokens t;
474
	int lineno;
475
	char buf[BUFSIZ];
476
	const char *errstr;
477
478
	r = NULL;
479
	if (getattr(csline, &t) == FALSE)
480
		return (FALSE);
481
482
	lineno = strtonum(t.lineno, INT_MIN, INT_MAX, &errstr);
483
	if (errstr)
484
		return (FALSE);
485
486
	if (addentryfn == NULL || strcmp(addentryfn, t.fname) != 0) {
487
		if ((r = malloc(sizeof(struct csrecord))) == NULL)
488
			return (FALSE);
489
		addentryr = r;
490
		if ((r->filename = strndup(t.fname, NFILEN)) == NULL)
491
			goto cleanup;
492
		addentryfn = r->filename;
493
		TAILQ_INIT(&r->matches);
494
		if ((m = malloc(sizeof(struct csmatch))) == NULL)
495
			goto cleanup;
496
		m->lineno = lineno;
497
		TAILQ_INSERT_TAIL(&r->matches, m, entry);
498
		TAILQ_INSERT_TAIL(&csrecords, r, entry);
499
		addline(bp, "");
500
		if (snprintf(buf, sizeof(buf), "*** %s", t.fname) < 0)
501
			goto cleanup;
502
		addline(bp, buf);
503
	} else {
504
		if ((m = malloc(sizeof(struct csmatch))) == NULL)
505
			goto cleanup;
506
		m->lineno = lineno;
507
		TAILQ_INSERT_TAIL(&addentryr->matches, m, entry);
508
	}
509
	prettyprint(bp, &t);
510
	return (TRUE);
511
cleanup:
512
	free(r);
513
	return (FALSE);
514
}
515
516
/*
517
 * Cscope line: <filename> <function> <lineno> <pattern>
518
 */
519
int
520
getattr(char *line, struct cstokens *t)
521
{
522
	char *p;
523
524
	if ((p = strchr(line, ' ')) == NULL)
525
		return (FALSE);
526
	*p++ = '\0';
527
	t->fname = line;
528
	line = p;
529
530
	if ((p = strchr(line, ' ')) == NULL)
531
		return (FALSE);
532
	*p++ = '\0';
533
	t->function = line;
534
	line = p;
535
536
	if ((p = strchr(line, ' ')) == NULL)
537
		return (FALSE);
538
	*p++ = '\0';
539
	t->lineno = line;
540
541
	if (*p == '\0')
542
		return (FALSE);
543
	t->pattern = p;
544
545
	return (TRUE);
546
}
547
548
void
549
prettyprint(struct buffer *bp, struct cstokens *t)
550
{
551
	char buf[BUFSIZ];
552
553
	if (snprintf(buf, sizeof(buf), "%s[%s]\t\t%s",
554
	    t->function, t->lineno, ltrim(t->pattern)) < 0)
555
		return;
556
	addline(bp, buf);
557
}
558
559
const char *
560
ltrim(const char *s)
561
{
562
	while (isblank((unsigned char)*s))
563
		s++;
564
	return s;
565
}
566
567
void
568
csflush(void)
569
{
570
	struct csrecord *r;
571
	struct csmatch *m;
572
573
	while ((r = TAILQ_FIRST(&csrecords)) != NULL) {
574
		free(r->filename);
575
		while ((m = TAILQ_FIRST(&r->matches)) != NULL) {
576
			TAILQ_REMOVE(&r->matches, m, entry);
577
			free(m);
578
		}
579
		TAILQ_REMOVE(&csrecords, r, entry);
580
		free(r);
581
	}
582
	addentryr = NULL;
583
	addentryfn = NULL;
584
	currecord = NULL;
585
	curmatch = NULL;
586
}
587
588
/*
589
 * Check if the cmd exists in $PATH. Split on ":" and iterate through
590
 * all paths in $PATH.
591
 */
592
int
593
csexists(const char *cmd)
594
{
595
	char fname[NFILEN], *dir, *path, *pathc, *tmp;
596
	int  len, dlen;
597
598
	/* Special case if prog contains '/' */
599
	if (strchr(cmd, '/')) {
600
		if (access(cmd, F_OK) == -1)
601
			return (FALSE);
602
		else
603
			return (TRUE);
604
	}
605
	if ((tmp = getenv("PATH")) == NULL)
606
		return (FALSE);
607
	if ((pathc = path = strndup(tmp, NFILEN)) == NULL) {
608
		dobeep();
609
		ewprintf("out of memory");
610
		return (FALSE);
611
	}
612
	while ((dir = strsep(&path, ":")) != NULL) {
613
		if (*dir == '\0')
614
			continue;
615
616
		dlen = strlen(dir);
617
		while (dlen > 0 && dir[dlen-1] == '/')
618
			dir[--dlen] = '\0';     /* strip trailing '/' */
619
620
		len = snprintf(fname, sizeof(fname), "%s/%s", dir, cmd);
621
		if (len == -1 || len >= sizeof(fname)) {
622
			dobeep();
623
			ewprintf("path too long");
624
			goto cleanup;
625
		}
626
		if(access(fname, F_OK) == 0) {
627
			free(pathc);
628
			return (TRUE);
629
		}
630
	}
631
cleanup:
632
	free(pathc);
633
	return (FALSE);
634
}