xref: /netbsd-src/external/bsd/openldap/dist/doc/devel/variadic_debug/09-merge.cocci (revision e670fd5c413e99c2f6a37901bb21c537fcd322d2)
1// Note that this file has not actually been used in the end, since
2// 07-shortcut.cocci covers everything we needed in the project, but being
3// simpler, it makes the intent of 07-shortcut.cocci clearer
4
5
6// Splice string `s` into the format string `fmtstring` replacing the
7// %-parameter at position `pos`
8@initialize:python@
9@@
10
11#regex from https://stackoverflow.com/questions/30011379/how-can-i-parse-a-c-format-string-in-python
12import re
13fmtstring = '''\
14(                                  # start of capture group 1
15%                                  # literal "%"
16(?:                                # first option
17(?:[-+0 #]{0,5})                   # optional flags
18(?:\d+|\*)?                        # width
19(?:\.(?:\d+|\*))?                  # precision
20(?:h|l|ll|w|I|I32|I64)?            # size
21[cCdiouxXeEfgGaAnpsSZ]             # type
22) |                                # OR
23%%)                                # literal "%%"
24'''
25
26regex = re.compile(fmtstring, re.X)
27
28def parse_format(f):
29    return tuple((m.span(), m.group()) for m in
30        regex.finditer(f))
31
32def insert_at_pos(fmt, s, pos):
33    formats = parse_format(fmt)
34    span, format = formats[pos]
35    acc = fmt[:span[0]]
36    if s.startswith('"'):
37        acc += s[1:]
38    else:
39        acc += '" '
40        acc += s
41    if acc.endswith('"'):
42        acc = acc[:-1] + fmt[span[1]:]
43    else:
44        acc += ' "'
45        acc += fmt[span[1]:]
46    return acc
47
48// Identify the redundant snprintfs (within a locked region)
49@a exists@
50expression lock, E, L;
51expression list args_before, args, args_after;
52identifier buf;
53expression format1, format2;
54type T;
55position p1, p2;
56@@
57
58{
59...
60T buf;
61...
62ldap_pvt_thread_mutex_lock(lock);
63...
64snprintf@p1( buf, E, format1, args );
65...
66ldap_pvt_thread_mutex_unlock(lock);
67...
68Debug@p2( L, format2, args_before, buf, args_after );
69...
70}
71
72// Merge the format strings with insert_at_pos above
73@script:python a_process@
74format1 << a.format1;
75format2 << a.format2;
76args_before << a.args_before;
77merged;
78@@
79
80pos = len(args_before.elements)
81coccinelle.merged = insert_at_pos(format2, format1, pos)
82
83// And merge the two together, replacing the extra buffer that's not used anymore
84@a_replace@
85position a.p1, a.p2;
86identifier a_process.merged;
87
88expression lock, E, L;
89expression list args_before, args, args_after;
90identifier buf;
91expression format1, format2;
92type T;
93@@
94
95{
96...
97-T buf;
98...
99ldap_pvt_thread_mutex_lock(lock);
100...
101-snprintf@p1( buf, E, format1, args );
102+Debug( L, merged, args_before, args, args_after );
103...
104ldap_pvt_thread_mutex_unlock(lock);
105...
106-Debug@p2( L, format2, args_before, buf, args_after );
107...
108}
109
110// Once again (same as the 'a' series above, but those that remain to be sorted
111// now don't need to stay within a locked region
112@b exists@
113expression E, L;
114expression list args_before, args, args_after;
115identifier buf;
116expression format1, format2;
117position p1, p2;
118@@
119
120snprintf@p1( buf, E, format1, args );
121...
122Debug@p2( L, format2, args_before, buf, args_after );
123
124@script:python b_process@
125format1 << b.format1;
126format2 << b.format2;
127args_before << b.args_before;
128merged;
129@@
130
131pos = len(args_before.elements)
132coccinelle.merged = insert_at_pos(format2, format1, pos)
133
134@b_replace@
135position b.p1, b.p2;
136identifier b_process.merged;
137
138expression E, L;
139expression list args_before, args, args_after;
140identifier buf;
141expression format1, format2;
142@@
143
144-snprintf@p1( buf, E, format1, args );
145+Debug( L, merged, args_before, args, args_after );
146...
147-Debug@p2( L, format2, args_before, buf, args_after );
148