s_print.c 7.66 KB
Newer Older
Miller Puckette's avatar
Miller Puckette committed
1 2 3 4 5 6 7 8 9 10 11
/* Copyright (c) 1997-1999 Miller Puckette.
* For information on usage and redistribution, and for a DISCLAIMER OF ALL
* WARRANTIES, see the file, "LICENSE.txt," in this distribution.  */

#include "m_pd.h"
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include <errno.h>
#include "s_stuff.h"
12 13 14
#ifdef _MSC_VER  /* This is only for Microsoft's compiler, not cygwin, e.g. */
#define snprintf sprintf_s
#endif
Miller Puckette's avatar
Miller Puckette committed
15 16

t_printhook sys_printhook;
17
t_printhook sys_printhook_error;
Miller Puckette's avatar
Miller Puckette committed
18 19
int sys_printtostderr;

20 21 22 23 24 25 26 27
/* escape characters for tcl/tk */
static char* strnescape(char *dest, const char *src, size_t len)
{
    int ptin = 0;
    unsigned ptout = 0;
    for(; ptout < len; ptin++, ptout++)
    {
        int c = src[ptin];
28
        if (c == '\\' || c == '{' || c == '}' || c == ';')
29
            dest[ptout++] = '\\';
30 31
        dest[ptout] = src[ptin];
        if (c==0) break;
32 33 34 35 36 37 38 39 40 41 42 43 44 45
    }

    if(ptout < len)
        dest[ptout]=0;
    else
        dest[len-1]=0;

    return dest;
}

static char* strnpointerid(char *dest, const void *pointer, size_t len)
{
    *dest=0;
    if (pointer)
46
        snprintf(dest, len, ".x%zx", (t_uint)pointer);
47 48 49 50 51 52 53 54
    return dest;
}

static void doerror(const void *object, const char *s)
{
    char upbuf[MAXPDSTRING];
    upbuf[MAXPDSTRING-1]=0;

55
    if (sys_printhook || sys_printhook_error)
56 57
    {
        snprintf(upbuf, MAXPDSTRING-1, "error: %s", s);
58 59 60 61
        if (sys_printhook_error)
            (*sys_printhook_error)(upbuf);
        if (sys_printhook)
            (*sys_printhook)(upbuf);
62 63 64 65 66 67
    }
    else if (sys_printtostderr)
        fprintf(stderr, "error: %s", s);
    else
    {
        char obuf[MAXPDSTRING];
user's avatar
user committed
68 69 70
        //sys_vgui("pdtk_posterror {%s} 1 {%s}\n",
        //    strnpointerid(obuf, object, MAXPDSTRING),
        //    strnescape(upbuf, s, MAXPDSTRING));
Jonathan Wilkes's avatar
Jonathan Wilkes committed
71 72 73 74
        gui_vmess("gui_post_error", "sis",
            strnpointerid(obuf, object, MAXPDSTRING),
            1,
            strnescape(upbuf, s, MAXPDSTRING));
75 76 77
    }
}

78 79
static void dologpost(const void *object, const int level, const char *s)
{
80 81 82 83
    /* 1. s is at most MAXPDSTRING, but we're prepending a stupid header
       below. So for sanity, we first overallocate here to ensure the stupid
       header doesn't end up overflowing the buffer. */
    char upbuf[MAXPDSTRING * 2];
84 85 86 87

    // what about sys_printhook_verbose ?
    if (sys_printhook) 
    {
88 89 90 91 92 93 94 95 96
        /* 2. The "n" in snprintf stands for "evil": we have to subtract one
           from total size so the null doesn't get truncated */ 
        snprintf(upbuf, MAXPDSTRING * 2 - 1, "verbose(%d): %s", level, s);
        /* 3. Finally, we add a null at MAXPDSTRING-1 so that we end up with
           a string that fits inside MAXPDSTRING for use with t_symbol, etc.

           If anyone knows how I was *supposed* to do this safely within the
           constraints of C's stupid stdlib, please teach me... */
        upbuf[MAXPDSTRING-1]=0;
97 98 99 100 101 102 103 104 105 106 107
        (*sys_printhook)(upbuf);
    }
    else if (sys_printtostderr) 
    {
        fprintf(stderr, "verbose(%d): %s", level, s);
    }
    else
    {
        //sys_vgui("::pdwindow::logpost {%s} %d {%s}\n", 
                 //strnpointerid(obuf, object, MAXPDSTRING), 
                 //level, strnescape(upbuf, s, MAXPDSTRING));
user's avatar
user committed
108 109 110
        //sys_vgui("pdtk_post {%s}\n", 
        //         strnescape(upbuf, s, MAXPDSTRING));
        gui_vmess("gui_post", "s",
111 112 113 114
                 strnescape(upbuf, s, MAXPDSTRING));
    }
}

Miller Puckette's avatar
Miller Puckette committed
115 116 117 118 119 120 121 122 123 124
static void dopost(const char *s)
{
    if (sys_printhook)
        (*sys_printhook)(s);
    else if (sys_printtostderr)
        fprintf(stderr, "%s", s);
    else
    {
        char upbuf[MAXPDSTRING];
        int ptin = 0, ptout = 0, len = strlen(s);
125 126 127
        //static int heldcr = 0;
        //if (heldcr)
        //    upbuf[ptout++] = '\n', heldcr = 0;
Miller Puckette's avatar
Miller Puckette committed
128 129 130 131 132 133 134 135
        for (; ptin < len && ptout < MAXPDSTRING-3;
            ptin++, ptout++)
        {
            int c = s[ptin];
            if (c == '\\' || c == '{' || c == '}' || c == ';')
                upbuf[ptout++] = '\\';
            upbuf[ptout] = s[ptin];
        }
136 137
        //if (ptout && upbuf[ptout-1] == '\n')
        //    upbuf[--ptout] = 0, heldcr = 1;
Miller Puckette's avatar
Miller Puckette committed
138
        upbuf[ptout] = 0;
Jonathan Wilkes's avatar
Jonathan Wilkes committed
139 140
//        sys_vgui("pdtk_post {%s}\n", upbuf);
        gui_vmess("gui_post", "s", upbuf);
Miller Puckette's avatar
Miller Puckette committed
141 142 143
    }
}

