aboutsummaryrefslogblamecommitdiffstats
path: root/file.c
blob: 27dfce0d000834a593768fa14f519c80777ab140 (plain) (tree)
1
2
3
4
5
6
7
8
9
                                                        




                      
                                                
                     
      

                  

                     
                  













                                               

                            
                                             
                                               
                                                       

                                         
                                                                    
                                              
                                                               
                     
                                        

                                            
                                                         

                                    
                     







                                               






                                     
                     










                               

                                   




                                                 
                  

                             
                                 
                      

                                       




                                   
                                
                                   

                                          

      















                                                                                                         


                            
                         

                                                              

                       
                                     

                    


                    


                      
                               




                                                        
                               


                                                               
                   




                                                       









                              


      
                  



                                   
                 


                   
                       
                            
                                                

                                              
                                                   

                                                 
                                              

                                                      
                                                 


                                                               
  
                 
 

                          







                             
                      

                         

                                                                    




                 
                         
                         
                                                                            






                                                




                                                                   




                                                          

                                                       



















                                                     
                                                                          





                                              

                                                                        












                                                              
                                  





                                     









                                                                       





                                          
                                  
 


                                                                    
     
                







                                              
                                  



                     
             
                       





                                                                     
     
                                  
                    
 









                                              

                                      











                               





















                                                                 
                                                                   









                                                          
                                        




                   


                                         
                                      








                              

                                              









                                            
                                                                

















                                                                    
                                                 


                                     
                         
                                      
                              





                                          



               
                                                        


                 




                                                     














                                                
                




                                      


                                         
                               


                           
                                                     

 
   
                                                   


































                                                               
 


                
                      
 
              
            
                   
 
                                          
                                                                        



                            
                       
                    



                                



      
    
                                                                
 



                 






                                 
                     










                                                         






                                              
                                             



                                                         







                                                

                               
                                       
                                                                                




                                        
                                           












                                                 




                                                                



                                                
                                                                  
                                                                          
                     





                                                                 
                     


                                                     
                                                               
             

                

                                                       




                           
                                               
                           

                               
                                                                                   
                                           








                                                                       




























                                                                            


                                                       



                                                                        
                                          


                                   










                                                                            







                                                                              
                                                                     



































                                                               
                                                        
                                   
                                                  
                 
                                                             
                                                                                             
                                                           
                 

                                                            
                 

                                                          



                                                           

                                                             
                 

                                                             
                 
                                                          
                                                
                                
                 
                                                                 
                                                
                                      














                                                               

                                                                            

                                                                       




                                                                            
                          

                                                                               
                                                                   
                                                                     







                                                                              
                     
                                                              



                                                                           


                                                                      
                                                                                 

                                                                 

                                                                                          
                                                 
                                                                       





                                                                              







                                                                   





                                              
                                           












                                                
                     


                          

                    


      
                                     




                            
                                                                     











                                                                   
                             









                                            












                                                 









                                                                         
                                                  



                                                                 
          
                          
 

































                                             
                                                














                                       
                    

               
 


                   




                    









                                    
     














                                                    
     





































                                                                  


          
                                                                     
                                              



                          
                                                                












































                                                                      
                                               



                                     
                           
 








                                                                         





                                                                               





















                                                                          
                                                                          















                                                                  


                                         





























                                                                               

      



                                                                       

       
                                                                       






                                                                  






                                                                          


                                                          






                                                                    




                  



                                       


















































                                                                          
                 








                                                                   
                       
                                                                   
                                                                              






                                                                    

                                           
                                                    


                                                                       

                     







                                                                 







                                                         

                                                                  



                   
                    

                                              



                                                                         

                    
                                                           





                                                                 

                        


                                                                   







                                                            


                                                           

        

                                                                   


                                                                     
                                                                                
                                                            

              
















                                                                              
                              
             
                  











                                                                            
 




                                                                               
                         


                                                                             
     


                                                                       
      
             
         



                                                             
                                                        
                                         
     


              



                                                                   


                                                                               

 



                                       

                                                                       
 
                                   
                                       

                                 






                                                                    
                                           


                                      


                                       
                
                                




                             
           



                                 

                                 
                                                                            
                              




                           


                                                                                     

                           




















                                                               
                 




                               
                                                
                                

                                               



                                                                              
                                                                          
                                                  

                                                               
                                                                  



                                 
                                                    
                                      
                                        





                                                   
                  



























                                                               
                         
                              
                                        
                              
                                                                               

                                                       
                         
             
      


                                                                           
         
                    

     
                                 



                                     



                    

                                    
                                  
                          
                       
                                 


                             
                    







                                     

                             

                                        
                      





                                             
                                                                   
                                                
                                                          


                                                               
                          

                                                                            



                                                 
                                             
                                          

                                                           
                            
                




                                                

                                                                             
                                                                            

                                              

                             

                                                                  
                                




























                                                                                





                                                   
         

                                                         
                                                                        


                                                          


                                                                    


                                                                              


                                                                          


                                 

                                      
                                             
                                









                                                                      



                                                                            
                                                   
                                                                  
                                                                        


                                 

                                      
                                             
                                

                            
                                         



                                     
                                                       
 



                                     

                                                                   

               
                                                              



































                                                             
     



                                                                 
                
      
                   









                                                  
     





                                                       
                            
                                  
                               
                                         
                                                                  


                             
      
     


                                     

































                                                                        



                                             


                          



                                    
 



                                                            

                           
                   
                          
                       
                                 

                                                              

                                     
                                                              
                                        
                                                                         

            

                                                   

                                   
                                                              
                        



                                          
                                        

                                                      


                                                      






                                                         









                                                              
                    


                                 


                 




                                    

                                                                 
                                                                       

                               





                                                  

                                                                 

                                                                             





                                                                             

                                                             
                              
                           
                                     


                     
                              
                           
                                     

                                         
                                         

                                                                          

                  


                                                                      

                                           
                                                                      
                                




                             
                          

                                                 


                                      

                                                   
      
                                       
                                                                              
                     
                                                     
                    





                                                                     
                                        




                                    

                                         
                                  
      
                 
                                       
                                  
      

                                   
                       
                                         
                          
                 


                                                                   
                                                    
                                                
                                                    
                                
                                               
                    

                                                            

                                                                               
      



                                          




                                                


             

                             



                                                     
                           
                      
                   
                             




















































                                                                       
                  
                                         
      














                         
                  

                                         
      








                    

                  

                                            
                 


                                                   
                 

                                                 
                 

                                                   
                 
















                                                          
                 
                                                           
                 

                                            
                 



                                                   



                 













                                       
             



                                       
             













































                                                                 

                                                 



                            

                                                          











                                           

                                                          




                                           

                                                 






































                                                                              
                                                                   

                                             
                                                                               










































                                                               
                                       























                                                             
                                                






















                                             

                                                  



                                                 
                                              
                                         
                                                  
                  
                                                        





















                                                         
            











                                                                          
                                 

















































                                                                           

                                                                               







                                                                        

                                                                               
                       


                                                  
                                                               

             
                   








                                                                              
                         



































                                                                         
                                           







                                         
                               
















                                                       

     







                                                      


                                                                            












































                                                       

                                                               




                                        
                
                                    
                     












                                                                
                  
                                          
                     





                                              
                                 
                                
                 
















                                      

                                  
















                                                       

                                                        
               
 

                                  

                            








                                        
                                             
                




                                                                   
                                              













                                                                
                                     
                
                                                         





                                                  
                                                          








                                               
    

                                                                      
 
                                

















                                                      

         
                                       




























                                                                      
                           







                                                  
                                                                               



                                     
 
                                                            
                                        











                                                                          
                                                                   









                                         
     
                                                       





























                                                                            






































                                                                  
   
                                              
 
                                        
                

                                                        



                                                
                        



                                                
                        

                                           

                                             
           












                                                                   

                                            











                                                        

                                            













                                                     



                                              



                        
                                                      

                          
                                                        

                         
                                                       



                  
            
                 
                            
                                                    


                                                 
                                                    
                                                                            
                                                         


                                                                   
     




                             









                                                                     

                                                             


















                                                                            
                       
     
        




                                                         
                
                                           

                           

                                             
                                     
                            




                                         

























































                                                                     
                    







                                                                          
                                                 


            
                                             
                     




                                          
                  
                                                                 
                                       

                                   
                                           

                      

                                            
                                                       
                                           


                                                       
                         


                                           



                                               
                     
                                                 
                                                       
                                                                         



                                                       

                         

         
                          




                              
          




                                                      
                             










                                  
     

                                    
                
                                        
            
                                          
                         
     





                          
                                            



                  



               
                                                    












                                                                            
                                     
 
                                 
                             
                   


                  

                                                           
                                                
     

                       
 













                                                 
                                             














                               




                                                                      







                                                                  
                           


                         
                                   









                              

                                                                         
                                                                              



                                      
          
                                       











                                                    













                                                                        
























                                                              

                               
                   
                                                  
                
                                      


                                  




































                                          
   
                                      
 
                   

            

                                                           
                                                

     



                                                              
 
                  
                              
                                                                

                                                                                  
                                      




                                                                       


                                             

        




                                                 
               








                           
                              




                                                                               

                                                            

        

















                                                      
                                                                    

































































                                                                           
                              



                                                           
                                                               


                                                 




                                                                       




                                                                             



                                                           
                                      
                                                                




                                           
                                                   
 
                   
            
 

                                                           
                                                

     




                                                  




                                                                    


                                                  


                                    


                                                                 

                                                                  


                                        
 
               





                        
          
 
                             

                    

                                                                      





                                                                    
                              
                                       

                                           
                              

                                                       
                 







                        
                             
               
                             



                         


                               







                                                                       






                                                               
                 


                             
                              
                                                 
                                 
        
                  



                                             
                                                    

                     
                                                   

                    
                                                  


                     
                                         


                                                       
              





                                                       
                                           


               
                 
           















                        
           



















                                                         
                                                
 

                                                 
 
                                 









                                                          
                                                          
                              
                                               



                                               

                                            
 









                                                


                                              
                                                





                                                            
                              


                                                                             
                                                             













                                                                            
                                                        



                





                                     





                     



                                                     
                                   






















































                                                                        
                                                               
        
                                                           






                                                                       


                           



                              


























                                                                                
                            
                                          
                            
                                          
                            




                        





                                              
                                         


                               

                             









                                                    
 


                                                                     
                                                 














                               





























































                                                                     
                

                                                                              

                                                                  










                                                                          


                                                                              

                                                                  
















                                                                              
                



                                                                              

                                                                      


                                               
                                                                            





                                                          
                                                 



                                                     
                                                                          





                    
                
                              

                                                                   




                                                                              
                                                        





                                          
                


                                                                              

                                                                      






                                                    
                




























                                                                              


                                                                              






                                                            
                                                     





































                                                                 
                
















                                                                     
                


                                                       

                                                                   







                                                                              



                                     



                                     




                                           
                 



                                                     










                                                                              
                       
                




                                                                          
                


                                                                          




                                                
                              


                                                                 
                                  





                                                                          
                                  

                                                                      
                             

                  
                                                  
                
                                          
                                                                              



                                                                      






                                                                          




                                                                  




























                                                                     
                        
                





                                                                              
                 
                          
                






                                                                              
                 
                      
                  
                        
                
















                                                                              
                 
                        
                    
                







                                                                              

                     

                                      

                    

                                     
                 


                                 
                 


                                

                 
                             




                                                    
                                                    
                                                      






                                                         
                                                       
                                     
 

                                            








                                                          
                                             




                                                   











                                                               







                                                             
                        










                                                               
                    
                                  










































                                                                               
                                                                          










                                                       
                





                                                                              
                




                                                                              
                




                                                                              










                                                                              




                                                                          
                

                                                                              


                                           
                 
                     
                

                                                                          

                         
                    
                                  




                                           
                                  


                                           
                                  
                                      


                                   
                          







                                           
                                  


                                                  
                                
                                        

                         




                                           






                                                          
                                           
                                   
                                           















                                                                         
                                                




                                                  






                                                                       
             
                                          
                                                                    

                                               
                                                                      





                                                                   
             


                   







                                                      




                    



                                                 

                    



                                                 
                 











                                               
                  

                                                 

                    

                                                 
                 
                  
                                            
                                      



                    
                                            
                                      

                    
                                            
                                      
                 



                     



                                                         
                                  








                                                                    
                                  








                                                                  
                                  




                                                                           
                   


                                                                
                                  
                                                                               


                                             

                                  
                                             
                   
                     

                 
                                    






















                                                       
                                                          


                                                         
                                  


                                         
                       



                                                 
                      

               
                     
                  
      



















                                                            





                                     




                                     



                                                                      



                               







                                             

                                                      
                      

                























































                                                                      






                                                                         
                     
                                     
                                        
                           

                              

                                                                    
                                                                   
                     
                                                                

                                                               



                                                                   

                                                                 






                                                                      


                                                                        

                                            
                                                              
                                                                       
                                                                       
                                                                  












                                                                     




                                      

                                                                 



















                                                                              

                                                                 

                                                               
                                                                            









                                                                   

                                                                              
                                                



                                                                          























                                                                               













                                                         
                                                



                                                
                                 

                                     
                                                                   
                                                                     

                                                                              


                                                                               




                                            

                                                                          


                                                           
























                                                                            

                                                                              





                                                                
                                     


                                                             















                                                                       



                                                                  
                                                   
                                               






                                         
                                                                              

                                                                  
                                   

                                                               

                                                                  







                                                                  
                                                      
                                                                 













                                                                    

                                                                         


                                                                       

                                                                      








                                                                            
                                                          




                                                                             

                                                               



                                                          
                                                                  

                                                                  
                          













                                                                             
                                                                           




                                                                        
                                                                              








                                                                      






                                               

                                                                     




















































                                                                              

                                                                 
                                                          



                                      

                                                                







                                                       

                                                                
                                                               
                 
                                     






                                                                     


                                             


                                                                    



                                            

                                       
                     
                            
      


                                        






                                                                


                                                             
                   
                                             


                                   

 


                                            



































                                                                              
                          

 
    
                                            



















                                              
                                              










                                                       
                  
 
                  



                                                                           
                                              

                                                         
                            
                         
                                                    












                                                                        
            







                                     
                                 























                                                             
                                                                        

                  



                                          
               
                             

                                       






                                                      

                                                                        
                                      


                  

                      
              






                                                          

                           
                           
                                                                      
                       
                                                                    
                          
 
                                                          


                             

                                           
                  
                                                               


                                                                               


                                           




                                                                           
                                                                           
                                                                    
                                 
                 
             



                                                                               
                                           
                              

         
                                                                        
                                               

                         
                                                      
                                                



                                                                             

                                         
             




                                      


                                           
                                     

                                 
             
                         
                                                                            

                                                              
                 
                               
             




                                       
                         






                                     

         
                  





                                                                   
                                                                          






                                                       
                                                                   
                                                                           



                                                                      
                                                                    


                                              

                                        


















                                                                       
                      




                         

                                
                                                  

                                 

                              
                                                

















                                                                  
                                         
                     















                                                                
                 
                               
                                               










                                                                               
                 




                                                                          

                          









                                                               
                      



                                                               
                 

                                                            

                  






                                                                              
                 
                      
                 
















                                                                                 
                     
                                                





                                                                              
             



                                                          
 

                                                          
 





                                                                             
                  

                                                     
                                                 

                                                                    
                  
                                           
                                                 

                                                    


             
                                                                 
























                                                                



                           
                                                   
                     
                            







                                         
                                              
                                                                        




                              
                     
                           
                                                


                                                                           


















                                                                   

                                                         










                                                
                                          





                                              

                                                      
                                          
                                         









                                                                  
                   
                                                  





                    


                                                          

      
                                       




                              
                        




                                                    
                                                                            


      
                                                       
















                                                               
                                               


                                        
                    







                                     
                           





















                                                                        
                                      























                                                                           
                           














                                                         
                                                                     

              
                                                       







                                                                   
                                                                












                                
                                                       





                                

                            
















                             
    














                                                                            

                                            


                                          

                                         



                                              
                                                        



                                         






                                                          







                                                                 




                                                                           
                                                       
                                                                 

                                                                         
                           


                                                                             
                 
                            

                                                             
      



                                                
      



                                        

                                                           
                                                                  




                                                                       

                                                    

                                                

         
                                                 

                                          



                                                               


                                                

                                        





                                           
                                                        


     
    
                                                                    

                                           

                       



                                      
                
                            
      
                                                            
 
                     

                        
                                
                                            

                  
                            
                                                        


                                                 


                   
                 










                                                      





                                                



                         

                                         









                                                                             



                                                                  

                                        
                      
 


                                                        
                                                          


                               
                        







                                                             



                                                                       



                                           



                                              


                                    

                                   
                                        



                            
                 
                                                    
                                                               
                                    



                                         
                                

         
                        
                                                                  





                                     

                                                          



                                                 





                                                      
                          
                       
                                 
                                              


                      


                                 

                                              



                                      
                      
                   
                             

                                 
                           
                                                


                                    
                                        








                                                  
                                                            
                   
            
                              


                                          


                                 


                              



                                        

                                                   
                                




                                          
     
 
                                          
 
                      
                   
                             





                                                

                                     
                   
                                                  
            
                    









                                      
                                                   
 
                                          
                           



                                                            


                                              


                                  
                        

                                        
                      
 


                                      
                                                          










                                                           
                                                       
                      














                                            
                                                                        


                                        





















                                

                                                                  


                                                                            


















                                                  


                                                











                                                
                                                
 
                              

                 
                                  

               
                                   
                     

                    
                                                            







                                              

                                        
                      
 

                                                       







                                           
                                                          









                                                                



                                                                       



                                            



                                              


                                    

                                   
                                        

                         








                                                                

                                                  
                     



                                                       
                     




                                                    
                      
                   
                             
                                        
                                           










                                                









                                            
                   


                             

                         


                                                        

                                                                      







                                                                
                    




                                 
                




                                    
                     

               

                                              
                                          
                                                              

                                         
                                                                      

                                       
                                       
                                      
 







                                                  
                                       



                  














                                                                 

                             








                                                  


                                   
    
                                 


                             



                        

                                                         
      
 


                                    


                               
      
                                                     














                                                            
                                                                         









                                                
                                                                        

                                    









                                                                   
 
                                            

                    
                        
                                                       
                                                          










                                                                






                                       
                                                            
                        
                                                      
                                                          







                               
                                                







                                           

                                                            

                               

                                                     


































                                                 
                                      



                                         

                                                           


                                             





                                                                         
                                

      

                                                  
                         

                                                

                                                                            

                                                               




                                                 





                                       
                                  



                                      
                                                        




                         
                     










                                     




                             



                                  







                                                    


                                                                            






                                                  

                                        











                                                                 

                                                  
                     





                                                       
                                              
                                                                              
                     
                          
                                                    



                                                           





                               
     
                               
      

                                                                       
     



                                                        
      

















































                                                                 
                                   
                                                            










                                                  

                                        
                      
              





























                                                    
     

                                                  
                      
                   
                             
               
                               



             

                                                             


                         
                
                       
                              






                                        

                                                                             







                                                    
 
        
                                         
                                                             


                                                      
                                                                               
               
                                      
                                                                             


                      


                                      

                                                                        
                    
                      






                                            
                         







                                 



                                          
     
                                                                     

                                                      




                                         
                                   


                                                                   
                                      
                                    
                                                  
                                  
         


                                                               
                                      
                                     
                                                  
                                  
         





                                                                          
                                         
                                                          

              






                                                                  
                                                                   
                               
                            

                








                                   
                                   
































                                            
                                                   

            
              
                       






                                                   
                        



                                      
                                                         
                                                            
                                        
                       
                                  
         


                                          



                                                          




                                         

                                                                       


                                              

                                         
                                                                       


                                                  
         













                                                
                      


                                  

                                                                       
                                           




                              
                                                                    





                                               

                                    
                       
                           
         
                                                        



                        


                                          







                                                                    
                                   
                                            
                                                                   
                                       













                                    
              
                




                                                   




                                                         
                                                             

                                        
                                  



                                              

                                                                        


                                              

                                     
                                                                  


                                             













                                                
                      


                                  
                                                                          
                                          



                         
                                                                      





                                               

                                    
                       
                           
         
                                                        









                                                                     
                                
                                            
                                                       
                                      



































                                                             
              


                            
                                                       
                                    








                         
 

                     
                        

                                

          

                       
                                   
     
               

 
           
                                          


               
                                      

                                    
                     
                                  
 



                                                                

                                                                    

                                                


                                    
                         

                  







                                     

                                            
                                     



                                      
     
                                  
      
                        



                                   
     
 


                                     
                        



                                

                      
                         
                                                        












                                             





















                                                                   
                        





                                              
                                                                         











































                                       
                         


               
                                                            

                                        

                                                                            







                          





                                  

















                                            
                                        



                                       
                                                                               
                         
                                                                               




                          

                          



















                                                           







                                                                     



                                                                   


                                                                     
                             



                                



                         
/* $Id: file.c,v 1.181 2003/01/10 16:33:46 ukai Exp $ */
#include "fm.h"
#include <sys/types.h>
#include "myctype.h"
#include <signal.h>
#include <setjmp.h>
#if defined(HAVE_WAITPID) || defined(HAVE_WAIT3)
#include <sys/wait.h>
#endif
#include <stdio.h>
#include <time.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <utime.h>
/* foo */

#include "html.h"
#include "parsetagx.h"
#include "local.h"
#include "regex.h"

#ifndef max
#define max(a,b)        ((a) > (b) ? (a) : (b))
#endif				/* not max */
#ifndef min
#define min(a,b)        ((a) > (b) ? (b) : (a))
#endif				/* not min */

static int frame_source = 0;

static void FTPhalfclose(InputStream stream);
static int _MoveFile(char *path1, char *path2);
static void uncompress_stream(URLFile *uf, char **src);
static FILE *lessopen_stream(char *path);
static Buffer *loadcmdout(char *cmd,
			  Buffer *(*loadproc) (URLFile *, Buffer *),
			  Buffer *defaultbuf);
static void addnewline(Buffer *buf, char *line, Lineprop *prop,
#ifdef USE_ANSI_COLOR
		       Linecolor *color,
#endif
		       int pos, int nlines);
static void addLink(Buffer *buf, struct parsed_tag *tag);

static Lineprop propBuffer[LINELEN];
#ifdef USE_ANSI_COLOR
static Linecolor colorBuffer[LINELEN];
#endif

static JMP_BUF AbortLoading;

static struct table *tables[MAX_TABLE];
static struct table_mode table_mode[MAX_TABLE];

#ifdef USE_IMAGE
static ParsedURL *cur_baseURL = NULL;
#ifdef JP_CHARSET
static char cur_document_code;
#endif
#endif

static Str cur_title;
static Str cur_select;
static Str select_str;
static int select_is_multiple;
static int n_selectitem;
static Str cur_option;
static Str cur_option_value;
static Str cur_option_label;
static int cur_option_selected;
static int cur_status;
#ifdef MENU_SELECT
/* menu based <select>  */
FormSelectOption *select_option;
static int max_select = MAX_SELECT;
static int n_select;
static int cur_option_maxwidth;
#endif				/* MENU_SELECT */

static Str cur_textarea;
Str *textarea_str;
static int cur_textarea_size;
static int cur_textarea_rows;
static int cur_textarea_readonly;
static int n_textarea;
static int ignore_nl_textarea;
static int max_textarea = MAX_TEXTAREA;

static int http_response_code;

#ifdef JP_CHARSET
static char content_charset = '\0';
static char meta_charset = '\0';
static char guess_charset(char *p);
static char check_charset(char *s);
static char check_accept_charset(char *s);
#endif

struct link_stack {
    int cmd;
    short offset;
    short pos;
    struct link_stack *next;
};

static struct link_stack *link_stack = NULL;

#define FORMSTACK_SIZE 10
#define FRAMESTACK_SIZE 10

#ifdef USE_NNTP
#define Str_news_endline(s) ((s)->ptr[0]=='.'&&((s)->ptr[1]=='\n'||(s)->ptr[1]=='\r'||(s)->ptr[1]=='\0'))
#endif				/* USE_NNTP */

#define INITIAL_FORM_SIZE 10
static FormList **forms;
static int *form_stack;
static int form_max = -1;
static int forms_size = 0;
#define cur_form_id ((form_sp >= 0)? form_stack[form_sp] : -1)
static int form_sp = 0;

static clen_t current_content_length;

static int cur_hseq;
#ifdef USE_IMAGE
static int cur_iseq;
#endif

#define MAX_UL_LEVEL 9
#ifdef KANJI_SYMBOLS
char *ullevel[MAX_UL_LEVEL] = {
    "��", "��", "��", "��", "��", "��", "��", "��", "��"
};
#define HR_RULE "��"
#define HR_RULE_WIDTH 2
#else				/* not KANJI_SYMBOLS */
char *ullevel[MAX_UL_LEVEL] = {
    NBSP "*", NBSP "+", NBSP "o", NBSP "#", NBSP "@", NBSP "-",
    NBSP "=", "**", "--"
};
#define HR_RULE "-"
#define HR_RULE_WIDTH 1
#endif				/* not KANJI_SYMBOLS */

#ifdef USE_COOKIE
/* This array should be somewhere else */
char *violations[COO_EMAX] = {
    "internal error",
    "tail match failed",
    "wrong number of dots",
    "RFC 2109 4.3.2 rule 1",
    "RFC 2109 4.3.2 rule 2.1",
    "RFC 2109 4.3.2 rule 2.2",
    "RFC 2109 4.3.2 rule 3",
    "RFC 2109 4.3.2 rule 4",
    "RFC XXXX 4.3.2 rule 5"
};
#endif

/* *INDENT-OFF* */
static struct compression_decoder {
    int type;
    char *ext;
    char *mime_type;
    int auxbin_p;
    char *cmd;
    char *name;
    char *encoding;
    char *encodings[4];
} compression_decoders[] = {
    { CMP_COMPRESS, ".gz", "application/x-gzip",
      0, GUNZIP_CMDNAME, GUNZIP_NAME, "gzip", 
      {"gzip", "x-gzip", NULL} }, 
    { CMP_COMPRESS, ".Z", "application/x-compress",
      0, GUNZIP_CMDNAME, GUNZIP_NAME, "compress",
      {"compress", "x-compress", NULL} }, 
    { CMP_BZIP2, ".bz2", "application/x-bzip",
      0, BUNZIP2_CMDNAME, BUNZIP2_NAME, "bzip, bzip2",
      {"x-bzip", "bzip", "bzip2", NULL} }, 
    { CMP_DEFLATE, NULL, "application/x-deflate",
      1, INFLATE_CMDNAME, INFLATE_NAME, "deflate",
      {"deflate", "x-deflate", NULL} }, 
    { CMP_NOCOMPRESS, NULL, NULL, 0, NULL, NULL, NULL, {NULL}},
};
/* *INDENT-ON* */

#define SAVE_BUF_SIZE 1536

static MySignalHandler
KeyAbort(SIGNAL_ARG)
{
    LONGJMP(AbortLoading, 1);
    SIGNAL_RETURN;
}

int
currentLn(Buffer *buf)
{
    if (buf->currentLine)
	/*     return buf->currentLine->real_linenumber + 1;      */
	return buf->currentLine->linenumber + 1;
    else
	return 1;
}

static Buffer *
loadSomething(URLFile *f,
	      char *path,
	      Buffer *(*loadproc) (URLFile *, Buffer *), Buffer *defaultbuf)
{
    Buffer *buf;

    if ((buf = loadproc(f, defaultbuf)) == NULL)
	return NULL;

    buf->filename = path;
    if (buf->buffername == NULL || buf->buffername[0] == '\0') {
	buf->buffername = checkHeader(buf, "Subject:");
	if (buf->buffername == NULL)
	    buf->buffername = conv_from_system(lastFileName(path));
    }
    if (buf->currentURL.scheme == SCM_UNKNOWN)
	buf->currentURL.scheme = f->scheme;
    buf->real_scheme = f->scheme;
    if (f->scheme == SCM_LOCAL && buf->sourcefile == NULL)
	buf->sourcefile = path;
    if (f->scheme != SCM_NNTP && f->scheme != SCM_NEWS)
	UFclose(f);
    return buf;
}

int
dir_exist(char *path)
{
    struct stat stbuf;

    if (path == NULL || *path == '\0')
	return 0;
    if (stat(path, &stbuf) == -1)
	return 0;
    return IS_DIRECTORY(stbuf.st_mode);
}

static int
is_dump_text_type(char *type)
{
    struct mailcap *mcap;
    return (type && (mcap = searchExtViewer(type)) &&
	    (mcap->flags & (MAILCAP_HTMLOUTPUT | MAILCAP_COPIOUSOUTPUT)));
}

static int
is_text_type(char *type)
{
    return (type == NULL || type[0] == '\0' ||
	    strncasecmp(type, "text/", 5) == 0 ||
	    strncasecmp(type, "message/", sizeof("message/") - 1) == 0);
}

static int
is_plain_text_type(char *type)
{
    return ((type && strcasecmp(type, "text/plain") == 0) ||
	    (is_text_type(type) && !is_dump_text_type(type)));
}

static void
check_compression(char *path, URLFile *uf)
{
    int len;
    struct compression_decoder *d;

    if (path == NULL)
	return;

    len = strlen(path);
    uf->compression = CMP_NOCOMPRESS;
    for (d = compression_decoders; d->type != CMP_NOCOMPRESS; d++) {
	int elen;
	if (d->ext == NULL)
	    continue;
	elen = strlen(d->ext);
	if (len > elen && strcasecmp(&path[len - elen], d->ext) == 0) {
	    uf->compression = d->type;
	    uf->guess_type = d->mime_type;
	    break;
	}
    }
}

static char *
compress_application_type(int compression)
{
    struct compression_decoder *d;

    for (d = compression_decoders; d->type != CMP_NOCOMPRESS; d++) {
	if (d->type == compression)
	    return d->mime_type;
    }
    return NULL;
}

static char *
uncompressed_file_type(char *path, char **ext)
{
    int len, slen;
    Str fn;
    char *t0;
    struct compression_decoder *d;

    if (path == NULL)
	return NULL;

    slen = 0;
    len = strlen(path);
    for (d = compression_decoders; d->type != CMP_NOCOMPRESS; d++) {
	if (d->ext == NULL)
	    continue;
	slen = strlen(d->ext);
	if (len > slen && strcasecmp(&path[len - slen], d->ext) == 0)
	    break;
    }
    if (d->type == CMP_NOCOMPRESS)
	return NULL;

    fn = Strnew_charp(path);
    Strshrink(fn, slen);
    if (ext)
	*ext = filename_extension(fn->ptr, 0);
    t0 = guessContentType(fn->ptr);
    if (t0 == NULL)
	t0 = "text/plain";
    return t0;
}

static int
setModtime(char *path, time_t modtime)
{
    struct utimbuf t;
    struct stat st;

    if (stat(path, &st) == 0)
	t.actime = st.st_atime;
    else
	t.actime = time(NULL);
    t.modtime = modtime;
    return utime(path, &t);
}

void
examineFile(char *path, URLFile *uf)
{
    struct stat stbuf;

    uf->guess_type = NULL;
    if (path == NULL || *path == '\0' ||
	stat(path, &stbuf) == -1 || NOT_REGULAR(stbuf.st_mode)) {
	uf->stream = NULL;
	return;
    }
    uf->stream = openIS(path);
    if (!do_download) {
	if (use_lessopen && getenv("LESSOPEN") != NULL) {
	    FILE *fp;
	    uf->guess_type = guessContentType(path);
	    if (uf->guess_type == NULL)
		uf->guess_type = "text/plain";
	    if (strcasecmp(uf->guess_type, "text/html") == 0)
		return;
	    if ((fp = lessopen_stream(path))) {
		UFclose(uf);
		uf->stream = newFileStream(fp, (void (*)())pclose);
		uf->guess_type = "text/plain";
		return;
	    }
	}
	check_compression(path, uf);
	if (uf->compression != CMP_NOCOMPRESS) {
	    char *ext = uf->ext;
	    char *t0 = uncompressed_file_type(path, &ext);
	    uf->guess_type = t0;
	    uf->ext = ext;
	    uncompress_stream(uf, NULL);
	    return;
	}
    }
}

#define S_IXANY	(S_IXUSR|S_IXGRP|S_IXOTH)

int
check_command(char *cmd, int auxbin_p)
{
    static char *path = NULL;
    Str dirs;
    char *p, *np;
    Str pathname;
    struct stat st;

    if (path == NULL)
	path = getenv("PATH");
    if (auxbin_p)
	dirs = Strnew_charp(w3m_auxbin_dir());
    else
	dirs = Strnew_charp(path);
    for (p = dirs->ptr; p != NULL; p = np) {
	np = strchr(p, PATH_SEPARATOR);
	if (np)
	    *np++ = '\0';
	pathname = Strnew();
	Strcat_charp(pathname, p);
	Strcat_char(pathname, '/');
	Strcat_charp(pathname, cmd);
	if (stat(pathname->ptr, &st) == 0 && S_ISREG(st.st_mode)
	    && (st.st_mode & S_IXANY) != 0)
	    return 1;
    }
    return 0;
}

char *
acceptableEncoding()
{
    static Str encodings = NULL;
    struct compression_decoder *d;
    TextList *l;
    char *p;

    if (encodings != NULL)
	return encodings->ptr;
    l = newTextList();
    for (d = compression_decoders; d->type != CMP_NOCOMPRESS; d++) {
	if (check_command(d->cmd, d->auxbin_p)) {
	    pushText(l, d->encoding);
	}
    }
    encodings = Strnew();
    while ((p = popText(l)) != NULL) {
	if (encodings->length)
	    Strcat_charp(encodings, ", ");
	Strcat_charp(encodings, p);
    }
    return encodings->ptr;
}

/* 
 * convert line
 */
Str
convertLine(URLFile *uf, Str line, char *code, int mode)
{
#ifdef JP_CHARSET
    char ic;
    if ((ic = checkShiftCode(line, *code)) != '\0') {
	if (UseAutoDetect)
	    *code = ic;
	line = conv_str(line, *code, InnerCode);
    }
#endif				/* JP_CHARSET */
    cleanup_line(line, mode);
#ifdef USE_NNTP
    if (uf->scheme == SCM_NEWS)
	Strchop(line);
#endif				/* USE_NNTP */
    return line;
}

/* 
 * loadFile: load file to buffer
 */
Buffer *
loadFile(char *path)
{
    Buffer *buf;
    URLFile uf;
    init_stream(&uf, SCM_LOCAL, NULL);
    examineFile(path, &uf);
    if (uf.stream == NULL)
	return NULL;
    buf = newBuffer(INIT_BUFFER_WIDTH);
    if (SkipHeader)
	readHeader(&uf, buf, TRUE, NULL);
    current_content_length = 0;
#ifdef JP_CHARSET
    content_charset = '\0';
#endif
    return loadSomething(&uf, path, loadBuffer, buf);
}

