2013-04-03 10:33:57 +02:00
|
|
|
|
#!/usr/bin/env python3
|
|
|
|
|
# -*- coding: utf-8 -*-
|
|
|
|
|
|
|
|
|
|
'''
|
|
|
|
|
ponysay - Ponysay, cowsay reimplementation for ponies
|
2013-04-03 20:34:46 +02:00
|
|
|
|
|
2013-04-03 10:33:57 +02:00
|
|
|
|
Copyright (C) 2012, 2013 Erkin Batu Altunbaş et al.
|
|
|
|
|
|
2013-04-03 20:34:46 +02:00
|
|
|
|
|
|
|
|
|
This program is free software: you can redistribute it and/or modify
|
|
|
|
|
it under the terms of the GNU 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 General Public License for more details.
|
|
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
|
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
If you intend to redistribute ponysay or a fork of it commercially,
|
|
|
|
|
it contains aggregated images, some of which may not be commercially
|
|
|
|
|
redistribute, you would be required to remove those. To determine
|
|
|
|
|
whether or not you may commercially redistribute an image make use
|
|
|
|
|
that line ‘FREE: yes’, is included inside the image between two ‘$$$’
|
|
|
|
|
lines and the ‘FREE’ is and upper case and directly followed by
|
|
|
|
|
the colon.
|
2013-04-03 10:33:57 +02:00
|
|
|
|
'''
|
|
|
|
|
from common import *
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class Metadata():
|
|
|
|
|
'''
|
2013-08-12 07:54:31 +02:00
|
|
|
|
Metadata functions
|
2013-04-03 10:33:57 +02:00
|
|
|
|
'''
|
2013-08-12 07:54:31 +02:00
|
|
|
|
|
2013-04-03 10:33:57 +02:00
|
|
|
|
@staticmethod
|
|
|
|
|
def makeRestrictionLogic(restriction):
|
2013-08-12 07:54:31 +02:00
|
|
|
|
'''
|
|
|
|
|
Make restriction test logic function
|
|
|
|
|
|
|
|
|
|
@param restriction:list<string> Metadata based restrictions
|
|
|
|
|
@return :dict<str, str>→bool Test function
|
|
|
|
|
'''
|
2013-04-03 10:33:57 +02:00
|
|
|
|
def get_test(cell):
|
|
|
|
|
strict = cell[0][-1] != '?'
|
2013-04-04 01:30:38 +02:00
|
|
|
|
key = cell[0]
|
|
|
|
|
if not strict:
|
|
|
|
|
key = key[:-1]
|
2013-04-03 10:33:57 +02:00
|
|
|
|
invert = cell[1][0] == '!'
|
|
|
|
|
value = cell[1][1 if invert else 0:]
|
|
|
|
|
|
|
|
|
|
class SITest():
|
|
|
|
|
def __init__(self, cellkey, cellvalue):
|
2013-04-04 01:30:38 +02:00
|
|
|
|
(self.cellkey, self.cellvalue) = (cellkey, cellvalue)
|
2013-04-03 10:33:57 +02:00
|
|
|
|
def __call__(self, has):
|
2013-04-04 01:30:38 +02:00
|
|
|
|
return False if self.cellkey not in has else (self.cellvalue not in has[self.cellkey])
|
|
|
|
|
def __str__(self):
|
|
|
|
|
return 'si(%s : %s)' % (self.cellkey, self.callvalue)
|
2013-04-03 10:33:57 +02:00
|
|
|
|
class STest():
|
|
|
|
|
def __init__(self, cellkey, cellvalue):
|
2013-04-04 01:30:38 +02:00
|
|
|
|
(self.cellkey, self.cellvalue) = (cellkey, cellvalue)
|
2013-04-03 10:33:57 +02:00
|
|
|
|
def __call__(self, has):
|
2013-04-04 01:30:38 +02:00
|
|
|
|
return False if self.cellkey not in has else (self.cellvalue in has[self.cellkey])
|
|
|
|
|
def __str__(self):
|
|
|
|
|
return 's(%s : %s)' % (self.cellkey, self.callvalue)
|
2013-04-03 10:33:57 +02:00
|
|
|
|
class ITest():
|
|
|
|
|
def __init__(self, cellkey, cellvalue):
|
2013-04-04 01:30:38 +02:00
|
|
|
|
(self.cellkey, self.cellvalue) = (cellkey, cellvalue)
|
2013-04-03 10:33:57 +02:00
|
|
|
|
def __call__(self, has):
|
2013-04-04 01:30:38 +02:00
|
|
|
|
return True if self.cellkey not in has else (self.cellvalue not in has[self.cellkey])
|
|
|
|
|
def __str__(self):
|
|
|
|
|
return 'i(%s : %s)' % (self.cellkey, self.callvalue)
|
2013-04-03 10:33:57 +02:00
|
|
|
|
class NTest():
|
|
|
|
|
def __init__(self, cellkey, cellvalue):
|
2013-04-04 01:30:38 +02:00
|
|
|
|
(self.cellkey, self.cellvalue) = (cellkey, cellvalue)
|
2013-04-03 10:33:57 +02:00
|
|
|
|
def __call__(self, has):
|
2013-04-04 01:30:38 +02:00
|
|
|
|
return True if self.cellkey not in has else (self.cellvalue in has[self.cellkey])
|
|
|
|
|
def __str__(self):
|
|
|
|
|
return 'n(%s : %s)' % (self.cellkey, self.callvalue)
|
2013-04-03 10:33:57 +02:00
|
|
|
|
|
|
|
|
|
if strict and invert: return SITest(key, value)
|
|
|
|
|
if strict: return STest(key, value)
|
|
|
|
|
if invert: return ITest(key, value)
|
|
|
|
|
return NTest(key, value)
|
|
|
|
|
|
2013-04-04 01:30:38 +02:00
|
|
|
|
class Logic():
|
|
|
|
|
def __init__(self, table):
|
|
|
|
|
self.table = table
|
|
|
|
|
def __call__(self, cells):
|
|
|
|
|
for alternative in self.table:
|
|
|
|
|
ok = True
|
|
|
|
|
for cell in alternative:
|
|
|
|
|
if not cell(cells):
|
|
|
|
|
ok = False
|
|
|
|
|
break
|
|
|
|
|
if ok:
|
|
|
|
|
return True
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
table = [[get_test((cell[:cell.index('=')].upper(), cell[cell.index('=') + 1:]))
|
|
|
|
|
for cell in clause.replace('_', '').replace(' ', '').split('+')]
|
|
|
|
|
for clause in restriction
|
|
|
|
|
]
|
2013-04-03 10:33:57 +02:00
|
|
|
|
|
2013-04-04 01:30:38 +02:00
|
|
|
|
return Logic(table)
|
2013-04-03 10:33:57 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
|
def restrictedPonies(ponydir, logic):
|
2013-08-12 07:54:31 +02:00
|
|
|
|
'''
|
|
|
|
|
Get ponies that pass restriction
|
|
|
|
|
|
|
|
|
|
@param ponydir:str Pony directory, must end with `os.sep`
|
|
|
|
|
@param logic:(str)→bool Restriction test functor
|
|
|
|
|
@return :list<str> Passed ponies
|
|
|
|
|
'''
|
2013-04-03 22:57:59 +02:00
|
|
|
|
import pickle
|
2013-04-03 10:33:57 +02:00
|
|
|
|
passed = []
|
|
|
|
|
if os.path.exists(ponydir + 'metadata'):
|
|
|
|
|
data = None
|
|
|
|
|
with open(ponydir + 'metadata', 'rb') as file:
|
2013-04-03 22:57:59 +02:00
|
|
|
|
data = pickle.load(file)
|
2013-04-03 10:33:57 +02:00
|
|
|
|
for ponydata in data:
|
|
|
|
|
(pony, meta) = ponydata
|
|
|
|
|
if logic(meta):
|
|
|
|
|
passed.append(pony)
|
|
|
|
|
return passed
|
2013-04-03 17:03:42 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@staticmethod
|
2013-08-12 07:57:07 +02:00
|
|
|
|
def getFitting(fitting, requirement, file):
|
2013-08-12 07:54:31 +02:00
|
|
|
|
'''
|
|
|
|
|
Get ponies that fit the terminal
|
|
|
|
|
|
|
|
|
|
@param fitting:add(str)→void The set to fill
|
|
|
|
|
@param requirement:int The maximum allowed value
|
|
|
|
|
@param file:istream The file with all data
|
|
|
|
|
'''
|
2013-04-03 17:03:42 +02:00
|
|
|
|
data = file.read() # not too much data, can load everything at once
|
|
|
|
|
ptr = 0
|
|
|
|
|
while data[ptr] != 47: # 47 == ord('/')
|
|
|
|
|
ptr += 1
|
|
|
|
|
ptr += 1
|
|
|
|
|
size = 0
|
|
|
|
|
while data[ptr] != 47: # 47 == ord('/')
|
|
|
|
|
size = (size * 10) - (data[ptr] & 15)
|
|
|
|
|
ptr += 1
|
|
|
|
|
ptr += 1
|
|
|
|
|
jump = ptr - size
|
|
|
|
|
stop = 0
|
|
|
|
|
backjump = 0
|
|
|
|
|
while ptr < jump:
|
|
|
|
|
size = 0
|
|
|
|
|
while data[ptr] != 47: # 47 == ord('/')
|
|
|
|
|
size = (size * 10) - (data[ptr] & 15)
|
|
|
|
|
ptr += 1
|
|
|
|
|
ptr += 1
|
|
|
|
|
if -size > requirement:
|
|
|
|
|
if backjump > 0:
|
|
|
|
|
ptr = backjump
|
|
|
|
|
while data[ptr] != 47: # 47 == ord('/')
|
|
|
|
|
stop = (stop * 10) - (data[ptr] & 15)
|
|
|
|
|
ptr += 1
|
|
|
|
|
stop = -stop
|
|
|
|
|
break
|
|
|
|
|
backjump = ptr
|
|
|
|
|
while data[ptr] != 47: # 47 == ord('/')
|
|
|
|
|
ptr += 1
|
|
|
|
|
ptr += 1
|
|
|
|
|
if ptr == jump:
|
|
|
|
|
stop = len(data)
|
|
|
|
|
else:
|
|
|
|
|
ptr = jump
|
|
|
|
|
stop += ptr
|
|
|
|
|
passed = data[jump : stop].decode('utf8', 'replace').split('/')
|
|
|
|
|
for pony in passed:
|
|
|
|
|
fitting.add(pony)
|
2013-04-03 10:33:57 +02:00
|
|
|
|
|