1%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    2%
    3%  FILE     : Env/env_clima.pl
    4%
    5%  AUTHOR : Sebastian Sardina (2003)
    6%  EMAIL  : ssardina@cs.toronto.edu
    7%  WWW    : www.cs.toronto.edu/~ssardina www.cs.toronto.edu/cogrobo
    8%  TYPE   : system independent code
    9%  TESTED : SWI Prolog 5.0.10 www.swi-prolog.org
   10%
   11% This files provides the device for working with the CLIMA game simulation server 
   12%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   13%
   14%                             November 15, 2002
   15%
   16% This software was developed by the Cognitive Robotics Group under the
   17% direction of Hector Levesque and Ray Reiter.
   18%
   19%        Do not distribute without permission.
   20%        Include this notice in any copy made.
   21%
   22%
   23%         Copyright (c) 2000 by The University of Toronto,
   24%                        Toronto, Ontario, Canada.
   25%
   26%                          All Rights Reserved
   27%
   28% Permission to use, copy, and modify, this software and its
   29% documentation for non-commercial research purpose is hereby granted
   30% without fee, provided that the above copyright notice appears in all
   31% copies and that both the copyright notice and this permission notice
   32% appear in supporting documentation, and that the name of The University
   33% of Toronto not be used in advertising or publicity pertaining to
   34% distribution of the software without specific, written prior
   35% permission.  The University of Toronto makes no representations about
   36% the suitability of this software for any purpose.  It is provided "as
   37% is" without express or implied warranty.
   38% 
   39% THE UNIVERSITY OF TORONTO DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
   40% SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
   41% FITNESS, IN NO EVENT SHALL THE UNIVERSITY OF TORONTO BE LIABLE FOR ANY
   42% SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
   43% RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
   44% CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
   45% CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
   46% 
   47%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   48% 
   49% This file assumes that the following is defined in env_gen.pl:
   50%
   51% -- start/0     : initialization of the environment (called when loaded)
   52% -- finalize/0  : finalization of the environment (called when exiting)
   53% -- main_dir/1  : obtain the root IndiGolog directory
   54% -- report_exog_event(A, M): 
   55%                  report exogenous event A with message M to the
   56%                  environment manager
   57% -- All compatibility libraries depending on the architecture such us:
   58%    -- compat_swi/compat_ecl compatibility libraries providing:
   59%
   60% -- The following two dynamic predicates should be available:
   61%    -- listen_to(Type, Name, Channel) 
   62%            listen to Channel of Type (stream/socket) with Name
   63%    -- terminate/0
   64%            order the termination of the application
   65%
   66% -- The following should be implemented here:
   67%
   68%  -- name_dev/1              : mandatory *
   69%  -- initializeInterfaces(L) : mandatory *
   70%  -- finalizeInterfaces(L)   : mandatory *
   71%  -- execute/4               : mandatory *
   72%  -- handle_steam/1          : as needed
   73%  -- listen_to/3             : as needed
   74%
   75%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   76:- include(env_gen).      % INCLUDE THE CORE OF THE DEVICE MANAGER
   77
   78:- dynamic bye_message_received/0. % game is over and the device may finish
   79
   80%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   81% CONSTANTS TO BE USED
   82%
   83% name_dev/1 : state the name of the device manager (e.g., simulator, rcx)
   84%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   85% Set name of the environment here. (THIS CONSTANT IS MANDATORY, DONT DELETE!)
   86name_dev(clima06). 
   87
   88% Set verbose debug level
   89:- set_debug_level(5).   90
   91
   92
   93%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   94% A - INITIALIZATION AND FINALIZATION OF INTERFACES
   95%     initializeInterfaces/1 and finalizeInterfaces/1
   96%
   97% HERE YOU SHOULD INITIALIZE AND FINALIZE EACH OF THE INTERFACES TO BE USED
   98%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   99initializeInterfaces(L) :- 
  100           % 1 - Obtain IP and Port from L
  101        member([ipsim,SIP], L),   
  102        member([portsim, SP], L),  	% Get Host and Port of CLIMA simulator
  103        member([agentLogin, AL], L),  		
  104        member([agentPass, AP], L),  	% Get agent user/password participating in simulator
  105        string_to_number(SP, Port),
  106	string_to_atom(SIP, IP),
  107	assert(clima_location(IP, Port)),		% Assert the clima server location
  108	assert(clima_agentid(AL, AP)),			% Assert the agent login information
  109           % 2 - Setup communication with CLIMA game server
  110        report_message(system(2),'Establishing connection to CLIMA SIMULATOR'),
  111        printKbInstructions,
  112        connectToGameSimulator.
  113
  114finalizeInterfaces(_) :- 
  115        disconnectFromGameSimulator,
  116        report_message(system(2), 'Disconnection from CLIMA SIMULATOR successful').
  117
  118
  119
  120% printKbInstructions: Print instructions on how to enter keyboard input
  121printKbInstructions :-
  122    writeln('*********************************************************'), 
  123    writeln('* NOTE: This is the CLIMA SIMULATOR device manager'), 
  124    writeln('*   This window will show the communication'), 
  125    writeln('*   with the CLIMA GAME SIMULATOR'), 
  126    writeln('*********************************************************'),
  127    nl.
  128
  129
  130% Set main connection to CLIMA simulator socket at Host/Port
  131connectToGameSimulator :-
  132	clima_location(Host, Port),
  133	clima_agentid(AgentUser, AgentPass),
  134	clima_connect(Host, Port, comm_sim), 
  135	clima_authenticate(comm_sim, AgentUser, AgentPass, R), !,
  136	(R = ok -> 
  137	        report_message(system(2), 'Communication with CLIMA SIMULATOR established'),
  138		assert(listen_to(socket, comm_sim, comm_sim)),
  139		sleep(1),
  140		report_exog_event(connected(climaServer), _)
  141	; 
  142		report_message(error, ['Cannot authenticate to game server: ',R])
  143	).
  144
  145% Finalize main connection to CLIMA simulator socket
  146disconnectFromGameSimulator :- 
  147	retractall(listen_to(socket, comm_sim, comm_sim)),
  148	clima_disconnect(comm_sim).
  149
  150
  151
  152%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  153% B - HANDLERS FOR EACH STREAM/SOCKET THAT IS BEING HEARD:  handle_stream/1
  154%
  155% HERE YOU SHOULD WRITE HOW TO HANDLE DATA COMMING FROM EACH OF THE
  156% INTERFACES/CHANNELS USED
  157%
  158% OBS: handle_stream/1 must always end up succeeding!
  159%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  160:- dynamic actionStatus/3. % Used to record each action request from the simulator
  161
  162
  163% Handle data comming from the CLIMA simulator
  164handle_stream(comm_sim) :- 
  165	get_socket_stream(comm_sim, read, Read),
  166	at_end_of_stream(Read), 
  167	report_message(system(2), ['Game server disconnection']), !,
  168	(bye_message_received -> 
  169		report_message(system(2), 
  170			['Bye message was already received. Ready for termination.']),
  171		disconnectFromGameSimulator
  172		
  173	;
  174		repeat,
  175		report_message(system(2), ['Reconnecting to game server...']),
  176		disconnectFromGameSimulator,
  177		sleep(5),
  178		(connectToGameSimulator -> true ; fail)
  179	).
  180
  181	
  182
  183handle_stream(comm_sim) :- 
  184	get_socket_stream(comm_sim, read, Read),
  185	\+ at_end_of_stream(Read),
  186	clima_receive_XML(comm_sim, XMLMess), !, 
  187	report_message(system(3), ['Message from game server: ', XMLMess]),
  188	handle_xml_message(XMLMess).
  189
  190
  191
  192
  193% Handle each possible message from the CLIMA simulator by sending the
  194% corresponding exogenous action to the agent
  195handle_xml_message(XMLMess) :-
  196	unfold_clima_message(XMLMess, sim-start, TimeStamp, BodyXMLMess), !,
  197	report_exog_event(simStart(TimeStamp, BodyXMLMess), _).
  198handle_xml_message(XMLMess) :-
  199	unfold_clima_message(XMLMess, request-action, TimeStamp, BodyXMLMess), !,
  200	member(id(IdAction), BodyXMLMess),
  201	member(deadline(Deadline), BodyXMLMess),
  202	asserta(actionStatus(Deadline, IdAction, pending)), % Observe we assert on the top!
  203	report_exog_event(requestAction(TimeStamp, BodyXMLMess), _).
  204	%drop_at_depot(BodyXMLMess).
  205handle_xml_message(XMLMess) :-
  206	unfold_clima_message(XMLMess, bye, _, _), !,
  207	assert(bye_message_received),
  208	report_exog_event(halt_exec, _).
  209handle_xml_message(XMLMess) :-
  210	unfold_clima_message(XMLMess, sim-end, TimeStamp, BodyXMLMess), !,
  211	report_exog_event(simEnd(TimeStamp, BodyXMLMess), _).
  212handle_xml_message(XMLMess) :- 
  213	report_message(warning, ['Message unknown from game simulator: ', XMLMess]).
  214
  215:- dynamic justDropped/0.  216
  217drop_at_depot(Data) :-
  218	member(cells(LCells), Data), 
  219	member(cell(cur, LCellProp), LCells),
  220	member(depot,LCellProp), !,
  221	execute(drop, _, 0, _),
  222	assert(justDropped).
  223drop_at_depot(_).
  224
  225
  226
  227
  228
  229%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  230% C - EXECUTION MODULE: execute/4
  231%
  232% This part implements the execution capabilities of the environment
  233%
  234% execute(Action, Type, Sensing) : execute Action of Type and return Sensing
  235%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  236% Execute Action in game simulator
  237%execute(drop, _, _, ok) :- retract(justDropped), !.
  238execute(Action, _, N, Sensing) :- 
  239	(retract(actionStatus(Deadline, IdAction, pending)) ->
  240		true
  241	;
  242		report_message(warning,['Action *',(Action,N),'* is being sent without any previous request pending']),
  243		IdAction=0
  244		
  245	), 
  246 	%\+ tooLate(Deadline),
  247        report_message(action, ['Executing non-sensing action: *',(Action,N,IdAction),'*']), 
  248	(clima_execute(comm_sim, Action, IdAction, ok) ->
  249		Sensing=ok,
  250		report_message(system(4), ['The action *',(Action,N,IdAction),'* was sent to game server'])
  251	;
  252		Sensing=failed, 
  253		report_message(system(4), ['The following action failed to execute: *',	(Action,N,IdAction),'*'])
  254	),
  255	asserta(actionStatus(Deadline, IdAction, Sensing)).
  256
  257% Currently, it is never too late to send an action to the game server.
  258tooLate(_) :- fail.
  259
  260%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  261%%%%%%% DIRECT COMMUNICATION WITH CLIMA SIMULATOR %%%%%%%%%%%
  262%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  263
  264
  265
  266%%%%%%%%%%%%%%%%%%%%%%%
  267% CLIMA PROTOCOL: High-level API
  268%%%%%%%%%%%%%%%%%%%%%%%
  269
  270% Connection/disconnection
  271clima_connect(Host, Port, ConnID) :-
  272	catch(socket(internet, stream, ConnID), E, 
  273		(report_message(error, ['Cannot create socket : ',E]),fail) ), 
  274	catch(connect(ConnID, Host/Port), E, 
  275		(report_message(error, ['Cannot connect to game server: ',E]),fail) ), 
  276	get_socket_stream(ConnID, write, StreamW),
  277	get_socket_stream(ConnID, read, StreamR),
  278	set_stream(StreamW, encoding(text)),
  279	set_stream(StreamR, encoding(utf8)).
  280
  281clima_disconnect(ConnID) :- close_socket(ConnID).
  282
  283% Agent authentication
  284clima_authenticate(ConnID, AgentUser, AgentPass, Result) :-
  285	unfold_clima_message(XMLMess, auth-request, -1, [username(AgentUser), 
  286					password(AgentPass)]),
  287	clima_send_XML(ConnID, XMLMess),	!,
  288	(select([ConnID], 5, [ConnID]) ->
  289		clima_receive_XML(ConnID,MessReply), !,
  290		unfold_clima_message(MessReply, auth-response, _, BodyMess),
  291		member(result(Result), BodyMess)
  292	;	
  293		Result=timeout
  294	).
  295
  296
  297% Execute Action with IdAction in the simulator via socket ConnID
  298clima_execute(ConnID, Action, IdAction, ok) :-
  299	atom(Action), 
  300	unfold_clima_message(XMLMess, action, -1, [type(Action), id(IdAction)]),
  301	clima_send_XML(ConnID, XMLMess), !.
  302clima_execute(ConnID, Action, IdAction, ok) :-
  303	compound(Action),
  304	Action =.. [Name|Param],
  305	unfold_clima_message(XMLMess, action, -1, [type(Name), param(Param), id(IdAction)]),
  306	clima_send_XML(ConnID, XMLMess), !.
  307clima_execute(_, _, _, failed).
  308
  309
  310
  311	
  312% 	[element(message, [type=auth-response], [element(authentication, [result=fail], [])])]
  313
  314% 	(Type=[type='auth-request'] ; Type=[type=auth-request]),
  315% 	Body= element(authentication,[username=AgentUser,password=AgentPass], []),
  316% 	XMLMess=element(message, Type, ['\n', Body, '\n']),
  317% 	clima_send_XML(ConnID, XMLMess),
  318% 	(select([ConnID], 5, [ConnID]) ->
  319% 		clima_receive_XML(ConnID,MessReply),
  320% 		unfold_clima_message(MessReply, auth-response, _, BodyMess),
  321% 		member(result(Result), BodyMess)
  322% 	;	
  323% 		Result=timeout
  324% 	).
  325
  326
  327%%%%%%%%%%%%%%%%%%%%%%%
  328% CLIMA PROTOCOL: low-level interface
  329%%%%%%%%%%%%%%%%%%%%%%%
  330
  331% SEND/RECEIVE XML Messages
  332clima_send_XML(ConnID, XMLMess) :-
  333	get_socket_stream(ConnID, write, Stream),
  334	xml_write(Stream, XMLMess, []),
  335	swritef(NullByte, '\0'),
  336	write(Stream, NullByte),	% Send necessary null-byte (thanks to Stavros)
  337	flush(Stream).
  338
  339clima_receive_XML(ConnID, XMLMess) :-
  340	get_socket_stream(ConnID, read, Stream),
  341	( (peek_code(Stream, R), member(R,[0,10])) ->
  342		get_code(Stream, _),
  343		XMLMess=element(message, null, null)
  344	;
  345		catch(load_structure(Stream, LXMLMess, [parse(element)]), _, 
  346			(LXMLMess=[element(message,null,null)], writeln(segmentation)) ),
  347		(peek_code(Stream, 10) -> get_code(Stream,10) ; true),
  348		(peek_code(Stream, 0) -> get_code(Stream,0) ; true),
  349		XMLMess=element(message, _, _),
  350		member(XMLMess, LXMLMess)
  351	).
  352
  353
  354
  355
  356%%%%%%%%%%%%%%%%%%%%%%%
  357% CLIMA PROTOCOL: message handling
  358%%%%%%%%%%%%%%%%%%%%%%%
  359
  360% Decompose a top-level CLIMA message into Type, TimeStamp, and its elements
  361clima_message(Mess, Type, TimeStamp, ListElements) :-
  362	\+ var(Mess),
  363	Mess=element(message, LAttrMess, LContentMess),
  364	member(type=Type, LAttrMess),
  365	(member(timestamp=TimeStamp, LAttrMess) -> true ; TimeStamp = -1),
  366	findall([NameE, LAttrE, LContE], 
  367		member(element(NameE, LAttrE, LContE), LContentMess), ListElements).
  368
  369clima_message(Mess, Type, TimeStamp, ListElements) :-
  370	var(Mess),
  371	Mess=element(message, [type=Type|LAttrMess], LContentMess),
  372	(TimeStamp = -1 -> LAttrMess=[] ; LAttrMess=[timestamp=TimeStamp]),
  373	setof(element(NameE, LAttrE, LContE), 
  374		member([NameE, LAttrE, LContE], ListElements),  LContentMess).
  375
  376
  377% unfold_clima_messages(XMLMess, Type, TimeStamp, BodyXMLMess)
  378%	Extract all information from a CLIMA message XMLMess
  379
  380% Messages from the simulation server: server---> agent
  381unfold_clima_message(XMLMess, auth-response, NTimeStamp, Data) :-
  382	permutation(Data, [result(Result)]), !,
  383	(T= 'auth-response' ; T = auth-response),
  384	clima_message(XMLMess, T, TimeStamp, LElements),
  385	any_to_number(TimeStamp, NTimeStamp),
  386	member([authentication,LAttr,[]], LElements),
  387	member(result=Result, LAttr).
  388unfold_clima_message(XMLMess, sim-start, NTimeStamp, Data) :-
  389	permutation(Data,
  390		[id(Id), opponent(Opp), steps(NSteps), 
  391			gsizeX(NGX), gsizeY(NGY), depotX(NDX), depotY(NDY)]), !,
  392	(T= 'sim-start' ; T = sim-start),
  393	clima_message(XMLMess, T, TimeStamp, LElements),
  394	any_to_number(TimeStamp, NTimeStamp),
  395	member([simulation,LAttr,[]], LElements),
  396	member(id=Id, LAttr),
  397	member(opponent=Opp, LAttr),
  398	member(steps=Steps, LAttr), any_to_number(Steps, NSteps),
  399	member(gsizex=GX, LAttr), any_to_number(GX, NGX),
  400	member(gsizey=GY, LAttr), any_to_number(GY, NGY),
  401	member(depotx=DX, LAttr), any_to_number(DX, NDX),
  402	member(depoty=DY, LAttr), any_to_number(DY, NDY).
  403unfold_clima_message(XMLMess, sim-end, NTimeStamp, Data) :-
  404	permutation(Data, [score(NScore), result(Result)]), !,
  405	(T= 'sim-end' ; T = sim-end),
  406	clima_message(XMLMess, T, TimeStamp, LElements), 
  407	any_to_number(TimeStamp, NTimeStamp),
  408	(member([sim-result, LAttr, []], LElements) ; 
  409			member(['sim-result', LAttr, []], LElements)),
  410	member(score=Score, LAttr), any_to_number(Score, NScore),
  411	member(result=Result, LAttr).
  412unfold_clima_message(XMLMess, bye, TimeStamp, []) :-
  413	(T= 'bye' ; T = bye),
  414	clima_message(XMLMess, T, TimeStamp, []).
  415unfold_clima_message(XMLMess, request-action, NTimeStamp, Data) :-
  416	permutation(Data, [step(NStep),posX(NPosX),posY(NPosY),items(NItems),
  417				deadline(NDeadline),id(Id),cells(CellsInfo)]), !,
  418		% CLIMA06 uses 'requestaction' - CLIMA07 uses 'request-action'
  419	(T= 'requestaction' ; T = requestaction ; T=request-action ; T='request-action'),
  420	clima_message(XMLMess, T, TimeStamp, LElements), 
  421	any_to_number(TimeStamp, NTimeStamp),
  422	member([perception,LAttr,LCont],  LElements),
  423	member(step=Step, LAttr),  any_to_number(Step, NStep),
  424	member(posx=PosX, LAttr), any_to_number(PosX, NPosX),
  425	member(posy=PosY, LAttr), any_to_number(PosY, NPosY),
  426		% No of gold items is only present in the 2007 edition of CLIMA
  427	(member(items=Items, LAttr) -> any_to_number(Items, NItems) ; NItems=(-1)),
  428	member(deadline=Deadline, LAttr), any_to_number(Deadline, NDeadline),
  429	member(id=Id, LAttr),
  430	extract_cells_info(LCont, CellsInfo).
  431
  432
  433
  434% Messages from the agent to the simulation server: agent ---> server
  435unfold_clima_message(XMLMess, action, TimeStamp, Data) :-
  436	(TypeData = [id(Id), type(Action), param(Param)] ; TypeData = [id(Id), type(Action)]),
  437	length(TypeData, LenTypeData),
  438	permutation(Data, TypeData), !,
  439	(T= 'action' ; T = action),
  440	length(LElements, 1),
  441	length(LAttr,LenTypeData),
  442	clima_message(XMLMess, T, TimeStamp, LElements),
  443	member([action, LAttr, []],  LElements),
  444	member(type=Action, LAttr),
  445	member(id=Id, LAttr),
  446	(LenTypeData=3 -> member(param=Param, LAttr) ; true).
  447
  448unfold_clima_message(XMLMess, auth-request, TimeStamp, Data) :-
  449	TypeData = [username(AgentUser), password(AgentPass)],
  450	length(TypeData, LenTypeData),
  451	permutation(Data, TypeData), !,
  452	(T= 'auth-request' ; T = auth-request),
  453	length(LElements,1),
  454	length(LAttr,LenTypeData),
  455	clima_message(XMLMess, T, TimeStamp, LElements),
  456	member([authentication, LAttr, []],  LElements),
  457	member(username=AgentUser, LAttr),
  458	member(password=AgentPass, LAttr).
  459
  460
  461
  462
  463
  464% Given a content LContCells containing the information on the sorroundin cells
  465% it extracts that XML info into list LCellsInfo
  466extract_cells_info(LContCells, LCellsInfo) :-
  467	findall(cell(IDCell,PropCell), 
  468			(member(element(cell,[id=IDCell], PropCellRaw), LContCells),
  469			  extract_cell_info(PropCellRaw,PropCell), 
  470			  PropCell\=[]), 	LCellsInfo).	% only include cells with info
  471extract_cell_info(PropCellRaw, LPropCell) :-
  472	findall(agent(AgentType), 
  473		member(element(agent, [type=AgentType], []), PropCellRaw), L1),
  474	findall(obstacle, member(element(obstacle, [], []), PropCellRaw), L2),
  475	findall(gold, member(element(gold, [], []), PropCellRaw), L3),
  476	findall(mark(Mark), member(element(mark, [value=Mark], []), PropCellRaw), L4),
  477	findall(empty, member(element(empty, [], []), PropCellRaw), L5),
  478	findall(depot, member(element(depot, [], []), PropCellRaw), L6),
  479	LL=[L1,L2,L3,L4,L5,L6],
  480	flatten(LL, LPropCell).
  481
  482
  483%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  484% EOF:  Env/env_clima.pl
  485%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  486
  487/*
  488
  489
  490% 
  491
  492tcp_socket(S) ,tcp_connect(S,'tea.dyndns.org':12300), tcp_open_socket(S,R,W), set_stream(W,encoding(text)), 
  493 xml_write(W,element(message, [type='auth-request'], ['\n', element(authentication, [username=china3, password=1], []), '\n']),[]),nl
  494 (W),swritef(NullByte,'\0'),write(W,NullByte), flush_output(W).
  495
  496*/