int
matchattr(char *p, char *attr, int len, Str *value)
{
    int quoted;
    char *q = NULL;

    if (strncasecmp(p, attr, len) == 0) {
	p += len;
	SKIP_BLANKS(p);
	if (value) {
	    *value = Strnew();
	    if (*p == '=') {
		p++;
		SKIP_BLANKS(p);
		quoted = 0;
		while (!IS_ENDL(*p) && (quoted || *p != ';')) {
		    if (!IS_SPACE(*p))
			q = p;
		    if (*p == '"')
			quoted = (quoted) ? 0 : 1;
		    else
			Strcat_char(*value, *p);
		    p++;
		}
		if (q)
		    Strshrink(*value, p - q - 1);
	    }
	    return 1;
	}
	else {
	    if (IS_ENDT(*p)) {
		return 1;
	    }
	}
    }
    return 0;
}

#ifdef USE_IMAGE
#ifdef USE_XFACE
static char *
xface2xpm(char *xface)
{
    char *xpm;
    FILE *f;
    struct stat st;

    xpm = tmpfname(TMPF_DFL, ".xpm")->ptr;
    f = popen(Sprintf("%s > %s", auxbinFile(XFACE2XPM), xpm)->ptr, "w");
    if (!f)
	return NULL;
    fprintf(f, "%s", xface);
    pclose(f);
    if (stat(xpm, &st))
	return NULL;
    pushText(fileToDelete, xpm);
    if (!st.st_size)
	return NULL;
    return xpm;
}
#endif
#endif

void
readHeader(URLFile *uf, Buffer *newBuf, int thru, ParsedURL *pu)
{
    char *p, *q;
#ifdef USE_COOKIE
    char *emsg;
#endif
    char c;
    Str lineBuf2 = NULL;
    Str tmp;
    TextList *headerlist;
#ifdef JP_CHARSET
    char code = DocumentCode, ic;
#endif
    FILE *src = NULL;

    headerlist = newBuf->document_header = newTextList();
    if (uf->scheme == SCM_HTTP
#ifdef USE_SSL
	|| uf->scheme == SCM_HTTPS
#endif				/* USE_SSL */
	)
	http_response_code = -1;
    else
	http_response_code = 0;

    if (thru && !newBuf->header_source) {
	Str tmpf = tmpfname(TMPF_DFL, NULL);
	pushText(fileToDelete, tmpf->ptr);
	src = fopen(tmpf->ptr, "w");
	if (src)
	    newBuf->header_source = tmpf->ptr;
    }
    while ((tmp = StrmyUFgets(uf))->length) {
#ifdef USE_NNTP
	if (uf->scheme == SCM_NEWS && tmp->ptr[0] == '.')
	    Strshrinkfirst(tmp, 1);
#endif
#ifdef HTTP_DEBUG
	{
	    FILE *ff;
	    ff = fopen("zzrequest", "a");
	    Strfputs(tmp, ff);
	    fclose(ff);
	}
#endif				/* HTTP_DEBUG */
	if (src)
	    Strfputs(tmp, src);
	cleanup_line(tmp, HEADER_MODE);
	if (tmp->ptr[0] == '\n' || tmp->ptr[0] == '\r' || tmp->ptr[0] == '\0') {
	    if (!lineBuf2)
		/* there is no header */
		break;
	    /* last header */
	}
	else if (!(w3m_dump & DUMP_HEAD)) {
	    if (lineBuf2) {
		Strcat(lineBuf2, tmp);
	    }
	    else {
		lineBuf2 = tmp;
	    }
	    c = UFgetc(uf);
	    UFundogetc(uf);
	    if (c == ' ' || c == '\t')
		/* header line is continued */
		continue;
	    lineBuf2 = decodeMIME(lineBuf2->ptr);
#ifdef JP_CHARSET
	    if ((ic = checkShiftCode(lineBuf2, code)) != '\0') {
		if (UseAutoDetect)
		    code = ic;
		lineBuf2 = conv_str(lineBuf2, code, InnerCode);
	    }
#endif				/* JP_CHARSET */
	    /* separated with line and stored */
	    tmp = Strnew_size(lineBuf2->length);
	    for (p = lineBuf2->ptr; *p; p = q) {
		for (q = p; *q && *q != '\r' && *q != '\n'; q++) ;
		lineBuf2 = checkType(Strnew_charp_n(p, q - p), propBuffer,
#ifdef USE_ANSI_COLOR
				     NULL, NULL,
#endif
				     min(LINELEN, q - p));
		Strcat(tmp, lineBuf2);
		if (thru)
		    addnewline(newBuf, lineBuf2->ptr, propBuffer,
#ifdef USE_ANSI_COLOR
			       NULL,
#endif
			       lineBuf2->length, -1);
		for (; *q && (*q == '\r' || *q == '\n'); q++) ;
	    }
#ifdef USE_IMAGE
#ifdef USE_XFACE
	    if (thru && activeImage && displayImage &&
		!strncasecmp(tmp->ptr, "X-Face:", 7)) {
		char *tmpf;
		Str src;
		URLFile f;
		Line *l;

		tmpf = xface2xpm(&tmp->ptr[7]);
		if (tmpf) {
		    src =
			Sprintf
			("<img src=\"file:%s\" alt=\"X-Face\" width=48 height=48>",
			 html_quote(tmpf));
		    init_stream(&f, SCM_LOCAL, newStrStream(src));
		    loadHTMLstream(&f, newBuf, NULL, TRUE);
		    for (l = newBuf->lastLine; l && l->real_linenumber;
			 l = l->prev)
			l->real_linenumber = 0;
		}
	    }
#endif
#endif
	    lineBuf2 = tmp;
	}
	else {
	    lineBuf2 = tmp;
	}
	if ((uf->scheme == SCM_HTTP
#ifdef USE_SSL
	     || uf->scheme == SCM_HTTPS
#endif				/* USE_SSL */
	    ) && http_response_code == -1) {
	    p = lineBuf2->ptr;
	    while (*p && !IS_SPACE(*p))
		p++;
	    while (*p && IS_SPACE(*p))
		p++;
	    http_response_code = atoi(p);
	    if (fmInitialized) {
		message(lineBuf2->ptr, 0, 0);
		refresh();
	    }
	}
	if (!strncasecmp(lineBuf2->ptr, "content-transfer-encoding:", 26)) {
	    p = lineBuf2->ptr + 26;
	    while (IS_SPACE(*p))
		p++;
	    if (!strncasecmp(p, "base64", 6))
		uf->encoding = ENC_BASE64;
	    else if (!strncasecmp(p, "quoted-printable", 16))
		uf->encoding = ENC_QUOTE;
	    else if (!strncasecmp(p, "uuencode", 8) ||
		     !strncasecmp(p, "x-uuencode", 10))
		uf->encoding = ENC_UUENCODE;
	    else
		uf->encoding = ENC_7BIT;
	}
	else if (!strncasecmp(lineBuf2->ptr, "content-encoding:", 17)) {
	    struct compression_decoder *d;
	    p = lineBuf2->ptr + 17;
	    while (IS_SPACE(*p))
		p++;
	    uf->compression = CMP_NOCOMPRESS;
	    for (d = compression_decoders; d->type != CMP_NOCOMPRESS; d++) {
		char **e;
		for (e = d->encodings; *e != NULL; e++) {
		    if (strncasecmp(p, *e, strlen(*e)) == 0) {
			uf->compression = d->type;
			break;
		    }
		}
		if (uf->compression != CMP_NOCOMPRESS)
		    break;
	    }
	}
#ifdef USE_COOKIE
	else if (use_cookie && accept_cookie &&
		 pu && check_cookie_accept_domain(pu->host) &&
		 (!strncasecmp(lineBuf2->ptr, "Set-Cookie:", 11) ||
		  !strncasecmp(lineBuf2->ptr, "Set-Cookie2:", 12))) {
	    Str name = Strnew(), value = Strnew(), domain = NULL, path = NULL,
		comment = NULL, commentURL = NULL, port = NULL, tmp2;
	    int version, quoted, flag = 0;
	    time_t expires = (time_t) - 1;

	    q = NULL;
	    if (lineBuf2->ptr[10] == '2') {
		p = lineBuf2->ptr + 12;
		version = 1;
	    }
	    else {
		p = lineBuf2->ptr + 11;
		version = 0;
	    }
#ifdef DEBUG
	    fprintf(stderr, "Set-Cookie: [%s]\n", p);
#endif				/* DEBUG */
	    SKIP_BLANKS(p);
	    while (*p != '=' && !IS_ENDT(*p))
		Strcat_char(name, *(p++));
	    Strremovetrailingspaces(name);
	    if (*p == '=') {
		p++;
		SKIP_BLANKS(p);
		quoted = 0;
		while (!IS_ENDL(*p) && (quoted || *p != ';')) {
		    if (!IS_SPACE(*p))
			q = p;
		    if (*p == '"')
			quoted = (quoted) ? 0 : 1;
		    Strcat_char(value, *(p++));
		}
		if (q)
		    Strshrink(value, p - q - 1);
	    }
	    while (*p == ';') {
		p++;
		SKIP_BLANKS(p);
		if (matchattr(p, "expires", 7, &tmp2)) {
		    /* version 0 */
		    expires = mymktime(tmp2->ptr);
		}
		else if (matchattr(p, "max-age", 7, &tmp2)) {
		    /* XXX Is there any problem with max-age=0? (RFC 2109 ss. 4.2.1, 4.2.2 */
		    expires = time(NULL) + atol(tmp2->ptr);
		}
		else if (matchattr(p, "domain", 6, &tmp2)) {
		    domain = tmp2;
		}
		else if (matchattr(p, "path", 4, &tmp2)) {
		    path = tmp2;
		}
		else if (matchattr(p, "secure", 6, NULL)) {
		    flag |= COO_SECURE;
		}
		else if (matchattr(p, "comment", 7, &tmp2)) {
		    comment = tmp2;
		}
		else if (matchattr(p, "version", 7, &tmp2)) {
		    version = atoi(tmp2->ptr);
		}
		else if (matchattr(p, "port", 4, &tmp2)) {
		    /* version 1, Set-Cookie2 */
		    port = tmp2;
		}
		else if (matchattr(p, "commentURL", 10, &tmp2)) {
		    /* version 1, Set-Cookie2 */
		    commentURL = tmp2;
		}
		else if (matchattr(p, "discard", 7, NULL)) {
		    /* version 1, Set-Cookie2 */
		    flag |= COO_DISCARD;
		}
		quoted = 0;
		while (!IS_ENDL(*p) && (quoted || *p != ';')) {
		    if (*p == '"')
			quoted = (quoted) ? 0 : 1;
		    p++;
		}
	    }
	    if (pu && name->length > 0) {
		int err;
		if (flag & COO_SECURE)
		    disp_message_nsec("Received a secured cookie", FALSE, 1,
				      TRUE, FALSE);
		else
		    disp_message_nsec(Sprintf("Received cookie: %s=%s",
					      name->ptr, value->ptr)->ptr,
				      FALSE, 1, TRUE, FALSE);
		err =
		    add_cookie(pu, name, value, expires, domain, path, flag,
			       comment, version, port, commentURL);
		if (err) {
		    char *ans = (accept_bad_cookie == ACCEPT_BAD_COOKIE_ACCEPT)
			? "y" : NULL;
		    if (fmInitialized && (err & COO_OVERRIDE_OK) &&
			accept_bad_cookie == ACCEPT_BAD_COOKIE_ASK) {
			Str msg = Sprintf("Accept bad cookie from %s for %s?",
					  pu->host,
					  ((domain && domain->ptr)
					   ? domain->ptr : "<localdomain>"));
			if (msg->length > COLS - 10)
			    Strshrink(msg, msg->length - (COLS - 10));
			Strcat_charp(msg, " (y/n)");
			ans = inputAnswer(msg->ptr);
		    }
		    if (ans == NULL || TOLOWER(*ans) != 'y' ||
			(err =
			 add_cookie(pu, name, value, expires, domain, path,
				    flag | COO_OVERRIDE, comment, version,
				    port, commentURL))) {
			err = (err & ~COO_OVERRIDE_OK) - 1;
			if (err >= 0 && err < COO_EMAX)
			    emsg = Sprintf("This cookie was rejected "
					   "to prevent security violation. [%s]",
					   violations[err])->ptr;
			else
			    emsg =
				"This cookie was rejected to prevent security violation.";
			record_err_message(emsg);
			disp_message_nsec(emsg, FALSE, 1, TRUE, FALSE);
		    }
		    else
			disp_message_nsec(Sprintf
					  ("Accepting invalid cookie: %s=%s",
					   name->ptr, value->ptr)->ptr, FALSE,
					  1, TRUE, FALSE);
		}
	    }
	}
#endif				/* USE_COOKIE */
	else if (!strncasecmp(lineBuf2->ptr, "w3m-control:", 12) &&
		 uf->scheme == SCM_LOCAL_CGI) {
	    Str funcname = Strnew();
	    int f;

	    p = lineBuf2->ptr + 12;
	    SKIP_BLANKS(p);
	    while (*p && !IS_SPACE(*p))
		Strcat_char(funcname, *(p++));
	    SKIP_BLANKS(p);
	    f = getFuncList(funcname->ptr);
	    if (f >= 0) {
		tmp = Strnew_charp(p);
		Strchop(tmp);
		pushEvent(f, tmp->ptr);
	    }
	}
	if (headerlist)
	    pushText(headerlist, lineBuf2->ptr);
	Strfree(lineBuf2);
	lineBuf2 = NULL;
    }
    if (thru)
	addnewline(newBuf, "", propBuffer,
#ifdef USE_ANSI_COLOR
		   NULL,
#endif
		   0, -1);
    if (src)
	fclose(src);
}

char *
checkHeader(Buffer *buf, char *field)
{
    int len = strlen(field);
    TextListItem *i;
    char *p;

    if (buf == NULL || field == NULL || buf->document_header == NULL)
	return NULL;
    for (i = buf->document_header->first; i != NULL; i = i->next) {
	if (!strncasecmp(i->ptr, field, len)) {
	    p = i->ptr + len;
	    SKIP_BLANKS(p);
	    return p;
	}
    }
    return NULL;
}

char *
checkContentType(Buffer *buf)
{
    char *p;
    Str r;
    p = checkHeader(buf, "Content-Type:");
    if (p == NULL)
	return NULL;
    r = Strnew();
    while (*p && *p != ';' && !IS_SPACE(*p))
	Strcat_char(r, *p++);
#ifdef JP_CHARSET
    if ((p = strcasestr(p, "charset")) != NULL) {
	p += 7;
	SKIP_BLANKS(p);
	if (*p == '=') {
	    p++;
	    SKIP_BLANKS(p);
	    content_charset = guess_charset(p);
	}
    }
#endif
    return r->ptr;
}

struct auth_param {
    char *name;
    Str val;
};

struct http_auth {
    int pri;
    char *scheme;
    struct auth_param *param;
    Str (*cred) (struct http_auth * ha, Str uname, Str pw, ParsedURL *pu,
		 HRequest *hr, FormList *request);
};

#define TOKEN_PAT	"[^][()<>@,;:\\\"/?={} \t\001-\037\177]*"

static Str
extract_auth_val(char **q)
{
    unsigned char *qq = *(unsigned char **)q;
    int quoted = 0;
    Str val = Strnew();

    SKIP_BLANKS(qq);
    if (*qq == '"') {
	quoted = TRUE;
	Strcat_char(val, *qq++);
    }
    while (*qq != '\0') {
	if (quoted && *qq == '"') {
	    Strcat_char(val, *qq++);
	    break;
	}
	if (!quoted) {
	    switch (*qq) {
	    case '(':
	    case ')':
	    case '<':
	    case '>':
	    case '@':
	    case ',':
	    case ';':
	    case ':':
	    case '\\':
	    case '"':
	    case '/':
	    case '?':
	    case '=':
	    case ' ':
	    case '\t':
		qq++;
		goto end_token;
	    default:
		if (*qq <= 037 || *qq == 0177) {
		    qq++;
		    goto end_token;
		}
	    }
	}
	else if (quoted && *qq == '\\')
	    Strcat_char(val, *qq++);
	Strcat_char(val, *qq++);
    }
  end_token:
    if (*qq != '\0') {
	SKIP_BLANKS(qq);
	if (*qq == ',')
	    qq++;
    }
    *q = (char *)qq;
    return val;
}

static Str
qstr_unquote(Str s)
{
    char *p;

    if (s == NULL)
	return NULL;
    p = s->ptr;
    if (*p == '"') {
	Str tmp = Strnew();
	for (p++; *p != '\0'; p++) {
	    if (*p == '\\')
		p++;
	    Strcat_char(tmp, *p);
	}
	if (Strlastchar(tmp) == '"')
	    Strshrink(tmp, 1);
	return tmp;
    }
    else
	return s;
}

static char *
extract_auth_param(char *q, struct auth_param *auth)
{
    struct auth_param *ap;
    char *q0;
    Regex re_token;

    newRegex(TOKEN_PAT, FALSE, &re_token, NULL);

    for (ap = auth; ap->name != NULL; ap++) {
	ap->val = NULL;
    }

    while (*q != '\0') {
	SKIP_BLANKS(q);
	for (ap = auth; ap->name != NULL; ap++) {
	    if (strncasecmp(q, ap->name, strlen(ap->name)) == 0) {
		q += strlen(ap->name);
		SKIP_BLANKS(q);
		if (*q != '=')
		    return q;
		q++;
		ap->val = extract_auth_val(&q);
		break;
	    }
	}
	if (ap->name == NULL) {
	    /* skip unknown param */
	    if (RegexMatch(&re_token, q, -1, TRUE) == 0)
		return q;
	    MatchedPosition(&re_token, &q0, &q);
	    SKIP_BLANKS(q);
	    if (*q != '=')
		return q;
	    q++;
	    extract_auth_val(&q);
	}
    }
    return q;
}

static Str
get_auth_param(struct auth_param *auth, char *name)
{
    struct auth_param *ap;
    for (ap = auth; ap->name != NULL; ap++) {
	if (strcasecmp(name, ap->name) == 0)
	    return ap->val;
    }
    return NULL;
}

static Str
AuthBasicCred(struct http_auth *ha, Str uname, Str pw, ParsedURL *pu,
	      HRequest *hr, FormList *request)
{
    Str s = Strdup(uname);
    Strcat_char(s, ':');
    Strcat(s, pw);
    return Strnew_m_charp("Basic ", encodeB(s->ptr)->ptr, NULL);
}

#ifdef USE_DIGEST_AUTH
#include <openssl/md5.h>

/* RFC2617: 3.2.2 The Authorization Request Header
 * 
 * credentials      = "Digest" digest-response
 * digest-response  = 1#( username | realm | nonce | digest-uri
 *                    | response | [ algorithm ] | [cnonce] |
 *                     [opaque] | [message-qop] |
 *                         [nonce-count]  | [auth-param] )
 *
 * username         = "username" "=" username-value
 * username-value   = quoted-string
 * digest-uri       = "uri" "=" digest-uri-value
 * digest-uri-value = request-uri   ; As specified by HTTP/1.1
 * message-qop      = "qop" "=" qop-value
 * cnonce           = "cnonce" "=" cnonce-value
 * cnonce-value     = nonce-value
 * nonce-count      = "nc" "=" nc-value
 * nc-value         = 8LHEX
 * response         = "response" "=" request-digest
 * request-digest = <"> 32LHEX <">
 * LHEX             =  "0" | "1" | "2" | "3" |
 *                     "4" | "5" | "6" | "7" |
 *                     "8" | "9" | "a" | "b" |
 *                     "c" | "d" | "e" | "f"
 */

static Str
digest_hex(char *p)
{
    char *h = "0123456789abcdef";
    Str tmp = Strnew_size(MD5_DIGEST_LENGTH * 2 + 1);
    int i;
    for (i = 0; i < MD5_DIGEST_LENGTH; i++, p++) {
	Strcat_char(tmp, h[(*p >> 4) & 0x0f]);
	Strcat_char(tmp, h[*p & 0x0f]);
    }
    return tmp;
}

static Str
AuthDigestCred(struct http_auth *ha, Str uname, Str pw, ParsedURL *pu,
	       HRequest *hr, FormList *request)
{
    Str tmp, a1buf, a2buf, rd, s;
    char md5[MD5_DIGEST_LENGTH + 1];
    Str uri = HTTPrequestURI(pu, hr);
    char nc[] = "00000001";

    Str algorithm = qstr_unquote(get_auth_param(ha->param, "algorithm"));
    Str nonce = qstr_unquote(get_auth_param(ha->param, "nonce"));
    Str cnonce = qstr_unquote(get_auth_param(ha->param, "cnonce"));
    Str qop = qstr_unquote(get_auth_param(ha->param, "qop"));

    if (cnonce == NULL)
	cnonce = Strnew_charp("cnonce");	/* XXX */

    /* A1 = unq(username-value) ":" unq(realm-value) ":" passwd */
    tmp = Strnew_m_charp(uname->ptr, ":",
			 qstr_unquote(get_auth_param(ha->param, "realm"))->ptr,
			 ":", pw->ptr, NULL);
    MD5(tmp->ptr, strlen(tmp->ptr), md5);
    a1buf = digest_hex(md5);

    if (algorithm) {
	if (strcasecmp(algorithm->ptr, "MD5-sess") == 0) {
	    /* A1 = H(unq(username-value) ":" unq(realm-value) ":" passwd)
	     *      ":" unq(nonce-value) ":" unq(cnonce-value)
	     */
	    if (nonce == NULL)
		return NULL;
	    tmp = Strnew_m_charp(a1buf->ptr, ":",
				 qstr_unquote(nonce)->ptr,
				 ":", qstr_unquote(cnonce)->ptr, NULL);
	    MD5(tmp->ptr, strlen(tmp->ptr), md5);
	    a1buf = digest_hex(md5);
	}
	else if (strcasecmp(algorithm->ptr, "MD5") == 0)
	    /* ok default */
	    ;
	else
	    /* unknown algorithm */
	    return NULL;
    }

    /* A2 = Method ":" digest-uri-value */
    tmp = Strnew_m_charp(HTTPrequestMethod(hr)->ptr, ":", uri->ptr, NULL);
    if (qop && (strcasecmp(qop->ptr, "auth-int") == 0)) {
	/*  A2 = Method ":" digest-uri-value ":" H(entity-body) */
	Str ebody = Strnew();
	if (request && request->body) {
	    FILE *fp = fopen(request->body, "r");
	    if (fp != NULL) {
		int c;
		while ((c = fgetc(fp)) != EOF)
		    Strcat_char(ebody, c);
		fclose(fp);
	    }
	}
	MD5(ebody->ptr, strlen(ebody->ptr), md5);
	ebody = digest_hex(md5);
	Strcat_m_charp(tmp, ":", ebody->ptr, NULL);
    }
    MD5(tmp->ptr, strlen(tmp->ptr), md5);
    a2buf = digest_hex(md5);

    if (qop &&
	(strcasecmp(qop->ptr, "auth") == 0
	 || strcasecmp(qop->ptr, "auth-int") == 0)) {
	/* request-digest  = <"> < KD ( H(A1),     unq(nonce-value)
	 *                      ":" nc-value
	 *                      ":" unq(cnonce-value)
	 *                      ":" unq(qop-value)
	 *                      ":" H(A2)
	 *                      ) <">
	 */
	if (nonce == NULL)
	    return NULL;
	tmp = Strnew_m_charp(a1buf->ptr, ":", qstr_unquote(nonce)->ptr,
			     ":", nc,
			     ":", qstr_unquote(cnonce)->ptr,
			     ":", qstr_unquote(qop)->ptr,
			     ":", a2buf->ptr, NULL);
	MD5(tmp->ptr, strlen(tmp->ptr), md5);
	rd = digest_hex(md5);
    }
    else {
	/* compatibility with RFC 2069
	 * request_digest = KD(H(A1),  unq(nonce), H(A2))
	 */
	tmp = Strnew_m_charp(a1buf->ptr, ":",
			     qstr_unquote(get_auth_param(ha->param, "nonce"))->
			     ptr, ":", a2buf->ptr, NULL);
	MD5(tmp->ptr, strlen(tmp->ptr), md5);
	rd = digest_hex(md5);
    }

    /*
     * digest-response  = 1#( username | realm | nonce | digest-uri
     *                          | response | [ algorithm ] | [cnonce] |
     *                          [opaque] | [message-qop] |
     *                          [nonce-count]  | [auth-param] )
     */

    tmp = Strnew_m_charp("Digest username=\"", uname->ptr, "\"", NULL);
    Strcat_m_charp(tmp, ", realm=",
		   get_auth_param(ha->param, "realm")->ptr, NULL);
    Strcat_m_charp(tmp, ", nonce=",
		   get_auth_param(ha->param, "nonce")->ptr, NULL);
    Strcat_m_charp(tmp, ", uri=\"", uri->ptr, "\"", NULL);
    Strcat_m_charp(tmp, ", response=\"", rd->ptr, "\"", NULL);

    if (algorithm)
	Strcat_m_charp(tmp, ", algorithm=",
		       get_auth_param(ha->param, "algorithm")->ptr, NULL);

    if (cnonce)
	Strcat_m_charp(tmp, ", cnonce=\"", cnonce->ptr, "\"", NULL);

    if ((s = get_auth_param(ha->param, "opaque")) != NULL)
	Strcat_m_charp(tmp, ", opaque=", s->ptr, NULL);

    if (qop) {
	Strcat_m_charp(tmp, ", qop=",
		       get_auth_param(ha->param, "qop")->ptr, NULL);
	/* XXX how to count? */
	Strcat_m_charp(tmp, ", nc=", nc, NULL);
    }

    return tmp;
}
#endif

/* *INDENT-OFF* */
struct auth_param none_auth_param[] = {
    {NULL, NULL}
};

struct auth_param basic_auth_param[] = {
    {"realm", NULL},
    {NULL, NULL}
};

#ifdef USE_DIGEST_AUTH
/* RFC2617: 3.2.1 The WWW-Authenticate Response Header
 * challenge        =  "Digest" digest-challenge
 * 
 * digest-challenge  = 1#( realm | [ domain ] | nonce |
 *                       [ opaque ] |[ stale ] | [ algorithm ] |
 *                        [ qop-options ] | [auth-param] )
 *
 * domain            = "domain" "=" <"> URI ( 1*SP URI ) <">
 * URI               = absoluteURI | abs_path
 * nonce             = "nonce" "=" nonce-value
 * nonce-value       = quoted-string
 * opaque            = "opaque" "=" quoted-string
 * stale             = "stale" "=" ( "true" | "false" )
 * algorithm         = "algorithm" "=" ( "MD5" | "MD5-sess" |
 *                        token )
 * qop-options       = "qop" "=" <"> 1#qop-value <">
 * qop-value         = "auth" | "auth-int" | token
 */
struct auth_param digest_auth_param[] = {
    {"realm", NULL},
    {"domain", NULL},
    {"nonce", NULL},
    {"opaque", NULL},
    {"stale", NULL},
    {"algorithm", NULL},
    {"qop", NULL},
    {NULL, NULL}
};
#endif
/* for RFC2617: HTTP Authentication */
struct http_auth www_auth[] = {
    { 1, "Basic ", basic_auth_param, AuthBasicCred },
#ifdef USE_DIGEST_AUTH
    { 10, "Digest ", digest_auth_param, AuthDigestCred },
#endif
    { 0, NULL, NULL, NULL,}
};
/* *INDENT-ON* */

static struct http_auth *
findAuthentication(struct http_auth *hauth, Buffer *buf, char *auth_field)
{
    struct http_auth *ha;
    int len = strlen(auth_field);
    TextListItem *i;
    char *p0, *p;
    Regex re_token;

    newRegex(TOKEN_PAT, FALSE, &re_token, NULL);

    bzero(hauth, sizeof(struct http_auth));
    for (i = buf->document_header->first; i != NULL; i = i->next) {
	if (strncasecmp(i->ptr, auth_field, len) == 0) {
	    for (p = i->ptr + len; p != NULL && *p != '\0';) {
		SKIP_BLANKS(p);
		p0 = p;
		for (ha = &www_auth[0]; ha->scheme != NULL; ha++) {
		    if (strncasecmp(p, ha->scheme, strlen(ha->scheme)) == 0) {
			if (hauth->pri < ha->pri) {
			    *hauth = *ha;
			    p += strlen(ha->scheme);
			    SKIP_BLANKS(p);
			    p = extract_auth_param(p, hauth->param);
			    break;
			}
			else {
			    /* weak auth */
			    p += strlen(ha->scheme);
			    SKIP_BLANKS(p);
			    p = extract_auth_param(p, none_auth_param);
			}
		    }
		}
		if (p0 == p) {
		    /* all unknown auth failed */
		    if (RegexMatch(&re_token, p0, -1, TRUE) == 0)
			return NULL;
		    MatchedPosition(&re_token, &p0, &p);
		    SKIP_BLANKS(p);
		    p = extract_auth_param(p, none_auth_param);
		}
	    }
	}
    }
    return hauth->scheme ? hauth : NULL;
}

static Str
getAuthCookie(struct http_auth *hauth, char *auth_header,
	      TextList *extra_header, ParsedURL *pu, HRequest *hr,
	      FormList *request)
{
    Str ss;
    Str uname, pwd;
    Str tmp;
    TextListItem *i;
    int a_found;
    int auth_header_len = strlen(auth_header);
    char *realm = NULL;

    if (hauth)
	realm = qstr_unquote(get_auth_param(hauth->param, "realm"))->ptr;

    a_found = FALSE;
    for (i = extra_header->first; i != NULL; i = i->next) {
	if (!strncasecmp(i->ptr, auth_header, auth_header_len)) {
	    a_found = TRUE;
	    break;
	}
    }
    if (a_found) {
	if (!realm)
	    return NULL;
	/* This means that *-Authenticate: header is received after
	 * Authorization: header is sent to the server. 
	 */
	if (fmInitialized) {
	    message("Wrong username or password", 0, 0);
	    refresh();
	}
	else
	    fprintf(stderr, "Wrong username or password\n");
	sleep(1);
	ss = NULL;
	/* delete Authenticate: header from extra_header */
	delText(extra_header, i);

    }
    else
	ss = find_auth_cookie(pu->host, pu->port, pu->file, realm);
    if (realm && ss == NULL) {
	int proxy = !strncasecmp("Proxy-Authorization:", auth_header,
				 auth_header_len);

	if (!a_found && find_auth_user_passwd(pu, realm, &uname, &pwd, proxy)) {
	    /* found username & password in passwd file */ ;
	}
	else {
	    if (QuietMessage)
		return ss;
	    /* input username and password */
	    sleep(2);
	    if (fmInitialized) {
		char *pp;
		term_raw();
		if ((pp =
		     inputStr(Sprintf("Username for %s: ", realm)->ptr,
			      NULL)) == NULL)
		    return NULL;
		uname = Str_conv_to_system(Strnew_charp(pp));
		if ((pp =
		     inputLine(Sprintf("Password for %s: ", realm)->ptr, NULL,
			       IN_PASSWORD)) == NULL)
		    return NULL;
		pwd = Str_conv_to_system(Strnew_charp(pp));
		term_cbreak();
	    }
	    else {
		/*
		 * If post file is specified as '-', stdin is closed at this
		 * point.
		 * In this case, w3m cannot read username from stdin.
		 * So exit with error message.
		 * (This is same behavior as lwp-request.)
		 */
		if (feof(stdin) || ferror(stdin)) {
		    fprintf(stderr, "w3m: Authorization required for %s\n",
			    realm);
		    exit(1);
		}

		printf(proxy ? "Proxy Username for %s: " : "Username for %s: ",
		       realm);
		fflush(stdout);
		uname = Strfgets(stdin);
		Strchop(uname);
#ifdef HAVE_GETPASSPHRASE
		pwd = Strnew_charp((char *)
				   getpassphrase(proxy ? "Proxy Password: " :
						 "Password: "));
#else
		pwd = Strnew_charp((char *)
				   getpass(proxy ? "Proxy Password: " :
					   "Password: "));
#endif
	    }
	}
	ss = hauth->cred(hauth, uname, pwd, pu, hr, request);
    }
    if (ss) {
	tmp = Strnew_charp(auth_header);
	Strcat_m_charp(tmp, " ", ss->ptr, "\r\n", NULL);
	pushText(extra_header, tmp->ptr);
    }
    return ss;
}

static int
same_url_p(ParsedURL *pu1, ParsedURL *pu2)
{
    return (pu1->scheme == pu2->scheme && pu1->port == pu2->port &&
	    (pu1->host ? pu2->host ? !strcasecmp(pu1->host, pu2->host) : 0 : 1)
	    && (pu1->file ? pu2->
		file ? !strcmp(pu1->file, pu2->file) : 0 : 1));
}

/* 
 * loadGeneralFile: load file to buffer
 */
