~ubuntu-branches/ubuntu/precise/libpgm/precise

« back to all changes in this revision

Viewing changes to openpgm/pgm/examples/purinrecv.c

  • Committer: Bazaar Package Importer
  • Author(s): Gabriel de Perthuis
  • Date: 2011-04-07 16:48:52 UTC
  • Revision ID: james.westby@ubuntu.com-20110407164852-8uamem42ojeptj6l
Tags: upstream-5.1.116~dfsg
ImportĀ upstreamĀ versionĀ 5.1.116~dfsg

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* vim:ts=8:sts=8:sw=4:noai:noexpandtab
 
2
 *
 
3
 * ćƒ—ćƒŖćƒ³ PGM receiver
 
4
 *
 
5
 * Copyright (c) 2006-2010 Miru Limited.
 
6
 *
 
7
 * This library is free software; you can redistribute it and/or
 
8
 * modify it under the terms of the GNU Lesser General Public
 
9
 * License as published by the Free Software Foundation; either
 
10
 * version 2.1 of the License, or (at your option) any later version.
 
11
 *
 
12
 * This library is distributed in the hope that it will be useful,
 
13
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
14
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 
15
 * Lesser General Public License for more details.
 
16
 *
 
17
 * You should have received a copy of the GNU Lesser General Public
 
18
 * License along with this library; if not, write to the Free Software
 
19
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
20
 */
 
21
 
 
22
/* MSVC secure CRT */
 
23
#define _CRT_SECURE_NO_WARNINGS         1
 
24
 
 
25
#include <assert.h>
 
26
#include <locale.h>
 
27
#include <signal.h>
 
28
#include <stdio.h>
 
29
#include <stdlib.h>
 
30
#ifdef _MSC_VER
 
31
#       include <tchar.h>
 
32
#endif
 
33
#ifndef _WIN32
 
34
#       include <unistd.h>
 
35
#else
 
36
#       include "getopt.h"
 
37
#endif
 
38
#ifdef __APPLE__
 
39
#       include <pgm/in.h>
 
40
#endif
 
41
#include <pgm/pgm.h>
 
42
 
 
43
 
 
44
/* globals */
 
45
 
 
46
static int              port = 0;
 
47
static const char*      network = "";
 
48
static bool             use_multicast_loop = FALSE;
 
49
static int              udp_encap_port = 0;
 
50
 
 
51
static int              max_tpdu = 1500;
 
52
static int              sqns = 100;
 
53
 
 
54
static bool             use_pgmcc = FALSE;
 
55
static bool             use_fec = FALSE;
 
56
static int              rs_k = 8;
 
57
static int              rs_n = 255;
 
58
 
 
59
static pgm_sock_t*      sock = NULL;
 
60
static bool             is_terminated = FALSE;
 
61
 
 
62
#ifndef _WIN32
 
63
static int              terminate_pipe[2];
 
64
static void on_signal (int);
 
65
#else
 
66
static WSAEVENT         terminateEvent;
 
67
static BOOL on_console_ctrl (DWORD);
 
68
#endif
 
69
#ifndef _MSC_VER
 
70
static void usage (const char*) __attribute__((__noreturn__));
 
71
#else
 
72
static void usage (const char*);
 
73
#endif
 
74
 
 
75
static bool on_startup (void);
 
76
static int on_data (const void*restrict, const size_t, const struct pgm_sockaddr_t*restrict);
 
77
 
 
78
 
 
79
static void
 
80
usage (
 
81
        const char*     bin
 
82
        )
 
83
{
 
84
        fprintf (stderr, "Usage: %s [options]\n", bin);
 
85
        fprintf (stderr, "  -n <network>    : Multicast group or unicast IP address\n");
 
86
        fprintf (stderr, "  -s <port>       : IP port\n");
 
87
        fprintf (stderr, "  -p <port>       : Encapsulate PGM in UDP on IP port\n");
 
88
        fprintf (stderr, "  -c              : Enable PGMCC\n");
 
89
        fprintf (stderr, "  -f <type>       : Enable FEC with either proactive or ondemand parity\n");
 
90
        fprintf (stderr, "  -K <k>          : Configure Reed-Solomon code (n, k)\n");
 
91
        fprintf (stderr, "  -N <n>\n");
 
92
        fprintf (stderr, "  -l              : Enable multicast loopback and address sharing\n");
 
93
        fprintf (stderr, "  -i              : List available interfaces\n");
 
94
        exit (EXIT_SUCCESS);
 
95
}
 
