В файле описания принтера IBM 4029 LaserPrinter для очереди вывода ASCII длина страницы в строках задается с помощью атрибута wL. При определении значения атрибута wL происходит обработка внутренних ссылок, указанных в определении этого атрибута. Команда lsvirprt добавляет в файл описания принтера следующее определение атрибута wL:
Длина страницы в символах с учетом значения из базы данных (применяется с конвейерами) wL = %?%Cl%t%f!l%e%I_l%;
%? <IF> %Cl PUSH: (1, если в командной строке есть флаг -l; 0 в противном случае) %t <THEN> %f!l Для каждого флага x в командной строке выдать "-xАргумент" -> OUTPUT %e <ELSE> %I_l Включить в список: (число строк на странице) %; <END>
Команда %Cl проверяет, есть ли в командной строке флаг l; если он есть, то в стек заносится число 1; в противном случае в стек заносится 0. В данном случае в командной строке не был указан флаг l, и поэтому в стек будет добавлен 0. Команда %t проверяет, истинное (ненулевое) ли значение находится в стеке и (поскольку в стек только что был добавлен 0) выполняет оператор %e (else) %I_l.
Согласно следующей процедуре, команда lsvirprt присваивает атрибуту _l значение %IwY.
Длина страницы по умолчанию (в строках) wY = %?%G_z%{1}%&%t%GwJ%e%GwK%;%G_v%*%{300}%/%d
%? <IF> %G_z PUSH: (ориентация страницы) %{1} PUSH: (числовая константа 1) %& PUSH: (pop2 & pop1) -- поразрядная конъюнкция %t <THEN> %GwJ PUSH: (основная ширина страницы (-z 0) или вспомогательная длина страницы (-z1), в точках) %e <ELSE> %GwK PUSH: (основная длина страницы (-z 0) или вспомогательная ширина страницы (-z1), в точках) %; <END> %G_v PUSH: (плотность строк (число строк на дюйм)) %* PUSH: (pop2 * pop1) %{300} PUSH: (числовая константа 300) %/ PUSH: (pop2 / pop1) %d POP -> строка ASCII -> OUTPUT
Вычисление значения _l начинается с занесения в стек значения _z (ориентация страницы). В данном примере задание было запущено с помощью команды qprt -a1 -Pasc -fp -z1 -p12 -scourier -C -N3 /etc/motd, т.е. атрибуту z было присвоено значение 1. Поэтому в стек будет добавлено значение 1. Команда %{1} добавляет в стек еще одно значение 1, после чего команда %& забирает два значения из стека (в данном примере - две единицы) и выполняет для них поразрядную конъюнкцию. Результат операции (1) заносится в стек.
Примечание: Применение конъюнкции вместо обычного сравнения операндов обусловлено тем, что для флага z допустимы значения 0, 1, 2 и 3, задающие число поворотов страницы на 90 градусов. Значения 1 и 3 (нечетные) соответствуют альбомной, а 0 и 2 (четные) - книжной ориентации.
При выполнении команды %t в стеке находится число 1, поэтому перед дальнейшими вычислениями значения _l выполняется оператор then %GwJ.
В команде lsvirprt применяется следующая процедура вычисления значения атрибута wJ.
Основная ширина страницы (-z 0) или вспомогательная длина страницы (-z1), в точках wJ = %G_Q%Pq%?%GWu%{3}%<%t%?%gq%{1}%=%t%{2400}%e%gq%{2}%=%t%{2400 }%e%gq%{3}%=%t%{1999}%e%gq%{4}%=%t%{2330}%e%{2025}%;%e%?%gq%{1}%= %t%{1012}%e%gq%{2}%=%t%{1012}%e%gq%{3}%=%t%{1087}%e%gq%{4}%=%t%{1 149}%e%gq%{5}%=%t%{1763}%e%{1928}%;%;%d
%G_Q PUSH: (переопределение размера страницы для выбранного источника бумаги) %Pq POP -> внутренняя переменная q %? <IF> %GWu PUSH: (определить источник бумаги по значениям _O и _u.) %{3} PUSH: (числовая константа 3) %< PUSH: (pop2 < pop1 ?) %t <THEN> %? <IF> %gq PUSH: (внутренняя переменная q) %{1} PUSH: (числовая константа 1) %= PUSH: (pop2 = pop1 ?) %t <THEN> %{2400} PUSH: (числовая константа 2400) %e <ELSE> %gq PUSH: (внутренняя переменная q) %{2} PUSH: (числовая константа 2) %= PUSH: (pop2 = pop1 ?) %t <THEN> %{2400} PUSH: (числовая константа 2400) %e <ELSE> %gq PUSH: (внутренняя переменная q) %{3} PUSH: (числовая константа 3) %= PUSH: (pop2 = pop1 ?) %t <THEN> %{1999} PUSH: (числовая константа 1999) %e <ELSE> %gq PUSH: (внутренняя переменная q) %{4} PUSH: (числовая константа 4) %= PUSH: (pop2 = pop1 ?) %t <THEN> %{2330} PUSH: (числовая константа 2330) %e <ELSE> %{2025} PUSH: (числовая константа 2025) %; <END> %e <ELSE> %? <IF> %gq PUSH: (внутренняя переменная q) %{1} PUSH: (числовая константа 1) %= PUSH: (pop2 = pop1 ?) %t <THEN> %{1012} PUSH: (числовая константа 1012) %e <ELSE> %gq PUSH: (внутренняя переменная q) %{2} PUSH: (числовая константа 2) %= PUSH: (pop2 = pop1 ?) %t <THEN> %{1012} PUSH: (числовая константа 1012) %e <ELSE> %gq PUSH: (внутренняя переменная q) %{3} PUSH: (числовая константа 3) %= PUSH: (pop2 = pop1 ?) %t <THEN> %{1087} PUSH: (числовая константа 1087) %e <ELSE> %gq PUSH: (внутренняя переменная q) %{4} PUSH: (числовая константа 4) %= PUSH: (pop2 = pop1 ?) %t <THEN> %{1149} PUSH: (числовая константа 1149) %e <ELSE> %gq PUSH: (внутренняя переменная q) %{5} PUSH: (числовая константа 5) %= PUSH: (pop2 = pop1 ?) %t <THEN> %{1763} PUSH: (числовая константа 1763) %e <ELSE> %{1928} PUSH: (числовая константа 1928) %; <END> %; <END> %d POP -> строка ASCII -> OUTPUT
Вычисление значения wJ начинается с занесения в стек значения _Q - размера бумаги в выбранном устройстве подачи. Значение _Q равно %IwQ. Команда lsvirprt вычисляет значение wQ согласно следующей процедуре:
Размер бумаги или конвертов в устройстве подачи, заданном с помощью флагов -O и -u (см. атрибуты s0, s1, s2, s3 и s4) wQ = %?%GWu%{0}%=%t%Gs0%e%GWu%{1}%=%t%Gs1%e%GWu%{2}%=%t%Gs2%e%GWu%{3}% =%t%Gs3%e%Gs4%;%d
%? <IF> %GWu PUSH: (определить источник бумаги по значениям _O и _u.) %{0} PUSH: (числовая константа 0) %= PUSH: (pop2 = pop1 ?) %t <THEN> %Gs0 PUSH: (размер бумаги, подаваемой вручную) %e <ELSE> %GWu PUSH: (определить источник бумаги по значениям _O и _u.) %{1} PUSH: (числовая константа 1) %= PUSH: (pop2 = pop1 ?) %t <THEN> %Gs1 PUSH: (размер бумаги в лотке 1 (верхнем)) %e <ELSE> %GWu PUSH: (определить источник бумаги по значениям _O и _u.) %{2} PUSH: (числовая константа 2) %= PUSH: (pop2 = pop1 ?) %t <THEN> %Gs2 PUSH: (размер бумаги в лотке 2 (нижнем)) %e <ELSE> %GWu PUSH: (определить источник бумаги по значениям _O и _u.) %{3} PUSH: (числовая константа 3) %= PUSH: (pop2 = pop1 ?) %t <THEN> %Gs3 PUSH: (размер конвертов в устройстве подачи конвертов) %e <ELSE> %Gs4 PUSH: (размер конвертов, подаваемых вручную) %; <END> %d POP -> строка ASCII -> OUTPUT
Вычисление значения wQ начинается с занесения в стек значения Wu. Команда lsvirprt вычисляет значение Wu следующим образом:
Определение источника бумаги по значениям _O и _u. Wu = %?%CO%t%?%G_O%{1}%=%t%?%Cu%t%?%G_u%{2}%>%t%{4}%e%{0}%;%e%{0}%;%e% G_u%;%e%G_u%;%d
%? <IF> %CO PUSH: (1, если в командной строке есть флаг -O; 0 в противном случае) %t <THEN> %? <IF> %G_O PUSH: (Тип устройства подачи бумаги, предусмотрено только для совместимости со старыми версиями)) %{1} PUSH: (Числовая константа 1) %= PUSH: (pop2 = pop1 ?) %t <THEN> %? <IF> %Cu PUSH: (1, если в командной строке есть флаг -u; 0 в противном случае) %t <THEN> %? <IF> %G_u PUSH: (источник бумаги) %{2} PUSH: (числовая константа 2) %> PUSH: (pop2 > pop1 ?) %t <THEN> %{4} PUSH: (числовая константа 4) %e <ELSE> %{0} PUSH: (числовая константа 0) %; <END> %e <ELSE> %{0} PUSH: (числовая константа 0) %; <END> %e <ELSE> %G_u PUSH: (источник бумаги) %; <END> %e <ELSE> %G_u PUSH: (источник бумаги) %; <END> %d POP -> строка ASCII -> OUTPUT
Вычисление значения Wu начинается с выполнения команды %CO, заносящей в стек число 1, если в командной строке был указан флаг O, или число 0 в противном случае. В данном примере в командной строке не был указан флаг O, поэтому в стек было добавлено число 0. Команда %t, обнаружив в стеке 0, пропускает следующие 23 строки и переходит к оператору %e (else) в четвертой строке снизу. В операторе содержится только команда %G_u, заносящая в стек значение _u (источник бумаги). Атрибут _u по умолчанию для данного виртуального принтера равен 1, поэтому в стек будет добавлено число 1. Команда %; завершает выполнение условного оператора %?. Последняя команда - %d - забирает значение из стека (1) и выдает его в формате ASCII для последующего вычисления атрибута wQ.
Значение 1, возвращенное в качестве значения Wu в процедуру вычисления атрибута wQ, заносится в стек. Следующая команда %{0} заносит в стек число 0. Команда %= забирает два значения из стека (0 и 1), сравнивает их и, поскольку они не равны, заносит в стек значение 0.
Команда %t забирает значение из стека (0) и, поскольку оно нулевое, пропускает команду %Gs0 и переходит к оператору %e (else). В стек вновь заносится значение Wu (1). Команда %{1} заносит в стек значение 1. Команда %= проверяет равенство двух верхних значений в стеке (1 и 1) и, поскольку они равны, заносит вместо них в стек значение 1.
Поскольку в стеке находится значение 1, команда %t выполняет команду %Gs1. Атрибут s1 - это размер бумаги в лотке 1 (верхнем лотке), который для данного виртуального принтера по умолчанию равен 1. Поэтому в стек заносится число 1. Все остальные команды этой процедуры, за исключением последней, относятся к оператору wQ и пропускаются. Последняя команда (%d) забирает значение из стека (1) и возвращает его в формате ASCII в процедуру вычисления атрибута wJ.
Значение 1, возвращенное в качестве значения _Q в процедуру вычисления атрибута wJ, заносится в стек. Следующей командой оно забирается из стека и присваивается внутренней переменной q. В стек вновь заносится уже вычисленное значение Wu (1). Команда %{3} заносит в стек значение 3, затем команда %< забирает два значения из стека и проверяет, действительно ли второе полученное значение меньше, чем первое. Поскольку 1 меньше, чем 3, в стек заносится положительный результат (1). Затем команда %t забирает этот результат (1) из стека и начинает выполнение оператора if-then-else-then-else-then-else..., суть которого сводится к занесению в стек числа, соответствующего значению атрибута _Q.
Команда %gq заносит в стек значение внутренней переменной q (фактически - значение _Q). Команда %{1} заносит в стек значение 1. Затем команда %= забирает два значения из стека (два значения 1), сравнивает их и, получив положительный результат, заносит его в стек (1). Команда %t, забрав из стека 1, выполняет команду %{2400}, которая, в свою очередь, заносит в стек число 2400. После этого выполнение оператора wJ завершается и выполнение передается в последнюю строку процедуры. Последняя команда (%d) забирает из стека значение 2400 и возвращает его в формате ASCII в процедуру вычисления атрибута wY.
Значение 2400, возвращенное в качестве значения wJ в процедуру вычисления атрибута wY, заносится в стек. Команда %GwK, относящаяся к оператору else, пропускается, и команда %; завершает выполнение условного оператора. Команда %G_v заносит в стек число строк на дюйм (6). Команда %* забирает два числа из стека (6 и 2400), перемножает их и заносит в стек произведение (14400). Команда %{300} заносит в стек число 300. Команда %/ забирает из стека делитель и делимое (300 и 14400), выполняет деление и заносит частное (48) в стек. Команда %d забирает из стека значение 48 и возвращает его в формате ASCII в процедуру вычисления значения wL.
Число 48 возвращается в качестве значения _l в процедуру вычисления wL. Значение wL впервые вычисляется при определении значения атрибута ia - конвейера входного потока данных для заданий ASCII. В этой процедуре команда %IwL получает значение 48, которое присваивается флагу -! в команде вызова программы pioformat: /usr/lib/lpd/pio/fmtrs/piof5202 -l48. Значение -l48 было выдано в исходном диагностическом сообщении piobe, обсуждавшемся в этой главе; оно указано в разделе КОНВЕЙЕР ФИЛЬТРОВ почтового сообщения, отправленного пользователем qdaemon от имени процесса piobe.
Вычисление значения флага -w команды piof5202 проиллюстрировано в разделеВычисление ширины страницы по escape-последовательностям в файле описания принтера.
Операции, выполняемые над стеком в ходе вычисления длины страницы, проиллюстрированы на рисунке "Вычисление длины страницы". Ниже приведен пошаговый список этих операций, соответствующих данному файлу описания для очереди вывода asc и данной командной строке. Номера шагов в этом списке соответствуют числам, указанным слева от блоков на рисунке.
Рис. 4-1. Вычисление длины страницы
Ниже приведена логическая схема процедуры, выполняемой программой piobe при обработке ссылки на %IwL.
В технической документации по принтерам IBM LaserPrinter 4029 приведены рисунок и таблица с информацией о стандартных форматах бумаги и конвертов и размерах областей печати. Например, область печати на бумаге размером 8,5 x 11 дюймов составляет 2400 x 3200 точек (ширина х длина). Учтите, что если печать выполняется с поворотом на 90 или 270 градусов, то область печати составляет уже 3200 x 2400 точек (ширина х длина).
В этом случае применяется значение данного флага. (При этом не гарантируется, что данное значение будет допустимо - оно не проверяется.) В этом случае применяется значение данного флага. (При этом не гарантируется, что данное значение будет допустимо - оно не проверяется.) При альбомной ориентации длина страницы равна значению атрибута wJ. После вычисления длины страницы в точках для получения значения wY (числа строк на странице) достаточно учесть плотность строк.
В первую очередь при вычислении длины страницы _l определяется ее ориентация (_z). Как указано выше, при повороте на 90 или 270 градусов размер области печати по ширине и длине изменяется на противоположный. В условном операторе, который указан в начале процедуры вычисления wY, значение _z определяет, какое из двух значений (wJ и wK) является длиной страницы. Если в командной строке указан флаг Q, длина страницы будет вычисляться согласно его значению; в противном случае длина бумаги будет определена по значению Wu, которое, в свою очередь, зависит от значений _O (тип устройства подачи) и _u (источник бумаги). Обратите внимание, что процедура _Q начинается с вызова процедуры %IwQ. Процедура %IwQ начинается с вызова %IWu.
Поскольку в данном примере применяется альбомная ориентация, вызывается процедура wJ. К данному моменту известно лишь то, что печать выполняется с поворотом; фактический размер области печати еще не известен. Вычисление wJ начинается проверки наличия флага Q, задающего нестандартный размер бумаги. Если в командной строке указан флаг Q, длина страницы будет вычисляться согласно его значению; в противном случае длина бумаги будет определена по значению Wu, которое, в свою очередь, зависит от значений _O (тип устройства подачи) и _u (источник бумаги). Обратите внимание, что процедура _Q начинается с вызова процедуры %IwQ.
Поскольку в командной строке нет флага Q, вызывается процедура Wu, которая прежде всего проверяет, есть ли в командной строке флаг O. Этого флага нет, и поэтому выполняется ветвь условного оператора, возвращающая значение по умолчанию для атрибута _u из файла описания виртуального принтера. Это значение равно 1, и оно передается в процедуру вычисления wQ.
Поскольку при вычислении _l в конце концов вызывается процедура Wu, мы подробно рассмотрим ее. В этой процедуре используются значения атрибутов O, u и Q:
В процедуре вычисленияWu рассматриваются следующие случаи:
Процедура wQ состоит из сложного условного оператора if-then-else-then-else-then-else-then-else, в котором значение Wu последовательно сравнивается с числами 0, 1, 2 и 3. По результатам сравнения выбирается значение атрибута s0, s1, s2, s3 или s4 соответственно (s4 выбирается в случае, если совпадение не найдено). Этим атрибутам присвоены следующие значения, зависящие от конкретного принтера:
В описании виртуального принтера для очереди вывода ASCII и принтера IBM 4029 LaserPrinter предусмотрены следующие значения для этих атрибутов: s0, s1 и s2 равны 1, а s3 и s4 равны 3.
Затем управление возвращается процедуре wJ, которая также фактически состоит из сложного условного оператора. И ветвь if, и ветвь else этого оператора содержат цепочку операторов if-then-else-then-else... Значение атрибута Wu (источник бумаги, зависящий от значенийO и u) определяет ветвь внешнего оператора if: если Wu равно 1 или 2 (меньше 3), то выполняется предложение %t; в противном случае выполняется предложение %e. Это последний этап вычисления значения атрибута wJ - длины страницы в точках.
Условный оператор в предложении %t внешнего оператора if действует в случае, если подается обычная бумага; условный оператор в предложении %e - в случае, если подаются конверты. От значения Wu зависит ветвь внешнего оператора if, но во внутреннем операторе if ветвь выбирается по значению атрибута Q. Пять случаев, рассмотренных выше, обрабатываются следующим образом:
Случай 1: В процедуру вычисления wQ возвращается значение u, указанное в командной строке, или значение по умолчанию из файла описания виртуального принтера (1, основной лоток подачи бумаги). Оставшиеся команды процедуры wQ в зависимости от значения Wu выбирают значение одного из атрибутов s0, s1, s2, s3 и s4. Выбранное значение возвращается в процедуру вычисления wJ. Если u равно 1 или 2, то Q будет присвоено значение 1 (печать на обычной бумаге). Если u=3, то Q будет присвоено значение 3 (печать на конвертах). В первом случае (u=1 или 2) в процедуре wJ выполняется предложение then (%t) внешнего оператора if-then-else, и поскольку Q=1, длина страницы будет установлена равной 2400 точкам. Во втором случае ((u=3) выполняется предложение else (%e) внешнего оператора if-then-else, и поскольку Q=3, длина конвертов будет установлена равной 1087 точкам.
Случай 2: Совпадает со случаем 1.
Случай 3: Поскольку пользователь указал, что бумага будет подаваться вручную, но не задал устройство подачи, атрибуту Wu было присвоено значение 0, и оно было передано в процедуру wQ. В результате атрибуту wQ присваивается значение s0 (размер бумаги при подаче вручную -1). Поскольку атрибут u равен нулю, в процедуре wJ будет выполняться предложение then внешнего оператора if-then-else. В силу того, что Q= 1, длина страницы будет установлена равной 2400 точкам.
Случай 4: Пользователь указал, что бумага будет подаваться вручную, и с помощью флага u задал основное или вспомогательное устройство подачи бумаги (но не устройство подачи конвертов). Так же, как и в случае 3, длина страницы будет установлена равной 2400 точкам.
Случай 5: Пользователь указал, что подача будет осуществляться вручную, и с помощью флага u задал в качестве источника бумаги устройство подачи конвертов. В результате атрибуту Wu было присвоено значение 4. В результате атрибуту wQ присваивается значение s4 (размер конвертов при подаче вручную - 3). Поскольку атрибуту u присвоено значение 4, в процедуре wJ выполняется предложение else внешнего оператора if-then-else. В силу того, что Q=3, длина конвертов будет установлена равной 1087 точкам.
Рассматриваемый пример относится к случаю 1: в командной строке не указаны флаги O и u. Поэтому атрибуту Wu присваивается значение 1 - значение _u по умолчанию из файла описания виртуального принтера. В процедуре wQ выбирается атрибут s1, и в процедуру wJ возвращается значение 1. Поскольку u=1, выполняется оператор then внешнего оператора if-then-else, и в силу того, что Q=1, длина страницы устанавливается равной 2400 точкам. Это значение возвращается в процедуру вычисления _l.
Затем в процедуре _l на основании значений 2400 (число точек на странице в длину), 6 (число строк на дюйм) и 300 (число точек на дюйм) вычисляется число строк на странице - 48. Значение 48 возвращается в процедуру ia. Этим и объясняется появление строки -l48 в почтовом сообщении КОНВЕЙЕР ФИЛЬТРОВ.