Buffer *
loadGeneralFile(char *path, ParsedURL *volatile current, char *referer,
		int flag, FormList *volatile request)
{
    URLFile f, *volatile of = NULL;
    ParsedURL pu, *volatile puv = NULL;
    int volatile nredir = 0;
    int volatile nredir_size = 0;
    Buffer *b = NULL, *(*volatile proc)() = loadBuffer;
    char *volatile tpath;
    char *volatile t = "text/plain", *p, *volatile real_type = NULL;
    Buffer *volatile t_buf = NULL;
    int volatile searchHeader = SearchHeader;
    int volatile searchHeader_through = TRUE;
    MySignalHandler(*volatile prevtrap) (SIGNAL_ARG) = NULL;
    TextList *extra_header = newTextList();
    volatile Str ss = NULL;
    volatile Str realm = NULL;
    int volatile add_auth_cookie_flag;
    unsigned char status = HTST_NORMAL;
    URLOption url_option;
    Str tmp;
    HRequest hr;
    ParsedURL *volatile auth_pu;

    tpath = path;
    prevtrap = NULL;
    add_auth_cookie_flag = 0;

  load_doc:
    if (fmInitialized)
	term_raw();
    if (prevtrap)
	signal(SIGINT, prevtrap);
    url_option.referer = referer;
    url_option.flag = flag;
    f = openURL(tpath, &pu, current, &url_option, request, extra_header, of,
		&hr, &status);
    of = NULL;
#ifdef JP_CHARSET
    content_charset = '\0';
#endif
    if (f.stream == NULL) {
	/* openURL failure: it means either (1) the requested URL is a directory name
	 * on an FTP server, or (2) is a local directory name. 
	 */
	switch (f.scheme) {
	case SCM_FTPDIR:
	    {
		Str ftpdir = readFTPDir(&pu);
		if (ftpdir && ftpdir->length > 0) {
		    FILE *src;
		    tmp = tmpfname(TMPF_SRC, ".html");
		    pushText(fileToDelete, tmp->ptr);
		    src = fopen(tmp->ptr, "w");
		    if (src) {
			Strfputs(ftpdir, src);
			fclose(src);
		    }
		    b = loadHTMLString(ftpdir);
		    if (b) {
			if (b->currentURL.host == NULL
			    && b->currentURL.file == NULL)
			    copyParsedURL(&b->currentURL, &pu);
			b->real_scheme = pu.scheme;
			if (src)
			    b->sourcefile = tmp->ptr;
		    }
		    return b;
		}
	    }
	    break;
	case SCM_LOCAL:
	    {
		struct stat st;
		if (stat(pu.real_file, &st) < 0)
		    return NULL;
		if (S_ISDIR(st.st_mode)) {
		    if (UseExternalDirBuffer) {
			Str cmd = Sprintf("%s?cookie=%s&dir=%s#current",
					  DirBufferCommand,
					  (Str_form_quote(Local_cookie))->ptr,
					  pu.file);
			b = loadGeneralFile(cmd->ptr, NULL, NO_REFERER, 0,
					    NULL);
			if (b != NULL && b != NO_BUFFER) {
			    copyParsedURL(&b->currentURL, &pu);
			    b->filename = b->currentURL.real_file;
			}
			return b;
		    }
		    else {
			b = dirBuffer(pu.real_file);
			if (b == NULL)
			    return NULL;
			t = "text/html";
			b->real_scheme = pu.scheme;
			goto loaded;
		    }
		}
	    }
	    break;
#ifdef USE_NNTP
	case SCM_NEWS_GROUP:
	    {
		Str group = readNewsgroup(&pu);
		if (group && group->length > 0) {
		    FILE *src;
		    tmp = tmpfname(TMPF_SRC, ".html");
		    pushText(fileToDelete, tmp->ptr);
		    src = fopen(tmp->ptr, "w");
		    if (src) {
			Strfputs(group, src);
			fclose(src);
		    }
		    b = loadHTMLString(group);
		    if (b) {
			b->real_type = "news:group";
			if (b->currentURL.host == NULL
			    && b->currentURL.file == NULL)
			    copyParsedURL(&b->currentURL, &pu);
			b->real_scheme = pu.scheme;
			if (src)
			    b->sourcefile = tmp->ptr;
		    }
		    return b;
		}
	    }
	    break;
#endif
	case SCM_UNKNOWN:
#ifdef USE_EXTERNAL_URI_LOADER
	    tmp = searchURIMethods(&pu);
	    if (tmp != NULL) {
		b = loadGeneralFile(tmp->ptr, current, referer, flag, request);
		if (b != NULL && b != NO_BUFFER)
		    copyParsedURL(&b->currentURL, &pu);
		return b;
	    }
#endif
	    disp_err_message(Sprintf("Unknown URI: %s",
				     parsedURL2Str(&pu)->ptr)->ptr, FALSE);
	    break;
	}
	return NULL;
    }

    if (status == HTST_MISSING) {
	if (fmInitialized)
	    term_raw();
	if (prevtrap)
	    signal(SIGINT, prevtrap);
	UFclose(&f);
	return NULL;
    }

    /* openURL() succeeded */
    if (SETJMP(AbortLoading) != 0) {
	/* transfer interrupted */
	if (fmInitialized)
	    term_raw();
	signal(SIGINT, prevtrap);
	if (b)
	    discardBuffer(b);
	UFclose(&f);
	return NULL;
    }

    b = NULL;
    if (f.is_cgi) {
	/* local CGI */
	searchHeader = TRUE;
	searchHeader_through = FALSE;
    }
    if (header_string)
	header_string = NULL;
    prevtrap = signal(SIGINT, KeyAbort);
    if (fmInitialized)
	term_cbreak();
    if (pu.scheme == SCM_HTTP ||
#ifdef USE_SSL
	pu.scheme == SCM_HTTPS ||
#endif				/* USE_SSL */
	((
#ifdef USE_GOPHER
	     (pu.scheme == SCM_GOPHER && non_null(GOPHER_proxy)) ||
#endif				/* USE_GOPHER */
	     (pu.scheme == SCM_FTP && non_null(FTP_proxy))
	 ) && !Do_not_use_proxy && !check_no_proxy(pu.host))) {

	if (fmInitialized) {
	    term_cbreak();
	    message(Sprintf("%s contacted. Waiting for reply...", pu.host)->
		    ptr, 0, 0);
	    refresh();
	}
	if (t_buf == NULL)
	    t_buf = newBuffer(INIT_BUFFER_WIDTH);
#if 0				/* USE_SSL */
	if (IStype(f.stream) == IST_SSL) {
	    Str s = ssl_get_certificate(f.stream, pu.host);
	    if (s == NULL)
		return NULL;
	    else
		t_buf->ssl_certificate = s->ptr;
	}
#endif
	readHeader(&f, t_buf, FALSE, &pu);
	t = checkContentType(t_buf);
	if (t == NULL && pu.file != NULL) {
	    if (!((http_response_code >= 400 && http_response_code <= 407) ||
		  (http_response_code >= 500 && http_response_code <= 505)))
		t = guessContentType(pu.file);
	}
	if (t == NULL)
	    t = "text/plain";
	if (http_response_code >= 301 && http_response_code <= 303
	    && (p = checkHeader(t_buf, "Location:")) != NULL) {
	    /* document moved */
	    if (nredir >= FollowRedirection) {
		tmp =
		    Sprintf("Number of redirections exceeded %d at %s",
			    FollowRedirection, parsedURL2Str(&pu)->ptr);
		disp_err_message(tmp->ptr, FALSE);
	    }
	    else if (nredir_size > 0 &&
		     (same_url_p(&pu, &puv[(nredir - 1) % nredir_size]) ||
		      (!(nredir % 2)
		       && same_url_p(&pu, &puv[(nredir / 2) % nredir_size])))) {
		tmp =
		    Sprintf("Redirection loop detected (%s)",
			    parsedURL2Str(&pu)->ptr);
		disp_err_message(tmp->ptr, FALSE);
	    }
	    else {
		if (!puv) {
		    nredir_size = FollowRedirection / 2 + 1;
		    puv = New_N(ParsedURL, nredir_size);
		    memset(puv, 0, sizeof(ParsedURL) * nredir_size);
		}

		copyParsedURL(&puv[nredir % nredir_size], &pu);
		++nredir;
		tmp = Strnew_charp(p);
		Strchop(tmp);
		tpath = tmp->ptr;
		request = NULL;
		UFclose(&f);
		current = New(ParsedURL);
		copyParsedURL(current, &pu);
		t_buf->bufferprop |= BP_REDIRECTED;
		status = HTST_NORMAL;
		goto load_doc;
	    }
	}
	if (add_auth_cookie_flag && realm && ss) {
	    /* If authorization is required and passed */
	    add_auth_cookie(auth_pu->host, auth_pu->port, auth_pu->file,
			    qstr_unquote(realm)->ptr, ss);
	    add_auth_cookie_flag = 0;
	}
	if ((p = checkHeader(t_buf, "WWW-Authenticate:")) != NULL &&
	    http_response_code == 401) {
	    /* Authentication needed */
	    struct http_auth hauth;
	    if (findAuthentication(&hauth, t_buf, "WWW-Authenticate:") != NULL
		&& (realm = get_auth_param(hauth.param, "realm")) != NULL) {
		auth_pu = &pu;
		ss = getAuthCookie(&hauth, "Authorization:", extra_header,
				   auth_pu, &hr, request);
		if (ss == NULL) {
		    /* abort */
		    UFclose(&f);
		    if (fmInitialized)
			term_raw();
		    signal(SIGINT, prevtrap);
		    return NULL;
		}
		UFclose(&f);
		add_auth_cookie_flag = 1;
		status = HTST_NORMAL;
		goto load_doc;
	    }
	}
	if ((p = checkHeader(t_buf, "Proxy-Authenticate:")) != NULL &&
	    http_response_code == 407) {
	    /* Authentication needed */
	    struct http_auth hauth;
	    if (findAuthentication(&hauth, t_buf, "Proxy-Authenticate:")
		!= NULL
		&& (realm = get_auth_param(hauth.param, "realm")) != NULL) {
		auth_pu = schemeToProxy(pu.scheme);
		ss = getAuthCookie(&hauth, "Proxy-Authorization:",
				   extra_header, auth_pu, &hr, request);
		if (ss == NULL) {
		    /* abort */
		    UFclose(&f);
		    if (fmInitialized)
			term_raw();
		    signal(SIGINT, prevtrap);
		    return NULL;
		}
		UFclose(&f);
		add_auth_cookie_flag = 1;
		status = HTST_NORMAL;
		goto load_doc;
	    }
	}
	/* XXX: RFC2617 3.2.3 Authentication-Info: ? */

	if (status == HTST_CONNECT) {
	    of = &f;
	    goto load_doc;
	}

	f.modtime = mymktime(checkHeader(t_buf, "Last-Modified:"));
    }
#ifdef USE_NNTP
    else if (pu.scheme == SCM_NEWS || pu.scheme == SCM_NNTP) {
	t_buf = newBuffer(INIT_BUFFER_WIDTH);
	readHeader(&f, t_buf, TRUE, &pu);
	t = checkContentType(t_buf);
	if (t == NULL)
	    t = "text/plain";
    }
#endif				/* USE_NNTP */
#ifdef USE_GOPHER
    else if (pu.scheme == SCM_GOPHER) {
	switch (*pu.file) {
	case '0':
	    t = "text/plain";
	    break;
	case '1':
	    t = "gopher:directory";
	    break;
	case 'm':
	    t = "gopher:directory";
	    break;
	case 's':
	    t = "audio/basic";
	    break;
	case 'g':
	    t = "image/gif";
	    break;
	case 'h':
	    t = "text/html";
	    break;
	}
    }
#endif				/* USE_GOPHER */
    else if (pu.scheme == SCM_FTP) {
	check_compression(path, &f);
	if (f.compression != CMP_NOCOMPRESS) {
	    char *t1 = uncompressed_file_type(pu.file, NULL);
	    real_type = f.guess_type;
#if 0
	    if (t1 && strncasecmp(t1, "application/", 12) == 0) {
		f.compression = CMP_NOCOMPRESS;
		t = real_type;
	    }
	    else
#endif
	    if (t1)
		t = t1;
	    else
		t = real_type;
	}
	else {
	    real_type = guessContentType(pu.file);
	    if (real_type == NULL)
		real_type = "text/plain";
	    t = real_type;
	}
#if 0
	if (!strncasecmp(t, "application/", 12)) {
	    char *tmpf = tmpfname(TMPF_DFL, NULL)->ptr;
	    current_content_length = 0;
	    if (save2tmp(f, tmpf) < 0)
		UFclose(&f);
	    else {
		UFclose(&f);
		if (fmInitialized)
		    term_raw();
		signal(SIGINT, prevtrap);
		doFileMove(tmpf, guess_save_name(t_buf, pu.file));
	    }
	    return NO_BUFFER;
	}
#endif
    }
    else if (pu.scheme == SCM_DATA) {
	t = f.guess_type;
    }
    else if (searchHeader) {
	t_buf = newBuffer(INIT_BUFFER_WIDTH);
	readHeader(&f, t_buf, searchHeader_through, &pu);
	t = checkContentType(t_buf);
	if (t == NULL)
	    t = "text/plain";
	if (f.is_cgi && (p = checkHeader(t_buf, "Location:")) != NULL) {
	    /* document moved */
	    tmp = Strnew_charp(p);
	    Strchop(tmp);
	    tpath = tmp->ptr;
	    request = NULL;
	    UFclose(&f);
	    add_auth_cookie_flag = 0;
	    current = New(ParsedURL);
	    copyParsedURL(current, &pu);
	    t_buf->bufferprop |= BP_REDIRECTED;
	    status = HTST_NORMAL;
	    goto load_doc;
	}
	searchHeader = SearchHeader = FALSE;
    }
    else if (DefaultType) {
	t = DefaultType;
	DefaultType = NULL;
    }
    else {
	t = guessContentType(pu.file);
	if (t == NULL)
	    t = "text/plain";
	real_type = t;
	if (f.guess_type)
	    t = f.guess_type;
    }
    if (SkipHeader) {
	t_buf = newBuffer(INIT_BUFFER_WIDTH);
	readHeader(&f, t_buf, TRUE, NULL);
    }
    if (real_type == NULL)
	real_type = t;
    proc = loadBuffer;
#ifdef USE_IMAGE
    cur_baseURL = New(ParsedURL);
    copyParsedURL(cur_baseURL, &pu);
#endif

    current_content_length = 0;
    if ((p = checkHeader(t_buf, "Content-Length:")) != NULL)
	current_content_length = strtoclen(p);

    if (do_download) {
	/* download only */
	char *file;
	if (fmInitialized)
	    term_raw();
	signal(SIGINT, prevtrap);
	if (DecodeCTE && IStype(f.stream) != IST_ENCODED)
	    f.stream = newEncodedStream(f.stream, f.encoding);
	if (pu.scheme == SCM_LOCAL) {
	    struct stat st;
	    if (PreserveTimestamp && !stat(pu.real_file, &st))
		f.modtime = st.st_mtime;
	    file = conv_from_system(guess_save_name(NULL, pu.real_file));
	}
	else
	    file = guess_save_name(t_buf, pu.file);
	doFileSave(f, file);
	if (f.scheme == SCM_FTP)
	    FTPhalfclose(f.stream);
	else if (f.scheme != SCM_NNTP && f.scheme != SCM_NEWS)
	    UFclose(&f);
	return NO_BUFFER;
    }

    if (f.compression != CMP_NOCOMPRESS) {
	if (!(w3m_dump & DUMP_SOURCE) &&
	    (w3m_dump & ~DUMP_FRAME || is_text_type(t)
	     || searchExtViewer(t))) {
	    if (t_buf == NULL)
		t_buf = newBuffer(INIT_BUFFER_WIDTH);
	    uncompress_stream(&f, &t_buf->sourcefile);
	    uncompressed_file_type(pu.file, &f.ext);
	}
	else {
	    t = compress_application_type(f.compression);
	    f.compression = CMP_NOCOMPRESS;
	}
    }
#ifdef USE_IMAGE
    if (image_source) {
	Buffer *b = NULL;
	if (IStype(f.stream) != IST_ENCODED)
	    f.stream = newEncodedStream(f.stream, f.encoding);
	if (save2tmp(f, image_source) == 0) {
	    b = newBuffer(INIT_BUFFER_WIDTH);
	    b->sourcefile = image_source;
	    b->real_type = t;
	}
	UFclose(&f);
	if (fmInitialized)
	    term_raw();
	signal(SIGINT, prevtrap);
	return b;
    }
#endif

    if (!strcasecmp(t, "text/html"))
	proc = loadHTMLBuffer;
    else if (is_plain_text_type(t))
	proc = loadBuffer;
#ifdef USE_IMAGE
    else if (activeImage && displayImage && !useExtImageViewer &&
	     !(w3m_dump & ~DUMP_FRAME) && !strncasecmp(t, "image/", 6))
	proc = loadImageBuffer;
#endif
#ifdef USE_GOPHER
    else if (!strcasecmp(t, "gopher:directory")) {
	proc = loadGopherDir;
    }
#endif				/* USE_GOPHER */
    else if (w3m_backend) ;
    else if (!(w3m_dump & ~DUMP_FRAME) || is_dump_text_type(t)) {
	if (!do_download && doExternal(f,
				       pu.real_file ? pu.real_file : pu.file,
				       t, &b, t_buf)) {
	    if (b && b != NO_BUFFER) {
		b->real_scheme = f.scheme;
		b->real_type = real_type;
		if (b->currentURL.host == NULL && b->currentURL.file == NULL)
		    copyParsedURL(&b->currentURL, &pu);
	    }
	    if (f.scheme != SCM_NNTP && f.scheme != SCM_NEWS)
		UFclose(&f);
	    if (fmInitialized)
		term_raw();
	    signal(SIGINT, prevtrap);
	    return b;
	}
	else {
	    if (fmInitialized)
		term_raw();
	    signal(SIGINT, prevtrap);
	    if (pu.scheme == SCM_LOCAL) {
		UFclose(&f);
		_doFileCopy(pu.real_file,
			    conv_from_system(guess_save_name
					     (NULL, pu.real_file)), TRUE);
	    }
	    else {
		if (DecodeCTE && IStype(f.stream) != IST_ENCODED)
		    f.stream = newEncodedStream(f.stream, f.encoding);
		doFileSave(f, guess_save_name(t_buf, pu.file));
		if (f.scheme == SCM_FTP)
		    FTPhalfclose(f.stream);
		else if (f.scheme != SCM_NNTP && f.scheme != SCM_NEWS)
		    UFclose(&f);
	    }
	    return NO_BUFFER;
	}
    }

    if (flag & RG_FRAME) {
	if (t_buf == NULL)
	    t_buf = newBuffer(INIT_BUFFER_WIDTH);
	t_buf->bufferprop |= BP_FRAME;
    }
#ifdef USE_SSL
    if (IStype(f.stream) == IST_SSL)
	t_buf->ssl_certificate = f.ssl_certificate;
#endif
    frame_source = flag & RG_FRAME_SRC;
    b = loadSomething(&f, pu.real_file ? pu.real_file : pu.file, proc, t_buf);
    frame_source = 0;
    if (f.scheme != SCM_NNTP && f.scheme != SCM_NEWS)
	UFclose(&f);
    if (b) {
	b->real_scheme = f.scheme;
	b->real_type = real_type;
      loaded:
	if (b->currentURL.host == NULL && b->currentURL.file == NULL)
	    copyParsedURL(&b->currentURL, &pu);
	if (!strcasecmp(t, "text/html"))
	    b->type = "text/html";
	else if (w3m_backend) {
	    Str s = Strnew_charp(t);
	    b->type = s->ptr;
	}
#ifdef USE_IMAGE
	else if (proc == loadImageBuffer)
	    b->type = "text/html";
#endif
#ifdef USE_GOPHER
	else if (proc == loadGopherDir)
	    b->type = "text/html";
#endif
	else
	    b->type = "text/plain";
	if (pu.label) {
	    if (proc == loadHTMLBuffer) {
		Anchor *a;
#ifdef JP_CHARSET
		a = searchURLLabel(b,
				   conv(pu.label, b->document_code,
					InnerCode)->ptr);
#else				/* not JP_CHARSET */
		a = searchURLLabel(b, pu.label);
#endif				/* not JP_CHARSET */
		if (a != NULL) {
		    gotoLine(b, a->start.line);
#ifdef LABEL_TOPLINE
		    if (label_topline)
			b->topLine = lineSkip(b, b->topLine,
					      b->currentLine->linenumber
					      - b->topLine->linenumber, FALSE);
#endif
		    b->pos = a->start.pos;
		    arrangeCursor(b);
		}
	    }
	    else {		/* plain text */
		int l = atoi(pu.label);
		gotoLine(b, l);
		b->pos = 0;
		arrangeCursor(b);
	    }
	}
    }
    if (header_string)
	header_string = NULL;
#ifdef USE_NNTP
    if (f.scheme == SCM_NNTP || f.scheme == SCM_NEWS)
	reAnchorNewsheader(b);
#endif
    preFormUpdateBuffer(b);
    if (fmInitialized)
	term_raw();
    signal(SIGINT, prevtrap);
    return b;
}

#define TAG_IS(s,tag,len)\
  (strncasecmp(s,tag,len)==0&&(s[len] == '>' || IS_SPACE((int)s[len])))

static char *
has_hidden_link(struct readbuffer *obuf, int cmd)
{
    Str line = obuf->line;
    struct link_stack *p;

    if (Strlastchar(line) != '>')
	return NULL;

    for (p = link_stack; p; p = p->next)
	if (p->cmd == cmd)
	    break;
    if (!p)
	return NULL;

    if (obuf->pos == p->pos)
	return line->ptr + p->offset;

    return NULL;
}

static void
push_link(int cmd, int offset, int pos)
{
    struct link_stack *p;
    p = New(struct link_stack);
    p->cmd = cmd;
    p->offset = offset;
    p->pos = pos;
    p->next = link_stack;
    link_stack = p;
}

static int
is_period_char(int ch)
{
    switch (ch) {
    case ',':
    case '.':
    case ':':
    case ';':
    case '?':
    case '!':
    case ')':
    case ']':
    case '}':
    case '>':
#ifndef JP_CHARSET
    case 0x203A:		/* ">" */
#endif
	return 1;
    default:
	return 0;
    }
}

static int
is_beginning_char(int ch)
{
    switch (ch) {
    case '(':
    case '[':
    case '{':
    case '`':
    case '<':
#ifndef JP_CHARSET
    case 0x2018:		/* "`" */
    case 0x2039:		/* "<" */
#endif
	return 1;
    default:
	return 0;
    }
}

static int
is_word_char(int ch)
{
#ifndef JP_CHARSET
    switch (ch) {
    case 0x0152:		/* "OE"   */
    case 0x0153:		/* "oe"   */
	return 1;
    case 0x0178:		/* "Y:"   *//* ? */
    case 0x0192:		/* "f"    *//* ? */
    case 0x02C6:		/* "^"    *//* ? */
	return 0;
    case 0x02DC:		/* "~"    */
    case 0x03BC:		/* "\xB5" "mu" */
	return 1;
    case 0x2002:		/* " "    "ensp" */
    case 0x2003:		/* " "    "emsp" */
	return 0;
    case 0x2013:		/* "\xAD" "ndash" */
    case 0x2014:		/* "-"    "mdash" */
    case 0x2018:		/* "`"    "lsquo" */
    case 0x2019:		/* "'"    "rsquo" */
    case 0x201A:		/* "\xB8" "sbquo" */
    case 0x201C:		/* "\""   "ldquo" */
    case 0x201D:		/* "\""   "rdquo" */
    case 0x201E:		/* ",,"   "bdquo" */
    case 0x2022:		/* "*"    "bull" *//* ? */
    case 0x2030:		/* "0/00" "permil" */
    case 0x2032:		/* "'"    "prime" */
    case 0x2033:		/* "\""   "Prime" */
    case 0x2039:		/* "<"    "lsaquo" */
    case 0x203A:		/* ">"    "rsaquo" */
    case 0x2044:		/* "/"    "frasl" */
    case 0x20AC:		/* "=C="  "euro" */
    case 0x2122:		/* "TM"   "trade" */
	return 1;
    case 0x2205:		/* "\xF8" "empty" *//* ? */
	return 0;
    case 0x2212:		/* "-"    */
    case 0x223C:		/* "~"    */
	return 1;
    case 0x2260:		/* "!="   *//* ? */
    case 0x2261:		/* "="    *//* ? */
    case 0x2264:		/* "<="   *//* ? */
    case 0x2265:		/* ">="   *//* ? */
	return 0;
    }
#endif

#ifdef JP_CHARSET
    if (is_wckanji(ch) || IS_CNTRL(ch))
	return 0;
#else
    if (IS_CNTRL(ch))
	return 0;
#endif

    if (IS_ALNUM(ch))
	return 1;

    switch (ch) {
    case ',':
    case '.':
    case ':':
    case '\"':			/* " */
    case '\'':
    case '$':
    case '%':
    case '*':
    case '+':
    case '-':
    case '@':
    case '~':
    case '_':
	return 1;
    }
    if (ch == TIMES_CODE || ch == DIVIDE_CODE || ch == ANSP_CODE)
	return 0;
    if (ch >= AGRAVE_CODE || ch == NBSP_CODE)
	return 1;
    return 0;
}

int
is_boundary(int ch1, int ch2)
{
    if (!ch1 || !ch2)
	return 1;

    if (ch1 == ' ' && ch2 == ' ')
	return 0;

    if (ch1 != ' ' && is_period_char(ch2))
	return 0;

    if (ch2 != ' ' && is_beginning_char(ch1))
	return 0;

    if (is_word_char(ch1) && is_word_char(ch2))
	return 0;

    return 1;
}


static void
set_breakpoint(struct readbuffer *obuf, int tag_length)
{
    obuf->bp.len = obuf->line->length;
    obuf->bp.pos = obuf->pos;
    obuf->bp.tlen = tag_length;
    obuf->bp.flag = obuf->flag;
#ifdef FORMAT_NICE
    obuf->bp.flag &= ~RB_FILL;
#endif				/* FORMAT_NICE */
    obuf->bp.top_margin = obuf->top_margin;
    obuf->bp.bottom_margin = obuf->bottom_margin;

    if (!obuf->bp.init_flag)
	return;

    bcopy((void *)&obuf->anchor, (void *)&obuf->bp.anchor,
	  sizeof(obuf->anchor));
    obuf->bp.img_alt = obuf->img_alt;
    obuf->bp.in_bold = obuf->in_bold;
    obuf->bp.in_under = obuf->in_under;
    obuf->bp.nobr_level = obuf->nobr_level;
    obuf->bp.prev_ctype = obuf->prev_ctype;
    obuf->bp.init_flag = 0;
}

static void
back_to_breakpoint(struct readbuffer *obuf)
{
    obuf->flag = obuf->bp.flag;
    bcopy((void *)&obuf->bp.anchor, (void *)&obuf->anchor,
	  sizeof(obuf->anchor));
    obuf->img_alt = obuf->bp.img_alt;
    obuf->in_bold = obuf->bp.in_bold;
    obuf->in_under = obuf->bp.in_under;
    obuf->prev_ctype = obuf->bp.prev_ctype;
    obuf->pos = obuf->bp.pos;
    obuf->top_margin = obuf->bp.top_margin;
    obuf->bottom_margin = obuf->bp.bottom_margin;
    if (obuf->flag & RB_NOBR)
	obuf->nobr_level = obuf->bp.nobr_level;
}

static void
append_tags(struct readbuffer *obuf)
{
    int i;
    int len = obuf->line->length;
    int set_bp = 0;

    for (i = 0; i < obuf->tag_sp; i++) {
	switch (obuf->tag_stack[i]->cmd) {
	case HTML_A:
	case HTML_IMG_ALT:
	case HTML_B:
	case HTML_U:
	    push_link(obuf->tag_stack[i]->cmd, obuf->line->length, obuf->pos);
	    break;
	}
	Strcat_charp(obuf->line, obuf->tag_stack[i]->cmdname);
	switch (obuf->tag_stack[i]->cmd) {
	case HTML_NOBR:
	    if (obuf->nobr_level > 1)
		break;
	case HTML_WBR:
	    set_bp = 1;
	    break;
	}
    }
    obuf->tag_sp = 0;
    if (set_bp)
	set_breakpoint(obuf, obuf->line->length - len);
}

static void
push_tag(struct readbuffer *obuf, char *cmdname, int cmd)
{
    obuf->tag_stack[obuf->tag_sp] = New(struct cmdtable);
    obuf->tag_stack[obuf->tag_sp]->cmdname = allocStr(cmdname, -1);
    obuf->tag_stack[obuf->tag_sp]->cmd = cmd;
    obuf->tag_sp++;
    if (obuf->tag_sp >= TAG_STACK_SIZE || obuf->flag & (RB_SPECIAL & ~RB_NOBR))
	append_tags(obuf);
}

static void
push_nchars(struct readbuffer *obuf, int width,
	    char *str, int len, Lineprop mode)
{
    int delta = get_mclen(mode);
    append_tags(obuf);
    Strcat_charp_n(obuf->line, str, len);
    obuf->pos += width;
    if (width > 0 && len >= delta) {
	obuf->prevchar = mctowc(&str[len - delta], mode);
	obuf->prev_ctype = mode;
    }
    obuf->flag |= RB_NFLUSHED;
}

#define push_charp(obuf, width, str, mode)\
push_nchars(obuf, width, str, strlen(str), mode)

#define push_str(obuf, width, str, mode)\
push_nchars(obuf, width, str->ptr, str->length, mode)

static void
check_breakpoint(struct readbuffer *obuf, int pre_mode, int ch)
{
    int tlen, len = obuf->line->length;

    append_tags(obuf);
    if (pre_mode)
	return;
    tlen = obuf->line->length - len;
    if (tlen > 0 || is_boundary(obuf->prevchar, ch))
	set_breakpoint(obuf, tlen);
}

static void
push_char(struct readbuffer *obuf, int pre_mode, char ch)
{
    check_breakpoint(obuf, pre_mode, (unsigned char)ch);
    Strcat_char(obuf->line, ch);
    obuf->pos++;
    obuf->prevchar = (unsigned char)ch;
    if (ch != ' ')
	obuf->prev_ctype = PC_ASCII;
    obuf->flag |= RB_NFLUSHED;
}

#define PUSH(c) push_char(obuf, obuf->flag & RB_SPECIAL, c)

static void
push_spaces(struct readbuffer *obuf, int pre_mode, int width)
{
    int i;

    if (width <= 0)
	return;
    check_breakpoint(obuf, pre_mode, ' ');
    for (i = 0; i < width; i++)
	Strcat_char(obuf->line, ' ');
    obuf->pos += width;
    obuf->prevchar = ' ';
    obuf->flag |= RB_NFLUSHED;
}

static void
proc_mchar(struct readbuffer *obuf, int pre_mode,
	   int width, char **str, Lineprop mode)
{
    int ch = mctowc(*str, mode);

    check_breakpoint(obuf, pre_mode, ch);
    obuf->pos += width;
#ifdef JP_CHARSET
    if (IS_KANJI1(**str) && mode == PC_ASCII)
	Strcat_char(obuf->line, ' ');
    else if (mode == PC_KANJI)
	Strcat_charp_n(obuf->line, *str, 2);
    else
#endif
	Strcat_char(obuf->line, **str);
    if (width > 0) {
	obuf->prevchar = ch;
	if (ch != ' ')
	    obuf->prev_ctype = mode;
    }
    (*str) += get_mclen(mode);
    obuf->flag |= RB_NFLUSHED;
}

void
push_render_image(Str str, int width, int limit,
		  struct html_feed_environ *h_env)
{
    struct readbuffer *obuf = h_env->obuf;
    int indent = h_env->envs[h_env->envc].indent;

    push_spaces(obuf, 1, (limit - width) / 2);
    push_str(obuf, width, str, PC_ASCII);
    push_spaces(obuf, 1, (limit - width + 1) / 2);
    if (width > 0)
	flushline(h_env, obuf, indent, 0, h_env->limit);
}

static int
sloppy_parse_line(char **str)
{
    if (**str == '<') {
	while (**str && **str != '>')
	    (*str)++;
	if (**str == '>')
	    (*str)++;
	return 1;
    }
    else {
	while (**str && **str != '<')
	    (*str)++;
	return 0;
    }
}

static void
passthrough(struct readbuffer *obuf, char *str, int back)
{
    int cmd;
    Str tok = Strnew();
    char *str_bak;

    if (back) {
	Str str_save = Strnew_charp(str);
	Strshrink(obuf->line, obuf->line->ptr + obuf->line->length - str);
	str = str_save->ptr;
    }
    while (*str) {
	str_bak = str;
	if (sloppy_parse_line(&str)) {
	    char *q = str_bak;
	    cmd = gethtmlcmd(&q);
	    if (back) {
		struct link_stack *p;
		for (p = link_stack; p; p = p->next) {
		    if (p->cmd == cmd) {
			link_stack = p->next;
			break;
		    }
		}
		back = 0;
	    }
	    else {
		Strcat_charp_n(tok, str_bak, str - str_bak);
		push_tag(obuf, tok->ptr, cmd);
		Strclear(tok);
	    }
	}
	else {
	    push_nchars(obuf, 0, str_bak, str - str_bak, obuf->prev_ctype);
	}
    }
}

#if 0
int
is_blank_line(char *line, int indent)
{
    int i, is_blank = 0;

    for (i = 0; i < indent; i++) {
	if (line[i] == '\0') {
	    is_blank = 1;
	}
	else if (line[i] != ' ') {
	    break;
	}
    }
    if (i == indent && line[i] == '\0')
	is_blank = 1;
    return is_blank;
}
#endif

void
fillline(struct readbuffer *obuf, int indent)
{
    push_spaces(obuf, 1, indent - obuf->pos);
    obuf->flag &= ~RB_NFLUSHED;
}

void
flushline(struct html_feed_environ *h_env, struct readbuffer *obuf, int indent,
	  int force, int width)
{
    TextLineList *buf = h_env->buf;
    FILE *f = h_env->f;
    Str line = obuf->line, pass = NULL;
    char *hidden_anchor = NULL, *hidden_img = NULL, *hidden_bold = NULL,
	*hidden_under = NULL, *hidden = NULL;

    if (w3m_debug) {
	FILE *df = fopen("zzzproc1", "a");
	fprintf(df, "flushline(%s,%d,%d,%d)\n", obuf->line->ptr, indent, force,
		width);
	if (buf) {
	    TextLineListItem *p;
	    for (p = buf->first; p; p = p->next) {
		fprintf(df, "buf=\"%s\"\n", p->ptr->line->ptr);
	    }
	}
	fclose(df);
    }

    if (!(obuf->flag & (RB_SPECIAL & ~RB_NOBR)) && Strlastchar(line) == ' ') {
	Strshrink(line, 1);
	obuf->pos--;
    }

    append_tags(obuf);

    if (obuf->anchor.url)
	hidden = hidden_anchor = has_hidden_link(obuf, HTML_A);
    if (obuf->img_alt) {
	if ((hidden_img = has_hidden_link(obuf, HTML_IMG_ALT)) != NULL) {
	    if (!hidden || hidden_img < hidden)
		hidden = hidden_img;
	}
    }
    if (obuf->in_bold) {
	if ((hidden_bold = has_hidden_link(obuf, HTML_B)) != NULL) {
	    if (!hidden || hidden_bold < hidden)
		hidden = hidden_bold;
	}
    }
    if (obuf->in_under) {
	if ((hidden_under = has_hidden_link(obuf, HTML_U)) != NULL) {
	    if (!hidden || hidden_under < hidden)
		hidden = hidden_under;
	}
    }
    if (hidden) {
	pass = Strnew_charp(hidden);
	Strshrink(line, line->ptr + line->length - hidden);
    }

    if (!(obuf->flag & (RB_SPECIAL & ~RB_NOBR)) && obuf->pos > width) {
	char *tp = &line->ptr[obuf->bp.len - obuf->bp.tlen];
	char *ep = &line->ptr[line->length];

	if (obuf->bp.pos == obuf->pos && tp <= ep &&
	    tp > line->ptr && tp[-1] == ' ') {
	    bcopy(tp, tp - 1, ep - tp + 1);
	    line->length--;
	    obuf->pos--;
	}
    }

    if (obuf->anchor.url && !hidden_anchor)
	Strcat_charp(line, "</a>");
    if (obuf->img_alt && !hidden_img)
	Strcat_charp(line, "</img_alt>");
    if (obuf->in_bold && !hidden_bold)
	Strcat_charp(line, "</b>");
    if (obuf->in_under && !hidden_under)
	Strcat_charp(line, "</u>");

    if (obuf->top_margin > 0) {
	int i;
	struct html_feed_environ h;
	struct readbuffer o;
	struct environment e[1];

	init_henv(&h, &o, e, 1, NULL, width, indent);
	o.line = Strnew_size(width + 20);
	o.pos = obuf->pos;
	o.flag = obuf->flag;
	o.top_margin = -1;
	o.bottom_margin = -1;
	Strcat_charp(o.line, "<pre_int>");
	for (i = 0; i < o.pos; i++)
	    Strcat_char(o.line, ' ');
	Strcat_charp(o.line, "</pre_int>");
	for (i = 0; i < obuf->top_margin; i++)
	    flushline(h_env, &o, indent, force, width);
    }

    if (force == 1 || obuf->flag & RB_NFLUSHED) {
	TextLine *lbuf = newTextLine(line, obuf->pos);
	if (RB_GET_ALIGN(obuf) == RB_CENTER) {
	    align(lbuf, width, ALIGN_CENTER);
	}
	else if (RB_GET_ALIGN(obuf) == RB_RIGHT) {
	    align(lbuf, width, ALIGN_RIGHT);
	}
	else if (RB_GET_ALIGN(obuf) == RB_LEFT && obuf->flag & RB_INTABLE) {
	    align(lbuf, width, ALIGN_LEFT);
	}
#ifdef FORMAT_NICE
	else if (obuf->flag & RB_FILL) {
	    char *p;
	    int rest, rrest;
	    int nspace, d, i;

	    rest = width - line->length;
	    if (rest > 1) {
		nspace = 0;
		for (p = line->ptr + indent; *p; p++) {
		    if (*p == ' ')
			nspace++;
		}
		if (nspace > 0) {
		    int indent_here = 0;
		    d = rest / nspace;
		    p = line->ptr;
		    while (IS_SPACE(*p)) {
			p++;
			indent_here++;
		    }
		    rrest = rest - d * nspace;
		    line = Strnew_size(width + 1);
		    for (i = 0; i < indent_here; i++)
			Strcat_char(line, ' ');
		    for (; *p; p++) {
			Strcat_char(line, *p);
			if (*p == ' ') {
			    for (i = 0; i < d; i++)
				Strcat_char(line, ' ');
			    if (rrest > 0) {
				Strcat_char(line, ' ');
				rrest--;
			    }
			}
		    }
		    lbuf = newTextLine(line, width);
		}
	    }
	}
#endif				/* FORMAT_NICE */
#ifdef TABLE_DEBUG
	if (w3m_debug) {
	    FILE *f = fopen("zzzproc1", "a");
	    fprintf(f, "pos=%d,%d, maxlimit=%d\n",
		    visible_length(lbuf->line->ptr), lbuf->pos,
		    h_env->maxlimit);
	    fclose(f);
	}
#endif
	if (lbuf->pos > h_env->maxlimit)
	    h_env->maxlimit = lbuf->pos;
	if (buf)
	    pushTextLine(buf, lbuf);
	else if (f) {
	    Strfputs(lbuf->line, f);
	    fputc('\n', f);
	}
	if (obuf->flag & RB_SPECIAL || obuf->flag & RB_NFLUSHED)
	    h_env->blank_lines = 0;
	else
	    h_env->blank_lines++;
    }
    else {
	char *p = line->ptr, *q;
	Str tmp = Strnew(), tmp2 = Strnew();

#define APPEND(str) \
	if (buf) \
	    appendTextLine(buf,(str),0); \
	else if (f) \
	    Strfputs((str),f)

	while (*p) {
	    q = p;
	    if (sloppy_parse_line(&p)) {
		Strcat_charp_n(tmp, q, p - q);
		if (force == 2) {
		    APPEND(tmp);
		}
		else
		    Strcat(tmp2, tmp);
		Strclear(tmp);
	    }
	}
	if (force == 2) {
	    if (pass) {
		APPEND(pass);
	    }
	    pass = NULL;
	}
	else {
	    if (pass)
		Strcat(tmp2, pass);
	    pass = tmp2;
	}
    }

    if (obuf->bottom_margin > 0) {
	int i;
	struct html_feed_environ h;
	struct readbuffer o;
	struct environment e[1];

	init_henv(&h, &o, e, 1, NULL, width, indent);
	o.line = Strnew_size(width + 20);
	o.pos = obuf->pos;
	o.flag = obuf->flag;
	o.top_margin = -1;
	o.bottom_margin = -1;
	Strcat_charp(o.line, "<pre_int>");
	for (i = 0; i < o.pos; i++)
	    Strcat_char(o.line, ' ');
	Strcat_charp(o.line, "</pre_int>");
	for (i = 0; i < obuf->bottom_margin; i++)
	    flushline(h_env, &o, indent, force, width);
    }
    if (obuf->top_margin < 0 || obuf->bottom_margin < 0)
	return;

    obuf->line = Strnew_size(256);
    obuf->pos = 0;
    obuf->top_margin = 0;
    obuf->bottom_margin = 0;
    obuf->prevchar = ' ';
    obuf->bp.init_flag = 1;
    obuf->flag &= ~RB_NFLUSHED;
    set_breakpoint(obuf, 0);
    obuf->prev_ctype = PC_ASCII;
    link_stack = NULL;
    fillline(obuf, indent);
    if (pass)
	passthrough(obuf, pass->ptr, 0);
    if (!hidden_anchor && obuf->anchor.url) {
	Str tmp;
	if (obuf->anchor.hseq > 0)
	    obuf->anchor.hseq = -obuf->anchor.hseq;
	tmp = Sprintf("<A HSEQ=\"%d\" HREF=\"", obuf->anchor.hseq);
	Strcat_charp(tmp, html_quote(obuf->anchor.url));
	if (obuf->anchor.target) {
	    Strcat_charp(tmp, "\" TARGET=\"");
	    Strcat_charp(tmp, html_quote(obuf->anchor.target));
	}
	if (obuf->anchor.referer) {
	    Strcat_charp(tmp, "\" REFERER=\"");
	    Strcat_charp(tmp, html_quote(obuf->anchor.referer));
	}
	if (obuf->anchor.title) {
	    Strcat_charp(tmp, "\" TITLE=\"");
	    Strcat_charp(tmp, html_quote(obuf->anchor.title));
	}
	if (obuf->anchor.accesskey) {
	    char *c = html_quote_char(obuf->anchor.accesskey);
	    Strcat_charp(tmp, "\" ACCESSKEY=\"");
	    if (c)
		Strcat_charp(tmp, c);
	    else
		Strcat_char(tmp, obuf->anchor.accesskey);
	}
	Strcat_charp(tmp, "\">");
	push_tag(obuf, tmp->ptr, HTML_A);
    }
    if (!hidden_img && obuf->img_alt) {
	Str tmp = Strnew_charp("<IMG_ALT SRC=\"");
	Strcat_charp(tmp, html_quote(obuf->img_alt->ptr));
	Strcat_charp(tmp, "\">");
	push_tag(obuf, tmp->ptr, HTML_IMG_ALT);
    }
    if (!hidden_bold && obuf->in_bold)
	push_tag(obuf, "<B>", HTML_B);
    if (!hidden_under && obuf->in_under)
	push_tag(obuf, "<U>", HTML_U);
}

void
do_blankline(struct html_feed_environ *h_env, struct readbuffer *obuf,
	     int indent, int indent_incr, int width)
{
    if (h_env->blank_lines == 0)
	flushline(h_env, obuf, indent, 1, width);
}

void
purgeline(struct html_feed_environ *h_env)
{
    char *p, *q;
    Str tmp;

    if (h_env->buf == NULL || h_env->blank_lines == 0)
	return;

    p = rpopTextLine(h_env->buf)->line->ptr;
    tmp = Strnew();
    while (*p) {
	q = p;
	if (sloppy_parse_line(&p)) {
	    Strcat_charp_n(tmp, q, p - q);
	}
    }
    appendTextLine(h_env->buf, tmp, 0);
    h_env->blank_lines--;
}

static int
close_effect0(struct readbuffer *obuf, int cmd)
{
    int i;
    char *p;

    for (i = obuf->tag_sp - 1; i >= 0; i--) {
	if (obuf->tag_stack[i]->cmd == cmd)
	    break;
    }
    if (i >= 0) {
	obuf->tag_sp--;
	bcopy(&obuf->tag_stack[i + 1], &obuf->tag_stack[i],
	      (obuf->tag_sp - i) * sizeof(struct cmdtable *));
	return 1;
    }
    else if ((p = has_hidden_link(obuf, cmd)) != NULL) {
	passthrough(obuf, p, 1);
	return 1;
    }
    return 0;
}

static void
close_anchor(struct html_feed_environ *h_env, struct readbuffer *obuf)
{
    if (obuf->anchor.url) {
	int i;
	char *p = NULL;
	int is_erased = 0;

	for (i = obuf->tag_sp - 1; i >= 0; i--) {
	    if (obuf->tag_stack[i]->cmd == HTML_A)
		break;
	}
	if (i < 0 && obuf->anchor.hseq > 0 && Strlastchar(obuf->line) == ' ') {
	    Strshrink(obuf->line, 1);
	    obuf->pos--;
	    is_erased = 1;
	}

	if (i >= 0 || (p = has_hidden_link(obuf, HTML_A))) {
	    if (obuf->anchor.hseq > 0) {
		HTMLlineproc1(ANSP, h_env);
		obuf->prevchar = ' ';
	    }
	    else {
		if (i >= 0) {
		    obuf->tag_sp--;
		    bcopy(&obuf->tag_stack[i + 1], &obuf->tag_stack[i],
			  (obuf->tag_sp - i) * sizeof(struct cmdtable *));
		}
		else {
		    passthrough(obuf, p, 1);
		}
		bzero((void *)&obuf->anchor, sizeof(obuf->anchor));
		return;
	    }
	    is_erased = 0;
	}
	if (is_erased) {
	    Strcat_char(obuf->line, ' ');
	    obuf->pos++;
	}

	push_tag(obuf, "</a>", HTML_N_A);
    }
    bzero((void *)&obuf->anchor, sizeof(obuf->anchor));
}

void
save_fonteffect(struct html_feed_environ *h_env, struct readbuffer *obuf)
{
    if (obuf->fontstat_sp < FONT_STACK_SIZE)
	bcopy(obuf->fontstat, obuf->fontstat_stack[obuf->fontstat_sp],
	      FONTSTAT_SIZE);
    obuf->fontstat_sp++;
    if (obuf->in_bold)
	push_tag(obuf, "</b>", HTML_N_B);
    if (obuf->in_under)
	push_tag(obuf, "</u>", HTML_N_U);
    bzero(obuf->fontstat, FONTSTAT_SIZE);
}

void
restore_fonteffect(struct html_feed_environ *h_env, struct readbuffer *obuf)
{
    if (obuf->fontstat_sp > 0)
	obuf->fontstat_sp--;
    if (obuf->fontstat_sp < FONT_STACK_SIZE)
	bcopy(obuf->fontstat_stack[obuf->fontstat_sp], obuf->fontstat,
	      FONTSTAT_SIZE);
    if (obuf->in_bold)
	push_tag(obuf, "<b>", HTML_B);
    if (obuf->in_under)
	push_tag(obuf, "<u>", HTML_U);
}

static Str
process_title(struct parsed_tag *tag)
{
    cur_title = Strnew();
    return NULL;
}

static Str
process_n_title(struct parsed_tag *tag)
{
    Str tmp;

    if (!cur_title)
	return NULL;
    Strremovefirstspaces(cur_title);
    Strremovetrailingspaces(cur_title);
    tmp = Strnew_m_charp("<title_alt title=\"",
			 html_quote(cur_title->ptr), "\">", NULL);
    cur_title = NULL;
    return tmp;
}

static void
feed_title(char *str)
{
    if (!cur_title)
	return;
    while (*str) {
	if (*str == '&')
	    Strcat_charp(cur_title, getescapecmd(&str));
	else if (*str == '\n' || *str == '\r') {
	    Strcat_char(cur_title, ' ');
	    str++;
	}
	else
	    Strcat_char(cur_title, *(str++));
    }
}

Str
process_img(struct parsed_tag *tag, int width)
{
    char *p, *q, *r, *r2 = NULL, *s, *t;
#ifdef USE_IMAGE
    int w, i, nw, ni = 1, n, w0 = -1, i0 = -1;
    int align, xoffset, yoffset, top, bottom, ismap = 0;
    int use_image = activeImage && displayImage;
#else
    int w, i, nw, n;
#endif
    int pre_int = FALSE;
    Str tmp = Strnew();

    if (!parsedtag_get_value(tag, ATTR_SRC, &p))
	return tmp;
    p = remove_space(p);
    q = NULL;
    parsedtag_get_value(tag, ATTR_ALT, &q);
    t = q;
    parsedtag_get_value(tag, ATTR_TITLE, &t);
    w = -1;
    if (parsedtag_get_value(tag, ATTR_WIDTH, &w)) {
	if (w < 0) {
	    if (width > 0)
		w = (int)(-width * pixel_per_char * w / 100 + 0.5);
	    else
		w = -1;
	}
#ifdef USE_IMAGE
	if (use_image) {
	    if (w > 0) {
		w = (int)(w * image_scale / 100 + 0.5);
		if (w == 0)
		    w = 1;
		else if (w > MAX_IMAGE_SIZE)
		    w = MAX_IMAGE_SIZE;
	    }
	}
#endif
    }
#ifdef USE_IMAGE
    if (use_image) {
	i = -1;
	if (parsedtag_get_value(tag, ATTR_HEIGHT, &i)) {
	    if (i > 0) {
		i = (int)(i * image_scale / 100 + 0.5);
		if (i == 0)
		    i = 1;
		else if (i > MAX_IMAGE_SIZE)
		    i = MAX_IMAGE_SIZE;
	    }
	    else {
		i = -1;
	    }
	}
	align = -1;
	parsedtag_get_value(tag, ATTR_ALIGN, &align);
	ismap = 0;
	if (parsedtag_exists(tag, ATTR_ISMAP))
	    ismap = 1;
    }
    else
#endif
	parsedtag_get_value(tag, ATTR_HEIGHT, &i);
    r = NULL;
    parsedtag_get_value(tag, ATTR_USEMAP, &r);

    tmp = Strnew_size(128);
#ifdef USE_IMAGE
    if (use_image) {
	switch (align) {
	case ALIGN_LEFT:
	    Strcat_charp(tmp, "<div_int align=left>");
	    break;
	case ALIGN_CENTER:
	    Strcat_charp(tmp, "<div_int align=center>");
	    break;
	case ALIGN_RIGHT:
	    Strcat_charp(tmp, "<div_int align=right>");
	    break;
	}
    }
#endif
    if (r) {
	Str tmp2;
	r2 = strchr(r, '#');
	s = "<form_int method=internal action=map>";
	tmp2 = process_form(parse_tag(&s, TRUE));
	if (tmp2)
	    Strcat(tmp, tmp2);
	Strcat(tmp, Sprintf("<input_alt fid=\"%d\" "
			    "type=hidden name=link value=\"", cur_form_id));
	Strcat_charp(tmp, html_quote((r2) ? r2 + 1 : r));
	Strcat(tmp, Sprintf("\"><input_alt hseq=\"%d\" fid=\"%d\" "
			    "type=submit no_effect=true>",
			    cur_hseq++, cur_form_id));
    }
#ifdef USE_IMAGE
    if (use_image) {
	w0 = w;
	i0 = i;
	if (w < 0 || i < 0) {
	    Image image;
	    ParsedURL u;

#ifdef JP_CHARSET
	    parseURL2(conv(p, InnerCode, cur_document_code)->ptr, &u,
		      cur_baseURL);
#else
	    parseURL2(p, &u, cur_baseURL);
#endif
	    image.url = parsedURL2Str(&u)->ptr;
	    if (!uncompressed_file_type(u.file, &image.ext))
		image.ext = filename_extension(u.file, TRUE);
	    image.cache = NULL;
	    image.width = w;
	    image.height = i;

	    image.cache = getImage(&image, cur_baseURL, IMG_FLAG_SKIP);
	    if (image.cache && image.cache->width > 0 &&
		image.cache->height > 0) {
		w = w0 = image.cache->width;
		i = i0 = image.cache->height;
	    }
	    if (w < 0)
		w = 8 * pixel_per_char;
	    if (i < 0)
		i = pixel_per_line;
	}
	nw = (w > 3) ? (int)((w - 3) / pixel_per_char + 1) : 1;
	ni = (i > 3) ? (int)((i - 3) / pixel_per_line + 1) : 1;
	Strcat(tmp,
	       Sprintf("<pre_int><img_alt hseq=\"%d\" src=\"", cur_iseq++));
	pre_int = TRUE;
    }
    else
#endif
    {
	if (w < 0)
	    w = 12 * pixel_per_char;
	nw = w ? (int)((w - 1) / pixel_per_char + 1) : 1;
	if (r) {
	    Strcat_charp(tmp, "<pre_int>");
	    pre_int = TRUE;
	}
	Strcat_charp(tmp, "<img_alt src=\"");
    }
    Strcat_charp(tmp, html_quote(p));
    Strcat_charp(tmp, "\"");
    if (t) {
	Strcat_charp(tmp, " title=\"");
	Strcat_charp(tmp, html_quote(t));
	Strcat_charp(tmp, "\"");
    }
#ifdef USE_IMAGE
    if (use_image) {
	if (w0 >= 0)
	    Strcat(tmp, Sprintf(" width=%d", w0));
	if (i0 >= 0)
	    Strcat(tmp, Sprintf(" height=%d", i0));
	switch (align) {
	case ALIGN_TOP:
	    top = 0;
	    bottom = ni - 1;
	    yoffset = 0;
	    break;
	case ALIGN_MIDDLE:
	    top = ni / 2;
	    bottom = top;
	    if (top * 2 == ni)
		yoffset = (int)(((ni + 1) * pixel_per_line - i) / 2);
	    else
		yoffset = (int)((ni * pixel_per_line - i) / 2);
	    break;
	case ALIGN_BOTTOM:
	    top = ni - 1;
	    bottom = 0;
	    yoffset = (int)(ni * pixel_per_line - i);
	    break;
	default:
	    top = ni - 1;
	    bottom = 0;
	    if (ni == 1 && ni * pixel_per_line > i)
		yoffset = 0;
	    else {
		yoffset = (int)(ni * pixel_per_line - i);
		if (yoffset <= -2)
		    yoffset++;
	    }
	    break;
	}
	xoffset = (int)((nw * pixel_per_char - w) / 2);
	if (xoffset)
	    Strcat(tmp, Sprintf(" xoffset=%d", xoffset));
	if (yoffset)
	    Strcat(tmp, Sprintf(" yoffset=%d", yoffset));
	if (top)
	    Strcat(tmp, Sprintf(" top_margin=%d", top));
	if (bottom)
	    Strcat(tmp, Sprintf(" bottom_margin=%d", bottom));
	if (r) {
	    Strcat_charp(tmp, " usemap=\"");
	    Strcat_charp(tmp, html_quote((r2) ? r2 + 1 : r));
	    Strcat_charp(tmp, "\"");
	}
	if (ismap)
	    Strcat_charp(tmp, " ismap");
    }
#endif
    Strcat_charp(tmp, ">");
    if (q != NULL && *q == '\0' && ignore_null_img_alt)
	q = NULL;
    if (q != NULL) {
	n = strlen(q);
#ifdef USE_IMAGE
	if (use_image) {
	    if (n > nw) {
		n = nw;
		Strcat_charp(tmp, html_quote(Strnew_charp_n(q, nw)->ptr));
	    }
	    else
		Strcat_charp(tmp, html_quote(q));
	}
	else
#endif
	    Strcat_charp(tmp, html_quote(q));
	goto img_end;
    }
    if (w > 0 && i > 0) {
	/* guess what the image is! */
	if (w < 32 && i < 48) {
	    /* must be an icon or space */
	    n = 1;
	    if (strcasestr(p, "space") || strcasestr(p, "blank"))
		Strcat_charp(tmp, "_");
	    else {
		if (w * i < 8 * 16)
		    Strcat_charp(tmp, "*");
		else {
#ifdef KANJI_SYMBOLS
		    Strcat_charp(tmp, "��");
		    n = 2;
#else				/* not KANJI_SYMBOLS */
		    Strcat_charp(tmp, "#");
#endif				/* not KANJI_SYMBOLS */
		}
	    }
	    goto img_end;
	}
	if (w > 200 && i < 13) {
	    /* must be a horizontal line */
	    if (!pre_int) {
		Strcat_charp(tmp, "<pre_int>");
		pre_int = TRUE;
	    }
#ifndef KANJI_SYMBOLS
	    Strcat_charp(tmp, "<_RULE TYPE=10>");
#endif				/* not KANJI_SYMBOLS */
	    for (i = 0; i < nw - (HR_RULE_WIDTH - 1); i += HR_RULE_WIDTH)
		Strcat_charp(tmp, HR_RULE);
#ifndef KANJI_SYMBOLS
	    Strcat_charp(tmp, "</_RULE>");
#endif				/* not KANJI_SYMBOLS */
	    n = i;
	    goto img_end;
	}
    }
    for (q = p; *q; q++) ;
    while (q > p && *q != '/')
	q--;
    if (*q == '/')
	q++;
    Strcat_char(tmp, '[');
    n = 1;
    p = q;
    for (; *q; q++) {
	if (!IS_ALNUM(*q) && *q != '_' && *q != '-') {
	    break;
	}
	Strcat_char(tmp, *q);
	n++;
	if (n + 1 >= nw)
	    break;
    }
    Strcat_char(tmp, ']');
    n++;
  img_end:
#ifdef USE_IMAGE
    if (use_image) {
	for (; n < nw; n++)
	    Strcat_char(tmp, ' ');
    }
#endif
    Strcat_charp(tmp, "</img_alt>");
    if (pre_int)
	Strcat_charp(tmp, "</pre_int>");
    if (r) {
	Strcat_charp(tmp, "</input_alt>");
	process_n_form();
    }
#ifdef USE_IMAGE
    if (use_image) {
	switch (align) {
	case ALIGN_RIGHT:
	case ALIGN_CENTER:
	case ALIGN_LEFT:
	    Strcat_charp(tmp, "</div_int>");
	    break;
	}
    }
#endif
    return tmp;
}

Str
process_anchor(struct parsed_tag *tag, char *tagbuf)
{
    if (parsedtag_need_reconstruct(tag)) {
	parsedtag_set_value(tag, ATTR_HSEQ, Sprintf("%d", cur_hseq++)->ptr);
	return parsedtag2str(tag);
    }
    else {
	Str tmp = Sprintf("<a hseq=\"%d\"", cur_hseq++);
	Strcat_charp(tmp, tagbuf + 2);
	return tmp;
    }
}

Str
process_input(struct parsed_tag *tag)
{
    int i, w, v, x, y, z, iw, ih;
    char *q, *p, *r, *p2, *s;
    Str tmp = NULL;
    char *qq = "";
    int qlen = 0;

    if (cur_form_id < 0) {
	char *s = "<form_int method=internal action=none>";
	tmp = process_form(parse_tag(&s, TRUE));
    }
    if (tmp == NULL)
	tmp = Strnew();

    p = "text";
    parsedtag_get_value(tag, ATTR_TYPE, &p);
    q = NULL;
    parsedtag_get_value(tag, ATTR_VALUE, &q);
    r = "";
    parsedtag_get_value(tag, ATTR_NAME, &r);
    w = 20;
    parsedtag_get_value(tag, ATTR_SIZE, &w);
    i = 20;
    parsedtag_get_value(tag, ATTR_MAXLENGTH, &i);
    p2 = NULL;
    parsedtag_get_value(tag, ATTR_ALT, &p2);
    x = parsedtag_exists(tag, ATTR_CHECKED);
    y = parsedtag_exists(tag, ATTR_ACCEPT);
    z = parsedtag_exists(tag, ATTR_READONLY);

    v = formtype(p);
    if (v == FORM_UNKNOWN)
	return NULL;

    if (!q) {
	switch (v) {
	case FORM_INPUT_IMAGE:
	case FORM_INPUT_SUBMIT:
	case FORM_INPUT_BUTTON:
	    q = "SUBMIT";
	    break;
	case FORM_INPUT_RESET:
	    q = "RESET";
	    break;
	    /* if no VALUE attribute is specified in 
	     * <INPUT TYPE=CHECKBOX> tag, then the value "on" is used 
	     * as a default value. It is not a part of HTML4.0 
	     * specification, but an imitation of Netscape behaviour. 
	     */
	case FORM_INPUT_CHECKBOX:
	    q = "on";
	}
    }
    /* VALUE attribute is not allowed in <INPUT TYPE=FILE> tag. */
    if (v == FORM_INPUT_FILE)
	q = NULL;
    if (q) {
	qq = html_quote(q);
	qlen = strlen(q);
    }

    Strcat_charp(tmp, "<pre_int>");
    switch (v) {
    case FORM_INPUT_PASSWORD:
    case FORM_INPUT_TEXT:
    case FORM_INPUT_FILE:
    case FORM_INPUT_CHECKBOX:
	Strcat_char(tmp, '[');
	break;
    case FORM_INPUT_RADIO:
	Strcat_char(tmp, '(');
    }
    Strcat(tmp, Sprintf("<input_alt hseq=\"%d\" fid=\"%d\" type=%s "
			"name=\"%s\" width=%d maxlength=%d value=\"%s\"",
			cur_hseq++, cur_form_id, p, html_quote(r), w, i, qq));
    if (x)
	Strcat_charp(tmp, " checked");
    if (y)
	Strcat_charp(tmp, " accept");
    if (z)
	Strcat_charp(tmp, " readonly");
    Strcat_char(tmp, '>');

    if (v == FORM_INPUT_HIDDEN)
	Strcat_charp(tmp, "</input_alt></pre_int>");
    else {
	switch (v) {
	case FORM_INPUT_PASSWORD:
	case FORM_INPUT_TEXT:
	case FORM_INPUT_FILE:
	    Strcat_charp(tmp, "<u>");
	    break;
	case FORM_INPUT_IMAGE:
	    s = NULL;
	    parsedtag_get_value(tag, ATTR_SRC, &s);
	    if (s) {
		Strcat(tmp, Sprintf("<img src=\"%s\"", html_quote(s)));
		if (p2)
		    Strcat(tmp, Sprintf(" alt=\"%s\"", html_quote(p2)));
		if (parsedtag_get_value(tag, ATTR_WIDTH, &iw))
		    Strcat(tmp, Sprintf(" width=\"%d\"", iw));
		if (parsedtag_get_value(tag, ATTR_HEIGHT, &ih))
		    Strcat(tmp, Sprintf(" height=\"%d\"", ih));
		Strcat_charp(tmp, ">");
		Strcat_charp(tmp, "</input_alt></pre_int>");
		return tmp;
	    }
	case FORM_INPUT_SUBMIT:
	case FORM_INPUT_BUTTON:
	case FORM_INPUT_RESET:
	    Strcat_charp(tmp, "[");
	    break;
	}
	switch (v) {
	case FORM_INPUT_PASSWORD:
	    i = 0;
	    if (q) {
		for (; i < qlen && i < w; i++)
		    Strcat_char(tmp, '*');
	    }
	    for (; i < w; i++)
		Strcat_char(tmp, ' ');
	    break;
	case FORM_INPUT_TEXT:
	case FORM_INPUT_FILE:
	    if (q)
		Strcat(tmp, textfieldrep(Strnew_charp(q), w));
	    else {
		for (i = 0; i < w; i++)
		    Strcat_char(tmp, ' ');
	    }
	    break;
	case FORM_INPUT_SUBMIT:
	case FORM_INPUT_BUTTON:
	    if (p2)
		Strcat_charp(tmp, html_quote(p2));
	    else
		Strcat_charp(tmp, qq);
	    break;
	case FORM_INPUT_RESET:
	    Strcat_charp(tmp, qq);
	    break;
	case FORM_INPUT_RADIO:
	case FORM_INPUT_CHECKBOX:
	    if (x)
		Strcat_char(tmp, '*');
	    else
		Strcat_char(tmp, ' ');
	    break;
	}
	switch (v) {
	case FORM_INPUT_PASSWORD:
	case FORM_INPUT_TEXT:
	case FORM_INPUT_FILE:
	    Strcat_charp(tmp, "</u>");
	    break;
	case FORM_INPUT_IMAGE:
	case FORM_INPUT_SUBMIT:
	case FORM_INPUT_BUTTON:
	case FORM_INPUT_RESET:
	    Strcat_charp(tmp, "]");
	}
	Strcat_charp(tmp, "</input_alt>");
	switch (v) {
	case FORM_INPUT_PASSWORD:
	case FORM_INPUT_TEXT:
	case FORM_INPUT_FILE:
	case FORM_INPUT_CHECKBOX:
	    Strcat_char(tmp, ']');
	    break;
	case FORM_INPUT_RADIO:
	    Strcat_char(tmp, ')');
	}
	Strcat_charp(tmp, "</pre_int>");
    }
    return tmp;
}

Str
process_select(struct parsed_tag *tag)
{
    Str tmp = NULL;
    char *p;

    if (cur_form_id < 0) {
	char *s = "<form_int method=internal action=none>";
	tmp = process_form(parse_tag(&s, TRUE));
    }

    p = "";
    parsedtag_get_value(tag, ATTR_NAME, &p);
    cur_select = Strnew_charp(p);
    select_is_multiple = parsedtag_exists(tag, ATTR_MULTIPLE);

#ifdef MENU_SELECT
    if (!select_is_multiple) {
	select_str = Sprintf("<pre_int>[<input_alt hseq=\"%d\" "
			     "fid=\"%d\" type=select name=\"%s\" selectnumber=%d",
			     cur_hseq++, cur_form_id, html_quote(p), n_select);
	Strcat_charp(select_str, ">");
	if (n_select == max_select) {
	    max_select *= 2;
	    select_option =
		New_Reuse(FormSelectOption, select_option, max_select);
	}
	select_option[n_select].first = NULL;
	select_option[n_select].last = NULL;
	cur_option_maxwidth = 0;
    }
    else
#endif				/* MENU_SELECT */
	select_str = Strnew();
    cur_option = NULL;
    cur_status = R_ST_NORMAL;
    n_selectitem = 0;
    return tmp;
}

Str
process_n_select(void)
{
    if (cur_select == NULL)
	return NULL;
    process_option();
#ifdef MENU_SELECT
    if (!select_is_multiple) {
	if (select_option[n_select].first) {
	    FormItemList sitem;
	    chooseSelectOption(&sitem, select_option[n_select].first);
	    Strcat(select_str, textfieldrep(sitem.label, cur_option_maxwidth));
	}
	Strcat_charp(select_str, "</input_alt>]</pre_int>");
	n_select++;
    }
    else
#endif				/* MENU_SELECT */
	Strcat_charp(select_str, "<br>");
    cur_select = NULL;
    n_selectitem = 0;
    return select_str;
}

void
feed_select(char *str)
{
    Str tmp = Strnew();
    int prev_status = cur_status;
    static int prev_spaces = -1;
    char *p;

    if (cur_select == NULL)
	return;
    while (read_token(tmp, &str, &cur_status, 0, 0)) {
	if (cur_status != R_ST_NORMAL || prev_status != R_ST_NORMAL)
	    continue;
	p = tmp->ptr;
	if (tmp->ptr[0] == '<' && Strlastchar(tmp) == '>') {
	    struct parsed_tag *tag;
	    char *q;
	    if (!(tag = parse_tag(&p, FALSE)))
		continue;
	    switch (tag->tagid) {
	    case HTML_OPTION:
		process_option();
		cur_option = Strnew();
		if (parsedtag_get_value(tag, ATTR_VALUE, &q))
		    cur_option_value = Strnew_charp(q);
		else
		    cur_option_value = NULL;
		if (parsedtag_get_value(tag, ATTR_LABEL, &q))
		    cur_option_label = Strnew_charp(q);
		else
		    cur_option_label = NULL;
		cur_option_selected = parsedtag_exists(tag, ATTR_SELECTED);
		prev_spaces = -1;
		break;
	    case HTML_N_OPTION:
		/* do nothing */
		break;
	    default:
		/* never happen */
		break;
	    }
	}
	else if (cur_option) {
	    while (*p) {
		if (IS_SPACE(*p) && prev_spaces != 0) {
		    p++;
		    if (prev_spaces > 0)
			prev_spaces++;
		}
		else {
		    if (IS_SPACE(*p))
			prev_spaces = 1;
		    else
			prev_spaces = 0;
		    if (*p == '&')
			Strcat_charp(cur_option, getescapecmd(&p));
		    else
			Strcat_char(cur_option, *(p++));
		}
	    }
	}
    }
}

void
process_option(void)
{
    char begin_char = '[', end_char = ']';

    if (cur_select == NULL || cur_option == NULL)
	return;
    while (cur_option->length > 0 && IS_SPACE(Strlastchar(cur_option)))
	Strshrink(cur_option, 1);
    if (cur_option_value == NULL)
	cur_option_value = cur_option;
    if (cur_option_label == NULL)
	cur_option_label = cur_option;
#ifdef MENU_SELECT
    if (!select_is_multiple) {
	if (cur_option_label->length > cur_option_maxwidth)
	    cur_option_maxwidth = cur_option_label->length;
	addSelectOption(&select_option[n_select],
			cur_option_value,
			cur_option_label, cur_option_selected);
	return;
    }
#endif				/* MENU_SELECT */
    if (!select_is_multiple) {
	begin_char = '(';
	end_char = ')';
    }
    Strcat(select_str, Sprintf("<br><pre_int>%c<input_alt hseq=\"%d\" "
			       "fid=\"%d\" type=%s name=\"%s\" value=\"%s\"",
			       begin_char, cur_hseq++, cur_form_id,
			       select_is_multiple ? "checkbox" : "radio",
			       html_quote(cur_select->ptr),
			       html_quote(cur_option_value->ptr)));
    if (cur_option_selected)
	Strcat_charp(select_str, " checked>*</input_alt>");
    else
	Strcat_charp(select_str, "> </input_alt>");
    Strcat_char(select_str, end_char);
    Strcat_charp(select_str, html_quote(cur_option_label->ptr));
    Strcat_charp(select_str, "</pre_int>");
    n_selectitem++;
}

Str
process_textarea(struct parsed_tag *tag, int width)
{
    Str tmp = NULL;
    char *p;

    if (cur_form_id < 0) {
	char *s = "<form_int method=internal action=none>";
	tmp = process_form(parse_tag(&s, TRUE));
    }

    p = "";
    parsedtag_get_value(tag, ATTR_NAME, &p);
    cur_textarea = Strnew_charp(p);
    cur_textarea_size = 20;
    if (parsedtag_get_value(tag, ATTR_COLS, &p)) {
	cur_textarea_size = atoi(p);
	if (p[strlen(p) - 1] == '%')
	    cur_textarea_size = width * cur_textarea_size / 100 - 2;
	if (cur_textarea_size <= 0)
	    cur_textarea_size = 20;
    }
    cur_textarea_rows = 1;
    if (parsedtag_get_value(tag, ATTR_ROWS, &p)) {
	cur_textarea_rows = atoi(p);
	if (cur_textarea_rows <= 0)
	    cur_textarea_rows = 1;
    }
    cur_textarea_readonly = parsedtag_exists(tag, ATTR_READONLY);
    if (n_textarea >= max_textarea) {
	max_textarea *= 2;
	textarea_str = New_Reuse(Str, textarea_str, max_textarea);
    }
    textarea_str[n_textarea] = Strnew();
    ignore_nl_textarea = TRUE;

    return tmp;
}

