home *** CD-ROM | disk | FTP | other *** search
/ Amiga Plus Leser 15 / Amiga Plus Leser CD 15.iso / Tools / Development / MosaicSRC / libwww2 / HTFormat.c < prev    next >
Encoding:
C/C++ Source or Header  |  2002-03-13  |  17.0 KB  |  730 lines

  1. /*        Manage different file formats            HTFormat.c
  2. **        =============================
  3. **
  4. */
  5.  
  6. #include "HTFormat.h"
  7.  
  8. PUBLIC float HTMaxSecs = 1e10;        /* No effective limit */
  9. PUBLIC float HTMaxLength = 1e10;    /* No effective limit */
  10.  
  11. #include "HTUtils.h"
  12. #include "tcp.h"
  13.  
  14. #include "HTMLDTD.h"
  15. #include "HText.h"
  16. #include "HTAlert.h"
  17. #include "HTList.h"
  18. #include "HTInit.h"
  19. #include "HTFWriter.h"
  20. #include "HTPlain.h"
  21. #include "SGML.h"
  22. #include "HTML.h"
  23. #include "HTMLGen.h"
  24.  
  25. /* #define TRACE 1 */
  26.  
  27. /* From gui-documents.c. */
  28. extern int loading_inlined_images;
  29.  
  30.  
  31. PUBLIC    BOOL HTOutputSource = NO;    /* Flag: shortcut parser to stdout */
  32. extern    BOOL interactive;
  33.  
  34. struct _HTStream {
  35.       CONST HTStreamClass*    isa;
  36.       /* ... */
  37. };
  38.  
  39.  
  40. /* Whoooooooooooooa ugly!!! */
  41. int loading_length = -1;
  42.  
  43.  
  44. /*    Presentation methods
  45. **    --------------------
  46. */
  47.  
  48. PUBLIC    HTList * HTPresentations = 0;
  49. PUBLIC    HTPresentation* default_presentation = 0;
  50.  
  51.  
  52. /*    Define a presentation system command for a content-type
  53. **    -------------------------------------------------------
  54. */
  55. PUBLIC void HTSetPresentation ARGS5(
  56.     CONST char *, representation,
  57.     CONST char *, command,
  58.     float,    quality,
  59.     float,    secs,
  60.     float,    secs_per_byte
  61. ){
  62.  
  63.     HTPresentation * pres = (HTPresentation *)malloc(sizeof(HTPresentation));
  64.  
  65.     pres->rep = HTAtom_for(representation);
  66.     pres->rep_out = WWW_PRESENT;        /* Fixed for now ... :-) */
  67.     pres->converter = HTSaveAndExecute;     /* Fixed for now ...     */
  68.     pres->quality = quality;
  69.     pres->secs = secs;
  70.     pres->secs_per_byte = secs_per_byte;
  71.     pres->rep = HTAtom_for(representation);
  72.     pres->command = 0;
  73.     StrAllocCopy(pres->command, command);
  74.  
  75.     if (!HTPresentations) HTPresentations = HTList_new();
  76.  
  77.     if (strcmp(representation, "*")==0) {
  78.     if (default_presentation) free(default_presentation);
  79.     default_presentation = pres;
  80.     } else {
  81.     HTList_addObjectAtEnd(HTPresentations, pres);
  82.     }
  83. }
  84.  
  85.  
  86. /*    Define a built-in function for a content-type
  87. **    ---------------------------------------------
  88. */
  89. PUBLIC void HTSetConversion ARGS6(
  90.     CONST char *, representation_in,
  91.     CONST char *, representation_out,
  92.     HTConverter*,    converter,
  93.     float,    quality,
  94.     float,    secs,
  95.     float,    secs_per_byte
  96. ){
  97.  
  98.     HTPresentation * pres = (HTPresentation *)malloc(sizeof(HTPresentation));
  99.  
  100.     pres->rep = HTAtom_for(representation_in);
  101.     pres->rep_out = HTAtom_for(representation_out);
  102.     pres->converter = converter;
  103.     pres->command = NULL;        /* Fixed */
  104.     pres->quality = quality;
  105.     pres->secs = secs;
  106.     pres->secs_per_byte = secs_per_byte;
  107.     pres->command = 0;
  108.  
  109.     if (!HTPresentations) HTPresentations = HTList_new();
  110.  
  111.     if (strcmp(representation_in, "*")==0) {
  112.     if (default_presentation) free(default_presentation);
  113.     default_presentation = pres;
  114.     } else {
  115.     HTList_addObject(HTPresentations, pres);
  116.     }
  117. }
  118.  
  119.  
  120.  
  121. /*    File buffering
  122. **    --------------
  123. **
  124. **    The input file is read using the macro which can read from
  125. **    a socket or a file.
  126. **    The input buffer size, if large will give greater efficiency and
  127. **    release the server faster, and if small will save space on PCs etc.
  128. */
  129. #ifndef _DNET
  130. #define INPUT_BUFFER_SIZE 65536
  131. #else
  132. #define INPUT_BUFFER_SIZE 2048
  133. #endif
  134. PRIVATE char input_buffer[INPUT_BUFFER_SIZE];
  135. PRIVATE char * input_pointer;
  136. PRIVATE char * input_limit;
  137. PRIVATE int input_file_number;
  138.  
  139.  
  140. /*    Set up the buffering
  141. **
  142. **    These routines are public because they are in fact needed by
  143. **    many parsers, and on PCs and Macs we should not duplicate
  144. **    the static buffer area.
  145. */
  146. PUBLIC void HTInitInput ARGS1 (int,file_number)
  147. {
  148.     input_file_number = file_number;
  149.     input_pointer = input_limit = input_buffer;
  150. }
  151.  
  152. PUBLIC int interrupted_in_htgetcharacter = 0;
  153. PUBLIC char HTGetCharacter NOARGS
  154. {
  155.   char ch;
  156.   interrupted_in_htgetcharacter = 0;
  157.   do
  158.     {
  159. #ifndef _DNET
  160.       if (input_pointer >= input_limit)
  161.     {
  162.       int status =
  163.         NETREAD(input_file_number, input_buffer, INPUT_BUFFER_SIZE);
  164.       if (status <= 0)
  165.         {
  166.           if (status == 0)
  167.         return (char)EOF;
  168.           if (status == HT_INTERRUPTED)
  169.         {
  170.           if (TRACE)
  171.             fprintf (stderr, "HTFormat: Interrupted in HTGetCharacter\n");
  172.           interrupted_in_htgetcharacter = 1;
  173.           return (char)EOF;
  174.         }
  175.           if (TRACE)
  176.         fprintf(stderr,
  177.             "HTFormat: File read error %d\n", status);
  178.           return (char)EOF;
  179.         }
  180.       input_pointer = input_buffer;
  181.       input_limit = input_buffer + status;
  182.     }
  183.       ch = *input_pointer++;
  184. #else
  185.       int status=NETREAD(input_file_number,&ch,1);
  186.       if (status <= 0)
  187.     return (char)EOF;
  188. #endif
  189.     }
  190.   while (ch == (char) 13); /* Ignore ASCII carriage return */
  191.  
  192.   return ch;
  193. }
  194.  
  195. /*    Stream the data to an ouput file as binary
  196. */
  197. PUBLIC int HTOutputBinary ARGS2( int,        input,
  198.                   FILE *,    output)
  199. {
  200.   int rv;
  201.  
  202.   do
  203.     {
  204.       int status = NETREAD(input, input_buffer, INPUT_BUFFER_SIZE);
  205.       if (status <= 0)
  206.     {
  207.       if (status == 0)
  208.         return 0;
  209.       if (TRACE) fprintf(stderr,
  210.                  "HTFormat: File read error %d\n", status);
  211.       return 2;            /* Error */
  212.     }
  213.       rv = fwrite(input_buffer, sizeof(char), status, output);
  214.       if (rv != status)
  215.     {
  216.       int err = errno;
  217.       static const char msg_fmt [] = "Error writing output file (%ld)";
  218.       char *buf = malloc (strlen (msg_fmt) + 16);
  219.       extern void application_user_feedback (char *);
  220.  
  221.       if (buf)
  222.         {
  223.           sprintf (buf, msg_fmt, err);
  224.           application_user_feedback (buf);
  225.           free (buf);
  226.         }
  227.       return 2;
  228.     }
  229.     } while (YES);
  230. }
  231.  
  232.  
  233. static int partial_wildcard_matches (HTFormat r1, HTFormat r2)
  234. {
  235.   /* r1 is the presentation format we're currently looking at out
  236.      of the list we understand.  r2 is the one we need to get to. */
  237.   char *s1, *s2, *subtype1 = NULL, *subtype2 = NULL;
  238.   int i;
  239.  
  240.   s1 = HTAtom_name (r1);
  241.   s2 = HTAtom_name (r2);
  242.  
  243.   if (!s1 || !s2)
  244.     return 0;
  245.  
  246.   s1 = strdup (s1);
  247.   s2 = strdup (s2);
  248.  
  249.   for (i = 0; i < strlen (s1); i++)
  250.     if (s1[i] == '/')
  251.       {
  252.     s1[i] = '\0';
  253.     subtype1 = &(s1[i+1]);
  254.     /* Now s1 contains the main type and subtype1 contains
  255.        the subtype. */
  256.     goto done1;
  257.       }
  258.  
  259.  done1:
  260.   if (!subtype1)
  261.     goto nope;
  262.  
  263.   /* Bail if we don't have a wildcard possibility. */
  264.   if (subtype1[0] != '*')
  265.     goto nope;
  266.  
  267.   for (i = 0; i < strlen (s2); i++)
  268.     if (s2[i] == '/')
  269.       {
  270.     s2[i] = '\0';
  271.     subtype2 = &(s2[i+1]);
  272.     /* Now s2 contains the main type and subtype2 contains
  273.        the subtype. */
  274.     goto done2;
  275.       }
  276.  
  277.  done2:
  278.   if (!subtype2)
  279.     goto nope;
  280.  
  281.   /* Bail if s1 and s2 aren't the same and s1[0] isn't '*'. */
  282.   if (strcmp (s1, s2) && s1[0] != '*')
  283.     goto nope;
  284.  
  285.   /* OK, so now either we have the same main types or we have a wildcard
  286.      type for s1.  We also know that we have a wildcard possibility in
  287.      s1.  Therefore, at this point, we have a match. */
  288.   free (s1);
  289.   free (s2);
  290.   return 1;
  291.  
  292.  nope:
  293.   free (s1);
  294.   free (s2);
  295.   return 0;
  296. }
  297.  
  298.  
  299. /*        Create a filter stack
  300. **        ---------------------
  301. **
  302. **    If a wildcard match is made, a temporary HTPresentation
  303. **    structure is made to hold the destination format while the
  304. **    new stack is generated. This is just to pass the out format to
  305. **    MIME so far.  Storing the format of a stream in the stream might
  306. **    be a lot neater.
  307. */
  308. PUBLIC HTStream * HTStreamStack ARGS5(
  309.     HTFormat,        format_in,
  310.     HTFormat,        rep_out,
  311.     int,            compressed,
  312.     HTStream*,        sink,
  313.     HTParentAnchor*,    anchor)
  314. {
  315.   HTAtom * wildcard = HTAtom_for("*");
  316.   HTPresentation temp;
  317.  
  318.   /* Inherit force_dump_to_file from mo-www.c. */
  319.   extern int force_dump_to_file;
  320.  
  321.   if (TRACE)
  322.     fprintf(stderr,
  323.         "[HTStreamStack] Constructing stream stack for %s to %s\n",
  324.         HTAtom_name(format_in),
  325.         HTAtom_name(rep_out));
  326.   if (TRACE)
  327.     fprintf (stderr,
  328.          "               Compressed is %d\n", compressed);
  329.  
  330.   if (rep_out == WWW_SOURCE ||
  331.       rep_out == format_in)
  332.     {
  333.       if (TRACE)
  334.     fprintf (stderr,
  335.          "[HTStreamStack] rep_out == WWW_SOURCE | rep_out == format_in; returning sink\n");
  336.       return sink;
  337.     }
  338.  
  339.   if (!HTPresentations)
  340.     HTFormatInit();    /* set up the list */
  341.  
  342.   if (force_dump_to_file && format_in != WWW_MIME)
  343.     {
  344.       return HTSaveAndExecute (NULL, anchor, sink, format_in, compressed);
  345.     }
  346.  
  347.   {
  348.     int n = HTList_count(HTPresentations);
  349.     int i;
  350.     HTPresentation * pres;
  351.     for(i=0; i<n; i++)
  352.       {
  353.     pres = HTList_objectAt(HTPresentations, i);
  354.     if (TRACE)
  355.       {
  356.         fprintf (stderr, "HTFormat: looking at pres '%s'\n",
  357.              HTAtom_name (pres->rep));
  358.         if (pres->command)
  359.           fprintf (stderr, "HTFormat: pres->command is '%s'\n",
  360.                pres->command);
  361.         else
  362.           fprintf (stderr, "HTFormat: pres->command doesn't exist\n");
  363.       }
  364.     if (pres->rep == format_in ||
  365.         partial_wildcard_matches (pres->rep, format_in))
  366.       {
  367.         if (pres->command && strstr (pres->command, "mosaic-internal-present"))
  368.           {
  369.         if (TRACE)
  370.           fprintf (stderr, "[HTStreamStack] HEY HEY HEY caught internal-present\n");
  371.         return HTPlainPresent (pres, anchor, sink, format_in, compressed);
  372.           }
  373.         if (pres->rep_out == rep_out)
  374.           {
  375.         if (TRACE)
  376.           fprintf (stderr,
  377.                "[HTStreamStack] pres->rep_out == rep_out\n");
  378.         return (*pres->converter)(pres, anchor, sink, format_in, compressed);
  379.           }
  380.         if (pres->rep_out == wildcard)
  381.           {
  382.         if (TRACE)
  383.           fprintf (stderr,
  384.                "[HTStreamStack] pres->rep_out == wildcard\n");
  385.         temp = *pres;/* make temp conversion to needed fmt */
  386.         temp.rep_out = rep_out;     /* yuk */
  387.         return (*pres->converter)(&temp, anchor, sink, format_in, compressed);
  388.           }
  389.       }
  390.       }
  391.   }
  392.  
  393.   if (TRACE)
  394.     {
  395.       fprintf (stderr, "[HTStreamStack] Returning NULL at bottom.\n");
  396.     }
  397.  
  398.   return NULL;
  399. }
  400.  
  401.  
  402. /*        Find the cost of a filter stack
  403. **        -------------------------------
  404. **
  405. **    Must return the cost of the same stack which StreamStack would set up.
  406. **
  407. ** On entry,
  408. **    length    The size of the data to be converted
  409. */
  410. PUBLIC float HTStackValue ARGS4(
  411.     HTFormat,        format_in,
  412.     HTFormat,        rep_out,
  413.     float,            initial_value,
  414.     long int,        length)
  415. {
  416.     HTAtom * wildcard = HTAtom_for("*");
  417.  
  418.     if (TRACE) fprintf(stderr,
  419.     "HTFormat: Evaluating stream stack for %s worth %.3f to %s\n",
  420.     HTAtom_name(format_in), initial_value,
  421.     HTAtom_name(rep_out));
  422.  
  423.     if (rep_out == WWW_SOURCE ||
  424.     rep_out == format_in) return 0.0;
  425.  
  426.     if (!HTPresentations) HTFormatInit();    /* set up the list */
  427.  
  428.     {
  429.     int n = HTList_count(HTPresentations);
  430.     int i;
  431.     HTPresentation * pres;
  432.     for(i=0; i<n; i++) {
  433.         pres = HTList_objectAt(HTPresentations, i);
  434.         if (pres->rep == format_in && (
  435.             pres->rep_out == rep_out ||
  436.             pres->rep_out == wildcard)) {
  437.         float value = initial_value * pres->quality;
  438.         if (HTMaxSecs != 0.0)
  439.         value = value - (length*pres->secs_per_byte + pres->secs)
  440.                      /HTMaxSecs;
  441.         return value;
  442.         }
  443.     }
  444.     }
  445.  
  446.     return -1e30;        /* Really bad */
  447.  
  448. }
  449.  
  450.  
  451. /*    Push data from a socket down a stream
  452. **    -------------------------------------
  453. **
  454. **   This routine is responsible for creating and PRESENTING any
  455. **   graphic (or other) objects described by the file.
  456. **
  457. **   The file number given is assumed to be a TELNET stream ie containing
  458. **   CRLF at the end of lines which need to be stripped to LF for unix
  459. **   when the format is textual.
  460. **
  461. */
  462. PUBLIC int HTCopy ARGS3(int,            file_number,
  463.              HTStream*,        sink,
  464.              int,            bytes_already_read)
  465. {
  466.   HTStreamClass targetClass;
  467.   char line[256];
  468.   char *msg;
  469.   int bytes = bytes_already_read;
  470.   extern int twirl_increment;
  471.   int next_twirl = twirl_increment;
  472.   int rv = 0;
  473.  
  474.   HTClearActiveIcon();
  475.  
  476.   if (loading_length == -1)
  477.     msg = (loading_inlined_images ?
  478.        "Read %d bytes of inlined image data." :
  479.        "Read %d bytes of data.");
  480.   else
  481.     /* We have a loading_length. */
  482.     msg = (loading_inlined_images ?
  483.        "Read %d of %d bytes of inlined image data." :
  484.        "Read %d of %d bytes of data.");
  485.  
  486.  
  487.   /*    Push the data down the stream
  488.    **
  489.    */
  490.   targetClass = *(sink->isa);    /* Copy pointers to procedures */
  491.  
  492.   /*    Push binary from socket down sink
  493.    */
  494.   for(;;)
  495.     {
  496.       int status, intr;
  497.  
  498.       if (bytes > next_twirl)
  499.     {
  500.       intr = HTCheckActiveIcon(1);
  501.       next_twirl += twirl_increment;
  502.     }
  503.       else
  504.     {
  505.       intr = HTCheckActiveIcon(0);
  506.     }
  507.       if (intr)
  508.     {
  509.       HTProgress ("Data transfer interrupted.");
  510.       (*targetClass.handle_interrupt)(sink);
  511.       rv = -1;
  512.       goto ready_to_leave;
  513.     }
  514.  
  515.       status = NETREAD(file_number, input_buffer, INPUT_BUFFER_SIZE);
  516.       if (status <= 0)
  517.     {
  518.       if (status == 0)
  519.         break;
  520.       if (status == HT_INTERRUPTED)
  521.         {
  522.           HTProgress ("Data transfer interrupted.");
  523.           (*targetClass.handle_interrupt)(sink);
  524.           rv = -1;
  525.           goto ready_to_leave;
  526.         }
  527.       if (errno == ENOTCONN || errno == ECONNRESET || errno == EPIPE)
  528.         {
  529.           /* Arrrrgh, HTTP 0/1 compability problem, maybe. */
  530.           rv = -2;
  531.           goto ready_to_leave;
  532.         }
  533.       break;
  534.     }
  535.  
  536.       if (TRACE)
  537.       {
  538.     int i;
  539.  
  540.     fprintf (stderr, "HTCopy: put_block on input_buffer '", input_buffer);
  541.     for (i = 0; i < status; i++)
  542.         if (isprint (input_buffer [i]))
  543.             fputc (input_buffer [i], stderr);
  544.         else
  545.             fputc (' ', stderr);
  546.     fprintf (stderr, "'\n");
  547.       }
  548.       (*targetClass.put_block)(sink, input_buffer, status);
  549.  
  550.       bytes += status;
  551.       if (loading_length == -1)
  552.     sprintf (line, msg, bytes);
  553.       else
  554.     sprintf (line, msg, bytes, loading_length);
  555.       HTProgress (line);
  556.     } /* next bufferload */
  557.  
  558.   HTProgress (loading_inlined_images ?
  559.           "Data transfer complete." : "Data transfer complete.");
  560.  
  561.   NETCLOSE (file_number);
  562.  
  563.   /* Success. */
  564.   rv = 0;
  565.  
  566.  ready_to_leave:
  567.   /* Reset ourselves so we don't get confused. */
  568.   loading_length = -1;
  569.  
  570.   return rv;
  571. }
  572.  
  573.  
  574.  
  575. /*    Push data from a file pointer down a stream
  576. **    -------------------------------------
  577. **
  578. **   This routine is responsible for creating and PRESENTING any
  579. **   graphic (or other) objects described by the file.
  580. **
  581. **
  582. */
  583. PUBLIC void HTFileCopy ARGS2(
  584.     FILE *,         fp,
  585.     HTStream*,        sink)
  586. {
  587.     HTStreamClass targetClass;
  588.  
  589.     targetClass = *(sink->isa); /* Copy pointers to procedures */
  590.  
  591.     for(;;) {
  592.     int status = fread(input_buffer, 1, INPUT_BUFFER_SIZE, fp);
  593.     if (status == 0) { /* EOF or error */
  594.         if (ferror(fp) == 0) break;
  595.         if (TRACE) fprintf(stderr,
  596.         "HTFormat: Read error, read returns %d\n", ferror(fp));
  597.         break;
  598.     }
  599.     (*targetClass.put_block)(sink, input_buffer, status);
  600.     } /* next bufferload */
  601.  
  602.     fclose (fp);
  603.     return;
  604. }
  605.  
  606.  
  607. PUBLIC void HTFileCopyToText ARGS2(
  608.     FILE *,         fp,
  609.     HText *,        text)
  610. {
  611.   for(;;)
  612.     {
  613.       int status = fread(input_buffer, 1, INPUT_BUFFER_SIZE, fp);
  614.       if (status == 0)
  615.     { /* EOF or error */
  616.       if (ferror(fp) == 0) break;
  617.       if (TRACE) fprintf(stderr,
  618.                  "HTFormat: Read error, read returns %d\n", ferror(fp));
  619.       break;
  620.     }
  621.       HText_appendBlock (text, input_buffer, status);
  622.   } /* next bufferload */
  623.  
  624.   fclose (fp);
  625.   return;
  626. }
  627.  
  628.  
  629. /*    Parse a socket given format and file number
  630. **
  631. **   This routine is responsible for creating and PRESENTING any
  632. **   graphic (or other) objects described by the file.
  633. **
  634. **   The file number given is assumed to be a TELNET stream ie containing
  635. **   CRLF at the end of lines which need to be stripped to LF for unix
  636. **   when the format is textual.
  637. **
  638. */
  639. PUBLIC int HTParseSocket ARGS6(
  640.     HTFormat,        format_in,
  641.     HTFormat,        format_out,
  642.     HTParentAnchor *,    anchor,
  643.     int,            file_number,
  644.     HTStream*,        sink,
  645.     int,            compressed)
  646. {
  647.   HTStream * stream;
  648.   HTStreamClass targetClass;
  649.   int rv;
  650.  
  651.   stream = HTStreamStack(format_in,
  652.              format_out,
  653.              compressed,
  654.              sink, anchor);
  655.  
  656.   if (!stream)
  657.     {
  658.       char buffer[1024];    /* @@@@@@@@ */
  659.       sprintf(buffer, "Sorry, can't convert from %s to %s.",
  660.           HTAtom_name(format_in), HTAtom_name(format_out));
  661.       if (TRACE) fprintf(stderr, "HTFormat: %s\n", buffer);
  662.       return HTLoadError(sink, 501, buffer);
  663.     }
  664.  
  665.   targetClass = *(stream->isa); /* Copy pointers to procedures */
  666.   rv = HTCopy(file_number, stream, 0);
  667.   if (rv == -1)
  668.     {
  669.       /* handle_interrupt should have been done in HTCopy */
  670.       /* (*targetClass.handle_interrupt)(stream); */
  671.       return HT_INTERRUPTED;
  672.     }
  673.  
  674.   (*targetClass.end_document)(stream);
  675.  
  676.   /* New thing: we force close the data socket here, so that if
  677.      an external viewer gets forked off in the free method below,
  678.      the connection doesn't remain upon until the child exits --
  679.      which it does if we don't do this. */
  680. /*  NETCLOSE (file_number);  Already Closed... */
  681.  
  682.   (*targetClass.free)(stream);
  683.  
  684.   return HT_LOADED;
  685. }
  686.  
  687.  
  688.  
  689. /*    Parse a file given format and file pointer
  690. **
  691. **   This routine is responsible for creating and PRESENTING any
  692. **   graphic (or other) objects described by the file.
  693. **
  694. **   The file number given is assumed to be a TELNET stream ie containing
  695. **   CRLF at the end of lines which need to be stripped to LF for unix
  696. **   when the format is textual.
  697. **
  698. */
  699. PUBLIC int HTParseFile ARGS6(
  700.     HTFormat,        format_in,
  701.     HTFormat,        format_out,
  702.     HTParentAnchor *,    anchor,
  703.     FILE *,         fp,
  704.     HTStream*,        sink,
  705.     int,            compressed)
  706. {
  707.     HTStream * stream;
  708.     HTStreamClass targetClass;
  709.  
  710.     stream = HTStreamStack(format_in,
  711.                format_out,
  712.                compressed,
  713.                sink , anchor);
  714.  
  715.     if (!stream) {
  716.     char buffer[1024];    /* @@@@@@@@ */
  717.     sprintf(buffer, "Sorry, can't convert from %s to %s.",
  718.         HTAtom_name(format_in), HTAtom_name(format_out));
  719.     if (TRACE) fprintf(stderr, "HTFormat(in HTParseFile): %s\n", buffer);
  720.     return HTLoadError(sink, 501, buffer);
  721.     }
  722.  
  723.     targetClass = *(stream->isa);    /* Copy pointers to procedures */
  724.     HTFileCopy(fp, stream);
  725.     (*targetClass.end_document)(stream);
  726.     (*targetClass.free)(stream);
  727.  
  728.     return HT_LOADED;
  729. }
  730.