Line data Source code
1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /*
3 : * This file is part of the LibreOffice project.
4 : *
5 : * This Source Code Form is subject to the terms of the Mozilla Public
6 : * License, v. 2.0. If a copy of the MPL was not distributed with this
7 : * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 : */
9 :
10 : #include "sal/config.h"
11 :
12 : #include <cassert>
13 : #include <cstddef>
14 : #include <cstdlib>
15 : #include <iostream>
16 : #include <map>
17 : #include <utility>
18 : #include <vector>
19 :
20 : #include "config_version.h"
21 : #include "osl/endian.h"
22 : #include "osl/file.h"
23 : #include "osl/file.hxx"
24 : #include "osl/process.h"
25 : #include "rtl/process.h"
26 : #include "rtl/string.h"
27 : #include "rtl/string.hxx"
28 : #include "rtl/textenc.h"
29 : #include "rtl/textcvt.h"
30 : #include "rtl/ustring.hxx"
31 : #include "sal/macros.h"
32 : #include "sal/main.h"
33 : #include "unoidl/unoidl.hxx"
34 :
35 : namespace {
36 :
37 0 : void badUsage() {
38 : std::cerr
39 0 : << "Usage:" << std::endl << std::endl
40 0 : << " unoidl-write [<registries>] [@<entities file>] <unoidl file>"
41 0 : << std::endl << std::endl
42 : << ("where each <registry> is either a new- or legacy-format .rdb file,"
43 0 : " a single .idl")
44 0 : << std::endl
45 : << ("file, or a root directory of an .idl file tree; and the UTF-8"
46 0 : " encoded <entities")
47 0 : << std::endl
48 : << ("file> contains zero or more space-separated names of (non-module)"
49 0 : " entities to")
50 0 : << std::endl
51 : << ("include in the output, and, if omitted, defaults to the complete"
52 0 : " content of the")
53 0 : << std::endl << "last <registry>, if any." << std::endl;
54 0 : std::exit(EXIT_FAILURE);
55 : }
56 :
57 1640 : OUString getArgumentUri(sal_uInt32 argument, bool * entities) {
58 1640 : OUString arg;
59 1640 : rtl_getAppCommandArg(argument, &arg.pData);
60 1640 : if (arg.startsWith("@", &arg)) {
61 5 : if (entities == 0) {
62 0 : badUsage();
63 : }
64 5 : *entities = true;
65 1635 : } else if (entities != 0) {
66 663 : *entities = false;
67 : }
68 3280 : OUString url;
69 1640 : osl::FileBase::RC e1 = osl::FileBase::getFileURLFromSystemPath(arg, url);
70 1640 : if (e1 != osl::FileBase::E_None) {
71 : std::cerr
72 0 : << "Cannot convert \"" << arg << "\" to file URL, error code "
73 0 : << +e1 << std::endl;
74 0 : std::exit(EXIT_FAILURE);
75 : }
76 3280 : OUString cwd;
77 1640 : oslProcessError e2 = osl_getProcessWorkingDir(&cwd.pData);
78 1640 : if (e2 != osl_Process_E_None) {
79 : std::cerr
80 0 : << "Cannot obtain working directory, error code " << +e2
81 0 : << std::endl;
82 0 : std::exit(EXIT_FAILURE);
83 : }
84 1640 : OUString abs;
85 1640 : e1 = osl::FileBase::getAbsoluteFileURL(cwd, url, abs);
86 1640 : if (e1 != osl::FileBase::E_None) {
87 : std::cerr
88 0 : << "Cannot make \"" << url
89 0 : << "\" into an absolute file URL, error code " << +e1 << std::endl;
90 0 : std::exit(EXIT_FAILURE);
91 : }
92 3280 : return abs;
93 : }
94 :
95 53971 : sal_uInt64 getOffset(osl::File & file) {
96 : sal_uInt64 off;
97 53971 : osl::FileBase::RC e = file.getPos(off);
98 53971 : if (e != osl::FileBase::E_None) {
99 : std::cerr
100 0 : << "Cannot determine current position in <" << file.getURL()
101 0 : << ">, error code " << +e << std::endl;
102 0 : std::exit(EXIT_FAILURE);
103 : }
104 53971 : return off;
105 : }
106 :
107 203784 : void write(osl::File & file, void const * buffer, sal_uInt64 size) {
108 : sal_uInt64 n;
109 203784 : osl::FileBase::RC e = file.write(buffer, size, n);
110 203784 : if (e != osl::FileBase::E_None) {
111 : std::cerr
112 0 : << "Cannot write to <" << file.getURL() << ">, error code " << +e
113 0 : << std::endl;
114 0 : std::exit(EXIT_FAILURE);
115 : }
116 203784 : if (n != size) {
117 : std::cerr
118 0 : << "Bad write of " << n << " instead of " << size << " bytes to <"
119 0 : << file.getURL() << '>' << std::endl;
120 0 : std::exit(EXIT_FAILURE);
121 : }
122 203784 : }
123 :
124 27810 : void write8(osl::File & file, sal_uInt64 value) {
125 27810 : if (value > 0xFF) {
126 : std::cerr
127 0 : << "Cannot write value >= 2^8; input is too large" << std::endl;
128 0 : std::exit(EXIT_FAILURE);
129 : }
130 : unsigned char buf[1];
131 27810 : buf[0] = value & 0xFF;
132 27810 : write(file, buf, SAL_N_ELEMENTS(buf));
133 27810 : }
134 :
135 5233 : void write16(osl::File & file, sal_uInt64 value) {
136 5233 : if (value > 0xFFFF) {
137 : std::cerr
138 0 : << "Cannot write value >= 2^16; input is too large" << std::endl;
139 0 : std::exit(EXIT_FAILURE);
140 : }
141 : unsigned char buf[2];
142 5233 : buf[0] = value & 0xFF;
143 5233 : buf[1] = (value >> 8) & 0xFF;
144 5233 : write(file, buf, SAL_N_ELEMENTS(buf));
145 5233 : }
146 :
147 135311 : void write32(osl::File & file, sal_uInt64 value) {
148 135311 : if (value > 0xFFFFFFFF) {
149 : std::cerr
150 0 : << "Cannot write value >= 2^32; input is too large" << std::endl;
151 0 : std::exit(EXIT_FAILURE);
152 : }
153 : unsigned char buf[4];
154 135311 : buf[0] = value & 0xFF;
155 135311 : buf[1] = (value >> 8) & 0xFF;
156 135311 : buf[2] = (value >> 16) & 0xFF;
157 135311 : buf[3] = (value >> 24) & 0xFF;
158 135311 : write(file, buf, SAL_N_ELEMENTS(buf));
159 135311 : }
160 :
161 59 : void write64(osl::File & file, sal_uInt64 value) {
162 : unsigned char buf[8];
163 59 : buf[0] = value & 0xFF;
164 59 : buf[1] = (value >> 8) & 0xFF;
165 59 : buf[2] = (value >> 16) & 0xFF;
166 59 : buf[3] = (value >> 24) & 0xFF;
167 59 : buf[4] = (value >> 32) & 0xFF;
168 59 : buf[5] = (value >> 40) & 0xFF;
169 59 : buf[6] = (value >> 48) & 0xFF;
170 59 : buf[7] = (value >> 56) & 0xFF;
171 59 : write(file, buf, SAL_N_ELEMENTS(buf));
172 59 : }
173 :
174 22 : void writeIso60599Binary32(osl::File & file, float value) {
175 : union {
176 : unsigned char buf[4];
177 : float f; // assuming float is ISO 60599 binary32
178 : } sa;
179 22 : sa.f = value;
180 : #if defined OSL_BIGENDIAN
181 : std::swap(sa.buf[0], sa.buf[3]);
182 : std::swap(sa.buf[1], sa.buf[2]);
183 : #endif
184 22 : write(file, sa.buf, SAL_N_ELEMENTS(sa.buf));
185 22 : }
186 :
187 2 : void writeIso60599Binary64(osl::File & file, double value) {
188 : union {
189 : unsigned char buf[8];
190 : float d; // assuming double is ISO 60599 binary64
191 : } sa;
192 2 : sa.d = value;
193 : #if defined OSL_BIGENDIAN
194 : std::swap(sa.buf[0], sa.buf[7]);
195 : std::swap(sa.buf[1], sa.buf[6]);
196 : std::swap(sa.buf[2], sa.buf[5]);
197 : std::swap(sa.buf[3], sa.buf[4]);
198 : #endif
199 2 : write(file, sa.buf, SAL_N_ELEMENTS(sa.buf));
200 2 : }
201 :
202 70333 : OString toAscii(OUString const & name) {
203 70333 : OString ascii;
204 70333 : if (!name.convertToString(
205 : &ascii, RTL_TEXTENCODING_ASCII_US,
206 : (RTL_UNICODETOTEXT_FLAGS_UNDEFINED_ERROR
207 70333 : | RTL_UNICODETOTEXT_FLAGS_INVALID_ERROR)))
208 : {
209 : std::cerr
210 0 : << "Cannot convert \"" << name << "\" to US ASCII" << std::endl;
211 0 : std::exit(EXIT_FAILURE);
212 : }
213 70333 : return ascii;
214 : }
215 :
216 281 : OString toUtf8(OUString const & string) {
217 281 : OString ascii;
218 281 : if (!string.convertToString(
219 : &ascii, RTL_TEXTENCODING_UTF8,
220 : (RTL_UNICODETOTEXT_FLAGS_UNDEFINED_ERROR
221 281 : | RTL_UNICODETOTEXT_FLAGS_INVALID_ERROR)))
222 : {
223 : std::cerr
224 0 : << "Cannot convert \"" << string << "\" to UTF-8" << std::endl;
225 0 : std::exit(EXIT_FAILURE);
226 : }
227 281 : return ascii;
228 : }
229 :
230 18624 : sal_uInt64 writeNulName(osl::File & file, OUString const & name) {
231 18624 : OString ascii(toAscii(name));
232 18624 : if (ascii.indexOf('\0') != -1) {
233 : std::cerr
234 0 : << "Name \"" << ascii << "\" contains NUL characters" << std::endl;
235 0 : std::exit(EXIT_FAILURE);
236 : }
237 18624 : sal_uInt64 off = getOffset(file);
238 18624 : write(file, ascii.getStr(), ascii.getLength() + 1);
239 18624 : return off;
240 : }
241 :
242 51990 : void writeIdxString(osl::File & file, OString const & string) {
243 51990 : static std::map< OString, sal_uInt64 > reuse;
244 51990 : std::map< OString, sal_uInt64 >::iterator i(reuse.find(string));
245 51990 : if (i == reuse.end()) {
246 16127 : reuse.insert(std::make_pair(string, getOffset(file)));
247 : assert(
248 : (static_cast< sal_uInt64 >(string.getLength()) & 0x80000000) == 0);
249 16127 : write32(file, static_cast< sal_uInt64 >(string.getLength()));
250 16127 : write(file, string.getStr(), string.getLength());
251 : } else {
252 35863 : if ((i->second & 0x80000000) != 0) {
253 : std::cerr
254 0 : << "Cannot write index 0x" << std::hex << i->second << std::dec
255 0 : << " of \"" << string << "\"; input is too large" << std::endl;
256 0 : std::exit(EXIT_FAILURE);
257 : }
258 35863 : write32(file, i->second | 0x80000000);
259 : }
260 51990 : }
261 :
262 51709 : void writeIdxName(osl::File & file, OUString const & name) {
263 51709 : writeIdxString(file, toAscii(name));
264 51709 : }
265 :
266 37690 : void writeAnnotations(
267 : osl::File & file, bool annotate,
268 : std::vector< OUString > const & annotations)
269 : {
270 : assert(annotate || annotations.empty());
271 37690 : if (annotate) {
272 1392 : write32(file, annotations.size());
273 : // overflow from std::vector::size_type -> sal_uInt64 is unrealistic
274 5019 : for (std::vector< OUString >::const_iterator i(annotations.begin());
275 3346 : i != annotations.end(); ++i)
276 : {
277 281 : writeIdxString(file, toUtf8(*i));
278 : }
279 : }
280 37690 : }
281 :
282 6347 : void writeKind(
283 : osl::File & file,
284 : rtl::Reference< unoidl::PublishableEntity > const & entity,
285 : bool annotated, bool flag = false)
286 : {
287 : assert(entity.is());
288 6347 : sal_uInt64 v = entity->getSort();
289 6347 : if (entity->isPublished()) {
290 2874 : v |= 0x80;
291 : }
292 6347 : if (annotated) {
293 228 : v |= 0x40;
294 : }
295 6347 : if (flag) {
296 608 : v |= 0x20;
297 : }
298 6347 : write8(file, v);
299 6347 : }
300 :
301 33380 : struct Item {
302 6676 : explicit Item(rtl::Reference< unoidl::Entity > const & theEntity):
303 6676 : entity(theEntity), nameOffset(0), dataOffset(0)
304 6676 : {}
305 :
306 : rtl::Reference< unoidl::Entity > entity;
307 : std::map< OUString, Item > module;
308 : sal_uInt64 nameOffset;
309 : sal_uInt64 dataOffset;
310 : };
311 :
312 59740 : struct ConstItem {
313 11948 : ConstItem(
314 : unoidl::ConstantValue const & theConstant,
315 : std::vector< OUString > const & theAnnotations):
316 : constant(theConstant), annotations(theAnnotations), nameOffset(0),
317 11948 : dataOffset(0)
318 11948 : {}
319 :
320 : unoidl::ConstantValue constant;
321 : std::vector< OUString > annotations;
322 : sal_uInt64 nameOffset;
323 : sal_uInt64 dataOffset;
324 : };
325 :
326 5 : void mapEntities(
327 : rtl::Reference< unoidl::Manager > const & manager, OUString const & uri,
328 : std::map< OUString, Item > & map)
329 : {
330 : assert(manager.is());
331 5 : osl::File f(uri);
332 5 : osl::FileBase::RC e = f.open(osl_File_OpenFlag_Read);
333 5 : if (e != osl::FileBase::E_None) {
334 : std::cerr
335 0 : << "Cannot open <" << f.getURL() << "> for reading, error code "
336 0 : << +e << std::endl;
337 0 : std::exit(EXIT_FAILURE);
338 : }
339 : for (;;) {
340 : sal_Bool eof;
341 10 : e = f.isEndOfFile(&eof);
342 10 : if (e != osl::FileBase::E_None) {
343 : std::cerr
344 0 : << "Cannot check <" << f.getURL() << "> for EOF, error code "
345 0 : << +e << std::endl;
346 0 : std::exit(EXIT_FAILURE);
347 : }
348 10 : if (eof) {
349 5 : break;
350 : }
351 5 : rtl::ByteSequence s1;
352 5 : e = f.readLine(s1);
353 5 : if (e != osl::FileBase::E_None) {
354 : std::cerr
355 0 : << "Cannot read from <" << f.getURL() << ">, error code "
356 0 : << +e << std::endl;
357 0 : std::exit(EXIT_FAILURE);
358 : }
359 10 : OUString s2;
360 5 : if (!rtl_convertStringToUString(
361 5 : &s2.pData, reinterpret_cast< char const * >(s1.getConstArray()),
362 : s1.getLength(), RTL_TEXTENCODING_UTF8,
363 : (RTL_TEXTTOUNICODE_FLAGS_UNDEFINED_ERROR
364 : | RTL_TEXTTOUNICODE_FLAGS_MBUNDEFINED_ERROR
365 10 : | RTL_TEXTTOUNICODE_FLAGS_INVALID_ERROR)))
366 : {
367 : std::cerr
368 0 : << "Cannot interpret line read from <" << f.getURL()
369 0 : << "> as UTF-8" << std::endl;
370 0 : std::exit(EXIT_FAILURE);
371 : }
372 5330 : for (sal_Int32 i = 0; i != -1;) {
373 5320 : OUString t(s2.getToken(0, ' ', i));
374 5320 : if (!t.isEmpty()) {
375 5315 : rtl::Reference< unoidl::Entity > ent(manager->findEntity(t));
376 5315 : if (!ent.is()) {
377 : std::cerr
378 0 : << "Unknown entity \"" << t << "\" read from <"
379 0 : << f.getURL() << ">" << std::endl;
380 0 : std::exit(EXIT_FAILURE);
381 : }
382 5315 : if (ent->getSort() == unoidl::Entity::SORT_MODULE) {
383 : std::cerr
384 0 : << "Module entity \"" << t << "\" read from <"
385 0 : << f.getURL() << ">" << std::endl;
386 0 : std::exit(EXIT_FAILURE);
387 : }
388 5315 : std::map< OUString, Item > * map2 = ↦
389 5315 : for (sal_Int32 j = 0;;) {
390 26228 : OUString id(t.getToken(0, '.', j));
391 26228 : if (j == -1) {
392 5315 : map2->insert(std::make_pair(id, Item(ent)));
393 5315 : break;
394 : }
395 20913 : std::map< OUString, Item >::iterator k(map2->find(id));
396 20913 : if (k == map2->end()) {
397 : rtl::Reference< unoidl::Entity > ent2(
398 149 : manager->findEntity(t.copy(0, j - 1)));
399 : assert(ent2.is());
400 149 : k = map2->insert(std::make_pair(id, Item(ent2))).first;
401 : }
402 : assert(
403 : k->second.entity->getSort()
404 : == unoidl::Entity::SORT_MODULE);
405 20913 : map2 = &k->second.module;
406 20913 : }
407 : }
408 5320 : }
409 5 : }
410 10 : e = f.close();
411 5 : if (e != osl::FileBase::E_None) {
412 : std::cerr
413 0 : << "Cannot close <" << f.getURL() << "> after reading, error code "
414 0 : << +e << std::endl;
415 0 : std::exit(EXIT_FAILURE);
416 5 : }
417 5 : }
418 :
419 473 : void mapCursor(
420 : rtl::Reference< unoidl::MapCursor > const & cursor,
421 : std::map< OUString, Item > & map)
422 : {
423 473 : if (cursor.is()) {
424 : for (;;) {
425 1685 : OUString name;
426 2897 : rtl::Reference< unoidl::Entity > ent(cursor->getNext(&name));
427 1685 : if (!ent.is()) {
428 473 : break;
429 : }
430 : std::pair< std::map< OUString, Item >::iterator, bool > i(
431 1212 : map.insert(std::make_pair(name, Item(ent))));
432 1212 : if (!i.second) {
433 0 : std::cout << "Duplicate name \"" << name << '"' << std::endl;
434 0 : std::exit(EXIT_FAILURE);
435 : }
436 1212 : if (i.first->second.entity->getSort()
437 : == unoidl::Entity::SORT_MODULE)
438 : {
439 : mapCursor(
440 : rtl::Reference< unoidl::ModuleEntity >(
441 : static_cast< unoidl::ModuleEntity * >(
442 360 : i.first->second.entity.get()))->createCursor(),
443 360 : i.first->second.module);
444 : }
445 1212 : }
446 : }
447 473 : }
448 :
449 627 : sal_uInt64 writeMap(
450 : osl::File & file, std::map< OUString, Item > & map, std::size_t * rootSize)
451 : {
452 7303 : for (std::map< OUString, Item >::iterator i(map.begin()); i != map.end();
453 : ++i)
454 : {
455 6676 : switch (i->second.entity->getSort()) {
456 : case unoidl::Entity::SORT_MODULE:
457 329 : i->second.dataOffset = writeMap(file, i->second.module, 0);
458 329 : break;
459 : case unoidl::Entity::SORT_ENUM_TYPE:
460 : {
461 : rtl::Reference< unoidl::EnumTypeEntity > ent2(
462 : static_cast< unoidl::EnumTypeEntity * >(
463 254 : i->second.entity.get()));
464 254 : bool ann = !ent2->getAnnotations().empty();
465 4783 : for (std::vector< unoidl::EnumTypeEntity::Member >::
466 254 : const_iterator j(ent2->getMembers().begin());
467 4751 : !ann && j != ent2->getMembers().end(); ++j)
468 : {
469 1335 : ann = !j->annotations.empty();
470 : }
471 254 : i->second.dataOffset = getOffset(file);
472 254 : writeKind(file, ent2.get(), ann);
473 254 : write32(file, ent2->getMembers().size());
474 4953 : for (std::vector< unoidl::EnumTypeEntity::Member >::
475 254 : const_iterator j(ent2->getMembers().begin());
476 3302 : j != ent2->getMembers().end(); ++j)
477 : {
478 1397 : writeIdxName(file, j->name);
479 1397 : write32(file, static_cast< sal_uInt32 >(j->value));
480 1397 : writeAnnotations(file, ann, j->annotations);
481 : }
482 254 : writeAnnotations(file, ann, ent2->getAnnotations());
483 254 : break;
484 : }
485 : case unoidl::Entity::SORT_PLAIN_STRUCT_TYPE:
486 : {
487 : rtl::Reference< unoidl::PlainStructTypeEntity > ent2(
488 : static_cast< unoidl::PlainStructTypeEntity * >(
489 471 : i->second.entity.get()));
490 471 : bool ann = !ent2->getAnnotations().empty();
491 5886 : for (std::vector< unoidl::PlainStructTypeEntity::Member >::
492 471 : const_iterator j(ent2->getDirectMembers().begin());
493 5856 : !ann && j != ent2->getDirectMembers().end(); ++j)
494 : {
495 1486 : ann = !j->annotations.empty();
496 : }
497 471 : i->second.dataOffset = getOffset(file);
498 : writeKind(
499 471 : file, ent2.get(), ann, !ent2->getDirectBase().isEmpty());
500 471 : if (!ent2->getDirectBase().isEmpty()) {
501 111 : writeIdxName(file, ent2->getDirectBase());
502 : }
503 471 : write32(file, ent2->getDirectMembers().size());
504 6036 : for (std::vector< unoidl::PlainStructTypeEntity::Member >::
505 471 : const_iterator j(ent2->getDirectMembers().begin());
506 4024 : j != ent2->getDirectMembers().end(); ++j)
507 : {
508 1541 : writeIdxName(file, j->name);
509 1541 : writeIdxName(file, j->type);
510 1541 : writeAnnotations(file, ann, j->annotations);
511 : }
512 471 : writeAnnotations(file, ann, ent2->getAnnotations());
513 471 : break;
514 : }
515 : case unoidl::Entity::SORT_POLYMORPHIC_STRUCT_TYPE_TEMPLATE:
516 : {
517 : rtl::Reference< unoidl::PolymorphicStructTypeTemplateEntity >
518 : ent2(
519 : static_cast<
520 : unoidl::PolymorphicStructTypeTemplateEntity * >(
521 58 : i->second.entity.get()));
522 58 : bool ann = !ent2->getAnnotations().empty();
523 366 : for (std::vector<
524 : unoidl::PolymorphicStructTypeTemplateEntity::Member >::
525 : const_iterator j(
526 58 : ent2->getMembers().begin());
527 366 : !ann && j != ent2->getMembers().end(); ++j)
528 : {
529 64 : ann = !j->annotations.empty();
530 : }
531 58 : i->second.dataOffset = getOffset(file);
532 58 : writeKind(file, ent2.get(), ann);
533 58 : write32(file, ent2->getTypeParameters().size());
534 357 : for (std::vector< OUString >::const_iterator j(
535 58 : ent2->getTypeParameters().begin());
536 238 : j != ent2->getTypeParameters().end(); ++j)
537 : {
538 61 : writeIdxName(file, *j);
539 : }
540 58 : write32(file, ent2->getMembers().size());
541 366 : for (std::vector<
542 : unoidl::PolymorphicStructTypeTemplateEntity::Member >::
543 : const_iterator j(
544 58 : ent2->getMembers().begin());
545 244 : j != ent2->getMembers().end(); ++j)
546 : {
547 64 : sal_uInt64 f = 0;
548 64 : if (j->parameterized) {
549 39 : f |= 0x01;
550 : }
551 64 : write8(file, f);
552 64 : writeIdxName(file, j->name);
553 64 : writeIdxName(file, j->type);
554 64 : writeAnnotations(file, ann, j->annotations);
555 : }
556 58 : writeAnnotations(file, ann, ent2->getAnnotations());
557 58 : break;
558 : }
559 : case unoidl::Entity::SORT_EXCEPTION_TYPE:
560 : {
561 : rtl::Reference< unoidl::ExceptionTypeEntity > ent2(
562 : static_cast< unoidl::ExceptionTypeEntity * >(
563 303 : i->second.entity.get()));
564 303 : bool ann = !ent2->getAnnotations().empty();
565 1400 : for (std::vector< unoidl::ExceptionTypeEntity::Member >::
566 303 : const_iterator j(ent2->getDirectMembers().begin());
567 1396 : !ann && j != ent2->getDirectMembers().end(); ++j)
568 : {
569 163 : ann = !j->annotations.empty();
570 : }
571 303 : i->second.dataOffset = getOffset(file);
572 : writeKind(
573 303 : file, ent2.get(), ann, !ent2->getDirectBase().isEmpty());
574 303 : if (!ent2->getDirectBase().isEmpty()) {
575 254 : writeIdxName(file, ent2->getDirectBase());
576 : }
577 303 : write32(file, ent2->getDirectMembers().size());
578 1401 : for (std::vector< unoidl::ExceptionTypeEntity::Member >::
579 303 : const_iterator j(ent2->getDirectMembers().begin());
580 934 : j != ent2->getDirectMembers().end(); ++j)
581 : {
582 164 : writeIdxName(file, j->name);
583 164 : writeIdxName(file, j->type);
584 164 : writeAnnotations(file, ann, j->annotations);
585 : }
586 303 : writeAnnotations(file, ann, ent2->getAnnotations());
587 303 : break;
588 : }
589 : case unoidl::Entity::SORT_INTERFACE_TYPE:
590 : {
591 : rtl::Reference< unoidl::InterfaceTypeEntity > ent2(
592 : static_cast< unoidl::InterfaceTypeEntity * >(
593 2172 : i->second.entity.get()));
594 2172 : bool ann = !ent2->getAnnotations().empty();
595 13593 : for (std::vector< unoidl::AnnotatedReference >::const_iterator
596 2172 : j(ent2->getDirectMandatoryBases().begin());
597 13401 : !ann && j != ent2->getDirectMandatoryBases().end(); ++j)
598 : {
599 2327 : ann = !j->annotations.empty();
600 : }
601 6789 : for (std::vector< unoidl::AnnotatedReference >::const_iterator
602 2172 : j(ent2->getDirectOptionalBases().begin());
603 6597 : !ann && j != ent2->getDirectOptionalBases().end(); ++j)
604 : {
605 59 : ann = !j->annotations.empty();
606 : }
607 10304 : for (std::vector< unoidl::InterfaceTypeEntity::Attribute >::
608 2172 : const_iterator j(ent2->getDirectAttributes().begin());
609 10108 : !ann && j != ent2->getDirectAttributes().end(); ++j)
610 : {
611 1230 : ann = !j->annotations.empty();
612 : }
613 24777 : for (std::vector< unoidl::InterfaceTypeEntity::Method >::
614 2172 : const_iterator j(ent2->getDirectMethods().begin());
615 24567 : !ann && j != ent2->getDirectMethods().end(); ++j)
616 : {
617 6052 : ann = !j->annotations.empty();
618 : }
619 2172 : i->second.dataOffset = getOffset(file);
620 2172 : writeKind(file, ent2.get(), ann);
621 2172 : write32(file, ent2->getDirectMandatoryBases().size());
622 13788 : for (std::vector< unoidl::AnnotatedReference >::const_iterator
623 2172 : j(ent2->getDirectMandatoryBases().begin());
624 9192 : j != ent2->getDirectMandatoryBases().end(); ++j)
625 : {
626 2424 : writeIdxName(file, j->name);
627 2424 : writeAnnotations(file, ann, j->annotations);
628 : }
629 2172 : write32(file, ent2->getDirectOptionalBases().size());
630 6693 : for (std::vector< unoidl::AnnotatedReference >::const_iterator
631 2172 : j(ent2->getDirectOptionalBases().begin());
632 4462 : j != ent2->getDirectOptionalBases().end(); ++j)
633 : {
634 59 : writeIdxName(file, j->name);
635 59 : writeAnnotations(file, ann, j->annotations);
636 : }
637 2172 : write32(file, ent2->getDirectAttributes().size());
638 10254 : for (std::vector< unoidl::InterfaceTypeEntity::Attribute >::
639 2172 : const_iterator j(ent2->getDirectAttributes().begin());
640 6836 : j != ent2->getDirectAttributes().end(); ++j)
641 : {
642 1246 : sal_uInt64 f = 0;
643 1246 : if (j->bound) {
644 161 : f |= 0x01;
645 : }
646 1246 : if (j->readOnly) {
647 275 : f |= 0x02;
648 : }
649 1246 : write8(file, f);
650 1246 : writeIdxName(file, j->name);
651 1246 : writeIdxName(file, j->type);
652 1246 : write32(file, j->getExceptions.size());
653 4065 : for (std::vector< OUString >::const_iterator k(
654 1246 : j->getExceptions.begin());
655 2710 : k != j->getExceptions.end(); ++k)
656 : {
657 109 : writeIdxName(file, *k);
658 : }
659 1246 : if (!j->readOnly) {
660 971 : write32(file, j->setExceptions.size());
661 3300 : for (std::vector< OUString >::const_iterator k(
662 971 : j->setExceptions.begin());
663 2200 : k != j->setExceptions.end(); ++k)
664 : {
665 129 : writeIdxName(file, *k);
666 : }
667 : }
668 1246 : writeAnnotations(file, ann, j->annotations);
669 : }
670 2172 : write32(file, ent2->getDirectMethods().size());
671 25662 : for (std::vector< unoidl::InterfaceTypeEntity::Method >::
672 2172 : const_iterator j(ent2->getDirectMethods().begin());
673 17108 : j != ent2->getDirectMethods().end(); ++j)
674 : {
675 6382 : writeIdxName(file, j->name);
676 6382 : writeIdxName(file, j->returnType);
677 6382 : write32(file, j->parameters.size());
678 40389 : for (std::vector<
679 : unoidl::InterfaceTypeEntity::Method::Parameter >::
680 6382 : const_iterator k(j->parameters.begin());
681 26926 : k != j->parameters.end(); ++k)
682 : {
683 7081 : write8(file, k->direction);
684 7081 : writeIdxName(file, k->name);
685 7081 : writeIdxName(file, k->type);
686 : }
687 6382 : write32(file, j->exceptions.size());
688 28158 : for (std::vector< OUString >::const_iterator k(
689 6382 : j->exceptions.begin());
690 18772 : k != j->exceptions.end(); ++k)
691 : {
692 3004 : writeIdxName(file, *k);
693 : }
694 6382 : writeAnnotations(file, ann, j->annotations);
695 : }
696 2172 : writeAnnotations(file, ann, ent2->getAnnotations());
697 2172 : break;
698 : }
699 : case unoidl::Entity::SORT_TYPEDEF:
700 : {
701 : rtl::Reference< unoidl::TypedefEntity > ent2(
702 : static_cast< unoidl::TypedefEntity * >(
703 88 : i->second.entity.get()));
704 88 : bool ann = !ent2->getAnnotations().empty();
705 88 : i->second.dataOffset = getOffset(file);
706 88 : writeKind(file, ent2.get(), ann);
707 88 : writeIdxName(file, ent2->getType());
708 88 : writeAnnotations(file, ann, ent2->getAnnotations());
709 88 : break;
710 : }
711 : case unoidl::Entity::SORT_CONSTANT_GROUP:
712 : {
713 : rtl::Reference< unoidl::ConstantGroupEntity > ent2(
714 : static_cast< unoidl::ConstantGroupEntity * >(
715 1211 : i->second.entity.get()));
716 2422 : std::map< OUString, ConstItem > cmap;
717 39477 : for (std::vector< unoidl::ConstantGroupEntity::Member >::
718 1211 : const_iterator j(ent2->getMembers().begin());
719 26318 : j != ent2->getMembers().end(); ++j)
720 : {
721 23896 : if (!cmap.insert(
722 : std::make_pair(
723 23896 : j->name, ConstItem(j->value, j->annotations))).
724 11948 : second)
725 : {
726 : std::cout
727 0 : << "Duplicate constant group member name \""
728 0 : << j->name << '"' << std::endl;
729 0 : std::exit(EXIT_FAILURE);
730 : }
731 : }
732 39477 : for (std::map< OUString, ConstItem >::iterator j(cmap.begin());
733 26318 : j != cmap.end(); ++j)
734 : {
735 11948 : j->second.dataOffset = getOffset(file);
736 11948 : sal_uInt64 v = j->second.constant.type;
737 11948 : if (!j->second.annotations.empty()) {
738 14 : v |= 0x80;
739 : }
740 11948 : write8(file, v);
741 11948 : switch (j->second.constant.type) {
742 : case unoidl::ConstantValue::TYPE_BOOLEAN:
743 2 : write8(file, j->second.constant.booleanValue ? 1 : 0);
744 2 : break;
745 : case unoidl::ConstantValue::TYPE_BYTE:
746 : write8(
747 : file,
748 : static_cast< sal_uInt8 >(
749 261 : j->second.constant.byteValue));
750 261 : break;
751 : case unoidl::ConstantValue::TYPE_SHORT:
752 : write16(
753 : file,
754 : static_cast< sal_uInt16 >(
755 2082 : j->second.constant.shortValue));
756 2082 : break;
757 : case unoidl::ConstantValue::TYPE_UNSIGNED_SHORT:
758 14 : write16(file, j->second.constant.unsignedShortValue);
759 14 : break;
760 : case unoidl::ConstantValue::TYPE_LONG:
761 : write32(
762 : file,
763 : static_cast< sal_uInt32 >(
764 9492 : j->second.constant.longValue));
765 9492 : break;
766 : case unoidl::ConstantValue::TYPE_UNSIGNED_LONG:
767 14 : write32(file, j->second.constant.unsignedLongValue);
768 14 : break;
769 : case unoidl::ConstantValue::TYPE_HYPER:
770 : write64(
771 : file,
772 : static_cast< sal_uInt64 >(
773 45 : j->second.constant.hyperValue));
774 45 : break;
775 : case unoidl::ConstantValue::TYPE_UNSIGNED_HYPER:
776 14 : write64(file, j->second.constant.unsignedHyperValue);
777 14 : break;
778 : case unoidl::ConstantValue::TYPE_FLOAT:
779 : writeIso60599Binary32(
780 22 : file, j->second.constant.floatValue);
781 22 : break;
782 : case unoidl::ConstantValue::TYPE_DOUBLE:
783 : writeIso60599Binary64(
784 2 : file, j->second.constant.doubleValue);
785 2 : break;
786 : default:
787 0 : for (;;) { std::abort(); } // this cannot happen
788 : }
789 : writeAnnotations(
790 11948 : file, !j->second.annotations.empty(),
791 23896 : j->second.annotations);
792 : }
793 39477 : for (std::map< OUString, ConstItem >::iterator j(
794 1211 : cmap.begin());
795 26318 : j != cmap.end(); ++j)
796 : {
797 11948 : j->second.nameOffset = writeNulName(file, j->first);
798 : }
799 1211 : bool ann = !ent2->getAnnotations().empty();
800 1211 : i->second.dataOffset = getOffset(file);
801 1211 : writeKind(file, ent2.get(), ann);
802 1211 : write32(file, cmap.size());
803 : // overflow from std::map::size_type -> sal_uInt64 is
804 : // unrealistic
805 39477 : for (std::map< OUString, ConstItem >::iterator j(
806 1211 : cmap.begin());
807 26318 : j != cmap.end(); ++j)
808 : {
809 11948 : write32(file, j->second.nameOffset);
810 11948 : write32(file, j->second.dataOffset);
811 : }
812 1211 : writeAnnotations(file, ann, ent2->getAnnotations());
813 2422 : break;
814 : }
815 : case unoidl::Entity::SORT_SINGLE_INTERFACE_BASED_SERVICE:
816 : {
817 : rtl::Reference< unoidl::SingleInterfaceBasedServiceEntity >
818 : ent2(
819 : static_cast<
820 : unoidl::SingleInterfaceBasedServiceEntity * >(
821 532 : i->second.entity.get()));
822 532 : bool dfltCtor = ent2->getConstructors().size() == 1
823 532 : && ent2->getConstructors()[0].defaultConstructor;
824 532 : bool ann = !ent2->getAnnotations().empty();
825 532 : if (!dfltCtor) {
826 1833 : for (std::vector<
827 : unoidl::SingleInterfaceBasedServiceEntity::
828 : Constructor >::const_iterator j(
829 289 : ent2->getConstructors().begin());
830 1833 : !ann && j != ent2->getConstructors().end(); ++j)
831 : {
832 322 : ann = !j->annotations.empty();
833 : }
834 : }
835 532 : i->second.dataOffset = getOffset(file);
836 532 : writeKind(file, ent2.get(), ann, dfltCtor);
837 532 : writeIdxName(file, ent2->getBase());
838 532 : if (!dfltCtor) {
839 289 : write32(file, ent2->getConstructors().size());
840 1833 : for (std::vector<
841 : unoidl::SingleInterfaceBasedServiceEntity::
842 : Constructor >::const_iterator j(
843 289 : ent2->getConstructors().begin());
844 1222 : j != ent2->getConstructors().end(); ++j)
845 : {
846 322 : if (j->defaultConstructor) {
847 : std::cout
848 0 : << "Unexpected default constructor \""
849 0 : << j->name << '"' << std::endl;
850 0 : std::exit(EXIT_FAILURE);
851 : }
852 322 : writeIdxName(file, j->name);
853 322 : write32(file, j->parameters.size());
854 2562 : for (std::vector<
855 : unoidl::SingleInterfaceBasedServiceEntity::
856 : Constructor::Parameter >::const_iterator k(
857 322 : j->parameters.begin());
858 1708 : k != j->parameters.end(); ++k)
859 : {
860 532 : sal_uInt64 f = 0;
861 532 : if (k->rest) {
862 10 : f |= 0x04;
863 : }
864 532 : write8(file, f);
865 532 : writeIdxName(file, k->name);
866 532 : writeIdxName(file, k->type);
867 : }
868 322 : write32(file, j->exceptions.size());
869 1170 : for (std::vector< OUString >::const_iterator k(
870 322 : j->exceptions.begin());
871 780 : k != j->exceptions.end(); ++k)
872 : {
873 68 : writeIdxName(file, *k);
874 : }
875 322 : writeAnnotations(file, ann, j->annotations);
876 : }
877 : }
878 532 : writeAnnotations(file, ann, ent2->getAnnotations());
879 532 : break;
880 : }
881 : case unoidl::Entity::SORT_ACCUMULATION_BASED_SERVICE:
882 : {
883 : rtl::Reference< unoidl::AccumulationBasedServiceEntity > ent2(
884 : static_cast< unoidl::AccumulationBasedServiceEntity * >(
885 1060 : i->second.entity.get()));
886 1060 : bool ann = !ent2->getAnnotations().empty();
887 5414 : for (std::vector< unoidl::AnnotatedReference >::const_iterator
888 1060 : j(ent2->getDirectMandatoryBaseServices().begin());
889 5332 : !ann && j != ent2->getDirectMandatoryBaseServices().end();
890 : ++j)
891 : {
892 731 : ann = !j->annotations.empty();
893 : }
894 3405 : for (std::vector< unoidl::AnnotatedReference >::const_iterator
895 1060 : j(ent2->getDirectOptionalBaseServices().begin());
896 3321 : !ann && j != ent2->getDirectOptionalBaseServices().end();
897 : ++j)
898 : {
899 61 : ann = !j->annotations.empty();
900 : }
901 7691 : for (std::vector< unoidl::AnnotatedReference >::const_iterator
902 1060 : j(ent2->getDirectMandatoryBaseInterfaces().begin());
903 2549 : (!ann
904 10152 : && j != ent2->getDirectMandatoryBaseInterfaces().end());
905 : ++j)
906 : {
907 1489 : ann = !j->annotations.empty();
908 : }
909 4052 : for (std::vector< unoidl::AnnotatedReference >::const_iterator
910 1060 : j(ent2->getDirectOptionalBaseInterfaces().begin());
911 3958 : !ann && j != ent2->getDirectOptionalBaseInterfaces().end();
912 : ++j)
913 : {
914 275 : ann = !j->annotations.empty();
915 : }
916 11982 : for (std::vector<
917 : unoidl::AccumulationBasedServiceEntity::Property >::
918 : const_iterator j(
919 1060 : ent2->getDirectProperties().begin());
920 11862 : !ann && j != ent2->getDirectProperties().end(); ++j)
921 : {
922 2914 : ann = !j->annotations.empty();
923 : }
924 1060 : i->second.dataOffset = getOffset(file);
925 1060 : writeKind(file, ent2.get(), ann);
926 1060 : write32(file, ent2->getDirectMandatoryBaseServices().size());
927 5400 : for (std::vector< unoidl::AnnotatedReference >::const_iterator
928 1060 : j(ent2->getDirectMandatoryBaseServices().begin());
929 3600 : j != ent2->getDirectMandatoryBaseServices().end(); ++j)
930 : {
931 740 : writeIdxName(file, j->name);
932 740 : writeAnnotations(file, ann, j->annotations);
933 : }
934 1060 : write32(file, ent2->getDirectOptionalBaseServices().size());
935 3366 : for (std::vector< unoidl::AnnotatedReference >::const_iterator
936 1060 : j(ent2->getDirectOptionalBaseServices().begin());
937 2244 : j != ent2->getDirectOptionalBaseServices().end(); ++j)
938 : {
939 62 : writeIdxName(file, j->name);
940 62 : writeAnnotations(file, ann, j->annotations);
941 : }
942 1060 : write32(file, ent2->getDirectMandatoryBaseInterfaces().size());
943 7869 : for (std::vector< unoidl::AnnotatedReference >::const_iterator
944 1060 : j(ent2->getDirectMandatoryBaseInterfaces().begin());
945 5246 : j != ent2->getDirectMandatoryBaseInterfaces().end(); ++j)
946 : {
947 1563 : writeIdxName(file, j->name);
948 1563 : writeAnnotations(file, ann, j->annotations);
949 : }
950 1060 : write32(file, ent2->getDirectOptionalBaseInterfaces().size());
951 4062 : for (std::vector< unoidl::AnnotatedReference >::const_iterator
952 1060 : j(ent2->getDirectOptionalBaseInterfaces().begin());
953 2708 : j != ent2->getDirectOptionalBaseInterfaces().end(); ++j)
954 : {
955 294 : writeIdxName(file, j->name);
956 294 : writeAnnotations(file, ann, j->annotations);
957 : }
958 1060 : write32(file, ent2->getDirectProperties().size());
959 12591 : for (std::vector<
960 : unoidl::AccumulationBasedServiceEntity::Property >::
961 : const_iterator j(
962 1060 : ent2->getDirectProperties().begin());
963 8394 : j != ent2->getDirectProperties().end(); ++j)
964 : {
965 3137 : write16(file, static_cast< sal_uInt16 >(j->attributes));
966 3137 : writeIdxName(file, j->name);
967 3137 : writeIdxName(file, j->type);
968 3137 : writeAnnotations(file, ann, j->annotations);
969 : }
970 1060 : writeAnnotations(file, ann, ent2->getAnnotations());
971 1060 : break;
972 : }
973 : case unoidl::Entity::SORT_INTERFACE_BASED_SINGLETON:
974 : {
975 : rtl::Reference< unoidl::InterfaceBasedSingletonEntity > ent2(
976 : static_cast< unoidl::InterfaceBasedSingletonEntity * >(
977 187 : i->second.entity.get()));
978 187 : bool ann = !ent2->getAnnotations().empty();
979 187 : i->second.dataOffset = getOffset(file);
980 187 : writeKind(file, ent2.get(), ann);
981 187 : writeIdxName(file, ent2->getBase());
982 187 : writeAnnotations(file, ann, ent2->getAnnotations());
983 187 : break;
984 : }
985 : case unoidl::Entity::SORT_SERVICE_BASED_SINGLETON:
986 : {
987 : rtl::Reference< unoidl::ServiceBasedSingletonEntity > ent2(
988 : static_cast< unoidl::ServiceBasedSingletonEntity * >(
989 11 : i->second.entity.get()));
990 11 : bool ann = !ent2->getAnnotations().empty();
991 11 : i->second.dataOffset = getOffset(file);
992 11 : writeKind(file, ent2.get(), ann);
993 11 : writeIdxName(file, ent2->getBase());
994 11 : writeAnnotations(file, ann, ent2->getAnnotations());
995 11 : break;
996 : }
997 : }
998 : }
999 7303 : for (std::map< OUString, Item >::iterator i(map.begin()); i != map.end();
1000 : ++i)
1001 : {
1002 6676 : i->second.nameOffset = writeNulName(file, i->first);
1003 : }
1004 627 : sal_uInt64 off = getOffset(file);
1005 627 : if (rootSize == 0) {
1006 329 : write8(file, 0); // SORT_MODULE
1007 329 : write32(file, map.size());
1008 : // overflow from std::map::size_type -> sal_uInt64 is unrealistic
1009 : } else {
1010 298 : *rootSize = map.size();
1011 : // overflow from std::map::size_type -> std::size_t is unrealistic
1012 : }
1013 7303 : for (std::map< OUString, Item >::iterator i(map.begin()); i != map.end();
1014 : ++i)
1015 : {
1016 6676 : write32(file, i->second.nameOffset);
1017 6676 : write32(file, i->second.dataOffset);
1018 : }
1019 627 : return off;
1020 : }
1021 :
1022 : }
1023 :
1024 1336 : SAL_IMPLEMENT_MAIN() {
1025 : try {
1026 668 : sal_uInt32 args = rtl_getAppCommandArgCount();
1027 668 : if (args == 0) {
1028 0 : badUsage();
1029 : }
1030 668 : rtl::Reference< unoidl::Manager > mgr(new unoidl::Manager);
1031 668 : bool entities = false;
1032 1336 : rtl::Reference< unoidl::Provider > prov;
1033 1336 : std::map< OUString, Item > map;
1034 1640 : for (sal_uInt32 i = 0; i != args - 1; ++i) {
1035 : assert(args > 1);
1036 1342 : OUString uri(getArgumentUri(i, i == args - 2 ? &entities : 0));
1037 1342 : if (entities) {
1038 5 : mapEntities(mgr, uri, map);
1039 : } else {
1040 : try {
1041 1337 : prov = mgr->addProvider(uri);
1042 0 : } catch (unoidl::NoSuchFileException &) {
1043 : std::cerr
1044 0 : << "Input <" << uri << "> does not exist" << std::endl;
1045 0 : std::exit(EXIT_FAILURE);
1046 : }
1047 : }
1048 1342 : }
1049 298 : if (!entities) {
1050 : mapCursor(
1051 293 : (prov.is()
1052 293 : ? prov->createRootCursor()
1053 : : rtl::Reference< unoidl::MapCursor >()),
1054 586 : map);
1055 : }
1056 596 : osl::File f(getArgumentUri(args - 1, 0));
1057 298 : osl::FileBase::RC e = f.open(osl_File_OpenFlag_Write);
1058 298 : if (e == osl::FileBase::E_NOENT) {
1059 11 : e = f.open(osl_File_OpenFlag_Write | osl_File_OpenFlag_Create);
1060 : }
1061 298 : if (e != osl::FileBase::E_None) {
1062 : std::cerr
1063 0 : << "Cannot open <" << f.getURL() << "> for writing, error code "
1064 0 : << +e << std::endl;
1065 0 : std::exit(EXIT_FAILURE);
1066 : }
1067 298 : write(f, "UNOIDL\xFF\0", 8);
1068 298 : write32(f, 0); // root map offset
1069 298 : write32(f, 0); // root map size
1070 : write(
1071 : f,
1072 298 : RTL_CONSTASCII_STRINGPARAM(
1073 : "\0** Created by LibreOffice " LIBO_VERSION_DOTTED
1074 298 : " unoidl-write **\0"));
1075 : std::size_t size;
1076 298 : sal_uInt64 off = writeMap(f, map, &size);
1077 298 : e = f.setSize(getOffset(f)); // truncate in case it already existed
1078 298 : if (e != osl::FileBase::E_None) {
1079 : std::cerr
1080 0 : << "Cannot set size of <" << f.getURL() << ">, error code "
1081 0 : << +e << std::endl;
1082 0 : std::exit(EXIT_FAILURE);
1083 : }
1084 298 : e = f.setPos(osl_Pos_Absolut, 8);
1085 298 : if (e != osl::FileBase::E_None) {
1086 : std::cerr
1087 0 : << "Cannot rewind current position in <" << f.getURL()
1088 0 : << ">, error code " << +e << std::endl;
1089 0 : std::exit(EXIT_FAILURE);
1090 : }
1091 298 : write32(f, off);
1092 298 : write32(f, size);
1093 : // overflow from std::map::size_type -> sal_uInt64 is unrealistic
1094 298 : e = f.close();
1095 298 : if (e != osl::FileBase::E_None) {
1096 : std::cerr
1097 0 : << "Cannot close <" << f.getURL()
1098 0 : << "> after writing, error code " << +e << std::endl;
1099 0 : std::exit(EXIT_FAILURE);
1100 : }
1101 966 : return EXIT_SUCCESS;
1102 0 : } catch (unoidl::FileFormatException & e1) {
1103 : std::cerr
1104 740 : << "Bad input <" << e1.getUri() << ">: " << e1.getDetail()
1105 370 : << std::endl;
1106 370 : std::exit(EXIT_FAILURE);
1107 : }
1108 2004 : }
1109 :
1110 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|