Str
process_n_textarea(void)
{
    Str tmp;
    int i;

    if (cur_textarea == NULL)
	return NULL;

    tmp = Strnew();
    Strcat(tmp, Sprintf("<pre_int>[<input_alt hseq=\"%d\" fid=\"%d\" "
			"type=textarea name=\"%s\" size=%d rows=%d "
			"top_margin=%d textareanumber=%d",
			cur_hseq, cur_form_id,
			html_quote(cur_textarea->ptr),
			cur_textarea_size, cur_textarea_rows,
			cur_textarea_rows - 1, n_textarea));
    if (cur_textarea_readonly)
	Strcat_charp(tmp, " readonly");
    Strcat_charp(tmp, "><u>");
    for (i = 0; i < cur_textarea_size; i++)
	Strcat_char(tmp, ' ');
    Strcat_charp(tmp, "</u></input_alt>]</pre_int>\n");
    cur_hseq++;
    n_textarea++;
    cur_textarea = NULL;

    return tmp;
}

void
feed_textarea(char *str)
{
    if (cur_textarea == NULL)
	return;
    if (ignore_nl_textarea) {
	if (*str == '\r')
	    str++;
	if (*str == '\n')
	    str++;
    }
    ignore_nl_textarea = FALSE;
    while (*str) {
	if (*str == '&')
	    Strcat_charp(textarea_str[n_textarea], getescapecmd(&str));
	else if (*str == '\n') {
	    Strcat_charp(textarea_str[n_textarea], "\r\n");
	    str++;
	}
	else if (*str != '\r')
	    Strcat_char(textarea_str[n_textarea], *(str++));
    }
}

Str
process_hr(struct parsed_tag *tag, int width, int indent_width)
{
    Str tmp = Strnew_charp("<nobr>");
    int i, w = 0;
    int x = ALIGN_CENTER;

    if (width > indent_width)
	width -= indent_width;
    if (parsedtag_get_value(tag, ATTR_WIDTH, &w))
	w = REAL_WIDTH(w, width);
    else
	w = width;

    parsedtag_get_value(tag, ATTR_ALIGN, &x);
    switch (x) {
    case ALIGN_CENTER:
	Strcat_charp(tmp, "<div_int align=center>");
	break;
    case ALIGN_RIGHT:
	Strcat_charp(tmp, "<div_int align=right>");
	break;
    case ALIGN_LEFT:
	Strcat_charp(tmp, "<div_int align=left>");
	break;
    }
#ifndef KANJI_SYMBOLS
    Strcat_charp(tmp, "<_RULE TYPE=10>");
#endif				/* not KANJI_SYMBOLS */
    w -= HR_RULE_WIDTH - 1;
    if (w <= 0)
	w = 1;
    for (i = 0; i < w; i += HR_RULE_WIDTH) {
	Strcat_charp(tmp, HR_RULE);
    }
#ifndef KANJI_SYMBOLS
    Strcat_charp(tmp, "</_RULE>");
#endif				/* not KANJI_SYMBOLS */
    Strcat_charp(tmp, "</div_int></nobr>");
    return tmp;
}

#ifdef JP_CHARSET
static char
check_charset(char *s)
{
    switch (*s) {
    case CODE_EUC:
    case CODE_SJIS:
    case CODE_JIS_n:
    case CODE_JIS_m:
    case CODE_JIS_N:
    case CODE_JIS_j:
    case CODE_JIS_J:
    case CODE_INNER_EUC:
	return *s;
    }
    return 0;
}

static char
check_accept_charset(char *s)
{
    char *e;
    char c;

    while (*s) {
	while (*s && (IS_SPACE(*s) || *s == ','))
	    s++;
	if (!*s)
	    break;
	e = s;
	while (*e && !(IS_SPACE(*e) || *e == ','))
	    e++;
	c = guess_charset(Strnew_charp_n(s, e - s)->ptr);
	if (c)
	    return c;
	s = e;
    }
    return 0;
}
#endif				/* JP_CHARSET */

static Str
process_form_int(struct parsed_tag *tag, int fid)
{
    char *p, *q, *r, *s, *tg, *n;
    char cs = 0;

    p = "get";
    parsedtag_get_value(tag, ATTR_METHOD, &p);
    q = "!CURRENT_URL!";
    parsedtag_get_value(tag, ATTR_ACTION, &q);
    r = NULL;
#ifdef JP_CHARSET
    if (parsedtag_get_value(tag, ATTR_ACCEPT_CHARSET, &r))
	cs = check_accept_charset(r);
    if (!cs && parsedtag_get_value(tag, ATTR_CHARSET, &r))
	cs = check_charset(r);
#endif				/*JP_CHARSET */
    s = NULL;
    parsedtag_get_value(tag, ATTR_ENCTYPE, &s);
    tg = NULL;
    parsedtag_get_value(tag, ATTR_TARGET, &tg);
    n = NULL;
    parsedtag_get_value(tag, ATTR_NAME, &n);

    if (fid < 0) {
	form_max++;
	form_sp++;
	fid = form_max;
    }
    else {			/* <form_int> */
	if (form_max < fid)
	    form_max = fid;
	form_sp = fid;
    }
    if (forms_size == 0) {
	forms_size = INITIAL_FORM_SIZE;
	forms = New_N(FormList *, forms_size);
	form_stack = NewAtom_N(int, forms_size);
    }
    else if (forms_size <= form_max) {
	forms_size += form_max;
	forms = New_Reuse(FormList *, forms, forms_size);
	form_stack = New_Reuse(int, form_stack, forms_size);
    }
    form_stack[form_sp] = fid;

    if (w3m_halfdump) {
	Str tmp = Sprintf("<form_int fid=\"%d\" action=\"%s\" method=\"%s\"",
			  fid, html_quote(q), html_quote(p));
	if (s)
	    Strcat(tmp, Sprintf(" enctype=\"%s\"", html_quote(s)));
	if (tg)
	    Strcat(tmp, Sprintf(" target=\"%s\"", html_quote(tg)));
	if (n)
	    Strcat(tmp, Sprintf(" name=\"%s\"", html_quote(n)));
#ifdef JP_CHARSET
	if (r)
	    Strcat(tmp, Sprintf(" accept-charset=\"%s\"", code_to_str(cs)));
#endif
	Strcat_charp(tmp, ">");
	return tmp;
    }

    forms[fid] = newFormList(q, p, &cs, s, tg, n, NULL);
    return NULL;
}

Str
process_form(struct parsed_tag *tag)
{
    return process_form_int(tag, -1);
}

Str
process_n_form(void)
{
    if (form_sp >= 0)
	form_sp--;
    return NULL;
}

static void
clear_ignore_p_flag(int cmd, struct readbuffer *obuf)
{
    static int clear_flag_cmd[] = {
	HTML_HR, HTML_UNKNOWN
    };
    int i;

    for (i = 0; clear_flag_cmd[i] != HTML_UNKNOWN; i++) {
	if (cmd == clear_flag_cmd[i]) {
	    obuf->flag &= ~RB_IGNORE_P;
	    return;
	}
    }
}

static void
set_alignment(struct readbuffer *obuf, struct parsed_tag *tag)
{
    long flag = -1;
    int align;

    if (parsedtag_get_value(tag, ATTR_ALIGN, &align)) {
	switch (align) {
	case ALIGN_CENTER:
	    flag = RB_CENTER;
	    break;
	case ALIGN_RIGHT:
	    flag = RB_RIGHT;
	    break;
	case ALIGN_LEFT:
	    flag = RB_LEFT;
	}
    }
    RB_SAVE_FLAG(obuf);
    if (flag != -1) {
	RB_SET_ALIGN(obuf, flag);
    }
}

#ifdef ID_EXT
static void
process_idattr(struct readbuffer *obuf, int cmd, struct parsed_tag *tag)
{
    char *id = NULL, *framename = NULL;
    Str idtag = NULL;

    /* 
     * HTML_TABLE is handled by the other process.
     */
    if (cmd == HTML_TABLE)
	return;

    parsedtag_get_value(tag, ATTR_ID, &id);
    parsedtag_get_value(tag, ATTR_FRAMENAME, &framename);
    if (id == NULL)
	return;
    if (framename)
	idtag = Sprintf("<_id id=\"%s\" framename=\"%s\">",
			html_quote(id), html_quote(framename));
    else
	idtag = Sprintf("<_id id=\"%s\">", html_quote(id));
    push_tag(obuf, idtag->ptr, HTML_NOP);
}
#endif				/* ID_EXT */

#define CLOSE_P if (obuf->flag & RB_P) { \
      flushline(h_env, obuf, envs[h_env->envc].indent,0,h_env->limit);\
      RB_RESTORE_FLAG(obuf);\
      obuf->flag &= ~RB_P;\
    }

#define CLOSE_A \
    CLOSE_P; \
    close_anchor(h_env, obuf);

#define CLOSE_DT \
    if (obuf->flag & RB_IN_DT) { \
      obuf->flag &= ~RB_IN_DT; \
      HTMLlineproc1("</b>", h_env); \
    }

#define PUSH_ENV(cmd) \
    if (++h_env->envc_real < h_env->nenv) { \
      ++h_env->envc; \
      envs[h_env->envc].env = cmd; \
      envs[h_env->envc].count = 0; \
      if (h_env->envc <= MAX_INDENT_LEVEL) \
        envs[h_env->envc].indent = envs[h_env->envc - 1].indent + INDENT_INCR; \
      else \
        envs[h_env->envc].indent = envs[h_env->envc - 1].indent; \
    }

#define POP_ENV \
    if (h_env->envc_real-- < h_env->nenv) \
      h_env->envc--;

static int
ul_type(struct parsed_tag *tag, int default_type)
{
    char *p;
    if (parsedtag_get_value(tag, ATTR_TYPE, &p)) {
	if (!strcasecmp(p, "disc"))
	    return (int)'d';
	else if (!strcasecmp(p, "circle"))
	    return (int)'c';
	else if (!strcasecmp(p, "square"))
	    return (int)'s';
    }
    return default_type;
}

int
getMetaRefreshParam(char *q, Str *refresh_uri)
{
    int refresh_interval;
    char *r;
    Str s_tmp = NULL;

    if (q == NULL || refresh_uri == NULL)
	return 0;

    refresh_interval = atoi(q);
    if (refresh_interval < 0)
	return 0;

    while (*q) {
	if (!strncasecmp(q, "url=", 4)) {
	    q += 4;
	    if (*q == '\"')	/* " */
		q++;
	    r = q;
	    while (*r && !IS_SPACE(*r) && *r != ';')
		r++;
	    s_tmp = Strnew_charp_n(q, r - q);

	    if (s_tmp->ptr[s_tmp->length - 1] == '\"') {	/* " 
								 */
		s_tmp->length--;
		s_tmp->ptr[s_tmp->length] = '\0';
	    }
	    q = r;
	}
	while (*q && *q != ';')
	    q++;
	if (*q == ';')
	    q++;
	while (*q && *q == ' ')
	    q++;
    }
    *refresh_uri = s_tmp;
    return refresh_interval;
}

int
HTMLtagproc1(struct parsed_tag *tag, struct html_feed_environ *h_env)
{
    char *p, *q, *r;
    int i, w, x, y, z, count, width;
    struct readbuffer *obuf = h_env->obuf;
    struct environment *envs = h_env->envs;
    Str tmp;
    int hseq;
    int cmd;
#ifdef ID_EXT
    char *id = NULL;
#endif				/* ID_EXT */

    cmd = tag->tagid;

    if (obuf->flag & RB_PRE) {
	switch (cmd) {
	case HTML_NOBR:
	case HTML_N_NOBR:
	case HTML_PRE_INT:
	case HTML_N_PRE_INT:
	    return 1;
	}
    }

    switch (cmd) {
    case HTML_B:
	obuf->in_bold++;
	if (obuf->in_bold > 1)
	    return 1;
	return 0;
    case HTML_N_B:
	if (obuf->in_bold == 1 && close_effect0(obuf, HTML_B))
	    obuf->in_bold = 0;
	if (obuf->in_bold > 0) {
	    obuf->in_bold--;
	    if (obuf->in_bold == 0)
		return 0;
	}
	return 1;
    case HTML_U:
	obuf->in_under++;
	if (obuf->in_under > 1)
	    return 1;
	return 0;
    case HTML_N_U:
	if (obuf->in_under == 1 && close_effect0(obuf, HTML_U))
	    obuf->in_under = 0;
	if (obuf->in_under > 0) {
	    obuf->in_under--;
	    if (obuf->in_under == 0)
		return 0;
	}
	return 1;
    case HTML_EM:
	HTMLlineproc1("<b>", h_env);
	return 1;
    case HTML_N_EM:
	HTMLlineproc1("</b>", h_env);
	return 1;
    case HTML_P:
    case HTML_N_P:
	CLOSE_A;
	if (!(obuf->flag & RB_IGNORE_P)) {
	    flushline(h_env, obuf, envs[h_env->envc].indent, 1, h_env->limit);
	    do_blankline(h_env, obuf, envs[h_env->envc].indent, 0,
			 h_env->limit);
	}
	obuf->flag |= RB_IGNORE_P;
	if (cmd == HTML_P) {
	    set_alignment(obuf, tag);
	    obuf->flag |= RB_P;
	}
	return 1;
    case HTML_BR:
	flushline(h_env, obuf, envs[h_env->envc].indent, 1, h_env->limit);
	h_env->blank_lines = 0;
	return 1;
    case HTML_H:
	if (!(obuf->flag & (RB_PREMODE | RB_IGNORE_P))) {
	    flushline(h_env, obuf, envs[h_env->envc].indent, 0, h_env->limit);
	    do_blankline(h_env, obuf, envs[h_env->envc].indent, 0,
			 h_env->limit);
	}
	HTMLlineproc1("<b>", h_env);
	set_alignment(obuf, tag);
	return 1;
    case HTML_N_H:
	HTMLlineproc1("</b>", h_env);
	if (!(obuf->flag & RB_PREMODE)) {
	    flushline(h_env, obuf, envs[h_env->envc].indent, 0, h_env->limit);
	}
	do_blankline(h_env, obuf, envs[h_env->envc].indent, 0, h_env->limit);
	RB_RESTORE_FLAG(obuf);
	close_anchor(h_env, obuf);
	obuf->flag |= RB_IGNORE_P;
	return 1;
    case HTML_UL:
    case HTML_OL:
    case HTML_BLQ:
	CLOSE_A;
	if (!(obuf->flag & RB_IGNORE_P)) {
	    flushline(h_env, obuf, envs[h_env->envc].indent, 0, h_env->limit);
	    if (!(obuf->flag & RB_PREMODE) &&
		(h_env->envc == 0 || cmd == HTML_BLQ))
		do_blankline(h_env, obuf, envs[h_env->envc].indent, 0,
			     h_env->limit);
	}
	PUSH_ENV(cmd);
	if (cmd == HTML_UL || cmd == HTML_OL) {
	    if (parsedtag_get_value(tag, ATTR_START, &count) && count > 0) {
		envs[h_env->envc].count = count - 1;
	    }
	}
	if (cmd == HTML_OL) {
	    envs[h_env->envc].type = '1';
	    if (parsedtag_get_value(tag, ATTR_TYPE, &p)) {
		envs[h_env->envc].type = (int)*p;
	    }
	}
	if (cmd == HTML_UL)
	    envs[h_env->envc].type = ul_type(tag, 0);
	flushline(h_env, obuf, envs[h_env->envc].indent, 0, h_env->limit);
	return 1;
    case HTML_N_UL:
    case HTML_N_OL:
    case HTML_N_DL:
    case HTML_N_BLQ:
	CLOSE_DT;
	CLOSE_A;
	if (h_env->envc > 0) {
	    flushline(h_env, obuf, envs[h_env->envc - 1].indent, 0,
		      h_env->limit);
	    POP_ENV;
	    if (!(obuf->flag & RB_PREMODE) &&
		(h_env->envc == 0 || cmd == HTML_N_DL || cmd == HTML_N_BLQ)) {
		do_blankline(h_env, obuf,
			     envs[h_env->envc].indent,
			     INDENT_INCR, h_env->limit);
		obuf->flag |= RB_IGNORE_P;
	    }
	}
	close_anchor(h_env, obuf);
	return 1;
    case HTML_DL:
	CLOSE_A;
	if (!(obuf->flag & RB_IGNORE_P)) {
	    flushline(h_env, obuf, envs[h_env->envc].indent, 0, h_env->limit);
	    if (!(obuf->flag & RB_PREMODE))
		do_blankline(h_env, obuf, envs[h_env->envc].indent, 0,
			     h_env->limit);
	}
	PUSH_ENV(cmd);
	if (parsedtag_exists(tag, ATTR_COMPACT))
	    envs[h_env->envc].env = HTML_DL_COMPACT;
	obuf->flag |= RB_IGNORE_P;
	return 1;
    case HTML_LI:
	CLOSE_A;
	CLOSE_DT;
	if (h_env->envc > 0) {
	    Str num;
	    flushline(h_env, obuf,
		      envs[h_env->envc - 1].indent, 0, h_env->limit);
	    envs[h_env->envc].count++;
	    if (parsedtag_get_value(tag, ATTR_VALUE, &p)) {
		count = atoi(p);
		if (count > 0)
		    envs[h_env->envc].count = count;
	    }
	    switch (envs[h_env->envc].env) {
	    case HTML_UL:
		envs[h_env->envc].type = ul_type(tag, envs[h_env->envc].type);
		for (i = 0; i < INDENT_INCR - 3; i++)
		    push_charp(obuf, 1, NBSP, PC_ASCII);
		switch (envs[h_env->envc].type) {
#ifdef KANJI_SYMBOLS
		case 'd':
		    push_charp(obuf, 2, "��", PC_ASCII);
		    break;
		case 'c':
		    push_charp(obuf, 2, "��", PC_ASCII);
		    break;
		case 's':
		    push_charp(obuf, 2, "��", PC_ASCII);
		    break;
#endif				/* KANJI_SYMBOLS */
		default:
		    push_charp(obuf, 2,
			       ullevel[(h_env->envc_real - 1) % MAX_UL_LEVEL],
			       PC_ASCII);
		    break;
		}
		push_charp(obuf, 1, NBSP, PC_ASCII);
		obuf->prevchar = ' ';
		break;
	    case HTML_OL:
		if (parsedtag_get_value(tag, ATTR_TYPE, &p))
		    envs[h_env->envc].type = (int)*p;
		switch (envs[h_env->envc].type) {
		case 'i':
		    num = romanNumeral(envs[h_env->envc].count);
		    break;
		case 'I':
		    num = romanNumeral(envs[h_env->envc].count);
		    Strupper(num);
		    break;
		case 'a':
		    num = romanAlphabet(envs[h_env->envc].count);
		    break;
		case 'A':
		    num = romanAlphabet(envs[h_env->envc].count);
		    Strupper(num);
		    break;
		default:
		    num = Sprintf("%d", envs[h_env->envc].count);
		    break;
		}
#if INDENT_INCR >= 4
		Strcat_charp(num, ". ");
#else				/* INDENT_INCR < 4 */
		Strcat_char(num, '.');
#endif				/* INDENT_INCR < 4 */
		push_spaces(obuf, 1, INDENT_INCR - num->length);
		push_str(obuf, num->length, num, PC_ASCII);
		break;
	    default:
		push_spaces(obuf, 1, INDENT_INCR);
		break;
	    }
	}
	else {
	    flushline(h_env, obuf, 0, 0, h_env->limit);
	}
	obuf->flag |= RB_IGNORE_P;
	return 1;
    case HTML_DT:
	CLOSE_A;
	if (h_env->envc == 0 ||
	    (h_env->envc_real < h_env->nenv &&
	     envs[h_env->envc].env != HTML_DL &&
	     envs[h_env->envc].env != HTML_DL_COMPACT)) {
	    PUSH_ENV(HTML_DL);
	}
	if (h_env->envc > 0) {
	    flushline(h_env, obuf,
		      envs[h_env->envc - 1].indent, 0, h_env->limit);
	}
	if (!(obuf->flag & RB_IN_DT)) {
	    HTMLlineproc1("<b>", h_env);
	    obuf->flag |= RB_IN_DT;
	}
	obuf->flag |= RB_IGNORE_P;
	return 1;
    case HTML_DD:
	CLOSE_A;
	CLOSE_DT;
	if (envs[h_env->envc].env == HTML_DL_COMPACT) {
	    if (obuf->pos > envs[h_env->envc].indent)
		flushline(h_env, obuf, envs[h_env->envc].indent, 0,
			  h_env->limit);
	    else
		push_spaces(obuf, 1, envs[h_env->envc].indent - obuf->pos);
	}
	else
	    flushline(h_env, obuf, envs[h_env->envc].indent, 0, h_env->limit);
	/* obuf->flag |= RB_IGNORE_P; */
	return 1;
    case HTML_TITLE:
	close_anchor(h_env, obuf);
	process_title(tag);
	obuf->flag |= RB_TITLE;
	obuf->end_tag = HTML_N_TITLE;
	return 1;
    case HTML_N_TITLE:
	if (!(obuf->flag & RB_TITLE))
	    return 1;
	obuf->flag &= ~RB_TITLE;
	obuf->end_tag = 0;
	tmp = process_n_title(tag);
	if (tmp)
	    HTMLlineproc1(tmp->ptr, h_env);
	return 1;
    case HTML_TITLE_ALT:
	if (parsedtag_get_value(tag, ATTR_TITLE, &p))
	    h_env->title = html_unquote(p);
	return 0;
    case HTML_FRAMESET:
	PUSH_ENV(cmd);
	push_charp(obuf, 9, "--FRAME--", PC_ASCII);
	flushline(h_env, obuf, envs[h_env->envc].indent, 0, h_env->limit);
	return 0;
    case HTML_N_FRAMESET:
	if (h_env->envc > 0) {
	    POP_ENV;
	    flushline(h_env, obuf, envs[h_env->envc].indent, 0, h_env->limit);
	}
	return 0;
    case HTML_NOFRAMES:
	CLOSE_A;
	flushline(h_env, obuf, envs[h_env->envc].indent, 0, h_env->limit);
	obuf->flag |= (RB_NOFRAMES | RB_IGNORE_P);
	/* istr = str; */
	return 1;
    case HTML_N_NOFRAMES:
	CLOSE_A;
	flushline(h_env, obuf, envs[h_env->envc].indent, 0, h_env->limit);
	obuf->flag &= ~RB_NOFRAMES;
	return 1;
    case HTML_FRAME:
	q = r = NULL;
	parsedtag_get_value(tag, ATTR_SRC, &q);
	parsedtag_get_value(tag, ATTR_NAME, &r);
	if (q) {
	    q = html_quote(q);
	    push_tag(obuf, Sprintf("<a hseq=\"%d\" href=\"%s\">",
				   cur_hseq++, q)->ptr, HTML_A);
	    if (r)
		q = html_quote(r);
	    push_charp(obuf, strlen(q), q, PC_ASCII);
	    push_tag(obuf, "</a>", HTML_N_A);
	}
	flushline(h_env, obuf, envs[h_env->envc].indent, 0, h_env->limit);
	return 0;
    case HTML_HR:
	close_anchor(h_env, obuf);
	tmp = process_hr(tag, h_env->limit, envs[h_env->envc].indent);
	HTMLlineproc1(tmp->ptr, h_env);
	obuf->prevchar = ' ';
	return 1;
    case HTML_PRE:
	x = parsedtag_exists(tag, ATTR_FOR_TABLE);
	CLOSE_A;
	if (!(obuf->flag & RB_IGNORE_P)) {
	    flushline(h_env, obuf, envs[h_env->envc].indent, 0, h_env->limit);
	    if (!x)
		do_blankline(h_env, obuf, envs[h_env->envc].indent, 0,
			     h_env->limit);
	}
	else
	    fillline(obuf, envs[h_env->envc].indent);
	obuf->flag |= (RB_PRE | RB_IGNORE_P);
	/* istr = str; */
	return 1;
    case HTML_N_PRE:
	flushline(h_env, obuf, envs[h_env->envc].indent, 0, h_env->limit);
	if (!(obuf->flag & RB_IGNORE_P)) {
	    do_blankline(h_env, obuf, envs[h_env->envc].indent, 0,
			 h_env->limit);
	    obuf->flag |= RB_IGNORE_P;
	}
	obuf->flag &= ~RB_PRE;
	close_anchor(h_env, obuf);
	return 1;
    case HTML_PRE_INT:
	i = obuf->line->length;
	append_tags(obuf);
	if (!(obuf->flag & RB_SPECIAL)) {
	    set_breakpoint(obuf, obuf->line->length - i);
	}
	obuf->flag |= RB_PRE_INT;
	return 0;
    case HTML_N_PRE_INT:
	push_tag(obuf, "</pre_int>", HTML_N_PRE_INT);
	obuf->flag &= ~RB_PRE_INT;
	if (!(obuf->flag & RB_SPECIAL) && obuf->pos > obuf->bp.pos) {
	    obuf->prevchar = '\0';
	    obuf->prev_ctype = PC_CTRL;
	}
	return 1;
    case HTML_NOBR:
	obuf->flag |= RB_NOBR;
	obuf->nobr_level++;
	return 0;
    case HTML_N_NOBR:
	if (obuf->nobr_level > 0)
	    obuf->nobr_level--;
	if (obuf->nobr_level == 0)
	    obuf->flag &= ~RB_NOBR;
	return 0;
    case HTML_PRE_PLAIN:
	CLOSE_A;
	if (!(obuf->flag & RB_IGNORE_P)) {
	    flushline(h_env, obuf, envs[h_env->envc].indent, 0, h_env->limit);
	    do_blankline(h_env, obuf, envs[h_env->envc].indent, 0,
			 h_env->limit);
	}
	obuf->flag |= (RB_PRE | RB_IGNORE_P);
	return 1;
    case HTML_N_PRE_PLAIN:
	CLOSE_A;
	if (!(obuf->flag & RB_IGNORE_P)) {
	    flushline(h_env, obuf, envs[h_env->envc].indent, 0, h_env->limit);
	    do_blankline(h_env, obuf, envs[h_env->envc].indent, 0,
			 h_env->limit);
	    obuf->flag |= RB_IGNORE_P;
	}
	obuf->flag &= ~RB_PRE;
	return 1;
    case HTML_LISTING:
    case HTML_XMP:
    case HTML_PLAINTEXT:
	CLOSE_A;
	if (!(obuf->flag & RB_IGNORE_P)) {
	    flushline(h_env, obuf, envs[h_env->envc].indent, 0, h_env->limit);
	    do_blankline(h_env, obuf, envs[h_env->envc].indent, 0,
			 h_env->limit);
	}
	obuf->flag |= (RB_PLAIN | RB_IGNORE_P);
	switch (cmd) {
	case HTML_LISTING:
	    obuf->end_tag = HTML_N_LISTING;
	    break;
	case HTML_XMP:
	    obuf->end_tag = HTML_N_XMP;
	    break;
	case HTML_PLAINTEXT:
	    obuf->end_tag = MAX_HTMLTAG;
	    break;
	}
	return 1;
    case HTML_N_LISTING:
    case HTML_N_XMP:
	CLOSE_A;
	if (!(obuf->flag & RB_IGNORE_P)) {
	    flushline(h_env, obuf, envs[h_env->envc].indent, 0, h_env->limit);
	    do_blankline(h_env, obuf, envs[h_env->envc].indent, 0,
			 h_env->limit);
	    obuf->flag |= RB_IGNORE_P;
	}
	obuf->flag &= ~RB_PLAIN;
	obuf->end_tag = 0;
	return 1;
    case HTML_SCRIPT:
	obuf->flag |= RB_SCRIPT;
	obuf->end_tag = HTML_N_SCRIPT;
	return 1;
    case HTML_STYLE:
	obuf->flag |= RB_STYLE;
	obuf->end_tag = HTML_N_STYLE;
	return 1;
    case HTML_N_SCRIPT:
	obuf->flag &= ~RB_SCRIPT;
	obuf->end_tag = 0;
	return 1;
    case HTML_N_STYLE:
	obuf->flag &= ~RB_STYLE;
	obuf->end_tag = 0;
	return 1;
    case HTML_A:
	if (obuf->anchor.url)
	    close_anchor(h_env, obuf);

	hseq = 0;

	if (parsedtag_get_value(tag, ATTR_HREF, &p))
	    obuf->anchor.url = Strnew_charp(p)->ptr;
	if (parsedtag_get_value(tag, ATTR_TARGET, &p))
	    obuf->anchor.target = Strnew_charp(p)->ptr;
	if (parsedtag_get_value(tag, ATTR_REFERER, &p))
	    obuf->anchor.referer = Strnew_charp(p)->ptr;
	if (parsedtag_get_value(tag, ATTR_TITLE, &p))
	    obuf->anchor.title = Strnew_charp(p)->ptr;
	if (parsedtag_get_value(tag, ATTR_ACCESSKEY, &p))
	    obuf->anchor.accesskey = (unsigned char)*p;
	if (parsedtag_get_value(tag, ATTR_HSEQ, &hseq))
	    obuf->anchor.hseq = hseq;

	if (hseq == 0 && obuf->anchor.url) {
	    obuf->anchor.hseq = cur_hseq;
	    tmp = process_anchor(tag, h_env->tagbuf->ptr);
	    push_tag(obuf, tmp->ptr, HTML_A);
	    return 1;
	}
	return 0;
    case HTML_N_A:
	close_anchor(h_env, obuf);
	return 1;
    case HTML_IMG:
	tmp = process_img(tag, h_env->limit);
	HTMLlineproc1(tmp->ptr, h_env);
	return 1;
    case HTML_IMG_ALT:
	if (parsedtag_get_value(tag, ATTR_SRC, &p))
	    obuf->img_alt = Strnew_charp(p);
#ifdef USE_IMAGE
	i = 0;
	if (parsedtag_get_value(tag, ATTR_TOP_MARGIN, &i)) {
	    if (i > obuf->top_margin)
		obuf->top_margin = i;
	}
	i = 0;
	if (parsedtag_get_value(tag, ATTR_BOTTOM_MARGIN, &i)) {
	    if (i > obuf->bottom_margin)
		obuf->bottom_margin = i;
	}
#endif
	return 0;
    case HTML_N_IMG_ALT:
	if (obuf->img_alt) {
	    if (!close_effect0(obuf, HTML_IMG_ALT))
		push_tag(obuf, "</img_alt>", HTML_N_IMG_ALT);
	    obuf->img_alt = NULL;
	}
	return 1;
    case HTML_INPUT_ALT:
	i = 0;
	if (parsedtag_get_value(tag, ATTR_TOP_MARGIN, &i)) {
	    if (i > obuf->top_margin)
		obuf->top_margin = i;
	}
	i = 0;
	if (parsedtag_get_value(tag, ATTR_BOTTOM_MARGIN, &i)) {
	    if (i > obuf->bottom_margin)
		obuf->bottom_margin = i;
	}
	return 0;
    case HTML_TABLE:
	close_anchor(h_env, obuf);
	obuf->table_level++;
	if (obuf->table_level >= MAX_TABLE)
	    break;
	w = BORDER_NONE;
	/* x: cellspacing, y: cellpadding */
	x = 2;
	y = 1;
	z = 0;
	width = 0;
	if (parsedtag_exists(tag, ATTR_BORDER)) {
	    if (parsedtag_get_value(tag, ATTR_BORDER, &w)) {
		if (w > 2)
		    w = BORDER_THICK;
		else if (w < 0) {	/* weird */
		    w = BORDER_THIN;
		}
	    }
	    else
		w = BORDER_THIN;
	}
	if (parsedtag_get_value(tag, ATTR_WIDTH, &i)) {
	    if (obuf->table_level == 0)
		width = REAL_WIDTH(i, h_env->limit - envs[h_env->envc].indent);
	    else
		width = RELATIVE_WIDTH(i);
	}
	if (parsedtag_exists(tag, ATTR_HBORDER))
	    w = BORDER_NOWIN;
	parsedtag_get_value(tag, ATTR_CELLSPACING, &x);
	parsedtag_get_value(tag, ATTR_CELLPADDING, &y);
	parsedtag_get_value(tag, ATTR_VSPACE, &z);
#ifdef ID_EXT
	parsedtag_get_value(tag, ATTR_ID, &id);
#endif				/* ID_EXT */
	tables[obuf->table_level] = begin_table(w, x, y, z);
#ifdef ID_EXT
	if (id != NULL)
	    tables[obuf->table_level]->id = Strnew_charp(id);
#endif				/* ID_EXT */
	table_mode[obuf->table_level].pre_mode = 0;
	table_mode[obuf->table_level].indent_level = 0;
	table_mode[obuf->table_level].nobr_level = 0;
	table_mode[obuf->table_level].caption = 0;
	table_mode[obuf->table_level].end_tag = 0;	/* HTML_UNKNOWN */
#ifndef TABLE_EXPAND
	tables[obuf->table_level]->total_width = width;
#else
	tables[obuf->table_level]->real_width = width;
	tables[obuf->table_level]->total_width = 0;
#endif
	return 1;
    case HTML_N_TABLE:
	/* should be processed in HTMLlineproc() */
	return 1;
    case HTML_CENTER:
	CLOSE_A;
	if (!(obuf->flag & (RB_PREMODE | RB_IGNORE_P)))
	    flushline(h_env, obuf, envs[h_env->envc].indent, 0, h_env->limit);
	RB_SAVE_FLAG(obuf);
	RB_SET_ALIGN(obuf, RB_CENTER);
	return 1;
    case HTML_N_CENTER:
	CLOSE_A;
	if (!(obuf->flag & RB_PREMODE))
	    flushline(h_env, obuf, envs[h_env->envc].indent, 0, h_env->limit);
	RB_RESTORE_FLAG(obuf);
	return 1;
    case HTML_DIV:
	CLOSE_A;
	if (!(obuf->flag & RB_IGNORE_P))
	    flushline(h_env, obuf, envs[h_env->envc].indent, 0, h_env->limit);
	set_alignment(obuf, tag);
	return 1;
    case HTML_N_DIV:
	CLOSE_A;
	flushline(h_env, obuf, envs[h_env->envc].indent, 0, h_env->limit);
	RB_RESTORE_FLAG(obuf);
	return 1;
    case HTML_DIV_INT:
	CLOSE_P;
	if (!(obuf->flag & RB_IGNORE_P))
	    flushline(h_env, obuf, envs[h_env->envc].indent, 0, h_env->limit);
	set_alignment(obuf, tag);
	return 1;
    case HTML_N_DIV_INT:
	CLOSE_P;
	flushline(h_env, obuf, envs[h_env->envc].indent, 0, h_env->limit);
	RB_RESTORE_FLAG(obuf);
	return 1;
    case HTML_FORM:
	CLOSE_A;
	if (!(obuf->flag & RB_IGNORE_P))
	    flushline(h_env, obuf, envs[h_env->envc].indent, 0, h_env->limit);
	tmp = process_form(tag);
	if (tmp)
	    HTMLlineproc1(tmp->ptr, h_env);
	return 1;
    case HTML_N_FORM:
	CLOSE_A;
	flushline(h_env, obuf, envs[h_env->envc].indent, 0, h_env->limit);
	obuf->flag |= RB_IGNORE_P;
	process_n_form();
	return 1;
    case HTML_INPUT:
	close_anchor(h_env, obuf);
	tmp = process_input(tag);
	if (tmp)
	    HTMLlineproc1(tmp->ptr, h_env);
	return 1;
    case HTML_SELECT:
	close_anchor(h_env, obuf);
	tmp = process_select(tag);
	if (tmp)
	    HTMLlineproc1(tmp->ptr, h_env);
	obuf->flag |= RB_INSELECT;
	obuf->end_tag = HTML_N_SELECT;
	return 1;
    case HTML_N_SELECT:
	obuf->flag &= ~RB_INSELECT;
	obuf->end_tag = 0;
	tmp = process_n_select();
	if (tmp)
	    HTMLlineproc1(tmp->ptr, h_env);
	return 1;
    case HTML_OPTION:
	/* nothing */
	return 1;
    case HTML_TEXTAREA:
	close_anchor(h_env, obuf);
	tmp = process_textarea(tag, h_env->limit);
	if (tmp)
	    HTMLlineproc1(tmp->ptr, h_env);
	obuf->flag |= RB_INTXTA;
	obuf->end_tag = HTML_N_TEXTAREA;
	return 1;
    case HTML_N_TEXTAREA:
	obuf->flag &= ~RB_INTXTA;
	obuf->end_tag = 0;
	tmp = process_n_textarea();
	if (tmp)
	    HTMLlineproc1(tmp->ptr, h_env);
	return 1;
    case HTML_ISINDEX:
	p = "";
	q = "!CURRENT_URL!";
	parsedtag_get_value(tag, ATTR_PROMPT, &p);
	parsedtag_get_value(tag, ATTR_ACTION, &q);
	tmp = Strnew_m_charp("<form method=get action=\"",
			     html_quote(q),
			     "\">",
			     html_quote(p),
			     "<input type=text name=\"\" accept></form>",
			     NULL);
	HTMLlineproc1(tmp->ptr, h_env);
	return 1;
    case HTML_META:
	p = q = NULL;
	parsedtag_get_value(tag, ATTR_HTTP_EQUIV, &p);
	parsedtag_get_value(tag, ATTR_CONTENT, &q);
#ifdef JP_CHARSET
	if (p && q && !strcasecmp(p, "Content-Type") &&
	    (q = strcasestr(q, "charset")) != NULL) {
	    q += 7;
	    SKIP_BLANKS(q);
	    if (*q == '=') {
		q++;
		SKIP_BLANKS(q);
		meta_charset = guess_charset(q);
	    }
	}
	else
#endif
	if (p && q && !strcasecmp(p, "refresh")) {
	    int refresh_interval;
	    tmp = NULL;
	    refresh_interval = getMetaRefreshParam(q, &tmp);
	    if (tmp) {
		q = html_quote(tmp->ptr);
		tmp = Sprintf("Refresh (%d sec) <a href=\"%s\">%s</a>",
			      refresh_interval, q, q);
	    }
	    else if (refresh_interval > 0)
		tmp = Sprintf("Refresh (%d sec)", refresh_interval);
	    if (tmp) {
		HTMLlineproc1(tmp->ptr, h_env);
		do_blankline(h_env, obuf, envs[h_env->envc].indent, 0,
			     h_env->limit);
		if (!is_redisplay &&
		    !((obuf->flag & RB_NOFRAMES) && RenderFrame)) {
		    tag->need_reconstruct = TRUE;
		    return 0;
		}
	    }
	}
	return 1;
    case HTML_BASE:
#ifdef USE_IMAGE
	p = NULL;
	if (parsedtag_get_value(tag, ATTR_HREF, &p)) {
	    if (!cur_baseURL)
		cur_baseURL = New(ParsedURL);
	    parseURL(p, cur_baseURL, NULL);
	}
#endif
    case HTML_MAP:
    case HTML_N_MAP:
    case HTML_AREA:
	return 0;
    case HTML_DEL:
	if (displayInsDel)
	    HTMLlineproc1("<U>[DEL:</U>", h_env);
	else
	    obuf->flag |= RB_DEL;
	return 1;
    case HTML_N_DEL:
	if (displayInsDel)
	    HTMLlineproc1("<U>:DEL]</U>", h_env);
	else
	    obuf->flag &= ~RB_DEL;
	return 1;
    case HTML_S:
	if (displayInsDel)
	    HTMLlineproc1("<U>[S:</U>", h_env);
	else
	    obuf->flag |= RB_S;
	return 1;
    case HTML_N_S:
	if (displayInsDel)
	    HTMLlineproc1("<U>:S]</U>", h_env);
	else
	    obuf->flag &= ~RB_S;
	return 1;
    case HTML_INS:
	if (displayInsDel)
	    HTMLlineproc1("<U>[INS:</U>", h_env);
	return 1;
    case HTML_N_INS:
	if (displayInsDel)
	    HTMLlineproc1("<U>:INS]</U>", h_env);
	return 1;
    case HTML_SUP:
	if (!(obuf->flag & (RB_DEL | RB_S)))
	    HTMLlineproc1("^", h_env);
	return 1;
    case HTML_N_SUP:
	return 1;
    case HTML_SUB:
	if (!(obuf->flag & (RB_DEL | RB_S)))
	    HTMLlineproc1("[", h_env);
	return 1;
    case HTML_N_SUB:
	if (!(obuf->flag & (RB_DEL | RB_S)))
	    HTMLlineproc1("]", h_env);
	return 1;
    case HTML_FONT:
    case HTML_N_FONT:
    case HTML_NOP:
	return 1;
    case HTML_BGSOUND:
	if (view_unseenobject) {
	    if (parsedtag_get_value(tag, ATTR_SRC, &p)) {
		Str s;
		q = html_quote(p);
		s = Sprintf("<A HREF=\"%s\">bgsound(%s)</A>", q, q);
		HTMLlineproc1(s->ptr, h_env);
	    }
	}
	return 1;
    case HTML_EMBED:
	if (view_unseenobject) {
	    if (parsedtag_get_value(tag, ATTR_SRC, &p)) {
		Str s;
		q = html_quote(p);
		s = Sprintf("<A HREF=\"%s\">embed(%s)</A>", q, q);
		HTMLlineproc1(s->ptr, h_env);
	    }
	}
	return 1;
    case HTML_APPLET:
	if (view_unseenobject) {
	    if (parsedtag_get_value(tag, ATTR_ARCHIVE, &p)) {
		Str s;
		q = html_quote(p);
		s = Sprintf("<A HREF=\"%s\">applet archive(%s)</A>", q, q);
		HTMLlineproc1(s->ptr, h_env);
	    }
	}
	return 1;
    case HTML_BODY:
	if (view_unseenobject) {
	    if (parsedtag_get_value(tag, ATTR_BACKGROUND, &p)) {
		Str s;
		q = html_quote(p);
		s = Sprintf("<IMG SRC=\"%s\" ALT=\"bg image(%s)\"><BR>", q, q);
		HTMLlineproc1(s->ptr, h_env);
	    }
	}
    case HTML_N_HEAD:
	if (obuf->flag & RB_TITLE)
	    HTMLlineproc1("</title>", h_env);
    case HTML_HEAD:
    case HTML_N_BODY:
	return 1;
    default:
	/* obuf->prevchar = '\0'; */
	return 0;
    }
    /* not reached */
    return 0;
}

