📄 utils.c
字号:
/* Round TIMEOUTs smaller than 1 to 1, not to zero. This is because alarm(0) means "never deliver the alarm", i.e. "wait forever", which is not what someone who specifies a 0.5s timeout would expect. */ secs = 1; alarm (secs);#endif /* not ITIMER_REAL */}/* Cancel the alarm set with alarm_set. */static voidalarm_cancel (void){#ifdef ITIMER_REAL struct itimerval disable; xzero (disable); setitimer (ITIMER_REAL, &disable, NULL);#else /* not ITIMER_REAL */ alarm (0);#endif /* not ITIMER_REAL */}/* Call FUN(ARG), but don't allow it to run for more than TIMEOUT seconds. Returns true if the function was interrupted with a timeout, false otherwise. This works by setting up SIGALRM to be delivered in TIMEOUT seconds using setitimer() or alarm(). The timeout is enforced by longjumping out of the SIGALRM handler. This has several advantages compared to the traditional approach of relying on signals causing system calls to exit with EINTR: * The callback function is *forcibly* interrupted after the timeout expires, (almost) regardless of what it was doing and whether it was in a syscall. For example, a calculation that takes a long time is interrupted as reliably as an IO operation. * It works with both SYSV and BSD signals because it doesn't depend on the default setting of SA_RESTART. * It doesn't require special handler setup beyond a simple call to signal(). (It does use sigsetjmp/siglongjmp, but they're optional.) The only downside is that, if FUN allocates internal resources that are normally freed prior to exit from the functions, they will be lost in case of timeout. */boolrun_with_timeout (double timeout, void (*fun) (void *), void *arg){ int saved_errno; if (timeout == 0) { fun (arg); return false; } signal (SIGALRM, abort_run_with_timeout); if (SETJMP (run_with_timeout_env) != 0) { /* Longjumped out of FUN with a timeout. */ signal (SIGALRM, SIG_DFL); return true; } alarm_set (timeout); fun (arg); /* Preserve errno in case alarm() or signal() modifies it. */ saved_errno = errno; alarm_cancel (); signal (SIGALRM, SIG_DFL); errno = saved_errno; return false;}#else /* not USE_SIGNAL_TIMEOUT */#ifndef WINDOWS/* A stub version of run_with_timeout that just calls FUN(ARG). Don't define it under Windows, because Windows has its own version of run_with_timeout that uses threads. */boolrun_with_timeout (double timeout, void (*fun) (void *), void *arg){ fun (arg); return false;}#endif /* not WINDOWS */#endif /* not USE_SIGNAL_TIMEOUT */#ifndef WINDOWS/* Sleep the specified amount of seconds. On machines without nanosleep(), this may sleep shorter if interrupted by signals. */voidxsleep (double seconds){#ifdef HAVE_NANOSLEEP /* nanosleep is the preferred interface because it offers high accuracy and, more importantly, because it allows us to reliably restart receiving a signal such as SIGWINCH. (There was an actual Debian bug report about --limit-rate malfunctioning while the terminal was being resized.) */ struct timespec sleep, remaining; sleep.tv_sec = (long) seconds; sleep.tv_nsec = 1000000000 * (seconds - (long) seconds); while (nanosleep (&sleep, &remaining) < 0 && errno == EINTR) /* If nanosleep has been interrupted by a signal, adjust the sleeping period and return to sleep. */ sleep = remaining;#elif defined(HAVE_USLEEP) /* If usleep is available, use it in preference to select. */ if (seconds >= 1) { /* On some systems, usleep cannot handle values larger than 1,000,000. If the period is larger than that, use sleep first, then add usleep for subsecond accuracy. */ sleep (seconds); seconds -= (long) seconds; } usleep (seconds * 1000000);#else /* fall back select */ /* Note that, although Windows supports select, it can't be used to implement sleeping because Winsock's select doesn't implement timeout when it is passed NULL pointers for all fd sets. (But it does under Cygwin, which implements Unix-compatible select.) */ struct timeval sleep; sleep.tv_sec = (long) seconds; sleep.tv_usec = 1000000 * (seconds - (long) seconds); select (0, NULL, NULL, NULL, &sleep); /* If select returns -1 and errno is EINTR, it means we were interrupted by a signal. But without knowing how long we've actually slept, we can't return to sleep. Using gettimeofday to track sleeps is slow and unreliable due to clock skew. */#endif}#endif /* not WINDOWS *//* Encode the octets in DATA of length LENGTH to base64 format, storing the result to DEST. The output will be zero-terminated, and must point to a writable buffer of at least 1+BASE64_LENGTH(length) bytes. The function returns the length of the resulting base64 data, not counting the terminating zero. This implementation does not emit newlines after 76 characters of base64 data. */intbase64_encode (const void *data, int length, char *dest){ /* Conversion table. */ static const char tbl[64] = { 'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P', 'Q','R','S','T','U','V','W','X','Y','Z','a','b','c','d','e','f', 'g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v', 'w','x','y','z','0','1','2','3','4','5','6','7','8','9','+','/' }; /* Access bytes in DATA as unsigned char, otherwise the shifts below don't work for data with MSB set. */ const unsigned char *s = data; /* Theoretical ANSI violation when length < 3. */ const unsigned char *end = (const unsigned char *) data + length - 2; char *p = dest; /* Transform the 3x8 bits to 4x6 bits, as required by base64. */ for (; s < end; s += 3) { *p++ = tbl[s[0] >> 2]; *p++ = tbl[((s[0] & 3) << 4) + (s[1] >> 4)]; *p++ = tbl[((s[1] & 0xf) << 2) + (s[2] >> 6)]; *p++ = tbl[s[2] & 0x3f]; } /* Pad the result if necessary... */ switch (length % 3) { case 1: *p++ = tbl[s[0] >> 2]; *p++ = tbl[(s[0] & 3) << 4]; *p++ = '='; *p++ = '='; break; case 2: *p++ = tbl[s[0] >> 2]; *p++ = tbl[((s[0] & 3) << 4) + (s[1] >> 4)]; *p++ = tbl[((s[1] & 0xf) << 2)]; *p++ = '='; break; } /* ...and zero-terminate it. */ *p = '\0'; return p - dest;}/* Store in C the next non-whitespace character from the string, or \0 when end of string is reached. */#define NEXT_CHAR(c, p) do { \ c = (unsigned char) *p++; \} while (ISSPACE (c))#define IS_ASCII(c) (((c) & 0x80) == 0)/* Decode data from BASE64 (a null-terminated string) into memory pointed to by DEST. DEST is assumed to be large enough to accomodate the decoded data, which is guaranteed to be no more than 3/4*strlen(base64). Since DEST is assumed to contain binary data, it is not NUL-terminated. The function returns the length of the data written to TO. -1 is returned in case of error caused by malformed base64 input. This function originates from Free Recode. */intbase64_decode (const char *base64, void *dest){ /* Table of base64 values for first 128 characters. Note that this assumes ASCII (but so does Wget in other places). */ static const signed char base64_char_to_value[128] = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0- 9 */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 10- 19 */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 20- 29 */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 30- 39 */ -1, -1, -1, 62, -1, -1, -1, 63, 52, 53, /* 40- 49 */ 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, /* 50- 59 */ -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, /* 60- 69 */ 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, /* 70- 79 */ 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, /* 80- 89 */ 25, -1, -1, -1, -1, -1, -1, 26, 27, 28, /* 90- 99 */ 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, /* 100-109 */ 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, /* 110-119 */ 49, 50, 51, -1, -1, -1, -1, -1 /* 120-127 */ };#define BASE64_CHAR_TO_VALUE(c) ((int) base64_char_to_value[c])#define IS_BASE64(c) ((IS_ASCII (c) && BASE64_CHAR_TO_VALUE (c) >= 0) || c == '=') const char *p = base64; char *q = dest; while (1) { unsigned char c; unsigned long value; /* Process first byte of a quadruplet. */ NEXT_CHAR (c, p); if (!c) break; if (c == '=' || !IS_BASE64 (c)) return -1; /* illegal char while decoding base64 */ value = BASE64_CHAR_TO_VALUE (c) << 18; /* Process second byte of a quadruplet. */ NEXT_CHAR (c, p); if (!c) return -1; /* premature EOF while decoding base64 */ if (c == '=' || !IS_BASE64 (c)) return -1; /* illegal char while decoding base64 */ value |= BASE64_CHAR_TO_VALUE (c) << 12; *q++ = value >> 16; /* Process third byte of a quadruplet. */ NEXT_CHAR (c, p); if (!c) return -1; /* premature EOF while decoding base64 */ if (!IS_BASE64 (c)) return -1; /* illegal char while decoding base64 */ if (c == '=') { NEXT_CHAR (c, p); if (!c) return -1; /* premature EOF while decoding base64 */ if (c != '=') return -1; /* padding `=' expected but not found */ continue; } value |= BASE64_CHAR_TO_VALUE (c) << 6; *q++ = 0xff & value >> 8; /* Process fourth byte of a quadruplet. */ NEXT_CHAR (c, p); if (!c) return -1; /* premature EOF while decoding base64 */ if (c == '=') continue; if (!IS_BASE64 (c)) return -1; /* illegal char while decoding base64 */ value |= BASE64_CHAR_TO_VALUE (c); *q++ = 0xff & value; }#undef IS_BASE64#undef BASE64_CHAR_TO_VALUE return q - (char *) dest;}#undef IS_ASCII#undef NEXT_CHAR/* Simple merge sort for use by stable_sort. Implementation courtesy Zeljko Vrba with additional debugging by Nenad Barbutov. */static voidmergesort_internal (void *base, void *temp, size_t size, size_t from, size_t to, int (*cmpfun) (const void *, const void *)){#define ELT(array, pos) ((char *)(array) + (pos) * size) if (from < to) { size_t i, j, k; size_t mid = (to + from) / 2; mergesort_internal (base, temp, size, from, mid, cmpfun); mergesort_internal (base, temp, size, mid + 1, to, cmpfun); i = from; j = mid + 1; for (k = from; (i <= mid) && (j <= to); k++) if (cmpfun (ELT (base, i), ELT (base, j)) <= 0) memcpy (ELT (temp, k), ELT (base, i++), size); else memcpy (ELT (temp, k), ELT (base, j++), size); while (i <= mid) memcpy (ELT (temp, k++), ELT (base, i++), size); while (j <= to) memcpy (ELT (temp, k++), ELT (base, j++), size); for (k = from; k <= to; k++) memcpy (ELT (base, k), ELT (temp, k), size); }#undef ELT}/* Stable sort with interface exactly like standard library's qsort. Uses mergesort internally, allocating temporary storage with alloca. */voidstable_sort (void *base, size_t nmemb, size_t size, int (*cmpfun) (const void *, const void *)){ if (size > 1) { void *temp = alloca (nmemb * size * sizeof (void *)); mergesort_internal (base, temp, size, 0, nmemb - 1, cmpfun); }}/* Print a decimal number. If it is equal to or larger than ten, the number is rounded. Otherwise it is printed with one significant digit without trailing zeros and with no more than three fractional digits total. For example, 0.1 is printed as "0.1", 0.035 is printed as "0.04", 0.0091 as "0.009", and 0.0003 as simply "0". This is useful for displaying durations because it provides order-of-magnitude information without unnecessary clutter -- long-running downloads are shown without the fractional part, and short ones still retain one significant digit. */const char *print_decimal (double number){ static char buf[32]; double n = number >= 0 ? number : -number; if (n >= 9.95) /* Cut off at 9.95 because the below %.1f would round 9.96 to "10.0" instead of "10". OTOH 9.94 will print as "9.9". */ snprintf (buf, sizeof buf, "%.0f", number); else if (n >= 0.95) snprintf (buf, sizeof buf, "%.1f", number); else if (n >= 0.001) snprintf (buf, sizeof buf, "%.1g", number); else if (n >= 0.0005) /* round [0.0005, 0.001) to 0.001 */ snprintf (buf, sizeof buf, "%.3f", number); else /* print numbers close to 0 as 0, not 0.000 */ strcpy (buf, "0"); return buf;}#ifdef TESTINGconst char *test_subdir_p(){ int i; struct { char *d1; char *d2; bool result; } test_array[] = { { "/somedir", "/somedir", true }, { "/somedir", "/somedir/d2", true }, { "/somedir/d1", "/somedir", false }, }; for (i = 0; i < countof(test_array); ++i) { bool res = subdir_p (test_array[i].d1, test_array[i].d2); mu_assert ("test_subdir_p: wrong result", res == test_array[i].result); } return NULL;}const char *test_dir_matches_p(){ int i; struct { char *dirlist[3]; char *dir; bool result; } test_array[] = { { { "/somedir", "/someotherdir", NULL }, "somedir", true }, { { "/somedir", "/someotherdir", NULL }, "anotherdir", false }, { { "/somedir", "/*otherdir", NULL }, "anotherdir", true }, { { "/somedir/d1", "/someotherdir", NULL }, "somedir/d1", true }, { { "*/*d1", "/someotherdir", NULL }, "somedir/d1", true }, { { "/somedir/d1", "/someotherdir", NULL }, "d1", false },
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -