]> git.ipfire.org Git - people/ms/strongswan.git/blame - src/libstrongswan/utils/utils.c
utils: Support Windows path separators in path_basename/dirname
[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 {
244 return strdup(".");
245 }
8182631b 246 while (pos > path && *pos == DIRECTORY_SEPARATOR[0])
766141bc
TB
247 { /* skip superfluous slashes */
248 pos--;
249 }
250 return strndup(path, pos - path + 1);
251}
252
253/**
254 * Described in header.
255 */
256char* path_basename(const char *path)
257{
258 char *pos, *trail = NULL;
259
260 if (!path || !*path)
261 {
262 return strdup(".");
263 }
8182631b 264 pos = strrchr(path, DIRECTORY_SEPARATOR[0]);
766141bc
TB
265 if (pos && !pos[1])
266 { /* if path ends with slashes we have to look beyond them */
8182631b 267 while (pos > path && *pos == DIRECTORY_SEPARATOR[0])
766141bc
TB
268 { /* skip trailing slashes */
269 pos--;
270 }
8182631b 271 if (pos == path && *pos == DIRECTORY_SEPARATOR[0])
766141bc 272 { /* contains only slashes */
8182631b 273 return strdup(DIRECTORY_SEPARATOR);
766141bc
TB
274 }
275 trail = pos + 1;
8182631b 276 pos = memrchr(path, DIRECTORY_SEPARATOR[0], trail - path);
766141bc
TB
277 }
278 pos = pos ? pos + 1 : (char*)path;
279 return trail ? strndup(pos, trail - pos) : strdup(pos);
280}
281
6c20579a
TB
282/**
283 * Described in header.
284 */
285bool mkdir_p(const char *path, mode_t mode)
286{
fc1afcc8 287 int len;
6c20579a
TB
288 char *pos, full[PATH_MAX];
289 pos = full;
290 if (!path || *path == '\0')
291 {
292 return TRUE;
293 }
294 len = snprintf(full, sizeof(full)-1, "%s", path);
295 if (len < 0 || len >= sizeof(full)-1)
296 {
8b0e0910 297 DBG1(DBG_LIB, "path string %s too long", path);
6c20579a
TB
298 return FALSE;
299 }
300 /* ensure that the path ends with a '/' */
301 if (full[len-1] != '/')
302 {
303 full[len++] = '/';
304 full[len] = '\0';
305 }
306 /* skip '/' at the beginning */
307 while (*pos == '/')
308 {
309 pos++;
310 }
311 while ((pos = strchr(pos, '/')))
312 {
313 *pos = '\0';
314 if (access(full, F_OK) < 0)
315 {
a3f7dfc1
MW
316#ifdef WIN32
317 if (_mkdir(full) < 0)
318#else
6c20579a 319 if (mkdir(full, mode) < 0)
a3f7dfc1 320#endif
6c20579a 321 {
8b0e0910 322 DBG1(DBG_LIB, "failed to create directory %s", full);
6c20579a
TB
323 return FALSE;
324 }
325 }
326 *pos = '/';
327 pos++;
328 }
329 return TRUE;
330}
331
4d174272
MW
332ENUM(tty_color_names, TTY_RESET, TTY_BG_DEF,
333 "\e[0m",
334 "\e[1m",
335 "\e[4m",
336 "\e[5m",
337 "\e[30m",
338 "\e[31m",
339 "\e[32m",
340 "\e[33m",
341 "\e[34m",
342 "\e[35m",
343 "\e[36m",
344 "\e[37m",
345 "\e[39m",
346 "\e[40m",
347 "\e[41m",
348 "\e[42m",
349 "\e[43m",
350 "\e[44m",
351 "\e[45m",
352 "\e[46m",
353 "\e[47m",
354 "\e[49m",
355);
356
357/**
358 * Get the escape string for a given TTY color, empty string on non-tty FILE
359 */
360char* tty_escape_get(int fd, tty_escape_t escape)
361{
362 if (!isatty(fd))
363 {
364 return "";
365 }
366 switch (escape)
367 {
368 case TTY_RESET:
369 case TTY_BOLD:
370 case TTY_UNDERLINE:
371 case TTY_BLINKING:
1f2b8c8c
MW
372#ifdef WIN32
373 return "";
374#endif
4d174272
MW
375 case TTY_FG_BLACK:
376 case TTY_FG_RED:
377 case TTY_FG_GREEN:
378 case TTY_FG_YELLOW:
379 case TTY_FG_BLUE:
380 case TTY_FG_MAGENTA:
381 case TTY_FG_CYAN:
382 case TTY_FG_WHITE:
383 case TTY_FG_DEF:
384 case TTY_BG_BLACK:
385 case TTY_BG_RED:
386 case TTY_BG_GREEN:
387 case TTY_BG_YELLOW:
388 case TTY_BG_BLUE:
389 case TTY_BG_MAGENTA:
390 case TTY_BG_CYAN:
391 case TTY_BG_WHITE:
392 case TTY_BG_DEF:
393 return enum_to_name(tty_color_names, escape);
1f2b8c8c 394 /* warn if a escape code is missing */
4d174272
MW
395 }
396 return "";
397}
2a595276 398
9a8fdc15
TB
399#ifndef HAVE_CLOSEFROM
400/**
401 * Described in header.
402 */
403void closefrom(int lowfd)
404{
5051bd23
TB
405 char fd_dir[PATH_MAX];
406 int maxfd, fd, len;
407
408 /* try to close only open file descriptors on Linux... */
409 len = snprintf(fd_dir, sizeof(fd_dir), "/proc/%u/fd", getpid());
4a4cf41b 410 if (len > 0 && len < sizeof(fd_dir) && access(fd_dir, F_OK) == 0)
5051bd23
TB
411 {
412 enumerator_t *enumerator = enumerator_create_directory(fd_dir);
413 if (enumerator)
414 {
68fcf917 415 char *rel;
5051bd23
TB
416 while (enumerator->enumerate(enumerator, &rel, NULL, NULL))
417 {
418 fd = atoi(rel);
419 if (fd >= lowfd)
420 {
421 close(fd);
422 }
423 }
424 enumerator->destroy(enumerator);
425 return;
426 }
427 }
428
429 /* ...fall back to closing all fds otherwise */
d3c30b35
MW
430#ifdef WIN32
431 maxfd = _getmaxstdio();
432#else
9a8fdc15 433 maxfd = (int)sysconf(_SC_OPEN_MAX);
d3c30b35 434#endif
9a8fdc15
TB
435 if (maxfd < 0)
436 {
437 maxfd = 256;
438 }
439 for (fd = lowfd; fd < maxfd; fd++)
440 {
441 close(fd);
442 }
443}
444#endif /* HAVE_CLOSEFROM */
445
3f310c0d
MW
446/**
447 * Return monotonic time
448 */
449time_t time_monotonic(timeval_t *tv)
450{
4de7401a
MW
451#ifdef WIN32
452 ULONGLONG ms;
453 time_t s;
454
455 ms = GetTickCount64();
456 s = ms / 1000;
457 if (tv)
458 {
459 tv->tv_sec = s;
460 tv->tv_usec = (ms - (s * 1000)) * 1000;
461 }
462 return s;
463#else /* !WIN32 */
b2944d71
TB
464#if defined(HAVE_CLOCK_GETTIME) && \
465 (defined(HAVE_CONDATTR_CLOCK_MONOTONIC) || \
466 defined(HAVE_PTHREAD_COND_TIMEDWAIT_MONOTONIC))
3d5818ec
MW
467 /* as we use time_monotonic() for condvar operations, we use the
468 * monotonic time source only if it is also supported by pthread. */
3f310c0d 469 timespec_t ts;
7daf5226 470
3f310c0d
MW
471 if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0)
472 {
473 if (tv)
474 {
475 tv->tv_sec = ts.tv_sec;
476 tv->tv_usec = ts.tv_nsec / 1000;
477 }
478 return ts.tv_sec;
479 }
b2944d71 480#endif /* HAVE_CLOCK_GETTIME && (...) */
3f310c0d
MW
481 /* Fallback to non-monotonic timestamps:
482 * On MAC OS X, creating monotonic timestamps is rather difficult. We
483 * could use mach_absolute_time() and catch sleep/wakeup notifications.
3d5818ec
MW
484 * We stick to the simpler (non-monotonic) gettimeofday() for now.
485 * But keep in mind: we need the same time source here as in condvar! */
3f310c0d
MW
486 if (!tv)
487 {
488 return time(NULL);
489 }
490 if (gettimeofday(tv, NULL) != 0)
491 { /* should actually never fail if passed pointers are valid */
492 return -1;
493 }
494 return tv->tv_sec;
4de7401a 495#endif /* !WIN32 */
3f310c0d
MW
496}
497
081ae2eb
MW
498/**
499 * return null
500 */
501void *return_null()
502{
503 return NULL;
504}
505
da17b016
MW
506/**
507 * returns TRUE
508 */
509bool return_true()
510{
511 return TRUE;
512}
513
514/**
515 * returns FALSE
516 */
517bool return_false()
518{
519 return FALSE;
520}
521
502edf42
MW
522/**
523 * returns FAILED
524 */
525status_t return_failed()
526{
527 return FAILED;
528}
529
4755ab50
MW
530/**
531 * returns SUCCESS
532 */
533status_t return_success()
534{
535 return SUCCESS;
536}
537
233b853d
MW
538/**
539 * nop operation
540 */
541void nop()
542{
543}
544
0f603d42 545#if !defined(HAVE_GCC_ATOMIC_OPERATIONS) && !defined(HAVE_GCC_SYNC_OPERATIONS)
efd0fe21 546
552cc11b 547/**
f1c9653e 548 * Spinlock for ref_get/put
552cc11b 549 */
f1c9653e 550static spinlock_t *ref_lock;
552cc11b
MW
551
552/**
efd0fe21 553 * Increase refcount
552cc11b 554 */
3160b92a 555refcount_t ref_get(refcount_t *ref)
552cc11b 556{
3160b92a
MW
557 refcount_t current;
558
f1c9653e 559 ref_lock->lock(ref_lock);
3160b92a 560 current = ++(*ref);
f1c9653e
MW
561 ref_lock->unlock(ref_lock);
562
3160b92a 563 return current;
552cc11b
MW
564}
565
566/**
efd0fe21 567 * Decrease refcount
552cc11b
MW
568 */
569bool ref_put(refcount_t *ref)
570{
571 bool more_refs;
7daf5226 572
f1c9653e 573 ref_lock->lock(ref_lock);
21f411b8 574 more_refs = --(*ref) > 0;
f1c9653e 575 ref_lock->unlock(ref_lock);
552cc11b
MW
576 return !more_refs;
577}
5317dd68 578
efedd0d2
TB
579/**
580 * Current refcount
581 */
582refcount_t ref_cur(refcount_t *ref)
583{
584 refcount_t current;
585
f1c9653e 586 ref_lock->lock(ref_lock);
efedd0d2 587 current = *ref;
f1c9653e
MW
588 ref_lock->unlock(ref_lock);
589
efedd0d2
TB
590 return current;
591}
592
5317dd68 593/**
f1c9653e 594 * Spinlock for all compare and swap operations.
5317dd68 595 */
f1c9653e 596static spinlock_t *cas_lock;
5317dd68
TB
597
598/**
599 * Compare and swap if equal to old value
600 */
601#define _cas_impl(name, type) \
602bool cas_##name(type *ptr, type oldval, type newval) \
603{ \
604 bool swapped; \
f1c9653e 605 cas_lock->lock(cas_lock); \
5317dd68 606 if ((swapped = (*ptr == oldval))) { *ptr = newval; } \
f1c9653e 607 cas_lock->unlock(cas_lock); \
5317dd68
TB
608 return swapped; \
609}
610
611_cas_impl(bool, bool)
612_cas_impl(ptr, void*)
613
0f603d42 614#endif /* !HAVE_GCC_ATOMIC_OPERATIONS && !HAVE_GCC_SYNC_OPERATIONS */
552cc11b 615
2077d996 616
9df621d2 617#ifdef HAVE_FMEMOPEN_FALLBACK
2077d996
MW
618
619static int fmemread(chunk_t *cookie, char *buf, int size)
620{
621 int len;
622
623 len = min(size, cookie->len);
624 memcpy(buf, cookie->ptr, len);
625 *cookie = chunk_skip(*cookie, len);
626
627 return len;
628}
629
630static int fmemwrite(chunk_t *cookie, const char *buf, int size)
631{
632 int len;
633
634 len = min(size, cookie->len);
635 memcpy(cookie->ptr, buf, len);
636 *cookie = chunk_skip(*cookie, len);
637
638 return len;
639}
640
641static int fmemclose(void *cookie)
642{
643 free(cookie);
644 return 0;
645}
646
647FILE *fmemopen(void *buf, size_t size, const char *mode)
648{
649 chunk_t *cookie;
650
651 INIT(cookie,
652 .ptr = buf,
653 .len = size,
654 );
655
656 return funopen(cookie, (void*)fmemread, (void*)fmemwrite, NULL, fmemclose);
657}
658
659#endif /* FMEMOPEN fallback*/
660
f1c9653e
MW
661/**
662 * See header
663 */
664void utils_init()
665{
666#ifdef WIN32
667 windows_init();
668#endif
669
670#if !defined(HAVE_GCC_ATOMIC_OPERATIONS) && !defined(HAVE_GCC_SYNC_OPERATIONS)
671 ref_lock = spinlock_create();
672 cas_lock = spinlock_create();
673#endif
674
675 strerror_init();
676}
677
678/**
679 * See header
680 */
681void utils_deinit()
682{
683#ifdef WIN32
684 windows_deinit();
685#endif
686
687#if !defined(HAVE_GCC_ATOMIC_OPERATIONS) && !defined(HAVE_GCC_SYNC_OPERATIONS)
688 ref_lock->destroy(ref_lock);
689 cas_lock->destroy(cas_lock);
690#endif
691
692 strerror_deinit();
693}
694
552cc11b 695/**
d25ce370 696 * Described in header.
552cc11b 697 */
1b40b74d 698int time_printf_hook(printf_hook_data_t *data, printf_hook_spec_t *spec,
d25ce370 699 const void *const *args)
552cc11b
MW
700{
701 static const char* months[] = {
702 "Jan", "Feb", "Mar", "Apr", "May", "Jun",
703 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
704 };
705 time_t *time = *((time_t**)(args[0]));
13f2d3a2 706 bool utc = *((int*)(args[1]));
8f129319 707 struct tm t, *ret = NULL;
7daf5226 708
8f129319 709 if (*time != UNDEFINED_TIME)
552cc11b 710 {
8f129319
MW
711 if (utc)
712 {
713 ret = gmtime_r(time, &t);
714 }
715 else
716 {
717 ret = localtime_r(time, &t);
718 }
552cc11b 719 }
8f129319 720 if (ret == NULL)
552cc11b 721 {
8f129319
MW
722 return print_in_hook(data, "--- -- --:--:--%s----",
723 utc ? " UTC " : " ");
552cc11b 724 }
1b40b74d 725 return print_in_hook(data, "%s %02d %02d:%02d:%02d%s%04d",
d25ce370
TB
726 months[t.tm_mon], t.tm_mday, t.tm_hour, t.tm_min,
727 t.tm_sec, utc ? " UTC " : " ", t.tm_year + 1900);
552cc11b
MW
728}
729
730/**
d25ce370 731 * Described in header.
552cc11b 732 */
1b40b74d 733int time_delta_printf_hook(printf_hook_data_t *data, printf_hook_spec_t *spec,
d25ce370 734 const void *const *args)
552cc11b
MW
735{
736 char* unit = "second";
d25ce370
TB
737 time_t *arg1 = *((time_t**)(args[0]));
738 time_t *arg2 = *((time_t**)(args[1]));
876961cf 739 u_int64_t delta = llabs(*arg1 - *arg2);
7daf5226 740
552cc11b
MW
741 if (delta > 2 * 60 * 60 * 24)
742 {
743 delta /= 60 * 60 * 24;
744 unit = "day";
745 }
746 else if (delta > 2 * 60 * 60)
747 {
748 delta /= 60 * 60;
749 unit = "hour";
750 }
751 else if (delta > 2 * 60)
752 {
753 delta /= 60;
754 unit = "minute";
755 }
1b40b74d 756 return print_in_hook(data, "%" PRIu64 " %s%s", delta, unit,
876961cf 757 (delta == 1) ? "" : "s");
552cc11b
MW
758}
759
760/**
761 * Number of bytes per line to dump raw data
762 */
763#define BYTES_PER_LINE 16
764
765static char hexdig_upper[] = "0123456789ABCDEF";
766
767/**
d25ce370 768 * Described in header.
552cc11b 769 */
1b40b74d 770int mem_printf_hook(printf_hook_data_t *data,
d25ce370 771 printf_hook_spec_t *spec, const void *const *args)
552cc11b
MW
772{
773 char *bytes = *((void**)(args[0]));
817ab8a8 774 u_int len = *((int*)(args[1]));
7daf5226 775
552cc11b
MW
776 char buffer[BYTES_PER_LINE * 3];
777 char ascii_buffer[BYTES_PER_LINE + 1];
778 char *buffer_pos = buffer;
779 char *bytes_pos = bytes;
780 char *bytes_roof = bytes + len;
781 int line_start = 0;
782 int i = 0;
783 int written = 0;
7daf5226 784
1b40b74d 785 written += print_in_hook(data, "=> %u bytes @ %p", len, bytes);
7daf5226 786
552cc11b
MW
787 while (bytes_pos < bytes_roof)
788 {
789 *buffer_pos++ = hexdig_upper[(*bytes_pos >> 4) & 0xF];
790 *buffer_pos++ = hexdig_upper[ *bytes_pos & 0xF];
791
792 ascii_buffer[i++] =
793 (*bytes_pos > 31 && *bytes_pos < 127) ? *bytes_pos : '.';
794
7daf5226 795 if (++bytes_pos == bytes_roof || i == BYTES_PER_LINE)
552cc11b
MW
796 {
797 int padding = 3 * (BYTES_PER_LINE - i);
7daf5226 798
552cc11b
MW
799 while (padding--)
800 {
801 *buffer_pos++ = ' ';
802 }
803 *buffer_pos++ = '\0';
804 ascii_buffer[i] = '\0';
7daf5226 805
1b40b74d 806 written += print_in_hook(data, "\n%4d: %s %s",
323f9f99 807 line_start, buffer, ascii_buffer);
7daf5226 808
552cc11b
MW
809 buffer_pos = buffer;
810 line_start += BYTES_PER_LINE;
811 i = 0;
812 }
813 else
814 {
815 *buffer_pos++ = ' ';
816 }
817 }
818 return written;
819}