#define PPUSH(p,c) {outp[pos]=(p);outc[pos]=(c);pos++;}

static TextLineListItem *_tl_lp2;

static Str
textlist_feed()
{
    TextLine *p;
    if (_tl_lp2 != NULL) {
	p = _tl_lp2->ptr;
	_tl_lp2 = _tl_lp2->next;
	return p->line;
    }
    return NULL;
}

static void
HTMLlineproc2body(Buffer *buf, Str (*feed) (), int llimit)
{
    Anchor *a_href = NULL, *a_img = NULL, *a_form = NULL;
    char outc[LINELEN];
    char *p, *q, *r, *s, *t, *str;
    Lineprop outp[LINELEN], mode, effect;
    int pos;
    int nlines;
    FILE *debug = NULL;
    struct frameset *frameset_s[FRAMESTACK_SIZE];
    int frameset_sp = -1;
    union frameset_element *idFrame = NULL;
    char *id = NULL;
    int hseq, form_id;
    Str line;
    char *endp;
#ifndef KANJI_SYMBOLS
    char rule = 0;
#endif
    int internal = 0;
    Anchor **a_textarea = NULL;
#ifdef MENU_SELECT
    Anchor **a_select = NULL;
#endif

    n_textarea = -1;
    if (!max_textarea) {	/* halfload */
	max_textarea = MAX_TEXTAREA;
	textarea_str = New_N(Str, max_textarea);
	a_textarea = New_N(Anchor *, max_textarea);
    }
#ifdef MENU_SELECT
    n_select = -1;
    if (!max_select) {		/* halfload */
	max_select = MAX_SELECT;
	select_option = New_N(FormSelectOption, max_select);
	a_select = New_N(Anchor *, max_select);
    }
#endif

    if (w3m_debug)
	debug = fopen("zzzerr", "a");

    effect = 0;
    nlines = 0;
    while ((line = feed()) != NULL) {
	if (w3m_debug) {
	    Strfputs(line, debug);
	    fputc('\n', debug);
	}
	if (n_textarea >= 0 && *(line->ptr) != '<') {	/* halfload */
	    Strcat(textarea_str[n_textarea], line);
	    continue;
	}
      proc_again:
	if (++nlines == llimit)
	    break;
	pos = 0;
#ifdef ENABLE_REMOVE_TRAILINGSPACES
	Strremovetrailingspaces(line);
#endif
	str = line->ptr;
	endp = str + line->length;
	while (str < endp && pos < LINELEN) {
	    mode = get_mctype(str);
#ifndef KANJI_SYMBOLS
	    if (effect & PC_RULE && *str != '<') {
		PPUSH(PC_ASCII | effect, rule | 0x80);
		str++;
	    }
	    else
#endif
	    if (mode == PC_CTRL || IS_INTSPACE(*str)) {
		PPUSH(PC_ASCII | effect, ' ');
		str++;
	    }
#ifdef JP_CHARSET
	    else if (mode == PC_KANJI) {
		PPUSH(PC_KANJI1 | effect, str[0]);
		PPUSH(PC_KANJI2 | effect, str[1]);
		str += 2;
	    }
#endif
	    else if (mode == PC_ASCII && *str != '<' && *str != '&') {
		PPUSH(mode | effect, *(str++));
	    }
	    else if (*str == '&') {
		/* 
		 * & escape processing
		 */
		int emode;
		p = getescapecmd(&str);
		while (*p) {
		    emode = get_mctype(p);
#ifdef JP_CHARSET
		    if (emode == PC_KANJI) {
			PPUSH(PC_KANJI1 | effect, p[0]);
			PPUSH(PC_KANJI2 | effect, p[1]);
			p += 2;
		    }
		    else
#endif
		    {
			PPUSH(emode | effect, *(p++));
		    }
		}
	    }
	    else {
		/* tag processing */
		struct parsed_tag *tag;
		if (!(tag = parse_tag(&str, TRUE)))
		    continue;
		switch (tag->tagid) {
		case HTML_B:
		    effect |= PE_BOLD;
		    break;
		case HTML_N_B:
		    effect &= ~PE_BOLD;
		    break;
		case HTML_U:
		    effect |= PE_UNDER;
		    break;
		case HTML_N_U:
		    effect &= ~PE_UNDER;
		    break;
		case HTML_A:
		    if (renderFrameSet &&
			parsedtag_get_value(tag, ATTR_FRAMENAME, &p)) {
			p = url_quote_conv(p, buf->document_code);
			if (!idFrame || strcmp(idFrame->body->name, p)) {
			    idFrame = search_frame(renderFrameSet, p);
			    if (idFrame && idFrame->body->attr != F_BODY)
				idFrame = NULL;
			}
		    }
		    p = r = s = NULL;
		    q = buf->baseTarget;
		    t = "";
		    hseq = 0;
		    id = NULL;
		    if (parsedtag_get_value(tag, ATTR_NAME, &id)) {
			id = url_quote_conv(id, buf->document_code);
			registerName(buf, id, currentLn(buf), pos);
		    }
		    if (parsedtag_get_value(tag, ATTR_HREF, &p))
			p = url_quote_conv(remove_space(p),
					   buf->document_code);
		    if (parsedtag_get_value(tag, ATTR_TARGET, &q))
			q = url_quote_conv(q, buf->document_code);
		    if (parsedtag_get_value(tag, ATTR_REFERER, &r))
			r = url_quote_conv(r, buf->document_code);
		    parsedtag_get_value(tag, ATTR_TITLE, &s);
		    parsedtag_get_value(tag, ATTR_ACCESSKEY, &t);
		    parsedtag_get_value(tag, ATTR_HSEQ, &hseq);
		    if (hseq > 0)
			buf->hmarklist =
			    putHmarker(buf->hmarklist, currentLn(buf),
				       pos, hseq - 1);
		    if (id && idFrame)
			idFrame->body->nameList =
			    putAnchor(idFrame->body->nameList, id, NULL,
				      (Anchor **)NULL, NULL, NULL, '\0',
				      currentLn(buf), pos);
		    if (p) {
			effect |= PE_ANCHOR;
			a_href = registerHref(buf, p, q, r, s,
					      *t, currentLn(buf), pos);
			a_href->hseq = ((hseq > 0) ? hseq : -hseq) - 1;
			a_href->slave = (hseq > 0) ? FALSE : TRUE;
		    }
		    break;
		case HTML_N_A:
		    effect &= ~PE_ANCHOR;
		    if (a_href) {
			a_href->end.line = currentLn(buf);
			a_href->end.pos = pos;
			if (a_href->start.line == a_href->end.line &&
			    a_href->start.pos == a_href->end.pos)
			    a_href->hseq = -1;
			a_href = NULL;
		    }
		    break;

		case HTML_LINK:
		    addLink(buf, tag);
		    break;

		case HTML_IMG_ALT:
		    if (parsedtag_get_value(tag, ATTR_SRC, &p)) {
#ifdef USE_IMAGE
			int w = -1, h = -1, iseq = 0, ismap = 0;
			int xoffset = 0, yoffset = 0, top = 0, bottom = 0;
			parsedtag_get_value(tag, ATTR_HSEQ, &iseq);
			parsedtag_get_value(tag, ATTR_WIDTH, &w);
			parsedtag_get_value(tag, ATTR_HEIGHT, &h);
			parsedtag_get_value(tag, ATTR_XOFFSET, &xoffset);
			parsedtag_get_value(tag, ATTR_YOFFSET, &yoffset);
			parsedtag_get_value(tag, ATTR_TOP_MARGIN, &top);
			parsedtag_get_value(tag, ATTR_BOTTOM_MARGIN, &bottom);
			if (parsedtag_exists(tag, ATTR_ISMAP))
			    ismap = 1;
			q = NULL;
			parsedtag_get_value(tag, ATTR_USEMAP, &q);
			if (iseq > 0) {
			    buf->imarklist = putHmarker(buf->imarklist,
							currentLn(buf), pos,
							iseq - 1);
			}
#endif
			s = NULL;
			parsedtag_get_value(tag, ATTR_TITLE, &s);
			p = url_quote_conv(remove_space(p),
					   buf->document_code);
			a_img = registerImg(buf, p, s, currentLn(buf), pos);
#ifdef USE_IMAGE
			a_img->hseq = iseq;
			a_img->image = NULL;
			if (iseq > 0) {
			    ParsedURL u;
			    Image *image;

			    parseURL2(a_img->url, &u, cur_baseURL);
			    a_img->image = image = New(Image);
			    image->url = parsedURL2Str(&u)->ptr;
			    if (!uncompressed_file_type(u.file, &image->ext))
				image->ext = filename_extension(u.file, TRUE);
			    image->cache = NULL;
			    image->width =
				(w > MAX_IMAGE_SIZE) ? MAX_IMAGE_SIZE : w;
			    image->height =
				(h > MAX_IMAGE_SIZE) ? MAX_IMAGE_SIZE : h;
			    image->xoffset = xoffset;
			    image->yoffset = yoffset;
			    image->y = currentLn(buf) - top;
			    if (image->xoffset < 0 && pos == 0)
				image->xoffset = 0;
			    if (image->yoffset < 0 && image->y == 1)
				image->yoffset = 0;
			    image->rows = 1 + top + bottom;
			    image->map = q;
			    image->ismap = ismap;
			    image->touch = 0;
			    image->cache = getImage(image, cur_baseURL,
						    IMG_FLAG_SKIP);
			}
			else if (iseq < 0) {
			    BufferPoint *po = buf->imarklist->marks - iseq - 1;
			    Anchor *a = retrieveAnchor(buf->img,
						       po->line, po->pos);
			    if (a) {
				a_img->url = a->url;
				a_img->image = a->image;
			    }
			}
#endif
		    }
		    effect |= PE_IMAGE;
		    break;
		case HTML_N_IMG_ALT:
		    effect &= ~PE_IMAGE;
		    if (a_img) {
			a_img->end.line = currentLn(buf);
			a_img->end.pos = pos;
		    }
		    a_img = NULL;
		    break;
		case HTML_INPUT_ALT:
		    {
			FormList *form;
			int top = 0, bottom = 0;
			int textareanumber = -1;
#ifdef MENU_SELECT
			int selectnumber = -1;
#endif
			hseq = 0;
			form_id = -1;

			parsedtag_get_value(tag, ATTR_HSEQ, &hseq);
			parsedtag_get_value(tag, ATTR_FID, &form_id);
			parsedtag_get_value(tag, ATTR_TOP_MARGIN, &top);
			parsedtag_get_value(tag, ATTR_BOTTOM_MARGIN, &bottom);
			if (form_id < 0 || form_id > form_max || forms == NULL)
			    break;	/* outside of <form>..</form> */
			form = forms[form_id];
			if (hseq > 0) {
			    int hpos = pos;
			    if (*str == '[')
				hpos++;
			    buf->hmarklist =
				putHmarker(buf->hmarklist, currentLn(buf),
					   hpos, hseq - 1);
			}
			if (!form->target)
			    form->target = buf->baseTarget;
			if (a_textarea &&
			    parsedtag_get_value(tag, ATTR_TEXTAREANUMBER,
						&textareanumber)) {
			    if (textareanumber >= max_textarea) {
				max_textarea = 2 * textareanumber;
				textarea_str = New_Reuse(Str, textarea_str,
							 max_textarea);
				a_textarea = New_Reuse(Anchor *, a_textarea,
						       max_textarea);
			    }
			}
#ifdef MENU_SELECT
			if (a_select &&
			    parsedtag_get_value(tag, ATTR_SELECTNUMBER,
						&selectnumber)) {
			    if (selectnumber >= max_select) {
				max_select = 2 * selectnumber;
				select_option = New_Reuse(FormSelectOption,
							  select_option,
							  max_select);
				a_select = New_Reuse(Anchor *, a_select,
						     max_select);
			    }
			}
#endif
			a_form =
			    registerForm(buf, form, tag, currentLn(buf), pos);
			if (a_textarea && textareanumber >= 0)
			    a_textarea[textareanumber] = a_form;
#ifdef MENU_SELECT
			if (a_select && selectnumber >= 0)
			    a_select[selectnumber] = a_form;
#endif
			if (a_form) {
			    a_form->hseq = hseq - 1;
			    a_form->y = currentLn(buf) - top;
			    a_form->rows = 1 + top + bottom;
			    if (!parsedtag_exists(tag, ATTR_NO_EFFECT))
				effect |= PE_FORM;
			    break;
			}
		    }
		case HTML_N_INPUT_ALT:
		    effect &= ~PE_FORM;
		    if (a_form) {
			a_form->end.line = currentLn(buf);
			a_form->end.pos = pos;
			if (a_form->start.line == a_form->end.line &&
			    a_form->start.pos == a_form->end.pos)
			    a_form->hseq = -1;
		    }
		    a_form = NULL;
		    break;
		case HTML_MAP:
		    if (parsedtag_get_value(tag, ATTR_NAME, &p)) {
			MapList *m = New(MapList);
			m->name = Strnew_charp(p);
			m->area = newGeneralList();
			m->next = buf->maplist;
			buf->maplist = m;
		    }
		    break;
		case HTML_N_MAP:
		    /* nothing to do */
		    break;
		case HTML_AREA:
		    if (buf->maplist == NULL)	/* outside of <map>..</map> */
			break;
		    if (parsedtag_get_value(tag, ATTR_HREF, &p)) {
			MapArea *a;
			p = url_quote_conv(remove_space(p),
					   buf->document_code);
			t = NULL;
			parsedtag_get_value(tag, ATTR_TARGET, &t);
			q = "";
			parsedtag_get_value(tag, ATTR_ALT, &q);
			r = NULL;
			s = NULL;
#ifdef USE_IMAGE
			parsedtag_get_value(tag, ATTR_SHAPE, &r);
			parsedtag_get_value(tag, ATTR_COORDS, &s);
#endif
			a = newMapArea(p, t, q, r, s);
			pushValue(buf->maplist->area, (void *)a);
		    }
		    break;
		case HTML_FRAMESET:
		    frameset_sp++;
		    if (frameset_sp >= FRAMESTACK_SIZE)
			break;
		    frameset_s[frameset_sp] = newFrameSet(tag);
		    if (frameset_s[frameset_sp] == NULL)
			break;
		    if (frameset_sp == 0) {
			if (buf->frameset == NULL) {
			    buf->frameset = frameset_s[frameset_sp];
			}
			else
			    pushFrameTree(&(buf->frameQ),
					  frameset_s[frameset_sp], NULL);
		    }
		    else
			addFrameSetElement(frameset_s[frameset_sp - 1],
					   *(union frameset_element *)
					   &frameset_s[frameset_sp]);
		    break;
		case HTML_N_FRAMESET:
		    if (frameset_sp >= 0)
			frameset_sp--;
		    break;
		case HTML_FRAME:
		    if (frameset_sp >= 0 && frameset_sp < FRAMESTACK_SIZE) {
			union frameset_element element;

			element.body = newFrame(tag, buf);
			addFrameSetElement(frameset_s[frameset_sp], element);
		    }
		    break;
		case HTML_BASE:
		    if (parsedtag_get_value(tag, ATTR_HREF, &p)) {
			p = url_quote_conv(remove_space(p),
					   buf->document_code);
			if (!buf->baseURL)
			    buf->baseURL = New(ParsedURL);
			parseURL(p, buf->baseURL, NULL);
		    }
		    if (parsedtag_get_value(tag, ATTR_TARGET, &p))
			buf->baseTarget =
			    url_quote_conv(p, buf->document_code);
		    break;
		case HTML_META:
		    p = q = NULL;
		    parsedtag_get_value(tag, ATTR_HTTP_EQUIV, &p);
		    parsedtag_get_value(tag, ATTR_CONTENT, &q);
		    if (p && q && !strcasecmp(p, "refresh") && MetaRefresh) {
			Str tmp = NULL;
			int refresh_interval = getMetaRefreshParam(q, &tmp);
#ifdef USE_ALARM
			if (tmp) {
			    p = url_quote_conv(remove_space(tmp->ptr),
					       buf->document_code);
			    buf->event = setAlarmEvent(buf->event,
						       refresh_interval,
						       AL_IMPLICIT_ONCE,
						       FUNCNAME_gorURL, p);
			}
			else if (refresh_interval > 0)
			    buf->event = setAlarmEvent(buf->event,
						       refresh_interval,
						       AL_IMPLICIT,
						       FUNCNAME_reload, NULL);
#else
			if (tmp && refresh_interval == 0) {
			    p = url_quote_conv(remove_space(tmp->ptr),
					       buf->document_code);
			    pushEvent(FUNCNAME_gorURL, p);
			}
#endif
		    }
		    break;
		case HTML_INTERNAL:
		    internal = HTML_INTERNAL;
		    break;
		case HTML_N_INTERNAL:
		    internal = HTML_N_INTERNAL;
		    break;
		case HTML_FORM_INT:
		    if (parsedtag_get_value(tag, ATTR_FID, &form_id))
			process_form_int(tag, form_id);
		    break;
		case HTML_TEXTAREA_INT:
		    if (parsedtag_get_value(tag, ATTR_TEXTAREANUMBER,
					    &n_textarea)
			&& n_textarea < max_textarea) {
			textarea_str[n_textarea] = Strnew();
		    }
		    else
			n_textarea = -1;
		    break;
		case HTML_N_TEXTAREA_INT:
		    if (n_textarea >= 0) {
			FormItemList *item =
			    (FormItemList *)a_textarea[n_textarea]->url;
			item->init_value = item->value =
			    textarea_str[n_textarea];
		    }
		    break;
#ifdef MENU_SELECT
		case HTML_SELECT_INT:
		    if (parsedtag_get_value(tag, ATTR_SELECTNUMBER, &n_select)
			&& n_select < max_select) {
			select_option[n_select].first = NULL;
			select_option[n_select].last = NULL;
		    }
		    else
			n_select = -1;
		    break;
		case HTML_N_SELECT_INT:
		    if (n_select >= 0) {
			FormItemList *item =
			    (FormItemList *)a_select[n_select]->url;
			item->select_option = select_option[n_select].first;
			chooseSelectOption(item, item->select_option);
			item->init_selected = item->selected;
			item->init_value = item->value;
			item->init_label = item->label;
		    }
		    break;
		case HTML_OPTION_INT:
		    if (n_select >= 0) {
			int selected;
			q = "";
			parsedtag_get_value(tag, ATTR_LABEL, &q);
			p = q;
			parsedtag_get_value(tag, ATTR_VALUE, &p);
			selected = parsedtag_exists(tag, ATTR_SELECTED);
			addSelectOption(&select_option[n_select],
					Strnew_charp(p), Strnew_charp(q),
					selected);
		    }
		    break;
#endif
		case HTML_TITLE_ALT:
		    if (parsedtag_get_value(tag, ATTR_TITLE, &p))
			buf->buffername = html_unquote(p);
		    break;
#ifndef KANJI_SYMBOLS
		case HTML_RULE:
		    effect |= PC_RULE;
		    if (parsedtag_get_value(tag, ATTR_TYPE, &p))
			rule = (char)atoi(p);
		    break;
		case HTML_N_RULE:
		    effect &= ~PC_RULE;
		    break;
#endif				/* not KANJI_SYMBOLS */
		}
#ifdef	ID_EXT
		id = NULL;
		if (parsedtag_get_value(tag, ATTR_ID, &id)) {
		    id = url_quote_conv(id, buf->document_code);
		    registerName(buf, id, currentLn(buf), pos);
		}
		if (renderFrameSet &&
		    parsedtag_get_value(tag, ATTR_FRAMENAME, &p)) {
		    p = url_quote_conv(p, buf->document_code);
		    if (!idFrame || strcmp(idFrame->body->name, p)) {
			idFrame = search_frame(renderFrameSet, p);
			if (idFrame && idFrame->body->attr != F_BODY)
			    idFrame = NULL;
		    }
		}
		if (id && idFrame)
		    idFrame->body->nameList =
			putAnchor(idFrame->body->nameList, id, NULL,
				  (Anchor **)NULL, NULL, NULL, '\0',
				  currentLn(buf), pos);
#endif				/* ID_EXT */
	    }
	}
	/* end of processing for one line */
	if (!internal)
	    addnewline(buf, outc, outp,
#ifdef USE_ANSI_COLOR
		       NULL,
#endif
		       pos, nlines);
	if (internal == HTML_N_INTERNAL)
	    internal = 0;
	if (str != endp) {
	    line = Strsubstr(line, str - line->ptr, endp - str);
	    goto proc_again;
	}
    }
    if (w3m_debug)
	fclose(debug);
    for (form_id = 1; form_id <= form_max; form_id++)
	forms[form_id]->next = forms[form_id - 1];
    buf->formlist = (form_max >= 0) ? forms[form_max] : NULL;
    if (n_textarea)
	addMultirowsForm(buf, buf->formitem);
#ifdef USE_IMAGE
    addMultirowsImg(buf, buf->img);
#endif
}

static void
addLink(Buffer *buf, struct parsed_tag *tag)
{
    char *href = NULL, *title = NULL, *ctype = NULL, *rel = NULL, *rev = NULL;
    char type = LINK_TYPE_NONE;
    LinkList *l;

    parsedtag_get_value(tag, ATTR_HREF, &href);
    if (href)
	href = url_quote_conv(remove_space(href), buf->document_code);
    parsedtag_get_value(tag, ATTR_TITLE, &title);
    parsedtag_get_value(tag, ATTR_TYPE, &ctype);
    parsedtag_get_value(tag, ATTR_REL, &rel);
    if (rel != NULL) {
	/* forward link type */
	type = LINK_TYPE_REL;
	if (title == NULL)
	    title = rel;
    }
    parsedtag_get_value(tag, ATTR_REV, &rev);
    if (rev != NULL) {
	/* reverse link type */
	type = LINK_TYPE_REV;
	if (title == NULL)
	    title = rev;
    }

    l = New(LinkList);
    l->url = href;
    l->title = title;
    l->ctype = ctype;
    l->type = type;
    l->next = NULL;
    if (buf->linklist) {
	LinkList *i;
	for (i = buf->linklist; i->next; i = i->next) ;
	i->next = l;
    }
    else
	buf->linklist = l;
}

void
HTMLlineproc2(Buffer *buf, TextLineList *tl)
{
    _tl_lp2 = tl->first;
    HTMLlineproc2body(buf, textlist_feed, -1);
}

static InputStream _file_lp2;

static Str
file_feed()
{
    Str s;
    s = StrISgets(_file_lp2);
    if (s->length == 0) {
	ISclose(_file_lp2);
	return NULL;
    }
    return s;
}

void
HTMLlineproc3(Buffer *buf, InputStream stream)
{
    _file_lp2 = stream;
    HTMLlineproc2body(buf, file_feed, -1);
}

static void
proc_escape(struct readbuffer *obuf, char **str_return)
{
    char *str = *str_return, *estr;
    int ech = getescapechar(str_return);
    int width, n_add = *str_return - str;
    Lineprop mode;

    if (ech < 0) {
	*str_return = str;
	proc_mchar(obuf, obuf->flag & RB_SPECIAL, 1, str_return, PC_ASCII);
	return;
    }
    mode = IS_CNTRL(ech) ? PC_CTRL : PC_ASCII;

    check_breakpoint(obuf, obuf->flag & RB_SPECIAL, ech);
    estr = conv_entity(ech);
    width = strlen(estr);
    if (width == 1 && ech == (unsigned char)*estr &&
	ech != '&' && ech != '<' && ech != '>')
	push_charp(obuf, width, estr, mode);
    else
	push_nchars(obuf, width, str, n_add, mode);
    obuf->prevchar = ech;
    obuf->prev_ctype = mode;
}


static int
need_flushline(struct html_feed_environ *h_env, struct readbuffer *obuf,
	       Lineprop mode)
{
    char ch;

    if (obuf->flag & RB_PRE_INT) {
	if (obuf->pos > h_env->limit)
	    return 1;
	else
	    return 0;
    }

    ch = Strlastchar(obuf->line);
    /* if (ch == ' ' && obuf->tag_sp > 0) */
    if (ch == ' ')
	return 0;

    if (obuf->pos > h_env->limit)
	return 1;

    return 0;
}

static int
table_width(struct html_feed_environ *h_env, int table_level)
{
    int width;
    if (table_level < 0)
	return 0;
    width = tables[table_level]->total_width;
    if (table_level > 0 || width > 0)
	return width;
    return h_env->limit - h_env->envs[h_env->envc].indent;
}

