docs/cut

annotate code/cut.c__gnu.1992-11-08 @ 31:106609b64dc4

minor corrections and improvements in the text
author markus schnalke <meillo@marmaro.de>
date Tue, 15 Sep 2015 17:20:20 +0200
parents
children
rev   line source
meillo@14 1 /* cut - remove parts of lines of files
meillo@14 2 Copyright (C) 1984 by David M. Ihnat
meillo@14 3
meillo@14 4 This program is a total rewrite of the Bell Laboratories Unix(Tm)
meillo@14 5 command of the same name, as of System V. It contains no proprietary
meillo@14 6 code, and therefore may be used without violation of any proprietary
meillo@14 7 agreements whatsoever. However, you will notice that the program is
meillo@14 8 copyrighted by me. This is to assure the program does *not* fall
meillo@14 9 into the public domain. Thus, I may specify just what I am now:
meillo@14 10 This program may be freely copied and distributed, provided this notice
meillo@14 11 remains; it may not be sold for profit without express written consent of
meillo@14 12 the author.
meillo@14 13 Please note that I recreated the behavior of the Unix(Tm) 'cut' command
meillo@14 14 as faithfully as possible; however, I haven't run a full set of regression
meillo@14 15 tests. Thus, the user of this program accepts full responsibility for any
meillo@14 16 effects or loss; in particular, the author is not responsible for any losses,
meillo@14 17 explicit or incidental, that may be incurred through use of this program.
meillo@14 18
meillo@14 19 I ask that any bugs (and, if possible, fixes) be reported to me when
meillo@14 20 possible. -David Ihnat (312) 784-4544 ignatz@homebru.chi.il.us
meillo@14 21
meillo@14 22 POSIX changes, bug fixes, long-named options, and cleanup
meillo@14 23 by David MacKenzie <djm@ai.mit.edu>.
meillo@14 24
meillo@14 25 Options:
meillo@14 26 --bytes=byte-list
meillo@14 27 -b byte-list Print only the bytes in positions listed
meillo@14 28 in BYTE-LIST.
meillo@14 29 Tabs and backspaces are treated like any
meillo@14 30 other character; they take up 1 byte.
meillo@14 31
meillo@14 32 --characters=character-list
meillo@14 33 -c character-list Print only characters in positions listed
meillo@14 34 in CHARACTER-LIST.
meillo@14 35 The same as -b for now, but
meillo@14 36 internationalization will change that.
meillo@14 37 Tabs and backspaces are treated like any
meillo@14 38 other character; they take up 1 character.
meillo@14 39
meillo@14 40 --fields=field-list
meillo@14 41 -f field-list Print only the fields listed in FIELD-LIST.
meillo@14 42 Fields are separated by a TAB by default.
meillo@14 43
meillo@14 44 --delimiter=delim
meillo@14 45 -d delim For -f, fields are separated by the first
meillo@14 46 character in DELIM instead of TAB.
meillo@14 47
meillo@14 48 -n Do not split multibyte chars (no-op for now).
meillo@14 49
meillo@14 50 --only-delimited
meillo@14 51 -s For -f, do not print lines that do not contain
meillo@14 52 the field separator character.
meillo@14 53
meillo@14 54 The BYTE-LIST, CHARACTER-LIST, and FIELD-LIST are one or more numbers
meillo@14 55 or ranges separated by commas. The first byte, character, and field
meillo@14 56 are numbered 1.
meillo@14 57
meillo@14 58 A FILE of `-' means standard input. */
meillo@14 59
meillo@14 60 #define _GNU_SOURCE
meillo@14 61 #include <ctype.h>
meillo@14 62 #ifndef isblank
meillo@14 63 #define isblank(c) ((c) == ' ' || (c) == '\t')
meillo@14 64 #endif
meillo@14 65 #include <stdio.h>
meillo@14 66 #include <getopt.h>
meillo@14 67 #include <sys/types.h>
meillo@14 68 #include "system.h"
meillo@14 69
meillo@14 70 #ifdef isascii
meillo@14 71 #define ISDIGIT(c) (isascii ((c)) && isdigit ((c)))
meillo@14 72 #else
meillo@14 73 #define ISDIGIT(c) (isdigit ((c)))
meillo@14 74 #endif
meillo@14 75
meillo@14 76 char *xmalloc ();
meillo@14 77 char *xrealloc ();
meillo@14 78 int set_fields ();
meillo@14 79 int cut_file ();
meillo@14 80 void cut_stream ();
meillo@14 81 void cut_bytes ();
meillo@14 82 void cut_fields ();
meillo@14 83 void enlarge_line ();
meillo@14 84 void error ();
meillo@14 85 void invalid_list ();
meillo@14 86 void usage ();
meillo@14 87
meillo@14 88 /* The number of elements allocated for the input line
meillo@14 89 and the byte or field number.
meillo@14 90 Enlarged as necessary. */
meillo@14 91 int line_size;
meillo@14 92
meillo@14 93 /* Processed output buffer. */
meillo@14 94 char *outbuf;
meillo@14 95
meillo@14 96 /* Where to save next char to output. */
meillo@14 97 char *outbufptr;
meillo@14 98
meillo@14 99 /* Raw line buffer for field mode. */
meillo@14 100 char *inbuf;
meillo@14 101
meillo@14 102 /* Where to save next input char. */
meillo@14 103 char *inbufptr;
meillo@14 104
meillo@14 105 /* What can be done about a byte or field. */
meillo@14 106 enum field_action
meillo@14 107 {
meillo@14 108 field_omit,
meillo@14 109 field_output
meillo@14 110 };
meillo@14 111
meillo@14 112 /* In byte mode, which bytes to output.
meillo@14 113 In field mode, which `delim'-separated fields to output.
meillo@14 114 Both bytes and fields are numbered starting with 1,
meillo@14 115 so the first element of `fields' is unused. */
meillo@14 116 enum field_action *fields;
meillo@14 117
meillo@14 118 enum operating_mode
meillo@14 119 {
meillo@14 120 undefined_mode,
meillo@14 121
meillo@14 122 /* Output characters that are in the given bytes. */
meillo@14 123 byte_mode,
meillo@14 124
meillo@14 125 /* Output the given delimeter-separated fields. */
meillo@14 126 field_mode
meillo@14 127 };
meillo@14 128
meillo@14 129 enum operating_mode operating_mode;
meillo@14 130
meillo@14 131 /* If nonzero,
meillo@14 132 for field mode, do not output lines containing no delimeter characters. */
meillo@14 133 int delimited_lines_only;
meillo@14 134
meillo@14 135 /* The delimeter character for field mode. */
meillo@14 136 unsigned char delim;
meillo@14 137
meillo@14 138 /* Nonzero if we have ever read standard input. */
meillo@14 139 int have_read_stdin;
meillo@14 140
meillo@14 141 /* The name this program was run with. */
meillo@14 142 char *program_name;
meillo@14 143
meillo@14 144 struct option longopts[] =
meillo@14 145 {
meillo@14 146 {"bytes", 1, 0, 'b'},
meillo@14 147 {"characters", 1, 0, 'c'},
meillo@14 148 {"fields", 1, 0, 'f'},
meillo@14 149 {"delimiter", 1, 0, 'd'},
meillo@14 150 {"only-delimited", 0, 0, 's'},
meillo@14 151 {0, 0, 0, 0}
meillo@14 152 };
meillo@14 153
meillo@14 154 void
meillo@14 155 main (argc, argv)
meillo@14 156 int argc;
meillo@14 157 char **argv;
meillo@14 158 {
meillo@14 159 int optc, exit_status = 0;
meillo@14 160
meillo@14 161 program_name = argv[0];
meillo@14 162
meillo@14 163 line_size = 512;
meillo@14 164 operating_mode = undefined_mode;
meillo@14 165 delimited_lines_only = 0;
meillo@14 166 delim = '\0';
meillo@14 167 have_read_stdin = 0;
meillo@14 168
meillo@14 169 fields = (enum field_action *)
meillo@14 170 xmalloc (line_size * sizeof (enum field_action));
meillo@14 171 outbuf = (char *) xmalloc (line_size);
meillo@14 172 inbuf = (char *) xmalloc (line_size);
meillo@14 173
meillo@14 174 for (optc = 0; optc < line_size; optc++)
meillo@14 175 fields[optc] = field_omit;
meillo@14 176
meillo@14 177 while ((optc = getopt_long (argc, argv, "b:c:d:f:ns", longopts, (int *) 0))
meillo@14 178 != EOF)
meillo@14 179 {
meillo@14 180 switch (optc)
meillo@14 181 {
meillo@14 182 case 'b':
meillo@14 183 case 'c':
meillo@14 184 /* Build the byte list. */
meillo@14 185 if (operating_mode != undefined_mode)
meillo@14 186 usage ();
meillo@14 187 operating_mode = byte_mode;
meillo@14 188 if (set_fields (optarg) == 0)
meillo@14 189 error (2, 0, "no fields given");
meillo@14 190 break;
meillo@14 191
meillo@14 192 case 'f':
meillo@14 193 /* Build the field list. */
meillo@14 194 if (operating_mode != undefined_mode)
meillo@14 195 usage ();
meillo@14 196 operating_mode = field_mode;
meillo@14 197 if (set_fields (optarg) == 0)
meillo@14 198 error (2, 0, "no fields given");
meillo@14 199 break;
meillo@14 200
meillo@14 201 case 'd':
meillo@14 202 /* New delimiter. */
meillo@14 203 if (optarg[0] == '\0')
meillo@14 204 error (2, 0, "no delimiter given");
meillo@14 205 if (optarg[1] != '\0')
meillo@14 206 error (2, 0, "delimiter must be a single character");
meillo@14 207 delim = optarg[0];
meillo@14 208 break;
meillo@14 209
meillo@14 210 case 'n':
meillo@14 211 break;
meillo@14 212
meillo@14 213 case 's':
meillo@14 214 delimited_lines_only++;
meillo@14 215 break;
meillo@14 216
meillo@14 217 default:
meillo@14 218 usage ();
meillo@14 219 }
meillo@14 220 }
meillo@14 221
meillo@14 222 if (operating_mode == undefined_mode)
meillo@14 223 usage ();
meillo@14 224
meillo@14 225 if ((delimited_lines_only || delim != '\0') && operating_mode != field_mode)
meillo@14 226 usage ();
meillo@14 227
meillo@14 228 if (delim == '\0')
meillo@14 229 delim = '\t';
meillo@14 230
meillo@14 231 if (optind == argc)
meillo@14 232 exit_status |= cut_file ("-");
meillo@14 233 else
meillo@14 234 for (; optind < argc; optind++)
meillo@14 235 exit_status |= cut_file (argv[optind]);
meillo@14 236
meillo@14 237 if (have_read_stdin && fclose (stdin) == EOF)
meillo@14 238 {
meillo@14 239 error (0, errno, "-");
meillo@14 240 exit_status = 1;
meillo@14 241 }
meillo@14 242 if (ferror (stdout) || fclose (stdout) == EOF)
meillo@14 243 error (1, 0, "write error");
meillo@14 244
meillo@14 245 exit (exit_status);
meillo@14 246 }
meillo@14 247
meillo@14 248 /* Select for printing the positions in `fields' that are listed in
meillo@14 249 byte or field specification FIELDSTR. FIELDSTR should be
meillo@14 250 composed of one or more numbers or ranges of numbers, separated by
meillo@14 251 blanks or commas. Incomplete ranges may be given: `-m' means
meillo@14 252 `1-m'; `n-' means `n' through end of line or last field.
meillo@14 253
meillo@14 254 Return the number of fields selected. */
meillo@14 255
meillo@14 256 int
meillo@14 257 set_fields (fieldstr)
meillo@14 258 char *fieldstr;
meillo@14 259 {
meillo@14 260 int initial = 1; /* Value of first number in a range. */
meillo@14 261 int dash_found = 0; /* Nonzero if a '-' is found in this field. */
meillo@14 262 int value = 0; /* If nonzero, a number being accumulated. */
meillo@14 263 int fields_selected = 0; /* Number of fields selected so far. */
meillo@14 264 /* If nonzero, index of first field in a range that goes to end of line. */
meillo@14 265 int eol_range_start = 0;
meillo@14 266
meillo@14 267 for (;;)
meillo@14 268 {
meillo@14 269 if (*fieldstr == '-')
meillo@14 270 {
meillo@14 271 /* Starting a range. */
meillo@14 272 if (dash_found)
meillo@14 273 invalid_list ();
meillo@14 274 dash_found++;
meillo@14 275 fieldstr++;
meillo@14 276
meillo@14 277 if (value)
meillo@14 278 {
meillo@14 279 if (value >= line_size)
meillo@14 280 enlarge_line (value);
meillo@14 281 initial = value;
meillo@14 282 value = 0;
meillo@14 283 }
meillo@14 284 else
meillo@14 285 initial = 1;
meillo@14 286 }
meillo@14 287 else if (*fieldstr == ',' || isblank (*fieldstr) || *fieldstr == '\0')
meillo@14 288 {
meillo@14 289 /* Ending the string, or this field/byte sublist. */
meillo@14 290 if (dash_found)
meillo@14 291 {
meillo@14 292 dash_found = 0;
meillo@14 293
meillo@14 294 /* A range. Possibilites: -n, m-n, n-.
meillo@14 295 In any case, `initial' contains the start of the range. */
meillo@14 296 if (value == 0)
meillo@14 297 {
meillo@14 298 /* `n-'. From `initial' to end of line. */
meillo@14 299 eol_range_start = initial;
meillo@14 300 fields_selected++;
meillo@14 301 }
meillo@14 302 else
meillo@14 303 {
meillo@14 304 /* `m-n' or `-n' (1-n). */
meillo@14 305 if (value < initial)
meillo@14 306 invalid_list ();
meillo@14 307
meillo@14 308 if (value >= line_size)
meillo@14 309 enlarge_line (value);
meillo@14 310
meillo@14 311 /* Is there already a range going to end of line? */
meillo@14 312 if (eol_range_start != 0)
meillo@14 313 {
meillo@14 314 /* Yes. Is the new sequence already contained
meillo@14 315 in the old one? If so, no processing is
meillo@14 316 necessary. */
meillo@14 317 if (initial < eol_range_start)
meillo@14 318 {
meillo@14 319 /* No, the new sequence starts before the
meillo@14 320 old. Does the old range going to end of line
meillo@14 321 extend into the new range? */
meillo@14 322 if (eol_range_start < value)
meillo@14 323 /* Yes. Simply move the end of line marker. */
meillo@14 324 eol_range_start = initial;
meillo@14 325 else
meillo@14 326 {
meillo@14 327 /* No. A simple range, before and disjoint from
meillo@14 328 the range going to end of line. Fill it. */
meillo@14 329 for (; initial <= value; initial++)
meillo@14 330 fields[initial] = field_output;
meillo@14 331 }
meillo@14 332
meillo@14 333 /* In any case, some fields were selected. */
meillo@14 334 fields_selected++;
meillo@14 335 }
meillo@14 336 }
meillo@14 337 else
meillo@14 338 {
meillo@14 339 /* There is no range going to end of line. */
meillo@14 340 for (; initial <= value; initial++)
meillo@14 341 fields[initial] = field_output;
meillo@14 342 fields_selected++;
meillo@14 343 }
meillo@14 344 value = 0;
meillo@14 345 }
meillo@14 346 }
meillo@14 347 else if (value != 0)
meillo@14 348 {
meillo@14 349 /* A simple field number, not a range. */
meillo@14 350 if (value >= line_size)
meillo@14 351 enlarge_line (value);
meillo@14 352
meillo@14 353 fields[value] = field_output;
meillo@14 354 value = 0;
meillo@14 355 fields_selected++;
meillo@14 356 }
meillo@14 357
meillo@14 358 if (*fieldstr == '\0')
meillo@14 359 {
meillo@14 360 /* If there was a range going to end of line, fill the
meillo@14 361 array from the end of line point. */
meillo@14 362 if (eol_range_start)
meillo@14 363 for (initial = eol_range_start; initial < line_size; initial++)
meillo@14 364 fields[initial] = field_output;
meillo@14 365
meillo@14 366 return fields_selected;
meillo@14 367 }
meillo@14 368
meillo@14 369 fieldstr++;
meillo@14 370 }
meillo@14 371 else if (ISDIGIT (*fieldstr))
meillo@14 372 {
meillo@14 373 value = 10 * value + *fieldstr - '0';
meillo@14 374 fieldstr++;
meillo@14 375 }
meillo@14 376 else
meillo@14 377 invalid_list ();
meillo@14 378 }
meillo@14 379 }
meillo@14 380
meillo@14 381 /* Process file FILE to standard output.
meillo@14 382 Return 0 if successful, 1 if not. */
meillo@14 383
meillo@14 384 int
meillo@14 385 cut_file (file)
meillo@14 386 char *file;
meillo@14 387 {
meillo@14 388 FILE *stream;
meillo@14 389
meillo@14 390 if (!strcmp (file, "-"))
meillo@14 391 {
meillo@14 392 have_read_stdin = 1;
meillo@14 393 stream = stdin;
meillo@14 394 }
meillo@14 395 else
meillo@14 396 {
meillo@14 397 stream = fopen (file, "r");
meillo@14 398 if (stream == NULL)
meillo@14 399 {
meillo@14 400 error (0, errno, "%s", file);
meillo@14 401 return 1;
meillo@14 402 }
meillo@14 403 }
meillo@14 404
meillo@14 405 cut_stream (stream);
meillo@14 406
meillo@14 407 if (ferror (stream))
meillo@14 408 {
meillo@14 409 error (0, errno, "%s", file);
meillo@14 410 return 1;
meillo@14 411 }
meillo@14 412 if (!strcmp (file, "-"))
meillo@14 413 clearerr (stream); /* Also clear EOF. */
meillo@14 414 else if (fclose (stream) == EOF)
meillo@14 415 {
meillo@14 416 error (0, errno, "%s", file);
meillo@14 417 return 1;
meillo@14 418 }
meillo@14 419 return 0;
meillo@14 420 }
meillo@14 421
meillo@14 422 void
meillo@14 423 cut_stream (stream)
meillo@14 424 FILE *stream;
meillo@14 425 {
meillo@14 426 if (operating_mode == byte_mode)
meillo@14 427 cut_bytes (stream);
meillo@14 428 else
meillo@14 429 cut_fields (stream);
meillo@14 430 }
meillo@14 431
meillo@14 432 /* Print the file open for reading on stream STREAM
meillo@14 433 with the bytes marked `field_omit' in `fields' removed from each line. */
meillo@14 434
meillo@14 435 void
meillo@14 436 cut_bytes (stream)
meillo@14 437 FILE *stream;
meillo@14 438 {
meillo@14 439 register int c; /* Each character from the file. */
meillo@14 440 int doneflag = 0; /* Nonzero if EOF reached. */
meillo@14 441 int char_count; /* Number of chars in the line so far. */
meillo@14 442
meillo@14 443 while (doneflag == 0)
meillo@14 444 {
meillo@14 445 /* Start processing a line. */
meillo@14 446 outbufptr = outbuf;
meillo@14 447 char_count = 0;
meillo@14 448
meillo@14 449 do
meillo@14 450 {
meillo@14 451 c = getc (stream);
meillo@14 452 if (c == EOF)
meillo@14 453 {
meillo@14 454 doneflag++;
meillo@14 455 break;
meillo@14 456 }
meillo@14 457
meillo@14 458 /* If this character is to be sent, stow it in the outbuffer. */
meillo@14 459
meillo@14 460 if (++char_count == line_size - 1)
meillo@14 461 enlarge_line (char_count);
meillo@14 462
meillo@14 463 if (fields[char_count] == field_output || c == '\n')
meillo@14 464 *outbufptr++ = c;
meillo@14 465 }
meillo@14 466 while (c != '\n');
meillo@14 467
meillo@14 468 if (char_count)
meillo@14 469 fwrite (outbuf, sizeof (char), outbufptr - outbuf, stdout);
meillo@14 470 }
meillo@14 471 }
meillo@14 472
meillo@14 473 /* Print the file open for reading on stream STREAM
meillo@14 474 with the fields marked `field_omit' in `fields' removed from each line.
meillo@14 475 All characters are initially stowed in the raw input buffer, until
meillo@14 476 at least one field has been found. */
meillo@14 477
meillo@14 478 void
meillo@14 479 cut_fields (stream)
meillo@14 480 FILE *stream;
meillo@14 481 {
meillo@14 482 register int c; /* Each character from the file. */
meillo@14 483 int doneflag = 0; /* Nonzero if EOF reached. */
meillo@14 484 int char_count; /* Number of chars in line before any delim. */
meillo@14 485 int fieldfound; /* Nonzero if any fields to print found. */
meillo@14 486 int curr_field; /* Current index in `fields'. */
meillo@14 487
meillo@14 488 while (doneflag == 0)
meillo@14 489 {
meillo@14 490 char_count = 0;
meillo@14 491 fieldfound = 0;
meillo@14 492 curr_field = 1;
meillo@14 493 outbufptr = outbuf;
meillo@14 494 inbufptr = inbuf;
meillo@14 495
meillo@14 496 do
meillo@14 497 {
meillo@14 498 c = getc (stream);
meillo@14 499 if (c == EOF)
meillo@14 500 {
meillo@14 501 doneflag++;
meillo@14 502 break;
meillo@14 503 }
meillo@14 504
meillo@14 505 if (fields[curr_field] == field_output && c != '\n')
meillo@14 506 {
meillo@14 507 /* Working on a field. It, and its terminating
meillo@14 508 delimiter, go only into the processed buffer. */
meillo@14 509 fieldfound = 1;
meillo@14 510 if (outbufptr - outbuf == line_size - 2)
meillo@14 511 enlarge_line (outbufptr - outbuf);
meillo@14 512 *outbufptr++ = c;
meillo@14 513 }
meillo@14 514 else if (fieldfound == 0)
meillo@14 515 {
meillo@14 516 if (++char_count == line_size - 1)
meillo@14 517 enlarge_line (char_count);
meillo@14 518 *inbufptr++ = c;
meillo@14 519 }
meillo@14 520
meillo@14 521 if (c == delim && ++curr_field == line_size - 1)
meillo@14 522 enlarge_line (curr_field);
meillo@14 523 }
meillo@14 524 while (c != '\n');
meillo@14 525
meillo@14 526 if (fieldfound)
meillo@14 527 {
meillo@14 528 /* Something was found. Print it. */
meillo@14 529 if (outbufptr[-1] == delim)
meillo@14 530 --outbufptr; /* Suppress trailing delimiter. */
meillo@14 531
meillo@14 532 fwrite (outbuf, sizeof (char), outbufptr - outbuf, stdout);
meillo@14 533 if (c == '\n')
meillo@14 534 putc (c, stdout);
meillo@14 535 }
meillo@14 536 else if (!delimited_lines_only && char_count)
meillo@14 537 /* A line with some characters, no delimiters, and no
meillo@14 538 suppression. Print it. */
meillo@14 539 fwrite (inbuf, sizeof (char), inbufptr - inbuf, stdout);
meillo@14 540 }
meillo@14 541 }
meillo@14 542
meillo@14 543 /* Extend the buffers to accomodate at least NEW_SIZE characters. */
meillo@14 544
meillo@14 545 void
meillo@14 546 enlarge_line (new_size)
meillo@14 547 int new_size;
meillo@14 548 {
meillo@14 549 char *newp;
meillo@14 550 int i;
meillo@14 551
meillo@14 552 new_size += 256; /* Leave some room to grow. */
meillo@14 553
meillo@14 554 fields = (enum field_action *)
meillo@14 555 xrealloc (fields, new_size * sizeof (enum field_action));
meillo@14 556
meillo@14 557 newp = (char *) xrealloc (outbuf, new_size);
meillo@14 558 outbufptr += newp - outbuf;
meillo@14 559 outbuf = newp;
meillo@14 560
meillo@14 561 newp = (char *) xrealloc (inbuf, new_size);
meillo@14 562 inbufptr += newp - inbuf;
meillo@14 563 inbuf = newp;
meillo@14 564
meillo@14 565 for (i = line_size; i < new_size; i++)
meillo@14 566 fields[i] = field_omit;
meillo@14 567 line_size = new_size;
meillo@14 568 }
meillo@14 569
meillo@14 570 void
meillo@14 571 invalid_list ()
meillo@14 572 {
meillo@14 573 error (2, 0, "invalid byte or field list");
meillo@14 574 }
meillo@14 575
meillo@14 576 void
meillo@14 577 usage ()
meillo@14 578 {
meillo@14 579 fprintf (stderr, "\
meillo@14 580 Usage: %s {-b byte-list,--bytes=byte-list} [-n] [file...]\n\
meillo@14 581 %s {-c character-list,--characters=character-list} [file...]\n\
meillo@14 582 %s {-f field-list,--fields=field-list} [-d delim] [-s]\n\
meillo@14 583 [--delimiter=delim] [--only-delimited] [file...]\n",
meillo@14 584 program_name, program_name, program_name);
meillo@14 585 exit (2);
meillo@14 586 }