cfad47cfa3/tads3/vmerr.cpp

4b825dc642cb6eb9a060e54bf8d69288fbee4904cfad47cfa334b206c65f22086bcc5d63e6f70944
1
#ifdef RCSID
2
static char RCSid[] =
3
"$Header: d:/cvsroot/tads/tads3/vmerr.cpp,v 1.4 1999/07/11 00:46:59 MJRoberts Exp $";
4
#endif
5
6
/* 
7
 *   Copyright (c) 1998, 2002 Michael J. Roberts.  All Rights Reserved.
8
 *   
9
 *   Please see the accompanying license file, LICENSE.TXT, for information
10
 *   on using and copying this software.  
11
 */
12
/*
13
Name
14
  vmerr.cpp - VM error handling
15
Function
16
  This module contains global variables required for the error handler.
17
Notes
18
  
19
Modified
20
  10/20/98 MJRoberts  - Creation
21
*/
22
23
#include <stdarg.h>
24
#include <stdlib.h>
25
#include <assert.h>
26
27
#include "t3std.h"
28
#include "vmtype.h"
29
#include "vmerr.h"
30
31
32
/*
33
 *   Global error context pointer, and reference count for the error
34
 *   subsystem 
35
 */
36
err_context_t *G_err = 0;
37
int G_err_refs = 0;
38
39
/*
40
 *   Initialize the global error context 
41
 */
42
void err_init(size_t param_stack_size)
43
{
44
    /* count the reference */
45
    ++G_err_refs;
46
    
47
    /* ignore this if we're already initialized */
48
    if (G_err_refs > 1)
49
        return;
50
    
51
    /* 
52
     *   allocate the error context, adding in room for the parameter
53
     *   stack 
54
     */
55
    G_err = (err_context_t *)t3malloc(sizeof(err_context_t)
56
                                      + param_stack_size);
57
58
    /* initialize the parameter stack pointers */
59
    G_err->param_free_ = G_err->param_stack_;
60
    G_err->param_stack_size_ = param_stack_size;
61
    G_err->cur_exc_ = 0;
62
63
    /* we have no active frame yet */
64
    G_err->cur_frame_ = 0;
65
}
66
67
/*
68
 *   Delete the global error context 
69
 */
70
void err_terminate()
71
{
72
    /* decrease the error system reference counter */
73
    --G_err_refs;
74
75
    /* if that leaves no references, delete the error context */
76
    if (G_err_refs == 0)
77
    {
78
        /* free the global error structure */
79
        t3free(G_err);
80
        G_err = 0;
81
82
        /* delete external messages, if we loaded them */
83
        err_delete_message_array(&vm_messages, &vm_message_count,
84
                                 vm_messages_english,
85
                                 vm_message_count_english);
86
    }
87
}
88
89
/*
90
 *   Throw the error currently on the stack 
91
 */
92
static void err_throw_current()
93
{
94
    err_state_t new_state;
95
96
    /*
97
     *   Figure out what state the enclosing frame will be in after this
98
     *   jump.  If we're currently in a 'trying' block, we'll now be in an
99
     *   'exception' state.  Otherwise, we must have been in a 'catch' or
100
     *   'finally' already - in these cases, the new state is 'rethrown',
101
     *   because we have an exception within an exception handler.  
102
     */
103
    if (G_err->cur_frame_->state_ == ERR_STATE_TRYING)
104
        new_state = ERR_STATE_EXCEPTION;
105
    else
106
        new_state = ERR_STATE_RETHROWN;
107
108
    /* jump to the enclosing frame's exception handler */
109
    longjmp(G_err->cur_frame_->jmpbuf_, new_state);
110
}
111
112
/*
113
 *   Throw an exception 
114
 */
115
void err_throw(err_id_t error_code)
116
{
117
    /* throw the error, with no parameters */
118
    err_throw_a(error_code, 0);
119
}
120
121
/*
122
 *   Allocate space in the exception parameter stack. 
123
 */
