
/*
 *      Copyright (c) 2023, an unpublished work by CodeSecure, Inc.
 *                      ALL RIGHTS RESERVED
 *
 *      Copyright (c) 2007-2023, an unpublished work by GrammaTech, Inc.
 *                      ALL RIGHTS RESERVED
 *
 *      This software is furnished under a license and may be used and
 *      copied only in accordance with the terms of such license and the       
 *      inclusion of the above copyright notice.  This software or any
 *      other copies thereof may not be provided or otherwise made
 *      available to any other person.  Title to and ownership of the
 *      software is retained by CodeSecure, Inc.
 */

/* UCvar_plugin.c
 * 
 * a small example plugin that reports declarations for variables
 * whose names contain upper case characters
 */

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>


/* All C plug-ins for CodeSonar must include this file. */

#include "csonar_plugin.h"

/* This plug-in accesses various parts of the internal representation
 * (IR), so the relevant API headers must be included. */

#include "cs_pdg_vertex.h"
#include "cs_abs_loc.h"
#include "cs_pdg.h"
#include "cs_utility.h"
#include "cs_source_files.h"

/* upvar is a new \link WarningClasses/WarningClasses.html warning
 * class \endlink defined by this plug-in. It must be created in
 * a setup visitor. */

static cs_warningclass_t upvar;


/* helper function for check_var_case 
 *
 * returns 1 if instr contains an upper case character
 * (or a bad-valued character, although this should never happen)
 * 0 otherwise
 */
static int has_upper_char(cs_string instr, cs_size_t sz)
{
    cs_size_t i;
    for (i=0; i<sz; i++)
    {
        if (isupper((unsigned char) instr[i])) return 1;
    }
    return 0;
}


/* Visitors should be at the finest granularity that is appropriate
 * for the check. This plug-in is checking a property of variable
 * names, so we define a visitor that operates on variable ABS_LOCs. */

static void check_var_case(cs_abs_loc sym, void *ctx)
{
    cs_string varname;
    cs_size_t namesize,ns;
    char * buf;
    cs_abs_loc_kind varkind;

    /* We are only interested in user-defined variables. */
    if ( cs_abs_loc_get_kind(sym, &varkind) != CS_SUCCESS ) return;
    if ( varkind != cs_abs_loc_kind_user) return;
    
    /* Retrieving the variable name is a three-step process.  
     *
     * 1. Call cs_abs_loc_name() with NULL and 0 as the second and
     * third parameters, respectively, in order to set namesize to the
     * length of the name (+1). This call should return CS_TRUNCATED,
     * since a variable name should not be able to fit into a 0-byte
     * space.
     *
     * 2. Allocate space for the varname according to the value of
     * namesize.
     *
     * 3. Call cs_abs_loc_name() again to set varname to the variable
     * name.
     */
    if ( cs_abs_loc_name(sym,NULL,0,&namesize) == CS_TRUNCATED)
    {
        varname = malloc(namesize * sizeof(char));

        /* Check that malloc() didn't fail. */
        if( !varname )         
            abort();
        if ( cs_abs_loc_name(sym,varname,namesize,&ns) 
             == CS_SUCCESS)
        {
            /* If an upper case character is found, and the variable
             * is declared in user code, issue a warning and return.*/
            if ( has_upper_char(varname, namesize) )
            {
                cs_sfid sfid;
                cs_line line;
                cs_result r;

                r = cs_abs_loc_file_line( sym, &sfid, &line );

                /* ... and if the variable is declared at some
                 * location in the source code... */
                if ( r == CS_SUCCESS )
                {
                    char filename[1024];
                    cs_size_t filename_sz;

                    /* ... and that location is not in a library...*/  
                    r = cs_file_get_include_name( sfid, 
                                                  filename, 
                                                  sizeof(filename), 
                                                  &filename_sz );
                    if( r != CS_TRUNCATED && r != CS_SUCCESS )
                        abort();

                    if( strstr( filename, "/libmodels/" )
                        || strstr( filename, "\\libmodels\\" )
                        || strstr( filename, "/smel/" )
                        || strstr( filename, "\\smel\\" ) )
                    {
                        free(varname);
                        return;
                    }
                    /* ... then construct and issue a warning.*/
                    buf = malloc(namesize + strlen("variable name %s has upper case character"));
                    if( !buf )
                        abort();
                    sprintf( buf,    
                             "variable name %s has upper case character",
                             varname );
                    csonar_report_location_warning( sfid, 
                                                    line, 
                                                    upvar, 
                                                    buf,
                                                    csrf_none,
                                                    NULL,
                                                    NULL);
                    free(buf);
                } 
                free(varname);
                return;
            } 
        } 
        free(varname);
    }
}


/* The plug-in defines a new warning class to go with its new
 * check. We do not associate any \link
 * WarningClasses/WarningClasses.html CodeSonar mnemonics \endlink or
 * \link WarningClasses/CWE/CWE.html CWE identifiers 
 * \endlink with the new class, as none are appropriate. 
 * The new warning class must be created with
 * csonar_create_warningclass(), which must be called from a setup
 * visitor.*/

static void setup(void *ctx)
{
    upvar = csonar_create_warningclass(
            "Variable name has upper case characters",
            "",
            1.0,
            csonar_bcf_padding,
            csws_style );
}


/* Every C plug-in for CodeSonar must define this function,
 * which must contain all the calls to csonar_add_*_visitor() and
 * csonar_create_warningclass() required for the plug-in. */

static void cs_plug_main(void)
{
    csonar_add_setup_visitor(NULL, setup, NULL);

    /* check_var_case() must be added as an ABS_LOC visitor inside
     * cs_plug_main() in order for CodeSonar to add it to the
     * visitor list. */
    csonar_add_abs_loc_visitor(NULL, check_var_case, NULL);
}

/* All CodeSonar C plug-ins require a line of this form at the
 * top level. Note that the argument to CS_DEFINE_PLUGIN corresponds
 * to the name of the compiled plug-in (which will be either
 * UCvar_plugin.dll or UCvar_plugin.so, depending on your system).*/

CS_DEFINE_PLUGIN(UCvar_plugin)