96
 
 
97
int
 
98
main (
 
99
        int             argc,
 
100
        char*           argv[]
 
101
        )
 
102
{
 
103
        pgm_error_t* pgm_err = NULL;
 
104
 
 
105
        setlocale (LC_ALL, "");
 
106
 
 
107
#if !defined(_WIN32)
 
108
        puts ("惗ćƒŖćƒ³ ćƒ—ćƒŖćƒ³");
 
109
#else
 
110
/* Windows consoles have incredibly limited Unicode support */
 
111
        puts ("purin purin");
 
112
#endif
 
113
        if (!pgm_init (&pgm_err)) {
 
114
                fprintf (stderr, "Unable to start PGM engine: %s\n", pgm_err->message);
 
115
                pgm_error_free (pgm_err);
 
116
                return EXIT_FAILURE;
 
117
        }
 
118
 
 
119
/* parse program arguments */
 
120
#ifdef _WIN32
 
121
        const char* binary_name = strrchr (argv[0], '\\');
 
122
#else
 
123
        const char* binary_name = strrchr (argv[0], '/');
 
124
#endif
 
125
        if (NULL == binary_name)        binary_name = argv[0];
 
126
        else                            binary_name++;
 
127
 
 
128
        int c;
 
129
        while ((c = getopt (argc, argv, "s:n:p:cf:K:N:lih")) != -1)
 
130
        {
 
131
                switch (c) {
 
132
                case 'n':       network = optarg; break;
 
133
                case 's':       port = atoi (optarg); break;
 
134
                case 'p':       udp_encap_port = atoi (optarg); break;
 
135
                case 'c':       use_pgmcc = TRUE; break;
 
136
                case 'f':       use_fec = TRUE; break;
 
137
                case 'K':       rs_k = atoi (optarg); break;
 
138
                case 'N':       rs_n = atoi (optarg); break;
 
139
                case 'l':       use_multicast_loop = TRUE; break;
 
140
 
 
141
                case 'i':
 
142
                        pgm_if_print_all();
 
143
                        return EXIT_SUCCESS;
 
144
 
 
145
                case 'h':
 
146
                case '?': usage (binary_name);
 
147
                }
 
148
        }
 
149
 
 
150
        if (use_fec && ( !rs_n || !rs_k )) {
 
151
                fprintf (stderr, "Invalid Reed-Solomon parameters RS(%d,%d).\n", rs_n, rs_k);
 
152
                usage (binary_name);
 
153
        }
 
154
 
 
155
/* setup signal handlers */
 
156
#ifdef SIGHUP
 
157
        signal (SIGHUP,  SIG_IGN);
 
158
#endif
 
159
#ifndef _WIN32
 
160
        int e = pipe (terminate_pipe);
 
161
        assert (0 == e);
 
162
        signal (SIGINT,  on_signal);
 
163
        signal (SIGTERM, on_signal);
 
164
#else
 
165
        terminateEvent = WSACreateEvent();
 
166
        SetConsoleCtrlHandler ((PHANDLER_ROUTINE)on_console_ctrl, TRUE);
 
167
        setvbuf (stdout, (char *) NULL, _IONBF, 0);
 
168
#endif /* !_WIN32 */
 
169
 
 
170
        if (!on_startup()) {
 
171
                fprintf (stderr, "Startup failed\n");
 
172
                return EXIT_FAILURE;
 
173
        }
 
174
 
 
175
/* dispatch loop */
 
176
#ifndef _WIN32
 
177
        int fds;
 
178
        fd_set readfds;
 
179
#else
 
180
        SOCKET recv_sock, pending_sock;
 
181
        DWORD cEvents = PGM_RECV_SOCKET_READ_COUNT + 1;
 
182
        WSAEVENT waitEvents[ PGM_RECV_SOCKET_READ_COUNT + 1 ];
 
183
        socklen_t socklen = sizeof (SOCKET);
 
184
 
 
185
        waitEvents[0] = terminateEvent;
 
186
        waitEvents[1] = WSACreateEvent();
 
187
        waitEvents[2] = WSACreateEvent();
 
188
        assert (2 == PGM_RECV_SOCKET_READ_COUNT);
 
189
        pgm_getsockopt (sock, IPPROTO_PGM, PGM_RECV_SOCK, &recv_sock, &socklen);
 
190
        WSAEventSelect (recv_sock, waitEvents[1], FD_READ);
 
191
        pgm_getsockopt (sock, IPPROTO_PGM, PGM_PENDING_SOCK, &pending_sock, &socklen);
 
192
        WSAEventSelect (pending_sock, waitEvents[2], FD_READ);
 
193
#endif /* !_WIN32 */
 
194
        puts ("Entering PGM message loop ... ");
 
195
        do {
 
196
                struct timeval tv;
 
197
#ifdef _WIN32
 
198
                DWORD dwTimeout, dwEvents;
 
199
#endif
 
200
                char buffer[4096];
 
201
                size_t len;
 
202
                struct pgm_sockaddr_t from;
 
203
                socklen_t fromlen = sizeof (from);
 
204
                const int status = pgm_recvfrom (sock,
 
205
                                                 buffer,
 
206
                                                 sizeof(buffer),
 
207
                                                 0,
 
208
                                                 &len,
 
209
                                                 &from,
 
210
                                                 &fromlen,
 
211
                                                 &pgm_err);
 
212
                switch (status) {
 
213
                case PGM_IO_STATUS_NORMAL:
 
214
                        on_data (buffer, len, &from);
 
215
                        break;
 
216
                case PGM_IO_STATUS_TIMER_PENDING:
 
217
                        {
 
218
                                socklen_t optlen = sizeof (tv);
 
219
                                pgm_getsockopt (sock, IPPROTO_PGM, PGM_TIME_REMAIN, &tv, &optlen);
 
220
                        }
 
221
                        goto block;
 
222
                case PGM_IO_STATUS_RATE_LIMITED:
 
223
                        {
 
224
                                socklen_t optlen = sizeof (tv);
 
225
                                pgm_getsockopt (sock, IPPROTO_PGM, PGM_RATE_REMAIN, &tv, &optlen);
 
226
                        }
 
227
                case PGM_IO_STATUS_WOULD_BLOCK:
 
228
/* select for next event */
 
229
block:
 
230
#ifndef _WIN32
 
231
                        fds = terminate_pipe[0] + 1;
 
232
                        FD_ZERO(&readfds);
 
233
                        FD_SET(terminate_pipe[0], &readfds);
 
234
                        pgm_select_info (sock, &readfds, NULL, &fds);
 
235
                        fds = select (fds, &readfds, NULL, NULL, PGM_IO_STATUS_WOULD_BLOCK == status ? NULL : &tv);
 
236
#else
 
237
                        dwTimeout = PGM_IO_STATUS_WOULD_BLOCK == status ? WSA_INFINITE : (DWORD)((tv.tv_sec * 1000) + (tv.tv_usec / 1000));
 
238
                        dwEvents = WSAWaitForMultipleEvents (cEvents, waitEvents, FALSE, dwTimeout, FALSE);
 
239
                        switch (dwEvents) {
 
240
                        case WSA_WAIT_EVENT_0+1: WSAResetEvent (waitEvents[1]); break;
 
241
                        case WSA_WAIT_EVENT_0+2: WSAResetEvent (waitEvents[2]); break;
 
242
                        default: break;
 
243
                        }
 
244
#endif /* !_WIN32 */
 
245
                        break;
 
246
 
 
247
                default:
 
248
                        if (pgm_err) {
 
249
                                fprintf (stderr, "%s\n", pgm_err->message);
 
250
                                pgm_error_free (pgm_err);
 
251
                                pgm_err = NULL;
 
252
                        }
 
253
                        if (PGM_IO_STATUS_ERROR == status)
 
254
                                break;
 
255
                }
 
256
        } while (!is_terminated);
 
257
 
 
258
        puts ("Message loop terminated, cleaning up.");
 
259
 
 
260
/* cleanup */
 
261
#ifndef _WIN32
 
262
        close (terminate_pipe[0]);
 
263
        close (terminate_pipe[1]);
 
264
#else
 
265
        WSACloseEvent (waitEvents[0]);
 
266
        WSACloseEvent (waitEvents[1]);
 
267
        WSACloseEvent (waitEvents[2]);
 
268
#endif /* !_WIN32 */
 
269
 
 
270
        if (sock) {
 
271
                puts ("Destroying PGM socket.");
 
272
                pgm_close (sock, TRUE);
 
273
                sock = NULL;
 
274
        }
 
275
 
 
276
        puts ("PGM engine shutdown.");
 
277
        pgm_shutdown ();
 
278
        puts ("finished.");
 
279
        return EXIT_SUCCESS;
 
280
}
 
