1#!/usr/local/bin/python3.8
2#
3# This file is Copyright (c) 2010 by the GPSD project
4# SPDX-License-Identifier: BSD-2-clause
5#
6# With -p, dump a Python status mask list translated from the C one.
7#
8# With -c, generate C code to dump masks for debugging purposes.
9#
10# With -t, tabulate usage of defines to find unused ones.  Requires -c or -d.
11
12# This code runs compatibly under Python 2 and 3.x for x >= 2.
13# Preserve this property!
14from __future__ import absolute_import, print_function, division
15
16import getopt
17import glob
18import sys
19
20try:
21    from subprocess import getstatusoutput
22except ImportError:
23    from commands import getstatusoutput
24
25
26class SourceExtractor(object):
27    def __init__(self, sourcefile, clientside):
28        self.sourcefile = sourcefile
29        self.clientside = clientside
30        self.daemonfiles = [
31            "gpsd.c",
32            "libgpsd_core.c",
33            "pseudonmea.c",
34            "drivers.c",
35            "gpsmon.c",
36            "subframe.c"
37        ] + glob.glob("driver_*.c") + glob.glob("monitor_*.c")
38        self.masks = []
39        self.primitive_masks = []
40        for line in open(self.sourcefile):
41            if (((line.startswith("#define") and
42                 ("_SET" in line or "_IS" in line)))):
43                fields = line.split()
44                self.masks.append((fields[1], fields[2]))
45                if ((fields[2].startswith("(1llu<<") or
46                     fields[2].startswith("INTERNAL_SET"))):
47                    self.primitive_masks.append((fields[1], fields[2]))
48
49    def in_library(self, flag):
50        (status, _output) = getstatusoutput(
51            "grep '%s' libgps_core.c libgps_json.c gpsctl.c" % flag)
52        return status == 0
53
54    def in_daemon(self, flag):
55        (status, _output) = getstatusoutput(
56            "grep '%s' %s" % (flag, " ".join(self.daemonfiles)))
57        return status == 0
58
59    def relevant(self, flag):
60        if self.clientside:
61            return self.in_library(flag)
62
63        return self.in_daemon(flag)
64
65
66if __name__ == '__main__':
67    try:
68        (options, arguments) = getopt.getopt(sys.argv[1:], "cdpt")
69        pythonize = tabulate = False
70        clientgen = daemongen = False
71        for (switch, val) in options:
72            if switch == '-p':
73                pythonize = True
74            if switch == '-c':
75                clientgen = True
76            if switch == '-d':
77                daemongen = True
78            if switch == '-t':
79                tabulate = True
80
81        if not arguments:
82            srcdir = '.'
83        else:
84            srcdir = arguments[0]
85
86        clientside = SourceExtractor(srcdir + "/gps.h", clientside=True)
87        daemonside = SourceExtractor(srcdir + "/gpsd.h", clientside=False)
88        if clientgen:
89            source = clientside
90            banner = "Library"
91        elif daemongen:
92            source = daemonside
93            banner = "Daemon"
94        else:
95            sys.stderr.write("maskaudit: need -c or -d option\n")
96            sys.exit(1)
97
98        if tabulate:
99            print("%-14s	%8s" % (" ", banner))
100            for (flag, value) in source.masks:
101                print("%-14s	%8s" % (flag, source.relevant(flag)))
102        if pythonize:
103            for (d, v) in source.masks:
104                if v[-1] == 'u':
105                    v = v[:-1]
106                print("%-15s\t= %s" % (d, v))
107        if not pythonize and not tabulate:
108            maxout = 0
109            for (d, v) in source.primitive_masks:
110                if source.relevant(d):
111                    stem = d
112                    if stem.endswith("_SET"):
113                        stem = stem[:-4]
114                    if stem.endswith("_IS"):
115                        stem = stem[:-3]
116                    maxout += len(stem) + 1
117            print("""/* This code is generated.  Do not hand-hack it! */
118
119/*
120 * Also, beware that it is something of a CPU hog when called on every packet.
121 * Try to write guards so it is only called at higher log levels.
122 */
123
124#include \"gpsd_config.h\"  /* must be before all includes */
125
126#include <stdio.h>
127#include <string.h>
128
129#include \"gpsd.h\"
130
131const char *gps_maskdump(gps_mask_t set)
132{
133    static char buf[%d];
134    const struct {
135        gps_mask_t      mask;
136        const char      *name;
137    } *sp, names[] = {""" % (maxout + 3,))
138            masks = clientside.primitive_masks + daemonside.primitive_masks
139            for (flag, value) in masks:
140                stem = flag
141                if stem.endswith("_SET"):
142                    stem = stem[:-4]
143                if stem.endswith("_IS"):
144                    stem = stem[:-3]
145                print("        {%s,\t\"%s\"}," % (flag, stem))
146            print('''\
147    };
148
149    memset(buf, '\\0', sizeof(buf));
150    buf[0] = '{';
151    for (sp = names; sp < names + sizeof(names)/sizeof(names[0]); sp++)
152        if ((set & sp->mask)!=0) {
153            (void)strlcat(buf, sp->name, sizeof(buf));
154            (void)strlcat(buf, "|", sizeof(buf));
155        }
156    if (buf[1] != \'\\0\')
157        buf[strlen(buf)-1] = \'\\0\';
158    (void)strlcat(buf, "}", sizeof(buf));
159    return buf;
160}
161''')
162    except KeyboardInterrupt:
163        pass
164
165# The following sets edit modes for GNU EMACS
166# Local Variables:
167# mode:python
168# End:
169