124
static void *err_stack_alloc(size_t siz)
125
{
126
    void *ret;
127
    
128
    /* round the size up to the OS allocation alignment boundary */
129
    siz = osrndsz(siz);
130
131
    /* if we don't have room in the parameter stack, it's a fatal error */
132
    if ((G_err->param_stack_size_
133
         - (G_err->param_free_ - G_err->param_stack_)) < siz)
134
        err_abort("no space for exception parameters");
135
136
    /* 
137
     *   get the current free pointer - this is where we'll allocate the
138
     *   requested space 
139
     */
140
    ret = G_err->param_free_;
141
142
    /* advance the free pointer to consume the requested space */
143
    G_err->param_free_ += siz;
144
145
    /* return a poiner to the allocated space */
146
    return ret;
147
}
148
149
/*
150
 *   Store an exception parameter string.  Allocates space for the string in
151
 *   the exception parameter stack, stores a null-terminated copy, and
152
 *   returns a pointer to the copy of the string. 
153
 */
154
static char *err_store_param_str(const char *str, size_t len)
155
{
156
    char *new_str;
157
    
158
    /* allocate space for the string and a null terminator */
159
    new_str = (char *)err_stack_alloc(len + 1);
160
161
    /* store a copy of the string */
162
    memcpy(new_str, str, len);
163
164
    /* null-terminate the copy */
165
    new_str[len] = '\0';
166
167
    /* return a pointer to the newly-allocated copy */
168
    return new_str;
169
}
170
171
/*
172
 *   Throw an exception with parameters in va_list format
173
 */
