Генератор правил для ClamAV по отбросу писем с исполняемыми файлами в архивах

Скопировать нижележащий скрипт в файл, запустить, перенаправив вывод в новый файл в каталоге базы данных ClamAV (/usr/share/clamav).
Перезапустить антивирус.
Всё.
Сделано по аналогии с Sanesecurity.Foxhole

#!/bin/bash 
#schwarzschild radius 
for containertype in CL_TYPE_ZIP CL_TYPE_RAR CL_TYPE_ARJ CL_TYPE_7Z CL_TYPE_MAIL CL_TYPE_POSIX_TAR CL_TYPE_OLD_TAR CL_TYPE_CPIO_OLD CL_TYPE_CPIO_ODC CL_TYPE_CPIO_CRC CL_TYPE_CPIO_NEWC CL_TYPE_MSCAB do
for extensionname in js exe dll vbs pif com bat scr lnk vsbs reg cmd msi url vxd fav prf class ocx drv cif dun isp ade adp asp bas chm cpl crt hta ins isp jse lib mde msd msp mst sct shb sys vb vbe vxd wsc wsf wsh do 
echo "schwarzschild.radius.$containertype.$extensionname:$containertype:*:(?i)\.$extensionname\$:*:*:*:*:*:*" 
done 
done 

Функция для разбора значения параметра булева типа

// Принимает на входе строку, которая может содержать 1, 0, on, off, yes, no, true, false 
// Возвращает 1 в случае true, 0 - в случае false и -1 в случае, если строка не распознана
//
int parse_boolean_param(char *param_value) {
    int ret = -1;
    unsigned long long like_string = 0;
    char *ptr = (char *)&like_string;
    int size = 0;
// Строки длиннее 8 символов даже не рассматриваем (они не поместятся в long long)
    for (; size < 9; size++) { 
        if (str[size])
            ptr[size] = str[size];
        else 
            break; 
    }
// Самая длинная строка - false. Если строка длинее, это заведомо неверный аргумент
    if (size > 5) return ret;

    // Битовая маска для побитового сложения со строкой (приведение к строчным буквам)
    static const unsigned long long to_lower = 0x2020202020202020;
    // приведение к строчным буквам
    string |= to_lower;

    // Через преобразование строки в число long long (до 8 символов char)
    // Проверялось на Win64 и Linux64
    switch (string) {
    case 2314885530818453553: // 1
    case 2314885530818473583: // on
    case 2314885530823910777: // yes
    case 2314885531981673076: ret = 1; break; // true
    case 2314885530818453552: // 0
    case 2314885530823059055: // off
    case 2314885530818473838: // no
    case 2314885828568703334: ret = 0; break; // false
    default: break;
    }
    return ret;
}

Управление МК Arduino циркуляционным насосом для полотенцесушителя

Возникла необходимость установки циркуляционного насоса в полотенцесушителе, поскольку через некоторое время после ремонта горячая вода перестала самотёком заходить в него. Был куплен сам насос и смонтирован куда надо, с физикой всё в порядке. А вот с менеджментом и экономикой стало как-то грустно: даже заявленные 25 Вт мощности насоса выливаются в 219 кВт*ч потребления за год, или, если в деньгах - примерно в 800 рублей добавочных расходов. Уменьшить энергопотребление можно различными путями: 1. Заменить насос на менее производительный и маломощный; 2. запускать насос эпизодически, по мере надобности (остывания полотенцесушителей). С первым пунктом всё сложно: чем ниже энергопотребление, тем выше цены. Второй пункт можно реализовать при помощи механических, электронных таймеров (300-700 рублей) или термореле ( >2000 рублей). Бытовые таймеры страдают либо грубым квантованием времени (шаг 15 минут в суточных механических) или недостаточным количеством программ (порядка 20 в электронных), ну а готовые термореле дороговаты и требуют перенастройки при изменении температуры теплоносителя.

В связи с вышесказанным родилась хотелка самонастраиваемого устройства регулировки работы насоса. Предъявляемые требования: низкое энергопотребление, автоматическая адаптация к температуре теплоносителя, самонастройка длительности времени работы/простоя.

Для реализации использовал МК семейства Arduino (Пробовал как Mega, так и Nano), цифровой термодатчик Dallas DS18xxx, резистор на 4,7кОм, реле рабочим напряжением 5В и коммутируемым 220В (я брал твердотельное OMRON), и блок питания на 5В постоянного тока. Разумеется, нужны ещё провода, пара клеммников и коробка, куда будет уложено всё богатство. Схема творения не сохранилась, но способы подключения реле и термодатчиков широко описаны в примерах на различных сайтах, ничего нового изобретено не было. Термодатчик был размещён на выходящей из полотенцесушителя трубе, ближе к магистрали.