281
 
 
282
#ifndef _WIN32
 
283
static
 
284
void
 
285
on_signal (
 
286
        int             signum
 
287
        )
 
288
{
 
289
        printf ("on_signal (signum:%d)\n", signum);
 
290
        is_terminated = TRUE;
 
291
        const char one = '1';
 
292
        const size_t writelen = write (terminate_pipe[1], &one, sizeof(one));
 
293
        assert (sizeof(one) == writelen);
 
294
}
 
295
#else
 
296
static
 
297
BOOL
 
298
on_console_ctrl (
 
299
        DWORD           dwCtrlType
 
300
        )
 
301
{
 
302
        printf ("on_console_ctrl (dwCtrlType:%lu)\n", (unsigned long)dwCtrlType);
 
303
        is_terminated = TRUE;
 
304
        WSASetEvent (terminateEvent);
 
305
        return TRUE;
 
306
}
 
307
#endif /* !_WIN32 */
 
308
 
 
309
static
 
310
bool
 
311
on_startup (void)
 
312
{
 
313
        struct pgm_addrinfo_t* res = NULL;
 
314
        pgm_error_t* pgm_err = NULL;
 
315
        sa_family_t sa_family = AF_UNSPEC;
 
316
 
 
317
/* parse network parameter into PGM socket address structure */
 
318
        if (!pgm_getaddrinfo (network, NULL, &res, &pgm_err)) {
 
319
                fprintf (stderr, "Parsing network parameter: %s\n", pgm_err->message);
 
320
                goto err_abort;
 
321
        }
 
322
 
 
323
        sa_family = res->ai_send_addrs[0].gsr_group.ss_family;
 
324
 
 
325
        if (udp_encap_port) {
 
326
                puts ("Create PGM/UDP socket.");
 
327
                if (!pgm_socket (&sock, sa_family, SOCK_SEQPACKET, IPPROTO_UDP, &pgm_err)) {
 
328
                        fprintf (stderr, "Creating PGM/UDP socket: %s\n", pgm_err->message);
 
329
                        goto err_abort;
 
330
                }
 
331
                pgm_setsockopt (sock, IPPROTO_PGM, PGM_UDP_ENCAP_UCAST_PORT, &udp_encap_port, sizeof(udp_encap_port));
 
332
                pgm_setsockopt (sock, IPPROTO_PGM, PGM_UDP_ENCAP_MCAST_PORT, &udp_encap_port, sizeof(udp_encap_port));
 
333
        } else {
 
334
                puts ("Create PGM/IP socket.");
 
335
                if (!pgm_socket (&sock, sa_family, SOCK_SEQPACKET, IPPROTO_PGM, &pgm_err)) {
 
336
                        fprintf (stderr, "Creating PGM/IP socket: %s\n", pgm_err->message);
 
337
                        goto err_abort;
 
338
                }
 
339
        }
 
340
 
 
341
/* Use RFC 2113 tagging for PGM Router Assist */
 
342
        const int no_router_assist = 0;
 
343
        pgm_setsockopt (sock, IPPROTO_PGM, PGM_IP_ROUTER_ALERT, &no_router_assist, sizeof(no_router_assist));
 
344
 
 
345
        pgm_drop_superuser();
 
346
 
 
347
/* set PGM parameters */
 
348
        const int recv_only = 1,
 
349
                  passive = 0,
 
350
                  peer_expiry = pgm_secs (300),
 
351
                  spmr_expiry = pgm_msecs (250),
 
352
                  nak_bo_ivl = pgm_msecs (50),
 
353
                  nak_rpt_ivl = pgm_secs (2),
 
354
                  nak_rdata_ivl = pgm_secs (2),
 
355
                  nak_data_retries = 50,
 
356
                  nak_ncf_retries = 50;
 
357
 
 
358
        pgm_setsockopt (sock, IPPROTO_PGM, PGM_RECV_ONLY, &recv_only, sizeof(recv_only));
 
359
        pgm_setsockopt (sock, IPPROTO_PGM, PGM_PASSIVE, &passive, sizeof(passive));
 
360
        pgm_setsockopt (sock, IPPROTO_PGM, PGM_MTU, &max_tpdu, sizeof(max_tpdu));
 
361
        pgm_setsockopt (sock, IPPROTO_PGM, PGM_RXW_SQNS, &sqns, sizeof(sqns));
 
362
        pgm_setsockopt (sock, IPPROTO_PGM, PGM_PEER_EXPIRY, &peer_expiry, sizeof(peer_expiry));
 
363
        pgm_setsockopt (sock, IPPROTO_PGM, PGM_SPMR_EXPIRY, &spmr_expiry, sizeof(spmr_expiry));
 
364
        pgm_setsockopt (sock, IPPROTO_PGM, PGM_NAK_BO_IVL, &nak_bo_ivl, sizeof(nak_bo_ivl));
 
365
        pgm_setsockopt (sock, IPPROTO_PGM, PGM_NAK_RPT_IVL, &nak_rpt_ivl, sizeof(nak_rpt_ivl));
 
366
        pgm_setsockopt (sock, IPPROTO_PGM, PGM_NAK_RDATA_IVL, &nak_rdata_ivl, sizeof(nak_rdata_ivl));
 
367
        pgm_setsockopt (sock, IPPROTO_PGM, PGM_NAK_DATA_RETRIES, &nak_data_retries, sizeof(nak_data_retries));
 
368
        pgm_setsockopt (sock, IPPROTO_PGM, PGM_NAK_NCF_RETRIES, &nak_ncf_retries, sizeof(nak_ncf_retries));
 
369
 
 
370
#ifdef I_UNDERSTAND_PGMCC_AND_FEC_ARE_NOT_SUPPORTED
 
371
        if (use_pgmcc) {
 
372
                struct pgm_pgmccinfo_t pgmccinfo;
 
373
                pgmccinfo.ack_bo_ivl            = pgm_msecs (50);
 
374
                pgmccinfo.ack_c                 = 75;
 
375
                pgmccinfo.ack_c_p               = 500;
 
376
                pgm_setsockopt (sock, IPPROTO_PGM, PGM_USE_PGMCC, &pgmccinfo, sizeof(pgmccinfo));
 
377
        }
 
378
        if (use_fec) {
 
379
                struct pgm_fecinfo_t fecinfo;
 
380
                fecinfo.block_size              = rs_n;
 
381
                fecinfo.proactive_packets       = 0;
 
382
                fecinfo.group_size              = rs_k;
 
383
                fecinfo.ondemand_parity_enabled = TRUE;
 
384
                fecinfo.var_pktlen_enabled      = FALSE;
 
385
                pgm_setsockopt (sock, IPPROTO_PGM, PGM_USE_FEC, &fecinfo, sizeof(fecinfo));
 
386
        }
 
387
#endif
 
388
 
 
389
/* create global session identifier */
 
390
        struct pgm_sockaddr_t addr;
 
391
        memset (&addr, 0, sizeof(addr));
 
392
        addr.sa_port = port ? port : DEFAULT_DATA_DESTINATION_PORT;
 
393
        addr.sa_addr.sport = DEFAULT_DATA_SOURCE_PORT;
 
394
        if (!pgm_gsi_create_from_hostname (&addr.sa_addr.gsi, &pgm_err)) {
 
395
                fprintf (stderr, "Creating GSI: %s\n", pgm_err->message);
 
396
                goto err_abort;
 
397
        }
 
398
 
 
399
/* assign socket to specified address */
 
400
        struct pgm_interface_req_t if_req;
 
401
        memset (&if_req, 0, sizeof(if_req));
 
402
        if_req.ir_interface = res->ai_recv_addrs[0].gsr_interface;
 
403
        if_req.ir_scope_id  = 0;
 
404
        if (AF_INET6 == sa_family) {
 
405
                struct sockaddr_in6 sa6;
 
406
                memcpy (&sa6, &res->ai_recv_addrs[0].gsr_group, sizeof(sa6));
 
407
                if_req.ir_scope_id = sa6.sin6_scope_id;
 
408
        }
 
409
        if (!pgm_bind3 (sock,
 
410
                        &addr, sizeof(addr),
 
411
                        &if_req, sizeof(if_req),        /* tx interface */
 
412
                        &if_req, sizeof(if_req),        /* rx interface */
 
413
                        &pgm_err))
 
414
        {
 
415
                fprintf (stderr, "Binding PGM socket: %s\n", pgm_err->message);
 
416
                goto err_abort;
 
417
        }
 
418
 
 
419
/* join IP multicast groups */
 
420
        for (unsigned i = 0; i < res->ai_recv_addrs_len; i++)
 
421
                pgm_setsockopt (sock, IPPROTO_PGM, PGM_JOIN_GROUP, &res->ai_recv_addrs[i], sizeof(struct group_req));
 
422
        pgm_setsockopt (sock, IPPROTO_PGM, PGM_SEND_GROUP, &res->ai_send_addrs[0], sizeof(struct group_req));
 
423
        pgm_freeaddrinfo (res);
 
424
 
 
425
/* set IP parameters */
 
426
        const int nonblocking = 1,
 
427
                  multicast_loop = use_multicast_loop ? 1 : 0,
 
428
                  multicast_hops = 16,
 
429
                  dscp = 0x2e << 2;             /* Expedited Forwarding PHB for network elements, no ECN. */
 
430
 
 
431
        pgm_setsockopt (sock, IPPROTO_PGM, PGM_MULTICAST_LOOP, &multicast_loop, sizeof(multicast_loop));
 
432
        pgm_setsockopt (sock, IPPROTO_PGM, PGM_MULTICAST_HOPS, &multicast_hops, sizeof(multicast_hops));
 
433
        if (AF_INET6 != sa_family)
 
434
                pgm_setsockopt (sock, IPPROTO_PGM, PGM_TOS, &dscp, sizeof(dscp));
 
435
        pgm_setsockopt (sock, IPPROTO_PGM, PGM_NOBLOCK, &nonblocking, sizeof(nonblocking));
 
436
 
 
437
        if (!pgm_connect (sock, &pgm_err)) {
 
438
                fprintf (stderr, "Connecting PGM socket: %s\n", pgm_err->message);
 
439
                goto err_abort;
 
440
        }
 
441
 
 
442
        puts ("Startup complete.");
 
443
        return TRUE;
 
444
 
 
445
err_abort:
 
446
        if (NULL != sock) {
 
447
                pgm_close (sock, FALSE);
 
448
                sock = NULL;
 
449
        }
 
450
        if (NULL != res) {
 
451
                pgm_freeaddrinfo (res);
 
452
                res = NULL;
 
453
        }
 
454
        if (NULL != pgm_err) {
 
455
                pgm_error_free (pgm_err);
 
456
                pgm_err = NULL;
 
457
        }
 
458
        if (NULL != sock) {
 
459
                pgm_close (sock, FALSE);
 
460
                sock = NULL;
 
461
        }
 
462
        return FALSE;
 
463
}
 