174
static void err_throw_v(err_id_t error_code, int param_count, va_list va)
175
{
176
    size_t siz;
177
    int i;
178
    CVmException *exc;
179
    CVmExcParam *param;
180
181
    /*
182
     *   Assert that the size of the err_param_type enum is no larger than
183
     *   the size of the native 'int' type.  Since this routine is called
184
     *   with a varargs list, and since ANSI requires compilers to promote
185
     *   any enum type smaller than int to int when passing to a varargs
186
     *   function, we must retrieve our err_param_type arguments (via the
187
     *   va_arg() macro) with type 'int' rather than type 'enum
188
     *   err_param_type'.
189
     *   
190
     *   The promotion to int is mandatory, so retrieving our values as
191
     *   'int' values is the correct, portable thing to do; the only
192
     *   potential problem is that our enum type could be defined to be
193
     *   *larger* than int, in which case the compiler would pass it to us
194
     *   as the larger type, not int, and our retrieval would be incorrect.
195
     *   The only way a compiler would be allowed to do this is if our enum
196
     *   type actually required a type larger than unsigned int (in other
197
     *   words, we had enum values higher than the largest unsigned int
198
     *   value for the local platform).  This is extremely unlikely, but
199
     *   we'll assert our assumption here to ensure that the problem is
200
     *   readily apparent should it ever occur.  
201
     */
202
    assert(sizeof(err_param_type) <= sizeof(int));
203
204
    /*
205
     *   If the current stack frame is already handling an error, pop the
206
     *   current error, since the current error will no longer be
207
     *   accessible after this throw.  
208
     */
209
    if (G_err->cur_frame_->state_ == ERR_STATE_EXCEPTION
210
        || G_err->cur_frame_->state_ == ERR_STATE_CAUGHT
211
        || G_err->cur_frame_->state_ == ERR_STATE_RETHROWN)
212
        err_pop_exc();
213
214
    /* 
215
     *   Figure out how much space we need.  Start with the size of the base
216
     *   CVmException class, since we always need a CVmException structure.
217
     *   This structure has space for one argument descriptor built in, so
218
     *   subtract that off from our base size, since we'll add in space for
219
     *   the argument descriptors separately.  
220
     */
221
    siz = sizeof(CVmException) - sizeof(CVmExcParam);
222
223
    /* 
224
     *   Add in space the parameter descriptors.  We need one CVmExcParam
225
     *   structure to describe each parameter.  
226
     */
227
    siz += param_count * sizeof(CVmExcParam);
228
229
    /* 
230
     *   allocate space for the base exception structure in the exception
231
     *   parameter stack 
232
     */
233
    exc = (CVmException *)err_stack_alloc(siz);
234
235
    /* fill in our base structure */
236
    exc->error_code_ = error_code;
237
    exc->param_count_ = param_count;
238
239
    /* 
240
     *   link to the previous exception on the stack, so that we can pop
241
     *   this exception when we're done with it 
242
     */
243
    exc->prv_ = G_err->cur_exc_;
244
245
    /* this is now the current exception */
246
    G_err->cur_exc_ = exc;
247
    
248
    /* 
249
     *   We now have our base exception structure, with enough space for the
250
     *   parameter descriptors, but we still need to store the parameter
251
     *   values themselves.
252
     */
253
    for (i = 0, param = exc->params_ ; i < param_count ; ++i, ++param)
254
    {
255
        err_param_type typ;
256
        const char *sptr;
257
        size_t slen;
258
        
259
        /* get the type indicator, and store it in the descriptor */
260
        typ = (err_param_type)va_arg(va, int);
261
        
262
        /* store the type */
263
        param->type_ = typ;
264
265
        /* store the argument's value */
266
        switch(typ)
267
        {
268
        case ERR_TYPE_INT:
269
            /* store the integer */
270
            param->val_.intval_ = va_arg(va, int);
271
            break;
272
273
        case ERR_TYPE_ULONG:
274
            /* store the unsigned long */
275
            param->val_.ulong_ = va_arg(va, unsigned long);
276
            break;
277
278
        case ERR_TYPE_TEXTCHAR:
279
            /* 
280
             *   It's a (textchar_t *) string, null-terminated.  Get the
281
             *   string pointer and calculate its length. 
282
             */
283
            sptr = va_arg(va, textchar_t *);
284
            slen = get_strlen(sptr);
285
286
            /* store it in parameter memory */
287
            param->val_.strval_ = err_store_param_str(sptr, slen);
288
            break;
289
290
        case ERR_TYPE_TEXTCHAR_LEN:
291
            /* 
292
             *   It's a (textchar_t *) string with an explicit length given
293
             *   as a separate size_t parameter. 
294
             */
295
            sptr = va_arg(va, textchar_t *);
296
            slen = va_arg(va, size_t);
297
298
            /* store it in parameter memory */
299
            param->val_.strval_ = err_store_param_str(sptr, slen);
300
301
            /* 
302
             *   change the type to a regular textchar string now, since
303
             *   we've converted the value to a null-terminated string 
304
             */
305
            param->type_ = ERR_TYPE_TEXTCHAR;
306
            break;
307
308
        case ERR_TYPE_CHAR:
309
            /* it's a (char *) string, null-terminated */
310
            sptr = va_arg(va, char *);
311
            slen = strlen(sptr);
312
313
            /* store it */
314
            param->val_.charval_ = err_store_param_str(sptr, slen);
315
            break;
316
317
        case ERR_TYPE_CHAR_LEN:
318
            /* it's a (char *) string with an explicit size_t size */
319
            sptr = va_arg(va, char *);
320
            slen = va_arg(va, size_t);
321
322
            /* store it */
323
            param->val_.charval_ = err_store_param_str(sptr, slen);
324
            
325
            /* skip the string */
326
            va_arg(va, char *);
327
328
            /* 
329
             *   change the type to a regular char string, since we've added
330
             *   null termination 
331
             */
332
            param->type_ = ERR_TYPE_CHAR;
333
            break;
334
        }
335
    }
336
337
    /* throw the error that we just pushed */
338
    err_throw_current();
339
}
340
341
#ifdef MICROSOFT
342
/*
343
 *   Microsoft Visual C++ optimizer workaround - not applicable to other
344
 *   systems.
345
 *   
346
 *   For MSVC, we need to turn off warning 4702 ("unreachable code").  MSVC
347
 *   is too clever by half for our implementation here of err_throw_a(), and
348
 *   the only recourse (empirically) seems to be to turn off this warning for
349
 *   the duration of this function.
350
 *   
351
 *   The issue is that err_throw_a() makes a call to err_throw_v() within a
352
 *   va_start()...va_end() pair.  The MSVC optimizer recognizes that
353
 *   err_throw_v() never returns, so it marks any code following a call to
354
 *   same as unreachable.  But we *have to* include the va_end() call after
355
 *   the err_throw_v() call.  The va_end() is *required* for portability -
356
 *   some compilers expand va_end() to structural C code (for example, to
357
 *   insert a close brace to balance an open brace inserted by va_start()).
358
 *   So we can't omit the va_end() call regardless of its run-time
359
 *   reachability.
360
 *   
361
 *   So, we have code that's both unreachable and required.  The only
362
 *   apparent solution is to disable the unreachable-code warning for the
363
 *   duration of this function.
364
 *   
365
 *   (Conceivably, we could trick the optimizer by moving the err_throw_a()
366
 *   implementation to a separate module; the optimizer probably couldn't
367
 *   track the does-not-return status of err_throw_v() across modules.  But
368
 *   that wouldn't be any cleaner in any sense - it would just trick the
369
 *   compiler in a different way.  Better to explicitly turn off the warning;
370
 *   at least that way the workaround is plain and explicit.)  
371
 */