/* HTML processing first pass */
void
HTMLlineproc0(char *line, struct html_feed_environ *h_env, int internal)
{
    Lineprop mode;
    int cmd;
    struct readbuffer *obuf = h_env->obuf;
    int indent, delta;
    struct parsed_tag *tag;
    Str tokbuf;
    struct table *tbl = NULL;
    struct table_mode *tbl_mode = NULL;
    int tbl_width = 0;

    if (w3m_debug) {
	FILE *f = fopen("zzzproc1", "a");
	fprintf(f, "%c%c%c%c",
		(obuf->flag & RB_PREMODE) ? 'P' : ' ',
		(obuf->table_level >= 0) ? 'T' : ' ',
		(obuf->flag & RB_INTXTA) ? 'X' : ' ',
		(obuf->flag & (RB_SCRIPT | RB_STYLE)) ? 'S' : ' ');
	fprintf(f, "HTMLlineproc1(\"%s\",%d,%lx)\n", line, h_env->limit,
		(unsigned long)h_env);
	fclose(f);
    }

    tokbuf = Strnew();

  table_start:
    if (obuf->table_level >= 0) {
	int level = min(obuf->table_level, MAX_TABLE - 1);
	tbl = tables[level];
	tbl_mode = &table_mode[level];
	tbl_width = table_width(h_env, level);
    }

    while (*line != '\0') {
	char *str, *p;
	int is_tag = FALSE;
	int pre_mode = (obuf->table_level >= 0) ? tbl_mode->pre_mode :
	    obuf->flag;
	int end_tag = (obuf->table_level >= 0) ? tbl_mode->end_tag :
	    obuf->end_tag;

	if (*line == '<' || obuf->status != R_ST_NORMAL) {
	    /* 
	     * Tag processing
	     */
	    if (obuf->status == R_ST_EOL)
		obuf->status = R_ST_NORMAL;
	    else {
		read_token(h_env->tagbuf, &line, &obuf->status,
			   pre_mode & RB_PREMODE, obuf->status != R_ST_NORMAL);
		if (obuf->status != R_ST_NORMAL)
		    return;
	    }
	    if (h_env->tagbuf->length == 0)
		continue;
	    str = h_env->tagbuf->ptr;
	    if (*str == '<') {
		if (str[1] && REALLY_THE_BEGINNING_OF_A_TAG(str))
		    is_tag = TRUE;
		else if (!(pre_mode & (RB_PLAIN | RB_INTXTA | RB_INSELECT |
				       RB_SCRIPT | RB_STYLE | RB_TITLE))) {
		    line = Strnew_m_charp(str + 1, line, NULL)->ptr;
		    str = "&lt;";
		}
	    }
	}
	else {
	    read_token(tokbuf, &line, &obuf->status, pre_mode & RB_PREMODE, 0);
	    if (obuf->status != R_ST_NORMAL)	/* R_ST_AMP ? */
		obuf->status = R_ST_NORMAL;
	    str = tokbuf->ptr;
	}

	if (pre_mode & (RB_PLAIN | RB_INTXTA | RB_INSELECT | RB_SCRIPT |
			RB_STYLE | RB_TITLE)) {
	    if (is_tag) {
		p = str;
		if ((tag = parse_tag(&p, internal))) {
		    if (tag->tagid == end_tag ||
			(pre_mode & RB_INSELECT && tag->tagid == HTML_N_FORM)
			|| (pre_mode & RB_TITLE
			    && (tag->tagid == HTML_N_HEAD
				|| tag->tagid == HTML_BODY)))
			goto proc_normal;
		}
	    }
	    /* title */
	    if (pre_mode & RB_TITLE) {
		feed_title(str);
		continue;
	    }
	    /* select */
	    if (pre_mode & RB_INSELECT) {
		if (obuf->table_level >= 0)
		    goto proc_normal;
		feed_select(str);
		continue;
	    }
	    if (is_tag) {
		if (strncmp(str, "<!--", 4) && (p = strchr(str + 1, '<'))) {
		    str = Strnew_charp_n(str, p - str)->ptr;
		    line = Strnew_m_charp(p, line, NULL)->ptr;
		}
		is_tag = FALSE;
	    }
	    if (obuf->table_level >= 0)
		goto proc_normal;
	    /* textarea */
	    if (pre_mode & RB_INTXTA) {
		feed_textarea(str);
		continue;
	    }
	    /* script */
	    if (pre_mode & RB_SCRIPT)
		continue;
	    /* style */
	    if (pre_mode & RB_STYLE)
		continue;
	}

      proc_normal:
	if (obuf->table_level >= 0) {
	    /* 
	     * within table: in <table>..</table>, all input tokens
	     * are fed to the table renderer, and then the renderer
	     * makes HTML output.
	     */
	    switch (feed_table(tbl, str, tbl_mode, tbl_width, internal)) {
	    case 0:
		/* </table> tag */
		obuf->table_level--;
		if (obuf->table_level >= MAX_TABLE - 1)
		    continue;
		end_table(tbl);
		if (obuf->table_level >= 0) {
		    struct table *tbl0 = tables[obuf->table_level];
		    str = Sprintf("<table_alt tid=%d>", tbl0->ntable)->ptr;
		    pushTable(tbl0, tbl);
		    tbl = tbl0;
		    tbl_mode = &table_mode[obuf->table_level];
		    tbl_width = table_width(h_env, obuf->table_level);
		    feed_table(tbl, str, tbl_mode, tbl_width, TRUE);
		    continue;
		    /* continue to the next */
		}
		if (obuf->flag & RB_DEL)
		    continue;
		/* all tables have been read */
		if (tbl->vspace > 0 && !(obuf->flag & RB_IGNORE_P)) {
		    int indent = h_env->envs[h_env->envc].indent;
		    flushline(h_env, obuf, indent, 0, h_env->limit);
		    do_blankline(h_env, obuf, indent, 0, h_env->limit);
		}
		save_fonteffect(h_env, obuf);
		renderTable(tbl, tbl_width, h_env);
		restore_fonteffect(h_env, obuf);
		obuf->flag &= ~RB_IGNORE_P;
		if (tbl->vspace > 0) {
		    int indent = h_env->envs[h_env->envc].indent;
		    do_blankline(h_env, obuf, indent, 0, h_env->limit);
		    obuf->flag |= RB_IGNORE_P;
		}
		obuf->prevchar = ' ';
		continue;
	    case 1:
		/* <table> tag */
		break;
	    default:
		continue;
	    }
	}

	if (is_tag) {
/*** Beginning of a new tag ***/
	    if ((tag = parse_tag(&str, internal)))
		cmd = tag->tagid;
	    else
		continue;
	    /* process tags */
	    if (HTMLtagproc1(tag, h_env) == 0) {
		/* preserve the tag for second-stage processing */
		if (parsedtag_need_reconstruct(tag))
		    h_env->tagbuf = parsedtag2str(tag);
		push_tag(obuf, h_env->tagbuf->ptr, cmd);
	    }
#ifdef ID_EXT
	    else {
		process_idattr(obuf, cmd, tag);
	    }
#endif				/* ID_EXT */
	    obuf->bp.init_flag = 1;
	    clear_ignore_p_flag(cmd, obuf);
	    if (cmd == HTML_TABLE)
		goto table_start;
	    else
		continue;
	}

	if (obuf->flag & (RB_DEL | RB_S))
	    continue;
	while (*str) {
	    mode = get_mctype(str);
	    delta = get_mclen(mode);
	    if (obuf->flag & (RB_SPECIAL & ~RB_NOBR)) {
		char ch = *str;
		if (!(obuf->flag & RB_PLAIN) && (*str == '&')) {
		    char *p = str;
		    int ech = getescapechar(&p);
		    if (ech == '\n' || ech == '\r') {
			ch = '\n';
			str = p - 1;
		    }
		    else if (ech == '\t') {
			ch = '\t';
			str = p - 1;
		    }
		}
		if (ch != '\n')
		    obuf->flag &= ~RB_IGNORE_P;
		if (ch == '\n') {
		    str++;
		    if (obuf->flag & RB_IGNORE_P) {
			obuf->flag &= ~RB_IGNORE_P;
			continue;
		    }
		    if (obuf->flag & RB_PRE_INT)
			PUSH(' ');
		    else
			flushline(h_env, obuf, h_env->envs[h_env->envc].indent,
				  1, h_env->limit);
		}
		else if (ch == '\t') {
		    do {
			PUSH(' ');
		    } while ((h_env->envs[h_env->envc].indent + obuf->pos)
			     % Tabstop != 0);
		    str++;
		}
		else if (obuf->flag & RB_PLAIN) {
		    char *p = html_quote_char(*str);
		    if (p) {
			push_charp(obuf, 1, p, PC_ASCII);
			str++;
		    }
		    else {
			proc_mchar(obuf, 1, delta, &str, mode);
		    }
		}
		else {
		    if (*str == '&')
			proc_escape(obuf, &str);
		    else
			proc_mchar(obuf, 1, delta, &str, mode);
		}
		if (obuf->flag & (RB_SPECIAL & ~RB_PRE_INT))
		    continue;
	    }
	    else {
		if (!IS_SPACE(*str))
		    obuf->flag &= ~RB_IGNORE_P;
		if ((mode == PC_ASCII || mode == PC_CTRL) && IS_SPACE(*str)) {
		    if (obuf->prevchar != ' ') {
			PUSH(' ');
		    }
		    str++;
		}
		else {
#ifdef JP_CHARSET
		    if (mode == PC_KANJI &&
			obuf->pos > h_env->envs[h_env->envc].indent &&
			Strlastchar(obuf->line) == ' ') {
			while (obuf->line->length >= 2 &&
			       !strncmp(obuf->line->ptr + obuf->line->length -
					2, "  ", 2)
			       && obuf->pos >= h_env->envs[h_env->envc].indent) {
			    Strshrink(obuf->line, 1);
			    obuf->pos--;
			}
			if (obuf->line->length >= 3 &&
			    obuf->prev_ctype == PC_KANJI &&
			    Strlastchar(obuf->line) == ' ' &&
			    obuf->pos >= h_env->envs[h_env->envc].indent) {
			    Strshrink(obuf->line, 1);
			    obuf->pos--;
			}
		    }
#endif				/* JP_CHARSET */
		    if (*str == '&')
			proc_escape(obuf, &str);
		    else
			proc_mchar(obuf, obuf->flag & RB_SPECIAL, delta, &str,
				   mode);
		}
	    }
	    if (need_flushline(h_env, obuf, mode)) {
		char *bp = obuf->line->ptr + obuf->bp.len;
		char *tp = bp - obuf->bp.tlen;
		int i = 0;

		if (tp > obuf->line->ptr && tp[-1] == ' ')
		    i = 1;

		indent = h_env->envs[h_env->envc].indent;
		if (obuf->bp.pos - i > indent) {
		    Str line;
		    append_tags(obuf);
		    line = Strnew_charp(bp);
		    Strshrink(obuf->line, obuf->line->length - obuf->bp.len);
#ifdef FORMAT_NICE
		    if (obuf->pos - i > h_env->limit)
			obuf->flag |= RB_FILL;
#endif				/* FORMAT_NICE */
		    back_to_breakpoint(obuf);
		    flushline(h_env, obuf, indent, 0, h_env->limit);
#ifdef FORMAT_NICE
		    obuf->flag &= ~RB_FILL;
#endif				/* FORMAT_NICE */
		    HTMLlineproc1(line->ptr, h_env);
		}
	    }
	}
    }
    if (!(obuf->flag & (RB_SPECIAL | RB_INTXTA | RB_INSELECT))) {
	char *tp;
	int i = 0;

	if (obuf->bp.pos == obuf->pos) {
	    tp = &obuf->line->ptr[obuf->bp.len - obuf->bp.tlen];
	}
	else {
	    tp = &obuf->line->ptr[obuf->line->length];
	}

	if (tp > obuf->line->ptr && tp[-1] == ' ')
	    i = 1;
	indent = h_env->envs[h_env->envc].indent;
	if (obuf->pos - i > h_env->limit) {
#ifdef FORMAT_NICE
	    obuf->flag |= RB_FILL;
#endif				/* FORMAT_NICE */
	    flushline(h_env, obuf, indent, 0, h_env->limit);
#ifdef FORMAT_NICE
	    obuf->flag &= ~RB_FILL;
#endif				/* FORMAT_NICE */
	}
    }
}

extern char *NullLine;
extern Lineprop NullProp[];

static void
addnewline(Buffer *buf, char *line, Lineprop *prop,
#ifdef USE_ANSI_COLOR
	   Linecolor *color,
#endif
	   int pos, int nlines)
{
    Line *l;
    l = New(Line);
    l->next = NULL;
    if (pos > 0) {
	l->lineBuf = allocStr(line, pos);
	l->propBuf = NewAtom_N(Lineprop, pos);
	bcopy((void *)prop, (void *)l->propBuf, pos * sizeof(Lineprop));
    }
    else {
	l->lineBuf = NullLine;
	l->propBuf = NullProp;
    }
#ifdef USE_ANSI_COLOR
    if (pos > 0 && color) {
	l->colorBuf = NewAtom_N(Linecolor, pos);
	bcopy((void *)color, (void *)l->colorBuf, pos * sizeof(Linecolor));
    }
    else {
	l->colorBuf = NULL;
    }
#endif
    l->len = pos;
    l->width = -1;
    l->prev = buf->currentLine;
    if (buf->currentLine) {
	l->next = buf->currentLine->next;
	buf->currentLine->next = l;
    }
    else
	l->next = NULL;
    if (buf->lastLine == NULL || buf->lastLine == buf->currentLine)
	buf->lastLine = l;
    buf->currentLine = l;
    if (buf->firstLine == NULL)
	buf->firstLine = l;
    l->linenumber = ++buf->allLine;
    if (nlines < 0) {
	/*     l->real_linenumber = l->linenumber;     */
	l->real_linenumber = 0;
    }
    else {
	l->real_linenumber = nlines;
    }
    l = NULL;
}

/* 
 * loadHTMLBuffer: read file and make new buffer
 */
Buffer *
loadHTMLBuffer(URLFile *f, Buffer *newBuf)
{
    FILE *src = NULL;
    Str tmp;

    if (newBuf == NULL)
	newBuf = newBuffer(INIT_BUFFER_WIDTH);
    if (newBuf->sourcefile == NULL &&
	(f->scheme != SCM_LOCAL || newBuf->mailcap)) {
	tmp = tmpfname(TMPF_SRC, ".html");
	pushText(fileToDelete, tmp->ptr);
	src = fopen(tmp->ptr, "w");
	if (src)
	    newBuf->sourcefile = tmp->ptr;
    }

    loadHTMLstream(f, newBuf, src, newBuf->bufferprop & BP_FRAME);

    newBuf->topLine = newBuf->firstLine;
    newBuf->lastLine = newBuf->currentLine;
    newBuf->currentLine = newBuf->firstLine;
    if (n_textarea)
	formResetBuffer(newBuf, newBuf->formitem);
    if (src)
	fclose(src);

    return newBuf;
}

static char *_size_unit[] = { "b", "kb", "Mb", "Gb", "Tb",
    "Pb", "Eb", "Zb", "Bb", "Yb", NULL
};

char *
convert_size(clen_t size, int usefloat)
{
    float csize;
    int sizepos = 0;
    char **sizes = _size_unit;

    csize = (float)size;
    while (csize >= 999.495 && sizes[sizepos + 1]) {
	csize = csize / 1024.0;
	sizepos++;
    }
    return Sprintf(usefloat ? "%.3g%s" : "%.0f%s",
		   floor(csize * 100.0 + 0.5) / 100.0, sizes[sizepos])->ptr;
}

char *
convert_size2(clen_t size1, clen_t size2, int usefloat)
{
    char **sizes = _size_unit;
    float csize, factor = 1;
    int sizepos = 0;

    csize = (float)((size1 > size2) ? size1 : size2);
    while (csize / factor >= 999.495 && sizes[sizepos + 1]) {
	factor *= 1024.0;
	sizepos++;
    }
    return Sprintf(usefloat ? "%.3g/%.3g%s" : "%.0f/%.0f%s",
		   floor(size1 / factor * 100.0 + 0.5) / 100.0,
		   floor(size2 / factor * 100.0 + 0.5) / 100.0,
		   sizes[sizepos])->ptr;
}

void
showProgress(clen_t * linelen, clen_t * trbyte)
{
    int i, j, rate, duration, eta, pos;
    static time_t last_time, start_time;
    time_t cur_time;
    Str messages;
    char *fmtrbyte, *fmrate;

    if (!fmInitialized)
	return;

    if (current_content_length > 0) {
	double ratio;
	cur_time = time(0);
	if (cur_time == last_time)
	    return;
	last_time = cur_time;
	if (*trbyte == 0) {
	    move(LASTLINE, 0);
	    clrtoeolx();
	    start_time = cur_time;
	}
	*trbyte += *linelen;
	*linelen = 0;
	move(LASTLINE, 0);
	ratio = 100.0 * (*trbyte) / current_content_length;
	fmtrbyte = convert_size2(*trbyte, current_content_length, 1);
	duration = cur_time - start_time;
	if (duration) {
	    rate = *trbyte / duration;
	    fmrate = convert_size(rate, 1);
	    eta = rate ? (current_content_length - *trbyte) / rate : -1;
	    messages = Sprintf("%11s %3.0f%% "
			       "%7s/s "
			       "eta %02d:%02d:%02d     ",
			       fmtrbyte, ratio,
			       fmrate,
			       eta / (60 * 60), (eta / 60) % 60, eta % 60);
	}
	else {
	    messages = Sprintf("%11s %3.0f%%                          ",
			       fmtrbyte, ratio);
	}
	addstr(messages->ptr);
	pos = 42;
	i = pos + (COLS - pos - 1) * (*trbyte) / current_content_length;
	move(LASTLINE, pos);
#if 0				/* def KANJI_SYMBOLS */
	for (j = pos; j <= i; j += 2)
	    addstr("��");
#else				/* not 0 */
	standout();
	addch(' ');
	for (j = pos + 1; j <= i; j++)
	    addch('|');
	standend();
#endif				/* not 0 */
	/* no_clrtoeol(); */
	refresh();
    }
    else if (*linelen > 1000) {
	cur_time = time(0);
	if (cur_time == last_time)
	    return;
	last_time = cur_time;
	if (*trbyte == 0) {
	    move(LASTLINE, 0);
	    clrtoeolx();
	    start_time = cur_time;
	}
	*trbyte += *linelen;
	*linelen = 0;
	move(LASTLINE, 0);
	fmtrbyte = convert_size(*trbyte, 1);
	duration = cur_time - start_time;
	if (duration) {
	    fmrate = convert_size(*trbyte / duration, 1);
	    messages = Sprintf("%7s loaded %7s/s", fmtrbyte, fmrate);
	}
	else {
	    messages = Sprintf("%7s loaded", fmtrbyte);
	}
	message(messages->ptr, 0, 0);
	refresh();
    }
}

void
init_henv(struct html_feed_environ *h_env, struct readbuffer *obuf,
	  struct environment *envs, int nenv, TextLineList *buf,
	  int limit, int indent)
{
    envs[0].indent = indent;

    obuf->line = Strnew();
    obuf->cprop = 0;
    obuf->pos = 0;
    obuf->prevchar = ' ';
    obuf->flag = RB_IGNORE_P;
    obuf->flag_sp = 0;
    obuf->status = R_ST_NORMAL;
    obuf->table_level = -1;
    obuf->nobr_level = 0;
    bzero((void *)&obuf->anchor, sizeof(obuf->anchor));
    obuf->img_alt = 0;
    obuf->in_bold = 0;
    obuf->in_under = 0;
    obuf->prev_ctype = PC_ASCII;
    obuf->tag_sp = 0;
    obuf->fontstat_sp = 0;
    obuf->top_margin = 0;
    obuf->bottom_margin = 0;
    obuf->bp.init_flag = 1;
    set_breakpoint(obuf, 0);

    h_env->buf = buf;
    h_env->f = NULL;
    h_env->obuf = obuf;
    h_env->tagbuf = Strnew();
    h_env->limit = limit;
    h_env->maxlimit = 0;
    h_env->envs = envs;
    h_env->nenv = nenv;
    h_env->envc = 0;
    h_env->envc_real = 0;
    h_env->title = NULL;
    h_env->blank_lines = 0;
}

void
completeHTMLstream(struct html_feed_environ *h_env, struct readbuffer *obuf)
{
    close_anchor(h_env, obuf);
    if (obuf->img_alt) {
	push_tag(obuf, "</img_alt>", HTML_N_IMG_ALT);
	obuf->img_alt = NULL;
    }
    if (obuf->in_bold) {
	push_tag(obuf, "</b>", HTML_N_B);
	obuf->in_bold = 0;
    }
    if (obuf->in_under) {
	push_tag(obuf, "</u>", HTML_N_U);
	obuf->in_under = 0;
    }
    if (obuf->flag & RB_INTXTA)
	HTMLlineproc1("</textarea>", h_env);
    /* for unbalanced select tag */
    if (obuf->flag & RB_INSELECT)
	HTMLlineproc1("</select>", h_env);
    if (obuf->flag & RB_TITLE)
	HTMLlineproc1("</title>", h_env);

    /* for unbalanced table tag */
    while (obuf->table_level >= 0) {
	table_mode[obuf->table_level].pre_mode
	    &= ~(TBLM_SCRIPT | TBLM_STYLE | TBLM_PLAIN);
	HTMLlineproc1("</table>", h_env);
    }
}

static void
print_internal_information(struct html_feed_environ *henv)
{
    int i;
    Str s;
    TextLineList *tl = newTextLineList();

    s = Strnew_charp("<internal>");
    pushTextLine(tl, newTextLine(s, 0));
    if (henv->title) {
	s = Strnew_m_charp("<title_alt title=\"",
			   html_quote(henv->title), "\">", NULL);
	pushTextLine(tl, newTextLine(s, 0));
    }
#if 0
    if (form_max >= 0) {
	FormList *fp;
	for (i = 0; i <= form_max; i++) {
	    fp = forms[i];
	    s = Sprintf("<form_int fid=\"%d\" action=\"%s\" method=\"%s\"",
			i, html_quote(fp->action->ptr),
			(fp->method == FORM_METHOD_POST) ? "post"
			: ((fp->method ==
			    FORM_METHOD_INTERNAL) ? "internal" : "get"));
	    if (fp->target)
		Strcat(s, Sprintf(" target=\"%s\"", html_quote(fp->target)));
	    if (fp->enctype == FORM_ENCTYPE_MULTIPART)
		Strcat_charp(s, " enctype=\"multipart/form-data\"");
#ifdef JP_CHARSET
	    if (fp->charset)
		Strcat(s, Sprintf(" accept-charset=\"%s\"",
				  code_to_str(fp->charset)));
#endif
	    Strcat_charp(s, ">");
	    pushTextLine(tl, newTextLine(s, 0));
	}
    }
#endif
#ifdef MENU_SELECT
    if (n_select > 0) {
	FormSelectOptionItem *ip;
	for (i = 0; i < n_select; i++) {
	    s = Sprintf("<select_int selectnumber=%d>", i);
	    pushTextLine(tl, newTextLine(s, 0));
	    for (ip = select_option[i].first; ip; ip = ip->next) {
		s = Sprintf("<option_int value=\"%s\" label=\"%s\"%s>",
			    html_quote(ip->value ? ip->value->ptr :
				       ip->label->ptr),
			    html_quote(ip->label->ptr),
			    ip->checked ? " selected" : "");
		pushTextLine(tl, newTextLine(s, 0));
	    }
	    s = Strnew_charp("</select_int>");
	    pushTextLine(tl, newTextLine(s, 0));
	}
    }
#endif				/* MENU_SELECT */
    if (n_textarea > 0) {
	for (i = 0; i < n_textarea; i++) {
	    s = Sprintf("<textarea_int textareanumber=%d>", i);
	    pushTextLine(tl, newTextLine(s, 0));
	    s = Strnew_charp(html_quote(textarea_str[i]->ptr));
	    Strcat_charp(s, "</textarea_int>");
	    pushTextLine(tl, newTextLine(s, 0));
	}
    }
    s = Strnew_charp("</internal>");
    pushTextLine(tl, newTextLine(s, 0));

    if (henv->buf)
	appendTextLineList(henv->buf, tl);
    else if (henv->f) {
	TextLineListItem *p;
	for (p = tl->first; p; p = p->next)
	    fprintf(henv->f, "%s\n", p->ptr->line->ptr);
    }
}

void
loadHTMLstream(URLFile *f, Buffer *newBuf, FILE * src, int internal)
{
    struct environment envs[MAX_ENV_LEVEL];
    clen_t linelen = 0;
    clen_t trbyte = 0;
    Str lineBuf2 = Strnew();
    char code;
    struct html_feed_environ htmlenv1;
    struct readbuffer obuf;
#ifdef USE_IMAGE
    int volatile image_flag;
#endif
    MySignalHandler(*volatile prevtrap) (SIGNAL_ARG) = NULL;

    cur_title = NULL;
    n_textarea = 0;
    cur_textarea = NULL;
    max_textarea = MAX_TEXTAREA;
    textarea_str = New_N(Str, max_textarea);
#ifdef MENU_SELECT
    n_select = 0;
    max_select = MAX_SELECT;
    select_option = New_N(FormSelectOption, max_select);
#endif				/* MENU_SELECT */
    cur_select = NULL;
    form_sp = -1;
    form_max = -1;
    forms_size = 0;
    forms = NULL;
    cur_hseq = 1;
#ifdef USE_IMAGE
    cur_iseq = 1;
    if (newBuf->image_flag)
	image_flag = newBuf->image_flag;
    else if (activeImage && displayImage && autoImage)
	image_flag = IMG_FLAG_AUTO;
    else
	image_flag = IMG_FLAG_SKIP;
    if (newBuf->currentURL.file)
	cur_baseURL = baseURL(newBuf);
#endif

    if (w3m_halfload) {
	newBuf->buffername = "---";
#ifdef JP_CHARSET
	newBuf->document_code = InnerCode;
#endif				/* JP_CHARSET */
	max_textarea = 0;
#ifdef MENU_SELECT
	max_select = 0;
#endif
	HTMLlineproc3(newBuf, f->stream);
	w3m_halfload = FALSE;
	return;
    }

    init_henv(&htmlenv1, &obuf, envs, MAX_ENV_LEVEL, NULL, newBuf->width, 0);

    if (w3m_halfdump)
	htmlenv1.f = stdout;
    else
	htmlenv1.buf = newTextLineList();

    if (SETJMP(AbortLoading) != 0) {
	HTMLlineproc1("<br>Transfer Interrupted!<br>", &htmlenv1);
	goto phase2;
    }
    prevtrap = signal(SIGINT, KeyAbort);
    if (fmInitialized)
	term_cbreak();

#ifdef JP_CHARSET
    if (newBuf != NULL && newBuf->document_code != '\0')
	code = newBuf->document_code;
    else if (content_charset != '\0' && UseContentCharset)
	code = content_charset;
    else
	code = DocumentCode;
    meta_charset = '\0';
#endif
#if	0
    do_blankline(&htmlenv1, &obuf, 0, 0, htmlenv1.limit);
    obuf.flag = RB_IGNORE_P;
#endif
    if (IStype(f->stream) != IST_ENCODED)
	f->stream = newEncodedStream(f->stream, f->encoding);
    while ((lineBuf2 = StrmyUFgets(f))->length) {
#ifdef USE_NNTP
	if (f->scheme == SCM_NEWS && lineBuf2->ptr[0] == '.') {
	    Strshrinkfirst(lineBuf2, 1);
	    if (lineBuf2->ptr[0] == '\n' || lineBuf2->ptr[0] == '\r' ||
		lineBuf2->ptr[0] == '\0') {
		/*
		 * iseos(f->stream) = TRUE;
		 */
		break;
	    }
	}
#endif				/* USE_NNTP */
	if (src)
	    Strfputs(lineBuf2, src);
	linelen += lineBuf2->length;
	if (w3m_dump & DUMP_SOURCE)
	    continue;
	showProgress(&linelen, &trbyte);
	/*
	 * if (frame_source)
	 * continue;
	 */
#ifdef JP_CHARSET
	if (meta_charset != '\0') {	/* <META> */
	    if (content_charset == '\0' && UseContentCharset) {
		code = meta_charset;
#ifdef USE_IMAGE
		cur_document_code = code;
#endif
	    }
	    meta_charset = '\0';
	}
#endif
	if (!internal) {
	    lineBuf2 = convertLine(f, lineBuf2, &code, HTML_MODE);
#ifdef JP_CHARSET
#ifdef USE_IMAGE
	    cur_document_code = code;
#endif
#endif
	}
	HTMLlineproc0(lineBuf2->ptr, &htmlenv1, internal);
    }
    if (obuf.status != R_ST_NORMAL) {
	obuf.status = R_ST_EOL;
	HTMLlineproc0("\n", &htmlenv1, internal);
    }
    obuf.status = R_ST_NORMAL;
    completeHTMLstream(&htmlenv1, &obuf);
    flushline(&htmlenv1, &obuf, 0, 2, htmlenv1.limit);
    if (htmlenv1.title)
	newBuf->buffername = htmlenv1.title;
    if (w3m_halfdump) {
	if (fmInitialized)
	    term_raw();
	signal(SIGINT, prevtrap);
	print_internal_information(&htmlenv1);
	return;
    }
    if (w3m_backend) {
	if (fmInitialized)
	    term_raw();
	signal(SIGINT, prevtrap);
	print_internal_information(&htmlenv1);
	backend_halfdump_buf = htmlenv1.buf;
	return;
    }
  phase2:
    newBuf->trbyte = trbyte + linelen;
    if (fmInitialized)
	term_raw();
    signal(SIGINT, prevtrap);
#ifdef JP_CHARSET
    newBuf->document_code = code;
    content_charset = '\0';
#endif				/* JP_CHARSET */
#ifdef USE_IMAGE
    newBuf->image_flag = image_flag;
#endif
    HTMLlineproc2(newBuf, htmlenv1.buf);
}

/* 
 * loadHTMLString: read string and make new buffer
 */
Buffer *
loadHTMLString(Str page)
{
    URLFile f;
    MySignalHandler(*volatile prevtrap) (SIGNAL_ARG) = NULL;
    Buffer *newBuf;
    Str tmp;
    FILE *volatile src = NULL;

    newBuf = newBuffer(INIT_BUFFER_WIDTH);
    if (SETJMP(AbortLoading) != 0) {
	if (fmInitialized)
	    term_raw();
	signal(SIGINT, prevtrap);
	discardBuffer(newBuf);
	return NULL;
    }
    prevtrap = signal(SIGINT, KeyAbort);
    if (fmInitialized)
	term_cbreak();

    init_stream(&f, SCM_LOCAL, newStrStream(page));

    if (w3m_dump & DUMP_FRAME) {
	tmp = tmpfname(TMPF_SRC, ".html");
	pushText(fileToDelete, tmp->ptr);
	src = fopen(tmp->ptr, "w");
	if (src)
	    newBuf->sourcefile = tmp->ptr;
    }

    loadHTMLstream(&f, newBuf, src, TRUE);

    if (fmInitialized)
	term_raw();
    signal(SIGINT, prevtrap);
    newBuf->topLine = newBuf->firstLine;
    newBuf->lastLine = newBuf->currentLine;
    newBuf->currentLine = newBuf->firstLine;
#ifdef JP_CHARSET
    newBuf->document_code = InnerCode;
#endif				/* JP_CHARSET */
    newBuf->type = "text/html";
    newBuf->real_type = newBuf->type;
    if (n_textarea)
	formResetBuffer(newBuf, newBuf->formitem);
    if (src)
	fclose(src);

    return newBuf;
}

#ifdef USE_GOPHER

/* 
 * loadGopherDir: get gopher directory
 */
Buffer *
loadGopherDir(URLFile *uf, Buffer *volatile newBuf)
{
    Str tmp, lbuf, name, file, host, port;
    char code = CODE_ASCII;
    char *p, *q;
    FILE *src;
    URLFile f;
    MySignalHandler(*volatile prevtrap) (SIGNAL_ARG) = NULL;

    if (newBuf == NULL)
	newBuf = newBuffer(INIT_BUFFER_WIDTH);
    tmp = Strnew_charp("<pre>\n");

    if (SETJMP(AbortLoading) != 0)
	goto gopher_end;
    prevtrap = signal(SIGINT, KeyAbort);
    if (fmInitialized)
	term_cbreak();

#ifdef JP_CHARSET
    if (newBuf->document_code != '\0')
	code = newBuf->document_code;
    else if (content_charset != '\0' && UseContentCharset)
	code = content_charset;
    else
	code = DocumentCode;
    content_charset = '\0';
#endif
    while (1) {
	if (lbuf = StrUFgets(uf), lbuf->length == 0)
	    break;
	if (lbuf->ptr[0] == '.' &&
	    (lbuf->ptr[1] == '\n' || lbuf->ptr[1] == '\r'))
	    break;
	lbuf = convertLine(uf, lbuf, &code, HTML_MODE);
	p = lbuf->ptr;
	for (q = p; *q && *q != '\t'; q++) ;
	name = Strnew_charp_n(p, q - p);
	if (!*q)
	    continue;
	p = q + 1;
	for (q = p; *q && *q != '\t'; q++) ;
	file = Strnew_charp_n(p, q - p);
	if (!*q)
	    continue;
	p = q + 1;
	for (q = p; *q && *q != '\t'; q++) ;
	host = Strnew_charp_n(p, q - p);
	if (!*q)
	    continue;
	p = q + 1;
	for (q = p; *q && *q != '\t' && *q != '\r' && *q != '\n'; q++) ;
	port = Strnew_charp_n(p, q - p);

	switch (name->ptr[0]) {
	case '0':
	    p = "[text file]  ";
	    break;
	case '1':
	    p = "[directory]  ";
	    break;
	case 'm':
	    p = "[message]    ";
	    break;
	case 's':
	    p = "[sound]      ";
	    break;
	case 'g':
	    p = "[gif]        ";
	    break;
	case 'h':
	    p = "[HTML]       ";
	    break;
	default:
	    p = "[unsupported]";
	    break;
	}
	q = Strnew_m_charp("gopher://", host->ptr, ":", port->ptr,
			   "/", file->ptr, NULL)->ptr;
	Strcat_m_charp(tmp, "<a href=\"",
		       html_quote(url_quote_conv(q, code)),
		       "\">", p, html_quote(name->ptr + 1), "</a>\n", NULL);
    }

  gopher_end:
    if (fmInitialized)
	term_raw();
    signal(SIGINT, prevtrap);

    Strcat_charp(tmp, "</pre>\n");

    file = tmpfname(TMPF_SRC, ".html");
    src = fopen(file->ptr, "w");
    newBuf->sourcefile = file->ptr;
    pushText(fileToDelete, file->ptr);

    init_stream(&f, SCM_LOCAL, newStrStream(tmp));
    loadHTMLstream(&f, newBuf, src, TRUE);
    if (src)
	fclose(src);

#ifdef JP_CHARSET
    newBuf->document_code = code;
#endif				/* JP_CHARSET */
    newBuf->topLine = newBuf->firstLine;
    newBuf->lastLine = newBuf->currentLine;
    newBuf->currentLine = newBuf->firstLine;

    return newBuf;
}
#endif				/* USE_GOPHER */

/* 
 * loadBuffer: read file and make new buffer
 */
Buffer *
loadBuffer(URLFile *uf, Buffer *volatile newBuf)
{
    FILE *volatile src = NULL;
    char code;
    Str lineBuf2;
    volatile char pre_lbuf = '\0';
    int nlines;
    Str tmpf;
    clen_t linelen = 0, trbyte = 0;
#ifdef USE_ANSI_COLOR
    int check_color;
#endif
    MySignalHandler(*volatile prevtrap) (SIGNAL_ARG) = NULL;

    if (newBuf == NULL)
	newBuf = newBuffer(INIT_BUFFER_WIDTH);
    lineBuf2 = Strnew();

    if (SETJMP(AbortLoading) != 0) {
	goto _end;
    }
    prevtrap = signal(SIGINT, KeyAbort);
    if (fmInitialized)
	term_cbreak();

    if (newBuf->sourcefile == NULL &&
	(uf->scheme != SCM_LOCAL || newBuf->mailcap)) {
	tmpf = tmpfname(TMPF_SRC, NULL);
	src = fopen(tmpf->ptr, "w");
	if (src)
	    newBuf->sourcefile = tmpf->ptr;
    }
#ifdef JP_CHARSET
    if (newBuf->document_code != '\0')
	code = newBuf->document_code;
    else if (content_charset != '\0' && UseContentCharset)
	code = content_charset;
    else
	code = DocumentCode;
    content_charset = '\0';
#endif

    nlines = 0;
    if (IStype(uf->stream) != IST_ENCODED)
	uf->stream = newEncodedStream(uf->stream, uf->encoding);
    while ((lineBuf2 = StrmyISgets(uf->stream))->length) {
#ifdef USE_NNTP
	if (uf->scheme == SCM_NEWS && lineBuf2->ptr[0] == '.') {
	    Strshrinkfirst(lineBuf2, 1);
	    if (lineBuf2->ptr[0] == '\n' || lineBuf2->ptr[0] == '\r' ||
		lineBuf2->ptr[0] == '\0') {
		/*
		 * iseos(uf->stream) = TRUE;
		 */
		break;
	    }
	}
#endif				/* USE_NNTP */
	if (src)
	    Strfputs(lineBuf2, src);
	linelen += lineBuf2->length;
	if (w3m_dump & DUMP_SOURCE)
	    continue;
	showProgress(&linelen, &trbyte);
	if (frame_source)
	    continue;
	lineBuf2 = convertLine(uf, lineBuf2, &code, PAGER_MODE);
	if (squeezeBlankLine) {
	    if (lineBuf2->ptr[0] == '\n' && pre_lbuf == '\n') {
		++nlines;
		continue;
	    }
	    pre_lbuf = lineBuf2->ptr[0];
	}
	++nlines;
	Strchop(lineBuf2);
	lineBuf2 = checkType(lineBuf2, propBuffer,
#ifdef USE_ANSI_COLOR
			     colorBuffer, &check_color,
#endif
			     LINELEN);
	addnewline(newBuf, lineBuf2->ptr, propBuffer,
#ifdef USE_ANSI_COLOR
		   check_color ? colorBuffer : NULL,
#endif
		   lineBuf2->length, nlines);
    }
  _end:
    if (fmInitialized)
	term_raw();
    signal(SIGINT, prevtrap);
    newBuf->topLine = newBuf->firstLine;
    newBuf->lastLine = newBuf->currentLine;
    newBuf->currentLine = newBuf->firstLine;
    newBuf->trbyte = trbyte + linelen;
#ifdef JP_CHARSET
    newBuf->document_code = code;
#endif				/* JP_CHARSET */
    if (src)
	fclose(src);

    return newBuf;
}

