/*
 * Solaris X consolidation configuration for Parfait static analysis
 * This file provides additional information about functions that Parfait
 * can use to make more accurate analysis.
 */

/*
 * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice (including the next
 * paragraph) shall be included in all copies or substantial portions of the
 * Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 */

include 'std.conf';

/* Local copy until added to parfait's std.conf (Bug 14121100) */
asprintf(p,const fmt,args...) : printf(fmt,args), noescape {
    if (result >= 0) {
        *p == new(result);
    }
}
vasprintf(p,const fmt,va) : noescape {
    if (result >= 0) {
        *p == new(result);
    }
}

/* Local copy until added to parfait's std.conf (Bug 14121213) */
strndup(const s, n) : noescape {
    if ( result != 0 ) {
        result == new(undefined);
        strlen(result) == PRE ( strlen(s) < n ? strlen(s) : n);
    }
}

/* Define custom bug types for common precondition assertions */
bugtype "null-pointer-deref-call-X"(ptr) : "null-pointer-deref" {
   name="Null Pointer Dereference in X function";
   priority="error";
   enabled="true";
   message="Null pointer dereference in call to ", callee_name(), ". Pointer ", name(ptr), " may be dereferenced";
}

/*************************************************************************
 * Client side functions
 */

/** libX11 **/
bugtype "X-resource-leak"(xid) {
    name="X Resource Leak";
    priority="error";
    enabled="true";
    message="Leaked X Resource ", name(xid);
}

resource <x-resource> {
    name="X Resource";
    "X-resource-leak"(resource): noleak(resource);
    resource == 0 => !isa(resource);
}

/* Listed pretty much in same order as <X11/Xlib.h> */

/* Todo: XLoadQueryFont...XNewModifiermap */

XCreateImage(const display, v, dp, f, o, data, w, h, bp, bpl) {
    "null-pointer-deref-call-X"(display) : display != 0;
    if (result != 0) {
        result == new(undefined);
    }
}
XDestroyImage(p) { /* actually in <X11/Xutil.h> */
    "null-pointer-deref-call-X"(p) : p != 0;
    delete(p);
}
/* Todo: XInitImage, XGetImage, XGetSubImage */

XOpenDisplay(const d) {
    if (result != 0) {
        result == new(undefined);
    }
}

XFetchBuffer(d, n, b) {
    "null-pointer-deref-call-X"(d) : d != 0;
    if (result != 0) {
        *n == size(result);
        result == new(*n);
    }
}
XFetchBytes(d, n) : XFetchBuffer(d, n, 0);

XGetAtomName(d, a) {
    "null-pointer-deref-call-X"(d) : d != 0;
    if (result != 0) {
        result == new(undefined);
    }
}
/* Todo: XGetAtomNames, XGetDefault, XDisplayName, XKeysymToString,
   XInternAtom, XInternAtoms */

XCopyColormapAndFree(d, c) : noescape {
    "null-pointer-deref-call-X"(d) : d != 0;
    result != 0;
    init<x-resource>(result);
}
XCreateColormap(d, w, v, a) : noescape {
    "null-pointer-deref-call-X"(d) : d != 0;
    result != 0;
    init<x-resource>(result);
}
XCreatePixmapCursor(d, s, m, fg, bg, x, y) : noescape {
    "null-pointer-deref-call-X"(d) : d != 0;
    result != 0;
    init<x-resource>(result);
}
XCreateGlyphCursor(d, sf, mf, sc, mc, fg, bg) : noescape {
    "null-pointer-deref-call-X"(d) : d != 0;
    result != 0;
    init<x-resource>(result);
}
XCreateFontCursor(d, s) : noescape {
    "null-pointer-deref-call-X"(d) : d != 0;
    result != 0;
    init<x-resource>(result);
}
XLoadFont(d, c) : noescape {
    "null-pointer-deref-call-X"(d) : d != 0;
    result != 0;
    init<x-resource>(result);
}

XCreateGC(d, dr, vm, v) : noescape {
    "null-pointer-deref-call-X"(d) : d != 0;
    if (result != 0) {
        result == new(112); /* sizeof(struct _XGC) */
    }
}
/* Todo: XGContextFromGC, XFlushGC */