372
#pragma warning(push)
373
#pragma warning(disable: 4702)
374
#endif
375
376
/*
377
 *   Throw an exception with parameters 
378
 */
379
void err_throw_a(err_id_t error_code, int param_count, ...)
380
{
381
    va_list marker;
382
383
    /* build the argument list and throw the error */
384
    va_start(marker, param_count);
385
    err_throw_v(error_code, param_count, marker);
386
    va_end(marker);
387
}
388
389
/* MSVC - restore previous warning state (see above) */
390
#ifdef MICROSOFT
391
#pragma warning(pop)
392
#endif
393
394
/*
395
 *   Re-throw the current exception.  This is valid only from 'catch'
396
 *   blocks.  
397
 */
398
void err_rethrow()
399
{
400
    /* throw the error currently on the stack */
401
    err_throw_current();
402
}
403
404
/*
405
 *   Pop the current exception from the exception stack. 
406
 */
407
void err_pop_exc()
408
{
409
    /* 
410
     *   if there's anything on the stack, remove it and replace it with
411
     *   the previous item on the stack 
412
     */
413
    if (G_err->cur_exc_ != 0)
414
    {
415
        /* 
416
         *   since we're removing this element, its space can now be
417
         *   re-used for the next exception allocation (since exceptions
418
         *   are always stacked, the space we use to consume them can be
419
         *   treated as a stack as well) 
420
         */
421
        G_err->param_free_ = (char *)G_err->cur_exc_;
422
423
        /* remove the top element of the stack */
424
        G_err->cur_exc_ = G_err->cur_exc_->prv_;
425
    }
426
    else
427
    {
428
        /* no errors are on the stack, so the parameter space is all free */
429
        G_err->param_free_ = G_err->param_stack_;
430
    }
431
}
432
433
/*
434
 *   Abort the program with a serious, unrecoverable error
435
 */
436
void err_abort(const char *message)
437
{
438
    printf("%s\n", message);
439
    exit(2);
440
}
441
442
/*
443
 *   Determine the current error stack depth 
444
 */
445
int err_stack_depth()
446
{
447
    int cnt;
448
    CVmException *exc;
449
450
    /* count exceptions in the stack */
451
    for (cnt = 0, exc = G_err->cur_exc_ ; exc != 0 ; exc = exc->prv_)
452
    {
453
        /* count this exception */
454
        ++cnt;
455
    }
456
457
    /* return the number of exceptions in the stack */
458
    return cnt;
459
}
460
461
/* ------------------------------------------------------------------------ */
462
/*
463
 *   Try loading a message file.  Returns zero on success, non-zero if an
464
 *   error occurred.  
465
 */
