messages.l
上传用户:seven77cht
上传日期:2007-01-04
资源大小:486k
文件大小:18k
源码类别:

浏览器

开发平台:

Unix_Linux

  1. VAR    $[a-z_0-9]+
  2. %x ASSIGNMENT_START
  3. %x ASSIGNMENT_WORD ASSIGNMENT_DOUBLE_QUOTES ASSIGNMENT_SINGLE_QUOTES
  4. %x ALTERNATE_TEST
  5. %x ALTERNATE_SKIP_FIRST ALTERNATE_USE_FIRST
  6. %x ALTERNATE_SKIP_SECOND ALTERNATE_USE_SECOND
  7. %{
  8. /***************************************
  9.   $Header: /home/amb/wwwoffle/RCS/messages.l 1.28 2000/03/22 18:34:45 amb Exp $
  10.   WWWOFFLE - World Wide Web Offline Explorer - Version 2.5e.
  11.   Parse the HTML to create the messages and serve the local web-pages.
  12.   ******************/ /******************
  13.   Written by Andrew M. Bishop
  14.   This file Copyright 1998,99,2000 Andrew M. Bishop
  15.   It may be distributed under the GNU Public License, version 2, or
  16.   any higher version.  See section COPYING of the GNU Public license
  17.   for conditions under which this file may be redistributed.
  18.   ***************************************/
  19. #include <stdlib.h>
  20. #include <string.h>
  21. #include <ctype.h>
  22. #ifdef __STDC__
  23. #include <stdarg.h>
  24. #else
  25. #include <varargs.h>
  26. #endif
  27. #include <time.h>
  28. #include <sys/stat.h>
  29. #include <unistd.h>
  30. #include <fcntl.h>
  31. #include "version.h"
  32. #include "wwwoffle.h"
  33. #include "errors.h"
  34. #include "config.h"
  35. #include "misc.h"
  36. extern int msg_yylex(void);
  37. #define msg_yywrap() 1
  38. #define YY_NO_UNPUT
  39. static void html_message_body(char *template, va_list ap);
  40. static void write_or_append_string(char *str);
  41. static void add_variable(char *var,char *val);
  42. static void delete_variables(void);
  43. static char *get_value(char *var);
  44. static void strip_trailing_whitespace(char *string);
  45. /*+ A known fixed empty string. +*/
  46. static char *empty="";
  47. /*+ The file descriptor that we are reading from. +*/
  48. static int msg_yyfd=-1;
  49. /*+ The file descriptor that we are writing to. +*/
  50. static int out_fd=-1;
  51. /*+ The string we are appending to. +*/
  52. static char *out_str=NULL;
  53. /*+ The list of variables. +*/
  54. static char **variables=NULL;
  55. /*+ The list of values. +*/
  56. static char **values=NULL;
  57. /*+ The number of variables. +*/
  58. static int nvariables=0;
  59. /*++++++++++++++++++++++++++++++++++++++
  60.   Output a local page.
  61.   int fd The file descriptor to write to.
  62.   char *path The path of the page.
  63.   Header *request_head The request that was made for this page.
  64.   ++++++++++++++++++++++++++++++++++++++*/
  65. void LocalPage(int fd,char *path,Header *request_head)
  66. {
  67.  struct stat buf;
  68.  char *file;
  69.  /* Don't allow paths backwards */
  70.  if(strstr(path,"/../"))
  71.    {
  72.     PrintMessage(Warning,"Illegal path containing '/../' for the local page '%s'.",path);
  73.     HTMLMessage(fd,404,"WWWOFFLE Page Not Found",NULL,"PageNotFound",
  74.                 "url",path,
  75.                 NULL);
  76.     return;
  77.    }
  78.  file=(char*)malloc(strlen(path)+16);
  79.  strcpy(file,"html");
  80.  strcat(file,path);
  81.  if(path[strlen(path)-1]=='/')
  82.     strcat(file,"index.html");
  83.  if(stat(file,&buf))
  84.    {
  85.     PrintMessage(Warning,"Cannot access the local page '%s' [%!s].",file);
  86.     HTMLMessage(fd,404,"WWWOFFLE Page Not Found",NULL,"PageNotFound",
  87.                 "url",path,
  88.                 NULL);
  89.    }
  90.  else if(S_ISREG(buf.st_mode) && buf.st_mode&S_IROTH)
  91.    {
  92.     int htmlfd=open(file,O_RDONLY);
  93.     if(htmlfd==-1)
  94.       {
  95.        PrintMessage(Warning,"Cannot open the local page '%s' [%!s].",file);
  96.        HTMLMessage(fd,404,"WWWOFFLE Page Not Found",NULL,"PageNotFound",
  97.                    "url",path,
  98.                    NULL);
  99.       }
  100.     else
  101.       {
  102.        char *ims=NULL;
  103.        time_t since=0;
  104.        if((ims=GetHeader(request_head,"If-Modified-Since",NULL)))
  105.           since=DateToTimeT(ims);
  106.        if(since>=buf.st_mtime)
  107.           HTMLMessageHead(fd,304,"WWWOFFLE Not Modified",
  108.                           NULL);
  109.        else
  110.          {
  111.           char buffer[READ_BUFFER_SIZE];
  112.           int n;
  113.           HTMLMessageHead(fd,200,"WWWOFFLE Local OK",
  114.                           "Last-Modified",RFC822Date(buf.st_mtime,1),
  115.                           "Content-Type",WhatMIMEType(file),
  116.                           NULL);
  117.           init_buffer(htmlfd);
  118.           while((n=read_data(htmlfd,buffer,READ_BUFFER_SIZE))>0)
  119.              write_data(fd,buffer,n);
  120.          }
  121.        close(htmlfd);
  122.       }
  123.    }
  124.  else if(S_ISDIR(buf.st_mode))
  125.    {
  126.     char *localhost=GetLocalHost(1);
  127.     char *dir=(char*)malloc(strlen(path)+strlen(localhost)+12);
  128.     strcpy(dir,"http://");
  129.     strcat(dir,localhost);
  130.     strcat(dir,path);
  131.     strcat(dir,"/");
  132.     HTMLMessage(fd,301,"WWWOFFLE Local Dir Redirect",dir,"LocalDirRedirect",
  133.                 "dir",path,
  134.                 NULL);
  135.     free(dir);
  136.     free(localhost);
  137.    }
  138.  else
  139.    {
  140.     PrintMessage(Warning,"Not a regular file or wrong permissions for the local page '%s'.",file);
  141.     HTMLMessage(fd,404,"WWWOFFLE Page Not Found",NULL,"PageNotFound",
  142.                 "url",path,
  143.                 NULL);
  144.    }
  145. }
  146. /*++++++++++++++++++++++++++++++++++++++
  147.   Create a simple message using the template in the html directory.
  148.   char *HTMLMessage Returns a string instead of writing to a file if fd==-1.
  149.   int fd The file descriptor to write it to.
  150.   int status_val The numeric status value.
  151.   char *status_str The status string.
  152.   char *location A Location: HTTP header or NULL for none.
  153.   char *template The name of the template for the message.
  154.   ... A list of variable-value pairs to use in the parsing (NULL terminated).
  155.   ++++++++++++++++++++++++++++++++++++++*/
  156. char *HTMLMessage(int fd,int status_val,char *status_str,char *location,char *template, ...)
  157. {
  158.  va_list ap;
  159.  if(location)
  160.     HTMLMessageHead(fd,status_val,status_str,
  161.                     "Location",location,
  162.                     NULL);
  163.  else
  164.     HTMLMessageHead(fd,status_val,status_str,
  165.                     NULL);
  166. #ifdef __STDC__
  167.  va_start(ap,template);
  168. #else
  169.  va_start(ap);
  170. #endif
  171.  html_message_body(template,ap);
  172.  va_end(ap);
  173.  return(out_str);
  174. }
  175. /*++++++++++++++++++++++++++++++++++++++
  176.   Create an html header using the specified fields.
  177.   char *HTMLMessageHead Returns a string instead of writing to a file if fd==-1.
  178.   int fd The file descriptor to write it to.
  179.   int status_val The numeric status value.
  180.   char *status_str The status string.
  181.   ... A list of variable-value pairs to use in the header (NULL terminated).
  182.   ++++++++++++++++++++++++++++++++++++++*/
  183. char *HTMLMessageHead(int fd,int status_val,char *status_str, ...)
  184. {
  185.  char *headline;
  186.  char *var,*val;
  187.  va_list ap;
  188.  int content_type=0;
  189.  out_fd=fd;
  190.  out_str=NULL;
  191. #ifdef __STDC__
  192.  va_start(ap,status_str);
  193. #else
  194.  va_start(ap);
  195. #endif
  196.  /* The start of the header */
  197.  headline=(char*)malloc(strlen(status_str)+32);
  198.  sprintf(headline,"HTTP/1.0 %d %srn",status_val,status_str);
  199.  write_or_append_string(headline);
  200.  free(headline);
  201.  write_or_append_string("Server: WWWOFFLE/" WWWOFFLE_VERSION "rn");
  202.  write_or_append_string("Date: ");
  203.  write_or_append_string(RFC822Date(time(NULL),1));
  204.  write_or_append_string("rn");
  205.  /* Start filling in the header. */
  206.  while((var=va_arg(ap,char*)))
  207.    {
  208.     val=va_arg(ap,char*);
  209.     if(!strcmp(var,"Content-Type"))
  210.        content_type=1;
  211.     if(val)
  212.       {
  213.        write_or_append_string(var);
  214.        write_or_append_string(": ");
  215.        write_or_append_string(val);
  216.        write_or_append_string("rn");
  217.       }
  218.    }
  219.  va_end(ap);
  220.  /* The end of the header. */
  221.  if(!content_type)
  222.     write_or_append_string("Content-type: text/htmlrn");
  223.  write_or_append_string("rn");
  224.  return(out_str);
  225. }
  226. /*++++++++++++++++++++++++++++++++++++++
  227.   Create a HTML message body by passing the specified template through the micro-language processor.
  228.   char *HTMLMessageBody Returns a string instead of writing to a file if fd==-1.
  229.   int fd The file descriptor to write to.
  230.   char *template The name of the template for the message.
  231.   ... A list of variable-value pairs to use in the parsing (NULL terminated).
  232.   ++++++++++++++++++++++++++++++++++++++*/
  233. char *HTMLMessageBody(int fd,char *template, ...)
  234. {
  235.  va_list ap;
  236.  out_fd=fd;
  237.  out_str=NULL;
  238. #ifdef __STDC__
  239.  va_start(ap,template);
  240. #else
  241.  va_start(ap);
  242. #endif
  243.  html_message_body(template,ap);
  244.  va_end(ap);
  245.  return(out_str);
  246. }
  247. /*++++++++++++++++++++++++++++++++++++++
  248.   Create a HTML message body by passing the specified template through the micro-language processor.
  249.   int fd The file descriptor to write to.
  250.   char *template The name of the template for the message.
  251.   va_list ap A list of variable-value pairs to use in the parsing (NULL terminated).
  252.   ++++++++++++++++++++++++++++++++++++++*/
  253. static void html_message_body(char *template,va_list ap)
  254. {
  255.  char *localhost;
  256.  char *file,*var,*val;
  257.  static int first=1;
  258.  /* Set up the variables. */
  259.  localhost=GetLocalHost(1);
  260.  add_variable("localhost",localhost);
  261.  while((var=va_arg(ap,char*)))
  262.    {
  263.     val=va_arg(ap,char*);
  264.     add_variable(var,val);
  265.    }
  266.  /* Open the template file. */
  267.  file=(char*)malloc(sizeof("html/messages/")+strlen(template)+sizeof(".html")+1);
  268.  strcpy(file,"html/messages/");
  269.  strcat(file,template);
  270.  strcat(file,".html");
  271.  msg_yyfd=open(file,O_RDONLY);
  272.  if(msg_yyfd==-1)
  273.     PrintMessage(Fatal,"Cannot open the message template '%s' [%!s].",file);
  274.  init_buffer(msg_yyfd);
  275.  /* Parse the template and fill in the gaps. */
  276.  if(!first)
  277.     msg_yyrestart(NULL);
  278.  msg_yylex();
  279.  close(msg_yyfd);
  280.  delete_variables();
  281.  first=0;
  282. }
  283. /*++++++++++++++++++++++++++++++++++++++
  284.   Write the string to the file descriptor out_fd or append to the string out_str.
  285.   char *str The string to write or append.
  286.   ++++++++++++++++++++++++++++++++++++++*/
  287. static void write_or_append_string(char *str)
  288. {
  289.  if(out_fd==-1)
  290.    {
  291.     if(out_str)
  292.       {
  293.        out_str=(char*)realloc((void*)out_str,strlen(out_str)+strlen(str)+1);
  294.        strcat(out_str,str);
  295.       }
  296.     else
  297.       {
  298.        out_str=(char*)malloc(256+strlen(str));
  299.        strcpy(out_str,str);
  300.       }
  301.    }
  302.  else
  303.     write_string(out_fd,str);
  304. }
  305. /*++++++++++++++++++++++++++++++++++++++
  306.   Add a new variable.
  307.   char *var The variable to add.
  308.   char *val The value of the variable.
  309.   ++++++++++++++++++++++++++++++++++++++*/
  310. static void add_variable(char *var,char *val)
  311. {
  312.  int i;
  313.  for(i=0;i<nvariables;i++)
  314.     if(!strcmp(var,variables[i]))
  315.       {
  316.        if(values[i]!=empty)
  317.           free(values[i]);
  318.        if(val)
  319.          {values[i]=(char*)malloc(strlen(val)+1); strcpy(values[i],val);}
  320.        else
  321.           values[i]=empty;
  322.        return;
  323.       }
  324.  if(nvariables==0)
  325.    {
  326.     variables=(char**)malloc(8*sizeof(char*));
  327.     values   =(char**)malloc(8*sizeof(char*));
  328.    }
  329.  else if(nvariables%8==0)
  330.    {
  331.     variables=(char**)realloc((void*)variables,(nvariables+8)*sizeof(char*));
  332.     values   =(char**)realloc((void*)values   ,(nvariables+8)*sizeof(char*));
  333.    }
  334.  variables[nvariables]=(char*)malloc(strlen(var)+1); strcpy(variables[nvariables],var);
  335.  if(val)
  336.    {values[nvariables]=(char*)malloc(strlen(val)+1); strcpy(values[nvariables],val);}
  337.  else
  338.     values[i]=empty;
  339.  nvariables++;
  340. }
  341. /*++++++++++++++++++++++++++++++++++++++
  342.   Delete all of the variables.
  343.   ++++++++++++++++++++++++++++++++++++++*/
  344. static void delete_variables(void)
  345. {
  346.  int i;
  347.  for(i=0;i<nvariables;i++)
  348.    {
  349.     free(variables[i]);
  350.     if(values[i]!=empty)
  351.        free(values[i]);
  352.    }
  353.  if(nvariables)
  354.    {
  355.     free(variables);
  356.     free(values);
  357.     nvariables=0;
  358.     variables=NULL;
  359.     values=NULL;
  360.    }
  361. }
  362. /*++++++++++++++++++++++++++++++++++++++
  363.   Get the value of the named variable.
  364.   char *get_value Return the value of the variable or an empty string.
  365.   char *var The variable to get the value of.
  366.   ++++++++++++++++++++++++++++++++++++++*/
  367. static char *get_value(char *var)
  368. {
  369.  int i;
  370.  for(i=0;i<nvariables;i++)
  371.     if(!strcmp(var,variables[i]))
  372.        return(values[i]);
  373.  return("");
  374. }
  375. /*++++++++++++++++++++++++++++++++++++++
  376.   Strip trailing white-space (this is done so that the lex is simpler, no variable trailing context).
  377.   char *string The string to strip the spaces from.
  378.   ++++++++++++++++++++++++++++++++++++++*/
  379. static void strip_trailing_whitespace(char *string)
  380. {
  381.  char *p=string;
  382.  while(*p && !isspace(*p))
  383.     p++;
  384.  *p=0;
  385. }
  386. /*+ A macro to read data that can be used by the lexer. +*/
  387. #define YY_INPUT(buf,result,max_size) 
  388.         if((result=read_data(msg_yyfd,buf,max_size))==-1) 
  389.            result=0;
  390. /*+ A macro to append a string to the end of the existing one. +*/
  391. #define APPEND_VAL(xxx) 
  392.  if(val) 
  393.    { 
  394.     val=(char*)realloc((void*)val,strlen(val)+strlen(xxx)+1); 
  395.     strcat(val,xxx); 
  396.    } 
  397.  else 
  398.    { 
  399.     val=(char*)malloc(strlen(xxx)+1); 
  400.     strcpy(val,xxx); 
  401.    } 
  402. }
  403. %}
  404. %%
  405.  char *var=NULL;
  406.  char *val=NULL;
  407.  int previous=INITIAL;
  408.  int any=0;
  409. [^$\n]+                               { write_or_append_string(msg_yytext); any++; }
  410. n                                      { write_or_append_string(msg_yytext); any=0; }
  411. \["'${}?]                            { write_or_append_string(msg_yytext+1); any++; }
  412. $                                      { write_or_append_string(msg_yytext); any++; }
  413. {VAR}                                   { char *str=get_value(msg_yytext+1); write_or_append_string(str); }
  414. {VAR}[ t]*/=                           { strip_trailing_whitespace(msg_yytext);
  415.                                           var=(char*)malloc(strlen(msg_yytext)); strcpy(var,msg_yytext+1); val=NULL;
  416.                                           previous=INITIAL; val=NULL; BEGIN(ASSIGNMENT_START); }
  417. {VAR}[ t]*/?                          { strip_trailing_whitespace(msg_yytext);
  418.                                           val=get_value(msg_yytext+1); while(input()!='{');
  419.                                           if(*val) BEGIN(ALTERNATE_USE_FIRST); else BEGIN(ALTERNATE_SKIP_FIRST); }
  420. {VAR}[ t]*/?=                         { strip_trailing_whitespace(msg_yytext);
  421.                                           val=get_value(msg_yytext+1); while(input()!='=');
  422.                                           BEGIN(ALTERNATE_TEST); }
  423. <ASSIGNMENT_START>[^'" trn=}]      { yyless(0); BEGIN(ASSIGNMENT_WORD); }
  424. <ASSIGNMENT_START>"                    { BEGIN(ASSIGNMENT_DOUBLE_QUOTES); }
  425. <ASSIGNMENT_START>'                    { BEGIN(ASSIGNMENT_SINGLE_QUOTES); }
  426. <ASSIGNMENT_START>[ t]+                { }
  427. <ASSIGNMENT_START>=                     { }
  428. <ASSIGNMENT_START>}                    { yyless(0); add_variable(var,NULL); free(var); BEGIN(previous); }
  429. <ASSIGNMENT_START>r*n                 { add_variable(var,NULL); free(var); BEGIN(previous); }
  430. <ASSIGNMENT_WORD>[^}$ trn]           { APPEND_VAL(msg_yytext); }
  431. <ASSIGNMENT_WORD>$                     { APPEND_VAL("$"); }
  432. <ASSIGNMENT_WORD>\}                   { APPEND_VAL("}"); }
  433. <ASSIGNMENT_WORD>{VAR}                  { APPEND_VAL(get_value(msg_yytext+1)); }
  434. <ASSIGNMENT_WORD>}                     { yyless(0); add_variable(var,val); if(val)free(val); free(var); BEGIN(previous); }
  435. <ASSIGNMENT_WORD>[ trn]+             { add_variable(var,val); if(val)free(val); free(var); BEGIN(previous); }
  436. <ASSIGNMENT_DOUBLE_QUOTES>[^\"$]+     { APPEND_VAL(msg_yytext); }
  437. <ASSIGNMENT_DOUBLE_QUOTES>\"          { APPEND_VAL("""); }
  438. <ASSIGNMENT_DOUBLE_QUOTES>\            { APPEND_VAL("\"); }
  439. <ASSIGNMENT_DOUBLE_QUOTES>$            { APPEND_VAL("$"); }
  440. <ASSIGNMENT_DOUBLE_QUOTES>{VAR}         { APPEND_VAL(get_value(msg_yytext+1)); }
  441. <ASSIGNMENT_DOUBLE_QUOTES>"            { add_variable(var,val); if(val)free(val); free(var); BEGIN(previous); }
  442. <ASSIGNMENT_SINGLE_QUOTES>[^\'$]+     { APPEND_VAL(msg_yytext); }
  443. <ASSIGNMENT_SINGLE_QUOTES>\'          { APPEND_VAL("'"); }
  444. <ASSIGNMENT_SINGLE_QUOTES>\            { APPEND_VAL("\"); }
  445. <ASSIGNMENT_SINGLE_QUOTES>$            { APPEND_VAL("$"); }
  446. <ASSIGNMENT_SINGLE_QUOTES>{VAR}         { APPEND_VAL(get_value(msg_yytext+1)); }
  447. <ASSIGNMENT_SINGLE_QUOTES>'            { add_variable(var,val); if(val)free(val); free(var); BEGIN(previous); }
  448. <ALTERNATE_TEST>[ t]+                  { }
  449. <ALTERNATE_TEST>[^'" trn{]+        { while(input()!='{');
  450.                                           if(!strcmp(val,msg_yytext)) BEGIN(ALTERNATE_USE_FIRST); else BEGIN(ALTERNATE_SKIP_FIRST); }
  451. <ALTERNATE_TEST>["'rn{]             { BEGIN(INITIAL); }
  452. <ALTERNATE_USE_FIRST>[^\}$n]+         { write_or_append_string(msg_yytext); any++; }
  453. <ALTERNATE_USE_FIRST>n                 { if(any) write_or_append_string(msg_yytext); any=0; }
  454. <ALTERNATE_USE_FIRST>\}               { write_or_append_string("}"); any++; }
  455. <ALTERNATE_USE_FIRST>\$               { write_or_append_string("$"); any++; }
  456. <ALTERNATE_USE_FIRST>\                 { write_or_append_string(msg_yytext); any++; }
  457. <ALTERNATE_USE_FIRST>$                 { write_or_append_string(msg_yytext); any++; }
  458. <ALTERNATE_USE_FIRST>{VAR}              { char *str=get_value(msg_yytext+1); write_or_append_string(str); any++; }
  459. <ALTERNATE_USE_FIRST>{VAR}[ t]*/=      { strip_trailing_whitespace(msg_yytext);
  460.                                           var=(char*)malloc(strlen(msg_yytext)); strcpy(var,msg_yytext+1);
  461.                                           previous=ALTERNATE_USE_FIRST; val=NULL; BEGIN(ASSIGNMENT_START); }
  462. <ALTERNATE_USE_FIRST>}[^{]*{          { BEGIN(ALTERNATE_SKIP_SECOND); }
  463. <ALTERNATE_SKIP_FIRST>[^\}]+           { }
  464. <ALTERNATE_SKIP_FIRST>\}              { }
  465. <ALTERNATE_SKIP_FIRST>\                { }
  466. <ALTERNATE_SKIP_FIRST>}[^{]*{         { BEGIN(ALTERNATE_USE_SECOND); }
  467. <ALTERNATE_USE_SECOND>[^\}$n]+        { write_or_append_string(msg_yytext); any++; }
  468. <ALTERNATE_USE_SECOND>n                { if(any) write_or_append_string(msg_yytext); any=0; }
  469. <ALTERNATE_USE_SECOND>\}              { write_or_append_string("}"); any++; }
  470. <ALTERNATE_USE_SECOND>\$              { write_or_append_string("$"); any++; }
  471. <ALTERNATE_USE_SECOND>\                { write_or_append_string(msg_yytext); any++; }
  472. <ALTERNATE_USE_SECOND>$                { write_or_append_string(msg_yytext); any++; }
  473. <ALTERNATE_USE_SECOND>{VAR}             { char *str=get_value(msg_yytext+1); write_or_append_string(str); any++; }
  474. <ALTERNATE_USE_SECOND>{VAR}[ t]*/=     { strip_trailing_whitespace(msg_yytext);
  475.                                           var=(char*)malloc(strlen(msg_yytext)); strcpy(var,msg_yytext+1);
  476.                                           previous=ALTERNATE_USE_SECOND; val=NULL; BEGIN(ASSIGNMENT_START); }
  477. <ALTERNATE_USE_SECOND>}[ t]*r*n     { BEGIN(INITIAL); if(any) write_or_append_string("n"); any=0; }
  478. <ALTERNATE_USE_SECOND>}[ t]+          { BEGIN(INITIAL); write_or_append_string(" "); }
  479. <ALTERNATE_USE_SECOND>}                { BEGIN(INITIAL); }
  480. <ALTERNATE_SKIP_SECOND>[^\}]+         { }
  481. <ALTERNATE_SKIP_SECOND>\}             { }
  482. <ALTERNATE_SKIP_SECOND>\               { }
  483. <ALTERNATE_SKIP_SECOND>}[ t]*r*n    { BEGIN(INITIAL); if(any) write_or_append_string("n"); any=0; }
  484. <ALTERNATE_SKIP_SECOND>}[ t]+         { BEGIN(INITIAL); write_or_append_string(" "); }
  485. <ALTERNATE_SKIP_SECOND>}               { BEGIN(INITIAL); }
  486. %%