GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.bin/mg/cscope.c Lines: 0 276 0.0 %
Date: 2017-11-13 Branches: 0 168 0.0 %

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