466
int err_load_message_file(osfildef *fp,
467
                          const err_msg_t **arr, size_t *arr_size,
468
                          const err_msg_t *default_arr,
469
                          size_t default_arr_size)
470
{
471
    char buf[128];
472
    size_t i;
473
    err_msg_t *msg;
474
    
475
    /* read the file signature */
476
    if (osfrb(fp, buf, sizeof(VM_MESSAGE_FILE_SIGNATURE))
477
        || memcmp(buf, VM_MESSAGE_FILE_SIGNATURE,
478
                  sizeof(VM_MESSAGE_FILE_SIGNATURE)) != 0)
479
        goto fail;
480
481
    /* delete any previously-loaded message array */
482
    err_delete_message_array(arr, arr_size, default_arr, default_arr_size);
483
484
    /* read the message count */
485
    if (osfrb(fp, buf, 2))
486
        goto fail;
487
488
    /* set the new message count */
489
    *arr_size = osrp2(buf);
490
491
    /* allocate the message array */
492
    *arr = (err_msg_t *)t3malloc(*arr_size * sizeof(err_msg_t));
493
    if (*arr == 0)
494
        goto fail;
495
496
    /* clear the memory */
497
    memset((err_msg_t *)*arr, 0, *arr_size * sizeof(err_msg_t));
498
499
    /* read the individual messages */
500
    for (i = 0, msg = (err_msg_t *)*arr ; i < *arr_size ; ++i, ++msg)
501
    {
502
        size_t len1, len2;
503
        
504
        /* read the current message ID and the length of the two messages */
505
        if (osfrb(fp, buf, 8))
506
            goto fail;
507
508
        /* set the message ID */
509
        msg->msgnum = (int)t3rp4u(buf);
510
511
        /* get the short and long mesage lengths */
512
        len1 = osrp2(buf + 4);
513
        len2 = osrp2(buf + 6);
514
515
        /* allocate buffers */
516
        msg->short_msgtxt = (char *)t3malloc(len1 + 1);
517
        msg->long_msgtxt = (char *)t3malloc(len2 + 1);
518
519
        /* if either one failed, give up */
520
        if (msg->short_msgtxt == 0 || msg->long_msgtxt == 0)
521
            goto fail;
522
523
        /* read the two messages */
524
        if (osfrb(fp, (char *)msg->short_msgtxt, len1)
525
            || osfrb(fp, (char *)msg->long_msgtxt, len2))
526
            goto fail;
527
528
        /* null-terminate the strings */
529
        *(char *)(msg->short_msgtxt + len1) = '\0';
530
        *(char *)(msg->long_msgtxt + len2) = '\0';
531
    }
532
533
    /* success */
534
    return 0;
535
536
fail:
537
    /* revert back to the built-in array */
538
    err_delete_message_array(arr, arr_size,
539
                             default_arr, default_arr_size);
540
541
    /* indicate failure */
542
    return 1;
543
}
544
545
/*
546
 *   Determine if an external message file has been loaded for the default
547
 *   VM message set 
548
 */
549
int err_is_message_file_loaded()
550
{
551
    /* 
552
     *   if we're not using the compiled-in message array, we must have
553
     *   loaded an external message set 
554
     */
555
    return vm_messages != &vm_messages_english[0];
556
}
557
558
/*
559
 *   Delete the message array, if one is loaded 
560
 */
561
void err_delete_message_array(const err_msg_t **arr, size_t *arr_size,
562
                              const err_msg_t *default_arr,
563
                              size_t default_arr_size)
