📄 urlbreaker.c
字号:
/**
* URL分析,URL的格式
* http://[user[:passwd]@]<host>[:[<port>]][/<path_to_file>[?<query>]]
* Xu Lubing
* Nov. 22, 2002
*/
#include "urlbreaker.h"
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#if !__GNUC__
#include <malloc.h>
#endif
#ifdef WIN32
#define strncasecmp _strnicmp
#endif
#define DEFAULT_PORT 80
#define HTTP_HEADING "http://"
#define DEFAULT_FILE "/"
#define HTTP_HEADING_LEN 7 /*= strlen(HTTP_HEADING);*/
#define DEFAULT_FILE_LEN 1 /* = strlen(DEFAULT_FILE); */
/* 错误代码定义 */
typedef enum {
NO_ERROR = 0,
NO_URL,
NO_URLINFO,
MEM_ERROR,
UNSUPPORTED_PROTOCOL,
HOST_EXPECTED,
BAD_HOST_FORMAT,
BAD_PORT_FORMAT
} ERROR_CODE;
/* 错误原因定义 */
static const char* ERROR_MESSAGE[] = {
NULL,
"给定的URL为NULL",
"urlInfo为NULL",
"申请内存失败",
"协议部分必须以\"http://\"打头",
"缺少主机部分",
"主机部分格式错误",
"端口号必须是数字"
};
/**
* 分析URL
* IN:
* url: 给定的URL
* urlInfo: 存放结果。变量请事先定义,调用函数时会进行初始化
* OUT:
* NULL: 分析成功
* 非空: 分析失败,返回的是错误原因
*/
const char* parseURL(const char* url, URLINFO* urlInfo)
{
ERROR_CODE errno = NO_ERROR;
int urlLen;
char* p;
int hasPort = 0; /*是否有端口号标志*/
/* 检查输入 */
if (urlInfo == NULL)
{
errno = NO_URLINFO;
goto EXIT;
}
memset(urlInfo, 0, sizeof(URLINFO)); /* 初始化 */
if (url == NULL)
{
errno = NO_URL;
goto EXIT;
}
urlLen = strlen(url);
urlInfo->tmpbuf = (char*)malloc(urlLen + 1 + DEFAULT_FILE_LEN + 1);
if (urlInfo->tmpbuf == NULL) {
errno = MEM_ERROR;
goto EXIT;
}
strcpy(urlInfo->tmpbuf, url);
urlInfo->port = DEFAULT_PORT;
/*比较协议部分*/
if (strncasecmp(urlInfo->tmpbuf, HTTP_HEADING, HTTP_HEADING_LEN) != 0)
{
errno = UNSUPPORTED_PROTOCOL;
goto EXIT;
}
/* 取认证部分 */
urlInfo->user = urlInfo->tmpbuf+HTTP_HEADING_LEN;
p = strchr(urlInfo->user, '@');
if (p != NULL)
{
char* pTest;
*p = '\0'; /* 临时设为字符串结尾 */
pTest = strchr(urlInfo->user, '/');
if (pTest != NULL)
{
/* '@'在'/'后,不是用户认证信息,恢复'@' */
*p = '@';
urlInfo->user = NULL;
}
else
{
/* 认为有认证信息,找口令与用户名的分隔符 */
pTest = strchr(urlInfo->user, ':');
if (pTest != NULL)
{
*pTest = '\0';
urlInfo->passwd = pTest+1;
}
urlInfo->host = p+1; /* 主机起始位置确定 */
}
}
else
{
urlInfo->user = NULL;
}
/*取主机部分*/
if (urlInfo->user == NULL)
{
urlInfo->host = urlInfo->tmpbuf + HTTP_HEADING_LEN;
}
/*主机部分必须有字符且不能以'.'打头*/
if (urlInfo->host[0] == '\0')
{
errno = HOST_EXPECTED;
goto EXIT;
}
if (urlInfo->host[0] == '.')
{
errno = BAD_HOST_FORMAT;
goto EXIT;
}
for (p=(char*)(urlInfo->host); (*p!='\0' && *p!='/' && *p!=':'); p++);
/*主机部分不能以'.'结束*/
if (*(p-1) == '.')
{
errno = BAD_HOST_FORMAT;
goto EXIT;
}
switch (*p)
{
case '\0':
/*整个分析结束,复制缺省文件名*/
urlInfo->file = DEFAULT_FILE;
goto EXIT;
case ':':
/*有端口号*/
hasPort = 1;
break;
case '/':
/*文件开始*/
break;
}
*p++ = '\0'; /*字符串结束标志*/
/*取端口号*/
if (hasPort)
{
if (isdigit(*p))
{
urlInfo->port = atoi(p);
/*跳过所有的数字*/
for (; (*p != '\0') && isdigit(*p); p++);
}
/*如果后面仍有字符,必须是'/'*/
if (*p != '\0' && *p != '/')
{
errno = BAD_PORT_FORMAT;
goto EXIT;
}
/*处理文件*/
if (*p == '/')
{
urlInfo->file = p;
}
else
{
urlInfo->file = DEFAULT_FILE;
}
goto GETQUERY;
}
/*取文件*/
urlInfo->file = p; /*设置指针头部*/
p = urlInfo->tmpbuf + urlLen;
while (p >= urlInfo->file)
{
*(p+1) = *p;
p--;
}
*((char*)(urlInfo->file)) = '/';
GETQUERY:
/* 取query */
urlInfo->query = strchr(urlInfo->file, '?');
if (urlInfo->query != NULL)
{
urlInfo->query++;
}
EXIT:
if (errno != NO_ERROR)
{
/* 如果有错误,释放分配的空间 */
freeURLInfo(urlInfo);
}
return ERROR_MESSAGE[errno];
}
/**
* 释放结果中的动态空间
* IN:
* urlInfo: 存放结果的结果。
*/
void freeURLInfo(URLINFO* urlInfo)
{
if (urlInfo != NULL && urlInfo->tmpbuf != NULL)
{
free(urlInfo->tmpbuf);
urlInfo->tmpbuf = NULL;
}
}
/**
* 输出分析结果,仅用于测试
* IN:
* urlInfo: 存放结果的结果。
* out: 文件指针,如stdout
*/
void dumpURLInfo(URLINFO* urlInfo, FILE* out)
{
if (urlInfo == NULL || urlInfo->tmpbuf == NULL || out == NULL)
{
return;
}
fprintf(out, "host: %s\n"
"port: %d\n"
"file: %s\n"
"query: %s\n"
"user: %s\n"
"passwd: %s\n",
(urlInfo->host != NULL ? urlInfo->host : "[no host]"),
urlInfo->port,
(urlInfo->file != NULL ? urlInfo->file : "[no file]"),
(urlInfo->query != NULL ? urlInfo->query : "[no query]"),
(urlInfo->user != NULL ? urlInfo->user : "[no user]"),
(urlInfo->passwd != NULL ? urlInfo->passwd : "[no passwd]")
);
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -