программирование
Магия с сенсором освещения
Помимо сенсора внезапного движения многие новые «Макбуки» оборудованы сенсором освещённости, он едва заметно располагается рядом с видеокамерой.
Мне интересно стало попробовать поработать и с ним тоже. На этот раз цель была — научиться эффектным жестом блокировать свой ноут. Конечно, ложных срабатываний масса —

Код на Си получился чуть более ста строк и на этот раз я распознаю шаблон поведения, он задаётся в подстановке
При сканировании чисел с датчика я всегда рассматриваю десять значений от которых считаю среднее и каждое значение рассматриваю как отклонение от этого среднего, собственно, вот код:
// Магические пассы, улавливаемые через датчик освещённости
// Written by Evgeny Stepanischev Jun 2013
// gcc lightlock.c -framework IOKit -framework ApplicationServices -O3 -o lightlock
#include <IOKit/IOKitLib.h>
#include <CoreFoundation/CoreFoundation.h>
#include <ApplicationServices/ApplicationServices.h>
#include <unistd.h>
#define WINDOWSIZE 10
#define GESTURES "+vvv000-^^^"
#define UPDATEINTERVAL .01
const kGetSensorReadingID = 0;
int makeReaction(uint64_t current);
static io_connect_t port = 0;
void macLock () {
CGSCreateLoginSession(NULL);
}
void updateTimerCallBack(CFRunLoopTimerRef timer, void *info) {
IOItemCount osize = 2;
uint64_t values[osize];
kern_return_t ret = IOConnectCallMethod(port, kGetSensorReadingID,
NULL, 0, NULL, 0, values, &osize, NULL, 0);
if (ret == KERN_SUCCESS) {
if (makeReaction(values[0])) {
macLock();
}
}
else if (ret != kIOReturnBusy) {
mach_error("IOConnectCallMethod: ", ret);
exit(-3);
}
}
int main() {
io_service_t service = IOServiceGetMatchingService(
kIOMasterPortDefault, IOServiceMatching("AppleLMUController")
);
if (service == kIOReturnNoDevice) {
fprintf(stderr, "Cannot find Ambient Light Sensor\n");
exit(-1);
}
kern_return_t ret = IOServiceOpen(service, mach_task_self(), 0, &port);
IOObjectRelease(service);
if (ret != KERN_SUCCESS) {
mach_error("IOServiceOpen:", ret);
exit(-2);
}
CFRunLoopTimerRef timer = CFRunLoopTimerCreate(
kCFAllocatorDefault,
CFAbsoluteTimeGetCurrent() + UPDATEINTERVAL,
UPDATEINTERVAL, 0, 0, updateTimerCallBack, NULL
);
CFRunLoopAddTimer(CFRunLoopGetCurrent(), timer, kCFRunLoopDefaultMode);
CFRunLoopRun();
exit(0);
}
int checkMask(char mask, const int64_t diff, const int64_t prevdiff) {
switch (mask) {
case '+': return diff > 0;
case '-': return diff < 0;
case '0': return diff == 0;
case 'v': return prevdiff > diff;
case '^': return diff > prevdiff;
}
return 0;
}
int makeReaction(uint64_t current) {
static uint64_t window[WINDOWSIZE];
static uint64_t prevdiff = 0;
static gesturepos = 0;
uint64_t average = current;
int i, notzerocnt = 1;
for (i = WINDOWSIZE-1; i > 0; i--) {
if (window[i] = window[i-1]) {
average += window[i];
notzerocnt++;
}
}
window[0] = current;
average /= notzerocnt;
int64_t diff = average - current;
if (checkMask(GESTURES[gesturepos], diff, prevdiff)) {
gesturepos++;
} else {
if (!gesturepos || !checkMask(GESTURES[gesturepos-1], diff, prevdiff)) {
gesturepos = 0;
}
}
prevdiff = diff;
return gesturepos == sizeof GESTURES - 1;
}В функции main, в конце стоит магический код, который нужен для того, чтобы вызываемые мною внутренние шестерёнки «Мака» благополучно крутились, иначе функция CGSCreateLoginSession будет ждать когда ей перепадёт процессорного времени.
Ну и традиционно, если
Многозначная логика в действии
Это прекрасно чуть более, чем полностью, друзья!

На скриншоте
Мне ещё сама реализация нравится. Это же надо обладать таким сломанным мозгом, чтобы так написать. Ну почему вот так не сделать?
if (!_ok && Math.random() <= 0.1) {
return res;
}MacGreener C#

