Add more warnings source to 1.6) and fix warnings
[jvcard.git] / src / be / nikiroo / jvcard / launcher / Main.java
CommitLineData
7da41ecd
NR
1package be.nikiroo.jvcard.launcher;
2
3import java.io.File;
f06c8100 4import java.io.FileInputStream;
7da41ecd 5import java.io.IOException;
f06c8100 6import java.io.InputStream;
7da41ecd 7import java.lang.reflect.Field;
7da41ecd
NR
8import java.net.Socket;
9import java.nio.charset.Charset;
10import java.util.LinkedList;
11import java.util.List;
12
26d254a3
NR
13import javax.imageio.ImageIO;
14
7da41ecd 15import be.nikiroo.jvcard.Card;
26d254a3
NR
16import be.nikiroo.jvcard.Contact;
17import be.nikiroo.jvcard.Data;
18import be.nikiroo.jvcard.TypeInfo;
5ad0e17e 19import be.nikiroo.jvcard.launcher.CardResult.MergeCallback;
e643219c 20import be.nikiroo.jvcard.launcher.Optional.NotSupportedException;
7da41ecd 21import be.nikiroo.jvcard.parsers.Format;
845fb1d7 22import be.nikiroo.jvcard.remote.Command;
7da41ecd 23import be.nikiroo.jvcard.remote.SimpleSocket;
f06c8100
NR
24import be.nikiroo.jvcard.resources.DisplayBundle;
25import be.nikiroo.jvcard.resources.DisplayOption;
26import be.nikiroo.jvcard.resources.RemoteBundle;
27import be.nikiroo.jvcard.resources.StringId;
28import be.nikiroo.jvcard.resources.TransBundle;
29import be.nikiroo.utils.ImageUtils;
30import be.nikiroo.utils.StringUtils;
31import be.nikiroo.utils.Version;
32import be.nikiroo.utils.resources.Bundles;
7da41ecd
NR
33
34/**
35 * This class contains the runnable Main method. It will parse the user supplied
36 * parameters and take action based upon those. Most of the time, it will start
37 * a MainWindow.
38 *
39 * @author niki
f06c8100 40 *
7da41ecd
NR
41 */
42public class Main {
43 static public final String APPLICATION_TITLE = "jVcard";
7da41ecd
NR
44
45 static private final int ERR_NO_FILE = 1;
46 static private final int ERR_SYNTAX = 2;
47 static private final int ERR_INTERNAL = 3;
e119a1c1 48 static private TransBundle transService;
7da41ecd 49
30a4aa17
NR
50 static private String defaultFn;
51 static private boolean forceComputedFn;
52
26d254a3 53 enum Mode {
88eb8122 54 CONTACT_MANAGER, I18N, SERVER, LOAD_PHOTO, SAVE_PHOTO, SAVE_CONFIG, HELP
26d254a3
NR
55 }
56
7da41ecd 57 /**
9b8cb729 58 * Translate the given {@link StringId} into user text.
7da41ecd 59 *
9b8cb729 60 * @param stringId
7da41ecd 61 * the ID to translate
9b8cb729
NR
62 * @param values
63 * the values to insert instead of the place holders in the
64 * translation
7da41ecd 65 *
9b8cb729 66 * @return the translated text with the given value where required
7da41ecd 67 */
e119a1c1 68 static public String trans(StringId id, Object... values) {
59597d59 69 return transService.getString(id, values);
7da41ecd
NR
70 }
71
72 /**
73 * Check if unicode characters should be used.
74 *
75 * @return TRUE to allow unicode
76 */
77 static public boolean isUnicode() {
78 return transService.isUnicode();
79 }
80
81 /**
82 * Start the application.
83 *
84 * <p>
85 * The returned exit codes are:
86 * <ul>
87 * <li>1: no files to open</li>
88 * <li>2: invalid syntax</li>
89 * <li>3: internal error</li>
90 * </ul>
91 * </p>
92 *
93 * @param args
94 * the parameters (see <tt>--help</tt> to know which are
95 * supported)
96 */
97 public static void main(String[] args) {
98 Boolean textMode = null;
99 boolean noMoreParams = false;
100 boolean filesTried = false;
101
102 // get the "system default" language to help translate the --help
103 // message if needed
104 String language = null;
e119a1c1 105 transService = new TransBundle(language);
7da41ecd
NR
106
107 boolean unicode = transService.isUnicode();
26d254a3 108 String dir = null;
7da41ecd 109 List<String> files = new LinkedList<String>();
26d254a3
NR
110 int port = -1;
111 Mode mode = Mode.CONTACT_MANAGER;
112 String format = null;
7da41ecd
NR
113 for (int index = 0; index < args.length; index++) {
114 String arg = args[index];
115 if (!noMoreParams && arg.equals("--")) {
116 noMoreParams = true;
117 } else if (!noMoreParams && arg.equals("--help")) {
88eb8122
NR
118 if (mode != Mode.CONTACT_MANAGER) {
119 SERR(StringId.CLI_SERR_MODES);
120 return;
121 }
122 mode = Mode.HELP;
7da41ecd
NR
123 } else if (!noMoreParams && arg.equals("--tui")) {
124 textMode = true;
125 } else if (!noMoreParams && arg.equals("--gui")) {
126 textMode = false;
127 } else if (!noMoreParams && arg.equals("--noutf")) {
128 unicode = false;
129 transService.setUnicode(unicode);
130 } else if (!noMoreParams && arg.equals("--lang")) {
131 index++;
132 if (index >= args.length) {
88eb8122 133 SERR(StringId.CLI_SERR_NOLANG);
7da41ecd
NR
134 return;
135 }
136
137 language = args[index];
e119a1c1 138 transService = new TransBundle(language);
7da41ecd
NR
139 transService.setUnicode(unicode);
140 } else if (!noMoreParams && arg.equals("--config")) {
141 index++;
142 if (index >= args.length) {
88eb8122 143 SERR(StringId.CLI_SERR_NODIR);
7da41ecd
NR
144 return;
145 }
146
147 Bundles.setDirectory(args[index]);
e119a1c1 148 transService = new TransBundle(language);
7da41ecd 149 transService.setUnicode(unicode);
e119a1c1
NR
150 } else if (!noMoreParams && arg.equals("--save-config")) {
151 index++;
152 if (index >= args.length) {
88eb8122 153 SERR(StringId.CLI_SERR_NODIR);
e119a1c1
NR
154 return;
155 }
156 dir = args[index];
157
158 if (mode != Mode.CONTACT_MANAGER) {
88eb8122 159 SERR(StringId.CLI_SERR_MODES);
e119a1c1
NR
160 return;
161 }
162 mode = Mode.SAVE_CONFIG;
7da41ecd 163 } else if (!noMoreParams && arg.equals("--server")) {
26d254a3 164 if (mode != Mode.CONTACT_MANAGER) {
88eb8122 165 SERR(StringId.CLI_SERR_MODES);
26d254a3
NR
166 return;
167 }
168 mode = Mode.SERVER;
169
7da41ecd
NR
170 index++;
171 if (index >= args.length) {
88eb8122 172 SERR(StringId.CLI_SERR_NOPORT);
7da41ecd
NR
173 return;
174 }
175
176 try {
177 port = Integer.parseInt(args[index]);
178 } catch (NumberFormatException e) {
88eb8122 179 SERR(StringId.CLI_SERR_BADPORT, "" + args[index]);
7da41ecd
NR
180 return;
181 }
182 } else if (!noMoreParams && arg.equals("--i18n")) {
26d254a3 183 if (mode != Mode.CONTACT_MANAGER) {
88eb8122 184 SERR(StringId.CLI_SERR_MODES);
26d254a3
NR
185 return;
186 }
187 mode = Mode.I18N;
188
7da41ecd
NR
189 index++;
190 if (index >= args.length) {
88eb8122 191 SERR(StringId.CLI_SERR_NODIR);
7da41ecd
NR
192 return;
193 }
9b8cb729 194
26d254a3
NR
195 dir = args[index];
196 } else if (!noMoreParams
197 && (arg.equals("--load-photo")
198 || arg.equals("--save-photo") || arg
199 .equals("--only-photo"))) {
200 if (mode != Mode.CONTACT_MANAGER) {
88eb8122 201 SERR(StringId.CLI_SERR_MODES);
26d254a3
NR
202 return;
203 }
204
205 if (arg.equals("--load-photo")) {
206 mode = Mode.LOAD_PHOTO;
207 } else if (arg.equals("--save-photo")) {
208 mode = Mode.SAVE_PHOTO;
26d254a3
NR
209 }
210
211 index++;
212 if (index >= args.length) {
88eb8122 213 SERR(StringId.CLI_SERR_NODIR);
26d254a3
NR
214 return;
215 }
216
217 dir = args[index];
218
219 index++;
220 if (index >= args.length) {
88eb8122 221 SERR(StringId.CLI_SERR_NOFORMAT);
26d254a3
NR
222 return;
223 }
224
225 format = args[index];
7da41ecd
NR
226 } else {
227 filesTried = true;
228 files.addAll(open(arg));
229 }
230 }
9b8cb729 231
f578f3af 232 // Force headless mode if we run in forced-text mode
26d254a3 233 if (mode != Mode.CONTACT_MANAGER || (textMode != null && textMode)) {
f578f3af
NR
234 // same as -Djava.awt.headless=true
235 System.setProperty("java.awt.headless", "true");
236 }
7da41ecd
NR
237
238 if (unicode) {
239 utf8();
240 }
241
30a4aa17
NR
242 // N/FN fix information:
243 readNFN();
244
7da41ecd 245 // Error management:
26d254a3 246 if (mode == Mode.SERVER && files.size() > 0) {
88eb8122
NR
247 SERR(StringId.CLI_SERR_NOLANG, "--server");
248 return;
26d254a3 249 } else if (mode == Mode.I18N && files.size() > 0) {
88eb8122
NR
250 SERR(StringId.CLI_SERR_NOLANG, "--i18n");
251 return;
26d254a3 252 } else if (mode == Mode.I18N && language == null) {
88eb8122 253 SERR(StringId.CLI_SERR_NOLANG);
26d254a3
NR
254 } else if ((mode == Mode.CONTACT_MANAGER || mode == Mode.SAVE_PHOTO || mode == Mode.LOAD_PHOTO)
255 && files.size() == 0) {
7da41ecd
NR
256 if (files.size() == 0 && !filesTried) {
257 files.addAll(open("."));
258 }
259
260 if (files.size() == 0) {
88eb8122 261 ERR(StringId.CLI_ERR, StringId.CLI_ERR_NOFILES, ERR_NO_FILE);
7da41ecd
NR
262 return;
263 }
264 }
265 //
266
26d254a3 267 switch (mode) {
e119a1c1
NR
268 case SAVE_CONFIG: {
269 try {
270 if (!new File(dir).isDirectory()) {
271 if (!new File(dir).mkdir()) {
88eb8122
NR
272 System.err.println(trans(
273 StringId.CLI_ERR_CANNOT_CREATE_CONFDIR, dir));
e119a1c1
NR
274 }
275 }
276
e27d1404 277 new TransBundle().updateFile(dir); // default locale
f06c8100 278 for (String lang : new TransBundle().getKnownLanguages()) {
e27d1404
NR
279 new TransBundle(lang).updateFile(dir);
280 }
281
f06c8100 282 // new UIColors().updateFile(dir);
e119a1c1
NR
283 new DisplayBundle().updateFile(dir);
284 new RemoteBundle().updateFile(dir);
285 } catch (IOException e) {
286 e.printStackTrace();
59597d59 287 System.err.flush();
e119a1c1
NR
288 System.exit(ERR_INTERNAL);
289 }
290 break;
291 }
26d254a3 292 case SERVER: {
7da41ecd 293 try {
02b341aa 294 Optional.runServer(port);
e643219c
NR
295 } catch (IOException e) {
296 ERR(StringId.CLI_ERR, StringId.CLI_ERR_CANNOT_START,
297 ERR_INTERNAL);
298 return;
299 } catch (NotSupportedException e) {
300 if (!e.isCompiledIn()) {
88eb8122
NR
301 ERR(StringId.CLI_ERR, StringId.CLI_ERR_NO_REMOTING,
302 ERR_INTERNAL);
303 return;
7da41ecd 304 }
d5260eeb
NR
305 e.printStackTrace();
306 ERR(StringId.CLI_ERR, StringId.CLI_ERR, ERR_INTERNAL);
307 return;
7da41ecd 308 }
26d254a3
NR
309 break;
310 }
311 case I18N: {
7da41ecd 312 try {
e119a1c1 313 transService.updateFile(dir);
7da41ecd 314 } catch (IOException e) {
d459d7e1
NR
315 ERR(StringId.CLI_ERR, StringId.CLI_ERR_CANNOT_CREATE_LANG,
316 ERR_INTERNAL);
317 return;
7da41ecd 318 }
26d254a3
NR
319 break;
320 }
26d254a3
NR
321 case LOAD_PHOTO: {
322 for (String file : files) {
323 try {
324 Card card = getCard(file, null).getCard();
325 for (Contact contact : card) {
326 String filename = contact.toString(format, "");
f29274a7 327 File f = new File(dir, filename);
26d254a3
NR
328
329 if (f.exists()) {
e3fe9834 330 System.out.println("Loading " + f);
26d254a3 331 try {
a1783d00
NR
332 String type = "jpeg";
333 int dotIndex = filename.indexOf('.');
334 if (dotIndex >= 0
335 && (dotIndex + 1) < filename.length()) {
336 type = filename.substring(dotIndex + 1)
337 .toLowerCase();
338 }
339
f06c8100
NR
340 String b64;
341 InputStream in = null;
342 try {
343 in = new FileInputStream(f);
344 b64 = ImageUtils.toBase64(in);
345 } finally {
346 if (in != null) {
347 in.close();
348 }
349 }
26d254a3 350
88eb8122
NR
351 // remove previous photos:
352 for (Data photo = contact
353 .getPreferredData("PHOTO"); photo != null; photo = contact
354 .getPreferredData("PHOTO")) {
355 photo.delete();
26d254a3 356 }
88eb8122 357 //
26d254a3
NR
358
359 List<TypeInfo> types = new LinkedList<TypeInfo>();
360 types.add(new TypeInfo("ENCODING", "b"));
a1783d00 361 types.add(new TypeInfo("TYPE", type));
26d254a3
NR
362 Data photo = new Data(types, "PHOTO", b64, null);
363 contact.add(photo);
364 } catch (IOException e) {
365 System.err.println("Cannot read photo: "
366 + filename);
367 }
368 }
369 }
370 card.save();
371 } catch (IOException e) {
88eb8122
NR
372 System.err
373 .println(trans(StringId.CLI_ERR_CANNOT_OPEN, file));
26d254a3
NR
374 }
375 }
376 break;
377 }
378 case SAVE_PHOTO: {
379 for (String file : files) {
380 try {
381 Card card = getCard(file, null).getCard();
382 for (Contact contact : card) {
383 Data photo = contact.getPreferredData("PHOTO");
384 if (photo != null) {
385 String filename = contact.toString(format, "");
386 File f = new File(dir, filename + ".png");
e3fe9834 387 System.out.println("Saving " + f);
26d254a3
NR
388 try {
389 ImageIO.write(
f06c8100 390 ImageUtils.fromBase64(photo.getValue()),
26d254a3
NR
391 "png", f);
392 } catch (IOException e) {
88eb8122
NR
393 System.err.println(trans(
394 StringId.CLI_ERR_CANNOT_SAVE_PHOTO,
395 contact.getPreferredDataValue("FN")));
26d254a3
NR
396 }
397 }
398 }
399 } catch (IOException e) {
88eb8122
NR
400 System.err
401 .println(trans(StringId.CLI_ERR_CANNOT_OPEN, file));
26d254a3
NR
402 }
403 }
404 break;
405 }
406 case CONTACT_MANAGER: {
7da41ecd 407 try {
02b341aa 408 Optional.startTui(textMode, files);
e643219c
NR
409 } catch (IOException e) {
410 ERR(StringId.CLI_ERR, StringId.CLI_ERR_CANNOT_START,
411 ERR_NO_FILE);
412 return;
413 } catch (NotSupportedException e) {
414 if (!e.isCompiledIn()) {
88eb8122
NR
415 ERR(StringId.CLI_ERR, StringId.CLI_ERR_NO_TUI, ERR_INTERNAL);
416 return;
7da41ecd 417 }
d5260eeb
NR
418 e.printStackTrace();
419 ERR(StringId.CLI_ERR, StringId.CLI_ERR, ERR_INTERNAL);
420 return;
7da41ecd 421 }
26d254a3
NR
422 break;
423 }
88eb8122 424 case HELP: {
f06c8100
NR
425 System.out.println(APPLICATION_TITLE + " "
426 + Version.getCurrentVersion());
88eb8122
NR
427 System.out.println();
428
429 System.out.println(trans(StringId.CLI_HELP));
430 System.out.println();
431
432 System.out.println(trans(StringId.CLI_HELP_MODES));
433 System.out.println("\t--help : "
434 + trans(StringId.CLI_HELP_MODE_HELP));
435 System.out.println("\t(--tui|--gui) (--noutf) ... : "
436 + trans(StringId.CLI_HELP_MODE_CONTACT_MANAGER));
437 System.out.println("\t--server PORT ... : "
438 + trans(StringId.CLI_HELP_MODE_SERVER));
439 System.out.println("\t--save-config DIR : "
440 + trans(StringId.CLI_HELP_MODE_SAVE_CONFIG));
441 System.out.println("\t--i18n DIR ---lang LANG : "
442 + trans(StringId.CLI_HELP_MODE_I18N));
443 System.out.println("\t--load-photo DIR FORMAT ... : "
444 + trans(StringId.CLI_HELP_MODE_LOAD_PHOTO));
445 System.out.println("\t--save-photo DIR FORMAT ... : "
446 + trans(StringId.CLI_HELP_MODE_SAVE_PHOTO));
447 System.out.println();
448
449 System.out.println(trans(StringId.CLI_HELP_OPTIONS));
450 System.out.println("\t-- : " + trans(StringId.CLI_HELP_DD));
451 System.out.println("\t--lang LANG : "
452 + trans(StringId.CLI_HELP_LANG));
453 System.out.println("\t--tui : " + trans(StringId.CLI_HELP_TUI));
454 System.out.println("\t--gui : " + trans(StringId.CLI_HELP_GUI));
99f631de
NR
455 System.out.println("\t--noutf : "
456 + trans(StringId.CLI_HELP_NOUTF_OPTION));
88eb8122
NR
457 System.out.println("\t--config : "
458 + trans(StringId.CLI_HELP_CONFIG));
459 System.out.println();
460
461 System.out.println(trans(StringId.CLI_HELP_FOOTER));
462 System.out.println();
463
464 }
7da41ecd
NR
465 }
466 }
467
468 /**
469 * Return the {@link Card} corresponding to the given resource name -- a
30a4aa17
NR
470 * file or a remote jvcard URL.
471 *
472 * <p>
473 * Will also fix the FN if required (see display.properties).
474 * </p>
7da41ecd
NR
475 *
476 * @param input
477 * a filename or a remote jvcard url with named resource (e.g.:
478 * <tt>jvcard://localhost:4444/coworkers.vcf</tt>)
5ad0e17e
NR
479 * @param callback
480 * the {@link MergeCallback} to call in case of conflict, or NULL
481 * to disallow conflict management (the {@link Card} will not be
482 * allowed to synchronise in case of conflicts)
7da41ecd
NR
483 *
484 * @return the {@link Card}
485 *
486 * @throws IOException
487 * in case of IO error or remoting not available
488 */
5ad0e17e
NR
489 static public CardResult getCard(String input, MergeCallback callback)
490 throws IOException {
7da41ecd
NR
491 boolean remote = false;
492 Format format = Format.Abook;
493 String ext = input;
494 if (ext.contains(".")) {
495 String tab[] = ext.split("\\.");
496 if (tab.length > 1 && tab[tab.length - 1].equalsIgnoreCase("vcf")) {
497 format = Format.VCard21;
498 }
499 }
500
501 if (input.contains("://")) {
502 format = Format.VCard21;
503 remote = true;
504 }
505
5ad0e17e 506 CardResult card = null;
7da41ecd
NR
507 try {
508 if (remote) {
5ad0e17e 509 card = Optional.syncCard(input, callback);
7da41ecd 510 } else {
5ad0e17e
NR
511 card = new CardResult(new Card(new File(input), format), false,
512 false, false);
7da41ecd
NR
513 }
514 } catch (IOException ioe) {
515 throw ioe;
e643219c 516 } catch (NotSupportedException e) {
4298276a 517 throw new IOException("Remoting support not available", e);
7da41ecd
NR
518 }
519
30a4aa17
NR
520 // Fix the FN value
521 if (defaultFn != null) {
522 try {
523 for (Contact contact : card.getCard()) {
524 Data name = contact.getPreferredData("FN");
525 if (name == null || name.getValue().length() == 0
526 || forceComputedFn) {
99f631de 527 name.setValue(contact.toString(defaultFn, "").trim());
30a4aa17
NR
528 }
529 }
530 } catch (Exception e) {
531 // sync failed -> getCard() throws.
532 // do not update.
533 }
534 }
535
7da41ecd
NR
536 return card;
537 }
538
7da41ecd
NR
539 /**
540 * Open the given path and add all its files if it is a directory or just
541 * this one if not to the returned list.
542 *
543 * @param path
544 * the path to open
545 *
546 * @return the list of opened files
547 */
548 static private List<String> open(String path) {
549 List<String> files = new LinkedList<String>();
550
551 if (path != null && path.startsWith("jvcard://")) {
552 if (path.endsWith("/")) {
553 files.addAll(list(path));
554 } else {
555 files.add(path);
556 }
557 } else {
558 File file = new File(path);
559 if (file.exists()) {
560 if (file.isDirectory()) {
561 for (File subfile : file.listFiles()) {
562 if (!subfile.isDirectory())
563 files.add(subfile.getAbsolutePath());
564 }
565 } else {
566 files.add(file.getAbsolutePath());
567 }
568 } else {
569 System.err.println("File or directory not found: \"" + path
570 + "\"");
571 }
572 }
573
574 return files;
575 }
576
577 /**
578 * List all the available {@link Card}s on the given network location (which
579 * is expected to be a jVCard remote server, obviously).
580 *
581 * @param path
582 * the jVCard remote server path (e.g.:
583 * <tt>jvcard://localhost:4444/</tt>)
584 *
585 * @return the list of {@link Card}s
586 */
587 static private List<String> list(String path) {
588 List<String> files = new LinkedList<String>();
589
590 try {
591 String host = path.split("\\:")[1].substring(2);
592 int port = Integer.parseInt(path.split("\\:")[2].replaceAll("/$",
593 ""));
594 SimpleSocket s = new SimpleSocket(new Socket(host, port),
595 "sync client");
596 s.open(true);
597
845fb1d7 598 s.sendCommand(Command.LIST_CARD);
7da41ecd
NR
599 for (String p : s.receiveBlock()) {
600 files.add(path
601 + p.substring(StringUtils.fromTime(0).length() + 1));
602 }
603 s.close();
604 } catch (Exception e) {
605 e.printStackTrace();
606 }
607
608 return files;
609 }
610
611 /**
612 * Really, really ask for UTF-8 encoding.
613 */
614 static private void utf8() {
615 try {
616 System.setProperty("file.encoding", "UTF-8");
617 Field charset = Charset.class.getDeclaredField("defaultCharset");
618 charset.setAccessible(true);
619 charset.set(null, null);
620 } catch (SecurityException e) {
621 } catch (NoSuchFieldException e) {
622 } catch (IllegalArgumentException e) {
623 } catch (IllegalAccessException e) {
624 }
625 }
30a4aa17
NR
626
627 /**
628 * Read display.properties to know if we should fix the FN field when empty,
629 * or always, or never.
630 */
631 static private void readNFN() {
e119a1c1 632 DisplayBundle map = new DisplayBundle();
30a4aa17 633
e119a1c1
NR
634 defaultFn = map.getString(DisplayOption.CONTACT_DETAILS_DEFAULT_FN);
635
636 forceComputedFn = map.getBoolean(
637 DisplayOption.CONTACT_DETAILS_SHOW_COMPUTED_FN, false);
30a4aa17 638 }
88eb8122
NR
639
640 /**
641 * Syntax error detected, closing the application with an error message.
642 *
643 * @param err
644 * the syntax error case
645 */
646 static private void SERR(StringId err, Object... values) {
647 ERR(StringId.CLI_SERR, err, ERR_SYNTAX, values);
648 }
649
650 /**
651 * Error detected, closing the application with an error message.
652 *
653 * @param err
654 * the error case
655 * @param suberr
656 * the suberror or NULL if none
657 * @param CODE
658 * the error code as declared above
659 */
660 static private void ERR(StringId err, StringId suberr, int CODE,
661 Object... subvalues) {
662 if (suberr == null)
663 System.err.println(trans(err));
664 else
665 System.err.println(trans(err, trans(suberr, subvalues)));
666
59597d59 667 System.err.flush();
88eb8122
NR
668 System.exit(CODE);
669 }
7da41ecd 670}