Home | History | Annotate | Line # | Download | only in lint1
      1  1.13  rillig /* $NetBSD: ckctype.c,v 1.13 2024/12/18 18:14:54 rillig Exp $ */
      2   1.1  rillig 
      3   1.1  rillig /*-
      4   1.1  rillig  * Copyright (c) 2021 The NetBSD Foundation, Inc.
      5   1.1  rillig  * All rights reserved.
      6   1.1  rillig  *
      7   1.1  rillig  * This code is derived from software contributed to The NetBSD Foundation
      8   1.1  rillig  * by Roland Illig <rillig (at) NetBSD.org>.
      9   1.1  rillig  *
     10   1.1  rillig  * Redistribution and use in source and binary forms, with or without
     11   1.1  rillig  * modification, are permitted provided that the following conditions
     12   1.1  rillig  * are met:
     13   1.1  rillig  * 1. Redistributions of source code must retain the above copyright
     14   1.1  rillig  *    notice, this list of conditions and the following disclaimer.
     15   1.1  rillig  * 2. Redistributions in binary form must reproduce the above copyright
     16   1.1  rillig  *    notice, this list of conditions and the following disclaimer in the
     17   1.1  rillig  *    documentation and/or other materials provided with the distribution.
     18   1.1  rillig  *
     19   1.1  rillig  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
     20   1.1  rillig  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     21   1.1  rillig  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     22   1.1  rillig  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
     23   1.1  rillig  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     24   1.1  rillig  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     25   1.1  rillig  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     26   1.1  rillig  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     27   1.1  rillig  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     28   1.1  rillig  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     29   1.1  rillig  * POSSIBILITY OF SUCH DAMAGE.
     30   1.1  rillig  */
     31   1.1  rillig 
     32   1.1  rillig #if HAVE_NBTOOL_CONFIG_H
     33   1.1  rillig #include "nbtool_config.h"
     34   1.1  rillig #endif
     35   1.1  rillig 
     36   1.1  rillig #include <sys/cdefs.h>
     37   1.1  rillig 
     38   1.5  rillig #if defined(__RCSID)
     39  1.13  rillig __RCSID("$NetBSD: ckctype.c,v 1.13 2024/12/18 18:14:54 rillig Exp $");
     40   1.1  rillig #endif
     41   1.1  rillig 
     42   1.1  rillig #include <string.h>
     43   1.1  rillig 
     44   1.1  rillig #include "lint1.h"
     45   1.1  rillig 
     46   1.1  rillig /*
     47   1.4  rillig  * Check that the functions from <ctype.h> are used properly.  They must not
     48   1.4  rillig  * be called with an argument of type 'char'.  In such a case, the argument
     49   1.4  rillig  * must be converted to 'unsigned char'.  The tricky thing is that even though
     50   1.6  rillig  * the parameter type is declared as 'int', a 'char' argument must not be
     51   1.6  rillig  * directly cast to 'int', as that would preserve negative argument values.
     52   1.1  rillig  *
     53   1.4  rillig  * See also:
     54   1.4  rillig  *	ctype(3)
     55   1.4  rillig  *	https://stackoverflow.com/a/60696378
     56   1.1  rillig  */
     57   1.1  rillig 
     58   1.1  rillig static bool
     59   1.1  rillig is_ctype_function(const char *name)
     60   1.1  rillig {
     61   1.1  rillig 
     62   1.3  rillig 	if (name[0] == 'i' && name[1] == 's')
     63   1.3  rillig 		return strcmp(name, "isalnum") == 0 ||
     64   1.8  rillig 		    strcmp(name, "isalpha") == 0 ||
     65   1.8  rillig 		    strcmp(name, "isblank") == 0 ||
     66   1.8  rillig 		    strcmp(name, "iscntrl") == 0 ||
     67   1.8  rillig 		    strcmp(name, "isdigit") == 0 ||
     68   1.8  rillig 		    strcmp(name, "isgraph") == 0 ||
     69   1.8  rillig 		    strcmp(name, "islower") == 0 ||
     70   1.8  rillig 		    strcmp(name, "isprint") == 0 ||
     71   1.8  rillig 		    strcmp(name, "ispunct") == 0 ||
     72   1.8  rillig 		    strcmp(name, "isspace") == 0 ||
     73   1.8  rillig 		    strcmp(name, "isupper") == 0 ||
     74   1.8  rillig 		    strcmp(name, "isxdigit") == 0;
     75   1.3  rillig 
     76   1.3  rillig 	if (name[0] == 't' && name[1] == 'o')
     77   1.3  rillig 		return strcmp(name, "tolower") == 0 ||
     78   1.8  rillig 		    strcmp(name, "toupper") == 0;
     79   1.3  rillig 
     80   1.3  rillig 	return false;
     81   1.1  rillig }
     82   1.1  rillig 
     83   1.1  rillig static bool
     84   1.1  rillig is_ctype_table(const char *name)
     85   1.1  rillig {
     86   1.1  rillig 
     87   1.1  rillig 	/* NetBSD sys/ctype_bits.h 1.6 from 2016-01-22 */
     88   1.1  rillig 	if (strcmp(name, "_ctype_tab_") == 0 ||
     89   1.1  rillig 	    strcmp(name, "_tolower_tab_") == 0 ||
     90   1.1  rillig 	    strcmp(name, "_toupper_tab_") == 0)
     91   1.1  rillig 		return true;
     92   1.1  rillig 
     93   1.1  rillig 	/* NetBSD sys/ctype_bits.h 1.1 from 2010-06-01 */
     94   1.1  rillig 	return strcmp(name, "_ctype_") == 0;
     95   1.1  rillig }
     96   1.1  rillig 
     97   1.1  rillig static void
     98   1.1  rillig check_ctype_arg(const char *func, const tnode_t *arg)
     99   1.1  rillig {
    100   1.1  rillig 	const tnode_t *on, *cn;
    101   1.1  rillig 
    102  1.11  rillig 	for (on = arg; on->tn_op == CVT; on = on->u.ops.left)
    103   1.1  rillig 		if (on->tn_type->t_tspec == UCHAR)
    104   1.1  rillig 			return;
    105   1.1  rillig 	if (on->tn_type->t_tspec == UCHAR)
    106   1.1  rillig 		return;
    107   1.1  rillig 
    108   1.1  rillig 	if (arg->tn_op == CVT && arg->tn_cast) {
    109   1.9  rillig 		/* argument to '%s' must be cast to 'unsigned char', not ... */
    110   1.1  rillig 		warning(342, func, type_name(arg->tn_type));
    111   1.1  rillig 		return;
    112   1.1  rillig 	}
    113   1.1  rillig 
    114   1.1  rillig 	cn = before_conversion(arg);
    115  1.13  rillig 	if (cn->tn_type->t_tspec != UCHAR && cn->tn_type->t_tspec != INT) {
    116   1.1  rillig 		/* argument to '%s' must be 'unsigned char' or EOF, not '%s' */
    117   1.1  rillig 		warning(341, func, type_name(cn->tn_type));
    118   1.1  rillig 		return;
    119   1.1  rillig 	}
    120   1.1  rillig }
    121   1.1  rillig 
    122   1.1  rillig void
    123  1.10  rillig check_ctype_function_call(const function_call *call)
    124   1.1  rillig {
    125   1.1  rillig 
    126  1.12  rillig 	if (call->args_len == 1 &&
    127  1.10  rillig 	    call->func->tn_op == NAME &&
    128  1.11  rillig 	    is_ctype_function(call->func->u.sym->s_name))
    129  1.11  rillig 		check_ctype_arg(call->func->u.sym->s_name, call->args[0]);
    130   1.1  rillig }
    131   1.1  rillig 
    132   1.1  rillig void
    133   1.1  rillig check_ctype_macro_invocation(const tnode_t *ln, const tnode_t *rn)
    134   1.1  rillig {
    135   1.1  rillig 
    136   1.2  rillig 	if (ln->tn_op == PLUS &&
    137  1.11  rillig 	    ln->u.ops.left != NULL &&
    138  1.11  rillig 	    ln->u.ops.left->tn_op == LOAD &&
    139  1.11  rillig 	    ln->u.ops.left->u.ops.left != NULL &&
    140  1.11  rillig 	    ln->u.ops.left->u.ops.left->tn_op == NAME &&
    141  1.11  rillig 	    is_ctype_table(ln->u.ops.left->u.ops.left->u.sym->s_name))
    142   1.2  rillig 		check_ctype_arg("function from <ctype.h>", rn);
    143   1.1  rillig }
    144