Переписал МакГринер с Си на Си#. Основной код
Весь код разбит, преимущественно, на три класса — основной класс, где выполняется приложение, класс IOKit — низкоуровневые и чуть более высокоуровневые биндинги для библиотек МакОСи и IOKitMotionSensor — ещё более высокоуровневая обёртка над куском кода для опроса датчика.
Есть ещё два вспомогательных класса — IOKitException, класс кидаемых исключений и IOKitObject — класс всех ресурсов, которые требуется высвобождать (он следит за своевременным их высвобождением при помощи блока using).
Не знаю какую версию я буду дальше развивать (и буду ли), но версия на Си# более прожорлива —
Хорошо, что мой братишка давно программирует на этом языке, с его консультациями в некоторых тонкостях удаётся разобраться быстрее.
Как узнать, что пользователь печатает веб-страницу на принтере?
У нас тут задача в процессе производства продукта появилась — занести в лог, что пользователь сделал попытку печати
Придумал такое решение — перед печатью браузер накладывает стиль CSS для печати, если он есть, этим можно воспользоваться:
@media print {
body {
background: url(http://example.org/user-tries-to-print.php);
}
}Браузер попытается загрузить фоновое изображение (и загружает, я попробовал), выполнится серверный скрипт и положит в логи то, что нужно.
Первые эксперименты с C#
Поучился обращаться к динамическим библиотекам «Мака» (dylib)
using System;
using System.Runtime.InteropServices;
using MonoMac;
using MonoMac.CoreFoundation;
using MonoMac.ObjCRuntime;
namespace MacGreenerFragment
{
class SleepAwake
{
[DllImport ("IOKit", CharSet = CharSet.Ansi)]
static extern int IORegistryEntryFromPath (IntPtr masterPort, string path);
[DllImport ("IOKit")]
static extern void IOObjectRelease (int @object);
[DllImport ("IOKit")]
static extern void IORegistryEntrySetCFProperty(int entry, IntPtr propertyName, IntPtr property);
static void macSleepAwake(bool sleep)
{
var handle = Dlfcn.dlopen (Constants.CoreFoundationLibrary, 0);
IntPtr True = Dlfcn.GetIntPtr (handle, "kCFBooleanTrue");
IntPtr False = Dlfcn.GetIntPtr (handle, "kCFBooleanFalse");
int r = IORegistryEntryFromPath(IntPtr.Zero, "IOService:/IOResources/IODisplayWrangler");
if (r != 0) {
IORegistryEntrySetCFProperty(r, ((CFString)"IORequestIdle").Handle, sleep ? True : False);
IOObjectRelease(r);
}
}
}
}На Си это 11 строк.
exe2com под Линуксом и «Маком»
Если вам
char *strlwr(char *string) {
unsigned char *p;
for (p = (unsigned char *)string; *p != 0; ++p)
*p = (unsigned char)tolower (*p);
return string;
}gcc -fno-builtin-exit exe2com.c -o exec2comПосле этого утилита нормально работает.
Обнаружение Ретины
Поскольку у меня теперь «Макбук» с Ретиной, я изучаю вопрос как сделать сайты пригодными к отображению на таких экранах. Возможно читатели с Ретинами уже заметили
Наиболее животрепещущий вопрос — подготовка изображений для Ретины.
Напомню, что Ретина — технология Эпл, ставшая уже нарицательной, заключающаяся в примерении экранов высокого разрешения (например, на моём ноуте это 2560×1600) с отображением на нём обычной графики так, как бы это выглядело на экранах вдвое низкого разрешения. При этом шрифт смотрится просто великолепно, как на бумаге, а вот с картинками беда.
В вебе бо́льшая часть графики выглядит не очень (хотя масса сайтов уже подготовилась). Для того, чтобы стало хорошо, все изображения надо готовить вдвое большего размера, при этом указывая для них в коде вдвое меньший размер, тогда проявляется вся мощь Ретины — будет видно самые мелкие детали изображения и пропадёт размытие.
Пока ещё не у всех Ретины, хочется, чтобы клиенты, у которых её нет, не грузили лишнего. К сожалению, загвоздка в том, что браузеры пока не научились сообщать серверу, что мы имеем дело с таким (пока) необычным экраном и есть только клиенские способы выяснить это.
Появилось уже несколько библиотек, которые на стороне клиента делают замены картинок на подходящие под тип экрана. Это всё равно вызывает лиший трафик, так как некоторые картинки успевают загрузиться, а если изображений много, замена, бывает, подтормаживает.
Насколько я вижу, даже в Эпл не придумали достойной альтернативы этой техники и у них сайт «улучшается» по мере загрузки — сначала грузятся обычные изображения, потом «ретиновые», если это необходимо.
Вообще, у Джипега, например, есть прогрессивная загрузка — по мере загрузки «прогрессивное» изображение не грузится сверху вниз, а становится всё чётче и чётче (хорошая иллюстрация этого есть в одном из параграфов «Ководства»). Надо будет поэксперементировать в эту сторону — нельзя ли прервать передачу на нужной детализации, если экран не «ретиновый».
А пока, у меня такое решение. Используем
На стороне клиента первой строкой каждой нашей страницы ставим проверку по «ретиновые» экраны с установкой «печенья»:
<script>if(window.devicePixelRatio>=2)document.cookie='retina=1'</script>Этот скрипт выполнится первым и все дальнейшие запросы на сервер будут содержать строку «retina=1», если мы имеем дело с Ретиной. Все изображения, для которых у нас есть две копии — обычная и «ретиновая» (в два раза большая) называем одинаково, но с постфиксом «@2x» для большей, при этом в коде указываем данные разрешения для более мелкой картинки:
<img src="/images/owl@2x.jpg" width="400" height="560" alt="Совушка">Сейчас, конечно, Апач теряет популярность, но я воспользуюсь синтаксисом mod_rewrite, желающие могут переделать в Энжин Икс
RewriteEngine On
RewriteCond %{HTTP_COOKIE} !retina=1
RewriteRule (.*)@2x\.(gif|jpe?g|png|webp) $1.$2 [L,NC]Этот код означает, что если не установлено «печенье» «retina=1», то нужно убрать постфикс «@2x» из имени картинки.
В итоге, если у нас Ретина, «печенька» ставится в самом начале, все дальнейшие запросы к серверу идут с ней и картинки грузятся как указано в коде — например, если указано «owl@2x.jpg», то эта картинка и грузится.
Если у нас обычный экран, «печеньки» нет, запросы к серверу идут без неё и из картинок вырезается постфикс «@2x», т. е. картинка «cat.jpg» грузится по своему имени, а вместо «owl@2x.jpg» загрузится «owl.jpg».
Voilà!
MacGreener
Выложил утилиту, которую описывал недавно (используя датчики «Макбука Про», заставляет его засыпать, если его поднять со стола) на «Гитхаб», назвал её MacGreener. Не знаю как сейчас, а раньше «зелёный» (green) в технике ассоциировался с экономией электроэнергии, так как этой утилитой я делаю «Мак» «зеленее» (greener), отсюда и название.
Я немного изменил алгоритм и внёс ещё одно важное изменение: теперь, если «Мак» поставить на стол, он проснётся. Напомню, программа работает только при наличии соотвествующего датчика. Рядом с исходным кодом лежит скомпилированная версия.
Берём «Макбук» со стола, экран гаснет или блокируется
В предыдущей заметке я писал, что обнаружил сенсор внезапного движения у своего «Макбука». Решил извлечь практическую пользу из этого — написал программу на Си, которая отправляет ноут в спячку, стоит поднять его со стола:
// gcc -framework CoreFoundation -framework IOKit gethensleep.c -o getthensleep
// Evgeny Stepanischev May 2013
#include <IOKit/IOKitLib.h>
#include <CoreFoundation/CoreFoundation.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <stdbool.h>
#include <sys/param.h>
#define RETURNONFAIL(code) do { if (errcode != KERN_SUCCESS) {\
fprintf(stderr, "Error code: 0x%Xn", GETCODE(errcode));\
return code;\
} } while (0)
#define GETCODE(err) ((err)&0x3fff)
#define THRESHOLD 0.2
typedef struct {
int16_t x;
int16_t y;
int16_t z;
} ostruct;
void macSleep() {
io_registry_entry_t r = IORegistryEntryFromPath(
kIOMasterPortDefault,
"IOService:/IOResources/IODisplayWrangler"
);
if (r) {
IORegistryEntrySetCFProperty(r, CFSTR("IORequestIdle"), kCFBooleanTrue);
IOObjectRelease(r);
}
}
int main() {
mach_port_t masterPort;
kern_return_t errcode;
io_iterator_t iterator;
io_object_t service;
io_connect_t connect;
void *in;
ostruct *out;
size_t osize = 40, isize = 40;
errcode = IOMasterPort(MACH_PORT_NULL, &masterPort);
RETURNONFAIL(-1);
CFMutableDictionaryRef matchingDictionary = IOServiceMatching("SMCMotionSensor");
errcode = IOServiceGetMatchingServices(masterPort, matchingDictionary, &iterator);
RETURNONFAIL(-2);
service = IOIteratorNext(iterator);
IOObjectRelease(iterator);
if (service == kIOReturnNoDevice) {
return -3;
}
errcode = IOServiceOpen(service, mach_task_self(), 0, &connect);
IOObjectRelease(service);
RETURNONFAIL(-4);
in = malloc(isize);
out = malloc(osize);
if (in == NULL || out == NULL) {
return -6;
}
memset(out, 0, osize);
memset(in, 1, isize);
int16_t prevz;
bool inited = false;
for(;;) {
errcode = IOConnectCallStructMethod(connect, 5, in, osize, out, &isize);
RETURNONFAIL(-5);
if (inited) {
float min = MIN(prevz, (*out).z);
float max = MAX(prevz, (*out).z);
if (max) {
float dev = 1 - min / max;
if (dev > THRESHOLD) {
macSleep();
}
}
} else {
inited = true;
}
prevz = (*out).z;
usleep(10000);
}
return 0;
}Порог срабатывания (0.2) я подобрал экспериментальным путём. Скомпилировать можно компилятором gcc (входит в XCode), строка компиляции указана первой в приведёном коде. Можно засунуть в автозагрузку и наслаждаться эффектом.
Кстати, если хотите, то можно вместо спячки заставить ноут заблокировать экран (т. е. показать экран логина), для этого надо вместо функции macSleep вызвать другую:
// нужно добавить ещё один ключ компиляции: -framework ApplicationServices
#include <ApplicationServices/ApplicationServices.h>
void macLock () {
CGSCreateLoginSession(NULL);
}Сенсор внезапного движения
Очень давно, на всех носимых устройствах, оснащённых жёстким диском (ноута, смартфонах) появился сенсор внезапного движения. В жёстком диске считывающая головка находится, не касаясь, над поверхностью диска с данными. Если ноут резко поднять, головка может чиркнуть по поверхности диска и испортить его. Для этого нужен сенсор внезапного движения (акселерометр), он реагирует на ускорение в любую сторону и «паркует» головку — уводит её в зону, где она ничего не может повредить.
На ноутах с твёрдотельным накопителем (ССД), этого сенсора обычно не бывает — он там не нужен. Например, на моём Макбуке Эйр его не было, нет его и на 15″ Макбуке Про с Ретиной моего друга, я специально смотрел — в этих ноутах нет жёстких дисков. В Макбуках Про предыдущей модели, без Ретины и с жёстким диском, сенсор, естественно, был.
Каково же было моё удивление, когда я обнаружил, после покупки Макбука Про 13″, что в сведениях о системе есть упомянутый сенсор:

Думаю, его просто забыли убрать, наверное это рудимент прежней модели Макбука. Но наличие сенсора не говорит о том, что он работает. Я сегодня потратил пару часов, чтобы разобраться как в Маке читают этот сенсор и написал вот такую программу на Си:
// MacbookPro (OS X 10.5+) SMS reading. Evgeny Stepanischev May 2013
// gcc -framework IOKit motion.c
#include <IOKit/IOKitLib.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define RETURNONFAIL(code) do { if (errcode != KERN_SUCCESS) {\
fprintf(stderr, "Error code: 0x%Xn", GETCODE(errcode));\
return code;\
} } while (0)
#define GETCODE(err) ((err)&0x3fff)
typedef struct {
int16_t x;
int16_t y;
int16_t z;
} ostruct;
int main() {
mach_port_t masterPort;
kern_return_t errcode;
io_iterator_t iterator;
io_object_t service;
io_connect_t connect;
void *in;
ostruct *out;
size_t osize = 40, isize = 40;
errcode = IOMasterPort(MACH_PORT_NULL, &masterPort);
RETURNONFAIL(-1);
CFMutableDictionaryRef matchingDictionary = IOServiceMatching("SMCMotionSensor");
errcode = IOServiceGetMatchingServices(masterPort, matchingDictionary, &iterator);
RETURNONFAIL(-2);
service = IOIteratorNext(iterator);
IOObjectRelease(iterator);
if (service == kIOReturnNoDevice) {
return -3;
}
errcode = IOServiceOpen(service, mach_task_self(), 0, &connect);
IOObjectRelease(service);
RETURNONFAIL(-4);
in = malloc(isize);
out = malloc(osize);
if (in == NULL || out == NULL) {
return -6;
}
memset(out, 0, osize);
memset(in, 1, isize);
errcode = IOConnectCallStructMethod(connect, 5, in, osize, out, &isize);
RETURNONFAIL(-5);
printf("%d, %d, %d\n", (*out).x, (*out).y, (*out).z);
return 0;
}Программа выводит три числа: показатели наклона влево/вправо, назад/вперёд и показатель ускорения, который меняется, если ноут резко поднять или опустить.
Размер структуры 40 и второй параметр функции IOConnectCallStructMethod («5») — «магические» значения, первое — фиксированный размер буфера, который требуется функции, второе — номер вызываемой функции в ядре (SMCMotionSensor).
MongoDB разонравилась
Очень много раньше хорошего слышал про «Монгу», прочитал по ней пару книг, даже пытался использовать в рабочих проектах. Всё, теперь ни ногой.
Естественно, если
Инвертирование в CSS без фильтров
Немногие знают, что инвертирование части
Я говорю о поддержке значения invert свойства