564
{
565
    /* 
566
     *   If the message array is valid, and it's not set to point to the
567
     *   built-in array of English messages, we must have allocated it, so
568
     *   we must now free it.  We don't need to free it if it points to
569
     *   the English array, because that's static data linked into the VM
570
     *   executable.  
571
     */
572
    if (*arr != 0 && *arr != default_arr)
573
    {
574
        size_t i;
575
        err_msg_t *msg;
576
        
577
        /* delete each message in the array */
578
        for (i = 0, msg = (err_msg_t *)*arr ; i < *arr_size ; ++i, ++msg)
579
        {
580
            /* delete the strings for this entry */
581
            if (msg->short_msgtxt != 0)
582
                t3free((char *)msg->short_msgtxt);
583
            if (msg->long_msgtxt != 0)
584
                t3free((char *)msg->long_msgtxt);
585
        }
586
587
        /* delete the message array itself */
588
        t3free((err_msg_t *)*arr);
589
    }
590
591
    /* set the messages array back to the built-in english messages */
592
    *arr = default_arr;
593
    *arr_size = default_arr_size;
594
}
595
596
/* ------------------------------------------------------------------------ */
597
/*
598
 *   Find an error message 
599
 */
600
const char *err_get_msg(const err_msg_t *msg_array, size_t msg_count,
601
                        int msgnum, int verbose)
602
{
603
    int hi, lo, cur;
604
605
    /* perform a binary search of the message list */
606
    lo = 0;
607
    hi = msg_count - 1;
608
    while (lo <= hi)
609
    {
610
        /* split the difference */
611
        cur = lo + (hi - lo)/2;
612
613
        /* is it a match? */
614
        if (msg_array[cur].msgnum == msgnum)
615
        {
616
            /* it's the one - return the text */
617
            return (verbose
618
                    ? msg_array[cur].long_msgtxt
619
                    : msg_array[cur].short_msgtxt);
620
        }
621
        else if (msgnum > msg_array[cur].msgnum)
622
        {
623
            /* we need to go higher */
624
            lo = (cur == lo ? cur + 1 : cur);
625
        }
626
        else
627
        {
628
            /* we need to go lower */
629
            hi = (cur == hi ? cur - 1 : cur);
630
        }
631
    }
632
633
    /* no such message */
634
    return 0;
635
}
636
637
/* ------------------------------------------------------------------------ */
638
/*
639
 *   Format a message with the parameters contained in an exception object.
640
 *   Suports the following format codes:
641
 *   
642
 *   %s - String.  Formats an ERR_TYPE_CHAR or ERR_TYPE_TEXTCHAR value,
643
 *   including the counted-length versions.
644
 *   
645
 *   %d, %u, %x - signed/unsigned decimal integer, hexadecimal integer.
646
 *   Formats an ERR_TYPE_INT value or an ERR_TYPE_ULONG value.
647
 *   Automatically uses the correct size for the argument.
648
 *   
649
 *   %% - Formats as a single percent sign.  
650
 */
651
void err_format_msg(char *outbuf, size_t outbuflen,
652
                    const char *msg, const CVmException *exc)
