📄 util.c
字号:
if (string < eos) {
size_t line_len = (eos-string) + pCurLen + 2;
char *line = tor_malloc(line_len);
memcpy(line, prefixCur, pCurLen);
memcpy(line+pCurLen, string, eos-string);
line[line_len-2] = '\n';
line[line_len-1] = '\0';
smartlist_add(out, line);
}
}
/* =====
* Time
* ===== */
/** Return the number of microseconds elapsed between *start and *end.
*/
long
tv_udiff(const struct timeval *start, const struct timeval *end)
{
long udiff;
long secdiff = end->tv_sec - start->tv_sec;
if (labs(secdiff+1) > LONG_MAX/1000000) {
log_warn(LD_GENERAL, "comparing times too far apart.");
return LONG_MAX;
}
udiff = secdiff*1000000L + (end->tv_usec - start->tv_usec);
return udiff;
}
/** Yield true iff <b>y</b> is a leap-year. */
#define IS_LEAPYEAR(y) (!(y % 4) && ((y % 100) || !(y % 400)))
/** Helper: Return the number of leap-days between Jan 1, y1 and Jan 1, y2. */
static int
n_leapdays(int y1, int y2)
{
--y1;
--y2;
return (y2/4 - y1/4) - (y2/100 - y1/100) + (y2/400 - y1/400);
}
/** Number of days per month in non-leap year; used by tor_timegm. */
static const int days_per_month[] =
{ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
/** Return a time_t given a struct tm. The result is given in GMT, and
* does not account for leap seconds.
*/
time_t
tor_timegm(struct tm *tm)
{
/* This is a pretty ironclad timegm implementation, snarfed from Python2.2.
* It's way more brute-force than fiddling with tzset().
*/
time_t ret;
unsigned long year, days, hours, minutes;
int i;
year = tm->tm_year + 1900;
if (year < 1970 || tm->tm_mon < 0 || tm->tm_mon > 11) {
log_warn(LD_BUG, "Out-of-range argument to tor_timegm");
return -1;
}
tor_assert(year < INT_MAX);
days = 365 * (year-1970) + n_leapdays(1970,(int)year);
for (i = 0; i < tm->tm_mon; ++i)
days += days_per_month[i];
if (tm->tm_mon > 1 && IS_LEAPYEAR(year))
++days;
days += tm->tm_mday - 1;
hours = days*24 + tm->tm_hour;
minutes = hours*60 + tm->tm_min;
ret = minutes*60 + tm->tm_sec;
return ret;
}
/* strftime is locale-specific, so we need to replace those parts */
static const char *WEEKDAY_NAMES[] =
{ "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
static const char *MONTH_NAMES[] =
{ "Jan", "Feb", "Mar", "Apr", "May", "Jun",
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
/** Set <b>buf</b> to the RFC1123 encoding of the GMT value of <b>t</b>.
* The buffer must be at least RFC1123_TIME_LEN+1 bytes long.
*
* (RFC1123 format is Fri, 29 Sep 2006 15:54:20 GMT)
*/
void
format_rfc1123_time(char *buf, time_t t)
{
struct tm tm;
tor_gmtime_r(&t, &tm);
strftime(buf, RFC1123_TIME_LEN+1, "___, %d ___ %Y %H:%M:%S GMT", &tm);
tor_assert(tm.tm_wday >= 0);
tor_assert(tm.tm_wday <= 6);
memcpy(buf, WEEKDAY_NAMES[tm.tm_wday], 3);
tor_assert(tm.tm_wday >= 0);
tor_assert(tm.tm_mon <= 11);
memcpy(buf+8, MONTH_NAMES[tm.tm_mon], 3);
}
/** Parse the the RFC1123 encoding of some time (in GMT) from <b>buf</b>,
* and store the result in *<b>t</b>.
*
* Return 0 on succcess, -1 on failure.
*/
int
parse_rfc1123_time(const char *buf, time_t *t)
{
struct tm tm;
char month[4];
char weekday[4];
int i, m;
if (strlen(buf) != RFC1123_TIME_LEN)
return -1;
memset(&tm, 0, sizeof(tm));
if (sscanf(buf, "%3s, %d %3s %d %d:%d:%d GMT", weekday,
&tm.tm_mday, month, &tm.tm_year, &tm.tm_hour,
&tm.tm_min, &tm.tm_sec) < 7) {
char *esc = esc_for_log(buf);
log_warn(LD_GENERAL, "Got invalid RFC1123 time %s", esc);
tor_free(esc);
return -1;
}
m = -1;
for (i = 0; i < 12; ++i) {
if (!strcmp(month, MONTH_NAMES[i])) {
m = i;
break;
}
}
if (m<0) {
char *esc = esc_for_log(buf);
log_warn(LD_GENERAL, "Got invalid RFC1123 time %s: No such month", esc);
tor_free(esc);
return -1;
}
tm.tm_mon = m;
if (tm.tm_year < 1970) {
char *esc = esc_for_log(buf);
log_warn(LD_GENERAL,
"Got invalid RFC1123 time %s. (Before 1970)", esc);
tor_free(esc);
return -1;
}
tm.tm_year -= 1900;
*t = tor_timegm(&tm);
return 0;
}
/** Set <b>buf</b> to the ISO8601 encoding of the local value of <b>t</b>.
* The buffer must be at least ISO_TIME_LEN+1 bytes long.
*
* (ISO8601 format is 2006-10-29 10:57:20)
*/
void
format_local_iso_time(char *buf, time_t t)
{
struct tm tm;
strftime(buf, ISO_TIME_LEN+1, "%Y-%m-%d %H:%M:%S", tor_localtime_r(&t, &tm));
}
/** Set <b>buf</b> to the ISO8601 encoding of the GMT value of <b>t</b>.
* The buffer must be at least ISO_TIME_LEN+1 bytes long.
*/
void
format_iso_time(char *buf, time_t t)
{
struct tm tm;
strftime(buf, ISO_TIME_LEN+1, "%Y-%m-%d %H:%M:%S", tor_gmtime_r(&t, &tm));
}
/** Given an ISO-formatted UTC time value (after the epoch) in <b>cp</b>,
* parse it and store its value in *<b>t</b>. Return 0 on success, -1 on
* failure. Ignore extraneous stuff in <b>cp</b> separated by whitespace from
* the end of the time string. */
int
parse_iso_time(const char *cp, time_t *t)
{
struct tm st_tm;
#ifdef HAVE_STRPTIME
if (!strptime(cp, "%Y-%m-%d %H:%M:%S", &st_tm)) {
log_warn(LD_GENERAL, "ISO time was unparseable by strptime"); return -1;
}
#else
unsigned int year=0, month=0, day=0, hour=100, minute=100, second=100;
if (sscanf(cp, "%u-%u-%u %u:%u:%u", &year, &month,
&day, &hour, &minute, &second) < 6) {
log_warn(LD_GENERAL, "ISO time was unparseable"); return -1;
}
if (year < 1970 || month < 1 || month > 12 || day < 1 || day > 31 ||
hour > 23 || minute > 59 || second > 61) {
log_warn(LD_GENERAL, "ISO time was nonsensical"); return -1;
}
st_tm.tm_year = year-1900;
st_tm.tm_mon = month-1;
st_tm.tm_mday = day;
st_tm.tm_hour = hour;
st_tm.tm_min = minute;
st_tm.tm_sec = second;
#endif
if (st_tm.tm_year < 70) {
char *esc = esc_for_log(cp);
log_warn(LD_GENERAL, "Got invalid ISO time %s. (Before 1970)", esc);
tor_free(esc);
return -1;
}
*t = tor_timegm(&st_tm);
return 0;
}
/** Given a <b>date</b> in one of the three formats allowed by HTTP (ugh),
* parse it into <b>tm</b>. Return 0 on success, negative on failure. */
int
parse_http_time(const char *date, struct tm *tm)
{
const char *cp;
char month[4];
char wkday[4];
int i;
tor_assert(tm);
memset(tm, 0, sizeof(*tm));
/* First, try RFC1123 or RFC850 format: skip the weekday. */
if ((cp = strchr(date, ','))) {
++cp;
if (sscanf(date, "%2d %3s %4d %2d:%2d:%2d GMT",
&tm->tm_mday, month, &tm->tm_year,
&tm->tm_hour, &tm->tm_min, &tm->tm_sec) == 6) {
/* rfc1123-date */
tm->tm_year -= 1900;
} else if (sscanf(date, "%2d-%3s-%2d %2d:%2d:%2d GMT",
&tm->tm_mday, month, &tm->tm_year,
&tm->tm_hour, &tm->tm_min, &tm->tm_sec) == 6) {
/* rfc850-date */
} else {
return -1;
}
} else {
/* No comma; possibly asctime() format. */
if (sscanf(date, "%3s %3s %2d %2d:%2d:%2d %4d",
wkday, month, &tm->tm_mday,
&tm->tm_hour, &tm->tm_min, &tm->tm_sec, &tm->tm_year) == 7) {
tm->tm_year -= 1900;
} else {
return -1;
}
}
month[3] = '\0';
/* Okay, now decode the month. */
for (i = 0; i < 12; ++i) {
if (!strcasecmp(MONTH_NAMES[i], month)) {
tm->tm_mon = i+1;
}
}
if (tm->tm_year < 0 ||
tm->tm_mon < 1 || tm->tm_mon > 12 ||
tm->tm_mday < 0 || tm->tm_mday > 31 ||
tm->tm_hour < 0 || tm->tm_hour > 23 ||
tm->tm_min < 0 || tm->tm_min > 59 ||
tm->tm_sec < 0 || tm->tm_sec > 61)
return -1; /* Out of range, or bad month. */
return 0;
}
/** Given an <b>interval</b> in seconds, try to write it to the
* <b>out_len</b>-byte buffer in <b>out</b> in a human-readable form.
* Return 0 on success, -1 on failure.
*/
int
format_time_interval(char *out, size_t out_len, long interval)
{
/* We only report seconds if there's no hours. */
long sec = 0, min = 0, hour = 0, day = 0;
if (interval < 0)
interval = -interval;
if (interval >= 86400) {
day = interval / 86400;
interval %= 86400;
}
if (interval >= 3600) {
hour = interval / 3600;
interval %= 3600;
}
if (interval >= 60) {
min = interval / 60;
interval %= 60;
}
sec = interval;
if (day) {
return tor_snprintf(out, out_len, "%ld days, %ld hours, %ld minutes",
day, hour, min);
} else if (hour) {
return tor_snprintf(out, out_len, "%ld hours, %ld minutes", hour, min);
} else if (min) {
return tor_snprintf(out, out_len, "%ld minutes, %ld seconds", min, sec);
} else {
return tor_snprintf(out, out_len, "%ld seconds", sec);
}
}
/* =====
* Fuzzy time
* XXXX021 Use this consistently or rip it out.
* ===== */
/* In a perfect world, everybody would run ntp, and ntp would be perfect, so
* if we wanted to know "Is the current time before time X?" we could just say
* "time(NULL) < X".
*
* But unfortunately, many users are running Tor in an imperfect world, on
* even more imperfect computers. Hence, we need to track time oddly. We
* model the user's computer as being "skewed" from accurate time by
* -<b>ftime_skew</b> seconds, such that our best guess of the current time is
* time(NULL)+ftime_skew. We also assume that our measurements of time may
* have up to <b>ftime_slop</b> seconds of inaccuracy; IOW, our window of
* estimate for the current time is now + ftime_skew +/- ftime_slop.
*/
static int ftime_skew = 0;
static int ftime_slop = 60;
void
ftime_set_maximum_sloppiness(int seconds)
{
tor_assert(seconds >= 0);
ftime_slop = seconds;
}
void
ftime_set_estimated_skew(int seconds)
{
ftime_skew = seconds;
}
#if 0
void
ftime_get_window(time_t now, ftime_t *ft_out)
{
ft_out->earliest = now + ftime_skew - ftime_slop;
ft_out->latest = now + ftime_skew + ftime_slop;
}
#endif
int
ftime_maybe_after(time_t now, time_t when)
{
/* It may be after when iff the latest possible current time is after when */
return (now + ftime_skew + ftime_slop) >= when;
}
int
ftime_maybe_before(time_t now, time_t when)
{
/* It may be before when iff the earliest possible current time is before */
return (now + ftime_skew - ftime_slop) < when;
}
int
ftime_definitely_after(time_t now, time_t when)
{
/* It is definitely after when if the earliest time it could be is still
* after when. */
return (now + ftime_skew - ftime_slop) >= when;
}
int
ftime_definitely_before(time_t now, time_t when)
{
/* It is definitely before when if the latest time it could be is still
* before when. */
return (now + ftime_skew + ftime_slop) < when;
}
/* =====
* File helpers
* ===== */
/** Write <b>count</b> bytes from <b>buf</b> to <b>fd</b>. <b>isSocket</b>
* must be 1 if fd was returned by socket() or accept(), and 0 if fd
* was returned by open(). Return the number of bytes written, or -1
* on error. Only use if fd is a blocking fd. */
int
write_all(int fd, const char *buf, size_t count, int isSocket)
{
size_t written = 0;
ssize_t result;
tor_assert(count < INT_MAX); /*XXXX021 make returnval an ssize_t */
while (written != count) {
if (isSocket)
result = tor_socket_send(fd, buf+written, count-written, 0);
else
result = write(fd, buf+written, count-written);
if (result<0)
return -1;
written += result;
}
return (int)count;
}
/** Read from <b>fd</b> to <b>buf</b>, until we get <b>count</b> bytes
* or reach the end of the file. <b>isSocket</b> must be 1 if fd
* was returned by socket() or accept(), and 0 if fd was returned by
* open(). Return the number of bytes read, or -1 on error. Only use
* if fd is a blocking fd. */
int
read_all(int fd, char *buf, size_t count, int isSocket)
{
/*XXXX021 return ssize_t. */
size_t numread = 0;
ssize_t result;
if (count > SIZE_T_CEILING || count > INT_MAX)
return -1;
while (numread != count) {
if (isSocket)
result = tor_socket_recv(fd, buf+numread, count-numread, 0);
else
result = read(fd, buf+numread, count-numread);
if (result<0)
return -1;
else if (result == 0)
break;
numread += result;
}
return (int)numread;
}
/*
* Filesystem operations.
*/
/** Clean up <b>name</b> so that we can use it in a call to "stat". On Unix,
* we do nothing. On Windows, we remove a trailing slash, unless the path is
* the root of a disk. */
static void
clean_name_for_stat(char *name)
{
#ifdef MS_WINDOWS
size_t len = strlen(name);
if (!len)
return;
if (name[len-1]=='\\' || name[len-1]=='/') {
if (len == 1 || (len==3 && name[1]==':'))
return;
name[len-1]='\0';
}
#else
(void)name;
#endif
}
/** Return FN_ERROR if filename can't be read, FN_NOENT if it doesn't
* exist, FN_FILE if it is a regular file, or FN_DIR if it's a
* directory. On FN_ERROR, sets errno. */
file_status_t
file_status(const char *fname)
{
struct stat st;
char *f;
int r;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -