-###PERL;
-#
+###RUN_PERL: #!/usr/bin/perl
+
# /bsta/2words
# 2words is generated from 2words.1.pl.
-# 09.01.2023
#
# The wordgame interface
#
-# Copyright (C) 2016 - 2017, 2023 Balthasar Szczepański
+# Copyright (C) 2016, 2017, 2023, 2024 Balthasar Szczepański
#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Affero General Public License as
-# published by the Free Software Foundation, either version 3 of the
-# License, or (at your option) any later version.
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU Affero General Public License for more details.
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
#
-# You should have received a copy of the GNU Affero General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
use strict;
-#use warnings;
-###LIB;
-use bsta_lib qw(failpage gethttpheader getcgi entityencode readdatafile writedatafile urlencode bb2ht);
-use File::Copy;
-
-###STORY_PATH;
-###LOGO_PATH;
-###FAVICON_PATH;
-###WEBSITE;
-###WEBSITE_NAME;
-###CSS_PATH;
-###TWOWORDS_PATH;
-###CGI_PATH;
-###SETTINGS_PATH;
-###INDEX_PATH;
-###INTF_DATE;
-###DATA_PATH;
-###VIEWER_PATH;
-###DEFAULT_PATH;
-###STATE_PATH;
-###WWW_PATH;
-###STORY_LENGTH;
-###PAGE_LENGTH;
-###FIRSTPAGE_LENGTH;
-###COIN_PATH;
-###COIN_DATE;
-###LIST_PATH;
+use utf8;
+# use Encode::Locale ('decode_argv');
+use Encode ('encode', 'decode');
+
+###PERL_LIB: use lib /botm/lib/bsta
+use botm_common (
+ 'HTTP_STATUS',
+ 'http_header_status', 'http_header_allow',
+ 'read_data_file', 'write_data_file',
+ 'merge_url',
+ 'read_header_env',
+ 'html_entity_encode_dec',
+ 'url_query_decode',
+ 'open_encoded'
+);
+use bsta_lib (
+ 'STATE', 'INTF_STATE',
+ 'get_id',
+ 'fail_method', 'fail_content_type',
+ 'print_html_start', 'print_html_end',
+ 'print_html_head_start', 'print_html_head_end',
+ 'print_html_body_start', 'print_html_body_end',
+ 'write_index',
+ 'get_remote_addr',
+ 'merge_settings',
+ 'ong'
+);
+
+###PERL_CGI_PATH: CGI_PATH = /bsta/
+###PERL_CGI_2WORDS_PATH: CGI_2WORDS_PATH = /bsta/2words
+
+###PERL_DATA_SETTINGS_PATH: DATA_SETTINGS_PATH = /botm/data/bsta/settings
+###PERL_DATA_STATE_PATH: DATA_STATE_PATH = /botm/data/bsta/state
+###PERL_DATA_STORY_PATH: DATA_STORY_PATH = /botm/data/bsta/story
+
+###PERL_WEBSITE_NAME: WEBSITE_NAME = Bicycles on the Moon
+
+###PERL_STORY_LENGTH: STORY_LENGTH = 16
+###PERL_PAGE_LENGTH: PAGE_LENGTH = 16
+###PERL_FIRSTPAGE_LENGTH: FIRSTPAGE_LENGTH = 4
+
+binmode STDIN, ':encoding(UTF-8)';
+binmode STDOUT, ':encoding(UTF-8)';
+binmode STDERR, ':encoding(UTF-8)';
+# decode_argv();
my %http;
my %cgi;
my %story;
-my %newstory;
+my %new_story;
my %settings;
my %state;
-my %gotolist;
my $time = time();
srand ($time-$$);
my $IP;
my $words;
my $color2;
-my $lastip;
-my $storyid;
+my $last_IP;
+my $story_id;
my $turn;
+my $status;
+my $allow;
my $message;
-my $firstletter;
-my $secondletter;
-my $intfstate;
-my $intfpass;
-my $intfpause;
-my $intfmode;
-my $storypath;
-my $storyfile;
-my $storylock;
-my @storylines;
-my $ongstate;
+my $first_letter;
+my $second_letter;
+my $last_letter;
+my $intf_state;
+my $intf_pass;
+my $intf_pause;
+my $intf_mode;
+my $story_i_path;
+my $fh;
+my $story_lock;
+my @story_lines;
+my $ong_state;
my $page;
+my $cmd_clear;
+my $cmd_clear_all;
delete @ENV{qw(IFS CDPATH ENV BASH_ENV)};
-###PATH;
+###PERL_SET_PATH: $ENV{'PATH'} = /usr/local/bin:/usr/bin:/bin;
if ($ENV{'REQUEST_METHOD'} =~ /^(HEAD|GET|POST)$/) {
- $method=$1;
+ $method = $1;
}
-else{
- exit failpage("Status: 405 Method Not Allowed\nAllow: GET, POST, HEAD\n","405 Method Not Allowed","The interface does not support the $ENV{'REQUEST_METHOD'} method.",$method);
+else {
+ exit fail_method($ENV{'REQUEST_METHOD'}, ['GET','POST', 'HEAD']);
}
-%http = gethttpheader (\%ENV);
-%cgi = getcgi($ENV{'QUERY_STRING'});
+%http = read_header_env(\%ENV);
+%cgi = url_query_decode($ENV{'QUERY_STRING'});
if ($method eq 'POST') {
if ($http{'content-type'} eq 'application/x-www-form-urlencoded') {
- my %cgipost=getcgi( <STDIN> );
- foreach my $ind (keys %cgipost) {
- $cgi{$ind}=$cgipost{$ind};
- }
+ my %cgi_post = url_query_decode( <STDIN> );
+ %cgi = merge_settings(\%cgi, \%cgi_post);
}
# multipart not supported
else{
- exit failpage("Status: 415 Unsupported Media Type\n","415 Unsupported Media Type","Unsupported Content-type: $http{'content-type'}.");
+ exit fail_content_type($method, $http{'content-type'});
}
}
-if ($ENV{'PATH_INFO'} =~ /^\/(.+)$/) {
- $page=int($1);
-}
-else {
- $page=0;
-}
-
-if ($ENV{'HTTP_X_FORWARDED_FOR'} =~ /^([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)$/) {
- $IP=$1;
-}
-elsif ($ENV{'REMOTE_ADDR'} =~ /^([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)$/) {
- $IP=$1;
-}
-else {
- $IP='0.0.0.0';
-}
-
+$IP = get_remote_addr();
+$page = get_id(\%cgi);
if ($cgi{'words'} ne '') {
- $words=$cgi{'words'};
+ $words = $cgi{'words'};
}
-%settings=readdatafile(SETTINGS_PATH);
-%state=readdatafile(STATE_PATH);
-$ongstate=int($state{'state'});
+%settings = read_data_file(DATA_SETTINGS_PATH());
+%state = read_data_file(DATA_STATE_PATH());
+$ong_state = int($state{'state'});
+$cmd_clear = $settings{'password'}.' clear';
+$cmd_clear_all = $settings{'password'}.' clearall';
-$storylock=0;
-if (open ($storyfile,"+<",STORY_PATH)){
- $storylock=1;
- if (flock($storyfile,2)) {
- $storylock=2;
+$story_lock=0;
+if (open_encoded($fh, "+<:encoding(UTF-8)", DATA_STORY_PATH())) {
+ $story_lock=1;
+ if (flock($fh,2)) {
+ $story_lock=2;
}
- %story=readdatafile($storyfile);
+ %story = read_data_file($fh);
- if ($story{'lastip'} =~ /^([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)$/) {
- $lastip=$1;
+ if ($story{'lastip'} =~ /^.+$/) {
+ $last_IP=$&;
}
else {
- $lastip='0.0.0.0';
+ $last_IP='0.0.0.0';
}
- $storyid = int($story{'id'});
- $intfpass = int($story{'pass'});
- $intfstate = int($story{'state'});
- $intfmode = $intfstate;
- $intfpause = $intfstate & 1;
- if ($intfpause) {
- $intfmode -= 1;
- }
+ $last_letter = lc($story{'letter'});
+ $story_id = int($story{'id'});
+ $intf_pass = int($story{'pass'});
+ $intf_state = int($story{'state'});
+ $intf_mode = $intf_state & INTF_STATE->{'mode'};
+ $intf_pause = $intf_state & INTF_STATE->{'||'};
- if ($IP ne $lastip) {
+ if ($IP ne $last_IP) {
$turn = 1;
}
else {
- $turn = 0 ;
+ $turn = 0;
}
- if($words =~ /^bstaaaclear(all)?$/ || $intfstate < 0) {
- if($words eq 'bstaaaclearall' || $intfstate < -1) {
- $story{'id'}=0;
+ if (
+ ($intf_state < 0) || (
+ ($method eq 'POST') && (
+ ($words eq $cmd_clear) ||
+ ($words eq $cmd_clear_all)
+ )
+ )
+ ) {
+ if (
+ ($words eq $cmd_clear_all) ||
+ ($intf_state < -1)
+ ) {
+ $story{'id'} = 0;
}
- $story{'content'}='';
- $story{'lastip'}='0.0.0.0';
- $story{'letter'}='';
- $story{'pass'}='0';
- $story{'state'}='0';
- $turn=0;
- if($ongstate == 0) {
- writeindex(INDEX_PATH,0,0,0);
+ $story{'content'} = '';
+ $story{'lastip' } = '0.0.0.0';
+ $story{'letter' } = '';
+ $story{'pass' } = 0;
+ $story{'state' } = INTF_STATE->{'X'};
+ $turn = 0;
+ if ($ong_state == STATE->{'inactive'}) {
+ write_index(
+ \%state,
+ \%settings,
+ $story{'pass'},
+ $story{'state'},
+ 0 # pause
+ );
}
- writedatafile($storyfile,%story);
+ write_data_file($fh, \%story);
}
- if ($words ne '') {
+ if (($words ne '') && ($method eq 'POST')) {
if (!$turn) {
+ $status = HTTP_STATUS->{'forbidden'};
$message = "It's not your turn.";
}
- else {
- if ($words =~ /^([!"\(\),\.:;\?][ \t]*)?([A-Za-z][A-Za-z'\-]*[A-Za-z']?)([!"\(\),\.:;\? \t][ \t]*)([A-Za-z][A-Za-z'\-]*[A-Za-z']?)([!"\(\),\.:;\?]?[ \t]*)$/) {
+ # TODO: consider allowing non-ASCII letters in words.
+ # (not very important in English language)
+ elsif ($words =~ /^([!"\(\),\.:;\?][ \t]*)?([A-Za-z][A-Za-z'\-]*[A-Za-z']?)([!"\(\),\.:;\? \t][ \t]*)([A-Za-z][A-Za-z'\-]*[A-Za-z']?)([!"\(\),\.:;\?]?[ \t]*)$/) {
+ # we have 2 words
+ $first_letter = lc(substr($2, 0, 1));
+ $second_letter = lc(substr($4, 0, 1));
+ if (
+ ($first_letter ne $last_letter) &&
+ ($last_letter ne '')
+ ) {
+ $status = HTTP_STATUS->{'bad_request'};
+ $message = 'The first word must start with '.uc($last_letter).'.';
+ }
+ elsif ($first_letter eq $second_letter) {
+ $status = HTTP_STATUS->{'bad_request'};
+ $message = 'The second word can\'t also start with '.uc($first_letter).'.';
+ }
+ else {
+ # words are valid
+ # update state
+ $story{'content'} = $story{'content'} . $words."\n";
+ $turn = 0;
+ $story{'lastip'} = $IP;
+ $story{'letter'} = $second_letter;
- $firstletter = lc(substr($2,0,1));
- $secondletter = lc(substr($4,0,1));
- if (($firstletter ne $story{'letter'})&&($story{'letter'} ne '')) {
- $message = 'The first word must start with '.uc($story{'letter'}).'.';
- }
- elsif ($firstletter eq $secondletter) {
- $message = 'The second word can\'t also start with '.uc($firstletter).'.';
+ if ($cgi{'next'} ne '') {
+ # start next game
+ if (split(/\r?\n/,$story{'content'}) >= (STORY_LENGTH-1)) {
+ # store finished game
+ $story_i_path = DATA_STORY_PATH.$story_id;
+ write_data_file($story_i_path, \%story);
+ # init new game
+ $new_story{'id' } = $story_id + 1;
+ $new_story{'letter' } = '';
+ $new_story{'lastip' } = $IP;
+ $new_story{'content'} = '';
+ $new_story{'pass' } = 0;
+ $new_story{'state' } = INTF_STATE->{'X'};
+ # reset hidden interface
+ $intf_state = INTF_STATE->{'X'};
+ $intf_pass = 0;
+ $intf_mode = INTF_STATE->{'X'};
+ $intf_pause= 0;
+ if($ong_state == STATE->{'inactive'}) {
+ # ONG not activated yet; reset index
+ write_index(
+ \%state,
+ \%settings,
+ $intf_pass,
+ $intf_mode,
+ $intf_pause
+ );
+ }
+ # save new game
+ write_data_file($fh, \%new_story);
+ }
+ else {
+ $message = 'To early to finish this wordgame.';
+ write_data_file($fh, \%story);
+ }
}
else {
- $story{'content'} = $story{'content'} . $words."\n";
- $turn = 0;
- $story{'lastip'} = $IP;
- $story{'letter'} = $secondletter;
-
- if ($cgi{'next'} ne '') {
- if (split(/\r?\n/,$story{'content'}) >= (STORY_LENGTH-1)) {
- $storypath = STORY_PATH.$storyid;
- writedatafile($storypath,%story);
- $newstory{'id'} = $storyid + 1;
- $newstory{'letter'}='';
- $newstory{'lastip'}=$IP;
- $newstory{'content'}='';
- $newstory{'pass'}='0';
- $newstory{'state'}='0';
- $intfstate=0;
- $intfpass=0;
- $intfmode=0;
- $intfpause=0;
- if($ongstate == 0) {
- writeindex(INDEX_PATH,0,0,0);
- }
- writedatafile($storyfile,%newstory);
- }
- else {
- $message = 'To early to finish this wordgame.';
- writedatafile($storyfile,%story);
+ # continue the game
+ if ($intf_pass == 1) {
+ # hidden interface was already active; deactivate
+ $intf_pass = 2;
+ $story{'pass'} = 2;
+ if($ong_state == STATE->{'inactive'}) {
+ write_index(
+ \%state,
+ \%settings,
+ $intf_pass,
+ $intf_mode,
+ $intf_pause
+ );
}
}
- else {
- if ($intfpass == 1){
- $intfpass = 2;
- $story{'pass'} = '2';
- if($ongstate == 0) {
- writeindex(INDEX_PATH,2,0,0);
- }
+ elsif(lc($2).' '.lc($4) eq $settings{'unlock'}) {
+ # correct password for the hidden interface!
+ if ($intf_pass != 0) {
+ $message = 'The password has already been used in this story.';
}
- elsif(lc($2).' '.lc($4) eq $settings{'unlock'}) {
- if ($intfpass == 0) {
- if($ongstate == 0) {
- my %framedata = readdatafile(DATA_PATH.0);
- my %default = readdatafile(DEFAULT_PATH);
- my $inpath;
- my $outpath;
-
- $framedata{'ongtime'} = $time;
- $gotolist{'title-0'} = $framedata{'title'};
- $gotolist{'ongtime-0'} = $framedata{'ongtime'};
- writedatafile(DATA_PATH.0,%framedata);
- writedatafile(LIST_PATH,%gotolist);
-
- foreach my $ind (keys %default) {
- unless(defined($framedata{$ind})){
- $framedata{$ind}=$default{$ind};
- }
- }
-
- $inpath = DATA_PATH.sprintf($settings{'frame'},0,$framedata{'ext'});
- $outpath = WWW_PATH.sprintf($settings{'frame'},0,$framedata{'ext'});
-
- if(copy ($inpath, $outpath)) {
- $intfpass = 1;
- $intfstate = 0;
- $intfmode=0;
- $intfpause=0;
- $story{'pass'} = '1';
- $story{'state'} = '0';
- writeindex(INDEX_PATH,1,0,0);
- }
- }
- else {
- $message = "???";
- }
+ elsif ($ong_state != STATE->{'inactive'}) {
+ # ONG already active, nothing to do here
+ $message = "???";
+ }
+ else {
+ # ready to activate?
+ my $r;
+
+ # ONG tape interface
+ $r = ong(
+ 'i', # ID: tape interface
+ $time, # ONG time; not relevant
+ 0, # timer; not relevant
+ 0, # update; not relevant
+ 0, # print
+ \%settings, # not relevant
+ '', # %default; not relevant
+ '', # %frame_data; not relevant
+ '' # $goto_list; not relevant
+ );
+ if ($r) {
+ # ONG CFRT
+ $r = ong(
+ 'c', # ID: CFRT
+ $time, # ONG time; not relevant
+ 0, # timer; not relevant
+ 0, # update; not relevant
+ 0, # print
+ \%settings,
+ '', # %default
+ '', # %frame_data
+ '' # $goto_list; not relevant
+ );
}
- else {
- $message = 'The password has already been used in this story.';
+ if ($r) {
+ # ONG frame 0!
+ $r = ong(
+ 0, # frame ID
+ $time, # ONG time; might get overwritten later
+ 0, # timer
+ 0, # update
+ 0, # print
+ \%settings,
+ '', # %default
+ '', # %frame_data
+ '' # $goto_list
+ );
+ }
+ if($r) {
+ # new state of hidden interface
+ $intf_pass = 1;
+ $intf_state = INTF_STATE->{'X'};
+ $intf_mode = INTF_STATE->{'X'};
+ $intf_pause = 0;
+ $story{'pass'} = 1;
+ $story{'state'} = INTF_STATE->{'X'};
+ write_index(
+ \%state,
+ \%settings,
+ $intf_pass,
+ $intf_mode,
+ $intf_pause
+ );
}
}
- writedatafile($storyfile,%story);
}
+ write_data_file($fh, \%story);
}
}
- else {
- $message = 'Please, two words, not more, not less (some punctuation is allowed).';
- }
- }
- }
- elsif (($cgi{'s'} ne '') && ($intfpass==1) && ($ongstate == 0)) {
- $intfstate = int($cgi{'s'});
- if($intfstate > 63 || $intfstate <0) {
- $intfstate = 0;
}
- $intfmode = $intfstate;
- $intfpause = $intfstate & 1;
- if ($intfpause) {
- $intfmode -= 1;
+ else {
+ $status = HTTP_STATUS->{'bad_request'};
+ $message = 'Please, two words, not more, not less (some punctuation is allowed).';
}
- $story{'state'} = $intfstate;
- writeindex(INDEX_PATH,1,$intfmode,$intfpause);
- writedatafile($storyfile,%story);
}
- @storylines = split(/\r?\n/,$story{'content'});
- if(@storylines & 1) {
+ elsif (
+ ($cgi{'s'} ne '') &&
+ ($intf_pass == 1) &&
+ ($ong_state == STATE->{'inactive'})
+ ) {
+ $intf_state = int($cgi{'s'}) & INTF_STATE->{'mask'};
+ $intf_mode = $intf_state & INTF_STATE->{'mode'};
+ $intf_pause = $intf_state & INTF_STATE->{'||'};
+ $story{'state'} = $intf_state;
+ write_index(
+ \%state,
+ \%settings,
+ $intf_pass,
+ $intf_mode,
+ $intf_pause
+ );
+ write_data_file($fh, \%story);
+ }
+ @story_lines = split(/\r?\n/, $story{'content'});
+ if(@story_lines & 1) {
$turn = !$turn;
}
- close($storyfile);
+ close($fh);
}
-print "Content-type: text/html\n\n";
+if ($status ne '') {
+ print http_header_status($status);
+}
+if ($allow ne '') {
+ print http_header_allow($allow);
+}
+print "Content-type: text/html; charset=UTF-8\n\n";
+
if($method eq 'HEAD') {
exit;
}
-print '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "">'."\n";
-print '<html lang="en"><head>'."\n";
-print '<title>Two words • '.WEBSITE_NAME.'</title>'."\n";
-print '<meta http-equiv="Content-type" content="text/html; charset=UTF-8">'."\n";
-print '<link rel="icon" type="image/png" href="'.FAVICON_PATH.'">'."\n";
-print '<link rel="stylesheet" href="'.CSS_PATH.'">'."\n";
-print '</head><body>'."\n";
-print '<a href="/"><img id="botmlogo" src="'.LOGO_PATH.'" alt="'.WEBSITE.'"></a>'."\n";
-print '<div id="all">'."\n";
+my $max_page = int(($story_id - FIRSTPAGE_LENGTH - 1) / PAGE_LENGTH) + 1;
+my $newer_available = ($page > 0);
+my $older_available = ($page < $max_page);
+my $show_intf = ($intf_pass == 1) && ($ong_state == STATE->{'inactive'});
+my $id_start =
+ $story_id-1 -(
+ ($page == 0) ? 0 : (
+ (($page-1) * PAGE_LENGTH ) + FIRSTPAGE_LENGTH
+ )
+ );
+my $id_stop = $story_id-1 - (($page*PAGE_LENGTH) + FIRSTPAGE_LENGTH);
+if ($id_stop < 0) {
+ $id_stop = -1;
+}
+
+my $bsta_url = CGI_PATH;
+my $twowords_url = CGI_2WORDS_PATH;
+my $newer_url;
+my $older_url;
+my $oldest_url;
+my $newest_url = merge_url(
+ {'path' => $twowords_url},
+ {'path' => 0}
+);
+if ($newer_available) {
+ $newer_url = merge_url(
+ {'path' => $twowords_url},
+ {'path' => $page-1}
+ );
+}
+if ($older_available) {
+ $older_url = merge_url(
+ {'path' => $twowords_url},
+ {'path' => $page+1}
+ );
+ $oldest_url = merge_url(
+ {'path' => $twowords_url},
+ {'path' => $max_page}
+ );
+}
+my $button_4_url = merge_url(
+ {'path' => $twowords_url},
+ {'query' => {'s' => (INTF_STATE->{'>'} | $intf_pause)}}
+);
+my $button_3_url = merge_url(
+ {'path' => $twowords_url},
+ {'query' => {'s' => (INTF_STATE->{'<<'} | $intf_pause)}}
+);
+my $button_2_url = merge_url(
+ {'path' => $twowords_url},
+ {'query' => {'s' => (INTF_STATE->{'>>'} | $intf_pause)}}
+);
+my $button_1_url = merge_url(
+ {'path' => $twowords_url},
+ {'query' => {'s' => INTF_STATE->{'X'}}}
+);
+my $button_0_url = merge_url(
+ {'path' => $twowords_url},
+ {'query' => {'s' => ($intf_pause ? $intf_mode : ($intf_mode | INTF_STATE->{'||'}))}}
+);
+my $button_5_img = merge_url(
+ {'path' => CGI_PATH()},
+ {'path' => 'intf-20.gif'}
+);
+my $button_4_img = merge_url(
+ {'path' => CGI_PATH()},
+ {'path' => 'intf-10'.(($intf_mode == INTF_STATE->{'>'}) ? '_' : '').'.gif'}
+);
+my $button_3_img = merge_url(
+ {'path' => CGI_PATH()},
+ {'path' => 'intf-08'.(($intf_mode == INTF_STATE->{'<<'}) ? '_' : '').'.gif'}
+);
+my $button_2_img = merge_url(
+ {'path' => CGI_PATH()},
+ {'path' => 'intf-04'.(($intf_mode == INTF_STATE->{'>>'}) ? '_' : '').'.gif'}
+);
+my $button_1_img = merge_url(
+ {'path' => CGI_PATH()},
+ {'path' => 'intf-02.gif'}
+);
+my $button_0_img = merge_url(
+ {'path' => CGI_PATH()},
+ {'path' => 'intf-01'.($intf_pause ? '_' : '').'.gif'}
+);
+my $intf_img_id = '';
+if ($intf_state == INTF_STATE->{'>'}) {
+ $intf_img_id = '_10'
+}
+elsif ($intf_mode == INTF_STATE->{'<<'}) {
+ $intf_img_id = '_08'
+}
+elsif ($intf_mode == INTF_STATE->{'>>'}) {
+ $intf_img_id = '_04'
+}
+my $intf_img = merge_url(
+ {'path' => CGI_PATH()},
+ {'path' => 'intf-00'.$intf_img_id.'.gif'}
+);
+
+my $_bsta_url = html_entity_encode_dec($bsta_url , 1);
+my $_twowords_url = html_entity_encode_dec($twowords_url , 1);
+my $_newest_url = html_entity_encode_dec($newest_url , 1);
+my $_newer_url = html_entity_encode_dec($newer_url , 1);
+my $_older_url = html_entity_encode_dec($older_url , 1);
+my $_oldest_url = html_entity_encode_dec($oldest_url , 1);
+my $_button_4_url = html_entity_encode_dec($button_4_url , 1);
+my $_button_3_url = html_entity_encode_dec($button_3_url , 1);
+my $_button_2_url = html_entity_encode_dec($button_2_url , 1);
+my $_button_1_url = html_entity_encode_dec($button_1_url , 1);
+my $_button_0_url = html_entity_encode_dec($button_0_url , 1);
+my $_button_5_img = html_entity_encode_dec($button_5_img , 1);
+my $_button_4_img = html_entity_encode_dec($button_4_img , 1);
+my $_button_3_img = html_entity_encode_dec($button_3_img , 1);
+my $_button_2_img = html_entity_encode_dec($button_2_img , 1);
+my $_button_1_img = html_entity_encode_dec($button_1_img , 1);
+my $_button_0_img = html_entity_encode_dec($button_0_img , 1);
+my $_intf_img = html_entity_encode_dec($intf_img , 1);
+my $_message = html_entity_encode_dec($message , 1);
+my $_website_name = html_entity_encode_dec(WEBSITE_NAME(), 1);
+
+print_html_start(\*STDOUT);
+print_html_head_start(\*STDOUT);
+
+
+print ' <title>Two words • '.$_website_name.'</title>'."\n";
+print ' <link rel="start" href="'.$_oldest_url.'">'."\n";
+if ($older_available) {
+ print ' <link rel="prev" href="'.$_older_url.'">'."\n";
+}
+if ($newer_available) {
+ print ' <link rel="next" href="'.$_newer_url.'">'."\n";
+}
+
+print_html_head_end(\*STDOUT);
+print_html_body_start(\*STDOUT);
-print '<div id="inst" class="ins">'."\n";
+print ' <div id="inst" class="ins">'."\n";
-print '<div id="title">'."\n";
-print '<H1 id="titletext">Two words</H1>'."\n";
-print '</div>'."\n";
+print ' <div id="title">'."\n";
+print ' <h1 id="titletext">Two words</h1>'."\n";
+print ' </div>'."\n";
if ($page == 0) {
- print '<div id="storypuzzle">'."\n";
- for (my $i = 0; $i < @storylines; ++$i){
- print '<span class="'.($turn?'ni':'br').'">'.entityencode($storylines[$i]).'</span>'."\n";
+ print ' <div id="storypuzzle">'."\n";
+ for (my $i = 0; $i < @story_lines; ++$i) {
+ print ' <span class="'.($turn ? 'ni':'br').'">'.html_entity_encode_dec($story_lines[$i], 1).'</span>'."\n";
$turn = !$turn;
}
- print '</div>'."\n";
+ print ' </div>'."\n";
- print '<div id="command">'."\n";
+ print ' <div id="command">'."\n";
if ($message ne '') {
- print '<span class="br">'.entityencode($message).'</span>'."\n";
+ print ' <span class="br">'.$_message.'</span>'."\n";
}
if ($turn) {
- print '<form method="post" action="'.TWOWORDS_PATH.'">'."\n";
+ print ' <form method="post" action="'.$_twowords_url.'">'."\n";
if ($message eq '') {
if ($story{"content"} eq '') {
- print 'Two words, please:<br>'."\n";
+ print ' Two words, please:<br>'."\n";
}
else {
- print 'Please continue, two words:<br>'."\n";
+ print ' Please continue, two words:<br>'."\n";
}
}
- print '<input class="intx" type="text" name="words">'."\n";
- print '<input class="inbt" type="submit" value="enter">'."\n";
- if(@storylines >= (STORY_LENGTH-1)) {
- print '<input class="inbt" type="submit" name="next" value="enter and then start a new one">'."\n";
+ print ' <input class="intx" type="text" name="words">'."\n";
+ print ' <input class="inbt" type="submit" value="enter">'."\n";
+ if (@story_lines >= (STORY_LENGTH-1)) {
+ print ' <input class="inbt" type="submit" name="next" value="enter and then start a new one">'."\n";
}
- print '</form>'."\n";
+ print ' </form>'."\n";
}
else {
if ($message eq '') {
- print 'Wait for it.'."\n";
+ print ' Wait for it.'."\n";
}
}
- print '</div>'."\n";
+ print ' </div>'."\n";
}
elsif ($message ne '') {
- print '<div id="command">'."\n";
- print '<span class="br">'.entityencode($message).'</span>'."\n";
- print '</div>'."\n";
+ print ' <div id="command">'."\n";
+ print ' <span class="br">'.$_message.'</span>'."\n";
+ print ' </div>'."\n";
}
+print ' </div>'."\n";
-if(($intfpass == 1) && ($ongstate == 0)) {
- print '</div><div id="framespace">'."\n";
- print '<table id="intftable" cellspacing="0" cellpadding="0"><tr class="intf">'."\n";
-
- print '<td colspan="6" class="intf"><img src="'.CGI_PATH.'intf-00';
- if ($intfmode == 4) {
- print '_04';
- }
- elsif ($intfmode == 8) {
- print '_08';
- }
- elsif ($intfstate == 16) {
- print '_10';
- }
- print'.gif" alt="" class="intf"></td>'."\n";
+if ($show_intf) {
+ print ' <div id="framespace">'."\n";
+ print ' <table id="intftable" cellspacing="0" cellpadding="0">'."\n";
+ print ' <tr class="intf">'."\n";
+ print ' <td colspan="6" class="intf"><img src="'.$_intf_img.'" alt="" class="intf"></td>'."\n";
+ print ' </tr>'."\n";
- print '</tr><tr class="intf">'."\n";
- print '<td class="intf"><img src="'.CGI_PATH.'intf-20.gif" alt="o" class="intf"></td>'."\n";
- print '<td class="intf"><a href="'.CGI_PATH.'2words?s='.($intfpause?17:16).'"><img src="'.CGI_PATH.'intf-10'.(($intfmode==16)?'_':'').'.gif" class="intf" alt=">"></td>'."\n";
- print '<td class="intf"><a href="'.CGI_PATH.'2words?s='.($intfpause?9:8).'"><img src="'.CGI_PATH.'intf-08'.(($intfmode==8)?'_':'').'.gif" class="intf" alt="<<"></td>'."\n";
- print '<td class="intf"><a href="'.CGI_PATH.'2words?s='.($intfpause?5:4).'"><img src="'.CGI_PATH.'intf-04'.(($intfmode==4)?'_':'').'.gif" class="intf" alt=">>"></td>'."\n";
- print '<td class="intf"><a href="'.CGI_PATH.'2words?s='.($intfpause?0:0).'"><img src="'.CGI_PATH.'intf-02.gif" class="intf" alt="^"></td>'."\n";
- print '<td class="intf"><a href="'.CGI_PATH.'2words?s='.($intfpause?$intfmode:$intfmode+1).'"><img src="'.CGI_PATH.'intf-01'.($intfpause?'_':'').'.gif" class="intf" alt="||"></td>'."\n";
- print '</tr></table>'."\n";
+ print ' <tr class="intf">'."\n";
+ print ' <td class="intf"><img src="'.$_button_5_img.'" alt="o" class="intf"></td>'."\n";
+ print ' <td class="intf"><a href="'.$_button_4_url.'"><img src="'.$_button_4_img.'" class="intf" alt=">"></a></td>'."\n";
+ print ' <td class="intf"><a href="'.$_button_3_url.'"><img src="'.$_button_3_img.'" class="intf" alt="<<"></a></td>'."\n";
+ print ' <td class="intf"><a href="'.$_button_2_url.'"><img src="'.$_button_2_img.'" class="intf" alt=">>"></a></td>'."\n";
+ print ' <td class="intf"><a href="'.$_button_1_url.'"><img src="'.$_button_1_img.'" class="intf" alt="^"></a></td>'."\n";
+ print ' <td class="intf"><a href="'.$_button_0_url.'"><img src="'.$_button_0_img.'" class="intf" alt="||"></a></td>'."\n";
+ print ' </tr>'."\n";
+ print ' </table>'."\n";
+ print ' </div>'."\n";
}
-print '</div><div id="insb" class="ins">'."\n";
-print '<div id="undertext">'."\n";
-for (my $i = $storyid-1-(($page!=0)?((($page-1)*PAGE_LENGTH)+FIRSTPAGE_LENGTH):0); $i > ($storyid-1-($page*PAGE_LENGTH)- FIRSTPAGE_LENGTH) && $i >= 0; --$i) {
- $storypath = STORY_PATH.$i;
- %newstory = readdatafile($storypath);
- print '<p class="'.(($i&1)?'br':'ni').'" id="s'.$i.'">'.entityencode($newstory{'content'}).'</p>'."\n";
+print ' <div id="insb" class="ins">'."\n";
+
+print ' <div id="undertext">'."\n";
+for (my $i = $id_start; $i > $id_stop; --$i) {
+ $story_i_path = DATA_STORY_PATH.$i;
+ %new_story = read_data_file($story_i_path);
+ print ' <p class="'.(($i&1)?'br':'ni').'" id="s'.$i.'">'.html_entity_encode_dec($new_story{'content'}).'</p>'."\n";
}
+print ' </div>'."\n";
-print '</div>'."\n";
-print '<div id="underlinks">'."\n";
-print '<a href="'.CGI_PATH.'">BSTA</a> | <a href="'.TWOWORDS_PATH.'">Once again</a>';
-if(($storyid - ($page*PAGE_LENGTH)) - FIRSTPAGE_LENGTH > 0) {
- print ' | <a href="'.TWOWORDS_PATH.'/'.($page+1).'">Before</a>';
+print ' <div id="underlinks">'."\n";
+print ' <a href="'.$_bsta_url.'">BSTA</a> |'."\n";
+print ' <a href="'.$_twowords_url.'">Once again</a>';
+if ($older_available) {
+ print ' |'."\n";
+ print ' <a href="'.$_older_url.'">Before</a>';
}
-if($page > 0) {
- print ' | <a href="'.TWOWORDS_PATH.'/'.($page-1).'">Unbefore</a>';
+if ($newer_available) {
+ print ' |'."\n";
+ print ' <a href="'.$newer_url.'">Unbefore</a>';
}
-if(($storyid - ($page*PAGE_LENGTH)) - FIRSTPAGE_LENGTH > 0) {
- print ' | <a href="'.TWOWORDS_PATH.'/'.(int(($storyid - FIRSTPAGE_LENGTH - 1) / PAGE_LENGTH) + 1).'">Initially</a>';
+if ($older_available) {
+ print ' |'."\n";
+ print '<a href="'.$_oldest_url.'">Initially</a>';
}
if($turn) {
- print ' | (Entering words here is irreversible. Your actions might be remembered forever. So please be reasonable.)';
+ print ' |'."\n";
+ print ' (Entering words here is irreversible. Your actions might be remembered forever. So please be reasonable.)';
}
print "\n";
-print '</div>'."\n";
-
-print '</div>'."\n";
-print '</div>'."\n";
-print '<a href="/" class="cz">'.WEBSITE.'</a>'."\n";
-print '</body></html>'."\n";
-
-sub writeindex {
- (my $indexpath, my $pass, my $mode, my $pause) = @_;
- my $indexfile;
- my $indexof;
-
- if(ref($indexpath)) {
- $indexfile=$indexpath;
- unless (seek($indexfile, 0, 0)) {
- return 0;
- }
- }
- else {
- unless (open ($indexfile, ">", $indexpath)) {
- return 0;
- }
- }
-
- $indexof = CGI_PATH;
- $indexof =~ s/\/$//g;
-
- my %coin = readdatafile(COIN_PATH);
-
- if ($pass != 1) {
- print $indexfile '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">'."\n";
- print $indexfile '<html>'."\n";
- print $indexfile ' <head>'."\n";
- print $indexfile ' <title>Index of '.$indexof.'</title>'."\n";
- print $indexfile ' </head>'."\n";
- print $indexfile ' <body>'."\n";
- print $indexfile '<h1>Index of '.$indexof.'</h1>'."\n";
- print $indexfile '<table><tr><th><img src="/icons/blank.gif" alt="[ICO]"></th><th><a href="?C=N;O=D">Name</a></th><th><a href="?C=M;O=A">Last modified</a></th><th><a href="?C=S;O=A">Size</a></th><th><a href="?C=D;O=A">Description</a></th></tr><tr><th colspan="5"><hr></th></tr>'."\n";
- print $indexfile '<tr><td valign="top"><img src="/icons/back.gif" alt="[DIR]"></td><td><a href="/">Parent Directory</a></td><td> </td><td align="right"> - </td><td> </td></tr>'."\n";
- print $indexfile '<tr><td valign="top"><img src="/icons/folder.gif" alt="[DIR]"></td><td><a href="2words/">2words/</a></td><td align="right">'.INTF_DATE.' </td><td align="right"> - </td><td> </td></tr>'."\n";
- print $indexfile '<tr><td valign="top"><img src="/icons/folder.gif" alt="[DIR]"></td><td><a href="coin/">coin/</a></td><td align="right">'.COIN_DATE.' </td><td align="right"> - </td><td> Coincidence </td></tr>'."\n";
- print $indexfile '<tr><th colspan="5"><hr></th></tr>'."\n";
- print $indexfile '</table>'."\n";
- print $indexfile '<address>Apache/2.2.22 (Debian) Server at '.WEBSITE.' Port 80</address>'."\n";
- print $indexfile '</body></html>'."\n";
- }
- else {
- my %framedata;
- my %nextframedata;
- my %default;
-
- %framedata = readdatafile(DATA_PATH.0);
- %nextframedata = readdatafile(DATA_PATH.1);
- %default=readdatafile(DEFAULT_PATH);
-
- # if($mode == 16 && $pause) {
- # $framedata{'ongtime'} = $time;
- # writedatafile(DATA_PATH.0,%framedata);
- # }
-
- foreach my $ind (keys %default) {
- unless(defined($framedata{$ind})){
- $framedata{$ind}=$default{$ind};
- }
- unless(defined($nextframedata{$ind})){
- $nextframedata{$ind}=$default{$ind};
- }
- }
-
-
- print $indexfile '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "">'."\n";
- print $indexfile '<html lang="en"><head>'."\n";
- print $indexfile '<title>';
- if($mode == 8 || $mode == 4) {
- print $indexfile 'Index of';
- }
- elsif($mode == 16) {
- print $indexfile entityencode($settings{'story'});
- if($pause) {
- print $indexfile ' • '.WEBSITE_NAME;
- }
- }
- else {
- print $indexfile 'Index of '.$indexof;
- }
- print $indexfile '</title>'."\n";
- print $indexfile '<meta http-equiv="Content-type" content="text/html; charset=UTF-8">'."\n";
- print $indexfile '<link rel="icon" type="image/png" href="'.FAVICON_PATH.'">'."\n";
- print $indexfile '<link rel="stylesheet" href="'.CSS_PATH.'">'."\n";
- print $indexfile '</head><body>'."\n";
- print $indexfile '<a href="/"><img id="botmlogo" src="'.LOGO_PATH.'" alt="'.WEBSITE.'"></a>'."\n";
- print $indexfile '<div id="all">'."\n";
-
- print $indexfile '<div id="inst" class="ins">'."\n";
-
- print $indexfile '<div id="title">'."\n";
- print $indexfile '<H1 id="titletext">';
- if($mode == 8 || $mode == 4) {
- print $indexfile 'Index of';
- }
- elsif($mode == 16) {
- print $indexfile entityencode($settings{'story'});
- }
- else {
- print $indexfile 'Index of '.$indexof;
- }
- print $indexfile '</H1>'."\n";
- print $indexfile '</div>'."\n";
-
- print $indexfile '</div><div id="framespace">'."\n";
- print $indexfile '<img src="'.CGI_PATH;
- if($mode == 8) {
- print $indexfile 'intf-ll.gif'
- }
- elsif($mode == 4) {
- print $indexfile 'intf-pp.gif'
- }
- elsif($mode == 16) {
- if($pause) {
- print $indexfile sprintf($settings{'frame'},0,$framedata{'ext'}).'" title="'.entityencode($framedata{'title'});
- }
- else {
- print $indexfile 'intf-tr.gif'
- }
- }
- else {
- print $indexfile 'intf-kw.gif'
- }
- print $indexfile '" alt="0" id="frame">'."\n";
-
- print $indexfile '</div><div id="insb" class="ins">'."\n";
- print $indexfile '<div id="undertext">'."\n";
- if($mode == 8 || $mode == 4) {
- print $indexfile '<img src="/icons/back.gif" alt="[DIR]"> <a href="/">Parent Directory</a><br><img src="/icons/folder.gif" alt="[DIR]"> <a href="#">yyyyb/</a>'."\n";
- }
- elsif ($mode == 16) {
- if($pause) {
- print $indexfile bb2ht($framedata{'content'})."\n";
- }
- else {
- print $indexfile "...\n";
- }
- }
- else {
- print $indexfile '<img src="/icons/back.gif" alt="[DIR]"> <a href="/">Parent Directory</a><br>'."\n";
- print $indexfile '<img src="/icons/folder.gif" alt="[DIR]"> <a href="2words/">2words/</a> '.INTF_DATE.' - <br>'."\n";
- print $indexfile '<img src="/icons/folder.gif" alt="[DIR]"> <a href="coin/">coin/</a> '.COIN_DATE.' - '.entityencode($coin{'server'})."\n";
- }
- print $indexfile '</div>'."\n";
-
- print $indexfile '<div id="command">'."\n";
- if ($mode == 8) {
- print $indexfile '[<span class="br">EE</span>:<span class="br">EE</span>:<span class="br">EE</span>]'."\n";
- }
- elsif ($mode == 4) {
- print $indexfile '[<span class="ni">EE</span>:<span class="ni">EE</span>:<span class="ni">EE</span>]'."\n";
- }
- elsif ($mode == 16) {
- if($pause) {
- print $indexfile '[<span class="br">00</span>:<span class="br">00</span>:<span class="br">00</span>]<br>'."\n";
- print $indexfile '><a href="'.VIEWER_PATH.'/1">'.entityencode($nextframedata{'title'}).'</a>'."\n";
- }
- else {
- print $indexfile '[<span class="ni">--</span>:<span class="ni">--</span>:<span class="ni">--</span>]<br>'."\n";
- print $indexfile '>...<span class="inp">_</span>'."\n";
- }
- }
- else {
- }
- print $indexfile '</div>'."\n";
-
- print $indexfile '</div>'."\n";
-
- print $indexfile '</div>'."\n";
- print $indexfile '<a href="/" class="cz">'.WEBSITE.'</a>'."\n";
-
- print $indexfile '</body></html>'."\n";
- }
-
- unless (ref($indexpath)) {
- close ($indexfile);
- }
- else {
- truncate ($indexfile , tell($indexfile));
- }
-
- return 1;
-}
+print ' </div>'."\n";
+
+print ' </div>'."\n";
+
+print_html_body_end(\*STDOUT, $ong_state == STATE->{'inactive'});
+print_html_end(\*STDOUT);