XCreatePixmap(d, dr, w, h, dp) {
    "null-pointer-deref-call-X"(d) : d != 0;
    result != 0;
    init<x-resource>(result);
}
XCreateBitmapFromData(d, dr, dt, w, h) {
    "null-pointer-deref-call-X"(d) : d != 0;
    result != 0;
    init<x-resource>(result);
}
XCreatePixmapFromBitmapData(d, dr, dt, w, h, fg, bg, dp) {
    "null-pointer-deref-call-X"(d) : d != 0;
    result != 0;
    init<x-resource>(result);
}
XCreateSimpleWindow(d,p,x,y,w,h,bw,b,bg) {
    "null-pointer-deref-call-X"(d) : d != 0;
    result != 0;
    init<x-resource>(result);
}
/* Todo: XGetSelectionOwner */
XCreateWindow(d,p,x,y,w,h,bw,dp,c,v,vm,a) {
    "null-pointer-deref-call-X"(d) : d != 0;
    result != 0;
    init<x-resource>(result);
}
/* Todo: XListInstalledColormaps, XListFonts, XListFontsWithInfo, XGetFontPath */

XListExtensions(d, n) : noescape {
    "null-pointer-deref-call-X"(d) : d != 0;
    if (result != 0) {
        result == new(undefined); /* n * char* + extension strings */
    }
}
XListProperties(d, w, n) : noescape {
    "null-pointer-deref-call-X"(d) : d != 0;
    if (result != 0) {
        result == new(n * 4); 
    }
}
XListHosts(d, n, s) : noescape {
    "null-pointer-deref-call-X"(d) : d != 0;
    if (result != 0) {
        result == new(undefined); /* n * char* + extension strings */
    }
}

XKeycodeToKeysym(d, k, i) : noescape {
    "null-pointer-deref-call-X"(d) : d != 0;
}
XLookupKeysym(e, i) : noescape {
    "null-pointer-deref-call-X"(e) : e != 0;
}   
XGetKeyboardMapping(d, fk, kc, ks) : noescape {
    "null-pointer-deref-call-X"(d) : d != 0;
    if (result != 0) {
        result == new(kc * ks);
    }
}
XStringToKeysym(s) : noescape {
    "null-pointer-deref-call-X"(s) : s != 0;
}
XMaxRequestSize(d) : noescape {
    "null-pointer-deref-call-X"(d) : d != 0;
}
XExtendedMaxRequestSize(d) : XMaxRequestSize(d);
XScreenResourceString(d) : XMaxRequestSize(d);
XDisplayMotionBufferSize(d) : XMaxRequestSize(d);
XVisualIdFromVisual(v) : noescape {
    /* Todo: visual resource id? */
    "null-pointer-deref-call-X"(v) : v != 0;
}

XLockDisplay(d) : XMaxRequestSize(d);
XUnlockDisplay(d) : XMaxRequestSize(d);

XAddExtension(d) {
    "null-pointer-deref-call-X"(d) : d != 0;
    if (result != 0) {
        result == new(128);
    }
}
XInitExtension(d, n) : XAddExtension(d);
XFindOnExtensionList(s, n) {
    "null-pointer-deref-call-X"(s) : s != 0;
}

/* these are routines for which there are also macros */
XDefaultRootWindow(d) : noescape {
    "null-pointer-deref-call-X"(d) : d != 0;
}
XRootWindow(d, n) : XDefaultRootWindow(d);
XRootWindowOfScreen(s) : XDefaultRootWindow(s);
XDefaultVisual(d, n) : XDefaultRootWindow(d);
XDefaultVisualOfScreen(s) : XDefaultRootWindow(s);
XDefaultGC(d, n) : XDefaultRootWindow(d);
XDefaultGCOfScreen(s) : XDefaultRootWindow(s);
XBlackPixel(d, n) : XDefaultRootWindow(d);
XWhitePixel(d, n) : XDefaultRootWindow(d);
XBlackPixelOfScreen(s) : XDefaultRootWindow(s);
XWhitePixelOfScreen(s) : XDefaultRootWindow(s);
XNextRequest(d) : XDefaultRootWindow(d);
XLastKnownRequestProcessed(d) : XDefaultRootWindow(d);
XServerVendor(d) : XDefaultRootWindow(d);
XDisplayString(d) : XDefaultRootWindow(d);

XDefaultColormap(d, n) : noescape {
    /* Todo: colormap resource id? */
    "null-pointer-deref-call-X"(d) : d != 0;
}
XDefaultColormapOfScreen(s) : XDefaultColormap(s, 0);

XDisplayOfScreen(s) : XDefaultRootWindow(s);
XScreenOfDisplay(d, n) : XDefaultRootWindow(d);
XDefaultScreenOfDisplay(d) : XDefaultRootWindow(d);
XEventMaskOfScreen(s) : XDefaultRootWindow(s);
XScreenNumberOfScreen(s) : XDefaultRootWindow(s);

/* Todo: XSetErrorHandler, XSetIOErrorHandler */

