11b49b804e
- Don't build the grub2-efi-ia32-* packages on i686 (pjones) - Add efi-export-env and efi-load-env commands (pjones) - Make it possible to subtract conditions from debug= (pjones) - Try to set -fPIE and friends on libgnu.a (pjones) - Add more options to blscfg command to make it more flexible - Add support for prepend early initrds to the BLS entries - Fix grub.cfg-XXX look up when booting over TFTP Signed-off-by: Javier Martinez Canillas <javierm@redhat.com>
591 lines
17 KiB
Diff
591 lines
17 KiB
Diff
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
|
From: Javier Martinez Canillas <javierm@redhat.com>
|
|
Date: Mon, 21 Jan 2019 17:33:13 +0100
|
|
Subject: [PATCH] blscfg: store the BLS entries in a sorted linked list
|
|
|
|
The parsed BLS entries are stored in an array, that it's sorted
|
|
using the quick sort algorithm before populating the boot menu.
|
|
|
|
This works on the assumption that all BLS entries are parsed at
|
|
the same time and that are displayed just after being retrieved.
|
|
|
|
But the support could be made more flexible, and have different
|
|
commands to load and display the entries. Keeping the BLS in a
|
|
sorted link simplify the code considerably, while not making it
|
|
much less efficient.
|
|
|
|
While being there, mark entries that have been used to populate
|
|
the boot menu so multiple calls to the blscfg command don't add
|
|
duplicated entries.
|
|
|
|
Signed-off-by: Javier Martinez Canillas <javierm@redhat.com>
|
|
---
|
|
grub-core/Makefile.core.def | 1 -
|
|
grub-core/commands/blscfg.c | 177 ++++++++++++----------------
|
|
grub-core/commands/bls_qsort.h | 255 -----------------------------------------
|
|
3 files changed, 71 insertions(+), 362 deletions(-)
|
|
delete mode 100644 grub-core/commands/bls_qsort.h
|
|
|
|
diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def
|
|
index 017080bd599..8a00c6177e1 100644
|
|
--- a/grub-core/Makefile.core.def
|
|
+++ b/grub-core/Makefile.core.def
|
|
@@ -796,7 +796,6 @@ module = {
|
|
module = {
|
|
name = blscfg;
|
|
common = commands/blscfg.c;
|
|
- common = commands/bls_qsort.h;
|
|
common = commands/loadenv.h;
|
|
enable = powerpc_ieee1275;
|
|
enable = efi;
|
|
diff --git a/grub-core/commands/blscfg.c b/grub-core/commands/blscfg.c
|
|
index c432c6ba27a..304d73908ae 100644
|
|
--- a/grub-core/commands/blscfg.c
|
|
+++ b/grub-core/commands/blscfg.c
|
|
@@ -19,6 +19,7 @@
|
|
* along with GRUB. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
+#include <grub/list.h>
|
|
#include <grub/types.h>
|
|
#include <grub/misc.h>
|
|
#include <grub/mm.h>
|
|
@@ -36,7 +37,6 @@
|
|
|
|
GRUB_MOD_LICENSE ("GPLv3+");
|
|
|
|
-#include "bls_qsort.h"
|
|
#include "loadenv.h"
|
|
|
|
#define GRUB_BLS_CONFIG_PATH "/loader/entries/"
|
|
@@ -54,45 +54,17 @@ struct keyval
|
|
|
|
struct bls_entry
|
|
{
|
|
+ struct bls_entry *next;
|
|
+ struct bls_entry **prev;
|
|
struct keyval **keyvals;
|
|
int nkeyvals;
|
|
char *filename;
|
|
+ bool visible;
|
|
};
|
|
|
|
-static struct bls_entry **entries;
|
|
-static int nentries;
|
|
+static struct bls_entry *entries = NULL;
|
|
|
|
-static struct bls_entry *bls_new_entry(void)
|
|
-{
|
|
- struct bls_entry **new_entries;
|
|
- struct bls_entry *entry;
|
|
- int new_n = nentries + 1;
|
|
-
|
|
- new_entries = grub_realloc (entries, new_n * sizeof (struct bls_entry *));
|
|
- if (!new_entries)
|
|
- {
|
|
- grub_error (GRUB_ERR_OUT_OF_MEMORY,
|
|
- "couldn't find space for BLS entry list");
|
|
- return NULL;
|
|
- }
|
|
-
|
|
- entries = new_entries;
|
|
-
|
|
- entry = grub_malloc (sizeof (*entry));
|
|
- if (!entry)
|
|
- {
|
|
- grub_error (GRUB_ERR_OUT_OF_MEMORY,
|
|
- "couldn't find space for BLS entry list");
|
|
- return NULL;
|
|
- }
|
|
-
|
|
- grub_memset (entry, 0, sizeof (*entry));
|
|
- entries[nentries] = entry;
|
|
-
|
|
- nentries = new_n;
|
|
-
|
|
- return entry;
|
|
-}
|
|
+#define FOR_BLS_ENTRIES(var) FOR_LIST_ELEMENTS (var, entries)
|
|
|
|
static int bls_add_keyval(struct bls_entry *entry, char *key, char *val)
|
|
{
|
|
@@ -138,24 +110,6 @@ static int bls_add_keyval(struct bls_entry *entry, char *key, char *val)
|
|
return 0;
|
|
}
|
|
|
|
-static void bls_free_entry(struct bls_entry *entry)
|
|
-{
|
|
- int i;
|
|
-
|
|
- for (i = 0; i < entry->nkeyvals; i++)
|
|
- {
|
|
- struct keyval *kv = entry->keyvals[i];
|
|
- grub_free ((void *)kv->key);
|
|
- grub_free (kv->val);
|
|
- grub_free (kv);
|
|
- }
|
|
-
|
|
- grub_free (entry->keyvals);
|
|
- grub_free (entry->filename);
|
|
- grub_memset (entry, 0, sizeof (*entry));
|
|
- grub_free (entry);
|
|
-}
|
|
-
|
|
/* Find they value of the key named by keyname. If there are allowed to be
|
|
* more than one, pass a pointer to an int set to -1 the first time, and pass
|
|
* the same pointer through each time after, and it'll return them in sorted
|
|
@@ -387,43 +341,17 @@ split_cmp(char *nvr0, char *nvr1, int has_name)
|
|
return ret;
|
|
}
|
|
|
|
-/* return 1: p0 is newer than p1 */
|
|
-/* 0: p0 and p1 are the same version */
|
|
-/* -1: p1 is newer than p0 */
|
|
-static int bls_cmp(const void *p0, const void *p1, void *state)
|
|
+/* return 1: e0 is newer than e1 */
|
|
+/* 0: e0 and e1 are the same version */
|
|
+/* -1: e1 is newer than e0 */
|
|
+static int bls_cmp(const struct bls_entry *e0, const struct bls_entry *e1)
|
|
{
|
|
- struct bls_entry * e0 = *(struct bls_entry **)p0;
|
|
- struct bls_entry * e1 = *(struct bls_entry **)p1;
|
|
- bool use_version = *(bool *)state;
|
|
- char *v0, *v1;
|
|
char *id0, *id1;
|
|
- int l, r;
|
|
-
|
|
- if (use_version)
|
|
- {
|
|
- v0 = grub_strdup(bls_get_val(e0, "version", NULL));
|
|
- v1 = grub_strdup(bls_get_val(e1, "version", NULL));
|
|
-
|
|
- r = split_cmp(v0, v1, 0);
|
|
-
|
|
- grub_free(v0);
|
|
- grub_free(v1);
|
|
-
|
|
- if (r != 0)
|
|
- return r;
|
|
- }
|
|
+ int r;
|
|
|
|
id0 = grub_strdup(e0->filename);
|
|
id1 = grub_strdup(e1->filename);
|
|
|
|
- l = grub_strlen(id0);
|
|
- if (l > 5 && grub_strcmp(id0 + l - 5, ".conf"))
|
|
- id0[l-5] = '\0';
|
|
-
|
|
- l = grub_strlen(id1);
|
|
- if (l > 5 && grub_strcmp(id1 + l - 5, ".conf"))
|
|
- id1[l-5] = '\0';
|
|
-
|
|
r = split_cmp(id0, id1, 1);
|
|
|
|
grub_free(id0);
|
|
@@ -432,6 +360,53 @@ static int bls_cmp(const void *p0, const void *p1, void *state)
|
|
return r;
|
|
}
|
|
|
|
+static void list_add_tail(grub_list_t head, grub_list_t item)
|
|
+{
|
|
+ item->next = head;
|
|
+ if (head->prev)
|
|
+ (*head->prev)->next = item;
|
|
+ item->prev = head->prev;
|
|
+ head->prev = &item;
|
|
+}
|
|
+
|
|
+static int bls_add_entry(struct bls_entry *entry)
|
|
+{
|
|
+ struct bls_entry *e, *last = NULL;
|
|
+ int rc;
|
|
+
|
|
+ if (!entries) {
|
|
+ grub_dprintf ("blscfg", "Add entry with id \"%s\"\n", entry->filename);
|
|
+ entries = entry;
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ FOR_BLS_ENTRIES(e) {
|
|
+ rc = bls_cmp(entry, e);
|
|
+
|
|
+ if (!rc)
|
|
+ return GRUB_ERR_BAD_ARGUMENT;
|
|
+
|
|
+ if (rc == 1) {
|
|
+ grub_dprintf ("blscfg", "Add entry with id \"%s\"\n", entry->filename);
|
|
+ list_add_tail (GRUB_AS_LIST (e), GRUB_AS_LIST (entry));
|
|
+ if (e == entries) {
|
|
+ entries = entry;
|
|
+ entry->prev = NULL;
|
|
+ }
|
|
+ return 0;
|
|
+ }
|
|
+ last = e;
|
|
+ }
|
|
+
|
|
+ if (last) {
|
|
+ grub_dprintf ("blscfg", "Add entry with id \"%s\"\n", entry->filename);
|
|
+ last->next = entry;
|
|
+ entry->prev = &last;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
struct read_entry_info {
|
|
const char *devid;
|
|
const char *dirname;
|
|
@@ -442,6 +417,7 @@ static int read_entry (
|
|
const struct grub_dirhook_info *dirhook_info UNUSED,
|
|
void *data)
|
|
{
|
|
+ int rc = 0;
|
|
grub_size_t n;
|
|
char *p;
|
|
grub_file_t f = NULL;
|
|
@@ -471,7 +447,7 @@ static int read_entry (
|
|
if (sz == GRUB_FILE_SIZE_UNKNOWN || sz > 1024*1024)
|
|
goto finish;
|
|
|
|
- entry = bls_new_entry();
|
|
+ entry = grub_zalloc (sizeof (*entry));
|
|
if (!entry)
|
|
goto finish;
|
|
|
|
@@ -485,7 +461,6 @@ static int read_entry (
|
|
{
|
|
char *buf;
|
|
char *separator;
|
|
- int rc;
|
|
|
|
buf = grub_file_getline (f);
|
|
if (!buf)
|
|
@@ -519,6 +494,9 @@ static int read_entry (
|
|
break;
|
|
}
|
|
|
|
+ if (!rc)
|
|
+ bls_add_entry(entry);
|
|
+
|
|
finish:
|
|
grub_free (p);
|
|
|
|
@@ -779,10 +757,10 @@ struct find_entry_info {
|
|
static int find_entry (struct find_entry_info *info)
|
|
{
|
|
struct read_entry_info read_entry_info;
|
|
+ struct bls_entry *entry = NULL;
|
|
grub_fs_t blsdir_fs = NULL;
|
|
grub_device_t blsdir_dev = NULL;
|
|
const char *blsdir = NULL;
|
|
- bool use_version = true;
|
|
int fallback = 0;
|
|
int r = 0;
|
|
|
|
@@ -810,7 +788,7 @@ read_fallback:
|
|
} while (e);
|
|
}
|
|
|
|
- if (!nentries && !fallback) {
|
|
+ if (!entries && !fallback) {
|
|
read_entry_info.dirname = "/boot" GRUB_BLS_CONFIG_PATH;
|
|
grub_dprintf ("blscfg", "Entries weren't found in %s, fallback to %s\n",
|
|
blsdir, read_entry_info.dirname);
|
|
@@ -818,27 +796,14 @@ read_fallback:
|
|
goto read_fallback;
|
|
}
|
|
|
|
- grub_dprintf ("blscfg", "Sorting %d entries\n", nentries);
|
|
+ grub_dprintf ("blscfg", "%s Creating entries from bls\n", __func__);
|
|
+ FOR_BLS_ENTRIES(entry) {
|
|
+ if (entry->visible)
|
|
+ continue;
|
|
|
|
- for (r = 0; r < nentries && use_version; r++) {
|
|
- if (!bls_get_val(entries[r], "version", NULL))
|
|
- use_version = false;
|
|
+ create_entry(entry);
|
|
+ entry->visible = true;
|
|
}
|
|
-
|
|
- bls_qsort(&entries[0], nentries, sizeof (struct bls_entry *), bls_cmp, &use_version);
|
|
-
|
|
- grub_dprintf ("blscfg", "%s Creating %d entries from bls\n", __func__, nentries);
|
|
- for (r = nentries - 1; r >= 0; r--)
|
|
- create_entry(entries[r]);
|
|
-
|
|
- for (r = 0; r < nentries; r++)
|
|
- bls_free_entry (entries[r]);
|
|
-
|
|
- nentries = 0;
|
|
-
|
|
- grub_free (entries);
|
|
- entries = NULL;
|
|
-
|
|
return 0;
|
|
}
|
|
|
|
diff --git a/grub-core/commands/bls_qsort.h b/grub-core/commands/bls_qsort.h
|
|
deleted file mode 100644
|
|
index 572765fa3f2..00000000000
|
|
--- a/grub-core/commands/bls_qsort.h
|
|
+++ /dev/null
|
|
@@ -1,255 +0,0 @@
|
|
-/* quicksort
|
|
- * This file from the GNU C Library.
|
|
- * Copyright (C) 1991-2016 Free Software Foundation, Inc.
|
|
- * Written by Douglas C. Schmidt (schmidt@ics.uci.edu).
|
|
- *
|
|
- * GRUB -- GRand Unified Bootloader
|
|
- *
|
|
- * GRUB is free software: you can redistribute it and/or modify
|
|
- * it under the terms of the GNU General Public License as published by
|
|
- * the Free Software Foundation, either version 3 of the License, or
|
|
- * (at your option) any later version.
|
|
- *
|
|
- * GRUB is distributed in the hope that it will be useful,
|
|
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
- * GNU General Public License for more details.
|
|
- *
|
|
- * You should have received a copy of the GNU General Public License
|
|
- * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
|
|
- */
|
|
-
|
|
-/* If you consider tuning this algorithm, you should consult first:
|
|
- Engineering a sort function; Jon Bentley and M. Douglas McIlroy;
|
|
- Software - Practice and Experience; Vol. 23 (11), 1249-1265, 1993. */
|
|
-
|
|
-#include <grub/types.h>
|
|
-#include <grub/misc.h>
|
|
-#include <grub/mm.h>
|
|
-
|
|
-#define CHAR_BIT 8
|
|
-
|
|
-/* Byte-wise swap two items of size SIZE. */
|
|
-#define SWAP(a, b, size) \
|
|
- do \
|
|
- { \
|
|
- grub_size_t __size = (size); \
|
|
- char *__a = (a), *__b = (b); \
|
|
- do \
|
|
- { \
|
|
- char __tmp = *__a; \
|
|
- *__a++ = *__b; \
|
|
- *__b++ = __tmp; \
|
|
- } while (--__size > 0); \
|
|
- } while (0)
|
|
-
|
|
-/* Discontinue quicksort algorithm when partition gets below this size.
|
|
- This particular magic number was chosen to work best on a Sun 4/260. */
|
|
-#define MAX_THRESH 4
|
|
-
|
|
-/* Stack node declarations used to store unfulfilled partition obligations. */
|
|
-typedef struct
|
|
- {
|
|
- char *lo;
|
|
- char *hi;
|
|
- } stack_node;
|
|
-
|
|
-/* The next 4 #defines implement a very fast in-line stack abstraction. */
|
|
-/* The stack needs log (total_elements) entries (we could even subtract
|
|
- log(MAX_THRESH)). Since total_elements has type grub_size_t, we get as
|
|
- upper bound for log (total_elements):
|
|
- bits per byte (CHAR_BIT) * sizeof(grub_size_t). */
|
|
-#define STACK_SIZE (CHAR_BIT * sizeof(grub_size_t))
|
|
-#define PUSH(low, high) ((void) ((top->lo = (low)), (top->hi = (high)), ++top))
|
|
-#define POP(low, high) ((void) (--top, (low = top->lo), (high = top->hi)))
|
|
-#define STACK_NOT_EMPTY (stack < top)
|
|
-
|
|
-typedef int (*grub_compar_d_fn_t) (const void *p0, const void *p1, void *state);
|
|
-
|
|
-/* Order size using quicksort. This implementation incorporates
|
|
- four optimizations discussed in Sedgewick:
|
|
-
|
|
- 1. Non-recursive, using an explicit stack of pointer that store the
|
|
- next array partition to sort. To save time, this maximum amount
|
|
- of space required to store an array of SIZE_MAX is allocated on the
|
|
- stack. Assuming a 32-bit (64 bit) integer for grub_size_t, this needs
|
|
- only 32 * sizeof(stack_node) == 256 bytes (for 64 bit: 1024 bytes).
|
|
- Pretty cheap, actually.
|
|
-
|
|
- 2. Chose the pivot element using a median-of-three decision tree.
|
|
- This reduces the probability of selecting a bad pivot value and
|
|
- eliminates certain extraneous comparisons.
|
|
-
|
|
- 3. Only quicksorts TOTAL_ELEMS / MAX_THRESH partitions, leaving
|
|
- insertion sort to order the MAX_THRESH items within each partition.
|
|
- This is a big win, since insertion sort is faster for small, mostly
|
|
- sorted array segments.
|
|
-
|
|
- 4. The larger of the two sub-partitions is always pushed onto the
|
|
- stack first, with the algorithm then concentrating on the
|
|
- smaller partition. This *guarantees* no more than log (total_elems)
|
|
- stack size is needed (actually O(1) in this case)! */
|
|
-
|
|
-static inline void UNUSED
|
|
-bls_qsort (void *const pbase, grub_size_t total_elems, grub_size_t size,
|
|
- grub_compar_d_fn_t cmp, void *arg)
|
|
-{
|
|
- char *base_ptr = (char *) pbase;
|
|
-
|
|
- const grub_size_t max_thresh = MAX_THRESH * size;
|
|
-
|
|
- if (total_elems == 0)
|
|
- /* Avoid lossage with unsigned arithmetic below. */
|
|
- return;
|
|
-
|
|
- if (total_elems > MAX_THRESH)
|
|
- {
|
|
- char *lo = base_ptr;
|
|
- char *hi = &lo[size * (total_elems - 1)];
|
|
- stack_node stack[STACK_SIZE];
|
|
- stack_node *top = stack;
|
|
-
|
|
- PUSH (NULL, NULL);
|
|
-
|
|
- while (STACK_NOT_EMPTY)
|
|
- {
|
|
- char *left_ptr;
|
|
- char *right_ptr;
|
|
-
|
|
- /* Select median value from among LO, MID, and HI. Rearrange
|
|
- LO and HI so the three values are sorted. This lowers the
|
|
- probability of picking a pathological pivot value and
|
|
- skips a comparison for both the LEFT_PTR and RIGHT_PTR in
|
|
- the while loops. */
|
|
-
|
|
- char *mid = lo + size * ((hi - lo) / size >> 1);
|
|
-
|
|
- if ((*cmp) ((void *) mid, (void *) lo, arg) < 0)
|
|
- SWAP (mid, lo, size);
|
|
- if ((*cmp) ((void *) hi, (void *) mid, arg) < 0)
|
|
- SWAP (mid, hi, size);
|
|
- else
|
|
- goto jump_over;
|
|
- if ((*cmp) ((void *) mid, (void *) lo, arg) < 0)
|
|
- SWAP (mid, lo, size);
|
|
- jump_over:;
|
|
-
|
|
- left_ptr = lo + size;
|
|
- right_ptr = hi - size;
|
|
-
|
|
- /* Here's the famous ``collapse the walls'' section of quicksort.
|
|
- Gotta like those tight inner loops! They are the main reason
|
|
- that this algorithm runs much faster than others. */
|
|
- do
|
|
- {
|
|
- while ((*cmp) ((void *) left_ptr, (void *) mid, arg) < 0)
|
|
- left_ptr += size;
|
|
-
|
|
- while ((*cmp) ((void *) mid, (void *) right_ptr, arg) < 0)
|
|
- right_ptr -= size;
|
|
-
|
|
- if (left_ptr < right_ptr)
|
|
- {
|
|
- SWAP (left_ptr, right_ptr, size);
|
|
- if (mid == left_ptr)
|
|
- mid = right_ptr;
|
|
- else if (mid == right_ptr)
|
|
- mid = left_ptr;
|
|
- left_ptr += size;
|
|
- right_ptr -= size;
|
|
- }
|
|
- else if (left_ptr == right_ptr)
|
|
- {
|
|
- left_ptr += size;
|
|
- right_ptr -= size;
|
|
- break;
|
|
- }
|
|
- }
|
|
- while (left_ptr <= right_ptr);
|
|
-
|
|
- /* Set up pointers for next iteration. First determine whether
|
|
- left and right partitions are below the threshold size. If so,
|
|
- ignore one or both. Otherwise, push the larger partition's
|
|
- bounds on the stack and continue sorting the smaller one. */
|
|
-
|
|
- if ((grub_size_t) (right_ptr - lo) <= max_thresh)
|
|
- {
|
|
- if ((grub_size_t) (hi - left_ptr) <= max_thresh)
|
|
- /* Ignore both small partitions. */
|
|
- POP (lo, hi);
|
|
- else
|
|
- /* Ignore small left partition. */
|
|
- lo = left_ptr;
|
|
- }
|
|
- else if ((grub_size_t) (hi - left_ptr) <= max_thresh)
|
|
- /* Ignore small right partition. */
|
|
- hi = right_ptr;
|
|
- else if ((right_ptr - lo) > (hi - left_ptr))
|
|
- {
|
|
- /* Push larger left partition indices. */
|
|
- PUSH (lo, right_ptr);
|
|
- lo = left_ptr;
|
|
- }
|
|
- else
|
|
- {
|
|
- /* Push larger right partition indices. */
|
|
- PUSH (left_ptr, hi);
|
|
- hi = right_ptr;
|
|
- }
|
|
- }
|
|
- }
|
|
-
|
|
- /* Once the BASE_PTR array is partially sorted by quicksort the rest
|
|
- is completely sorted using insertion sort, since this is efficient
|
|
- for partitions below MAX_THRESH size. BASE_PTR points to the beginning
|
|
- of the array to sort, and END_PTR points at the very last element in
|
|
- the array (*not* one beyond it!). */
|
|
-
|
|
-#define min(x, y) ((x) < (y) ? (x) : (y))
|
|
-
|
|
- {
|
|
- char *const end_ptr = &base_ptr[size * (total_elems - 1)];
|
|
- char *tmp_ptr = base_ptr;
|
|
- char *thresh = min(end_ptr, base_ptr + max_thresh);
|
|
- char *run_ptr;
|
|
-
|
|
- /* Find smallest element in first threshold and place it at the
|
|
- array's beginning. This is the smallest array element,
|
|
- and the operation speeds up insertion sort's inner loop. */
|
|
-
|
|
- for (run_ptr = tmp_ptr + size; run_ptr <= thresh; run_ptr += size)
|
|
- if ((*cmp) ((void *) run_ptr, (void *) tmp_ptr, arg) < 0)
|
|
- tmp_ptr = run_ptr;
|
|
-
|
|
- if (tmp_ptr != base_ptr)
|
|
- SWAP (tmp_ptr, base_ptr, size);
|
|
-
|
|
- /* Insertion sort, running from left-hand-side up to right-hand-side. */
|
|
-
|
|
- run_ptr = base_ptr + size;
|
|
- while ((run_ptr += size) <= end_ptr)
|
|
- {
|
|
- tmp_ptr = run_ptr - size;
|
|
- while ((*cmp) ((void *) run_ptr, (void *) tmp_ptr, arg) < 0)
|
|
- tmp_ptr -= size;
|
|
-
|
|
- tmp_ptr += size;
|
|
- if (tmp_ptr != run_ptr)
|
|
- {
|
|
- char *trav;
|
|
-
|
|
- trav = run_ptr + size;
|
|
- while (--trav >= run_ptr)
|
|
- {
|
|
- char c = *trav;
|
|
- char *hi, *lo;
|
|
-
|
|
- for (hi = lo = trav; (lo -= size) >= tmp_ptr; hi = lo)
|
|
- *hi = *lo;
|
|
- *hi = c;
|
|
- }
|
|
- }
|
|
- }
|
|
- }
|
|
-}
|
|
-
|