Наложить сверху элемент с аутлайном можно любым способом, я использовал position: absolute:
<style>
.invert {
outline: invert solid 75px;
position: absolute;
width: 50px; height: 0;
margin: 75px 0 0 75px;
}
.content {
width: 200px;
height: 150px;
position: absolute;
left: 200px;
top: 200px;
text-align: center;
}
</style>
<div class="content">
<div class="invert"></div>
<img src="/i/foto.jpg">
</div>Для того, чтобы высчитать размеры накрывающего блока, нужно немного арифметики. У меня большую размерность имеет ширина (накрываемый блок размером 200×150), поэтому будем плясать от неё. Высота накрывающего блока должна быть в этом случае равна нулю, ширина — разности между шириной и высотой накрываемого блока,
Думаю, ни для кого не составит труда самостоятельно провести вычисления для обратного случая — когда высота больше ширины.
Тонкое подчёркивание ссылок с разрывами на буквах «р», «у» и других
У меня на сайте два изменения.
Но просто сделать так же не интересно, и я решил сделать лучше. Спорно получилось ли лучше, тут есть простор для экспериментов.
Подчёркивание ссылок и Тёмы и Ильи сделано одинаково — убрано родное подчёркивание, а взамен установлено свойство
Сначала позвольте показать что получилось, обратите внимание на разрыв на хвостике «р», когда я добился более плотного прилегания, оказалось, что перечёркивание хвостов меня не устраивает, хорошо что удалось это поправить:

Собственно, мне захотелось сначала исправить этот недостаток — слишком далеко отстоящее подчёркивание. Готовое решение у меня было, я
Я довольно долго эксперементировал и нашёл,
И вот тут меня ждала неприятность. Дело в том, что некоторое время назад появился способ, позволяющий узнать какие сайты вы посещали; интернет пестрел скриптами, которые в шутку журили вас, если обнаруживали, что вы до этого посещали
Способ проверки простой — на экран выводится ссылка, посещение которой пользователем мы хотим проверить, а потом мы смотрим у неё, скажем, цвет или другой параметр, который мы установили различающимся для посещённых и не посещённых ссылок. Я хорошо помню эту историю, потому что сделал на основе этого скрипт, который определял логинились ли вы в Гмейл — я просто смотрел загружал ли ваш браузер определённые ресурсы, которые грузятся только после логина. Но прежде чем я придумал как это можно практически использовать, дыру начали закрывать, так что скрипт не пригодился.
Естественно, браузеры не могли не отреагировать на эту дыру. Кажется, Файерфокс первым отреагировавшим на проблему — они предложили «врать» разработчику о стиле посещённых ссылок и разрешили менять для них только строго определённый список
Увы, это меняет многое. У меня для того, чтобы цвет посещённых ссылок не различался с цветом подчёркивания, нужно было поменять
В голове перед сном я прокручивал список разрешённых к изменению свойств и одно из них,

Теперь, если выставить его в качестве стиля заднего фона ссылки, а потом ещё задать цвет фона, ссылка оказывается аккуратно подчёркнутой в нужном мне, регулируемом месте (я же могу сделать прозрачным любой пиксель в фоне).
Ок, это сработало для посещённых ссылок, но в «Хроме» и «Сафари» меня ждал неприятный сюрприз: на мгновение, до того как браузер успевал отрисовать мой ГИФ на заднем фоне, все ссылки выглядели уродливо: был виден их цветной задний фон:

Я подумал, что мне бы помогло, если я бы
Получился следующий стиль:
a {
background: #00e url(data:image/gif;base64,R0lGODlhAQA7APABAP …и так далее… AAACCISPqcvtEEwBADs=) 0 100% repeat-x;
background-image: -webkit-linear-gradient(90deg, #fff 0, #fff 2px, rgba(0,0,0,0) 2px, #fff 3px, #fff 59px);
}Это полностью решило проблему — мигание на вебкитовских браузерах пропало. Кстати, я применил стиль линейного градиента, в новом формате — он работает в «Сафари» с версии 5.1 и в «Хроме» с десятой. Мне кажется, в этом нет ничего страшного — доля старых браузеров, в данном случае, ничтожна мала.
Когда я поставил подчёркивание куда мне хочется, возникла другая проблема. Эстетическая. Подчёркивание довольно уродливо перечёркивало нижние выносные элементы букв. На память, по аналогии, пришло моё баловство с фильтром Glow в Интернет Эксплорере. Там удавалось добиться интересных эффектов с задним фоном, поскольку эффект ложится и на него. Тут у меня ситуация такая же, но фильтров больше не существует, правда есть их реинкарнация —W3C заботливо слизало идею в другом синтаксисе.
После некоторых экспериментов, я решил, что тут подходит
a {
text-shadow: 1px -1px 1px white, -1px -1px 1px white, 2px -1px 2px white, -2px -1px 2px white;
}В браузерах, которые этот стиль не поддерживают, подчёркивание просто не будет разорвано.
Стиль, в результате выглядит примерно так:
a {
text-shadow: 1px -1px 1px white, -1px -1px 1px white, 2px -1px 2px white, -2px -1px 2px white;
text-decoration: none;
background: #00e url(data:image/gif;base64,R0lGODlhAQA7APABAP … и так далее … AAACCISPqcvtEEwBADs=) 0 100% repeat-x;
background-image: -webkit-linear-gradient(90deg, #fff 0, #fff 2px, rgba(0,0,0,0) 2px, #fff 3px, #fff 59px);
}
a:visited {
color: #551a8b;
background-color: #551a8b;
}
a:hover {
background-color: red;
color: red;
}Очень компактно, без лишнего и с плавной деградацией для старых браузеров.
Почему меня беспокоит размер программы
В комментариях к одной из заметок, где я показывал программу на Гугл Гоу меня спросили почему мне так важен размер программы, я там как раз сетовал, что размер скомпилированной программы получился около четырёх мегабайт.
С одной стороны, конечно, средства хранения сейчас дёшевы и для современных дисков это не объём. С другой стороны, с оперативной памятью всё несколько похуже. Мы пока разделяем два пространства: пространство выполнения программ и пространство их хранения. Хотя идут эксперименты по их совмещению, в продаже пока ничего подобного нет.
Поэтому данность: количество оперативной памяти ограничено, в случае моего ноута — сильно ограничено, у меня их всего 2ГиБ, это мало по нынешним временам, но что делать. Типичные современные объёмы оперативной памяти не отличаются от этой цифры на порядки, обычно это 4—8 ГиБ.

Соответственно, меня расстраивают большие программы просто по той причине, что они занимают много памяти, которой и так немного. Если у вас открыт браузер, Фотошоп, Офис и ещё
Соответственно, чем больше некомпактных утилит (например, на «Гоу») будет работать, тем меньше у меня памяти, тем медленнее система, что меня расстраивает, конечно.
Удивительный Си
Кажется, на любом языке программирования можно написать очень запутанную программу. Лидер, наверное,
main(_){_^448&&main(-~_);putchar(--_%64?32|-~7[__TIME__-_/8%8][">'txiZ^(~z?"-48]>>";;;====~$::199"[_*2&8|_/64]/(_&2?1:8)%8&1:10);}Эта программа выводит время на момент компиляции в следующем формате:

У Анатолия Воробья есть полный анализ этой программы, остроумно сделано!
Как скачать документ из АИС ЦГАСО

Изучаю тут самарский электронный архив, который сделан в «Эларе». Насколько я понял, продукт называется АИС ЦГАСО. Убиваю сразу двух зайцев — разбираюсь как всё работает и пытаюсь найти нужные мне документы по родственникам.
Ребята из «Элара» молодцы, делают очень нужное дело — оцифровывают бумажные архивы, это невероятный труд, но, к сожалению, интерфейс просмотра документов оставляет желать лучшего, он мне сжёг массу нервов. Решил просто выкачать нужные мне документы на компьютер и дело с концом. Написал специальную программу для скачивания документов из АИС ЦГАСО, на Гугл Гоу. Получилось довольно компактно.
Основная фишка — паралельное скачивание документов в несколько потоков. Не уверен, что это действительно работает (я не до конца понимаю как Гоу переключает свои горутины), но в теории должно. Добавлено: «полевые испытания» подтвердили, что документы действительно скачиваются параллельно. Удалось сделать крайне просто:
ch := make(chan byte, N)
for i, id := range documents {
ch <- 1
go func(id string, index int) {
name := padZero5(strconv.Itoa(index)) + ".jpg"
copyUrlToFile(strings.Replace(url, `%id`, id, 1), *dir + "/" + name)
<-ch
}(id, i)
}Первой строкой в коде создаётся канал заданной размерности. С ним будут работать горутины (это такие легковесные потоки Гоу). В него можно положить только N значений, после чего следующая операция записи будет ждать его освобождения. С помещением в канал каждого нового значения, запускается горутина для скачивания одного листа документа, как только горутина отработала, она убирает из канала одно значение и цикл основного потока снова его кладёт и запускает новую горутину.
В итоге, одновременно, не будет запущено более N горутин.
Пользоваться программой просто (если
go run cgodownloader.go скачанный-файл.htmlПрограмма обработает документ, покажет сколько страниц найдено и начнёт их скачивать в виде больших джпегов в ту же папку, показывая их имена. У меня на большом документе (около 1,5 тысяч страниц) заняло прилично времени — час с лишним.

Зато теперь просматривать удобно.
30 банковских дней-2
Жена попросила во вчерашнюю программу ряд улучшений внести и пожаловалась, что она на одном из компьютеров на работе запускается медленно. Ну, я решил переписать её на Гоу, раз такое дело, может быстрее стартовать будет. Кроме того, мне всегда интересно посмотреть как одна и та же программа выглядит на разных языках программирования.
package main
import (
"encoding/json"
"os"
s "strconv"
t "time"
"net/http"
"io/ioutil"
"fmt"
r "regexp"
"bufio"
"strings"
fp "path/filepath"
)
type date struct {
D byte
M byte
Y int
}
func CreateDate(d, m string, y int) date {
day, _ := s.Atoi(d)
month, _ := s.Atoi(m)
return date{D:byte(day), M:byte(month), Y:y}
}
func (d date) FindIn(list []date) bool {
for _, cur := range list {
if d.D == cur.D && d.M == cur.M && (d.Y == cur.Y || cur.Y == 0) {
return true
}
}
return false
}
func (d date) ToTime() t.Time {
return t.Date(d.Y, t.Month(d.M), int(d.D), 0, 0, 0, 0, t.UTC)
}
func (d date) IsWeekEnd() bool {
dw := string(d.ToTime().Weekday())
return dw == "Saturday" || dw == "Sunday"
}
func (d date) Add(delta string) date {
dur, _ := t.ParseDuration(delta)
t := d.ToTime().Add(dur)
return date{D:byte(t.Day()), M:byte(t.Month()), Y:t.Year()}
}
func (d date) String() string {
return fmt.Sprintf("%02d.%02d.%04d", d.D, d.M, d.Y)
}
func loadCache(name string) (holidays []date) {
f, err := os.OpenFile(name, os.O_RDONLY, 0666)
if err != nil {
return nil
}
defer f.Close()
if err := json.NewDecoder(f).Decode(&holidays); err != nil {
return nil
}
return
}
func saveCache(name string, holidays []date) bool {
f, err := os.OpenFile(name, os.O_WRONLY|os.O_TRUNC|os.O_CREATE, 0666)
if err != nil {
return false
}
defer f.Close()
if err := json.NewEncoder(f).Encode(&holidays); err != nil {
return false
}
return true
}
func getCurrentCalend() []byte {
if resp, err := http.Get("http://www.calend.ru/work/"); err == nil {
defer resp.Body.Close()
if body, err := ioutil.ReadAll(resp.Body); err == nil {
return body
}
}
return nil
}
func getCurrentHolidays() (dates []date) {
table, _ := r.Compile(`(?s)<table\s.+?time_of_death(.+)</table>`)
chunk, _ := r.Compile(`(?s)<td\s+class="\S+\s+col5"\s+day="(\d+)"\s+month="(\d+)"`)
dates = []date{}
year := t.Now().Year()
for _, value := range chunk.FindAllStringSubmatch(string(table.Find(getCurrentCalend())), -1) {
dates = append(dates, CreateDate(value[1], value[2], year))
}
return
}
func ReadUserInput(message string) string {
fmt.Print(message)
input, _ := bufio.NewReader(os.Stdin).ReadString('\n')
return input
}
func main() {
var currentholidays []date
year := t.Now().Year()
cachename := s.Itoa(year) + ".json"
// если кеш существует
if _, e := os.Stat(cachename); e == nil {
currentholidays = loadCache(cachename)
} else {
currentholidays = getCurrentHolidays()
saveCache(cachename, currentholidays)
}
// загружаем другие кеши
caches, _ := fp.Glob("*.json")
for _, name := range caches {
if name != cachename {
currentholidays = append(currentholidays, loadCache(name)...)
}
}
// опрашиваем пользователя
mask, _ := r.Compile(`^(?m)(\d+)\D+(\d+)`)
for {
var userdate date
for {
d := mask.FindStringSubmatch(ReadUserInput("Enter date (dd.mm): "))
if len(d) > 0 {
userdate = CreateDate(d[1], d[2], year)
break
}
}
var period int
for {
period, _ = s.Atoi(strings.TrimRight(ReadUserInput("Enter period: "), "\r\n"))
if period > 0 {
break
}
}
for ; period > 0; period-- {
for {
userdate = userdate.Add("24h")
if !userdate.FindIn(currentholidays) && !userdate.IsWeekEnd() {
break
}
}
}
fmt.Printf("Result: %s\n\n", userdate)
}
}Меня поразил размер скомпилированной программы. Вот эта мелочь под «Маком» занимает 3,9Мб, под «Виндой» — 3,7! Жена попросила убрать гуишное окошко, поэтому я сделал программу полностью консольной. Два разочарования: в регулярных выражениях под Виндой неверно определяется конец строки (не учитывается «\r», судя по всему) и второе — в Гоу нет ничего, чтобы работать с однобайтными кодировками, а в консоли Винды до сих пор CP866. Пришлось выводить всё
Объём увеличился,
30 банковских дней
Делал жене программу, чтобы отсчитывать от введёной даты 30 банковских дней в текущем году, может пригодится кому (в запускаемый файл преобразовал при помощи PyInstaller, получается один файл, очень удобно). При первом запуске программа скачивает календарь с сайта Calend.Ru и сохраняет рядом, потом берёт из него выходные и праздничные дни.
# coding: utf-8
import re
import httplib
import pickle
import ctypes
from datetime import date, datetime, timedelta
MB_OKCANCEL = 1
MB_SYSTEMMODAL = 4096
IDOK = 1
IDCANCEL = 2
def getCalend():
h = httplib.HTTPConnection('www.calend.ru')
h.request('GET', '/work/')
return h.getresponse().read()
def getHolidays():
table = re.findall(r'<table\s.+?time_of_death(.+)</table>', getCalend(), re.DOTALL)[0]
return re.findall(r'<td\s+class="\S+\s+col5"\s+day="(\d+)"\s+month="(\d+)"', table, re.DOTALL)
def readHolidays():
year = str(date.today().year)
try:
with open(year + '.cache', 'r') as f:
holidays = pickle.load(f)
except IOError:
with open(year + '.cache', 'w') as f:
holidays = tuple(getHolidays())
pickle.dump(holidays, f)
return [datetime(day=int(d[0]), month=int(d[1]), year=int(year)) for d in holidays]
def getStartDate():
while 1:
input = re.split(r'\D', raw_input("Enter start date (dd.mm): "))
try:
input = [int(x) for x in input]
if len(input) == 2:
break
except ValueError:
pass
return (date.today().year, input[1], input[0])
delta = timedelta(days = 1)
holidays = readHolidays()
msgbox = ctypes.windll.user32.MessageBoxW
while 1:
try:
date = datetime(*getStartDate())
for i in xrange(0, 30):
while 1:
date += delta
if date not in holidays: break
if msgbox(
0,
u'Получилась следующая дата: %02d.%02d.%04d' % (date.day, date.month, date.year),
u'Ответ',
MB_OKCANCEL | MB_SYSTEMMODAL
) == IDCANCEL:
raise StopIteration
except (KeyboardInterrupt, StopIteration):
breakПрограмму написал быстро, а сложнее всего было её отправить — ГМэйл упорно не принимает запускаемые файлы, даже в архивах.
Какие графические форматы поддерживает ваш браузер?
Я тут скрипт накидал, который проверяет какие графические форматы поддерживает ваш браузер. Проверяется JPEG, GIF, PNG, XMB, WebP, BMP, SVG, JPEG XR, TIFF, PDF, EMF, WMF, WBMP (Wireless BMP),
Тестируется поддержка в теге IMG.
Любопытно, что «Сафари» до сих пор поддерживает
Дополнение: оказывается, SVG в моем тесте был не до конца верный (я его просто сконвертировал из гифа 1×1
Чудны́ дела твои, PHP!
Может быть
Я делал это функцией copy, первом аргументом передавал УРЛ, вторым — путь на файловой системе. В логах, после изменения конфигурации, ПХП ругался, что «http:» — это неправильное начало для разрешённого пути. Удивительно, но стоило заменить copy// на её, фактически, эквивалент, как всё заработало:
if ($fin = fopen($url, 'rb') and $fout = fopen($file, 'wb')) {
stream_copy_to_stream($fin, $fout);
fclose($fout);
fclose($fin);
return true;
} else {
return false;
}Такое поведение больше похоже на баг, ПХП у меня не последний (5.3.21 сейчас), может поправили уже.