Принцип работы.

При включении МК включается насос, начинает измеряться температура. Насос работает, пока растёт температура на выходной трубе. При прекращении роста температуры отсчитывается wait_before_pump_off секунд и насос отключается. Далее МК ожидает падения температуры на hysteresis градусов, после чего насос включается и цикл повторяется. Если по какой-то причине (изначально недостаточная температура теплоносителя, хорошая теплоизоляция полотенцесушителя, ошибочное значение параметров) температура не может снизиться на величину гистерезиса, контроллер отсчитывает max_state_time_counter секунд и включает насос. Эти три параметра влияют на соотношение и длительность интервалов вкл/выкл. На практике достигнуто соотношение работы/отдыха на уровне порядка 1/3,5 при данных значениях параметров.

Код скетча:

#include <OneWire.h>

struct TempRelay {
int PumpAddress; //Пин выхода на силовое реле
bool Heating; //Cостояние реле вкл/выкл
byte addr[8]; //Адрес датчика Dallas
byte type_s; //Тип датичка Dallas
int time_counter; //Время работы насоса
int state_time_counter; //Время нахождения в текущем состоянии
int higher_temp; //Максимально достигнутая температура * 16
};

int wait_before_pump_off = 60; // Время работы насоса после достижения максимальной температуры трубы
int hysteresis = 5*16; // Градусы Цельсия, умноженные на 16 (приведение к raw-формату dallas). Падение температуры для повторного включения насоса.
int max_state_time_counter = 3600; //Максимальное время нахождения в одном из состояний, в секундах.
int loop_delay_calculator_counter = 0;
int loop_timer = 0; // Время выполнения одного прохода функции loop. Для вычисления задержки.
int loop_first_time_check = 0;

TempRelay TR;
OneWire ds(2); // Работать с датчиками Dallas на пине 2 (нужен резистор на 4.7 кОм)


void setup(void) {
int i = 0;
Serial.println("Init...");
Serial.begin(9600);
TR = {4, false, {0,0,0,0,0,0,0,0},0, 0, 0, -55*16};
pinMode(TR.PumpAddress, OUTPUT);

ds.write(0x60);
int sensors = 0;
while (ds.search(TR.addr)){
sensors++;
};
ds.reset_search();
if (!sensors) {
Serial.println("No DS18xxx sensors.");
}
else
{
Serial.print("Sensors on the wire: ");
Serial.println(sensors);

Serial.print("HW id:");
for( i = 0; i < 8; i++) {
Serial.write(' ');
Serial.print(TR.addr[i], HEX);
}
if (OneWire::crc8(TR.addr, 7) != TR.addr[7]) {
Serial.println("CRC is not valid! Bad wire?");
return;
}
Serial.println();
}
// the first ROM byte indicates which chip
switch (TR.addr[0]) {
case 0x10:
Serial.println(" Chip = DS18S20"); // or old DS1820
TR.type_s = 1;
break;
case 0x28:
Serial.println(" Chip = DS18B20");
TR.type_s = 0;
break;
case 0x22:
Serial.println(" Chip = DS1822");
TR.type_s = 0;
break;
default:
Serial.println("Device is not a DS18x20 family device.");
return;
}
int16_t raw = read_dallas(&ds, TR.addr);
delay(1000); // Дать время на формирование результата
raw = read_dallas(&ds, TR.addr);
pump_on(&TR);
pump_diag(&TR, raw);
Serial.println("Init complete...");
}


void loop() {
//Засечка времени начала первого прохода
if (loop_delay_calculator_counter == 0)
loop_first_time_check = millis();

loop_delay_calculator_counter++;

int16_t raw = 0;
raw = read_dallas(&ds, TR.addr);


TR.state_time_counter++;
if (TR.Heating) {
if (TR.higher_temp < raw){
TR.higher_temp = raw;
TR.time_counter = 0;
}
else
TR.time_counter++;
if(TR.time_counter > wait_before_pump_off || TR.state_time_counter > max_state_time_counter){
pump_diag(&TR, (float)raw);
pump_off(&TR);
}
}
else
{
if (raw + hysteresis < TR.higher_temp || TR.state_time_counter > max_state_time_counter){
pump_diag(&TR, (float)raw);
pump_on(&TR);
}
}

if (loop_delay_calculator_counter > 100)
{
loop_delay_calculator_counter--;
delay(1000-loop_timer); //Подгоняем продолжительность цикла к 1 секунде. Приблизительно. Особого практического смысла эта фича не имеет, можно просто поставить задержку в 1 сек.
}
else
{
if (loop_delay_calculator_counter == 100) // Считаем среднее время выполнения одного цикла после прохождения 100 циклов
loop_timer = (millis() - loop_first_time_check) / 100;
}
TR.state_time_counter--;
TR.time_counter = 0;
}

