| 1 | /* $Id$ */ |
| 2 | |
| 3 | /* |
| 4 | * Copyright (c) 1990-1997 Sam Leffler |
| 5 | * Copyright (c) 1991-1997 Silicon Graphics, Inc. |
| 6 | * |
| 7 | * Permission to use, copy, modify, distribute, and sell this software and |
| 8 | * its documentation for any purpose is hereby granted without fee, provided |
| 9 | * that (i) the above copyright notices and this permission notice appear in |
| 10 | * all copies of the software and related documentation, and (ii) the names of |
| 11 | * Sam Leffler and Silicon Graphics may not be used in any advertising or |
| 12 | * publicity relating to the software without the specific, prior written |
| 13 | * permission of Sam Leffler and Silicon Graphics. |
| 14 | * |
| 15 | * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, |
| 16 | * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY |
| 17 | * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. |
| 18 | * |
| 19 | * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR |
| 20 | * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, |
| 21 | * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, |
| 22 | * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF |
| 23 | * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE |
| 24 | * OF THIS SOFTWARE. |
| 25 | */ |
| 26 | |
| 27 | /* |
| 28 | * Convert a CCITT Group 3 or 4 FAX file to TIFF Group 3 or 4 format. |
| 29 | */ |
| 30 | #include "tif_config.h" |
| 31 | |
| 32 | #include <stdio.h> |
| 33 | #include <stdlib.h> /* should have atof & getopt */ |
| 34 | |
| 35 | #ifdef HAVE_UNISTD_H |
| 36 | # include <unistd.h> |
| 37 | #endif |
| 38 | |
| 39 | #ifdef HAVE_FCNTL_H |
| 40 | # include <fcntl.h> |
| 41 | #endif |
| 42 | |
| 43 | #ifdef HAVE_IO_H |
| 44 | # include <io.h> |
| 45 | #endif |
| 46 | |
| 47 | #include "tiffiop.h" |
| 48 | |
| 49 | #ifndef BINMODE |
| 50 | # define BINMODE |
| 51 | #endif |
| 52 | |
| 53 | #ifndef EXIT_SUCCESS |
| 54 | # define EXIT_SUCCESS 0 |
| 55 | #endif |
| 56 | #ifndef EXIT_FAILURE |
| 57 | # define EXIT_FAILURE 1 |
| 58 | #endif |
| 59 | |
| 60 | #define TIFFhowmany8(x) (((x)&0x07)?((uint32)(x)>>3)+1:(uint32)(x)>>3) |
| 61 | |
| 62 | TIFF *faxTIFF; |
| 63 | char *rowbuf; |
| 64 | char *refbuf; |
| 65 | |
| 66 | uint32 xsize = 1728; |
| 67 | int verbose; |
| 68 | int stretch; |
| 69 | uint16 badfaxrun; |
| 70 | uint32 badfaxlines; |
| 71 | |
| 72 | int copyFaxFile(TIFF* tifin, TIFF* tifout); |
| 73 | static void usage(void); |
| 74 | |
| 75 | int |
| 76 | main(int argc, char* argv[]) |
| 77 | { |
| 78 | FILE *in; |
| 79 | TIFF *out = NULL; |
| 80 | TIFFErrorHandler whandler = NULL; |
| 81 | int compression_in = COMPRESSION_CCITTFAX3; |
| 82 | int compression_out = COMPRESSION_CCITTFAX3; |
| 83 | int fillorder_in = FILLORDER_LSB2MSB; |
| 84 | int fillorder_out = FILLORDER_LSB2MSB; |
| 85 | uint32 group3options_in = 0; /* 1d-encoded */ |
| 86 | uint32 group3options_out = 0; /* 1d-encoded */ |
| 87 | uint32 group4options_in = 0; /* compressed */ |
| 88 | uint32 group4options_out = 0; /* compressed */ |
| 89 | uint32 defrowsperstrip = (uint32) 0; |
| 90 | uint32 rowsperstrip; |
| 91 | int photometric_in = PHOTOMETRIC_MINISWHITE; |
| 92 | int photometric_out = PHOTOMETRIC_MINISWHITE; |
| 93 | int mode = FAXMODE_CLASSF; |
| 94 | int rows; |
| 95 | int c; |
| 96 | int pn, npages; |
| 97 | float resY = 196.0; |
| 98 | extern int optind; |
| 99 | extern char* optarg; |
| 100 | |
| 101 | |
| 102 | while ((c = getopt(argc, argv, "R:X:o:1234ABLMPUW5678abcflmprsuvwz?")) != -1) |
| 103 | switch (c) { |
| 104 | /* input-related options */ |
| 105 | case '3': /* input is g3-encoded */ |
| 106 | compression_in = COMPRESSION_CCITTFAX3; |
| 107 | break; |
| 108 | case '4': /* input is g4-encoded */ |
| 109 | compression_in = COMPRESSION_CCITTFAX4; |
| 110 | break; |
| 111 | case 'U': /* input is uncompressed (g3 and g4) */ |
| 112 | group3options_in |= GROUP3OPT_UNCOMPRESSED; |
| 113 | group4options_in |= GROUP4OPT_UNCOMPRESSED; |
| 114 | break; |
| 115 | case '1': /* input is 1d-encoded (g3 only) */ |
| 116 | group3options_in &= ~GROUP3OPT_2DENCODING; |
| 117 | break; |
| 118 | case '2': /* input is 2d-encoded (g3 only) */ |
| 119 | group3options_in |= GROUP3OPT_2DENCODING; |
| 120 | break; |
| 121 | case 'P': /* input has not-aligned EOL (g3 only) */ |
| 122 | group3options_in &= ~GROUP3OPT_FILLBITS; |
| 123 | break; |
| 124 | case 'A': /* input has aligned EOL (g3 only) */ |
| 125 | group3options_in |= GROUP3OPT_FILLBITS; |
| 126 | break; |
| 127 | case 'W': /* input has 0 mean white */ |
| 128 | photometric_in = PHOTOMETRIC_MINISWHITE; |
| 129 | break; |
| 130 | case 'B': /* input has 0 mean black */ |
| 131 | photometric_in = PHOTOMETRIC_MINISBLACK; |
| 132 | break; |
| 133 | case 'L': /* input has lsb-to-msb fillorder */ |
| 134 | fillorder_in = FILLORDER_LSB2MSB; |
| 135 | break; |
| 136 | case 'M': /* input has msb-to-lsb fillorder */ |
| 137 | fillorder_in = FILLORDER_MSB2LSB; |
| 138 | break; |
| 139 | case 'R': /* input resolution */ |
| 140 | resY = (float) atof(optarg); |
| 141 | break; |
| 142 | case 'X': /* input width */ |
| 143 | xsize = (uint32) atoi(optarg); |
| 144 | break; |
| 145 | |
| 146 | /* output-related options */ |
| 147 | case '7': /* generate g3-encoded output */ |
| 148 | compression_out = COMPRESSION_CCITTFAX3; |
| 149 | break; |
| 150 | case '8': /* generate g4-encoded output */ |
| 151 | compression_out = COMPRESSION_CCITTFAX4; |
| 152 | break; |
| 153 | case 'u': /* generate uncompressed output (g3 and g4) */ |
| 154 | group3options_out |= GROUP3OPT_UNCOMPRESSED; |
| 155 | group4options_out |= GROUP4OPT_UNCOMPRESSED; |
| 156 | break; |
| 157 | case '5': /* generate 1d-encoded output (g3 only) */ |
| 158 | group3options_out &= ~GROUP3OPT_2DENCODING; |
| 159 | break; |
| 160 | case '6': /* generate 2d-encoded output (g3 only) */ |
| 161 | group3options_out |= GROUP3OPT_2DENCODING; |
| 162 | break; |
| 163 | case 'c': /* generate "classic" g3 format */ |
| 164 | mode = FAXMODE_CLASSIC; |
| 165 | break; |
| 166 | case 'f': /* generate Class F format */ |
| 167 | mode = FAXMODE_CLASSF; |
| 168 | break; |
| 169 | case 'm': /* output's fillorder is msb-to-lsb */ |
| 170 | fillorder_out = FILLORDER_MSB2LSB; |
| 171 | break; |
| 172 | case 'l': /* output's fillorder is lsb-to-msb */ |
| 173 | fillorder_out = FILLORDER_LSB2MSB; |
| 174 | break; |
| 175 | case 'o': |
| 176 | out = TIFFOpen(optarg, "w"); |
| 177 | if (out == NULL) { |
| 178 | fprintf(stderr, |
| 179 | "%s: Can not create or open %s\n", |
| 180 | argv[0], optarg); |
| 181 | return EXIT_FAILURE; |
| 182 | } |
| 183 | break; |
| 184 | case 'a': /* generate EOL-aligned output (g3 only) */ |
| 185 | group3options_out |= GROUP3OPT_FILLBITS; |
| 186 | break; |
| 187 | case 'p': /* generate not EOL-aligned output (g3 only) */ |
| 188 | group3options_out &= ~GROUP3OPT_FILLBITS; |
| 189 | break; |
| 190 | case 'r': /* rows/strip */ |
| 191 | defrowsperstrip = atol(optarg); |
| 192 | break; |
| 193 | case 's': /* stretch image by dup'ng scanlines */ |
| 194 | stretch = 1; |
| 195 | break; |
| 196 | case 'w': /* undocumented -- for testing */ |
| 197 | photometric_out = PHOTOMETRIC_MINISWHITE; |
| 198 | break; |
| 199 | case 'b': /* undocumented -- for testing */ |
| 200 | photometric_out = PHOTOMETRIC_MINISBLACK; |
| 201 | break; |
| 202 | case 'z': /* undocumented -- for testing */ |
| 203 | compression_out = COMPRESSION_LZW; |
| 204 | break; |
| 205 | case 'v': /* -v for info */ |
| 206 | verbose++; |
| 207 | break; |
| 208 | case '?': |
| 209 | usage(); |
| 210 | /*NOTREACHED*/ |
| 211 | } |
| 212 | npages = argc - optind; |
| 213 | if (npages < 1) |
| 214 | usage(); |
| 215 | |
| 216 | rowbuf = _TIFFmalloc(TIFFhowmany8(xsize)); |
| 217 | refbuf = _TIFFmalloc(TIFFhowmany8(xsize)); |
| 218 | if (rowbuf == NULL || refbuf == NULL) { |
| 219 | fprintf(stderr, "%s: Not enough memory\n", argv[0]); |
| 220 | return (EXIT_FAILURE); |
| 221 | } |
| 222 | |
| 223 | if (out == NULL) { |
| 224 | out = TIFFOpen("fax.tif", "w"); |
| 225 | if (out == NULL) { |
| 226 | fprintf(stderr, "%s: Can not create fax.tif\n", |
| 227 | argv[0]); |
| 228 | return (EXIT_FAILURE); |
| 229 | } |
| 230 | } |
| 231 | |
| 232 | faxTIFF = TIFFClientOpen("(FakeInput)", "w", |
| 233 | /* TIFFClientOpen() fails if we don't set existing value here */ |
| 234 | TIFFClientdata(out), |
| 235 | TIFFGetReadProc(out), TIFFGetWriteProc(out), |
| 236 | TIFFGetSeekProc(out), TIFFGetCloseProc(out), |
| 237 | TIFFGetSizeProc(out), TIFFGetMapFileProc(out), |
| 238 | TIFFGetUnmapFileProc(out)); |
| 239 | if (faxTIFF == NULL) { |
| 240 | fprintf(stderr, "%s: Can not create fake input file\n", |
| 241 | argv[0]); |
| 242 | return (EXIT_FAILURE); |
| 243 | } |
| 244 | TIFFSetMode(faxTIFF, O_RDONLY); |
| 245 | TIFFSetField(faxTIFF, TIFFTAG_IMAGEWIDTH, xsize); |
| 246 | TIFFSetField(faxTIFF, TIFFTAG_SAMPLESPERPIXEL, 1); |
| 247 | TIFFSetField(faxTIFF, TIFFTAG_BITSPERSAMPLE, 1); |
| 248 | TIFFSetField(faxTIFF, TIFFTAG_FILLORDER, fillorder_in); |
| 249 | TIFFSetField(faxTIFF, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG); |
| 250 | TIFFSetField(faxTIFF, TIFFTAG_PHOTOMETRIC, photometric_in); |
| 251 | TIFFSetField(faxTIFF, TIFFTAG_YRESOLUTION, resY); |
| 252 | TIFFSetField(faxTIFF, TIFFTAG_RESOLUTIONUNIT, RESUNIT_INCH); |
| 253 | |
| 254 | /* NB: this must be done after directory info is setup */ |
| 255 | TIFFSetField(faxTIFF, TIFFTAG_COMPRESSION, compression_in); |
| 256 | if (compression_in == COMPRESSION_CCITTFAX3) |
| 257 | TIFFSetField(faxTIFF, TIFFTAG_GROUP3OPTIONS, group3options_in); |
| 258 | else if (compression_in == COMPRESSION_CCITTFAX4) |
| 259 | TIFFSetField(faxTIFF, TIFFTAG_GROUP4OPTIONS, group4options_in); |
| 260 | for (pn = 0; optind < argc; pn++, optind++) { |
| 261 | in = fopen(argv[optind], "r" BINMODE); |
| 262 | if (in == NULL) { |
| 263 | fprintf(stderr, |
| 264 | "%s: %s: Can not open\n", argv[0], argv[optind]); |
| 265 | continue; |
| 266 | } |
| 267 | #if defined(_WIN32) && defined(USE_WIN32_FILEIO) |
| 268 | TIFFSetClientdata(faxTIFF, (thandle_t)_get_osfhandle(fileno(in))); |
| 269 | #else |
| 270 | TIFFSetClientdata(faxTIFF, (thandle_t)fileno(in)); |
| 271 | #endif |
| 272 | TIFFSetFileName(faxTIFF, (const char*)argv[optind]); |
| 273 | TIFFSetField(out, TIFFTAG_IMAGEWIDTH, xsize); |
| 274 | TIFFSetField(out, TIFFTAG_BITSPERSAMPLE, 1); |
| 275 | TIFFSetField(out, TIFFTAG_COMPRESSION, compression_out); |
| 276 | TIFFSetField(out, TIFFTAG_PHOTOMETRIC, photometric_out); |
| 277 | TIFFSetField(out, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT); |
| 278 | TIFFSetField(out, TIFFTAG_SAMPLESPERPIXEL, 1); |
| 279 | switch (compression_out) { |
| 280 | /* g3 */ |
| 281 | case COMPRESSION_CCITTFAX3: |
| 282 | TIFFSetField(out, TIFFTAG_GROUP3OPTIONS, |
| 283 | group3options_out); |
| 284 | TIFFSetField(out, TIFFTAG_FAXMODE, mode); |
| 285 | rowsperstrip = |
| 286 | (defrowsperstrip)?defrowsperstrip:(uint32)-1L; |
| 287 | break; |
| 288 | |
| 289 | /* g4 */ |
| 290 | case COMPRESSION_CCITTFAX4: |
| 291 | TIFFSetField(out, TIFFTAG_GROUP4OPTIONS, |
| 292 | group4options_out); |
| 293 | TIFFSetField(out, TIFFTAG_FAXMODE, mode); |
| 294 | rowsperstrip = |
| 295 | (defrowsperstrip)?defrowsperstrip:(uint32)-1L; |
| 296 | break; |
| 297 | |
| 298 | default: |
| 299 | rowsperstrip = (defrowsperstrip) ? |
| 300 | defrowsperstrip : TIFFDefaultStripSize(out, 0); |
| 301 | } |
| 302 | TIFFSetField(out, TIFFTAG_ROWSPERSTRIP, rowsperstrip); |
| 303 | TIFFSetField(out, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG); |
| 304 | TIFFSetField(out, TIFFTAG_FILLORDER, fillorder_out); |
| 305 | TIFFSetField(out, TIFFTAG_SOFTWARE, "fax2tiff"); |
| 306 | TIFFSetField(out, TIFFTAG_XRESOLUTION, 204.0); |
| 307 | if (!stretch) { |
| 308 | TIFFGetField(faxTIFF, TIFFTAG_YRESOLUTION, &resY); |
| 309 | TIFFSetField(out, TIFFTAG_YRESOLUTION, resY); |
| 310 | } else |
| 311 | TIFFSetField(out, TIFFTAG_YRESOLUTION, 196.); |
| 312 | TIFFSetField(out, TIFFTAG_RESOLUTIONUNIT, RESUNIT_INCH); |
| 313 | TIFFSetField(out, TIFFTAG_PAGENUMBER, pn, npages); |
| 314 | |
| 315 | if (!verbose) |
| 316 | whandler = TIFFSetWarningHandler(NULL); |
| 317 | rows = copyFaxFile(faxTIFF, out); |
| 318 | fclose(in); |
| 319 | if (!verbose) |
| 320 | (void) TIFFSetWarningHandler(whandler); |
| 321 | |
| 322 | TIFFSetField(out, TIFFTAG_IMAGELENGTH, rows); |
| 323 | |
| 324 | if (verbose) { |
| 325 | fprintf(stderr, "%s:\n", argv[optind]); |
| 326 | fprintf(stderr, "%d rows in input\n", rows); |
| 327 | fprintf(stderr, "%ld total bad rows\n", |
| 328 | (long) badfaxlines); |
| 329 | fprintf(stderr, "%d max consecutive bad rows\n", badfaxrun); |
| 330 | } |
| 331 | if (compression_out == COMPRESSION_CCITTFAX3 && |
| 332 | mode == FAXMODE_CLASSF) { |
| 333 | TIFFSetField(out, TIFFTAG_BADFAXLINES, badfaxlines); |
| 334 | TIFFSetField(out, TIFFTAG_CLEANFAXDATA, badfaxlines ? |
| 335 | CLEANFAXDATA_REGENERATED : CLEANFAXDATA_CLEAN); |
| 336 | TIFFSetField(out, TIFFTAG_CONSECUTIVEBADFAXLINES, badfaxrun); |
| 337 | } |
| 338 | TIFFWriteDirectory(out); |
| 339 | } |
| 340 | TIFFClose(out); |
| 341 | _TIFFfree(rowbuf); |
| 342 | _TIFFfree(refbuf); |
| 343 | return (EXIT_SUCCESS); |
| 344 | } |
| 345 | |
| 346 | int |
| 347 | copyFaxFile(TIFF* tifin, TIFF* tifout) |
| 348 | { |
| 349 | uint32 row; |
| 350 | uint32 linesize = TIFFhowmany8(xsize); |
| 351 | uint16 badrun; |
| 352 | int ok; |
| 353 | |
| 354 | tifin->tif_rawdatasize = TIFFGetFileSize(tifin); |
| 355 | tifin->tif_rawdata = _TIFFmalloc(tifin->tif_rawdatasize); |
| 356 | if (tifin->tif_rawdata == NULL) { |
| 357 | TIFFError(tifin->tif_name, "Not enough memory"); |
| 358 | return (0); |
| 359 | } |
| 360 | if (!ReadOK(tifin, tifin->tif_rawdata, tifin->tif_rawdatasize)) { |
| 361 | TIFFError(tifin->tif_name, "Read error at scanline 0"); |
| 362 | return (0); |
| 363 | } |
| 364 | tifin->tif_rawcp = tifin->tif_rawdata; |
| 365 | tifin->tif_rawcc = tifin->tif_rawdatasize; |
| 366 | |
| 367 | (*tifin->tif_setupdecode)(tifin); |
| 368 | (*tifin->tif_predecode)(tifin, (tsample_t) 0); |
| 369 | tifin->tif_row = 0; |
| 370 | badfaxlines = 0; |
| 371 | badfaxrun = 0; |
| 372 | |
| 373 | _TIFFmemset(refbuf, 0, linesize); |
| 374 | row = 0; |
| 375 | badrun = 0; /* current run of bad lines */ |
| 376 | while (tifin->tif_rawcc > 0) { |
| 377 | ok = (*tifin->tif_decoderow)(tifin, (tdata_t) rowbuf, |
| 378 | linesize, 0); |
| 379 | if (!ok) { |
| 380 | badfaxlines++; |
| 381 | badrun++; |
| 382 | /* regenerate line from previous good line */ |
| 383 | _TIFFmemcpy(rowbuf, refbuf, linesize); |
| 384 | } else { |
| 385 | if (badrun > badfaxrun) |
| 386 | badfaxrun = badrun; |
| 387 | badrun = 0; |
| 388 | _TIFFmemcpy(refbuf, rowbuf, linesize); |
| 389 | } |
| 390 | tifin->tif_row++; |
| 391 | |
| 392 | if (TIFFWriteScanline(tifout, rowbuf, row, 0) < 0) { |
| 393 | fprintf(stderr, "%s: Write error at row %ld.\n", |
| 394 | tifout->tif_name, (long) row); |
| 395 | break; |
| 396 | } |
| 397 | row++; |
| 398 | if (stretch) { |
| 399 | if (TIFFWriteScanline(tifout, rowbuf, row, 0) < 0) { |
| 400 | fprintf(stderr, "%s: Write error at row %ld.\n", |
| 401 | tifout->tif_name, (long) row); |
| 402 | break; |
| 403 | } |
| 404 | row++; |
| 405 | } |
| 406 | } |
| 407 | if (badrun > badfaxrun) |
| 408 | badfaxrun = badrun; |
| 409 | _TIFFfree(tifin->tif_rawdata); |
| 410 | return (row); |
| 411 | } |
| 412 | |
| 413 | char* stuff[] = { |
| 414 | "usage: fax2tiff [options] input.raw...", |
| 415 | "where options are:", |
| 416 | " -3 input data is G3-encoded [default]", |
| 417 | " -4 input data is G4-encoded", |
| 418 | " -U input data is uncompressed (G3 or G4)", |
| 419 | " -1 input data is 1D-encoded (G3 only) [default]", |
| 420 | " -2 input data is 2D-encoded (G3 only)", |
| 421 | " -P input is not EOL-aligned (G3 only) [default]", |
| 422 | " -A input is EOL-aligned (G3 only)", |
| 423 | " -M input data has MSB2LSB bit order", |
| 424 | " -L input data has LSB2MSB bit order [default]", |
| 425 | " -B input data has min 0 means black", |
| 426 | " -W input data has min 0 means white [default]", |
| 427 | " -R # input data has # resolution (lines/inch) [default is 196]", |
| 428 | " -X # input data has # width [default is 1728]", |
| 429 | "", |
| 430 | " -o out.tif write output to out.tif", |
| 431 | " -7 generate G3-encoded output [default]", |
| 432 | " -8 generate G4-encoded output", |
| 433 | " -u generate uncompressed output (G3 or G4)", |
| 434 | " -5 generate 1D-encoded output (G3 only)", |
| 435 | " -6 generate 2D-encoded output (G3 only) [default]", |
| 436 | " -p generate not EOL-aligned output (G3 only)", |
| 437 | " -a generate EOL-aligned output (G3 only) [default]", |
| 438 | " -c generate \"classic\" TIFF format", |
| 439 | " -f generate TIFF Class F (TIFF/F) format [default]", |
| 440 | " -m output fill order is MSB2LSB", |
| 441 | " -l output fill order is LSB2MSB [default]", |
| 442 | " -r # make each strip have no more than # rows", |
| 443 | " -s stretch image by duplicating scanlines", |
| 444 | " -v print information about conversion work", |
| 445 | " -z generate LZW compressed output", |
| 446 | NULL |
| 447 | }; |
| 448 | |
| 449 | static void |
| 450 | usage(void) |
| 451 | { |
| 452 | char buf[BUFSIZ]; |
| 453 | int i; |
| 454 | |
| 455 | setbuf(stderr, buf); |
| 456 | fprintf(stderr, "%s\n\n", TIFFGetVersion()); |
| 457 | for (i = 0; stuff[i] != NULL; i++) |
| 458 | fprintf(stderr, "%s\n", stuff[i]); |
| 459 | exit(EXIT_FAILURE); |
| 460 | } |
| 461 | |
| 462 | /* vim: set ts=8 sts=8 sw=8 noet: */ |