|
|
|
@ -2,6 +2,7 @@
|
|
|
|
|
* Copyright (c) 1988 Mark Nudleman
|
|
|
|
|
* Copyright (c) 1988, 1993
|
|
|
|
|
* The Regents of the University of California. All rights reserved.
|
|
|
|
|
* Portions copyright (c) 1996, 1997, 1998 Shigio Yamaguchi.
|
|
|
|
|
*
|
|
|
|
|
* Redistribution and use in source and binary forms, with or without
|
|
|
|
|
* modification, are permitted provided that the following conditions
|
|
|
|
@ -36,43 +37,158 @@
|
|
|
|
|
static char sccsid[] = "@(#)tags.c 8.1 (Berkeley) 6/6/93";
|
|
|
|
|
#endif /* not lint */
|
|
|
|
|
|
|
|
|
|
#ifndef lint
|
|
|
|
|
static const char rcsid[] =
|
|
|
|
|
"$Id$";
|
|
|
|
|
#endif /* not lint */
|
|
|
|
|
|
|
|
|
|
#include <sys/types.h>
|
|
|
|
|
#include <sys/queue.h>
|
|
|
|
|
|
|
|
|
|
#include <ctype.h>
|
|
|
|
|
#include <stdio.h>
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
#include <string.h>
|
|
|
|
|
#include <less.h>
|
|
|
|
|
|
|
|
|
|
#include "less.h"
|
|
|
|
|
|
|
|
|
|
#define WHITESP(c) ((c)==' ' || (c)=='\t')
|
|
|
|
|
|
|
|
|
|
char *tagfile;
|
|
|
|
|
char *tagpattern;
|
|
|
|
|
|
|
|
|
|
static char *tags = "tags";
|
|
|
|
|
|
|
|
|
|
extern int linenums;
|
|
|
|
|
extern int sigs;
|
|
|
|
|
extern char *line;
|
|
|
|
|
char *tagfile; /* Name of source file containing current tag */
|
|
|
|
|
|
|
|
|
|
static enum { CTAGS, GTAGS } tagstyle = GTAGS;
|
|
|
|
|
static char *findctag(), *findgtag(), *nextgtag(), *prevgtag();
|
|
|
|
|
static ctagsearch(), gtagsearch();
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Find a tag in the "tags" file.
|
|
|
|
|
* Sets "tagfile" to the name of the file containing the tag,
|
|
|
|
|
* and "tagpattern" to the search pattern which should be used
|
|
|
|
|
* to find the tag.
|
|
|
|
|
* Load information about the tag. The global variable tagfile will point to
|
|
|
|
|
* the file that contains the tag, or will be NULL if information could not be
|
|
|
|
|
* found (in which case an error message will have been printed). After
|
|
|
|
|
* loading the file named by tagfile, tagsearch() should be called to
|
|
|
|
|
* set the current position to the tag.
|
|
|
|
|
*/
|
|
|
|
|
findtag(tag)
|
|
|
|
|
register char *tag;
|
|
|
|
|
char *tag; /* The tag to load */
|
|
|
|
|
{
|
|
|
|
|
/*
|
|
|
|
|
* Try using gtags or ctags first, as indicated by tagstyle. If
|
|
|
|
|
* that fails, try the other. Someday there may even be a way to
|
|
|
|
|
* assert a certain tagstyle...
|
|
|
|
|
*/
|
|
|
|
|
switch(tagstyle) {
|
|
|
|
|
case CTAGS:
|
|
|
|
|
tagfile = findctag(tag);
|
|
|
|
|
if (!tagfile && (tagfile = findgtag(tag))) tagstyle = GTAGS;
|
|
|
|
|
if (tagfile) return;
|
|
|
|
|
break;
|
|
|
|
|
case GTAGS:
|
|
|
|
|
/* Would be nice to print the number of tag references
|
|
|
|
|
* we found (for nexttag() and prevtag()) in a (not-)error()
|
|
|
|
|
* message. */
|
|
|
|
|
tagfile = findgtag(tag);
|
|
|
|
|
if (!tagfile && (tagfile = findctag(tag))) tagstyle = CTAGS;
|
|
|
|
|
if (tagfile) return;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
error("could not find relevent tag information");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Load information about the next number'th tag, if the last findtag() call
|
|
|
|
|
* found multiple tag references. The global variable tagfile will point to the
|
|
|
|
|
* file that contains the tag, or will be NULL if information could not be
|
|
|
|
|
* found (in which case an error message will have been printed). After
|
|
|
|
|
* loading the file named by tagfile, tagsearch() should be called to set
|
|
|
|
|
* the current position to the tag.
|
|
|
|
|
*/
|
|
|
|
|
nexttag(number)
|
|
|
|
|
int number; /* How many tags to go forward by */
|
|
|
|
|
{
|
|
|
|
|
if (number < 0) number = -number; /* positive only, please */
|
|
|
|
|
|
|
|
|
|
switch(tagstyle) {
|
|
|
|
|
case CTAGS:
|
|
|
|
|
break;
|
|
|
|
|
case GTAGS:
|
|
|
|
|
while (number--) tagfile = nextgtag();
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
if (!tagfile)
|
|
|
|
|
error("no next tag");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* The antithesis to nexttag().
|
|
|
|
|
*/
|
|
|
|
|
prevtag(number)
|
|
|
|
|
int number; /* How many tags to go backwards by */
|
|
|
|
|
{
|
|
|
|
|
if (number < 0) number = -number; /* positive only, please */
|
|
|
|
|
|
|
|
|
|
switch(tagstyle) {
|
|
|
|
|
case CTAGS:
|
|
|
|
|
break;
|
|
|
|
|
case GTAGS:
|
|
|
|
|
while (number--) tagfile = prevgtag();
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
if (!tagfile)
|
|
|
|
|
error("no previous tag");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Try and position the currently loaded file at the last tag that was
|
|
|
|
|
* succesfully passed to findtag() or chosen with nexttag() and prevtag().
|
|
|
|
|
* An error message will be printed if unsuccessful.
|
|
|
|
|
*/
|
|
|
|
|
tagsearch()
|
|
|
|
|
{
|
|
|
|
|
switch(tagstyle) {
|
|
|
|
|
case CTAGS:
|
|
|
|
|
if (ctagsearch())
|
|
|
|
|
error("could not locate ctag");
|
|
|
|
|
return;
|
|
|
|
|
case GTAGS:
|
|
|
|
|
if (gtagsearch())
|
|
|
|
|
error("could not locate gtag");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*******************************************************************************
|
|
|
|
|
*
|
|
|
|
|
* ctags
|
|
|
|
|
*
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
extern int linenums;
|
|
|
|
|
extern char *line;
|
|
|
|
|
|
|
|
|
|
static char *ctagpattern;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Find specified tag in the ctags(1)-format tag file ctagfile. Returns
|
|
|
|
|
* pointer to a static buffer holding the name of the file containing
|
|
|
|
|
* the tag. Returns NULL on failure. The next call to ctagsearch() will
|
|
|
|
|
* position the currently loaded file at the tag.
|
|
|
|
|
*/
|
|
|
|
|
static char *
|
|
|
|
|
findctag(tag)
|
|
|
|
|
register char *tag; /* tag to search for */
|
|
|
|
|
{
|
|
|
|
|
register char *p;
|
|
|
|
|
register FILE *f;
|
|
|
|
|
register int taglen;
|
|
|
|
|
int search_char;
|
|
|
|
|
static char tline[200];
|
|
|
|
|
static char tline[200]; /* XXX should be dynamic */
|
|
|
|
|
const char *ctagfile = "tags";
|
|
|
|
|
char *retr;
|
|
|
|
|
|
|
|
|
|
if ((f = fopen(tags, "r")) == NULL)
|
|
|
|
|
{
|
|
|
|
|
error("No tags file");
|
|
|
|
|
tagfile = NULL;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if ((f = fopen(ctagfile, "r")) == NULL)
|
|
|
|
|
return (NULL);
|
|
|
|
|
|
|
|
|
|
taglen = strlen(tag);
|
|
|
|
|
|
|
|
|
@ -81,6 +197,9 @@ findtag(tag)
|
|
|
|
|
*/
|
|
|
|
|
while (fgets(tline, sizeof(tline), f) != NULL)
|
|
|
|
|
{
|
|
|
|
|
if (sigs)
|
|
|
|
|
break; /* abandon */
|
|
|
|
|
|
|
|
|
|
if (strncmp(tag, tline, taglen) != 0 || !WHITESP(tline[taglen]))
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
@ -92,10 +211,9 @@ findtag(tag)
|
|
|
|
|
* search characters.
|
|
|
|
|
* Parse the line and extract these parts.
|
|
|
|
|
*/
|
|
|
|
|
tagfile = tagpattern = NULL;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Skip over the whitespace after the tag name.
|
|
|
|
|
* Skip over the tag and the whitespace after the tag name.
|
|
|
|
|
*/
|
|
|
|
|
for (p = tline; !WHITESP(*p) && *p != '\0'; p++)
|
|
|
|
|
continue;
|
|
|
|
@ -107,9 +225,9 @@ findtag(tag)
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Save the file name.
|
|
|
|
|
* Skip over the whitespace after the file name.
|
|
|
|
|
* Skip over the filename and whitespace after the file name.
|
|
|
|
|
*/
|
|
|
|
|
tagfile = p;
|
|
|
|
|
retr = p;
|
|
|
|
|
while (!WHITESP(*p) && *p != '\0')
|
|
|
|
|
p++;
|
|
|
|
|
*p++ = '\0';
|
|
|
|
@ -127,7 +245,7 @@ findtag(tag)
|
|
|
|
|
search_char = *p++;
|
|
|
|
|
if (*p == '^')
|
|
|
|
|
p++;
|
|
|
|
|
tagpattern = p;
|
|
|
|
|
ctagpattern = p; /* cock ctagsearch() */
|
|
|
|
|
while (*p != search_char && *p != '\0')
|
|
|
|
|
p++;
|
|
|
|
|
if (p[-1] == '$')
|
|
|
|
@ -135,23 +253,26 @@ findtag(tag)
|
|
|
|
|
*p = '\0';
|
|
|
|
|
|
|
|
|
|
(void)fclose(f);
|
|
|
|
|
return;
|
|
|
|
|
return (retr);
|
|
|
|
|
}
|
|
|
|
|
(void)fclose(f);
|
|
|
|
|
error("No such tag in tags file");
|
|
|
|
|
tagfile = NULL;
|
|
|
|
|
return (NULL);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Search for a tag.
|
|
|
|
|
* Locate the tag that was loaded by findctag().
|
|
|
|
|
* This is a stripped-down version of search().
|
|
|
|
|
* We don't use search() for several reasons:
|
|
|
|
|
* - We don't want to blow away any search string we may have saved.
|
|
|
|
|
* - The various regular-expression functions (from different systems:
|
|
|
|
|
* regcmp vs. re_comp) behave differently in the presence of
|
|
|
|
|
* parentheses (which are almost always found in a tag).
|
|
|
|
|
*
|
|
|
|
|
* Returns -1 if it was unable to position at the requested pattern,
|
|
|
|
|
* 0 otherwise.
|
|
|
|
|
*/
|
|
|
|
|
tagsearch()
|
|
|
|
|
static
|
|
|
|
|
ctagsearch()
|
|
|
|
|
{
|
|
|
|
|
off_t pos, linepos, forw_raw_line();
|
|
|
|
|
int linenum;
|
|
|
|
@ -166,7 +287,7 @@ tagsearch()
|
|
|
|
|
* until we hit end-of-file.
|
|
|
|
|
*/
|
|
|
|
|
if (sigs)
|
|
|
|
|
return (1);
|
|
|
|
|
return (-1);
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Read the next line, and save the
|
|
|
|
@ -178,13 +299,7 @@ tagsearch()
|
|
|
|
|
linenum++;
|
|
|
|
|
|
|
|
|
|
if (pos == NULL_POSITION)
|
|
|
|
|
{
|
|
|
|
|
/*
|
|
|
|
|
* We hit EOF without a match.
|
|
|
|
|
*/
|
|
|
|
|
error("Tag not found");
|
|
|
|
|
return (1);
|
|
|
|
|
}
|
|
|
|
|
return (-1); /* Tag not found. */
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* If we're using line numbers, we might as well
|
|
|
|
@ -197,10 +312,247 @@ tagsearch()
|
|
|
|
|
/*
|
|
|
|
|
* Test the line to see if we have a match.
|
|
|
|
|
*/
|
|
|
|
|
if (strcmp(tagpattern, line) == 0)
|
|
|
|
|
if (strcmp(ctagpattern, line) == 0)
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
jump_loc(linepos);
|
|
|
|
|
return (0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*******************************************************************************
|
|
|
|
|
*
|
|
|
|
|
* gtags
|
|
|
|
|
*
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* The findgtag() and getentry() functions are stolen, more or less, from the
|
|
|
|
|
* patches to nvi-1.79 included in Shigio Yamaguchi's global-3.42 distribution.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* The queue of tags generated by the last findgtag() call.
|
|
|
|
|
*/
|
|
|
|
|
static CIRCLEQ_HEAD(gtag_q, gtag) gtag_q;
|
|
|
|
|
struct gtag {
|
|
|
|
|
CIRCLEQ_ENTRY(gtag) ptrs;
|
|
|
|
|
char *file; /* source file containing the tag */
|
|
|
|
|
int line; /* appropriate line number of source file */
|
|
|
|
|
};
|
|
|
|
|
static struct gtag *curgtag;
|
|
|
|
|
static getentry();
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* The findgtag() will try and load information about the requested tag.
|
|
|
|
|
* It does this by calling "global -x tag; global -xr tag;" and storing the
|
|
|
|
|
* parsed output for future use by gtagsearch_f() and gtagsearch_b(). A
|
|
|
|
|
* pointer to a static buffer containing the name of the source file will
|
|
|
|
|
* be returned, or NULL on failure. The first filename printed by global is
|
|
|
|
|
* returned (hopefully the function definition) and the other filenames may
|
|
|
|
|
* be accessed by nextgtag() and prevgtag().
|
|
|
|
|
*/
|
|
|
|
|
static char *
|
|
|
|
|
findgtag(tag)
|
|
|
|
|
char *tag; /* tag to load */
|
|
|
|
|
{
|
|
|
|
|
struct gtag *gtag_p1, *gtag_p2;
|
|
|
|
|
char command[512];
|
|
|
|
|
char buf[256];
|
|
|
|
|
FILE *fp;
|
|
|
|
|
|
|
|
|
|
if (!tag) return (NULL); /* Sanity check */
|
|
|
|
|
|
|
|
|
|
/* Clear any existing tag circle queue */
|
|
|
|
|
/* XXX Ideally, we wouldn't do this until after we know that we
|
|
|
|
|
* can load some other tag information. */
|
|
|
|
|
curgtag = NULL;
|
|
|
|
|
gtag_p1 = gtag_q.cqh_first;
|
|
|
|
|
if (gtag_p1) while (gtag_p1 != (void *)>ag_q) {
|
|
|
|
|
gtag_p2 = gtag_p1->ptrs.cqe_next;
|
|
|
|
|
free(gtag_p1);
|
|
|
|
|
gtag_p1 = gtag_p2;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Allocate and initialize the tag queue structure. */
|
|
|
|
|
CIRCLEQ_INIT(>ag_q);
|
|
|
|
|
|
|
|
|
|
/* Get our data from global(1) */
|
|
|
|
|
snprintf(command, sizeof(command),
|
|
|
|
|
"(global -x '%s'; global -xr '%s') 2>/dev/null", tag, tag);
|
|
|
|
|
if (fp = popen(command, "r")) {
|
|
|
|
|
while (fgets(buf, sizeof(buf), fp)) {
|
|
|
|
|
char *name, *file, *line;
|
|
|
|
|
|
|
|
|
|
if (sigs) {
|
|
|
|
|
pclose(fp);
|
|
|
|
|
return (NULL);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* chop(buf) */
|
|
|
|
|
if (buf[strlen(buf) - 1] == '\n')
|
|
|
|
|
buf[strlen(buf) - 1] = 0;
|
|
|
|
|
else
|
|
|
|
|
while (fgetc(fp) != '\n')
|
|
|
|
|
;
|
|
|
|
|
|
|
|
|
|
if (getentry(buf, &name, &file, &line)) {
|
|
|
|
|
/*
|
|
|
|
|
* Couldn't parse this line for some reason.
|
|
|
|
|
* We'll just pretend it never happened.
|
|
|
|
|
*/
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Add to queue */
|
|
|
|
|
gtag_p1 = malloc(sizeof(struct gtag));
|
|
|
|
|
if (!gtag_p1) {
|
|
|
|
|
pclose(fp);
|
|
|
|
|
error("malloc() failed");
|
|
|
|
|
return (NULL);
|
|
|
|
|
}
|
|
|
|
|
gtag_p1->file = file;
|
|
|
|
|
gtag_p1->file = malloc(strlen(file));
|
|
|
|
|
if (!gtag_p1->file) {
|
|
|
|
|
pclose(fp);
|
|
|
|
|
error("malloc() failed");
|
|
|
|
|
return (NULL);
|
|
|
|
|
}
|
|
|
|
|
strcpy(gtag_p1->file, file);
|
|
|
|
|
gtag_p1->line = atoi(line);
|
|
|
|
|
CIRCLEQ_INSERT_TAIL(>ag_q, gtag_p1, ptrs);
|
|
|
|
|
}
|
|
|
|
|
pclose(fp);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Check to see if we found anything. */
|
|
|
|
|
if (gtag_q.cqh_first == (void *)>ag_q)
|
|
|
|
|
return (NULL); /* Nope! */
|
|
|
|
|
|
|
|
|
|
curgtag = gtag_q.cqh_first;
|
|
|
|
|
return (curgtag->file);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Return the filename required for the next gtag in the queue that was setup
|
|
|
|
|
* by findgtag(). The next call to gtagsearch() will try to position at the
|
|
|
|
|
* appropriate tag.
|
|
|
|
|
*/
|
|
|
|
|
static char *
|
|
|
|
|
nextgtag()
|
|
|
|
|
{
|
|
|
|
|
if (!curgtag) {
|
|
|
|
|
/* No tag stack loaded */
|
|
|
|
|
return (NULL);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
curgtag = curgtag->ptrs.cqe_next;
|
|
|
|
|
if (curgtag == (void *)>ag_q) {
|
|
|
|
|
/* Wrapped around to the head of the queue */
|
|
|
|
|
curgtag = ((struct gtag_q *)curgtag)->cqh_first;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return (curgtag->file);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Return the filename required for the previous gtag in the queue that was
|
|
|
|
|
* setup by findgtat(). The next call to gtagsearch() will try to position
|
|
|
|
|
* at the appropriate tag.
|
|
|
|
|
*/
|
|
|
|
|
static char *
|
|
|
|
|
prevgtag()
|
|
|
|
|
{
|
|
|
|
|
if (!curgtag) {
|
|
|
|
|
/* No tag stack loaded */
|
|
|
|
|
return (NULL);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
curgtag = curgtag->ptrs.cqe_prev;
|
|
|
|
|
if (curgtag == (void *)>ag_q) {
|
|
|
|
|
/* Wrapped around to the head of the queue */
|
|
|
|
|
curgtag = ((struct gtag_q *)curgtag)->cqh_last;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return (curgtag->file);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Position the current file at at what is hopefully the tag that was chosen
|
|
|
|
|
* using either findtag() or one of nextgtag() and prevgtag(). Returns -1
|
|
|
|
|
* if it was unable to position at the tag, 0 if succesful.
|
|
|
|
|
*/
|
|
|
|
|
static
|
|
|
|
|
gtagsearch()
|
|
|
|
|
{
|
|
|
|
|
if (!curgtag)
|
|
|
|
|
return (-1); /* No gtags loaded! */
|
|
|
|
|
|
|
|
|
|
jump_back(curgtag->line);
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* XXX We'll assume we were successful --- jump_back() will call error()
|
|
|
|
|
* if it fails, so the user will receive some kind of notification.
|
|
|
|
|
* Eventually, jump_back() should do its work silently and let us
|
|
|
|
|
* perform the error notification, eventually allowing our caller
|
|
|
|
|
* (presumably tagsearch()) to go error("Could not locate tag.");
|
|
|
|
|
*/
|
|
|
|
|
return (0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* The getentry() parses output from the global(1) command. The output
|
|
|
|
|
* must be in the format described below. Returns 0 on success, -1 on
|
|
|
|
|
* error. The tag, file, and line will each be NUL-terminated pointers
|
|
|
|
|
* into buf.
|
|
|
|
|
*
|
|
|
|
|
* gtags temporary file format.
|
|
|
|
|
* <tag> <lineno> <file> <image>
|
|
|
|
|
*
|
|
|
|
|
* sample.
|
|
|
|
|
* +------------------------------------------------
|
|
|
|
|
* |main 30 main.c main(argc, argv)
|
|
|
|
|
* |func 21 subr.c func(arg)
|
|
|
|
|
*/
|
|
|
|
|
static
|
|
|
|
|
getentry(buf, tag, file, line)
|
|
|
|
|
char *buf; /* output from global -x */
|
|
|
|
|
char **tag; /* name of the tag we actually found */
|
|
|
|
|
char **file; /* file in which to find this tag */
|
|
|
|
|
char **line; /* line number of file where this tag is found */
|
|
|
|
|
{
|
|
|
|
|
char *p = buf;
|
|
|
|
|
|
|
|
|
|
for (*tag = p; *p && !isspace(*p); p++) /* tag name */
|
|
|
|
|
;
|
|
|
|
|
if (*p == 0)
|
|
|
|
|
goto err;
|
|
|
|
|
*p++ = 0;
|
|
|
|
|
for (; *p && isspace(*p); p++) /* (skip blanks) */
|
|
|
|
|
;
|
|
|
|
|
if (*p == 0)
|
|
|
|
|
goto err;
|
|
|
|
|
*line = p; /* line no */
|
|
|
|
|
for (*line = p; *p && !isspace(*p); p++)
|
|
|
|
|
;
|
|
|
|
|
if (*p == 0)
|
|
|
|
|
goto err;
|
|
|
|
|
*p++ = 0;
|
|
|
|
|
for (; *p && isspace(*p); p++) /* (skip blanks) */
|
|
|
|
|
;
|
|
|
|
|
if (*p == 0)
|
|
|
|
|
goto err;
|
|
|
|
|
*file = p; /* file name */
|
|
|
|
|
for (*file = p; *p && !isspace(*p); p++)
|
|
|
|
|
;
|
|
|
|
|
if (*p == 0)
|
|
|
|
|
goto err;
|
|
|
|
|
*p = 0;
|
|
|
|
|
|
|
|
|
|
/* value check */
|
|
|
|
|
if (strlen(*tag) && strlen(*line) && strlen(*file) && atoi(*line) > 0)
|
|
|
|
|
return (0); /* OK */
|
|
|
|
|
err:
|
|
|
|
|
return (-1); /* ERROR */
|
|
|
|
|
}
|
|
|
|
|