diff --git a/.editorconfig b/.editorconfig
index 9cebe6d6d..cd3315bd8 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -9,7 +9,7 @@ end_of_line = lf
insert_final_newline = true
# 4 space indentation
-[*.{c,h,lua}]
+[*.{c,h,lua,py}]
indent_style = space
indent_size = 4
diff --git a/process/CMakeLists.txt b/process/CMakeLists.txt
index 174fae694..0ffb7b4a2 100644
--- a/process/CMakeLists.txt
+++ b/process/CMakeLists.txt
@@ -1,5 +1,6 @@
install(PROGRAMS create-orders backup-eressea run-turn send-zip-report
send-bz2-report compress.py compress.sh epasswd.py orders-process
+process-orders.py accept-orders.py
checkpasswd.py sendreport.sh sendreports.sh orders-accept DESTINATION bin)
install(DIRECTORY cron/ DESTINATION bin USE_SOURCE_PERMISSIONS
diff --git a/process/accept-orders.py b/process/accept-orders.py
new file mode 100755
index 000000000..3bf9ea965
--- /dev/null
+++ b/process/accept-orders.py
@@ -0,0 +1,377 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+from email.Utils import parseaddr
+from email.Parser import Parser
+import os
+import os.path
+import ConfigParser
+from re import compile, IGNORECASE
+from stat import ST_MTIME
+from string import upper, split, replace
+import logging
+import sys
+import subprocess
+from sys import stdin
+from time import ctime, sleep, time
+from socket import gethostname
+from rfc822 import parsedate_tz, mktime_tz
+
+if 'ERESSEA' in os.environ:
+ dir = os.environ['ERESSEA']
+elif 'HOME' in os.environ:
+ dir = os.path.join(os.environ['HOME'], 'eressea')
+else: # WTF? No HOME?
+ dir = "/home/eressea/eressea"
+if not os.path.isdir(dir):
+ print "please set the ERESSEA environment variable to the install path"
+ sys.exit(1)
+rootdir = dir
+
+game = int(sys.argv[1])
+gamedir = os.path.join(rootdir, "game-%d" % (game, ))
+frommail = 'eressea-server@kn-bremen.de'
+gamename = 'Eressea'
+sender = '%s Server <%s>' % (gamename, frommail)
+
+inifile = os.path.join(gamedir, 'eressea.ini')
+if not os.path.exists(inifile):
+ print "no such file: " . inifile
+else:
+ config = ConfigParser.ConfigParser()
+ config.read(inifile)
+ if config.has_option('game', 'email'):
+ frommail = config.get('game', 'email')
+ if config.has_option('game', 'name'):
+ gamename = config.get('game', 'name')
+ if config.has_option('game', 'sender'):
+ sender = config.get('game', 'sender')
+ else:
+ sender = "%s Server <%s>" % (gamename, frommail)
+ config = None
+prefix = 'turn-'
+hostname = gethostname()
+orderbase = "orders.dir"
+sendmail = True
+# maximum number of reports per sender:
+maxfiles = 20
+# write headers to file?
+writeheaders = True
+# reject all html email?
+rejecthtml = True
+
+def unlock_file(filename):
+ try:
+ os.unlink(filename+".lock")
+ except:
+ print "could not unlock %s.lock, file not found" % filename
+
+def lock_file(filename):
+ i = 0
+ wait = 1
+ if not os.path.exists(filename):
+ file=open(filename, "w")
+ file.close()
+ while True:
+ try:
+ os.symlink(filename, filename+".lock")
+ return
+ except:
+ i = i+1
+ if i == 5: unlock_file(filename)
+ sleep(wait)
+ wait = wait*2
+
+messages = {
+ "multipart-en" :
+ "ERROR: The orders you sent contain no plaintext. " \
+ "The Eressea server cannot process orders containing HTML " \
+ "or invalid attachments, which are the reasons why this " \
+ "usually happens. Please change the settings of your mail " \
+ "software and re-send the orders.",
+
+ "multipart-de" :
+ "FEHLER: Die von dir eingeschickte Mail enthält keinen " \
+ "Text. Evtl. hast Du den Zug als HTML oder als anderweitig " \
+ "ungültig formatierte Mail ingeschickt. Wir können ihn " \
+ "deshalb nicht berücksichtigen. Schicke den Zug nochmals " \
+ "als reinen Text ohne Formatierungen ein.",
+
+ "maildate-de":
+ "Es erreichte uns bereits ein Zug mit einem späteren " \
+ "Absendedatum (%s > %s). Entweder ist deine " \
+ "Systemzeit verstellt, oder ein Zug hat einen anderen Zug von " \
+ "dir auf dem Transportweg überholt. Entscheidend für die " \
+ "Auswertungsreihenfolge ist das Absendedatum, d.h. der Date:-Header " \
+ "deiner Mail.",
+
+ "maildate-en":
+ "The server already received an order file that was sent at a later " \
+ "date (%s > %s). Either your system clock is wrong, or two messages have " \
+ "overtaken each other on the way to the server. The order of " \
+ "execution on the server is always according to the Date: header in " \
+ "your mail.",
+
+ "nodate-en":
+ "Your message did not contain a valid Date: header in accordance with RFC2822.",
+
+ "nodate-de":
+ "Deine Nachricht enthielt keinen gueltigen Date: header nach RFC2822.",
+
+ "error-de":
+ "Fehler",
+
+ "error-en":
+ "Error",
+
+ "warning-de":
+ "Warnung",
+
+ "warning-en":
+ "Warning",
+
+ "subject-de":
+ "Befehle angekommen",
+
+ "subject-en":
+ "orders received"
+}
+
+# return 1 if addr is a valid email address
+def valid_email(addr):
+ rfc822_specials = '/()<>@,;:\\"[]'
+ # First we validate the name portion (name@domain)
+ c = 0
+ while c < len(addr):
+ if addr[c] == '"' and (not c or addr[c - 1] == '.' or addr[c - 1] == '"'):
+ c = c + 1
+ while c < len(addr):
+ if addr[c] == '"': break
+ if addr[c] == '\\' and addr[c + 1] == ' ':
+ c = c + 2
+ continue
+ if ord(addr[c]) < 32 or ord(addr[c]) >= 127: return 0
+ c = c + 1
+ else: return 0
+ if addr[c] == '@': break
+ if addr[c] != '.': return 0
+ c = c + 1
+ continue
+ if addr[c] == '@': break
+ if ord(addr[c]) <= 32 or ord(addr[c]) >= 127: return 0
+ if addr[c] in rfc822_specials: return 0
+ c = c + 1
+ if not c or addr[c - 1] == '.': return 0
+
+ # Next we validate the domain portion (name@domain)
+ domain = c = c + 1
+ if domain >= len(addr): return 0
+ count = 0
+ while c < len(addr):
+ if addr[c] == '.':
+ if c == domain or addr[c - 1] == '.': return 0
+ count = count + 1
+ if ord(addr[c]) <= 32 or ord(addr[c]) >= 127: return 0
+ if addr[c] in rfc822_specials: return 0
+ c = c + 1
+ return count >= 1
+
+# return the replyto or from address in the header
+def get_sender(header):
+ replyto = header.get("Reply-To")
+ if replyto is None:
+ replyto = header.get("From")
+ if replyto is None: return None
+ x = parseaddr(replyto)
+ return x[1]
+
+# return first available filename basename,[0-9]+
+def available_file(dirname, basename):
+ ver = 0
+ maxdate = 0
+ filename = "%s/%s,%s,%d" % (dirname, basename, hostname, ver)
+ while os.path.exists(filename):
+ maxdate = max(os.stat(filename)[ST_MTIME], maxdate)
+ ver = ver + 1
+ filename = "%s/%s,%s,%d" % (dirname, basename, hostname, ver)
+ if ver >= maxfiles:
+ return None, None
+ return maxdate, filename
+
+def formatpar(string, l=76, indent=2):
+ words = split(string)
+ res = ""
+ ll = 0
+ first = 1
+
+ for word in words:
+ if first == 1:
+ res = word
+ first = 0
+ ll = len(word)
+ else:
+ if ll + len(word) > l:
+ res = res + "\n"+" "*indent+word
+ ll = len(word) + indent
+ else:
+ res = res+" "+word
+ ll = ll + len(word) + 1
+
+ return res+"\n"
+
+def store_message(message, filename):
+ outfile = open(filename, "w")
+ outfile.write(message.as_string())
+ outfile.close()
+ return
+
+def write_part(outfile, part):
+ charset = part.get_content_charset()
+ payload = part.get_payload(decode=True)
+
+ if charset is None:
+ charset = "latin1"
+ try:
+ msg = payload.decode(charset, "ignore")
+ except:
+ msg = payload
+ charset = None
+ try:
+ utf8 = msg.encode("utf-8", "ignore")
+ outfile.write(utf8)
+ except:
+ outfile.write(msg)
+ return False
+ outfile.write("\n");
+ return True
+
+def copy_orders(message, filename, sender):
+ # print the header first
+ if writeheaders:
+ from os.path import split
+ dirname, basename = split(filename)
+ dirname = dirname + '/headers'
+ if not os.path.exists(dirname): os.mkdir(dirname)
+ outfile = open(dirname + '/' + basename, "w")
+ for name, value in message.items():
+ outfile.write(name + ": " + value + "\n")
+ outfile.close()
+
+ found = False
+ outfile = open(filename, "w")
+ if message.is_multipart():
+ for part in message.get_payload():
+ content_type = part.get_content_type()
+ logger.debug("found content type %s for %s" % (content_type, sender))
+ if content_type=="text/plain":
+ if write_part(outfile, part):
+ found = True
+ else:
+ charset = part.get_content_charset()
+ logger.error("could not write text/plain part (charset=%s) for %s" % (charset, sender))
+
+ else:
+ if write_part(outfile, message):
+ found = True
+ else:
+ charset = message.get_content_charset()
+ logger.error("could not write text/plain message (charset=%s) for %s" % (charset, sender))
+ outfile.close()
+ return found
+
+# create a file, containing:
+# game=0 locale=de file=/path/to/filename email=rcpt@domain.to
+def accept(game, locale, stream, extend=None):
+ global rootdir, orderbase, gamedir, gamename, sender
+ if extend is not None:
+ orderbase = orderbase + ".pre-" + extend
+ savedir = os.path.join(gamedir, orderbase)
+ # check if it's one of the pre-sent orders.
+ # create the save-directories if they don't exist
+ if not os.path.exists(gamedir): os.mkdir(gamedir)
+ if not os.path.exists(savedir): os.mkdir(savedir)
+ # parse message
+ message = Parser().parse(stream)
+ email = get_sender(message)
+ logger = logging.getLogger(email)
+ # write syslog
+ if email is None or valid_email(email)==0:
+ logger.warning("invalid email address: " + str(email))
+ return -1
+ logger.info("received orders from " + email)
+ # get an available filename
+ lock_file(gamedir + "/orders.queue")
+ maxdate, filename = available_file(savedir, prefix + email)
+ if filename is None:
+ logger.warning("more than " + str(maxfiles) + " orders from " + email)
+ return -1
+ # copy the orders to the file
+ text_ok = copy_orders(message, filename, email)
+ unlock_file(gamedir + "/orders.queue")
+
+ warning, msg, fail = None, "", False
+ maildate = message.get("Date")
+ if maildate != None:
+ turndate = mktime_tz(parsedate_tz(maildate))
+ os.utime(filename, (turndate, turndate))
+ logger.debug("mail date is '%s' (%d)" % (maildate, turndate))
+ if False and turndate < maxdate:
+ logger.warning("inconsistent message date " + email)
+ warning = " (" + messages["warning-" + locale] + ")"
+ msg = msg + formatpar(messages["maildate-" + locale] % (ctime(maxdate),ctime(turndate)), 76, 2) + "\n"
+ else:
+ logger.warning("missing message date " + email)
+ warning = " (" + messages["warning-" + locale] + ")"
+ msg = msg + formatpar(messages["nodate-" + locale], 76, 2) + "\n"
+
+ if not text_ok:
+ warning = " (" + messages["error-" + locale] + ")"
+ msg = msg + formatpar(messages["multipart-" + locale], 76, 2) + "\n"
+ logger.warning("rejected - no text/plain in orders from " + email)
+ os.unlink(filename)
+ savedir = savedir + "/rejected"
+ if not os.path.exists(savedir): os.mkdir(savedir)
+ lock_file(gamedir + "/orders.queue")
+ maxdate, filename = available_file(savedir, prefix + email)
+ store_message(message, filename)
+ unlock_file(gamedir + "/orders.queue")
+ fail = True
+
+ if sendmail and warning is not None:
+ subject = gamename + " " + messages["subject-"+locale] + warning
+ print("mail " + subject)
+ ps = subprocess.Popen(['mutt', '-s', subject, email], stdin=subprocess.PIPE)
+ ps.communicate(msg)
+
+ if not sendmail:
+ print text_ok, fail, email
+ print filename
+
+ if not fail:
+ lock_file(gamedir + "/orders.queue")
+ queue = open(gamedir + "/orders.queue", "a")
+ queue.write("email=%s file=%s locale=%s game=%s\n" % (email, filename, locale, game))
+ queue.close()
+ unlock_file(gamedir + "/orders.queue")
+
+ logger.info("done - accepted orders from " + email)
+
+ return 0
+
+# the main body of the script:
+try:
+ os.mkdir(os.path.join(rootdir, 'log'))
+except:
+ pass # already exists?
+LOG_FILENAME=os.path.join(rootdir, 'log/orders.log')
+logging.basicConfig(level=logging.DEBUG, filename=LOG_FILENAME)
+logger = logging
+delay = None # TODO: parse the turn delay
+locale = sys.argv[2]
+infile = stdin
+if len(sys.argv)>3:
+ infile = open(sys.argv[3], "r")
+retval = accept(game, locale, infile, delay)
+if infile!=stdin:
+ infile.close()
+sys.exit(retval)
diff --git a/process/orders-accept b/process/orders-accept
index 2f8f0bd29..266424a41 100755
--- a/process/orders-accept
+++ b/process/orders-accept
@@ -1,378 +1,5 @@
-#!/usr/bin/env python
-# -*- coding: iso-8859-1 -*-
+#!/bin/sh
+SCRIPT=$(readlink -f $0)
+cd $(dirname $SCRIPT)
+python accept-orders.py "$@"
-from email.Utils import parseaddr
-from email.Parser import Parser
-import os
-import os.path
-import ConfigParser
-from re import compile, IGNORECASE
-from stat import ST_MTIME
-from string import upper, split, replace
-import logging
-import sys
-from sys import stdin
-from time import ctime, sleep, time
-from socket import gethostname
-from rfc822 import parsedate_tz, mktime_tz
-
-if 'ERESSEA' in os.environ:
- dir = os.environ['ERESSEA']
-elif 'HOME' in os.environ:
- dir = os.path.join(os.environ['HOME'], '/eressea')
-else: # WTF? No HOME?
- dir = "/home/eressea/eressea"
-if not os.path.isdir(dir):
- print "please set the ERESSEA environment variable to the install path"
- sys.exit(1)
-rootdir = dir
-
-game = int(sys.argv[1])
-gamedir = os.path.join(rootdir, "game-%d" % (game, ))
-frommail = 'eressea-server@kn-bremen.de'
-gamename = 'Eressea'
-sender = '%s Server <%s>' % (gamename, frommail)
-
-inifile = os.path.join(gamedir, 'eressea.ini')
-if not os.path.exists(inifile):
- print "no such file: " . inifile
-else:
- config = ConfigParser.ConfigParser()
- config.read(inifile)
- if config.has_option('game', 'email'):
- frommail = config.get('game', 'email')
- if config.has_option('game', 'name'):
- gamename = config.get('game', 'name')
- if config.has_option('game', 'sender'):
- sender = config.get('game', 'sender')
- else:
- sender = "%s Server <%s>" % (gamename, frommail)
- config = None
-prefix = 'turn-'
-hostname = gethostname()
-orderbase = "orders.dir"
-sendmail = True
-# maximum number of reports per sender:
-maxfiles = 20
-# write headers to file?
-writeheaders = True
-# reject all html email?
-rejecthtml = True
-
-def unlock_file(filename):
- try:
- os.unlink(filename+".lock")
- except:
- print "could not unlock %s.lock, file not found" % filename
-
-def lock_file(filename):
- i = 0
- wait = 1
- if not os.path.exists(filename):
- file=open(filename, "w")
- file.close()
- while True:
- try:
- os.symlink(filename, filename+".lock")
- return
- except:
- i = i+1
- if i == 5: unlock_file(filename)
- sleep(wait)
- wait = wait*2
-
-messages = {
- "multipart-en" :
- "ERROR: The orders you sent contain no plaintext. " \
- "The Eressea server cannot process orders containing HTML " \
- "or invalid attachments, which are the reasons why this " \
- "usually happens. Please change the settings of your mail " \
- "software and re-send the orders.",
-
- "multipart-de" :
- "FEHLER: Die von dir eingeschickte Mail enth�lt keinen " \
- "Text. Evtl. hast Du den Zug als HTML oder als anderweitig " \
- "ung�ltig formatierte Mail ingeschickt. Wir k�nnen ihn " \
- "deshalb nicht ber�cksichtigen. Schicke den Zug nochmals " \
- "als reinen Text ohne Formatierungen ein.",
-
- "maildate-de":
- "Es erreichte uns bereits ein Zug mit einem sp�teren " \
- "Absendedatum (%s > %s). Entweder ist deine " \
- "Systemzeit verstellt, oder ein Zug hat einen anderen Zug von " \
- "dir auf dem Transportweg �berholt. Entscheidend f�r die " \
- "Auswertungsreihenfolge ist das Absendedatum, d.h. der Date:-Header " \
- "deiner Mail.",
-
- "maildate-en":
- "The server already received an order file that was sent at a later " \
- "date (%s > %s). Either your system clock is wrong, or two messages have " \
- "overtaken each other on the way to the server. The order of " \
- "execution on the server is always according to the Date: header in " \
- "your mail.",
-
- "nodate-en":
- "Your message did not contain a valid Date: header in accordance with RFC2822.",
-
- "nodate-de":
- "Deine Nachricht enthielt keinen gueltigen Date: header nach RFC2822.",
-
- "error-de":
- "Fehler",
-
- "error-en":
- "Error",
-
- "warning-de":
- "Warnung",
-
- "warning-en":
- "Warning",
-
- "subject-de":
- "Befehle angekommen",
-
- "subject-en":
- "orders received"
-}
-
-# return 1 if addr is a valid email address
-def valid_email(addr):
- rfc822_specials = '/()<>@,;:\\"[]'
- # First we validate the name portion (name@domain)
- c = 0
- while c < len(addr):
- if addr[c] == '"' and (not c or addr[c - 1] == '.' or addr[c - 1] == '"'):
- c = c + 1
- while c < len(addr):
- if addr[c] == '"': break
- if addr[c] == '\\' and addr[c + 1] == ' ':
- c = c + 2
- continue
- if ord(addr[c]) < 32 or ord(addr[c]) >= 127: return 0
- c = c + 1
- else: return 0
- if addr[c] == '@': break
- if addr[c] != '.': return 0
- c = c + 1
- continue
- if addr[c] == '@': break
- if ord(addr[c]) <= 32 or ord(addr[c]) >= 127: return 0
- if addr[c] in rfc822_specials: return 0
- c = c + 1
- if not c or addr[c - 1] == '.': return 0
-
- # Next we validate the domain portion (name@domain)
- domain = c = c + 1
- if domain >= len(addr): return 0
- count = 0
- while c < len(addr):
- if addr[c] == '.':
- if c == domain or addr[c - 1] == '.': return 0
- count = count + 1
- if ord(addr[c]) <= 32 or ord(addr[c]) >= 127: return 0
- if addr[c] in rfc822_specials: return 0
- c = c + 1
- return count >= 1
-
-# return the replyto or from address in the header
-def get_sender(header):
- replyto = header.get("Reply-To")
- if replyto is None:
- replyto = header.get("From")
- if replyto is None: return None
- x = parseaddr(replyto)
- return x[1]
-
-# return first available filename basename,[0-9]+
-def available_file(dirname, basename):
- ver = 0
- maxdate = 0
- filename = "%s/%s,%s,%d" % (dirname, basename, hostname, ver)
- while os.path.exists(filename):
- maxdate = max(os.stat(filename)[ST_MTIME], maxdate)
- ver = ver + 1
- filename = "%s/%s,%s,%d" % (dirname, basename, hostname, ver)
- if ver >= maxfiles:
- return None, None
- return maxdate, filename
-
-def formatpar(string, l=76, indent=2):
- words = split(string)
- res = ""
- ll = 0
- first = 1
-
- for word in words:
- if first == 1:
- res = word
- first = 0
- ll = len(word)
- else:
- if ll + len(word) > l:
- res = res + "\n"+" "*indent+word
- ll = len(word) + indent
- else:
- res = res+" "+word
- ll = ll + len(word) + 1
-
- return res+"\n"
-
-def store_message(message, filename):
- outfile = open(filename, "w")
- outfile.write(message.as_string())
- outfile.close()
- return
-
-def write_part(outfile, part):
- charset = part.get_content_charset()
- payload = part.get_payload(decode=True)
-
- if charset is None:
- charset = "latin1"
- try:
- msg = payload.decode(charset, "ignore")
- except:
- msg = payload
- charset = None
- try:
- utf8 = msg.encode("utf-8", "ignore")
- outfile.write(utf8)
- except:
- outfile.write(msg)
- return False
- outfile.write("\n");
- return True
-
-def copy_orders(message, filename, sender):
- # print the header first
- if writeheaders:
- from os.path import split
- dirname, basename = split(filename)
- dirname = dirname + '/headers'
- if not os.path.exists(dirname): os.mkdir(dirname)
- outfile = open(dirname + '/' + basename, "w")
- for name, value in message.items():
- outfile.write(name + ": " + value + "\n")
- outfile.close()
-
- found = False
- outfile = open(filename, "w")
- if message.is_multipart():
- for part in message.get_payload():
- content_type = part.get_content_type()
- logger.debug("found content type %s for %s" % (content_type, sender))
- if content_type=="text/plain":
- if write_part(outfile, part):
- found = True
- else:
- charset = part.get_content_charset()
- logger.error("could not write text/plain part (charset=%s) for %s" % (charset, sender))
-
- else:
- if write_part(outfile, message):
- found = True
- else:
- charset = message.get_content_charset()
- logger.error("could not write text/plain message (charset=%s) for %s" % (charset, sender))
- outfile.close()
- return found
-
-# create a file, containing:
-# game=0 locale=de file=/path/to/filename email=rcpt@domain.to
-def accept(game, locale, stream, extend=None):
- global rootdir, orderbase, gamedir, gamename, sender
- if extend is not None:
- orderbase = orderbase + ".pre-" + extend
- savedir = os.path.join(gamedir, orderbase)
- # check if it's one of the pre-sent orders.
- # create the save-directories if they don't exist
- if not os.path.exists(gamedir): os.mkdir(gamedir)
- if not os.path.exists(savedir): os.mkdir(savedir)
- # parse message
- message = Parser().parse(stream)
- email = get_sender(message)
- logger = logging.getLogger(email)
- # write syslog
- if email is None or valid_email(email)==0:
- logger.warning("invalid email address: " + str(email))
- return -1
- logger.info("received orders from " + email)
- # get an available filename
- lock_file(gamedir + "/orders.queue")
- maxdate, filename = available_file(savedir, prefix + email)
- if filename is None:
- logger.warning("more than " + str(maxfiles) + " orders from " + email)
- return -1
- # copy the orders to the file
- text_ok = copy_orders(message, filename, email)
- unlock_file(gamedir + "/orders.queue")
-
- warning, msg, fail = None, "", False
- maildate = message.get("Date")
- if maildate != None:
- turndate = mktime_tz(parsedate_tz(maildate))
- os.utime(filename, (turndate, turndate))
- logger.debug("mail date is '%s' (%d)" % (maildate, turndate))
- if turndate < maxdate:
- logger.warning("inconsistent message date " + email)
- warning = " (" + messages["warning-" + locale] + ")"
- msg = msg + formatpar(messages["maildate-" + locale] % (ctime(maxdate),ctime(turndate)), 76, 2) + "\n"
- else:
- logger.warning("missing message date " + email)
- warning = " (" + messages["warning-" + locale] + ")"
- msg = msg + formatpar(messages["nodate-" + locale], 76, 2) + "\n"
-
- if not text_ok:
- warning = " (" + messages["error-" + locale] + ")"
- msg = msg + formatpar(messages["multipart-" + locale], 76, 2) + "\n"
- logger.warning("rejected - no text/plain in orders from " + email)
- os.unlink(filename)
- savedir = savedir + "/rejected"
- if not os.path.exists(savedir): os.mkdir(savedir)
- lock_file(gamedir + "/orders.queue")
- maxdate, filename = available_file(savedir, prefix + email)
- store_message(message, filename)
- unlock_file(gamedir + "/orders.queue")
- fail = True
-
- if sendmail and warning is not None:
- subject = gamename + " " + messages["subject-"+locale] + warning
- mail = "Subject: %s\nFrom: %s\nTo: %s\n\n" % (subject, sender, email) + msg
- from smtplib import SMTP
- server = SMTP("localhost")
- server.sendmail(sender, email, mail)
- server.close()
-
- if not sendmail:
- print text_ok, fail, email
- print filename
-
- if not fail:
- lock_file(gamedir + "/orders.queue")
- queue = open(gamedir + "/orders.queue", "a")
- queue.write("email=%s file=%s locale=%s game=%s\n" % (email, filename, locale, game))
- queue.close()
- unlock_file(gamedir + "/orders.queue")
-
- logger.info("done - accepted orders from " + email)
-
- return 0
-
-# the main body of the script:
-try:
- os.mkdir(os.path.join(rootdir, 'log'))
-except:
- pass # already exists?
-LOG_FILENAME=os.path.join(rootdir, 'log/orders.log')
-logging.basicConfig(level=logging.DEBUG, filename=LOG_FILENAME)
-logger = logging
-delay=None # TODO: parse the turn delay
-locale = sys.argv[2]
-infile = stdin
-if len(sys.argv)>3:
- infile = open(sys.argv[3], "r")
-retval = accept(game, locale, infile, delay)
-if infile!=stdin:
- infile.close()
-sys.exit(retval)
diff --git a/process/orders-process b/process/orders-process
index 79c7b3378..38b2d1115 100755
--- a/process/orders-process
+++ b/process/orders-process
@@ -1,242 +1,5 @@
-#!/usr/bin/env python
-# -*- coding: iso-8859-1 -*-
+#!/bin/sh
+SCRIPT=$(readlink -f $0)
+cd $(dirname $SCRIPT)
+python process-orders.py "$@"
-from os import unlink, symlink, rename, popen, tmpfile
-import sys
-import os
-import os.path
-import ConfigParser
-from re import compile, IGNORECASE
-from string import split, join, upper, strip
-from sys import argv, exit
-from time import sleep, time, ctime
-from syslog import openlog, closelog, syslog
-
-from epasswd import EPasswd
-
-def pwd_get_email(faction, pwd, pwdfile=None):
- return None
-
-def split_filename(filename):
- return os.path.split(filename)
-
-def unlock_file(filename):
- try:
- unlink(filename+".lock")
- except:
- print "could not unlock %s.lock, file not found" % filename
- raise
-
-def lock_file(filename):
- i = 0
- wait = 1
- if not os.path.exists(filename):
- file=open(filename, "w")
- file.close()
- while True:
- try:
- symlink(filename, filename+".lock")
- return
- except:
- i = i+1
- if i == 5:
- raise
- sleep(wait)
- wait = wait*2
-
-messages = {
-"subject-de": "Befehle angekommen",
-"subject-en": "orders received",
-
-"validate-en": "Validating",
-"validate-de": "Verarbeite",
-
-"faction-en": "Faction",
-"faction-de": "Partei",
-
-"unknown-de": "WARNUNG: Die Partei ist nicht bekannt, oder das Passwort falsch!",
-"unknown-en": "WARNING: This faction is unknown, or the password is incorrect!",
-
-"warning-de": "Warnung",
-"warning-en": "Warning",
-
-"error-de": "Fehler",
-"error-en": "Error",
-}
-
-game = int(sys.argv[1])
-echeck_cmd = "/home/eressea/echeck/echeck.sh"
-maxlines = 25
-
-# base directory for all your games:
-install_dir = "/home/eressea/eressea"
-if 'ERESSEA' in os.environ:
- install_dir = os.environ['ERESSEA']
-elif 'HOME' in os.environ:
- install_dir = os.path.join(os.environ['HOME'], '/eressea')
-if not os.path.isdir(install_dir):
- print "please set the ERESSEA environment variable to the install path"
- sys.exit(1)
-
-game_dir = os.path.join(install_dir, "game-%d" % (game, ))
-frommail = 'eressea-server@kn-bremen.de'
-gamename = 'Eressea'
-sender = '%s Server <%s>' % (gamename, frommail)
-
-inifile = os.path.join(game_dir, 'eressea.ini')
-if not os.path.exists(inifile):
- print "no such file: " . inifile
-else:
- config = ConfigParser.ConfigParser()
- config.read(inifile)
- if config.has_option('game', 'email'):
- frommail = config.get('game', 'email')
- if config.has_option('game', 'name'):
- gamename = config.get('game', 'name')
- if config.has_option('game', 'sender'):
- sender = config.get('game', 'sender')
- else:
- sender = "%s Server <%s>" % (gamename, frommail)
- config = None
-
-queue_file = os.path.join(game_dir, "orders.queue")
-if not os.path.exists(queue_file):
- exit(0)
-
-# regular expression that finds the start of a faction
-fact_re = compile("^\s*(eressea|partei|faction)\s+([a-zA-Z0-9]+)\s+\"?([^\"]*)\"?", IGNORECASE)
-
-def check_pwd(filename, email, pw_data):
- results = []
- try:
- file = open(filename, "r")
- except:
- print "could not open file", filename
- return results
- for line in file.readlines():
- mo = fact_re.search(strip(line))
- if mo != None:
- fact_nr = str(mo.group(2))
- fact_pw = str(mo.group(3))
- if pw_data.fac_exists(fact_nr):
- if not pw_data.check(fact_nr, fact_pw):
- game_email = pw_data.get_email(fact_nr)
- results = results + [ (fact_nr, game_email, False, fact_pw) ]
- else:
- game_email = pw_data.get_email(fact_nr)
- results = results + [ (fact_nr, game_email, True, fact_pw) ]
- else:
- results = results + [ (fact_nr, None, False, fact_pw) ]
- return results
-
-def echeck(filename, locale, rules):
- dirname, filename = split_filename(filename)
- stream = popen("%s %s %s %s %s" % (echeck_cmd, locale, filename, dirname, rules), 'r')
- lines = stream.readlines()
- if len(lines)==0:
- stream.close()
- return None
- if len(lines)>maxlines:
- mail = join(lines[:maxlines-3] + ["...", "\n"] + lines[-3:], '')
- else:
- mail = join(lines[:maxlines], '')
- stream.close()
- return mail
-
-# parse the queue file -
-#print "connecting to SMTP..."
-from smtplib import SMTP
-try:
- server = SMTP("localhost")
-except:
- print "could not connect to SMTP server"
- exit(0)
-
-#print "reading password file..."
-pw_data = EPasswd(os.path.join(game_dir,"passwd"))
-
-#print "reading orders.queue..."
-# move the queue file to a save space while locking it:
-try:
- lock_file(queue_file)
-except:
- exit(0)
-queuefile = open(queue_file, "r")
-lines = queuefile.readlines()
-queuefile.close()
-
-# copy to a temp file
-
-tname="/tmp/orders.queue.%s" % str(time())
-try:
- lock_file(tname)
-except:
- exit(0)
-tmpfile=open(tname, "w")
-for line in lines:
- tmpfile.write(line)
-tmpfile.close()
-
-openlog("orders")
-
-unlink(queue_file)
-try:
- unlock_file(queue_file)
-except:
- pass
-
-for line in lines:
- tokens = split(line[:-1], ' ')
- dict = {}
- for token in tokens:
- name, value = split(token, '=')
- dict[name] = value
-
- email = dict["email"]
- locale = dict["locale"]
- game = int(dict["game"])
- infile = dict["file"]
- gamename='[E%d]' % game
- rules='e%d' % game
- warning = ""
- failed = True
- results = check_pwd(infile, email, pw_data)
- logfile = open(os.path.join(game_dir, "zug.log"), "a")
- dirname, filename = split_filename(infile)
- msg = messages["validate-"+locale] + " " + infile + "\n\n"
- for faction, game_email, success, pwd in results:
- msg = msg + messages["faction-"+locale] + " " + faction + "\n"
- if success: failed = False
- else: msg = msg + messages["unknown-"+locale] + "\n"
- msg = msg + "\n"
- logfile.write("%s:%s:%s:%s:%s:%s\n" % (ctime(time()), email, game_email, faction, pwd, success))
- logfile.close()
-
- if failed:
- warning = " (" + messages["warning-" + locale] + ")"
- syslog("failed - no valid password in " + infile)
- else:
- result = None
- if os.path.exists(echeck_cmd):
- result = echeck(infile, locale, rules)
- if result is None:
- # echeck did not finish
- msg = msg + "Echeck is broken. Your turn was accepted, but could not be verified.\n"
- warning = " (" + messages["warning-" + locale] + ")"
- syslog("process - echeck broken, " + infile)
- else:
- msg = msg + result
- syslog("process - checked orders in " + infile)
-
- subject = gamename + " " + messages["subject-" + locale] + warning
- msg = "Subject: %s\nFrom: %s\nTo: %s\nContent-Type: text/plain; charset=utf-8\n\n" % (subject, sender, email) + msg
- try:
- server.sendmail(sender, email, msg)
- except:
- syslog("failed - cannot send to " + email)
-
-server.close()
-
-closelog()
-unlink(tname)
-unlock_file(tname)
diff --git a/process/process-orders.py b/process/process-orders.py
new file mode 100755
index 000000000..8ed5e8625
--- /dev/null
+++ b/process/process-orders.py
@@ -0,0 +1,215 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+from os import unlink, symlink, rename, popen, tmpfile
+import sys
+import os
+import os.path
+import ConfigParser
+import subprocess
+from re import compile, IGNORECASE
+from string import split, join, upper, strip
+from sys import argv, exit
+from time import sleep, time, ctime
+from syslog import openlog, closelog, syslog
+
+from epasswd import EPasswd
+
+def pwd_get_email(faction, pwd, pwdfile=None):
+ return None
+
+def split_filename(filename):
+ return os.path.split(filename)
+
+def unlock_file(filename):
+ try:
+ unlink(filename+".lock")
+ except:
+ print "could not unlock %s.lock, file not found" % filename
+ raise
+
+def lock_file(filename):
+ i = 0
+ wait = 1
+ if not os.path.exists(filename):
+ file=open(filename, "w")
+ file.close()
+ while True:
+ try:
+ symlink(filename, filename+".lock")
+ return
+ except:
+ i = i+1
+ if i == 5:
+ raise
+ sleep(wait)
+ wait = wait*2
+
+messages = {
+"subject-de": "Befehle angekommen",
+"subject-en": "orders received",
+
+"validate-en": "Validating",
+"validate-de": "Verarbeite",
+
+"faction-en": "Faction",
+"faction-de": "Partei",
+
+"unknown-de": "WARNUNG: Die Partei ist nicht bekannt, oder das Passwort falsch!",
+"unknown-en": "WARNING: This faction is unknown, or the password is incorrect!",
+
+"warning-de": "Warnung",
+"warning-en": "Warning",
+
+"error-de": "Fehler",
+"error-en": "Error",
+}
+
+game = int(sys.argv[1])
+echeck_cmd = "/home/eressea/echeck/echeck.sh"
+maxlines = 25
+
+# base directory for all your games:
+install_dir = "/home/eressea/eressea"
+if 'ERESSEA' in os.environ:
+ install_dir = os.environ['ERESSEA']
+elif 'HOME' in os.environ:
+ install_dir = os.path.join(os.environ['HOME'], 'eressea')
+if not os.path.isdir(install_dir):
+ print "please set the ERESSEA environment variable to the install path"
+ sys.exit(1)
+
+game_dir = os.path.join(install_dir, "game-%d" % (game, ))
+gamename = 'Eressea'
+
+inifile = os.path.join(game_dir, 'eressea.ini')
+queue_file = os.path.join(game_dir, "orders.queue")
+if not os.path.exists(queue_file):
+ exit(0)
+
+# regular expression that finds the start of a faction
+fact_re = compile("^\s*(eressea|partei|faction)\s+([a-zA-Z0-9]+)\s+\"?([^\"]*)\"?", IGNORECASE)
+
+def check_pwd(filename, email, pw_data):
+ results = []
+ try:
+ file = open(filename, "r")
+ except:
+ print "could not open file", filename
+ return results
+ for line in file.readlines():
+ mo = fact_re.search(strip(line))
+ if mo != None:
+ fact_nr = str(mo.group(2))
+ fact_pw = str(mo.group(3))
+ if pw_data.fac_exists(fact_nr):
+ if not pw_data.check(fact_nr, fact_pw):
+ game_email = pw_data.get_email(fact_nr)
+ results = results + [ (fact_nr, game_email, False, fact_pw) ]
+ else:
+ game_email = pw_data.get_email(fact_nr)
+ results = results + [ (fact_nr, game_email, True, fact_pw) ]
+ else:
+ results = results + [ (fact_nr, None, False, fact_pw) ]
+ return results
+
+def echeck(filename, locale, rules):
+ dirname, filename = split_filename(filename)
+ stream = popen("%s %s %s %s %s" % (echeck_cmd, locale, filename, dirname, rules), 'r')
+ lines = stream.readlines()
+ if len(lines)==0:
+ stream.close()
+ return None
+ if len(lines)>maxlines:
+ mail = join(lines[:maxlines-3] + ["...", "\n"] + lines[-3:], '')
+ else:
+ mail = join(lines[:maxlines], '')
+ stream.close()
+ return mail
+
+#print "reading password file..."
+pw_data = EPasswd(os.path.join(game_dir,"passwd"))
+
+#print "reading orders.queue..."
+# move the queue file to a save space while locking it:
+try:
+ lock_file(queue_file)
+except:
+ exit(0)
+queuefile = open(queue_file, "r")
+lines = queuefile.readlines()
+queuefile.close()
+
+# copy to a temp file
+
+tname="/tmp/orders.queue.%s" % str(time())
+try:
+ lock_file(tname)
+except:
+ exit(0)
+tmpfile=open(tname, "w")
+for line in lines:
+ tmpfile.write(line)
+tmpfile.close()
+
+openlog("orders")
+
+unlink(queue_file)
+try:
+ unlock_file(queue_file)
+except:
+ pass
+
+for line in lines:
+ tokens = split(line[:-1], ' ')
+ dict = {}
+ for token in tokens:
+ name, value = split(token, '=')
+ dict[name] = value
+
+ email = dict["email"]
+ locale = dict["locale"]
+ game = int(dict["game"])
+ infile = dict["file"]
+ gamename='[E%d]' % game
+ rules='e%d' % game
+ warning = ""
+ failed = True
+ results = check_pwd(infile, email, pw_data)
+ logfile = open(os.path.join(game_dir, "zug.log"), "a")
+ dirname, filename = split_filename(infile)
+ msg = messages["validate-"+locale] + " " + infile + "\n\n"
+ for faction, game_email, success, pwd in results:
+ msg = msg + messages["faction-"+locale] + " " + faction + "\n"
+ if success: failed = False
+ else: msg = msg + messages["unknown-"+locale] + "\n"
+ msg = msg + "\n"
+ logfile.write("%s:%s:%s:%s:%s:%s\n" % (ctime(time()), email, game_email, faction, pwd, success))
+ logfile.close()
+
+ if failed:
+ warning = " (" + messages["warning-" + locale] + ")"
+ syslog("failed - no valid password in " + infile)
+ else:
+ result = None
+ if os.path.exists(echeck_cmd):
+ result = echeck(infile, locale, rules)
+ if result is None:
+ # echeck did not finish
+ msg = msg + "Echeck is broken. Your turn was accepted, but could not be verified.\n"
+ warning = " (" + messages["warning-" + locale] + ")"
+ syslog("process - echeck broken, " + infile)
+ else:
+ msg = msg + result
+ syslog("process - checked orders in " + infile)
+
+ subject = gamename + " " + messages["subject-" + locale] + warning
+ try:
+ sp = subprocess.Popen(['mutt', '-s', subject, email], stdin=subprocess.PIPE)
+ sp.communicate(msg)
+ except:
+ syslog("failed - cannot send to " + email)
+
+closelog()
+unlink(tname)
+unlock_file(tname)
diff --git a/res/core/messages.xml b/res/core/messages.xml
index 757243d9f..d566c82e6 100644
--- a/res/core/messages.xml
+++ b/res/core/messages.xml
@@ -698,10 +698,16 @@
-
+
-
+
+
+
+
+
+
+
@@ -5494,12 +5500,12 @@
-
+
-
+
diff --git a/res/translations/messages.de.po b/res/translations/messages.de.po
index 13fd10c96..3413ffc2c 100644
--- a/res/translations/messages.de.po
+++ b/res/translations/messages.de.po
@@ -1355,8 +1355,11 @@ msgstr "\"Es herrscht eine fröhliche und ausgelassene Stimmung. ($int36($id))\"
msgid "buildroad"
msgstr "\"$unit($unit) erweitert in $region($region) das Straßennetz um $int($size).\""
-msgid "nr_borderlist_postfix"
-msgstr "\"$if($transparent,\" befindet sich\",\" versperrt\") ${object}$if($transparent,\"\",\" die Sicht\").\""
+msgid "nr_border_opaque"
+msgstr "Im $direction($dir) verperrt ${object} die Sicht."
+
+msgid "nr_border_transparent"
+msgstr "Im $direction($dir) befindet sich ${object}."
msgid "effectstrength"
msgstr "\"$unit($mage) erhöht die Körperkraft von $unit.dative($target) beträchtlich.\""
diff --git a/res/translations/messages.en.po b/res/translations/messages.en.po
index fa795d0f6..6eaf6f258 100644
--- a/res/translations/messages.en.po
+++ b/res/translations/messages.en.po
@@ -1355,8 +1355,11 @@ msgstr "\"Everyone in this region seems to be having a very good time. ($int36($
msgid "buildroad"
msgstr "\"$unit($unit) extends the road network in $region($region) by $int($size).\""
-msgid "nr_borderlist_postfix"
-msgstr "\"$if($transparent,\" there is\",\" sight is blocked by \") ${object}.\""
+msgid "nr_border_opaque"
+msgstr "To the $direction($dir), ${object} is blocking the view."
+
+msgid "nr_border_transparent"
+msgstr "To the $direction($dir) is ${object}."
msgid "effectstrength"
msgstr "\"$unit($mage) increases the strength of $unit($target) dramatically.\""
diff --git a/res/translations/strings.de.po b/res/translations/strings.de.po
index e56e36f55..afcceb94b 100644
--- a/res/translations/strings.de.po
+++ b/res/translations/strings.de.po
@@ -1906,9 +1906,6 @@ msgstr "Mallorn"
msgid "castle"
msgstr "Burg"
-msgid "nr_borderlist_infix"
-msgstr ", im"
-
msgctxt "race"
msgid "shadowbat_p"
msgstr "Todesflattern"
@@ -3706,9 +3703,6 @@ msgctxt "spell"
msgid "analyse_object"
msgstr "Lied des Ortes analysieren"
-msgid "nr_borderlist_lastfix"
-msgstr "und im"
-
msgctxt "race"
msgid "shadowknight_d"
msgstr "Schattenrittern"
@@ -5573,7 +5567,7 @@ msgid "swamp_trail"
msgstr "der Sumpf von %s"
msgid "nr_nb_final"
-msgstr "und im"
+msgstr "und im "
msgid "aoc"
msgstr "Katzenamulett"
diff --git a/res/translations/strings.en.po b/res/translations/strings.en.po
index 8629d5f5e..c8bd637db 100644
--- a/res/translations/strings.en.po
+++ b/res/translations/strings.en.po
@@ -1642,9 +1642,6 @@ msgstr "mallorn"
msgid "castle"
msgstr "castle"
-msgid "nr_borderlist_infix"
-msgstr ", to the "
-
msgctxt "race"
msgid "shadowbat_p"
msgstr "darkbats"
@@ -3261,9 +3258,6 @@ msgctxt "spell"
msgid "analyse_object"
msgstr "Analysis"
-msgid "nr_borderlist_lastfix"
-msgstr ", and to the "
-
msgctxt "race"
msgid "shadowknight_d"
msgstr "shadow knights"
diff --git a/scripts/tests/e2/e2features.lua b/scripts/tests/e2/e2features.lua
index 4758fce39..be09ec448 100644
--- a/scripts/tests/e2/e2features.lua
+++ b/scripts/tests/e2/e2features.lua
@@ -491,8 +491,29 @@ function test_dwarf_mining()
local f = faction.create('dwarf')
local r = region.create(0, 0, 'plain')
local u = unit.create(f, r)
- u.name = 'Xolgrim'
u:set_skill('mining', 2)
assert_equal(2, u:get_skill('mining'))
assert_equal(4, u:eff_skill('mining'))
end
+
+function test_buy_sell()
+ local f = faction.create('human')
+ local r = region.create(0, 0, 'plain')
+ local u = unit.create(f, r)
+ local lux = r.luxury
+ local b = building.create(r, 'castle')
+ b.size = 10
+ u:set_skill('trade', 1)
+ item = 'silk'
+ name = 'Seide'
+ if lux == 'silk' then
+ item = 'balm'
+ name = 'Balsam'
+ end
+ u:add_item(item, 5)
+ u:add_order('VERKAUFE 1 ' .. name)
+ assert_equal(0, u:get_item('money'))
+ process_orders()
+ assert_equal(4, u:get_item(item))
+ assert_not_equal(0, u:get_item('money'))
+end
diff --git a/src/exparse.c b/src/exparse.c
index e6975fc17..07acb090e 100644
--- a/src/exparse.c
+++ b/src/exparse.c
@@ -818,7 +818,9 @@ static void start_resources(parseinfo *pi, const XML_Char *el, const XML_Char **
handle_requirement(pi, el, attr);
}
else if (xml_strcmp(el, "luxury") == 0) {
- rtype->ltype = new_luxurytype(itype, 0);
+ int price = atoi(attr_get(attr, "price"));
+ assert(price > 0);
+ rtype->ltype = new_luxurytype(itype, price);
}
else if (xml_strcmp(el, "potion") == 0) {
int i, level = 0;
@@ -1090,9 +1092,13 @@ static void start_races(parseinfo *pi, const XML_Char *el, const XML_Char **attr
name = attr_get(attr, "name");
if (name) {
+ int i;
+
assert(!rc);
pi->object = rc = rc_get_or_create(name);
- int i;
+ while (AT_NONE != rc->attack[nattacks].type) {
+ ++nattacks;
+ }
for (i = 0; attr[i]; i += 2) {
const XML_Char *key = attr[i], *val = attr[i + 1];
diff --git a/src/kernel/connection.c b/src/kernel/connection.c
index 014fce9d8..213700fe1 100644
--- a/src/kernel/connection.c
+++ b/src/kernel/connection.c
@@ -188,7 +188,7 @@ border_type *find_bordertype(const char *name)
{
border_type *bt = bordertypes;
- while (bt && strcmp(bt->__name, name)!=0)
+ while (bt && strcmp(bt->_name, name)!=0)
bt = bt->next;
return bt;
}
@@ -563,7 +563,7 @@ void write_borders(struct storage *store)
for (b = bhash; b != NULL; b = b->next) {
if (b->type->valid && !b->type->valid(b))
continue;
- WRITE_TOK(store, b->type->__name);
+ WRITE_TOK(store, b->type->_name);
WRITE_INT(store, b->id);
WRITE_INT(store, b->from->uid);
WRITE_INT(store, b->to->uid);
@@ -614,7 +614,7 @@ int read_borders(gamedata *data)
if (to == from && from) {
direction_t dir = (direction_t)(rng_int() % MAXDIRECTIONS);
region *r = rconnect(from, dir);
- log_error("[read_borders] invalid %s in %s\n", type->__name, regionname(from, NULL));
+ log_error("[read_borders] invalid %s in %s\n", type->_name, regionname(from, NULL));
if (r != NULL)
to = r;
}
@@ -633,5 +633,5 @@ int read_borders(gamedata *data)
}
const char * border_name(const connection *co, const struct region * r, const struct faction * f, int flags) {
- return (co->type->name) ? co->type->name(co, r, f, flags) : co->type->__name;
+ return (co->type->name) ? co->type->name(co, r, f, flags) : co->type->_name;
}
diff --git a/src/kernel/connection.h b/src/kernel/connection.h
index c847f690f..af412b036 100644
--- a/src/kernel/connection.h
+++ b/src/kernel/connection.h
@@ -45,7 +45,7 @@ extern "C" {
} connection;
typedef struct border_type {
- const char *__name; /* internal use only */
+ const char *_name; /* internal use only */
variant_type datatype;
bool(*transparent) (const connection *, const struct faction *);
/* is it possible to see through this? */
diff --git a/src/move.c b/src/move.c
index c1ded5faa..990d6fc14 100644
--- a/src/move.c
+++ b/src/move.c
@@ -73,7 +73,6 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#include
#include
#include
-#include
#include
#include
#include
@@ -1047,15 +1046,17 @@ int movewhere(const unit * u, const char *token, region * r, region ** resultp)
order * cycle_route(order * ord, const struct locale *lang, int gereist)
{
int cm = 0;
- char tail[1024], *bufp = tail;
- char neworder[2048], *obuf = neworder;
+ char tail[1024];
+ char neworder[2048];
char token[128];
direction_t d = NODIRECTION;
bool paused = false;
- bool pause;
order *norder;
- size_t size = sizeof(tail) - 1;
+ sbstring sbtail;
+ sbstring sborder;
+ sbs_init(&sbtail, tail, sizeof(tail));
+ sbs_init(&sborder, neworder, sizeof(neworder));
assert(getkeyword(ord) == K_ROUTE);
tail[0] = '\0';
neworder[0] = '\0';
@@ -1063,14 +1064,10 @@ order * cycle_route(order * ord, const struct locale *lang, int gereist)
for (cm = 0;; ++cm) {
const char *s;
- pause = false;
s = gettoken(token, sizeof(token));
if (s && *s) {
d = get_direction(s, lang);
- if (d == D_PAUSE) {
- pause = true;
- }
- else if (d == NODIRECTION) {
+ if (d == NODIRECTION) {
break;
}
}
@@ -1079,38 +1076,37 @@ order * cycle_route(order * ord, const struct locale *lang, int gereist)
}
if (cm < gereist) {
/* TODO: hier sollte keine PAUSE auftreten */
- assert(!pause);
- if (!pause) {
+ assert (d != D_PAUSE);
+ if (d != D_PAUSE) {
const char *loc = LOC(lang, shortdirections[d]);
assert(loc);
- if (bufp != tail) {
- bufp = STRLCPY_EX(bufp, " ", &size, "cycle_route");
+ if (sbs_length(&sbtail) > 0) {
+ sbs_strcat(&sbtail, " ");
}
- bufp = STRLCPY_EX(bufp, loc, &size, "cycle_route");
+ sbs_strcat(&sbtail, loc);
}
}
else if (strlen(neworder) > sizeof(neworder) / 2)
break;
- else if (cm == gereist && !paused && pause) {
+ else if (cm == gereist && !paused && (d == D_PAUSE)) {
const char *loc = LOC(lang, parameters[P_PAUSE]);
- bufp = STRLCPY_EX(bufp, " ", &size, "cycle_route");
- bufp = STRLCPY_EX(bufp, loc, &size, "cycle_route");
+ sbs_strcat(&sbtail, " ");
+ sbs_strcat(&sbtail, loc);
paused = true;
}
- else if (pause) {
- /* da PAUSE nicht in ein shortdirections[d] umgesetzt wird (ist
- * hier keine normale direction), muss jede PAUSE einzeln
- * herausgefiltert und explizit gesetzt werden */
- if (neworder != obuf) {
- obuf += str_strlcat(obuf, " ", sizeof(neworder) - (obuf - neworder));
- }
- obuf += str_strlcat(obuf, LOC(lang, parameters[P_PAUSE]), sizeof(neworder) - (obuf - neworder));
- }
else {
- if (neworder != obuf) {
- obuf += str_strlcat(obuf, " ", sizeof(neworder) - (obuf - neworder));
+ if (sbs_length(&sbtail) > 0) {
+ sbs_strcat(&sborder, " ");
+ }
+ if (d == D_PAUSE) {
+ /* da PAUSE nicht in ein shortdirections[d] umgesetzt wird (ist
+ * hier keine normale direction), muss jede PAUSE einzeln
+ * herausgefiltert und explizit gesetzt werden */
+ sbs_strcat(&sborder, LOC(lang, parameters[P_PAUSE]));
+ }
+ else {
+ sbs_strcat(&sborder, LOC(lang, shortdirections[d]));
}
- obuf += str_strlcat(obuf, LOC(lang, shortdirections[d]), sizeof(neworder) - (obuf - neworder));
}
}
@@ -2250,10 +2246,9 @@ static direction_t hunted_dir(attrib * at, int id)
int follow_ship(unit * u, order * ord)
{
region *rc = u->region;
- size_t bytes;
+ sbstring sbcmd;
int moves, id, speed;
- char command[256], *bufp = command;
- size_t size = sizeof(command);
+ char command[256];
direction_t dir;
if (fval(u, UFL_NOTMOVING)) {
@@ -2289,11 +2284,10 @@ int follow_ship(unit * u, order * ord)
return 0;
}
- bufp = command;
- bytes = slprintf(bufp, size, "%s %s", LOC(u->faction->locale, keyword(K_MOVE)), LOC(u->faction->locale, directions[dir]));
- assert(bytes <= INT_MAX);
- if (wrptr(&bufp, &size, (int)bytes) != 0)
- WARN_STATIC_BUFFER();
+ sbs_init(&sbcmd, command, sizeof(command));
+ sbs_strcpy(&sbcmd, LOC(u->faction->locale, keyword(K_MOVE)));
+ sbs_strcat(&sbcmd, " ");
+ sbs_strcat(&sbcmd, LOC(u->faction->locale, directions[dir]));
moves = 1;
@@ -2309,8 +2303,8 @@ int follow_ship(unit * u, order * ord)
rc = rconnect(rc, dir);
while (rc && moves < speed && (dir = hunted_dir(rc->attribs, id)) != NODIRECTION) {
const char *loc = LOC(u->faction->locale, directions[dir]);
- bufp = STRLCPY_EX(bufp, " ", &size, "hunt");
- bufp = STRLCPY_EX(bufp, loc, &size, "hunt");
+ sbs_strcat(&sbcmd, " ");
+ sbs_strcat(&sbcmd, loc);
moves++;
rc = rconnect(rc, dir);
}
diff --git a/src/move.test.c b/src/move.test.c
index a2fa4e760..f8c11114f 100644
--- a/src/move.test.c
+++ b/src/move.test.c
@@ -495,6 +495,7 @@ static void test_follow_ship_msg(CuTest * tc) {
follow_ship(u, ord);
+ CuAssertPtrEquals(tc, r, u->region);
CuAssertPtrNotNull(tc, msg = test_find_messagetype(u->faction->msgs, "error18"));
p = msg->parameters[2].v;
CuAssertPtrNotNull(tc, p);
diff --git a/src/report.c b/src/report.c
index 3976bb05b..2a0e9eb3a 100644
--- a/src/report.c
+++ b/src/report.c
@@ -769,11 +769,11 @@ static void rp_battles(struct stream *out, faction * f)
while (bm) {
char buf[256];
RENDER(f, buf, sizeof(buf), ("header_battle", "region", bm->r));
- newline(out);
centre(out, buf, true);
newline(out);
rp_messages(out, bm->msgs, f, 0, false);
bm = bm->next;
+ newline(out);
}
}
}
@@ -1237,39 +1237,17 @@ void report_region(struct stream *out, const region * r, faction * f)
if (edges)
newline(out);
for (e = edges; e; e = e->next) {
- bool first = true;
message *msg;
- bufp = buf;
- size = sizeof(buf) - 1;
for (d = 0; d != MAXDIRECTIONS; ++d) {
- if (!e->exist[d])
- continue;
- /* this localization might not work for every language but is fine for de and en */
- if (first)
- bytes = (int)str_strlcpy(bufp, LOC(f->locale, "nr_borderlist_prefix"), size);
- else if (e->lastd == d)
- bytes = (int)str_strlcpy(bufp, LOC(f->locale, "nr_borderlist_lastfix"), size);
- else
- bytes = (int)str_strlcpy(bufp, LOC(f->locale, "nr_borderlist_infix"), size);
- if (wrptr(&bufp, &size, bytes) != 0)
- WARN_STATIC_BUFFER();
- bytes = (int)str_strlcpy(bufp, LOC(f->locale, directions[d]), size);
- if (wrptr(&bufp, &size, bytes) != 0)
- WARN_STATIC_BUFFER();
- first = false;
+ if (e->exist[d]) {
+ msg = msg_message(e->transparent ? "nr_border_transparent" : "nr_border_opaque",
+ "object dir", e->name, d);
+ nr_render(msg, f->locale, buf, sizeof(buf), f);
+ msg_release(msg);
+ paragraph(out, buf, 0, 0, 0);
+ }
}
- /* TODO name is localized? Works for roads anyway... */
- /* TODO: creating messages during reporting makes them not show up in CR? */
- msg = msg_message("nr_borderlist_postfix", "transparent object",
- e->transparent, e->name);
- bytes = (int)nr_render(msg, f->locale, bufp, size, f);
- msg_release(msg);
- if (wrptr(&bufp, &size, bytes) != 0)
- WARN_STATIC_BUFFER();
-
- *bufp = 0;
- paragraph(out, buf, 0, 0, 0);
}
if (edges) {
while (edges) {
@@ -1299,7 +1277,6 @@ static void statistics(struct stream *out, const region * r, const faction * f)
}
}
/* print */
- newline(out);
m = msg_message("nr_stat_header", "region", r);
nr_render(m, f->locale, buf, sizeof(buf), f);
msg_release(m);
@@ -2305,8 +2282,10 @@ report_plaintext(const char *filename, report_context * ctx,
report_travelthru(out, r, f);
}
- if (wants_stats && r->seen.mode >= seen_unit)
+ if (wants_stats && r->seen.mode >= seen_unit) {
statistics(out, r, f);
+ newline(out);
+ }
/* Nachrichten an REGION in der Region */
if (r->seen.mode >= seen_travel) {
diff --git a/src/util/strings.c b/src/util/strings.c
index f4bbd54a5..ef351ab1f 100644
--- a/src/util/strings.c
+++ b/src/util/strings.c
@@ -275,6 +275,11 @@ void sbs_strcpy(struct sbstring *sbs, const char *str)
sbs->end = sbs->begin + len;
}
+size_t sbs_length(const struct sbstring *sbs)
+{
+ return sbs->end - sbs->begin;
+}
+
char *str_unescape(char *str) {
char *read = str, *write = str;
while (*read) {
diff --git a/src/util/strings.h b/src/util/strings.h
index 2d93b414e..bd6f0de58 100644
--- a/src/util/strings.h
+++ b/src/util/strings.h
@@ -50,6 +50,7 @@ extern "C" {
void sbs_strcat(struct sbstring *sbs, const char *str);
void sbs_strncat(struct sbstring *sbs, const char *str, size_t size);
void sbs_strcpy(struct sbstring *sbs, const char *str);
+ size_t sbs_length(const struct sbstring *sbs);
/* benchmark for units:
* JENKINS_HASH: 5.25 misses/hit (with good cache behavior)