653
{
654
    int curarg;
655
    const char *p;
656
    char *dst;
657
    int exc_argc;
658
659
    /* if there's no space at all, ignore the request */
660
    if (outbuf == 0 || outbuflen == 0)
661
        return;
662
663
    /* get the number of parameters in the exception object */
664
    exc_argc = (exc == 0 ? 0 : exc->get_param_count());
665
666
    /* start with the first parameter */
667
    curarg = 0;
668
669
    /* start at the beginning of the buffer */
670
    dst = outbuf;
671
672
    /* if there's no message, there's nothing to return */
673
    if (msg == 0)
674
    {
675
        *dst = '\0';
676
        return;
677
    }
678
679
    /* scan the format string for formatting codes */
680
    for (p = msg ; *p != '\0' ; ++p)
681
    {
682
        /* 
683
         *   If we're out of space, stop now.  Make sure we leave room for
684
         *   the terminating null byte. 
685
         */
686
        if (dst + 1 >= outbuf + outbuflen)
687
            break;
688
        
689
        /* if it's a format specifier, translate it */
690
        if (*p == '%')
691
        {
692
            const char *src;
693
            char srcbuf[30];
694
            err_param_type typ;
695
            size_t len;
696
            int use_strlen;
697
            
698
            /* 
699
             *   if no more parameters are available, ignore the
700
             *   formatting code entirely, and leave it in the string as
701
             *   it is 
702
             */
703
            if (curarg >= exc_argc)
704
            {
705
                *dst++ = *p;
706
                continue;
707
            }
708
709
            /* get the type of the current parameter */
710
            typ = exc->get_param_type(curarg);
711
            
712
            /* 
713
             *   presume we'll want to use strlen to get the length of the
714
             *   source value 
715
             */
716
            use_strlen = TRUE;
717
            
718
            /* skip the '%' and determine what follows */
719
            ++p;
720
            switch (*p)
721
            {
722
            case 's':
723
                /* get the string value using the appropriate type */
724
                if (typ == ERR_TYPE_TEXTCHAR)
725
                    src = exc->get_param_text(curarg);
726
                else if (typ == ERR_TYPE_CHAR)
727
                    src = exc->get_param_char(curarg);
728
                else if (typ == ERR_TYPE_CHAR_LEN)
729
                {
730
                    /* get the string value and its length */
731
                    src = exc->get_param_char_len(curarg, &len);
732
733
                    /* 
734
                     *   src isn't null terminated, so don't use strlen to
735
                     *   get its length - we already have it from the
736
                     *   parameter data 
737
                     */
738
                    use_strlen = FALSE;
739
                }
740
                else
741
                    src = "s";
742
                break;
743
744
            case 'd':
745
                src = srcbuf;
746
                if (typ == ERR_TYPE_INT)
747
                    sprintf(srcbuf, "%d", exc->get_param_int(curarg));
748
                else if (typ == ERR_TYPE_ULONG)
749
                    sprintf(srcbuf, "%ld", exc->get_param_ulong(curarg));
750
                else
751
                    src = "d";
752
                break;
753
754
            case 'u':
755
                src = srcbuf;
756
                if (typ == ERR_TYPE_INT)
757
                    sprintf(srcbuf, "%u", exc->get_param_int(curarg));
758
                else if (typ == ERR_TYPE_ULONG)
759
                    sprintf(srcbuf, "%lu", exc->get_param_ulong(curarg));
760
                else
761
                    src = "u";
762
                break;
763
764
            case 'x':
765
                src = srcbuf;
766
                if (typ == ERR_TYPE_INT)
767
                    sprintf(srcbuf, "%x", exc->get_param_int(curarg));
768
                else if (typ == ERR_TYPE_ULONG)
769
                    sprintf(srcbuf, "%lx", exc->get_param_ulong(curarg));
770
                else
771
                    src = "x";
772
                break;
773
774
            case '%':
775
                /* add a single percent sign */
776
                src = "%";
777
                break;
778
779
            default:
780
                /* invalid format character; leave the whole thing intact */
781
                src = srcbuf;
782
                srcbuf[0] = '%';
783
                srcbuf[1] = *p;
784
                srcbuf[2] = '\0';
785
                break;
786
            }
787
788
            /* get the length, if it's null-terminated */
789
            if (use_strlen)
790
                len = strlen(src);
791
792
            /* figure out how much of the value we can copy */
793
            if (len > outbuflen - (dst - outbuf) - 1)
794
                len = outbuflen - (dst - outbuf) - 1;
795
796
            /* copy the value and advance past it in the output buffer */
797
            memcpy(dst, src, len);
798
            dst += len;
799
800
            /* consume the argument */
801
            ++curarg;
802
        }
803
        else
804
        {
805
            /* just copy the current character as it is */
806
            *dst++ = *p;
807
        }
808
    }
809
810
    /* add the trailing null */
811
    *dst++ = '\0';
812
}
813