mirror of
http://git.haproxy.org/git/haproxy.git/
synced 2025-02-20 20:57:00 +00:00
MEDIUM: stats: add proxy name filtering on the statistic page
This patch adds a "scope" box in the statistics page in order to display only proxies with a name that contains the requested value. The scope filter is preserved across all clicks on the page.
This commit is contained in:
parent
d9bdccda55
commit
88c278fadf
@ -745,4 +745,7 @@ char *env_expand(char *in);
|
|||||||
*/
|
*/
|
||||||
#define fddebug(msg...) do { char *_m = NULL; memprintf(&_m, ##msg); if (_m) write(-1, _m, strlen(_m)); free(_m); } while (0)
|
#define fddebug(msg...) do { char *_m = NULL; memprintf(&_m, ##msg); if (_m) write(-1, _m, strlen(_m)); free(_m); } while (0)
|
||||||
|
|
||||||
|
/* same as strstr() but case-insensitive */
|
||||||
|
const char *strnistr(const char *str1, int len_str1, const char *str2, int len_str2);
|
||||||
|
|
||||||
#endif /* _COMMON_STANDARD_H */
|
#endif /* _COMMON_STANDARD_H */
|
||||||
|
@ -54,6 +54,11 @@
|
|||||||
#define STAT_CLI_O_SET 10 /* set entries in tables */
|
#define STAT_CLI_O_SET 10 /* set entries in tables */
|
||||||
#define STAT_CLI_O_STAT 11 /* dump stats */
|
#define STAT_CLI_O_STAT 11 /* dump stats */
|
||||||
|
|
||||||
|
/* HTML form to limit output scope */
|
||||||
|
#define STAT_SCOPE_TXT_MAXLEN 20 /* max len for scope substring */
|
||||||
|
#define STAT_SCOPE_INPUT_NAME "scope" /* pattern form scope name <input> in html form */
|
||||||
|
#define STAT_SCOPE_PATTERN "?" STAT_SCOPE_INPUT_NAME "="
|
||||||
|
|
||||||
extern struct si_applet http_stats_applet;
|
extern struct si_applet http_stats_applet;
|
||||||
|
|
||||||
void stats_io_handler(struct stream_interface *si);
|
void stats_io_handler(struct stream_interface *si);
|
||||||
|
@ -124,6 +124,8 @@ struct stream_interface {
|
|||||||
struct proxy *px;
|
struct proxy *px;
|
||||||
struct server *sv;
|
struct server *sv;
|
||||||
void *l;
|
void *l;
|
||||||
|
int scope_str; /* limit scope to a frontend/backend substring */
|
||||||
|
int scope_len; /* length of the string above in the buffer */
|
||||||
int px_st; /* STAT_PX_ST* */
|
int px_st; /* STAT_PX_ST* */
|
||||||
unsigned int flags; /* STAT_* */
|
unsigned int flags; /* STAT_* */
|
||||||
int iid, type, sid; /* proxy id, type and service id if bounding of stats is enabled */
|
int iid, type, sid; /* proxy id, type and service id if bounding of stats is enabled */
|
||||||
|
102
src/dumpstats.c
102
src/dumpstats.c
@ -2695,11 +2695,25 @@ static int stats_dump_be_stats(struct stream_interface *si, struct proxy *px, in
|
|||||||
*/
|
*/
|
||||||
static void stats_dump_html_px_hdr(struct stream_interface *si, struct proxy *px, struct uri_auth *uri)
|
static void stats_dump_html_px_hdr(struct stream_interface *si, struct proxy *px, struct uri_auth *uri)
|
||||||
{
|
{
|
||||||
|
char scope_txt[STAT_SCOPE_TXT_MAXLEN + sizeof STAT_SCOPE_PATTERN];
|
||||||
|
|
||||||
if (px->cap & PR_CAP_BE && px->srv && (si->applet.ctx.stats.flags & STAT_ADMIN)) {
|
if (px->cap & PR_CAP_BE && px->srv && (si->applet.ctx.stats.flags & STAT_ADMIN)) {
|
||||||
/* A form to enable/disable this proxy servers */
|
/* A form to enable/disable this proxy servers */
|
||||||
|
|
||||||
|
/* scope_txt = search pattern + search query, si->applet.ctx.stats.scope_len is always <= STAT_SCOPE_TXT_MAXLEN */
|
||||||
|
scope_txt[0] = 0;
|
||||||
|
if (si->applet.ctx.stats.scope_len) {
|
||||||
|
strcpy(scope_txt, STAT_SCOPE_PATTERN);
|
||||||
|
memcpy(scope_txt + strlen(STAT_SCOPE_PATTERN), bo_ptr(si->ob->buf) + si->applet.ctx.stats.scope_str, si->applet.ctx.stats.scope_len);
|
||||||
|
scope_txt[strlen(STAT_SCOPE_PATTERN) + si->applet.ctx.stats.scope_len] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
chunk_appendf(&trash,
|
chunk_appendf(&trash,
|
||||||
"<form action=\"%s\" method=\"post\">",
|
"<form action=\"%s%s%s%s\" method=\"post\">",
|
||||||
uri->uri_prefix);
|
uri->uri_prefix,
|
||||||
|
(si->applet.ctx.stats.flags & STAT_HIDE_DOWN) ? ";up" : "",
|
||||||
|
(si->applet.ctx.stats.flags & STAT_NO_REFRESH) ? ";norefresh" : "",
|
||||||
|
scope_txt);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* print a new table */
|
/* print a new table */
|
||||||
@ -2768,19 +2782,19 @@ static void stats_dump_html_px_end(struct stream_interface *si, struct proxy *px
|
|||||||
if ((px->cap & PR_CAP_BE) && px->srv && (si->applet.ctx.stats.flags & STAT_ADMIN)) {
|
if ((px->cap & PR_CAP_BE) && px->srv && (si->applet.ctx.stats.flags & STAT_ADMIN)) {
|
||||||
/* close the form used to enable/disable this proxy servers */
|
/* close the form used to enable/disable this proxy servers */
|
||||||
chunk_appendf(&trash,
|
chunk_appendf(&trash,
|
||||||
"Choose the action to perform on the checked servers : "
|
"Choose the action to perform on the checked servers : "
|
||||||
"<select name=action>"
|
"<select name=action>"
|
||||||
"<option value=\"\"></option>"
|
"<option value=\"\"></option>"
|
||||||
"<option value=\"disable\">Disable</option>"
|
"<option value=\"disable\">Disable</option>"
|
||||||
"<option value=\"enable\">Enable</option>"
|
"<option value=\"enable\">Enable</option>"
|
||||||
"<option value=\"stop\">Soft Stop</option>"
|
"<option value=\"stop\">Soft Stop</option>"
|
||||||
"<option value=\"start\">Soft Start</option>"
|
"<option value=\"start\">Soft Start</option>"
|
||||||
"<option value=\"shutdown\">Kill Sessions</option>"
|
"<option value=\"shutdown\">Kill Sessions</option>"
|
||||||
"</select>"
|
"</select>"
|
||||||
"<input type=\"hidden\" name=\"b\" value=\"#%d\">"
|
"<input type=\"hidden\" name=\"b\" value=\"#%d\">"
|
||||||
" <input type=\"submit\" value=\"Apply\">"
|
" <input type=\"submit\" value=\"Apply\">"
|
||||||
"</form>",
|
"</form>",
|
||||||
px->uuid);
|
px->uuid);
|
||||||
}
|
}
|
||||||
|
|
||||||
chunk_appendf(&trash, "<p>\n");
|
chunk_appendf(&trash, "<p>\n");
|
||||||
@ -2829,6 +2843,13 @@ static int stats_dump_proxy_to_buffer(struct stream_interface *si, struct proxy
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* if the user has requested a limited output and the proxy
|
||||||
|
* name does not match, skip it.
|
||||||
|
*/
|
||||||
|
if (si->applet.ctx.stats.scope_len &&
|
||||||
|
strnistr(px->id, strlen(px->id), bo_ptr(si->ob->buf) + si->applet.ctx.stats.scope_str, si->applet.ctx.stats.scope_len) == NULL)
|
||||||
|
return 1;
|
||||||
|
|
||||||
if ((si->applet.ctx.stats.flags & STAT_BOUND) &&
|
if ((si->applet.ctx.stats.flags & STAT_BOUND) &&
|
||||||
(si->applet.ctx.stats.iid != -1) &&
|
(si->applet.ctx.stats.iid != -1) &&
|
||||||
(px->uuid != si->applet.ctx.stats.iid))
|
(px->uuid != si->applet.ctx.stats.iid))
|
||||||
@ -3092,6 +3113,7 @@ static void stats_dump_html_head(struct uri_auth *uri)
|
|||||||
static void stats_dump_html_info(struct stream_interface *si, struct uri_auth *uri)
|
static void stats_dump_html_info(struct stream_interface *si, struct uri_auth *uri)
|
||||||
{
|
{
|
||||||
unsigned int up = (now.tv_sec - start_date.tv_sec);
|
unsigned int up = (now.tv_sec - start_date.tv_sec);
|
||||||
|
char scope_txt[STAT_SCOPE_TXT_MAXLEN + sizeof STAT_SCOPE_PATTERN];
|
||||||
|
|
||||||
/* WARNING! this has to fit the first packet too.
|
/* WARNING! this has to fit the first packet too.
|
||||||
* We are around 3.5 kB, add adding entries will
|
* We are around 3.5 kB, add adding entries will
|
||||||
@ -3147,44 +3169,70 @@ static void stats_dump_html_info(struct stream_interface *si, struct uri_auth *u
|
|||||||
run_queue_cur, nb_tasks_cur, idle_pct
|
run_queue_cur, nb_tasks_cur, idle_pct
|
||||||
);
|
);
|
||||||
|
|
||||||
|
/* scope_txt = search query, si->applet.ctx.stats.scope_len is always <= STAT_SCOPE_TXT_MAXLEN */
|
||||||
|
memcpy(scope_txt, bo_ptr(si->ob->buf) + si->applet.ctx.stats.scope_str, si->applet.ctx.stats.scope_len);
|
||||||
|
scope_txt[si->applet.ctx.stats.scope_len] = '\0';
|
||||||
|
|
||||||
|
chunk_appendf(&trash,
|
||||||
|
"<li><form method=GET ACTION='%s%s%s'>Scope : <input value='%s' name='" STAT_SCOPE_INPUT_NAME "' autofocus size=8 maxlength='%d'/></form>\n",
|
||||||
|
uri->uri_prefix,
|
||||||
|
(si->applet.ctx.stats.flags & STAT_HIDE_DOWN) ? ";up" : "",
|
||||||
|
(si->applet.ctx.stats.flags & STAT_NO_REFRESH) ? ";norefresh" : "",
|
||||||
|
(si->applet.ctx.stats.scope_len > 0) ? scope_txt : "",
|
||||||
|
STAT_SCOPE_TXT_MAXLEN);
|
||||||
|
|
||||||
|
/* scope_txt = search pattern + search query, si->applet.ctx.stats.scope_len is always <= STAT_SCOPE_TXT_MAXLEN */
|
||||||
|
scope_txt[0] = 0;
|
||||||
|
if (si->applet.ctx.stats.scope_len) {
|
||||||
|
strcpy(scope_txt, STAT_SCOPE_PATTERN);
|
||||||
|
memcpy(scope_txt + strlen(STAT_SCOPE_PATTERN), bo_ptr(si->ob->buf) + si->applet.ctx.stats.scope_str, si->applet.ctx.stats.scope_len);
|
||||||
|
scope_txt[strlen(STAT_SCOPE_PATTERN) + si->applet.ctx.stats.scope_len] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
if (si->applet.ctx.stats.flags & STAT_HIDE_DOWN)
|
if (si->applet.ctx.stats.flags & STAT_HIDE_DOWN)
|
||||||
chunk_appendf(&trash,
|
chunk_appendf(&trash,
|
||||||
"<li><a href=\"%s%s%s\">Show all servers</a><br>\n",
|
"<li><a href=\"%s%s%s%s\">Show all servers</a><br>\n",
|
||||||
uri->uri_prefix,
|
uri->uri_prefix,
|
||||||
"",
|
"",
|
||||||
(si->applet.ctx.stats.flags & STAT_NO_REFRESH) ? ";norefresh" : "");
|
(si->applet.ctx.stats.flags & STAT_NO_REFRESH) ? ";norefresh" : "",
|
||||||
|
scope_txt);
|
||||||
else
|
else
|
||||||
chunk_appendf(&trash,
|
chunk_appendf(&trash,
|
||||||
"<li><a href=\"%s%s%s\">Hide 'DOWN' servers</a><br>\n",
|
"<li><a href=\"%s%s%s%s\">Hide 'DOWN' servers</a><br>\n",
|
||||||
uri->uri_prefix,
|
uri->uri_prefix,
|
||||||
";up",
|
";up",
|
||||||
(si->applet.ctx.stats.flags & STAT_NO_REFRESH) ? ";norefresh" : "");
|
(si->applet.ctx.stats.flags & STAT_NO_REFRESH) ? ";norefresh" : "",
|
||||||
|
scope_txt);
|
||||||
|
|
||||||
if (uri->refresh > 0) {
|
if (uri->refresh > 0) {
|
||||||
if (si->applet.ctx.stats.flags & STAT_NO_REFRESH)
|
if (si->applet.ctx.stats.flags & STAT_NO_REFRESH)
|
||||||
chunk_appendf(&trash,
|
chunk_appendf(&trash,
|
||||||
"<li><a href=\"%s%s%s\">Enable refresh</a><br>\n",
|
"<li><a href=\"%s%s%s%s\">Enable refresh</a><br>\n",
|
||||||
uri->uri_prefix,
|
uri->uri_prefix,
|
||||||
(si->applet.ctx.stats.flags & STAT_HIDE_DOWN) ? ";up" : "",
|
(si->applet.ctx.stats.flags & STAT_HIDE_DOWN) ? ";up" : "",
|
||||||
"");
|
"",
|
||||||
|
scope_txt);
|
||||||
else
|
else
|
||||||
chunk_appendf(&trash,
|
chunk_appendf(&trash,
|
||||||
"<li><a href=\"%s%s%s\">Disable refresh</a><br>\n",
|
"<li><a href=\"%s%s%s%s\">Disable refresh</a><br>\n",
|
||||||
uri->uri_prefix,
|
uri->uri_prefix,
|
||||||
(si->applet.ctx.stats.flags & STAT_HIDE_DOWN) ? ";up" : "",
|
(si->applet.ctx.stats.flags & STAT_HIDE_DOWN) ? ";up" : "",
|
||||||
";norefresh");
|
";norefresh",
|
||||||
|
scope_txt);
|
||||||
}
|
}
|
||||||
|
|
||||||
chunk_appendf(&trash,
|
chunk_appendf(&trash,
|
||||||
"<li><a href=\"%s%s%s\">Refresh now</a><br>\n",
|
"<li><a href=\"%s%s%s%s\">Refresh now</a><br>\n",
|
||||||
uri->uri_prefix,
|
uri->uri_prefix,
|
||||||
(si->applet.ctx.stats.flags & STAT_HIDE_DOWN) ? ";up" : "",
|
(si->applet.ctx.stats.flags & STAT_HIDE_DOWN) ? ";up" : "",
|
||||||
(si->applet.ctx.stats.flags & STAT_NO_REFRESH) ? ";norefresh" : "");
|
(si->applet.ctx.stats.flags & STAT_NO_REFRESH) ? ";norefresh" : "",
|
||||||
|
scope_txt);
|
||||||
|
|
||||||
chunk_appendf(&trash,
|
chunk_appendf(&trash,
|
||||||
"<li><a href=\"%s;csv%s\">CSV export</a><br>\n",
|
"<li><a href=\"%s;csv%s%s\">CSV export</a><br>\n",
|
||||||
uri->uri_prefix,
|
uri->uri_prefix,
|
||||||
(uri->refresh > 0) ? ";norefresh" : "");
|
(uri->refresh > 0) ? ";norefresh" : "",
|
||||||
|
scope_txt);
|
||||||
|
|
||||||
chunk_appendf(&trash,
|
chunk_appendf(&trash,
|
||||||
"</ul></td>"
|
"</ul></td>"
|
||||||
|
@ -2971,6 +2971,8 @@ int http_handle_stats(struct session *s, struct channel *req)
|
|||||||
|
|
||||||
/* Was the status page requested with a POST ? */
|
/* Was the status page requested with a POST ? */
|
||||||
if (unlikely(txn->meth == HTTP_METH_POST)) {
|
if (unlikely(txn->meth == HTTP_METH_POST)) {
|
||||||
|
char scope_txt[STAT_SCOPE_TXT_MAXLEN + sizeof STAT_SCOPE_PATTERN];
|
||||||
|
|
||||||
if (si->applet.ctx.stats.flags & STAT_ADMIN) {
|
if (si->applet.ctx.stats.flags & STAT_ADMIN) {
|
||||||
if (msg->msg_state < HTTP_MSG_100_SENT) {
|
if (msg->msg_state < HTTP_MSG_100_SENT) {
|
||||||
/* If we have HTTP/1.1 and Expect: 100-continue, then we must
|
/* If we have HTTP/1.1 and Expect: 100-continue, then we must
|
||||||
@ -2993,6 +2995,14 @@ int http_handle_stats(struct session *s, struct channel *req)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
si->applet.ctx.stats.st_code = STAT_STATUS_DENY;
|
si->applet.ctx.stats.st_code = STAT_STATUS_DENY;
|
||||||
|
/* scope_txt = search pattern + search query, si->applet.ctx.stats.scope_len is always <= STAT_SCOPE_TXT_MAXLEN */
|
||||||
|
scope_txt[0] = 0;
|
||||||
|
if (si->applet.ctx.stats.scope_len) {
|
||||||
|
strcpy(scope_txt, STAT_SCOPE_PATTERN);
|
||||||
|
memcpy(scope_txt + strlen(STAT_SCOPE_PATTERN), bo_ptr(req->buf) + si->applet.ctx.stats.scope_str, si->applet.ctx.stats.scope_len);
|
||||||
|
scope_txt[strlen(STAT_SCOPE_PATTERN) + si->applet.ctx.stats.scope_len] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* We don't want to land on the posted stats page because a refresh will
|
/* We don't want to land on the posted stats page because a refresh will
|
||||||
* repost the data. We don't want this to happen on accident so we redirect
|
* repost the data. We don't want this to happen on accident so we redirect
|
||||||
@ -3003,14 +3013,17 @@ int http_handle_stats(struct session *s, struct channel *req)
|
|||||||
"Cache-Control: no-cache\r\n"
|
"Cache-Control: no-cache\r\n"
|
||||||
"Content-Type: text/plain\r\n"
|
"Content-Type: text/plain\r\n"
|
||||||
"Connection: close\r\n"
|
"Connection: close\r\n"
|
||||||
"Location: %s;st=%s\r\n"
|
"Location: %s;st=%s%s%s%s\r\n"
|
||||||
"\r\n",
|
"\r\n",
|
||||||
uri->uri_prefix,
|
uri->uri_prefix,
|
||||||
((si->applet.ctx.stats.st_code > STAT_STATUS_INIT) &&
|
((si->applet.ctx.stats.st_code > STAT_STATUS_INIT) &&
|
||||||
(si->applet.ctx.stats.st_code < STAT_STATUS_SIZE) &&
|
(si->applet.ctx.stats.st_code < STAT_STATUS_SIZE) &&
|
||||||
stat_status_codes[si->applet.ctx.stats.st_code]) ?
|
stat_status_codes[si->applet.ctx.stats.st_code]) ?
|
||||||
stat_status_codes[si->applet.ctx.stats.st_code] :
|
stat_status_codes[si->applet.ctx.stats.st_code] :
|
||||||
stat_status_codes[STAT_STATUS_UNKN]);
|
stat_status_codes[STAT_STATUS_UNKN],
|
||||||
|
(si->applet.ctx.stats.flags & STAT_HIDE_DOWN) ? ";up" : "",
|
||||||
|
(si->applet.ctx.stats.flags & STAT_NO_REFRESH) ? ";norefresh" : "",
|
||||||
|
scope_txt);
|
||||||
|
|
||||||
s->txn.status = 303;
|
s->txn.status = 303;
|
||||||
s->logs.tv_request = now;
|
s->logs.tv_request = now;
|
||||||
@ -7827,6 +7840,44 @@ int stats_check_uri(struct stream_interface *si, struct http_txn *txn, struct pr
|
|||||||
}
|
}
|
||||||
h++;
|
h++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
si->applet.ctx.stats.scope_str = 0;
|
||||||
|
si->applet.ctx.stats.scope_len = 0;
|
||||||
|
h = uri + uri_auth->uri_len;
|
||||||
|
while (h <= uri + msg->sl.rq.u_l - 8) {
|
||||||
|
if (memcmp(h, STAT_SCOPE_INPUT_NAME "=", strlen(STAT_SCOPE_INPUT_NAME) + 1) == 0) {
|
||||||
|
int itx = 0;
|
||||||
|
const char *h2;
|
||||||
|
char scope_txt[STAT_SCOPE_TXT_MAXLEN + 1];
|
||||||
|
const char *err;
|
||||||
|
|
||||||
|
h += strlen(STAT_SCOPE_INPUT_NAME) + 1;
|
||||||
|
h2 = h;
|
||||||
|
si->applet.ctx.stats.scope_str = h2 - msg->chn->buf->p;
|
||||||
|
while (*h != ';' && *h != '\0' && *h != '&' && *h != ' ' && *h != '\n') {
|
||||||
|
itx++;
|
||||||
|
h++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (itx > STAT_SCOPE_TXT_MAXLEN)
|
||||||
|
itx = STAT_SCOPE_TXT_MAXLEN;
|
||||||
|
si->applet.ctx.stats.scope_len = itx;
|
||||||
|
|
||||||
|
/* scope_txt = search query, si->applet.ctx.stats.scope_len is always <= STAT_SCOPE_TXT_MAXLEN */
|
||||||
|
memcpy(scope_txt, h2, itx);
|
||||||
|
scope_txt[itx] = '\0';
|
||||||
|
err = invalid_char(scope_txt);
|
||||||
|
if (err) {
|
||||||
|
/* bad char in search text => clear scope */
|
||||||
|
si->applet.ctx.stats.scope_str = 0;
|
||||||
|
si->applet.ctx.stats.scope_len = 0;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
h++;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2081,6 +2081,55 @@ char *env_expand(char *in)
|
|||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* same as strstr() but case-insensitive and with limit length */
|
||||||
|
const char *strnistr(const char *str1, int len_str1, const char *str2, int len_str2)
|
||||||
|
{
|
||||||
|
char *pptr, *sptr, *start;
|
||||||
|
uint slen, plen;
|
||||||
|
uint tmp1, tmp2;
|
||||||
|
|
||||||
|
if (str1 == NULL || len_str1 == 0) // search pattern into an empty string => search is not found
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
if (str2 == NULL || len_str2 == 0) // pattern is empty => every str1 match
|
||||||
|
return str1;
|
||||||
|
|
||||||
|
if (len_str1 < len_str2) // pattern is longer than string => search is not found
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
for (tmp1 = 0, start = (char *)str1, pptr = (char *)str2, slen = len_str1, plen = len_str2; slen >= plen; start++, slen--) {
|
||||||
|
while (toupper(*start) != toupper(*str2)) {
|
||||||
|
start++;
|
||||||
|
slen--;
|
||||||
|
tmp1++;
|
||||||
|
|
||||||
|
if (tmp1 >= len_str1)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
/* if pattern longer than string */
|
||||||
|
if (slen < plen)
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
sptr = start;
|
||||||
|
pptr = (char *)str2;
|
||||||
|
|
||||||
|
tmp2 = 0;
|
||||||
|
while (toupper(*sptr) == toupper(*pptr)) {
|
||||||
|
sptr++;
|
||||||
|
pptr++;
|
||||||
|
tmp2++;
|
||||||
|
|
||||||
|
if (*pptr == '\0' || tmp2 == len_str2) /* end of pattern found */
|
||||||
|
return start;
|
||||||
|
if (*sptr == '\0' || tmp2 == len_str1) /* end of string found and the pattern is not fully found */
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Local variables:
|
* Local variables:
|
||||||
* c-indent-level: 8
|
* c-indent-level: 8
|
||||||
|
Loading…
Reference in New Issue
Block a user