464
 
 
465
static
 
466
int
 
467
on_data (
 
468
        const void*                  restrict data,
 
469
        const size_t                          len,
 
470
        const struct pgm_sockaddr_t* restrict from
 
471
        )
 
472
{
 
473
/* protect against non-null terminated strings */
 
474
        char buf[1024], tsi[PGM_TSISTRLEN];
 
475
        const size_t buflen = MIN(sizeof(buf) - 1, len);
 
476
#ifndef CONFIG_HAVE_SECURITY_ENHANCED_CRT
 
477
        strncpy (buf, (const char*)data, buflen);
 
478
        buf[buflen] = '\0';
 
479
#else
 
480
        strncpy_s (buf, buflen, (const char*)data, _TRUNCATE);
 
481
#endif
 
482
        pgm_tsi_print_r (&from->sa_addr, tsi, sizeof(tsi));
 
483
#ifndef _WIN32
 
484
        printf ("\"%s\" (%zu bytes from %s)\n",
 
485
                        buf, len, tsi);
 
486
#else
 
487
/* Microsoft CRT will crash on %zu */
 
488
        printf ("\"%s\" (%lu bytes from %s)\n",
 
489
                        buf, (unsigned long)len, tsi);
 
490
#endif
 
491
        return 0;
 
492
}
 
493
 
 
494
/* eof */