📄 utils.c
字号:
if (suf[0] && !strcasecmp (suf + 1, "html")) return true; return false;}/* Read a line from FP and return the pointer to freshly allocated storage. The storage space is obtained through malloc() and should be freed with free() when it is no longer needed. The length of the line is not limited, except by available memory. The newline character at the end of line is retained. The line is terminated with a zero character. After end-of-file is encountered without anything being read, NULL is returned. NULL is also returned on error. To distinguish between these two cases, use the stdio function ferror(). */char *read_whole_line (FILE *fp){ int length = 0; int bufsize = 82; char *line = xmalloc (bufsize); while (fgets (line + length, bufsize - length, fp)) { length += strlen (line + length); if (length == 0) /* Possible for example when reading from a binary file where a line begins with \0. */ continue; if (line[length - 1] == '\n') break; /* fgets() guarantees to read the whole line, or to use up the space we've given it. We can double the buffer unconditionally. */ bufsize <<= 1; line = xrealloc (line, bufsize); } if (length == 0 || ferror (fp)) { xfree (line); return NULL; } if (length + 1 < bufsize) /* Relieve the memory from our exponential greediness. We say `length + 1' because the terminating \0 is not included in LENGTH. We don't need to zero-terminate the string ourselves, though, because fgets() does that. */ line = xrealloc (line, length + 1); return line;}/* Read FILE into memory. A pointer to `struct file_memory' are returned; use struct element `content' to access file contents, and the element `length' to know the file length. `content' is *not* zero-terminated, and you should *not* read or write beyond the [0, length) range of characters. After you are done with the file contents, call read_file_free to release the memory. Depending on the operating system and the type of file that is being read, read_file() either mmap's the file into memory, or reads the file into the core using read(). If file is named "-", fileno(stdin) is used for reading instead. If you want to read from a real file named "-", use "./-" instead. */struct file_memory *read_file (const char *file){ int fd; struct file_memory *fm; long size; bool inhibit_close = false; /* Some magic in the finest tradition of Perl and its kin: if FILE is "-", just use stdin. */ if (HYPHENP (file)) { fd = fileno (stdin); inhibit_close = true; /* Note that we don't inhibit mmap() in this case. If stdin is redirected from a regular file, mmap() will still work. */ } else fd = open (file, O_RDONLY); if (fd < 0) return NULL; fm = xnew (struct file_memory);#ifdef HAVE_MMAP { struct_fstat buf; if (fstat (fd, &buf) < 0) goto mmap_lose; fm->length = buf.st_size; /* NOTE: As far as I know, the callers of this function never modify the file text. Relying on this would enable us to specify PROT_READ and MAP_SHARED for a marginal gain in efficiency, but at some cost to generality. */ fm->content = mmap (NULL, fm->length, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0); if (fm->content == (char *)MAP_FAILED) goto mmap_lose; if (!inhibit_close) close (fd); fm->mmap_p = 1; return fm; } mmap_lose: /* The most common reason why mmap() fails is that FD does not point to a plain file. However, it's also possible that mmap() doesn't work for a particular type of file. Therefore, whenever mmap() fails, we just fall back to the regular method. */#endif /* HAVE_MMAP */ fm->length = 0; size = 512; /* number of bytes fm->contents can hold at any given time. */ fm->content = xmalloc (size); while (1) { wgint nread; if (fm->length > size / 2) { /* #### I'm not sure whether the whole exponential-growth thing makes sense with kernel read. On Linux at least, read() refuses to read more than 4K from a file at a single chunk anyway. But other Unixes might optimize it better, and it doesn't *hurt* anything, so I'm leaving it. */ /* Normally, we grow SIZE exponentially to make the number of calls to read() and realloc() logarithmic in relation to file size. However, read() can read an amount of data smaller than requested, and it would be unreasonable to double SIZE every time *something* was read. Therefore, we double SIZE only when the length exceeds half of the entire allocated size. */ size <<= 1; fm->content = xrealloc (fm->content, size); } nread = read (fd, fm->content + fm->length, size - fm->length); if (nread > 0) /* Successful read. */ fm->length += nread; else if (nread < 0) /* Error. */ goto lose; else /* EOF */ break; } if (!inhibit_close) close (fd); if (size > fm->length && fm->length != 0) /* Due to exponential growth of fm->content, the allocated region might be much larger than what is actually needed. */ fm->content = xrealloc (fm->content, fm->length); fm->mmap_p = 0; return fm; lose: if (!inhibit_close) close (fd); xfree (fm->content); xfree (fm); return NULL;}/* Release the resources held by FM. Specifically, this calls munmap() or xfree() on fm->content, depending whether mmap or malloc/read were used to read in the file. It also frees the memory needed to hold the FM structure itself. */voidread_file_free (struct file_memory *fm){#ifdef HAVE_MMAP if (fm->mmap_p) { munmap (fm->content, fm->length); } else#endif { xfree (fm->content); } xfree (fm);}/* Free the pointers in a NULL-terminated vector of pointers, then free the pointer itself. */voidfree_vec (char **vec){ if (vec) { char **p = vec; while (*p) xfree (*p++); xfree (vec); }}/* Append vector V2 to vector V1. The function frees V2 and reallocates V1 (thus you may not use the contents of neither pointer after the call). If V1 is NULL, V2 is returned. */char **merge_vecs (char **v1, char **v2){ int i, j; if (!v1) return v2; if (!v2) return v1; if (!*v2) { /* To avoid j == 0 */ xfree (v2); return v1; } /* Count v1. */ for (i = 0; v1[i]; i++) ; /* Count v2. */ for (j = 0; v2[j]; j++) ; /* Reallocate v1. */ v1 = xrealloc (v1, (i + j + 1) * sizeof (char **)); memcpy (v1 + i, v2, (j + 1) * sizeof (char *)); xfree (v2); return v1;}/* Append a freshly allocated copy of STR to VEC. If VEC is NULL, it is allocated as needed. Return the new value of the vector. */char **vec_append (char **vec, const char *str){ int cnt; /* count of vector elements, including the one we're about to append */ if (vec != NULL) { for (cnt = 0; vec[cnt]; cnt++) ; ++cnt; } else cnt = 1; /* Reallocate the array to fit the new element and the NULL. */ vec = xrealloc (vec, (cnt + 1) * sizeof (char *)); /* Append a copy of STR to the vector. */ vec[cnt - 1] = xstrdup (str); vec[cnt] = NULL; return vec;}/* Sometimes it's useful to create "sets" of strings, i.e. special hash tables where you want to store strings as keys and merely query for their existence. Here is a set of utility routines that makes that transparent. */voidstring_set_add (struct hash_table *ht, const char *s){ /* First check whether the set element already exists. If it does, do nothing so that we don't have to free() the old element and then strdup() a new one. */ if (hash_table_contains (ht, s)) return; /* We use "1" as value. It provides us a useful and clear arbitrary value, and it consumes no memory -- the pointers to the same string "1" will be shared by all the key-value pairs in all `set' hash tables. */ hash_table_put (ht, xstrdup (s), "1");}/* Synonym for hash_table_contains... */intstring_set_contains (struct hash_table *ht, const char *s){ return hash_table_contains (ht, s);}/* Convert the specified string set to array. ARRAY should be large enough to hold hash_table_count(ht) char pointers. */void string_set_to_array (struct hash_table *ht, char **array){ hash_table_iterator iter; for (hash_table_iterate (ht, &iter); hash_table_iter_next (&iter); ) *array++ = iter.key;}/* Free the string set. This frees both the storage allocated for keys and the actual hash table. (hash_table_destroy would only destroy the hash table.) */voidstring_set_free (struct hash_table *ht){ hash_table_iterator iter; for (hash_table_iterate (ht, &iter); hash_table_iter_next (&iter); ) xfree (iter.key); hash_table_destroy (ht);}/* Utility function: simply call xfree() on all keys and values of HT. */voidfree_keys_and_values (struct hash_table *ht){ hash_table_iterator iter; for (hash_table_iterate (ht, &iter); hash_table_iter_next (&iter); ) { xfree (iter.key); xfree (iter.value); }}/* Get digit grouping data for thousand separors by calling localeconv(). The data includes separator string and grouping info and is cached after the first call to the function. In locales that don't set a thousand separator (such as the "C" locale), this forces it to be ",". We are now only showing thousand separators in one place, so this shouldn't be a problem in practice. */static voidget_grouping_data (const char **sep, const char **grouping){ static const char *cached_sep; static const char *cached_grouping; static bool initialized; if (!initialized) { /* Get the grouping info from the locale. */ struct lconv *lconv = localeconv (); cached_sep = lconv->thousands_sep; cached_grouping = lconv->grouping;#if ! USE_NLS_PROGRESS_BAR /* We can't count column widths, so ensure that the separator * is single-byte only (let check below determine what byte). */ if (strlen(cached_sep) > 1) cached_sep = "";#endif if (!*cached_sep) { /* Many locales (such as "C" or "hr_HR") don't specify grouping, which we still want to use it for legibility. In those locales set the sep char to ',', unless that character is used for decimal point, in which case set it to ".". */ if (*lconv->decimal_point != ',') cached_sep = ","; else cached_sep = "."; cached_grouping = "\x03"; } initialized = true; } *sep = cached_sep; *grouping = cached_grouping;}/* Return a printed representation of N with thousand separators. This should respect locale settings, with the exception of the "C" locale which mandates no separator, but we use one anyway. Unfortunately, we cannot use %'d (in fact it would be %'j) to get the separators because it's too non-portable, and it's hard to test for this feature at configure time. Besides, it wouldn't display separators in the "C" locale, still used by many Unix users. */const char *with_thousand_seps (wgint n){ static char outbuf[48]; char *p = outbuf + sizeof outbuf; /* Info received from locale */ const char *grouping, *sep; int seplen; /* State information */ int i = 0, groupsize; const char *atgroup; bool negative = n < 0; /* Initialize grouping data. */ get_grouping_data (&sep, &grouping); seplen = strlen (sep); atgroup = grouping; groupsize = *atgroup++; /* This would overflow on WGINT_MIN, but printing negative numbers is not an important goal of this fuinction. */ if (negative) n = -n; /* Write the number into the buffer, backwards, inserting the separators as necessary. */ *--p = '\0'; while (1) { *--p = n % 10 + '0'; n /= 10; if (n == 0) break; /* Prepend SEP to every groupsize'd digit and get new groupsize. */ if (++i == groupsize) { if (seplen == 1) *--p = *sep; else memcpy (p -= seplen, sep, seplen); i = 0; if (*atgroup) groupsize = *atgroup++; } } if (negative) *--p = '-';
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -