├── Makefile
├── README.md
├── binaries
├── linux
│ ├── qcndiff32
│ └── qcndiff64
└── windows
│ ├── qcndiff32.exe
│ └── qcndiff64.exe
├── main.cpp
├── nv.txt
├── qcn.cpp
├── qcn.hpp
└── testfiles
├── 906k.txt
├── 906s.txt
├── G900F-bkp.txt
├── empty.txt
├── tst1.txt
└── tst2.txt
/Makefile:
--------------------------------------------------------------------------------
1 | ifeq ($(OS),Windows_NT)
2 | RMOPTS=2> nul
3 | RM=@del
4 | INCLUDE=-ID:\boost_1_57_0
5 | LIBSUFFIX=-mgw49-mt-1_57
6 | LDOPTS=-static
7 | EXT=.exe
8 | ifeq ($(PROCESSOR_ARCHITECTURE),AMD64)
9 | ARCH=64
10 | endif
11 | ifeq ($(PROCESSOR_ARCHITECTURE),x86)
12 | ARCH=32
13 | endif
14 | ifeq ($(ARCH),64)
15 | LIBPATH=-LD:\boost_1_57_0\x64\lib
16 | endif
17 | ifeq ($(ARCH),32)
18 | LIBPATH=-LD:\boost_1_57_0\x86\lib
19 | endif
20 | else
21 | RMOPTS=2> /dev/null || true
22 | RM=@rm -f
23 | LIBSUFFIX=
24 | EXT=
25 | UNAME_S := $(shell uname -s)
26 | ifeq ($(UNAME_S),Linux)
27 | UNAME_P := $(shell uname -p)
28 | ifeq ($(UNAME_P),x86_64)
29 | ARCH=64
30 | endif
31 | ifneq ($(filter %86,$(UNAME_P)),)
32 | ARCH=32
33 | endif
34 | endif
35 | endif
36 |
37 | STANDARD=-std=c++11
38 | CPPFLAGS=-m$(ARCH) $(STANDARD) $(INCLUDE) -c
39 | LDFLAGS=-m$(ARCH) $(LIBPATH) $(LDOPTS)
40 | LIBS=-lboost_program_options$(LIBSUFFIX) -lboost_filesystem$(LIBSUFFIX) -lboost_system$(LIBSUFFIX)
41 | EXENAME=qcndiff
42 | CLEANFILES=*.o $(EXENAME)$(ARCH)$(EXE)
43 |
44 | $(EXENAME)$(ARCH): qcn$(ARCH).o main$(ARCH).o
45 | g++ $(LDFLAGS) -o $(EXENAME)$(ARCH) qcn$(ARCH).o main$(ARCH).o $(LIBS)
46 | # strip $(EXENAME)$(ARCH)$(EXT)
47 |
48 | qcn$(ARCH).o: qcn.cpp qcn.hpp
49 | g++ $(CPPFLAGS) -o qcn$(ARCH).o qcn.cpp
50 |
51 | main$(ARCH).o: qcn.cpp qcn.hpp main.cpp
52 | g++ $(CPPFLAGS) -o main$(ARCH).o main.cpp
53 |
54 | clean:
55 | $(RM) $(CLEANFILES) $(RMOPTS)
56 |
57 | target:
58 | echo Processor architecture is $(PROCESSOR_ARCHITECTURE)
59 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
QCNDIFF
2 |
3 | This is a utility that parses and displays differences between two text format .qcn files produced by Qualcomm's QPST (Qualcomm Product Support Tools). Examples for Linux usage and compilation are given for a RHEL / Centos / Fedora distribution. Adapt as necessary to your own distribution.
4 |
5 | HOW TO USE
6 |
7 | There are pre-built binaries for 64 and 32 bit Windows as well as pre-built binaries for 32 and 64 bit Linux. The Linux binaries were built on Fedora 20 and the Windows binaries were built with MinGW and statically linked. For Linux you will need to install the boost shared libraries
8 | ````
9 | yum install boost
10 | ````
11 |
12 | Program usage is as follows
13 |
14 | ````
15 | Usage: qcndiff64 [options] file file
16 |
17 | -h [ --help ] show help message
18 |
19 | -t [ --type ] arg (=p) show differences
20 | p for items present in both files
21 | m for items missing in either file
22 | b for both present and missing items
23 |
24 | -f [ --format ] arg (=i) output format
25 | i for interleaved output
26 | s for sequential output
27 | c to suppress item data and print only
28 | count
29 |
30 | -l [ --lookup ] arg (=nv.txt) nv item descriptions
31 | ````
32 |
33 | Interleaved output shows the nvitem that is different for both files before displaying the next one. Sequential output displays all the differing items in the first file before proceeding to display the second file.
34 |
35 | If the file nv.txt exists (use -l to override the name) it will be used to look up text descriptions of the codes in order to render the output more friendly.
36 |
37 | HOW TO COMPILE
38 |
39 | Linux
40 |
41 | ````
42 | yum install boost boost-devel
43 | make
44 | ````
45 |
46 | If you wish to compile for a different architecture, for example you run x86_64 and you wish to compile a 32 bit variant, then first make sure you have the 32 bit libraries and then override the target with the ARCH commandline option
47 |
48 | ````
49 | yum install boost.i686 boost-devel.i686
50 | make ARCH=32
51 | ````
52 |
53 | Windows
54 |
55 | You will need to install MinGW. You need separate toolchains for the 64 bit versions and the 32 bit versions. You will also need to compile the boost libraries.
56 |
57 | Once Boost is compiled, edit the Makefile to change the paths to the libraries and include files to conform to where you have them installed. Take note that if the default --layout==versioning option is used to compile Boost then you may also need to change the suffix option in the Makefile
58 |
59 | CHANGELOG
60 |
61 | * 0.2 - add dictionary facility to allow the lookup of text descriptions
62 | * 0.1 - initial release
63 |
64 | DOWNLOAD LINKS
65 |
66 | MinGW-32 binary distribution: http://sourceforge.net/projects/mingw-w64/files/Toolchains%20targetting%20Win32/Personal%20Builds/mingw-builds/4.9.2/threads-posix/dwarf/i686-4.9.2-release-posix-dwarf-rt_v3-rev0.7z/download
67 |
68 | MinGW-64 binary distribution: http://sourceforge.net/projects/mingw-w64/files/Toolchains%20targetting%20Win64/Personal%20Builds/mingw-builds/4.9.2/threads-posix/seh/x86_64-4.9.2-release-posix-seh-rt_v3-rev0.7z/download
69 |
70 | Boost 1.57 source: http://sourceforge.net/projects/boost/files/boost/1.57.0/
71 |
72 | gmake for Windows: http://sourceforge.net/projects/mingw-w64/files/Toolchains%20targetting%20Win32/Personal%20Builds/mingw-builds/4.9.2/threads-posix/dwarf/i686-4.9.2-release-posix-dwarf-rt_v3-rev0.7z/download
73 |
--------------------------------------------------------------------------------
/binaries/linux/qcndiff32:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dl12345/qcn/d0119024f010d8e1923099d7087afa763f9db61e/binaries/linux/qcndiff32
--------------------------------------------------------------------------------
/binaries/linux/qcndiff64:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dl12345/qcn/d0119024f010d8e1923099d7087afa763f9db61e/binaries/linux/qcndiff64
--------------------------------------------------------------------------------
/binaries/windows/qcndiff32.exe:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dl12345/qcn/d0119024f010d8e1923099d7087afa763f9db61e/binaries/windows/qcndiff32.exe
--------------------------------------------------------------------------------
/binaries/windows/qcndiff64.exe:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dl12345/qcn/d0119024f010d8e1923099d7087afa763f9db61e/binaries/windows/qcndiff64.exe
--------------------------------------------------------------------------------
/main.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | This program is free software: you can redistribute it and/or modify
3 | it under the terms of the GNU General Public License as published by
4 | the Free Software Foundation, either version 3 of the License, or
5 | (at your option) any later version.
6 |
7 | This program is distributed in the hope that it will be useful,
8 | but WITHOUT ANY WARRANTY; without even the implied warranty of
9 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 | GNU General Public License for more details.
11 |
12 | You should have received a copy of the GNU General Public License
13 | along with this program. If not, see .
14 |
15 | Copyright 2014 dl12345@xda-developers forum
16 |
17 | */
18 |
19 | #include
20 | #include
21 | #include
22 | #include
23 | #include
24 | #include
25 | #include
26 | #include "qcn.hpp"
27 |
28 | using qcn::Qcn;
29 | namespace fs = boost::filesystem;
30 | typedef enum {interleave, sequential, count} printformat;
31 |
32 | bool const ProcessCommandLine(
33 | int ac,
34 | char *av[],
35 | Qcn::cmp& cmp,
36 | printformat& format,
37 | std::string& fileone,
38 | std::string& filetwo,
39 | std::string& filedict
40 | )
41 | {
42 | namespace po = boost::program_options;
43 | typedef std::vector files_type;
44 |
45 | char t, pf;
46 | files_type f;
47 |
48 | std::string prog(fs::path(av[0]).filename().string());
49 | std::string usage = "Usage: " + prog + " [options] file file";
50 |
51 | po::options_description visible;
52 | visible.add_options()
53 | ("help,h", "show help message\n")
54 | ("type,t", po::value(&t)->default_value('p'),
55 | "show differences\n"
56 | " p for items present in both files\n"
57 | " m for items missing in either file\n"
58 | " b for both present and missing items\n")
59 | ("format,f", po::value(&pf)->default_value('i'),
60 | "output format\n"
61 | " i for interleaved output\n"
62 | " s for sequential output\n"
63 | " c to suppress item data and print only count\n")
64 | ("lookup,l", po::value(&filedict)->default_value("nv.txt"),
65 | "nv item descriptions")
66 | ;
67 |
68 | po::options_description hidden("hidden options");
69 | hidden.add_options()("input,i", po::value(&f), "input file");
70 |
71 | po::positional_options_description p;
72 | p.add("input", -1);
73 |
74 | po::options_description all;
75 | all.add(visible).add(hidden);
76 |
77 | po::variables_map vm;
78 | po::store(
79 | po::command_line_parser(ac, av).options(all).positional(p).run(), vm
80 | );
81 | po::notify(vm);
82 |
83 | if (vm.count("help"))
84 | {
85 | std::cout << std::endl << usage << std::endl << std::endl;
86 | std::cout << visible;
87 | return false;
88 | }
89 |
90 | // process and set input files
91 |
92 | if (vm.count("input") && f.size() == 2)
93 | {
94 | fileone = f[0];
95 | filetwo = f[1];
96 |
97 | if (!fs::exists(fileone))
98 | {
99 | std::cout << prog << ": " << fileone << " not found" << std::endl;
100 | return false;
101 | }
102 |
103 | if (!fs::exists(filetwo))
104 | {
105 | std::cout << prog << ": " << filetwo << " not found" << std::endl;
106 | return false;
107 | }
108 | }
109 | else
110 | {
111 | std::cout << std::endl << usage << std::endl << std::endl;
112 | std::cout << visible;
113 | return false;
114 | }
115 |
116 | // process and set comparison type
117 |
118 | if (vm.count("type"))
119 | {
120 | switch(t)
121 | {
122 | case 'p':
123 | cmp = Qcn::cmp::present;
124 | break;
125 | case 'm':
126 | cmp = Qcn::cmp::missing;
127 | break;
128 | case 'b':
129 | cmp = Qcn::cmp::both;
130 | break;
131 | default:
132 | std::cout << std::endl << usage << std::endl << std::endl;
133 | std::cout << visible;
134 | return false;
135 | }
136 | }
137 |
138 | // process print output format
139 |
140 | if (vm.count("format"))
141 | {
142 | switch(pf)
143 | {
144 | case 'i':
145 | format = interleave;
146 | break;
147 | case 's':
148 | format = sequential;
149 | break;
150 | case 'c':
151 | format = count;
152 | break;
153 | default:
154 | std::cout << std::endl << usage << std::endl << std::endl;
155 | std::cout << visible;
156 | return false;
157 | }
158 | }
159 |
160 | return true;
161 | }
162 |
163 | void PrintOutput(
164 | qcn::diff_type const& d,
165 | std::string const& fileone,
166 | std::string const& filetwo,
167 | std::string const& filedict,
168 | printformat p = interleave
169 | )
170 | {
171 | std::cout << std::endl;
172 | std::cout << "Found " << d.size() << " non matching items";
173 | std::cout << std::endl << std::endl;
174 |
175 | qcn::Dictionary dict(filedict);
176 | bool printinfo = false;
177 | if (dict.Open()) printinfo = true;
178 |
179 | switch(p)
180 | {
181 | case interleave:
182 |
183 | for (auto i = d.begin(); i != d.end(); ++i)
184 | {
185 | auto p = *i;
186 |
187 | if (printinfo)
188 | {
189 | auto j = dict.Find(p.first.code);
190 |
191 | if (j != dict.end())
192 | {
193 | auto q = *j;
194 |
195 | p.first.description = q.description;
196 | p.second.description = q.description;
197 |
198 | p.first.category = q.category;
199 | p.second.category = q.category;
200 | }
201 | }
202 |
203 | std::cout << '[' << fileone << "]: ";
204 | std::cout << p.first << std::endl;
205 |
206 | std::cout << '[' << filetwo << "]: ";
207 | std::cout << p.second << std::endl;
208 | }
209 | break;
210 |
211 | case sequential:
212 |
213 | std::cout << '[' << fileone << "]: " << std::endl << std::endl;
214 | for (auto i = d.begin(); i != d.end(); ++i)
215 | {
216 | auto p = *i;
217 | std::cout << p.first << std::endl;
218 | }
219 |
220 | std::cout << '[' << filetwo << "]: " << std::endl << std::endl;
221 | for (auto i = d.begin(); i != d.end(); ++i)
222 | {
223 | auto p = *i;
224 | std::cout << p.second << std::endl;
225 | }
226 | break;
227 |
228 | default:
229 | break;
230 | }
231 | }
232 |
233 | int main(int argc, char *argv[])
234 | {
235 | Qcn::cmp cmp;
236 | std::string nameone, nametwo, nameinfo;
237 | printformat pf;
238 |
239 | if (ProcessCommandLine(argc, argv, cmp, pf, nameone, nametwo, nameinfo))
240 | {
241 | qcn::Qcn fileone(nameone), filetwo(nametwo);
242 | auto n1 = fs::path(nameone).filename().string();
243 | auto n2 = fs::path(nametwo).filename().string();
244 |
245 | if (fileone.Open() && filetwo.Open())
246 | {
247 |
248 | auto d = qcn::Compare(fileone, filetwo, cmp);
249 | PrintOutput(d, n1, n2, nameinfo, pf);
250 | }
251 | else
252 | {
253 | if (!fileone.IsOpen())
254 | {
255 | std::cout << n1 << ": ";
256 | std::cout << fileone.ErrorMessage() << std::endl;
257 | }
258 | if (!filetwo.IsOpen())
259 | {
260 | std::cout << n2 << ": ";
261 | std::cout << filetwo.ErrorMessage() << std::endl;
262 | }
263 | return 1;
264 | }
265 | }
266 | return 0;
267 | }
268 |
269 |
--------------------------------------------------------------------------------
/qcn.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | This program is free software: you can redistribute it and/or modify
3 | it under the terms of the GNU General Public License as published by
4 | the Free Software Foundation, either version 3 of the License, or
5 | (at your option) any later version.
6 |
7 | This program is distributed in the hope that it will be useful,
8 | but WITHOUT ANY WARRANTY; without even the implied warranty of
9 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 | GNU General Public License for more details.
11 |
12 | You should have received a copy of the GNU General Public License
13 | along with this program. If not, see .
14 |
15 | Copyright 2014 dl12345@xda-developers forum
16 |
17 | */
18 |
19 | #ifdef DEBUG
20 | #define BOOST_SPIRIT_DEBUG
21 | #endif
22 |
23 | #include
24 | #include "qcn.hpp"
25 |
26 | namespace qcn
27 | {
28 | diff_type const Compare(
29 | Qcn const& lhs,
30 | Qcn const& rhs,
31 | Qcn::cmp const cmp,
32 | bool const recurse
33 | )
34 | {
35 | // option ::present will perform an inner join of non-matching items
36 | // option ::missing will perform an outer join of missing items
37 | // option ::both will perform an outer join of all non-matching items
38 |
39 | diff_type d;
40 | for (auto l = lhs.begin(); l != lhs.end(); ++l)
41 | {
42 | auto r = rhs.Find(l->code);
43 | if (r != rhs.end())
44 | {
45 | if (cmp != Qcn::cmp::missing && *l != *r) // inner join
46 | {
47 | pair_type p(*l, *r);
48 | d.push_back(p);
49 | }
50 | }
51 | else if (cmp != Qcn::cmp::present) // left outer join missing items
52 | {
53 | Qcn::item empty;
54 | pair_type p(*l, empty);
55 | d.push_back(p);
56 | }
57 | }
58 | if (cmp != Qcn::cmp::present && recurse) // right outer join missing items
59 | {
60 | auto rhs_only = Compare(rhs, lhs, Qcn::cmp::missing, false);
61 | for (auto s = rhs_only.begin(); s != rhs_only.end(); ++s)
62 | {
63 | // swap the order so that we have rhs and lhs correctly ordered
64 |
65 | Qcn::item empty;
66 | pair_type p(empty, s->first);
67 | d.push_back(p);
68 | }
69 | }
70 | return d;
71 | }
72 |
73 | }
74 |
75 |
76 |
77 |
78 |
--------------------------------------------------------------------------------
/qcn.hpp:
--------------------------------------------------------------------------------
1 | /*
2 | This program is free software: you can redistribute it and/or modify
3 | it under the terms of the GNU General Public License as published by
4 | the Free Software Foundation, either version 3 of the License, or
5 | (at your option) any later version.
6 |
7 | This program is distributed in the hope that it will be useful,
8 | but WITHOUT ANY WARRANTY; without even the implied warranty of
9 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 | GNU General Public License for more details.
11 |
12 | You should have received a copy of the GNU General Public License
13 | along with this program. If not, see .
14 |
15 | Copyright 2014 dl12345@xda-developers forum
16 |
17 | */
18 |
19 | #ifndef QCN_H
20 | #define QCN_H
21 |
22 | #include
23 | #include
24 | #include
25 | #include
26 | #include
27 | #include
28 | #include
29 | #include
30 | #include
31 | #include
32 |
33 |
34 | namespace qcn
35 | {
36 | namespace spirit = boost::spirit;
37 | namespace qi = boost::spirit::qi;
38 | namespace ascii = boost::spirit::ascii;
39 | namespace fusion = boost::fusion;
40 | namespace range = boost::range_detail;
41 | namespace phx = boost::phoenix;
42 |
43 | class Dictionary;
44 | class Qcn;
45 |
46 | struct qitem;
47 | struct dict_code;
48 |
49 | typedef unsigned int uint;
50 |
51 | // Skipper type for parser
52 | typedef ascii::space_type space_type;
53 |
54 | // item grammar types
55 | typedef struct qitem qcn_item_type;
56 | typedef std::vector qcn_items_type;
57 | typedef std::vector< uint > qcn_item_data_type;
58 | typedef boost::unordered_map qcn_map_type;
59 |
60 | // dictionary grammar types
61 | typedef struct dict_code dict_code_type;
62 | typedef std::vector dict_codes_type;
63 | typedef boost::unordered_map dict_map_type;
64 |
65 | // comparison type pairs
66 | typedef std::pair pair_type;
67 | typedef std::vector diff_type;
68 |
69 |
70 | struct qitem
71 | {
72 | uint code;
73 | std::string description;
74 | std::string category;
75 | std::string status;
76 | qcn_item_data_type data;
77 |
78 | qitem(): code(0) {}
79 |
80 | qitem(struct qitem const& c)
81 | : code(c.code), status(c.status), data(c.data) {}
82 |
83 | bool operator==(struct qitem const& rhs) const
84 | {
85 | if (code == 0 && status.empty()) // the empty object doesn't match
86 | {
87 | return false;
88 | }
89 | if (code == rhs.code && status == rhs.status && data == rhs.data)
90 | {
91 | return true;
92 | }
93 | return false;
94 | }
95 |
96 | bool operator!=(struct qitem const& rhs) const
97 | {
98 | if (code == 0 && status.empty())
99 | {
100 | return true;
101 | }
102 | if (code != rhs.code || status != rhs.status || data != rhs.data)
103 | {
104 | return true;
105 | }
106 | return false;
107 | }
108 |
109 | friend std::ostream& operator<<(std::ostream& o, struct qitem const& q)
110 | {
111 | o << std::setw(4) << std::setfill('0');
112 | o << std::dec << q.code;
113 |
114 | if (!q.description.empty())
115 | {
116 | o << " (" << q.description << ", " << q.category;
117 | }
118 | else
119 | {
120 | o << " (0x";
121 | o << std::setw(4) << std::setfill('0');
122 | o << std::uppercase << std::hex << q.code;
123 | }
124 | o << ") - " << (q.status == "" ? "Missing" : q.status) << std::endl;
125 |
126 | for (auto i = 0; i < q.data.size(); i++)
127 | {
128 | if (i && i % 16 == 0)
129 | {
130 | std::cout << std::endl;
131 | }
132 | else if (i) o << " ";
133 |
134 | o << std::setw(2) << std::setfill('0');
135 | o << std::uppercase << std::hex << q.data[i];
136 | }
137 | if(q.data.size() > 0) o << std::endl;
138 | return o;
139 | }
140 |
141 | };
142 |
143 | template < typename Iterator, typename Skipper = space_type >
144 | class qcnparser : public qi::grammar< Iterator, qcn_items_type(), Skipper >
145 | {
146 | public:
147 |
148 | // qcn grammar types
149 | typedef uint code_type;
150 | typedef std::vector< uint > leaf_type;
151 | typedef fusion::vector2< std::string, uint > size_type;
152 | typedef fusion::vector2< size_type, size_type > info_type;
153 |
154 | qcnparser() : qcnparser::base_type(qcndata), size_(0)
155 | {
156 |
157 | using qi::uint_;
158 | using qi::ulong_;
159 | using qi::hex;
160 | using qi::char_;
161 | using qi::string;
162 | using qi::lit;
163 | using qi::eol;
164 | using qi::uint_parser;
165 | using qi::repeat;
166 | using qi::_val;
167 | using qi::omit;
168 | using qi::_1;
169 | using qi::attr;
170 | using qi::eps;
171 | using phx::ref;
172 |
173 | qcndata = omit[+(header)] >> *(item);
174 |
175 | header = '[' >> description >> ']';
176 |
177 | description = nvitemslabel | iteminfo;
178 |
179 | nvitemslabel = lit("NV items") ;
180 |
181 | iteminfo = itemnumber >> ',' >> itemsize;
182 |
183 | itemnumber = string("Complete items") >> '-' >> uint_;
184 |
185 | // In the presence of a semantic action the % operator is
186 | // necessary to ensure the propagation of the attribute if
187 | // _val is not assigned explicitly
188 |
189 | itemsize %= (string("Items size") >> '-' >> uint_[ref(size_)=_1]);
190 |
191 | item = itempresent | itemnotpresent;
192 |
193 | itempresent = itemcode >> statusok >> itemdata;
194 |
195 | itemnotpresent = itemcode
196 | >> statusother
197 | >> attr(qcn_item_data_type());
198 |
199 | itemcode = ulong_ >> omit [ itemdiscard ];
200 |
201 | itemdiscard = '(' >> lit("0x") >> hex >> ')' >> "-";
202 |
203 | statusok = string("OK");
204 |
205 | statusother = (string("Inactive item")
206 | | string("Parameter bad")
207 | | string("Access denied"));
208 |
209 | // Rules separated to allow for semantic action on data if desired
210 |
211 | // For example
212 | // omit[eps] >> itemleaf[_val=convert(_1, qcn_item_data_type())];
213 |
214 | itemdata = omit[eps] >> itemleaf;
215 | itemleaf = repeat(ref(size_))[hex];
216 |
217 | #ifdef BOOST_SPIRIT_DEBUG
218 |
219 | BOOST_SPIRIT_DEBUG_NODE(qcndata);
220 | BOOST_SPIRIT_DEBUG_NODE(header);
221 | BOOST_SPIRIT_DEBUG_NODE(item);
222 |
223 | #endif
224 |
225 | }
226 | private:
227 | qi::rule qcndata;
228 |
229 | qi::rule header;
230 | qi::rule description;
231 |
232 | qi::rule nvitemslabel;
233 | qi::rule iteminfo;
234 |
235 | qi::rule itemnumber;
236 | qi::rule itemsize;
237 |
238 | qi::rule item;
239 | qi::rule itempresent;
240 | qi::rule itemnotpresent;
241 |
242 | qi::rule statusok;
243 | qi::rule statusother;
244 |
245 | qi::rule itemcode;
246 | qi::rule itemdiscard;
247 | qi::rule itemdata;
248 | qi::rule itemleaf;
249 |
250 | unsigned int size_;
251 |
252 | };
253 |
254 | struct dict_code
255 | {
256 |
257 | uint code;
258 | std::string description;
259 | std::string category;
260 |
261 | friend std::ostream& operator<<(
262 | std::ostream& o,
263 | struct dict_code const& c)
264 | {
265 | o << c.description << " - " << c.category;
266 | return o;
267 | }
268 |
269 | };
270 |
271 | template < typename Iterator, typename Skipper = space_type >
272 | class codeparser : public qi::grammar
273 | {
274 | public:
275 |
276 | codeparser() : codeparser::base_type(codes)
277 | {
278 |
279 | using qi::uint_;
280 | using qi::char_;
281 | using qi::string;
282 | using qi::lit;
283 | using qi::eol;
284 | using qi::omit;
285 | using qi::no_skip;
286 | using qi::_a;
287 |
288 | codes = *(uint_ >> sep >> description >> sep >> category);
289 |
290 | sep = omit[ char_("^") ];
291 |
292 | description =
293 | omit[char_("\"") ]
294 | >> no_skip[+(char_ - char_("\""))]
295 | >> omit[char_("\"")]
296 | ;
297 |
298 | category =
299 | omit[ char_("'\"") ]
300 | >> no_skip[+(char_ - char_("*\""))]
301 | >> no_skip[ omit[ *(char_ - eol) ] >> eol]
302 | ;
303 |
304 | #ifdef BOOST_SPIRIT_DEBUG
305 |
306 | BOOST_SPIRIT_DEBUG_NODE(codes);
307 | BOOST_SPIRIT_DEBUG_NODE(description);
308 | BOOST_SPIRIT_DEBUG_NODE(category);
309 | BOOST_SPIRIT_DEBUG_NODE(sep);
310 |
311 | #endif
312 |
313 | }
314 | private:
315 | qi::rule codes;
316 | qi::rule sep;
317 | qi::rule description;
318 | qi::rule category;
319 | };
320 |
321 | template <
322 | typename T_data_type,
323 | typename T_item_type,
324 | typename T_map_type,
325 | typename T_parser_type
326 | >
327 | class DataFile
328 | {
329 | public:
330 |
331 | typedef typename T_data_type::iterator d_iterator;
332 | typedef typename T_map_type::iterator m_iterator;
333 | typedef typename T_map_type::const_iterator m_const_iterator;
334 |
335 | DataFile(std::string const& filename)
336 | : filename_(filename),
337 | err_(""),
338 | success_(false)
339 | {
340 | }
341 |
342 | DataFile(DataFile const& rhs)
343 | : filename_(rhs.filename_),
344 | err_(rhs.err_),
345 | success_(rhs.success_),
346 | map_(rhs.map_)
347 | {
348 | }
349 |
350 | ~DataFile() {}
351 |
352 | bool const Open()
353 | {
354 | using spirit::ascii::space;
355 | using qi::eoi;
356 | using qi::blank;
357 |
358 | std::ifstream in(filename_);
359 | if (!in.is_open())
360 | {
361 | err_ = "Could not open input file";
362 | success_ = false;
363 | return success_;
364 | }
365 | in.unsetf(std::ios::skipws);
366 |
367 | typedef spirit::istream_iterator iterator_type;
368 |
369 | iterator_type begin(in);
370 | iterator_type end;
371 |
372 | if (begin == end)
373 | {
374 | err_ = "Empty input file";
375 | success_ = false;
376 | return success_;
377 | }
378 |
379 | T_parser_type p;
380 | bool r = phrase_parse(begin, end, p >> eoi, space, data_);
381 |
382 | if (r && begin == end)
383 | {
384 | MakeHashTable();
385 | success_ = true;
386 | return success_;
387 | }
388 | else
389 | {
390 | err_ = "Invalid format input file";
391 | success_ = false;
392 | return success_;
393 | }
394 | return false;
395 | }
396 |
397 | bool const IsOpen() const { return success_; }
398 | uint const Size() const { return map_.size(); }
399 | std::string const& ErrorMessage() { return err_; }
400 |
401 | public:
402 |
403 | class iterator
404 | : public boost::iterator_adaptor<
405 | iterator,
406 | m_iterator,
407 | T_item_type&,
408 | boost::forward_traversal_tag
409 | >
410 | {
411 | public:
412 | iterator(): iterator::iterator_adaptor_() {}
413 |
414 | iterator(const m_iterator& i) : iterator::iterator_adaptor_(i){}
415 |
416 | T_item_type& dereference() const
417 | {
418 | return this->base()->second;
419 | }
420 |
421 | T_item_type* reference() const
422 | {
423 | return &(this->base()->second);
424 | }
425 |
426 | private:
427 | friend class boost::iterator_core_access;
428 | };
429 |
430 | class const_iterator
431 | : public boost::iterator_adaptor<
432 | const_iterator,
433 | m_const_iterator,
434 | T_item_type const&,
435 | boost::forward_traversal_tag
436 | >
437 | {
438 | public:
439 |
440 | const_iterator(): const_iterator::iterator_adaptor_() {}
441 |
442 | const_iterator(const m_const_iterator& i)
443 | : const_iterator::iterator_adaptor_(i) {}
444 |
445 | const_iterator(const iterator& i)
446 | : const_iterator::iterator_adaptor_(i.base()) {}
447 |
448 | T_item_type const& dereference() const
449 | {
450 | return (T_item_type const&) this->base()->second;
451 | }
452 |
453 | T_item_type const* reference() const
454 | {
455 | return (T_item_type const *) &(this->base()->second);
456 | }
457 |
458 | private:
459 | friend class boost::iterator_core_access;
460 | };
461 |
462 | iterator begin()
463 | {
464 | return iterator(map_.begin());
465 | }
466 |
467 | iterator end()
468 | {
469 | return iterator(map_.end());
470 | }
471 |
472 | const_iterator begin() const
473 | {
474 | return const_iterator(map_.begin());
475 | }
476 |
477 | const_iterator end() const
478 | {
479 | return const_iterator(map_.end());
480 | }
481 |
482 | iterator Find(uint const& key) { return map_.find(key); }
483 | const_iterator Find(uint const& key) const { return map_.find(key); }
484 |
485 |
486 | private:
487 |
488 | virtual typename T_map_type::key_type const Key(d_iterator const& i)=0;
489 |
490 | void MakeHashTable()
491 | {
492 | for (auto i = data_.begin(); i != data_.end(); ++i)
493 | {
494 | map_[Key(i)] = *i;
495 | }
496 | data_.clear();
497 | }
498 |
499 | std::string filename_;
500 | std::string err_;
501 | bool success_;
502 | T_data_type data_;
503 | T_map_type map_;
504 | };
505 |
506 | class Dictionary: public DataFile <
507 | dict_codes_type,
508 | dict_code_type,
509 | dict_map_type,
510 | codeparser
511 | >
512 | {
513 | public:
514 |
515 | Dictionary(std::string const& filename) : DataFile(filename) {}
516 | Dictionary(Dictionary const& rhs) : DataFile(rhs) {}
517 |
518 |
519 | private:
520 |
521 | typename dict_map_type::key_type const Key(d_iterator const& i)
522 | {
523 | return i->code;
524 | }
525 |
526 | };
527 |
528 | class Qcn: public DataFile <
529 | qcn_items_type,
530 | qcn_item_type,
531 | qcn_map_type,
532 | qcnparser< spirit::istream_iterator >
533 | >
534 | {
535 | public:
536 |
537 | typedef qcn_item_type item;
538 | typedef enum {present, missing, both} cmp;
539 |
540 | Qcn(std::string const& filename) : DataFile(filename) {}
541 | Qcn(Qcn const& rhs) : DataFile(rhs) {}
542 |
543 | private:
544 |
545 | typename qcn_map_type::key_type const Key(d_iterator const& i)
546 | {
547 | return i->code;
548 | }
549 |
550 | };
551 |
552 | diff_type const Compare (
553 | Qcn const& lhs,
554 | Qcn const& rhs,
555 | Qcn::cmp const cmp = Qcn::cmp::both,
556 | bool const recurse = true
557 | );
558 |
559 | }
560 |
561 | BOOST_FUSION_ADAPT_STRUCT(
562 | qcn::qitem,
563 | (qcn::uint, code)
564 | (std::string, status)
565 | (std::vector, data)
566 | )
567 |
568 | BOOST_FUSION_ADAPT_STRUCT(
569 | qcn::dict_code,
570 | (qcn::uint, code)
571 | (std::string, description)
572 | (std::string, category)
573 | )
574 |
575 | #endif
576 |
--------------------------------------------------------------------------------
/testfiles/empty.txt:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dl12345/qcn/d0119024f010d8e1923099d7087afa763f9db61e/testfiles/empty.txt
--------------------------------------------------------------------------------
/testfiles/tst1.txt:
--------------------------------------------------------------------------------
1 | [NV items]
2 | [Complete items - 653, Items size - 128]
3 |
4 | 00004 (0x0004) - Inactive item
5 |
6 | 00005 (0x0005) - OK
7 | 3A 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
8 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
9 | 00 00 00 00 FF 00 00 00 00 00 00 00 00 00 00 00
10 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
11 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
12 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
13 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
14 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
15 |
16 | 00007 (0x0007) - OK
17 | 64 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
18 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
19 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
20 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
21 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
22 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
23 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
24 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
25 |
26 | 06828 (0x1AAC) - OK
27 | D5 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00
28 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
29 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
30 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
31 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
32 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
33 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
34 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
35 |
36 | 06829 (0x1AAD) - Inactive item
37 |
38 | 06593 (0x19C1) - OK
39 | BE 0A EA 0A 16 0B 42 0B 6E 0B 9A 0B C6 0B F2 0B
40 | 1E 0C 4A 0C 76 0C A2 0C CE 0C FA 0C 26 0D 52 0D
41 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
42 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
43 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
44 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
45 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
46 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
47 |
--------------------------------------------------------------------------------
/testfiles/tst2.txt:
--------------------------------------------------------------------------------
1 | [NV items]
2 | [Complete items - 653, Items size - 128]
3 |
4 | 00004 (0x0004) - Inactive item
5 |
6 | 00005 (0x0005) - OK
7 | 3A 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
8 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
9 | 00 00 00 00 FF 00 00 00 00 00 00 00 00 00 00 00
10 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
11 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
12 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
13 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
14 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
15 |
16 | 00007 (0x0007) - OK
17 | 64 00 00 00 00 00 01 00 00 00 00 00 00 00 00 00
18 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
19 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
20 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
21 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
22 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
23 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
24 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
25 |
26 | 06828 (0x1AAC) - OK
27 | D5 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00
28 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
29 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
30 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
31 | 00 00 00 00 00 00 01 00 00 00 00 00 00 00 00 00
32 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
33 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
34 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
35 |
36 | 06829 (0x1AAD) - Parameter bad
37 |
38 | 00010 (0x000A) - OK
39 | 00 1F 00 00 00 00 00 00 00 00 00 00 00 00 00 00
40 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
41 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
42 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
43 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
44 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
45 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
46 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
47 |
--------------------------------------------------------------------------------