int16_t read_dallas(OneWire *ds, byte *address){
byte data[12];
byte present = 0;
ds->reset();
ds->select(address);
// при паразитном питании нужна задержка.
ds->write(0x44, 1); // start conversion, with parasite power on at the end
// delay(1000); // maybe 750ms is enough, maybe not
// we might do a ds.depower() here, but the reset will take care of it.

present = ds->reset();
ds->select(address);
ds->write(0xBE);

// Serial.print(" Data = ");
// Serial.print(present, HEX);
// Serial.print(" ");
for (byte i = 0; i < 9; i++) { // we need 9 bytes
data[i] = ds->read();
// Serial.print(data[i], HEX);
// Serial.print(" ");
}

/* float celsius;
celsius = (float)raw_temp(TR.type_s, data) / 16.0;
Serial.print("Temperature = ");
Serial.print(celsius);
Serial.println(" Celsius");

*/
// Serial.print(" CRC=");
// Serial.print(OneWire::crc8(data, 8), HEX);
// Serial.println();
return raw_temp(TR.type_s, data);
}

void pump_diag(TempRelay *no, float raw){
float celsius = raw / 16.0;
Serial.print("Reached temperature = ");
Serial.print(celsius);
Serial.println(" Celsius");

Serial.print("Pump was ");
Serial.print(no->state_time_counter);
Serial.print(" sec. ");
Serial.println(no->Heating?"on":"off");
}

void pump_off(TempRelay *no){
digitalWrite(no->PumpAddress, HIGH); // почему в nano - HIGH, а в mega LOW?
no->Heating = false;
no->state_time_counter = 0;
}

void pump_on(TempRelay *no){
digitalWrite(no->PumpAddress, LOW); // почему в nano - LOW, а в mega HIGH?
no->Heating = true;
no->time_counter = 0;
no->higher_temp = 0;
no->state_time_counter = 0;
}

int16_t raw_temp(int type_s, byte *data)
{
// Convert the data to actual temperature
// because the result is a 16 bit signed integer, it should
// be stored to an "int16_t" type, which is always 16 bits
// even when compiled on a 32 bit processor.
int16_t raw = (data[1] << 8) | data[0];
if (type_s) {
raw = raw << 3; // 9 bit resolution default
if (data[7] == 0x10) {
// "count remain" gives full 12 bit resolution
raw = (raw & 0xFFF0) + 12 - data[6];
}
} else {
byte cfg = (data[4] & 0x60);
// at lower res, the low bits are undefined, so let's zero them
if (cfg == 0x00) raw = raw & ~7; // 9 bit resolution, 93.75 ms
else if (cfg == 0x20) raw = raw & ~3; // 10 bit res, 187.5 ms
else if (cfg == 0x40) raw = raw & ~1; // 11 bit res, 375 ms
//// default is 12 bit resolution, 750 ms conversion time
}
return raw;
}

Множественные формы числительных в Java

Возникла задача сделать многоязычное приложение на Java с выводом чисел и указанием того, что, собственно, выводится, например: 2 кирпича, 10 лет, 5 подушек и т.д. Известные решения (gettext, ChoiceFormat) меня не воодушевили, так как в одном случае надо порождать кучу файлов (по числу языков) и цеплять объёмную библиотеку, а во втором - некорректно обрабатывается русский язык. Решение было принято в пользу создания своего велосипеда и написании небольшого класса, который осуществляет выбор формы существительного при числительном в зависимости от языка и значения числительного.

Информация для "мяса" класса была взята отсюда: http://translate.sourceforge.net/wiki/l10n/pluralforms. Использовать, правда, её с ходу не получилось, так как она содержит некоторые ошибки в синтаксисе, впрочем, не фатальные.

Использование класса осуществляется следующим образом: при создании объекта вызывается конструктор, который либо сам пытается выяснить текущую локаль, либо ему её указывают принудительно. Далее, в нём производится сопоставление кода локали ISO и внутреннего "кода языка". Собственно говоря, это самая долгая операция, поэтому лучше её делать как можно реже. После этого можно вызывать метод convert(int value, "текст1|текст2|..."); на вход которого подаётся число и несколько форм существительного, которые представляют собой одну строку и разделяются вертикальной чертой.

 

 

// Data about plural forms get here:
// http://translate.sourceforge.net/wiki/l10n/pluralforms

