]> git.ipfire.org Git - people/ms/strongswan.git/blame - src/libstrongswan/utils/utils.c
utils: Return plain drive letter as base/pathname for drive letters on Windows
[people/ms/strongswan.git] / src / libstrongswan / utils / utils.c
CommitLineData
552cc11b 1/*
766141bc 2 * Copyright (C) 2008-2014 Tobias Brunner
552cc11b
MW
3 * Copyright (C) 2005-2008 Martin Willi
4 * Hochschule fuer Technik Rapperswil
5 *
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation; either version 2 of the License, or (at your
9 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
10 *
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * for more details.
552cc11b
MW
15 */
16
766141bc 17#define _GNU_SOURCE /* for memrchr */
4de7401a
MW
18#ifdef WIN32
19/* for GetTickCount64, Windows 7 */
20# define _WIN32_WINNT 0x0601
21#endif
22
23#include "utils.h"
24
6c20579a 25#include <sys/stat.h>
552cc11b 26#include <string.h>
552cc11b 27#include <stdio.h>
6c20579a 28#include <unistd.h>
876961cf 29#include <inttypes.h>
74b14b40 30#include <stdint.h>
d24a74c5 31#include <limits.h>
6c20579a 32#include <dirent.h>
f464d750 33#include <time.h>
552cc11b 34
f1c9653e
MW
35#include <library.h>
36#include <utils/debug.h>
37#include <utils/chunk.h>
38#include <collections/enumerator.h>
39#include <threading/spinlock.h>
552cc11b 40
a8809bb0 41ENUM(status_names, SUCCESS, NEED_MORE,
552cc11b
MW
42 "SUCCESS",
43 "FAILED",
44 "OUT_OF_RES",
45 "ALREADY_DONE",
46 "NOT_SUPPORTED",
47 "INVALID_ARG",
48 "NOT_FOUND",
49 "PARSE_ERROR",
50 "VERIFY_ERROR",
51 "INVALID_STATE",
52 "DESTROY_ME",
53 "NEED_MORE",
54);
55
552cc11b
MW
56/**
57 * Described in header.
58 */
01e43e31 59void memxor(u_int8_t dst[], u_int8_t src[], size_t n)
552cc11b 60{
01e43e31 61 int m, i;
7daf5226 62
01e43e31 63 /* byte wise XOR until dst aligned */
09846603 64 for (i = 0; (uintptr_t)&dst[i] % sizeof(long) && i < n; i++)
4fd233a7 65 {
01e43e31 66 dst[i] ^= src[i];
4fd233a7 67 }
01e43e31 68 /* try to use words if src shares an aligment with dst */
74b14b40 69 switch (((uintptr_t)&src[i] % sizeof(long)))
552cc11b 70 {
01e43e31
MW
71 case 0:
72 for (m = n - sizeof(long); i <= m; i += sizeof(long))
73 {
74 *(long*)&dst[i] ^= *(long*)&src[i];
75 }
76 break;
77 case sizeof(int):
78 for (m = n - sizeof(int); i <= m; i += sizeof(int))
79 {
80 *(int*)&dst[i] ^= *(int*)&src[i];
81 }
82 break;
83 case sizeof(short):
84 for (m = n - sizeof(short); i <= m; i += sizeof(short))
85 {
86 *(short*)&dst[i] ^= *(short*)&src[i];
87 }
88 break;
89 default:
90 break;
91 }
92 /* byte wise XOR of the rest */
93 for (; i < n; i++)
94 {
95 dst[i] ^= src[i];
552cc11b
MW
96 }
97}
98
ed678b52
MW
99/**
100 * Described in header.
101 */
102void memwipe_noinline(void *ptr, size_t n)
103{
104 memwipe_inline(ptr, n);
105}
106
81736d7d
TB
107/**
108 * Described in header.
109 */
110void *memstr(const void *haystack, const char *needle, size_t n)
111{
2ed241ae 112 const u_char *pos = haystack;
7b91011d
TB
113 size_t l;
114
115 if (!haystack || !needle || (l = strlen(needle)) == 0)
116 {
117 return NULL;
118 }
81736d7d
TB
119 for (; n >= l; ++pos, --n)
120 {
121 if (memeq(pos, needle, l))
122 {
123 return (void*)pos;
124 }
125 }
126 return NULL;
127}
128
2ed241ae
TB
129/**
130 * Described in header.
131 */
132void *utils_memrchr(const void *s, int c, size_t n)
133{
134 const u_char *pos;
135
136 if (!s || !n)
137 {
138 return NULL;
139 }
140
141 for (pos = s + n - 1; pos >= (u_char*)s; pos--)
142 {
143 if (*pos == (u_char)c)
144 {
145 return (void*)pos;
146 }
147 }
148 return NULL;
149}
150
d543d9ca
TB
151/**
152 * Described in header.
153 */
154char* translate(char *str, const char *from, const char *to)
155{
156 char *pos = str;
157 if (strlen(from) != strlen(to))
158 {
159 return str;
160 }
161 while (pos && *pos)
162 {
163 char *match;
164 if ((match = strchr(from, *pos)) != NULL)
165 {
166 *pos = to[match - from];
167 }
168 pos++;
169 }
170 return str;
ccb6758e
TB
171}
172
173/**
174 * Described in header.
175 */
176char* strreplace(const char *str, const char *search, const char *replace)
177{
178 size_t len, slen, rlen, count = 0;
179 char *res, *pos, *found, *dst;
180
181 if (!str || !*str || !search || !*search || !replace)
182 {
183 return (char*)str;
184 }
185 slen = strlen(search);
186 rlen = strlen(replace);
187 if (slen != rlen)
188 {
189 for (pos = (char*)str; (pos = strstr(pos, search)); pos += slen)
190 {
191 found = pos;
192 count++;
193 }
194 if (!count)
195 {
196 return (char*)str;
197 }
198 len = (found - str) + strlen(found) + count * (rlen - slen);
199 }
200 else
201 {
202 len = strlen(str);
203 }
204 found = strstr(str, search);
205 if (!found)
206 {
207 return (char*)str;
208 }
209 dst = res = malloc(len + 1);
210 pos = (char*)str;
211 do
212 {
213 len = found - pos;
214 memcpy(dst, pos, len);
215 dst += len;
216 memcpy(dst, replace, rlen);
217 dst += rlen;
218 pos = found + slen;
219 }
220 while ((found = strstr(pos, search)));
221 strcpy(dst, pos);
222 return res;
d543d9ca
TB
223}
224
766141bc
TB
225/**
226 * Described in header.
227 */
228char* path_dirname(const char *path)
229{
230 char *pos;
231
8182631b 232 pos = path ? strrchr(path, DIRECTORY_SEPARATOR[0]) : NULL;
766141bc
TB
233
234 if (pos && !pos[1])
235 { /* if path ends with slashes we have to look beyond them */
8182631b 236 while (pos > path && *pos == DIRECTORY_SEPARATOR[0])
766141bc
TB
237 { /* skip trailing slashes */
238 pos--;
239 }
8182631b 240 pos = memrchr(path, DIRECTORY_SEPARATOR[0], pos - path + 1);
766141bc
TB
241 }
242 if (!pos)
243 {
2496eaff
MW
244#ifdef WIN32
245 if (path && strlen(path))
246 {
247 if ((isalpha(path[0]) && path[1] == ':'))
248 { /* if just a drive letter given, return that as dirname */
249 return chunk_clone(chunk_from_chars(path[0], ':', 0)).ptr;
250 }
251 }
252#endif
766141bc
TB
253 return strdup(".");
254 }
8182631b 255 while (pos > path && *pos == DIRECTORY_SEPARATOR[0])
766141bc
TB
256 { /* skip superfluous slashes */
257 pos--;
258 }
259 return strndup(path, pos - path + 1);
260}
261
262/**
263 * Described in header.
264 */
265char* path_basename(const char *path)
266{
267 char *pos, *trail = NULL;
268
269 if (!path || !*path)
270 {
271 return strdup(".");
272 }
8182631b 273 pos = strrchr(path, DIRECTORY_SEPARATOR[0]);
766141bc
TB
274 if (pos && !pos[1])
275 { /* if path ends with slashes we have to look beyond them */
8182631b 276 while (pos > path && *pos == DIRECTORY_SEPARATOR[0])
766141bc
TB
277 { /* skip trailing slashes */
278 pos--;
279 }
8182631b 280 if (pos == path && *pos == DIRECTORY_SEPARATOR[0])
766141bc 281 { /* contains only slashes */
8182631b 282 return strdup(DIRECTORY_SEPARATOR);
766141bc
TB
283 }
284 trail = pos + 1;
8182631b 285 pos = memrchr(path, DIRECTORY_SEPARATOR[0], trail - path);
766141bc
TB
286 }
287 pos = pos ? pos + 1 : (char*)path;
288 return trail ? strndup(pos, trail - pos) : strdup(pos);
289}
290
6c20579a
TB
291/**
292 * Described in header.
293 */
294bool mkdir_p(const char *path, mode_t mode)
295{
fc1afcc8 296 int len;
6c20579a
TB
297 char *pos, full[PATH_MAX];
298 pos = full;
299 if (!path || *path == '\0')
300 {
301 return TRUE;
302 }
303 len = snprintf(full, sizeof(full)-1, "%s", path);
304 if (len < 0 || len >= sizeof(full)-1)
305 {
8b0e0910 306 DBG1(DBG_LIB, "path string %s too long", path);
6c20579a
TB
307 return FALSE;
308 }
309 /* ensure that the path ends with a '/' */
310 if (full[len-1] != '/')
311 {
312 full[len++] = '/';
313 full[len] = '\0';
314 }
315 /* skip '/' at the beginning */
316 while (*pos == '/')
317 {
318 pos++;
319 }
320 while ((pos = strchr(pos, '/')))
321 {
322 *pos = '\0';
323 if (access(full, F_OK) < 0)
324 {
a3f7dfc1
MW
325#ifdef WIN32
326 if (_mkdir(full) < 0)
327#else
6c20579a 328 if (mkdir(full, mode) < 0)
a3f7dfc1 329#endif
6c20579a 330 {
8b0e0910 331 DBG1(DBG_LIB, "failed to create directory %s", full);
6c20579a
TB
332 return FALSE;
333 }
334 }
335 *pos = '/';
336 pos++;
337 }
338 return TRUE;
339}
340
4d174272
MW
341ENUM(tty_color_names, TTY_RESET, TTY_BG_DEF,
342 "\e[0m",
343 "\e[1m",
344 "\e[4m",
345 "\e[5m",
346 "\e[30m",
347 "\e[31m",
348 "\e[32m",
349 "\e[33m",
350 "\e[34m",
351 "\e[35m",
352 "\e[36m",
353 "\e[37m",
354 "\e[39m",
355 "\e[40m",
356 "\e[41m",
357 "\e[42m",
358 "\e[43m",
359 "\e[44m",
360 "\e[45m",
361 "\e[46m",
362 "\e[47m",
363 "\e[49m",
364);
365
366/**
367 * Get the escape string for a given TTY color, empty string on non-tty FILE
368 */
369char* tty_escape_get(int fd, tty_escape_t escape)
370{
371 if (!isatty(fd))
372 {
373 return "";
374 }
375 switch (escape)
376 {
377 case TTY_RESET:
378 case TTY_BOLD:
379 case TTY_UNDERLINE:
380 case TTY_BLINKING:
1f2b8c8c
MW
381#ifdef WIN32
382 return "";
383#endif
4d174272
MW
384 case TTY_FG_BLACK:
385 case TTY_FG_RED:
386 case TTY_FG_GREEN:
387 case TTY_FG_YELLOW:
388 case TTY_FG_BLUE:
389 case TTY_FG_MAGENTA:
390 case TTY_FG_CYAN:
391 case TTY_FG_WHITE:
392 case TTY_FG_DEF:
393 case TTY_BG_BLACK:
394 case TTY_BG_RED:
395 case TTY_BG_GREEN:
396 case TTY_BG_YELLOW:
397 case TTY_BG_BLUE:
398 case TTY_BG_MAGENTA:
399 case TTY_BG_CYAN:
400 case TTY_BG_WHITE:
401 case TTY_BG_DEF:
402 return enum_to_name(tty_color_names, escape);
1f2b8c8c 403 /* warn if a escape code is missing */
4d174272
MW
404 }
405 return "";
406}
2a595276 407
9a8fdc15
TB
408#ifndef HAVE_CLOSEFROM
409/**
410 * Described in header.
411 */
412void closefrom(int lowfd)
413{
5051bd23
TB
414 char fd_dir[PATH_MAX];
415 int maxfd, fd, len;
416
417 /* try to close only open file descriptors on Linux... */
418 len = snprintf(fd_dir, sizeof(fd_dir), "/proc/%u/fd", getpid());
4a4cf41b 419 if (len > 0 && len < sizeof(fd_dir) && access(fd_dir, F_OK) == 0)
5051bd23
TB
420 {
421 enumerator_t *enumerator = enumerator_create_directory(fd_dir);
422 if (enumerator)
423 {
68fcf917 424 char *rel;
5051bd23
TB
425 while (enumerator->enumerate(enumerator, &rel, NULL, NULL))
426 {
427 fd = atoi(rel);
428 if (fd >= lowfd)
429 {
430 close(fd);
431 }
432 }
433 enumerator->destroy(enumerator);
434 return;
435 }
436 }
437
438 /* ...fall back to closing all fds otherwise */
d3c30b35
MW
439#ifdef WIN32
440 maxfd = _getmaxstdio();
441#else
9a8fdc15 442 maxfd = (int)sysconf(_SC_OPEN_MAX);
d3c30b35 443#endif
9a8fdc15
TB
444 if (maxfd < 0)
445 {
446 maxfd = 256;
447 }
448 for (fd = lowfd; fd < maxfd; fd++)
449 {
450 close(fd);
451 }
452}
453#endif /* HAVE_CLOSEFROM */
454
3f310c0d
MW
455/**
456 * Return monotonic time
457 */
458time_t time_monotonic(timeval_t *tv)
459{
4de7401a
MW
460#ifdef WIN32
461 ULONGLONG ms;
462 time_t s;
463
464 ms = GetTickCount64();
465 s = ms / 1000;
466 if (tv)
467 {
468 tv->tv_sec = s;
469 tv->tv_usec = (ms - (s * 1000)) * 1000;
470 }
471 return s;
472#else /* !WIN32 */
b2944d71
TB
473#if defined(HAVE_CLOCK_GETTIME) && \
474 (defined(HAVE_CONDATTR_CLOCK_MONOTONIC) || \
475 defined(HAVE_PTHREAD_COND_TIMEDWAIT_MONOTONIC))
3d5818ec
MW
476 /* as we use time_monotonic() for condvar operations, we use the
477 * monotonic time source only if it is also supported by pthread. */
3f310c0d 478 timespec_t ts;
7daf5226 479
3f310c0d
MW
480 if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0)
481 {
482 if (tv)
483 {
484 tv->tv_sec = ts.tv_sec;
485 tv->tv_usec = ts.tv_nsec / 1000;
486 }
487 return ts.tv_sec;
488 }
b2944d71 489#endif /* HAVE_CLOCK_GETTIME && (...) */
3f310c0d
MW
490 /* Fallback to non-monotonic timestamps:
491 * On MAC OS X, creating monotonic timestamps is rather difficult. We
492 * could use mach_absolute_time() and catch sleep/wakeup notifications.
3d5818ec
MW
493 * We stick to the simpler (non-monotonic) gettimeofday() for now.
494 * But keep in mind: we need the same time source here as in condvar! */
3f310c0d
MW
495 if (!tv)
496 {
497 return time(NULL);
498 }
499 if (gettimeofday(tv, NULL) != 0)
500 { /* should actually never fail if passed pointers are valid */
501 return -1;
502 }
503 return tv->tv_sec;
4de7401a 504#endif /* !WIN32 */
3f310c0d
MW
505}
506
081ae2eb
MW
507/**
508 * return null
509 */
510void *return_null()
511{
512 return NULL;
513}
514
da17b016
MW
515/**
516 * returns TRUE
517 */
518bool return_true()
519{
520 return TRUE;
521}
522
523/**
524 * returns FALSE
525 */
526bool return_false()
527{
528 return FALSE;
529}
530
502edf42
MW
531/**
532 * returns FAILED
533 */
534status_t return_failed()
535{
536 return FAILED;
537}
538
4755ab50
MW
539/**
540 * returns SUCCESS
541 */
542status_t return_success()
543{
544 return SUCCESS;
545}
546
233b853d
MW
547/**
548 * nop operation
549 */
550void nop()
551{
552}
553
0f603d42 554#if !defined(HAVE_GCC_ATOMIC_OPERATIONS) && !defined(HAVE_GCC_SYNC_OPERATIONS)
efd0fe21 555
552cc11b 556/**
f1c9653e 557 * Spinlock for ref_get/put
552cc11b 558 */
f1c9653e 559static spinlock_t *ref_lock;
552cc11b
MW
560
561/**
efd0fe21 562 * Increase refcount
552cc11b 563 */
3160b92a 564refcount_t ref_get(refcount_t *ref)
552cc11b 565{
3160b92a
MW
566 refcount_t current;
567
f1c9653e 568 ref_lock->lock(ref_lock);
3160b92a 569 current = ++(*ref);
f1c9653e
MW
570 ref_lock->unlock(ref_lock);
571
3160b92a 572 return current;
552cc11b
MW
573}
574
575/**
efd0fe21 576 * Decrease refcount
552cc11b
MW
577 */
578bool ref_put(refcount_t *ref)
579{
580 bool more_refs;
7daf5226 581
f1c9653e 582 ref_lock->lock(ref_lock);
21f411b8 583 more_refs = --(*ref) > 0;
f1c9653e 584 ref_lock->unlock(ref_lock);
552cc11b
MW
585 return !more_refs;
586}
5317dd68 587
efedd0d2
TB
588/**
589 * Current refcount
590 */
591refcount_t ref_cur(refcount_t *ref)
592{
593 refcount_t current;
594
f1c9653e 595 ref_lock->lock(ref_lock);
efedd0d2 596 current = *ref;
f1c9653e
MW
597 ref_lock->unlock(ref_lock);
598
efedd0d2
TB
599 return current;
600}
601
5317dd68 602/**
f1c9653e 603 * Spinlock for all compare and swap operations.
5317dd68 604 */
f1c9653e 605static spinlock_t *cas_lock;
5317dd68
TB
606
607/**
608 * Compare and swap if equal to old value
609 */
610#define _cas_impl(name, type) \
611bool cas_##name(type *ptr, type oldval, type newval) \
612{ \
613 bool swapped; \
f1c9653e 614 cas_lock->lock(cas_lock); \
5317dd68 615 if ((swapped = (*ptr == oldval))) { *ptr = newval; } \
f1c9653e 616 cas_lock->unlock(cas_lock); \
5317dd68
TB
617 return swapped; \
618}
619
620_cas_impl(bool, bool)
621_cas_impl(ptr, void*)
622
0f603d42 623#endif /* !HAVE_GCC_ATOMIC_OPERATIONS && !HAVE_GCC_SYNC_OPERATIONS */
552cc11b 624
2077d996 625
9df621d2 626#ifdef HAVE_FMEMOPEN_FALLBACK
2077d996
MW
627
628static int fmemread(chunk_t *cookie, char *buf, int size)
629{
630 int len;
631
632 len = min(size, cookie->len);
633 memcpy(buf, cookie->ptr, len);
634 *cookie = chunk_skip(*cookie, len);
635
636 return len;
637}
638
639static int fmemwrite(chunk_t *cookie, const char *buf, int size)
640{
641 int len;
642
643 len = min(size, cookie->len);
644 memcpy(cookie->ptr, buf, len);
645 *cookie = chunk_skip(*cookie, len);
646
647 return len;
648}
649
650static int fmemclose(void *cookie)
651{
652 free(cookie);
653 return 0;
654}
655
656FILE *fmemopen(void *buf, size_t size, const char *mode)
657{
658 chunk_t *cookie;
659
660 INIT(cookie,
661 .ptr = buf,
662 .len = size,
663 );
664
665 return funopen(cookie, (void*)fmemread, (void*)fmemwrite, NULL, fmemclose);
666}
667
668#endif /* FMEMOPEN fallback*/
669
f1c9653e
MW
670/**
671 * See header
672 */
673void utils_init()
674{
675#ifdef WIN32
676 windows_init();
677#endif
678
679#if !defined(HAVE_GCC_ATOMIC_OPERATIONS) && !defined(HAVE_GCC_SYNC_OPERATIONS)
680 ref_lock = spinlock_create();
681 cas_lock = spinlock_create();
682#endif
683
684 strerror_init();
685}
686
687/**
688 * See header
689 */
690void utils_deinit()
691{
692#ifdef WIN32
693 windows_deinit();
694#endif
695
696#if !defined(HAVE_GCC_ATOMIC_OPERATIONS) && !defined(HAVE_GCC_SYNC_OPERATIONS)
697 ref_lock->destroy(ref_lock);
698 cas_lock->destroy(cas_lock);
699#endif
700
701 strerror_deinit();
702}
703
552cc11b 704/**
d25ce370 705 * Described in header.
552cc11b 706 */
1b40b74d 707int time_printf_hook(printf_hook_data_t *data, printf_hook_spec_t *spec,
d25ce370 708 const void *const *args)
552cc11b
MW
709{
710 static const char* months[] = {
711 "Jan", "Feb", "Mar", "Apr", "May", "Jun",
712 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
713 };
714 time_t *time = *((time_t**)(args[0]));
13f2d3a2 715 bool utc = *((int*)(args[1]));
8f129319 716 struct tm t, *ret = NULL;
7daf5226 717
8f129319 718 if (*time != UNDEFINED_TIME)
552cc11b 719 {
8f129319
MW
720 if (utc)
721 {
722 ret = gmtime_r(time, &t);
723 }
724 else
725 {
726 ret = localtime_r(time, &t);
727 }
552cc11b 728 }
8f129319 729 if (ret == NULL)
552cc11b 730 {
8f129319
MW
731 return print_in_hook(data, "--- -- --:--:--%s----",
732 utc ? " UTC " : " ");
552cc11b 733 }
1b40b74d 734 return print_in_hook(data, "%s %02d %02d:%02d:%02d%s%04d",
d25ce370
TB
735 months[t.tm_mon], t.tm_mday, t.tm_hour, t.tm_min,
736 t.tm_sec, utc ? " UTC " : " ", t.tm_year + 1900);
552cc11b
MW
737}
738
739/**
d25ce370 740 * Described in header.
552cc11b 741 */
1b40b74d 742int time_delta_printf_hook(printf_hook_data_t *data, printf_hook_spec_t *spec,
d25ce370 743 const void *const *args)
552cc11b
MW
744{
745 char* unit = "second";
d25ce370
TB
746 time_t *arg1 = *((time_t**)(args[0]));
747 time_t *arg2 = *((time_t**)(args[1]));
876961cf 748 u_int64_t delta = llabs(*arg1 - *arg2);
7daf5226 749
552cc11b
MW
750 if (delta > 2 * 60 * 60 * 24)
751 {
752 delta /= 60 * 60 * 24;
753 unit = "day";
754 }
755 else if (delta > 2 * 60 * 60)
756 {
757 delta /= 60 * 60;
758 unit = "hour";
759 }
760 else if (delta > 2 * 60)
761 {
762 delta /= 60;
763 unit = "minute";
764 }
1b40b74d 765 return print_in_hook(data, "%" PRIu64 " %s%s", delta, unit,
876961cf 766 (delta == 1) ? "" : "s");
552cc11b
MW
767}
768
769/**
770 * Number of bytes per line to dump raw data
771 */
772#define BYTES_PER_LINE 16
773
774static char hexdig_upper[] = "0123456789ABCDEF";
775
776/**
d25ce370 777 * Described in header.
552cc11b 778 */
1b40b74d 779int mem_printf_hook(printf_hook_data_t *data,
d25ce370 780 printf_hook_spec_t *spec, const void *const *args)
552cc11b
MW
781{
782 char *bytes = *((void**)(args[0]));
817ab8a8 783 u_int len = *((int*)(args[1]));
7daf5226 784
552cc11b
MW
785 char buffer[BYTES_PER_LINE * 3];
786 char ascii_buffer[BYTES_PER_LINE + 1];
787 char *buffer_pos = buffer;
788 char *bytes_pos = bytes;
789 char *bytes_roof = bytes + len;
790 int line_start = 0;
791 int i = 0;
792 int written = 0;
7daf5226 793
1b40b74d 794 written += print_in_hook(data, "=> %u bytes @ %p", len, bytes);
7daf5226 795
552cc11b
MW
796 while (bytes_pos < bytes_roof)
797 {
798 *buffer_pos++ = hexdig_upper[(*bytes_pos >> 4) & 0xF];
799 *buffer_pos++ = hexdig_upper[ *bytes_pos & 0xF];
800
801 ascii_buffer[i++] =
802 (*bytes_pos > 31 && *bytes_pos < 127) ? *bytes_pos : '.';
803
7daf5226 804 if (++bytes_pos == bytes_roof || i == BYTES_PER_LINE)
552cc11b
MW
805 {
806 int padding = 3 * (BYTES_PER_LINE - i);
7daf5226 807
552cc11b
MW
808 while (padding--)
809 {
810 *buffer_pos++ = ' ';
811 }
812 *buffer_pos++ = '\0';
813 ascii_buffer[i] = '\0';
7daf5226 814
1b40b74d 815 written += print_in_hook(data, "\n%4d: %s %s",
323f9f99 816 line_start, buffer, ascii_buffer);
7daf5226 817
552cc11b
MW
818 buffer_pos = buffer;
819 line_start += BYTES_PER_LINE;
820 i = 0;
821 }
822 else
823 {
824 *buffer_pos++ = ' ';
825 }
826 }
827 return written;
828}