XListPixmapFormats(d, c) : noescape {
    "null-pointer-deref-call-X"(d) : d != 0;
    "null-pointer-deref-call-X"(c) : c != 0;
    if (result != 0) {
        result == new(c * 12);  /* sizeof(XPixmapFormatValues) */
    }    
}
XListDepths(d, s, c) : noescape {
    "null-pointer-deref-call-X"(d) : d != 0;
    "null-pointer-deref-call-X"(c) : c != 0;
    if (result != 0) {
        result == new(c * 4);  /* sizeof(int) */
    }    
}
/* Todo: ICCCM routines - XReconfigureWMWindow...XSetWMColormapWindows,
   XSetTransientForHint */
XFreeStringList(l) : free(l);

XActivateScreenSaver(d) : noescape {
    "null-pointer-deref-call-X"(d) : d != 0;
}
XAddHost(d, h) : noescape {
    "null-pointer-deref-call-X"(d) : d != 0;
    "null-pointer-deref-call-X"(h) : h != 0;
}
XAddHosts(d, h, n) : noescape {
    "null-pointer-deref-call-X"(d) : d != 0;
    "null-pointer-deref-call-X"(h) : h != 0;
}
/* Todo: XAddToExtensionList...XClearWindow */
XCloseDisplay(d) {
    "null-pointer-deref-call-X"(d) : d != 0;
    delete(d);
}
/* Todo: XConfigureWindow...XDeleteProperty */
XDestroyWindow(d, w) {
    "null-pointer-deref-call-X"(d) : d != 0;
    destroy<x-resource>(w);
}
XDestroySubwindows(d, w) : XDestroyWindow(d, w); /* Todo: subwindows? */
/* Todo: XDoesBackingStore...XDisplayHeightMM */
XDisplayKeycodes(d, min, max) : noescape {
    "null-pointer-deref-call-X"(d) : d != 0;
    min >= 8;
    max <= 255;
}
/* Todo: XDisplayPlanes...XEventsQueued */
XFetchName(d, w, name) : noescape {
    "null-pointer-deref-call-X"(d) : d != 0;
    if (name != 0) {
       name == new(undefined);
    }
}
/* Todo: XFillArcs...XForceScreenSaver */
XFree(p) : free(p);
XFreeColormap(d, c) : noescape {
    "null-pointer-deref-call-X"(d) : d != 0;
    destroy<x-resource>(c);
}
/* Todo: XFreeColors */
XFreeCursor(d, c) : noescape {
    "null-pointer-deref-call-X"(d) : d != 0;
    destroy<x-resource>(c);
}
XFreeExtensionList(l) : free(l);
XFreeFont(d, f) : noescape {
    "null-pointer-deref-call-X"(d) : d != 0;
    destroy<x-resource>(f);
}
/* Todo: XFreeFontInfo */
XFreeFontNames(l) : free(l);
XFreeFontPath(l) : free(l);
XFreeGC(d, gc) : noescape {
    "null-pointer-deref-call-X"(d) : d != 0;
    destroy<x-resource>(gc);
}
XFreeModifierMap(m) : free(m);
XFreePixmap(d, p) : noescape {
    "null-pointer-deref-call-X"(d) : d != 0;
    destroy<x-resource>(p);
}
/* Todo: XGeometry...XGetGeometry */
XGetIconName(d, w, name) : XFetchName(d, w, name);
/* Todo: XGetInputFocus...XGetTransientForHint */
XGetWindowProperty(d, w, p, lo, ll, del, rt, at, af, n, bar, prop) : noescape {
    "null-pointer-deref-call-X"(d) : d != 0;
    if (prop != 0) {
       prop == new(undefined);
    }
}
/* Todo: XGetWindowAttributes...XQueryColors */
XQueryExtension(d, n, op, ev, er) : noescape {
    "null-pointer-deref-call-X"(d) : d != 0;
    if (result != 0) {
       op >= 128;
       op <= 255;
    }
}
/* Todo: XQueryKeymap...XQueryTextExtents16 */
XQueryTree(d, w, r, p, c, nc) : noescape {
    "null-pointer-deref-call-X"(d) : d != 0;
    "null-pointer-deref-call-X"(c) : c != 0;
    if (result != 0) {
       c == new(nc * 4);
    }
}
/* Todo: XRaiseWindow...XRemoveFromSaveSet */
XRemoveHost(d, h) : XAddHost(d, h);
XRemoveHosts(d, h, n) : XAddHosts(d, h, n);
/* Todo: XReparentWindow...XFreeEventData */