import java.util.Locale;

public class i10n_pluralforms {
	private int lang_id = 0;
	private int nplurals = 0;
	private String language = "";

	public i10n_pluralforms (Locale locale){
		language = locale.getLanguage();
		lang_id = languageToIntId(language);
		return;
	}
	public i10n_pluralforms (){
		Locale locale = Locale.getDefault();
		language = locale.getLanguage();
		lang_id = languageToIntId(language);
		return;
	}

private int languageToIntId(String language)
{
		 if (language.compareToIgnoreCase("ach") == 0){lang_id = 1;}
	else if (language.compareToIgnoreCase("af") == 0){lang_id = 2;}
	else if (language.compareToIgnoreCase("ak") == 0){lang_id = 3;}
	else if (language.compareToIgnoreCase("sq") == 0){lang_id = 4;}
	else if (language.compareToIgnoreCase("am") == 0){lang_id = 5;}
	else if (language.compareToIgnoreCase("ar") == 0){lang_id = 6;}
	else if (language.compareToIgnoreCase("an") == 0){lang_id = 7;}
	else if (language.compareToIgnoreCase("es_AR") == 0){lang_id = 8;}
	else if (language.compareToIgnoreCase("hy") == 0){lang_id = 9;}
	else if (language.compareToIgnoreCase("ast") == 0){lang_id = 10;}
	else if (language.compareToIgnoreCase("ay") == 0){lang_id = 11;}
	else if (language.compareToIgnoreCase("az") == 0){lang_id = 12;}
	else if (language.compareToIgnoreCase("eu") == 0){lang_id = 13;}
	else if (language.compareToIgnoreCase("be") == 0){lang_id = 14;}
	else if (language.compareToIgnoreCase("bn") == 0){lang_id = 15;}
	else if (language.compareToIgnoreCase("bs") == 0){lang_id = 16;}
	else if (language.compareToIgnoreCase("pt_BR") == 0){lang_id = 17;}
	else if (language.compareToIgnoreCase("br") == 0){lang_id = 18;}
	else if (language.compareToIgnoreCase("bg") == 0){lang_id = 19;}
	else if (language.compareToIgnoreCase("ca") == 0){lang_id = 20;}
	else if (language.compareToIgnoreCase("cgg") == 0){lang_id = 21;}
	else if (language.compareToIgnoreCase("zh") == 0){lang_id = 22;}
	else if (language.compareToIgnoreCase("kw") == 0){lang_id = 23;}
	else if (language.compareToIgnoreCase("hr") == 0){lang_id = 24;}
	else if (language.compareToIgnoreCase("cs") == 0){lang_id = 25;}
	else if (language.compareToIgnoreCase("da") == 0){lang_id = 26;}
	else if (language.compareToIgnoreCase("nl") == 0){lang_id = 27;}
	else if (language.compareToIgnoreCase("dz") == 0){lang_id = 28;}
	else if (language.compareToIgnoreCase("en") == 0){lang_id = 29;}
	else if (language.compareToIgnoreCase("eo") == 0){lang_id = 30;}
	else if (language.compareToIgnoreCase("et") == 0){lang_id = 31;}
	else if (language.compareToIgnoreCase("fo") == 0){lang_id = 32;}
	else if (language.compareToIgnoreCase("fil") == 0){lang_id = 33;}
	else if (language.compareToIgnoreCase("fi") == 0){lang_id = 34;}
	else if (language.compareToIgnoreCase("fr") == 0){lang_id = 35;}
	else if (language.compareToIgnoreCase("fy") == 0){lang_id = 36;}
	else if (language.compareToIgnoreCase("fur") == 0){lang_id = 37;}
	else if (language.compareToIgnoreCase("ff") == 0){lang_id = 38;}
	else if (language.compareToIgnoreCase("gl") == 0){lang_id = 39;}
	else if (language.compareToIgnoreCase("ka") == 0){lang_id = 40;}
	else if (language.compareToIgnoreCase("de") == 0){lang_id = 41;}
	else if (language.compareToIgnoreCase("el") == 0){lang_id = 42;}
	else if (language.compareToIgnoreCase("gu") == 0){lang_id = 43;}
	else if (language.compareToIgnoreCase("gun") == 0){lang_id = 44;}
	else if (language.compareToIgnoreCase("ha") == 0){lang_id = 45;}
	else if (language.compareToIgnoreCase("he") == 0){lang_id = 46;}
	else if (language.compareToIgnoreCase("hi") == 0){lang_id = 47;}
	else if (language.compareToIgnoreCase("hu") == 0){lang_id = 48;}
	else if (language.compareToIgnoreCase("is") == 0){lang_id = 49;}
	else if (language.compareToIgnoreCase("id") == 0){lang_id = 50;}
	else if (language.compareToIgnoreCase("ia") == 0){lang_id = 51;}
	else if (language.compareToIgnoreCase("ga") == 0){lang_id = 52;}
	else if (language.compareToIgnoreCase("it") == 0){lang_id = 53;}
	else if (language.compareToIgnoreCase("ja") == 0){lang_id = 54;}
	else if (language.compareToIgnoreCase("jv") == 0){lang_id = 55;}
	else if (language.compareToIgnoreCase("kn") == 0){lang_id = 56;}
	else if (language.compareToIgnoreCase("csb") == 0){lang_id = 57;}
	else if (language.compareToIgnoreCase("kk") == 0){lang_id = 58;}
	else if (language.compareToIgnoreCase("km") == 0){lang_id = 59;}
	else if (language.compareToIgnoreCase("ko") == 0){lang_id = 60;}
	else if (language.compareToIgnoreCase("ku") == 0){lang_id = 61;}
	else if (language.compareToIgnoreCase("ky") == 0){lang_id = 62;}
	else if (language.compareToIgnoreCase("lo") == 0){lang_id = 63;}
	else if (language.compareToIgnoreCase("lv") == 0){lang_id = 64;}
	else if (language.compareToIgnoreCase("lb") == 0){lang_id = 65;}
	else if (language.compareToIgnoreCase("ln") == 0){lang_id = 66;}
	else if (language.compareToIgnoreCase("lt") == 0){lang_id = 67;}
	else if (language.compareToIgnoreCase("jbo") == 0){lang_id = 68;}
	else if (language.compareToIgnoreCase("mk") == 0){lang_id = 69;}
	else if (language.compareToIgnoreCase("mai") == 0){lang_id = 70;}
	else if (language.compareToIgnoreCase("mg") == 0){lang_id = 71;}
	else if (language.compareToIgnoreCase("ms") == 0){lang_id = 72;}
	else if (language.compareToIgnoreCase("ml") == 0){lang_id = 73;}
	else if (language.compareToIgnoreCase("mt") == 0){lang_id = 74;}
	else if (language.compareToIgnoreCase("mnk") == 0){lang_id = 75;}
	else if (language.compareToIgnoreCase("mi") == 0){lang_id = 76;}
	else if (language.compareToIgnoreCase("arn") == 0){lang_id = 77;}
	else if (language.compareToIgnoreCase("mr") == 0){lang_id = 78;}
	else if (language.compareToIgnoreCase("mfe") == 0){lang_id = 79;}
	else if (language.compareToIgnoreCase("mn") == 0){lang_id = 80;}
	else if (language.compareToIgnoreCase("nah") == 0){lang_id = 81;}
	else if (language.compareToIgnoreCase("nap") == 0){lang_id = 82;}
	else if (language.compareToIgnoreCase("ne") == 0){lang_id = 83;}
	else if (language.compareToIgnoreCase("se") == 0){lang_id = 84;}
	else if (language.compareToIgnoreCase("nso") == 0){lang_id = 85;}
	else if (language.compareToIgnoreCase("no") == 0){lang_id = 86;}
	else if (language.compareToIgnoreCase("nb") == 0){lang_id = 87;}
	else if (language.compareToIgnoreCase("nn") == 0){lang_id = 88;}
	else if (language.compareToIgnoreCase("oc") == 0){lang_id = 89;}
	else if (language.compareToIgnoreCase("or") == 0){lang_id = 90;}
	else if (language.compareToIgnoreCase("pap") == 0){lang_id = 91;}
	else if (language.compareToIgnoreCase("ps") == 0){lang_id = 92;}
	else if (language.compareToIgnoreCase("fa") == 0){lang_id = 93;}
	else if (language.compareToIgnoreCase("pms") == 0){lang_id = 94;}
	else if (language.compareToIgnoreCase("pl") == 0){lang_id = 95;}
	else if (language.compareToIgnoreCase("pt") == 0){lang_id = 96;}
	else if (language.compareToIgnoreCase("pa") == 0){lang_id = 97;}
	else if (language.compareToIgnoreCase("ro") == 0){lang_id = 98;}
	else if (language.compareToIgnoreCase("rm") == 0){lang_id = 99;}
	else if (language.compareToIgnoreCase("ru") == 0){lang_id = 100;}
	else if (language.compareToIgnoreCase("sco") == 0){lang_id = 101;}
	else if (language.compareToIgnoreCase("gd") == 0){lang_id = 102;}
	else if (language.compareToIgnoreCase("sr") == 0){lang_id = 103;}
	else if (language.compareToIgnoreCase("si") == 0){lang_id = 104;}
	else if (language.compareToIgnoreCase("sk") == 0){lang_id = 105;}
	else if (language.compareToIgnoreCase("sl") == 0){lang_id = 106;}
	else if (language.compareToIgnoreCase("so") == 0){lang_id = 107;}
	else if (language.compareToIgnoreCase("son") == 0){lang_id = 108;}
	else if (language.compareToIgnoreCase("es") == 0){lang_id = 109;}
	else if (language.compareToIgnoreCase("su") == 0){lang_id = 110;}
	else if (language.compareToIgnoreCase("sw") == 0){lang_id = 111;}
	else if (language.compareToIgnoreCase("sv") == 0){lang_id = 112;}
	else if (language.compareToIgnoreCase("tg") == 0){lang_id = 113;}
	else if (language.compareToIgnoreCase("ta") == 0){lang_id = 114;}
	else if (language.compareToIgnoreCase("tt") == 0){lang_id = 115;}
	else if (language.compareToIgnoreCase("te") == 0){lang_id = 116;}
	else if (language.compareToIgnoreCase("th") == 0){lang_id = 117;}
	else if (language.compareToIgnoreCase("bo") == 0){lang_id = 118;}
	else if (language.compareToIgnoreCase("ti") == 0){lang_id = 119;}
	else if (language.compareToIgnoreCase("tr") == 0){lang_id = 120;}
	else if (language.compareToIgnoreCase("tk") == 0){lang_id = 121;}
	else if (language.compareToIgnoreCase("uk") == 0){lang_id = 122;}
	else if (language.compareToIgnoreCase("ur") == 0){lang_id = 123;}
	else if (language.compareToIgnoreCase("ug") == 0){lang_id = 124;}
	else if (language.compareToIgnoreCase("uz") == 0){lang_id = 125;}
	else if (language.compareToIgnoreCase("vi") == 0){lang_id = 126;}
	else if (language.compareToIgnoreCase("wa") == 0){lang_id = 127;}
	else if (language.compareToIgnoreCase("cy") == 0){lang_id = 128;}
	else if (language.compareToIgnoreCase("wo") == 0){lang_id = 129;}
	else if (language.compareToIgnoreCase("sah") == 0){lang_id = 130;}
	else if (language.compareToIgnoreCase("yo") == 0){lang_id = 131;}
	else lang_id = 0;
	return lang_id;
}

public String convert (int integer, String pluralforms){
	int plural = 0;
	int n = Math.abs(integer);

	switch (lang_id)
	{
	case 1: nplurals=2; plural=(n > 1?1:0); break;
	case 2: nplurals=2; plural=(n == 1?0:1); break;
	case 3: nplurals=2; plural=(n > 1?1:0); break;
	case 4: nplurals=2; plural=(n == 1?0:1); break;
	case 5: nplurals=2; plural=(n > 1?1:0); break;
	case 6: nplurals=6; plural=(n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 && n%100<=10 ? 3 : n%100>=11 ? 4 : 5); break;
	case 7: nplurals=2; plural=(n == 1?0:1); break;
	case 8: nplurals=2; plural=(n == 1?0:1); break;
	case 9: nplurals=2; plural=(n == 1?0:1); break;
	case 10: nplurals=2; plural=(n == 1?0:1); break;
	case 11: nplurals=1; plural=0; break;
	case 12: nplurals=2; plural=(n == 1?0:1); break;
	case 13: nplurals=2; plural=(n == 1?0:1); break;
	case 14: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2); break;
	case 15: nplurals=2; plural=(n == 1?0:1); break;
	case 16: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2); break;
	case 17: nplurals=2; plural=(n == 1?0:1); break;
	case 18: nplurals=2; plural=(n > 1?1:0); break;
	case 19: nplurals=2; plural=(n == 1?0:1); break;
	case 20: nplurals=2; plural=(n == 1?0:1); break;
	case 21: nplurals=1; plural=0; break;
	case 22: nplurals=1; plural=0; break;
	case 23: nplurals=4; plural= ((n==1) ? 0 : (n==2) ? 1 : (n == 3) ? 2 : 3); break;
	case 24: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2); break;
	case 25: nplurals=3; plural=((n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2); break;
	case 26: nplurals=2; plural=(n == 1?0:1); break;
	case 27: nplurals=2; plural=(n == 1?0:1); break;
	case 28: nplurals=1; plural=0; break;
	case 29: nplurals=2; plural=(n == 1?0:1); break;
	case 30: nplurals=2; plural=(n == 1?0:1); break;
	case 31: nplurals=2; plural=(n == 1?0:1); break;
	case 32: nplurals=2; plural=(n == 1?0:1); break;
	case 33: nplurals=2; plural=(n > 1?1:0); break;
	case 34: nplurals=2; plural=(n == 1?0:1); break;
	case 35: nplurals=2; plural=(n > 1?1:0); break;
	case 36: nplurals=2; plural=(n == 1?0:1); break;
	case 37: nplurals=2; plural=(n == 1?0:1); break;
	case 38: nplurals=2; plural=(n == 1?0:1); break;
	case 39: nplurals=2; plural=(n == 1?0:1); break;
	case 40: nplurals=1; plural=0; break;
	case 41: nplurals=2; plural=(n == 1?0:1); break;
	case 42: nplurals=2; plural=(n == 1?0:1); break;
	case 43: nplurals=2; plural=(n == 1?0:1); break;
	case 44: nplurals=2; plural =(n > 1?1:0); break;
	case 45: nplurals=2; plural=(n == 1?0:1); break;
	case 46: nplurals=2; plural=(n == 1?0:1); break;
	case 47: nplurals=2; plural=(n == 1?0:1); break;
	case 48: nplurals=2; plural=(n == 1?0:1); break;
	case 49: nplurals=2; plural=((n%10!=1 || n%100==11)?1:0); break;
	case 50: nplurals=1; plural=0; break;
	case 51: nplurals=2; plural=(n == 1?0:1); break;
	case 52: nplurals=5; plural=(n==1 ? 0 : n==2 ? 1 : n<7 ? 2 : n<11 ? 3 : 4); break;
	case 53: nplurals=2; plural=(n == 1?0:1); break;
	case 54: nplurals=1; plural=0; break;
	case 55: nplurals=2; plural=(n == 0?0:1); break;
	case 56: nplurals=2; plural=(n == 1?0:1); break;
	case 57: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2); break;
	case 58: nplurals=1; plural=0; break;
	case 59: nplurals=1; plural=0; break;
	case 60: nplurals=1; plural=0; break;
	case 61: nplurals=2; plural=(n == 1?0:1); break;
	case 62: nplurals=1; plural=0; break;
	case 63: nplurals=1; plural=0; break;
	case 64: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n != 0 ? 1 : 2); break;
	case 65: nplurals=2; plural=(n == 1?0:1); break;
	case 66: nplurals=2; plural=(n > 1?1:0); break;
	case 67: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && (n%100<10 || n%100>=20) ? 1 : 2); break;
	case 68: nplurals=1; plural=0; break;
	case 69: nplurals=2; plural= (n==1 || n%10==1 ? 0 : 1); break;
	case 70: nplurals=2; plural=(n == 1?0:1); break;
	case 71: nplurals=2; plural=(n > 1?1:0); break;
	case 72: nplurals=1; plural=0; break;
	case 73: nplurals=2; plural=(n == 1?0:1); break;
	case 74: nplurals=4; plural=(n==1 ? 0 : n==0 || ( n%100>1 && n%100<11) ? 1 : (n%100>10 && n%100<20 ) ? 2 : 3); break;
	case 75: nplurals=3; plural=(n==0 ? 0 : n==1 ? 1 : 2); break;
	case 76: nplurals=2; plural=(n > 1?1:0); break;
	case 77: nplurals=2; plural=(n > 1?1:0); break;
	case 78: nplurals=2; plural=(n == 1?0:1); break;
	case 79: nplurals=2; plural=(n > 1?1:0); break;
	case 80: nplurals=2; plural=(n == 1?0:1); break;
	case 81: nplurals=2; plural=(n == 1?0:1); break;
	case 82: nplurals=2; plural=(n == 1?0:1); break;
	case 83: nplurals=2; plural=(n == 1?0:1); break;
	case 84: nplurals=2; plural=(n == 1?0:1); break;
	case 85: nplurals=2; plural=(n == 1?0:1); break;
	case 86: nplurals=2; plural=(n == 1?0:1); break;
	case 87: nplurals=2; plural=(n == 1?0:1); break;
	case 88: nplurals=2; plural=(n == 1?0:1); break;
	case 89: nplurals=2; plural=(n > 1?1:0); break;
	case 90: nplurals=2; plural=(n == 1?0:1); break;
	case 91: nplurals=2; plural=(n == 1?0:1); break;
	case 92: nplurals=2; plural=(n == 1?0:1); break;
	case 93: nplurals=1; plural=0; break;
	case 94: nplurals=2; plural=(n == 1?0:1); break;
	case 95: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2); break;
	case 96: nplurals=2; plural=(n == 1?0:1); break;
	case 97: nplurals=2; plural=(n == 1?0:1); break;
	case 98: nplurals=3; plural=(n==1 ? 0 : (n==0 || (n%100 > 0 && n%100 < 20)) ? 1 : 2); break;
	case 99: nplurals=2; plural=(n == 1?0:1); break;
	case 100: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2); break;
	case 101: nplurals=2; plural=(n == 1?0:1); break;
	case 102: nplurals=4; plural=(n==1 || n==11) ? 0 : (n==2 || n==12) ? 1 : (n > 2 && n < 20) ? 2 : 3; break;
	case 103: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2); break;
	case 104: nplurals=2; plural=(n == 1?0:1); break;
	case 105: nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2; break;
	case 106: nplurals=4; plural=(n%100==1 ? 1 : n%100==2 ? 2 : n%100==3 || n%100==4 ? 3 : 0); break;
	case 107: nplurals=2; plural=(n == 1?0:1); break;
	case 108: nplurals=2; plural=(n == 1?0:1); break;
	case 109: nplurals=2; plural=(n == 1?0:1); break;
	case 110: nplurals=1; plural=0; break;
	case 111: nplurals=2; plural=(n == 1?0:1); break;
	case 112: nplurals=2; plural=(n == 1?0:1); break;
	case 113: nplurals=2; plural=(n > 1?1:0); break;
	case 114: nplurals=2; plural=(n == 1?0:1); break;
	case 115: nplurals=1; plural=0; break;
	case 116: nplurals=2; plural=(n == 1?0:1); break;
	case 117: nplurals=1; plural=0; break;
	case 118: nplurals=1; plural=0; break;
	case 119: nplurals=2; plural=(n > 1?1:0); break;
	case 120: nplurals=2; plural=(n > 1?1:0); break;
	case 121: nplurals=2; plural=(n == 1?0:1); break;
	case 122: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2); break;
	case 123: nplurals=2; plural=(n == 1?0:1); break;
	case 124: nplurals=1; plural=0;; break;
	case 125: nplurals=2; plural=(n > 1?1:0); break;
	case 126: nplurals=1; plural=0; break;
	case 127: nplurals=2; plural=(n > 1?1:0); break;
	case 128: nplurals=4; plural= ((n==1) ? 0 : (n==2) ? 1 : (n != 8 && n != 11) ? 2 : 3); break;
	case 129: nplurals=1; plural=0; break;
	case 130: nplurals=1; plural=0; break;
	case 131: nplurals=2; plural=(n == 1?0:1); break;

	}
	if(nplurals == 0) return "i10n_pluralforms: Oops! Unknown language " + language;
	String parsedString[] = pluralforms.split("\\|");
	if(parsedString.length < nplurals) return "i10n_pluralforms: Oops! Not enough plural forms in \"" + pluralforms + "\" for language " + language;

    return parsedString[plural];	
}

}


Поддержка PostgreSQL в daloRADIUS

Некоторое время назад искал веб-интерфейс к RADIUS-серверу freeradius с поддержкой СУБД PostgreSQL. Поиски оказались не очень результативными, таких продуктов оказалось только два: веб-интерфейс, идущий в комплекте с исходниками freeradius, и daloRADIUS. Веб-интерфейс freeradius не запустился вовсе (ибо PHP3), а вот daloRADIUS с PgSQL работать не то чтобы отказалась, но регулярно выдавала ошибки.

После анализа исходных текстов стало понятно, что daloRADIUS, хоть и спроектирован для работы с многими СУБД, на данном этапе разработки (Версия 0.9-8) заточен под работу с MySQL, так как все SQL-запросы сделаны с учётом синтаксиса этой СУБД.

В результате для собственных нужд я внёс изменения в логику создания SQL-запросов, которая теперь обеспечивает работу как с MySQL, так и PostgreSQL. Внесённые изменения были оформлены в виде патча к версии daloRADIUS 0.9-8 и содержат, помимо изменённых запросов, SQL-скрипт для создания необходимых для работы daloRADIUS таблиц и записей.

Патч: daloradius-0.9-8-pgsql.patch.bz2

 

Joomla 1.5

Joomla 1.5. Как её есть.

При установке столкнулся с проблемами:

1. Нет русифицированного интерфейса.

2. Не печатаются PDF с русскими буквами.

Решение первой проблемы было найдено на joomla.ru, пусть и не совсем безгрешное: скачать.

Вторая проблема решилась скачиванием дистрибутива TCPDF - PDF class for PHP, выдиранием из него шрифта dejavusans и прописыванием его в языковом модуле в качестве шрифта для PDF.