postprint.c 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794
  1. /*
  2. *
  3. * postprint - PostScript translator for ASCII files.
  4. *
  5. * A simple program that translates ASCII files into PostScript. All it really
  6. * does is expand tabs and backspaces, handle character quoting, print text lines,
  7. * and control when pages are started based on the requested number of lines per
  8. * page.
  9. *
  10. * The PostScript prologue is copied from *prologue before any of the input files
  11. * are translated. The program expects that the following procedures are defined
  12. * in that file:
  13. *
  14. * setup
  15. *
  16. * mark ... setup -
  17. *
  18. * Handles special initialization stuff that depends on how the program
  19. * was called. Expects to find a mark followed by key/value pairs on the
  20. * stack. The def operator is applied to each pair up to the mark, then
  21. * the default state is set up.
  22. *
  23. * pagesetup
  24. *
  25. * page pagesetup -
  26. *
  27. * Does whatever is needed to set things up for the next page. Expects
  28. * to find the current page number on the stack.
  29. *
  30. * l
  31. *
  32. * string l -
  33. *
  34. * Prints string starting in the first column and then goes to the next
  35. * line.
  36. *
  37. * L
  38. *
  39. * mark string column string column ... L mark
  40. *
  41. * Prints each string on the stack starting at the horizontal position
  42. * selected by column. Used when tabs and spaces can be sufficiently well
  43. * compressed to make the printer overhead worthwhile. Always used when
  44. * we have to back up.
  45. *
  46. * LL
  47. *
  48. * mark string column string column ... LL mark
  49. *
  50. * Like L, but only used to prevent potential PostScript stack overflow
  51. * from too many string/column pairs. Stays on the current line. It will
  52. * not be needed often!!
  53. *
  54. * done
  55. *
  56. * done
  57. *
  58. * Makes sure the last page is printed. Only needed when we're printing
  59. * more than one page on each sheet of paper.
  60. *
  61. * Almost everything has been changed in this version of postprint. The program
  62. * is more intelligent, especially about tabs, spaces, and backspacing, and as a
  63. * result output files usually print faster. Output files also now conform to
  64. * Adobe's file structuring conventions, which is undoubtedly something I should
  65. * have done in the first version of the program. If the number of lines per page
  66. * is set to 0, which can be done using the -l option, pointsize will be used to
  67. * guess a reasonable value. The estimate is based on the values of LINESPP,
  68. * POINTSIZE, and pointsize, and assumes LINESPP lines would fit on a page if
  69. * we printed in size POINTSIZE. Selecting a point size using the -s option and
  70. * adding -l0 to the command line forces the guess to be made.
  71. *
  72. * Many default values, like the magnification and orientation, are defined in
  73. * the prologue, which is where they belong. If they're changed (by options), an
  74. * appropriate definition is made after the prologue is added to the output file.
  75. * The -P option passes arbitrary PostScript through to the output file. Among
  76. * other things it can be used to set (or change) values that can't be accessed by
  77. * other options.
  78. *
  79. */
  80. #include <stdio.h>
  81. #include <signal.h>
  82. #include <ctype.h>
  83. #ifdef plan9
  84. #define isascii(c) ((unsigned char)(c)<=0177)
  85. #endif
  86. #include <sys/types.h>
  87. #include <fcntl.h>
  88. #include "comments.h" /* PostScript file structuring comments */
  89. #include "gen.h" /* general purpose definitions */
  90. #include "path.h" /* for the prologue */
  91. #include "ext.h" /* external variable declarations */
  92. #include "postprint.h" /* a few special definitions */
  93. char *optnames = "a:c:ef:l:m:n:o:p:r:s:t:x:y:A:C:E:J:L:P:R:DI";
  94. char *prologue = POSTPRINT; /* default PostScript prologue */
  95. char *formfile = FORMFILE; /* stuff for multiple pages per sheet */
  96. int formsperpage = 1; /* page images on each piece of paper */
  97. int copies = 1; /* and this many copies of each sheet */
  98. int linespp = LINESPP; /* number of lines per page */
  99. int pointsize = POINTSIZE; /* in this point size */
  100. int tabstops = TABSTOPS; /* tabs set at these columns */
  101. int crmode = 0; /* carriage return mode - 0, 1, or 2 */
  102. int extended = TRUE; /* use escapes for unprintable chars */
  103. int col = 1; /* next character goes in this column */
  104. int line = 1; /* on this line */
  105. int stringcount = 0; /* number of strings on the stack */
  106. int stringstart = 1; /* column where current one starts */
  107. Fontmap fontmap[] = FONTMAP; /* for translating font names */
  108. char *fontname = "Courier"; /* use this PostScript font */
  109. int page = 0; /* page we're working on */
  110. int printed = 0; /* printed this many pages */
  111. FILE *fp_in = stdin; /* read from this file */
  112. FILE *fp_out = stdout; /* and write stuff here */
  113. FILE *fp_acct = NULL; /* for accounting data */
  114. /*****************************************************************************/
  115. main(agc, agv)
  116. int agc;
  117. char *agv[];
  118. {
  119. /*
  120. *
  121. * A simple program that translates ASCII files into PostScript. If there's more
  122. * than one input file, each begins on a new page.
  123. *
  124. */
  125. argc = agc; /* other routines may want them */
  126. argv = agv;
  127. prog_name = argv[0]; /* really just for error messages */
  128. init_signals(); /* sets up interrupt handling */
  129. header(); /* PostScript header and prologue */
  130. options(); /* handle the command line options */
  131. setup(); /* for PostScript */
  132. arguments(); /* followed by each input file */
  133. done(); /* print the last page etc. */
  134. account(); /* job accounting data */
  135. exit(x_stat); /* not much could be wrong */
  136. } /* End of main */
  137. /*****************************************************************************/
  138. init_signals()
  139. {
  140. /*
  141. *
  142. * Makes sure we handle interrupts.
  143. *
  144. */
  145. if ( signal(SIGINT, interrupt) == SIG_IGN ) {
  146. signal(SIGINT, SIG_IGN);
  147. signal(SIGQUIT, SIG_IGN);
  148. signal(SIGHUP, SIG_IGN);
  149. } else {
  150. signal(SIGHUP, interrupt);
  151. signal(SIGQUIT, interrupt);
  152. } /* End else */
  153. signal(SIGTERM, interrupt);
  154. } /* End of init_signals */
  155. /*****************************************************************************/
  156. header()
  157. {
  158. int ch; /* return value from getopt() */
  159. int old_optind = optind; /* for restoring optind - should be 1 */
  160. /*
  161. *
  162. * Scans the option list looking for things, like the prologue file, that we need
  163. * right away but could be changed from the default. Doing things this way is an
  164. * attempt to conform to Adobe's latest file structuring conventions. In particular
  165. * they now say there should be nothing executed in the prologue, and they have
  166. * added two new comments that delimit global initialization calls. Once we know
  167. * where things really are we write out the job header, follow it by the prologue,
  168. * and then add the ENDPROLOG and BEGINSETUP comments.
  169. *
  170. */
  171. while ( (ch = getopt(argc, argv, optnames)) != EOF )
  172. if ( ch == 'L' )
  173. prologue = optarg;
  174. else if ( ch == '?' )
  175. error(FATAL, "");
  176. optind = old_optind; /* get ready for option scanning */
  177. fprintf(stdout, "%s", CONFORMING);
  178. fprintf(stdout, "%s %s\n", VERSION, PROGRAMVERSION);
  179. fprintf(stdout, "%s %s\n", DOCUMENTFONTS, ATEND);
  180. fprintf(stdout, "%s %s\n", PAGES, ATEND);
  181. fprintf(stdout, "%s", ENDCOMMENTS);
  182. if ( cat(prologue) == FALSE )
  183. error(FATAL, "can't read %s", prologue);
  184. if ( DOROUND )
  185. cat(ROUNDPAGE);
  186. fprintf(stdout, "%s", ENDPROLOG);
  187. fprintf(stdout, "%s", BEGINSETUP);
  188. fprintf(stdout, "mark\n");
  189. } /* End of header */
  190. /*****************************************************************************/
  191. options()
  192. {
  193. int ch; /* return value from getopt() */
  194. /*
  195. *
  196. * Reads and processes the command line options. Added the -P option so arbitrary
  197. * PostScript code can be passed through. Expect it could be useful for changing
  198. * definitions in the prologue for which options have not been defined.
  199. *
  200. * Although any PostScript font can be used, things will only work well for
  201. * constant width fonts.
  202. *
  203. */
  204. while ( (ch = getopt(argc, argv, optnames)) != EOF ) {
  205. switch ( ch ) {
  206. case 'a': /* aspect ratio */
  207. fprintf(stdout, "/aspectratio %s def\n", optarg);
  208. break;
  209. case 'c': /* copies */
  210. copies = atoi(optarg);
  211. fprintf(stdout, "/#copies %s store\n", optarg);
  212. break;
  213. case 'e': /* obsolete - it's now always on */
  214. extended = TRUE;
  215. break;
  216. case 'f': /* use this PostScript font */
  217. fontname = get_font(optarg);
  218. fprintf(stdout, "/font /%s def\n", fontname);
  219. break;
  220. case 'l': /* lines per page */
  221. linespp = atoi(optarg);
  222. break;
  223. case 'm': /* magnification */
  224. fprintf(stdout, "/magnification %s def\n", optarg);
  225. break;
  226. case 'n': /* forms per page */
  227. formsperpage = atoi(optarg);
  228. fprintf(stdout, "%s %s\n", FORMSPERPAGE, optarg);
  229. fprintf(stdout, "/formsperpage %s def\n", optarg);
  230. break;
  231. case 'o': /* output page list */
  232. out_list(optarg);
  233. break;
  234. case 'p': /* landscape or portrait mode */
  235. if ( *optarg == 'l' )
  236. fprintf(stdout, "/landscape true def\n");
  237. else fprintf(stdout, "/landscape false def\n");
  238. break;
  239. case 'r': /* carriage return mode */
  240. crmode = atoi(optarg);
  241. break;
  242. case 's': /* point size */
  243. pointsize = atoi(optarg);
  244. fprintf(stdout, "/pointsize %s def\n", optarg);
  245. break;
  246. case 't': /* tabstops */
  247. tabstops = atoi(optarg);
  248. break;
  249. case 'x': /* shift things horizontally */
  250. fprintf(stdout, "/xoffset %s def\n", optarg);
  251. break;
  252. case 'y': /* and vertically on the page */
  253. fprintf(stdout, "/yoffset %s def\n", optarg);
  254. break;
  255. case 'A': /* force job accounting */
  256. case 'J':
  257. if ( (fp_acct = fopen(optarg, "a")) == NULL )
  258. error(FATAL, "can't open accounting file %s", optarg);
  259. break;
  260. case 'C': /* copy file straight to output */
  261. if ( cat(optarg) == FALSE )
  262. error(FATAL, "can't read %s", optarg);
  263. break;
  264. case 'E': /* text font encoding */
  265. fontencoding = optarg;
  266. break;
  267. case 'L': /* PostScript prologue file */
  268. prologue = optarg;
  269. break;
  270. case 'P': /* PostScript pass through */
  271. fprintf(stdout, "%s\n", optarg);
  272. break;
  273. case 'R': /* special global or page level request */
  274. saverequest(optarg);
  275. break;
  276. case 'D': /* debug flag */
  277. debug = ON;
  278. break;
  279. case 'I': /* ignore FATAL errors */
  280. ignore = ON;
  281. break;
  282. case '?': /* don't understand the option */
  283. error(FATAL, "");
  284. break;
  285. default: /* don't know what to do for ch */
  286. error(FATAL, "missing case for option %c\n", ch);
  287. break;
  288. } /* End switch */
  289. } /* End while */
  290. argc -= optind; /* get ready for non-option args */
  291. argv += optind;
  292. } /* End of options */
  293. /*****************************************************************************/
  294. char *get_font(name)
  295. char *name; /* name the user asked for */
  296. {
  297. int i; /* for looking through fontmap[] */
  298. /*
  299. *
  300. * Called from options() to map a user's font name into a legal PostScript name.
  301. * If the lookup fails *name is returned to the caller. That should let you choose
  302. * any PostScript font, although things will only work well for constant width
  303. * fonts.
  304. *
  305. */
  306. for ( i = 0; fontmap[i].name != NULL; i++ )
  307. if ( strcmp(name, fontmap[i].name) == 0 )
  308. return(fontmap[i].val);
  309. return(name);
  310. } /* End of get_font */
  311. /*****************************************************************************/
  312. setup()
  313. {
  314. /*
  315. *
  316. * Handles things that must be done after the options are read but before the
  317. * input files are processed. linespp (lines per page) can be set using the -l
  318. * option. If it's not positive we calculate a reasonable value using the
  319. * requested point size - assuming LINESPP lines fit on a page in point size
  320. * POINTSIZE.
  321. *
  322. */
  323. writerequest(0, stdout); /* global requests eg. manual feed */
  324. setencoding(fontencoding);
  325. fprintf(stdout, "setup\n");
  326. if ( formsperpage > 1 ) {
  327. if ( cat(formfile) == FALSE )
  328. error(FATAL, "can't read %s", formfile);
  329. fprintf(stdout, "%d setupforms\n", formsperpage);
  330. } /* End if */
  331. fprintf(stdout, "%s", ENDSETUP);
  332. if ( linespp <= 0 )
  333. linespp = LINESPP * POINTSIZE / pointsize;
  334. } /* End of setup */
  335. /*****************************************************************************/
  336. arguments()
  337. {
  338. /*
  339. *
  340. * Makes sure all the non-option command line arguments are processed. If we get
  341. * here and there aren't any arguments left, or if '-' is one of the input files
  342. * we'll translate stdin.
  343. *
  344. */
  345. if ( argc < 1 )
  346. text();
  347. else { /* at least one argument is left */
  348. while ( argc > 0 ) {
  349. if ( strcmp(*argv, "-") == 0 )
  350. fp_in = stdin;
  351. else if ( (fp_in = fopen(*argv, "r")) == NULL )
  352. error(FATAL, "can't open %s", *argv);
  353. text();
  354. if ( fp_in != stdin )
  355. fclose(fp_in);
  356. argc--;
  357. argv++;
  358. } /* End while */
  359. } /* End else */
  360. } /* End of arguments */
  361. /*****************************************************************************/
  362. done()
  363. {
  364. /*
  365. *
  366. * Finished with all the input files, so mark the end of the pages with a TRAILER
  367. * comment, make sure the last page prints, and add things like the PAGES comment
  368. * that can only be determined after all the input files have been read.
  369. *
  370. */
  371. fprintf(stdout, "%s", TRAILER);
  372. fprintf(stdout, "done\n");
  373. fprintf(stdout, "%s %s\n", DOCUMENTFONTS, fontname);
  374. fprintf(stdout, "%s %d\n", PAGES, printed);
  375. } /* End of done */
  376. /*****************************************************************************/
  377. account()
  378. {
  379. /*
  380. *
  381. * Writes an accounting record to *fp_acct provided it's not NULL. Accounting is
  382. * requested using the -A or -J options.
  383. *
  384. */
  385. if ( fp_acct != NULL )
  386. fprintf(fp_acct, " print %d\n copies %d\n", printed, copies);
  387. } /* End of account */
  388. /*****************************************************************************/
  389. text()
  390. {
  391. int ch; /* next input character */
  392. /*
  393. *
  394. * Translates *fp_in into PostScript. Intercepts space, tab, backspace, newline,
  395. * return, and formfeed. Everything else goes to oput(), which handles quoting
  396. * (if needed) and escapes for nonascii characters if extended is TRUE. The
  397. * redirect(-1) call forces the initial output to go to /dev/null - so stuff
  398. * that formfeed() does at the end of each page goes to /dev/null rather than
  399. * the real output file.
  400. *
  401. */
  402. redirect(-1); /* get ready for the first page */
  403. formfeed(); /* force PAGE comment etc. */
  404. while ( (ch = getc(fp_in)) != EOF )
  405. switch ( ch ) {
  406. case '\n':
  407. newline();
  408. break;
  409. case '\t':
  410. case '\b':
  411. case ' ':
  412. spaces(ch);
  413. break;
  414. case '\014':
  415. formfeed();
  416. break;
  417. case '\r':
  418. if ( crmode == 1 )
  419. spaces(ch);
  420. else if ( crmode == 2 )
  421. newline();
  422. break;
  423. default:
  424. oput(ch);
  425. break;
  426. } /* End switch */
  427. formfeed(); /* next file starts on a new page? */
  428. } /* End of text */
  429. /*****************************************************************************/
  430. formfeed()
  431. {
  432. /*
  433. *
  434. * Called whenever we've finished with the last page and want to get ready for the
  435. * next one. Also used at the beginning and end of each input file, so we have to
  436. * be careful about what's done. The first time through (up to the redirect() call)
  437. * output goes to /dev/null.
  438. *
  439. * Adobe now recommends that the showpage operator occur after the page level
  440. * restore so it can be easily redefined to have side-effects in the printer's VM.
  441. * Although it seems reasonable I haven't implemented it, because it makes other
  442. * things, like selectively setting manual feed or choosing an alternate paper
  443. * tray, clumsy - at least on a per page basis.
  444. *
  445. */
  446. if ( fp_out == stdout ) /* count the last page */
  447. printed++;
  448. endline(); /* print the last line */
  449. fprintf(fp_out, "cleartomark\n");
  450. fprintf(fp_out, "showpage\n");
  451. fprintf(fp_out, "saveobj restore\n");
  452. fprintf(fp_out, "%s %d %d\n", ENDPAGE, page, printed);
  453. if ( ungetc(getc(fp_in), fp_in) == EOF )
  454. redirect(-1);
  455. else redirect(++page);
  456. fprintf(fp_out, "%s %d %d\n", PAGE, page, printed+1);
  457. fprintf(fp_out, "/saveobj save def\n");
  458. fprintf(fp_out, "mark\n");
  459. writerequest(printed+1, fp_out);
  460. fprintf(fp_out, "%d pagesetup\n", printed+1);
  461. line = 1;
  462. } /* End of formfeed */
  463. /*****************************************************************************/
  464. newline()
  465. {
  466. /*
  467. *
  468. * Called when we've read a newline character. The call to startline() ensures
  469. * that at least an empty string is on the stack.
  470. *
  471. */
  472. startline();
  473. endline(); /* print the current line */
  474. if ( ++line > linespp ) /* done with this page */
  475. formfeed();
  476. } /* End of newline */
  477. /*****************************************************************************/
  478. spaces(ch)
  479. int ch; /* next input character */
  480. {
  481. int endcol; /* ending column */
  482. int i; /* final distance - in spaces */
  483. /*
  484. *
  485. * Counts consecutive spaces, tabs, and backspaces and figures out where the next
  486. * string should start. Once that's been done we try to choose an efficient way
  487. * to output the required number of spaces. The choice is between using procedure
  488. * l with a single string on the stack and L with several string and column pairs.
  489. * We usually break even, in terms of the size of the output file, if we need four
  490. * consecutive spaces. More means using L decreases the size of the file. For now
  491. * if there are less than 6 consecutive spaces we just add them to the current
  492. * string, otherwise we end that string, follow it by its starting position, and
  493. * begin a new one that starts at endcol. Backspacing is always handled this way.
  494. *
  495. */
  496. startline(); /* so col makes sense */
  497. endcol = col;
  498. do {
  499. if ( ch == ' ' )
  500. endcol++;
  501. else if ( ch == '\t' )
  502. endcol += tabstops - ((endcol - 1) % tabstops);
  503. else if ( ch == '\b' )
  504. endcol--;
  505. else if ( ch == '\r' )
  506. endcol = 1;
  507. else break;
  508. } while ( ch = getc(fp_in) ); /* if ch is 0 we'd quit anyway */
  509. ungetc(ch, fp_in); /* wasn't a space, tab, or backspace */
  510. if ( endcol < 1 ) /* can't move past left edge */
  511. endcol = 1;
  512. if ( (i = endcol - col) >= 0 && i < 6 )
  513. for ( ; i > 0; i-- )
  514. oput((int)' ');
  515. else {
  516. endstring();
  517. col = stringstart = endcol;
  518. } /* End else */
  519. } /* End of spaces */
  520. /*****************************************************************************/
  521. startline()
  522. {
  523. /*
  524. *
  525. * Called whenever we want to be certain we're ready to start pushing characters
  526. * into an open string on the stack. If stringcount is positive we've already
  527. * started, so there's nothing to do. The first string starts in column 1.
  528. *
  529. */
  530. if ( stringcount < 1 ) {
  531. putc('(', fp_out);
  532. stringstart = col = 1;
  533. stringcount = 1;
  534. } /* End if */
  535. } /* End of startline */
  536. /*****************************************************************************/
  537. endstring()
  538. {
  539. /*
  540. *
  541. * End the current string and start a new one.
  542. *
  543. */
  544. if ( stringcount > 100 ) { /* don't put too much on the stack */
  545. fprintf(fp_out, ")%d LL\n(", stringstart-1);
  546. stringcount = 2; /* kludge - don't let endline() use l */
  547. } else {
  548. fprintf(fp_out, ")%d(", stringstart-1);
  549. stringcount++;
  550. } /* End else */
  551. } /* End of endstring */
  552. /*****************************************************************************/
  553. endline()
  554. {
  555. /*
  556. *
  557. * Generates a call to the PostScript procedure that processes all the text on
  558. * the stack - provided stringcount is positive. If one string is on the stack
  559. * the fast procedure (ie. l) is used to print the line, otherwise the slower
  560. * one that processes string and column pairs is used.
  561. *
  562. */
  563. if ( stringcount == 1 )
  564. fprintf(fp_out, ")l\n");
  565. else if ( stringcount > 1 )
  566. fprintf(fp_out, ")%d L\n", stringstart-1);
  567. stringcount = 0;
  568. } /* End of endline */
  569. /*****************************************************************************/
  570. oput(ch)
  571. int ch; /* next output character */
  572. {
  573. /*
  574. *
  575. * Responsible for adding all printing characters from the input file to the
  576. * open string on top of the stack.
  577. *
  578. */
  579. if ( isascii(ch) && isprint(ch) ) {
  580. startline();
  581. if ( ch == '(' || ch == ')' || ch == '\\' )
  582. putc('\\', fp_out);
  583. putc(ch, fp_out);
  584. col++;
  585. } else if ( extended == TRUE ) {
  586. startline();
  587. fprintf(fp_out, "\\%.3o", ch & 0377);
  588. col++;
  589. } /* End if */
  590. } /* End of oput */
  591. /*****************************************************************************/
  592. redirect(pg)
  593. int pg; /* next page we're printing */
  594. {
  595. static FILE *fp_null = NULL; /* if output is turned off */
  596. /*
  597. *
  598. * If we're not supposed to print page pg, fp_out will be directed to /dev/null,
  599. * otherwise output goes to stdout.
  600. *
  601. */
  602. if ( pg >= 0 && in_olist(pg) == ON )
  603. fp_out = stdout;
  604. else if ( (fp_out = fp_null) == NULL )
  605. fp_out = fp_null = fopen("/dev/null", "w");
  606. } /* End of redirect */
  607. /*****************************************************************************/