/* Allocation functions from <X11/Xutil.h> */
XAllocClassHint() : malloc(size(result));
XAllocIconSize() : malloc(size(result));
XAllocSizeHints() : malloc(size(result));
XAllocStandardColormap() : malloc(size(result));
XAllocWMHints() : malloc(size(result));

/** libXt
 * Functions from Xt's Alloc.c - like libc ones, except exit on failure,
 * instead of return NULL
 */
XtAsprintf(p,const fmt,args...) : printf(fmt,args), noescape {
    *p == new(result);
}

XtMalloc(s) {
    result == new(s);
}

XtRealloc(p,s) {
    delete(p);
    result == new(s);
}

XtCalloc(n,s) {
    result == new(n*s);
}

XtFree(p) : free(p);

/*************************************************************************
 * Server side functions
 */

/* include/misc.h: byte swapping */
          /* XXX - does not work! */
bugtype "wrong-size-swap"(ptr,funcsize) {
    name="Wrong size Xserver swap function used";
    priority="error";
    enabled="true";
    message="Wrong size swap function used: ", callee_name(), " expects ", funcsize, "-byte variable, got ", size(ptr),"-byte ", name(ptr), " instead.";
}

swap_uint32(x) : noescape {
    "wrong-size-swap"(x,4): size(*x) == 4;
}
swap_uint16(x) : noescape {
    "wrong-size-swap"(x,2): size(*x) == 4;
}
SwapLongs(x,count) : noescape {
    "wrong-size-swap"(x,4): size(x) == 4;
}
SwapShorts(x,count) : noescape {
    "wrong-size-swap"(x,2): size(x) == 4;
}

/* dix/pixmap.c */
AllocatePixmap(const pScreen, pixDataSize) : noescape {
    if (result != 0) {
        result == new(pixDataSize);
    }
}
FreePixmap(p) : free(p);

/* dix/region.c */
RegionCreate(const rect, s) {
    result == new(12); /* sizeof struct pixman_region16 */
}
RegionDestroy(p) : free(p);

/* fb/fbpixmap.c */
fbCreatePixmapBpp(const pS, w, h, d, b, ht) {
    if (result != 0) {
        result == new(w*h);
    }
}
fbCreatePixmap(const pS, w, h, d, ht) : fbCreatePixmapBpp(pS, w, h, d, d, ht);
fbDestroyPixmap(p) : FreePixmap(p);
                 
/* os/log.c: logging functions */
LogWrite(verb, const fmt, args...) : printf(fmt, args), noescape;
LogMessageVerb(type, verb, const fmt, args...) : printf(fmt, args), noescape;
LogMessage(type, const fmt, args...) : printf(fmt, args), noescape;
FatalError(const fmt, args...) : printf(fmt, args), noescape, exit;
ErrorF(const fmt, args...) : printf(fmt, args), noescape;
AbortServer(): exit;

/* os/utils.c: allocation & other helpers */
Xalloc(s) : malloc(s);
XNFalloc(s) {
    result == new(s);
}
Xcalloc(s) : calloc(1,s);
XNFcalloc(s) : XNFalloc(s);
Xrealloc(p, s) : realloc(p, s);
XNFrealloc(p, s) : realloc(p, s);
Xfree(p) : free(p);
Xstrdup(const s) : noescape {
    if ( s == 0 ) {
        result == 0;
    } else {
        if( result != 0 ) {
            result == new(strlen(s)+1);
            strlen(result) == strlen(s);
        }
    }
}
XNFstrdup(const s) : noescape {
    if ( s == 0 ) {
        result == 0;
    } else {
        result == new(strlen(s)+1);
        strlen(result) == strlen(s);
    }
}
OsAbort() : abort();
Fopen(f, m) : fopen(f, m);
Fclose(p) : fclose(p);
          
/* os/xprintf.c: allocating printf wrappers/variants */
Xvasprintf(p, fmt, va) : vasprintf(p, fmt, va);
Xasprintf(p, fmt, args...) : asprintf(p, fmt, args);
XNFvasprintf(p, fmt, va) : noescape {
    *p == new(result);
}
XNFasprintf(p, fmt, args...) : noescape, printf(fmt, args) {
    *p == new(result);
}
Xvprintf(fmt, va) : noescape {
    if (result != 0) {
        result == new(undefined);
    }
}
Xprintf(fmt, args...) : noescape, printf(fmt, args) {
    if (result != 0) {
        result == new(undefined);
    }
}
XNFvprintf(fmt, va) : noescape {
    result == new(undefined);
}
XNFprintf(fmt, args...) : noescape, printf(fmt, args) {
    result == new(undefined);
}