144 145 146 147 148 149 150 151 152 153 154 155
void logpost(const void *object, const int level, const char *fmt, ...)
{
    char buf[MAXPDSTRING];
    va_list ap;
    va_start(ap, fmt);
    vsnprintf(buf, MAXPDSTRING-1, fmt, ap);
    va_end(ap);
    strcat(buf, "\n");

    dologpost(object, level, buf);
}

Miller Puckette's avatar
Miller Puckette committed
156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187
void post(const char *fmt, ...)
{
    char buf[MAXPDSTRING];
    va_list ap;
    va_start(ap, fmt);
    vsnprintf(buf, MAXPDSTRING-1, fmt, ap);
    va_end(ap);
    strcat(buf, "\n");
    dopost(buf);
}

void startpost(const char *fmt, ...)
{
    char buf[MAXPDSTRING];
    va_list ap;
    va_start(ap, fmt);
    vsnprintf(buf, MAXPDSTRING-1, fmt, ap);
    va_end(ap);
    dopost(buf);
}

void poststring(const char *s)
{
    dopost(" ");
    dopost(s);
}

void postatom(int argc, t_atom *argv)
{
    int i;
    for (i = 0; i < argc; i++)
    {
Miller Puckette's avatar
Miller Puckette committed
188 189
        char buf[MAXPDSTRING];
        atom_string(argv+i, buf, MAXPDSTRING);
Miller Puckette's avatar
Miller Puckette committed
190 191 192 193
        poststring(buf);
    }
}

194
void postfloat(t_float f)
Miller Puckette's avatar
Miller Puckette committed
195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213
{
    t_atom a;
    SETFLOAT(&a, f);
    postatom(1, &a);
}

void endpost(void)
{
    dopost("\n");
}

void error(const char *fmt, ...)
{
    char buf[MAXPDSTRING];
    va_list ap;
    va_start(ap, fmt);
    vsnprintf(buf, MAXPDSTRING-1, fmt, ap);
    va_end(ap);
    strcat(buf, "\n");
214 215

    doerror(NULL, buf);
Miller Puckette's avatar
Miller Puckette committed
216 217 218 219 220 221 222 223
}

void verbose(int level, const char *fmt, ...)
{
    char buf[MAXPDSTRING];
    va_list ap;
    if(level>sys_verbose)return;
    dopost("verbose(");
224
    postfloat((t_float)level);
Miller Puckette's avatar
Miller Puckette committed
225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246
    dopost("):");
    
    va_start(ap, fmt);
    vsnprintf(buf, MAXPDSTRING-1, fmt, ap);
    va_end(ap);
    strcat(buf, "\n");
    dopost(buf);
}

    /* here's the good way to log errors -- keep a pointer to the
    offending or offended object around so the user can search for it
    later. */

static void *error_object;
static char error_string[256];
void canvas_finderror(void *object);

void pd_error(void *object, const char *fmt, ...)
{
    char buf[MAXPDSTRING];
    va_list ap;
    static int saidit;
247

Miller Puckette's avatar
Miller Puckette committed
248 249 250 251
    va_start(ap, fmt);
    vsnprintf(buf, MAXPDSTRING-1, fmt, ap);
    va_end(ap);
    strcat(buf, "\n");
252 253 254

    doerror(object, buf);

Miller Puckette's avatar
Miller Puckette committed
255 256 257
    error_object = object;
    if (!saidit)
    {
258 259
        /* move this to a function in the GUI so that we can change the
           message without having to recompile */
260
        post("... click the link above to track it down, or click the 'Find Last Error' item in the Edit menu.");
Miller Puckette's avatar
Miller Puckette committed
261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276
        saidit = 1;
    }
}

void glob_finderror(t_pd *dummy)
{
    if (!error_object)
        post("no findable error yet.");
    else
    {
        post("last trackable error:");
        post("%s", error_string);
        canvas_finderror(error_object);
    }
}

277 278 279
void glob_findinstance(t_pd *dummy, t_symbol*s)
{
    // revert s to (potential) pointer to object
280 281
    t_int obj = 0;
    if (sscanf(s->s_name, ".x%zx", &obj))
282 283 284 285 286 287 288 289
    {
        if (obj)
        {
            canvas_finderror((void *)obj);
        }
    }
}

Miller Puckette's avatar
Miller Puckette committed
290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322
void bug(const char *fmt, ...)
{
    char buf[MAXPDSTRING];
    va_list ap;
    dopost("consistency check failed: ");
    va_start(ap, fmt);
    vsnprintf(buf, MAXPDSTRING-1, fmt, ap);
    va_end(ap);
    strcat(buf, "\n");
    dopost(buf);
}

    /* this isn't worked out yet. */
static const char *errobject;
static const char *errstring;

void sys_logerror(const char *object, const char *s)
{
    errobject = object;
    errstring = s;
}

void sys_unixerror(const char *object)
{
    errobject = object;
    errstring = strerror(errno);
}

void sys_ouch(void)
{
    if (*errobject) error("%s: %s", errobject, errstring);
    else error("%s", errstring);
}