ckctype.c revision 1.7
1169695Skan/* $NetBSD: ckctype.c,v 1.7 2023/09/13 20:31:58 rillig Exp $ */
2169695Skan
3169695Skan/*-
4169695Skan * Copyright (c) 2021 The NetBSD Foundation, Inc.
5169695Skan * All rights reserved.
6169695Skan *
7169695Skan * This code is derived from software contributed to The NetBSD Foundation
8169695Skan * by Roland Illig <rillig@NetBSD.org>.
9169695Skan *
10169695Skan * Redistribution and use in source and binary forms, with or without
11169695Skan * modification, are permitted provided that the following conditions
12169695Skan * are met:
13169695Skan * 1. Redistributions of source code must retain the above copyright
14169695Skan *    notice, this list of conditions and the following disclaimer.
15169695Skan * 2. Redistributions in binary form must reproduce the above copyright
16169695Skan *    notice, this list of conditions and the following disclaimer in the
17169695Skan *    documentation and/or other materials provided with the distribution.
18169695Skan *
19169695Skan * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20169695Skan * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21169695Skan * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22169695Skan * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23169695Skan * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24169695Skan * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25169695Skan * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26169695Skan * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27169695Skan * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28169695Skan * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29169695Skan * POSSIBILITY OF SUCH DAMAGE.
30169695Skan */
31169695Skan
32169695Skan#if HAVE_NBTOOL_CONFIG_H
33169695Skan#include "nbtool_config.h"
34169695Skan#endif
35169695Skan
36169695Skan#include <sys/cdefs.h>
37169695Skan
38169695Skan#if defined(__RCSID)
39169695Skan__RCSID("$NetBSD: ckctype.c,v 1.7 2023/09/13 20:31:58 rillig Exp $");
40169695Skan#endif
41169695Skan
42169695Skan#include <string.h>
43169695Skan
44169695Skan#include "lint1.h"
45169695Skan
46169695Skan/*
47169695Skan * Check that the functions from <ctype.h> are used properly.  They must not
48169695Skan * be called with an argument of type 'char'.  In such a case, the argument
49169695Skan * must be converted to 'unsigned char'.  The tricky thing is that even though
50169695Skan * the parameter type is declared as 'int', a 'char' argument must not be
51169695Skan * directly cast to 'int', as that would preserve negative argument values.
52169695Skan *
53169695Skan * See also:
54169695Skan *	ctype(3)
55169695Skan *	https://stackoverflow.com/a/60696378
56169695Skan */
57169695Skan
58169695Skanstatic bool
59169695Skanis_ctype_function(const char *name)
60169695Skan{
61169695Skan
62169695Skan	if (name[0] == 'i' && name[1] == 's')
63169695Skan		return strcmp(name, "isalnum") == 0 ||
64169695Skan		       strcmp(name, "isalpha") == 0 ||
65169695Skan		       strcmp(name, "isblank") == 0 ||
66169695Skan		       strcmp(name, "iscntrl") == 0 ||
67169695Skan		       strcmp(name, "isdigit") == 0 ||
68169695Skan		       strcmp(name, "isgraph") == 0 ||
69169695Skan		       strcmp(name, "islower") == 0 ||
70169695Skan		       strcmp(name, "isprint") == 0 ||
71169695Skan		       strcmp(name, "ispunct") == 0 ||
72169695Skan		       strcmp(name, "isspace") == 0 ||
73169695Skan		       strcmp(name, "isupper") == 0 ||
74169695Skan		       strcmp(name, "isxdigit") == 0;
75169695Skan
76169695Skan	if (name[0] == 't' && name[1] == 'o')
77169695Skan		return strcmp(name, "tolower") == 0 ||
78169695Skan		       strcmp(name, "toupper") == 0;
79169695Skan
80169695Skan	return false;
81169695Skan}
82169695Skan
83169695Skanstatic bool
84169695Skanis_ctype_table(const char *name)
85169695Skan{
86169695Skan
87169695Skan	/* NetBSD sys/ctype_bits.h 1.6 from 2016-01-22 */
88169695Skan	if (strcmp(name, "_ctype_tab_") == 0 ||
89169695Skan	    strcmp(name, "_tolower_tab_") == 0 ||
90169695Skan	    strcmp(name, "_toupper_tab_") == 0)
91169695Skan		return true;
92169695Skan
93169695Skan	/* NetBSD sys/ctype_bits.h 1.1 from 2010-06-01 */
94169695Skan	return strcmp(name, "_ctype_") == 0;
95169695Skan}
96169695Skan
97169695Skanstatic void
98169695Skancheck_ctype_arg(const char *func, const tnode_t *arg)
99169695Skan{
100169695Skan	const tnode_t *on, *cn;
101169695Skan
102169695Skan	for (on = arg; on->tn_op == CVT; on = on->tn_left)
103169695Skan		if (on->tn_type->t_tspec == UCHAR)
104169695Skan			return;
105169695Skan	if (on->tn_type->t_tspec == UCHAR)
106169695Skan		return;
107169695Skan
108169695Skan	if (arg->tn_op == CVT && arg->tn_cast) {
109169695Skan		/* argument to '%s' must be cast to 'unsigned char', not to '%s' */
110169695Skan		warning(342, func, type_name(arg->tn_type));
111169695Skan		return;
112169695Skan	}
113169695Skan
114169695Skan	cn = before_conversion(arg);
115169695Skan	if (cn->tn_type->t_tspec == CHAR) {
116169695Skan		/* argument to '%s' must be 'unsigned char' or EOF, not '%s' */
117169695Skan		warning(341, func, type_name(cn->tn_type));
118169695Skan		return;
119169695Skan	}
120169695Skan}
121169695Skan
122169695Skanvoid
123169695Skancheck_ctype_function_call(const tnode_t *func, const tnode_t *args)
124169695Skan{
125169695Skan
126169695Skan	if (func->tn_op == NAME &&
127169695Skan	    is_ctype_function(func->tn_sym->s_name) &&
128169695Skan	    args != NULL &&
129169695Skan	    tn_ck_left(args) != NULL &&
130169695Skan	    args->tn_right == NULL)
131169695Skan		check_ctype_arg(func->tn_sym->s_name, args->tn_left);
132169695Skan}
133169695Skan
134169695Skanvoid
135169695Skancheck_ctype_macro_invocation(const tnode_t *ln, const tnode_t *rn)
136169695Skan{
137169695Skan
138169695Skan	if (ln->tn_op == PLUS &&
139169695Skan	    ln->tn_left != NULL &&
140169695Skan	    ln->tn_left->tn_op == LOAD &&
141169695Skan	    ln->tn_left->tn_left != NULL &&
142169695Skan	    ln->tn_left->tn_left->tn_op == NAME &&
143169695Skan	    is_ctype_table(ln->tn_left->tn_left->tn_sym->s_name))
144169695Skan		check_ctype_arg("function from <ctype.h>", rn);
145169695Skan}
146169695Skan