#ifdef USE_IMAGE
Buffer *
loadImageBuffer(URLFile *uf, Buffer *newBuf)
{
    Image *image;
    ImageCache *cache;
    Str tmp, tmpf;
    FILE *src = NULL;
    URLFile f;
    MySignalHandler(*prevtrap) ();
    struct stat st;

    loadImage(IMG_FLAG_STOP);
    image = New(Image);
    image->url = uf->url;
    image->ext = uf->ext;
    image->width = -1;
    image->height = -1;
    cache = getImage(image, cur_baseURL, IMG_FLAG_AUTO);
    if (!cur_baseURL->is_nocache && cache->loaded & IMG_FLAG_LOADED &&
	!stat(cache->file, &st))
	goto image_buffer;

    prevtrap = signal(SIGINT, KeyAbort);
    if (fmInitialized)
	term_cbreak();
    if (IStype(uf->stream) != IST_ENCODED)
	uf->stream = newEncodedStream(uf->stream, uf->encoding);
    if (save2tmp(*uf, cache->file) < 0) {
	UFclose(uf);
	if (fmInitialized)
	    term_raw();
	signal(SIGINT, prevtrap);
	return NULL;
    }
    UFclose(uf);
    if (fmInitialized)
	term_raw();
    signal(SIGINT, prevtrap);

    cache->loaded = IMG_FLAG_LOADED;
    cache->index = 0;

  image_buffer:
    if (newBuf == NULL)
	newBuf = newBuffer(INIT_BUFFER_WIDTH);
    cache->loaded |= IMG_FLAG_DONT_REMOVE;
    if (newBuf->sourcefile == NULL && uf->scheme != SCM_LOCAL)
	newBuf->sourcefile = cache->file;

    tmp = Sprintf("<img src=\"%s\"><br><br>", html_quote(image->url));
    tmpf = tmpfname(TMPF_SRC, ".html");
    src = fopen(tmpf->ptr, "w");
    newBuf->mailcap_source = tmpf->ptr;
    pushText(fileToDelete, tmpf->ptr);

    init_stream(&f, SCM_LOCAL, newStrStream(tmp));
    loadHTMLstream(&f, newBuf, src, TRUE);
    if (src)
	fclose(src);

    newBuf->topLine = newBuf->firstLine;
    newBuf->lastLine = newBuf->currentLine;
    newBuf->currentLine = newBuf->firstLine;
    newBuf->image_flag = IMG_FLAG_AUTO;
    return newBuf;
}
#endif

#ifndef KANJI_SYMBOLS
static Str
conv_rule(Line *l)
{
    Str tmp = NULL;
    char *p = l->lineBuf, *ep = p + l->len;
    Lineprop *pr = l->propBuf;

    for (; p < ep; p++, pr++) {
	if (*pr & PC_RULE) {
	    if (tmp == NULL) {
		tmp = Strnew_size(l->len);
		Strcopy_charp_n(tmp, l->lineBuf, p - l->lineBuf);
	    }
	    Strcat_char(tmp, alt_rule[*p & 0xF]);
	}
	else if (tmp != NULL)
	    Strcat_char(tmp, *p);
    }
    if (tmp)
	return tmp;
    else
	return Strnew_charp_n(l->lineBuf, l->len);
}
#endif

/* 
 * saveBuffer: write buffer to file
 */
void
saveBuffer(Buffer *buf, FILE * f)
{
    Line *l = buf->firstLine;
    Str tmp;

#ifndef KANJI_SYMBOLS
    int is_html = FALSE;

    if (buf->type && !strcasecmp(buf->type, "text/html"))
	is_html = TRUE;
#endif

  pager_next:
    for (; l != NULL; l = l->next) {
#ifndef KANJI_SYMBOLS
	if (is_html)
	    tmp = conv_rule(l);
	else
#endif
	    tmp = Strnew_charp_n(l->lineBuf, l->len);
#ifdef JP_CHARSET
	tmp = conv_str(tmp, InnerCode, DisplayCode);
#endif
	Strfputs(tmp, f);
	if (Strlastchar(tmp) != '\n')
	    putc('\n', f);
    }
    if (buf->pagerSource && !(buf->bufferprop & BP_CLOSE)) {
	l = getNextPage(buf, PagerMax);
	goto pager_next;
    }
}

static Buffer *
loadcmdout(char *cmd,
	   Buffer *(*loadproc) (URLFile *, Buffer *), Buffer *defaultbuf)
{
    FILE *f, *popen(const char *, const char *);
    Buffer *buf;
    URLFile uf;

    if (cmd == NULL || *cmd == '\0')
	return NULL;
    f = popen(cmd, "r");
    if (f == NULL)
	return NULL;
    init_stream(&uf, SCM_UNKNOWN, newFileStream(f, (void (*)())pclose));
    buf = loadproc(&uf, defaultbuf);
    UFclose(&uf);
    return buf;
}

/* 
 * getshell: execute shell command and get the result into a buffer
 */
Buffer *
getshell(char *cmd)
{
    Buffer *buf;

    buf = loadcmdout(cmd, loadBuffer, NULL);
    if (buf == NULL)
	return NULL;
    buf->filename = cmd;
    buf->buffername = Sprintf("%s %s", SHELLBUFFERNAME,
			      conv_from_system(cmd))->ptr;
    return buf;
}

/* 
 * getpipe: execute shell command and connect pipe to the buffer
 */
Buffer *
getpipe(char *cmd)
{
    FILE *f, *popen(const char *, const char *);
    Buffer *buf;

    if (cmd == NULL || *cmd == '\0')
	return NULL;
    f = popen(cmd, "r");
    if (f == NULL)
	return NULL;
    buf = newBuffer(INIT_BUFFER_WIDTH);
    buf->pagerSource = newFileStream(f, (void (*)())pclose);
    buf->filename = cmd;
    buf->buffername = Sprintf("%s %s", PIPEBUFFERNAME,
			      conv_from_system(cmd))->ptr;
    buf->bufferprop |= BP_PIPE;
    return buf;
}

/* 
 * Open pager buffer
 */
Buffer *
openPagerBuffer(InputStream stream, Buffer *buf)
{

    if (buf == NULL)
	buf = newBuffer(INIT_BUFFER_WIDTH);
    buf->pagerSource = stream;
    buf->buffername = getenv("MAN_PN");
    if (buf->buffername == NULL)
	buf->buffername = PIPEBUFFERNAME;
    else
	buf->buffername = conv_from_system(buf->buffername);
    buf->bufferprop |= BP_PIPE;
#ifdef JP_CHARSET
    if (content_charset != '\0' && UseContentCharset)
	buf->document_code = content_charset;
#endif
    buf->currentLine = buf->firstLine;

    return buf;
}

Buffer *
openGeneralPagerBuffer(InputStream stream)
{
    Buffer *buf;
    char *t = "text/plain";
    Buffer *t_buf = NULL;
    URLFile uf;

    init_stream(&uf, SCM_UNKNOWN, stream);

#ifdef JP_CHARSET
    content_charset = '\0';
#endif
    if (SearchHeader) {
	t_buf = newBuffer(INIT_BUFFER_WIDTH);
	readHeader(&uf, t_buf, TRUE, NULL);
	t = checkContentType(t_buf);
	if (t == NULL)
	    t = "text/plain";
	if (t_buf) {
	    t_buf->topLine = t_buf->firstLine;
	    t_buf->currentLine = t_buf->lastLine;
	}
	SearchHeader = FALSE;
    }
    else if (DefaultType) {
	t = DefaultType;
	DefaultType = NULL;
    }
    if (!strcasecmp(t, "text/html")) {
	buf = loadHTMLBuffer(&uf, t_buf);
	buf->type = "text/html";
    }
    else if (is_plain_text_type(t)) {
	if (IStype(stream) != IST_ENCODED)
	    stream = newEncodedStream(stream, uf.encoding);
	buf = openPagerBuffer(stream, t_buf);
	buf->type = "text/plain";
    }
#ifdef USE_IMAGE
    else if (activeImage && displayImage && !useExtImageViewer &&
	     !(w3m_dump & ~DUMP_FRAME) && !strncasecmp(t, "image/", 6)) {
	cur_baseURL = New(ParsedURL);
	parseURL("-", cur_baseURL, NULL);
	buf = loadImageBuffer(&uf, t_buf);
	buf->type = "text/html";
    }
#endif
    else {
	if (doExternal(uf, "-", t, &buf, t_buf)) {
	    UFclose(&uf);
	    if (buf == NULL || buf == NO_BUFFER)
		return buf;
	}
	else {			/* unknown type is regarded as text/plain */
	    if (IStype(stream) != IST_ENCODED)
		stream = newEncodedStream(stream, uf.encoding);
	    buf = openPagerBuffer(stream, t_buf);
	    buf->type = "text/plain";
	}
    }
    buf->real_type = t;
    buf->currentURL.scheme = SCM_LOCAL;
    buf->currentURL.file = "-";
    return buf;
}

Line *
getNextPage(Buffer *buf, int plen)
{
    Line *l, *fl, *pl = buf->lastLine;
    Line *rl = NULL;
    int len, i, nlines = 0;
    clen_t linelen = buf->linelen, trbyte = buf->trbyte;
    Str lineBuf2;
    char pre_lbuf = '\0';
    URLFile uf;
    char code;
    int squeeze_flag = 0;
#ifdef USE_ANSI_COLOR
    int check_color;
#endif

    if (buf->pagerSource == NULL)
	return NULL;

    if (fmInitialized)
	crmode();
    if (pl != NULL) {
	nlines = pl->real_linenumber;
	pre_lbuf = *(pl->lineBuf);
	if (pre_lbuf == '\0')
	    pre_lbuf = '\n';
    }

#ifdef JP_CHARSET
    if (buf->document_code)
	code = buf->document_code;
    else
	code = DocumentCode;
#endif
    init_stream(&uf, SCM_UNKNOWN, NULL);
    for (i = 0; i < plen; i++) {
	lineBuf2 = StrmyISgets(buf->pagerSource);
	if (lineBuf2->length == 0) {
	    /* Assume that `cmd == buf->filename' */
	    if (buf->filename)
		buf->buffername = Sprintf("%s %s",
					  CPIPEBUFFERNAME,
					  conv_from_system(buf->filename))->
		    ptr;
	    else if (getenv("MAN_PN") == NULL)
		buf->buffername = CPIPEBUFFERNAME;
	    buf->bufferprop |= BP_CLOSE;
	    trbyte += linelen;
	    linelen = 0;
	    break;
	}
	linelen += lineBuf2->length;
	showProgress(&linelen, &trbyte);
	lineBuf2 = convertLine(&uf, lineBuf2, &code, PAGER_MODE);
	if (squeezeBlankLine) {
	    squeeze_flag = 0;
	    if (lineBuf2->ptr[0] == '\n' && pre_lbuf == '\n') {
		++nlines;
		--i;
		squeeze_flag = 1;
		continue;
	    }
	    pre_lbuf = lineBuf2->ptr[0];
	}
	++nlines;
	Strchop(lineBuf2);
	lineBuf2 = checkType(lineBuf2, propBuffer,
#ifdef USE_ANSI_COLOR
			     colorBuffer, &check_color,
#endif
			     LINELEN);
	len = lineBuf2->length;
	l = New(Line);
	l->lineBuf = lineBuf2->ptr;
	l->propBuf = NewAtom_N(Lineprop, len);
	bcopy((void *)propBuffer, (void *)l->propBuf, len * sizeof(Lineprop));
#ifdef USE_ANSI_COLOR
	if (check_color) {
	    l->colorBuf = NewAtom_N(Linecolor, len);
	    bcopy((void *)colorBuffer, (void *)l->colorBuf,
		  len * sizeof(Linecolor));
	}
	else {
	    l->colorBuf = NULL;
	}
#endif
	l->len = len;
	l->width = -1;
	l->prev = pl;
#if 0
	if (squeezeBlankLine) {
#endif
	    l->real_linenumber = nlines;
	    l->linenumber = (pl == NULL ? nlines : pl->linenumber + 1);
#if 0
	}
	else {
	    l->real_linenumber = l->linenumber = nlines;
	}
#endif
	if (pl == NULL) {
	    pl = l;
	    buf->firstLine = buf->topLine = buf->currentLine = l;
	}
	else {
	    pl->next = l;
	    pl = l;
	}
	if (rl == NULL)
	    rl = l;
	if (nlines > PagerMax) {
	    fl = buf->firstLine;
	    buf->firstLine = fl->next;
	    fl->next->prev = NULL;
	    if (buf->topLine == fl)
		buf->topLine = fl->next;
	    if (buf->currentLine == fl)
		buf->currentLine = fl->next;
	}
    }
    if (pl != NULL)
	pl->next = NULL;
    buf->lastLine = pl;
    if (rl == NULL && squeeze_flag) {
	rl = pl;
    }
    if (fmInitialized)
	term_raw();
    buf->linelen = linelen;
    buf->trbyte = trbyte;
#ifdef JP_CHARSET
    buf->document_code = code;
#endif
    return rl;
}

static void
FTPhalfclose(InputStream stream)
{
    if (IStype(stream) == IST_FILE && file_of(stream)) {
	Ftpfclose(file_of(stream));
	file_of(stream) = NULL;
    }
}

int
save2tmp(URLFile uf, char *tmpf)
{
    FILE *ff;
    int check;
    clen_t linelen = 0, trbyte = 0;
    MySignalHandler(*volatile prevtrap) (SIGNAL_ARG) = NULL;
    static JMP_BUF env_bak;

    ff = fopen(tmpf, "wb");
    if (ff == NULL) {
	/* fclose(f); */
	return -1;
    }
    bcopy(AbortLoading, env_bak, sizeof(JMP_BUF));
    if (SETJMP(AbortLoading) != 0) {
	goto _end;
    }
    prevtrap = signal(SIGINT, KeyAbort);
    if (fmInitialized)
	term_cbreak();
    check = 0;
#ifdef USE_NNTP
    if (uf.scheme == SCM_NEWS) {
	char c;
	while (c = UFgetc(&uf), !iseos(uf.stream)) {
	    if (c == '\n') {
		if (check == 0)
		    check++;
		else if (check == 3)
		    break;
	    }
	    else if (c == '.' && check == 1)
		check++;
	    else if (c == '\r' && check == 2)
		check++;
	    else
		check = 0;
	    putc(c, ff);
	    linelen += sizeof(c);
	    showProgress(&linelen, &trbyte);
	}
    }
    else
#endif				/* USE_NNTP */
    {
	Str buf = Strnew_size(SAVE_BUF_SIZE);
	while (UFread(&uf, buf, SAVE_BUF_SIZE)) {
	    Strfputs(buf, ff);
	    linelen += buf->length;
	    showProgress(&linelen, &trbyte);
	}
    }
  _end:
    bcopy(env_bak, AbortLoading, sizeof(JMP_BUF));
    if (fmInitialized)
	term_raw();
    signal(SIGINT, prevtrap);
    fclose(ff);
    current_content_length = 0;
    return 0;
}

int
doExternal(URLFile uf, char *path, char *type, Buffer **bufp,
	   Buffer *defaultbuf)
{
    Str tmpf, command;
    struct mailcap *mcap;
    int mc_stat;
    Buffer *buf = NULL;
    char *header, *src = NULL;

    if (!(mcap = searchExtViewer(type)))
	return 0;

    tmpf = tmpfname(TMPF_DFL, NULL);

    if (mcap->nametemplate) {
	Str tmp =
	    unquote_mailcap(mcap->nametemplate, NULL, tmpf->ptr, NULL, NULL);
	if (Strncmp(tmpf, tmp, tmpf->length) == 0) {
	    tmpf = tmp;
	    goto _save;
	}
    }
    if (uf.ext && *uf.ext) {
	Strcat_charp(tmpf, uf.ext);
    }

  _save:
    if (IStype(uf.stream) != IST_ENCODED)
	uf.stream = newEncodedStream(uf.stream, uf.encoding);
    header = checkHeader(defaultbuf, "Content-Type:");
    if (header)
	header = conv_to_system(header);
    command = unquote_mailcap(mcap->viewer, type, tmpf->ptr, header, &mc_stat);
#ifndef __EMX__
    if (!(mc_stat & MCSTAT_REPNAME)) {
	Str tmp = Sprintf("(%s) < %s", command->ptr, shell_quote(tmpf->ptr));
	command = tmp;
    }
#endif

    pushText(fileToDelete, tmpf->ptr);
#ifdef HAVE_SETPGRP
    if (!(mcap->flags & (MAILCAP_HTMLOUTPUT | MAILCAP_COPIOUSOUTPUT)) &&
	!(mcap->flags & MAILCAP_NEEDSTERMINAL) && BackgroundExtViewer) {
	flush_tty();
	if (!fork()) {
	    reset_signals();
	    signal(SIGINT, SIG_IGN);
	    close_tty();
	    QuietMessage = TRUE;
	    fmInitialized = FALSE;
	    if (save2tmp(uf, tmpf->ptr) < 0)
		exit(1);
	    UFclose(&uf);
	    myExec(command->ptr);
	}
	*bufp = NO_BUFFER;
	return 1;
    }
    else
#endif
    {
	if (save2tmp(uf, tmpf->ptr) < 0) {
	    *bufp = NULL;
	    return 1;
	}
    }
    if (mcap->flags & (MAILCAP_HTMLOUTPUT | MAILCAP_COPIOUSOUTPUT)) {
	if (defaultbuf == NULL)
	    defaultbuf = newBuffer(INIT_BUFFER_WIDTH);
	if (defaultbuf->sourcefile)
	    src = defaultbuf->sourcefile;
	else
	    src = tmpf->ptr;
	defaultbuf->sourcefile = NULL;
	defaultbuf->mailcap = mcap;
    }
    if (mcap->flags & MAILCAP_HTMLOUTPUT) {
	buf = loadcmdout(command->ptr, loadHTMLBuffer, defaultbuf);
	if (buf && buf != NO_BUFFER) {
	    buf->type = "text/html";
	    buf->mailcap_source = buf->sourcefile;
	    buf->sourcefile = src;
	}
    }
    else if (mcap->flags & MAILCAP_COPIOUSOUTPUT) {
	buf = loadcmdout(command->ptr, loadBuffer, defaultbuf);
	if (buf && buf != NO_BUFFER) {
	    buf->type = "text/plain";
	    buf->mailcap_source = buf->sourcefile;
	    buf->sourcefile = src;
	}
    }
    else {
	if (mcap->flags & MAILCAP_NEEDSTERMINAL || !BackgroundExtViewer) {
	    fmTerm();
	    mySystem(command->ptr, 0);
	    fmInit();
	    if (CurrentTab && Currentbuf)
		displayBuffer(Currentbuf, B_FORCE_REDRAW);
	}
	else {
	    mySystem(command->ptr, 1);
	}
	buf = NO_BUFFER;
    }
    if (buf && buf != NO_BUFFER) {
	buf->filename = path;
	if (buf->buffername == NULL || buf->buffername[0] == '\0')
	    buf->buffername = conv_from_system(lastFileName(path));
	buf->edit = mcap->edit;
	buf->mailcap = mcap;
    }
    *bufp = buf;
    return 1;
}

static int
_MoveFile(char *path1, char *path2)
{
    InputStream f1;
    FILE *f2;
    int is_pipe;
    clen_t linelen = 0, trbyte = 0;
    Str buf;

    f1 = openIS(path1);
    if (f1 == NULL)
	return -1;
    if (*path2 == '|' && PermitSaveToPipe) {
	is_pipe = TRUE;
	f2 = popen(path2 + 1, "w");
    }
    else {
	is_pipe = FALSE;
	f2 = fopen(path2, "wb");
    }
    if (f2 == NULL) {
	ISclose(f1);
	return -1;
    }
    current_content_length = 0;
    buf = Strnew_size(SAVE_BUF_SIZE);
    while (ISread(f1, buf, SAVE_BUF_SIZE)) {
	Strfputs(buf, f2);
	linelen += buf->length;
	showProgress(&linelen, &trbyte);
    }
    ISclose(f1);
    if (is_pipe)
	pclose(f2);
    else
	fclose(f2);
    return 0;
}

void
_doFileCopy(char *tmpf, char *defstr, int download)
{
    Str msg;
    Str filen;
    char *p, *q = NULL;
    pid_t pid;
    char *lock;
#if !(defined(HAVE_SYMLINK) && defined(HAVE_LSTAT))
    FILE *f;
#endif
    struct stat st;
    clen_t size = 0;
    int is_pipe = FALSE;

    if (fmInitialized) {
	p = searchKeyData();
	if (p == NULL || *p == '\0') {
	    q = inputLineHist("(Download)Save file to: ",
			      defstr, IN_COMMAND, SaveHist);
	    if (q == NULL || *q == '\0')
		return;
	    p = conv_to_system(q);
	}
	if (*p == '|' && PermitSaveToPipe)
	    is_pipe = TRUE;
	else {
	    if (q) {
		p = unescape_spaces(Strnew_charp(q))->ptr;
		p = conv_to_system(q);
	    }
	    p = expandName(p);
	    if (checkOverWrite(p) < 0)
		return;
	}
	if (checkCopyFile(tmpf, p) < 0) {
	    msg = Sprintf("Can't copy. %s and %s are identical.",
			  conv_from_system(tmpf), conv_from_system(p));
	    disp_err_message(msg->ptr, FALSE);
	    return;
	}
	if (!download) {
	    if (_MoveFile(tmpf, p) < 0) {
		msg = Sprintf("Can't save to %s", conv_from_system(p));
		disp_err_message(msg->ptr, FALSE);
	    }
	    return;
	}
	lock = tmpfname(TMPF_DFL, ".lock")->ptr;
#if defined(HAVE_SYMLINK) && defined(HAVE_LSTAT)
	symlink(p, lock);
#else
	f = fopen(lock, "w");
	if (f)
	    fclose(f);
#endif
	pushText(fileToDelete, lock);
	flush_tty();
	pid = fork();
	if (!pid) {
	    reset_signals();
	    signal(SIGINT, SIG_IGN);
	    SETPGRP();
	    close_tty();
	    QuietMessage = TRUE;
	    fmInitialized = FALSE;
	    if (!_MoveFile(tmpf, p) && PreserveTimestamp && !is_pipe &&
		!stat(tmpf, &st))
		setModtime(p, st.st_mtime);
	    unlink(lock);
	    exit(0);
	}
	if (!stat(tmpf, &st))
	    size = st.st_size;
	addDownloadList(pid, conv_from_system(tmpf), p, lock, size);
    }
    else {
	q = searchKeyData();
	if (q == NULL || *q == '\0') {
	    printf("(Download)Save file to: ");
	    fflush(stdout);
	    filen = Strfgets(stdin);
	    if (filen->length == 0)
		return;
	    q = filen->ptr;
	}
	for (p = q + strlen(q) - 1; IS_SPACE(*p); p--) ;
	*(p + 1) = '\0';
	if (*q == '\0')
	    return;
	p = q;
	if (*p == '|' && PermitSaveToPipe)
	    is_pipe = TRUE;
	else {
	    p = expandName(p);
	    if (checkOverWrite(p) < 0)
		return;
	}
	if (checkCopyFile(tmpf, p) < 0) {
	    printf("Can't copy. %s and %s are identical.", tmpf, p);
	    return;
	}
	if (_MoveFile(tmpf, p) < 0)
	    printf("Can't save to %s\n", p);
	else if (PreserveTimestamp && !is_pipe && !stat(tmpf, &st))
	    setModtime(p, st.st_mtime);
    }
}

void
doFileMove(char *tmpf, char *defstr)
{
    doFileCopy(tmpf, defstr);
    unlink(tmpf);
}

void
doFileSave(URLFile uf, char *defstr)
{
    Str msg;
    Str filen;
    char *p, *q;
    pid_t pid;
    char *lock;
#if !(defined(HAVE_SYMLINK) && defined(HAVE_LSTAT))
    FILE *f;
#endif

    if (fmInitialized) {
	p = searchKeyData();
	if (p == NULL || *p == '\0') {
	    p = inputLineHist("(Download)Save file to: ",
			      defstr, IN_FILENAME, SaveHist);
	    if (p == NULL || *p == '\0')
		return;
	    p = conv_to_system(p);
	}
	if (checkOverWrite(p) < 0)
	    return;
	if (checkSaveFile(uf.stream, p) < 0) {
	    msg = Sprintf("Can't save. Load file and %s are identical.",
			  conv_from_system(p));
	    disp_err_message(msg->ptr, FALSE);
	    return;
	}
	/*
	 * if (save2tmp(uf, p) < 0) {
	 * msg = Sprintf("Can't save to %s", conv_from_system(p));
	 * disp_err_message(msg->ptr, FALSE);
	 * }
	 */
	lock = tmpfname(TMPF_DFL, ".lock")->ptr;
#if defined(HAVE_SYMLINK) && defined(HAVE_LSTAT)
	symlink(p, lock);
#else
	f = fopen(lock, "w");
	if (f)
	    fclose(f);
#endif
	pushText(fileToDelete, lock);
	flush_tty();
	pid = fork();
	if (!pid) {
	    reset_signals();
	    signal(SIGINT, SIG_IGN);
	    SETPGRP();
	    close_tty();
	    QuietMessage = TRUE;
	    fmInitialized = FALSE;
	    if (!save2tmp(uf, p) && PreserveTimestamp && uf.modtime != -1)
		setModtime(p, uf.modtime);
	    UFclose(&uf);
	    unlink(lock);
	    exit(0);
	}
	addDownloadList(pid, uf.url, p, lock, current_content_length);
    }
    else {
	q = searchKeyData();
	if (q == NULL || *q == '\0') {
	    printf("(Download)Save file to: ");
	    fflush(stdout);
	    filen = Strfgets(stdin);
	    if (filen->length == 0)
		return;
	    q = filen->ptr;
	}
	for (p = q + strlen(q) - 1; IS_SPACE(*p); p--) ;
	*(p + 1) = '\0';
	if (*q == '\0')
	    return;
	p = expandName(q);
	if (checkOverWrite(p) < 0)
	    return;
	if (checkSaveFile(uf.stream, p) < 0) {
	    printf("Can't save. Load file and %s are identical.", p);
	    return;
	}
	if (save2tmp(uf, p) < 0)
	    printf("Can't save to %s\n", p);
	else if (PreserveTimestamp && uf.modtime != -1)
	    setModtime(p, uf.modtime);
    }
}

int
checkCopyFile(char *path1, char *path2)
{
    struct stat st1, st2;

    if (*path2 == '|' && PermitSaveToPipe)
	return 0;
    if ((stat(path1, &st1) == 0) && (stat(path2, &st2) == 0))
	if (st1.st_ino == st2.st_ino)
	    return -1;
    return 0;
}

int
checkSaveFile(InputStream stream, char *path2)
{
    struct stat st1, st2;
    int des = ISfileno(stream);

    if (des < 0)
	return 0;
    if (*path2 == '|' && PermitSaveToPipe)
	return 0;
    if ((fstat(des, &st1) == 0) && (stat(path2, &st2) == 0))
	if (st1.st_ino == st2.st_ino)
	    return -1;
    return 0;
}

int
checkOverWrite(char *path)
{
    struct stat st;
    char *ans;

    if (stat(path, &st) < 0)
	return 0;
    ans = inputAnswer("File exists. Overwrite? (y/n)");
    if (ans && TOLOWER(*ans) == 'y')
	return 0;
    else
	return -1;
}

char *
inputAnswer(char *prompt)
{
    char *ans;

    if (QuietMessage)
	return "n";
    if (fmInitialized) {
	term_raw();
	ans = inputChar(prompt);
    }
    else {
	printf(prompt);
	fflush(stdout);
	ans = Strfgets(stdin)->ptr;
    }
    return ans;
}

static void
uncompress_stream(URLFile *uf, char **src)
{
    int pid1;
    int fd1[2];
    char *expand_cmd = GUNZIP_CMDNAME;
    char *expand_name = GUNZIP_NAME;
    char *tmpf = NULL;
    char *ext = NULL;
    struct compression_decoder *d;

    if (IStype(uf->stream) != IST_ENCODED) {
	uf->stream = newEncodedStream(uf->stream, uf->encoding);
	uf->encoding = ENC_7BIT;
    }
    for (d = compression_decoders; d->type != CMP_NOCOMPRESS; d++) {
	if (uf->compression == d->type) {
	    if (d->auxbin_p)
		expand_cmd = auxbinFile(d->cmd);
	    else
		expand_cmd = d->cmd;
	    expand_name = d->name;
	    ext = d->ext;
	    break;
	}
    }
    uf->compression = CMP_NOCOMPRESS;

    if (pipe(fd1) < 0) {
	UFclose(uf);
	return;
    }

    if (uf->scheme != SCM_LOCAL) {
	tmpf = tmpfname(TMPF_DFL, ext)->ptr;
	pushText(fileToDelete, tmpf);
	if (save2tmp(*uf, tmpf) < 0) {
	    UFclose(uf);
	    return;
	}
#if 0
	if (uf->scheme != SCM_FTP)
#endif
	    UFclose(uf);
	if (src)
	    *src = tmpf;
	else
	    uf->scheme = SCM_LOCAL;
    }

    flush_tty();
    /* fd1[0]: read, fd1[1]: write */
    if ((pid1 = fork()) == 0) {
	reset_signals();
	signal(SIGINT, SIG_IGN);
	close_tty();
	QuietMessage = TRUE;
	fmInitialized = FALSE;
	close(fd1[0]);
	if (tmpf) {
#ifdef USE_BINMODE_STREAM
	    int tmpfd = open(tmpf, O_RDONLY | O_BINARY);
#else
	    int tmpfd = open(tmpf, O_RDONLY);
#endif
	    if (tmpfd < 0) {
		close(fd1[1]);
		exit(1);
	    }
	    dup2(tmpfd, 0);
	}
	else {
	    /* child */
	    int pid2;
	    int fd2[2];
	    if (pipe(fd2) < 0) {
		close(fd1[1]);
		UFclose(uf);
		exit(1);
	    }
	    if ((pid2 = fork()) == 0) {
		/* child */
		Str buf = Strnew_size(SAVE_BUF_SIZE);
		close(fd2[0]);
		while (UFread(uf, buf, SAVE_BUF_SIZE)) {
		    if (write(fd2[1], buf->ptr, buf->length) < 0) {
			close(fd2[1]);
			exit(0);
		    }
		}
		close(fd2[1]);
		exit(0);
	    }
	    close(fd2[1]);
	    dup2(fd2[0], 0);
	}
	dup2(fd1[1], 1);
	dup2(fd1[1], 2);
	execlp(expand_cmd, expand_name, NULL);
	exit(0);
    }
    close(fd1[1]);
    if (tmpf == NULL)
	UFclose(uf);
    uf->stream = newFileStream(fdopen(fd1[0], "rb"), (void (*)())pclose);
}

static FILE *
lessopen_stream(char *path)
{
    char *lessopen;
    FILE *fp;

    lessopen = getenv("LESSOPEN");
    if (lessopen == NULL) {
	return NULL;
    }
    if (lessopen[0] == '\0') {
	return NULL;
    }

    if (lessopen[0] == '|') {
	/* pipe mode */
	Str tmpf;
	int c;

	++lessopen;
	tmpf = Sprintf(lessopen, path);
	fp = popen(tmpf->ptr, "r");
	if (fp == NULL) {
	    return NULL;
	}
	c = getc(fp);
	if (c == EOF) {
	    fclose(fp);
	    return NULL;
	}
	ungetc(c, fp);
    }
    else {
	/* filename mode */
	/* not supported m(__)m */
	fp = NULL;
    }
    return fp;
}

#if 0
void
reloadBuffer(Buffer *buf)
{
    URLFile uf;

    if (buf->sourcefile == NULL || buf->pagerSource != NULL)
	return;
    init_stream(&uf, SCM_UNKNOWN, NULL);
    examineFile(buf->mailcap_source ? buf->mailcap_source : buf->sourcefile,
		&uf);
    if (uf.stream == NULL)
	return;
    is_redisplay = TRUE;
    buf->allLine = 0;
    buf->href = NULL;
    buf->name = NULL;
    buf->img = NULL;
    buf->formitem = NULL;
    buf->linklist = NULL;
    buf->maplist = NULL;
    if (buf->hmarklist)
	buf->hmarklist->nmark = 0;
    if (buf->imarklist)
	buf->imarklist->nmark = 0;
    if (!strcasecmp(buf->type, "text/html"))
	loadHTMLBuffer(&uf, buf);
    else
	loadBuffer(&uf, buf);
    UFclose(&uf);
    is_redisplay = FALSE;
}
#endif

#ifdef JP_CHARSET
static char
guess_charset(char *p)
{
    Str c = Strnew_size(strlen(p));
    if (strncasecmp(p, "x-", 2) == 0)
	p += 2;
    while (*p != '\0') {
	if (*p != '-' && *p != '_')
	    Strcat_char(c, TOLOWER(*p));
	p++;
    }
    if (strncmp(c->ptr, "euc", 3) == 0)
	return CODE_EUC;
    if (strncmp(c->ptr, "shiftjis", 8) == 0 || strncmp(c->ptr, "sjis", 4) == 0)
	return CODE_SJIS;
    if (strncmp(c->ptr, "iso2022jp", 9) == 0 || strncmp(c->ptr, "jis", 3) == 0)
	return CODE_JIS_n;
    return CODE_ASCII;
}
#endif

static char *
guess_filename(char *file)
{
    char *p = NULL, *s;

    if (file != NULL)
	p = mybasename(file);
    if (p == NULL || *p == '\0')
	return DEF_SAVE_FILE;
    s = p;
    if (*p == '#')
	p++;
    while (*p != '\0') {
	if ((*p == '#' && *(p + 1) != '\0') || *p == '?') {
	    *p = '\0';
	    break;
	}
	p++;
    }
    return s;
}

char *
guess_save_name(Buffer *buf, char *path)
{
    if (buf && buf->document_header) {
	Str name = NULL;
	char *p, *q;
	if ((p = checkHeader(buf, "Content-Disposition:")) != NULL &&
	    (q = strcasestr(p, "filename")) != NULL &&
	    (q == p || IS_SPACE(*(q - 1)) || *(q - 1) == ';') &&
	    matchattr(q, "filename", 8, &name))
	    path = name->ptr;
	else if ((p = checkHeader(buf, "Content-Type:")) != NULL &&
		 (q = strcasestr(p, "name")) != NULL &&
		 (q == p || IS_SPACE(*(q - 1)) || *(q - 1) == ';') &&
		 matchattr(q, "name", 4, &name))
	    path = name->ptr;
    }
    return guess_filename(path);
}

/* Local Variables:    */
/* c-basic-offset: 4   */
/* tab-width: 8        */
/* End:                */